From 827b018f250878504ff0eb3c07b726348c83a145 Mon Sep 17 00:00:00 2001 From: Jackson Roberts Date: Wed, 25 Feb 2026 23:00:51 -0600 Subject: [PATCH] auto-create admin role on startup, use API_BASE_URL for auth redirects --- package.json | 3 ++- src/api/auth/redirect.ts | 4 ++-- src/api/auth/uri.ts | 4 +++- src/constants.ts | 2 ++ src/index.ts | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f0f017b..4a515ec 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "utils:dev": "docker compose -f .docker/docker-compose.yml up --build", "utils:gen_private_keys": "bun ./utils/genPrivateKeys", "utils:create_admin_role": "bun ./utils/createAdminRole", - "utils:assign_user_role": "bun ./utils/assignUserRole" + "utils:assign_user_role": "bun ./utils/assignUserRole", + "db:check": "bunx prisma migrate diff --from-migrations prisma/migrations --to-schema prisma/schema.prisma --shadow-database-url $DATABASE_URL --exit-code" }, "dependencies": { "@azure/msal-node": "^5.0.2", diff --git a/src/api/auth/redirect.ts b/src/api/auth/redirect.ts index 6bb9cb3..6ac0063 100644 --- a/src/api/auth/redirect.ts +++ b/src/api/auth/redirect.ts @@ -1,7 +1,7 @@ import { Hono } from "hono/tiny"; import { createRoute } from "../../modules/api-utils/createRoute"; import * as msal from "@azure/msal-node"; -import { io, msalClient } from "../../constants"; +import { API_BASE_URL, io, msalClient } from "../../constants"; import { users } from "../../managers/users"; /* /v1/auth/redirect */ @@ -11,7 +11,7 @@ export default createRoute("get", ["/redirect"], async (c) => { const tokenRequest: msal.AuthorizationCodeRequest = { code: c.req.query().code as string, scopes: ["user.read"], - redirectUri: "http://localhost:3000/v1/auth/redirect", + redirectUri: `${API_BASE_URL}/v1/auth/redirect`, }; const authResult = await msalClient.acquireTokenByCode(tokenRequest); diff --git a/src/api/auth/uri.ts b/src/api/auth/uri.ts index ea55cf8..e559d37 100644 --- a/src/api/auth/uri.ts +++ b/src/api/auth/uri.ts @@ -1,5 +1,6 @@ import { Hono } from "hono/tiny"; import { createRoute } from "../../modules/api-utils/createRoute"; +import { API_BASE_URL } from "../../constants"; import cuid from "cuid"; /* /v1/auth/uri */ @@ -7,7 +8,8 @@ export default createRoute("get", ["/uri"], (c) => { c.status(200); const callbackKey = cuid(); - const msUri = `https://login.microsoftonline.com/${process.env.MICROSOFT_TENANT_ID}/oauth2/v2.0/authorize?client_id=${process.env.MICROSOFT_CLIENT_ID}&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fv1%2Fauth%2Fredirect&scope=openid+User.Read&state=${callbackKey}&prompt=login`; + const redirectUri = encodeURIComponent(`${API_BASE_URL}/v1/auth/redirect`); + const msUri = `https://login.microsoftonline.com/${process.env.MICROSOFT_TENANT_ID}/oauth2/v2.0/authorize?client_id=${process.env.MICROSOFT_CLIENT_ID}&response_type=code&redirect_uri=${redirectUri}&scope=openid+User.Read&state=${callbackKey}&prompt=login`; return c.json({ status: 200, diff --git a/src/constants.ts b/src/constants.ts index 9c91465..ec42fb4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -17,6 +17,8 @@ interface EnvKey { // ENV CONSTANTS export const PORT = process.env.PORT; +export const API_BASE_URL = + process.env.API_BASE_URL || `http://localhost:${PORT || 3000}`; export const prisma = new PrismaClient({ adapter }); diff --git a/src/index.ts b/src/index.ts index f79a1b2..d01791e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,49 @@ import { refresh } from "./api/auth"; import app from "./api/server"; -import { engine, PORT, unifi, unifiPassword, unifiUsername } from "./constants"; +import { + engine, + PORT, + prisma, + unifi, + unifiPassword, + unifiUsername, +} from "./constants"; import { unifiSites } from "./managers/unifiSites"; import { refreshCompanies } from "./modules/cw-utils/refreshCompanies"; import { refreshCatalog } from "./modules/cw-utils/procurement/refreshCatalog"; import { refreshInventory } from "./modules/cw-utils/procurement/refreshInventory"; import { events, setupEventDebugger } from "./modules/globalEvents"; +import { signPermissions } from "./modules/permission-utils/signPermissions"; +import { RoleController } from "./controllers/RoleController"; +import cuid from "cuid"; // Setup global event debugger in non-production environments if (Bun.env.NODE_ENV == "development") setupEventDebugger(); +// Ensure administrator role exists +const existingAdmin = await prisma.role.findFirst({ + where: { moniker: "administrator" }, + include: { users: { include: { roles: true } } }, +}); + +if (!existingAdmin) { + const id = cuid(); + const created = await prisma.role.create({ + data: { + id, + moniker: "administrator", + title: "Admin", + permissions: signPermissions({ + issuer: "roles", + subject: id, + permissions: ["*"], + }), + }, + include: { users: { include: { roles: true } } }, + }); + events.emit("role:created", new RoleController(created)); +} + // Refresh the internal list of companies every minute await refreshCompanies(); setInterval(() => {