changemaker.lite/api/src/modules/settings/settings.routes.ts
bunker-admin a77306fac2 Initial v2 commit: complete rebuild with unified API + React admin
Phase 1-14 complete:
- Unified Express.js API (TypeScript, Prisma ORM, PostgreSQL 16)
- React Admin GUI (Vite + Ant Design + Zustand)
- JWT auth with refresh tokens
- Influence: Campaigns, Representatives, Responses, Email Queue
- Map: Locations, Cuts, Shifts, Canvassing System
- NAR data import infrastructure (2025 format)
- Listmonk newsletter integration
- Landing page builder (GrapesJS)
- MkDocs + Code Server integration
- Volunteer portal with GPS tracking
- Monitoring stack (Prometheus, Grafana, Alertmanager)
- Pangolin tunnel integration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 10:05:04 -07:00

110 lines
3.4 KiB
TypeScript

import { Router, Request, Response, NextFunction } from 'express';
import { UserRole } from '@prisma/client';
import { siteSettingsService } from './settings.service';
import { updateSiteSettingsSchema } from './settings.schemas';
import { validate } from '../../middleware/validate';
import { authenticate } from '../../middleware/auth.middleware';
import { requireRole } from '../../middleware/rbac.middleware';
import { emailService } from '../../services/email.service';
const router = Router();
// GET /api/settings — public (needed by login page + public pages), strips SMTP credentials
router.get(
'/',
async (_req: Request, res: Response, next: NextFunction) => {
try {
const settings = await siteSettingsService.getPublic();
res.json(settings);
} catch (err) {
next(err);
}
}
);
// GET /api/settings/admin — SUPER_ADMIN only, returns full settings including SMTP
router.get(
'/admin',
authenticate,
requireRole(UserRole.SUPER_ADMIN),
async (_req: Request, res: Response, next: NextFunction) => {
try {
const settings = await siteSettingsService.get();
res.json(settings);
} catch (err) {
next(err);
}
}
);
// POST /api/settings/email/test-connection — SUPER_ADMIN only
router.post(
'/email/test-connection',
authenticate,
requireRole(UserRole.SUPER_ADMIN),
async (_req: Request, res: Response, next: NextFunction) => {
try {
const success = await emailService.testConnection();
res.json({ success, message: success ? 'SMTP connection verified' : 'SMTP connection failed' });
} catch (err) {
next(err);
}
}
);
// POST /api/settings/email/test-send — SUPER_ADMIN only
router.post(
'/email/test-send',
authenticate,
requireRole(UserRole.SUPER_ADMIN),
async (req: Request, res: Response, next: NextFunction) => {
try {
const { to } = req.body as { to?: string };
const settings = await siteSettingsService.get();
const recipient = to || settings.testEmailRecipient || 'admin@cmlite.org';
const result = await emailService.sendEmail({
to: recipient,
subject: 'Changemaker Lite — Test Email',
html: `<h2>SMTP Test Successful</h2><p>This email confirms that your SMTP configuration is working correctly.</p><p>Sent at: ${new Date().toISOString()}</p>`,
text: `SMTP Test Successful\n\nThis email confirms that your SMTP configuration is working correctly.\n\nSent at: ${new Date().toISOString()}`,
});
res.json({
success: result.success,
messageId: result.messageId,
testMode: result.testMode,
recipient,
});
} catch (err) {
next(err);
}
}
);
// PUT /api/settings — SUPER_ADMIN only
router.put(
'/',
authenticate,
requireRole(UserRole.SUPER_ADMIN),
validate(updateSiteSettingsSchema),
async (req: Request, res: Response, next: NextFunction) => {
try {
const settings = await siteSettingsService.update(req.body);
// If SMTP-related fields were updated, rebuild the transporter
const smtpFields = ['smtpHost', 'smtpPort', 'smtpUser', 'smtpPass', 'smtpFromAddress', 'smtpActiveProvider', 'emailTestMode', 'testEmailRecipient'];
const hasSmtpChanges = smtpFields.some((f) => f in req.body);
if (hasSmtpChanges) {
await emailService.rebuildTransporter();
}
res.json(settings);
} catch (err) {
next(err);
}
}
);
export { router as siteSettingsRouter };