feat: Update .gitignore and refine TypeScript configuration; clean up commented code and enhance dataset validation; npm updates
Some checks failed
CI / container-job (push) Failing after 35s

- Updated .gitignore to include new patterns
- Refined TypeScript configuration for better performance and readability
- Cleaned up commented code in several files
- Enhanced dataset validation logic
- Updated npm dependencies to the latest versions
This commit is contained in:
Kaimbacher 2025-01-29 11:26:21 +01:00
parent a5e0a36327
commit 8d47a58d29
22 changed files with 1315 additions and 4273 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ coverage
tmp tmp
docker-compose.yml docker-compose.yml
.env.test .env.test
public/assets

6
ace.js
View file

@ -15,10 +15,10 @@
/** /**
* Register hook to process TypeScript files using ts-node * Register hook to process TypeScript files using ts-node
*/ */
import { register } from 'node:module' import { register } from 'node:module';
register('ts-node/esm', import.meta.url) register('ts-node/esm', import.meta.url);
/** /**
* Import ace console entrypoint * Import ace console entrypoint
*/ */
await import('./bin/console.js') await import('./bin/console.js');

View file

@ -276,7 +276,7 @@ export default class DatasetsController {
validateSMTP: false, validateSMTP: false,
}); });
const validRecipientEmail: boolean = validationResult.valid; const validRecipientEmail: boolean = validationResult.valid;
let emailStatusMessage = ''; // let emailStatusMessage = '';
if (sendMail == true) { if (sendMail == true) {
if (dataset.editor.email && validRecipientEmail) { if (dataset.editor.email && validRecipientEmail) {
@ -289,7 +289,7 @@ export default class DatasetsController {
<p>Best regards,<br>Your Tethys reviewer: ${authUser.login}</p> <p>Best regards,<br>Your Tethys reviewer: ${authUser.login}</p>
`); `);
}); });
emailStatusMessage = ` A rejection email was successfully sent to ${dataset.editor.email}.`; // emailStatusMessage = ` A rejection email was successfully sent to ${dataset.editor.email}.`;
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
return response return response
@ -297,7 +297,7 @@ export default class DatasetsController {
.toRoute('reviewer.dataset.list'); .toRoute('reviewer.dataset.list');
} }
} else { } else {
emailStatusMessage = ` However, the email could not be sent because the editor's email address (${dataset.editor.email}) is not valid.`; // emailStatusMessage = ` However, the email could not be sent because the editor's email address (${dataset.editor.email}) is not valid.`;
} }
} }

View file

