diff --git a/.env.dev b/.env.dev index e423091..402ca7e 100644 --- a/.env.dev +++ b/.env.dev @@ -33,7 +33,7 @@ SMS_PATTERN_NEW_TICKET_GLOBAL=536596 SMS_PATTERN_NEW_CRITICISM=983724 SMS_PATTERN_USER_PASSWORD=486237 SMTP_HOST=smtps.danakcorp.com -SMTP_PORT=465 +SMTP_PORT=587 SMTP_USER=no-reply-danakcorp.com SMTP_PASS=Mahyar.SR4@ MAIL_FROM=no-reply diff --git a/database/migrations/1785200000000-addBlogCategoriesCommentsSlidersPermissions.ts b/database/migrations/1785200000000-addBlogCategoriesCommentsSlidersPermissions.ts new file mode 100644 index 0000000..e94a681 --- /dev/null +++ b/database/migrations/1785200000000-addBlogCategoriesCommentsSlidersPermissions.ts @@ -0,0 +1,191 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +const NEW_PERMISSIONS = [ + { name: "blog_categories_create", title: "ایجاد دسته‌بندی بلاگ", source: "blogs_create" }, + { name: "blog_categories_read", title: "مشاهده دسته‌بندی بلاگ", source: "blogs_read" }, + { name: "blog_categories_update", title: "ویرایش دسته‌بندی بلاگ", source: "blogs_update" }, + { name: "blog_categories_delete", title: "حذف دسته‌بندی بلاگ", source: "blogs_delete" }, + { name: "blog_comments_create", title: "ایجاد نظرات بلاگ", source: "blogs_create" }, + { name: "blog_comments_read", title: "مشاهده نظرات بلاگ", source: "blogs_read" }, + { name: "blog_comments_update", title: "ویرایش نظرات بلاگ", source: "blogs_update" }, + { name: "blog_comments_delete", title: "حذف نظرات بلاگ", source: "blogs_delete" }, + { name: "sliders_create", title: "ایجاد اسلایدر", source: "blogs_create" }, + { name: "sliders_read", title: "مشاهده اسلایدر", source: "blogs_read" }, + { name: "sliders_update", title: "ویرایش اسلایدر", source: "blogs_update" }, + { name: "sliders_delete", title: "حذف اسلایدر", source: "blogs_delete" }, +] as const; + +const CURRENT_ENUM_VALUES = [ + "services_create", + "services_read", + "services_update", + "services_delete", + "customers_create", + "customers_read", + "customers_update", + "customers_delete", + "agents_create", + "agents_read", + "agents_update", + "agents_delete", + "developers_create", + "developers_read", + "developers_update", + "developers_delete", + "invoices_create", + "invoices_read", + "invoices_update", + "invoices_delete", + "transactions_create", + "transactions_read", + "transactions_update", + "transactions_delete", + "discounts_create", + "discounts_read", + "discounts_update", + "discounts_delete", + "admins_create", + "admins_read", + "admins_update", + "admins_delete", + "tickets_create", + "tickets_read", + "tickets_update", + "tickets_delete", + "ticket_categories_create", + "ticket_categories_read", + "ticket_categories_update", + "ticket_categories_delete", + "ticket_masters_create", + "ticket_masters_read", + "ticket_masters_update", + "ticket_masters_delete", + "criticisms_create", + "criticisms_read", + "criticisms_update", + "criticisms_delete", + "contacts_us_create", + "contacts_us_read", + "contacts_us_update", + "contacts_us_delete", + "advertisements_create", + "advertisements_read", + "advertisements_update", + "advertisements_delete", + "announcements_create", + "announcements_read", + "announcements_update", + "announcements_delete", + "blogs_create", + "blogs_read", + "blogs_update", + "blogs_delete", + "learnings_create", + "learnings_read", + "learnings_update", + "learnings_delete", + "settings_create", + "settings_read", + "settings_update", + "settings_delete", + "bank_accounts_create", + "bank_accounts_read", + "bank_accounts_update", + "bank_accounts_delete", + "payments_create", + "payments_read", + "payments_update", + "payments_delete", + "support_plan_create", + "support_plan_read", + "support_plan_update", + "support_plan_delete", + "reseller_create", + "reseller_read", + "reseller_update", + "reseller_delete", + "workspace_create", + "workspace_read", + "workspace_update", + "workspace_delete", + "project_create", + "project_read", + "project_update", + "project_delete", + "task_create", + "task_read", + "task_update", + "task_delete", + "task_phase_create", + "task_phase_read", + "task_phase_update", + "task_phase_delete", + "logs", + "manage_sso_clients", + "dmenu", + "dkala", + "dpage", + "dmail", + "view_all_workspaces", + "view_all_projects", +]; + +const UPDATED_ENUM_VALUES = [ + ...CURRENT_ENUM_VALUES.slice(0, CURRENT_ENUM_VALUES.indexOf("blogs_delete") + 1), + ...NEW_PERMISSIONS.map((p) => p.name), + ...CURRENT_ENUM_VALUES.slice(CURRENT_ENUM_VALUES.indexOf("blogs_delete") + 1), +]; + +export class AddBlogCategoriesCommentsSlidersPermissions1785200000000 implements MigrationInterface { + name = "AddBlogCategoriesCommentsSlidersPermissions1785200000000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE text USING "name"::text`); + await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`); + + const enumList = UPDATED_ENUM_VALUES.map((v) => `'${v}'`).join(", "); + await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM(${enumList})`); + await queryRunner.query( + `ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"public"."permission_name_enum"`, + ); + + for (const perm of NEW_PERMISSIONS) { + const inserted = await queryRunner.query( + `INSERT INTO "permission" ("id", "name", "title", "group", "createdAt", "updatedAt") + VALUES (uuid_generate_v4(), $1, $2, 'content', NOW(), NOW()) + RETURNING id`, + [perm.name, perm.title], + ); + const newId = inserted[0].id as string; + + const sourcePerm = await queryRunner.query(`SELECT id FROM "permission" WHERE "name" = $1`, [perm.source]); + if (!sourcePerm.length) continue; + + await queryRunner.query( + `INSERT INTO "role_permission_relation" ("roleId", "permissionId") + SELECT "roleId", $1 FROM "role_permission_relation" WHERE "permissionId" = $2 + ON CONFLICT ("roleId", "permissionId") DO NOTHING`, + [newId, sourcePerm[0].id], + ); + } + } + + public async down(queryRunner: QueryRunner): Promise { + for (const perm of NEW_PERMISSIONS) { + const rows = await queryRunner.query(`SELECT id FROM "permission" WHERE "name" = $1`, [perm.name]); + if (!rows.length) continue; + + await queryRunner.query(`DELETE FROM "role_permission_relation" WHERE "permissionId" = $1`, [rows[0].id]); + await queryRunner.query(`DELETE FROM "permission" WHERE "id" = $1`, [rows[0].id]); + } + + await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE text USING "name"::text`); + await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`); + + const enumList = CURRENT_ENUM_VALUES.map((v) => `'${v}'`).join(", "); + await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM(${enumList})`); + await queryRunner.query( + `ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"public"."permission_name_enum"`, + ); + } +} diff --git a/dsc.code-workspace b/dsc.code-workspace index e801e60..278bba2 100644 --- a/dsc.code-workspace +++ b/dsc.code-workspace @@ -1,10 +1,10 @@ { "folders": [ { - "path": "./" + "path": "." }, { - "path": "./danak-admin" + "path": "../danak-admin" } ], "settings": {} diff --git a/src/configs/mailer.config.ts b/src/configs/mailer.config.ts index 81ca9cc..c1cba53 100755 --- a/src/configs/mailer.config.ts +++ b/src/configs/mailer.config.ts @@ -5,27 +5,33 @@ import { MailerAsyncOptions } from "@nestjs-modules/mailer/dist/interfaces/maile export function mailerConfig(): MailerAsyncOptions { return { inject: [ConfigService], - useFactory: (configService: ConfigService) => ({ - transport: { - host: configService.getOrThrow("SMTP_HOST"), - port: configService.getOrThrow("SMTP_PORT"), - secure: true, - auth: { - user: configService.getOrThrow("SMTP_USER"), - pass: configService.getOrThrow("SMTP_PASS"), - }, - }, - defaults: { - from: configService.getOrThrow("MAIL_FROM"), - }, + useFactory: (configService: ConfigService) => { + const port = Number(configService.getOrThrow("SMTP_PORT")); + // 465 = implicit TLS; 587 = STARTTLS (required by zone-mta behind Traefik) + const secure = port === 465; - template: { - dir: process.cwd() + "/src/modules/templates/email", - adapter: new HandlebarsAdapter(), - options: { - strict: true, + return { + transport: { + host: configService.getOrThrow("SMTP_HOST"), + port, + secure, + requireTLS: !secure, + auth: { + user: configService.getOrThrow("SMTP_USER"), + pass: configService.getOrThrow("SMTP_PASS"), + }, }, - }, - }), + defaults: { + from: configService.getOrThrow("MAIL_FROM"), + }, + template: { + dir: process.cwd() + "/src/modules/templates/email", + adapter: new HandlebarsAdapter(), + options: { + strict: true, + }, + }, + }; + }, }; } diff --git a/src/modules/blogs/blogs.controller.ts b/src/modules/blogs/blogs.controller.ts index 3fae9cf..f5325e5 100755 --- a/src/modules/blogs/blogs.controller.ts +++ b/src/modules/blogs/blogs.controller.ts @@ -149,7 +149,7 @@ export class BlogsController { @ApiOperation({ summary: "Get all comments for a blog (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_READ) + @PermissionsDec(PermissionEnum.BLOG_COMMENTS_READ) @Get(":id/comments") getBlogCommentsForAdmin(@Param() paramDto: ParamDto) { return this.blogCommentService.getBlogCommentsForAdmin(paramDto.id); @@ -157,7 +157,7 @@ export class BlogsController { @ApiOperation({ summary: "Get all comments for a blog (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_READ) + @PermissionsDec(PermissionEnum.BLOG_COMMENTS_READ) @Get("comments") getAllCommentForAdmin(@Query() queryDto: BlogCommentQueryDto) { return this.blogCommentService.getAllCommentForAdmin(queryDto); @@ -165,7 +165,7 @@ export class BlogsController { @ApiOperation({ summary: "Update the status of a comment (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_UPDATE) + @PermissionsDec(PermissionEnum.BLOG_COMMENTS_UPDATE) @Patch("comments/:id/status") updateCommentStatus(@Param() paramDto: ParamDto, @Body() updateCommentStatusDto: UpdateCommentStatusDto) { return this.blogCommentService.updateCommentStatus(paramDto.id, updateCommentStatusDto); @@ -196,7 +196,7 @@ export class BlogsController { @ApiOperation({ summary: "Create a new category (admin route)" }) @Post("categories") @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_CREATE) + @PermissionsDec(PermissionEnum.BLOG_CATEGORIES_CREATE) createCategory(@Body() createCategoryDto: CreateBlogCategoryDto) { return this.blogsService.createCategory(createCategoryDto); } @@ -204,7 +204,7 @@ export class BlogsController { @ApiOperation({ summary: "Update a category (admin route)" }) @Patch("categories/:id") @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_UPDATE) + @PermissionsDec(PermissionEnum.BLOG_CATEGORIES_UPDATE) updateCategory(@Param() paramDto: ParamDto, @Body() updateCategoryDto: UpdateBlogCategoryDto) { return this.blogsService.updateCategory(paramDto.id, updateCategoryDto); } @@ -212,13 +212,13 @@ export class BlogsController { @ApiOperation({ summary: "Delete a category (admin route)" }) @Delete("categories/:id") @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_DELETE) + @PermissionsDec(PermissionEnum.BLOG_CATEGORIES_DELETE) deleteCategory(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { return this.blogsService.deleteCategory(paramDto.id, userIpAndHeaders); } @ApiOperation({ summary: "toggle status of categories (admin route)" }) - @PermissionsDec(PermissionEnum.BLOGS_CREATE) + @PermissionsDec(PermissionEnum.BLOG_CATEGORIES_UPDATE) @HttpCode(HttpStatus.OK) @Post("categories/toggle-status/:id") toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { diff --git a/src/modules/sliders/sliders.controller.ts b/src/modules/sliders/sliders.controller.ts index d64eadf..490e331 100755 --- a/src/modules/sliders/sliders.controller.ts +++ b/src/modules/sliders/sliders.controller.ts @@ -18,7 +18,7 @@ export class SlidersController { @ApiOperation({ summary: "create slider (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_CREATE) + @PermissionsDec(PermissionEnum.SLIDERS_CREATE) @Post() create(@Body() createSliderDto: CreateSliderDto) { return this.slidersService.create(createSliderDto); @@ -26,7 +26,7 @@ export class SlidersController { @ApiOperation({ summary: "Get all sliders (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_READ) + @PermissionsDec(PermissionEnum.SLIDERS_READ) @Get() getSlidersList(@Query() queryDto: PaginationDto) { return this.slidersService.getSlidersList(queryDto); @@ -34,7 +34,7 @@ export class SlidersController { @ApiOperation({ summary: "Get a slider by id (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_READ) + @PermissionsDec(PermissionEnum.SLIDERS_READ) @Get(":id") getSliderById(@Param() paramDto: ParamDto) { return this.slidersService.getSliderById(paramDto.id); @@ -42,7 +42,7 @@ export class SlidersController { @ApiOperation({ summary: "Update a slider (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_UPDATE) + @PermissionsDec(PermissionEnum.SLIDERS_UPDATE) @Patch(":id") update(@Param() paramDto: ParamDto, @Body() updateSliderDto: UpdateSliderDto) { return this.slidersService.update(paramDto.id, updateSliderDto); @@ -50,7 +50,7 @@ export class SlidersController { @ApiOperation({ summary: "Delete a slider (admin route)" }) @AdminRoute() - @PermissionsDec(PermissionEnum.BLOGS_DELETE) + @PermissionsDec(PermissionEnum.SLIDERS_DELETE) @Delete(":id") remove(@Param() paramDto: ParamDto) { return this.slidersService.remove(paramDto.id); diff --git a/src/modules/users/constants/permission-meta.ts b/src/modules/users/constants/permission-meta.ts index d921db0..81ca679 100644 --- a/src/modules/users/constants/permission-meta.ts +++ b/src/modules/users/constants/permission-meta.ts @@ -87,6 +87,21 @@ export const permissionMeta: Record = { [PermissionEnum.BLOGS_UPDATE]: { title: "ویرایش بلاگ‌ها", group: PermissionGroupEnum.CONTENT }, [PermissionEnum.BLOGS_DELETE]: { title: "حذف بلاگ‌ها", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.BLOG_CATEGORIES_CREATE]: { title: "ایجاد دسته‌بندی بلاگ", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.BLOG_CATEGORIES_READ]: { title: "مشاهده دسته‌بندی بلاگ", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.BLOG_CATEGORIES_UPDATE]: { title: "ویرایش دسته‌بندی بلاگ", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.BLOG_CATEGORIES_DELETE]: { title: "حذف دسته‌بندی بلاگ", group: PermissionGroupEnum.CONTENT }, + + [PermissionEnum.BLOG_COMMENTS_CREATE]: { title: "ایجاد نظرات بلاگ", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.BLOG_COMMENTS_READ]: { title: "مشاهده نظرات بلاگ", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.BLOG_COMMENTS_UPDATE]: { title: "ویرایش نظرات بلاگ", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.BLOG_COMMENTS_DELETE]: { title: "حذف نظرات بلاگ", group: PermissionGroupEnum.CONTENT }, + + [PermissionEnum.SLIDERS_CREATE]: { title: "ایجاد اسلایدر", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.SLIDERS_READ]: { title: "مشاهده اسلایدر", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.SLIDERS_UPDATE]: { title: "ویرایش اسلایدر", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.SLIDERS_DELETE]: { title: "حذف اسلایدر", group: PermissionGroupEnum.CONTENT }, + [PermissionEnum.LEARNINGS_CREATE]: { title: "ایجاد آموزش‌ها", group: PermissionGroupEnum.CONTENT }, [PermissionEnum.LEARNINGS_READ]: { title: "مشاهده آموزش‌ها", group: PermissionGroupEnum.CONTENT }, [PermissionEnum.LEARNINGS_UPDATE]: { title: "ویرایش آموزش‌ها", group: PermissionGroupEnum.CONTENT }, diff --git a/src/modules/users/enums/permission.enum.ts b/src/modules/users/enums/permission.enum.ts index b3306ee..0f377ae 100755 --- a/src/modules/users/enums/permission.enum.ts +++ b/src/modules/users/enums/permission.enum.ts @@ -95,6 +95,24 @@ export enum PermissionEnum { BLOGS_UPDATE = "blogs_update", BLOGS_DELETE = "blogs_delete", + // blog_categories + BLOG_CATEGORIES_CREATE = "blog_categories_create", + BLOG_CATEGORIES_READ = "blog_categories_read", + BLOG_CATEGORIES_UPDATE = "blog_categories_update", + BLOG_CATEGORIES_DELETE = "blog_categories_delete", + + // blog_comments + BLOG_COMMENTS_CREATE = "blog_comments_create", + BLOG_COMMENTS_READ = "blog_comments_read", + BLOG_COMMENTS_UPDATE = "blog_comments_update", + BLOG_COMMENTS_DELETE = "blog_comments_delete", + + // sliders + SLIDERS_CREATE = "sliders_create", + SLIDERS_READ = "sliders_read", + SLIDERS_UPDATE = "sliders_update", + SLIDERS_DELETE = "sliders_delete", + // learnings LEARNINGS_CREATE = "learnings_create", LEARNINGS_READ = "learnings_read",