import type { ApplicationService } from '@adonisjs/core/types'; import { validator } from '@adonisjs/validator'; import { Request } from '@adonisjs/core/http'; import { RequestNegotiator } from '@adonisjs/validator/types'; // import { Rule } from '@adonisjs/validator/types'; // import { VanillaErrorReporter } from '@adonisjs/validator/build/src/error_reporter/index.js'; import db from '@adonisjs/lucid/services/db'; type Options = { table: string; column: string; whereNot?: { id: any }; }; declare module '@adonisjs/validator/types' { export interface Rules { translatedLanguage(mainLanguageField: string, typeField: string): Rule; uniqueArray(field: string): Rule; unique(args: Options): Rule; } } export default class ValidatorProvider { constructor(protected app: ApplicationService) {} /** * Register bindings to the container */ register() {} /** * The container bindings have booted */ async boot() { // Add custom validation rules // this.app.container.resolving('validator', (validator) => { // validator.rule('foo', () => {}) // }) // validator.configure({ // reporter: validator.reporters.vanilla, // }); await this.configureValidator(); validator.rule('uniqueArray', (dataArray, [field], { pointer, arrayExpressionPointer, errorReporter }) => { const array = dataArray; //validator.helpers.getFieldValue(data, field, tip); if (!Array.isArray(array)) { throw new Error(`The ${pointer} must be an array.`); } const uniqueValues = new Set(); for (let i = 0; i < array.length; i++) { const item = array[i]; const attributeValue = item[field]; // Extract the attribute value for uniqueness check if (uniqueValues.has(attributeValue)) { // throw new Error(`The ${field} array contains duplicate values for the ${field} attribute.`) errorReporter.report( pointer, 'uniqueArray', // Keep an eye on this `The ${pointer} array contains duplicate values for the ${field} attribute.`, arrayExpressionPointer, { field, array: pointer }, ); return; } uniqueValues.add(attributeValue); } }); validator.rule( 'unique', async (data, [{ table, column, whereNot }], { pointer, errorReporter, arrayExpressionPointer, field }) => { const value = data; // get(data, column);'admin' if (!value) { return; } let ignoreId: string | null = whereNot?.id; // let ignoreId: string | null = null; // const fields = args[1].split('/'); // const table = args[0]; // if (args[2]) { // ignoreId = args[2]; // } const columnName = column || field; const builder = db.from(table).select(columnName).where(columnName, '=', value); if (ignoreId) { builder.whereNot('id', '=', ignoreId); } const row = await builder.first(); if (row) { // throw message; errorReporter.report(pointer, 'unique', 'Unique validation failed', arrayExpressionPointer, { field }); } }, () => ({ async: true, }), ); validator.rule( 'translatedLanguage', (value, [mainLanguageField, typeField], { root, tip, pointer, arrayExpressionPointer, errorReporter }) => { if (typeof value !== 'string') { return; } // const fieldValue = validator. getValue(data, field) // this should return the "category_id" value present in "root", but i got undefined const mainLanguage = validator.helpers.getFieldValue(mainLanguageField, root, tip); const type = validator.helpers.getFieldValue(typeField, root, tip); //'type' = 'Translated' if (type && type === 'Translated') { if (value === mainLanguage) { errorReporter.report( pointer, 'translatedLanguage', // Keep an eye on this 'translatedLanguage validation failed', arrayExpressionPointer, { mainLanguage }, ); } } // if (value !== string.camelCase(value)) { // options.errorReporter.report( // options.pointer, // 'camelCase', // 'camelCase validation failed', // options.arrayExpressionPointer // ); // } }, ); validator.rule('fileExtension', async (value, [extensions], { pointer, arrayExpressionPointer, errorReporter }) => { const allowedExtensions = extensions.map((ext: string) => ext.toLowerCase()); const uploadedFile = value; if (!uploadedFile) { return; } const extension = uploadedFile.extname.toLowerCase().replace('.', ''); if (!allowedExtensions.includes(extension)) { errorReporter.report( pointer, 'fileExtension', 'Invalid file extension. Only {{ extensions }} files are allowed.', arrayExpressionPointer, ); } }); // validator.rule( // 'clamavScan', // (value, [field], { root, tip, pointer, arrayExpressionPointer, errorReporter }) => { // if (typeof value !== 'object') { // return; // } // const uploadedFile = validator.helpers.getFieldValue(field, root, tip); // // return rules.file({}, [ // // async (file) => { // // const clamdhost = process.env['CLAMD_HOST'] ?? '127.0.0.1'; // // const clamdport = Number(process.env['CLAMD_PORT']) ?? '3310'; // // try { // // var isInfected = await scanFileForViruses(file.tmpPath, clamdhost, clamdport); // // } catch (error) { // // throw new Error(`${pointer}: ${error.message}`); // // } // // }, // // ]); // }); // async function scanFileForViruses(filePath, host, port): Promise { // // const clamscan = await (new ClamScan().init()); // const opts: ClamScan.Options = { // preference: 'clamdscan', // clamdscan: { // active: true, // host, // port, // multiscan: true, // }, // }; // const clamscan = await new ClamScan().init(opts); // return new Promise((resolve, reject) => { // clamscan.isInfected(filePath, (err, file, isInfected: boolean) => { // if (err) { // reject(err); // } else if (isInfected) { // reject(new Error(`File ${file} is infected!`)); // } else { // resolve(isInfected); // } // }); // }); // } } /** * Configure the validator */ async configureValidator() { // const config = await this.app.container.make('config'); validator.configure({ reporter: validator.reporters.vanilla, // @ts-ignore negotiator: (callback: RequestNegotiator) => { this.getRequestReporter = callback; }, }); } getRequestReporter(request: Request) { // if (request.ajax()) { // return ErrorReporters.ApiErrorReporter; // } switch (request.accepts(['html', 'application/vnd.api+json', 'json'])) { case 'html': case null: return validator.reporters.vanilla; case 'json': return validator.reporters.api; case 'application/vnd.api+json': return validator.reporters.jsonapi; } } /** * The application has been booted */ async start() {} /** * The process has been started */ async ready() {} /** * Preparing to shutdown the app */ async shutdown() {} }