const express = require('express'); const passport = require('passport'); const db = require('../db/models'); const Project_ui_control_settingsService = require('../services/project_ui_control_settings'); const Project_ui_control_settingsDBApi = require('../db/api/project_ui_control_settings'); const { wrapAsync, commonErrorHandler, isUuidV4 } = require('../helpers'); const { checkCrudPermissions } = require('../middlewares/check-permissions'); const RuntimePresentationAccessService = require('../services/runtime-presentation-access'); const router = express.Router(); const jwtAuth = passport.authenticate('jwt', { session: false }); const allowAuthenticatedRead = (req, _res, next) => { if (['GET', 'OPTIONS'].includes(req.method)) { req.isRuntimePublicRequest = true; } return next(); }; const getRuntimeProjectSlug = async (req) => { if (req.runtimeContext?.headerProjectSlug) { return req.runtimeContext.headerProjectSlug; } if (!isUuidV4(req.params.projectId)) { return null; } const project = await db.projects.findByPk(req.params.projectId, { attributes: ['slug'], }); return project?.slug || null; }; const requireProductionOrAuth = async (req, res, next) => { const { environment } = req.params; const isProduction = environment === 'production'; const isReadOnly = ['GET', 'OPTIONS'].includes(req.method); let runtimeProjectSlug = null; let isPrivateProductionPresentation = false; try { runtimeProjectSlug = await getRuntimeProjectSlug(req); isPrivateProductionPresentation = await RuntimePresentationAccessService.isPrivateProductionPresentation( runtimeProjectSlug, ); } catch (error) { return next(error); } if (isProduction && isReadOnly && !isPrivateProductionPresentation) { return next(); } if (isProduction && isReadOnly && isPrivateProductionPresentation) { return passport.authenticate( 'jwt', { session: false }, async (error, user) => { if (error) return next(error); if (!user) { return res.status(401).send({ message: 'Authentication required' }); } req.currentUser = user; const canAccess = await RuntimePresentationAccessService.canUserAccessPrivateProductionPresentation( user, runtimeProjectSlug, ); if (!canAccess) { return res .status(403) .send({ message: 'Presentation access denied' }); } req.isRuntimePublicRequest = true; return next(); }, )(req, res, next); } return jwtAuth(req, res, next); }; router.use(allowAuthenticatedRead); router.use(checkCrudPermissions('project_ui_control_settings')); router.get( '/project/:projectId/env/:environment', requireProductionOrAuth, wrapAsync(async (req, res) => { const { projectId, environment } = req.params; if (!isUuidV4(projectId)) { return res.status(400).send({ message: 'Invalid project ID' }); } if (!['dev', 'stage', 'production'].includes(environment)) { return res.status(400).send({ message: 'Invalid environment' }); } const settings = await Project_ui_control_settingsService.findByProjectAndEnvironment( projectId, environment, req.currentUser, ); res.status(200).send(settings); }), ); router.put( '/project/:projectId/env/:environment', jwtAuth, wrapAsync(async (req, res) => { const { projectId, environment } = req.params; if (!isUuidV4(projectId)) { return res.status(400).send({ message: 'Invalid project ID' }); } if (!['dev', 'stage', 'production'].includes(environment)) { return res.status(400).send({ message: 'Invalid environment' }); } const settings = await Project_ui_control_settingsService.upsertForProject( projectId, environment, req.body.data || {}, req.currentUser, ); res.status(200).send(settings); }), ); router.delete( '/project/:projectId/env/:environment', jwtAuth, wrapAsync(async (req, res) => { const { projectId, environment } = req.params; if (!isUuidV4(projectId)) { return res.status(400).send({ message: 'Invalid project ID' }); } if (!['dev', 'stage', 'production'].includes(environment)) { return res.status(400).send({ message: 'Invalid environment' }); } const settings = await Project_ui_control_settingsService.findByProjectAndEnvironment( projectId, environment, req.currentUser, ); if (settings) { await Project_ui_control_settingsService.remove({ id: settings.id, currentUser: req.currentUser, runtimeContext: req.runtimeContext, }); } res.status(200).send({ success: true }); }), ); router.get( '/', jwtAuth, wrapAsync(async (req, res) => { const payload = await Project_ui_control_settingsDBApi.findAll(req.query); res.status(200).send(payload); }), ); router.use('/', commonErrorHandler); module.exports = router;