@ -1193,7 +1193,7 @@ export default class DatasetController {
throw error; throw error;
} else if (error instanceof Exception) { } else if (error instanceof Exception) {
// General exception handling // General exception handling
return response.flash('errors', { error: error.message }).redirect().back(); return response.flash('errors', error.message).redirect().back();
} else { } else {
session.flash({ error: 'An error occurred while deleting the dataset.' }); session.flash({ error: 'An error occurred while deleting the dataset.' });
return response.redirect().back(); return response.redirect().back();

View file

@ -273,7 +273,7 @@ export const updateDatasetValidator = vine.compile(
references: vine references: vine
.array( .array(
vine.object({ vine.object({
value: vine.string().trim().minLength(3).maxLength(255), value: vine.string().trim().minLength(3).maxLength(255).validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
relation: vine.enum(Object.values(RelationTypes)), relation: vine.enum(Object.values(RelationTypes)),
label: vine.string().trim().minLength(2).maxLength(255), label: vine.string().trim().minLength(2).maxLength(255),

View file

@ -142,7 +142,7 @@ export class VanillaErrorReporter implements ErrorReporterContract {
// } // }
this.hasErrors = true; this.hasErrors = true;
var test = field.getFieldPath(); // var test = field.getFieldPath();
// this.errors.push(error); // this.errors.push(error);
// if (this.errors[error.field]) { // if (this.errors[error.field]) {

View file

@ -47,7 +47,7 @@ const databaseConfig = defineConfig({
migrations: { migrations: {
naturalSort: true, naturalSort: true,
}, },
healthCheck: false, // healthCheck: false,
debug: false, debug: false,
pool: { min: 1, max: 100 }, pool: { min: 1, max: 100 },
}, },

View file

@ -12,7 +12,7 @@ const mailConfig = defineConfig({
mailers: { mailers: {
smtp: transports.smtp({ smtp: transports.smtp({
socketTimeout: 5000,// Overall timeout (5 seconds) // socketTimeout: 5000,// Overall timeout (5 seconds)
host: env.get('SMTP_HOST', ''), host: env.get('SMTP_HOST', ''),
port: env.get('SMTP_PORT'), port: env.get('SMTP_PORT'),
secure: false, secure: false,
@ -30,10 +30,10 @@ const mailConfig = defineConfig({
}, */ }, */
}), }),
resend: transports.resend({ // resend: transports.resend({
key: env.get('RESEND_API_KEY'), // key: env.get('RESEND_API_KEY'),
baseUrl: 'https://api.resend.com', // baseUrl: 'https://api.resend.com',
}), // }),
}, },
}); });

View file

@ -6,7 +6,7 @@
*/ */
import env from '#start/env'; import env from '#start/env';
import app from '@adonisjs/core/services/app'; // import app from '@adonisjs/core/services/app';
import { defineConfig, stores } from '@adonisjs/session'; import { defineConfig, stores } from '@adonisjs/session';
const sessionConfig = defineConfig({ const sessionConfig = defineConfig({

5289
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,28 +15,29 @@
"format-check": "prettier --check ./**/*.{ts,js}", "format-check": "prettier --check ./**/*.{ts,js}",
"test": "node ace test" "test": "node ace test"
}, },
"eslintIgnore": [ "eslintConfig": {
"build" "ignorePatterns": [
], "build"
]
},
"alias": { "alias": {
"vue": "./node_modules/vue/dist/vue.esm-bundler.js" "vue": "./node_modules/vue/dist/vue.esm-bundler.js"
}, },
"devDependencies": { "devDependencies": {
"@adonisjs/assembler": "^7.1.1", "@adonisjs/assembler": "^7.1.1",
"@adonisjs/tsconfig": "^1.2.1", "@adonisjs/tsconfig": "^1.4.0",
"@babel/core": "^7.20.12", "@babel/core": "^7.20.12",
"@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.20.13", "@babel/plugin-proposal-decorators": "^7.20.13",
"@babel/plugin-transform-runtime": "^7.19.6", "@babel/plugin-transform-runtime": "^7.19.6",
"@babel/preset-env": "^7.20.2", "@babel/preset-env": "^7.20.2",
"@babel/preset-typescript": "^7.18.6", "@babel/preset-typescript": "^7.18.6",
"@japa/api-client": "^2.0.3", "@japa/assert": "^4.0.1",
"@japa/assert": "^3.0.0", "@japa/plugin-adonisjs": "^4.0.0",
"@japa/plugin-adonisjs": "^3.0.0", "@japa/runner": "^4.2.0",
"@japa/runner": "^3.1.1",
"@mdi/js": "^7.1.96", "@mdi/js": "^7.1.96",
"@poppinss/utils": "^6.7.2", "@poppinss/utils": "^6.7.2",
"@swc/core": "^1.4.2", "@swc/core": "^1.10.1",
"@symfony/webpack-encore": "^5.0.1", "@symfony/webpack-encore": "^5.0.1",
"@tailwindcss/forms": "^0.5.2", "@tailwindcss/forms": "^0.5.2",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
@ -44,7 +45,7 @@
"@types/escape-html": "^1.0.4", "@types/escape-html": "^1.0.4",
"@types/leaflet": "^1.9.3", "@types/leaflet": "^1.9.3",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@types/node": "^22.5.5", "@types/node": "^22.10.2",
"@types/proxy-addr": "^2.0.0", "@types/proxy-addr": "^2.0.0",
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@types/source-map-support": "^0.5.6", "@types/source-map-support": "^0.5.6",
@ -60,14 +61,14 @@
"eslint-plugin-prettier": "^5.0.0-alpha.2", "eslint-plugin-prettier": "^5.0.0-alpha.2",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"pinia": "^2.0.30", "pinia": "^2.0.30",
"pino-pretty": "^11.2.2", "pino-pretty": "^13.0.0",
"postcss-loader": "^8.1.1", "postcss-loader": "^8.1.1",
"prettier": "^3.0.0", "prettier": "^3.4.2",
"supertest": "^6.3.3", "supertest": "^6.3.3",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.4",
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.1.3", "typescript": "~5.7",
"vue": "^3.4.26", "vue": "^3.4.26",
"vue-facing-decorator": "^3.0.0", "vue-facing-decorator": "^3.0.0",
"vue-loader": "^17.0.1", "vue-loader": "^17.0.1",
@ -75,26 +76,29 @@
"xslt3": "^2.5.0" "xslt3": "^2.5.0"
}, },
"dependencies": { "dependencies": {
"@adonisjs/auth": "^9.1.1", "@adonisjs/auth": "^9.2.4",
"@adonisjs/core": "^6.3.1", "@adonisjs/core": "^6.17.0",
"@adonisjs/cors": "^2.2.1", "@adonisjs/cors": "^2.2.1",
"@adonisjs/drive": "^2.3.0", "@adonisjs/drive": "^3.2.0",
"@adonisjs/encore": "^1.0.0", "@adonisjs/encore": "^1.0.0",
"@adonisjs/inertia": "^1.0.0-7", "@adonisjs/inertia": "^2.1.3",
"@adonisjs/lucid": "^21.1.0", "@adonisjs/lucid": "^21.5.1",
"@adonisjs/mail": "^9.2.2", "@adonisjs/mail": "^9.2.2",
"@adonisjs/redis": "^9.1.0", "@adonisjs/redis": "^9.1.0",
"@adonisjs/session": "^7.1.1", "@adonisjs/session": "^7.5.0",
"@adonisjs/shield": "^8.1.1", "@adonisjs/shield": "^8.1.1",
"@adonisjs/static": "^1.1.1", "@adonisjs/static": "^1.1.1",
"@adonisjs/vite": "^4.0.0",
"@eidellev/adonis-stardust": "^3.0.0", "@eidellev/adonis-stardust": "^3.0.0",
"@fontsource/archivo-black": "^5.0.1", "@fontsource/archivo-black": "^5.0.1",
"@fontsource/inter": "^5.0.1", "@fontsource/inter": "^5.0.1",
"@inertiajs/inertia": "^0.11.1", "@inertiajs/inertia": "^0.11.1",
"@inertiajs/vue3": "^1.0.0", "@inertiajs/vue3": "^2.0.3",
"@opensearch-project/opensearch": "^2.4.0", "@opensearch-project/opensearch": "^2.4.0",
"@phc/format": "^1.0.0", "@phc/format": "^1.0.0",
"@vinejs/vine": "^2.0.0", "@poppinss/manager": "^5.0.2",
"@vinejs/vine": "^3.0.0",
"axios": "^1.7.9",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"clamscan": "^2.1.2", "clamscan": "^2.1.2",

View file

@ -69,7 +69,7 @@ export default class MailProvider {
const mailConfigProvider = this.app.config.get('mail'); const mailConfigProvider = this.app.config.get('mail');
const config = await configProvider.resolve<any>(this.app, mailConfigProvider); const config = await configProvider.resolve<any>(this.app, mailConfigProvider);
const iwas = await config.mailers.smtp(); await config.mailers.smtp();
// iwas.config.host = 'hhhost'; // iwas.config.host = 'hhhost';
// this.app.config.set('mail.mailers.smtp.host', 'xhost'); // this.app.config.set('mail.mailers.smtp.host', 'xhost');
// const iwas = await config.mailers.smtp(); // const iwas = await config.mailers.smtp();

View file

@ -5,7 +5,7 @@
|*/ |*/
import type { ApplicationService } from '@adonisjs/core/types'; import type { ApplicationService } from '@adonisjs/core/types';
import vine, { BaseLiteralType, Vine } from '@vinejs/vine'; import vine, { BaseLiteralType, Vine } from '@vinejs/vine';
import type { Validation, FieldContext, FieldOptions } from '@vinejs/vine/types'; import type { FieldContext, FieldOptions } from '@vinejs/vine/types';
// import type { MultipartFile, FileValidationOptions } from '@adonisjs/bodyparser/types'; // import type { MultipartFile, FileValidationOptions } from '@adonisjs/bodyparser/types';
import type { MultipartFile } from '@adonisjs/core/bodyparser'; import type { MultipartFile } from '@adonisjs/core/bodyparser';
import type { FileValidationOptions } from '@adonisjs/core/types/bodyparser'; import type { FileValidationOptions } from '@adonisjs/core/types/bodyparser';
@ -28,8 +28,7 @@ declare module '@vinejs/vine' {
* Extend HTTP request class * Extend HTTP request class
*/ */
declare module '@adonisjs/core/http' { declare module '@adonisjs/core/http' {
interface Request extends RequestValidator { interface Request extends RequestValidator {}
}
} }
/** /**
@ -48,7 +47,7 @@ export async function getEnabledExtensions() {
.flat(); .flat();
return extensions; return extensions;
}; }
/** /**
* VineJS validation rule that validates the file to be an * VineJS validation rule that validates the file to be an
* instance of BodyParser MultipartFile class. * instance of BodyParser MultipartFile class.
@ -82,8 +81,8 @@ const isMultipartFile = vine.createRule(async (file: MultipartFile | unknown, op
if (validatedFile.allowedExtensions === undefined && validationOptions.extnames) { if (validatedFile.allowedExtensions === undefined && validationOptions.extnames) {
validatedFile.allowedExtensions = await getEnabledExtensions(); validatedFile.allowedExtensions = await getEnabledExtensions();
} }
/** /**
* wieder löschen * wieder löschen
* Set extensions when it's defined in the options and missing * Set extensions when it's defined in the options and missing
* on the file instance * on the file instance
*/ */
@ -111,14 +110,16 @@ export class VineMultipartFile extends BaseLiteralType<MultipartFile, MultipartF
// extnames: (18) ['gpkg', 'htm', 'html', 'csv', 'txt', 'asc', 'c', 'cc', 'h', 'srt', 'tiff', 'pdf', 'png', 'zip', 'jpg', 'jpeg', 'jpe', 'xlsx'] // extnames: (18) ['gpkg', 'htm', 'html', 'csv', 'txt', 'asc', 'c', 'cc', 'h', 'srt', 'tiff', 'pdf', 'png', 'zip', 'jpg', 'jpeg', 'jpe', 'xlsx']
// size: '512mb' // size: '512mb'
public constructor(validationOptions?: FileRuleValidationOptions, options?: FieldOptions, validations?: Validation<any>[]) { // public constructor(validationOptions?: FileRuleValidationOptions, options?: FieldOptions, validations?: Validation<any>[]) {
public constructor(validationOptions?: FileRuleValidationOptions, options?: FieldOptions) {
// super(options, validations); // super(options, validations);
super(options, [isMultipartFile(validationOptions || {})]); super(options, [isMultipartFile(validationOptions || {})]);
this.validationOptions = validationOptions; this.validationOptions = validationOptions;
} }
public clone(): any { public clone(): any {
return new VineMultipartFile(this.validationOptions, this.cloneOptions(), this.cloneValidations()); // return new VineMultipartFile(this.validationOptions, this.cloneOptions(), this.cloneValidations());
return new VineMultipartFile(this.validationOptions, this.cloneOptions());
} }
} }
@ -153,9 +154,8 @@ export default class VinejsProvider {
* data for the current request using VineJS validators * data for the current request using VineJS validators
*/ */
Request.macro('validateUsing', function (...args) { Request.macro('validateUsing', function (...args) {
return new RequestValidator(this.ctx).validateUsing(...args); return new RequestValidator(this.ctx).validateUsing(...args);
}); });
} }
/** /**

View file

@ -20,7 +20,7 @@
import AuthLayout from '@/Layouts/Auth.vue'; import AuthLayout from '@/Layouts/Auth.vue';
import { reactive } from 'vue'; import { reactive } from 'vue';
import { useForm } from '@inertiajs/vue3'; import { useForm } from '@inertiajs/vue3';
import { Inertia } from '@inertiajs/inertia'; // import { Inertia } from '@inertiajs/inertia';
// import { NButton, NInput } from 'naive-ui'; // import { NButton, NInput } from 'naive-ui';
// import { useForm } from '@inertiajs/inertia-vue3' // import { useForm } from '@inertiajs/inertia-vue3'
import FormInput from '@/Components/FormInput.vue'; import FormInput from '@/Components/FormInput.vue';
@ -45,7 +45,7 @@ export default {
}); });
const submit = async () => { const submit = async () => {
await Inertia.post('/app/register', form); // await Inertia.post('/app/register', form);
}; };
return { form, submit }; return { form, submit };

View file

@ -72,9 +72,19 @@ createInertiaApp({
.use(EmitterPlugin); .use(EmitterPlugin);
// .component('inertia-link', Link) // .component('inertia-link', Link)
// Listen for navigation event to handle layout changes
// window.addEventListener('inertia:navigate', () => {
// layoutService.isAsideMobileExpanded = false;
// layoutService.isAsideLgActive = false;
// });
asyncPlugin.install('settings').then(() => { asyncPlugin.install('settings').then(() => {
app.mount(el); app.mount(el);
}); });
}, },
}); });

View file

@ -15,5 +15,5 @@
}, },
}, },
"include": ["./**/*.ts", "./**/*.vue"], "include": ["./**/*.ts", "./**/*.vue"],
"exclude": ["./utils/*.js"], "exclude": ["./utils/*.js", "./utils/Timer.js", "./utils/focusTrap.js"],
} }

View file

@ -1,16 +1,15 @@
/** /**
* Get the first day of the week * Get the first day of the week
* *
* @return {number} * @return {number}
*/ */
export function getFirstDay(): number { export function getFirstDay(): number {
if (typeof window.firstDay === 'undefined') { if (typeof window.firstDay === 'undefined') {
console.warn('No firstDay found') console.warn('No firstDay found');
return 1 return 1;
} }
return window.firstDay return window.firstDay;
} }
/** /**
@ -19,20 +18,12 @@ export function getFirstDay(): number {
* @return {string[]} * @return {string[]}
*/ */
export function getDayNames(): string[] { export function getDayNames(): string[] {
if (typeof window.dayNames === 'undefined') { if (typeof window.dayNames === 'undefined') {
console.warn('No dayNames found') console.warn('No dayNames found');
return [ return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
'Sunday', }
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
]
}
return window.dayNames return window.dayNames;
} }
/** /**
@ -41,12 +32,12 @@ export function getDayNames(): string[] {
* @return {string[]} * @return {string[]}
*/ */
export function getDayNamesShort(): string[] { export function getDayNamesShort(): string[] {
if (typeof window.dayNamesShort === 'undefined') { if (typeof window.dayNamesShort === 'undefined') {
console.warn('No dayNamesShort found') console.warn('No dayNamesShort found');
return ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'] return ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'];
} }
return window.dayNamesShort return window.dayNamesShort;
} }
/** /**
@ -55,12 +46,12 @@ export function getDayNamesShort(): string[] {
* @return {string[]} * @return {string[]}
*/ */
export function getDayNamesMin(): string[] { export function getDayNamesMin(): string[] {
if (typeof window.dayNamesMin === 'undefined') { if (typeof window.dayNamesMin === 'undefined') {
console.warn('No dayNamesMin found') console.warn('No dayNamesMin found');
return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
} }
return window.dayNamesMin return window.dayNamesMin;
} }
/** /**
@ -69,25 +60,12 @@ export function getDayNamesMin(): string[] {
* @return {string[]} * @return {string[]}
*/ */
export function getMonthNames(): string[] { export function getMonthNames(): string[] {
if (typeof window.monthNames === 'undefined') { if (typeof window.monthNames === 'undefined') {
console.warn('No monthNames found') console.warn('No monthNames found');
return [ return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
'January', }
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
]
}
return window.monthNames return window.monthNames;
} }
/** /**
@ -96,23 +74,10 @@ export function getMonthNames(): string[] {
* @return {string[]} * @return {string[]}
*/ */
export function getMonthNamesShort(): string[] { export function getMonthNamesShort(): string[] {
if (typeof window.monthNamesShort === 'undefined') { if (typeof window.monthNamesShort === 'undefined') {
console.warn('No monthNamesShort found') console.warn('No monthNamesShort found');
return [ return ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'];
'Jan.', }
'Feb.',
'Mar.',
'Apr.',
'May.',
'Jun.',
'Jul.',
'Aug.',
'Sep.',
'Oct.',
'Nov.',
'Dec.',
]
}
return window.monthNamesShort return window.monthNamesShort;
} }

View file

@ -79,7 +79,7 @@ router.group(() => {
}).as('register.show'); }).as('register.show');
router.post('/register', async ({ request, response }: HttpContext) => { router.post('/register', async ({ request, response }: HttpContext) => {
const data = await request.validateUsing(authValidator); await request.validateUsing(authValidator);
return response.redirect().toRoute('app.index'); return response.redirect().toRoute('app.index');
}).as('register.store'); }).as('register.store');
}).prefix('apps').as('apps').use(middleware.auth()); }).prefix('apps').as('apps').use(middleware.auth());

View file

@ -25,6 +25,7 @@ type Options = {
clientNameSizeLimit: number; clientNameSizeLimit: number;
}; };
// async function allowedMimetypeExtensions(file: VineMultipartFile | unknown, options: Options | unknown, field: FieldContext) {
async function allowedMimetypeExtensions(file: VineMultipartFile | unknown, options: Options | unknown, field: FieldContext) { async function allowedMimetypeExtensions(file: VineMultipartFile | unknown, options: Options | unknown, field: FieldContext) {
// if (typeof value !== 'string' && typeof value != 'number') { // if (typeof value !== 'string' && typeof value != 'number') {
// return; // return;
@ -73,10 +74,10 @@ export const allowedMimetypeExtensionsRule = vine.createRule(allowedMimetypeExte
declare module '#providers/vinejs_provider' { declare module '#providers/vinejs_provider' {
interface VineMultipartFile { interface VineMultipartFile {
allowedMimetypeExtensions(options?: Options): this; allowedMimetypeExtensions(options?: Options): this;
} }
} }
VineMultipartFile.macro('allowedMimetypeExtensions', function (this: VineMultipartFile, options: Options) { VineMultipartFile.macro('allowedMimetypeExtensions', function (this: VineMultipartFile, options: Options) {
return this.use(allowedMimetypeExtensionsRule(options)); return this.use(allowedMimetypeExtensionsRule(options));
}); });

View file

@ -3,37 +3,16 @@
| Preloaded File - node ace make:preload rules/fileScan | Preloaded File - node ace make:preload rules/fileScan
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
|*/ |*/
import { FieldContext } from '@vinejs/vine/types'; import { FieldContext } from '@vinejs/vine/types';
import vine, { errors } from '@vinejs/vine'; import vine, { errors } from '@vinejs/vine';
// import { VineString } from '@vinejs/vine';
import { VineMultipartFile, isBodyParserFile } from '#providers/vinejs_provider'; import { VineMultipartFile, isBodyParserFile } from '#providers/vinejs_provider';
import type { MultipartFile } from '@adonisjs/core/bodyparser'; import type { MultipartFile } from '@adonisjs/core/bodyparser';
import ClamScan from 'clamscan'; import ClamScan from 'clamscan';
/**
* Options accepted by the unique rule
*/
// type Options = {
// mainLanguageField: string;
// typeField: string;
// };
type Options = { type Options = {
// size: string | number;
// extnames: string[];
removeInfected: boolean; removeInfected: boolean;
// debugMode?: boolean;
// scanRecursively?: boolean;
host?: string; host?: string;
port?: number; port?: number;
// clamdscan: {
// active: boolean;
// host: string;
// port: number;
// multiscan: boolean;
// };
// preference: string;
}; };
async function fileScan(file: VineMultipartFile | unknown, options: Options, field: FieldContext) { async function fileScan(file: VineMultipartFile | unknown, options: Options, field: FieldContext) {
@ -44,42 +23,47 @@ async function fileScan(file: VineMultipartFile | unknown, options: Options, fie
return; return;
} }
const validatedFile = file as MultipartFile; const validatedFile = file as MultipartFile;
try { try {
await scanFileForViruses(validatedFile.tmpPath, options.host, options.port); //, 'gitea.lan', 3310); await scanFileForViruses(validatedFile.tmpPath, options);
// await this.scanFileForViruses("/tmp/testfile.txt");
} catch (error) { } catch (error) {
// If the file is infected or there's an error scanning the file, throw a validation exception // If the file is infected or there's an error scanning the file, throw a validation exception
// throw error; // throw error;
field.report(`Upload error. Code: ${error.code} message: ${error.messages.uploadError}`, 'fileScan', field); field.report(`Upload error. Code: ${error.code} message: ${error.messages.uploadError}`, 'fileScan', field);
} }
} }
async function scanFileForViruses(filePath: string | undefined, host?: string, port?: number): Promise<void> {
// const clamscan = await (new ClamScan().init()); async function scanFileForViruses(filePath: string | undefined, options: Options): Promise<void> {
if (!filePath) {
throw new errors.E_VALIDATION_ERROR({ uploadError: 'File path is undefined!' });
}
const opts: ClamScan.Options = { const opts: ClamScan.Options = {
removeInfected: true, // If true, removes infected files removeInfected: options.removeInfected, // If true, removes infected files
debugMode: false, // Whether or not to log info/debug/error msgs to the console debugMode: false, // If true, deep scan folders recursively
scanRecursively: true, // If true, deep scan folders recursively scanRecursively: true, // If true, deep scan folders recursively
clamdscan: { clamdscan: {
active: true, // If true, this module will consider using the clamdscan binary active: true, // If true, this module will consider using the clamdscan binary
host, host: options.host,
port, port: options.port,
multiscan: true, // Scan using all available cores! Yay! multiscan: true, // Scan using all available cores! Yay!
}, },
preference: 'clamdscan', // If clamdscan is found and active, it will be used by default
}; };
const clamscan = await new ClamScan().init(opts);
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const clamscan = await new ClamScan().init(opts);
// You can re-use the `clamscan` object as many times as you want // You can re-use the `clamscan` object as many times as you want
// const version = await clamscan.getVersion(); // const version = await clamscan.getVersion();
// console.log(`ClamAV Version: ${version}`); // console.log(`ClamAV Version: ${version}`);
const { file, isInfected, viruses } = await clamscan.isInfected(filePath); const result = await clamscan.isInfected(filePath);
if (!result || typeof result.isInfected === 'undefined') {
reject(new errors.E_VALIDATION_ERROR({ uploadError: 'Unexpected response from virus scan!' }));
return;
}
const { file, isInfected, viruses } = result;
if (isInfected) { if (isInfected) {
console.log(`${file} is infected with ${viruses}!`); console.log(`${file} is infected with ${viruses}!`); // reject(new ValidationException(true, { 'upload error': `File ${file} is infected!` }));
// reject(new ValidationException(true, { 'upload error': `File ${file} is infected!` })); reject(new errors.E_VALIDATION_ERROR({ uploadError: `File ${file} is infected with ${viruses}!` }));
reject(new errors.E_VALIDATION_ERROR({ uploadError: `File ${file} is infected!` }));
} else { } else {
resolve(); resolve();
} }

View file

@ -1,7 +1,7 @@
import { FieldContext } from '@vinejs/vine/types'; import { FieldContext } from '@vinejs/vine/types';
import vine from '@vinejs/vine'; import vine from '@vinejs/vine';
import { VineString } from '@vinejs/vine'; import { VineString } from '@vinejs/vine';
import axios, { AxiosInstance } from 'axios'; import axios from 'axios';
import { ReferenceIdentifierTypes } from '#contracts/enums'; import { ReferenceIdentifierTypes } from '#contracts/enums';
type Options = { type Options = {
@ -11,7 +11,7 @@ type Options = {
// Function to check if DOI exists using the DOI API // Function to check if DOI exists using the DOI API
async function checkDoiExists(doi: string): Promise<boolean> { async function checkDoiExists(doi: string): Promise<boolean> {
try { try {
const response = await axios.get(`${doi}`); const response = await axios.default.get(`${doi}`);
return response.status === 200; // If status is 200, DOI is valid return response.status === 200; // If status is 200, DOI is valid
} catch (error) { } catch (error) {
return false; // If request fails, DOI does not exist return false; // If request fails, DOI does not exist
@ -21,7 +21,7 @@ async function checkDoiExists(doi: string): Promise<boolean> {
// Function to check if ISBN exists using the Open Library API // Function to check if ISBN exists using the Open Library API
async function checkIsbnExists(isbn: string): Promise<boolean> { async function checkIsbnExists(isbn: string): Promise<boolean> {
try { try {
const response = await axios.get(`https://isbnsearch.org/isbn/${isbn}`); const response = await axios.default.get(`https://isbnsearch.org/isbn/${isbn}`);
return response.data && response.status == 200; // If title is returned, ISBN is valid return response.data && response.status == 200; // If title is returned, ISBN is valid
} catch (error) { } catch (error) {
return false; // If request fails, ISBN does not exist return false; // If request fails, ISBN does not exist

View file

@ -51,7 +51,7 @@ test.group('ReferenceValidation', () => {
vine.object({ vine.object({
reference: vine.string().validateReference({ typeField: 'type' }), reference: vine.string().validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
}) }),
); );
const data = { const data = {
@ -71,13 +71,13 @@ test.group('ReferenceValidation', () => {
vine.object({ vine.object({
reference: vine.string().validateReference({ typeField: 'type' }), reference: vine.string().validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
}) }),
); );
const data = { const data = {
// reference: '978-3-85316-090-9', // reference: '978-3-85316-090-9',
// reference: '9783853160909', // reference: '9783853160909',
// reference: '978-3-900312-64-0', // Geologische Karte der Republik Österreich 1 : 50.000 // reference: '978-3-900312-64-0', // Geologische Karte der Republik Österreich 1 : 50.000
reference: '3900312648', // Geologische Karte der Republik Österreich 1 : 50.000 reference: '3900312648', // Geologische Karte der Republik Österreich 1 : 50.000
type: ReferenceIdentifierTypes.ISBN, type: ReferenceIdentifierTypes.ISBN,
}; };
@ -95,7 +95,7 @@ test.group('ReferenceValidation', () => {
vine.object({ vine.object({
reference: vine.string().validateReference({ typeField: 'type' }), reference: vine.string().validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
}) }),
); );
const data = { const data = {
@ -116,7 +116,7 @@ test.group('ReferenceValidation', () => {
vine.object({ vine.object({
reference: vine.string().validateReference({ typeField: 'type' }), reference: vine.string().validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
}) }),
); );
const data = { const data = {
@ -137,7 +137,7 @@ test.group('ReferenceValidation', () => {
vine.object({ vine.object({
reference: vine.string().validateReference({ typeField: 'type' }), reference: vine.string().validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
}) }),
); );
const data = { const data = {
@ -158,7 +158,7 @@ test.group('ReferenceValidation', () => {
vine.object({ vine.object({
reference: vine.string().validateReference({ typeField: 'type' }), reference: vine.string().validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
}) }),
); );
const data = { const data = {
@ -179,7 +179,7 @@ test.group('ReferenceValidation', () => {
vine.object({ vine.object({
reference: vine.string().validateReference({ typeField: 'type' }), reference: vine.string().validateReference({ typeField: 'type' }),
type: vine.enum(Object.values(ReferenceIdentifierTypes)), type: vine.enum(Object.values(ReferenceIdentifierTypes)),
}) }),
); );
const data = { const data = {