Files
caddy-proxy-manager/app/(dashboard)/proxy-hosts/page.tsx
fuomag9 3a16d6e9b1 Replace next-auth with Better Auth, migrate DB columns to camelCase
- 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>
2026-04-12 21:11:48 +02:00

90 lines
3.5 KiB
TypeScript

import ProxyHostsClient from "./ProxyHostsClient";
import { listProxyHostsPaginated, countProxyHosts } from "@/src/lib/models/proxy-hosts";
import { listCertificates } from "@/src/lib/models/certificates";
import { listCaCertificates } from "@/src/lib/models/ca-certificates";
import { listAccessLists } from "@/src/lib/models/access-lists";
import { getAuthentikSettings } from "@/src/lib/settings";
import { listMtlsRoles } from "@/src/lib/models/mtls-roles";
import { listIssuedClientCertificates } from "@/src/lib/models/issued-client-certificates";
import { listUsers } from "@/src/lib/models/user";
import { listGroups } from "@/src/lib/models/groups";
import { getForwardAuthAccessForHost } from "@/src/lib/models/forward-auth";
import { requireAdmin } from "@/src/lib/auth";
const PER_PAGE = 25;
interface PageProps {
searchParams: Promise<{ page?: string; search?: string; sortBy?: string; sortDir?: string }>;
}
export default async function ProxyHostsPage({ searchParams }: PageProps) {
await requireAdmin();
const { page: pageParam, search: searchParam, sortBy: sortByParam, sortDir: sortDirParam } = await searchParams;
const page = Math.max(1, parseInt(pageParam ?? "1", 10) || 1);
const search = searchParam?.trim() || undefined;
const offset = (page - 1) * PER_PAGE;
const sortBy = sortByParam || undefined;
const sortDir = (sortDirParam === "asc" || sortDirParam === "desc") ? sortDirParam : "desc";
const [hosts, total, certificates, caCertificates, accessLists, authentikDefaults] = await Promise.all([
listProxyHostsPaginated(PER_PAGE, offset, search, sortBy, sortDir),
countProxyHosts(search),
listCertificates(),
listCaCertificates(),
listAccessLists(),
getAuthentikSettings(),
]);
// These are safe to fail if the RBAC migration hasn't been applied yet
const [mtlsRoles, issuedClientCerts, allUsers, allGroups] = await Promise.all([
listMtlsRoles().catch(() => []),
listIssuedClientCertificates().catch(() => []),
listUsers().catch(() => []),
listGroups().catch(() => []),
]);
// Build forward auth access map for hosts that have CPM forward auth enabled
const faHosts = hosts.filter((h) => h.cpmForwardAuth?.enabled);
const faAccessEntries = await Promise.all(
faHosts.map((h) => getForwardAuthAccessForHost(h.id).catch(() => []))
);
const forwardAuthAccessMap: Record<number, { userIds: number[]; groupIds: number[] }> = {};
faHosts.forEach((h, i) => {
const entries = faAccessEntries[i];
forwardAuthAccessMap[h.id] = {
userIds: entries.filter((e) => e.userId !== null).map((e) => e.userId!),
groupIds: entries.filter((e) => e.groupId !== null).map((e) => e.groupId!),
};
});
const forwardAuthUsers = allUsers.map((u) => ({
id: u.id,
email: u.email,
name: u.name,
role: u.role,
}));
const forwardAuthGroups = allGroups.map((g) => ({
id: g.id,
name: g.name,
description: g.description,
member_count: g.members.length,
}));
return (
<ProxyHostsClient
hosts={hosts}
certificates={certificates}
caCertificates={caCertificates}
accessLists={accessLists}
authentikDefaults={authentikDefaults}
pagination={{ total, page, perPage: PER_PAGE }}
initialSearch={search ?? ""}
initialSort={{ sortBy: sortBy ?? "createdAt", sortDir }}
mtlsRoles={mtlsRoles}
issuedClientCerts={issuedClientCerts}
forwardAuthUsers={forwardAuthUsers}
forwardAuthGroups={forwardAuthGroups}
forwardAuthAccessMap={forwardAuthAccessMap}
/>
);
}