123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- // db
- import { selPool, updPool } from "#db";
- import { DbSchema } from "#db-schema";
- import { sql } from "slonik";
- // api
- import { api, apiTypes } from "#api";
- // other
- import { z } from "zod";
- import { RouterUtils } from "#utils/router-utils.js";
- import { Request, Response } from "express";
- import sessionService from "#modules/users/auth/services/session-service.js";
- import { ApiError } from "#exceptions/api-error.js";
- import { v4, v7 } from "uuid";
- import { cPeService } from "./c-pe-service.js";
- import { cCustomFieldsValidateService } from "#modules/client/custom-fields/c-cf-validate-service.js";
- class ClientPeController {
- async getEventPeTypes(
- req: Request,
- res: Response,
- // next: NextFunction
- ) {
- const event = await sessionService.getCurrentEventFromReq(req);
- const peTypes = await selPool.any(sql.type(
- z.object({
- peTypeId: DbSchema.act.peTypes.peTypeId,
- code: DbSchema.act.peTypes.code,
- name: DbSchema.act.peTypes.name,
- }),
- )`
- select
- pe_type_id as "peTypeId",
- code,
- "name"
- from
- act.pe_types pt
- where
- pt.event_inst_id = ${event.eventInstId}
- `);
- RouterUtils.validAndSendResponse(api.client.pe.GET_EventPeTypes.res, res, {
- code: "success",
- peTypes: [...peTypes],
- });
- }
- async getPeTypeForCreate(
- req: Request,
- res: Response,
- // next: NextFunction
- ) {
- const event = await sessionService.getCurrentEventFromReq(req);
- const user = sessionService.getUserFromReq(req);
- const { peTypeCode } = api.client.pe.GET_PeType.req.parse(req.params);
- const eventId = event.eventId;
- const userId = user.userId;
- const peType = await cPeService.getPeTypeWithFieldsAndUserCopyValues(
- userId,
- peTypeCode,
- eventId,
- );
- if (!peType)
- throw ApiError.BadRequest(
- "peTypeNotFound",
- "Тип сущности участия не найден",
- );
- RouterUtils.validAndSendResponse(api.client.pe.GET_PeType.res, res, {
- code: "success",
- peType: peType,
- });
- }
- async createPe(req: Request, res: Response) {
- const event = await sessionService.getCurrentEventFromReq(req);
- const user = sessionService.getUserFromReq(req);
- const { fields, peTypeCode, name } =
- api.client.pe.POST_PartEntity.req.formData.body.parse(
- JSON.parse(req.body.body),
- );
- const files = req.files;
- const peType = await cPeService.getPeTypeWithFields(peTypeCode);
- if (!peType)
- throw ApiError.BadRequest(
- "peTypeNotFound",
- "Тип сущности участия не найден",
- );
- const refFields = peType.fields.map((f) => ({
- ...f,
- idKey: "peFfId",
- }));
- // валидация
- const validationResult =
- await cCustomFieldsValidateService.processAndValidateFields({
- inputFields: fields,
- referenceFields: refFields,
- files,
- idKey: "peFfId",
- addOldValue: false,
- });
- if (!validationResult.isValid)
- throw ApiError.BadRequest(
- "fieldsValidationFailed",
- JSON.stringify(validationResult.messages),
- );
- const validatedFields = validationResult.checkedfields;
- //
- //
- // вставляем в базу и сохраняем файлы
- const peId = v7();
- await updPool.transaction(async (tr) => {
- await tr.query(sql.unsafe`
- insert into act.part_entities
- (pe_id, pe_type_id, event_inst_id, owner_id, name)
- values
- (${peId}, ${peType.peTypeId}, ${event.eventInstId}, ${user.userId}, ${name})
- `);
- await cCustomFieldsValidateService.saveCustomFieldValuesInTransaction({
- tr,
- parentId: peId,
- action: "peCreate",
- inputFields: validatedFields,
- files,
- isDeleteBefore: false,
- });
- });
- RouterUtils.validAndSendResponse(api.client.pe.POST_PartEntity.res, res, {
- code: "success",
- peId,
- });
- }
- async getPeForPatch(req: Request, res: Response) {
- const { peId } = api.client.pe.GET_PeForPatch.req.parse(req.params);
- const user = sessionService.getUserFromReq(req);
- const pe = await cPeService.getPeWithValidatorsAndValues(peId);
- if (!pe)
- throw ApiError.BadRequest("peNotFound", "Сущность участия не найдена");
- if (pe.ownerId !== user.userId) throw ApiError.ForbiddenError();
- RouterUtils.validAndSendResponse(api.client.pe.GET_PeForPatch.res, res, {
- code: "success",
- pe: pe,
- });
- }
- async patchPe(req: Request, res: Response) {
- const { peId } = api.client.pe.PATCH_PartEntity.req.params.parse(
- req.params,
- );
- const { name, fields } =
- api.client.pe.PATCH_PartEntity.req.formData.body.parse(
- JSON.parse(req.body.body),
- );
- const files = req.files;
- const user = sessionService.getUserFromReq(req);
- const peType = await cPeService.getPeWithValidatorsAndValues(peId);
- if (!peType)
- throw ApiError.BadRequest(
- "peTypeNotFound",
- "Тип сущности участия не найден",
- );
- // проверка доступа
- if (peType.ownerId !== user.userId) throw ApiError.ForbiddenError();
- const refFields = peType.fields
- .map((f) => ({
- ...f,
- idKey: "peFfId",
- }))
- // только изменяемые
- .filter((f) => fields.some((ff) => ff.peFfId === f.peFfId));
- // валидация
- const validationResult =
- await cCustomFieldsValidateService.processAndValidateFields({
- inputFields: fields,
- referenceFields: refFields,
- files,
- idKey: "peFfId",
- addOldValue: true,
- });
- if (!validationResult.isValid)
- throw ApiError.BadRequest(
- "fieldsValidationFailed",
- JSON.stringify(validationResult.messages),
- );
- const validatedFields = validationResult.checkedfields;
- //
- //
- // вставляем в базу и сохраняем файлы
- await updPool.transaction(async (tr) => {
- if (name) {
- await tr.query(sql.unsafe`
- update act.part_entities
- set
- name = ${name}
- where
- pe_id = ${peId}
- `);
- }
- await cCustomFieldsValidateService.saveCustomFieldValuesInTransaction({
- tr,
- parentId: peId,
- action: "peCreate",
- inputFields: validatedFields,
- files,
- isDeleteBefore: true,
- });
- });
- RouterUtils.validAndSendResponse(api.client.pe.PATCH_PartEntity.res, res, {
- code: "success",
- });
- }
- async getMyPes(req: Request, res: Response) {
- const user = sessionService.getUserFromReq(req);
- const event = await sessionService.getCurrentEventFromReq(req);
- const ownerPes = await selPool.any(sql.type(
- z.object({
- peId: DbSchema.act.partEntities.peId,
- peTypeId: DbSchema.act.partEntities.peTypeId,
- peTypeCode: DbSchema.act.peTypes.code,
- peTypeName: DbSchema.act.peTypes.name,
- eventInstId: DbSchema.act.partEntities.eventInstId,
- ownerId: DbSchema.act.partEntities.ownerId,
- name: DbSchema.act.partEntities.name,
- }),
- )`
- select
- pe.pe_id "peId",
- pe.event_inst_id "eventInstId",
- pe.owner_id "ownerId",
- pe.pe_type_id "peTypeId",
- pt.code "peTypeCode",
- pt."name" "peTypeName",
- pe."name"
- from
- act.part_entities pe
- join act.pe_types pt on
- pt.pe_type_id = pe.pe_type_id
- where
- pe.event_inst_id = ${event.eventInstId}
- and pe.owner_id = ${user.userId}
- `);
- const memberPes = await selPool.any(sql.type(
- z.object({
- peMemberId: DbSchema.act.peMembers.peMemberId,
- peId: DbSchema.act.partEntities.peId,
- peTypeId: DbSchema.act.partEntities.peTypeId,
- peTypeCode: DbSchema.act.peTypes.code,
- peTypeName: DbSchema.act.peTypes.name,
- ownerId: DbSchema.act.partEntities.ownerId,
- name: DbSchema.act.partEntities.name,
- }),
- )`
- select
- pm.pe_member_id "peMemberId",
- pe.pe_id "peId",
- pe.event_inst_id "peInstId",
- pe.owner_id "ownerId",
- pe.pe_type_id "peTypeId",
- pt.code "peTypeCode",
- pt."name" "peTypeName",
- pe."name"
- from
- act.pe_members pm
- join act.part_entities pe on
- pe.pe_id = pm.pe_id
- join act.pe_types pt on
- pt.pe_type_id = pe.pe_type_id
- where
- pe.event_inst_id = ${event.eventInstId}
- and pm.user_id = ${user.userId}
- `);
- RouterUtils.validAndSendResponse(api.client.pe.GET_MyPes.res, res, {
- code: "success",
- owner: [...ownerPes],
- memeber: [...memberPes],
- });
- }
- async getPe(req: Request, res: Response) {
- const user = sessionService.getUserFromReq(req);
- const { peId } = api.client.pe.GET_PartEntity.req.parse(req.params);
- const isOwner = await selPool.exists(
- sql.unsafe`
- select
- pe_id
- from
- act.part_entities pe
- where
- pe.owner_id = ${user.userId}
- and pe.pe_id = ${peId}
- `,
- );
- // валделец
- if (isOwner) {
- const pe = await cPeService.getPeWithValues(peId);
- const members = await cPeService.getMembers(peId);
- const invites = await cPeService.getInvites(peId);
- if (!pe)
- throw ApiError.BadRequest("peNotFound", "Сущность участия не найдена");
- if (pe.ownerId !== user.userId) throw ApiError.ForbiddenError();
- RouterUtils.validAndSendResponse(api.client.pe.GET_PartEntity.res, res, {
- code: "success",
- pe: {
- ...pe,
- members: [...members],
- invites: [...invites],
- userRole: "owner",
- },
- });
- return;
- }
- // участник
- const isMember = await selPool.exists(
- sql.unsafe`
- select
- pm.pe_id
- from
- act.pe_members pm
- where
- pm.user_id = ${user.userId}
- and pm.pe_id = ${peId}
- `,
- );
- if (!isMember) throw ApiError.ForbiddenError();
- const pe = await cPeService.getPeForMember(peId);
- if (!pe)
- throw ApiError.BadRequest("peNotFound", "Сущность участия не найдена");
- RouterUtils.validAndSendResponse(api.client.pe.GET_PartEntity.res, res, {
- code: "success",
- pe: { ...pe, userRole: "member" },
- });
- }
- async createInvite(req: Request, res: Response) {
- const user = sessionService.getUserFromReq(req);
- const { name, limitVal, expirationDate } =
- api.client.pe.POST_Invite.req.body.parse(req.body);
- const { peId } = api.client.pe.POST_Invite.req.params.parse(req.params);
- const isOwner = await cPeService.checkPeOwner(user.userId, peId);
- if (!isOwner) throw ApiError.ForbiddenError();
- const peInviteId = v7();
- const peInviteUuid = v4();
- await updPool.transaction(async (t) => {
- await t.query(sql.unsafe`
- insert into act.pe_invites
- (pe_invite_id, pe_invite_uuid, pe_id, limit_val, name, expiration_date)
- values
- (${peInviteId}, ${peInviteUuid}, ${peId}, ${limitVal}, ${name}, ${expirationDate})
- `);
- });
- RouterUtils.validAndSendResponse(api.client.pe.POST_Invite.res, res, {
- code: "success",
- peInviteId,
- });
- }
- async getInvites(req: Request, res: Response) {
- const user = sessionService.getUserFromReq(req);
- const { peId } = api.client.pe.GET_Invites.req.parse(req.params);
- const isOwner = await cPeService.checkPeOwner(user.userId, peId);
- if (!isOwner) throw ApiError.ForbiddenError();
- const invites = await cPeService.getInvites(peId);
- RouterUtils.validAndSendResponse(api.client.pe.GET_Invites.res, res, {
- code: "success",
- invites: [...invites],
- });
- }
- async getInviteInfo(req: Request, res: Response) {
- const { peInviteUuid } = api.client.pe.GET_InviteInfo.req.params.parse(
- req.params,
- );
- const invite = await cPeService.getInviteInfo(peInviteUuid);
- if (!invite)
- throw ApiError.BadRequest("inviteNotFound", "Приглашение не найдено");
- RouterUtils.validAndSendResponse(api.client.pe.GET_InviteInfo.res, res, {
- code: "success",
- invite,
- });
- }
- async acceptInvite(req: Request, res: Response) {
- const user = sessionService.getUserFromReq(req);
- const { peInviteUuid } = api.client.pe.POST_AcceptInvite.req.params.parse(
- req.params,
- );
- const invite = await cPeService.getInviteInfo(peInviteUuid);
- if (!invite) {
- RouterUtils.validAndSendResponse(
- api.client.pe.POST_AcceptInvite.res,
- res,
- {
- code: "inviteNotFound",
- },
- 400,
- );
- return;
- }
- if (invite.limitVal && invite.countVal >= invite.limitVal) {
- RouterUtils.validAndSendResponse(
- api.client.pe.POST_AcceptInvite.res,
- res,
- {
- code: "inviteLimitExceeded",
- },
- 400,
- );
- return;
- }
- // TODO: много лишних данных
- const peMembers = await cPeService.getMembers(invite.peId);
- const isFound = peMembers.find((m) => m.userId === user.userId);
- if (isFound) {
- RouterUtils.validAndSendResponse(
- api.client.pe.POST_AcceptInvite.res,
- res,
- {
- code: "peMemberAlreadyExists",
- },
- 400,
- );
- return;
- }
- await updPool.transaction(async (t) => {
- t.query(sql.unsafe`
- insert into act.pe_members
- (pe_member_id, pe_id, user_id)
- values
- (${v7()}, ${invite.peId}, ${user.userId})
- `);
- t.query(sql.unsafe`
- update act.pe_invites
- set
- count_val = count_val + 1
- where
- pe_id = ${invite.peId}
- `);
- });
- RouterUtils.validAndSendResponse(api.client.pe.POST_AcceptInvite.res, res, {
- code: "success",
- peId: invite.peId,
- });
- }
- async getMyPesForActivity(req: Request, res: Response) {
- const user = sessionService.getUserFromReq(req);
- const event = await sessionService.getCurrentEventFromReq(req);
- const pes = await selPool.any(sql.type(apiTypes.activities.PeForActivity)`
- select
- pe.pe_id "peId",
- pe.event_inst_id "eventInstId",
- pe.owner_id "ownerId",
- pe.pe_type_id "peTypeId",
- pt.code "peTypeCode",
- pt."name" "peTypeName",
- pe."name",
- coalesce(m.members, '[]'::jsonb) members,
- v.fields
- from
- act.part_entities pe
- join act.pe_types pt on
- pt.pe_type_id = pe.pe_type_id
- -- members
- left join lateral (
- select
- jsonb_agg(jsonb_build_object(
- 'peMemberId', m.pe_member_id,
- 'userId', m.user_id,
- 'email', m.email,
- 'fields', m.fields
- )) as members
- from
- act.pe_members_with_fields_and_values m
- where
- m.pe_id = pe.pe_id
- ) m on
- true
- -- fields
- left join act.pe_with_fields_and_values v on
- v.pe_id = pe.pe_id
- where
- pe.event_inst_id = ${event.eventInstId}
- and pe.owner_id = ${user.userId}
- `);
- RouterUtils.validAndSendResponse(
- api.client.pe.GET_MyPesForActivity.res,
- res,
- {
- code: "success",
- pes: [...pes],
- },
- );
- }
- }
- export const clientPeController = new ClientPeController();
|