mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 07:25:38 -04:00
Replace nested loop queries with batch fetches using inArray() across discovery, tracking, metadata, settings, and system-health services. The biggest win is getContinueWatchingFeed() going from ~375+ queries to 5. Add a cached getSession() wrapper via React.cache() to deduplicate auth lookups shared between layouts and pages. Parallelize independent async operations in importTitle(), cacheImagesJob(), and the explore page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
import { drizzleAdapter } from "@better-auth/drizzle-adapter";
|
|
import { APIError, createAuthMiddleware } from "better-auth/api";
|
|
import { betterAuth } from "better-auth/minimal";
|
|
import { admin, genericOAuth } from "better-auth/plugins";
|
|
import {
|
|
isOidcAutoRegisterEnabled,
|
|
isOidcConfigured,
|
|
isPasswordLoginDisabled,
|
|
} from "@/lib/config";
|
|
import { db } from "@/lib/db/client";
|
|
import { createLogger } from "@/lib/logger";
|
|
import {
|
|
getUserCount,
|
|
isRegistrationOpen,
|
|
setSetting,
|
|
} from "@/lib/services/settings";
|
|
|
|
const oidcPlugin = isOidcConfigured()
|
|
? [
|
|
genericOAuth({
|
|
config: [
|
|
{
|
|
providerId: "oidc",
|
|
clientId: process.env.OIDC_CLIENT_ID ?? "",
|
|
clientSecret: process.env.OIDC_CLIENT_SECRET ?? "",
|
|
discoveryUrl: `${process.env.OIDC_ISSUER_URL}/.well-known/openid-configuration`,
|
|
scopes: ["openid", "email", "profile"],
|
|
pkce: true,
|
|
disableImplicitSignUp: !isOidcAutoRegisterEnabled(),
|
|
mapProfileToUser: (profile) => ({
|
|
name: profile.name || profile.preferred_username || profile.email,
|
|
}),
|
|
},
|
|
],
|
|
}),
|
|
]
|
|
: [];
|
|
|
|
const authLog = createLogger("auth");
|
|
|
|
export const auth = betterAuth({
|
|
logger: {
|
|
// Suppress unset secret/low entropy warnings during build
|
|
disabled: process.env.NEXT_PHASE === "phase-production-build",
|
|
level: "debug",
|
|
log: (level, message, ...args) => {
|
|
const fn = authLog[level as keyof typeof authLog];
|
|
if (fn) fn(message, ...args);
|
|
},
|
|
},
|
|
database: drizzleAdapter(db, {
|
|
provider: "sqlite",
|
|
}),
|
|
emailAndPassword: {
|
|
enabled: !isPasswordLoginDisabled(),
|
|
},
|
|
account: {
|
|
accountLinking: {
|
|
enabled: true,
|
|
trustedProviders: ["oidc"],
|
|
},
|
|
},
|
|
plugins: [admin(), ...oidcPlugin],
|
|
advanced: {
|
|
database: {
|
|
generateId: () => Bun.randomUUIDv7(),
|
|
},
|
|
},
|
|
hooks: {
|
|
before: createAuthMiddleware(async (ctx) => {
|
|
// Block email/password sign-up when registration is closed.
|
|
// This is endpoint-level so it doesn't affect OIDC user creation
|
|
// (which is gated by the genericOAuth plugin's disableImplicitSignUp).
|
|
if (ctx.path === "/sign-up/email") {
|
|
const open = await isRegistrationOpen();
|
|
if (!open) {
|
|
throw new APIError("FORBIDDEN", {
|
|
message: "Registration is currently closed",
|
|
});
|
|
}
|
|
}
|
|
}),
|
|
},
|
|
databaseHooks: {
|
|
user: {
|
|
create: {
|
|
before: async (userData) => {
|
|
// First user becomes admin regardless of auth method
|
|
const userCount = await getUserCount();
|
|
if (userCount === 0) {
|
|
return {
|
|
data: {
|
|
...userData,
|
|
role: "admin",
|
|
},
|
|
};
|
|
}
|
|
return { data: userData };
|
|
},
|
|
after: async () => {
|
|
// Auto-close registration after first user
|
|
const userCount = await getUserCount();
|
|
if (userCount === 1) {
|
|
await setSetting("registrationOpen", "false");
|
|
}
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|