Files
TX/artifacts/api-server/src/routes/apps.ts
T

173 lines
4.4 KiB
TypeScript
Raw Normal View History

import { Router, type IRouter } from "express";
import { eq, asc, inArray } from "drizzle-orm";
import { db } from "@workspace/db";
import {
appsTable,
appPermissionsTable,
userRolesTable,
rolePermissionsTable,
rolesTable,
} from "@workspace/db";
import { requireAuth, requireAdmin } from "../middlewares/auth";
import {
CreateAppBody,
UpdateAppBody,
GetAppParams,
UpdateAppParams,
DeleteAppParams,
} from "@workspace/api-zod";
const router: IRouter = Router();
router.get("/apps", requireAuth, async (req, res): Promise<void> => {
const userId = req.session.userId!;
const userRoleRows = await db
.select({ roleName: rolesTable.name, roleId: userRolesTable.roleId })
.from(userRolesTable)
.innerJoin(rolesTable, eq(userRolesTable.roleId, rolesTable.id))
.where(eq(userRolesTable.userId, userId));
const isAdmin = userRoleRows.some((r) => r.roleName === "admin");
if (isAdmin) {
const apps = await db
.select()
.from(appsTable)
.where(eq(appsTable.isActive, true))
.orderBy(asc(appsTable.sortOrder), asc(appsTable.nameEn));
res.json(apps);
return;
}
const userRoleIds = userRoleRows.map((r) => r.roleId);
const userPermissionIds = userRoleIds.length > 0
? (await db
.select({ permissionId: rolePermissionsTable.permissionId })
.from(rolePermissionsTable)
.where(inArray(rolePermissionsTable.roleId, userRoleIds))
).map((r) => r.permissionId)
: [];
const apps = await db
.select()
.from(appsTable)
.where(
eq(appsTable.isActive, true),
)
.orderBy(asc(appsTable.sortOrder), asc(appsTable.nameEn));
const appsWithPermissions = await db
.select({ appId: appPermissionsTable.appId })
.from(appPermissionsTable);
const restrictedAppIds = new Set(appsWithPermissions.map((r) => r.appId));
if (restrictedAppIds.size === 0) {
const unrestricted = await db
.select()
.from(appsTable)
.where(eq(appsTable.isActive, true))
.orderBy(asc(appsTable.sortOrder), asc(appsTable.nameEn));
res.json(unrestricted);
return;
}
const allowedRestrictedAppIds = userPermissionIds.length > 0
? (await db
.select({ appId: appPermissionsTable.appId })
.from(appPermissionsTable)
.where(inArray(appPermissionsTable.permissionId, userPermissionIds))
).map((r) => r.appId)
: [];
const allowedAppIdSet = new Set(allowedRestrictedAppIds);
const visibleApps = apps.filter(
(app) => !restrictedAppIds.has(app.id) || allowedAppIdSet.has(app.id),
);
res.json(visibleApps);
});
router.post("/apps", requireAdmin, async (req, res): Promise<void> => {
const parsed = CreateAppBody.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: parsed.error.message });
return;
}
const [app] = await db.insert(appsTable).values(parsed.data).returning();
res.status(201).json(app);
});
router.get("/apps/:id", requireAuth, async (req, res): Promise<void> => {
const params = GetAppParams.safeParse(req.params);
if (!params.success) {
res.status(400).json({ error: params.error.message });
return;
}
const [app] = await db
.select()
.from(appsTable)
.where(eq(appsTable.id, params.data.id));
if (!app) {
res.status(404).json({ error: "App not found" });
return;
}
res.json(app);
});
router.patch("/apps/:id", requireAdmin, async (req, res): Promise<void> => {
const params = UpdateAppParams.safeParse(req.params);
if (!params.success) {
res.status(400).json({ error: params.error.message });
return;
}
const parsed = UpdateAppBody.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: parsed.error.message });
return;
}
const [app] = await db
.update(appsTable)
.set(parsed.data)
.where(eq(appsTable.id, params.data.id))
.returning();
if (!app) {
res.status(404).json({ error: "App not found" });
return;
}
res.json(app);
});
router.delete("/apps/:id", requireAdmin, async (req, res): Promise<void> => {
const params = DeleteAppParams.safeParse(req.params);
if (!params.success) {
res.status(400).json({ error: params.error.message });
return;
}
const [app] = await db
.delete(appsTable)
.where(eq(appsTable.id, params.data.id))
.returning();
if (!app) {
res.status(404).json({ error: "App not found" });
return;
}
res.sendStatus(204);
});
export default router;