page.tsx100 lines · main
1import { apiFetch } from '@/lib/api';
2
3export const metadata = { title: 'Auth · sessions' };
4export const dynamic = 'force-dynamic';
5
6type SessionRow = {
7 handle: string;
8 userId: string;
9 tenantId: string;
10 expiresAt: string;
11 createdAt: string;
12};
13
14export default async function AuthProjectSessionsPage({
15 params,
16}: {
17 params: Promise<{ projectId: string }>;
18}) {
19 const { projectId } = await params;
20 let sessions: SessionRow[] = [];
21 let err: string | null = null;
22 let status = 0;
23
24 try {
25 const res = await apiFetch(
26 `/v1/auth-core/session/recent?limit=50&projectId=${encodeURIComponent(projectId)}`,
27 );
28 status = res.status;
29 if (res.status === 401) {
30 err = 'sign in to briven.tech to see sessions';
31 } else if (!res.ok) {
32 err = (await res.text().catch(() => '')) || res.statusText;
33 } else {
34 const body = (await res.json()) as { sessions?: SessionRow[] };
35 sessions = body.sessions ?? [];
36 }
37 } catch (e) {
38 err = e instanceof Error ? e.message : String(e);
39 }
40
41 return (
42 <section>
43 <header className="mb-6">
44 <h2 className="font-mono text-lg tracking-tight text-[var(--color-text)]">
45 sessions
46 </h2>
47 <p className="mt-1 font-mono text-sm text-[var(--color-text-muted)]">
48 who is signed in for this project
49 </p>
50 </header>
51
52 {err ? (
53 <div className="rounded-md border border-dashed border-[var(--color-border)] p-8 font-mono text-sm text-[var(--color-text-muted)]">
54 {status === 401 ? err : err || 'could not load sessions'}
55 </div>
56 ) : sessions.length === 0 ? (
57 <div className="rounded-md border border-dashed border-[var(--color-border)] p-8 font-mono text-sm text-[var(--color-text-muted)]">
58 no active sessions for this project.
59 </div>
60 ) : (
61 <div className="overflow-x-auto rounded-md border border-[var(--color-border-subtle)]">
62 <table className="w-full min-w-[640px] text-left font-mono text-xs">
63 <thead className="border-b border-[var(--color-border-subtle)] bg-[var(--color-surface)] text-[var(--color-text-muted)]">
64 <tr>
65 <th className="px-3 py-2 font-normal">user</th>
66 <th className="px-3 py-2 font-normal">created</th>
67 <th className="px-3 py-2 font-normal">expires</th>
68 <th className="px-3 py-2 font-normal">handle</th>
69 </tr>
70 </thead>
71 <tbody>
72 {sessions.map((s) => (
73 <tr
74 key={s.handle}
75 className="border-b border-[var(--color-border-subtle)] last:border-0"
76 >
77 <td className="max-w-[10rem] truncate px-3 py-2 text-[var(--color-text)]">
78 {s.userId}
79 </td>
80 <td className="px-3 py-2 text-[var(--color-text-muted)]">
81 {new Date(s.createdAt).toLocaleString()}
82 </td>
83 <td className="px-3 py-2 text-[var(--color-text-muted)]">
84 {new Date(s.expiresAt).toLocaleString()}
85 </td>
86 <td className="max-w-[8rem] truncate px-3 py-2 text-[var(--color-text-muted)]">
87 {s.handle}
88 </td>
89 </tr>
90 ))}
91 </tbody>
92 </table>
93 <p className="border-t border-[var(--color-border-subtle)] px-3 py-2 font-mono text-[10px] text-[var(--color-text-muted)]">
94 {sessions.length} sessions · this project
95 </p>
96 </div>
97 )}
98 </section>
99 );
100}