/* |-------------------------------------------------------------------------- | Provider File - node ace make:provider vinejsProvider |-------------------------------------------------------------------------- |*/ import type { ApplicationService } from '@adonisjs/core/types'; import vine, { BaseLiteralType, Vine } from '@vinejs/vine'; import type { Validation, FieldContext, FieldOptions } from '@vinejs/vine/types'; // import type { MultipartFile, FileValidationOptions } from '@adonisjs/bodyparser/types'; import type { MultipartFile } from '@adonisjs/core/bodyparser'; import type { FileValidationOptions } from '@adonisjs/core/types/bodyparser'; import { Request, RequestValidator } from '@adonisjs/core/http'; import MimeType from '#models/mime_type'; /** * Validation options accepted by the "file" rule */ export type FileRuleValidationOptions = Partial | ((field: FieldContext) => Partial); /** * Extend VineJS */ declare module '@vinejs/vine' { interface Vine { myfile(options?: FileRuleValidationOptions): VineMultipartFile; } } /** * Extend HTTP request class */ declare module '@adonisjs/core/http' { interface Request extends RequestValidator { } } /** * Checks if the value is an instance of multipart file * from bodyparser. */ export function isBodyParserFile(file: MultipartFile | unknown): boolean { return !!(file && typeof file === 'object' && 'isMultipartFile' in file); } export async function getEnabledExtensions() { const enabledExtensions = await MimeType.query().select('file_extension').where('enabled', true).exec(); const extensions = enabledExtensions .map((extension) => { return extension.file_extension.split('|'); }) .flat(); return extensions; }; /** * VineJS validation rule that validates the file to be an * instance of BodyParser MultipartFile class. */ const isMultipartFile = vine.createRule(async (file: MultipartFile | unknown, options: FileRuleValidationOptions, field: FieldContext) => { /** * Report error when value is not a field multipart * file object */ if (!isBodyParserFile(file)) { field.report('The {{ field }} must be a file', 'file', field); return; } // At this point, you can use type assertion to explicitly tell TypeScript that file is of type MultipartFile const validatedFile = file as MultipartFile; const validationOptions = typeof options === 'function' ? options(field) : options; /** * Set size when it's defined in the options and missing * on the file instance */ if (validatedFile.sizeLimit === undefined && validationOptions.size) { validatedFile.sizeLimit = validationOptions.size; } /** * Set extensions when it's defined in the options and missing * on the file instance */ // if (validatedFile.allowedExtensions === undefined && validationOptions.extnames) { // validatedFile.allowedExtensions = validationOptions.extnames; // } if (validatedFile.allowedExtensions === undefined && validationOptions.extnames) { validatedFile.allowedExtensions = await getEnabledExtensions(); } /** * wieder löschen * Set extensions when it's defined in the options and missing * on the file instance */ // if (file.clientNameSizeLimit === undefined && validationOptions.clientNameSizeLimit) { // file.clientNameSizeLimit = validationOptions.clientNameSizeLimit; // } /** * Validate file */ validatedFile.validate(); /** * Report errors */ validatedFile.errors.forEach((error) => { field.report(error.message, `file.${error.type}`, field, validationOptions); }); }); export class VineMultipartFile extends BaseLiteralType { // #private; // constructor(validationOptions?: FileRuleValidationOptions, options?: FieldOptions, validations?: Validation[]); // clone(): this; public validationOptions; // extnames: (18) ['gpkg', 'htm', 'html', 'csv', 'txt', 'asc', 'c', 'cc', 'h', 'srt', 'tiff', 'pdf', 'png', 'zip', 'jpg', 'jpeg', 'jpe', 'xlsx'] // size: '512mb' public constructor(validationOptions?: FileRuleValidationOptions, options?: FieldOptions, validations?: Validation[]) { // super(options, validations); super(options, [isMultipartFile(validationOptions || {})]); this.validationOptions = validationOptions; } public clone(): any { return new VineMultipartFile(this.validationOptions, this.cloneOptions(), this.cloneValidations()); } } export default class VinejsProvider { protected app: ApplicationService; constructor(app: ApplicationService) { this.app = app; this.app.usingVineJS = true; } /** * Register bindings to the container */ register() {} /** * The container bindings have booted */ boot(): void { // VineString.macro('translatedLanguage', function (this: VineString, options: Options) { // return this.use(translatedLanguageRule(options)); // }); Vine.macro('myfile', function (this: Vine, options) { return new VineMultipartFile(options); }); /** * The validate method can be used to validate the request * data for the current request using VineJS validators */ Request.macro('validateUsing', function (...args) { return new RequestValidator(this.ctx).validateUsing(...args); }); } /** * The application has been booted */ async start() {} /** * The process has been started */ async ready() {} /** * Preparing to shutdown the app */ async shutdown() {} }