Compare commits
93 Commits
9b32f35582
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f78691f91 | |||
| aef712ca1d | |||
| 77783549fa | |||
| be6e4a8d32 | |||
| af606ce9a7 | |||
| 7996617cf0 | |||
| 9e28dfd628 | |||
| c20e0ba684 | |||
| ced46e500d | |||
| c925d2e539 | |||
| 10035f3f25 | |||
| 19c396e9d1 | |||
| a13097bbd6 | |||
| 8aaac51b12 | |||
| b855d81817 | |||
| f66bc9fa8c | |||
| 6b222db890 | |||
| c82cc654f0 | |||
| c956bc0deb | |||
| 30d86c9775 | |||
| 4263d7118e | |||
| 0e8e0cba2e | |||
| 3a84740d9a | |||
| f35df6492c | |||
| 008bddb97a | |||
| cb77a55281 | |||
| 11445f0a07 | |||
| 8e23a06793 | |||
| c2a861041f | |||
| 1440b62373 | |||
| d93e4be6ef | |||
| 50d9fb2274 | |||
| 9ded147354 | |||
| 2768611a7c | |||
| b1a02d6b0d | |||
| 8b901e4d02 | |||
| 43b4f05894 | |||
| 7bef7d414a | |||
| 4cb20f6c0d | |||
| 95c947a587 | |||
| 8348692481 | |||
| 677fc5b384 | |||
| 8481a0e86f | |||
| b32d613a1b | |||
| b0668c7b2c | |||
| ff08da1330 | |||
| 29d647556c | |||
| a22d6ceb81 | |||
| eb6fbc3ce5 | |||
| 9565eda0fd | |||
| 8533682303 | |||
| e5c14bf5cc | |||
| e310411a4d | |||
| 0cd5a6a0cc | |||
| da71c20d6d | |||
| 263ede68b2 | |||
| d9283e3a90 | |||
| 5964a9b20d | |||
| 5715cbe9b1 | |||
| 2590a222b4 | |||
| 16a0c14149 | |||
| 9f61152d94 | |||
| a14bd490be | |||
| 47a5a2abb4 | |||
| 5763cb7d6c | |||
| 35d0d9f7cd | |||
| d4bd593f5b | |||
| c5b2cb93f0 | |||
| 30dba5ae37 | |||
| e57dd84ebc | |||
| 8d962ea6ee | |||
| 06ec73be92 | |||
| c22a41d29b | |||
| 4b95d35d87 | |||
| fc23a94fb6 | |||
| 1b70e49d93 | |||
| 3ad73a5271 | |||
| a51b19f85c | |||
| a199623eb4 | |||
| 4f76af87a6 | |||
| 27e9ad76a5 | |||
| c99a8a435c | |||
| 743d1eb79d | |||
| c2e0cb1bc6 | |||
| 6ad18d8f3e | |||
| db61030b44 | |||
| 31e3b5f00d | |||
| ff4a8dedba | |||
| 87772f46dd | |||
| 26b8268f73 | |||
| 3d59a2fa40 | |||
| bf98b8b9ce | |||
| 89d85e6d24 |
@@ -0,0 +1,75 @@
|
|||||||
|
NODE_ENV=production
|
||||||
|
PORT=3500
|
||||||
|
DB_NAME=dscDev
|
||||||
|
DB_USER=postgres
|
||||||
|
DB_PASS=1RWVdlUcZSf6PoFBQO8cprZk2DTkC89ttqeJN7aU2WQRDUEusK4z2FfNqNtV4I10
|
||||||
|
DB_HOST=78.157.34.13
|
||||||
|
DB_PORT=5432
|
||||||
|
SMS_API_URL=https://api.sms.ir/v1
|
||||||
|
SMS_API_KEY=QkkNxhyXZ6GtEf1soOTtikomO3mA4LaNQDH8mol8huDIwh00
|
||||||
|
SMS_PATTERN_OTP=842088
|
||||||
|
SMS_PATTERN_INVOICE=683883
|
||||||
|
SMS_PATTERN_LOGIN=149141
|
||||||
|
SMS_PATTERN_INVOICE_CREATED=829067
|
||||||
|
SMS_PATTERN_ANNOUNCEMENT=752330
|
||||||
|
SMS_PATTERN_WALLET_CHARGE=727726
|
||||||
|
SMS_PATTERN_WALLET_DEDUCTION=360785
|
||||||
|
SMS_PATTERN_TICKET_CREATED=853506
|
||||||
|
SMS_PATTERN_TICKET_ANSWERED=179464
|
||||||
|
SMS_PATTERN_TICKET_ASSIGNED_ADMIN=321927
|
||||||
|
SMS_PATTERN_INVOICE_APPROVED=303807
|
||||||
|
SMS_PATTERN_INVOICE_PAID=533817
|
||||||
|
SMS_PATTERN_RECURRING_INVOICE_DRAFT=219287
|
||||||
|
SMS_PATTERN_SUBSCRIPTION_CANCELLED=166142
|
||||||
|
SMS_PATTERN_INVOICE_REMINDER=370266
|
||||||
|
SMS_PATTERN_INVOICE_OVERDUE=160735
|
||||||
|
SMS_PATTERN_PAYMENT_REMINDER=629772
|
||||||
|
SMS_PATTERN_PAYMENT_CANCELLATION=197945
|
||||||
|
SMS_PATTERN_BLOG_NEW_COMMENT=378881
|
||||||
|
SMS_PATTERN_NEW_SERVICE_REVIEW=593446
|
||||||
|
SMS_PATTERN_NEW_CUSTOMER=966214
|
||||||
|
SMS_PATTERN_NEW_SUBSCRIPTION=757095
|
||||||
|
SMS_PATTERN_NEW_TICKET_GLOBAL=536596
|
||||||
|
SMS_PATTERN_NEW_CRITICISM=983724
|
||||||
|
SMS_PATTERN_USER_PASSWORD=486237
|
||||||
|
SMTP_HOST=smtps.danakcorp.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER=no-reply-danakcorp.com
|
||||||
|
SMTP_PASS=Mahyar.SR4@
|
||||||
|
MAIL_FROM=no-reply <no-reply@danakcorp.com>
|
||||||
|
EMAIL_SECRET=xDkdptbC2N7LoR2Aa4Q9U/aAGOi4DxG2748
|
||||||
|
REDIS_URI=redis://localhost:6379/1
|
||||||
|
CACHE_TTL=180000
|
||||||
|
THROTTLE_TTL=300000
|
||||||
|
THROTTLE_LIMIT=10
|
||||||
|
JWT_SECRET_KEY=2ygxIB1PJhK/L2PToqR3+52gRmUIdPfwTguYlB7ZRCdRyvA/N36jjkjbYJLoMxEL\n6xGmk5CQmCmn9c0FsBlN1g
|
||||||
|
JWT_ISSUER=console-danak
|
||||||
|
ACCESS_TOKEN_EXPIRE=80000
|
||||||
|
REFRESH_TOKEN_EXPIRE=6
|
||||||
|
BUCKET_NAME=danak-project
|
||||||
|
BUCKET_REGION=default
|
||||||
|
BUCKET_URL=https://s3.ir-thr-at1.arvanstorage.ir
|
||||||
|
BUCKET_ACCESS_KEY=4bd15218-60f1-4ce7-a7aa-3fd319b13372
|
||||||
|
BUCKET_SECRET_KEY=de772e5bd62fb04fb3669e67e09ddb101db60c65d77eea66b215ce58275e6a76
|
||||||
|
BUCKET_UPLOAD_URL=https://storage.danakcorp.com
|
||||||
|
ZARINPAL_MERCHANT_ID=fc0b7503-bba5-40d1-9f9d-dae52ac02eef
|
||||||
|
PARSIAN_LOGIN_ACCOUNT=uqXnB2q8rjkL6vN0xOh0
|
||||||
|
SITE_URL=https://console.danakcorp.com
|
||||||
|
API_URL=https://api.danakcorp.com
|
||||||
|
CALLBACK_URL=https://api.danakcorp.com/payments/verify
|
||||||
|
TELEGRAM_BOT_TOKEN=7839690719:AAGsyfwympR4rDLm2JCx43CQh52s1o865uY
|
||||||
|
TELEGRAM_BOT_NAME=danak_logs_bot
|
||||||
|
TELEGRAM_CHAT_ID=7801476800
|
||||||
|
EXTERNAL_API_KEYS=uqXssnB2q8rs2soa9almm1jkL6vN0xOh0
|
||||||
|
DMENU_BACKEND_URL=https://dmenu-api.danakcorp.com
|
||||||
|
DMENU_USERNAME=danak@dsc.com
|
||||||
|
DMENU_PASSWORD=DsCdAnAk?@ABC
|
||||||
|
DKALA_BACKEND_URL=https://dkala-api.danakcorp.com
|
||||||
|
DKALA_USERNAME=danak@dsc.com
|
||||||
|
DKALA_PASSWORD=DsCdAnAk?@ABC
|
||||||
|
DPAGE_BACKEND_URL=https://dpage-api.danakcorp.com
|
||||||
|
DPAGE_USERNAME=danak@dsc.com
|
||||||
|
DPAGE_PASSWORD=DsCdAnAk?@ABC
|
||||||
|
DMAIL_BACKEND_URL=https://dmail-api.danakcorp.com
|
||||||
|
DMAIL_USERNAME=danak@dsc.com
|
||||||
|
DMAIL_PASSWORD=DsCdAnAk?@ABC
|
||||||
+11
-7
@@ -2,10 +2,14 @@
|
|||||||
FROM node:22-alpine AS base
|
FROM node:22-alpine AS base
|
||||||
|
|
||||||
ENV TZ=Asia/Tehran
|
ENV TZ=Asia/Tehran
|
||||||
|
# Use the public npm registry to avoid paid/unstable mirror outages.
|
||||||
|
ENV NPM_CONFIG_REGISTRY=https://package-mirror.liara.ir/repository/npm
|
||||||
|
|
||||||
# Runflare build hosts cannot reach registry.npmjs.org; use their npm mirror.
|
# Change Alpine repo
|
||||||
# RUN npm install -g pnpm@9.15.9 --registry=https://mirror-npm.runflare.com
|
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.de.velop.ir/alpine|g' /etc/apk/repositories
|
||||||
RUN npm install -g pnpm@9.15.9
|
|
||||||
|
# Install pnpm globally (uses NPM_CONFIG_REGISTRY mirror above).
|
||||||
|
RUN npm install -g pnpm@9.15.9 --registry=https://package-mirror.liara.ir/repository/npm
|
||||||
|
|
||||||
# Stage 2: Install all dependencies (including dev)
|
# Stage 2: Install all dependencies (including dev)
|
||||||
FROM base AS deps
|
FROM base AS deps
|
||||||
@@ -15,9 +19,9 @@ COPY package.json pnpm-lock.yaml ./
|
|||||||
# @nestjs/cli and typescript are required for the build step.
|
# @nestjs/cli and typescript are required for the build step.
|
||||||
ENV NODE_ENV=development
|
ENV NODE_ENV=development
|
||||||
# RUN pnpm install --frozen-lockfile --loglevel info \
|
# RUN pnpm install --frozen-lockfile --loglevel info \
|
||||||
# --registry=https://mirror-npm.runflare.com
|
# --registry=https://package-mirror.liara.ir/repository/npm
|
||||||
|
|
||||||
RUN pnpm install --frozen-lockfile --loglevel info
|
RUN pnpm install --frozen-lockfile --loglevel info --registry=https://package-mirror.liara.ir/repository/npm
|
||||||
|
|
||||||
|
|
||||||
# Stage 3: Build
|
# Stage 3: Build
|
||||||
@@ -32,8 +36,8 @@ FROM base AS prod-deps
|
|||||||
WORKDIR /temp-deps
|
WORKDIR /temp-deps
|
||||||
COPY package.json pnpm-lock.yaml ./
|
COPY package.json pnpm-lock.yaml ./
|
||||||
# RUN pnpm install --prod --frozen-lockfile --loglevel info \
|
# RUN pnpm install --prod --frozen-lockfile --loglevel info \
|
||||||
# --registry=https://mirror-npm.runflare.com
|
# --registry=https://package-mirror.liara.ir/repository/npm
|
||||||
RUN pnpm install --prod --frozen-lockfile --loglevel info
|
RUN pnpm install --prod --frozen-lockfile --loglevel info --registry=https://package-mirror.liara.ir/repository/npm
|
||||||
|
|
||||||
|
|
||||||
# Stage 5: Runner
|
# Stage 5: Runner
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class TaskmanagerProject1782889623829 implements MigrationInterface {
|
||||||
|
name = 'TaskmanagerProject1782889623829'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "tm_project_users" ("projectId" uuid NOT NULL, "userId" uuid NOT NULL, CONSTRAINT "PK_5969064e19a1efa76b91de6a065" PRIMARY KEY ("projectId", "userId"))`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_fcc8e135e4891925d288dbb14e" ON "tm_project_users" ("projectId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_8e2d2c9d639d5fd806159f8698" ON "tm_project_users" ("userId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" ADD "backgroundPicture" text`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" ADD "backgroundColor" character varying(20)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" ADD "isActive" boolean NOT NULL DEFAULT true`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" ADD CONSTRAINT "FK_fcc8e135e4891925d288dbb14e5" FOREIGN KEY ("projectId") REFERENCES "tm_projects"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" ADD CONSTRAINT "FK_8e2d2c9d639d5fd806159f86980" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" DROP CONSTRAINT "FK_8e2d2c9d639d5fd806159f86980"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" DROP CONSTRAINT "FK_fcc8e135e4891925d288dbb14e5"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" DROP COLUMN "isActive"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" DROP COLUMN "backgroundColor"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" DROP COLUMN "backgroundPicture"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_8e2d2c9d639d5fd806159f8698"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_fcc8e135e4891925d288dbb14e"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "tm_project_users"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class Taskmanager1783150480192 implements MigrationInterface {
|
||||||
|
name = 'Taskmanager1783150480192'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_workspaces" DROP CONSTRAINT "FK_253e36f065cfc1eca58abaefb22"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_workspaces" DROP COLUMN "workspaceTypeId"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_workspaces" ADD "workspaceTypeId" uuid NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_workspaces" ADD CONSTRAINT "FK_253e36f065cfc1eca58abaefb22" FOREIGN KEY ("workspaceTypeId") REFERENCES "tm_workspace_types"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class Taskmanager31783244669371 implements MigrationInterface {
|
||||||
|
name = 'Taskmanager31783244669371'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_workspaces" ADD CONSTRAINT "UQ_6496168de3e6c5f006d494ebac5" UNIQUE ("name")`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_workspaces" DROP CONSTRAINT "UQ_6496168de3e6c5f006d494ebac5"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class TaskmanagerAddingNewEnumValueForPermissionEnumType1783324039378 implements MigrationInterface {
|
||||||
|
name = 'TaskmanagerAddingNewEnumValueForPermissionEnumType1783324039378'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum" RENAME TO "permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller', 'workspace', 'project')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"text"::"public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum_old"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum_old" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller', 'workspace')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum_old" USING "name"::"text"::"public"."permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum_old" RENAME TO "permission_name_enum"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class TMAddingTaskPhasePermissionToPermissionEnumType1783335279945 implements MigrationInterface {
|
||||||
|
name = 'TMAddingTaskPhasePermissionToPermissionEnumType1783335279945'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum" RENAME TO "permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller', 'workspace', 'project', 'task_phase')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"text"::"public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum_old"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum_old" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller', 'workspace', 'project')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum_old" USING "name"::"text"::"public"."permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum_old" RENAME TO "permission_name_enum"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class TMaddingSomeNewPermissionsToTheEnum1783428145772 implements MigrationInterface {
|
||||||
|
name = 'TMaddingSomeNewPermissionsToTheEnum1783428145772'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum" RENAME TO "permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller', 'workspace', 'view_all_workspaces', 'project', 'view_all_projects', 'task', 'task_phase', 'remark')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"text"::"public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum_old"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum_old" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller', 'workspace', 'project', 'task_phase')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum_old" USING "name"::"text"::"public"."permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum_old" RENAME TO "permission_name_enum"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class TMChangingFewLastPermissions1783759715100 implements MigrationInterface {
|
||||||
|
name = 'TMChangingFewLastPermissions1783759715100'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum" RENAME TO "permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM('services', 'customers', 'agents', 'developers', 'invoices', 'transactions', 'discounts', 'admins', 'tickets', 'criticisms', 'contacts_us', 'advertisements', 'announcements', 'blogs', 'learnings', 'logs', 'settings', 'bank_accounts', 'payments', 'manage_sso_clients', 'support_plan', 'dmenu', 'dkala', 'dpage', 'dmail', 'reseller', 'workspace', 'view_all_workspaces', 'project', 'view_all_projects', 'task', 'task_phase')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"text"::"public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum_old"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum_old" AS ENUM('admins', 'advertisements', 'agents', 'announcements', 'bank_accounts', 'blogs', 'contacts_us', 'criticisms', 'customers', 'developers', 'discounts', 'dkala', 'dmail', 'dmenu', 'dpage', 'invoices', 'learnings', 'logs', 'manage_sso_clients', 'payments', 'project', 'remark', 'reseller', 'services', 'settings', 'support_plan', 'task', 'task_phase', 'tickets', 'transactions', 'view_all_projects', 'view_all_workspaces', 'workspace')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum_old" USING "name"::"text"::"public"."permission_name_enum_old"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."permission_name_enum_old" RENAME TO "permission_name_enum"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddedPriorityToTaskEntity1784014433653 implements MigrationInterface {
|
||||||
|
name = 'AddedPriorityToTaskEntity1784014433653'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_tasks" ADD "priority" integer NOT NULL DEFAULT '1'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_tasks" DROP COLUMN "priority"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddTaskTimeTable1784100000000 implements MigrationInterface {
|
||||||
|
name = "AddTaskTimeTable1784100000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
CREATE TABLE "tm_times" (
|
||||||
|
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
|
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
|
"startedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||||
|
"endedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||||
|
"duration" integer NOT NULL,
|
||||||
|
"adminId" uuid NOT NULL,
|
||||||
|
"taskId" uuid NOT NULL,
|
||||||
|
CONSTRAINT "PK_tm_times" PRIMARY KEY ("id")
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE "tm_times"
|
||||||
|
ADD CONSTRAINT "FK_tm_times_admin"
|
||||||
|
FOREIGN KEY ("adminId") REFERENCES "user"("id")
|
||||||
|
ON DELETE RESTRICT ON UPDATE NO ACTION
|
||||||
|
`);
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE "tm_times"
|
||||||
|
ADD CONSTRAINT "FK_tm_times_task"
|
||||||
|
FOREIGN KEY ("taskId") REFERENCES "tm_tasks"("id")
|
||||||
|
ON DELETE CASCADE ON UPDATE NO ACTION
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_times" DROP CONSTRAINT "FK_tm_times_task"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_times" DROP CONSTRAINT "FK_tm_times_admin"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "tm_times"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class MakeTaskTimeEndedAtAndDurationNullable1784200000000
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = "MakeTaskTimeEndedAtAndDurationNullable1784200000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_times" ALTER COLUMN "endedAt" DROP NOT NULL`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_times" ALTER COLUMN "duration" DROP NOT NULL`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_times" ALTER COLUMN "endedAt" SET NOT NULL`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_times" ALTER COLUMN "duration" SET NOT NULL`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddTitleToPermission1784465631499 implements MigrationInterface {
|
||||||
|
name = "AddTitleToPermission1784465631499";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ADD "title" character varying(150)`);
|
||||||
|
await queryRunner.query(`UPDATE "permission" SET "title" = "name"::text WHERE "title" IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "title" SET NOT NULL`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" DROP COLUMN "title"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
type CrudResource = {
|
||||||
|
old: string;
|
||||||
|
group: string;
|
||||||
|
titles: { create: string; read: string; update: string; delete: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
const CRUD_RESOURCES: CrudResource[] = [
|
||||||
|
{
|
||||||
|
old: "services",
|
||||||
|
group: "services",
|
||||||
|
titles: { create: "ایجاد سرویسها", read: "مشاهده سرویسها", update: "ویرایش سرویسها", delete: "حذف سرویسها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "customers",
|
||||||
|
group: "customers",
|
||||||
|
titles: { create: "ایجاد مشتریان", read: "مشاهده مشتریان", update: "ویرایش مشتریان", delete: "حذف مشتریان" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "agents",
|
||||||
|
group: "representatives",
|
||||||
|
titles: { create: "ایجاد نمایندگان", read: "مشاهده نمایندگان", update: "ویرایش نمایندگان", delete: "حذف نمایندگان" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "developers",
|
||||||
|
group: "users",
|
||||||
|
titles: {
|
||||||
|
create: "ایجاد توسعهدهندگان",
|
||||||
|
read: "مشاهده توسعهدهندگان",
|
||||||
|
update: "ویرایش توسعهدهندگان",
|
||||||
|
delete: "حذف توسعهدهندگان",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "invoices",
|
||||||
|
group: "financial",
|
||||||
|
titles: { create: "ایجاد صورتحسابها", read: "مشاهده صورتحسابها", update: "ویرایش صورتحسابها", delete: "حذف صورتحسابها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "transactions",
|
||||||
|
group: "financial",
|
||||||
|
titles: { create: "ایجاد تراکنشها", read: "مشاهده تراکنشها", update: "ویرایش تراکنشها", delete: "حذف تراکنشها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "discounts",
|
||||||
|
group: "financial",
|
||||||
|
titles: { create: "ایجاد تخفیفها", read: "مشاهده تخفیفها", update: "ویرایش تخفیفها", delete: "حذف تخفیفها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "admins",
|
||||||
|
group: "users",
|
||||||
|
titles: { create: "ایجاد ادمینها", read: "مشاهده ادمینها", update: "ویرایش ادمینها", delete: "حذف ادمینها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "tickets",
|
||||||
|
group: "support",
|
||||||
|
titles: { create: "ایجاد تیکتها", read: "مشاهده تیکتها", update: "ویرایش تیکتها", delete: "حذف تیکتها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "criticisms",
|
||||||
|
group: "other",
|
||||||
|
titles: { create: "ایجاد انتقادات", read: "مشاهده انتقادات", update: "ویرایش انتقادات", delete: "حذف انتقادات" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "contacts_us",
|
||||||
|
group: "other",
|
||||||
|
titles: { create: "ایجاد تماس با ما", read: "مشاهده تماس با ما", update: "ویرایش تماس با ما", delete: "حذف تماس با ما" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "advertisements",
|
||||||
|
group: "content",
|
||||||
|
titles: { create: "ایجاد تبلیغات", read: "مشاهده تبلیغات", update: "ویرایش تبلیغات", delete: "حذف تبلیغات" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "announcements",
|
||||||
|
group: "content",
|
||||||
|
titles: { create: "ایجاد اطلاعیهها", read: "مشاهده اطلاعیهها", update: "ویرایش اطلاعیهها", delete: "حذف اطلاعیهها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "blogs",
|
||||||
|
group: "content",
|
||||||
|
titles: { create: "ایجاد بلاگها", read: "مشاهده بلاگها", update: "ویرایش بلاگها", delete: "حذف بلاگها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "learnings",
|
||||||
|
group: "content",
|
||||||
|
titles: { create: "ایجاد آموزشها", read: "مشاهده آموزشها", update: "ویرایش آموزشها", delete: "حذف آموزشها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "settings",
|
||||||
|
group: "other",
|
||||||
|
titles: { create: "ایجاد تنظیمات", read: "مشاهده تنظیمات", update: "ویرایش تنظیمات", delete: "حذف تنظیمات" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "bank_accounts",
|
||||||
|
group: "financial",
|
||||||
|
titles: {
|
||||||
|
create: "ایجاد حسابهای بانکی",
|
||||||
|
read: "مشاهده حسابهای بانکی",
|
||||||
|
update: "ویرایش حسابهای بانکی",
|
||||||
|
delete: "حذف حسابهای بانکی",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "payments",
|
||||||
|
group: "financial",
|
||||||
|
titles: { create: "ایجاد پرداختها", read: "مشاهده پرداختها", update: "ویرایش پرداختها", delete: "حذف پرداختها" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "support_plan",
|
||||||
|
group: "support",
|
||||||
|
titles: {
|
||||||
|
create: "ایجاد پلن پشتیبانی",
|
||||||
|
read: "مشاهده پلن پشتیبانی",
|
||||||
|
update: "ویرایش پلن پشتیبانی",
|
||||||
|
delete: "حذف پلن پشتیبانی",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "reseller",
|
||||||
|
group: "representatives",
|
||||||
|
titles: {
|
||||||
|
create: "ایجاد نمایندگی فروش",
|
||||||
|
read: "مشاهده نمایندگی فروش",
|
||||||
|
update: "ویرایش نمایندگی فروش",
|
||||||
|
delete: "حذف نمایندگی فروش",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "workspace",
|
||||||
|
group: "task_manager",
|
||||||
|
titles: { create: "ایجاد فضای کار", read: "مشاهده فضای کار", update: "ویرایش فضای کار", delete: "حذف فضای کار" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "project",
|
||||||
|
group: "task_manager",
|
||||||
|
titles: { create: "ایجاد پروژه", read: "مشاهده پروژه", update: "ویرایش پروژه", delete: "حذف پروژه" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "task",
|
||||||
|
group: "task_manager",
|
||||||
|
titles: { create: "ایجاد وظیفه", read: "مشاهده وظیفه", update: "ویرایش وظیفه", delete: "حذف وظیفه" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
old: "task_phase",
|
||||||
|
group: "task_manager",
|
||||||
|
titles: { create: "ایجاد فاز وظیفه", read: "مشاهده فاز وظیفه", update: "ویرایش فاز وظیفه", delete: "حذف فاز وظیفه" },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const SINGLE_PERMISSIONS: { name: string; group: string; title: string }[] = [
|
||||||
|
{ name: "logs", group: "other", title: "لاگها" },
|
||||||
|
{ name: "manage_sso_clients", group: "other", title: "مدیریت کلاینتهای SSO" },
|
||||||
|
{ name: "dmenu", group: "products", title: "دیمنو" },
|
||||||
|
{ name: "dkala", group: "products", title: "دیکالا" },
|
||||||
|
{ name: "dpage", group: "products", title: "دیپیج" },
|
||||||
|
{ name: "dmail", group: "products", title: "دیمیل" },
|
||||||
|
{ name: "view_all_workspaces", group: "task_manager", title: "مشاهده همه فضاهای کار" },
|
||||||
|
{ name: "view_all_projects", group: "task_manager", title: "مشاهده همه پروژهها" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const NEW_ENUM_VALUES = [
|
||||||
|
...CRUD_RESOURCES.flatMap((r) => [`${r.old}_create`, `${r.old}_read`, `${r.old}_update`, `${r.old}_delete`]),
|
||||||
|
...SINGLE_PERMISSIONS.map((p) => p.name),
|
||||||
|
];
|
||||||
|
|
||||||
|
const OLD_ENUM_VALUES = [
|
||||||
|
"services",
|
||||||
|
"customers",
|
||||||
|
"agents",
|
||||||
|
"developers",
|
||||||
|
"invoices",
|
||||||
|
"transactions",
|
||||||
|
"discounts",
|
||||||
|
"admins",
|
||||||
|
"tickets",
|
||||||
|
"criticisms",
|
||||||
|
"contacts_us",
|
||||||
|
"advertisements",
|
||||||
|
"announcements",
|
||||||
|
"blogs",
|
||||||
|
"learnings",
|
||||||
|
"logs",
|
||||||
|
"settings",
|
||||||
|
"bank_accounts",
|
||||||
|
"payments",
|
||||||
|
"manage_sso_clients",
|
||||||
|
"support_plan",
|
||||||
|
"dmenu",
|
||||||
|
"dkala",
|
||||||
|
"dpage",
|
||||||
|
"dmail",
|
||||||
|
"reseller",
|
||||||
|
"workspace",
|
||||||
|
"view_all_workspaces",
|
||||||
|
"project",
|
||||||
|
"view_all_projects",
|
||||||
|
"task",
|
||||||
|
"task_phase",
|
||||||
|
];
|
||||||
|
|
||||||
|
export class RefactorPermissionsToCrudAndGroup1784500000000 implements MigrationInterface {
|
||||||
|
name = "RefactorPermissionsToCrudAndGroup1784500000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TYPE "public"."permission_group_enum" AS ENUM('services', 'customers', 'users', 'financial', 'content', 'support', 'other', 'products', 'representatives', 'task_manager')`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ADD "group" "public"."permission_group_enum"`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE text USING "name"::text`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`);
|
||||||
|
|
||||||
|
for (const resource of CRUD_RESOURCES) {
|
||||||
|
const oldPerm = await queryRunner.query(`SELECT id FROM "permission" WHERE "name" = $1`, [resource.old]);
|
||||||
|
if (!oldPerm.length) continue;
|
||||||
|
|
||||||
|
const oldId = oldPerm[0].id as string;
|
||||||
|
const actions = ["create", "read", "update", "delete"] as const;
|
||||||
|
|
||||||
|
for (const action of actions) {
|
||||||
|
const newName = `${resource.old}_${action}`;
|
||||||
|
const title = resource.titles[action];
|
||||||
|
const inserted = await queryRunner.query(
|
||||||
|
`INSERT INTO "permission" ("id", "name", "title", "group", "createdAt", "updatedAt")
|
||||||
|
VALUES (uuid_generate_v4(), $1, $2, $3, NOW(), NOW())
|
||||||
|
RETURNING id`,
|
||||||
|
[newName, title, resource.group],
|
||||||
|
);
|
||||||
|
const newId = inserted[0].id as string;
|
||||||
|
|
||||||
|
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, oldId],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryRunner.query(`DELETE FROM "role_permission_relation" WHERE "permissionId" = $1`, [oldId]);
|
||||||
|
await queryRunner.query(`DELETE FROM "permission" WHERE "id" = $1`, [oldId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const perm of SINGLE_PERMISSIONS) {
|
||||||
|
await queryRunner.query(`UPDATE "permission" SET "group" = $1, "title" = COALESCE(NULLIF("title", ''), $2) WHERE "name" = $3`, [
|
||||||
|
perm.group,
|
||||||
|
perm.title,
|
||||||
|
perm.name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "group" SET NOT NULL`);
|
||||||
|
|
||||||
|
const enumList = NEW_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"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE text USING "name"::text`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`);
|
||||||
|
|
||||||
|
for (const resource of CRUD_RESOURCES) {
|
||||||
|
// const actions = ["create", "read", "update", "delete"];
|
||||||
|
const crudRows = await queryRunner.query(`SELECT id, name FROM "permission" WHERE "name" LIKE $1`, [`${resource.old}_%`]);
|
||||||
|
if (!crudRows.length) continue;
|
||||||
|
|
||||||
|
const inserted = await queryRunner.query(
|
||||||
|
`INSERT INTO "permission" ("id", "name", "title", "group", "createdAt", "updatedAt")
|
||||||
|
VALUES (uuid_generate_v4(), $1, $2, $3, NOW(), NOW())
|
||||||
|
RETURNING id`,
|
||||||
|
[resource.old, resource.old, resource.group],
|
||||||
|
);
|
||||||
|
const oldId = inserted[0].id as string;
|
||||||
|
|
||||||
|
for (const row of crudRows) {
|
||||||
|
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`,
|
||||||
|
[oldId, row.id],
|
||||||
|
);
|
||||||
|
await queryRunner.query(`DELETE FROM "role_permission_relation" WHERE "permissionId" = $1`, [row.id]);
|
||||||
|
await queryRunner.query(`DELETE FROM "permission" WHERE "id" = $1`, [row.id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldEnumList = OLD_ENUM_VALUES.map((v) => `'${v}'`).join(", ");
|
||||||
|
await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM(${oldEnumList})`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"public"."permission_name_enum"`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "permission" DROP COLUMN "group"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "public"."permission_group_enum"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddTicketToTaskEntity1784600000000 implements MigrationInterface {
|
||||||
|
name = "AddTicketToTaskEntity1784600000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_tasks" ADD "ticketId" uuid`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_tasks" ADD CONSTRAINT "FK_tm_tasks_ticket" FOREIGN KEY ("ticketId") REFERENCES "ticket"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_tasks" DROP CONSTRAINT "FK_tm_tasks_ticket"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_tasks" DROP COLUMN "ticketId"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddTaskCommentEntity1784700000000 implements MigrationInterface {
|
||||||
|
name = "AddTaskCommentEntity1784700000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "tm_task_comments" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "content" text NOT NULL, "userId" uuid NOT NULL, "taskId" uuid NOT NULL, CONSTRAINT "PK_tm_task_comments" PRIMARY KEY ("id"))`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_task_comments" ADD CONSTRAINT "FK_tm_task_comments_user" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE NO ACTION`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_task_comments" ADD CONSTRAINT "FK_tm_task_comments_task" FOREIGN KEY ("taskId") REFERENCES "tm_tasks"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_task_comments" DROP CONSTRAINT "FK_tm_task_comments_task"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_task_comments" DROP CONSTRAINT "FK_tm_task_comments_user"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "tm_task_comments"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
const NEW_PERMISSIONS = [
|
||||||
|
{ name: "ticket_categories_create", title: "ایجاد دستهبندی تیکت", source: "tickets_create" },
|
||||||
|
{ name: "ticket_categories_read", title: "مشاهده دستهبندی تیکت", source: "tickets_read" },
|
||||||
|
{ name: "ticket_categories_update", title: "ویرایش دستهبندی تیکت", source: "tickets_update" },
|
||||||
|
{ name: "ticket_categories_delete", title: "حذف دستهبندی تیکت", source: "tickets_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",
|
||||||
|
"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("tickets_delete") + 1),
|
||||||
|
...NEW_PERMISSIONS.map((p) => p.name),
|
||||||
|
...CURRENT_ENUM_VALUES.slice(CURRENT_ENUM_VALUES.indexOf("tickets_delete") + 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
export class AddTicketCategoriesPermissions1784800000000 implements MigrationInterface {
|
||||||
|
name = "AddTicketCategoriesPermissions1784800000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
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, 'support', 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<void> {
|
||||||
|
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"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddAttachmentsToTaskComment1784900000000 implements MigrationInterface {
|
||||||
|
name = "AddAttachmentsToTaskComment1784900000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "tm_task_comments" ADD "attachments" json NOT NULL DEFAULT '[]'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_task_comments" DROP COLUMN "attachments"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class AddTicketMasterEntity1785000000000 implements MigrationInterface {
|
||||||
|
name = "AddTicketMasterEntity1785000000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "ticket_master" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "userId" uuid NOT NULL, CONSTRAINT "UQ_ticket_master_user" UNIQUE ("userId"), CONSTRAINT "PK_ticket_master" PRIMARY KEY ("id"))`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "ticket_master" ADD CONSTRAINT "FK_ticket_master_user" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "ticket_master" DROP CONSTRAINT "FK_ticket_master_user"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "ticket_master"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
const NEW_PERMISSIONS = [
|
||||||
|
{ name: "ticket_masters_create", title: "ایجاد مستر تیکت", source: "tickets_create" },
|
||||||
|
{ name: "ticket_masters_read", title: "مشاهده مستر تیکت", source: "tickets_read" },
|
||||||
|
{ name: "ticket_masters_update", title: "ویرایش مستر تیکت", source: "tickets_update" },
|
||||||
|
{ name: "ticket_masters_delete", title: "حذف مستر تیکت", source: "tickets_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",
|
||||||
|
"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("ticket_categories_delete") + 1),
|
||||||
|
...NEW_PERMISSIONS.map((p) => p.name),
|
||||||
|
...CURRENT_ENUM_VALUES.slice(CURRENT_ENUM_VALUES.indexOf("ticket_categories_delete") + 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
export class AddTicketMastersPermissions1785100000000 implements MigrationInterface {
|
||||||
|
name = "AddTicketMastersPermissions1785100000000";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
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, 'support', 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<void> {
|
||||||
|
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"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<void> {
|
||||||
|
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<void> {
|
||||||
|
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"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ import { connectionSource } from "./connection";
|
|||||||
// import { seedCityAndProvince } from "./seeders/iran-city.seeder";
|
// import { seedCityAndProvince } from "./seeders/iran-city.seeder";
|
||||||
// import { seedNotifSettings } from "./seeders/notif-setting.seeder";
|
// import { seedNotifSettings } from "./seeders/notif-setting.seeder";
|
||||||
// import { seedPaymentGateways } from "./seeders/payment-gateway.seeder";
|
// import { seedPaymentGateways } from "./seeders/payment-gateway.seeder";
|
||||||
// import { seedPermissionsAndRoles } from "./seeders/role.seeder";
|
import { seedPermissionsAndRoles } from "./seeders/role.seeder";
|
||||||
import { seedBlogCommentReplies } from "./seeders/blog-comment-reply.seeder";
|
// import { seedBlogCommentReplies } from "./seeders/blog-comment-reply.seeder";
|
||||||
import { seedBlogComments } from "./seeders/blog-comment.seeder";
|
// import { seedBlogComments } from "./seeders/blog-comment.seeder";
|
||||||
|
|
||||||
const logger = new Logger("Seeder");
|
const logger = new Logger("Seeder");
|
||||||
|
|
||||||
@@ -15,14 +15,14 @@ export const runSeeder = async () => {
|
|||||||
await connectionSource.initialize();
|
await connectionSource.initialize();
|
||||||
logger.log("start seeding database");
|
logger.log("start seeding database");
|
||||||
//
|
//
|
||||||
// await seedPermissionsAndRoles(connectionSource, logger);
|
await seedPermissionsAndRoles(connectionSource, logger);
|
||||||
// await seedAdmin(connectionSource, logger);
|
// await seedAdmin(connectionSource, logger);
|
||||||
// await seedNotifSettings(connectionSource, logger);
|
// await seedNotifSettings(connectionSource, logger);
|
||||||
// await seedPaymentGateways(connectionSource, logger);
|
// await seedPaymentGateways(connectionSource, logger);
|
||||||
// await seedCityAndProvince(connectionSource, logger);
|
// await seedCityAndProvince(connectionSource, logger);
|
||||||
|
|
||||||
await seedBlogComments(connectionSource, logger);
|
// await seedBlogComments(connectionSource, logger);
|
||||||
await seedBlogCommentReplies(connectionSource, logger);
|
// await seedBlogCommentReplies(connectionSource, logger);
|
||||||
|
|
||||||
logger.log("seeding completed");
|
logger.log("seeding completed");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
@@ -1,13 +1,51 @@
|
|||||||
import { Logger } from "@nestjs/common";
|
import { Logger } from "@nestjs/common";
|
||||||
import { DataSource } from "typeorm";
|
import { DataSource } from "typeorm";
|
||||||
|
|
||||||
import { Permission } from "../../src/modules/users/entities/permission.entity";
|
// import { Permission } from "../../src/modules/users/entities/permission.entity";
|
||||||
import { Role } from "../../src/modules/users/entities/role.entity";
|
// import { Role } from "../../src/modules/users/entities/role.entity";
|
||||||
import { PermissionEnum } from "../../src/modules/users/enums/permission.enum";
|
import { PermissionEnum } from "../../src/modules/users/enums/permission.enum";
|
||||||
import { RoleEnum } from "../../src/modules/users/enums/role.enum";
|
import { RoleEnum } from "../../src/modules/users/enums/role.enum";
|
||||||
|
|
||||||
|
const permissionTitles: Record<PermissionEnum, string> = {
|
||||||
|
[PermissionEnum.SERVICES]: "سرویسها",
|
||||||
|
[PermissionEnum.CUSTOMERS]: "مشتریان",
|
||||||
|
[PermissionEnum.AGENTS]: "نمایندگان",
|
||||||
|
[PermissionEnum.DEVELOPERS]: "توسعهدهندگان",
|
||||||
|
[PermissionEnum.INVOICES]: "صورتحسابها",
|
||||||
|
[PermissionEnum.TRANSACTIONS]: "تراکنشها",
|
||||||
|
[PermissionEnum.DISCOUNTS]: "تخفیفها",
|
||||||
|
[PermissionEnum.ADMINS]: "ادمینها",
|
||||||
|
[PermissionEnum.TICKETS]: "تیکتها",
|
||||||
|
[PermissionEnum.CRITICISMS]: "انتقادات",
|
||||||
|
[PermissionEnum.CONTACTS_US]: "تماس با ما",
|
||||||
|
[PermissionEnum.ADVERTISEMENTS]: "تبلیغات",
|
||||||
|
[PermissionEnum.ANNOUNCEMENTS]: "اطلاعیهها",
|
||||||
|
[PermissionEnum.BLOGS]: "بلاگها",
|
||||||
|
[PermissionEnum.LEARNINGS]: "آموزشها",
|
||||||
|
[PermissionEnum.LOGS]: "لاگها",
|
||||||
|
[PermissionEnum.SETTINGS]: "تنظیمات",
|
||||||
|
[PermissionEnum.BANK_ACCOUNTS]: "حسابهای بانکی",
|
||||||
|
[PermissionEnum.PAYMENTS]: "پرداختها",
|
||||||
|
[PermissionEnum.MANAGE_SSO_CLIENTS]: "مدیریت کلاینتهای SSO",
|
||||||
|
[PermissionEnum.SUPPORT_PLAN]: "پلن پشتیبانی",
|
||||||
|
[PermissionEnum.DMENU]: "دیمنو",
|
||||||
|
[PermissionEnum.DKALA]: "دیکالا",
|
||||||
|
[PermissionEnum.DPAGE]: "دیپیج",
|
||||||
|
[PermissionEnum.DMAIL]: "دیمیل",
|
||||||
|
[PermissionEnum.RESELLER]: "نمایندگی فروش",
|
||||||
|
[PermissionEnum.WORKSPACE]: "فضای کار",
|
||||||
|
[PermissionEnum.VIEW_ALL_WORKSPACES]: "مشاهده همه فضاهای کار",
|
||||||
|
[PermissionEnum.PROJECT]: "پروژه",
|
||||||
|
[PermissionEnum.VIEW_ALL_PROJECTS]: "مشاهده همه پروژهها",
|
||||||
|
[PermissionEnum.TASK]: "وظیفه",
|
||||||
|
[PermissionEnum.TASK_PHASE]: "فاز وظیفه",
|
||||||
|
};
|
||||||
|
|
||||||
// Define permissions
|
// Define permissions
|
||||||
const permissions = Object.values(PermissionEnum).map((perm) => ({ name: perm }));
|
const permissions = Object.values(PermissionEnum).map((perm) => ({
|
||||||
|
name: perm,
|
||||||
|
title: permissionTitles[perm],
|
||||||
|
}));
|
||||||
|
|
||||||
// Define roles with their default permissions
|
// Define roles with their default permissions
|
||||||
const rolesWithPermissions: { name: RoleEnum; permissions: PermissionEnum[] }[] = [
|
const rolesWithPermissions: { name: RoleEnum; permissions: PermissionEnum[] }[] = [
|
||||||
@@ -23,8 +61,8 @@ const rolesWithPermissions: { name: RoleEnum; permissions: PermissionEnum[] }[]
|
|||||||
|
|
||||||
export const seedPermissionsAndRoles = async (dataSource: DataSource, logger: Logger) => {
|
export const seedPermissionsAndRoles = async (dataSource: DataSource, logger: Logger) => {
|
||||||
try {
|
try {
|
||||||
const permissionRepo = dataSource.getRepository(Permission);
|
const permissionRepo = dataSource.getRepository("Permission");
|
||||||
const roleRepo = dataSource.getRepository(Role);
|
const roleRepo = dataSource.getRepository("Role");
|
||||||
|
|
||||||
// Seed Permissions
|
// Seed Permissions
|
||||||
logger.log("Seeding Permissions...");
|
logger.log("Seeding Permissions...");
|
||||||
@@ -33,6 +71,9 @@ export const seedPermissionsAndRoles = async (dataSource: DataSource, logger: Lo
|
|||||||
if (!existingPerm) {
|
if (!existingPerm) {
|
||||||
const newPerm = permissionRepo.create(perm);
|
const newPerm = permissionRepo.create(perm);
|
||||||
await permissionRepo.save(newPerm);
|
await permissionRepo.save(newPerm);
|
||||||
|
} else if (!existingPerm.title || existingPerm.title !== perm.title) {
|
||||||
|
existingPerm.title = perm.title;
|
||||||
|
await permissionRepo.save(existingPerm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.log("Permissions seeded successfully");
|
logger.log("Permissions seeded successfully");
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../danak-admin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsEnum, IsInt, IsOptional, IsString, Max, Min } from "class-validator";
|
||||||
|
|
||||||
|
export enum CursorDirection {
|
||||||
|
NEXT = "next",
|
||||||
|
PREV = "prev",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CursorPaginationDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"Base64‑encoded cursor containing the priority, createdAt (ISO) and id of the last seen item. Leave empty for the first page.",
|
||||||
|
example: "MjAyNi0wNy0wMVQxMDowMDowMC4wMDBafDU=",
|
||||||
|
})
|
||||||
|
cursor?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@Max(50)
|
||||||
|
@Type(() => Number)
|
||||||
|
@ApiPropertyOptional({ type: "number", required: false, default: 10 })
|
||||||
|
limit?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(CursorDirection)
|
||||||
|
@ApiPropertyOptional({ enum: CursorDirection, default: CursorDirection.NEXT, description: "Direction to page in relative to 'cursor'." })
|
||||||
|
direction?: CursorDirection;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { applyDecorators } from "@nestjs/common";
|
||||||
|
import { ApiQuery } from "@nestjs/swagger";
|
||||||
|
import { CursorDirection } from "../DTO/cursor-pagination.dto";
|
||||||
|
|
||||||
|
export function CursorPagination() {
|
||||||
|
return applyDecorators(
|
||||||
|
ApiQuery({
|
||||||
|
name: "cursor",
|
||||||
|
required: false,
|
||||||
|
type: "string",
|
||||||
|
description: "Base64‑encoded cursor of the last item (contains priority, createdAt and id). Leave empty for the first page.",
|
||||||
|
example: "MjAyNi0wNy0wMVQxMDowMDowMC4wMDBafDU=",
|
||||||
|
}),
|
||||||
|
ApiQuery({
|
||||||
|
name: "limit",
|
||||||
|
required: false,
|
||||||
|
type: "number",
|
||||||
|
description: "Number of items per page (max 50)",
|
||||||
|
example: 10,
|
||||||
|
}),
|
||||||
|
ApiQuery({
|
||||||
|
name: "direction",
|
||||||
|
required: false,
|
||||||
|
enum: CursorDirection,
|
||||||
|
description: "Direction to page in relative to 'cursor': 'next' (default) or 'prev'.",
|
||||||
|
example: CursorDirection.NEXT,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -330,6 +330,12 @@ export const enum TicketMessageEnum {
|
|||||||
INVALID_TICKET_LIMIT = "محدودیت تعداد تیکت در پلن پشتیبانی شما نامعتبر است",
|
INVALID_TICKET_LIMIT = "محدودیت تعداد تیکت در پلن پشتیبانی شما نامعتبر است",
|
||||||
TICKET_LIMIT_EXCEEDED = "شما به محدودیت تعداد تیکت در پلن پشتیبانی خود رسیدهاید",
|
TICKET_LIMIT_EXCEEDED = "شما به محدودیت تعداد تیکت در پلن پشتیبانی خود رسیدهاید",
|
||||||
OPENED = "تیکت با موفقیت باز شد",
|
OPENED = "تیکت با موفقیت باز شد",
|
||||||
|
MASTER_USER_ID_REQUIRED = "شناسه کاربر مستر تیکت اجباری است",
|
||||||
|
MASTER_USER_ID_SHOULD_BE_UUID = "شناسه کاربر مستر تیکت باید یک UUID معتبر باشد",
|
||||||
|
MASTER_CREATED = "مستر تیکت با موفقیت ایجاد شد",
|
||||||
|
MASTER_NOT_FOUND = "مستر تیکت یافت نشد",
|
||||||
|
MASTER_EXIST = "این کاربر قبلاً به عنوان مستر تیکت ثبت شده است",
|
||||||
|
MASTER_DELETED = "مستر تیکت با موفقیت حذف شد",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum WalletMessage {
|
export const enum WalletMessage {
|
||||||
@@ -915,3 +921,55 @@ export const enum UploaderMessage {
|
|||||||
export const enum AccessLogMessage {
|
export const enum AccessLogMessage {
|
||||||
ACCESS_LOG_NOT_FOUND = "لاگ دسترسی مورد نظر یافت نشد",
|
ACCESS_LOG_NOT_FOUND = "لاگ دسترسی مورد نظر یافت نشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum WorkSpaceMessage {
|
||||||
|
WORKSPACE_EXISTS = "فضای کار با این اسم وجود دارد!",
|
||||||
|
WORKSPACE_NOT_FOUND = "فضای کار مورد نظر شما وجود ندارد!",
|
||||||
|
USERS_DONT_BELONG_TO_THIS_WORKSPACE = "کاربران تعیین شده برای این فضای کاری نیستند!",
|
||||||
|
WORKSPACE_SAME_NAME_EXISTS = "فضای کاری با این اسم از قبل وجود دارد!",
|
||||||
|
WORKSPACE_ID_PARAM_NOT_EMPTY = "پارامتر شناسه فضای کار نباید خالی باشد!",
|
||||||
|
WORKSPACE_ID_UUID = "شناسه فضای کار باید uuid باشد",
|
||||||
|
FORBIDDEN = "شما اجازه دسترسی به این فضای کار را ندارید و یا عضو آن نیستید!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum ProjectMessage {
|
||||||
|
PROJECT_EXISTS = "پروژه مورد نظر شما از قبل وجود دارد!",
|
||||||
|
PROJECT_NOT_FOUND = "پروژه مورد نظر شما وجود ندارد!",
|
||||||
|
PROJECT_CONTAINS_TASK_PHASES = "پروژه شامل فاز تسک است. لطفا ابتدا فاز تسک های این پروژه را حذف کنید.",
|
||||||
|
NOT_MEMBER_OF_PROJECT = "شما عضو این پروژه نیستید!",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum TaskPhaseMessage {
|
||||||
|
TASK_PHASE_NOT_FOUND = "فاز تسک مورد نظر پیدا نشد!",
|
||||||
|
TASK_PHASE_NOT_SAME_PROJECT = "فاز تسک جدید با فاز تسک قبلی در یک پروژه نیستند!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum TaskMessage {
|
||||||
|
TASK_NOT_FOUND = "تسک مورد نظر پیدا نشد!",
|
||||||
|
USERS_ASSIGNED_TO_TASK_NOT_PROJECT_MEMBER = "حداقل یکی از کاربران تعیین شده عضو پروژه نیستند!",
|
||||||
|
USER_NOT_TASK_MEMBER = "شما عضو این تسک نیستید!",
|
||||||
|
TASKS_NOT_IN_THE_SAME_TASK_PHASE = "تسک های مورد نظر داخل یک فاز تسک یکسان نیستند!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum remarkMessage {
|
||||||
|
REMARK_NOT_FOUND = "برچسب مورد نظر پیدا نشد!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum checkListItemMessage {
|
||||||
|
CHECKLISTITEM_NOT_FOUND = "آیتم چک لیست پیدا نشد!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum attachmentMessage {
|
||||||
|
ATTACHMENT_NOT_FOUND = "ضمیمه پیدا نشد!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum timeMessage {
|
||||||
|
TIME_NOT_FOUND = "زمان ثبت شده پیدا نشد!",
|
||||||
|
INVALID_TIME_RANGE = "زمان پایان باید بعد از زمان شروع باشد!",
|
||||||
|
TIME_ALREADY_STARTED = "برای این تسک یک زمان در حال اجرا وجود دارد!",
|
||||||
|
NO_RUNNING_TIME = "زمان در حال اجرایی برای این تسک پیدا نشد!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum taskCommentMessage {
|
||||||
|
TASK_COMMENT_NOT_FOUND = "نظر مورد نظر پیدا نشد!"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export interface ICursorPageFormat {
|
||||||
|
limit: number;
|
||||||
|
nextCursor: string | null;
|
||||||
|
previousCursor: string | null;
|
||||||
|
hasNextPage: boolean;
|
||||||
|
hasPreviousPage: boolean;
|
||||||
|
}
|
||||||
@@ -5,11 +5,17 @@ import { MailerAsyncOptions } from "@nestjs-modules/mailer/dist/interfaces/maile
|
|||||||
export function mailerConfig(): MailerAsyncOptions {
|
export function mailerConfig(): MailerAsyncOptions {
|
||||||
return {
|
return {
|
||||||
inject: [ConfigService],
|
inject: [ConfigService],
|
||||||
useFactory: (configService: ConfigService) => ({
|
useFactory: (configService: ConfigService) => {
|
||||||
|
const port = Number(configService.getOrThrow<number>("SMTP_PORT"));
|
||||||
|
// 465 = implicit TLS; 587 = STARTTLS (required by zone-mta behind Traefik)
|
||||||
|
const secure = port === 465;
|
||||||
|
|
||||||
|
return {
|
||||||
transport: {
|
transport: {
|
||||||
host: configService.getOrThrow<string>("SMTP_HOST"),
|
host: configService.getOrThrow<string>("SMTP_HOST"),
|
||||||
port: configService.getOrThrow<number>("SMTP_PORT"),
|
port,
|
||||||
secure: true,
|
secure,
|
||||||
|
requireTLS: !secure,
|
||||||
auth: {
|
auth: {
|
||||||
user: configService.getOrThrow<string>("SMTP_USER"),
|
user: configService.getOrThrow<string>("SMTP_USER"),
|
||||||
pass: configService.getOrThrow<string>("SMTP_PASS"),
|
pass: configService.getOrThrow<string>("SMTP_PASS"),
|
||||||
@@ -18,7 +24,6 @@ export function mailerConfig(): MailerAsyncOptions {
|
|||||||
defaults: {
|
defaults: {
|
||||||
from: configService.getOrThrow<string>("MAIL_FROM"),
|
from: configService.getOrThrow<string>("MAIL_FROM"),
|
||||||
},
|
},
|
||||||
|
|
||||||
template: {
|
template: {
|
||||||
dir: process.cwd() + "/src/modules/templates/email",
|
dir: process.cwd() + "/src/modules/templates/email",
|
||||||
adapter: new HandlebarsAdapter(),
|
adapter: new HandlebarsAdapter(),
|
||||||
@@ -26,6 +31,7 @@ export function mailerConfig(): MailerAsyncOptions {
|
|||||||
strict: true,
|
strict: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { FastifyRequest } from "fastify";
|
|||||||
import { Observable, map } from "rxjs";
|
import { Observable, map } from "rxjs";
|
||||||
|
|
||||||
import { IPageFormat } from "../../common/interfaces/IPagination";
|
import { IPageFormat } from "../../common/interfaces/IPagination";
|
||||||
|
import { ICursorPageFormat } from "../../common/interfaces/ICursor-pagination";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaginationInterceptor implements NestInterceptor {
|
export class PaginationInterceptor implements NestInterceptor {
|
||||||
@@ -10,7 +11,11 @@ export class PaginationInterceptor implements NestInterceptor {
|
|||||||
|
|
||||||
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
|
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
|
||||||
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
||||||
const query = request.query as { page: string; limit: string };
|
const query = request.query as {
|
||||||
|
page: string;
|
||||||
|
limit: string;
|
||||||
|
cursor?: string; // for cursor pagination
|
||||||
|
};
|
||||||
|
|
||||||
const page = parseInt(query.page, 10) || 1;
|
const page = parseInt(query.page, 10) || 1;
|
||||||
const limit = parseInt(query.limit, 10) || 10;
|
const limit = parseInt(query.limit, 10) || 10;
|
||||||
@@ -20,17 +25,40 @@ export class PaginationInterceptor implements NestInterceptor {
|
|||||||
|
|
||||||
return next.handle().pipe(
|
return next.handle().pipe(
|
||||||
map((data) => {
|
map((data) => {
|
||||||
|
// ---------- Offset (page‑based) pagination ----------
|
||||||
if (data && (data.paginate || data.count)) {
|
if (data && (data.paginate || data.count)) {
|
||||||
const { count, paginate, ...response } = data;
|
const { count, paginate, ...response } = data;
|
||||||
this.logger.log(`paginate response from ${className}.${handlerName}`);
|
this.logger.log(`offset paginate response from ${className}.${handlerName}`);
|
||||||
const pager = this.formatPage(page, limit, count, request);
|
const pager = this.formatPage(page, limit, count, request);
|
||||||
|
return { pager, ...response };
|
||||||
return {
|
|
||||||
pager,
|
|
||||||
...response,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- Cursor‑based pagination ----------
|
||||||
|
if (data && data.cursorPaginate === true) {
|
||||||
|
const {
|
||||||
|
data: items,
|
||||||
|
nextCursor,
|
||||||
|
previousCursor,
|
||||||
|
hasNextPage,
|
||||||
|
hasPreviousPage,
|
||||||
|
cursorPaginate,
|
||||||
|
...rest
|
||||||
|
} = data;
|
||||||
|
|
||||||
|
this.logger.log(`cursor paginate response from ${className}.${handlerName}`);
|
||||||
|
|
||||||
|
const pager: ICursorPageFormat = {
|
||||||
|
limit,
|
||||||
|
nextCursor,
|
||||||
|
previousCursor,
|
||||||
|
hasNextPage,
|
||||||
|
hasPreviousPage,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { items, pager, ...rest };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- No pagination ----------
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,41 +19,41 @@ export class AdsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Create new ads" })
|
@ApiOperation({ summary: "Create new ads" })
|
||||||
@Post()
|
@Post()
|
||||||
@PermissionsDec(PermissionEnum.ADVERTISEMENTS)
|
@PermissionsDec(PermissionEnum.ADVERTISEMENTS_CREATE)
|
||||||
createAds(@Body() createDto: CreateAdsDto) {
|
createAds(@Body() createDto: CreateAdsDto) {
|
||||||
return this.adsService.createAds(createDto);
|
return this.adsService.createAds(createDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get all ads" })
|
@ApiOperation({ summary: "Get all ads" })
|
||||||
@PermissionsDec(PermissionEnum.ADVERTISEMENTS)
|
@PermissionsDec(PermissionEnum.ADVERTISEMENTS_READ)
|
||||||
@Get("list")
|
@Get("list")
|
||||||
getAdsList(@Query() queryDto: AdsSearchQueryDto) {
|
getAdsList(@Query() queryDto: AdsSearchQueryDto) {
|
||||||
return this.adsService.getAdsList(queryDto);
|
return this.adsService.getAdsList(queryDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Get ads by id" })
|
@ApiOperation({ summary: "Get ads by id" })
|
||||||
@PermissionsDec(PermissionEnum.ADVERTISEMENTS)
|
@PermissionsDec(PermissionEnum.ADVERTISEMENTS_READ)
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
getAdsById(@Param() paramDto: ParamDto) {
|
getAdsById(@Param() paramDto: ParamDto) {
|
||||||
return this.adsService.getAdsById(paramDto.id);
|
return this.adsService.getAdsById(paramDto.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Update ads by id" })
|
@ApiOperation({ summary: "Update ads by id" })
|
||||||
@PermissionsDec(PermissionEnum.ADVERTISEMENTS)
|
@PermissionsDec(PermissionEnum.ADVERTISEMENTS_UPDATE)
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
updateAdsById(@Param() paramDto: ParamDto, @Body() updateDto: UpdateAdsDto) {
|
updateAdsById(@Param() paramDto: ParamDto, @Body() updateDto: UpdateAdsDto) {
|
||||||
return this.adsService.updateAdsById(paramDto.id, updateDto);
|
return this.adsService.updateAdsById(paramDto.id, updateDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Delete ads by id" })
|
@ApiOperation({ summary: "Delete ads by id" })
|
||||||
@PermissionsDec(PermissionEnum.ADVERTISEMENTS)
|
@PermissionsDec(PermissionEnum.ADVERTISEMENTS_DELETE)
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
deleteAdsById(@Param() paramDto: ParamDto) {
|
deleteAdsById(@Param() paramDto: ParamDto) {
|
||||||
return this.adsService.deleteAdsById(paramDto.id);
|
return this.adsService.deleteAdsById(paramDto.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Toggle ads status" })
|
@ApiOperation({ summary: "Toggle ads status" })
|
||||||
@PermissionsDec(PermissionEnum.ADVERTISEMENTS)
|
@PermissionsDec(PermissionEnum.ADVERTISEMENTS_UPDATE)
|
||||||
@Patch("toggle-status/:id")
|
@Patch("toggle-status/:id")
|
||||||
toggleAdsStatus(@Param() paramDto: ParamDto) {
|
toggleAdsStatus(@Param() paramDto: ParamDto) {
|
||||||
return this.adsService.toggleAdsStatus(paramDto.id);
|
return this.adsService.toggleAdsStatus(paramDto.id);
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ import { PermissionEnum } from "../users/enums/permission.enum";
|
|||||||
export class AnnouncementController {
|
export class AnnouncementController {
|
||||||
constructor(private readonly announcementService: AnnouncementService) {}
|
constructor(private readonly announcementService: AnnouncementService) {}
|
||||||
|
|
||||||
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
|
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS_CREATE)
|
||||||
@ApiProperty({ description: "Create a new announcement (admin route)" })
|
@ApiProperty({ description: "Create a new announcement (admin route)" })
|
||||||
@Post()
|
@Post()
|
||||||
create(@Body() createAnnouncementDto: CreateAnnouncementDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
create(@Body() createAnnouncementDto: CreateAnnouncementDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.announcementService.createAnnouncement(createAnnouncementDto, userIpAndHeaders);
|
return this.announcementService.createAnnouncement(createAnnouncementDto, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
|
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS_UPDATE)
|
||||||
@ApiProperty({ description: "Update an announcement (admin route)" })
|
@ApiProperty({ description: "Update an announcement (admin route)" })
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
update(
|
update(
|
||||||
@@ -34,7 +34,7 @@ export class AnnouncementController {
|
|||||||
return this.announcementService.updateAnnouncement(paramDto.id, updateAnnouncementDto, userIpAndHeaders);
|
return this.announcementService.updateAnnouncement(paramDto.id, updateAnnouncementDto, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
|
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS_DELETE)
|
||||||
@ApiProperty({ description: "Delete an announcement (admin route)" })
|
@ApiProperty({ description: "Delete an announcement (admin route)" })
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
delete(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
delete(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
@@ -42,7 +42,7 @@ export class AnnouncementController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiProperty({ description: "Get all announcements (admin route)" })
|
@ApiProperty({ description: "Get all announcements (admin route)" })
|
||||||
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
|
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS_READ)
|
||||||
@Get()
|
@Get()
|
||||||
getAnnouncements(@Query() queryDto: SearchAnnouncementQueryDto) {
|
getAnnouncements(@Query() queryDto: SearchAnnouncementQueryDto) {
|
||||||
return this.announcementService.getAnnouncements(queryDto);
|
return this.announcementService.getAnnouncements(queryDto);
|
||||||
@@ -61,7 +61,7 @@ export class AnnouncementController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiProperty({ description: "Get one announcements with id (admin route)" })
|
@ApiProperty({ description: "Get one announcements with id (admin route)" })
|
||||||
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS)
|
@PermissionsDec(PermissionEnum.ANNOUNCEMENTS_READ)
|
||||||
@Get(":id/admin")
|
@Get(":id/admin")
|
||||||
getAnnouncementByIdAdmin(@Param() paramDto: ParamDto) {
|
getAnnouncementByIdAdmin(@Param() paramDto: ParamDto) {
|
||||||
return this.announcementService.getAnnouncementByIdAdmin(paramDto.id);
|
return this.announcementService.getAnnouncementByIdAdmin(paramDto.id);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { SSOClientService } from "./sso-client.service";
|
|||||||
import { TokensService } from "./tokens.service";
|
import { TokensService } from "./tokens.service";
|
||||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
import { getPermissionsFromRoles } from "../../users/utils/role-permissions.util";
|
||||||
import { ITokenPayload } from "../interfaces/IToken-payload";
|
import { ITokenPayload } from "../interfaces/IToken-payload";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -46,7 +47,7 @@ export class SSOService {
|
|||||||
const payload: ITokenPayload = {
|
const payload: ITokenPayload = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
isAdmin: user.roles.some((r) => r.isAdmin),
|
isAdmin: user.roles.some((r) => r.isAdmin),
|
||||||
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
|
permissions: getPermissionsFromRoles(user.roles),
|
||||||
audience: clientId,
|
audience: clientId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ export class SSOService {
|
|||||||
const payload: ITokenPayload = {
|
const payload: ITokenPayload = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
isAdmin: user.roles.some((r) => r.isAdmin),
|
isAdmin: user.roles.some((r) => r.isAdmin),
|
||||||
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
|
permissions: getPermissionsFromRoles(user.roles),
|
||||||
audience: clientId,
|
audience: clientId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { DataSource, QueryRunner } from "typeorm";
|
|||||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
import { RefreshTokensRepository } from "../../users/repositories/refresh-token.repository";
|
import { RefreshTokensRepository } from "../../users/repositories/refresh-token.repository";
|
||||||
|
import { getPermissionsFromRoles } from "../../users/utils/role-permissions.util";
|
||||||
import { ITokenPayload } from "../interfaces/IToken-payload";
|
import { ITokenPayload } from "../interfaces/IToken-payload";
|
||||||
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
|
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ export class TokensService {
|
|||||||
{
|
{
|
||||||
id: user.id,
|
id: user.id,
|
||||||
isAdmin: user.roles.some((r) => r.isAdmin),
|
isAdmin: user.roles.some((r) => r.isAdmin),
|
||||||
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
|
permissions: getPermissionsFromRoles(user.roles),
|
||||||
},
|
},
|
||||||
queryRunner,
|
queryRunner,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export class BlogsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get all blogs (admin route)" })
|
@ApiOperation({ summary: "Get all blogs (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOGS_READ)
|
||||||
@Get("list")
|
@Get("list")
|
||||||
getBlogList(@Query() queryDto: BlogListSearchQueryDto) {
|
getBlogList(@Query() queryDto: BlogListSearchQueryDto) {
|
||||||
return this.blogsService.getBlogList(queryDto);
|
return this.blogsService.getBlogList(queryDto);
|
||||||
@@ -85,7 +85,7 @@ export class BlogsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "toggle status of blogs (admin route)" })
|
@ApiOperation({ summary: "toggle status of blogs (admin route)" })
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOGS_CREATE)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post(":id/toggle-status")
|
@Post(":id/toggle-status")
|
||||||
toggleBlogStatus(@Param() paramDto: ParamDto) {
|
toggleBlogStatus(@Param() paramDto: ParamDto) {
|
||||||
@@ -103,7 +103,7 @@ export class BlogsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Toggle pinned status of a blog (admin route)" })
|
@ApiOperation({ summary: "Toggle pinned status of a blog (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOGS_CREATE)
|
||||||
@Post(":id/toggle-pinned")
|
@Post(":id/toggle-pinned")
|
||||||
togglePinnedStatus(@Param() paramDto: ParamDto) {
|
togglePinnedStatus(@Param() paramDto: ParamDto) {
|
||||||
return this.blogsService.togglePinnedStatus(paramDto.id);
|
return this.blogsService.togglePinnedStatus(paramDto.id);
|
||||||
@@ -112,7 +112,7 @@ export class BlogsController {
|
|||||||
@ApiOperation({ summary: "Create a new blog (admin route)" })
|
@ApiOperation({ summary: "Create a new blog (admin route)" })
|
||||||
@Post()
|
@Post()
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOGS_CREATE)
|
||||||
createBlog(@Body() createBlogDto: CreateBlogDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
createBlog(@Body() createBlogDto: CreateBlogDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.blogsService.createBlog(createBlogDto, userIpAndHeaders);
|
return this.blogsService.createBlog(createBlogDto, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ export class BlogsController {
|
|||||||
@ApiOperation({ summary: "Update a blog (admin route)" })
|
@ApiOperation({ summary: "Update a blog (admin route)" })
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOGS_UPDATE)
|
||||||
updateBlog(@Param() paramDto: ParamDto, @Body() updateBlogDto: UpdateBlogDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
updateBlog(@Param() paramDto: ParamDto, @Body() updateBlogDto: UpdateBlogDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.blogsService.updateBlog(paramDto.id, updateBlogDto, userIpAndHeaders);
|
return this.blogsService.updateBlog(paramDto.id, updateBlogDto, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ export class BlogsController {
|
|||||||
@ApiOperation({ summary: "Delete a blog (admin route) " })
|
@ApiOperation({ summary: "Delete a blog (admin route) " })
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOGS_DELETE)
|
||||||
deleteBlog(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
deleteBlog(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.blogsService.deleteBlog(paramDto.id, userIpAndHeaders);
|
return this.blogsService.deleteBlog(paramDto.id, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ export class BlogsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get all comments for a blog (admin route)" })
|
@ApiOperation({ summary: "Get all comments for a blog (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOG_COMMENTS_READ)
|
||||||
@Get(":id/comments")
|
@Get(":id/comments")
|
||||||
getBlogCommentsForAdmin(@Param() paramDto: ParamDto) {
|
getBlogCommentsForAdmin(@Param() paramDto: ParamDto) {
|
||||||
return this.blogCommentService.getBlogCommentsForAdmin(paramDto.id);
|
return this.blogCommentService.getBlogCommentsForAdmin(paramDto.id);
|
||||||
@@ -157,7 +157,7 @@ export class BlogsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get all comments for a blog (admin route)" })
|
@ApiOperation({ summary: "Get all comments for a blog (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOG_COMMENTS_READ)
|
||||||
@Get("comments")
|
@Get("comments")
|
||||||
getAllCommentForAdmin(@Query() queryDto: BlogCommentQueryDto) {
|
getAllCommentForAdmin(@Query() queryDto: BlogCommentQueryDto) {
|
||||||
return this.blogCommentService.getAllCommentForAdmin(queryDto);
|
return this.blogCommentService.getAllCommentForAdmin(queryDto);
|
||||||
@@ -165,7 +165,7 @@ export class BlogsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Update the status of a comment (admin route)" })
|
@ApiOperation({ summary: "Update the status of a comment (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOG_COMMENTS_UPDATE)
|
||||||
@Patch("comments/:id/status")
|
@Patch("comments/:id/status")
|
||||||
updateCommentStatus(@Param() paramDto: ParamDto, @Body() updateCommentStatusDto: UpdateCommentStatusDto) {
|
updateCommentStatus(@Param() paramDto: ParamDto, @Body() updateCommentStatusDto: UpdateCommentStatusDto) {
|
||||||
return this.blogCommentService.updateCommentStatus(paramDto.id, updateCommentStatusDto);
|
return this.blogCommentService.updateCommentStatus(paramDto.id, updateCommentStatusDto);
|
||||||
@@ -196,7 +196,7 @@ export class BlogsController {
|
|||||||
@ApiOperation({ summary: "Create a new category (admin route)" })
|
@ApiOperation({ summary: "Create a new category (admin route)" })
|
||||||
@Post("categories")
|
@Post("categories")
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOG_CATEGORIES_CREATE)
|
||||||
createCategory(@Body() createCategoryDto: CreateBlogCategoryDto) {
|
createCategory(@Body() createCategoryDto: CreateBlogCategoryDto) {
|
||||||
return this.blogsService.createCategory(createCategoryDto);
|
return this.blogsService.createCategory(createCategoryDto);
|
||||||
}
|
}
|
||||||
@@ -204,7 +204,7 @@ export class BlogsController {
|
|||||||
@ApiOperation({ summary: "Update a category (admin route)" })
|
@ApiOperation({ summary: "Update a category (admin route)" })
|
||||||
@Patch("categories/:id")
|
@Patch("categories/:id")
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOG_CATEGORIES_UPDATE)
|
||||||
updateCategory(@Param() paramDto: ParamDto, @Body() updateCategoryDto: UpdateBlogCategoryDto) {
|
updateCategory(@Param() paramDto: ParamDto, @Body() updateCategoryDto: UpdateBlogCategoryDto) {
|
||||||
return this.blogsService.updateCategory(paramDto.id, updateCategoryDto);
|
return this.blogsService.updateCategory(paramDto.id, updateCategoryDto);
|
||||||
}
|
}
|
||||||
@@ -212,13 +212,13 @@ export class BlogsController {
|
|||||||
@ApiOperation({ summary: "Delete a category (admin route)" })
|
@ApiOperation({ summary: "Delete a category (admin route)" })
|
||||||
@Delete("categories/:id")
|
@Delete("categories/:id")
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOG_CATEGORIES_DELETE)
|
||||||
deleteCategory(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
deleteCategory(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.blogsService.deleteCategory(paramDto.id, userIpAndHeaders);
|
return this.blogsService.deleteCategory(paramDto.id, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "toggle status of categories (admin route)" })
|
@ApiOperation({ summary: "toggle status of categories (admin route)" })
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.BLOG_CATEGORIES_UPDATE)
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post("categories/toggle-status/:id")
|
@Post("categories/toggle-status/:id")
|
||||||
toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class ContactUsController {
|
|||||||
|
|
||||||
//******************** */
|
//******************** */
|
||||||
|
|
||||||
@PermissionsDec(PermissionEnum.CONTACTS_US)
|
@PermissionsDec(PermissionEnum.CONTACTS_US_READ)
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@ApiOperation({ summary: "Get all contact us (admin route)" })
|
@ApiOperation({ summary: "Get all contact us (admin route)" })
|
||||||
@Get()
|
@Get()
|
||||||
@@ -36,7 +36,7 @@ export class ContactUsController {
|
|||||||
//******************** */
|
//******************** */
|
||||||
|
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.CONTACTS_US)
|
@PermissionsDec(PermissionEnum.CONTACTS_US_READ)
|
||||||
@ApiOperation({ summary: "Get one contact us with id (admin route)" })
|
@ApiOperation({ summary: "Get one contact us with id (admin route)" })
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
getOneContactUs(@Param() paramDto: ParamDto) {
|
getOneContactUs(@Param() paramDto: ParamDto) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class CriticismController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.CRITICISMS)
|
@PermissionsDec(PermissionEnum.CRITICISMS_READ)
|
||||||
@ApiOperation({ summary: "Get all Criticism ===> login as admin" })
|
@ApiOperation({ summary: "Get all Criticism ===> login as admin" })
|
||||||
@Get()
|
@Get()
|
||||||
findAll(@Query() queryDto: SearchCriticismQueryDto) {
|
findAll(@Query() queryDto: SearchCriticismQueryDto) {
|
||||||
@@ -31,7 +31,7 @@ export class CriticismController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.CRITICISMS)
|
@PermissionsDec(PermissionEnum.CRITICISMS_READ)
|
||||||
@ApiOperation({ summary: "Get one Criticism with id ===> login as admin" })
|
@ApiOperation({ summary: "Get one Criticism with id ===> login as admin" })
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
fineOne(@Param() paramDto: ParamDto) {
|
fineOne(@Param() paramDto: ParamDto) {
|
||||||
@@ -39,7 +39,7 @@ export class CriticismController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.CRITICISMS)
|
@PermissionsDec(PermissionEnum.CRITICISMS_DELETE)
|
||||||
@ApiOperation({ summary: "Delete one Criticism with id ===> login as admin" })
|
@ApiOperation({ summary: "Delete one Criticism with id ===> login as admin" })
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
delete(@Param() paramDto: ParamDto) {
|
delete(@Param() paramDto: ParamDto) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
//------------------------ service categories ------------------------
|
//------------------------ service categories ------------------------
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@ApiOperation({ summary: "Create a new service category => admin route" })
|
@ApiOperation({ summary: "Create a new service category => admin route" })
|
||||||
@Post("categories")
|
@Post("categories")
|
||||||
createCategory(@Body() createDto: CreateDanakServiceCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
createCategory(@Body() createDto: CreateDanakServiceCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
@@ -45,7 +45,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@ApiOperation({ summary: "Get all service categories => admin route" })
|
@ApiOperation({ summary: "Get all service categories => admin route" })
|
||||||
@Get("categories")
|
@Get("categories")
|
||||||
getCategories(@Query() queryDto: CategorySearchQueryDto) {
|
getCategories(@Query() queryDto: CategorySearchQueryDto) {
|
||||||
@@ -53,7 +53,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@ApiOperation({ summary: "Get all service categories => admin route" })
|
@ApiOperation({ summary: "Get all service categories => admin route" })
|
||||||
@Pagination()
|
@Pagination()
|
||||||
@Get("categories/list")
|
@Get("categories/list")
|
||||||
@@ -69,7 +69,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@ApiOperation({ summary: "Get category by id => admin route" })
|
@ApiOperation({ summary: "Get category by id => admin route" })
|
||||||
@Get("categories/:id")
|
@Get("categories/:id")
|
||||||
getCategoryById(@Param() paramDto: ParamDto) {
|
getCategoryById(@Param() paramDto: ParamDto) {
|
||||||
@@ -77,7 +77,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_UPDATE)
|
||||||
@ApiOperation({ summary: "update category by id => admin route" })
|
@ApiOperation({ summary: "update category by id => admin route" })
|
||||||
@Patch("categories/:id")
|
@Patch("categories/:id")
|
||||||
updateCategory(
|
updateCategory(
|
||||||
@@ -89,7 +89,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_DELETE)
|
||||||
@ApiOperation({ summary: "delete category by id => admin route" })
|
@ApiOperation({ summary: "delete category by id => admin route" })
|
||||||
@Delete("categories/:id")
|
@Delete("categories/:id")
|
||||||
deleteCategoryById(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
deleteCategoryById(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
@@ -97,7 +97,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@ApiOperation({ summary: "toggle status of categories => admin route" })
|
@ApiOperation({ summary: "toggle status of categories => admin route" })
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post("categories/toggle-status/:id")
|
@Post("categories/toggle-status/:id")
|
||||||
@@ -106,7 +106,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
//-------------------- service management --------------------------
|
//-------------------- service management --------------------------
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@ApiOperation({ summary: "create new danak services => admin route" })
|
@ApiOperation({ summary: "create new danak services => admin route" })
|
||||||
@Post()
|
@Post()
|
||||||
createService(@Body() createDto: CreateServiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
createService(@Body() createDto: CreateServiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
@@ -114,7 +114,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@ApiOperation({ summary: "get all danak services ==> admin route" })
|
@ApiOperation({ summary: "get all danak services ==> admin route" })
|
||||||
@Get()
|
@Get()
|
||||||
getServices(@Query() queryDto: DanakServicesSearchQueryDto) {
|
getServices(@Query() queryDto: DanakServicesSearchQueryDto) {
|
||||||
@@ -136,7 +136,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@ApiOperation({ summary: "toggle status of danak service => admin route" })
|
@ApiOperation({ summary: "toggle status of danak service => admin route" })
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post("toggle-status/:id")
|
@Post("toggle-status/:id")
|
||||||
@@ -145,7 +145,7 @@ export class DanakServicesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@ApiOperation({ summary: "toggle status of danak service slider => admin route" })
|
@ApiOperation({ summary: "toggle status of danak service slider => admin route" })
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post("toggle-slider/:id")
|
@Post("toggle-slider/:id")
|
||||||
@@ -192,7 +192,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "update danak service" })
|
@ApiOperation({ summary: "update danak service" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_UPDATE)
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
updateDanakService(
|
updateDanakService(
|
||||||
@Param() paramDto: ParamDto,
|
@Param() paramDto: ParamDto,
|
||||||
@@ -204,7 +204,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "delete danak service" })
|
@ApiOperation({ summary: "delete danak service" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_DELETE)
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
deleteServiceById(@Param() paramDto: ParamDto) {
|
deleteServiceById(@Param() paramDto: ParamDto) {
|
||||||
return this.danakServicesService.deleteServiceById(paramDto.id);
|
return this.danakServicesService.deleteServiceById(paramDto.id);
|
||||||
@@ -212,7 +212,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "get service users" })
|
@ApiOperation({ summary: "get service users" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@Get(":id/users")
|
@Get(":id/users")
|
||||||
getServiceUsers(@Param() paramDto: ParamDto) {
|
getServiceUsers(@Param() paramDto: ParamDto) {
|
||||||
return this.danakServicesService.getServiceUsers(paramDto.id);
|
return this.danakServicesService.getServiceUsers(paramDto.id);
|
||||||
@@ -235,7 +235,7 @@ export class DanakServicesController {
|
|||||||
@ApiOperation({ summary: "get danak service reviews ==> admin route" })
|
@ApiOperation({ summary: "get danak service reviews ==> admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@Pagination()
|
@Pagination()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@Get("reviews")
|
@Get("reviews")
|
||||||
getDanakServiceReviews(@Query() queryDto: DanakServiceReviewQueryDto) {
|
getDanakServiceReviews(@Query() queryDto: DanakServiceReviewQueryDto) {
|
||||||
return this.danakServiceReviewService.getDanakServiceReviews(queryDto);
|
return this.danakServiceReviewService.getDanakServiceReviews(queryDto);
|
||||||
@@ -243,7 +243,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "update danak service review status ==> admin route" })
|
@ApiOperation({ summary: "update danak service review status ==> admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_UPDATE)
|
||||||
@Patch("reviews/:id/:status")
|
@Patch("reviews/:id/:status")
|
||||||
updateDanakServiceReviewStatus(@Param() updateParamDto: UpdateReviewStatusDto) {
|
updateDanakServiceReviewStatus(@Param() updateParamDto: UpdateReviewStatusDto) {
|
||||||
return this.danakServiceReviewService.updateDanakServiceReviewStatus(updateParamDto);
|
return this.danakServiceReviewService.updateDanakServiceReviewStatus(updateParamDto);
|
||||||
@@ -252,7 +252,7 @@ export class DanakServicesController {
|
|||||||
//------------------------ service guides ------------------------
|
//------------------------ service guides ------------------------
|
||||||
@ApiOperation({ summary: "Create a service guide => admin route" })
|
@ApiOperation({ summary: "Create a service guide => admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@Post("guides")
|
@Post("guides")
|
||||||
createServiceGuide(@Body() createDto: CreateServiceGuideDto) {
|
createServiceGuide(@Body() createDto: CreateServiceGuideDto) {
|
||||||
return this.danakServicesService.createServiceGuide(createDto);
|
return this.danakServicesService.createServiceGuide(createDto);
|
||||||
@@ -266,7 +266,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Update service guide => admin route" })
|
@ApiOperation({ summary: "Update service guide => admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_UPDATE)
|
||||||
@Patch("guides/:id")
|
@Patch("guides/:id")
|
||||||
updateServiceGuide(@Param() paramDto: ParamDto, @Body() updateDto: UpdateServiceGuideDto) {
|
updateServiceGuide(@Param() paramDto: ParamDto, @Body() updateDto: UpdateServiceGuideDto) {
|
||||||
return this.danakServicesService.updateServiceGuide(paramDto.id, updateDto);
|
return this.danakServicesService.updateServiceGuide(paramDto.id, updateDto);
|
||||||
@@ -274,7 +274,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Delete service guide => admin route" })
|
@ApiOperation({ summary: "Delete service guide => admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_DELETE)
|
||||||
@Delete("guides/:id")
|
@Delete("guides/:id")
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
deleteServiceGuide(@Param() paramDto: ParamDto) {
|
deleteServiceGuide(@Param() paramDto: ParamDto) {
|
||||||
@@ -283,7 +283,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get service guide by id => admin route" })
|
@ApiOperation({ summary: "Get service guide by id => admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@Get("guides/:id")
|
@Get("guides/:id")
|
||||||
getServiceGuideById(@Param() paramDto: ParamDto) {
|
getServiceGuideById(@Param() paramDto: ParamDto) {
|
||||||
return this.danakServicesService.getServiceGuideById(paramDto.id);
|
return this.danakServicesService.getServiceGuideById(paramDto.id);
|
||||||
@@ -299,7 +299,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get service feedbacks => admin route" })
|
@ApiOperation({ summary: "Get service feedbacks => admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@Pagination()
|
@Pagination()
|
||||||
@Get("feedback")
|
@Get("feedback")
|
||||||
getServiceFeedbacks(@Query() queryDto: ServiceFeedbackQueryDto) {
|
getServiceFeedbacks(@Query() queryDto: ServiceFeedbackQueryDto) {
|
||||||
@@ -308,7 +308,7 @@ export class DanakServicesController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get service feedback statistics => admin route" })
|
@ApiOperation({ summary: "Get service feedback statistics => admin route" })
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@Get(":id/feedback")
|
@Get(":id/feedback")
|
||||||
getServiceFeedbackStats(@Param() paramDto: ParamDto) {
|
getServiceFeedbackStats(@Param() paramDto: ParamDto) {
|
||||||
return this.danakServicesService.getServiceFeedback(paramDto.id);
|
return this.danakServicesService.getServiceFeedback(paramDto.id);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class DiscountsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Create a new discount" })
|
@ApiOperation({ summary: "Create a new discount" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.DISCOUNTS)
|
@PermissionsDec(PermissionEnum.DISCOUNTS_CREATE)
|
||||||
@Post()
|
@Post()
|
||||||
@ApiResponse({ status: 201, description: "Discount created successfully" })
|
@ApiResponse({ status: 201, description: "Discount created successfully" })
|
||||||
create(@Body() createDiscountDto: CreateDiscountDto) {
|
create(@Body() createDiscountDto: CreateDiscountDto) {
|
||||||
@@ -28,7 +28,7 @@ export class DiscountsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get all discounts for admin" })
|
@ApiOperation({ summary: "Get all discounts for admin" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.DISCOUNTS)
|
@PermissionsDec(PermissionEnum.DISCOUNTS_READ)
|
||||||
@Get()
|
@Get()
|
||||||
@ApiResponse({ status: 200, description: "Discounts fetched successfully" })
|
@ApiResponse({ status: 200, description: "Discounts fetched successfully" })
|
||||||
getDiscountsForAdmin(@Query() queryDto: SearchDiscountQueryDto) {
|
getDiscountsForAdmin(@Query() queryDto: SearchDiscountQueryDto) {
|
||||||
@@ -37,7 +37,7 @@ export class DiscountsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get discount by id" })
|
@ApiOperation({ summary: "Get discount by id" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.DISCOUNTS)
|
@PermissionsDec(PermissionEnum.DISCOUNTS_READ)
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
@ApiResponse({ status: 200, description: "Discount fetched successfully" })
|
@ApiResponse({ status: 200, description: "Discount fetched successfully" })
|
||||||
getDiscountById(@Param() paramDto: ParamDto) {
|
getDiscountById(@Param() paramDto: ParamDto) {
|
||||||
@@ -46,7 +46,7 @@ export class DiscountsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Toggle discount status" })
|
@ApiOperation({ summary: "Toggle discount status" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.DISCOUNTS)
|
@PermissionsDec(PermissionEnum.DISCOUNTS_CREATE)
|
||||||
@Post(":id/toggle-status")
|
@Post(":id/toggle-status")
|
||||||
toggleDiscountStatus(@Param() paramDto: ParamDto) {
|
toggleDiscountStatus(@Param() paramDto: ParamDto) {
|
||||||
return this.discountsService.toggleDiscountStatus(paramDto.id);
|
return this.discountsService.toggleDiscountStatus(paramDto.id);
|
||||||
@@ -54,7 +54,7 @@ export class DiscountsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Delete discount" })
|
@ApiOperation({ summary: "Delete discount" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.DISCOUNTS)
|
@PermissionsDec(PermissionEnum.DISCOUNTS_DELETE)
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
deleteDiscount(@Param() paramDto: ParamDto) {
|
deleteDiscount(@Param() paramDto: ParamDto) {
|
||||||
return this.discountsService.softDeleteDiscount(paramDto.id);
|
return this.discountsService.softDeleteDiscount(paramDto.id);
|
||||||
@@ -62,7 +62,7 @@ export class DiscountsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Update discount" })
|
@ApiOperation({ summary: "Update discount" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.DISCOUNTS)
|
@PermissionsDec(PermissionEnum.DISCOUNTS_UPDATE)
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
updateDiscount(@Param() paramDto: ParamDto, @Body() updateDiscountDto: UpdateDiscountDto) {
|
updateDiscount(@Param() paramDto: ParamDto, @Body() updateDiscountDto: UpdateDiscountDto) {
|
||||||
return this.discountsService.updateDiscount(paramDto.id, updateDiscountDto);
|
return this.discountsService.updateDiscount(paramDto.id, updateDiscountDto);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { FindBusinessesDto } from './dto/find-businesses.dto';
|
|||||||
import { AuthGuards } from '../../common/decorators/auth-guard.decorator';
|
import { AuthGuards } from '../../common/decorators/auth-guard.decorator';
|
||||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||||
import { AdminRoute } from '../../common/decorators/admin.decorator';
|
import { AdminRoute } from '../../common/decorators/admin.decorator';
|
||||||
|
import { PermissionsDec } from '../../common/decorators/permission.decorator';
|
||||||
|
import { PermissionEnum } from '../users/enums/permission.enum';
|
||||||
|
|
||||||
@Controller('dmail')
|
@Controller('dmail')
|
||||||
@ApiTags('d-mail')
|
@ApiTags('d-mail')
|
||||||
@@ -15,6 +17,7 @@ export class DmailController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
|
@PermissionsDec(PermissionEnum.DMAIL)
|
||||||
findAll(@Query() query: FindBusinessesDto) {
|
findAll(@Query() query: FindBusinessesDto) {
|
||||||
return this.dmailService.findAllBusinesses(query)
|
return this.dmailService.findAllBusinesses(query)
|
||||||
}
|
}
|
||||||
@@ -27,6 +30,7 @@ export class DmailController {
|
|||||||
|
|
||||||
@Patch(':id/active')
|
@Patch(':id/active')
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
|
@PermissionsDec(PermissionEnum.DMAIL)
|
||||||
update(@Param('id') id: string, @Body() updateDmailDto: UpdateDmailAciveStatusDto) {
|
update(@Param('id') id: string, @Body() updateDmailDto: UpdateDmailAciveStatusDto) {
|
||||||
return this.dmailService.updateDMailActiveStatus(id, updateDmailDto.isActive);
|
return this.dmailService.updateDMailActiveStatus(id, updateDmailDto.isActive);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,6 +444,46 @@ export class RestaurantService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async finishChargeRequest(invoiceId: string) {
|
||||||
|
this.logger.log(`Finishing charge request for invoice ${invoiceId}`);
|
||||||
|
try {
|
||||||
|
const url = `${this.config.baseUrl}/super-admin/restaurants/charge-request/finish`;
|
||||||
|
const { data } = await firstValueFrom(
|
||||||
|
this.httpService
|
||||||
|
.post(url, { invoiceId }, {
|
||||||
|
headers: this.getHeaders(),
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
catchError((err: AxiosError) => {
|
||||||
|
this.logger.error(
|
||||||
|
`Failed to finish charge request for invoice ${invoiceId}: ${err.message}`,
|
||||||
|
err.stack,
|
||||||
|
);
|
||||||
|
return throwError(() => err);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
this.logger.log(`Successfully finished charge request for invoice ${invoiceId}`);
|
||||||
|
return data;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof AxiosError && error.response) {
|
||||||
|
this.logger.error(
|
||||||
|
`External API error finishing charge request for invoice ${invoiceId} (status ${error.response.status}):`,
|
||||||
|
error.response.data,
|
||||||
|
);
|
||||||
|
const errorResponse = {
|
||||||
|
...error.response.data,
|
||||||
|
message: error.response.data.error?.message || error.message,
|
||||||
|
};
|
||||||
|
throw new HttpException(errorResponse, error.response.status);
|
||||||
|
}
|
||||||
|
this.logger.error(
|
||||||
|
`Error finishing charge request for invoice ${invoiceId}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async directLogin(userSubscriptionId: string, userId: string) {
|
async directLogin(userSubscriptionId: string, userId: string) {
|
||||||
try {
|
try {
|
||||||
// 1. Get userSubscription by id
|
// 1. Get userSubscription by id
|
||||||
|
|||||||
@@ -21,28 +21,28 @@ export class InvoicesController {
|
|||||||
constructor(private readonly invoiceService: InvoicesService) {}
|
constructor(private readonly invoiceService: InvoicesService) {}
|
||||||
|
|
||||||
@ApiOperation({ summary: "create an invoice ==> admin route" })
|
@ApiOperation({ summary: "create an invoice ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.INVOICES)
|
@PermissionsDec(PermissionEnum.INVOICES_CREATE)
|
||||||
@Post()
|
@Post()
|
||||||
createInvoice(@Body() createDto: CreateInvoiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
createInvoice(@Body() createDto: CreateInvoiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.invoiceService.createInvoiceAdmin(createDto, userIpAndHeaders);
|
return this.invoiceService.createInvoiceAdmin(createDto, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "update an invoice ==> admin route" })
|
@ApiOperation({ summary: "update an invoice ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.INVOICES)
|
@PermissionsDec(PermissionEnum.INVOICES_UPDATE)
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
updateInvoice(@Param() paramDto: ParamDto, @Body() updateDto: UpdateInvoiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
updateInvoice(@Param() paramDto: ParamDto, @Body() updateDto: UpdateInvoiceDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.invoiceService.updateInvoiceAdmin(paramDto.id, updateDto, userIpAndHeaders);
|
return this.invoiceService.updateInvoiceAdmin(paramDto.id, updateDto, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "delete an invoice ==> admin route" })
|
@ApiOperation({ summary: "delete an invoice ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.INVOICES)
|
@PermissionsDec(PermissionEnum.INVOICES_DELETE)
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
deleteInvoice(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
deleteInvoice(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) {
|
||||||
return this.invoiceService.deleteInvoiceAdmin(paramDto.id, userIpAndHeaders);
|
return this.invoiceService.deleteInvoiceAdmin(paramDto.id, userIpAndHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "get all invoices ==> admin route" })
|
@ApiOperation({ summary: "get all invoices ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.INVOICES)
|
@PermissionsDec(PermissionEnum.INVOICES_READ)
|
||||||
@Get()
|
@Get()
|
||||||
getInvoices(@Query() queryDto: InvoicesSearchQueryDto) {
|
getInvoices(@Query() queryDto: InvoicesSearchQueryDto) {
|
||||||
return this.invoiceService.getInvoices(queryDto);
|
return this.invoiceService.getInvoices(queryDto);
|
||||||
|
|||||||
@@ -948,9 +948,13 @@ export class InvoicesService {
|
|||||||
await this.addNotifyForWalletDeduction(invoice, user, userWallet, WalletMessage.SUPPORT_PLAN_WALLET_TRANSFER);
|
await this.addNotifyForWalletDeduction(invoice, user, userWallet, WalletMessage.SUPPORT_PLAN_WALLET_TRANSFER);
|
||||||
//
|
//
|
||||||
} else if (invoice.isExternal) {
|
} else if (invoice.isExternal) {
|
||||||
if (invoice.items[0]?.name == 'purchase_design') {
|
if (invoice.items[0]?.name == 'dmenu_wallet_charge') {
|
||||||
|
this.restaurantService.finishChargeRequest(invoice.id);
|
||||||
|
} else if (invoice.items[0]?.name == 'purchase_plan') {
|
||||||
this.dpageService.finishPurchaseDesign(invoice.externalBusinessId!, invoice.id)
|
this.dpageService.finishPurchaseDesign(invoice.externalBusinessId!, invoice.id)
|
||||||
} else if (invoice.items[0]?.name == 'purchase_new_catalogue') {
|
}
|
||||||
|
|
||||||
|
else if (invoice.items[0]?.name == 'purchase_new_catalogue') {
|
||||||
// call dpage api
|
// call dpage api
|
||||||
const businessId = invoice.externalBusinessId
|
const businessId = invoice.externalBusinessId
|
||||||
if (!businessId) {
|
if (!businessId) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class LearningController {
|
|||||||
constructor(private readonly learningService: LearningService) {}
|
constructor(private readonly learningService: LearningService) {}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Create learning video" })
|
@ApiOperation({ summary: "Create learning video" })
|
||||||
@PermissionsDec(PermissionEnum.LEARNINGS)
|
@PermissionsDec(PermissionEnum.LEARNINGS_CREATE)
|
||||||
@Post()
|
@Post()
|
||||||
createLearningVideo(@Body() createDto: CreateLearningDto) {
|
createLearningVideo(@Body() createDto: CreateLearningDto) {
|
||||||
return this.learningService.createLearningVideo(createDto);
|
return this.learningService.createLearningVideo(createDto);
|
||||||
@@ -37,14 +37,14 @@ export class LearningController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "delete learning video by id" })
|
@ApiOperation({ summary: "delete learning video by id" })
|
||||||
@PermissionsDec(PermissionEnum.LEARNINGS)
|
@PermissionsDec(PermissionEnum.LEARNINGS_DELETE)
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
deleteLearningVideoById(@Param() paramDto: ParamDto) {
|
deleteLearningVideoById(@Param() paramDto: ParamDto) {
|
||||||
return this.learningService.deleteLearningVideoById(paramDto.id);
|
return this.learningService.deleteLearningVideoById(paramDto.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "update learning video by id" })
|
@ApiOperation({ summary: "update learning video by id" })
|
||||||
@PermissionsDec(PermissionEnum.LEARNINGS)
|
@PermissionsDec(PermissionEnum.LEARNINGS_UPDATE)
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
updateLearningVideoById(@Param() paramDto: ParamDto, @Body() updateDto: UpdateLearningDto) {
|
updateLearningVideoById(@Param() paramDto: ParamDto, @Body() updateDto: UpdateLearningDto) {
|
||||||
return this.learningService.updateLearningVideo(paramDto.id, updateDto);
|
return this.learningService.updateLearningVideo(paramDto.id, updateDto);
|
||||||
@@ -53,7 +53,7 @@ export class LearningController {
|
|||||||
//----------------- category management ----------------------
|
//----------------- category management ----------------------
|
||||||
|
|
||||||
@ApiOperation({ summary: "Create learning category" })
|
@ApiOperation({ summary: "Create learning category" })
|
||||||
@PermissionsDec(PermissionEnum.LEARNINGS)
|
@PermissionsDec(PermissionEnum.LEARNINGS_CREATE)
|
||||||
@Post("category")
|
@Post("category")
|
||||||
createCategory(@Body() createDto: CreateLearningCategoryDto) {
|
createCategory(@Body() createDto: CreateLearningCategoryDto) {
|
||||||
return this.learningService.createLearningCategory(createDto);
|
return this.learningService.createLearningCategory(createDto);
|
||||||
@@ -72,14 +72,14 @@ export class LearningController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "update learning category ==> admin route" })
|
@ApiOperation({ summary: "update learning category ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.LEARNINGS)
|
@PermissionsDec(PermissionEnum.LEARNINGS_UPDATE)
|
||||||
@Patch("category/:id")
|
@Patch("category/:id")
|
||||||
updateLearningCategory(@Param() paramDto: ParamDto, @Body() updateDto: UpdateLearningCategoryDto) {
|
updateLearningCategory(@Param() paramDto: ParamDto, @Body() updateDto: UpdateLearningCategoryDto) {
|
||||||
return this.learningService.updateLearningCategory(paramDto.id, updateDto);
|
return this.learningService.updateLearningCategory(paramDto.id, updateDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "delete category by id" })
|
@ApiOperation({ summary: "delete category by id" })
|
||||||
@PermissionsDec(PermissionEnum.LEARNINGS)
|
@PermissionsDec(PermissionEnum.LEARNINGS_DELETE)
|
||||||
@Delete("category/:id")
|
@Delete("category/:id")
|
||||||
deleteCategoryById(@Param() paramDto: ParamDto) {
|
deleteCategoryById(@Param() paramDto: ParamDto) {
|
||||||
return this.learningService.deleteCategoryById(paramDto.id);
|
return this.learningService.deleteCategoryById(paramDto.id);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export class PaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "get deposit request of users ==> admin route" })
|
@ApiOperation({ summary: "get deposit request of users ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_READ)
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@Pagination()
|
@Pagination()
|
||||||
@Get("deposit/transfer")
|
@Get("deposit/transfer")
|
||||||
@@ -54,7 +54,7 @@ export class PaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "get deposit gateway payment ==> admin route" })
|
@ApiOperation({ summary: "get deposit gateway payment ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_READ)
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@Pagination()
|
@Pagination()
|
||||||
@Get("deposit/gateway")
|
@Get("deposit/gateway")
|
||||||
@@ -63,7 +63,7 @@ export class PaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "approve deposit request ==> admin route" })
|
@ApiOperation({ summary: "approve deposit request ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_UPDATE)
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@Post("deposit/transfer/approve/:id")
|
@Post("deposit/transfer/approve/:id")
|
||||||
approverDepositRequest(@Param() paramDto: ParamDto) {
|
approverDepositRequest(@Param() paramDto: ParamDto) {
|
||||||
@@ -71,7 +71,7 @@ export class PaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "reject deposit request ==> admin route" })
|
@ApiOperation({ summary: "reject deposit request ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_UPDATE)
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@Post("deposit/transfer/reject/:id")
|
@Post("deposit/transfer/reject/:id")
|
||||||
rejectDepositRequest(@Param() paramDto: ParamDto, @Body() rejectDto: RejectDepositRequestDto) {
|
rejectDepositRequest(@Param() paramDto: ParamDto, @Body() rejectDto: RejectDepositRequestDto) {
|
||||||
@@ -79,7 +79,7 @@ export class PaymentsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "get transaction for admin" })
|
@ApiOperation({ summary: "get transaction for admin" })
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_READ)
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@Pagination()
|
@Pagination()
|
||||||
@Get("transactions")
|
@Get("transactions")
|
||||||
@@ -108,7 +108,7 @@ export class PaymentsController {
|
|||||||
///------------------- bank account -----------------------------
|
///------------------- bank account -----------------------------
|
||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS, PermissionEnum.BANK_ACCOUNTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_CREATE, PermissionEnum.BANK_ACCOUNTS_CREATE)
|
||||||
@ApiOperation({ summary: "add bank account for transfer and deposit ==> admin route" })
|
@ApiOperation({ summary: "add bank account for transfer and deposit ==> admin route" })
|
||||||
@Post("bank-account")
|
@Post("bank-account")
|
||||||
addBankAccount(@Body() createDto: CreateBankAccountDto) {
|
addBankAccount(@Body() createDto: CreateBankAccountDto) {
|
||||||
@@ -124,7 +124,7 @@ export class PaymentsController {
|
|||||||
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@ApiOperation({ summary: "update bank account with its id" })
|
@ApiOperation({ summary: "update bank account with its id" })
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS, PermissionEnum.BANK_ACCOUNTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_UPDATE, PermissionEnum.BANK_ACCOUNTS_UPDATE)
|
||||||
@Patch("bank-account/:id")
|
@Patch("bank-account/:id")
|
||||||
updateBankAccount(@Body() updateDto: UpdateBankAccountDto, @Param() paramDto: ParamDto) {
|
updateBankAccount(@Body() updateDto: UpdateBankAccountDto, @Param() paramDto: ParamDto) {
|
||||||
return this.paymentsService.updateBankAccount(updateDto, paramDto.id);
|
return this.paymentsService.updateBankAccount(updateDto, paramDto.id);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class ResellerController {
|
|||||||
//***************** DSC Endpoints **************** */
|
//***************** DSC Endpoints **************** */
|
||||||
@ApiOperation({ summary: "Create reseller ==> admin route" })
|
@ApiOperation({ summary: "Create reseller ==> admin route" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.RESELLER)
|
@PermissionsDec(PermissionEnum.RESELLER_CREATE)
|
||||||
@Post()
|
@Post()
|
||||||
createReseller(@Body() dto: CreateResellerDto) {
|
createReseller(@Body() dto: CreateResellerDto) {
|
||||||
return this.resellerService.create(dto);
|
return this.resellerService.create(dto);
|
||||||
@@ -42,7 +42,7 @@ export class ResellerController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get resellers ==> admin route" })
|
@ApiOperation({ summary: "Get resellers ==> admin route" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.RESELLER)
|
@PermissionsDec(PermissionEnum.RESELLER_READ)
|
||||||
@Get()
|
@Get()
|
||||||
getResellers() {
|
getResellers() {
|
||||||
return this.resellerService.findAll();
|
return this.resellerService.findAll();
|
||||||
@@ -52,7 +52,7 @@ export class ResellerController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get withdraw requests ==> admin route" })
|
@ApiOperation({ summary: "Get withdraw requests ==> admin route" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.RESELLER)
|
@PermissionsDec(PermissionEnum.RESELLER_READ)
|
||||||
@Pagination()
|
@Pagination()
|
||||||
@Get("withdraw-request/admin")
|
@Get("withdraw-request/admin")
|
||||||
getRewardRequests(@Query() query: SearchWithdrawRequestQueryDto) {
|
getRewardRequests(@Query() query: SearchWithdrawRequestQueryDto) {
|
||||||
@@ -61,7 +61,7 @@ export class ResellerController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Confirm withdraw request ==> admin route" })
|
@ApiOperation({ summary: "Confirm withdraw request ==> admin route" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.RESELLER)
|
@PermissionsDec(PermissionEnum.RESELLER_UPDATE)
|
||||||
@Patch("withdraw-request/:id")
|
@Patch("withdraw-request/:id")
|
||||||
confirmWithdrawRequests(@Param() paramDto: ParamDto, @Body() dto: ConfirmwithdrawRequestDto) {
|
confirmWithdrawRequests(@Param() paramDto: ParamDto, @Body() dto: ConfirmwithdrawRequestDto) {
|
||||||
return this.resellerService.confirmWithdrawRequest(paramDto.id, dto.transactionId);
|
return this.resellerService.confirmWithdrawRequest(paramDto.id, dto.transactionId);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class SlidersController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "create slider (admin route)" })
|
@ApiOperation({ summary: "create slider (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.SLIDERS_CREATE)
|
||||||
@Post()
|
@Post()
|
||||||
create(@Body() createSliderDto: CreateSliderDto) {
|
create(@Body() createSliderDto: CreateSliderDto) {
|
||||||
return this.slidersService.create(createSliderDto);
|
return this.slidersService.create(createSliderDto);
|
||||||
@@ -26,7 +26,7 @@ export class SlidersController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get all sliders (admin route)" })
|
@ApiOperation({ summary: "Get all sliders (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.SLIDERS_READ)
|
||||||
@Get()
|
@Get()
|
||||||
getSlidersList(@Query() queryDto: PaginationDto) {
|
getSlidersList(@Query() queryDto: PaginationDto) {
|
||||||
return this.slidersService.getSlidersList(queryDto);
|
return this.slidersService.getSlidersList(queryDto);
|
||||||
@@ -34,7 +34,7 @@ export class SlidersController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Get a slider by id (admin route)" })
|
@ApiOperation({ summary: "Get a slider by id (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.SLIDERS_READ)
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
getSliderById(@Param() paramDto: ParamDto) {
|
getSliderById(@Param() paramDto: ParamDto) {
|
||||||
return this.slidersService.getSliderById(paramDto.id);
|
return this.slidersService.getSliderById(paramDto.id);
|
||||||
@@ -42,7 +42,7 @@ export class SlidersController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Update a slider (admin route)" })
|
@ApiOperation({ summary: "Update a slider (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.SLIDERS_UPDATE)
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
update(@Param() paramDto: ParamDto, @Body() updateSliderDto: UpdateSliderDto) {
|
update(@Param() paramDto: ParamDto, @Body() updateSliderDto: UpdateSliderDto) {
|
||||||
return this.slidersService.update(paramDto.id, updateSliderDto);
|
return this.slidersService.update(paramDto.id, updateSliderDto);
|
||||||
@@ -50,7 +50,7 @@ export class SlidersController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "Delete a slider (admin route)" })
|
@ApiOperation({ summary: "Delete a slider (admin route)" })
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.BLOGS)
|
@PermissionsDec(PermissionEnum.SLIDERS_DELETE)
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
remove(@Param() paramDto: ParamDto) {
|
remove(@Param() paramDto: ParamDto) {
|
||||||
return this.slidersService.remove(paramDto.id);
|
return this.slidersService.remove(paramDto.id);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class SubscriptionsController {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Delete subscription =. admin route" })
|
@ApiOperation({ summary: "Delete subscription =. admin route" })
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_DELETE)
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
deleteSubscription(@Param('id') id :string) {
|
deleteSubscription(@Param('id') id :string) {
|
||||||
@@ -39,14 +39,14 @@ export class SubscriptionsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Create a subscription plan" })
|
@ApiOperation({ summary: "Create a subscription plan" })
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@Post()
|
@Post()
|
||||||
createSubscription(@Body() createDto: AddSubscriptionsToServiceDto) {
|
createSubscription(@Body() createDto: AddSubscriptionsToServiceDto) {
|
||||||
return this.subscriptionService.createSubscriptionsPlan(createDto);
|
return this.subscriptionService.createSubscriptionsPlan(createDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "get all subscription plans" })
|
@ApiOperation({ summary: "get all subscription plans" })
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
@Get("service/:serviceId")
|
@Get("service/:serviceId")
|
||||||
getServiceSubscriptions(@Param() paramDto: ServiceIdParamDto, @Query() queryDto: ServiceSubsQueryDto) {
|
getServiceSubscriptions(@Param() paramDto: ServiceIdParamDto, @Query() queryDto: ServiceSubsQueryDto) {
|
||||||
return this.subscriptionService.getServiceSubscriptions(paramDto.serviceId, queryDto);
|
return this.subscriptionService.getServiceSubscriptions(paramDto.serviceId, queryDto);
|
||||||
@@ -65,7 +65,7 @@ export class SubscriptionsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "toggle status of service subs" })
|
@ApiOperation({ summary: "toggle status of service subs" })
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_CREATE)
|
||||||
@Post("toggle-status/:id")
|
@Post("toggle-status/:id")
|
||||||
toggleStatusOfServiceSub(@Param() paramDto: ParamDto) {
|
toggleStatusOfServiceSub(@Param() paramDto: ParamDto) {
|
||||||
return this.subscriptionService.toggleSubStatus(paramDto.id);
|
return this.subscriptionService.toggleSubStatus(paramDto.id);
|
||||||
@@ -73,14 +73,14 @@ export class SubscriptionsController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: "get a subscription plan by id" })
|
@ApiOperation({ summary: "get a subscription plan by id" })
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_READ)
|
||||||
getSubscription(@Param() paramDto: ParamDto) {
|
getSubscription(@Param() paramDto: ParamDto) {
|
||||||
return this.subscriptionService.getSubscriptionPlanById(paramDto.id);
|
return this.subscriptionService.getSubscriptionPlanById(paramDto.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "update a subscription plan by id" })
|
@ApiOperation({ summary: "update a subscription plan by id" })
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
@PermissionsDec(PermissionEnum.SERVICES)
|
@PermissionsDec(PermissionEnum.SERVICES_UPDATE)
|
||||||
updateSubscription(@Param() paramDto: ParamDto, @Body() updateDto: UpdateSubscriptionPlanDto) {
|
updateSubscription(@Param() paramDto: ParamDto, @Body() updateDto: UpdateSubscriptionPlanDto) {
|
||||||
return this.subscriptionService.updateSubscriptionPlan(paramDto.id, updateDto);
|
return this.subscriptionService.updateSubscriptionPlan(paramDto.id, updateDto);
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,7 @@ export class SubscriptionsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "get all user subscriptions with pagination and filters ==> admin route" })
|
@ApiOperation({ summary: "get all user subscriptions with pagination and filters ==> admin route" })
|
||||||
@PermissionsDec(PermissionEnum.PAYMENTS)
|
@PermissionsDec(PermissionEnum.PAYMENTS_READ)
|
||||||
@Get("admin/user-subscriptions/all")
|
@Get("admin/user-subscriptions/all")
|
||||||
getAllUserSubscriptions(@Query() queryDto: AdminUserSubscriptionsQueryDto) {
|
getAllUserSubscriptions(@Query() queryDto: AdminUserSubscriptionsQueryDto) {
|
||||||
return this.subscriptionService.getAllUserSubscriptions(queryDto);
|
return this.subscriptionService.getAllUserSubscriptions(queryDto);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class SupportPlansController {
|
|||||||
constructor(private readonly supportPlansService: SupportPlansService) {}
|
constructor(private readonly supportPlansService: SupportPlansService) {}
|
||||||
|
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.SUPPORT_PLAN)
|
@PermissionsDec(PermissionEnum.SUPPORT_PLAN_CREATE)
|
||||||
@ApiOperation({ summary: "Create a new support plan (admin)" })
|
@ApiOperation({ summary: "Create a new support plan (admin)" })
|
||||||
@Post()
|
@Post()
|
||||||
createSupportPlan(@Body() createSupportPlanDto: CreateSupportPlanDto) {
|
createSupportPlan(@Body() createSupportPlanDto: CreateSupportPlanDto) {
|
||||||
@@ -27,7 +27,7 @@ export class SupportPlansController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.SUPPORT_PLAN)
|
@PermissionsDec(PermissionEnum.SUPPORT_PLAN_READ)
|
||||||
@ApiOperation({ summary: "Get all support plans (admin)" })
|
@ApiOperation({ summary: "Get all support plans (admin)" })
|
||||||
@Get("list")
|
@Get("list")
|
||||||
getSupportPlansList(@Query() queryDto: GetSupportPlanListQueryDto) {
|
getSupportPlansList(@Query() queryDto: GetSupportPlanListQueryDto) {
|
||||||
@@ -35,7 +35,7 @@ export class SupportPlansController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.SUPPORT_PLAN)
|
@PermissionsDec(PermissionEnum.SUPPORT_PLAN_READ)
|
||||||
@ApiOperation({ summary: "Get a support plan by id (admin)" })
|
@ApiOperation({ summary: "Get a support plan by id (admin)" })
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
getSupportPlanById(@Param() paramDto: ParamDto) {
|
getSupportPlanById(@Param() paramDto: ParamDto) {
|
||||||
@@ -43,7 +43,7 @@ export class SupportPlansController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.SUPPORT_PLAN)
|
@PermissionsDec(PermissionEnum.SUPPORT_PLAN_DELETE)
|
||||||
@ApiOperation({ summary: "Delete a support plan by id (admin)" })
|
@ApiOperation({ summary: "Delete a support plan by id (admin)" })
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
deleteSupportPlanById(@Param() paramDto: ParamDto) {
|
deleteSupportPlanById(@Param() paramDto: ParamDto) {
|
||||||
@@ -51,7 +51,7 @@ export class SupportPlansController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.SUPPORT_PLAN)
|
@PermissionsDec(PermissionEnum.SUPPORT_PLAN_UPDATE)
|
||||||
@ApiOperation({ summary: "Update a support plan by id (admin)" })
|
@ApiOperation({ summary: "Update a support plan by id (admin)" })
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
updateSupportPlanById(@Param() paramDto: ParamDto, @Body() updateSupportPlanDto: UpdateSupportPlanDto) {
|
updateSupportPlanById(@Param() paramDto: ParamDto, @Body() updateSupportPlanDto: UpdateSupportPlanDto) {
|
||||||
@@ -71,7 +71,7 @@ export class SupportPlansController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AdminRoute()
|
@AdminRoute()
|
||||||
@PermissionsDec(PermissionEnum.SUPPORT_PLAN)
|
@PermissionsDec(PermissionEnum.SUPPORT_PLAN_READ)
|
||||||
@ApiOperation({ summary: "Get support plan users (admin)" })
|
@ApiOperation({ summary: "Get support plan users (admin)" })
|
||||||
@Get(":id/users")
|
@Get(":id/users")
|
||||||
getSupportPlanUsers(@Param() paramDto: ParamDto, @Query() queryDto: SearchSupportPlanUsersQueryDto) {
|
getSupportPlanUsers(@Param() paramDto: ParamDto, @Query() queryDto: SearchSupportPlanUsersQueryDto) {
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { Controller, Post, Patch, Delete, Param, Body } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||||
|
import { AttachmentService } from '../providers/attachment.service';
|
||||||
|
import { CreateAttachmentDto } from '../dto/attachment/create-attachment.dto';
|
||||||
|
import { UpdateAttachmentDto } from '../dto/attachment/update-attachment.dto';
|
||||||
|
import { TMAttachment } from '../entities/attachment.entity';
|
||||||
|
import { AuthGuards } from '../../../common/decorators/auth-guard.decorator';
|
||||||
|
import { AdminRoute } from '../../../common/decorators/admin.decorator';
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags('Task Manager - Attachments')
|
||||||
|
@Controller('task-manager/attachments')
|
||||||
|
export class AttachmentController {
|
||||||
|
constructor(private readonly attachmentService: AttachmentService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: 'Create a new attachment' })
|
||||||
|
@ApiResponse({ status: 201, description: 'Attachment created', type: TMAttachment })
|
||||||
|
@ApiResponse({ status: 404, description: 'Task not found' })
|
||||||
|
create(@Body() body: CreateAttachmentDto): Promise<TMAttachment> {
|
||||||
|
return this.attachmentService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ summary: 'Update an attachment' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Attachment ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Attachment updated', type: TMAttachment })
|
||||||
|
@ApiResponse({ status: 404, description: 'Attachment or Task not found' })
|
||||||
|
update(@Param('id') id: string, @Body() body: UpdateAttachmentDto): Promise<TMAttachment> {
|
||||||
|
return this.attachmentService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ summary: 'Delete an attachment' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Attachment ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Attachment deleted' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Attachment not found' })
|
||||||
|
remove(@Param('id') id: string): Promise<void> {
|
||||||
|
return this.attachmentService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Controller, Post, Patch, Delete, Param, Body } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||||
|
import { CheckListItemService } from "../providers/check-list-item.service";
|
||||||
|
import { CreateCheckListItemDto } from "../dto/check-list-item/create-check-list-item.dto";
|
||||||
|
import { UpdateCheckListItemDto } from "../dto/check-list-item/update-check-list-item.dto";
|
||||||
|
import { TMCheckListItem } from "../entities/check-list-item.entity";
|
||||||
|
import { AuthGuards } from "../../../common/decorators/auth-guard.decorator";
|
||||||
|
import { AdminRoute } from "../../../common/decorators/admin.decorator";
|
||||||
|
import { ParamDto } from "../../../common/DTO/param.dto";
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags("Task Manager - Check List Items")
|
||||||
|
@Controller("task-manager/check-list-items")
|
||||||
|
export class CheckListItemController {
|
||||||
|
constructor(private readonly checkListItemService: CheckListItemService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: "Create a new check list item" })
|
||||||
|
@ApiResponse({ status: 201, description: "CheckListItem created", type: TMCheckListItem })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
create(@Body() body: CreateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
return this.checkListItemService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@ApiOperation({ summary: "Update a check list item" })
|
||||||
|
@ApiParam({ name: "id", description: "CheckListItem ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "CheckListItem updated", type: TMCheckListItem })
|
||||||
|
@ApiResponse({ status: 404, description: "CheckListItem or Task not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
return this.checkListItemService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@ApiOperation({ summary: "Delete a check list item" })
|
||||||
|
@ApiParam({ name: "id", description: "CheckListItem ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "CheckListItem deleted", type: TMCheckListItem })
|
||||||
|
@ApiResponse({ status: 404, description: "CheckListItem not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<TMCheckListItem> {
|
||||||
|
return this.checkListItemService.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch("change-status/:id")
|
||||||
|
@ApiOperation({summary: "change status of a check list item."})
|
||||||
|
@ApiParam({name: "id", description: "CheckListItem ID"})
|
||||||
|
@ApiResponse({status: 200, description: "successfully changed the status!"})
|
||||||
|
@ApiResponse({ status: 404, description: "CheckListItem not found" })
|
||||||
|
changeStatus(@Param() paramDto: ParamDto) {
|
||||||
|
return this.checkListItemService.toggleStatus(paramDto.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import { Controller, Get, Post, Patch, Delete, Param, Body, Query } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||||
|
import { ProjectService } from "../providers/project.service";
|
||||||
|
import { CreateProjectDto } from "../dto/project/create-project.dto";
|
||||||
|
import { UpdateProjectDto } from "../dto/project/update-project.dto";
|
||||||
|
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||||
|
import { TMProject } from "../entities/project.entity";
|
||||||
|
import { AdminRoute } from "../../../common/decorators/admin.decorator";
|
||||||
|
import { PermissionsDec } from "../../../common/decorators/permission.decorator";
|
||||||
|
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||||
|
import { UserDec } from "../../../common/decorators/user.decorator";
|
||||||
|
import { AuthGuards } from "../../../common/decorators/auth-guard.decorator";
|
||||||
|
import { ParamDto } from "../../../common/DTO/param.dto";
|
||||||
|
import { ITokenPayload } from "../../auth/interfaces/IToken-payload";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags("Task Manager - Projects")
|
||||||
|
@Controller("task-manager/projects")
|
||||||
|
export class ProjectController {
|
||||||
|
constructor(private readonly projectService: ProjectService) {}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({summary: 'Get one project by ID'})
|
||||||
|
@ApiResponse({status: 200, description: 'received the project successfully!'})
|
||||||
|
@ApiResponse({status: 404, description: 'Project Not Found!'})
|
||||||
|
@ApiResponse({status: 400, description: 'Bad Request!'})
|
||||||
|
findOne(@Param('id') id: string) {
|
||||||
|
return this.projectService.findOneOrFail(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@PermissionsDec(PermissionEnum.PROJECT_CREATE)
|
||||||
|
@ApiOperation({ summary: "Create a new project" })
|
||||||
|
@ApiResponse({ status: 201, description: "Project created", type: TMProject })
|
||||||
|
@ApiResponse({ status: 400, description: "One or more users are not members of the workspace" })
|
||||||
|
@ApiResponse({ status: 404, description: "Workspace not found" })
|
||||||
|
create(@Body() body: CreateProjectDto): Promise<TMProject> {
|
||||||
|
return this.projectService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@PermissionsDec(PermissionEnum.PROJECT_UPDATE)
|
||||||
|
@ApiOperation({ summary: "Update a project" })
|
||||||
|
@ApiParam({ name: "id", description: "Project ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Project updated", type: TMProject })
|
||||||
|
@ApiResponse({ status: 400, description: "One or more users are not members of the workspace" })
|
||||||
|
@ApiResponse({ status: 404, description: "Project not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateProjectDto): Promise<TMProject> {
|
||||||
|
return this.projectService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@PermissionsDec(PermissionEnum.PROJECT_DELETE)
|
||||||
|
@ApiOperation({ summary: "Delete a project" })
|
||||||
|
@ApiParam({ name: "id", description: "Project ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Project deleted", type: TMProject })
|
||||||
|
@ApiResponse({ status: 404, description: "Project not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<TMProject> {
|
||||||
|
return this.projectService.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("user/workspace/:id")
|
||||||
|
@ApiOperation({ summary: "Get user projects in a specific workspace!" })
|
||||||
|
@ApiResponse({ status: 200, description: "Got user projects successfully!" })
|
||||||
|
@ApiResponse({ status: 400, description: "Bad Request!" })
|
||||||
|
findUserProjectsByWorkspace(
|
||||||
|
@UserDec() user: ITokenPayload,
|
||||||
|
@Param() paramDto: ParamDto,
|
||||||
|
@Query() pagination: PaginationDto,
|
||||||
|
): Promise<{ projects: TMProject[]; count: number; paginate: true }> {
|
||||||
|
return this.projectService.findUserProjectsByWorkspace(user.id, user.permissions, paramDto.id, pagination);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("detail/:id")
|
||||||
|
@ApiOperation({ summary: "Get project detail" })
|
||||||
|
@ApiResponse({ status: 200, description: "Got Project Detail Successfully!" })
|
||||||
|
@ApiResponse({ status: 400, description: "Bad request!" })
|
||||||
|
@ApiResponse({ status: 404, description: "The project was not found!" })
|
||||||
|
getProjectDetail(@Param() paramDto: ParamDto, @UserDec() user: ITokenPayload): Promise<TMProject | null> {
|
||||||
|
return this.projectService.getProjectDetail(paramDto.id, user.id, user.permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(":id/users")
|
||||||
|
@ApiOperation({ summary: "Get project users" })
|
||||||
|
@ApiResponse({ status: 200, description: "Got Project Users Successfully!" })
|
||||||
|
getProjectUsers(@Param() paramDto: ParamDto): Promise<User[]> {
|
||||||
|
return this.projectService.getProjectUsers(paramDto.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { Controller, Post, Patch, Delete, Param, Body } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||||
|
import { RemarkService } from "../providers/remark.service";
|
||||||
|
import { CreateRemarkDto } from "../dto/remark/create-remark.dto";
|
||||||
|
import { UpdateRemarkDto } from "../dto/remark/update-remark.dto";
|
||||||
|
import { TMRemark } from "../entities/remark.entity";
|
||||||
|
import { AuthGuards } from "../../../common/decorators/auth-guard.decorator";
|
||||||
|
import { AdminRoute } from "../../../common/decorators/admin.decorator";
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags("Task Manager - Remarks")
|
||||||
|
@Controller("task-manager/remarks")
|
||||||
|
export class RemarkController {
|
||||||
|
constructor(private readonly remarkService: RemarkService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: "Create a new remark" })
|
||||||
|
@ApiResponse({ status: 201, description: "Remark created", type: TMRemark })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
create(@Body() body: CreateRemarkDto): Promise<TMRemark> {
|
||||||
|
return this.remarkService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@ApiOperation({ summary: "Update a remark" })
|
||||||
|
@ApiParam({ name: "id", description: "Remark ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Remark updated", type: TMRemark })
|
||||||
|
@ApiResponse({ status: 404, description: "Remark or Task not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateRemarkDto): Promise<TMRemark> {
|
||||||
|
return this.remarkService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@ApiOperation({ summary: "Delete a remark" })
|
||||||
|
@ApiParam({ name: "id", description: "Remark ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Remark deleted" })
|
||||||
|
@ApiResponse({ status: 404, description: "Remark not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<void> {
|
||||||
|
return this.remarkService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Controller, Post, Patch, Delete, Param, Body, Get } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||||
|
import { TaskCommentService } from "../providers/task-comment.service";
|
||||||
|
import { CreateTaskCommentDto } from "../dto/task-comment/create-task-comment.dto";
|
||||||
|
import { UpdateTaskCommentDto } from "../dto/task-comment/update-task-comment.dto";
|
||||||
|
import { TMTaskComment } from "../entities/task-comment.entity";
|
||||||
|
import { AuthGuards } from "../../../common/decorators/auth-guard.decorator";
|
||||||
|
import { AdminRoute } from "../../../common/decorators/admin.decorator";
|
||||||
|
import { UserDec } from "../../../common/decorators/user.decorator";
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags("Task Manager - Comments")
|
||||||
|
@Controller("task-manager/comments")
|
||||||
|
export class TaskCommentController {
|
||||||
|
constructor(private readonly taskCommentService: TaskCommentService) {}
|
||||||
|
|
||||||
|
@Get("task/:taskId")
|
||||||
|
@ApiOperation({ summary: "Get all comments for a task" })
|
||||||
|
@ApiParam({ name: "taskId", description: "Task ID" })
|
||||||
|
getTaskComments(@Param("taskId") taskId: string): Promise<TMTaskComment[]> {
|
||||||
|
return this.taskCommentService.getTaskComments(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: "Create a new comment" })
|
||||||
|
@ApiResponse({ status: 201, description: "Comment created", type: TMTaskComment })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
create(@UserDec("id") userId: string, @Body() body: CreateTaskCommentDto): Promise<TMTaskComment> {
|
||||||
|
return this.taskCommentService.create(userId, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@ApiOperation({ summary: "Update a comment" })
|
||||||
|
@ApiParam({ name: "id", description: "Comment ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Comment updated", type: TMTaskComment })
|
||||||
|
@ApiResponse({ status: 404, description: "Comment or Task not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateTaskCommentDto): Promise<TMTaskComment> {
|
||||||
|
return this.taskCommentService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@ApiOperation({ summary: "Delete a comment" })
|
||||||
|
@ApiParam({ name: "id", description: "Comment ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Comment deleted" })
|
||||||
|
@ApiResponse({ status: 404, description: "Comment not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<void> {
|
||||||
|
return this.taskCommentService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Controller, Post, Patch, Delete, Param, Body } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||||
|
import { TaskPhaseService } from "../providers/task-phase.service";
|
||||||
|
import { CreateTaskPhaseDto } from "../dto/task-phase/create-task-phase.dto";
|
||||||
|
import { UpdateTaskPhaseDto } from "../dto/task-phase/update-task-phase.dto";
|
||||||
|
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||||
|
import { AuthGuards } from "../../../common/decorators/auth-guard.decorator";
|
||||||
|
import { AdminRoute } from "../../../common/decorators/admin.decorator";
|
||||||
|
import { PermissionsDec } from "../../../common/decorators/permission.decorator";
|
||||||
|
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags("Task Manager - Task Phases")
|
||||||
|
@Controller("task-manager/task-phases")
|
||||||
|
export class TaskPhaseController {
|
||||||
|
constructor(private readonly taskPhaseService: TaskPhaseService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@PermissionsDec(PermissionEnum.TASK_PHASE_CREATE)
|
||||||
|
@ApiOperation({ summary: "Create a new task phase" })
|
||||||
|
@ApiResponse({ status: 201, description: "TaskPhase created", type: TMTaskPhase })
|
||||||
|
@ApiResponse({ status: 404, description: "Project not found" })
|
||||||
|
create(@Body() body: CreateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
return this.taskPhaseService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@PermissionsDec(PermissionEnum.TASK_PHASE_UPDATE)
|
||||||
|
@ApiOperation({ summary: "Update a task phase" })
|
||||||
|
@ApiParam({ name: "id", description: "TaskPhase ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "TaskPhase updated", type: TMTaskPhase })
|
||||||
|
@ApiResponse({ status: 404, description: "TaskPhase or Project not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
return this.taskPhaseService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@PermissionsDec(PermissionEnum.TASK_PHASE_DELETE)
|
||||||
|
@ApiOperation({ summary: "Delete a task phase" })
|
||||||
|
@ApiParam({ name: "id", description: "TaskPhase ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "TaskPhase deleted" })
|
||||||
|
@ApiResponse({ status: 404, description: "TaskPhase not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<void> {
|
||||||
|
return this.taskPhaseService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { Controller, Get, Post, Patch, Delete, Param, Body, Query } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||||
|
import { TaskService } from "../providers/task.service";
|
||||||
|
import { CreateTaskDto } from "../dto/task/create-task.dto";
|
||||||
|
import { UpdateTaskDto } from "../dto/task/update-task.dto";
|
||||||
|
import { ChangeTaskPhaseDto } from "../dto/task/change-task-phase.dto";
|
||||||
|
import { TMTask } from "../entities/task.entity";
|
||||||
|
import { AuthGuards } from "../../../common/decorators/auth-guard.decorator";
|
||||||
|
import { AdminRoute } from "../../../common/decorators/admin.decorator";
|
||||||
|
import { UserDec } from "../../../common/decorators/user.decorator";
|
||||||
|
import { ParamDto } from "../../../common/DTO/param.dto";
|
||||||
|
import { PermissionsDec } from "../../../common/decorators/permission.decorator";
|
||||||
|
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||||
|
import { ITokenPayload } from "../../auth/interfaces/IToken-payload";
|
||||||
|
import { CursorPagination } from "../../../common/decorators/cursor-pagination.decorator";
|
||||||
|
import { CursorPaginationDto } from "../../../common/DTO/cursor-pagination.dto";
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags("Task Manager - Tasks")
|
||||||
|
@Controller("task-manager/tasks")
|
||||||
|
export class TaskController {
|
||||||
|
constructor(private readonly taskService: TaskService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@PermissionsDec(PermissionEnum.TASK_CREATE)
|
||||||
|
@ApiOperation({ summary: "Create a new task" })
|
||||||
|
@ApiResponse({ status: 201, description: "Task created", type: TMTask })
|
||||||
|
@ApiResponse({ status: 404, description: "TaskPhase not found" })
|
||||||
|
create(@Body() body: CreateTaskDto): Promise<TMTask> {
|
||||||
|
return this.taskService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@PermissionsDec(PermissionEnum.TASK_UPDATE)
|
||||||
|
@ApiOperation({ summary: "Update a task" })
|
||||||
|
@ApiParam({ name: "id", description: "Task ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Task updated", type: TMTask })
|
||||||
|
@ApiResponse({ status: 404, description: "Task or TaskPhase not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateTaskDto): Promise<TMTask> {
|
||||||
|
return this.taskService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id/change-phase")
|
||||||
|
@ApiOperation({ summary: "Move a task to a different phase within the same project" })
|
||||||
|
@ApiParam({ name: "id", description: "Task ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Task phase changed", type: TMTask })
|
||||||
|
@ApiResponse({ status: 400, description: "Target phase belongs to a different project" })
|
||||||
|
@ApiResponse({ status: 404, description: "Task or TaskPhase not found" })
|
||||||
|
changePhase(@Param("id") id: string,@UserDec() user: ITokenPayload, @Body() body: ChangeTaskPhaseDto): Promise<TMTask> {
|
||||||
|
return this.taskService.changePhase(id, user.permissions, user.id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@PermissionsDec(PermissionEnum.TASK_DELETE)
|
||||||
|
@ApiOperation({ summary: "Delete a task" })
|
||||||
|
@ApiParam({ name: "id", description: "Task ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Task deleted" })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<void> {
|
||||||
|
return this.taskService.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("detail/:id")
|
||||||
|
@ApiOperation({ summary: "Get Task Detail" })
|
||||||
|
@ApiParam({ name: "id", description: "Task ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Task detail received!" })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
getTaskDetail(@UserDec() user: ITokenPayload, @Param() paramDto: ParamDto): Promise<TMTask | null> {
|
||||||
|
return this.taskService.getTaskDetail(user.id, user.permissions, paramDto.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("task-phase/:id")
|
||||||
|
@ApiOperation({ summary: "Get Tasks by task phase" })
|
||||||
|
@ApiParam({ name: "id", description: "Task phase id" })
|
||||||
|
@ApiResponse({ status: 200, description: "Got Tasks by task phase successfully!" })
|
||||||
|
@ApiResponse({ status: 400, description: "Bad request from user." })
|
||||||
|
@CursorPagination()
|
||||||
|
findByTaskPhase(
|
||||||
|
@Param("id") taskPhaseId: string,
|
||||||
|
@Query() pagination: CursorPaginationDto,
|
||||||
|
): Promise<{
|
||||||
|
data: TMTask[];
|
||||||
|
nextCursor: string | null;
|
||||||
|
previousCursor: string | null;
|
||||||
|
hasNextPage: boolean;
|
||||||
|
hasPreviousPage: boolean;
|
||||||
|
cursorPaginate: true;
|
||||||
|
}> {
|
||||||
|
return this.taskService.findTasksByTaskPhase(taskPhaseId, pagination);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch("change-priority/:srcid/:desid")
|
||||||
|
@ApiOperation({ summary: "changing the task priority" })
|
||||||
|
@ApiParam({ name: "srcid", description: "ID of the source task" })
|
||||||
|
@ApiParam({ name: "desid", description: "ID of the destination task" })
|
||||||
|
@ApiResponse({ status: 200, description: "Changed the task priority successfully!" })
|
||||||
|
@ApiResponse({ status: 400, description: "Bad Request from user." })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found!" })
|
||||||
|
changePriority(@Param("srcid") srcId: string, @Param("desid") desId: string,@UserDec() user: ITokenPayload): Promise<TMTask> {
|
||||||
|
return this.taskService.changePriority(srcId, user.permissions, user.id, desId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { Controller, Post, Patch, Delete, Param, Body, Get } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||||
|
import { TimeService } from "../providers/time.service";
|
||||||
|
import { CreateTimeDto } from "../dto/time/create-time.dto";
|
||||||
|
import { UpdateTimeDto } from "../dto/time/update-time.dto";
|
||||||
|
import { TMTime } from "../entities/time.entity";
|
||||||
|
import { AuthGuards } from "../../../common/decorators/auth-guard.decorator";
|
||||||
|
import { AdminRoute } from "../../../common/decorators/admin.decorator";
|
||||||
|
import { UserDec } from "../../../common/decorators/user.decorator";
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags("Task Manager - Times")
|
||||||
|
@Controller("task-manager/times")
|
||||||
|
export class TimeController {
|
||||||
|
constructor(private readonly timeService: TimeService) { }
|
||||||
|
@Get("task/:taskId")
|
||||||
|
@ApiOperation({ summary: "Get all time entries for a task" })
|
||||||
|
@ApiParam({ name: "taskId", description: "Task ID" })
|
||||||
|
getTaskTimes(@UserDec("id") adminId: string, @Param("taskId") taskId: string): Promise<TMTime[]> {
|
||||||
|
return this.timeService.getTaskTimes(adminId, taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: "Create a new time entry for a task" })
|
||||||
|
@ApiResponse({ status: 201, description: "Time entry created", type: TMTime })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
create(@UserDec("id") adminId: string, @Body() body: CreateTimeDto): Promise<TMTime> {
|
||||||
|
return this.timeService.create(adminId, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("start/:taskId")
|
||||||
|
@ApiOperation({ summary: "Start tracking time for a task" })
|
||||||
|
@ApiParam({ name: "taskId", description: "Task ID" })
|
||||||
|
@ApiResponse({ status: 201, description: "Time tracking started", type: TMTime })
|
||||||
|
@ApiResponse({ status: 400, description: "A running time entry already exists" })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
start(@UserDec("id") adminId: string, @Param("taskId") taskId: string): Promise<TMTime> {
|
||||||
|
return this.timeService.start(adminId, taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("end/:taskId")
|
||||||
|
@ApiOperation({ summary: "Stop the current running time entry for a task" })
|
||||||
|
@ApiParam({ name: "taskId", description: "Task ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Time tracking stopped", type: TMTime })
|
||||||
|
@ApiResponse({ status: 400, description: "No running time entry found" })
|
||||||
|
@ApiResponse({ status: 404, description: "Task not found" })
|
||||||
|
end(@UserDec("id") adminId: string, @Param("taskId") taskId: string): Promise<TMTime> {
|
||||||
|
return this.timeService.end(adminId, taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@ApiOperation({ summary: "Update a time entry" })
|
||||||
|
@ApiParam({ name: "id", description: "Time entry ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Time entry updated", type: TMTime })
|
||||||
|
@ApiResponse({ status: 404, description: "Time entry or Task not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateTimeDto): Promise<TMTime> {
|
||||||
|
return this.timeService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@ApiOperation({ summary: "Delete a time entry" })
|
||||||
|
@ApiParam({ name: "id", description: "Time entry ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "Time entry deleted" })
|
||||||
|
@ApiResponse({ status: 404, description: "Time entry not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<void> {
|
||||||
|
return this.timeService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import { Controller, Get, Post, Patch, Delete, Param, Body } from "@nestjs/common";
|
|
||||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
|
||||||
import { WorkspaceTypeService } from "../providers/workspace-type.service";
|
|
||||||
import { CreateWorkspaceTypeDto } from "../dto/workspace-type/create-workspace-type.dto";
|
|
||||||
import { UpdateWorkspaceTypeDto } from "../dto/workspace-type/update-workspace-type.dto";
|
|
||||||
import { TMWorkspaceType } from "../entities/workspace-type.entity";
|
|
||||||
|
|
||||||
@ApiTags("Task Manager")
|
|
||||||
@Controller("task-manager/workspace-types")
|
|
||||||
export class WorkspaceTypeController {
|
|
||||||
constructor(private readonly workspaceTypeService: WorkspaceTypeService) {}
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
@ApiOperation({ summary: "Get all workspace types" })
|
|
||||||
@ApiResponse({ status: 200, description: "List of workspace types", type: [TMWorkspaceType] })
|
|
||||||
findAll(): Promise<TMWorkspaceType[]> {
|
|
||||||
return this.workspaceTypeService.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(":id")
|
|
||||||
@ApiOperation({ summary: "Get a workspace type by ID" })
|
|
||||||
@ApiParam({ name: "id", description: "WorkspaceType ID" })
|
|
||||||
@ApiResponse({ status: 200, description: "WorkspaceType found", type: TMWorkspaceType })
|
|
||||||
@ApiResponse({ status: 404, description: "WorkspaceType not found" })
|
|
||||||
findOne(@Param("id") id: string): Promise<TMWorkspaceType> {
|
|
||||||
return this.workspaceTypeService.findOne(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post()
|
|
||||||
@ApiOperation({ summary: "Create a new workspace type" })
|
|
||||||
@ApiResponse({ status: 201, description: "WorkspaceType created", type: TMWorkspaceType })
|
|
||||||
@ApiResponse({ status: 409, description: "WorkspaceType with this name already exists" })
|
|
||||||
create(@Body() body: CreateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
|
||||||
return this.workspaceTypeService.create(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Patch(":id")
|
|
||||||
@ApiOperation({ summary: "Update a workspace type" })
|
|
||||||
@ApiParam({ name: "id", description: "WorkspaceType ID" })
|
|
||||||
@ApiResponse({ status: 200, description: "WorkspaceType updated", type: TMWorkspaceType })
|
|
||||||
@ApiResponse({ status: 404, description: "WorkspaceType not found" })
|
|
||||||
@ApiResponse({ status: 409, description: "WorkspaceType with this name already exists" })
|
|
||||||
update(@Param("id") id: string, @Body() body: UpdateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
|
||||||
return this.workspaceTypeService.update(id, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete(":id")
|
|
||||||
@ApiOperation({ summary: "Delete a workspace type" })
|
|
||||||
@ApiParam({ name: "id", description: "WorkspaceType ID" })
|
|
||||||
@ApiResponse({ status: 200, description: "WorkspaceType deleted" })
|
|
||||||
@ApiResponse({ status: 404, description: "WorkspaceType not found" })
|
|
||||||
remove(@Param("id") id: string): Promise<TMWorkspaceType> {
|
|
||||||
return this.workspaceTypeService.remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,29 +4,42 @@ import { WorkspaceService } from '../providers/workspace.service';
|
|||||||
import { CreateWorkspaceDto } from '../dto/workspace/create-workspace.dto';
|
import { CreateWorkspaceDto } from '../dto/workspace/create-workspace.dto';
|
||||||
import { UpdateWorkspaceDto } from '../dto/workspace/update-workspace.dto';
|
import { UpdateWorkspaceDto } from '../dto/workspace/update-workspace.dto';
|
||||||
import { TMWorkspace } from '../entities/workspace.entity';
|
import { TMWorkspace } from '../entities/workspace.entity';
|
||||||
|
import { AdminRoute } from '../../../common/decorators/admin.decorator';
|
||||||
|
import { PermissionsDec } from '../../../common/decorators/permission.decorator';
|
||||||
|
import { PermissionEnum } from '../../users/enums/permission.enum';
|
||||||
|
import { UserDec } from '../../../common/decorators/user.decorator';
|
||||||
|
import { AuthGuards } from '../../../common/decorators/auth-guard.decorator';
|
||||||
|
import { ITokenPayload } from '../../auth/interfaces/IToken-payload';
|
||||||
|
import { ParamDto } from '../../../common/DTO/param.dto';
|
||||||
|
import { User } from '../../users/entities/user.entity';
|
||||||
|
|
||||||
@ApiTags('Task Manager')
|
@AuthGuards()
|
||||||
|
@AdminRoute()
|
||||||
|
@ApiTags('Task Manager - Workspaces')
|
||||||
@Controller('task-manager/workspaces')
|
@Controller('task-manager/workspaces')
|
||||||
export class WorkspaceController {
|
export class WorkspaceController {
|
||||||
constructor(private readonly workspaceService: WorkspaceService) {}
|
constructor(private readonly workspaceService: WorkspaceService) {}
|
||||||
|
|
||||||
@Get()
|
|
||||||
@ApiOperation({ summary: 'Get all workspaces' })
|
|
||||||
@ApiResponse({ status: 200, description: 'List of workspaces', type: [TMWorkspace] })
|
|
||||||
findAll(): Promise<TMWorkspace[]> {
|
|
||||||
return this.workspaceService.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@ApiOperation({ summary: 'Get a workspace by ID' })
|
@PermissionsDec(PermissionEnum.WORKSPACE_READ)
|
||||||
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
@ApiOperation({summary: "Get one workspace by ID"})
|
||||||
@ApiResponse({ status: 200, description: 'Workspace found', type: TMWorkspace })
|
@ApiResponse({status: 200, description: 'Received workspace', type: TMWorkspace})
|
||||||
@ApiResponse({ status: 404, description: 'Workspace not found' })
|
@ApiResponse({status: 404, description: 'Workspace Not Found!'})
|
||||||
|
@ApiResponse({status: 400, description: 'Bad Request!'})
|
||||||
findOne(@Param('id') id: string): Promise<TMWorkspace> {
|
findOne(@Param('id') id: string): Promise<TMWorkspace> {
|
||||||
return this.workspaceService.findOneOrFail(id);
|
return this.workspaceService.findOneOrFail(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get(':id/users')
|
||||||
|
@ApiOperation({ summary: 'Get workspace users' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Got Workspace Users Successfully!' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Workspace Not Found!' })
|
||||||
|
getWorkspaceUsers(@Param() paramDto: ParamDto): Promise<User[]> {
|
||||||
|
return this.workspaceService.getWorkspaceUsers(paramDto.id);
|
||||||
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@PermissionsDec(PermissionEnum.WORKSPACE_CREATE)
|
||||||
@ApiOperation({ summary: 'Create a new workspace' })
|
@ApiOperation({ summary: 'Create a new workspace' })
|
||||||
@ApiResponse({ status: 201, description: 'Workspace created', type: TMWorkspace })
|
@ApiResponse({ status: 201, description: 'Workspace created', type: TMWorkspace })
|
||||||
create(@Body() body: CreateWorkspaceDto): Promise<TMWorkspace> {
|
create(@Body() body: CreateWorkspaceDto): Promise<TMWorkspace> {
|
||||||
@@ -34,6 +47,7 @@ export class WorkspaceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
|
@PermissionsDec(PermissionEnum.WORKSPACE_UPDATE)
|
||||||
@ApiOperation({ summary: 'Update a workspace' })
|
@ApiOperation({ summary: 'Update a workspace' })
|
||||||
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
||||||
@ApiResponse({ status: 200, description: 'Workspace updated', type: TMWorkspace })
|
@ApiResponse({ status: 200, description: 'Workspace updated', type: TMWorkspace })
|
||||||
@@ -43,6 +57,7 @@ export class WorkspaceController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@PermissionsDec(PermissionEnum.WORKSPACE_DELETE)
|
||||||
@ApiOperation({ summary: 'Delete a workspace' })
|
@ApiOperation({ summary: 'Delete a workspace' })
|
||||||
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
||||||
@ApiResponse({ status: 200, description: 'Workspace deleted' })
|
@ApiResponse({ status: 200, description: 'Workspace deleted' })
|
||||||
@@ -50,4 +65,12 @@ export class WorkspaceController {
|
|||||||
remove(@Param('id') id: string): Promise<TMWorkspace> {
|
remove(@Param('id') id: string): Promise<TMWorkspace> {
|
||||||
return this.workspaceService.remove(id);
|
return this.workspaceService.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('byuser')
|
||||||
|
@ApiOperation({summary: 'Get User WorkSpaces'})
|
||||||
|
@ApiResponse({ status: 200, description: 'Got Workspaces successfully!' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Bad Request!' })
|
||||||
|
findByUser(@UserDec() user: ITokenPayload) {
|
||||||
|
return this.workspaceService.findByUser(user.id, user.permissions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsNotEmpty, IsUUID, IsEnum, MaxLength } from "class-validator";
|
||||||
|
import { TMAttachmentType } from "../../enums/attachment-type.enum";
|
||||||
|
|
||||||
|
export class CreateAttachmentDto {
|
||||||
|
@ApiProperty({ description: "Title of the attachment", example: "Project Requirements Doc" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(150)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Type of the attachment", enum: TMAttachmentType, example: TMAttachmentType.FILE })
|
||||||
|
@IsEnum(TMAttachmentType)
|
||||||
|
@IsNotEmpty()
|
||||||
|
type: TMAttachmentType;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "File URL or path of the attachment", example: "https://cdn.example.com/files/requirements.pdf" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
file: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task this attachment belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateAttachmentDto } from "./create-attachment.dto";
|
||||||
|
|
||||||
|
export class UpdateAttachmentDto extends PartialType(CreateAttachmentDto) {}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsNotEmpty, IsUUID, IsBoolean, MaxLength } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateCheckListItemDto {
|
||||||
|
@ApiProperty({ description: "Title of the check list item", example: "Write unit tests" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(150)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Whether the check list item is done", example: false })
|
||||||
|
@IsBoolean()
|
||||||
|
@IsNotEmpty()
|
||||||
|
isDone: boolean;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task this check list item belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { CreateCheckListItemDto } from './create-check-list-item.dto';
|
||||||
|
|
||||||
|
export class UpdateCheckListItemDto extends PartialType(CreateCheckListItemDto) {}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsDateString, MaxLength } from "class-validator";
|
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsDateString, IsBoolean, MaxLength } from "class-validator";
|
||||||
|
|
||||||
export class CreateProjectDto {
|
export class CreateProjectDto {
|
||||||
@ApiProperty({ description: "Name of the project", example: "Mobile App Redesign" })
|
@ApiProperty({ description: "Name of the project", example: "Mobile App Redesign" })
|
||||||
@@ -33,8 +33,29 @@ export class CreateProjectDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
backgroundPicture?: string;
|
backgroundPicture?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Background color of the project (hex or named)", required: false, example: "#4F46E5" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(20)
|
||||||
|
backgroundColor?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Whether the project is active", required: false, default: true, example: true })
|
||||||
|
@IsBoolean()
|
||||||
|
@IsOptional()
|
||||||
|
isActive?: boolean;
|
||||||
|
|
||||||
@ApiProperty({ description: "ID of the workspace this project belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
@ApiProperty({ description: "ID of the workspace this project belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: "IDs of users to assign to the project (must be members of the workspace)",
|
||||||
|
required: false,
|
||||||
|
type: [String],
|
||||||
|
example: ["b2c3d4e5-f6a7-8901-bcde-f12345678901"],
|
||||||
|
})
|
||||||
|
@IsUUID("4", { each: true })
|
||||||
|
@IsOptional()
|
||||||
|
userIds?: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PartialType } from '@nestjs/swagger';
|
import { PartialType } from "@nestjs/swagger";
|
||||||
import { CreateProjectDto } from './create-project.dto';
|
import { CreateProjectDto } from "./create-project.dto";
|
||||||
|
|
||||||
export class UpdateProjectDto extends PartialType(CreateProjectDto) {}
|
export class UpdateProjectDto extends PartialType(CreateProjectDto) {}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsNotEmpty, IsUUID, IsOptional, MaxLength } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateRemarkDto {
|
||||||
|
@ApiProperty({ description: "Title of the remark", example: "This task needs review" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(150)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Color code for the remark", required: false, example: "#F59E0B" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(20)
|
||||||
|
color?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task this remark belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateRemarkDto } from "./create-remark.dto";
|
||||||
|
|
||||||
|
export class UpdateRemarkDto extends PartialType(CreateRemarkDto) {}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsNotEmpty, IsUUID, IsOptional, IsArray } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateTaskCommentDto {
|
||||||
|
@ApiProperty({ description: "Comment content", example: "Please review this task before closing." })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
content: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task this comment belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskId: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: "Attachment file URLs or paths",
|
||||||
|
type: [String],
|
||||||
|
required: false,
|
||||||
|
example: ["https://cdn.example.com/files/screenshot.png"],
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsArray()
|
||||||
|
@IsString({ each: true })
|
||||||
|
attachments?: string[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateTaskCommentDto } from "./create-task-comment.dto";
|
||||||
|
|
||||||
|
export class UpdateTaskCommentDto extends PartialType(CreateTaskCommentDto) {}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsInt, MaxLength, Min } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateTaskPhaseDto {
|
||||||
|
@ApiProperty({ description: "Name of the task phase", example: "In Progress" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(100)
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Display order of the task phase", required: false, example: 1 })
|
||||||
|
@IsInt()
|
||||||
|
@IsOptional()
|
||||||
|
@Min(0)
|
||||||
|
order?: number;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Color code for the task phase", required: false, example: "#4F46E5" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(20)
|
||||||
|
color?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the project this phase belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
projectId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateTaskPhaseDto } from "./create-task-phase.dto";
|
||||||
|
|
||||||
|
export class UpdateTaskPhaseDto extends PartialType(CreateTaskPhaseDto) {}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsUUID, IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
export class ChangeTaskPhaseDto {
|
||||||
|
@ApiProperty({ description: 'ID of the new task phase', example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskPhaseId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsDateString, MaxLength } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateTaskDto {
|
||||||
|
@ApiProperty({ description: "Title of the task", example: "Implement login page" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(150)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Description of the task", required: false, example: "Implement the login page with JWT auth" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Color code for the task", required: false, example: "#4F46E5" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(20)
|
||||||
|
color?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Start date of the task", required: false, example: "2026-07-01" })
|
||||||
|
@IsDateString()
|
||||||
|
@IsOptional()
|
||||||
|
startDate?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "End date of the task", required: false, example: "2026-07-15" })
|
||||||
|
@IsDateString()
|
||||||
|
@IsOptional()
|
||||||
|
endDate?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task phase this task belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskPhaseId: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: "ID of the related ticket",
|
||||||
|
required: false,
|
||||||
|
example: "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
ticketId?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: "IDs of users to assign to the task",
|
||||||
|
required: false,
|
||||||
|
type: [String],
|
||||||
|
example: ["b2c3d4e5-f6a7-8901-bcde-f12345678901"],
|
||||||
|
})
|
||||||
|
@IsUUID("4", { each: true })
|
||||||
|
@IsOptional()
|
||||||
|
userIds?: string[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateTaskDto } from "./create-task.dto";
|
||||||
|
|
||||||
|
export class UpdateTaskDto extends PartialType(CreateTaskDto) {}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsDateString, IsNotEmpty, IsUUID } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateTimeDto {
|
||||||
|
@ApiProperty({ description: "Start time of the entry", example: "2026-07-15T08:00:00.000Z" })
|
||||||
|
@IsDateString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
startedAt: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "End time of the entry", example: "2026-07-15T10:30:00.000Z" })
|
||||||
|
@IsDateString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
endedAt: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task this time entry belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateTimeDto } from "./create-time.dto";
|
||||||
|
|
||||||
|
export class UpdateTimeDto extends PartialType(CreateTimeDto) {}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
|
||||||
import { IsString, IsOptional, IsInt, IsNotEmpty, MaxLength, Min } from "class-validator";
|
|
||||||
|
|
||||||
export class CreateWorkspaceTypeDto {
|
|
||||||
@ApiProperty({ description: "Name of the workspace type", example: "Engineering" })
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@MaxLength(100)
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: "Description of the workspace type",
|
|
||||||
required: false,
|
|
||||||
example: "Workspace type for engineering related work",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsOptional()
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: "Display order of the workspace type", example: 1 })
|
|
||||||
@IsInt()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@Min(0)
|
|
||||||
order: number;
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import { PartialType } from '@nestjs/swagger';
|
|
||||||
import { CreateWorkspaceTypeDto } from './create-workspace-type.dto';
|
|
||||||
|
|
||||||
export class UpdateWorkspaceTypeDto extends PartialType(CreateWorkspaceTypeDto) {}
|
|
||||||
@@ -8,11 +8,6 @@ export class CreateWorkspaceDto {
|
|||||||
@MaxLength(100)
|
@MaxLength(100)
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty({ description: "ID of the workspace type", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
|
||||||
@IsUUID()
|
|
||||||
@IsNotEmpty()
|
|
||||||
workspaceTypeId: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: "Description of the workspace", required: false, example: "Workspace for the engineering team" })
|
@ApiProperty({ description: "Description of the workspace", required: false, example: "Workspace for the engineering team" })
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm";
|
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany } from "typeorm";
|
||||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
import { TMWorkspace } from "./workspace.entity";
|
import { TMWorkspace } from "./workspace.entity";
|
||||||
import { TMTaskPhase } from "./task-phase.entity";
|
import { TMTaskPhase } from "./task-phase.entity";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
|
||||||
@Entity("tm_projects")
|
@Entity("tm_projects")
|
||||||
export class TMProject extends BaseEntity {
|
export class TMProject extends BaseEntity {
|
||||||
@@ -20,6 +21,12 @@ export class TMProject extends BaseEntity {
|
|||||||
@Column({ type: "text", nullable: true })
|
@Column({ type: "text", nullable: true })
|
||||||
backgroundPicture: string;
|
backgroundPicture: string;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 20, nullable: true })
|
||||||
|
backgroundColor: string;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", default: true })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
// --- Relations ---
|
// --- Relations ---
|
||||||
|
|
||||||
@ManyToOne(() => TMWorkspace, (workspace) => workspace.projects, {
|
@ManyToOne(() => TMWorkspace, (workspace) => workspace.projects, {
|
||||||
@@ -36,4 +43,12 @@ export class TMProject extends BaseEntity {
|
|||||||
cascade: ["insert", "update"],
|
cascade: ["insert", "update"],
|
||||||
})
|
})
|
||||||
taskPhases: TMTaskPhase[];
|
taskPhases: TMTaskPhase[];
|
||||||
|
|
||||||
|
@ManyToMany(() => User, (user) => user.projects)
|
||||||
|
@JoinTable({
|
||||||
|
name: "tm_project_users",
|
||||||
|
joinColumn: { name: "projectId", referencedColumnName: "id" },
|
||||||
|
inverseJoinColumn: { name: "userId", referencedColumnName: "id" },
|
||||||
|
})
|
||||||
|
users: User[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
|
||||||
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
import { TMTask } from "./task.entity";
|
||||||
|
|
||||||
|
@Entity("tm_task_comments")
|
||||||
|
export class TMTaskComment extends BaseEntity {
|
||||||
|
@Column({ type: "text" })
|
||||||
|
content: string;
|
||||||
|
|
||||||
|
@Column({ type: "json", default: [] })
|
||||||
|
attachments: string[];
|
||||||
|
|
||||||
|
// --- Relations ---
|
||||||
|
|
||||||
|
@ManyToOne(() => User, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "RESTRICT",
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: "userId" })
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@Column({ type: "uuid" })
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => TMTask, (task) => task.comments, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: "taskId" })
|
||||||
|
task: TMTask;
|
||||||
|
|
||||||
|
@Column({ type: "uuid" })
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,7 +4,10 @@ import { TMTaskPhase } from "./task-phase.entity";
|
|||||||
import { TMRemark } from "./remark.entity";
|
import { TMRemark } from "./remark.entity";
|
||||||
import { TMAttachment } from "./attachment.entity";
|
import { TMAttachment } from "./attachment.entity";
|
||||||
import { TMCheckListItem } from "./check-list-item.entity";
|
import { TMCheckListItem } from "./check-list-item.entity";
|
||||||
|
import { TMTime } from "./time.entity";
|
||||||
|
import { TMTaskComment } from "./task-comment.entity";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||||
|
|
||||||
@Entity("tm_tasks")
|
@Entity("tm_tasks")
|
||||||
export class TMTask extends BaseEntity {
|
export class TMTask extends BaseEntity {
|
||||||
@@ -23,6 +26,16 @@ export class TMTask extends BaseEntity {
|
|||||||
@Column({ type: "timestamptz", nullable: true })
|
@Column({ type: "timestamptz", nullable: true })
|
||||||
endDate: Date;
|
endDate: Date;
|
||||||
|
|
||||||
|
@Column({type: 'int', default: 1})
|
||||||
|
priority: number;
|
||||||
|
|
||||||
|
@ManyToOne(() => Ticket, { nullable: true, onDelete: "SET NULL" })
|
||||||
|
@JoinColumn({ name: "ticketId" })
|
||||||
|
ticket: Ticket | null;
|
||||||
|
|
||||||
|
@Column({ type: "uuid", nullable: true })
|
||||||
|
ticketId: string | null;
|
||||||
|
|
||||||
// --- Relations ---
|
// --- Relations ---
|
||||||
|
|
||||||
@ManyToOne(() => TMTaskPhase, (taskPhase) => taskPhase.tasks, {
|
@ManyToOne(() => TMTaskPhase, (taskPhase) => taskPhase.tasks, {
|
||||||
@@ -50,6 +63,16 @@ export class TMTask extends BaseEntity {
|
|||||||
})
|
})
|
||||||
checkListItems: TMCheckListItem[];
|
checkListItems: TMCheckListItem[];
|
||||||
|
|
||||||
|
@OneToMany(() => TMTime, (time) => time.task, {
|
||||||
|
cascade: ["insert", "update"],
|
||||||
|
})
|
||||||
|
times: TMTime[];
|
||||||
|
|
||||||
|
@OneToMany(() => TMTaskComment, (comment) => comment.task, {
|
||||||
|
cascade: ["insert", "update"],
|
||||||
|
})
|
||||||
|
comments: TMTaskComment[];
|
||||||
|
|
||||||
@ManyToMany(() => User, (user) => user.tasks)
|
@ManyToMany(() => User, (user) => user.tasks)
|
||||||
@JoinTable({
|
@JoinTable({
|
||||||
name: "tm_task_users",
|
name: "tm_task_users",
|
||||||
@@ -57,4 +80,10 @@ export class TMTask extends BaseEntity {
|
|||||||
inverseJoinColumn: { name: "userId", referencedColumnName: "id" },
|
inverseJoinColumn: { name: "userId", referencedColumnName: "id" },
|
||||||
})
|
})
|
||||||
users: User[];
|
users: User[];
|
||||||
|
|
||||||
|
// deriven(calculated) attributes. not database columns
|
||||||
|
attachmentCount?: number;
|
||||||
|
checkListItemCount?: number;
|
||||||
|
completedCheckListItemCount?: number;
|
||||||
|
time?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
|
||||||
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
|
import { TMTask } from "./task.entity";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
|
||||||
|
@Entity("tm_times")
|
||||||
|
export class TMTime extends BaseEntity {
|
||||||
|
@Column({ type: "timestamptz" })
|
||||||
|
startedAt: Date;
|
||||||
|
|
||||||
|
@Column({ type: "timestamptz", nullable: true })
|
||||||
|
endedAt: Date | null;
|
||||||
|
|
||||||
|
@Column({ type: "int", nullable: true })
|
||||||
|
duration: number | null;
|
||||||
|
|
||||||
|
// --- Relations ---
|
||||||
|
|
||||||
|
@ManyToOne(() => User, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "RESTRICT",
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: "adminId" })
|
||||||
|
admin: User;
|
||||||
|
|
||||||
|
@Column({ type: "uuid" })
|
||||||
|
adminId: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => TMTask, (task) => task.times, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: "taskId" })
|
||||||
|
task: TMTask;
|
||||||
|
|
||||||
|
@Column({ type: "uuid" })
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Column, Entity, OneToMany } from "typeorm";
|
|
||||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
|
||||||
import { TMWorkspace } from "./workspace.entity";
|
|
||||||
|
|
||||||
@Entity("tm_workspace_types")
|
|
||||||
export class TMWorkspaceType extends BaseEntity {
|
|
||||||
@Column({ type: "varchar", length: 100, unique: true })
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@Column({ type: "text", nullable: true })
|
|
||||||
description: string;
|
|
||||||
|
|
||||||
@Column({ type: "int", default: 0 })
|
|
||||||
order: number;
|
|
||||||
|
|
||||||
// --- Relations ---
|
|
||||||
|
|
||||||
@OneToMany(() => TMWorkspace, (workspace) => workspace.workspaceType)
|
|
||||||
workspaces: TMWorkspace[];
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany } from "typeorm";
|
import { Column, Entity, JoinTable, ManyToMany, OneToMany } from "typeorm";
|
||||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
import { TMWorkspaceType } from "./workspace-type.entity";
|
|
||||||
import { TMProject } from "./project.entity";
|
import { TMProject } from "./project.entity";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
|
||||||
@Entity("tm_workspaces")
|
@Entity("tm_workspaces")
|
||||||
export class TMWorkspace extends BaseEntity {
|
export class TMWorkspace extends BaseEntity {
|
||||||
@Column({ type: "varchar", length: 100 })
|
@Column({ type: "varchar", length: 100, unique: true })
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@Column({ type: "text", nullable: true })
|
@Column({ type: "text", nullable: true })
|
||||||
@@ -20,13 +19,6 @@ export class TMWorkspace extends BaseEntity {
|
|||||||
|
|
||||||
// --- Relations ---
|
// --- Relations ---
|
||||||
|
|
||||||
@ManyToOne(() => TMWorkspaceType, { nullable: false, eager: true })
|
|
||||||
@JoinColumn({ name: "workspaceTypeId" })
|
|
||||||
workspaceType: TMWorkspaceType;
|
|
||||||
|
|
||||||
@Column({ type: "uuid" })
|
|
||||||
workspaceTypeId: string;
|
|
||||||
|
|
||||||
@OneToMany(() => TMProject, (project) => project.workspace, {
|
@OneToMany(() => TMProject, (project) => project.workspace, {
|
||||||
cascade: ["insert", "update"],
|
cascade: ["insert", "update"],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { AttachmentRepository } from "../repositories/attachment.repository";
|
||||||
|
import { TMAttachment } from "../entities/attachment.entity";
|
||||||
|
import { CreateAttachmentDto } from "../dto/attachment/create-attachment.dto";
|
||||||
|
import { UpdateAttachmentDto } from "../dto/attachment/update-attachment.dto";
|
||||||
|
import { attachmentMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { TaskService } from "./task.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AttachmentService {
|
||||||
|
constructor(
|
||||||
|
private readonly attachmentRepository: AttachmentRepository,
|
||||||
|
private readonly taskService: TaskService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMAttachment> {
|
||||||
|
const attachment = await this.attachmentRepository.findOne({ where: { id }, relations: { task: true } });
|
||||||
|
if (!attachment) throw new NotFoundException(attachmentMessage.ATTACHMENT_NOT_FOUND);
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateAttachmentDto): Promise<TMAttachment> {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
|
||||||
|
const attachment = this.attachmentRepository.create(dto);
|
||||||
|
return this.attachmentRepository.save(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateAttachmentDto): Promise<TMAttachment> {
|
||||||
|
const attachment = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
if (dto.taskId) {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(attachment, dto);
|
||||||
|
return this.attachmentRepository.save(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOneOrFail(id);
|
||||||
|
await this.attachmentRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { CheckListItemRepository } from '../repositories/check-list-item.repository';
|
||||||
|
import { TMCheckListItem } from '../entities/check-list-item.entity';
|
||||||
|
import { CreateCheckListItemDto } from '../dto/check-list-item/create-check-list-item.dto';
|
||||||
|
import { UpdateCheckListItemDto } from '../dto/check-list-item/update-check-list-item.dto';
|
||||||
|
import { checkListItemMessage } from '../../../common/enums/message.enum';
|
||||||
|
import { TaskService } from './task.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CheckListItemService {
|
||||||
|
constructor(
|
||||||
|
private readonly checkListItemRepository: CheckListItemRepository,
|
||||||
|
private readonly taskService: TaskService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMCheckListItem> {
|
||||||
|
const checkListItem = await this.checkListItemRepository.findOneById(id);
|
||||||
|
if (!checkListItem) throw new NotFoundException(checkListItemMessage.CHECKLISTITEM_NOT_FOUND);
|
||||||
|
return checkListItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
|
||||||
|
const checkListItem = this.checkListItemRepository.create(dto);
|
||||||
|
return this.checkListItemRepository.save(checkListItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
const checkListItem = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
if (dto.taskId) {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(checkListItem, dto);
|
||||||
|
return this.checkListItemRepository.save(checkListItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<TMCheckListItem> {
|
||||||
|
const checkListItem = await this.findOneOrFail(id);
|
||||||
|
await this.checkListItemRepository.delete(id);
|
||||||
|
return checkListItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleStatus(id: string) : Promise<TMCheckListItem> {
|
||||||
|
const checkListItem = await this.findOneOrFail(id);
|
||||||
|
const isDone = checkListItem.isDone;
|
||||||
|
checkListItem.isDone = !isDone;
|
||||||
|
await this.checkListItemRepository.save(checkListItem);
|
||||||
|
|
||||||
|
return checkListItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { In, Repository } from "typeorm";
|
||||||
|
import { ProjectRepository } from "../repositories/project.repository";
|
||||||
|
import { TMProject } from "../entities/project.entity";
|
||||||
|
import { CreateProjectDto } from "../dto/project/create-project.dto";
|
||||||
|
import { UpdateProjectDto } from "../dto/project/update-project.dto";
|
||||||
|
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
import { ProjectMessage, WorkSpaceMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { WorkspaceService } from "./workspace.service";
|
||||||
|
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ProjectService {
|
||||||
|
constructor(
|
||||||
|
private readonly projectRepository: ProjectRepository,
|
||||||
|
private readonly workspaceService: WorkspaceService,
|
||||||
|
@InjectRepository(User)
|
||||||
|
private readonly userRepository: Repository<User>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMProject> {
|
||||||
|
const project = await this.projectRepository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: {
|
||||||
|
users: true,
|
||||||
|
taskPhases: true
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
users: {
|
||||||
|
id: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
profilePic: true,
|
||||||
|
},
|
||||||
|
taskPhases: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!project) throw new NotFoundException(ProjectMessage.PROJECT_NOT_FOUND);
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateProjectDto): Promise<TMProject> {
|
||||||
|
const { userIds, startDate, ...rest } = dto;
|
||||||
|
|
||||||
|
const workspace = await this.workspaceService.findOneOrFail(dto.workspaceId);
|
||||||
|
|
||||||
|
const project = this.projectRepository.create({
|
||||||
|
...rest,
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userIds?.length) {
|
||||||
|
const workspaceUserIds = workspace.users.map((u) => u.id);
|
||||||
|
const invalidUsers = userIds.filter((id) => !workspaceUserIds.includes(id));
|
||||||
|
if (invalidUsers.length) {
|
||||||
|
throw new BadRequestException(WorkSpaceMessage.USERS_DONT_BELONG_TO_THIS_WORKSPACE);
|
||||||
|
}
|
||||||
|
project.users = await this.userRepository.findBy({ id: In(userIds) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.projectRepository.save(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateProjectDto): Promise<TMProject> {
|
||||||
|
const project = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
const { userIds, startDate, ...rest } = dto;
|
||||||
|
|
||||||
|
Object.assign(project, {
|
||||||
|
...rest,
|
||||||
|
startDate: startDate ? new Date(startDate) : project.startDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userIds) {
|
||||||
|
if (userIds.length) {
|
||||||
|
const workspaceId = dto.workspaceId ?? project.workspaceId;
|
||||||
|
const workspace = await this.workspaceService.findOneOrFail(workspaceId);
|
||||||
|
|
||||||
|
const workspaceUserIds = workspace.users.map((u) => u.id);
|
||||||
|
const invalidUsers = userIds.filter((uid) => !workspaceUserIds.includes(uid));
|
||||||
|
if (invalidUsers.length) {
|
||||||
|
throw new BadRequestException(WorkSpaceMessage.USERS_DONT_BELONG_TO_THIS_WORKSPACE);
|
||||||
|
}
|
||||||
|
project.users = await this.userRepository.findBy({ id: In(userIds) });
|
||||||
|
} else {
|
||||||
|
project.users = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.projectRepository.save(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<TMProject> {
|
||||||
|
const project = await this.projectRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
relations: {
|
||||||
|
taskPhases: true,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
taskPhases: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!project) throw new NotFoundException(ProjectMessage.PROJECT_NOT_FOUND);
|
||||||
|
|
||||||
|
const taskPhases = project.taskPhases;
|
||||||
|
if (taskPhases !== undefined && taskPhases.length) {
|
||||||
|
throw new BadRequestException(ProjectMessage.PROJECT_CONTAINS_TASK_PHASES);
|
||||||
|
}
|
||||||
|
await this.projectRepository.delete(id);
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findUserProjectsByWorkspace(
|
||||||
|
userId: string,
|
||||||
|
permissions: PermissionEnum[],
|
||||||
|
wsId: string,
|
||||||
|
pagination: PaginationDto,
|
||||||
|
): Promise<{ projects: TMProject[]; count: number; paginate: true }> {
|
||||||
|
const workspace = await this.workspaceService.findOneOrFail(wsId);
|
||||||
|
|
||||||
|
const page = pagination.page ?? 1;
|
||||||
|
const limit = pagination.limit ?? 10;
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
const isPermissionIncluded = permissions.includes(PermissionEnum.PROJECT_READ);
|
||||||
|
const isUserMemeberOfWorkspace = workspace.users.some((user) => user.id === userId);
|
||||||
|
|
||||||
|
var res: [TMProject[], number];
|
||||||
|
if (isUserMemeberOfWorkspace && !isPermissionIncluded) {
|
||||||
|
res = await this.projectRepository.findUserProjects(userId, wsId, skip, limit);
|
||||||
|
} else if (isPermissionIncluded) {
|
||||||
|
res = await this.projectRepository.findAndCount({ where: { workspaceId: wsId } });
|
||||||
|
} else {
|
||||||
|
throw new ForbiddenException(WorkSpaceMessage.FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [projects, count] = res;
|
||||||
|
|
||||||
|
return { projects, count, paginate: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProjectDetail(projectId: string, userId: string, permissions: PermissionEnum[]): Promise<TMProject | null> {
|
||||||
|
const project = await this.projectRepository.findOne({
|
||||||
|
where: { id: projectId },
|
||||||
|
relations: {
|
||||||
|
users: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!project) throw new NotFoundException(ProjectMessage.PROJECT_NOT_FOUND);
|
||||||
|
|
||||||
|
const isUserMember = project.users.some((user) => user.id === userId);
|
||||||
|
if (!isUserMember && !permissions.includes(PermissionEnum.PROJECT_READ))
|
||||||
|
throw new ForbiddenException(ProjectMessage.NOT_MEMBER_OF_PROJECT);
|
||||||
|
|
||||||
|
const projectDetail = await this.projectRepository.getProjectDetail(projectId);
|
||||||
|
|
||||||
|
return projectDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProjectUsers(projectId: string): Promise<User[]> {
|
||||||
|
const project = await this.findOneOrFail(projectId);
|
||||||
|
return project.users;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { RemarkRepository } from "../repositories/remark.repository";
|
||||||
|
import { TMRemark } from "../entities/remark.entity";
|
||||||
|
import { CreateRemarkDto } from "../dto/remark/create-remark.dto";
|
||||||
|
import { UpdateRemarkDto } from "../dto/remark/update-remark.dto";
|
||||||
|
import { remarkMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { TaskService } from './task.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RemarkService {
|
||||||
|
constructor(
|
||||||
|
private readonly remarkRepository: RemarkRepository,
|
||||||
|
private readonly taskService: TaskService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMRemark> {
|
||||||
|
const remark = await this.remarkRepository.findOne({ where: { id } });
|
||||||
|
if (!remark) throw new NotFoundException(remarkMessage.REMARK_NOT_FOUND);
|
||||||
|
return remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateRemarkDto): Promise<TMRemark> {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
|
||||||
|
const remark = this.remarkRepository.create(dto);
|
||||||
|
return this.remarkRepository.save(remark);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateRemarkDto): Promise<TMRemark> {
|
||||||
|
const remark = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
if (dto.taskId) {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(remark, dto);
|
||||||
|
return this.remarkRepository.save(remark);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOneOrFail(id);
|
||||||
|
await this.remarkRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { TaskCommentRepository } from "../repositories/task-comment.repository";
|
||||||
|
import { TMTaskComment } from "../entities/task-comment.entity";
|
||||||
|
import { CreateTaskCommentDto } from "../dto/task-comment/create-task-comment.dto";
|
||||||
|
import { UpdateTaskCommentDto } from "../dto/task-comment/update-task-comment.dto";
|
||||||
|
import { taskCommentMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { TaskService } from "./task.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskCommentService {
|
||||||
|
constructor(
|
||||||
|
private readonly taskCommentRepository: TaskCommentRepository,
|
||||||
|
private readonly taskService: TaskService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMTaskComment> {
|
||||||
|
const comment = await this.taskCommentRepository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: { user: true, task: true },
|
||||||
|
});
|
||||||
|
if (!comment) throw new NotFoundException(taskCommentMessage.TASK_COMMENT_NOT_FOUND);
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTaskComments(taskId: string): Promise<TMTaskComment[]> {
|
||||||
|
await this.taskService.findOneOrFail(taskId);
|
||||||
|
|
||||||
|
return this.taskCommentRepository.find({
|
||||||
|
where: { taskId },
|
||||||
|
relations: { user: true },
|
||||||
|
order: { createdAt: "DESC" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(userId: string, dto: CreateTaskCommentDto): Promise<TMTaskComment> {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
|
||||||
|
const comment = this.taskCommentRepository.create({
|
||||||
|
...dto,
|
||||||
|
attachments: dto.attachments ?? [],
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
return this.taskCommentRepository.save(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateTaskCommentDto): Promise<TMTaskComment> {
|
||||||
|
const comment = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
if (dto.taskId) {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(comment, dto);
|
||||||
|
return this.taskCommentRepository.save(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOneOrFail(id);
|
||||||
|
await this.taskCommentRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { TaskPhaseRepository } from "../repositories/task-phase.repository";
|
||||||
|
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||||
|
import { CreateTaskPhaseDto } from "../dto/task-phase/create-task-phase.dto";
|
||||||
|
import { UpdateTaskPhaseDto } from "../dto/task-phase/update-task-phase.dto";
|
||||||
|
import { TaskPhaseMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { ProjectService } from "./project.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskPhaseService {
|
||||||
|
constructor(
|
||||||
|
private readonly taskPhaseRepository: TaskPhaseRepository,
|
||||||
|
private readonly projectService: ProjectService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMTaskPhase> {
|
||||||
|
const taskPhase = await this.taskPhaseRepository.findOne({where: {id}});
|
||||||
|
if (!taskPhase) throw new NotFoundException(TaskPhaseMessage.TASK_PHASE_NOT_FOUND);
|
||||||
|
return taskPhase;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
await this.projectService.findOneOrFail(dto.projectId);
|
||||||
|
const taskPhase = this.taskPhaseRepository.create(dto);
|
||||||
|
return this.taskPhaseRepository.save(taskPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
const taskPhase = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
if (dto.projectId) {
|
||||||
|
await this.projectService.findOneOrFail(dto.projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(taskPhase, dto);
|
||||||
|
return this.taskPhaseRepository.save(taskPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOneOrFail(id);
|
||||||
|
await this.taskPhaseRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { In, Repository } from "typeorm";
|
||||||
|
import { TaskRepository } from "../repositories/task.repository";
|
||||||
|
import { TMTask } from "../entities/task.entity";
|
||||||
|
import { CreateTaskDto } from "../dto/task/create-task.dto";
|
||||||
|
import { UpdateTaskDto } from "../dto/task/update-task.dto";
|
||||||
|
import { ChangeTaskPhaseDto } from "../dto/task/change-task-phase.dto";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||||
|
import { TaskMessage, TaskPhaseMessage, TicketMessageEnum } from "../../../common/enums/message.enum";
|
||||||
|
import { TaskPhaseRepository } from "../repositories/task-phase.repository";
|
||||||
|
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||||
|
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||||
|
import { encodeCursor } from "../../utils/providers/cursor-pagination.utils";
|
||||||
|
import { CursorDirection, CursorPaginationDto } from "../../../common/DTO/cursor-pagination.dto";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskService {
|
||||||
|
constructor(
|
||||||
|
private readonly taskRepository: TaskRepository,
|
||||||
|
private readonly taskPhaseRepository: TaskPhaseRepository,
|
||||||
|
@InjectRepository(User)
|
||||||
|
private readonly userRepository: Repository<User>,
|
||||||
|
@InjectRepository(Ticket)
|
||||||
|
private readonly ticketRepository: Repository<Ticket>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMTask> {
|
||||||
|
const task = await this.taskRepository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: {
|
||||||
|
taskPhase: true,
|
||||||
|
users: true,
|
||||||
|
ticket: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!task) throw new NotFoundException(TaskMessage.TASK_NOT_FOUND);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async findTicketOrFail(ticketId: string): Promise<Ticket> {
|
||||||
|
const ticket = await this.ticketRepository.findOne({ where: { id: ticketId } });
|
||||||
|
if (!ticket) throw new NotFoundException(TicketMessageEnum.TICKET_NOT_FOUND);
|
||||||
|
return ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findTaskPhaseOrFail(taskPhaseId: string): Promise<TMTaskPhase> {
|
||||||
|
const taskPhase = await this.taskPhaseRepository.findOne({
|
||||||
|
where: { id: taskPhaseId },
|
||||||
|
relations: {
|
||||||
|
project: {
|
||||||
|
users: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!taskPhase) throw new NotFoundException(TaskPhaseMessage.TASK_PHASE_NOT_FOUND);
|
||||||
|
|
||||||
|
return taskPhase;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateTaskDto): Promise<TMTask> {
|
||||||
|
const { userIds, startDate, endDate, ticketId, ...rest } = dto;
|
||||||
|
|
||||||
|
const taskPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
||||||
|
|
||||||
|
if (ticketId) {
|
||||||
|
await this.findTicketOrFail(ticketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userIds?.length) {
|
||||||
|
const isUsersValid = userIds.every((userId) => taskPhase.project.users.some((user) => user.id === userId));
|
||||||
|
if (!isUsersValid) throw new BadRequestException(TaskMessage.USERS_ASSIGNED_TO_TASK_NOT_PROJECT_MEMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
const task = this.taskRepository.create({
|
||||||
|
...rest,
|
||||||
|
ticketId,
|
||||||
|
startDate: startDate ? new Date(startDate) : undefined,
|
||||||
|
endDate: endDate ? new Date(endDate) : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userIds?.length) {
|
||||||
|
task.users = await this.userRepository.findBy({ id: In(userIds) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.taskRepository.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateTaskDto): Promise<TMTask> {
|
||||||
|
const task = await this.findOneOrFail(id);
|
||||||
|
const { userIds, startDate, endDate, ticketId, ...rest } = dto;
|
||||||
|
|
||||||
|
if (ticketId) {
|
||||||
|
await this.findTicketOrFail(ticketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dto.taskPhaseId) {
|
||||||
|
const taskPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
||||||
|
|
||||||
|
if (userIds?.length) {
|
||||||
|
const isUsersValid = userIds.every((userId) => taskPhase.project.users.some((user) => user.id === userId));
|
||||||
|
if (!isUsersValid) throw new BadRequestException(TaskMessage.USERS_ASSIGNED_TO_TASK_NOT_PROJECT_MEMBER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(task, {
|
||||||
|
...rest,
|
||||||
|
...(ticketId !== undefined && { ticketId }),
|
||||||
|
startDate: startDate ? new Date(startDate) : task.startDate,
|
||||||
|
endDate: endDate ? new Date(endDate) : task.endDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userIds) {
|
||||||
|
task.users = userIds.length ? await this.userRepository.findBy({ id: In(userIds) }) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.taskRepository.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async changePhase(id: string,permissions: PermissionEnum[],userId: string, dto: ChangeTaskPhaseDto): Promise<TMTask> {
|
||||||
|
const task = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
const isTaskMember = task.users.some((user) => user.id === userId);
|
||||||
|
const hasPermission = permissions.includes(PermissionEnum.TASK_UPDATE);
|
||||||
|
if (!isTaskMember && !hasPermission) throw new ForbiddenException(TaskMessage.USER_NOT_TASK_MEMBER);
|
||||||
|
|
||||||
|
const newPhase = await this.findTaskPhaseOrFail(dto.taskPhaseId);
|
||||||
|
|
||||||
|
if (task.taskPhase.projectId !== newPhase.projectId) {
|
||||||
|
throw new BadRequestException(TaskPhaseMessage.TASK_PHASE_NOT_SAME_PROJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
task.taskPhaseId = dto.taskPhaseId;
|
||||||
|
task.taskPhase = newPhase;
|
||||||
|
|
||||||
|
return this.taskRepository.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOneOrFail(id);
|
||||||
|
await this.taskRepository.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTaskDetail(userId: string, permissions: PermissionEnum[], taskId: string): Promise<TMTask | null> {
|
||||||
|
const task = await this.findOneOrFail(taskId);
|
||||||
|
|
||||||
|
const isTaskMember = task.users.some((user) => user.id === userId);
|
||||||
|
const hasPermission = permissions.includes(PermissionEnum.TASK_READ);
|
||||||
|
if (!isTaskMember && !hasPermission) throw new ForbiddenException(TaskMessage.USER_NOT_TASK_MEMBER);
|
||||||
|
|
||||||
|
const taskDetail = await this.taskRepository.getTaskDetail(taskId);
|
||||||
|
return taskDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findTasksByTaskPhase(
|
||||||
|
taskPhaseId: string,
|
||||||
|
pagination: CursorPaginationDto,
|
||||||
|
): Promise<{
|
||||||
|
data: TMTask[];
|
||||||
|
nextCursor: string | null;
|
||||||
|
previousCursor: string | null;
|
||||||
|
hasNextPage: boolean;
|
||||||
|
hasPreviousPage: boolean;
|
||||||
|
cursorPaginate: true;
|
||||||
|
}> {
|
||||||
|
await this.findTaskPhaseOrFail(taskPhaseId);
|
||||||
|
|
||||||
|
const isBackward = pagination.direction === CursorDirection.PREV;
|
||||||
|
const { items, hasMore } = await this.taskRepository.findTasksByTaskPhase(taskPhaseId, pagination);
|
||||||
|
|
||||||
|
// paging backward always has more ahead (we came from there); paging forward relies on
|
||||||
|
// whether a cursor was even supplied to know whether anything precedes this page
|
||||||
|
const hasNextPage = items.length > 0 && (isBackward ? true : hasMore);
|
||||||
|
const hasPreviousPage = items.length > 0 && (isBackward ? hasMore : !!pagination.cursor);
|
||||||
|
|
||||||
|
const nextCursor = hasNextPage ? encodeCursor(items[items.length - 1]) : null;
|
||||||
|
const previousCursor = hasPreviousPage ? encodeCursor(items[0]) : null;
|
||||||
|
|
||||||
|
return { data: items, nextCursor, previousCursor, hasNextPage, hasPreviousPage, cursorPaginate: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async changePriority(srcTaskId: string,permissions: PermissionEnum[],userId: string, destTaskId: string): Promise<TMTask> {
|
||||||
|
const srcTask = await this.findOneOrFail(srcTaskId);
|
||||||
|
const desTask = await this.findOneOrFail(destTaskId);
|
||||||
|
|
||||||
|
const isTaskMember = srcTask.users.some((user) => user.id === userId);
|
||||||
|
const hasPermission = permissions.includes(PermissionEnum.TASK_UPDATE);
|
||||||
|
if (!isTaskMember && !hasPermission) throw new ForbiddenException(TaskMessage.USER_NOT_TASK_MEMBER);
|
||||||
|
|
||||||
|
if (srcTask.taskPhaseId !== desTask.taskPhaseId)
|
||||||
|
throw new BadRequestException(TaskMessage.TASKS_NOT_IN_THE_SAME_TASK_PHASE);
|
||||||
|
|
||||||
|
let movingUp: boolean = false;
|
||||||
|
|
||||||
|
if(srcTask.priority < desTask.priority) {
|
||||||
|
movingUp = true;
|
||||||
|
} else if(srcTask.priority < desTask.priority) {
|
||||||
|
movingUp = false;
|
||||||
|
} else {
|
||||||
|
if(srcTask.createdAt > desTask.createdAt) {
|
||||||
|
movingUp = true;
|
||||||
|
}else {
|
||||||
|
movingUp = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const priorityChange: number = movingUp ? 1 : -1;
|
||||||
|
|
||||||
|
srcTask.priority = desTask.priority + priorityChange;
|
||||||
|
|
||||||
|
await this.taskRepository.save(srcTask);
|
||||||
|
|
||||||
|
return srcTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { IsNull } from "typeorm";
|
||||||
|
import { TimeRepository } from "../repositories/time.repository";
|
||||||
|
import { TMTime } from "../entities/time.entity";
|
||||||
|
import { CreateTimeDto } from "../dto/time/create-time.dto";
|
||||||
|
import { UpdateTimeDto } from "../dto/time/update-time.dto";
|
||||||
|
import { timeMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { TaskService } from "./task.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TimeService {
|
||||||
|
constructor(
|
||||||
|
private readonly timeRepository: TimeRepository,
|
||||||
|
private readonly taskService: TaskService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
private calculateDuration(startedAt: Date, endedAt: Date): number {
|
||||||
|
if (endedAt.getTime() < startedAt.getTime()) {
|
||||||
|
throw new BadRequestException(timeMessage.INVALID_TIME_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.floor((endedAt.getTime() - startedAt.getTime()) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async findRunningTime(adminId: string, taskId: string): Promise<TMTime | null> {
|
||||||
|
return this.timeRepository.findOne({
|
||||||
|
where: { adminId, taskId, endedAt: IsNull() },
|
||||||
|
order: { startedAt: "DESC" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string): Promise<TMTime> {
|
||||||
|
const time = await this.timeRepository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: { admin: true, task: true },
|
||||||
|
});
|
||||||
|
if (!time) throw new NotFoundException(timeMessage.TIME_NOT_FOUND);
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(adminId: string, taskId: string): Promise<TMTime> {
|
||||||
|
await this.taskService.findOneOrFail(taskId);
|
||||||
|
|
||||||
|
const runningTime = await this.findRunningTime(adminId, taskId);
|
||||||
|
if (runningTime) throw new BadRequestException(timeMessage.TIME_ALREADY_STARTED);
|
||||||
|
|
||||||
|
const time = this.timeRepository.create({
|
||||||
|
taskId,
|
||||||
|
adminId,
|
||||||
|
startedAt: new Date(),
|
||||||
|
endedAt: null,
|
||||||
|
duration: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.timeRepository.save(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTaskTimes(adminId: string, taskId: string): Promise<TMTime[]> {
|
||||||
|
return this.timeRepository.find({
|
||||||
|
where: { adminId, taskId },
|
||||||
|
order: { startedAt: "DESC" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async end(adminId: string, taskId: string): Promise<TMTime> {
|
||||||
|
await this.taskService.findOneOrFail(taskId);
|
||||||
|
|
||||||
|
const runningTime = await this.findRunningTime(adminId, taskId);
|
||||||
|
if (!runningTime) throw new BadRequestException(timeMessage.NO_RUNNING_TIME);
|
||||||
|
|
||||||
|
const endedAt = new Date();
|
||||||
|
runningTime.endedAt = endedAt;
|
||||||
|
runningTime.duration = this.calculateDuration(runningTime.startedAt, endedAt);
|
||||||
|
|
||||||
|
return this.timeRepository.save(runningTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(adminId: string, dto: CreateTimeDto): Promise<TMTime> {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
|
||||||
|
const startedAt = new Date(dto.startedAt);
|
||||||
|
const endedAt = new Date(dto.endedAt);
|
||||||
|
|
||||||
|
const time = this.timeRepository.create({
|
||||||
|
taskId: dto.taskId,
|
||||||
|
adminId,
|
||||||
|
startedAt,
|
||||||
|
endedAt,
|
||||||
|
duration: this.calculateDuration(startedAt, endedAt),
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.timeRepository.save(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateTimeDto): Promise<TMTime> {
|
||||||
|
const time = await this.findOneOrFail(id);
|
||||||
|
|
||||||
|
if (dto.taskId) {
|
||||||
|
await this.taskService.findOneOrFail(dto.taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startedAt = dto.startedAt ? new Date(dto.startedAt) : time.startedAt;
|
||||||
|
const endedAt = dto.endedAt ? new Date(dto.endedAt) : time.endedAt;
|
||||||
|
|
||||||
|
Object.assign(time, {
|
||||||
|
...dto,
|
||||||
|
startedAt,
|
||||||
|
endedAt,
|
||||||
|
duration: endedAt ? this.calculateDuration(startedAt, endedAt) : null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.timeRepository.save(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOneOrFail(id);
|
||||||
|
await this.timeRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { Injectable, NotFoundException, ConflictException } from "@nestjs/common";
|
|
||||||
import { WorkspaceTypeRepository } from "../repositories/workspace-type.repository";
|
|
||||||
import { TMWorkspaceType } from "../entities/workspace-type.entity";
|
|
||||||
import { CreateWorkspaceTypeDto } from "../dto/workspace-type/create-workspace-type.dto";
|
|
||||||
import { UpdateWorkspaceTypeDto } from "../dto/workspace-type/update-workspace-type.dto";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class WorkspaceTypeService {
|
|
||||||
constructor(private readonly workspaceTypeRepository: WorkspaceTypeRepository) {}
|
|
||||||
|
|
||||||
findAll(): Promise<TMWorkspaceType[]> {
|
|
||||||
return this.workspaceTypeRepository.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
async findOne(id: string): Promise<TMWorkspaceType> {
|
|
||||||
const workspaceType = await this.workspaceTypeRepository.findOneById(id);
|
|
||||||
if (!workspaceType) throw new NotFoundException(`WorkspaceType #${id} not found`);
|
|
||||||
return workspaceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
async create(dto: CreateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
|
||||||
const existing = await this.workspaceTypeRepository.findOneByName(dto.name);
|
|
||||||
if (existing) throw new ConflictException(`WorkspaceType with name "${dto.name}" already exists`);
|
|
||||||
|
|
||||||
const workspaceType = this.workspaceTypeRepository.create(dto);
|
|
||||||
return this.workspaceTypeRepository.save(workspaceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
async update(id: string, dto: UpdateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
|
||||||
await this.findOne(id);
|
|
||||||
|
|
||||||
if (dto.name) {
|
|
||||||
const existing = await this.workspaceTypeRepository.findOneByName(dto.name);
|
|
||||||
if (existing && existing.id !== id) throw new ConflictException(`WorkspaceType with name "${dto.name}" already exists`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.workspaceTypeRepository.update(id, dto);
|
|
||||||
return this.findOne(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async remove(id: string): Promise<TMWorkspaceType> {
|
|
||||||
const workspaceType = await this.findOne(id);
|
|
||||||
await this.workspaceTypeRepository.delete(id);
|
|
||||||
|
|
||||||
return workspaceType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +1,35 @@
|
|||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { In, Repository } from "typeorm";
|
import { In, Not, Repository } from "typeorm";
|
||||||
import { WorkspaceRepository } from "../repositories/workspace.repository";
|
import { WorkspaceRepository } from "../repositories/workspace.repository";
|
||||||
import { TMWorkspace } from "../entities/workspace.entity";
|
import { TMWorkspace } from "../entities/workspace.entity";
|
||||||
import { CreateWorkspaceDto } from "../dto/workspace/create-workspace.dto";
|
import { CreateWorkspaceDto } from "../dto/workspace/create-workspace.dto";
|
||||||
import { UpdateWorkspaceDto } from "../dto/workspace/update-workspace.dto";
|
import { UpdateWorkspaceDto } from "../dto/workspace/update-workspace.dto";
|
||||||
import { User } from "../../users/entities/user.entity";
|
import { User } from "../../users/entities/user.entity";
|
||||||
import { WorkspaceTypeRepository } from "../repositories/workspace-type.repository";
|
import { WorkSpaceMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceService {
|
export class WorkspaceService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly workspaceRepository: WorkspaceRepository,
|
private readonly workspaceRepository: WorkspaceRepository,
|
||||||
private readonly workspaceTypeRepository: WorkspaceTypeRepository,
|
|
||||||
@InjectRepository(User)
|
@InjectRepository(User)
|
||||||
private readonly userRepository: Repository<User>,
|
private readonly userRepository: Repository<User>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
findAll(): Promise<TMWorkspace[]> {
|
|
||||||
return this.workspaceRepository.findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
async findOneOrFail(id: string): Promise<TMWorkspace> {
|
async findOneOrFail(id: string): Promise<TMWorkspace> {
|
||||||
const workspace = await this.workspaceRepository.findOneById(id);
|
const workspace = await this.workspaceRepository.findOneWorkSpace(id);
|
||||||
if (!workspace) throw new NotFoundException(`Workspace #${id} not found`);
|
if (!workspace) throw new NotFoundException(WorkSpaceMessage.WORKSPACE_NOT_FOUND);
|
||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(dto: CreateWorkspaceDto): Promise<TMWorkspace> {
|
async create(dto: CreateWorkspaceDto): Promise<TMWorkspace> {
|
||||||
const { userIds, workspaceTypeId, ...rest } = dto;
|
const { userIds, ...rest } = dto;
|
||||||
|
|
||||||
const workspaceType = await this.workspaceTypeRepository.findOneById(workspaceTypeId);
|
const existingWorkspace = await this.workspaceRepository.findOne({ where: { name: rest.name } });
|
||||||
if (!workspaceType) throw new NotFoundException(`WorkSpaceType #${workspaceTypeId} not found!`);
|
if (existingWorkspace) {
|
||||||
|
throw new BadRequestException(WorkSpaceMessage.WORKSPACE_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
const workspace = this.workspaceRepository.create(rest);
|
const workspace = this.workspaceRepository.create(rest);
|
||||||
|
|
||||||
@@ -44,11 +42,11 @@ export class WorkspaceService {
|
|||||||
|
|
||||||
async update(id: string, dto: UpdateWorkspaceDto): Promise<TMWorkspace> {
|
async update(id: string, dto: UpdateWorkspaceDto): Promise<TMWorkspace> {
|
||||||
const workspace = await this.findOneOrFail(id);
|
const workspace = await this.findOneOrFail(id);
|
||||||
const { userIds, workspaceTypeId, ...rest } = dto;
|
const { userIds, ...rest } = dto;
|
||||||
|
|
||||||
if (workspaceTypeId) {
|
if (rest.name) {
|
||||||
const workspaceType = await this.workspaceTypeRepository.findOneById(workspaceTypeId);
|
const existingWorkSpaceWithTheSameName = await this.workspaceRepository.findOne({ where: { name: rest.name, id: Not(id) } });
|
||||||
if (!workspaceType) throw new NotFoundException(`WorkSpaceType #${workspaceTypeId} not found!`);
|
if (existingWorkSpaceWithTheSameName) throw new BadRequestException(WorkSpaceMessage.WORKSPACE_SAME_NAME_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(workspace, rest);
|
Object.assign(workspace, rest);
|
||||||
@@ -66,4 +64,16 @@ export class WorkspaceService {
|
|||||||
|
|
||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findByUser(userId: string, permissions: PermissionEnum[]): Promise<TMWorkspace[]> {
|
||||||
|
if(permissions.includes(PermissionEnum.WORKSPACE_READ)) {
|
||||||
|
return await this.workspaceRepository.find();
|
||||||
|
}
|
||||||
|
return await this.workspaceRepository.findByUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getWorkspaceUsers(workspaceId: string): Promise<User[]> {
|
||||||
|
const workspace = await this.findOneOrFail(workspaceId);
|
||||||
|
return workspace.users;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { TMAttachment } from '../entities/attachment.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AttachmentRepository extends Repository<TMAttachment>{
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMAttachment) attachmentRepository: Repository<TMAttachment>,
|
||||||
|
) {
|
||||||
|
super(attachmentRepository.target, attachmentRepository.manager, attachmentRepository.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { TMCheckListItem } from '../entities/check-list-item.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CheckListItemRepository extends Repository<TMCheckListItem>{
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMCheckListItem) checkListItemRepository: Repository<TMCheckListItem>,
|
||||||
|
) {
|
||||||
|
super(checkListItemRepository.target, checkListItemRepository.manager, checkListItemRepository.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { TMProject } from "../entities/project.entity";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ProjectRepository extends Repository<TMProject> {
|
||||||
|
constructor(@InjectRepository(TMProject) projectRepository: Repository<TMProject>) {
|
||||||
|
super(projectRepository.target, projectRepository.manager, projectRepository.queryRunner);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findUserProjects(userId: string, wsId: string, skip?: number, take?: number): Promise<[TMProject[], number]> {
|
||||||
|
return this.createQueryBuilder("project")
|
||||||
|
.innerJoin("project.workspace", "workspace")
|
||||||
|
.innerJoin("project.users", "user")
|
||||||
|
.where("workspace.id = :wsId", { wsId })
|
||||||
|
.andWhere("user.id = :userId", { userId })
|
||||||
|
.skip(skip ?? 0)
|
||||||
|
.take(take ?? 10)
|
||||||
|
.getManyAndCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProjectDetail(projectId: string): Promise<TMProject | null> {
|
||||||
|
return this.createQueryBuilder("project")
|
||||||
|
.leftJoinAndSelect("project.taskPhases", "taskPhase")
|
||||||
|
.where("project.id = :projectId", { projectId })
|
||||||
|
|
||||||
|
.select([
|
||||||
|
"project.id",
|
||||||
|
"project.name",
|
||||||
|
"taskPhase.id",
|
||||||
|
"taskPhase.name",
|
||||||
|
"taskPhase.order",
|
||||||
|
])
|
||||||
|
|
||||||
|
.orderBy("taskPhase.order", "ASC")
|
||||||
|
|
||||||
|
.getOne();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { TMRemark } from "../entities/remark.entity";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RemarkRepository extends Repository<TMRemark>{
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMRemark) remarkRepository: Repository<TMRemark>,
|
||||||
|
) {
|
||||||
|
super(remarkRepository.target, remarkRepository.manager, remarkRepository.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { TMTaskComment } from "../entities/task-comment.entity";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskCommentRepository extends Repository<TMTaskComment> {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMTaskComment) taskCommentRepository: Repository<TMTaskComment>,
|
||||||
|
) {
|
||||||
|
super(taskCommentRepository.target, taskCommentRepository.manager, taskCommentRepository.queryRunner);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user