From 5d5cee2d7e8eb27fbca69ada414da05f19bcf14e Mon Sep 17 00:00:00 2001 From: Akhil Meka Date: Tue, 18 Feb 2025 15:10:51 +0530 Subject: [PATCH] add config routes --- src/config/config.controller.ts | 38 ++++++++++++++++ src/config/config.route.ts | 39 +++++++++++++++++ src/config/config.schema.ts | 33 ++++++++++++++ src/config/config.service.ts | 77 +++++++++++++++++++++++++++++++++ src/routes.ts | 2 + src/server.ts | 2 + src/utils/claims.ts | 4 +- src/utils/roles.ts | 10 ++++- 8 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 src/config/config.controller.ts create mode 100644 src/config/config.route.ts create mode 100644 src/config/config.schema.ts create mode 100644 src/config/config.service.ts diff --git a/src/config/config.controller.ts b/src/config/config.controller.ts new file mode 100644 index 0000000..e7f357c --- /dev/null +++ b/src/config/config.controller.ts @@ -0,0 +1,38 @@ +import { FastifyReply, FastifyRequest } from "fastify"; +import { getConfig, resetConfig, updateConfig } from "./config.service"; +import { UpdateConfigInput } from "./config.schema"; + +export async function getConfigHandler(req: FastifyRequest, res: FastifyReply) { + try { + const config = await getConfig(req.user); + return res.code(200).send(config); + } catch (err) { + return err; + } +} + +export async function updateConfigHandler( + req: FastifyRequest, + res: FastifyReply +) { + const input = req.body as UpdateConfigInput; + + try { + const updatedConfig = await updateConfig(input, req.user); + return res.code(200).send(updatedConfig); + } catch (err) { + return err; + } +} + +export async function resetConfigHandler( + req: FastifyRequest, + res: FastifyReply +) { + try { + const newConfig = await resetConfig(req.user); + return res.code(200).send(newConfig); + } catch (err) { + return err; + } +} diff --git a/src/config/config.route.ts b/src/config/config.route.ts new file mode 100644 index 0000000..ee2583e --- /dev/null +++ b/src/config/config.route.ts @@ -0,0 +1,39 @@ +import { FastifyInstance } from "fastify"; +import { + getConfigHandler, + resetConfigHandler, + updateConfigHandler, +} from "./config.controller"; +import { $config } from "./config.schema"; + +export async function configRoutes(fastify: FastifyInstance) { + fastify.get( + "/", + { + config: { requiredClaims: ["config:read"] }, + preHandler: [fastify.authorize], + }, + getConfigHandler + ); + + fastify.patch( + "/", + { + schema: { + body: $config("updateConfigInput"), + }, + config: { requiredClaims: ["config:write"] }, + preHandler: [fastify.authorize], + }, + updateConfigHandler + ); + + fastify.post( + "/reset", + { + config: { requiredClaims: ["config:write"] }, + preHandler: [fastify.authorize], + }, + resetConfigHandler + ); +} diff --git a/src/config/config.schema.ts b/src/config/config.schema.ts new file mode 100644 index 0000000..850668a --- /dev/null +++ b/src/config/config.schema.ts @@ -0,0 +1,33 @@ +import { buildJsonSchemas } from "fastify-zod"; +import mongoose from "mongoose"; +import { z } from "zod"; + +export const configModel = mongoose.model( + "config", + new mongoose.Schema({ + tenantId: { + type: String, + unique: true, + }, + statusMap: Object, + updatedAt: Date, + updatedBy: { + type: mongoose.Types.ObjectId, + ref: "user", + }, + }), + "config" +); + +const updateConfigInput = z.object({ + statusMap: z.record(z.string(), z.array(z.string())).optional(), +}); + +export type UpdateConfigInput = z.infer; + +export const { schemas: configSchemas, $ref: $config } = buildJsonSchemas( + { + updateConfigInput, + }, + { $id: "config" } +); diff --git a/src/config/config.service.ts b/src/config/config.service.ts new file mode 100644 index 0000000..1c4e960 --- /dev/null +++ b/src/config/config.service.ts @@ -0,0 +1,77 @@ +import { AuthenticatedUser } from "../auth"; +import { configModel, UpdateConfigInput } from "./config.schema"; + +export async function getConfig(user: AuthenticatedUser) { + return await configModel + .findOne({ tenantId: user.tenantId }) + .populate({ path: "updatedBy", select: "pid name avatar" }); +} + +export async function updateConfig( + input: UpdateConfigInput, + user: AuthenticatedUser +) { + return await configModel + .findOneAndUpdate( + { tenantId: user.tenantId }, + { + updatedAt: new Date(), + updatedBy: user.userId, + ...input, + }, + { new: true } + ) + .populate({ path: "updatedBy", select: "pid name avatar" }); +} + +export async function resetConfig(user: AuthenticatedUser) { + return await configModel + .findOneAndUpdate( + { tenantId: user.tenantId }, + { + statusMap: { + Submitted: ["Submitted", "New", "Plans Received"], + "In Review": [ + "In Process", + "In Progress", + "In Review", + "Plan Review In Process", + ], + "Awaiting Client Reply": [ + "Rejected", + "Revision", + "Revision Required", + "Revisions Required", + "Review Verification", + "Awaiting Revisions", + "Pending Client Input", + "Pending Additional Review", + "Awaiting Client Reply", + "Ready to Issue", + "Pending Permit Issuance", + "Approved with Conditions", + ], + Issued: ["Permit Issued", "Issued"], + Completed: [ + "Approved", + "Administrative Close", + "Closed", + "Closed - Supp-Rev Approved", + "Closed - Void", + "Closed - Finaled", + "Closed - COC Issued", + "Closed - Withdrawn", + "Closed - CO Issued", + "Complete", + "CO Approved", + ], + Expired: ["Expired"], + "Cancel/Void": ["Cancel", "Canceled", "Withdrawn"], + }, + updatedAt: new Date(), + updatedBy: user.userId, + }, + { new: true } + ) + .populate({ path: "updatedBy", select: "pid name avatar" }); +} diff --git a/src/routes.ts b/src/routes.ts index 3c6fdf7..cf6bc4d 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -9,6 +9,7 @@ import { rtsRoutes } from "./rts/rts.route"; import { taskRoutes } from "./task/task.route"; import { realTimeRoutes } from "./realtime/realtime.route"; import { notificationRoutes } from "./notification/notification.route"; +import { configRoutes } from "./config/config.route"; export default async function routes(fastify: FastifyInstance) { fastify.addHook("preHandler", authHandler); @@ -20,5 +21,6 @@ export default async function routes(fastify: FastifyInstance) { fastify.register(rtsRoutes, { prefix: "/rts" }); fastify.register(taskRoutes, { prefix: "/tasks" }); fastify.register(notificationRoutes, { prefix: "/notifications" }); + fastify.register(configRoutes, { prefix: "/config" }); fastify.register(realTimeRoutes); } diff --git a/src/server.ts b/src/server.ts index de7cff1..fa9d1c5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -17,6 +17,7 @@ import { taskSchemas } from "./task/task.schema"; import { notificationSchemas } from "./notification/notification.schema"; import { noteSchemas } from "./note/note.schema"; import { webAuthnRoutes } from "./webauthn/webauthn.route"; +import { configSchemas } from "./config/config.schema"; const app = fastify({ logger: true, trustProxy: true }); @@ -47,6 +48,7 @@ for (const schema of [ ...taskSchemas, ...notificationSchemas, ...noteSchemas, + ...configSchemas, ]) { app.addSchema(schema); } diff --git a/src/utils/claims.ts b/src/utils/claims.ts index ca240cc..9227bef 100644 --- a/src/utils/claims.ts +++ b/src/utils/claims.ts @@ -21,4 +21,6 @@ export type Claim = | "task:write" | "task:delete" | "notification:read" - | "notification:write"; + | "notification:write" + | "config:read" + | "config:write"; diff --git a/src/utils/roles.ts b/src/utils/roles.ts index 21c131f..34867fb 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -26,6 +26,8 @@ export const rules: Record< "task:delete", "notification:read", "notification:write", + "config:read", + "config:write", ], hiddenFields: { orgs: ["__v"], @@ -36,7 +38,13 @@ export const rules: Record< }, }, builder: { - claims: ["permit:read", "file:upload", "file:download", "org:read"], + claims: [ + "permit:read", + "file:upload", + "file:download", + "org:read", + "config:read", + ], hiddenFields: { orgs: ["__v", "isClient", "name"], permits: ["__v"],