Add adult verification and erotic moderation features: Implement new routes and controller methods for managing adult verification requests, status updates, and document retrieval. Introduce erotic moderation actions and reports, enhancing administrative capabilities. Update chat and navigation controllers to support adult content filtering and access control. Enhance user parameter handling for adult verification status and requests, improving overall user experience and compliance.

This commit is contained in:
Torsten Schulz (local)
2026-03-27 09:14:54 +01:00
parent f93687c753
commit 3e6c09ab29
73 changed files with 4459 additions and 197 deletions

View File

@@ -12,6 +12,13 @@ import UserParamVisibilityType from '../models/type/user_param_visibility.js';
import UserParamVisibility from '../models/community/user_param_visibility.js';
import { encrypt } from '../utils/encryption.js';
import { sequelize } from '../utils/sequelize.js';
import fsPromises from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import { v4 as uuidv4 } from 'uuid';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/** Wie UserParam.value-Setter: bei Verschlüsselungsfehler leeren String speichern, nicht crashen. */
function encryptUserParamValue(plain) {
@@ -24,6 +31,90 @@ function encryptUserParamValue(plain) {
}
class SettingsService extends BaseService{
parseAdultVerificationRequest(value) {
if (!value) return null;
try {
return JSON.parse(value);
} catch {
return null;
}
}
normalizeAdultVerificationStatus(value) {
if (['pending', 'approved', 'rejected'].includes(value)) {
return value;
}
return 'none';
}
async getAdultAccessStateByUserId(userId) {
const userParams = await this.getUserParams(userId, ['birthdate', 'adult_verification_status', 'adult_verification_request']);
let birthdate = null;
let adultVerificationStatus = 'none';
let adultVerificationRequest = null;
for (const param of userParams) {
if (param.paramType.description === 'birthdate') {
birthdate = param.value;
}
if (param.paramType.description === 'adult_verification_status') {
adultVerificationStatus = this.normalizeAdultVerificationStatus(param.value);
}
if (param.paramType.description === 'adult_verification_request') {
adultVerificationRequest = this.parseAdultVerificationRequest(param.value);
}
}
const age = birthdate ? this.calculateAge(birthdate) : null;
const isAdult = age !== null && age >= 18;
return {
age,
isAdult,
adultVerificationStatus: isAdult ? adultVerificationStatus : 'none',
adultVerificationRequest: isAdult ? adultVerificationRequest : null,
adultAccessEnabled: isAdult && adultVerificationStatus === 'approved'
};
}
buildAdultVerificationFilePath(fileName) {
return path.join(__dirname, '..', 'images', 'adult-verification', fileName);
}
async saveAdultVerificationDocument(file) {
const allowedMimeTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
if (!file || !file.buffer) {
throw new Error('No verification document provided');
}
if (!allowedMimeTypes.includes(file.mimetype)) {
throw new Error('Unsupported verification document type');
}
const ext = path.extname(file.originalname || '').toLowerCase();
const safeExt = ext && ext.length <= 8 ? ext : (file.mimetype === 'application/pdf' ? '.pdf' : '.bin');
const fileName = `${uuidv4()}${safeExt}`;
const filePath = this.buildAdultVerificationFilePath(fileName);
await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
await fsPromises.writeFile(filePath, file.buffer);
return { fileName, filePath };
}
async upsertUserParam(userId, description, value) {
const paramType = await UserParamType.findOne({ where: { description } });
if (!paramType) {
throw new Error(`Missing user param type: ${description}`);
}
const existingParam = await UserParam.findOne({
where: { userId, paramTypeId: paramType.id }
});
if (existingParam) {
await existingParam.update({ value });
return existingParam;
}
return UserParam.create({
userId,
paramTypeId: paramType.id,
value
});
}
async getUserParams(userId, paramDescriptions) {
return await UserParam.findAll({
where: { userId },
@@ -299,10 +390,13 @@ class SettingsService extends BaseService{
email = null;
}
const adultAccess = await this.getAdultAccessStateByUserId(user.id);
return {
username: user.username,
email: email,
showinsearch: user.searchable
showinsearch: user.searchable,
...adultAccess
};
} catch (error) {
console.error('Error getting account settings:', error);
@@ -317,6 +411,8 @@ class SettingsService extends BaseService{
throw new Error('User not found');
}
const adultAccess = await this.getAdultAccessStateByUserId(user.id);
// Update username if provided
if (settings.username !== undefined) {
await user.update({ username: settings.username });
@@ -332,6 +428,17 @@ class SettingsService extends BaseService{
await user.update({ searchable: settings.showinsearch });
}
if (settings.requestAdultVerification) {
if (!adultAccess.isAdult) {
throw new Error('Adult verification can only be requested by adult users');
}
const normalizedValue = adultAccess.adultVerificationStatus === 'approved'
? 'approved'
: 'pending';
await this.upsertUserParam(user.id, 'adult_verification_status', normalizedValue);
}
// Update password if provided and not empty
if (settings.newpassword && settings.newpassword.trim() !== '') {
if (!settings.oldpassword || settings.oldpassword.trim() === '') {
@@ -357,6 +464,34 @@ class SettingsService extends BaseService{
}
}
async submitAdultVerificationRequest(hashedUserId, { note }, file) {
const user = await this.getUserByHashedId(hashedUserId);
if (!user) {
throw new Error('User not found');
}
const adultAccess = await this.getAdultAccessStateByUserId(user.id);
if (!adultAccess.isAdult) {
throw new Error('Adult verification can only be requested by adult users');
}
if (!file) {
throw new Error('No verification document provided');
}
const savedFile = await this.saveAdultVerificationDocument(file);
const requestPayload = {
fileName: savedFile.fileName,
originalName: file.originalname,
mimeType: file.mimetype,
note: note || '',
submittedAt: new Date().toISOString()
};
await this.upsertUserParam(user.id, 'adult_verification_request', JSON.stringify(requestPayload));
await this.upsertUserParam(user.id, 'adult_verification_status', adultAccess.adultVerificationStatus === 'approved' ? 'approved' : 'pending');
return requestPayload;
}
async getVisibilities() {
return UserParamVisibilityType.findAll();
}