forked from geolba/tethys.backend
- update to AdonisJS 6
This commit is contained in:
parent
f828ca4491
commit
cb51a4136f
167 changed files with 21485 additions and 21212 deletions
|
@ -1,11 +1,10 @@
|
|||
import type { ApplicationContract } from '@ioc:Adonis/Core/Application';
|
||||
import type Hash from '@ioc:Adonis/Core/Hash';
|
||||
// import HttpContextContract from '@ioc:Adonis/Core/HttpContext';
|
||||
import type Response from '@ioc:Adonis/Core/Response';
|
||||
import { LaravelHash } from './HashDriver';
|
||||
// // import HttpContextContract from '@ioc:Adonis/Core/HttpContext';
|
||||
// import { LaravelHash } from './HashDriver/index.js';
|
||||
import { ApplicationService } from '@adonisjs/core/types';
|
||||
// import { validator } from '@adonisjs/validator';
|
||||
|
||||
export default class AppProvider {
|
||||
constructor(protected app: ApplicationContract) {}
|
||||
constructor(protected app: ApplicationService) {}
|
||||
|
||||
public register() {
|
||||
// Register your own bindings
|
||||
|
@ -13,26 +12,36 @@ export default class AppProvider {
|
|||
|
||||
public async boot() {
|
||||
// IoC container is ready
|
||||
const hashInstance: typeof Hash = this.app.container.use('Adonis/Core/Hash');
|
||||
hashInstance.extend('bcrypt', () => {
|
||||
return new LaravelHash();
|
||||
});
|
||||
|
||||
const responseInstance: typeof Response = this.app.container.use('Adonis/Core/Response');
|
||||
responseInstance.macro('flash', function (key: string, message: any) {
|
||||
this.ctx!.session.flash(key, message);
|
||||
return this;
|
||||
});
|
||||
responseInstance.macro('toRoute', function (route: string) {
|
||||
this.redirect().toRoute(route);
|
||||
return this;
|
||||
});
|
||||
// this.app.container.singleton('Adonis/Core/Response', () => {
|
||||
// return FlashResponse;
|
||||
await import('../src/extensions.js');
|
||||
// const hashInstance: typeof hash = this.app.container.make('@adonisjs/core/services/hash');
|
||||
// hashInstance.extend('bcrypt', () => {
|
||||
// return new LaravelHash();
|
||||
// });
|
||||
|
||||
// this.app.container.singleton('Adonis/Core/HttpContext', () => {
|
||||
// });
|
||||
|
||||
|
||||
// this.app.container.resolving('validator', (validator) => {
|
||||
// validator.rule('foo', () => {})
|
||||
|
||||
// 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,
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
public async ready() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { ApplicationContract } from '@ioc:Adonis/Core/Application'
|
||||
import { ApplicationService } from "@adonisjs/core/types";
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -20,7 +20,7 @@ import type { ApplicationContract } from '@ioc:Adonis/Core/Application'
|
|||
|
|
||||
*/
|
||||
export default class DoiProvider {
|
||||
constructor(protected app: ApplicationContract) {}
|
||||
constructor(protected app: ApplicationService) {}
|
||||
|
||||
public register() {
|
||||
// Register your own bindings
|
||||
|
|
65
providers/HashDriver/helpers.ts
Normal file
65
providers/HashDriver/helpers.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { type ScryptOptions } from 'node:crypto';
|
||||
// export declare const MAX_UINT32: number;
|
||||
// export declare const MAX_UINT24: number;
|
||||
|
||||
import { promisify } from 'node:util';
|
||||
import { randomBytes, scrypt } from 'node:crypto';
|
||||
import phc from '@phc/format';
|
||||
|
||||
/**
|
||||
* Validates a number to be within a given range.
|
||||
*/
|
||||
export class RangeValidator {
|
||||
// static validate(label: string, value: unknown, range: [number, number]): void;
|
||||
static validate(label: string, value: unknown, range: [number, number]): void {
|
||||
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
||||
throw new TypeError(`The "${label}" option must be an integer`);
|
||||
}
|
||||
const [min, max] = range;
|
||||
if (value < min || value > max) {
|
||||
throw new TypeError(`The "${label}" option must be in the range (${min} <= ${label} <= ${max})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validates a value to be one of the allowed values
|
||||
*/
|
||||
export class EnumValidator {
|
||||
// static validate(label: string, value: unknown, allowedValues: any[]): void;
|
||||
static validate(label: string, value: unknown, allowedValues: any[]): void {
|
||||
if (!allowedValues.includes(value)) {
|
||||
throw new TypeError(`The "${label}" option must be one of: ${allowedValues}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Async function to generate random bytes
|
||||
*/
|
||||
// export declare const randomBytesAsync: (arg1: number) => Promise<Buffer>;
|
||||
export const randomBytesAsync: (arg1: number) => Promise<Buffer> = promisify(randomBytes);
|
||||
/**
|
||||
* Async version of scrypt.
|
||||
*/
|
||||
// export declare const scryptAsync: (arg1: string, arg2: Buffer, arg3: number, arg4: ScryptOptions) => Promise<Buffer>;
|
||||
export const scryptAsync: (arg1: string, arg2: Buffer, arg3: number, arg4: ScryptOptions) => Promise<Buffer> = promisify(scrypt);
|
||||
|
||||
export class PhcFormatter {
|
||||
/**
|
||||
* Serialize salt and hash with predefined options.
|
||||
*/
|
||||
serialize(salt: Buffer, hash: Buffer, options: any): string {
|
||||
return phc.serialize({
|
||||
id: options.id,
|
||||
version: options.version,
|
||||
params: options.params,
|
||||
salt,
|
||||
hash,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Deserialize a PHC string to an object
|
||||
*/
|
||||
deserialize(phcString: string): DeserializeResult {
|
||||
return phc.deserialize(phcString);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,34 @@
|
|||
import { HashDriverContract } from '@ioc:Adonis/Core/Hash';
|
||||
// const bcrypt = require("bcrypt");
|
||||
import bcrypt from 'bcryptjs';
|
||||
// import bcrypt from 'bcrypt/bcrypt.js'
|
||||
import { HashDriverContract, ManagerDriverFactory } from '@adonisjs/core/types/hash';
|
||||
import { EnumValidator, RangeValidator, PhcFormatter } from './helpers.js';
|
||||
|
||||
/**
|
||||
* Config accepted by the hash driver
|
||||
*/
|
||||
export type PbkdfConfig = {
|
||||
rounds: number;
|
||||
saltSize?: number;
|
||||
version?: number;
|
||||
}
|
||||
|
||||
|
||||
const saltRounds = 10;
|
||||
export class LaravelHash implements HashDriverContract {
|
||||
export class LaravelDriver implements HashDriverContract {
|
||||
private config: PbkdfConfig;
|
||||
// private binding;
|
||||
private phcFormatter = new PhcFormatter();
|
||||
|
||||
constructor(config: {}) {
|
||||
this.config = {
|
||||
rounds: 10,
|
||||
saltSize: 16,
|
||||
version: 98,
|
||||
...config
|
||||
};
|
||||
}
|
||||
|
||||
public async make(value: string) {
|
||||
const hashedValue = bcrypt.hashSync(value, saltRounds);
|
||||
return hashedValue;
|
||||
|
@ -18,4 +43,101 @@ export class LaravelHash implements HashDriverContract {
|
|||
}
|
||||
return await bcrypt.compareSync(plainValue, newHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value is a valid hash. This method just checks
|
||||
* for the formatting of the hash.
|
||||
*
|
||||
* ```ts
|
||||
* bcrypt.isValidHash('hello world') // false
|
||||
* bcrypt.isValidHash('$bcrypt$v=98$r=10$Jtxi46WJ26OQ0khsYLLlnw$knXGfuRFsSjXdj88JydPOnUIglvm1S8')
|
||||
* ```
|
||||
*/
|
||||
public isValidHash(value: string) {
|
||||
try {
|
||||
this.validatePhcString(value);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public needsReHash(value: string): boolean {
|
||||
if (value.startsWith('$2b') || value.startsWith('$2a')) {
|
||||
return true;
|
||||
}
|
||||
const phcNode = this.phcFormatter.deserialize(value);
|
||||
if (phcNode.id !== 'bcrypt') {
|
||||
return true;
|
||||
}
|
||||
if (phcNode.version !== this.config.version) {
|
||||
return true;
|
||||
}
|
||||
if (!phcNode.params) {
|
||||
return true;
|
||||
}
|
||||
if (phcNode.params.r !== this.config.rounds) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate phc hash string
|
||||
*/
|
||||
private validatePhcString(phcString: string) {
|
||||
const phcNode = this.phcFormatter.deserialize(phcString);
|
||||
if (!phcNode.version) {
|
||||
phcNode.version = 97;
|
||||
}
|
||||
if (phcNode.id !== 'bcrypt') {
|
||||
throw new TypeError(`Invalid "id" found in the phc string`);
|
||||
}
|
||||
if (!phcNode.params) {
|
||||
throw new TypeError(`No "params" found in the phc string`);
|
||||
}
|
||||
if (!phcNode.salt) {
|
||||
throw new TypeError(`No "salt" found in the phc string`);
|
||||
}
|
||||
if (!phcNode.hash) {
|
||||
throw new TypeError(`No "hash" found in the phc string`);
|
||||
}
|
||||
if (!phcNode.hash.byteLength) {
|
||||
throw new TypeError(`No "hash" found in the phc string`);
|
||||
}
|
||||
RangeValidator.validate('salt.byteLength', phcNode.salt.byteLength, [8, 1024]);
|
||||
EnumValidator.validate('version', phcNode.version, [97, 98]);
|
||||
RangeValidator.validate('r', phcNode.params.r, [4, 31]);
|
||||
return {
|
||||
id: phcNode.id,
|
||||
version: phcNode.version,
|
||||
hash: phcNode.hash,
|
||||
salt: phcNode.salt,
|
||||
params: {
|
||||
r: phcNode.params.r,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically importing underlying binding
|
||||
*/
|
||||
// private async importBinding() {
|
||||
// if (this.binding) {
|
||||
// return this.binding;
|
||||
// }
|
||||
// this.binding = await import('bcrypt');
|
||||
// return this.binding;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory function to reference the driver
|
||||
* inside the config file.
|
||||
*/
|
||||
export function laravelDriver (config: PbkdfConfig): ManagerDriverFactory {
|
||||
return () => {
|
||||
return new LaravelDriver(config)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import type { ApplicationContract } from '@ioc:Adonis/Core/Application';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Provider
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Your application is not ready when this file is loaded by the framework.
|
||||
| Hence, the top level imports relying on the IoC container will not work.
|
||||
| You must import them inside the life-cycle methods defined inside
|
||||
| the provider class.
|
||||
|
|
||||
| @example:
|
||||
|
|
||||
| public async ready () {
|
||||
| const Database = this.app.container.resolveBinding('Adonis/Lucid/Database')
|
||||
| const Event = this.app.container.resolveBinding('Adonis/Core/Event')
|
||||
| Event.on('db:query', Database.prettyPrint)
|
||||
| }
|
||||
|
|
||||
*/
|
||||
export default class QueryBuilderProvider {
|
||||
constructor(protected app: ApplicationContract) {}
|
||||
|
||||
public register() {
|
||||
// Register your own bindings
|
||||
}
|
||||
|
||||
public async boot() {
|
||||
// All bindings are ready, feel free to use them
|
||||
const { ModelQueryBuilder } = this.app.container.resolveBinding('Adonis/Lucid/Database');
|
||||
|
||||
ModelQueryBuilder.macro('pluck', async function (valueColumn: string, id?: string) {
|
||||
let rolesPluck = {};
|
||||
(await this).forEach((user, index) => {
|
||||
let idc;
|
||||
if (!id) {
|
||||
idc = index;
|
||||
} else {
|
||||
idc = user[id];
|
||||
}
|
||||
const value = user[valueColumn];
|
||||
// rolesPluck[idc] = user.name;
|
||||
rolesPluck[idc] = value;
|
||||
});
|
||||
return rolesPluck;
|
||||
});
|
||||
}
|
||||
|
||||
public async ready() {
|
||||
// App is ready
|
||||
}
|
||||
|
||||
public async shutdown() {
|
||||
// Cleanup, since app is going down
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
import type { ApplicationContract } from '@ioc:Adonis/Core/Application';
|
||||
// import TokenWorkerService from "#app/Library/Oai/TokenWorkerSerice";
|
||||
import type { ApplicationService } from "@adonisjs/core/types";
|
||||
import TokenWorkerContract from "#library/Oai/TokenWorkerContract";
|
||||
// import TokenWorkerService from "#library/Oai/TokenWorkerSerice";
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -19,26 +22,50 @@ import type { ApplicationContract } from '@ioc:Adonis/Core/Application';
|
|||
| }
|
||||
|
|
||||
*/
|
||||
|
||||
// https://github.com/adonisjs/core/discussions/4268
|
||||
export default class TokenWorkerProvider {
|
||||
public static needsApplication = true;
|
||||
private tokenWorkerInstance; //: TokenWorkerService | null = null;
|
||||
private tokenWorkerInstance: TokenWorkerContract | null = null;
|
||||
|
||||
constructor(protected app: ApplicationContract) {}
|
||||
constructor(protected app: ApplicationService) {}
|
||||
|
||||
public register() {
|
||||
public async register() {
|
||||
await this.setupDependancyInjectionBindings()
|
||||
}
|
||||
|
||||
public async setupDependancyInjectionBindings() {
|
||||
// const TokenWorkerService = await import('#app/Library/Oai/TokenWorkerSerice');
|
||||
const { default: TokenWorkerService } = await import('#library/Oai/TokenWorkerSerice');
|
||||
// Register your own bindings
|
||||
// Bind TokenWorker to the IoC container
|
||||
this.app.container.singleton('App/Library/Oai/TokenWorkerContract', () => {
|
||||
this.app.container.singleton(TokenWorkerContract, () => {
|
||||
// 1. import the oai configuration
|
||||
const ttl: number = 86400;
|
||||
|
||||
// 2. import our REDIS wrapper class
|
||||
const TokenWorkerService = require('App/Library/Oai/TokenWorkerSerice').default;
|
||||
// const TokenWorkerService = require('#library/Oai/TokenWorkerSerice').default;
|
||||
this.tokenWorkerInstance = new TokenWorkerService(ttl);
|
||||
|
||||
// 3. return a new instance
|
||||
return this.tokenWorkerInstance;
|
||||
});
|
||||
|
||||
// this.app.container.singleton(
|
||||
// '#app/Library/Oai/TokenWorkerContract',
|
||||
// () => {
|
||||
|
||||
// // 1. import the oai configuration
|
||||
// const ttl: number = 86400;
|
||||
|
||||
// // 2. import our REDIS wrapper class
|
||||
// // const TokenWorkerService = require('#app/Library/Oai/TokenWorkerSerice').default;
|
||||
// this.tokenWorkerInstance = new TokenWorkerService(ttl);
|
||||
|
||||
// // 3. return a new instance
|
||||
// return this.tokenWorkerInstance;
|
||||
// }
|
||||
// )
|
||||
}
|
||||
|
||||
// public async boot() {
|
||||
|
@ -51,7 +78,7 @@ export default class TokenWorkerProvider {
|
|||
// }
|
||||
|
||||
public async shutdown() {
|
||||
console.log('TokenServerProvider shutdown()');
|
||||
// console.log('TokenServerProvider shutdown()');
|
||||
// Cleanup, since app is going down
|
||||
if (this.tokenWorkerInstance) {
|
||||
// Call the disconnect method when the application is shutting down
|
||||
|
|
53
providers/inertia_provider.ts
Normal file
53
providers/inertia_provider.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import type { ApplicationService } from '@adonisjs/core/types';
|
||||
import { configProvider } from '@adonisjs/core';
|
||||
import { RuntimeException } from '@poppinss/utils';
|
||||
import InertiaMiddleware from '@adonisjs/inertia/inertia_middleware';
|
||||
import { ResolvedConfig } from '@adonisjs/inertia/types';
|
||||
|
||||
export default class InertiaProvider {
|
||||
constructor(protected app: ApplicationService) {}
|
||||
|
||||
/**
|
||||
* Register bindings to the container
|
||||
*/
|
||||
async register() {
|
||||
this.app.container.singleton(InertiaMiddleware, async () => {
|
||||
const inertiaConfigProvider = this.app.config.get('inertia');
|
||||
const config: ResolvedConfig | null = await configProvider.resolve(this.app, inertiaConfigProvider);
|
||||
// const vite = await this.app.container.make("vite");
|
||||
if (!config) {
|
||||
throw new RuntimeException('Invalid "config/inertia.ts" file. Make sure you are using the "defineConfig" method');
|
||||
}
|
||||
// return new InertiaMiddleware(config, vite);
|
||||
return new InertiaMiddleware(config);
|
||||
});
|
||||
await this.registerEdgePlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* The container bindings have booted
|
||||
*/
|
||||
async boot() {}
|
||||
|
||||
/**
|
||||
* The application has been booted
|
||||
*/
|
||||
async start() {}
|
||||
|
||||
/**
|
||||
* The process has been started
|
||||
*/
|
||||
async ready() {}
|
||||
|
||||
/**
|
||||
* Preparing to shutdown the app
|
||||
*/
|
||||
async shutdown() {}
|
||||
|
||||
protected async registerEdgePlugin(): Promise<void> {
|
||||
if (!this.app.usingEdgeJS) return;
|
||||
const edgeExports = await import('edge.js');
|
||||
const { edgePluginInertia } = await import('@adonisjs/inertia/plugins/edge');
|
||||
edgeExports.default.use(edgePluginInertia());
|
||||
}
|
||||
}
|
95
providers/query_builder_provider.ts
Normal file
95
providers/query_builder_provider.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { ApplicationService } from '@adonisjs/core/types';
|
||||
// import { Database, DatabaseQueryBuilder } from '@adonisjs/lucid/database';
|
||||
import { ModelQueryBuilder } from '@adonisjs/lucid/orm';
|
||||
// import db from '@adonisjs/lucid/services/db';
|
||||
import { LucidModel, LucidRow } from '@adonisjs/lucid/types/model';
|
||||
import { ChainableContract, ExcutableQueryBuilderContract } from '@adonisjs/lucid/types/querybuilder';
|
||||
|
||||
import { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model';
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Provider
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Your application is not ready when this file is loaded by the framework.
|
||||
| Hence, the top level imports relying on the IoC container will not work.
|
||||
| You must import them inside the life-cycle methods defined inside
|
||||
| the provider class.
|
||||
|
|
||||
| @example:
|
||||
|
|
||||
| public async ready () {
|
||||
| const Database = this.app.container.resolveBinding('Adonis/Lucid/Database')
|
||||
| const Event = this.app.container.resolveBinding('Adonis/Core/Event')
|
||||
| Event.on('db:query', Database.prettyPrint)
|
||||
| }
|
||||
|
|
||||
*/
|
||||
declare module '@adonisjs/lucid/types/model' {
|
||||
// interface ModelQueryBuilderContract<Model extends LucidModel, Result = InstanceType<Model>> {
|
||||
export interface ModelQueryBuilderContract<Model extends LucidModel, Result = InstanceType<Model>> extends ChainableContract, ExcutableQueryBuilderContract<Result[]> {
|
||||
// macro typescript definitions here
|
||||
// whereTrue(columnName: string): this;
|
||||
// whereFalse(columnName: string): this;
|
||||
// any(): Promise<boolean>;
|
||||
// selectCount(): Promise<BigInt>;
|
||||
// selectIds(primaryKey?: string): Promise<number[]>;
|
||||
// selectId(primaryKey?: string): Promise<number | undefined>;
|
||||
// selectIdOrFail(primaryKey?: string): Promise<number>;
|
||||
// pluck(valueColumn: string, id?: string): Promise<{ [key: string]: any }>;
|
||||
pluck<T extends LucidModel>(valueColumn: string, id?: string): Promise<{ [key: string]: any }>;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
declare module '@adonisjs/lucid/orm' {
|
||||
|
||||
class ModelQueryBuilder extends Chainable implements ModelQueryBuilderContract<LucidModel, LucidRow> {
|
||||
public pluck(valueColumn: string, id?: string): Promise<{ [key: string]: any }>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default class QueryBuilderProvider {
|
||||
constructor(protected app: ApplicationService) {}
|
||||
|
||||
public register() {
|
||||
// Register your own bindings
|
||||
}
|
||||
|
||||
public async boot() {
|
||||
// All bindings are ready, feel free to use them
|
||||
// const db = await this.app.container.make('lucid.db');
|
||||
|
||||
ModelQueryBuilder.macro('pluck', async function (this: ModelQueryBuilder, valueColumn: string, id?: string) {
|
||||
// let rolesPluck = {};
|
||||
let rolesPluck: { [key: number]: any } = {};
|
||||
const result = await this.exec();
|
||||
result.forEach((user, index) => {
|
||||
let idc;
|
||||
if (!id) {
|
||||
idc = index;
|
||||
} else {
|
||||
idc = user[id];
|
||||
}
|
||||
const value = user[valueColumn];
|
||||
// rolesPluck[idc] = user.name;
|
||||
rolesPluck[idc] = value;
|
||||
});
|
||||
return rolesPluck;
|
||||
});
|
||||
|
||||
// });
|
||||
|
||||
// validator.rule('foo', () => {})
|
||||
}
|
||||
|
||||
public async ready() {
|
||||
// App is ready
|
||||
}
|
||||
|
||||
public async shutdown() {
|
||||
// Cleanup, since app is going down
|
||||
}
|
||||
}
|
112
providers/stardust_provider.ts
Normal file
112
providers/stardust_provider.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
import type { ApplicationService } from '@adonisjs/core/types';
|
||||
import { TagContract } from 'edge.js/types';
|
||||
import router from '@adonisjs/core/services/router';
|
||||
import type { Edge } from 'edge.js';
|
||||
import type { HttpRouterService } from '@adonisjs/core/types';
|
||||
import StardustMiddleware from '#middleware/stardust_middleware';
|
||||
|
||||
export default class StardustProvider {
|
||||
public static needsApplication: boolean = true;
|
||||
protected app: ApplicationService;
|
||||
|
||||
constructor(app: ApplicationService) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
// https://edgejs.dev/docs/creating-custom-tags
|
||||
private stardustTag: TagContract = {
|
||||
block: false,
|
||||
seekable: true,
|
||||
tagName: 'routes',
|
||||
compile(_, buffer, token) {
|
||||
// buffer.outputVariableName = 'out';
|
||||
// buffer.outputRaw('Hello from router tag');
|
||||
|
||||
// const expression = parser.utils.transformAst(
|
||||
// parser.utils.generateAST(token.properties.jsArg, token.loc, token.filename),
|
||||
// token.filename,
|
||||
// parser,
|
||||
// );
|
||||
// const outputExpression = `${parser.utils.stringify(expression)}.split("").reverse().join("")`;
|
||||
// ''test'.split("").reverse().join("")'
|
||||
|
||||
// console.log(JSON.stringify(expression, null, 2));
|
||||
buffer.writeExpression(`\n out += state.routes(state.cspNonce)`, token.filename, token.loc.start.line);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the `@routes()` tag
|
||||
*/
|
||||
private async registerStardustTag(edge: Edge) {
|
||||
if (!this.app.usingEdgeJS) return;
|
||||
// const { edgePluginInertia } = await import('../src/plugins/edge/plugin.js');
|
||||
// edgeExports.default.use(edgePluginInertia());
|
||||
edge.registerTag(this.stardustTag);
|
||||
// edgeExports.registerTag(this.inertiaTag).
|
||||
}
|
||||
|
||||
private registerRoutesGlobal(edge: Edge, namedRoutes: Record<string, string>) {
|
||||
// Registering a global function
|
||||
edge.global('routes', (cspNonce: string | undefined) => {
|
||||
return `
|
||||
<script${cspNonce ? ` nonce="${cspNonce}"` : ''}>
|
||||
(globalThis || window).stardust = { namedRoutes: ${JSON.stringify(namedRoutes)} };
|
||||
</script>
|
||||
`;
|
||||
});
|
||||
// edge.global('reverse', 'arno');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of named routes
|
||||
*/
|
||||
private getNamedRoutes(router: HttpRouterService) {
|
||||
/**
|
||||
* Only sharing the main domain routes. Subdomains are
|
||||
* ignored for now. Let's see if many people need it
|
||||
*/
|
||||
|
||||
const mainDomainRoutes = router.toJSON()?.['root'] ?? [];
|
||||
return mainDomainRoutes.reduce((routes: any, route) => {
|
||||
if (route.name) {
|
||||
routes[route.name] = route.pattern;
|
||||
} else if (typeof route.handler === 'string') {
|
||||
routes[route.handler] = route.pattern;
|
||||
}
|
||||
return routes;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// private registerRoutesGlobal;
|
||||
/**
|
||||
* Registers named routes on the global scope in order to seamlessly support
|
||||
* stardust's functionality on the server
|
||||
* @param namedRoutes
|
||||
*/
|
||||
// private registerSsrRoutes(namedRoutes) {
|
||||
// globalThis.stardust = { namedRoutes };
|
||||
// }
|
||||
|
||||
public async ready(): Promise<void> {
|
||||
// this.app.container.bind('EidelLev/Stardust/Middleware', () => Stardust_1.default);
|
||||
this.app.container.bind(StardustMiddleware, () => {
|
||||
// return new InertiaMiddleware(config, vite);
|
||||
return new StardustMiddleware();
|
||||
});
|
||||
// this.app.container.(['edge.js', 'Adonis/Core/Route'], (View, Route) => {
|
||||
// const namedRoutes = this.getNamedRoutes(Route);
|
||||
// // this.registerRoutesGlobal(View, namedRoutes);
|
||||
// this.registerStardustTag(View);
|
||||
// // this.registerSsrRoutes(namedRoutes);
|
||||
// });
|
||||
const { default: edge } = await import('edge.js');
|
||||
// const router = await this.app.container.make('router');
|
||||
// this.app.container.resolving('router', async (router) => {
|
||||
// const routerService = await resolver.make('router')
|
||||
const namedRoutes = this.getNamedRoutes(router);
|
||||
this.registerRoutesGlobal(edge, namedRoutes);
|
||||
await this.registerStardustTag(edge);
|
||||
// });
|
||||
}
|
||||
}
|
248
providers/validator_provider.ts
Normal file
248
providers/validator_provider.ts
Normal file
|
@ -0,0 +1,248 @@
|
|||
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<boolean> {
|
||||
// // 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() {}
|
||||
}
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue