- Replace next-auth v5 beta with better-auth v1.6.2 (stable releases)
- Add multi-provider OAuth support with admin UI configuration
- New oauthProviders table with encrypted secrets (AES-256-GCM)
- Env var bootstrap (OAUTH_*) syncs to DB, UI-created providers fully editable
- OAuth provider REST API: GET/POST/PUT/DELETE /api/v1/oauth-providers
- Settings page "Authentication Providers" section for admin management
- Account linking uses new accounts table (multi-provider per user)
- Username plugin for credentials sign-in (replaces email@localhost pattern)
- bcrypt password compatibility (existing hashes work)
- Database-backed sessions via Kysely adapter (bun:sqlite direct)
- Configurable rate limiting via AUTH_RATE_LIMIT_* env vars
- All DB columns migrated from snake_case to camelCase
- All TypeScript types/models migrated to camelCase properties
- Removed casing: "snake_case" from Drizzle config
- Callback URL format: {baseUrl}/api/auth/oauth2/callback/{providerId}
- package-lock.json removed and gitignored (using bun.lock)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
3.3 KiB
TypeScript
88 lines
3.3 KiB
TypeScript
import SettingsClient from "./SettingsClient";
|
|
import { getCloudflareSettings, getGeneralSettings, getAuthentikSettings, getMetricsSettings, getLoggingSettings, getDnsSettings, getSetting, getUpstreamDnsResolutionSettings, getGeoBlockSettings } from "@/src/lib/settings";
|
|
import { getInstanceMode, getSlaveLastSync, getSlaveMasterToken, isInstanceModeFromEnv, isSyncTokenFromEnv, getEnvSlaveInstances } from "@/src/lib/instance-sync";
|
|
import { listInstances } from "@/src/lib/models/instances";
|
|
import { listOAuthProviders } from "@/src/lib/models/oauth-providers";
|
|
import { config } from "@/src/lib/config";
|
|
import { requireAdmin } from "@/src/lib/auth";
|
|
|
|
export default async function SettingsPage() {
|
|
await requireAdmin();
|
|
|
|
// Check if configuration is from environment variables
|
|
const modeFromEnv = isInstanceModeFromEnv();
|
|
const tokenFromEnv = isSyncTokenFromEnv();
|
|
|
|
const [general, cloudflare, authentik, metrics, logging, dns, upstreamDnsResolution, instanceMode, globalGeoBlock, oauthProviders] = await Promise.all([
|
|
getGeneralSettings(),
|
|
getCloudflareSettings(),
|
|
getAuthentikSettings(),
|
|
getMetricsSettings(),
|
|
getLoggingSettings(),
|
|
getDnsSettings(),
|
|
getUpstreamDnsResolutionSettings(),
|
|
getInstanceMode(),
|
|
getGeoBlockSettings(),
|
|
listOAuthProviders(),
|
|
]);
|
|
|
|
const [overrideGeneral, overrideCloudflare, overrideAuthentik, overrideMetrics, overrideLogging, overrideDns, overrideUpstreamDnsResolution] =
|
|
instanceMode === "slave"
|
|
? await Promise.all([
|
|
getSetting("general"),
|
|
getSetting("cloudflare"),
|
|
getSetting("authentik"),
|
|
getSetting("metrics"),
|
|
getSetting("logging"),
|
|
getSetting("dns"),
|
|
getSetting("upstream_dns_resolution")
|
|
])
|
|
: [null, null, null, null, null, null, null];
|
|
|
|
const [slaveToken, slaveLastSync] = instanceMode === "slave"
|
|
? await Promise.all([getSlaveMasterToken(), getSlaveLastSync()])
|
|
: [null, null];
|
|
|
|
const instances = instanceMode === "master" ? await listInstances() : [];
|
|
const envInstances = instanceMode === "master" ? getEnvSlaveInstances() : [];
|
|
|
|
return (
|
|
<SettingsClient
|
|
general={general}
|
|
cloudflare={{
|
|
hasToken: Boolean(cloudflare?.apiToken),
|
|
zoneId: cloudflare?.zoneId,
|
|
accountId: cloudflare?.accountId
|
|
}}
|
|
authentik={authentik}
|
|
metrics={metrics}
|
|
logging={logging}
|
|
dns={dns}
|
|
upstreamDnsResolution={upstreamDnsResolution}
|
|
globalGeoBlock={globalGeoBlock}
|
|
oauthProviders={oauthProviders}
|
|
baseUrl={config.baseUrl}
|
|
instanceSync={{
|
|
mode: instanceMode,
|
|
modeFromEnv,
|
|
tokenFromEnv,
|
|
overrides: {
|
|
general: overrideGeneral !== null,
|
|
cloudflare: overrideCloudflare !== null,
|
|
authentik: overrideAuthentik !== null,
|
|
metrics: overrideMetrics !== null,
|
|
logging: overrideLogging !== null,
|
|
dns: overrideDns !== null,
|
|
upstreamDnsResolution: overrideUpstreamDnsResolution !== null
|
|
},
|
|
slave: instanceMode === "slave" ? {
|
|
hasToken: Boolean(slaveToken),
|
|
lastSyncAt: slaveLastSync?.at ?? null,
|
|
lastSyncError: slaveLastSync?.error ?? null
|
|
} : null,
|
|
master: instanceMode === "master" ? { instances, envInstances } : null
|
|
}}
|
|
/>
|
|
);
|
|
}
|