diff --git a/backend/package.json b/backend/package.json index 55e55daee..4592a4ba2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -29,7 +29,7 @@ "class-validator": "^0.14.1", "cookie-parser": "^1.4.6", "date-fns": "^3.6.0", - "drizzle-orm": "^0.30.9", + "drizzle-orm": "^0.30.10", "graphql": "^16.8.1", "lodash": "^4.17.21", "nodemailer": "^6.9.13", @@ -39,7 +39,7 @@ "rxjs": "^7.8.1", "sharp": "^0.33.3", "socket.io": "^4.7.5", - "tar": "^7.0.1", + "tar": "^7.1.0", "ua-parser-js": "^1.0.37" }, "devDependencies": { @@ -62,20 +62,21 @@ "@nestjs/testing": "^10.3.8", "@types/archiver": "^6.0.2", "@types/bcrypt": "^5.0.2", - "@types/busboy": "^1.5.3", + "@types/busboy": "^1.5.4", "@types/cookie-parser": "^1.4.7", "@types/express": "^4.17.21", - "@types/lodash": "^4.17.0", - "@types/node": "^20.12.7", + "@types/lodash": "^4.17.1", + "@types/node": "^20.12.9", + "@types/nodemailer": "^6.4.15", "@types/object-path": "^0.11.4", - "@types/pg": "^8.11.5", + "@types/pg": "^8.11.6", "@types/supertest": "^6.0.2", "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/parser": "^7.8.0", "cross-env": "^7.0.3", "dotenv": "^16.4.5", - "drizzle-kit": "0.20.17-e38e63d", + "drizzle-kit": "0.20.17-57f44bf", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", diff --git a/backend/plugins/core/admin/admin.module.ts b/backend/plugins/core/admin/admin.module.ts index fc63fc8f2..800668da7 100644 --- a/backend/plugins/core/admin/admin.module.ts +++ b/backend/plugins/core/admin/admin.module.ts @@ -14,6 +14,7 @@ import { AdminNavModule } from "./nav/nav.module"; import { AdminLanguagesModule } from "./languages/languages.module"; import { AdminSettingsModule } from "./settings/settings.module"; import { AdminMetadataModule } from "./metadata/metadata.module"; +import { DownloadFilesAdminController } from "./files/download/download.controller"; @Module({ imports: [ @@ -29,6 +30,7 @@ import { AdminMetadataModule } from "./metadata/metadata.module"; AdminLanguagesModule, AdminSettingsModule, AdminMetadataModule - ] + ], + controllers: [DownloadFilesAdminController] }) export class AdminModule {} diff --git a/backend/plugins/core/admin/files/download/download.controller.ts b/backend/plugins/core/admin/files/download/download.controller.ts new file mode 100644 index 000000000..94fa0edf6 --- /dev/null +++ b/backend/plugins/core/admin/files/download/download.controller.ts @@ -0,0 +1,108 @@ +import { createReadStream, existsSync, unlinkSync } from "fs"; +import { join } from "path"; + +import { + Controller, + Get, + Param, + Req, + Res, + StreamableFile +} from "@nestjs/common"; +import { Request, Response } from "express"; + +import { DatabaseService } from "@/plugins/database/database.service"; +import { AuthorizationAdminSessionsService } from "../../sessions/authorization/authorization.service"; +import { InternalAuthorizationCoreSessionsService } from "@/plugins/core/sessions/authorization/internal/internal_authorization.service"; + +@Controller("files") +export class DownloadFilesAdminController { + constructor( + private service: InternalAuthorizationCoreSessionsService, + private readonly serviceAdmin: AuthorizationAdminSessionsService, + private databaseService: DatabaseService + ) {} + + @Get(":file") + async getFile( + @Res({ passthrough: true }) res: Response, + @Req() req: Request, + @Param() { file }: { file: string } + ): Promise { + const path = join(process.cwd(), "temp", file); + if (!existsSync(path)) { + res.status(404); + + return; + } + const userId = file.split(".")[0].split("--")[1].split("-")[0]; + const currentFile = { + name: file.split("--")[0], + type: file.split(".")[1] + }; + + const user = await this.databaseService.db.query.core_users.findFirst({ + where: (table, { eq }) => eq(table.id, +userId) + }); + + if (!user) { + res.status(404); + + return; + } + + const isAdmin = + await this.databaseService.db.query.core_admin_permissions.findFirst({ + where: (table, { eq, or }) => + or(eq(table.user_id, +userId), eq(table.group_id, user.group_id)) + }); + + if (isAdmin) { + try { + const data = await this.serviceAdmin.initialAuthorization({ + req, + res + }); + + if (+userId !== data.id) { + res.status(404); + + return; + } + } catch (e) { + res.status(404); + + return; + } + } else { + try { + const data = await this.service.authorization({ + req, + res + }); + + if (+userId !== data.id) { + res.status(404); + + return; + } + } catch (e) { + res.status(404); + + return; + } + } + + const streamFile = createReadStream(path); + res.set({ + "Content-Type": `application/${currentFile.type}`, + "Content-Disposition": `attachment; filename="${currentFile.name}.${currentFile.type}"` + }); + + streamFile.on("close", () => { + unlinkSync(path); + }); + + return new StreamableFile(streamFile); + } +} diff --git a/backend/plugins/core/admin/plugins/download/download.service.ts b/backend/plugins/core/admin/plugins/download/download.service.ts index ba0c3b765..0ede26e03 100644 --- a/backend/plugins/core/admin/plugins/download/download.service.ts +++ b/backend/plugins/core/admin/plugins/download/download.service.ts @@ -194,7 +194,7 @@ export class DownloadAdminPluginsService { try { await execShellCommand( - `npx drizzle-kit generate --out --config plugins/${code}/admin/database/drizzle.config.ts` + `npx drizzle-kit generate --config plugins/${code}/admin/database/drizzle.config.ts` ); } catch (err) { throw new CustomError({ diff --git a/backend/plugins/core/files/download/download.controller.ts b/backend/plugins/core/files/download/download.controller.ts index 2dd42cab9..b0f991ee7 100644 --- a/backend/plugins/core/files/download/download.controller.ts +++ b/backend/plugins/core/files/download/download.controller.ts @@ -1,106 +1,46 @@ -import { createReadStream, existsSync, unlinkSync } from "fs"; +import { createReadStream } from "fs"; import { join } from "path"; import { Controller, Get, Param, - Req, Res, - StreamableFile + StreamableFile, + Query } from "@nestjs/common"; -import { Request, Response } from "express"; +import { Response } from "express"; -import { InternalAuthorizationCoreSessionsService } from "../../sessions/authorization/internal/internal_authorization.service"; import { DatabaseService } from "@/plugins/database/database.service"; -import { AuthorizationAdminSessionsService } from "../../admin/sessions/authorization/authorization.service"; -@Controller("files") -export class DownloadFilesController { - constructor( - private service: InternalAuthorizationCoreSessionsService, - private readonly serviceAdmin: AuthorizationAdminSessionsService, - private databaseService: DatabaseService - ) {} +@Controller("secure_files") +export class DownloadSecureFilesController { + constructor(private databaseService: DatabaseService) {} - @Get(":file") + @Get(":id") async getFile( @Res({ passthrough: true }) res: Response, - @Req() req: Request, - @Param() { file }: { file: string } + @Param() { id }: { id: string }, + @Query() { security_key }: { security_key: string } ): Promise { - const path = join(process.cwd(), "temp", file); - if (!existsSync(path)) { - res.status(404); - - return; - } - const userId = file.split(".")[0].split("--")[1].split("-")[0]; - const currentFile = { - name: file.split("--")[0], - type: file.split(".")[1] - }; - - const user = await this.databaseService.db.query.core_users.findFirst({ - where: (table, { eq }) => eq(table.id, +userId) + const file = await this.databaseService.db.query.core_files.findFirst({ + where: (table, { eq }) => eq(table.id, +id) }); - if (!user) { + if (!file || file.security_key !== security_key) { res.status(404); return; } - const isAdmin = - await this.databaseService.db.query.core_admin_permissions.findFirst({ - where: (table, { eq, or }) => - or(eq(table.user_id, +userId), eq(table.group_id, user.group_id)) - }); - - if (isAdmin) { - try { - const data = await this.serviceAdmin.initialAuthorization({ - req, - res - }); - - if (+userId !== data.id) { - res.status(404); + const path = join(process.cwd(), file.dir_folder, file.file_name); - return; - } - } catch (e) { - res.status(404); - - return; - } - } else { - try { - const data = await this.service.authorization({ - req, - res - }); - - if (+userId !== data.id) { - res.status(404); - - return; - } - } catch (e) { - res.status(404); - - return; - } - } + const mediaType = file.mimetype.split("/")[0]; const streamFile = createReadStream(path); res.set({ - "Content-Type": `application/${currentFile.type}`, - "Content-Disposition": `attachment; filename="${currentFile.name}.${currentFile.type}"` - }); - - streamFile.on("close", () => { - unlinkSync(path); + "Content-Type": `application/${mediaType}`, + "Content-Disposition": `attachment; filename="${file.file_name}"` }); return new StreamableFile(streamFile); diff --git a/backend/plugins/core/files/files.module.ts b/backend/plugins/core/files/files.module.ts index d556e0dec..131a285c9 100644 --- a/backend/plugins/core/files/files.module.ts +++ b/backend/plugins/core/files/files.module.ts @@ -2,11 +2,11 @@ import { Global, Module } from "@nestjs/common"; import { UploadCoreFilesService } from "./helpers/upload/upload.service"; import { DeleteCoreFilesService } from "./helpers/delete/delete.service"; -import { DownloadFilesController } from "./download/download.controller"; import { AuthorizationAdminSessionsService } from "../admin/sessions/authorization/authorization.service"; import { ShowCoreFilesService } from "./show/show.service"; import { ShowCoreFilesResolver } from "./show/show.resolver"; import { CoreFilesCron } from "./files.cron"; +import { DownloadSecureFilesController } from "./download/download.controller"; @Module({ providers: [ShowCoreFilesService, ShowCoreFilesResolver, CoreFilesCron] @@ -21,6 +21,6 @@ export class CoreFilesModule {} AuthorizationAdminSessionsService ], exports: [UploadCoreFilesService, DeleteCoreFilesService], - controllers: [DownloadFilesController] + controllers: [DownloadSecureFilesController] }) export class CoreFilesModuleGlobal {} diff --git a/backend/plugins/core/helpers/text_language/parser/parser.service.ts b/backend/plugins/core/helpers/text_language/parser/parser.service.ts index a7dc530f9..f3cd3ed18 100644 --- a/backend/plugins/core/helpers/text_language/parser/parser.service.ts +++ b/backend/plugins/core/helpers/text_language/parser/parser.service.ts @@ -104,7 +104,7 @@ export class ParserTextLanguageCoreHelpersService extends HelpersParserTextLangu return update[0]; } - await this.databaseService.db + const data = await this.databaseService.db .insert(database) .values({ ...item, item_id } as { [Key in keyof PgTableWithColumns["$inferInsert"]]: @@ -113,6 +113,8 @@ export class ParserTextLanguageCoreHelpersService extends HelpersParserTextLangu | PgTableWithColumns["$inferInsert"][Key]; }) .returning(); + + return data[0]; }) ); diff --git a/backend/plugins/forum/admin/forums/create/create.service.ts b/backend/plugins/forum/admin/forums/create/create.service.ts index 49cebd2dc..eaefeb24f 100644 --- a/backend/plugins/forum/admin/forums/create/create.service.ts +++ b/backend/plugins/forum/admin/forums/create/create.service.ts @@ -55,10 +55,7 @@ export class CreateForumForumsService { ? theMostHighestPosition.position + 1 : 0, parent_id, - can_all_create: permissions.can_all_create, - can_all_read: permissions.can_all_read, - can_all_reply: permissions.can_all_reply, - can_all_view: permissions.can_all_view + ...permissions }) .returning(); @@ -81,7 +78,7 @@ export class CreateForumForumsService { await this.databaseService.db.insert(forum_forums_permissions).values( permissions.groups.map(item => ({ forum_id: data[0].id, - group_id: item.id, + group_id: item.group_id, ...item })) ); diff --git a/backend/plugins/forum/admin/forums/create/dto/create.args.ts b/backend/plugins/forum/admin/forums/create/dto/create.args.ts index 0a95b36f6..32e98b459 100644 --- a/backend/plugins/forum/admin/forums/create/dto/create.args.ts +++ b/backend/plugins/forum/admin/forums/create/dto/create.args.ts @@ -11,7 +11,7 @@ import { @InputType() class GroupsPermissionsCreateForumForums { @Field(() => Int) - id: number; + group_id: number; @Field(() => Boolean) can_view: boolean; diff --git a/backend/plugins/forum/admin/forums/edit/edit.service.ts b/backend/plugins/forum/admin/forums/edit/edit.service.ts index 015ec9afe..5f4781fa2 100644 --- a/backend/plugins/forum/admin/forums/edit/edit.service.ts +++ b/backend/plugins/forum/admin/forums/edit/edit.service.ts @@ -34,19 +34,6 @@ export class EditForumForumsService { id: number; permissions: PermissionsCreateForumForums; }) => { - // Uprate main permissions - await this.databaseService.db - .update(forum_forums_permissions) - .set({ - can_create: permissions.can_all_create, - can_read: permissions.can_all_read, - can_reply: permissions.can_all_reply, - can_view: permissions.can_all_view, - can_download_files: permissions.can_all_download_files - }) - .where(eq(forum_forums_permissions.forum_id, id)) - .returning(); - const getAllPermissions = await this.databaseService.db.query.forum_forums_permissions.findMany({ where: (table, { eq }) => eq(table.forum_id, id) @@ -54,12 +41,17 @@ export class EditForumForumsService { const update = await Promise.all( permissions.groups.map(async item => { - const itemExist = getAllPermissions.find(el => el.group_id === item.id); + const itemExist = getAllPermissions.find( + el => el.group_id === item.group_id + ); if (itemExist) { const update = await this.databaseService.db .update(forum_forums_permissions) - .set({ ...item, forum_id: id }) + .set({ + forum_id: id, + ...item + }) .where(eq(forum_forums_permissions.id, itemExist.id)) .returning(); @@ -68,7 +60,10 @@ export class EditForumForumsService { const insert = await this.databaseService.db .insert(forum_forums_permissions) - .values({ ...item, forum_id: id }) + .values({ + forum_id: id, + ...item + }) .returning(); return insert[0]; @@ -111,16 +106,17 @@ export class EditForumForumsService { await this.databaseService.db .update(forum_forums) .set({ - can_all_create: permissions.can_all_create, - can_all_read: permissions.can_all_read, - can_all_reply: permissions.can_all_reply, - can_all_view: permissions.can_all_view, - can_all_download_files: permissions.can_all_download_files, + ...permissions, parent_id }) .where(eq(forum_forums.id, id)) .returning(); + await this.updatePermissions({ + id, + permissions + }); + await this.parserTextLang.parse({ item_id: id, database: forum_forums_name, diff --git a/backend/plugins/forum/admin/forums/show/dto/show.obj.ts b/backend/plugins/forum/admin/forums/show/dto/show.obj.ts index 4e27abcf7..c5eabf860 100644 --- a/backend/plugins/forum/admin/forums/show/dto/show.obj.ts +++ b/backend/plugins/forum/admin/forums/show/dto/show.obj.ts @@ -6,7 +6,7 @@ import { PageInfo } from "@/types/database/pagination.type"; @ObjectType() export class GroupsPermissionsForumForums { @Field(() => Int) - id: number; + group_id: number; @Field(() => Boolean) can_view: boolean; diff --git a/backend/plugins/forum/topics/create/create.service.ts b/backend/plugins/forum/topics/create/create.service.ts index e05b337bb..315377760 100644 --- a/backend/plugins/forum/topics/create/create.service.ts +++ b/backend/plugins/forum/topics/create/create.service.ts @@ -90,7 +90,10 @@ export class CreateForumTopicsService { permissions: { can_reply: topic.forum.permissions.at(0)?.can_reply || topic.forum.can_all_reply, - can_edit: user.id === post.user.id + can_edit: user.id === post.user.id, + can_download_files: + topic.forum.permissions.at(0)?.can_download_files || + topic.forum.can_all_download_files } }; } diff --git a/backend/plugins/forum/topics/edit/edit.service.ts b/backend/plugins/forum/topics/edit/edit.service.ts index e5983cd85..2c5a8e3c4 100644 --- a/backend/plugins/forum/topics/edit/edit.service.ts +++ b/backend/plugins/forum/topics/edit/edit.service.ts @@ -101,7 +101,10 @@ export class EditForumTopicsService { can_reply: dataTopic.forum.permissions.at(0)?.can_reply || dataTopic.forum.can_all_reply, - can_edit: user.id === dataTopic.posts[0].user.id + can_edit: user.id === dataTopic.posts[0].user.id, + can_download_files: + dataTopic.forum.permissions.at(0)?.can_download_files || + dataTopic.forum.can_all_download_files } }; } diff --git a/backend/plugins/forum/topics/show/dto/show.obj.ts b/backend/plugins/forum/topics/show/dto/show.obj.ts index 92dc188b1..750ce9932 100644 --- a/backend/plugins/forum/topics/show/dto/show.obj.ts +++ b/backend/plugins/forum/topics/show/dto/show.obj.ts @@ -21,6 +21,9 @@ export class PermissionsTopicForums { @Field(() => Boolean) can_edit: boolean; + + @Field(() => Boolean) + can_download_files: boolean; } @ObjectType() diff --git a/backend/plugins/forum/topics/show/show.service.ts b/backend/plugins/forum/topics/show/show.service.ts index cdcf81266..e3c967a94 100644 --- a/backend/plugins/forum/topics/show/show.service.ts +++ b/backend/plugins/forum/topics/show/show.service.ts @@ -122,11 +122,17 @@ export class ShowTopicsForumsService { ? { can_reply: permissionsData?.can_reply || edge.forum.can_all_reply, - can_edit: user.id === post.user.id + can_edit: user.id === post.user.id, + can_download_files: + permissionsData?.can_download_files || + edge.forum.can_all_download_files } : { can_reply: false, - can_edit: false + can_edit: false, + can_download_files: + permissionsData?.can_download_files || + edge.forum.can_all_download_files }; return { diff --git a/backend/schema.gql b/backend/schema.gql index 2cd30bef8..5e44f9fa1 100644 --- a/backend/schema.gql +++ b/backend/schema.gql @@ -97,7 +97,7 @@ input GroupsPermissionsCreateForumForums { can_read: Boolean! can_reply: Boolean! can_view: Boolean! - id: Int! + group_id: Int! } type GroupsPermissionsForumForums { @@ -106,7 +106,7 @@ type GroupsPermissionsForumForums { can_read: Boolean! can_reply: Boolean! can_view: Boolean! - id: Int! + group_id: Int! } type ItemNavAdminPluginsAuthorization { @@ -264,6 +264,7 @@ type PermissionsForumForumsAdmin { } type PermissionsTopicForums { + can_download_files: Boolean! can_edit: Boolean! can_reply: Boolean! } diff --git a/frontend/admin/core/views/core/langs/create-edit/hooks/create-mutation-api.ts b/frontend/admin/core/views/core/langs/create-edit/hooks/create-mutation-api.ts index ce1b25cbc..644813fe8 100644 --- a/frontend/admin/core/views/core/langs/create-edit/hooks/create-mutation-api.ts +++ b/frontend/admin/core/views/core/langs/create-edit/hooks/create-mutation-api.ts @@ -8,6 +8,7 @@ import { Admin__Core_Languages__Create, type Admin__Core_Languages__CreateMutation } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const createMutationApi = async ( variables: Admin__Core_Languages__CreateMutationVariables @@ -22,7 +23,7 @@ export const createMutationApi = async ( }); revalidatePath("/", "layout"); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); revalidatePath("/admin/core/langs", "page"); return { data }; diff --git a/frontend/admin/core/views/core/langs/create-edit/hooks/edit-mutation-api.ts b/frontend/admin/core/views/core/langs/create-edit/hooks/edit-mutation-api.ts index 51cccba0c..067b6befb 100644 --- a/frontend/admin/core/views/core/langs/create-edit/hooks/edit-mutation-api.ts +++ b/frontend/admin/core/views/core/langs/create-edit/hooks/edit-mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Languages__EditMutation, type Admin__Core_Languages__EditMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const editMutationApi = async ( variables: Admin__Core_Languages__EditMutationVariables @@ -22,7 +23,7 @@ export const editMutationApi = async ( }); revalidatePath("/", "layout"); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); revalidatePath("/admin/core/langs", "page"); return { data }; diff --git a/frontend/admin/core/views/core/langs/table/actions/delete/hooks/mutation-api.ts b/frontend/admin/core/views/core/langs/table/actions/delete/hooks/mutation-api.ts index 1ed8ae001..b96a4c5e8 100644 --- a/frontend/admin/core/views/core/langs/table/actions/delete/hooks/mutation-api.ts +++ b/frontend/admin/core/views/core/langs/table/actions/delete/hooks/mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Languages__DeleteMutation, type Admin__Core_Languages__DeleteMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const mutationApi = async ( variables: Admin__Core_Languages__DeleteMutationVariables @@ -22,7 +23,7 @@ export const mutationApi = async ( }); revalidatePath("/", "layout"); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); revalidatePath("/admin/core/langs", "page"); return { data }; diff --git a/frontend/admin/core/views/core/styles/nav/create-edit/hooks/create-mutation-api.ts b/frontend/admin/core/views/core/styles/nav/create-edit/hooks/create-mutation-api.ts index d4646ae6c..cfbfbc42a 100644 --- a/frontend/admin/core/views/core/styles/nav/create-edit/hooks/create-mutation-api.ts +++ b/frontend/admin/core/views/core/styles/nav/create-edit/hooks/create-mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Nav__CreateMutation, type Admin__Core_Nav__CreateMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const createMutationApi = async ( variables: Admin__Core_Nav__CreateMutationVariables @@ -21,7 +22,7 @@ export const createMutationApi = async ( variables }); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); revalidatePath("/admin/core/styles/nav", "page"); return { data }; diff --git a/frontend/admin/core/views/core/styles/nav/create-edit/hooks/edit-mutation-api.ts b/frontend/admin/core/views/core/styles/nav/create-edit/hooks/edit-mutation-api.ts index ff7ecbede..06244e622 100644 --- a/frontend/admin/core/views/core/styles/nav/create-edit/hooks/edit-mutation-api.ts +++ b/frontend/admin/core/views/core/styles/nav/create-edit/hooks/edit-mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Nav__EditMutation, type Admin__Core_Nav__EditMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const editMutationApi = async ( variables: Admin__Core_Nav__EditMutationVariables @@ -21,7 +22,7 @@ export const editMutationApi = async ( variables }); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); revalidatePath("/admin/core/styles/nav", "page"); return { data }; diff --git a/frontend/admin/core/views/core/styles/nav/table/actions/delete/hooks/mutation-api.ts b/frontend/admin/core/views/core/styles/nav/table/actions/delete/hooks/mutation-api.ts index 40b43438b..3e7d6e1cb 100644 --- a/frontend/admin/core/views/core/styles/nav/table/actions/delete/hooks/mutation-api.ts +++ b/frontend/admin/core/views/core/styles/nav/table/actions/delete/hooks/mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Nav__DeleteMutation, type Admin__Core_Nav__DeleteMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const mutationApi = async ( variables: Admin__Core_Nav__DeleteMutationVariables @@ -21,7 +22,7 @@ export const mutationApi = async ( variables }); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); revalidatePath("/admin/core/styles/nav", "page"); revalidatePath("/", "layout"); diff --git a/frontend/admin/core/views/core/styles/nav/table/hooks/mutation-change-position-api.ts b/frontend/admin/core/views/core/styles/nav/table/hooks/mutation-change-position-api.ts index 8ecd33be9..9cadbc4a9 100644 --- a/frontend/admin/core/views/core/styles/nav/table/hooks/mutation-change-position-api.ts +++ b/frontend/admin/core/views/core/styles/nav/table/hooks/mutation-change-position-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Nav__Change_PositionMutationVariables, Admin__Core_Nav__Change_Position } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const mutationChangePositionApi = async ( variables: Admin__Core_Nav__Change_PositionMutationVariables @@ -21,7 +22,7 @@ export const mutationChangePositionApi = async ( variables }); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); return { data }; } catch (error) { diff --git a/frontend/admin/core/views/core/styles/themes/actions/create/hooks/mutation-api.ts b/frontend/admin/core/views/core/styles/themes/actions/create/hooks/mutation-api.ts index 1f1f79d0e..d04b1a88c 100644 --- a/frontend/admin/core/views/core/styles/themes/actions/create/hooks/mutation-api.ts +++ b/frontend/admin/core/views/core/styles/themes/actions/create/hooks/mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Themes__CreateMutation, type Admin__Core_Themes__CreateMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const mutationApi = async ( variables: Admin__Core_Themes__CreateMutationVariables @@ -22,7 +23,7 @@ export const mutationApi = async ( }); revalidatePath("/admin/core/styles/themes", "page"); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); return { data }; } catch (error) { diff --git a/frontend/admin/core/views/core/styles/themes/table/actions/delete/hooks/mutation-api.ts b/frontend/admin/core/views/core/styles/themes/table/actions/delete/hooks/mutation-api.ts index a691e66f1..28d9bdab0 100644 --- a/frontend/admin/core/views/core/styles/themes/table/actions/delete/hooks/mutation-api.ts +++ b/frontend/admin/core/views/core/styles/themes/table/actions/delete/hooks/mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Themes__DeleteMutation, type Admin__Core_Themes__DeleteMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const mutationApi = async ( variables: Admin__Core_Themes__DeleteMutationVariables @@ -22,7 +23,7 @@ export const mutationApi = async ( }); revalidatePath("/admin/core/styles/themes", "page"); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); return { data }; } catch (error) { diff --git a/frontend/admin/core/views/core/styles/themes/upload/hooks/mutation-api.ts b/frontend/admin/core/views/core/styles/themes/upload/hooks/mutation-api.ts index 4286667bd..776ddced2 100644 --- a/frontend/admin/core/views/core/styles/themes/upload/hooks/mutation-api.ts +++ b/frontend/admin/core/views/core/styles/themes/upload/hooks/mutation-api.ts @@ -8,6 +8,7 @@ import { type Admin__Core_Themes__UploadMutation, type Admin__Core_Themes__UploadMutationVariables } from "@/graphql/hooks"; +import { CoreApiTags } from "@/admin/core/api-tags"; export const mutationApi = async (formData: FormData) => { try { @@ -29,7 +30,7 @@ export const mutationApi = async (formData: FormData) => { ] }); - revalidateTag("Core_Sessions__Authorization"); + revalidateTag(CoreApiTags.Core_Sessions__Authorization); revalidatePath("/admin/core/styles/themes", "page"); return { data }; diff --git a/frontend/admin/forum/views/forums/create-edit/content/permissions/content.tsx b/frontend/admin/forum/views/forums/create-edit/content/permissions/content.tsx index 89605033e..f28db11f0 100644 --- a/frontend/admin/forum/views/forums/create-edit/content/permissions/content.tsx +++ b/frontend/admin/forum/views/forums/create-edit/content/permissions/content.tsx @@ -39,10 +39,18 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ [] ); - const onToggleAll = (value: string) => { + const onToggleAll = (id: string) => { + const stateToUpdate = !field.value[`can_all_${id}`]; + field.onChange({ ...field.value, - [value]: !field.value[value] + [`can_all_${id}`]: stateToUpdate, + groups: field.value.groups.map((group: { group_id: number }) => { + return { + ...group, + [`can_${id}`]: stateToUpdate + }; + }) }); }; @@ -72,13 +80,13 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ {permissions.map(permission => (
{permission.title} onToggleAll(`can_all_${permission.id}`)} + onClick={() => onToggleAll(permission.id)} checked={field.value[`can_all_${permission.id}`]} />
@@ -88,7 +96,7 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ )} itemContent={(index, item) => { const findItem = field.value.groups.find( - (group: { id: number }) => group.id === item.id + (group: { group_id: number }) => group.group_id === item.id ); // Check if: // 1. The permission is enabled for all groups @@ -99,7 +107,7 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ ) || permissions.every( permission => - findItem?.[permission.id] || + findItem?.[`can_${permission.id}`] || field.value[`can_all_${permission.id}`] ); @@ -132,7 +140,8 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ ...field.value, groups: [ ...field.value.groups.filter( - (group: { id: number }) => group.id !== item.id + (group: { group_id: number }) => + group.group_id !== item.id ), { id: item.id, @@ -152,40 +161,57 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ // 2. The permission is enabled for the current group const checked: boolean = !!( (field.value[`can_all_${permission.id}`] || - (findItem && findItem[permission.id])) && + (findItem && findItem[`can_${permission.id}`])) && item.guest !== permission.disableForGuest ); return ( - + { if (field.value[`can_all_${permission.id}`]) { const groupPermissions = mapValues( - keyBy(permissions, "id"), + keyBy( + permissions.map(item => ({ + id: `can_${item.id}` + })), + "id" + ), () => false ); - field.onChange({ - ...field.value, - [`can_all_${permission.id}`]: false, - groups: data.map( - (group: Pick) => { - if (group.id === item.id) { - return { - id: group.id, - ...groupPermissions, - [permission.id]: false - }; - } + const groups = data.map( + (group: Pick) => { + const findExistingGroup = field.value.groups.find( + ({ group_id }: { group_id: number }) => + group_id === group.id + ); + if (group.id === item.id) { return { - id: group.id, + group_id: group.id, ...groupPermissions, - [permission.id]: true + ...findExistingGroup, + [`can_${permission.id}`]: false }; } - ) + + return { + group_id: group.id, + ...groupPermissions, + ...findExistingGroup, + [`can_${permission.id}`]: true + }; + } + ); + + field.onChange({ + ...field.value, + [`can_all_${permission.id}`]: false, + groups }); return; @@ -193,7 +219,12 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ if (!findItem) { const groupPermissions = mapValues( - keyBy(permissions, "id"), + keyBy( + permissions.map(item => ({ + id: `can_${item.id}` + })), + "id" + ), item => { if (item.id === permission.id) { return true; @@ -221,11 +252,13 @@ export const ContentPermissionsContentCreateEditFormForumAdmin = ({ ...field.value, groups: [ ...field.value.groups.filter( - (group: { id: number }) => group.id !== item.id + (group: { group_id: number }) => + group.group_id !== item.id ), { ...findItem, - [permission.id]: !findItem[permission.id] + [`can_${permission.id}`]: + !findItem[`can_${permission.id}`] } ] }); diff --git a/frontend/admin/forum/views/forums/create-edit/hooks/mutation-create-api.ts b/frontend/admin/forum/views/forums/create-edit/hooks/mutation-create-api.ts index 91cfa92c7..448e1c8d4 100644 --- a/frontend/admin/forum/views/forums/create-edit/hooks/mutation-create-api.ts +++ b/frontend/admin/forum/views/forums/create-edit/hooks/mutation-create-api.ts @@ -1,6 +1,6 @@ "use server"; -import { revalidatePath, revalidateTag } from "next/cache"; +import { revalidatePath } from "next/cache"; import { fetcher } from "@/graphql/fetcher"; import { @@ -21,9 +21,6 @@ export const mutationCreateApi = async ( variables }); - revalidateTag("Forum_Forums__Show"); - revalidateTag("Forum_Forums__Show_Item"); - revalidateTag("Forum_Topics__Show"); revalidatePath("/admin/forum/forums", "page"); return { data }; diff --git a/frontend/admin/forum/views/forums/create-edit/hooks/mutation-edit-api.ts b/frontend/admin/forum/views/forums/create-edit/hooks/mutation-edit-api.ts index 50d9e1cb9..f06c9749a 100644 --- a/frontend/admin/forum/views/forums/create-edit/hooks/mutation-edit-api.ts +++ b/frontend/admin/forum/views/forums/create-edit/hooks/mutation-edit-api.ts @@ -1,7 +1,5 @@ "use server"; -import { revalidateTag } from "next/cache"; - import { fetcher } from "@/graphql/fetcher"; import { Admin__Forum_Forums__Edit, @@ -21,10 +19,6 @@ export const mutationEditApi = async ( variables }); - revalidateTag("Forum_Forums__Show"); - revalidateTag("Forum_Forums__Show_Item"); - revalidateTag("Forum_Topics__Show"); - return { data }; } catch (error) { return { error }; diff --git a/frontend/admin/forum/views/forums/create-edit/hooks/use-create-edit-form-forum-admin.ts b/frontend/admin/forum/views/forums/create-edit/hooks/use-create-edit-form-forum-admin.ts index 3b8a1c277..b2313d3eb 100644 --- a/frontend/admin/forum/views/forums/create-edit/hooks/use-create-edit-form-forum-admin.ts +++ b/frontend/admin/forum/views/forums/create-edit/hooks/use-create-edit-form-forum-admin.ts @@ -17,13 +17,7 @@ export const useCreateEditFormForumAdmin = ({ const { setOpen } = useDialog(); const formSchema = z.object({ - name: zodInput.languageInput - .min(1, { - message: t("errors.required") - }) - .refine(value => value.every(item => item.value.length <= 50), { - message: t("errors.max_length", { length: 50 }) - }), + name: zodInput.languageInput, description: zodInput.languageInput, permissions: z.object({ can_all_view: z.boolean(), @@ -33,7 +27,7 @@ export const useCreateEditFormForumAdmin = ({ can_all_download_files: z.boolean(), groups: z.array( z.object({ - id: z.number(), + group_id: z.number(), can_view: z.boolean(), can_read: z.boolean(), can_create: z.boolean(), @@ -50,12 +44,12 @@ export const useCreateEditFormForumAdmin = ({ name: data?.name ?? [], description: data?.description ?? [], permissions: { - can_all_view: data?.permissions.can_all_view ?? true, - can_all_read: data?.permissions.can_all_read ?? true, - can_all_create: data?.permissions.can_all_create ?? true, - can_all_reply: data?.permissions.can_all_reply ?? true, + can_all_view: data?.permissions.can_all_view ?? false, + can_all_read: data?.permissions.can_all_read ?? false, + can_all_create: data?.permissions.can_all_create ?? false, + can_all_reply: data?.permissions.can_all_reply ?? false, can_all_download_files: - data?.permissions.can_all_download_files ?? true, + data?.permissions.can_all_download_files ?? false, groups: data?.permissions.groups ?? [] } }, diff --git a/frontend/admin/forum/views/forums/table/hooks/mutation-change-position-api.ts b/frontend/admin/forum/views/forums/table/hooks/mutation-change-position-api.ts index da0fc864c..c1aa29f58 100644 --- a/frontend/admin/forum/views/forums/table/hooks/mutation-change-position-api.ts +++ b/frontend/admin/forum/views/forums/table/hooks/mutation-change-position-api.ts @@ -1,7 +1,5 @@ "use server"; -import { revalidateTag } from "next/cache"; - import { fetcher } from "@/graphql/fetcher"; import { type Admin__Forum_Forums__Change_PositionMutation, @@ -21,9 +19,6 @@ export const mutationChangePositionApi = async ( variables }); - revalidateTag("Forum_Forums__Show"); - revalidateTag("Forum_Forums__Show_Item"); - return { data }; } catch (error) { return { error }; diff --git a/frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts b/frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts index cb85ba5fe..4f14ba0bf 100644 --- a/frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts +++ b/frontend/admin/forum/views/forums/table/hooks/mutation-update-data-api.ts @@ -1,7 +1,5 @@ "use server"; -import { revalidateTag } from "next/cache"; - import { fetcher } from "@/graphql/fetcher"; import { Admin__Forum_Forums__Show, @@ -21,10 +19,6 @@ export const mutationUpdateDataApi = async ( variables }); - revalidateTag("Forum_Forums__Show"); - revalidateTag("Forum_Forums__Show_Item"); - revalidateTag("Forum_Topics__Show"); - return { data }; } catch (error) { return { error }; diff --git a/frontend/admin/forum/views/forums/table/item/actions/delete/hooks/mutation-api.ts b/frontend/admin/forum/views/forums/table/item/actions/delete/hooks/mutation-api.ts index 1fc2c476c..355dcf1d7 100644 --- a/frontend/admin/forum/views/forums/table/item/actions/delete/hooks/mutation-api.ts +++ b/frontend/admin/forum/views/forums/table/item/actions/delete/hooks/mutation-api.ts @@ -1,7 +1,5 @@ "use server"; -import { revalidateTag } from "next/cache"; - import { fetcher } from "@/graphql/fetcher"; import { Admin__Forum_Forums__Delete, @@ -21,10 +19,6 @@ export const mutationApi = async ( variables }); - revalidateTag("Forum_Forums__Show"); - revalidateTag("Forum_Forums__Show_Item"); - revalidateTag("Forum_Topics__Show"); - return { data }; } catch (error) { return { error }; diff --git a/frontend/app/[locale]/(main)/(container)/forum/[id]/query-api.ts b/frontend/app/[locale]/(main)/(container)/forum/[id]/query-api.ts index cd657d6de..7f7c7dec4 100644 --- a/frontend/app/[locale]/(main)/(container)/forum/[id]/query-api.ts +++ b/frontend/app/[locale]/(main)/(container)/forum/[id]/query-api.ts @@ -19,10 +19,6 @@ export const getForumItemData = async ({ id }: { id: string }) => { lastPostsArgs: { first: 1 } - }, - cache: "force-cache", - next: { - tags: ["Forum_Forums__Show_Item"] } }); diff --git a/frontend/app/[locale]/(main)/(container)/forum/topic/[id]/query-api.ts b/frontend/app/[locale]/(main)/(container)/forum/topic/[id]/query-api.ts index 7209b25e3..49f0edd0b 100644 --- a/frontend/app/[locale]/(main)/(container)/forum/topic/[id]/query-api.ts +++ b/frontend/app/[locale]/(main)/(container)/forum/topic/[id]/query-api.ts @@ -32,10 +32,6 @@ export const getTopicData = async ({ id: getIdFormString(id), sortBy, limit - }, - cache: "force-cache", - next: { - tags: ["Forum_Topics__Show"] } }); diff --git a/frontend/components/editor/editor.tsx b/frontend/components/editor/editor.tsx index 0492cdeda..7fc710522 100644 --- a/frontend/components/editor/editor.tsx +++ b/frontend/components/editor/editor.tsx @@ -128,7 +128,7 @@ export const Editor = ({
diff --git a/frontend/components/editor/extensions/files/files.ts b/frontend/components/editor/extensions/files/files.ts index b39453664..167ad1e2e 100644 --- a/frontend/components/editor/extensions/files/files.ts +++ b/frontend/components/editor/extensions/files/files.ts @@ -24,6 +24,7 @@ export interface FilesHandlerAttributes { mimetype: string; file_alt?: string; height?: number; + security_key?: string; width?: number; } @@ -52,7 +53,6 @@ export const FilesHandler = ({ uploadFiles }: FilesHandlerArgs) => name: "files", group: "inline", inline: true, - // content: "inline*", atom: true, selectable: true, draggable: true, @@ -87,6 +87,9 @@ export const FilesHandler = ({ uploadFiles }: FilesHandlerArgs) => }, height: { default: 0 + }, + security_key: { + default: "" } }; }, diff --git a/frontend/components/editor/footer/files/item/item.tsx b/frontend/components/editor/footer/files/item/item.tsx index c2573f6a0..69fa1c77b 100644 --- a/frontend/components/editor/footer/files/item/item.tsx +++ b/frontend/components/editor/footer/files/item/item.tsx @@ -51,7 +51,7 @@ export const ItemListFilesFooterEditor = ({ /> -
+
{file?.name ?? data?.file_name ?? "Error!"} @@ -76,6 +76,7 @@ export const ItemListFilesFooterEditor = ({ file_alt: data.file_alt ?? "", width: data.width ?? 0, height: data.height ?? 0, + security_key: data.security_key ?? "", id }); editor.commands.focus(); diff --git a/frontend/components/editor/read-only/file-download-button.tsx b/frontend/components/editor/read-only/file-download-button.tsx index d28305e11..9fa1f167f 100644 --- a/frontend/components/editor/read-only/file-download-button.tsx +++ b/frontend/components/editor/read-only/file-download-button.tsx @@ -2,6 +2,7 @@ import { File } from "lucide-react"; import Image from "next/image"; +import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { formatBytes } from "@/functions/format-bytes"; @@ -15,12 +16,15 @@ interface Props { file_size: number; id: number; mimetype: string; + allowDownloadAttachments?: boolean; file_alt?: string; height?: number; + security_key?: string; width?: number; } export const FileDownloadButton = ({ + allowDownloadAttachments, dir_folder, file_alt, file_name, @@ -29,8 +33,11 @@ export const FileDownloadButton = ({ height, id, mimetype, + security_key, width }: Props) => { + const t = useTranslations("core.editor.files"); + if (acceptMimeTypeImage.includes(mimetype) && width && height) { return ( @@ -46,16 +53,37 @@ export const FileDownloadButton = ({ ); } + if (!allowDownloadAttachments) { + return ( + + ); + } + return (