From 1c463d03e16400729208264a15e9ae2bf1a0441a Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Wed, 1 Jan 2025 14:47:56 +0100 Subject: [PATCH] fix(backend): Invalid parse permissions for API from client --- apps/backend/src/app.module.ts | 10 +-- apps/backend/src/plugins/welcome/config.json | 22 +----- .../scripts/check-update-schema-database.ts | 1 - .../staff/admin/services/create.service.ts | 7 +- .../staff/admin/services/edit.service.ts | 7 +- .../staff/admin/services/show.service.ts | 17 ++--- .../backend/src/database/schema/admins.ts | 5 +- packages/backend/src/helpers/user.service.ts | 6 +- packages/backend/src/main.ts | 1 - .../core/advanced/files/actions/actions.tsx | 2 +- .../create-edit-form/hooks/mutation-api.ts | 44 ++++++++---- .../admin/create-edit-form/hooks/use-form.ts | 68 +++++++++++-------- .../views/settings/views/files/content.tsx | 2 +- packages/shared/src/admin/auth.dto.ts | 6 +- .../src/admin/members/staff/admin.dto.ts | 20 +++--- packages/shared/src/admin/staff.dto.ts | 7 +- 16 files changed, 113 insertions(+), 112 deletions(-) diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index f8ae7b2c1..7679ce273 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -21,11 +21,11 @@ const google = createGoogleGenerativeAI({ schemaDatabase, }, ai: google('gemini-2.0-flash-exp'), - captcha: { - type: 'cloudflare_turnstile', - secret_key: '', - site_key: '', - }, + // captcha: { + // type: 'cloudflare_turnstile', + // secret_key: '', + // site_key: '', + // }, // email: emailResend({ // api_key: process.env.EMAIL_RESEND_API_KEY, // from: process.env.EMAIL_RESEND_FROM, diff --git a/apps/backend/src/plugins/welcome/config.json b/apps/backend/src/plugins/welcome/config.json index 41864c511..b71ccef8c 100644 --- a/apps/backend/src/plugins/welcome/config.json +++ b/apps/backend/src/plugins/welcome/config.json @@ -8,25 +8,5 @@ "author": "VitNode", "author_url": "https://vitnode.com/", "support_url": "https://github.com/VitNode/vitnode/issues", - "allow_default": true, - "nav": [ - { - "code": "test123", - "icon": "air-vent", - "keywords": ["test"], - "children": [ - { - "code": "saougfj", - "icon": "😍", - "keywords": [] - } - ] - } - ], - "permissions_admin": [ - { - "id": "test", - "permissions": [] - } - ] + "allow_default": true } diff --git a/packages/backend/scripts/check-update-schema-database.ts b/packages/backend/scripts/check-update-schema-database.ts index 63b48f702..ac96a16a4 100644 --- a/packages/backend/scripts/check-update-schema-database.ts +++ b/packages/backend/scripts/check-update-schema-database.ts @@ -130,7 +130,6 @@ export const checkUpdateSchemaDatabase = async ({ await db.insert(core_admin_permissions).values({ group_id: adminGroup.id, protected: true, - permissions: [], }); } diff --git a/packages/backend/src/core/admin/members/staff/admin/services/create.service.ts b/packages/backend/src/core/admin/members/staff/admin/services/create.service.ts index 7cb1b324d..eba3453f6 100644 --- a/packages/backend/src/core/admin/members/staff/admin/services/create.service.ts +++ b/packages/backend/src/core/admin/members/staff/admin/services/create.service.ts @@ -11,7 +11,6 @@ import { AdminStaffMembersAdmin, CreateAdminStaffMembersAdminBody, } from 'vitnode-shared/admin/members/staff/admin.dto'; -import { PermissionsStaffObjWithoutPluginName } from 'vitnode-shared/admin/staff.dto'; @Injectable() export class CreateAdminStaffMembersAdminService { @@ -47,7 +46,7 @@ export class CreateAdminStaffMembersAdminService { .values({ user_id, group_id, - permissions, + data: { permissions }, }) .returning(); @@ -82,7 +81,7 @@ export class CreateAdminStaffMembersAdminService { user_or_group: { ...user, }, - permissions: data.permissions as PermissionsStaffObjWithoutPluginName[], + permissions: data.data?.permissions ?? [], }; } @@ -96,7 +95,7 @@ export class CreateAdminStaffMembersAdminService { ...data.group, group_name: [], }, - permissions: data.permissions as PermissionsStaffObjWithoutPluginName[], + permissions: data.data?.permissions ?? [], }; } } diff --git a/packages/backend/src/core/admin/members/staff/admin/services/edit.service.ts b/packages/backend/src/core/admin/members/staff/admin/services/edit.service.ts index b8c4d6025..d41f7bb50 100644 --- a/packages/backend/src/core/admin/members/staff/admin/services/edit.service.ts +++ b/packages/backend/src/core/admin/members/staff/admin/services/edit.service.ts @@ -11,7 +11,6 @@ import { AdminStaffMembersAdmin, EditAdminStaffMembersAdminBody, } from 'vitnode-shared/admin/members/staff/admin.dto'; -import { PermissionsStaffObjWithoutPluginName } from 'vitnode-shared/admin/staff.dto'; @Injectable() export class EditAdminStaffMembersAdminService { @@ -43,7 +42,7 @@ export class EditAdminStaffMembersAdminService { await this.databaseService.db .update(core_admin_permissions) .set({ - permissions, + data: { permissions }, }) .where(eq(core_admin_permissions.id, id)); @@ -78,7 +77,7 @@ export class EditAdminStaffMembersAdminService { user_or_group: { ...user, }, - permissions: data.permissions as PermissionsStaffObjWithoutPluginName[], + permissions: data.data?.permissions ?? [], }; } @@ -92,7 +91,7 @@ export class EditAdminStaffMembersAdminService { ...data.group, group_name: [], }, - permissions: data.permissions as PermissionsStaffObjWithoutPluginName[], + permissions: data.data?.permissions ?? [], }; } } diff --git a/packages/backend/src/core/admin/members/staff/admin/services/show.service.ts b/packages/backend/src/core/admin/members/staff/admin/services/show.service.ts index 794b1d4e4..044047cd3 100644 --- a/packages/backend/src/core/admin/members/staff/admin/services/show.service.ts +++ b/packages/backend/src/core/admin/members/staff/admin/services/show.service.ts @@ -11,10 +11,7 @@ import { AdminStaffMembersAdminQuery, } from 'vitnode-shared/admin/members/staff/admin.dto'; import { ConfigPlugin } from 'vitnode-shared/admin/plugin.dto'; -import { - PermissionsStaffObj, - PermissionsStaffObjWithoutPluginName, -} from 'vitnode-shared/admin/staff.dto'; +import { PermissionsStaffObj } from 'vitnode-shared/admin/staff.dto'; import { SortDirectionEnum } from 'vitnode-shared/utils/pagination.enum'; import { coreAdminPermissions } from '../helpers/core-admin-permissions'; @@ -50,9 +47,9 @@ export class ShowAdminStaffMembersAdminService { return { plugin_code: plugin.code, plugin: plugin.name, - groups: (config.permissions_admin ?? []).map(group => ({ - ...group, - permissions: group.permissions ?? [], + groups: (config.permissions_admin ?? []).map(item => ({ + ...item, + permissions: item.permissions ?? [], })), }; }, @@ -109,8 +106,7 @@ export class ShowAdminStaffMembersAdminService { user_or_group: { ...user, }, - permissions: (edge.permissions ?? - []) as PermissionsStaffObjWithoutPluginName[], + permissions: edge.data?.permissions ?? [], }; } @@ -131,8 +127,7 @@ export class ShowAdminStaffMembersAdminService { ...edge.group, group_name, }, - permissions: (edge.permissions ?? - []) as PermissionsStaffObjWithoutPluginName[], + permissions: edge.data?.permissions ?? [], }; }), ); diff --git a/packages/backend/src/database/schema/admins.ts b/packages/backend/src/database/schema/admins.ts index e24edf10c..b3f9e8649 100644 --- a/packages/backend/src/database/schema/admins.ts +++ b/packages/backend/src/database/schema/admins.ts @@ -1,5 +1,6 @@ import { relations } from 'drizzle-orm'; import { index, pgTable } from 'drizzle-orm/pg-core'; +import { PermissionsStaffArgs } from 'vitnode-shared/admin/staff.dto'; import { core_groups } from './groups'; import { core_sessions_known_devices } from './sessions'; @@ -18,7 +19,9 @@ export const core_admin_permissions = pgTable( created_at: t.timestamp().notNull().defaultNow(), updated_at: t.timestamp().notNull().defaultNow(), protected: t.boolean().notNull().default(false), - permissions: t.jsonb().default('[]'), + data: t.jsonb().$type<{ permissions: PermissionsStaffArgs[] }>().default({ + permissions: [], + }), }), t => [ index('core_admin_permissions_group_id_idx').on(t.group_id), diff --git a/packages/backend/src/helpers/user.service.ts b/packages/backend/src/helpers/user.service.ts index 6d525fd10..a34d756f2 100644 --- a/packages/backend/src/helpers/user.service.ts +++ b/packages/backend/src/helpers/user.service.ts @@ -2,7 +2,7 @@ import { core_files } from '@/database/schema/files'; import { InternalDatabaseService } from '@/utils/database/internal_database.service'; import { ForbiddenException, Injectable } from '@nestjs/common'; import { eq, sum } from 'drizzle-orm'; -import { PermissionsStaffObjWithoutPluginName } from 'vitnode-shared/admin/staff.dto'; +import { PermissionsStaffArgs } from 'vitnode-shared/admin/staff.dto'; import { User, UserWithDangerousInfo } from 'vitnode-shared/user.dto'; @Injectable() @@ -13,7 +13,7 @@ export class UserHelper { user, }: { user: User; - }): Promise { + }): Promise { const admin = await this.databaseService.db.query.core_admin_permissions.findFirst({ where: (table, { or, eq }) => @@ -24,7 +24,7 @@ export class UserHelper { throw new ForbiddenException(); } - return admin.permissions as PermissionsStaffObjWithoutPluginName[]; + return admin.data?.permissions ?? []; } // Overload signatures async getUserById(params: { diff --git a/packages/backend/src/main.ts b/packages/backend/src/main.ts index b62070f3f..543ff4327 100644 --- a/packages/backend/src/main.ts +++ b/packages/backend/src/main.ts @@ -61,7 +61,6 @@ export const nestjsMainApp = async (app: INestApplication, options?: Args) => { new ValidationPipe({ transform: true, whitelist: true, - transformOptions: { enableImplicitConversion: true }, enableDebugMessages: process.env.NODE_ENV === 'development', }), ); diff --git a/packages/frontend/src/views/admin/views/core/advanced/files/actions/actions.tsx b/packages/frontend/src/views/admin/views/core/advanced/files/actions/actions.tsx index 3afd0b061..dea4b2dd1 100644 --- a/packages/frontend/src/views/admin/views/core/advanced/files/actions/actions.tsx +++ b/packages/frontend/src/views/admin/views/core/advanced/files/actions/actions.tsx @@ -20,7 +20,7 @@ export const ActionsFilesAdvancedCoreAdmin = (data: ShowFilesAdvancedAdmin) => { size: 'icon', variant: 'ghost', })} - href={`${CONFIG.backend_public_url}/${data.dir_folder}/${data.file_name}`} + href={`${CONFIG.backend_client_public_url}/${data.dir_folder}/${data.file_name}`} target="_blank" > diff --git a/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/mutation-api.ts b/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/mutation-api.ts index 36b8cf75b..cfec86a63 100644 --- a/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/mutation-api.ts +++ b/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/mutation-api.ts @@ -11,24 +11,44 @@ import { export const createMutationApi = async ( body: CreateAdminStaffMembersAdminBody, ) => { - await fetcher({ - url: '/admin/members/staff/admin', - method: 'POST', - body, - }); + try { + await fetcher({ + url: '/admin/members/staff/admin', + method: 'POST', + body, + }); - revalidatePath('/', 'layout'); + revalidatePath('/', 'layout'); + } catch (err) { + const error = err as Error; + + if (error.message.includes('ALREADY_EXISTS')) { + return { message: 'already_exists' }; + } + + throw err; + } }; export const editMutationApi = async ({ id, ...body }: EditAdminStaffMembersAdminBody & { id: number }) => { - await fetcher({ - url: `/admin/members/staff/admin/${id}`, - method: 'PUT', - body, - }); + try { + await fetcher({ + url: `/admin/members/staff/admin/${id}`, + method: 'PUT', + body, + }); + + revalidatePath('/', 'layout'); + } catch (err) { + const error = err as Error; + + if (error.message.includes('ALREADY_EXISTS')) { + return { message: 'already_exists' }; + } - revalidatePath('/', 'layout'); + throw err; + } }; diff --git a/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/use-form.ts b/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/use-form.ts index 82ceb4278..2f8d8d03e 100644 --- a/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/use-form.ts +++ b/packages/frontend/src/views/admin/views/members/staff/admin/create-edit-form/hooks/use-form.ts @@ -74,39 +74,35 @@ export const useFormCreateEditFormGroupsMembersAdmin = ({ values: z.infer, form: UseFormReturn>, ) => { - try { - if (data) { - await editMutationApi({ - id: data.id, - permissions: values.unrestricted ? [] : values.permissions, - }); - } else { - await createMutationApi({ - group_id: - values.type === 'group' && values.group?.[0].key - ? +values.group[0].key - : null, - user_id: - values.type === 'user' && values.user?.[0].key - ? +values.user[0].key - : null, - permissions: values.unrestricted ? [] : values.permissions, - }); - } + let error = ''; - setOpen?.(false); - toast.success(t(data ? 'edit.success' : 'add.success'), { - description: - values.type === 'group' && Array.isArray(values.group?.[0].value) - ? convertText(values.group[0].value) - : Array.isArray(values.user?.[0].value) - ? null - : values.user?.[0].value, + if (data) { + const mutation = await editMutationApi({ + id: data.id, + permissions: values.unrestricted ? [] : values.permissions, + }); + if (mutation?.message) { + error = mutation.message; + } + } else { + const mutation = await createMutationApi({ + group_id: + values.type === 'group' && values.group?.[0].key + ? +values.group[0].key + : null, + user_id: + values.type === 'user' && values.user?.[0].key + ? +values.user[0].key + : null, + permissions: values.unrestricted ? [] : values.permissions, }); - } catch (err) { - const error = err as Error; + if (mutation?.message) { + error = mutation.message; + } + } - if (error.message.includes('ALREADY_EXISTS')) { + if (error) { + if (error.includes('ALREADY_EXISTS')) { form.setError(values.type === 'user' ? 'user' : 'group', { type: 'manual', message: tShared('already_exists'), @@ -118,7 +114,19 @@ export const useFormCreateEditFormGroupsMembersAdmin = ({ toast.error(tCore('errors.title'), { description: tCore('errors.internal_server_error'), }); + + return; } + + setOpen?.(false); + toast.success(t(data ? 'edit.success' : 'add.success'), { + description: + values.type === 'group' && Array.isArray(values.group?.[0].value) + ? convertText(values.group[0].value) + : Array.isArray(values.user?.[0].value) + ? null + : values.user?.[0].value, + }); }; return { diff --git a/packages/frontend/src/views/theme/views/settings/views/files/content.tsx b/packages/frontend/src/views/theme/views/settings/views/files/content.tsx index 07ce86cc1..aa4dfd241 100644 --- a/packages/frontend/src/views/theme/views/settings/views/files/content.tsx +++ b/packages/frontend/src/views/theme/views/settings/views/files/content.tsx @@ -116,7 +116,7 @@ export const ContentFilesSettings = ({ size: 'icon', variant: 'ghost', })} - href={`${CONFIG.backend_public_url}/${row.dir_folder}/${row.file_name}`} + href={`${CONFIG.backend_client_public_url}/${row.dir_folder}/${row.file_name}`} target="_blank" > diff --git a/packages/shared/src/admin/auth.dto.ts b/packages/shared/src/admin/auth.dto.ts index 99b30aefe..7dbdb051a 100644 --- a/packages/shared/src/admin/auth.dto.ts +++ b/packages/shared/src/admin/auth.dto.ts @@ -2,7 +2,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsOptional, IsString } from 'class-validator'; import { UserWithDangerousInfo } from '../user.dto'; -import { PermissionsStaffObjWithoutPluginName } from './staff.dto'; +import { PermissionsStaffArgs } from './staff.dto'; export class ItemNavAuthAdminObj { @ApiProperty() @@ -52,8 +52,8 @@ export class ShowAuthAdminObj { @ApiProperty({ type: [NavAuthAdminObj] }) nav: NavAuthAdminObj[]; - @ApiProperty({ type: [PermissionsStaffObjWithoutPluginName] }) - permissions: PermissionsStaffObjWithoutPluginName[]; + @ApiProperty({ type: [PermissionsStaffArgs] }) + permissions: PermissionsStaffArgs[]; @ApiProperty() restart_server: boolean; diff --git a/packages/shared/src/admin/members/staff/admin.dto.ts b/packages/shared/src/admin/members/staff/admin.dto.ts index b512eb09d..75ceb3813 100644 --- a/packages/shared/src/admin/members/staff/admin.dto.ts +++ b/packages/shared/src/admin/members/staff/admin.dto.ts @@ -4,17 +4,20 @@ import { getSchemaPath, OmitType, } from '@nestjs/swagger'; -import { IsArray, IsEnum, IsNumber, IsOptional } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { + IsArray, + IsEnum, + IsNumber, + IsObject, + IsOptional, +} from 'class-validator'; import { StringLanguage } from '../../../string-language.dto'; import { GroupUser, User } from '../../../user.dto'; import { PaginationObj, PaginationQuery } from '../../../utils/pagination.dto'; import { SortDirectionEnum } from '../../../utils/pagination.enum'; -import { - PermissionsStaffArgs, - PermissionsStaffObj, - PermissionsStaffObjWithoutPluginName, -} from '../../staff.dto'; +import { PermissionsStaffArgs, PermissionsStaffObj } from '../../staff.dto'; import { ShowStaffMembersAdminSortEnum } from './admin.enum'; export class StaffGroupUser extends OmitType(GroupUser, ['name'] as const) { @@ -29,8 +32,8 @@ export class AdminStaffMembersAdmin { @ApiProperty() id: number; - @ApiProperty({ type: [PermissionsStaffObjWithoutPluginName] }) - permissions: PermissionsStaffObjWithoutPluginName[]; + @ApiProperty({ type: [PermissionsStaffArgs] }) + permissions: PermissionsStaffArgs[]; @ApiProperty() protected: boolean; @@ -77,6 +80,7 @@ export class CreateAdminStaffMembersAdminBody { @ApiProperty({ type: [PermissionsStaffArgs] }) @IsArray() + @IsObject({ each: true }) permissions: PermissionsStaffArgs[]; @ApiPropertyOptional() diff --git a/packages/shared/src/admin/staff.dto.ts b/packages/shared/src/admin/staff.dto.ts index 6bfabd5c6..587aa3eac 100644 --- a/packages/shared/src/admin/staff.dto.ts +++ b/packages/shared/src/admin/staff.dto.ts @@ -1,4 +1,4 @@ -import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { ApiProperty } from '@nestjs/swagger'; export class PermissionsStaff { @ApiProperty() @@ -26,8 +26,3 @@ export class PermissionsStaffObj { @ApiProperty() plugin_code: string; } - -export class PermissionsStaffObjWithoutPluginName extends OmitType( - PermissionsStaffObj, - ['plugin'] as const, -) {}