Upgrade system finished

This commit is contained in:
2026-03-22 21:47:09 -06:00
parent a71ba20176
commit bb1935027d
299 changed files with 8067 additions and 2758 deletions

View File

@@ -1 +1 @@
{"version":3,"file":"responses.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/influence/responses/responses.routes.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,oBAAoB,4CAAW,CAAC;AAiDtC,QAAA,MAAM,qBAAqB,4CAAW,CAAC;AA6EvC,QAAA,MAAM,oBAAoB,4CAAW,CAAC;AA6DtC,OAAO,EAAE,oBAAoB,IAAI,4BAA4B,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC"}
{"version":3,"file":"responses.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/influence/responses/responses.routes.ts"],"names":[],"mappings":"AAgBA,QAAA,MAAM,oBAAoB,4CAAW,CAAC;AAiDtC,QAAA,MAAM,qBAAqB,4CAAW,CAAC;AA6EvC,QAAA,MAAM,oBAAoB,4CAAW,CAAC;AA6DtC,OAAO,EAAE,oBAAoB,IAAI,4BAA4B,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC"}

View File

@@ -2,7 +2,6 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.responsesAdminRouter = exports.responsesPublicRouter = exports.responseCampaignPublicRouter = void 0;
const express_1 = require("express");
const client_1 = require("@prisma/client");
const responses_service_1 = require("./responses.service");
const responses_schemas_1 = require("./responses.schemas");
const validate_1 = require("../../../middleware/validate");
@@ -10,7 +9,7 @@ const auth_middleware_1 = require("../../../middleware/auth.middleware");
const auth_middleware_2 = require("../../../middleware/auth.middleware");
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
const rate_limit_1 = require("../../../middleware/rate-limit");
const ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.INFLUENCE_ADMIN, client_1.UserRole.MAP_ADMIN];
const roles_1 = require("../../../utils/roles");
// --- Campaign-scoped public routes (mount at /api/campaigns) ---
const campaignPublicRouter = (0, express_1.Router)();
exports.responseCampaignPublicRouter = campaignPublicRouter;
@@ -111,7 +110,7 @@ responsesPublicRouter.get('/:id/report/:token', async (req, res, next) => {
const responsesAdminRouter = (0, express_1.Router)();
exports.responsesAdminRouter = responsesAdminRouter;
responsesAdminRouter.use(auth_middleware_1.authenticate);
responsesAdminRouter.use((0, rbac_middleware_1.requireRole)(...ADMIN_ROLES));
responsesAdminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.INFLUENCE_ROLES));
// GET /api/responses
responsesAdminRouter.get('/', (0, validate_1.validate)(responses_schemas_1.listAdminResponsesSchema, 'query'), async (req, res, next) => {
try {

File diff suppressed because one or more lines are too long

View File

@@ -82,11 +82,11 @@ export declare const listAdminResponsesSchema: z.ZodObject<{
}, "strip", z.ZodTypeAny, {
limit: number;
page: number;
status?: "PENDING" | "APPROVED" | "REJECTED" | undefined;
status?: "APPROVED" | "PENDING" | "REJECTED" | undefined;
search?: string | undefined;
campaignId?: string | undefined;
}, {
status?: "PENDING" | "APPROVED" | "REJECTED" | undefined;
status?: "APPROVED" | "PENDING" | "REJECTED" | undefined;
search?: string | undefined;
limit?: number | undefined;
page?: number | undefined;
@@ -99,9 +99,9 @@ export declare const updateResponseStatusSchema: z.ZodObject<{
REJECTED: "REJECTED";
}>;
}, "strip", z.ZodTypeAny, {
status: "PENDING" | "APPROVED" | "REJECTED";
status: "APPROVED" | "PENDING" | "REJECTED";
}, {
status: "PENDING" | "APPROVED" | "REJECTED";
status: "APPROVED" | "PENDING" | "REJECTED";
}>;
export type SubmitResponseInput = z.infer<typeof submitResponseSchema>;
export type ListPublicResponsesInput = z.infer<typeof listPublicResponsesSchema>;

View File

@@ -4,14 +4,14 @@ exports.updateResponseStatusSchema = exports.listAdminResponsesSchema = exports.
const zod_1 = require("zod");
const client_1 = require("@prisma/client");
exports.submitResponseSchema = zod_1.z.object({
representativeName: zod_1.z.string().min(1, 'Representative name is required'),
representativeName: zod_1.z.string().min(1, 'Representative name is required').max(200),
representativeLevel: zod_1.z.nativeEnum(client_1.GovernmentLevel),
responseType: zod_1.z.nativeEnum(client_1.ResponseType),
responseText: zod_1.z.string().min(1, 'Response text is required'),
representativeTitle: zod_1.z.string().optional(),
responseText: zod_1.z.string().min(1, 'Response text is required').max(5000),
representativeTitle: zod_1.z.string().max(200).optional(),
representativeEmail: zod_1.z.string().email().optional(),
userComment: zod_1.z.string().optional(),
submittedByName: zod_1.z.string().optional(),
userComment: zod_1.z.string().max(1000).optional(),
submittedByName: zod_1.z.string().max(200).optional(),
submittedByEmail: zod_1.z.string().email().optional(),
isAnonymous: zod_1.z.boolean().optional().default(false),
sendVerification: zod_1.z.boolean().optional().default(false),

View File

@@ -1 +1 @@
{"version":3,"file":"responses.schemas.js","sourceRoot":"","sources":["../../../../src/modules/influence/responses/responses.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,2CAA+E;AAElE,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,kBAAkB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,iCAAiC,CAAC;IACxE,mBAAmB,EAAE,OAAC,CAAC,UAAU,CAAC,wBAAe,CAAC;IAClD,YAAY,EAAE,OAAC,CAAC,UAAU,CAAC,qBAAY,CAAC;IACxC,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,2BAA2B,CAAC;IAC5D,mBAAmB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1C,mBAAmB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAClD,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAC/C,WAAW,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAClD,gBAAgB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACxD,CAAC,CAAC;AAEU,QAAA,yBAAyB,GAAG,OAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACnD,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9D,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC5E,KAAK,EAAE,OAAC,CAAC,UAAU,CAAC,wBAAe,CAAC,CAAC,QAAQ,EAAE;CAChD,CAAC,CAAC;AAEU,QAAA,wBAAwB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC/C,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACnD,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9D,MAAM,EAAE,OAAC,CAAC,UAAU,CAAC,uBAAc,CAAC,CAAC,QAAQ,EAAE;IAC/C,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAC;AAEU,QAAA,0BAA0B,GAAG,OAAC,CAAC,MAAM,CAAC;IACjD,MAAM,EAAE,OAAC,CAAC,UAAU,CAAC,uBAAc,CAAC;CACrC,CAAC,CAAC"}
{"version":3,"file":"responses.schemas.js","sourceRoot":"","sources":["../../../../src/modules/influence/responses/responses.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,2CAA+E;AAElE,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,kBAAkB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,iCAAiC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACjF,mBAAmB,EAAE,OAAC,CAAC,UAAU,CAAC,wBAAe,CAAC;IAClD,YAAY,EAAE,OAAC,CAAC,UAAU,CAAC,qBAAY,CAAC;IACxC,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,2BAA2B,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IACtE,mBAAmB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IACnD,mBAAmB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAClD,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAC5C,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAC/C,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAC/C,WAAW,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAClD,gBAAgB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACxD,CAAC,CAAC;AAEU,QAAA,yBAAyB,GAAG,OAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACnD,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9D,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC5E,KAAK,EAAE,OAAC,CAAC,UAAU,CAAC,wBAAe,CAAC,CAAC,QAAQ,EAAE;CAChD,CAAC,CAAC;AAEU,QAAA,wBAAwB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC/C,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACnD,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9D,MAAM,EAAE,OAAC,CAAC,UAAU,CAAC,uBAAc,CAAC,CAAC,QAAQ,EAAE;IAC/C,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAC;AAEU,QAAA,0BAA0B,GAAG,OAAC,CAAC,MAAM,CAAC;IACjD,MAAM,EAAE,OAAC,CAAC,UAAU,CAAC,uBAAc,CAAC;CACrC,CAAC,CAAC"}

View File

@@ -100,8 +100,9 @@ export declare const responsesService: {
createdAt: Date;
updatedAt: Date;
upvoteCount: number;
campaignSlug: string;
submittedByUserId: string | null;
campaignId: string;
campaignSlug: string;
representativeName: string;
representativeTitle: string | null;
representativeLevel: import(".prisma/client").$Enums.GovernmentLevel;
@@ -119,7 +120,6 @@ export declare const responsesService: {
verifiedAt: Date | null;
verifiedBy: string | null;
submittedIp: string | null;
submittedByUserId: string | null;
}>;
deleteResponse(id: string): Promise<void>;
resendVerification(id: string): Promise<{

View File

@@ -1 +1 @@
{"version":3,"file":"responses.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/influence/responses/responses.service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAI7B,eAAO,MAAM,gBAAgB;yBAGA,MAAM,QAAQ,mBAAmB,aAAa,MAAM;;;;;uBAmEtD,MAAM,WAAW,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;mBA0D7C,MAAM;;;;;;uBA2BF,MAAM,WAAW,MAAM,WAAW,MAAM;;;;;;;6BAgClC,MAAM,WAAW,MAAM,WAAW,MAAM;;;uBAwB9C,MAAM,SAAS,MAAM;;;;;;;;;uBAqCrB,MAAM,SAAS,MAAM;;;;;;;;;qBA6BvB,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAwDvB,MAAM,QAAQ,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAUrC,MAAM;2BAOF,MAAM;;;CA6CpC,CAAC"}
{"version":3,"file":"responses.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/influence/responses/responses.service.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EAC1B,MAAM,qBAAqB,CAAC;AAI7B,eAAO,MAAM,gBAAgB;yBAGA,MAAM,QAAQ,mBAAmB,aAAa,MAAM;;;;;uBA8FtD,MAAM,WAAW,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;mBA0D7C,MAAM;;;;;;uBA2BF,MAAM,WAAW,MAAM,WAAW,MAAM;;;;;;;6BAgClC,MAAM,WAAW,MAAM,WAAW,MAAM;;;uBAwB9C,MAAM,SAAS,MAAM;;;;;;;;;uBAqCrB,MAAM,SAAS,MAAM;;;;;;;;;qBA6BvB,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAwDvB,MAAM,QAAQ,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAUrC,MAAM;2BAOF,MAAM;;;CA6CpC,CAAC"}

View File

@@ -6,8 +6,12 @@ const client_1 = require("@prisma/client");
const database_1 = require("../../../config/database");
const error_handler_1 = require("../../../middleware/error-handler");
const email_service_1 = require("../../../services/email.service");
const notification_queue_service_1 = require("../../../services/notification-queue.service");
const notification_helper_1 = require("../../../services/notification.helper");
const env_1 = require("../../../config/env");
const logger_1 = require("../../../utils/logger");
const metrics_1 = require("../../../utils/metrics");
const rocketchat_webhook_service_1 = require("../../../services/rocketchat-webhook.service");
const VERIFICATION_EXPIRY_DAYS = 30;
exports.responsesService = {
// --- Public ---
@@ -64,6 +68,32 @@ exports.responsesService = {
});
}
(0, metrics_1.recordResponseSubmission)();
// Notification: admin response submitted alert
try {
if (await (0, notification_helper_1.isNotificationEnabled)('notifyAdminResponseSubmitted')) {
const adminEmails = await (0, notification_helper_1.getAdminEmailsByRole)([client_1.UserRole.SUPER_ADMIN, client_1.UserRole.INFLUENCE_ADMIN]);
if (adminEmails.length > 0) {
const adminUrl = `${env_1.env.ADMIN_URL || 'http://localhost:3000'}/app/influence/responses`;
await notification_queue_service_1.notificationQueueService.enqueue({
type: 'admin-response-submitted',
adminEmails,
campaignTitle: campaign.title,
representativeName: data.representativeName,
responseType: data.responseType,
submitterName: data.isAnonymous ? 'Anonymous' : (data.submittedByName || 'Anonymous'),
adminUrl,
});
}
}
}
catch (err) {
logger_1.logger.error('Failed to enqueue response submitted notification:', err);
}
// Notify Rocket.Chat
rocketchat_webhook_service_1.rocketchatWebhookService.onCampaignResponseSubmitted({
campaignTitle: campaign.title,
representativeName: data.representativeName,
}).catch(() => { });
return {
id: response.id,
status: response.status,

File diff suppressed because one or more lines are too long