Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
import Joi from 'joi';
|
||||
|
||||
// Consent validation schemas
|
||||
export const consentSchema = Joi.object({
|
||||
type: Joi.string()
|
||||
.valid('marketing', 'analytics', 'data_processing', 'third_party_sharing', 'cookies')
|
||||
.required(),
|
||||
purposes: Joi.array()
|
||||
.items(Joi.string())
|
||||
.min(1)
|
||||
.required(),
|
||||
duration: Joi.number()
|
||||
.integer()
|
||||
.min(1)
|
||||
.max(730) // Max 2 years
|
||||
.default(365),
|
||||
renewable: Joi.boolean()
|
||||
.default(true),
|
||||
location: Joi.object({
|
||||
country: Joi.string().length(2),
|
||||
region: Joi.string(),
|
||||
city: Joi.string()
|
||||
}),
|
||||
preferences: Joi.object({
|
||||
channels: Joi.array().items(
|
||||
Joi.string().valid('email', 'sms', 'push', 'in_app')
|
||||
),
|
||||
frequency: Joi.string().valid('always', 'daily', 'weekly', 'monthly'),
|
||||
topics: Joi.array().items(Joi.string())
|
||||
})
|
||||
});
|
||||
|
||||
// Data request validation schemas
|
||||
export const dataRequestSchema = Joi.object({
|
||||
userId: Joi.string()
|
||||
.required(),
|
||||
type: Joi.string()
|
||||
.valid('access', 'portability', 'deletion', 'rectification', 'restriction', 'objection')
|
||||
.required(),
|
||||
requestDetails: Joi.object({
|
||||
scope: Joi.string()
|
||||
.valid('all', 'specific')
|
||||
.default('all'),
|
||||
dataCategories: Joi.when('scope', {
|
||||
is: 'specific',
|
||||
then: Joi.array().items(Joi.string()).min(1).required(),
|
||||
otherwise: Joi.array().items(Joi.string())
|
||||
}),
|
||||
format: Joi.string()
|
||||
.valid('json', 'csv', 'xml')
|
||||
.default('json'),
|
||||
delivery: Joi.string()
|
||||
.valid('download', 'email')
|
||||
.default('download'),
|
||||
corrections: Joi.when('type', {
|
||||
is: 'rectification',
|
||||
then: Joi.object().required(),
|
||||
otherwise: Joi.object()
|
||||
}),
|
||||
restrictions: Joi.when('type', {
|
||||
is: 'restriction',
|
||||
then: Joi.array().items(Joi.object({
|
||||
purpose: Joi.string().required(),
|
||||
restriction: Joi.string().required()
|
||||
})).required(),
|
||||
otherwise: Joi.array()
|
||||
}),
|
||||
objections: Joi.when('type', {
|
||||
is: 'objection',
|
||||
then: Joi.array().items(Joi.object({
|
||||
purpose: Joi.string().required(),
|
||||
reason: Joi.string().required()
|
||||
})).required(),
|
||||
otherwise: Joi.array()
|
||||
})
|
||||
}).required(),
|
||||
reason: Joi.string()
|
||||
.max(500),
|
||||
regulation: Joi.string()
|
||||
.valid('gdpr', 'ccpa', 'lgpd', 'other')
|
||||
.default('gdpr')
|
||||
});
|
||||
|
||||
// Violation validation schemas
|
||||
export const violationSchema = Joi.object({
|
||||
type: Joi.string()
|
||||
.valid(
|
||||
'unauthorized_access',
|
||||
'data_leak',
|
||||
'consent_expired',
|
||||
'retention_exceeded',
|
||||
'unauthorized_sharing',
|
||||
'security_breach',
|
||||
'non_compliance',
|
||||
'data_access_denied',
|
||||
'notification_failure'
|
||||
)
|
||||
.required(),
|
||||
severity: Joi.string()
|
||||
.valid('low', 'medium', 'high', 'critical')
|
||||
.required(),
|
||||
details: Joi.object({
|
||||
description: Joi.string()
|
||||
.required(),
|
||||
affectedUsers: Joi.number()
|
||||
.integer()
|
||||
.min(0)
|
||||
.required(),
|
||||
affectedData: Joi.array()
|
||||
.items(Joi.string())
|
||||
.required(),
|
||||
regulation: Joi.string()
|
||||
.valid('gdpr', 'ccpa', 'lgpd', 'other')
|
||||
.required(),
|
||||
articles: Joi.array()
|
||||
.items(Joi.string()),
|
||||
detectionMethod: Joi.string()
|
||||
.valid('automated', 'manual', 'user_report', 'audit')
|
||||
.required()
|
||||
}).required(),
|
||||
remediation: Joi.object({
|
||||
status: Joi.string()
|
||||
.valid('pending', 'in_progress', 'completed')
|
||||
.default('pending'),
|
||||
actions: Joi.array().items(Joi.object({
|
||||
action: Joi.string().required(),
|
||||
description: Joi.string(),
|
||||
targetDate: Joi.date(),
|
||||
status: Joi.string().valid('pending', 'completed').default('pending')
|
||||
}))
|
||||
})
|
||||
});
|
||||
|
||||
// Validation middleware
|
||||
export const validateConsent = (req, res, next) => {
|
||||
const { error } = consentSchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Validation error',
|
||||
details: error.details.map(d => d.message)
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
export const validateDataRequest = (req, res, next) => {
|
||||
const { error } = dataRequestSchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Validation error',
|
||||
details: error.details.map(d => d.message)
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
export const validateViolation = (req, res, next) => {
|
||||
const { error } = violationSchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Validation error',
|
||||
details: error.details.map(d => d.message)
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
// Additional validation helpers
|
||||
export const validateEmail = (email) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
};
|
||||
|
||||
export const validatePhoneNumber = (phone) => {
|
||||
const phoneRegex = /^\+?[1-9]\d{1,14}$/;
|
||||
return phoneRegex.test(phone);
|
||||
};
|
||||
|
||||
export const validateIPAddress = (ip) => {
|
||||
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
const ipv6Regex = /^([\da-f]{1,4}:){7}[\da-f]{1,4}$/i;
|
||||
return ipv4Regex.test(ip) || ipv6Regex.test(ip);
|
||||
};
|
||||
|
||||
export const validateDateRange = (startDate, endDate) => {
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
return start <= end && start <= new Date();
|
||||
};
|
||||
|
||||
export const validateRegulation = (regulation, country) => {
|
||||
const regulationMap = {
|
||||
gdpr: ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR',
|
||||
'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK',
|
||||
'SI', 'ES', 'SE', 'GB', 'IS', 'LI', 'NO'],
|
||||
ccpa: ['US-CA'],
|
||||
lgpd: ['BR']
|
||||
};
|
||||
|
||||
if (!regulationMap[regulation]) return false;
|
||||
|
||||
if (regulation === 'ccpa' && country === 'US') {
|
||||
// CCPA only applies to California
|
||||
return true;
|
||||
}
|
||||
|
||||
return regulationMap[regulation].includes(country);
|
||||
};
|
||||
|
||||
// Sanitization helpers
|
||||
export const sanitizeInput = (input) => {
|
||||
if (typeof input !== 'string') return input;
|
||||
|
||||
// Remove potential XSS vectors
|
||||
return input
|
||||
.replace(/[<>]/g, '')
|
||||
.replace(/javascript:/gi, '')
|
||||
.replace(/on\w+=/gi, '')
|
||||
.trim();
|
||||
};
|
||||
|
||||
export const sanitizeObject = (obj) => {
|
||||
const sanitized = {};
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (typeof value === 'string') {
|
||||
sanitized[key] = sanitizeInput(value);
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
sanitized[key] = Array.isArray(value)
|
||||
? value.map(item => typeof item === 'string' ? sanitizeInput(item) : item)
|
||||
: sanitizeObject(value);
|
||||
} else {
|
||||
sanitized[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
};
|
||||
Reference in New Issue
Block a user