mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(oidc): register cookie and OIDC plugins, extend config endpoint
This commit is contained in:
+38
-3
@@ -1,4 +1,5 @@
|
|||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
import cookie from "@fastify/cookie";
|
||||||
import cors from "@fastify/cors";
|
import cors from "@fastify/cors";
|
||||||
import rateLimit from "@fastify/rate-limit";
|
import rateLimit from "@fastify/rate-limit";
|
||||||
import { getDispatcherStatus, initDispatcher, isGpuAvailable } from "@snapotter/ai";
|
import { getDispatcherStatus, initDispatcher, isGpuAvailable } from "@snapotter/ai";
|
||||||
@@ -15,6 +16,7 @@ import { ensureAiDirs, recoverInterruptedInstalls } from "./lib/feature-status.j
|
|||||||
import { shutdownWorkerPool } from "./lib/worker-pool.js";
|
import { shutdownWorkerPool } from "./lib/worker-pool.js";
|
||||||
import { requirePermission } from "./permissions.js";
|
import { requirePermission } from "./permissions.js";
|
||||||
import { authMiddleware, authRoutes, ensureDefaultAdmin } from "./plugins/auth.js";
|
import { authMiddleware, authRoutes, ensureDefaultAdmin } from "./plugins/auth.js";
|
||||||
|
import { oidcRoutes } from "./plugins/oidc.js";
|
||||||
import { registerStatic } from "./plugins/static.js";
|
import { registerStatic } from "./plugins/static.js";
|
||||||
import { registerUpload } from "./plugins/upload.js";
|
import { registerUpload } from "./plugins/upload.js";
|
||||||
import { analyticsRoutes } from "./routes/analytics.js";
|
import { analyticsRoutes } from "./routes/analytics.js";
|
||||||
@@ -70,6 +72,22 @@ function ensureDefaultSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureDefaultSettings();
|
ensureDefaultSettings();
|
||||||
|
|
||||||
|
if (!env.COOKIE_SECRET) {
|
||||||
|
const existing = db
|
||||||
|
.select()
|
||||||
|
.from(schema.settings)
|
||||||
|
.where(eq(schema.settings.key, "cookie_secret"))
|
||||||
|
.get();
|
||||||
|
if (existing) {
|
||||||
|
(env as Record<string, unknown>).COOKIE_SECRET = existing.value;
|
||||||
|
} else {
|
||||||
|
const generated = randomUUID() + randomUUID();
|
||||||
|
db.insert(schema.settings).values({ key: "cookie_secret", value: generated }).run();
|
||||||
|
(env as Record<string, unknown>).COOKIE_SECRET = generated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await initAnalytics();
|
await initAnalytics();
|
||||||
|
|
||||||
// Mark any jobs left in processing/queued from a previous unclean shutdown
|
// Mark any jobs left in processing/queued from a previous unclean shutdown
|
||||||
@@ -151,12 +169,21 @@ await app.register(rateLimit, {
|
|||||||
// Multipart upload support
|
// Multipart upload support
|
||||||
await registerUpload(app);
|
await registerUpload(app);
|
||||||
|
|
||||||
|
// Cookie support (required for OIDC state and session cookies)
|
||||||
|
await app.register(cookie, {
|
||||||
|
secret: env.COOKIE_SECRET,
|
||||||
|
hook: "onRequest",
|
||||||
|
});
|
||||||
|
|
||||||
// Auth middleware (must be registered before routes it protects)
|
// Auth middleware (must be registered before routes it protects)
|
||||||
await authMiddleware(app);
|
await authMiddleware(app);
|
||||||
|
|
||||||
// Auth routes
|
// Auth routes
|
||||||
await authRoutes(app);
|
await authRoutes(app);
|
||||||
|
|
||||||
|
// OIDC routes
|
||||||
|
await oidcRoutes(app);
|
||||||
|
|
||||||
// File upload/download routes
|
// File upload/download routes
|
||||||
await fileRoutes(app);
|
await fileRoutes(app);
|
||||||
|
|
||||||
@@ -247,9 +274,17 @@ app.get("/api/v1/admin/health", async (request, reply) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Public config endpoint (for frontend to know if auth is required)
|
// Public config endpoint (for frontend to know if auth is required)
|
||||||
app.get("/api/v1/config/auth", async () => ({
|
app.get("/api/v1/config/auth", async () => {
|
||||||
authEnabled: env.AUTH_ENABLED,
|
const config: Record<string, unknown> = {
|
||||||
}));
|
authEnabled: env.AUTH_ENABLED,
|
||||||
|
};
|
||||||
|
if (env.OIDC_ENABLED) {
|
||||||
|
config.oidcEnabled = true;
|
||||||
|
config.oidcProviderName = env.OIDC_PROVIDER_NAME || null;
|
||||||
|
config.oidcLoginUrl = "/api/auth/oidc/login";
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
// Serve SPA in production
|
// Serve SPA in production
|
||||||
if (process.env.NODE_ENV === "production") {
|
if (process.env.NODE_ENV === "production") {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { dirname } from "node:path";
|
|||||||
mkdirSync(dirname(process.env.DB_PATH!), { recursive: true });
|
mkdirSync(dirname(process.env.DB_PATH!), { recursive: true });
|
||||||
mkdirSync(process.env.WORKSPACE_PATH!, { recursive: true });
|
mkdirSync(process.env.WORKSPACE_PATH!, { recursive: true });
|
||||||
|
|
||||||
|
import cookie from "@fastify/cookie";
|
||||||
import cors from "@fastify/cors";
|
import cors from "@fastify/cors";
|
||||||
import { APP_VERSION } from "@snapotter/shared";
|
import { APP_VERSION } from "@snapotter/shared";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
@@ -31,6 +32,7 @@ import { db, schema } from "../../apps/api/src/db/index.js";
|
|||||||
import { runMigrations } from "../../apps/api/src/db/migrate.js";
|
import { runMigrations } from "../../apps/api/src/db/migrate.js";
|
||||||
import { requirePermission } from "../../apps/api/src/permissions.js";
|
import { requirePermission } from "../../apps/api/src/permissions.js";
|
||||||
import { authMiddleware, authRoutes, ensureDefaultAdmin } from "../../apps/api/src/plugins/auth.js";
|
import { authMiddleware, authRoutes, ensureDefaultAdmin } from "../../apps/api/src/plugins/auth.js";
|
||||||
|
import { oidcRoutes } from "../../apps/api/src/plugins/oidc.js";
|
||||||
import { registerUpload } from "../../apps/api/src/plugins/upload.js";
|
import { registerUpload } from "../../apps/api/src/plugins/upload.js";
|
||||||
import { analyticsRoutes } from "../../apps/api/src/routes/analytics.js";
|
import { analyticsRoutes } from "../../apps/api/src/routes/analytics.js";
|
||||||
import { apiKeyRoutes } from "../../apps/api/src/routes/api-keys.js";
|
import { apiKeyRoutes } from "../../apps/api/src/routes/api-keys.js";
|
||||||
@@ -80,12 +82,18 @@ export async function buildTestApp(): Promise<TestApp> {
|
|||||||
// Multipart upload support
|
// Multipart upload support
|
||||||
await registerUpload(app);
|
await registerUpload(app);
|
||||||
|
|
||||||
|
// Cookie support
|
||||||
|
await app.register(cookie, { secret: "test-cookie-secret", hook: "onRequest" });
|
||||||
|
|
||||||
// Auth middleware (must be registered before routes)
|
// Auth middleware (must be registered before routes)
|
||||||
await authMiddleware(app);
|
await authMiddleware(app);
|
||||||
|
|
||||||
// Auth routes
|
// Auth routes
|
||||||
await authRoutes(app);
|
await authRoutes(app);
|
||||||
|
|
||||||
|
// OIDC routes
|
||||||
|
await oidcRoutes(app);
|
||||||
|
|
||||||
// File upload/download routes
|
// File upload/download routes
|
||||||
await fileRoutes(app);
|
await fileRoutes(app);
|
||||||
|
|
||||||
@@ -161,9 +169,15 @@ export async function buildTestApp(): Promise<TestApp> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Public config endpoint
|
// Public config endpoint
|
||||||
app.get("/api/v1/config/auth", async () => ({
|
app.get("/api/v1/config/auth", async () => {
|
||||||
authEnabled: env.AUTH_ENABLED,
|
const config: Record<string, unknown> = { authEnabled: env.AUTH_ENABLED };
|
||||||
}));
|
if (env.OIDC_ENABLED) {
|
||||||
|
config.oidcEnabled = true;
|
||||||
|
config.oidcProviderName = env.OIDC_PROVIDER_NAME || null;
|
||||||
|
config.oidcLoginUrl = "/api/auth/oidc/login";
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure Fastify is ready (all plugins loaded)
|
// Ensure Fastify is ready (all plugins loaded)
|
||||||
await app.ready();
|
await app.ready();
|
||||||
|
|||||||
Reference in New Issue
Block a user