ソースを参照

добавлен функционал задач

Vadim 7 ヶ月 前
コミット
92a65c5e30

+ 13 - 19
src/api/auth-api.ts

@@ -3,11 +3,9 @@ import { z } from "zod";
 class authApi {
   // /auth/registration
   public ZRegistration = {
-    req: z
-      .object({
-        email: z.string().email(),
-      })
-      .strict(),
+    req: z.object({
+      email: z.string().email(),
+    }),
     res: z.object({
       code: z.enum(["pinIsSent", "pinIsNotSent", "alreadyExists"]),
       transactionId: z.string().uuid().optional(),
@@ -16,14 +14,12 @@ class authApi {
 
   // /auth/confirm-registration
   public ZConfirmRegistration = {
-    req: z
-      .object({
-        password: z.string(),
-        name: z.string(),
-        transactionId: z.string().uuid(),
-        confirmPin: z.number().min(1000).max(9999),
-      })
-      .strict(),
+    req: z.object({
+      password: z.string(),
+      name: z.string(),
+      transactionId: z.string().uuid(),
+      confirmPin: z.number().min(1000).max(9999),
+    }),
     res: z.discriminatedUnion("code", [
       z.object({
         code: z.literal("registered"),
@@ -42,12 +38,10 @@ class authApi {
 
   // /auth/login
   public ZLogin = {
-    req: z
-      .object({
-        email: z.string().email(),
-        password: z.string(),
-      })
-      .strict(),
+    req: z.object({
+      email: z.string().email(),
+      password: z.string(),
+    }),
     res: z.object({
       code: z.enum(["userNotFound", "passIsWrong", "tooManyTries", "success"]),
       triesRemained: z.number().optional(),

+ 7 - 11
src/api/companies-management-api.ts

@@ -3,12 +3,10 @@ import { z } from "zod";
 class companyManagementApi {
   // /companies-management/create-company
   ZCreateCompany = {
-    req: z
-      .object({
-        name: z.string(),
-        timezone: z.string(),
-      })
-      .strict(),
+    req: z.object({
+      name: z.string(),
+      timezone: z.string(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
@@ -30,11 +28,9 @@ class companyManagementApi {
   };
 
   ZGetCompany = {
-    req: z
-      .object({
-        companyId: z.string().uuid(),
-      })
-      .strict(),
+    req: z.object({
+      companyId: z.string().uuid(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
       company: z.object({

+ 55 - 54
src/api/events-management-api.ts

@@ -2,33 +2,31 @@ import { z } from "zod";
 
 class eventsManagementApi {
   ZCreateEvent = {
-    req: z
-      .object({
-        localName: z.string(),
-        timezone: z.string().optional(),
-        dates: z.array(z.string()),
-        companyId: z.string().uuid(),
-        rolesToAdd: z.array(
-          z.object({
-            roleId: z.string().uuid(),
-            name: z.string(),
-            description: z.string(),
-            permissions: z.array(
-              z.object({
-                permissionId: z.string(),
-                permissionValueId: z.string(),
-              }),
-            ),
-          }),
-        ),
-        staff: z.array(
-          z.object({
-            userId: z.string().uuid(),
-            roleId: z.string().uuid(),
-          }),
-        ),
-      })
-      .strict(),
+    req: z.object({
+      localName: z.string(),
+      timezone: z.string().optional(),
+      dates: z.array(z.string()),
+      companyId: z.string().uuid(),
+      rolesToAdd: z.array(
+        z.object({
+          roleId: z.string().uuid(),
+          name: z.string(),
+          description: z.string(),
+          permissions: z.array(
+            z.object({
+              permissionId: z.string(),
+              permissionValueId: z.string(),
+            }),
+          ),
+        }),
+      ),
+      staff: z.array(
+        z.object({
+          userId: z.string().uuid(),
+          roleId: z.string().uuid(),
+        }),
+      ),
+    }),
     res: z.object({
       code: z.enum(["success"]),
       event_id: z.string().uuid(),
@@ -81,50 +79,53 @@ class eventsManagementApi {
             name: z.string(),
           }),
         ),
+
+        staff: z.array(
+          z.object({
+            user_id: z.string().uuid(),
+            name: z.string(),
+            role_id: z.string().uuid(),
+            role_name: z.string(),
+          }),
+        ),
       }),
     }),
   };
 
   ZCreateProgramPoint = {
-    req: z
-      .object({
-        eventId: z.string().uuid(),
-        name: z.string(),
-        startDate: z.string(),
-        endDate: z.string(),
-        roomId: z.string().uuid().optional(),
-        isInternal: z.boolean(),
-      })
-      .strict(),
+    req: z.object({
+      eventId: z.string().uuid(),
+      name: z.string(),
+      startDate: z.string(),
+      endDate: z.string(),
+      roomId: z.string().uuid().optional(),
+      isInternal: z.boolean(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
   };
 
   ZUpdateEvent = {
-    req: z
-      .object({
-        eventId: z.string().uuid(),
-        localName: z.string().optional(),
-        timezone: z.string().optional(),
-        dates: z.array(z.string()).optional(),
-      })
-      .strict(),
+    req: z.object({
+      eventId: z.string().uuid(),
+      localName: z.string().optional(),
+      timezone: z.string().optional(),
+      dates: z.array(z.string()).optional(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
   };
 
   ZUpdateProgramPoint = {
-    req: z
-      .object({
-        programPointId: z.string().uuid(),
-        name: z.string(),
-        startDate: z.string(),
-        endDate: z.string(),
-        roomId: z.string().uuid().optional(),
-      })
-      .strict(),
+    req: z.object({
+      programPointId: z.string().uuid(),
+      name: z.string(),
+      startDate: z.string(),
+      endDate: z.string(),
+      roomId: z.string().uuid().optional(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),

+ 13 - 19
src/api/locations-management-api.ts

@@ -2,31 +2,25 @@ import { z } from "zod";
 
 class locationsManagementApi {
   ZCreateLocation = {
-    req: z
-      .object({
-        name: z.string(),
-        eventId: z.string().uuid(),
-        rooms: z.array(
-          z.object({
-            name: z.string(),
-            parentId: z.string().uuid().nullable(),
-          }),
-        ),
-      })
-      .strict(),
-
+    req: z.object({
+      name: z.string(),
+      eventId: z.string().uuid(),
+      rooms: z.array(
+        z.object({
+          name: z.string(),
+          parentId: z.string().uuid().nullable(),
+        }),
+      ),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
   };
 
   ZGetEventLocations = {
-    req: z
-      .object({
-        eventId: z.string().uuid(),
-      })
-      .strict(),
-
+    req: z.object({
+      eventId: z.string().uuid(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
       locations: z.array(

+ 33 - 39
src/api/tasks-management-api.ts

@@ -2,64 +2,56 @@ import { z } from "zod";
 
 class tasksManagementApi {
   ZCreateTaskBlock = {
-    req: z
-      .object({
-        eventId: z.string().uuid(),
-        name: z.string(),
-      })
-      .strict(),
+    req: z.object({
+      eventId: z.string().uuid(),
+      name: z.string(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
   };
 
   ZUpdateTaskBlock = {
-    req: z
-      .object({
-        taskBlockId: z.string().uuid(),
-        name: z.string(),
-      })
-      .strict(),
+    req: z.object({
+      taskBlockId: z.string().uuid(),
+      name: z.string(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
   };
 
   ZCreateTask = {
-    req: z
-      .object({
-        name: z.string(),
-        startDate: z.string().nullable(),
-        endDate: z.string().nullable(),
-        accountableId: z.string().uuid().nullable(),
-        isTodo: z.boolean(),
-        programPointId: z.string().uuid().nullable(),
-        roomId: z.string().uuid().nullable(),
-        taskBlockId: z.string().uuid(),
-        executors: z.array(z.string()),
-      })
-      .strict(),
+    req: z.object({
+      name: z.string(),
+      startDate: z.string().nullable(),
+      endDate: z.string().nullable(),
+      accountableId: z.string().uuid().nullable(),
+      isTodo: z.boolean(),
+      programPointId: z.string().uuid().nullable(),
+      roomId: z.string().uuid().nullable(),
+      taskBlockId: z.string().uuid(),
+      executors: z.array(z.string()),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
   };
 
   ZUpdateTask = {
-    req: z
-      .object({
-        taskId: z.string().uuid(),
+    req: z.object({
+      taskId: z.string().uuid(),
 
-        name: z.string().optional(),
-        startDate: z.string().nullable().optional(),
-        endDate: z.string().nullable().optional(),
-        // TODO: проверить optional
-        accountableId: z.string().uuid().nullable().optional(),
-        isTodo: z.boolean().optional(),
-        programPointId: z.string().uuid().nullable().optional(),
-        roomId: z.string().uuid().nullable().optional(),
-        executors: z.array(z.string()).optional(),
-      })
-      .strict(),
+      name: z.string().optional(),
+      startDate: z.string().nullable().optional(),
+      endDate: z.string().nullable().optional(),
+      // TODO: сделать optional на фронте
+      accountableId: z.string().uuid().nullable().optional(),
+      isTodo: z.boolean().optional(),
+      programPointId: z.string().uuid().nullable().optional(),
+      roomId: z.string().uuid().nullable().optional(),
+      executors: z.array(z.string()).optional(),
+    }),
     res: z.object({
       code: z.enum(["success"]),
     }),
@@ -69,6 +61,8 @@ class tasksManagementApi {
     req: z.object({ taskBlockId: z.string().uuid() }).strict(),
     res: z.object({
       code: z.enum(["success"]),
+      task_block_id: z.string().uuid(),
+      name: z.string(),
       tasks: z.array(
         z.object({
           task_id: z.string().uuid(),

+ 27 - 13
src/db/db-interrceptors.ts

@@ -7,6 +7,26 @@ import {
 } from "slonik";
 
 import { logger } from "../plugins/logger.js";
+import { ApiError } from "#exceptions/api-error.js";
+import { UnexpectedError } from "#exceptions/unexpected-errors.js";
+
+export const createLoggerInterceptor = (): Interceptor => {
+  return {
+    beforeQueryExecution: (context, query) => {
+      logger.silly({ executingQuery: query.sql, parameters: query.values });
+      return null;
+    },
+
+    afterQueryExecution: (context, query, result) => {
+      logger.silly({
+        executedQuery: query.sql,
+        parameters: query.values,
+        result: result.rows,
+      });
+      return null;
+    },
+  };
+};
 
 export const createResultParserInterceptor = (): Interceptor => {
   return {
@@ -17,7 +37,6 @@ export const createResultParserInterceptor = (): Interceptor => {
     // TODO заменить на afterQueryExecution
     transformRow: (executionContext, actualQuery, row) => {
       const { resultParser } = executionContext;
-      logger.silly("Запрос: ", { actualQuery, row });
 
       if (!resultParser) {
         return row;
@@ -58,19 +77,14 @@ export const createErrorLoggingInterceptor = (): Interceptor => {
       const callerStack = executionContext.callerStack || "No stack";
 
       // Создаем новое исключение с дополнительной информацией.
-      const enhancedError = new Error(
-        `
-        Query failed: ${actualQuery.sql}
-        Parameters: ${JSON.stringify(actualQuery.values)}
-        Caller stack: ${callerStack}
-        Original error: ${error.message}
-      `.trim(),
-      );
-
-      // Сохраняем оригинальную ошибку как причину (если поддерживается).
-      (enhancedError as Error & { cause?: Error }).cause = error;
+      const enhancedError = {
+        query: actualQuery.sql,
+        params: actualQuery.values,
+        stack: callerStack,
+        error: error,
+      };
 
-      throw enhancedError;
+      throw UnexpectedError.DbError(enhancedError);
     },
   };
 };

+ 0 - 1
src/db/db-shema.ts

@@ -1,6 +1,5 @@
 import { z } from "zod";
 
-// TODO добавть везде uuid
 const ZDbShema = {
   events_management: {
     events: {

+ 2 - 0
src/db/db.ts

@@ -2,6 +2,7 @@ import { createPool } from "slonik";
 import {
   createErrorLoggingInterceptor,
   createResultParserInterceptor,
+  createLoggerInterceptor,
 } from "./db-interrceptors.js";
 import { config } from "#config";
 
@@ -15,6 +16,7 @@ const pool = await createPool(
   `postgres://${user}:${password}@${host}:${port}/${databaseName}`,
   {
     interceptors: [
+      createLoggerInterceptor(),
       createResultParserInterceptor(),
       createErrorLoggingInterceptor(),
     ],

+ 26 - 0
src/exceptions/unexpected-errors.ts

@@ -0,0 +1,26 @@
+export const UnexpectedError = class UnexpectedError extends Error {
+  code;
+  status;
+  errors;
+
+  constructor(
+    status: number,
+    code: string,
+    message: string | undefined,
+    errors?,
+  ) {
+    super(message);
+    this.status = status;
+    this.code = code;
+    this.errors = errors || [];
+  }
+
+  static DbError(errors) {
+    return new UnexpectedError(
+      500,
+      "dbError",
+      "Ошибка базы данных",
+      errors || [],
+    );
+  }
+};

+ 35 - 11
src/modules/events-management/events-router.ts

@@ -211,7 +211,7 @@ router.post("/get-event", async (req, res, next) => {
             e.event_id,
             e.local_name,
             e.timezone,
-            ARRAY_AGG(ed."date") as dates
+            COALESCE(ARRAY_REMOVE(ARRAY_AGG(ed."date"), NULL), '{}') AS dates
           from
             events_management.events e
           left join events_management.event_dates ed on
@@ -254,12 +254,11 @@ router.post("/get-event", async (req, res, next) => {
         `,
     );
 
-    // уникальные комнаты ивента
-    const eventRoomsIds = new Set<string>();
-    for (const point of programPoints) {
-      if (point.room_id) eventRoomsIds.add(point.room_id);
-    }
-    const eventRoomsIdsArr = Array.from(eventRoomsIds);
+    // комнаты ивента
+    const eventRoomsIds: string[] = programPoints
+      .map((point) => point.room_id)
+      .filter((roomId) => roomId !== null);
+
     //
     // rooms
     let rooms: readonly {
@@ -268,7 +267,7 @@ router.post("/get-event", async (req, res, next) => {
     }[] = [];
 
     // TODO ошибка в in?
-    if (eventRoomsIdsArr.length > 0) {
+    if (eventRoomsIds.length > 0) {
       rooms = await db.any(
         sql.type(
           z.object({
@@ -282,14 +281,14 @@ router.post("/get-event", async (req, res, next) => {
           from
             locations_management.rooms
           where
-            room_id in (${sql.join(eventRoomsIdsArr, sql.fragment`, `)})
+            room_id in (${sql.join(eventRoomsIds, sql.fragment`, `)})
           `,
       );
     }
 
     // task-blocks
     // TODO вынести
-    const REQUIRED_PERMISSION: AllPermissionsValues = "view_task_block_true";
+    const REQUIRED_TB_PERMISSION: AllPermissionsValues = "view_task_block_true";
     const taskBlocks = await db.any(
       sql.type(
         z.object({
@@ -308,7 +307,31 @@ router.post("/get-event", async (req, res, next) => {
         where
           tb.event_id = ${eventId} and
           cup.user_id = ${userId} and
-          cup.permission_value_id = ${REQUIRED_PERMISSION}
+          cup.permission_value_id = ${REQUIRED_TB_PERMISSION}
+        `,
+    );
+
+    const staff = await db.any(
+      sql.type(
+        z.object({
+          user_id: ZDbShema.users_management.users.user_id,
+          name: ZDbShema.users_management.users.name,
+          role_id: ZDbShema.permissions_management.roles.role_id,
+          role_name: ZDbShema.permissions_management.roles.name,
+        }),
+      )`
+        select
+          u.user_id,
+          u.name,
+          ur.role_id,
+          r.name as role_name
+        from
+          users_management.users u
+        join permissions_management.user_roles ur on
+          u.user_id = ur.user_id
+        join permissions_management.roles r on
+          r.role_id = ur.role_id
+        where ur.entity_id = ${eventId}
         `,
     );
 
@@ -320,6 +343,7 @@ router.post("/get-event", async (req, res, next) => {
         programPoints: [...programPoints],
         rooms: [...rooms],
         taskBlocks: [...taskBlocks],
+        staff: [...staff],
       },
     });
   } catch (e) {

+ 73 - 25
src/modules/tasks-management/tasks-router.ts

@@ -30,6 +30,9 @@ import { EntityesService } from "#modules/entities-management/entityes-service.j
 import { RouterUtils } from "#utils/router-utils.js";
 import { TasksManagementApi } from "#api/tasks-management-api.js";
 import { CheckPermissionsService } from "#modules/permissions-management/check-permissions-service.js";
+import { ApiError } from "#exceptions/api-error.js";
+import { logger } from "#plugins/logger.js";
+import { AllPermissionsValues } from "#modules/permissions-management/permissions-types.js";
 
 dayjs.extend(utc);
 
@@ -82,6 +85,10 @@ router.post("/create-task", async (req, res, next) => {
           (${taskId}, ${executorId})`,
       );
     }
+
+    RouterUtils.validAndSendResponse(TasksManagementApi.ZUpdateTask.res, res, {
+      code: "success",
+    });
   } catch (e) {
     next(e);
   }
@@ -92,7 +99,6 @@ router.post("/update-task", async (req, res, next) => {
     // валидация запроса
     const {
       taskId,
-
       endDate,
       startDate,
       isTodo,
@@ -116,37 +122,43 @@ router.post("/update-task", async (req, res, next) => {
     // edit task
     await db.query(
       sql.unsafe`
-      update tasks_management.tasks
-      set
+      UPDATE tasks_management.tasks
+      SET
         ${sql.join(
           [
-            name !== undefined ? `name = ${name}` : "name = name",
+            name !== undefined
+              ? sql.fragment`name = ${name}`
+              : sql.fragment`name = name`,
             startDate !== undefined
-              ? `start_date = ${startDate}`
-              : "start_date = start_date",
+              ? sql.fragment`start_date = ${startDate}`
+              : sql.fragment`start_date = start_date`,
             endDate !== undefined
-              ? `end_date = ${endDate}`
-              : "end_date = end_date",
-            isTodo !== undefined ? `is_todo = ${isTodo}` : "is_todo = is_todo",
+              ? sql.fragment`end_date = ${endDate}`
+              : sql.fragment`end_date = end_date`,
+            isTodo !== undefined
+              ? sql.fragment`is_todo = ${isTodo}`
+              : sql.fragment`is_todo = is_todo`,
             programPointId !== undefined
-              ? `program_point_id = ${programPointId}`
-              : "program_point_id = program_point_id",
-            roomId !== undefined ? `room_id = ${roomId}` : "room_id = room_id",
+              ? sql.fragment`program_point_id = ${programPointId}`
+              : sql.fragment`program_point_id = program_point_id`,
+            roomId !== undefined
+              ? sql.fragment`room_id = ${roomId}`
+              : sql.fragment`room_id = room_id`,
             accountableId !== undefined
-              ? `accountable_id = ${accountableId}`
-              : "accountable_id = accountable_id",
+              ? sql.fragment`accountable_id = ${accountableId}`
+              : sql.fragment`accountable_id = accountable_id`,
           ],
           sql.fragment`, `,
         )}
-      where
-        task_id = ${taskId}`,
+      WHERE task_id = ${taskId}
+      `,
     );
 
-    if (executors) {
+    if (executors && executors.length) {
       // del executors
       await db.query(
         sql.unsafe`
-      delete from task_management.task_executors
+      delete from tasks_management.task_executors
       where
         task_id = ${taskId}`,
       );
@@ -171,7 +183,7 @@ router.post("/update-task", async (req, res, next) => {
   }
 });
 
-router.post("/get-block-tasks", async (req, res, next) => {
+router.post("/get-task-block", async (req, res, next) => {
   try {
     // валидация запроса
     const { taskBlockId } = TasksManagementApi.ZGetBlockTasks.req.parse(
@@ -180,7 +192,33 @@ router.post("/get-block-tasks", async (req, res, next) => {
 
     const userId = UserUtils.getUserFromReq(req).userId;
 
-    const REQUIRED_PERMISSION = "task_view_true";
+    CheckPermissionsService.checkEntityPermission(
+      taskBlockId,
+      userId,
+      "view_task_block",
+      "view_task_block_true",
+    );
+
+    const taskBlock = await db.maybeOne(
+      sql.type(
+        z.object({
+          task_block_id: ZDbShema.tasks_management.task_blocks.task_block_id,
+          name: ZDbShema.tasks_management.task_blocks.name,
+        }),
+      )`
+    select
+      task_block_id,
+      name
+      from tasks_management.task_blocks
+      where task_block_id = ${taskBlockId}
+    `,
+    );
+
+    if (!taskBlock) {
+      throw ApiError.BadRequest("Task block not found", "Task block not found");
+    }
+
+    const REQUIRED_PERMISSION: AllPermissionsValues = "view_task_true";
     const ZDbTasks = ZDbShema.tasks_management.tasks;
     const tasks = await db.any(
       sql.type(
@@ -207,7 +245,9 @@ router.post("/get-block-tasks", async (req, res, next) => {
             t.task_block_id,
             t.program_point_id,
             t.room_id,
-            ARRAY_AGG(te.user_id) as executors
+            coalesce(ARRAY_REMOVE(ARRAY_AGG(te.user_id),
+            null),
+            '{}') as executors
           from
             tasks_management.tasks t
             --	executors
@@ -220,11 +260,11 @@ router.post("/get-block-tasks", async (req, res, next) => {
             --	perm
             cup.user_id = ${userId}
             and
-          cup.permission_value_id = ${REQUIRED_PERMISSION}
+                      cup.permission_value_id = ${REQUIRED_PERMISSION}
             and
             --
-          t.task_block_id = ${taskBlockId}
-          -- для executors
+                    t.task_block_id = ${taskBlockId}
+            -- для executors
           group by
             t.task_id,
             t.name,
@@ -238,11 +278,19 @@ router.post("/get-block-tasks", async (req, res, next) => {
   `,
     );
 
+    logger.info(tasks);
+    logger.info({ userId, taskBlockId, REQUIRED_PERMISSION });
+
     // res
     RouterUtils.validAndSendResponse(
       TasksManagementApi.ZGetBlockTasks.res,
       res,
-      { code: "success", tasks: [...tasks] },
+      {
+        code: "success",
+        task_block_id: taskBlock.task_block_id,
+        name: taskBlock.name,
+        tasks: [...tasks],
+      },
     );
   } catch (e) {
     next(e);