From e0ff71b117dcd6c9a20edcaf7de76b2f4aa38942 Mon Sep 17 00:00:00 2001 From: Arno Kaimbacher Date: Thu, 22 Jun 2023 17:20:04 +0200 Subject: [PATCH] - additional functionality for DatasetController.ts - additional validation rules like 'uniqueArray' - additional Lucid models like BaseModel.ts for filling attributes, Title.ts, Description.ts - npm updates for @adonisjs/core --- app/Controllers/Http/Admin/HomeController.ts | 16 +- app/Controllers/Http/Admin/UsersController.ts | 338 +++++++++--------- app/Controllers/Http/Api/AuthorsController.ts | 6 +- app/Controllers/Http/Api/DatasetController.ts | 22 +- app/Controllers/Http/Auth/AuthController.ts | 58 ++- .../Http/Submitter/DatasetController.ts | 268 ++++++++------ app/Exceptions/Handler.ts | 70 ++-- app/Exceptions/InvalidCredentialException.ts | 16 +- app/Middleware/Auth.ts | 105 +++--- app/Middleware/Can.ts | 125 +++---- app/Middleware/Is.ts | 100 +++--- app/Middleware/Role.ts | 119 +++--- app/Middleware/SilentAuth.ts | 20 +- app/Models/BaseModel.ts | 119 ++++++ app/Models/Dataset.ts | 114 ++++-- app/Models/Description.ts | 28 ++ app/Models/File.ts | 61 ++-- app/Models/HashValue.ts | 47 ++- app/Models/License.ts | 16 + app/Models/Permission.ts | 154 ++++---- app/Models/Person.ts | 130 ++++--- app/Models/Project.ts | 15 +- app/Models/Role.ts | 154 ++++---- app/Models/Title.ts | 28 ++ app/Models/User.ts | 134 +++---- app/Models/UserRole.ts | 126 +++---- app/Validators/AuthValidator.ts | 78 ++-- app/Validators/CreateDatasetValidator.ts | 157 ++++++++ app/Validators/CreateRoleValidator.ts | 122 +++---- app/Validators/CreateUserValidator.ts | 112 +++--- app/Validators/UpdateRoleValidator.ts | 168 ++++----- contracts/validator.ts | 1 + database/migrations/dataset_4_titles.ts | 2 +- database/migrations/dataset_9_persons.ts | 1 + index.d.ts | 8 +- package-lock.json | 166 ++++----- package.json | 4 +- postcss.config.js | 10 +- resources/js/Dataset.ts | 55 +-- resources/js/styles.js | 48 +-- start/routes/api.ts | 26 +- start/validator.ts | 28 ++ tailwind.config.js | 12 +- webpack.config.js | 171 +++++---- 44 files changed, 2002 insertions(+), 1556 deletions(-) create mode 100644 app/Models/BaseModel.ts create mode 100644 app/Models/Description.ts create mode 100644 app/Models/Title.ts create mode 100644 app/Validators/CreateDatasetValidator.ts diff --git a/app/Controllers/Http/Admin/HomeController.ts b/app/Controllers/Http/Admin/HomeController.ts index 1576326..33bc433 100644 --- a/app/Controllers/Http/Admin/HomeController.ts +++ b/app/Controllers/Http/Admin/HomeController.ts @@ -1,17 +1,17 @@ -import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; export default class HomeController { - public async index({}: HttpContextContract) {} + public async index({}: HttpContextContract) {} - public async create({}: HttpContextContract) {} + public async create({}: HttpContextContract) {} - public async store({}: HttpContextContract) {} + public async store({}: HttpContextContract) {} - public async show({}: HttpContextContract) {} + public async show({}: HttpContextContract) {} - public async edit({}: HttpContextContract) {} + public async edit({}: HttpContextContract) {} - public async update({}: HttpContextContract) {} + public async update({}: HttpContextContract) {} - public async destroy({}: HttpContextContract) {} + public async destroy({}: HttpContextContract) {} } diff --git a/app/Controllers/Http/Admin/UsersController.ts b/app/Controllers/Http/Admin/UsersController.ts index 2ad8f5b..4cab1b5 100644 --- a/app/Controllers/Http/Admin/UsersController.ts +++ b/app/Controllers/Http/Admin/UsersController.ts @@ -8,197 +8,207 @@ import { RenderResponse } from '@ioc:EidelLev/Inertia'; // import { schema, rules } from '@ioc:Adonis/Core/Validator'; export default class UsersController { - public async index({ auth, request, inertia }: HttpContextContract) { - const page = request.input('page', 1); - // const limit = 10 + public async index({ auth, request, inertia }: HttpContextContract) { + const page = request.input('page', 1); + // const limit = 10 - let users: ModelQueryBuilderContract = User.query(); + let users: ModelQueryBuilderContract = User.query(); - if (request.input('search')) { - // users = users.whereRaw('name like %?%', [request.input('search')]) - const searchTerm = request.input('search'); - users.where('login', 'ilike', `%${searchTerm}%`); - } + if (request.input('search')) { + // users = users.whereRaw('name like %?%', [request.input('search')]) + const searchTerm = request.input('search'); + users.where('login', 'ilike', `%${searchTerm}%`); + } - if (request.input('sort')) { - type SortOrder = 'asc' | 'desc' | undefined; - let attribute = request.input('sort'); - let sort_order: SortOrder = 'asc'; + if (request.input('sort')) { + type SortOrder = 'asc' | 'desc' | undefined; + let attribute = request.input('sort'); + let sort_order: SortOrder = 'asc'; - // if (strncmp($attribute, '-', 1) === 0) { - if (attribute.substr(0, 1) == '-') { - sort_order = 'desc'; - // attribute = substr(attribute, 1); - attribute = attribute.substr(1); - } - // $users->orderBy($attribute, $sort_order); - users.orderBy(attribute, sort_order); - } else { - // users.orderBy('created_at', 'desc'); - users.orderBy('id', 'asc'); - } + // if (strncmp($attribute, '-', 1) === 0) { + if (attribute.substr(0, 1) == '-') { + sort_order = 'desc'; + // attribute = substr(attribute, 1); + attribute = attribute.substr(1); + } + // $users->orderBy($attribute, $sort_order); + users.orderBy(attribute, sort_order); + } else { + // users.orderBy('created_at', 'desc'); + users.orderBy('id', 'asc'); + } - // const users = await User.query().orderBy('login').paginate(page, limit); + // const users = await User.query().orderBy('login').paginate(page, limit); - let usersResult = await users // User.query() - // .orderBy('login') - // .filter(qs) - // .preload('focusInterests') - // .preload('role') - .paginate(page, 5); + let usersResult = await users // User.query() + // .orderBy('login') + // .filter(qs) + // .preload('focusInterests') + // .preload('role') + .paginate(page, 5); - // var test = request.all(); + // var test = request.all(); - return inertia.render('Admin/User/Index', { - // testing: 'this is a test', - users: usersResult.toJSON(), - filters: request.all(), - can: { - create: await auth.user?.can(['user-create']), - edit: await auth.user?.can(['user-edit']), - delete: await auth.user?.can(['user-delete']), - }, - }); - } + return inertia.render('Admin/User/Index', { + // testing: 'this is a test', + users: usersResult.toJSON(), + filters: request.all(), + can: { + create: await auth.user?.can(['user-create']), + edit: await auth.user?.can(['user-edit']), + delete: await auth.user?.can(['user-delete']), + }, + }); + } - public async create({ inertia }: HttpContextContract) { - // let rolesPluck = {}; - // (await Role.query().select('id', 'name')).forEach((user) => { - // rolesPluck[user.id] = user.name; - // }); - const roles = await Role.query().select('id', 'name').pluck('name', 'id'); + public async create({ inertia }: HttpContextContract) { + // let rolesPluck = {}; + // (await Role.query().select('id', 'name')).forEach((user) => { + // rolesPluck[user.id] = user.name; + // }); + const roles = await Role.query().select('id', 'name').pluck('name', 'id'); - return inertia.render('Admin/User/Create', { - roles: roles, - }); - } + return inertia.render('Admin/User/Create', { + roles: roles, + }); + } - public async store({ request, response, session }: HttpContextContract) { - // node ace make:validator CreateUser - try { - // Step 2 - Validate request body against the schema - await request.validate(CreateUserValidator); - // console.log({ payload }); - } catch (error) { - // Step 3 - Handle errors - // return response.badRequest(error.messages); - throw error; - } - const input = request.only(['login', 'email', 'password']); - const user = await User.create(input); - if (request.input('roles')) { - const roles: Array = request.input('roles'); - await user.related('roles').attach(roles); - } + public async store({ request, response, session }: HttpContextContract) { + // node ace make:validator CreateUser + try { + // Step 2 - Validate request body against the schema + await request.validate(CreateUserValidator); + // console.log({ payload }); + } catch (error) { + // Step 3 - Handle errors + // return response.badRequest(error.messages); + throw error; + } + const input = request.only(['login', 'email', 'password']); + const user = await User.create(input); + if (request.input('roles')) { + const roles: Array = request.input('roles'); + await user.related('roles').attach(roles); + } - session.flash('message', 'User has been created successfully'); - return response.redirect().toRoute('user.index'); - } + session.flash('message', 'User has been created successfully'); + return response.redirect().toRoute('user.index'); + } - public async show({ request, inertia }: HttpContextContract) { - const id = request.param('id'); - const user = await User.query().where('id', id).firstOrFail(); + public async show({ request, inertia }: HttpContextContract) { + const id = request.param('id'); + const user = await User.query().where('id', id).firstOrFail(); - const roles = await Role.query().pluck('name', 'id'); - // const userHasRoles = user.roles; - const userRoles = await user.related('roles').query().orderBy('name').pluck('id'); + const roles = await Role.query().pluck('name', 'id'); + // const userHasRoles = user.roles; + const userRoles = await user.related('roles').query().orderBy('name').pluck('id'); - return inertia.render('Admin/User/Show', { - roles: roles, - user: user, - userHasRoles: userRoles, - }); - } + return inertia.render('Admin/User/Show', { + roles: roles, + user: user, + userHasRoles: userRoles, + }); + } - public async edit({ request, inertia }: HttpContextContract) { - const id = request.param('id'); - const user = await User.query().where('id', id).firstOrFail(); + public async edit({ request, inertia }: HttpContextContract) { + const id = request.param('id'); + const user = await User.query().where('id', id).firstOrFail(); - const roles = await Role.query().pluck('name', 'id'); - // const userHasRoles = user.roles; - const userHasRoles = await user.related('roles').query().orderBy('name').pluck('id'); - // let test = Object.keys(userHasRoles).map((key) => userHasRoles[key]); - return inertia.render('Admin/User/Edit', { - roles: roles, - user: user, - userHasRoles: Object.keys(userHasRoles).map((key) => userHasRoles[key]), //convert object to array with role ids - }); - } + const roles = await Role.query().pluck('name', 'id'); + // const userHasRoles = user.roles; + const userHasRoles = await user.related('roles').query().orderBy('name').pluck('id'); + // let test = Object.keys(userHasRoles).map((key) => userHasRoles[key]); + return inertia.render('Admin/User/Edit', { + roles: roles, + user: user, + userHasRoles: Object.keys(userHasRoles).map((key) => userHasRoles[key]), //convert object to array with role ids + }); + } - public async update({ request, response, session }: HttpContextContract) { - // node ace make:validator UpdateUser - const id = request.param('id'); - const user = await User.query().where('id', id).firstOrFail(); + public async update({ request, response, session }: HttpContextContract) { + // node ace make:validator UpdateUser + const id = request.param('id'); + const user = await User.query().where('id', id).firstOrFail(); - // validate update form - await request.validate(UpdateUserValidator); + // validate update form + await request.validate(UpdateUserValidator); - // password is optional - let input; - if (request.input('password')) { - input = request.only(['login', 'email', 'password']); - } else { - input = request.only(['login', 'email']); - } - await user.merge(input).save(); - // await user.save(); + // password is optional + let input; + if (request.input('password')) { + input = request.only(['login', 'email', 'password']); + } else { + input = request.only(['login', 'email']); + } + await user.merge(input).save(); + // await user.save(); - if (request.input('roles')) { - const roles: Array = request.input('roles'); - await user.related('roles').sync(roles); - } + if (request.input('roles')) { + const roles: Array = request.input('roles'); + await user.related('roles').sync(roles); + } - session.flash('message', 'User has been updated successfully'); - return response.redirect().toRoute('user.index'); - } + session.flash('message', 'User has been updated successfully'); + return response.redirect().toRoute('user.index'); + } - public async destroy({ request, response, session }: HttpContextContract) { - const id = request.param('id'); - const user = await User.findOrFail(id); - await user.delete(); + public async destroy({ request, response, session }: HttpContextContract) { + const id = request.param('id'); + const user = await User.findOrFail(id); + await user.delete(); - session.flash('message', `User ${user.login} has been deleted.`); - return response.redirect().toRoute('user.index'); - } + session.flash('message', `User ${user.login} has been deleted.`); + return response.redirect().toRoute('user.index'); + } - /** - * Show the user a form to change their personal information & password. - * - * @return — \Inertia\Response - */ - public accountInfo({ inertia, auth }: HttpContextContract): RenderResponse { - const user = auth.user; - // const id = request.param('id'); - // const user = await User.query().where('id', id).firstOrFail(); + /** + * Show the user a form to change their personal information & password. + * + * @return — \Inertia\Response + */ + public accountInfo({ inertia, auth }: HttpContextContract): RenderResponse { + const user = auth.user; + // const id = request.param('id'); + // const user = await User.query().where('id', id).firstOrFail(); - return inertia.render('Admin/User/AccountInfo', { - user: user, - }); - } + return inertia.render('Admin/User/AccountInfo', { + user: user, + }); + } - /** - * Save the modified personal information for a user. - * - * @param HttpContextContract ctx - * @return : RedirectContract - */ - public async accountInfoStore({ request, response, auth, session }: HttpContextContract) { - // validate update form - await request.validate(UpdateUserValidator); + /** + * Save the modified personal information for a user. + * + * @param HttpContextContract ctx + * @return : RedirectContract + */ + public async accountInfoStore({ request, response, auth, session }: HttpContextContract) { + // validate update form + await request.validate(UpdateUserValidator); - const payload = request.only(['login', 'email']); - auth.user?.merge(payload); - const user = await auth.user?.save(); - // $user = \Auth::user()->update($request->except(['_token'])); - let message; - if (user) { - message = 'Account updated successfully.'; - } else { - message = 'Error while saving. Please try again.'; - } + const payload = request.only(['login', 'email']); + auth.user?.merge(payload); + const user = await auth.user?.save(); + // $user = \Auth::user()->update($request->except(['_token'])); + let message; + if (user) { + message = 'Account updated successfully.'; + } else { + message = 'Error while saving. Please try again.'; + } - session.flash(message); - return response.redirect().toRoute('admin.account.info'); - //->with('message', __($message)); - } + session.flash(message); + return response.redirect().toRoute('admin.account.info'); + //->with('message', __($message)); + } + + // private async syncRoles(userId: number, roleIds: Array) { + // const user = await User.findOrFail(userId) + // // const roles: Role[] = await Role.query().whereIn('id', roleIds); + + // // await user.roles().sync(roles.rows.map(role => role.id)) + // await user.related("roles").sync(roleIds); + + // return user + // } } diff --git a/app/Controllers/Http/Api/AuthorsController.ts b/app/Controllers/Http/Api/AuthorsController.ts index cd4e389..7a54bfa 100644 --- a/app/Controllers/Http/Api/AuthorsController.ts +++ b/app/Controllers/Http/Api/AuthorsController.ts @@ -25,10 +25,8 @@ export default class AuthorsController { if (request.input('filter')) { // users = users.whereRaw('name like %?%', [request.input('search')]) const searchTerm = request.input('filter'); - authors - .whereILike('first_name', `%${searchTerm}%`) - .orWhereILike('last_name', `%${searchTerm}%`); - // .orWhere('email', 'like', `%${searchTerm}%`); + authors.whereILike('first_name', `%${searchTerm}%`).orWhereILike('last_name', `%${searchTerm}%`); + // .orWhere('email', 'like', `%${searchTerm}%`); } let persons = await authors; diff --git a/app/Controllers/Http/Api/DatasetController.ts b/app/Controllers/Http/Api/DatasetController.ts index d6e3777..f21ed1c 100644 --- a/app/Controllers/Http/Api/DatasetController.ts +++ b/app/Controllers/Http/Api/DatasetController.ts @@ -4,20 +4,12 @@ import Dataset from 'App/Models/Dataset'; // node ace make:controller Author export default class DatasetController { + public async index({}: HttpContextContract) { + // select * from gba.persons + // where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id" + // where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id")); + const datasets = await Dataset.query().where('server_state', 'published').orWhere('server_state', 'deleted'); - public async index({}: HttpContextContract) { - // select * from gba.persons - // where exists (select * from gba.documents inner join gba.link_documents_persons on "documents"."id" = "link_documents_persons"."document_id" - // where ("link_documents_persons"."role" = 'author') and ("persons"."id" = "link_documents_persons"."person_id")); - const datasets = await Dataset - .query() - .where('server_state', 'published') - .orWhere('server_state', 'deleted'); - - - - - return datasets; - } - + return datasets; + } } diff --git a/app/Controllers/Http/Auth/AuthController.ts b/app/Controllers/Http/Auth/AuthController.ts index 03fe266..8d16f3b 100644 --- a/app/Controllers/Http/Auth/AuthController.ts +++ b/app/Controllers/Http/Auth/AuthController.ts @@ -5,38 +5,36 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import AuthValidator from 'App/Validators/AuthValidator'; export default class AuthController { - // login function - public async login({ request, response, auth, session }: HttpContextContract) { - // console.log({ - // registerBody: request.body(), - // }); + // login function + public async login({ request, response, auth, session }: HttpContextContract) { + // console.log({ + // registerBody: request.body(), + // }); await request.validate(AuthValidator); - const plainPassword = await request.input('password'); - const email = await request.input('email'); - // grab uid and password values off request body - // const { email, password } = request.only(['email', 'password']) - - - - try { - // attempt to login - await auth.use("web").attempt(email, plainPassword); - } catch (error) { - // if login fails, return vague form message and redirect back - session.flash('message', 'Your username, email, or password is incorrect') - return response.redirect().back() - } + const plainPassword = await request.input('password'); + const email = await request.input('email'); + // grab uid and password values off request body + // const { email, password } = request.only(['email', 'password']) - // otherwise, redirect todashboard - response.redirect('/dashboard'); - } + try { + // attempt to login + await auth.use('web').attempt(email, plainPassword); + } catch (error) { + // if login fails, return vague form message and redirect back + session.flash('message', 'Your username, email, or password is incorrect'); + return response.redirect().back(); + } - // logout function - public async logout({ auth, response }: HttpContextContract) { - // await auth.logout(); - await auth.use('web').logout(); - response.redirect('/app/login'); - // return response.status(200); - } + // otherwise, redirect todashboard + response.redirect('/dashboard'); + } + + // logout function + public async logout({ auth, response }: HttpContextContract) { + // await auth.logout(); + await auth.use('web').logout(); + response.redirect('/app/login'); + // return response.status(200); + } } diff --git a/app/Controllers/Http/Submitter/DatasetController.ts b/app/Controllers/Http/Submitter/DatasetController.ts index 896183c..6d34006 100644 --- a/app/Controllers/Http/Submitter/DatasetController.ts +++ b/app/Controllers/Http/Submitter/DatasetController.ts @@ -1,33 +1,22 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; -// import User from 'App/Models/User'; +import User from 'App/Models/User'; +import Dataset from 'App/Models/Dataset'; // import Role from 'App/Models/Role'; // import Database from '@ioc:Adonis/Lucid/Database'; import License from 'App/Models/License'; import Project from 'App/Models/Project'; -// import type { ModelQueryBuilderContract } from '@ioc:Adonis/Lucid/Orm'; +import Title from 'App/Models/Title'; +import Description from 'App/Models/Description'; // import CreateUserValidator from 'App/Validators/CreateUserValidator'; // import UpdateUserValidator from 'App/Validators/UpdateUserValidator'; -// import { RenderResponse } from '@ioc:EidelLev/Inertia'; import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator'; import dayjs from 'dayjs'; -// import Application from '@ioc:Adonis/Core/Application'; - -enum TitleTypes { - Main = 'Main', - Sub = 'Sub', - Alternative = 'Alternative', - Translated = 'Translated', - Other = 'Other', -} - -enum DescriptionTypes { - Abstract = 'Abstract', - Methods = 'Methods', - Series_information = 'Series_information', - Technical_info = 'Technical_info', - Translated = 'Translated', - Other = 'Other', -} +import Person from 'App/Models/Person'; +import Database from '@ioc:Adonis/Lucid/Database'; +import { TransactionClientContract } from '@ioc:Adonis/Lucid/Database'; +import Subject from 'App/Models/Subject'; +import CreateDatasetValidator from 'App/Validators/CreateDatasetValidator'; +import { TitleTypes, DescriptionTypes } from 'Contracts/enums'; export default class DatasetController { public async create({ inertia }: HttpContextContract) { @@ -190,7 +179,7 @@ export default class DatasetController { depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]), depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]), }), - subjects: schema.array([rules.minLength(3)]).members( + subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members( schema.object().members({ value: schema.string({ trim: true }, [ rules.minLength(3), @@ -214,98 +203,161 @@ export default class DatasetController { } return response.redirect().back(); } - public async store({ request, response, session }: HttpContextContract) { - const newDatasetSchema = schema.create({ - // first step - language: schema.string({ trim: true }, [ - rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores - ]), - licenses: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one license for the new dataset - rights: schema.string([rules.equalTo('true')]), - // second step - type: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), - creating_corporation: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), - titles: schema.array([rules.minLength(1)]).members( - schema.object().members({ - value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), - type: schema.enum(Object.values(TitleTypes)), - language: schema.string({ trim: true }, [ - rules.minLength(2), - rules.maxLength(255), - rules.translatedLanguage('/language', 'type'), - ]), - }), - ), - descriptions: schema.array([rules.minLength(1)]).members( - schema.object().members({ - value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), - type: schema.enum(Object.values(DescriptionTypes)), - language: schema.string({ trim: true }, [ - rules.minLength(2), - rules.maxLength(255), - rules.translatedLanguage('/language', 'type'), - ]), - }), - ), - authors: schema.array([rules.minLength(1)]).members(schema.object().members({ email: schema.string({ trim: true }) })), - // third step - project_id: schema.number.optional(), - embargo_date: schema.date.optional({ format: 'yyyy-MM-dd' }, [rules.after(10, 'days')]), - coverage: schema.object().members({ - x_min: schema.number(), - x_max: schema.number(), - y_min: schema.number(), - y_max: schema.number(), - elevation_absolut: schema.number.optional(), - elevation_min: schema.number.optional([rules.requiredIfExists('elevation_max')]), - elevation_max: schema.number.optional([rules.requiredIfExists('elevation_min')]), - depth_absolut: schema.number.optional(), - depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]), - depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]), - }), - subjects: schema.array([rules.minLength(3)]).members( - schema.object().members({ - value: schema.string({ trim: true }, [ - rules.minLength(3), - rules.maxLength(255), - // rules.unique({ table: 'dataset_subjects', column: 'value' }), - ]), - // type: schema.enum(Object.values(TitleTypes)), - language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]), - }), - ), - // file: schema.file({ - // size: '100mb', - // extnames: ['jpg', 'gif', 'png'], - // }), - files: schema.array([rules.minLength(1)]).members( - schema.file({ - size: '100mb', - extnames: ['jpg', 'gif', 'png'], - }), - ), - // upload: schema.object().members({ - // label: schema.string({ trim: true }, [rules.maxLength(255)]), - - // // label: schema.string({ trim: true }, [ - // // // rules.minLength(3), - // // // rules.maxLength(255), - // // ]), - // }), - }); - - // const coverImages = request.file('files'); - // node ace make:validator CreateUser + public async store({ auth, request, response, session }: HttpContextContract) { + // node ace make:validator CreateDataset try { // Step 2 - Validate request body against the schema // await request.validate(CreateUserValidator); - await request.validate({ schema: newDatasetSchema, messages: this.messages }); + // await request.validate({ schema: newDatasetSchema, messages: this.messages }); + await request.validate(CreateDatasetValidator); // console.log({ payload }); } catch (error) { // Step 3 - Handle errors // return response.badRequest(error.messages); throw error; } + + // const user = new User(); + // user.email = 'example@example.com'; + // user.password = 'password'; + // await user.useTransaction(trx).save(); + + let trx: TransactionClientContract | null = null; + try { + trx = await Database.transaction(); + const user = (await User.find(auth.user?.id)) as User; + + // const dataset = await user.related('datasets').create({ + // type: request.input('type'), + // creatingCorporation: request.input('creating_corporation'), + // language: request.input('language'), + // }); + + // Create a new instance of the Dataset model: + const dataset = new Dataset(); + dataset.type = request.input('type'); + dataset.creatingCorporation = request.input('creating_corporation'); + dataset.language = request.input('language'); + dataset.embargoDate = request.input('embargo_date'); + //await dataset.related('user').associate(user); // speichert schon ab + // Dataset.$getRelation('user').boot(); + // Dataset.$getRelation('user').setRelated(dataset, user); + // dataset.$setRelated('user', user); + await user.useTransaction(trx).related('datasets').save(dataset); + + //store licenses: + const licenses: number[] = request.input('licenses', []); + dataset.useTransaction(trx).related('licenses').sync(licenses); + + const authors = request.input('authors', []); + for (const [key, person] of authors.entries()) { + const pivotData = { role: 'author', sort_order: key + 1 }; + + if (person.id !== undefined) { + await dataset + .useTransaction(trx) + .related('persons') + .attach({ + [person.id]: { + role: pivotData.role, + sort_order: pivotData.sort_order, + allow_email_contact: false, + }, + }); + } else { + const dataPerson = new Person(); + // use overwritten fill method + dataPerson.fill(person); + await dataset.useTransaction(trx).related('persons').save(dataPerson, false, { + role: pivotData.role, + sort_order: pivotData.sort_order, + allow_email_contact: false, + }); + } + } + + const contributors = request.input('contributors', []); + for (const [key, person] of contributors.entries()) { + const pivotData = { role: 'contributor', sort_order: key + 1 }; + + if (person.id !== undefined) { + await dataset + .useTransaction(trx) + .related('persons') + .attach({ + [person.id]: { + role: pivotData.role, + sort_order: pivotData.sort_order, + allow_email_contact: false, + }, + }); + } else { + const dataPerson = new Person(); + // use overwritten fill method + dataPerson.fill(person); + await dataset.useTransaction(trx).related('persons').save(dataPerson, false, { + role: pivotData.role, + sort_order: pivotData.sort_order, + allow_email_contact: false, + }); + } + } + + //save main and additional titles + const titles = request.input('titles', []); + for (const titleData of titles) { + const title = new Title(); + title.value = titleData.value; + title.language = titleData.language; + title.type = titleData.type; + await dataset.useTransaction(trx).related('titles').save(title); + } + + //save abstract and additional descriptions + const descriptions = request.input('descriptions', []); + for (const descriptionData of descriptions) { + // $descriptionReference = new Description($description); + // $dataset->abstracts()->save($descriptionReference); + const description = new Description(); + description.value = descriptionData.value; + description.language = descriptionData.language; + description.type = descriptionData.type; + await dataset.useTransaction(trx).related('descriptions').save(description); + } + + //save keywords + const keywords = request.input('subjects', []); + for (const keywordData of keywords) { + // $dataKeyword = new Subject($keyword); + // $dataset->subjects()->save($dataKeyword); + const keyword = await Subject.firstOrNew({ value: keywordData.value, type: keywordData.type }, keywordData); + if (keyword.$isNew == true) { + await dataset.useTransaction(trx).related('subjects').save(keyword); + } else { + await dataset.useTransaction(trx).related('subjects').attach([keyword.id]); + } + } + + // Dataset.$getRelation('persons').boot(); + // Dataset.$getRelation('persons').pushRelated(dataset, person) + // dataset.$pushRelated('persons', person); + + await trx.commit(); + + console.log('Users and posts created successfully'); + } catch (error) { + if (trx !== null) { + await trx.rollback(); + } + console.error('Failed to create dataset and related models:', error); + + // Handle the error and exit the controller code accordingly + // session.flash('message', 'Failed to create dataset and relaed models'); + // return response.redirect().back(); + throw error; + } + + // save data files: const coverImage = request.files('files')[0]; if (coverImage) { // clientName: 'Gehaltsschema.png' @@ -326,11 +378,6 @@ export default class DatasetController { ); // let path = coverImage.filePath; } - // const user = await User.create(input); - // if (request.input('roles')) { - // const roles: Array = request.input('roles'); - // await user.related('roles').attach(roles); - // } session.flash('message', 'Dataset has been created successfully'); // return response.redirect().toRoute('user.index'); @@ -367,6 +414,7 @@ export default class DatasetController { 'after': `{{ field }} must be older than ${dayjs().add(10, 'day')}`, 'subjects.minLength': 'at least {{ options.minLength }} keywords must be defined', + 'subjects.uniqueArray': 'The {{ options.array }} array must have unique values based on the {{ options.field }} attribute.', 'subjects.*.value.required': 'keyword value is required', 'subjects.*.value.minLength': 'keyword value must be at least {{ options.minLength }} characters long', 'subjects.*.type.required': 'keyword type is required', diff --git a/app/Exceptions/Handler.ts b/app/Exceptions/Handler.ts index 8598c12..bacdbb1 100644 --- a/app/Exceptions/Handler.ts +++ b/app/Exceptions/Handler.ts @@ -18,44 +18,44 @@ import Logger from '@ioc:Adonis/Core/Logger'; import HttpExceptionHandler from '@ioc:Adonis/Core/HttpExceptionHandler'; export default class ExceptionHandler extends HttpExceptionHandler { - protected statusPages = { - '401,403': 'errors/unauthorized', - '404': 'errors/not-found', - '500..599': 'errors/server-error', - }; + protected statusPages = { + '401,403': 'errors/unauthorized', + '404': 'errors/not-found', + '500..599': 'errors/server-error', + }; - constructor() { - super(Logger); - } + constructor() { + super(Logger); + } - public async handle(error: any, ctx: HttpContextContract) { - const { response, request, inertia } = ctx; + public async handle(error: any, ctx: HttpContextContract) { + const { response, request, inertia } = ctx; - /** - * Handle failed authentication attempt - */ - // if (['E_INVALID_AUTH_PASSWORD', 'E_INVALID_AUTH_UID'].includes(error.code)) { - // session.flash('errors', { login: error.message }); - // return response.redirect('/login'); - // } - // if ([401].includes(error.status)) { - // session.flash('errors', { login: error.message }); - // return response.redirect('/dashboard'); - // } + /** + * Handle failed authentication attempt + */ + // if (['E_INVALID_AUTH_PASSWORD', 'E_INVALID_AUTH_UID'].includes(error.code)) { + // session.flash('errors', { login: error.message }); + // return response.redirect('/login'); + // } + // if ([401].includes(error.status)) { + // session.flash('errors', { login: error.message }); + // return response.redirect('/dashboard'); + // } - // https://github.com/inertiajs/inertia-laravel/issues/56 - if (request.header('X-Inertia') && [500, 503, 404, 403, 401].includes(response.getStatus())) { - return inertia.render('Error', { - status: response.getStatus(), - message: error.message - }); - // ->toResponse($request) - // ->setStatusCode($response->status()); - } + // https://github.com/inertiajs/inertia-laravel/issues/56 + if (request.header('X-Inertia') && [500, 503, 404, 403, 401].includes(response.getStatus())) { + return inertia.render('Error', { + status: response.getStatus(), + message: error.message, + }); + // ->toResponse($request) + // ->setStatusCode($response->status()); + } - /** - * Forward rest of the exceptions to the parent class - */ - return super.handle(error, ctx); - } + /** + * Forward rest of the exceptions to the parent class + */ + return super.handle(error, ctx); + } } diff --git a/app/Exceptions/InvalidCredentialException.ts b/app/Exceptions/InvalidCredentialException.ts index c9b69f3..83c802f 100644 --- a/app/Exceptions/InvalidCredentialException.ts +++ b/app/Exceptions/InvalidCredentialException.ts @@ -1,4 +1,4 @@ -import { Exception } from "@adonisjs/core/build/standalone"; +import { Exception } from '@adonisjs/core/build/standalone'; /* |-------------------------------------------------------------------------- @@ -23,14 +23,14 @@ export default class InvalidCredentialException extends Exception { * Unable to find user */ static invalidUid() { - const error = new this("User not found", 400, "E_INVALID_AUTH_UID"); + const error = new this('User not found', 400, 'E_INVALID_AUTH_UID'); return error; } /** * Invalid user password */ static invalidPassword() { - const error = new this("Password mis-match", 400, "E_INVALID_AUTH_PASSWORD"); + const error = new this('Password mis-match', 400, 'E_INVALID_AUTH_PASSWORD'); return error; } @@ -41,18 +41,18 @@ export default class InvalidCredentialException extends Exception { // if (!ctx.session) { // return ctx.response.status(this.status).send(this.responseText); // } - ctx.session.flashExcept(["_csrf"]); - ctx.session.flash("auth", { + ctx.session.flashExcept(['_csrf']); + ctx.session.flash('auth', { error: error, /** * Will be removed in the future */ errors: { - uid: this.code === "E_INVALID_AUTH_UID" ? ["Invalid login id"] : null, - password: this.code === "E_INVALID_AUTH_PASSWORD" ? ["Invalid password"] : null, + uid: this.code === 'E_INVALID_AUTH_UID' ? ['Invalid login id'] : null, + password: this.code === 'E_INVALID_AUTH_PASSWORD' ? ['Invalid password'] : null, }, }); - ctx.response.redirect("back", true); + ctx.response.redirect('back', true); } /** diff --git a/app/Middleware/Auth.ts b/app/Middleware/Auth.ts index 231df40..f80edd6 100644 --- a/app/Middleware/Auth.ts +++ b/app/Middleware/Auth.ts @@ -1,6 +1,6 @@ -import { AuthenticationException } from '@adonisjs/auth/build/standalone' -import type { GuardsList } from '@ioc:Adonis/Addons/Auth' -import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import { AuthenticationException } from '@adonisjs/auth/build/standalone'; +import type { GuardsList } from '@ioc:Adonis/Addons/Auth'; +import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; /** * Auth middleware is meant to restrict un-authenticated access to a given route @@ -10,67 +10,58 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' * of named middleware. */ export default class AuthMiddleware { - /** - * The URL to redirect to when request is Unauthorized - */ - protected redirectTo = '/app/login' - - /** - * Authenticates the current HTTP request against a custom set of defined - * guards. - * - * The authentication loop stops as soon as the user is authenticated using any - * of the mentioned guards and that guard will be used by the rest of the code - * during the current request. - */ - protected async authenticate(auth: HttpContextContract['auth'], guards: (keyof GuardsList)[]) { /** - * Hold reference to the guard last attempted within the for loop. We pass - * the reference of the guard to the "AuthenticationException", so that - * it can decide the correct response behavior based upon the guard - * driver + * The URL to redirect to when request is Unauthorized */ - let guardLastAttempted: string | undefined + protected redirectTo = '/app/login'; - for (let guard of guards) { - guardLastAttempted = guard - - if (await auth.use(guard).check()) { + /** + * Authenticates the current HTTP request against a custom set of defined + * guards. + * + * The authentication loop stops as soon as the user is authenticated using any + * of the mentioned guards and that guard will be used by the rest of the code + * during the current request. + */ + protected async authenticate(auth: HttpContextContract['auth'], guards: (keyof GuardsList)[]) { /** - * Instruct auth to use the given guard as the default guard for - * the rest of the request, since the user authenticated - * succeeded here + * Hold reference to the guard last attempted within the for loop. We pass + * the reference of the guard to the "AuthenticationException", so that + * it can decide the correct response behavior based upon the guard + * driver */ - auth.defaultGuard = guard - return true - } + let guardLastAttempted: string | undefined; + + for (let guard of guards) { + guardLastAttempted = guard; + + if (await auth.use(guard).check()) { + /** + * Instruct auth to use the given guard as the default guard for + * the rest of the request, since the user authenticated + * succeeded here + */ + auth.defaultGuard = guard; + return true; + } + } + + /** + * Unable to authenticate using any guard + */ + throw new AuthenticationException('Unauthorized access', 'E_UNAUTHORIZED_ACCESS', guardLastAttempted, this.redirectTo); } /** - * Unable to authenticate using any guard + * Handle request */ - throw new AuthenticationException( - 'Unauthorized access', - 'E_UNAUTHORIZED_ACCESS', - guardLastAttempted, - this.redirectTo, - ) - } - - /** - * Handle request - */ - public async handle ( - { auth }: HttpContextContract, - next: () => Promise, - customGuards: (keyof GuardsList)[] - ) { - /** - * Uses the user defined guards or the default guard mentioned in - * the config file - */ - const guards = customGuards.length ? customGuards : [auth.name] - await this.authenticate(auth, guards) - await next() - } + public async handle({ auth }: HttpContextContract, next: () => Promise, customGuards: (keyof GuardsList)[]) { + /** + * Uses the user defined guards or the default guard mentioned in + * the config file + */ + const guards = customGuards.length ? customGuards : [auth.name]; + await this.authenticate(auth, guards); + await next(); + } } diff --git a/app/Middleware/Can.ts b/app/Middleware/Can.ts index bf6ef28..22c2f80 100644 --- a/app/Middleware/Can.ts +++ b/app/Middleware/Can.ts @@ -15,71 +15,74 @@ const userRoleTable = Config.get('rolePermission.user_role_table', 'link_account * Should be called after auth middleware */ export default class Can { - /** - * Handle request - */ - public async handle( - { auth, response }: HttpContextContract, - next: () => Promise, - permissionNames: string[] - ) { - /** - * Check if user is logged-in - */ - let user = await auth.user; - if (!user) { - return response.unauthorized({ error: 'Must be logged in' }); - } - let hasPermission = await this.checkHasPermissions(user, permissionNames); - if (!hasPermission) { - // return response.unauthorized({ - // error: `Doesn't have required role(s): ${permissionNames.join(',')}`, - // }); - throw new Exception(`Doesn't have required permission(s): ${permissionNames.join(',')}`, 401); - } - await next(); - } + /** + * Handle request + */ + public async handle({ auth, response }: HttpContextContract, next: () => Promise, permissionNames: string[]) { + /** + * Check if user is logged-in + */ + let user = await auth.user; + if (!user) { + return response.unauthorized({ error: 'Must be logged in' }); + } + let hasPermission = await this.checkHasPermissions(user, permissionNames); + if (!hasPermission) { + // return response.unauthorized({ + // error: `Doesn't have required role(s): ${permissionNames.join(',')}`, + // }); + throw new Exception(`Doesn't have required permission(s): ${permissionNames.join(',')}`, 401); + } + await next(); + } - private async checkHasPermissions(user: User, permissionNames: Array): Promise { - let rolePlaceHolder = '('; - let placeholders = new Array(permissionNames.length).fill('?'); - rolePlaceHolder += placeholders.join(','); - rolePlaceHolder += ')'; + private async checkHasPermissions(user: User, permissionNames: Array): Promise { + let rolePlaceHolder = '('; + let placeholders = new Array(permissionNames.length).fill('?'); + rolePlaceHolder += placeholders.join(','); + rolePlaceHolder += ')'; + + // let test = user + // .related('roles') + // .query() + // .count('permissions.name') + // .innerJoin('gba.role_has_permissions', function () { + // this.on('gba.role_has_permissions.role_id', 'roles.id'); + // }) + // .innerJoin('gba.permissions', function () { + // this.on('role_has_permissions.permission_id', 'permissions.id'); + // }) + // .andWhereIn('permissions.name', permissionNames); - // let test = user - // .related('roles') - // .query() - // .count('permissions.name') - // .innerJoin('gba.role_has_permissions', function () { - // this.on('gba.role_has_permissions.role_id', 'roles.id'); - // }) - // .innerJoin('gba.permissions', function () { - // this.on('role_has_permissions.permission_id', 'permissions.id'); - // }) - // .andWhereIn('permissions.name', permissionNames); - // select "permissions"."name" - // from "gba"."roles" - // inner join "gba"."link_accounts_roles" on "roles"."id" = "link_accounts_roles"."role_id" - // inner join "gba"."role_has_permissions" on "gba"."role_has_permissions"."role_id" = "roles"."id" - // inner join "gba"."permissions" on "role_has_permissions"."permission_id" = "permissions"."id" - // where ("permissions"."name" in ('dataset-list', 'dataset-publish')) + // from "gba"."roles" + // inner join "gba"."link_accounts_roles" on "roles"."id" = "link_accounts_roles"."role_id" + // inner join "gba"."role_has_permissions" on "gba"."role_has_permissions"."role_id" = "roles"."id" + // inner join "gba"."permissions" on "role_has_permissions"."permission_id" = "permissions"."id" + // where ("permissions"."name" in ('dataset-list', 'dataset-publish')) // and ("link_accounts_roles"."account_id" = 1) - let { - rows: { - 0: { permissioncount }, - }, - } = await Database.rawQuery( - 'SELECT count("p"."name") as permissionCount FROM ' + roleTable + - ' r INNER JOIN ' + userRoleTable + ' ur ON ur.role_id=r.id AND "ur"."account_id"=? ' + - ' INNER JOIN ' + rolePermissionTable + ' rp ON rp.role_id=r.id ' + - ' INNER JOIN ' + permissionTable + ' p ON rp.permission_id=p.id AND "p"."name" in ' + - rolePlaceHolder + - ' LIMIT 1', - [user.id, ...permissionNames] - ); + let { + rows: { + 0: { permissioncount }, + }, + } = await Database.rawQuery( + 'SELECT count("p"."name") as permissionCount FROM ' + + roleTable + + ' r INNER JOIN ' + + userRoleTable + + ' ur ON ur.role_id=r.id AND "ur"."account_id"=? ' + + ' INNER JOIN ' + + rolePermissionTable + + ' rp ON rp.role_id=r.id ' + + ' INNER JOIN ' + + permissionTable + + ' p ON rp.permission_id=p.id AND "p"."name" in ' + + rolePlaceHolder + + ' LIMIT 1', + [user.id, ...permissionNames], + ); - return permissioncount > 0; - } + return permissioncount > 0; + } } diff --git a/app/Middleware/Is.ts b/app/Middleware/Is.ts index 81db1ab..64c4d98 100644 --- a/app/Middleware/Is.ts +++ b/app/Middleware/Is.ts @@ -1,11 +1,11 @@ -import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' -import Config from '@ioc:Adonis/Core/Config' -import Database from '@ioc:Adonis/Lucid/Database' -import User from 'App/Models/User' +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; +import Config from '@ioc:Adonis/Core/Config'; +import Database from '@ioc:Adonis/Lucid/Database'; +import User from 'App/Models/User'; // import { Exception } from '@adonisjs/core/build/standalone' -const roleTable = Config.get('rolePermission.role_table', 'roles') -const userRoleTable = Config.get('rolePermission.user_role_table', 'user_roles') +const roleTable = Config.get('rolePermission.role_table', 'roles'); +const userRoleTable = Config.get('rolePermission.user_role_table', 'user_roles'); /** * Role authentication to check if user has any of the specified roles @@ -13,54 +13,50 @@ const userRoleTable = Config.get('rolePermission.user_role_table', 'user_roles') * Should be called after auth middleware */ export default class Is { - /** - * Handle request - */ - public async handle( - { auth, response }: HttpContextContract, - next: () => Promise, - roleNames: string[] - ) { /** - * Check if user is logged-in or not. + * Handle request */ - let user = await auth.user - if (!user) { - return response.unauthorized({ error: 'Must be logged in' }) + public async handle({ auth, response }: HttpContextContract, next: () => Promise, roleNames: string[]) { + /** + * Check if user is logged-in or not. + */ + let user = await auth.user; + if (!user) { + return response.unauthorized({ error: 'Must be logged in' }); + } + let hasRole = await this.checkHasRoles(user, roleNames); + if (!hasRole) { + return response.unauthorized({ + error: `Doesn't have required role(s): ${roleNames.join(',')}`, + }); + // return new Exception(`Doesn't have required role(s): ${roleNames.join(',')}`, + // 401, + // "E_INVALID_AUTH_UID"); + } + await next(); } - let hasRole = await this.checkHasRoles(user, roleNames) - if (!hasRole) { - return response.unauthorized({ - error: `Doesn't have required role(s): ${roleNames.join(',')}`, - }) - // return new Exception(`Doesn't have required role(s): ${roleNames.join(',')}`, - // 401, - // "E_INVALID_AUTH_UID"); + + private async checkHasRoles(user: User, roleNames: Array): Promise { + let rolePlaceHolder = '('; + let placeholders = new Array(roleNames.length).fill('?'); + rolePlaceHolder += placeholders.join(','); + rolePlaceHolder += ')'; + + let { + 0: { + 0: { roleCount }, + }, + } = await Database.rawQuery( + 'SELECT count(`ur`.`id`) as roleCount FROM ' + + userRoleTable + + ' ur INNER JOIN ' + + roleTable + + ' r ON ur.role_id=r.id WHERE `ur`.`user_id`=? AND `r`.`name` in ' + + rolePlaceHolder + + ' LIMIT 1', + [user.id, ...roleNames], + ); + + return roleCount > 0; } - await next() - } - - private async checkHasRoles(user: User, roleNames: Array): Promise { - let rolePlaceHolder = '(' - let placeholders = new Array(roleNames.length).fill('?') - rolePlaceHolder += placeholders.join(',') - rolePlaceHolder += ')' - - let { - 0: { - 0: { roleCount }, - }, - } = await Database.rawQuery( - 'SELECT count(`ur`.`id`) as roleCount FROM ' + - userRoleTable + - ' ur INNER JOIN ' + - roleTable + - ' r ON ur.role_id=r.id WHERE `ur`.`user_id`=? AND `r`.`name` in ' + - rolePlaceHolder + - ' LIMIT 1', - [user.id, ...roleNames] - ) - - return roleCount > 0 - } } diff --git a/app/Middleware/Role.ts b/app/Middleware/Role.ts index a99d8f1..7527ce4 100644 --- a/app/Middleware/Role.ts +++ b/app/Middleware/Role.ts @@ -7,77 +7,72 @@ import { Exception } from '@adonisjs/core/build/standalone'; const roleTable = Config.get('rolePermission.role_table', 'roles'); const userRoleTable = Config.get('rolePermission.user_role_table', 'link_accounts_roles'); - // node ace make:middleware role export default class Role { - // .middleware(['auth', 'role:admin,moderator']) - public async handle( - { auth, response }: HttpContextContract, - next: () => Promise, - userRoles: string[] - ) { - // Check if user is logged-in or not. - // let expression = ""; - // if (Array.isArray(args)) { - // expression = args.join(" || "); - // } + // .middleware(['auth', 'role:admin,moderator']) + public async handle({ auth, response }: HttpContextContract, next: () => Promise, userRoles: string[]) { + // Check if user is logged-in or not. + // let expression = ""; + // if (Array.isArray(args)) { + // expression = args.join(" || "); + // } - let user = await auth.user; - if (!user) { - return response.unauthorized({ error: 'Must be logged in' }); - } + let user = await auth.user; + if (!user) { + return response.unauthorized({ error: 'Must be logged in' }); + } - let hasRole = await this.checkHasRoles(user, userRoles); - if (!hasRole) { - // return response.unauthorized({ - // error: `Doesn't have required role(s): ${userRoles.join(',')}`, - // // error: `Doesn't have required role(s)`, - // }); - throw new Exception(`Doesn't have required role(s): ${userRoles.join(',')}`, 401); - } + let hasRole = await this.checkHasRoles(user, userRoles); + if (!hasRole) { + // return response.unauthorized({ + // error: `Doesn't have required role(s): ${userRoles.join(',')}`, + // // error: `Doesn't have required role(s)`, + // }); + throw new Exception(`Doesn't have required role(s): ${userRoles.join(',')}`, 401); + } - // code for middleware goes here. ABOVE THE NEXT CALL - await next(); - } + // code for middleware goes here. ABOVE THE NEXT CALL + await next(); + } - private async checkHasRoles(user: User, userRoles: string[]): Promise { - // await user.load("roles"); - // const ok = user.roles.map((role) => role.name); - // const roles = await user.getRoles(); + private async checkHasRoles(user: User, userRoles: string[]): Promise { + // await user.load("roles"); + // const ok = user.roles.map((role) => role.name); + // const roles = await user.getRoles(); - let rolePlaceHolder = '('; - let placeholders = new Array(userRoles.length).fill('?'); - rolePlaceHolder += placeholders.join(','); - rolePlaceHolder += ')'; + let rolePlaceHolder = '('; + let placeholders = new Array(userRoles.length).fill('?'); + rolePlaceHolder += placeholders.join(','); + rolePlaceHolder += ')'; - // const roles = await user - // .related('roles') - // .query() - // .count('*') // .select('name') - // .whereIn('name', userRoles); - // // .groupBy('name'); + // const roles = await user + // .related('roles') + // .query() + // .count('*') // .select('name') + // .whereIn('name', userRoles); + // // .groupBy('name'); - // select count(*) as roleCount - // from gba.roles - // inner join gba.link_accounts_roles - // on "roles"."id" = "link_accounts_roles"."role_id" - // where ("name" in ('administrator', 'editor')) and ("link_accounts_roles"."account_id" = 1) + // select count(*) as roleCount + // from gba.roles + // inner join gba.link_accounts_roles + // on "roles"."id" = "link_accounts_roles"."role_id" + // where ("name" in ('administrator', 'editor')) and ("link_accounts_roles"."account_id" = 1) - let { - rows: { - 0: { rolecount }, - }, - } = await Database.rawQuery( - 'SELECT count("r"."id") as roleCount FROM ' + - roleTable + - ' r INNER JOIN ' + - userRoleTable + - ' ur ON r.id=ur.role_id WHERE "ur"."account_id"=? AND "r"."name" in ' + - rolePlaceHolder + - ' LIMIT 1', - [user.id, ...userRoles] - ); + let { + rows: { + 0: { rolecount }, + }, + } = await Database.rawQuery( + 'SELECT count("r"."id") as roleCount FROM ' + + roleTable + + ' r INNER JOIN ' + + userRoleTable + + ' ur ON r.id=ur.role_id WHERE "ur"."account_id"=? AND "r"."name" in ' + + rolePlaceHolder + + ' LIMIT 1', + [user.id, ...userRoles], + ); - return rolecount > 0; - } + return rolecount > 0; + } } diff --git a/app/Middleware/SilentAuth.ts b/app/Middleware/SilentAuth.ts index 5d3ac8f..4b3d16f 100644 --- a/app/Middleware/SilentAuth.ts +++ b/app/Middleware/SilentAuth.ts @@ -1,4 +1,4 @@ -import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; /** * Silent auth middleware can be used as a global middleware to silent check @@ -7,15 +7,15 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' * The request continues as usual, even when the user is not logged-in. */ export default class SilentAuthMiddleware { - /** - * Handle request - */ - public async handle({ auth }: HttpContextContract, next: () => Promise) { /** - * Check if user is logged-in or not. If yes, then `ctx.auth.user` will be - * set to the instance of the currently logged in user. + * Handle request */ - await auth.check() - await next() - } + public async handle({ auth }: HttpContextContract, next: () => Promise) { + /** + * Check if user is logged-in or not. If yes, then `ctx.auth.user` will be + * set to the instance of the currently logged in user. + */ + await auth.check(); + await next(); + } } diff --git a/app/Models/BaseModel.ts b/app/Models/BaseModel.ts new file mode 100644 index 0000000..9c67d28 --- /dev/null +++ b/app/Models/BaseModel.ts @@ -0,0 +1,119 @@ +import { BaseModel as LucidBaseModel } from '@ioc:Adonis/Lucid/Orm'; +// import { ManyToManyQueryClient } from '@ioc:Adonis/Lucid/Orm'; + +// export class CustomManyToManyQueryClient extends ManyToManyQueryClient { +// public attach( +// relatedIds: any | any[], +// pivotAttributes: any = {}, +// trx?: ReturnType +// ) { +// return super.attach(relatedIds, (row) => { +// row.pivot.fill(pivotAttributes); +// }, trx); +// } +// } + +/** + * Helper to find if value is a valid Object or + * not + */ +export function isObject(value: any): boolean { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + +export default class BaseModel extends LucidBaseModel { + /** + * When `fill` method is called, then we may have a situation where it + * removed the values which exists in `original` and hence the dirty + * diff has to do a negative diff as well + */ + // private fillInvoked: boolean = false; + + public static fillable: string[] = []; + + public fill(attributes: any, allowExtraProperties: boolean = false): this { + this.$attributes = {}; + // const Model = this.constructor as typeof BaseModel; + + // for (const key in attributes) { + // if (Model.fillable.includes(key)) { + // const value = attributes[key]; + // if (Model.$hasColumn(key)) { + // this[key] = value; + // } + // } + // } + this.mergeFillableAttributes(attributes, allowExtraProperties); + // this.fillInvoked = true; + return this; + } + + /** + * Merge bulk attributes with existing attributes. + * + * 1. If key is unknown, it will be added to the `extras` object. + * 2. If key is defined as a relationship, it will be ignored and one must call `$setRelated`. + */ + public mergeFillableAttributes(values: any, allowExtraProperties: boolean = false): this { + const Model = this.constructor as typeof BaseModel; + + /** + * Merge values with the attributes + */ + if (isObject(values)) { + // Object.keys(values).forEach((key) => { + for (const key in values) { + if (Model.fillable.includes(key)) { + const value = values[key]; + + /** + * Set as column + */ + if (Model.$hasColumn(key)) { + this[key] = value; + continue; + } + + /** + * Resolve the attribute name from the column names. Since people + * usaully define the column names directly as well by + * accepting them directly from the API. + */ + const attributeName = Model.$keys.columnsToAttributes.get(key); + if (attributeName) { + this[attributeName] = value; + continue; + } + + /** + * If key is defined as a relation, then ignore it, since one + * must pass a qualified model to `this.$setRelated()` + */ + if (Model.$relationsDefinitions.has(key)) { + continue; + } + + /** + * If the property already exists on the model, then set it + * as it is vs defining it as an extra property + */ + if (this.hasOwnProperty(key)) { + this[key] = value; + continue; + } + + /** + * Raise error when not instructed to ignore non-existing properties. + */ + if (!allowExtraProperties) { + throw new Error(`Cannot define "${key}" on "${Model.name}" model, since it is not defined as a model property`); + } + + this.$extras[key] = value; + } + } + } + + return this; + } +} diff --git a/app/Models/Dataset.ts b/app/Models/Dataset.ts index 2337bfe..6c29345 100644 --- a/app/Models/Dataset.ts +++ b/app/Models/Dataset.ts @@ -1,52 +1,108 @@ import { - column, - BaseModel, - SnakeCaseNamingStrategy, - // computed, - manyToMany, - ManyToMany, + column, + BaseModel, + SnakeCaseNamingStrategy, + manyToMany, + ManyToMany, + belongsTo, + BelongsTo, + hasMany, + HasMany, } from '@ioc:Adonis/Lucid/Orm'; import { DateTime } from 'luxon'; import Person from './Person'; - +import User from './User'; +import Title from './Title'; +import Description from './Description'; +import License from './License'; +import Subject from './Subject'; export default class Dataset extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy(); - public static primaryKey = 'id'; - public static table = 'documents'; - public static selfAssignPrimaryKey = false; + public static namingStrategy = new SnakeCaseNamingStrategy(); + public static primaryKey = 'id'; + public static table = 'documents'; + public static selfAssignPrimaryKey = false; - @column({ isPrimary: true }) - public id: number; + @column({ isPrimary: true }) + public id: number; @column({}) - public server_state: boolean; + public server_state: boolean; @column({}) - public publisherName: string; + public publisherName: string; + + @column({ columnName: 'creating_corporation' }) + public creatingCorporation: string; @column.dateTime({ columnName: 'embargo_date' }) - public embargoDate: DateTime; + public embargoDate: DateTime; @column({}) - public type: string; + public type: string; + @column({}) + public language: string; - @column.dateTime({ columnName: 'server_date_published' }) - public serverDatePublished: DateTime; + @column({}) + public account_id: number | null = null; - @column.dateTime({ autoCreate: true, columnName: 'created_at' }) - public createdAt: DateTime; + @column.dateTime({ columnName: 'server_date_published' }) + public serverDatePublished: DateTime; - @column.dateTime({ autoCreate: true, autoUpdate: true }) - public updatedAt: DateTime; + @column.dateTime({ autoCreate: true, columnName: 'created_at' }) + public createdAt: DateTime; + + @column.dateTime({ autoCreate: true, autoUpdate: true, columnName: 'server_date_modified' }) + public updatedAt: DateTime; @manyToMany(() => Person, { - pivotForeignKey: 'document_id', - pivotRelatedForeignKey: 'person_id', - pivotTable: 'link_documents_persons', - pivotColumns: ['role', 'sort_order', 'allow_email_contact'] - }) - public persons: ManyToMany; + pivotForeignKey: 'document_id', + pivotRelatedForeignKey: 'person_id', + pivotTable: 'link_documents_persons', + pivotColumns: ['role', 'sort_order', 'allow_email_contact'], + }) + public persons: ManyToMany; + /** + * Get the account that the dataset belongs to + */ + @belongsTo(() => User, { + foreignKey: 'account_id', + }) + public user: BelongsTo; + + @hasMany(() => Title, { + foreignKey: 'document_id', + }) + public titles: HasMany; + + @hasMany(() => Description, { + foreignKey: 'document_id', + }) + public descriptions: HasMany; + + @manyToMany(() => License, { + pivotForeignKey: 'document_id', + pivotRelatedForeignKey: 'licence_id', + pivotTable: 'link_documents_licences', + }) + public licenses: ManyToMany; + + // public function subjects() + // { + // return $this->belongsToMany(\App\Models\Subject::class, 'link_dataset_subjects', 'document_id', 'subject_id'); + // } + @manyToMany(() => Subject, { + pivotForeignKey: 'document_id', + pivotRelatedForeignKey: 'subject_id', + pivotTable: 'link_dataset_subjects', + }) + public subjects: ManyToMany; + + // async save(): Promise { + // // Call the parent save method to persist changes to the database + // await super.save(); + // return this; + // } } diff --git a/app/Models/Description.ts b/app/Models/Description.ts new file mode 100644 index 0000000..a5d53b6 --- /dev/null +++ b/app/Models/Description.ts @@ -0,0 +1,28 @@ +import { column, belongsTo, BelongsTo } from '@ioc:Adonis/Lucid/Orm'; +import Dataset from './Dataset'; +import BaseModel from './BaseModel'; + +export default class Description extends BaseModel { + public static primaryKey = 'id'; + public static table = 'dataset_abstracts'; + public static selfAssignPrimaryKey = false; + public static timestamps = false; + public static fillable: string[] = ['value', 'type', 'language']; + + @column({}) + public document_id: number; + + @column() + public type: string; + + @column() + public value: string; + + @column() + public language: string; + + @belongsTo(() => Dataset, { + foreignKey: 'document_id', + }) + public dataset: BelongsTo; +} diff --git a/app/Models/File.ts b/app/Models/File.ts index 7918961..0d77f1a 100644 --- a/app/Models/File.ts +++ b/app/Models/File.ts @@ -1,54 +1,55 @@ import { DateTime } from 'luxon'; import { - column, - BaseModel, - hasMany, HasMany, - // manyToMany, - // ManyToMany, - SnakeCaseNamingStrategy, + column, + BaseModel, + hasMany, + HasMany, + // manyToMany, + // ManyToMany, + SnakeCaseNamingStrategy, } from '@ioc:Adonis/Lucid/Orm'; import HashValue from './HashValue'; export default class File extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy(); - public static primaryKey = 'id'; - public static table = 'document_files'; - public static selfAssignPrimaryKey = false; + public static namingStrategy = new SnakeCaseNamingStrategy(); + public static primaryKey = 'id'; + public static table = 'document_files'; + public static selfAssignPrimaryKey = false; - @column({ - isPrimary: true, - }) - public id: number; + @column({ + isPrimary: true, + }) + public id: number; - @column({}) - public pathName: string; - - @column() - public label: string; + @column({}) + public pathName: string; @column() - public comment: string; + public label: string; @column() - public mimetype: string; + public comment: string; @column() - public language: string; + public mimetype: string; @column() - public fileSize: bigint; + public language: string; @column() - public visibleInOai: boolean; + public fileSize: bigint; @column() - public sortOrder: number; + public visibleInOai: boolean; + + @column() + public sortOrder: number; @column.dateTime({ autoCreate: true }) - public createdAt: DateTime; + public createdAt: DateTime; - @column.dateTime({ autoCreate: true, autoUpdate: true }) - public updatedAt: DateTime; + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime; // public function hashvalues() // { @@ -58,5 +59,5 @@ export default class File extends BaseModel { @hasMany(() => HashValue, { foreignKey: 'file_id', }) - public hashvalues: HasMany; -} \ No newline at end of file + public hashvalues: HasMany; +} diff --git a/app/Models/HashValue.ts b/app/Models/HashValue.ts index 386e8f1..2c74670 100644 --- a/app/Models/HashValue.ts +++ b/app/Models/HashValue.ts @@ -1,41 +1,34 @@ -import { - column, - BaseModel, - belongsTo, - BelongsTo, - SnakeCaseNamingStrategy, -} from '@ioc:Adonis/Lucid/Orm'; +import { column, BaseModel, belongsTo, BelongsTo, SnakeCaseNamingStrategy } from '@ioc:Adonis/Lucid/Orm'; import File from './File'; export default class HashValue extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy(); - public static primaryKey = 'file_id, type'; - public static table = 'file_hashvalues'; - + public static namingStrategy = new SnakeCaseNamingStrategy(); + public static primaryKey = 'file_id, type'; + public static table = 'file_hashvalues'; + // static get primaryKey () { // return 'type, value' // } - static get incrementing () { - return false - } + static get incrementing() { + return false; + } - // @column({ - // isPrimary: true, - // }) - // public id: number; - - // Foreign key is still on the same model + // @column({ + // isPrimary: true, + // }) + // public id: number; + + // Foreign key is still on the same model @column({}) - public file_id: number; + public file_id: number; @column({}) - public type: string; + public type: string; - @column() - public value: string; + @column() + public value: string; @belongsTo(() => File) - public file: BelongsTo - -} \ No newline at end of file + public file: BelongsTo; +} diff --git a/app/Models/License.ts b/app/Models/License.ts index 23dfa0c..9de47c2 100644 --- a/app/Models/License.ts +++ b/app/Models/License.ts @@ -5,6 +5,22 @@ export default class License extends BaseModel { public static primaryKey = 'id'; public static table = 'document_licences'; public static selfAssignPrimaryKey = false; + public static timestamps = false; + public static fillable: string[] = [ + 'name_long', + 'name', + 'language', + 'link_licence', + 'link_logo', + 'desc_text', + 'desc_markup', + 'comment_internal', + 'mime_type', + 'sort_order', + 'language', + 'active', + 'pod_allowed', + ]; @column({ isPrimary: true, diff --git a/app/Models/Permission.ts b/app/Models/Permission.ts index ed1cfcf..4a44178 100644 --- a/app/Models/Permission.ts +++ b/app/Models/Permission.ts @@ -1,100 +1,92 @@ -import { - column, - BaseModel, - manyToMany, - ManyToMany, - SnakeCaseNamingStrategy, - beforeUpdate, - beforeCreate, -} from '@ioc:Adonis/Lucid/Orm'; +import { column, BaseModel, manyToMany, ManyToMany, SnakeCaseNamingStrategy, beforeUpdate, beforeCreate } from '@ioc:Adonis/Lucid/Orm'; import { DateTime } from 'luxon'; import dayjs from 'dayjs'; import Role from 'App/Models/Role'; export default class Permission extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy(); - public static primaryKey = 'id'; - public static table = 'permissions'; - public static selfAssignPrimaryKey = false; + public static namingStrategy = new SnakeCaseNamingStrategy(); + public static primaryKey = 'id'; + public static table = 'permissions'; + public static selfAssignPrimaryKey = false; - @column({ - isPrimary: true, - }) - public id: number; + @column({ + isPrimary: true, + }) + public id: number; - @column({}) - public role_id: number; + @column({}) + public role_id: number; - @column({}) - public display_name: string; + @column({}) + public display_name: string; - @column({}) - public name: string; + @column({}) + public name: string; - @column({}) - public description: string; + @column({}) + public description: string; - @column.dateTime({ - serialize: (value: Date | null) => { - return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; - }, - autoCreate: true, - }) - public created_at: DateTime; + @column.dateTime({ + serialize: (value: Date | null) => { + return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; + }, + autoCreate: true, + }) + public created_at: DateTime; - @column.dateTime({ - serialize: (value: Date | null) => { - return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; - }, - autoCreate: true, - autoUpdate: true, - }) - public updated_at: DateTime; + @column.dateTime({ + serialize: (value: Date | null) => { + return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; + }, + autoCreate: true, + autoUpdate: true, + }) + public updated_at: DateTime; - @beforeCreate() - @beforeUpdate() - public static async resetDate(role) { - role.created_at = this.formatDateTime(role.created_at); - role.updated_at = this.formatDateTime(role.updated_at); - } + @beforeCreate() + @beforeUpdate() + public static async resetDate(role) { + role.created_at = this.formatDateTime(role.created_at); + role.updated_at = this.formatDateTime(role.updated_at); + } - // public static boot() { - // super.boot() + // public static boot() { + // super.boot() - // this.before('create', async (_modelInstance) => { - // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at) - // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at) - // }) - // this.before('update', async (_modelInstance) => { - // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at) - // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at) - // }) - // } + // this.before('create', async (_modelInstance) => { + // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at) + // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at) + // }) + // this.before('update', async (_modelInstance) => { + // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at) + // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at) + // }) + // } - private static formatDateTime(datetime) { - let value = new Date(datetime); - return datetime - ? value.getFullYear() + - '-' + - (value.getMonth() + 1) + - '-' + - value.getDate() + - ' ' + - value.getHours() + - ':' + - value.getMinutes() + - ':' + - value.getSeconds() - : datetime; - } + private static formatDateTime(datetime) { + let value = new Date(datetime); + return datetime + ? value.getFullYear() + + '-' + + (value.getMonth() + 1) + + '-' + + value.getDate() + + ' ' + + value.getHours() + + ':' + + value.getMinutes() + + ':' + + value.getSeconds() + : datetime; + } - // @belongsTo(() => Role) - // public role: BelongsTo; + // @belongsTo(() => Role) + // public role: BelongsTo; - @manyToMany(() => Role, { - pivotForeignKey: 'permission_id', - pivotRelatedForeignKey: 'role_id', - pivotTable: 'role_has_permissions', - }) - public roles: ManyToMany; + @manyToMany(() => Role, { + pivotForeignKey: 'permission_id', + pivotRelatedForeignKey: 'role_id', + pivotTable: 'role_has_permissions', + }) + public roles: ManyToMany; } diff --git a/app/Models/Person.ts b/app/Models/Person.ts index e101cd8..8b15d85 100644 --- a/app/Models/Person.ts +++ b/app/Models/Person.ts @@ -1,83 +1,79 @@ -import { - column, - BaseModel, - SnakeCaseNamingStrategy, - computed, - manyToMany, - ManyToMany, -} from '@ioc:Adonis/Lucid/Orm'; +import { column, SnakeCaseNamingStrategy, computed, manyToMany, ManyToMany } from '@ioc:Adonis/Lucid/Orm'; import { DateTime } from 'luxon'; import dayjs from 'dayjs'; import Dataset from './Dataset'; +import BaseModel from './BaseModel'; export default class Person extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy(); - public static primaryKey = 'id'; - public static table = 'persons'; - public static selfAssignPrimaryKey = false; + public static namingStrategy = new SnakeCaseNamingStrategy(); + public static primaryKey = 'id'; + public static table = 'persons'; + public static selfAssignPrimaryKey = false; + // only the academic_title, email, first_name, identifier_orcid, last_name and name_type attributes are allowed to be mass assigned. + public static fillable: string[] = ['academic_title', 'email', 'first_name', 'identifier_orcid', 'last_name', 'name_type']; - @column({ - isPrimary: true, - }) - public id: number; - - @column({}) - public academicTitle: string; - - @column() - public email: string; - - @column({}) - public firstName: string; - - @column({}) - public lastName: string; - - @column({}) - public identifierOrcid: string; - - @column({}) - public status: boolean; - - @column({}) - public nameType: string; - - @column.dateTime({ - serialize: (value: Date | null) => { - return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; - }, - autoCreate: true, - }) - public createdAt: DateTime; - - @computed({ - serializeAs: 'name' + @column({ + isPrimary: true, }) - public get fullName() { - return `${this.firstName} ${this.lastName}`; - } + public id: number; + + @column({ columnName: 'academic_title' }) + public academicTitle: string; + + @column() + public email: string; + + @column({}) + public firstName: string; + + @column({}) + public lastName: string; + + @column({}) + public identifierOrcid: string; + + @column({}) + public status: boolean; + + @column({}) + public nameType: string; + + @column.dateTime({ + serialize: (value: Date | null) => { + return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; + }, + autoCreate: true, + }) + public createdAt: DateTime; + + @computed({ + serializeAs: 'name', + }) + public get fullName() { + return `${this.firstName} ${this.lastName}`; + } // @computed() - // public get progress(): number { - // return 50; - // } + // public get progress(): number { + // return 50; + // } // @computed() - // public get created_at() { - // return '2023-03-21 08:45:00'; - // } + // public get created_at() { + // return '2023-03-21 08:45:00'; + // } @computed() public get datasetCount() { - const stock = this.$extras.datasets_count //my pivot column name was "stock" - return stock - } + const stock = this.$extras.datasets_count; //my pivot column name was "stock" + return stock; + } - @manyToMany(() => Dataset, { - pivotForeignKey: 'person_id', - pivotRelatedForeignKey: 'document_id', - pivotTable: 'link_documents_persons', - pivotColumns: ['role', 'sort_order', 'allow_email_contact'] - }) - public datasets: ManyToMany; + @manyToMany(() => Dataset, { + pivotForeignKey: 'person_id', + pivotRelatedForeignKey: 'document_id', + pivotTable: 'link_documents_persons', + pivotColumns: ['role', 'sort_order', 'allow_email_contact'], + }) + public datasets: ManyToMany; } diff --git a/app/Models/Project.ts b/app/Models/Project.ts index 8c5080c..b06a574 100644 --- a/app/Models/Project.ts +++ b/app/Models/Project.ts @@ -12,7 +12,6 @@ export default class Project extends BaseModel { }) public id: number; - @column({}) public label: string; @@ -24,14 +23,12 @@ export default class Project extends BaseModel { @column.dateTime({ autoCreate: true, - }) - public created_at: DateTime; + }) + public created_at: DateTime; - @column.dateTime({ + @column.dateTime({ autoCreate: true, - autoUpdate: true - }) - public updated_at: DateTime; - - + autoUpdate: true, + }) + public updated_at: DateTime; } diff --git a/app/Models/Role.ts b/app/Models/Role.ts index 399d07b..a761d70 100644 --- a/app/Models/Role.ts +++ b/app/Models/Role.ts @@ -1,12 +1,4 @@ -import { - column, - BaseModel, - SnakeCaseNamingStrategy, - manyToMany, - ManyToMany, - beforeCreate, - beforeUpdate, -} from '@ioc:Adonis/Lucid/Orm'; +import { column, BaseModel, SnakeCaseNamingStrategy, manyToMany, ManyToMany, beforeCreate, beforeUpdate } from '@ioc:Adonis/Lucid/Orm'; import { DateTime } from 'luxon'; // import moment from 'moment'; @@ -15,91 +7,91 @@ import User from './User'; import Permission from 'App/Models/Permission'; export default class Role extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy(); - public static primaryKey = 'id'; - public static table = 'roles'; - public static selfAssignPrimaryKey = false; + public static namingStrategy = new SnakeCaseNamingStrategy(); + public static primaryKey = 'id'; + public static table = 'roles'; + public static selfAssignPrimaryKey = false; - @column({ - isPrimary: true, - }) - public id: number; + @column({ + isPrimary: true, + }) + public id: number; - @column({}) - public display_name: string; + @column({}) + public display_name: string; - @column({}) - public name: string; + @column({}) + public name: string; - @column({}) - public description: string; + @column({}) + public description: string; - @column.dateTime({ - serialize: (value: Date | null) => { - // return value ? moment(value).format('MMMM Do YYYY, HH:mm:ss') : value; - return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; - }, + @column.dateTime({ + serialize: (value: Date | null) => { + // return value ? moment(value).format('MMMM Do YYYY, HH:mm:ss') : value; + return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; + }, autoCreate: true, - }) - public created_at: DateTime; + }) + public created_at: DateTime; - @column.dateTime({ - serialize: (value: Date | null) => { - return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; - }, + @column.dateTime({ + serialize: (value: Date | null) => { + return value ? dayjs(value).format('MMMM D YYYY HH:mm a') : value; + }, autoCreate: true, - autoUpdate: true - }) - public updated_at: DateTime; + autoUpdate: true, + }) + public updated_at: DateTime; - @beforeCreate() + @beforeCreate() @beforeUpdate() - public static async resetDate(role) { - role.created_at = this.formatDateTime(role.created_at); - role.updated_at = this.formatDateTime(role.updated_at); - } + public static async resetDate(role) { + role.created_at = this.formatDateTime(role.created_at); + role.updated_at = this.formatDateTime(role.updated_at); + } - // public static boot() { - // super.boot(); + // public static boot() { + // super.boot(); - // this.before('create', async (_modelInstance) => { - // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at); - // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at); - // }); - // this.before('update', async (_modelInstance) => { - // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at); - // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at); - // }); - // } + // this.before('create', async (_modelInstance) => { + // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at); + // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at); + // }); + // this.before('update', async (_modelInstance) => { + // _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at); + // _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at); + // }); + // } - private static formatDateTime(datetime) { - let value = new Date(datetime); - return datetime - ? value.getFullYear() + - '-' + - (value.getMonth() + 1) + - '-' + - value.getDate() + - ' ' + - value.getHours() + - ':' + - value.getMinutes() + - ':' + - value.getSeconds() - : datetime; - } + private static formatDateTime(datetime) { + let value = new Date(datetime); + return datetime + ? value.getFullYear() + + '-' + + (value.getMonth() + 1) + + '-' + + value.getDate() + + ' ' + + value.getHours() + + ':' + + value.getMinutes() + + ':' + + value.getSeconds() + : datetime; + } - @manyToMany(() => User, { - pivotForeignKey: 'role_id', - pivotRelatedForeignKey: 'account_id', - pivotTable: 'link_accounts_roles', - }) - public users: ManyToMany; + @manyToMany(() => User, { + pivotForeignKey: 'role_id', + pivotRelatedForeignKey: 'account_id', + pivotTable: 'link_accounts_roles', + }) + public users: ManyToMany; - @manyToMany(() => Permission, { - pivotForeignKey: 'role_id', - pivotRelatedForeignKey: 'permission_id', - pivotTable: 'role_has_permissions', - }) - public permissions: ManyToMany; + @manyToMany(() => Permission, { + pivotForeignKey: 'role_id', + pivotRelatedForeignKey: 'permission_id', + pivotTable: 'role_has_permissions', + }) + public permissions: ManyToMany; } diff --git a/app/Models/Title.ts b/app/Models/Title.ts new file mode 100644 index 0000000..cb68302 --- /dev/null +++ b/app/Models/Title.ts @@ -0,0 +1,28 @@ +import { column, belongsTo, BelongsTo } from '@ioc:Adonis/Lucid/Orm'; +import Dataset from './Dataset'; +import BaseModel from './BaseModel'; + +export default class Title extends BaseModel { + public static primaryKey = 'id'; + public static table = 'dataset_titles'; + public static selfAssignPrimaryKey = false; + public static timestamps = false; + public static fillable: string[] = ['value', 'type', 'language']; + + @column({}) + public document_id: number; + + @column() + public type: string; + + @column() + public value: string; + + @column() + public language: string; + + @belongsTo(() => Dataset, { + foreignKey: 'document_id', + }) + public dataset: BelongsTo; +} diff --git a/app/Models/User.ts b/app/Models/User.ts index b2c889a..aa418b8 100644 --- a/app/Models/User.ts +++ b/app/Models/User.ts @@ -1,9 +1,10 @@ import { DateTime } from 'luxon'; -import { BaseModel, column, beforeSave, manyToMany, ManyToMany } from '@ioc:Adonis/Lucid/Orm'; +import { BaseModel, column, beforeSave, manyToMany, ManyToMany, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'; import Hash from '@ioc:Adonis/Core/Hash'; import Role from './Role'; import Database from '@ioc:Adonis/Lucid/Database'; import Config from '@ioc:Adonis/Core/Config'; +import Dataset from './Dataset'; // export default interface IUser { // id: number; @@ -22,82 +23,87 @@ const roleTable = Config.get('rolePermission.role_table', 'roles'); const userRoleTable = Config.get('rolePermission.user_role_table', 'link_accounts_roles'); export default class User extends BaseModel { - public static table = 'accounts'; + public static table = 'accounts'; - @column({ isPrimary: true }) - public id: number; + @column({ isPrimary: true }) + public id: number; - @column() - public login: string; + @column() + public login: string; - @column() - public email: string; + @column() + public email: string; - @column({ serializeAs: null }) - public password: string; + @column({ serializeAs: null }) + public password: string; - @column.dateTime({ autoCreate: true }) - public createdAt: DateTime; + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime; - @column.dateTime({ autoCreate: true, autoUpdate: true }) - public updatedAt: DateTime; + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime; - @beforeSave() - public static async hashPassword(user) { - if (user.$dirty.password) { - user.password = await Hash.make(user.password); - } - } + @beforeSave() + public static async hashPassword(user) { + if (user.$dirty.password) { + user.password = await Hash.make(user.password); + } + } - @manyToMany(() => Role, { - pivotForeignKey: 'account_id', - pivotRelatedForeignKey: 'role_id', - pivotTable: 'link_accounts_roles', - }) - public roles: ManyToMany; + @manyToMany(() => Role, { + pivotForeignKey: 'account_id', + pivotRelatedForeignKey: 'role_id', + pivotTable: 'link_accounts_roles', + }) + public roles: ManyToMany; - // https://github.com/adonisjs/core/discussions/1872#discussioncomment-132289 - public async getRoles(this: User): Promise { - const test = await this.related('roles').query(); - return test.map((role) => role.name); - } + @hasMany(() => Dataset, { + foreignKey: 'account_id', + }) + public datasets: HasMany; - public async can(permissionNames: Array): Promise { - // const permissions = await this.getPermissions() - // return Acl.check(expression, operand => _.includes(permissions, operand)) - const hasPermission = await this.checkHasPermissions(this, permissionNames); - return hasPermission; - } + // https://github.com/adonisjs/core/discussions/1872#discussioncomment-132289 + public async getRoles(this: User): Promise { + const test = await this.related('roles').query(); + return test.map((role) => role.name); + } - private async checkHasPermissions(user: User, permissionNames: Array): Promise { - let permissionPlaceHolder = '('; - let placeholders = new Array(permissionNames.length).fill('?'); - permissionPlaceHolder += placeholders.join(','); - permissionPlaceHolder += ')'; + public async can(permissionNames: Array): Promise { + // const permissions = await this.getPermissions() + // return Acl.check(expression, operand => _.includes(permissions, operand)) + const hasPermission = await this.checkHasPermissions(this, permissionNames); + return hasPermission; + } - let { - rows: { - 0: { permissioncount }, - }, - } = await Database.rawQuery( - 'SELECT count("p"."name") as permissionCount FROM ' + - roleTable + - ' r INNER JOIN ' + - userRoleTable + - ' ur ON ur.role_id=r.id AND "ur"."account_id"=? ' + - ' INNER JOIN ' + - rolePermissionTable + - ' rp ON rp.role_id=r.id ' + - ' INNER JOIN ' + - permissionTable + - ' p ON rp.permission_id=p.id AND "p"."name" in ' + - permissionPlaceHolder + - ' LIMIT 1', - [user.id, ...permissionNames] - ); + private async checkHasPermissions(user: User, permissionNames: Array): Promise { + let permissionPlaceHolder = '('; + let placeholders = new Array(permissionNames.length).fill('?'); + permissionPlaceHolder += placeholders.join(','); + permissionPlaceHolder += ')'; - return permissioncount > 0; - } + let { + rows: { + 0: { permissioncount }, + }, + } = await Database.rawQuery( + 'SELECT count("p"."name") as permissionCount FROM ' + + roleTable + + ' r INNER JOIN ' + + userRoleTable + + ' ur ON ur.role_id=r.id AND "ur"."account_id"=? ' + + ' INNER JOIN ' + + rolePermissionTable + + ' rp ON rp.role_id=r.id ' + + ' INNER JOIN ' + + permissionTable + + ' p ON rp.permission_id=p.id AND "p"."name" in ' + + permissionPlaceHolder + + ' LIMIT 1', + [user.id, ...permissionNames], + ); + + return permissioncount > 0; + } } // export default User; diff --git a/app/Models/UserRole.ts b/app/Models/UserRole.ts index 1e42431..3b3de94 100644 --- a/app/Models/UserRole.ts +++ b/app/Models/UserRole.ts @@ -1,74 +1,74 @@ -import {column, BaseModel, belongsTo, BelongsTo, SnakeCaseNamingStrategy} from '@ioc:Adonis/Lucid/Orm' +import { column, BaseModel, belongsTo, BelongsTo, SnakeCaseNamingStrategy } from '@ioc:Adonis/Lucid/Orm'; -import User from 'App/Models/User' -import Role from 'App/Models/Role' -import { DateTime } from 'luxon' +import User from 'App/Models/User'; +import Role from 'App/Models/Role'; +import { DateTime } from 'luxon'; // import moment from 'moment' export default class UserRole extends BaseModel { - public static namingStrategy = new SnakeCaseNamingStrategy() - public static primaryKey = 'id' - public static table = 'user_roles' - public static selfAssignPrimaryKey = false + public static namingStrategy = new SnakeCaseNamingStrategy(); + public static primaryKey = 'id'; + public static table = 'user_roles'; + public static selfAssignPrimaryKey = false; - @column({ - isPrimary: true, - }) - public id: number - - @column({}) - public user_id: number - - @column({}) - public role_id: number - - @column({ - // serialize: (value: DateTime | null) => { - // return value ? moment(value).format('lll') : value - // }, - }) - public created_at: DateTime - - @column({ - // serialize: (value: DateTime | null) => { - // return value ? moment(value).format('lll') : value - // }, - }) - public updated_at: DateTime - - public static boot() { - super.boot() - - this.before('create', async (_modelInstance) => { - _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at) - _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at) + @column({ + isPrimary: true, }) - this.before('update', async (_modelInstance) => { - _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at) - _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at) + public id: number; + + @column({}) + public user_id: number; + + @column({}) + public role_id: number; + + @column({ + // serialize: (value: DateTime | null) => { + // return value ? moment(value).format('lll') : value + // }, }) - } + public created_at: DateTime; - private static formatDateTime(datetime) { - let value = new Date(datetime) - return datetime - ? value.getFullYear() + - '-' + - (value.getMonth() + 1) + - '-' + - value.getDate() + - ' ' + - value.getHours() + - ':' + - value.getMinutes() + - ':' + - value.getSeconds() - : datetime - } + @column({ + // serialize: (value: DateTime | null) => { + // return value ? moment(value).format('lll') : value + // }, + }) + public updated_at: DateTime; - @belongsTo(() => User) - public user: BelongsTo + public static boot() { + super.boot(); - @belongsTo(() => Role) - public role: BelongsTo + this.before('create', async (_modelInstance) => { + _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at); + _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at); + }); + this.before('update', async (_modelInstance) => { + _modelInstance.created_at = this.formatDateTime(_modelInstance.created_at); + _modelInstance.updated_at = this.formatDateTime(_modelInstance.updated_at); + }); + } + + private static formatDateTime(datetime) { + let value = new Date(datetime); + return datetime + ? value.getFullYear() + + '-' + + (value.getMonth() + 1) + + '-' + + value.getDate() + + ' ' + + value.getHours() + + ':' + + value.getMinutes() + + ':' + + value.getSeconds() + : datetime; + } + + @belongsTo(() => User) + public user: BelongsTo; + + @belongsTo(() => Role) + public role: BelongsTo; } diff --git a/app/Validators/AuthValidator.ts b/app/Validators/AuthValidator.ts index a69f3e6..290f8c4 100644 --- a/app/Validators/AuthValidator.ts +++ b/app/Validators/AuthValidator.ts @@ -2,45 +2,45 @@ import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator'; import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; export default class AuthValidator { - constructor(protected ctx: HttpContextContract) {} + constructor(protected ctx: HttpContextContract) {} - /* - * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. - * - * For example: - * 1. The username must be of data type string. But then also, it should - * not contain special characters or numbers. - * ``` - * schema.string({}, [ rules.alpha() ]) - * ``` - * - * 2. The email must be of data type string, formatted as a valid - * email. But also, not used by any other user. - * ``` - * schema.string({}, [ - * rules.email(), - * rules.unique({ table: 'users', column: 'email' }), - * ]) - * ``` - */ - public schema = schema.create({ - email: schema.string({ trim: true }, [ - rules.email(), - // rules.unique({ table: 'accounts', column: 'email' }) - ]), - password: schema.string({}, [rules.minLength(6)]), - }); + /* + * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. + * + * For example: + * 1. The username must be of data type string. But then also, it should + * not contain special characters or numbers. + * ``` + * schema.string({}, [ rules.alpha() ]) + * ``` + * + * 2. The email must be of data type string, formatted as a valid + * email. But also, not used by any other user. + * ``` + * schema.string({}, [ + * rules.email(), + * rules.unique({ table: 'users', column: 'email' }), + * ]) + * ``` + */ + public schema = schema.create({ + email: schema.string({ trim: true }, [ + rules.email(), + // rules.unique({ table: 'accounts', column: 'email' }) + ]), + password: schema.string({}, [rules.minLength(6)]), + }); - /** - * Custom messages for validation failures. You can make use of dot notation `(.)` - * for targeting nested fields and array expressions `(*)` for targeting all - * children of an array. For example: - * - * { - * 'profile.username.required': 'Username is required', - * 'scores.*.number': 'Define scores as valid numbers' - * } - * - */ - public messages: CustomMessages = {}; + /** + * Custom messages for validation failures. You can make use of dot notation `(.)` + * for targeting nested fields and array expressions `(*)` for targeting all + * children of an array. For example: + * + * { + * 'profile.username.required': 'Username is required', + * 'scores.*.number': 'Define scores as valid numbers' + * } + * + */ + public messages: CustomMessages = {}; } diff --git a/app/Validators/CreateDatasetValidator.ts b/app/Validators/CreateDatasetValidator.ts new file mode 100644 index 0000000..5870d94 --- /dev/null +++ b/app/Validators/CreateDatasetValidator.ts @@ -0,0 +1,157 @@ +import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator'; +import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; +import dayjs from 'dayjs'; +import { TitleTypes, DescriptionTypes } from 'Contracts/enums'; + +export default class CreateDatasetValidator { + constructor(protected ctx: HttpContextContract) {} + + /* + * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. + * + * For example: + * 1. The username must be of data type string. But then also, it should + * not contain special characters or numbers. + * ``` + * schema.string({}, [ rules.alpha() ]) + * ``` + * + * 2. The email must be of data type string, formatted as a valid + * email. But also, not used by any other user. + * ``` + * schema.string({}, [ + * rules.email(), + * rules.unique({ table: 'users', column: 'email' }), + * ]) + * ``` + */ + public schema = schema.create({ + // first step + language: schema.string({ trim: true }, [ + rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores + ]), + licenses: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one license for the new dataset + rights: schema.string([rules.equalTo('true')]), + // second step + type: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), + creating_corporation: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), + titles: schema.array([rules.minLength(1)]).members( + schema.object().members({ + value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), + type: schema.enum(Object.values(TitleTypes)), + language: schema.string({ trim: true }, [ + rules.minLength(2), + rules.maxLength(255), + rules.translatedLanguage('/language', 'type'), + ]), + }), + ), + descriptions: schema.array([rules.minLength(1)]).members( + schema.object().members({ + value: schema.string({ trim: true }, [rules.minLength(3), rules.maxLength(255)]), + type: schema.enum(Object.values(DescriptionTypes)), + language: schema.string({ trim: true }, [ + rules.minLength(2), + rules.maxLength(255), + rules.translatedLanguage('/language', 'type'), + ]), + }), + ), + authors: schema.array([rules.minLength(1)]).members(schema.object().members({ email: schema.string({ trim: true }) })), + // third step + project_id: schema.number.optional(), + embargo_date: schema.date.optional({ format: 'yyyy-MM-dd' }, [rules.after(10, 'days')]), + coverage: schema.object().members({ + x_min: schema.number(), + x_max: schema.number(), + y_min: schema.number(), + y_max: schema.number(), + elevation_absolut: schema.number.optional(), + elevation_min: schema.number.optional([rules.requiredIfExists('elevation_max')]), + elevation_max: schema.number.optional([rules.requiredIfExists('elevation_min')]), + depth_absolut: schema.number.optional(), + depth_min: schema.number.optional([rules.requiredIfExists('depth_max')]), + depth_max: schema.number.optional([rules.requiredIfExists('depth_min')]), + }), + subjects: schema.array([rules.minLength(3), rules.uniqueArray('value')]).members( + schema.object().members({ + value: schema.string({ trim: true }, [ + rules.minLength(3), + rules.maxLength(255), + // rules.unique({ table: 'dataset_subjects', column: 'value' }), + ]), + // type: schema.enum(Object.values(TitleTypes)), + language: schema.string({ trim: true }, [rules.minLength(2), rules.maxLength(255)]), + }), + ), + // file: schema.file({ + // size: '100mb', + // extnames: ['jpg', 'gif', 'png'], + // }), + files: schema.array([rules.minLength(1)]).members( + schema.file({ + size: '100mb', + extnames: ['jpg', 'gif', 'png'], + }), + ), + // upload: schema.object().members({ + // label: schema.string({ trim: true }, [rules.maxLength(255)]), + + // // label: schema.string({ trim: true }, [ + // // // rules.minLength(3), + // // // rules.maxLength(255), + // // ]), + // }), + }); + + /** + * Custom messages for validation failures. You can make use of dot notation `(.)` + * for targeting nested fields and array expressions `(*)` for targeting all + * children of an array. For example: + * + * { + * 'profile.username.required': 'Username is required', + * 'scores.*.number': 'Define scores as valid numbers' + * } + * + */ + public messages: CustomMessages = { + 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', + 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', + 'required': '{{ field }} is required', + 'unique': '{{ field }} must be unique, and this value is already taken', + // 'confirmed': '{{ field }} is not correct', + 'licences.minLength': 'at least {{ options.minLength }} permission must be defined', + 'licences.*.number': 'Define roles as valid numbers', + 'rights.equalTo': 'you must agree to continue', + + 'titles.0.value.minLength': 'Main Title must be at least {{ options.minLength }} characters long', + 'titles.0.value.required': 'Main Title is required', + 'titles.*.value.required': 'Additional title is required, if defined', + 'titles.*.type.required': 'Additional title type is required', + 'titles.*.language.required': 'Additional title language is required', + 'titles.*.language.translatedLanguage': 'The language of the translated title must be different from the language of the dataset', + + 'descriptions.0.value.minLength': 'Main Abstract must be at least {{ options.minLength }} characters long', + 'descriptions.0.value.required': 'Main Abstract is required', + 'descriptions.*.value.required': 'Additional description is required, if defined', + 'descriptions.*.type.required': 'Additional description type is required', + 'descriptions.*.language.required': 'Additional description language is required', + 'descriptions.*.language.translatedLanguage': + 'The language of the translated description must be different from the language of the dataset', + + 'authors.minLength': 'at least {{ options.minLength }} author must be defined', + + 'after': `{{ field }} must be older than ${dayjs().add(10, 'day')}`, + + 'subjects.minLength': 'at least {{ options.minLength }} keywords must be defined', + 'subjects.uniqueArray': 'The {{ options.array }} array must have unique values based on the {{ options.field }} attribute.', + 'subjects.*.value.required': 'keyword value is required', + 'subjects.*.value.minLength': 'keyword value must be at least {{ options.minLength }} characters long', + 'subjects.*.type.required': 'keyword type is required', + 'subjects.*.language.required': 'language of keyword is required', + + 'files.*.size': 'file size is to big', + 'files.extnames': 'file extension is not supported', + }; +} diff --git a/app/Validators/CreateRoleValidator.ts b/app/Validators/CreateRoleValidator.ts index d8844b5..9ae4576 100644 --- a/app/Validators/CreateRoleValidator.ts +++ b/app/Validators/CreateRoleValidator.ts @@ -2,68 +2,68 @@ import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator'; import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; export default class CreateRoleValidator { - constructor(protected ctx: HttpContextContract) {} + constructor(protected ctx: HttpContextContract) {} - /* - * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. - * - * For example: - * 1. The username must be of data type string. But then also, it should - * not contain special characters or numbers. - * ``` - * schema.string({}, [ rules.alpha() ]) - * ``` - * - * 2. The email must be of data type string, formatted as a valid - * email. But also, not used by any other user. - * ``` - * schema.string({}, [ - * rules.email(), - * rules.unique({ table: 'users', column: 'email' }), - * ]) - * ``` - */ - public schema = schema.create({ - name: schema.string({ trim: true }, [ - rules.minLength(3), - rules.maxLength(255), - rules.unique({ table: 'roles', column: 'name' }), - rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores - ]), - display_name: schema.string.optional({ trim: true }, [ - rules.minLength(3), - rules.maxLength(255), - rules.unique({ table: 'roles', column: 'name' }), - rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores - ]), - description: schema.string.optional({}, [rules.minLength(3), rules.maxLength(255)]), - permissions: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new role - }); + /* + * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. + * + * For example: + * 1. The username must be of data type string. But then also, it should + * not contain special characters or numbers. + * ``` + * schema.string({}, [ rules.alpha() ]) + * ``` + * + * 2. The email must be of data type string, formatted as a valid + * email. But also, not used by any other user. + * ``` + * schema.string({}, [ + * rules.email(), + * rules.unique({ table: 'users', column: 'email' }), + * ]) + * ``` + */ + public schema = schema.create({ + name: schema.string({ trim: true }, [ + rules.minLength(3), + rules.maxLength(255), + rules.unique({ table: 'roles', column: 'name' }), + rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores + ]), + display_name: schema.string.optional({ trim: true }, [ + rules.minLength(3), + rules.maxLength(255), + rules.unique({ table: 'roles', column: 'name' }), + rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores + ]), + description: schema.string.optional({}, [rules.minLength(3), rules.maxLength(255)]), + permissions: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new role + }); - // emails: schema - // .array([rules.minLength(1)]) - // .members( - // schema.object().members({ email: schema.string({}, [rules.email()]) }) - // ), + // emails: schema + // .array([rules.minLength(1)]) + // .members( + // schema.object().members({ email: schema.string({}, [rules.email()]) }) + // ), - /** - * Custom messages for validation failures. You can make use of dot notation `(.)` - * for targeting nested fields and array expressions `(*)` for targeting all - * children of an array. For example: - * - * { - * 'profile.username.required': 'Username is required', - * 'scores.*.number': 'Define scores as valid numbers' - * } - * - */ - public messages: CustomMessages = { - 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', - 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', - 'required': '{{ field }} is required', - 'unique': '{{ field }} must be unique, and this value is already taken', - 'confirmed': '{{ field }} is not correct', - 'permissions.minLength': 'at least {{ options.minLength }} permission must be defined', - 'permissions.*.number': 'Define roles as valid numbers', - }; + /** + * Custom messages for validation failures. You can make use of dot notation `(.)` + * for targeting nested fields and array expressions `(*)` for targeting all + * children of an array. For example: + * + * { + * 'profile.username.required': 'Username is required', + * 'scores.*.number': 'Define scores as valid numbers' + * } + * + */ + public messages: CustomMessages = { + 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', + 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', + 'required': '{{ field }} is required', + 'unique': '{{ field }} must be unique, and this value is already taken', + 'confirmed': '{{ field }} is not correct', + 'permissions.minLength': 'at least {{ options.minLength }} permission must be defined', + 'permissions.*.number': 'Define roles as valid numbers', + }; } diff --git a/app/Validators/CreateUserValidator.ts b/app/Validators/CreateUserValidator.ts index d76b4c0..c871649 100644 --- a/app/Validators/CreateUserValidator.ts +++ b/app/Validators/CreateUserValidator.ts @@ -2,63 +2,63 @@ import { schema, CustomMessages, rules } from '@ioc:Adonis/Core/Validator'; import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; export default class CreateUserValidator { - constructor(protected ctx: HttpContextContract) {} + constructor(protected ctx: HttpContextContract) {} - /* - * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. - * - * For example: - * 1. The username must be of data type string. But then also, it should - * not contain special characters or numbers. - * ``` - * schema.string({}, [ rules.alpha() ]) - * ``` - * - * 2. The email must be of data type string, formatted as a valid - * email. But also, not used by any other user. - * ``` - * schema.string({}, [ - * rules.email(), - * rules.unique({ table: 'users', column: 'email' }), - * ]) - * ``` - */ - public schema = schema.create({ - login: schema.string({ trim: true }, [ - rules.minLength(3), - rules.maxLength(50), - rules.unique({ table: 'accounts', column: 'login' }), - rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores - ]), - email: schema.string({}, [rules.email(), rules.unique({ table: 'accounts', column: 'email' })]), - password: schema.string([rules.confirmed(), rules.minLength(6)]), - roles: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user - }); + /* + * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. + * + * For example: + * 1. The username must be of data type string. But then also, it should + * not contain special characters or numbers. + * ``` + * schema.string({}, [ rules.alpha() ]) + * ``` + * + * 2. The email must be of data type string, formatted as a valid + * email. But also, not used by any other user. + * ``` + * schema.string({}, [ + * rules.email(), + * rules.unique({ table: 'users', column: 'email' }), + * ]) + * ``` + */ + public schema = schema.create({ + login: schema.string({ trim: true }, [ + rules.minLength(3), + rules.maxLength(50), + rules.unique({ table: 'accounts', column: 'login' }), + rules.regex(/^[a-zA-Z0-9-_]+$/), //Must be alphanumeric with hyphens or underscores + ]), + email: schema.string({}, [rules.email(), rules.unique({ table: 'accounts', column: 'email' })]), + password: schema.string([rules.confirmed(), rules.minLength(6)]), + roles: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user + }); - // emails: schema - // .array([rules.minLength(1)]) - // .members( - // schema.object().members({ email: schema.string({}, [rules.email()]) }) - // ), + // emails: schema + // .array([rules.minLength(1)]) + // .members( + // schema.object().members({ email: schema.string({}, [rules.email()]) }) + // ), - /** - * Custom messages for validation failures. You can make use of dot notation `(.)` - * for targeting nested fields and array expressions `(*)` for targeting all - * children of an array. For example: - * - * { - * 'profile.username.required': 'Username is required', - * 'scores.*.number': 'Define scores as valid numbers' - * } - * - */ - public messages: CustomMessages = { - 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', - 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', - 'required': '{{ field }} is required', - 'unique': '{{ field }} must be unique, and this value is already taken', - 'confirmed': '{{ field }} is not correct', - 'roles.minLength': 'at least {{ options.minLength }} role must be defined', - 'roles.*.number': 'Define roles as valid numbers' - }; + /** + * Custom messages for validation failures. You can make use of dot notation `(.)` + * for targeting nested fields and array expressions `(*)` for targeting all + * children of an array. For example: + * + * { + * 'profile.username.required': 'Username is required', + * 'scores.*.number': 'Define scores as valid numbers' + * } + * + */ + public messages: CustomMessages = { + 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', + 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', + 'required': '{{ field }} is required', + 'unique': '{{ field }} must be unique, and this value is already taken', + 'confirmed': '{{ field }} is not correct', + 'roles.minLength': 'at least {{ options.minLength }} role must be defined', + 'roles.*.number': 'Define roles as valid numbers', + }; } diff --git a/app/Validators/UpdateRoleValidator.ts b/app/Validators/UpdateRoleValidator.ts index 4b934b8..e656d30 100644 --- a/app/Validators/UpdateRoleValidator.ts +++ b/app/Validators/UpdateRoleValidator.ts @@ -3,95 +3,95 @@ import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; // import { Request } from '@adonisjs/core/build/standalone'; export default class UpdateRoleValidator { - protected ctx: HttpContextContract; - public schema; + protected ctx: HttpContextContract; + public schema; - constructor(ctx: HttpContextContract) { - this.ctx = ctx; - this.schema = this.createSchema(); - } + constructor(ctx: HttpContextContract) { + this.ctx = ctx; + this.schema = this.createSchema(); + } - // public get schema() { - // return this._schema; - // } + // public get schema() { + // return this._schema; + // } - private createSchema() { - return schema.create({ - name: schema.string({ trim: true }, [ - rules.minLength(3), - rules.maxLength(50), - rules.unique({ - table: 'roles', - column: 'name', - whereNot: { id: this.ctx?.params.id }, - }), - rules.regex(/^[a-zA-Z0-9-_]+$/), - //Must be alphanumeric with hyphens or underscores - ]), - description: schema.string.optional({}, [rules.minLength(3), rules.maxLength(255)]), - permissions: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one permission for the new role - }); - } + private createSchema() { + return schema.create({ + name: schema.string({ trim: true }, [ + rules.minLength(3), + rules.maxLength(50), + rules.unique({ + table: 'roles', + column: 'name', + whereNot: { id: this.ctx?.params.id }, + }), + rules.regex(/^[a-zA-Z0-9-_]+$/), + //Must be alphanumeric with hyphens or underscores + ]), + description: schema.string.optional({}, [rules.minLength(3), rules.maxLength(255)]), + permissions: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one permission for the new role + }); + } - /* - * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. - * - * For example: - * 1. The username must be of data type string. But then also, it should - * not contain special characters or numbers. - * ``` - * schema.string({}, [ rules.alpha() ]) - * ``` - * - * 2. The email must be of data type string, formatted as a valid - * email. But also, not used by any other user. - * ``` - * schema.string({}, [ - * rules.email(), - * rules.unique({ table: 'users', column: 'email' }), - * ]) - * ``` - */ + /* + * Define schema to validate the "shape", "type", "formatting" and "integrity" of data. + * + * For example: + * 1. The username must be of data type string. But then also, it should + * not contain special characters or numbers. + * ``` + * schema.string({}, [ rules.alpha() ]) + * ``` + * + * 2. The email must be of data type string, formatted as a valid + * email. But also, not used by any other user. + * ``` + * schema.string({}, [ + * rules.email(), + * rules.unique({ table: 'users', column: 'email' }), + * ]) + * ``` + */ - // public refs = schema.refs({ - // id: this.ctx.params.id - // }) + // public refs = schema.refs({ + // id: this.ctx.params.id + // }) - // public schema = schema.create({ - // login: schema.string({ trim: true }, [ - // rules.minLength(3), - // rules.maxLength(50), - // rules.unique({ - // table: 'accounts', - // column: 'login', - // // whereNot: { id: this.refs.id } - // whereNot: { id: this.ctx?.params.id }, - // }), - // // rules.regex(/^[a-zA-Z0-9-_]+$/), - // //Must be alphanumeric with hyphens or underscores - // ]), - // email: schema.string({}, [rules.email(), rules.unique({ table: 'accounts', column: 'email' })]), - // password: schema.string.optional([rules.confirmed(), rules.minLength(6)]), - // roles: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user - // }); + // public schema = schema.create({ + // login: schema.string({ trim: true }, [ + // rules.minLength(3), + // rules.maxLength(50), + // rules.unique({ + // table: 'accounts', + // column: 'login', + // // whereNot: { id: this.refs.id } + // whereNot: { id: this.ctx?.params.id }, + // }), + // // rules.regex(/^[a-zA-Z0-9-_]+$/), + // //Must be alphanumeric with hyphens or underscores + // ]), + // email: schema.string({}, [rules.email(), rules.unique({ table: 'accounts', column: 'email' })]), + // password: schema.string.optional([rules.confirmed(), rules.minLength(6)]), + // roles: schema.array([rules.minLength(1)]).members(schema.number()), // define at least one role for the new user + // }); - /** - * Custom messages for validation failures. You can make use of dot notation `(.)` - * for targeting nested fields and array expressions `(*)` for targeting all - * children of an array. For example: - * - * { - * 'profile.username.required': 'Username is required', - * 'scores.*.number': 'Define scores as valid numbers' - * } - * - */ - public messages: CustomMessages = { - 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', - 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', - 'required': '{{ field }} is required', - 'unique': '{{ field }} must be unique, and this value is already taken', - 'permissions.minLength': 'at least {{ options.minLength }} permission must be defined', - 'permissions.*.number': 'Define permissions as valid numbers', - }; + /** + * Custom messages for validation failures. You can make use of dot notation `(.)` + * for targeting nested fields and array expressions `(*)` for targeting all + * children of an array. For example: + * + * { + * 'profile.username.required': 'Username is required', + * 'scores.*.number': 'Define scores as valid numbers' + * } + * + */ + public messages: CustomMessages = { + 'minLength': '{{ field }} must be at least {{ options.minLength }} characters long', + 'maxLength': '{{ field }} must be less then {{ options.maxLength }} characters long', + 'required': '{{ field }} is required', + 'unique': '{{ field }} must be unique, and this value is already taken', + 'permissions.minLength': 'at least {{ options.minLength }} permission must be defined', + 'permissions.*.number': 'Define permissions as valid numbers', + }; } diff --git a/contracts/validator.ts b/contracts/validator.ts index 287df31..459ab8c 100644 --- a/contracts/validator.ts +++ b/contracts/validator.ts @@ -1,5 +1,6 @@ declare module '@ioc:Adonis/Core/Validator' { interface Rules { translatedLanguage(mainLanguageField: string, typeField: string): Rule; + uniqueArray(field: string): Rule; } } diff --git a/database/migrations/dataset_4_titles.ts b/database/migrations/dataset_4_titles.ts index a7f677f..3b1e2c8 100644 --- a/database/migrations/dataset_4_titles.ts +++ b/database/migrations/dataset_4_titles.ts @@ -12,7 +12,7 @@ export default class DatasetTitles extends BaseSchema { .foreign('document_id', 'dataset_titles_document_id_foreign') .references('id') .inTable('documents') - .onDelete('CASCADE') // delete this titke when document is deleted + .onDelete('CASCADE') // delete this title when document is deleted .onUpdate('CASCADE'); // table.string('type', 255).notNullable(); table.enum('type', Object.values(TitleTypes)).notNullable(); diff --git a/database/migrations/dataset_9_persons.ts b/database/migrations/dataset_9_persons.ts index d0f208a..9f06bc3 100644 --- a/database/migrations/dataset_9_persons.ts +++ b/database/migrations/dataset_9_persons.ts @@ -20,6 +20,7 @@ export default class Persons extends BaseSchema { table.integer('registered_at'); // table.string('name_type', 255); table.enum('name_type', Object.values(PersonNameTypes)).notNullable(); + table.timestamp('created_at', { useTz: false }).nullable(); }); } diff --git a/index.d.ts b/index.d.ts index f0b66eb..1a8e357 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,5 +1,5 @@ declare module '*.vue' { - import type { DefineComponent } from "vue" - const component: DefineComponent<{}, {}, any> - export default component - } \ No newline at end of file + import type { DefineComponent } from 'vue'; + const component: DefineComponent<{}, {}, any>; + export default component; +} diff --git a/package-lock.json b/package-lock.json index d84c0ad..37f5973 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "dependencies": { "@adonisjs/auth": "^8.2.3", - "@adonisjs/core": "^5.8.3", + "@adonisjs/core": "^5.9.0", "@adonisjs/lucid": "^18.3.0", "@adonisjs/repl": "^3.1.11", "@adonisjs/session": "^6.4.0", @@ -571,9 +571,9 @@ } }, "node_modules/@adonisjs/shield": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@adonisjs/shield/-/shield-7.1.0.tgz", - "integrity": "sha512-+a4Z+LBcWd46gMh99Wf0+iJMowq4aKuD04kvYB4AgTV78ffn21AXq3bsnoklFACkidEJwZ3FlRfQfKE+z1vg+g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@adonisjs/shield/-/shield-7.1.1.tgz", + "integrity": "sha512-y1YzXwravcS/A1yxcyfSD/UrRi2+H9v0ntX9NgVhLYvBF5eHuPzQKgv9sICVjmj2z7n94HzcTAio0Rc32EX51Q==", "dependencies": { "@poppinss/utils": "^4.0.4", "csrf": "^3.1.0", @@ -2765,9 +2765,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4094,15 +4094,15 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", - "integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", + "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/type-utils": "5.59.11", - "@typescript-eslint/utils": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/type-utils": "5.60.0", + "@typescript-eslint/utils": "5.60.0", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", @@ -4128,14 +4128,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz", - "integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", + "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", "debug": "^4.3.4" }, "engines": { @@ -4155,13 +4155,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz", - "integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", + "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11" + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4172,13 +4172,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz", - "integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", + "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.11", - "@typescript-eslint/utils": "5.59.11", + "@typescript-eslint/typescript-estree": "5.60.0", + "@typescript-eslint/utils": "5.60.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -4199,9 +4199,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz", - "integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", + "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4212,13 +4212,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz", - "integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", + "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/visitor-keys": "5.59.11", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4289,17 +4289,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz", - "integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", + "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.11", - "@typescript-eslint/types": "5.59.11", - "@typescript-eslint/typescript-estree": "5.59.11", + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -4337,12 +4337,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.11", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz", - "integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==", + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", + "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.11", + "@typescript-eslint/types": "5.60.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -4723,9 +4723,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "bin": { "acorn": "bin/acorn" }, @@ -5741,9 +5741,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.8.tgz", - "integrity": "sha512-j+7xYe+v+q2Id9qbBeCI8WX5NmZSRe8es1+0xntD/+gaWXznP8tFEkv5IgSaHf5dS1YwVMbX/4W6m937mj+wQw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -5760,8 +5760,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001502", - "electron-to-chromium": "^1.4.428", + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", "node-releases": "^2.0.12", "update-browserslist-db": "^1.0.11" }, @@ -5943,9 +5943,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001503", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz", - "integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==", + "version": "1.0.30001506", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001506.tgz", + "integrity": "sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==", "dev": true, "funding": [ { @@ -7498,9 +7498,9 @@ } }, "node_modules/dotenv": { - "version": "16.1.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", - "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "engines": { "node": ">=12" }, @@ -7648,9 +7648,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.431", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.431.tgz", - "integrity": "sha512-m232JTVmCawA2vG+1azVxhKZ9Sv1Q//xxNv5PkP5rWxGgQE8c3CiZFrh8Xnp+d1NmNxlu3QQrGIfdeW5TtXX5w==", + "version": "1.4.436", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.436.tgz", + "integrity": "sha512-aktOxo8fnrMC8vOIBMVS3PXbT1nrPQ+SouUuN7Y0a+Rw3pOMrvIV92Ybnax7x4tugA+ZpYA5fOHTby7ama8OQQ==", "dev": true }, "node_modules/emittery": { @@ -7729,9 +7729,9 @@ } }, "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.9.0.tgz", + "integrity": "sha512-RODB4txU+xImYDemN5DqaKC0CHk05XSVkOX4pq0hK26Qx+1LChkuOyUDlGEjYb3ACr0n9qBhFjg37hQuJvpkRQ==", "dev": true, "peer": true, "bin": { @@ -7798,15 +7798,15 @@ } }, "node_modules/eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", + "@eslint/js": "8.43.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -12641,9 +12641,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -14206,9 +14206,9 @@ } }, "node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -15477,9 +15477,9 @@ } }, "node_modules/terser": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.0.tgz", - "integrity": "sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", + "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -16408,9 +16408,9 @@ } }, "node_modules/webpack": { - "version": "5.87.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.87.0.tgz", - "integrity": "sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==", + "version": "5.88.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.0.tgz", + "integrity": "sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==", "dev": true, "peer": true, "dependencies": { diff --git a/package.json b/package.json index 650c401..a11d0bc 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "node server.js", "lint": "eslint . --ext=.ts", "format": "prettier --write .", - "format-check": "prettier --check ./**/*.ts", + "format-check": "prettier --check ./**/*.{ts,js}", "test": "node ace test" }, "eslintConfig": { @@ -74,7 +74,7 @@ }, "dependencies": { "@adonisjs/auth": "^8.2.3", - "@adonisjs/core": "^5.8.3", + "@adonisjs/core": "^5.9.0", "@adonisjs/lucid": "^18.3.0", "@adonisjs/repl": "^3.1.11", "@adonisjs/session": "^6.4.0", diff --git a/postcss.config.js b/postcss.config.js index 33ad091..67cdf1a 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/resources/js/Dataset.ts b/resources/js/Dataset.ts index 16dd4a7..463266d 100644 --- a/resources/js/Dataset.ts +++ b/resources/js/Dataset.ts @@ -1,37 +1,46 @@ import { Ref } from 'vue'; export interface Dataset { - [key: string]: string | Ref| boolean | Array | Array<Description>| Array<Person> | number | (IErrorMessage | undefined) | Coverage | Array<File>; - language: Ref<string>; - // licenses: Array<number>; - rights: boolean; - type: string; - creating_corporation: string; + [key: string]: + | string + | Ref<string> + | boolean + | Array<Title> + | Array<Description> + | Array<Person> + | number + | (IErrorMessage | undefined) + | Coverage + | Array<File>; + language: Ref<string>; + // licenses: Array<number>; + rights: boolean; + type: string; + creating_corporation: string; titles: Array<Title>; descriptions: Array<Description>; authors: Array<Person>; contributors: Array<Person>; project_id?: number; - embargo_date?: string, - coverage: Coverage, + embargo_date?: string; + coverage: Coverage; errors?: IErrorMessage; - // async (user): Promise<void>; - subjects: Array<Subject>, - files: Array<TestFile> | undefined, + // async (user): Promise<void>; + subjects: Array<Subject>; + files: Array<TestFile> | undefined; // upload: TethysFile } - /** Provides information about files and allows JavaScript in a web page to access their content. */ export interface TestFile extends Blob { readonly lastModified: number; readonly name: string; readonly webkitRelativePath: string; - label: string, - sorting: number, + label: string; + sorting: number; } -// export interface TethysFile { +// export interface TethysFile { // label: string, // sorting: number, // upload: File, @@ -39,10 +48,10 @@ export interface TestFile extends Blob { export interface Subject { // id: number; - language: string - type: string; + language: string; + type: string; value: string; - external_key?: string; + external_key?: string; } export interface Title { @@ -68,7 +77,7 @@ export interface Person { } interface IErrorMessage { - [key: string]: Array<string>; + [key: string]: Array<string>; } export interface Coverage { @@ -84,7 +93,7 @@ export interface Coverage { depth_max?: number; depth_absolut?: number; - time_min?: number, - time_max?: number, - time_absolut?: number -} \ No newline at end of file + time_min?: number; + time_max?: number; + time_absolut?: number; +} diff --git a/resources/js/styles.js b/resources/js/styles.js index c190723..64771d7 100644 --- a/resources/js/styles.js +++ b/resources/js/styles.js @@ -1,25 +1,25 @@ export const basic = { - aside: "bg-gray-800", - asideScrollbars: "aside-scrollbars-gray", - asideBrand: "bg-gray-900 text-white", - asideMenuItem: "text-gray-300 hover:text-white", - asideMenuItemActive: "font-bold text-white", - asideMenuDropdown: "bg-gray-700/50", - navBarItemLabel: "text-black", - navBarItemLabelHover: "hover:text-blue-500", - navBarItemLabelActiveColor: "text-blue-600", - overlay: "from-gray-700 via-gray-900 to-gray-700", - }; - - export const white = { - aside: "bg-white", - asideScrollbars: "aside-scrollbars-light", - asideBrand: "", - asideMenuItem: "text-blue-600 hover:text-black dark:text-white", - asideMenuItemActive: "font-bold text-black dark:text-white", - asideMenuDropdown: "bg-gray-100/75", - navBarItemLabel: "text-blue-600", - navBarItemLabelHover: "hover:text-black", - navBarItemLabelActiveColor: "text-black", - overlay: "from-white via-gray-100 to-white", - }; \ No newline at end of file + aside: 'bg-gray-800', + asideScrollbars: 'aside-scrollbars-gray', + asideBrand: 'bg-gray-900 text-white', + asideMenuItem: 'text-gray-300 hover:text-white', + asideMenuItemActive: 'font-bold text-white', + asideMenuDropdown: 'bg-gray-700/50', + navBarItemLabel: 'text-black', + navBarItemLabelHover: 'hover:text-blue-500', + navBarItemLabelActiveColor: 'text-blue-600', + overlay: 'from-gray-700 via-gray-900 to-gray-700', +}; + +export const white = { + aside: 'bg-white', + asideScrollbars: 'aside-scrollbars-light', + asideBrand: '', + asideMenuItem: 'text-blue-600 hover:text-black dark:text-white', + asideMenuItemActive: 'font-bold text-black dark:text-white', + asideMenuDropdown: 'bg-gray-100/75', + navBarItemLabel: 'text-blue-600', + navBarItemLabelHover: 'hover:text-black', + navBarItemLabelActiveColor: 'text-black', + overlay: 'from-white via-gray-100 to-white', +}; diff --git a/start/routes/api.ts b/start/routes/api.ts index 8e9414f..4cd95cd 100644 --- a/start/routes/api.ts +++ b/start/routes/api.ts @@ -2,18 +2,18 @@ import Route from '@ioc:Adonis/Core/Route'; // API Route.group(() => { - // Route.post("register", "AuthController.register"); - // Route.post("login", "AuthController.login"); + // Route.post("register", "AuthController.register"); + // Route.post("login", "AuthController.login"); - Route.group(() => { - Route.get('authors', 'AuthorsController.index').as('author.index'); - Route.get('datasets', 'DatasetController.index').as('dataset.index'); - Route.get('persons', 'AuthorsController.persons').as('author.persons'); - // Route.get("author/:id", "TodosController.show"); - // Route.put("author/update", "TodosController.update"); - // Route.post("author", "TodosController.store"); - }); - // .middleware("auth:api"); + Route.group(() => { + Route.get('authors', 'AuthorsController.index').as('author.index'); + Route.get('datasets', 'DatasetController.index').as('dataset.index'); + Route.get('persons', 'AuthorsController.persons').as('author.persons'); + // Route.get("author/:id", "TodosController.show"); + // Route.put("author/update", "TodosController.update"); + // Route.post("author", "TodosController.store"); + }); + // .middleware("auth:api"); }) - .namespace('App/Controllers/Http/Api') - .prefix('api'); + .namespace('App/Controllers/Http/Api') + .prefix('api'); diff --git a/start/validator.ts b/start/validator.ts index e846c66..7d46f5a 100644 --- a/start/validator.ts +++ b/start/validator.ts @@ -11,6 +11,34 @@ https://issuehunt.io/r/adonisjs/validator/issues/84 // import { string } from '@ioc:Adonis/Core/Helpers'; import { validator } from '@ioc:Adonis/Core/Validator'; +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( 'translatedLanguage', (value, [mainLanguageField, typeField], { root, tip, pointer, arrayExpressionPointer, errorReporter }) => { diff --git a/tailwind.config.js b/tailwind.config.js index 4e4124e..d4e87f2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -15,10 +15,10 @@ module.exports = { 'primary': '#22C55E', 'primary-dark': '#DCFCE7', }, - // fontFamily: { - // sans: ['Inter', ...defaultTheme.fontFamily.sans], - // logo: ['Archivo Black', ...defaultTheme.fontFamily.sans], - // }, + fontFamily: { + sans: ['Inter', ...defaultTheme.fontFamily.sans], + logo: ['Archivo Black', ...defaultTheme.fontFamily.sans], + }, zIndex: { '-1': '-1', }, @@ -61,9 +61,7 @@ module.exports = { return { 'scrollbarWidth': 'thin', - 'scrollbarColor': `${theme(`colors.${color}.${thumb}`)} ${theme( - `colors.${color}.${track}`, - )}`, + 'scrollbarColor': `${theme(`colors.${color}.${thumb}`)} ${theme(`colors.${color}.${track}`)}`, '&::-webkit-scrollbar': { width: '8px', height: '8px', diff --git a/webpack.config.js b/webpack.config.js index e1776fd..f9d63db 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,39 +3,39 @@ const Encore = require('@symfony/webpack-encore'); const { VueLoaderPlugin } = require('vue-loader'); const babelLoader = { - // test: /\.js$/, - test: /\.(js|jsx|ts|tsx)$/, - // exclude: /(node_modules|bower_components)/, - // exclude: file => ( - // /node_modules/.test(file) && - // !/\.vue\.js/.test(file) - // ), - exclude: /node_modules/, - use: { - loader: 'babel-loader', - options: { - presets: [ - // ['@babel/preset-env', { - // "targets": "> 0.25%, not dead" - // // "targets": { - // // "edge": "17", - // // "firefox": "60", - // // "chrome": "67", - // // "safari": "11.1" - // // } - // }], - ['@babel/preset-env', {}], - 'babel-preset-typescript-vue3', //because of new vue setup method - // "@babel/preset-typescript" - ], - plugins: [ - // "@babel/plugin-transform-runtime", - ['@babel/plugin-proposal-decorators', { legacy: true }], + // test: /\.js$/, + test: /\.(js|jsx|ts|tsx)$/, + // exclude: /(node_modules|bower_components)/, + // exclude: file => ( + // /node_modules/.test(file) && + // !/\.vue\.js/.test(file) + // ), + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + // ['@babel/preset-env', { + // "targets": "> 0.25%, not dead" + // // "targets": { + // // "edge": "17", + // // "firefox": "60", + // // "chrome": "67", + // // "safari": "11.1" + // // } + // }], + ['@babel/preset-env', {}], + 'babel-preset-typescript-vue3', //because of new vue setup method + // "@babel/preset-typescript" + ], + plugins: [ + // "@babel/plugin-transform-runtime", + ['@babel/plugin-proposal-decorators', { legacy: true }], - '@babel/proposal-class-properties', - ], - }, - }, + '@babel/proposal-class-properties', + ], + }, + }, }; // const Components = require('unplugin-vue-components/webpack') @@ -47,7 +47,7 @@ const babelLoader = { |-------------------------------------------------------------------------- */ if (!Encore.isRuntimeEnvironmentConfigured()) { - Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev'); + Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev'); } /* @@ -168,23 +168,23 @@ Encore.enableVersioning(Encore.isProduction()); | */ Encore.configureDevServerOptions((options) => { - /** - * Normalize "options.static" property to an array - */ - if (!options.static) { - options.static = []; - } else if (!Array.isArray(options.static)) { - options.static = [options.static]; - } + /** + * Normalize "options.static" property to an array + */ + if (!options.static) { + options.static = []; + } else if (!Array.isArray(options.static)) { + options.static = [options.static]; + } - /** - * Enable live reload and add views directory - */ - options.liveReload = true; - options.static.push({ - directory: join(__dirname, './resources/views'), - watch: true, - }); + /** + * Enable live reload and add views directory + */ + options.liveReload = true; + options.static.push({ + directory: join(__dirname, './resources/views'), + watch: true, + }); }); /* @@ -268,42 +268,41 @@ Encore.enablePostCssLoader(); // // } // }) Encore.addLoader({ - test: /\.vue$/, - loader: 'vue-loader', - options: { - // loaders: { - // ts: 'ts-loader', - // }, - cacheDirectory: 'C:\\Users\\kaiarn\\Documents\\Software\\tethys.viewer\\node_modules\\.cache\\vue-loader', - cacheIdentifier: 'f930df3e', - babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy'], - }, -}) - .addPlugin(new VueLoaderPlugin()) - // .addAliases({ - // vue$: 'vue/dist/vue.runtime.esm-bundler.js', - // }); + test: /\.vue$/, + loader: 'vue-loader', + options: { + // loaders: { + // ts: 'ts-loader', + // }, + cacheDirectory: 'C:\\Users\\kaiarn\\Documents\\Software\\tethys.viewer\\node_modules\\.cache\\vue-loader', + cacheIdentifier: 'f930df3e', + babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy'], + }, +}).addPlugin(new VueLoaderPlugin()); +// .addAliases({ +// vue$: 'vue/dist/vue.runtime.esm-bundler.js', +// }); Encore.addLoader(babelLoader) -// Encore.enableTypeScriptLoader(config => { -// // Loader-specific options -// config.configFile = 'tsconfig.vue.json'; -// config.appendTsSuffixTo = [/\.vue$/]; -// config.transpileOnly = true; -// config.happyPackMode = false; -// }, { -// // Directly change the exclude rule -// exclude: /node_modules/, - -// }) - .addAliases({ - '@': join(__dirname, 'resources/js'), - vue$: 'vue/dist/vue.runtime.esm-bundler.js' - }) - .configureDefinePlugin((options) => { - options['__VUE_OPTIONS_API__'] = true; - options['__VUE_PROD_DEVTOOLS__'] = false; - }); + // Encore.enableTypeScriptLoader(config => { + // // Loader-specific options + // config.configFile = 'tsconfig.vue.json'; + // config.appendTsSuffixTo = [/\.vue$/]; + // config.transpileOnly = true; + // config.happyPackMode = false; + // }, { + // // Directly change the exclude rule + // exclude: /node_modules/, + + // }) + .addAliases({ + '@': join(__dirname, 'resources/js'), + 'vue$': 'vue/dist/vue.runtime.esm-bundler.js', + }) + .configureDefinePlugin((options) => { + options['__VUE_OPTIONS_API__'] = true; + options['__VUE_PROD_DEVTOOLS__'] = false; + }); // Encore.addAliases({ // '@': resolve(__dirname, 'resources/js') @@ -364,7 +363,7 @@ Encore.addLoader(babelLoader) */ const config = Encore.getWebpackConfig(); config.infrastructureLogging = { - level: 'warn', + level: 'warn', }; config.stats = 'errors-warnings'; // config.resolve.extensions.push('.vue'); @@ -381,7 +380,7 @@ config.stats = 'errors-warnings'; // // // '@': path.resolve(__dirname, 'resources/js'), // // } // }; -config.resolve.extensions = [ '.tsx', '.ts', '.mjs', '.js', '.jsx', '.vue', '.json', '.wasm']; +config.resolve.extensions = ['.tsx', '.ts', '.mjs', '.js', '.jsx', '.vue', '.json', '.wasm']; // config.resolve = { // // vue: 'vue/dist/vue.js' @@ -390,8 +389,6 @@ config.resolve.extensions = [ '.tsx', '.ts', '.mjs', '.js', '.jsx', '.vue', '.js // } // } - - /* |-------------------------------------------------------------------------- | Export config