Skip to content

Commit 0552866

Browse files
committed
feat(dashboard): GDPR deletion banner + CORS-safe route loaders
- Frozen account banner for pending deletion requests - deletionPending in session data from OIDC claims - Route loaders use proxy for client-side fetches (fixes CORS)
1 parent 6233f87 commit 0552866

4 files changed

Lines changed: 33 additions & 10 deletions

File tree

src/components/layout/dashboard-shell.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ export function DashboardShell({ session, services, children }: Props) {
1414
<div className="min-h-screen flex">
1515
<DynamicSidebar session={session} services={services} />
1616
<main className="flex-1 overflow-y-auto">
17+
{session.user.deletionPending && (
18+
<div className="bg-amber-50 border-b border-amber-200 px-8 py-3 text-sm text-amber-800 dark:bg-amber-950 dark:border-amber-800 dark:text-amber-200">
19+
Your account is scheduled for deletion. You can cancel this request
20+
in{" "}
21+
<a
22+
href="/my-account/data-deletion"
23+
className="font-medium underline hover:no-underline"
24+
>
25+
My Account &rarr; Data &amp; Privacy
26+
</a>
27+
.
28+
</div>
29+
)}
1730
<div className="mx-auto max-w-6xl px-8 py-8">{children}</div>
1831
</main>
1932
</div>

src/lib/auth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export interface SessionData {
9898
email: string;
9999
image?: string;
100100
role: string;
101+
deletionPending?: boolean;
101102
};
102103
}
103104

@@ -187,6 +188,7 @@ export async function fetchUserInfo(
187188
email: data.email ?? "",
188189
image: data.picture && data.picture !== "—" ? data.picture : undefined,
189190
role: data.role ?? "user",
191+
deletionPending: data.deletionPending ?? false,
190192
};
191193
}
192194

src/routes/_authenticated/$service/$.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ export const Route = createFileRoute("/_authenticated/$service/$")({
3030
}
3131
const endpoint = interpolateEndpoint(section.endpoint, pathParams);
3232
try {
33-
const response = await fetch(`${service.baseUrl}${endpoint}`, {
34-
headers: {
35-
Authorization: `Bearer ${ctx.session.accessToken}`,
36-
},
37-
});
33+
const isServer = typeof window === "undefined";
34+
const url = isServer
35+
? `${service.baseUrl}${endpoint}`
36+
: `/api/proxy/${service.slug}${endpoint}`;
37+
const headers: Record<string, string> = {};
38+
if (isServer) {
39+
headers.Authorization = `Bearer ${ctx.session.accessToken}`;
40+
}
41+
const response = await fetch(url, { headers });
3842
if (!response.ok) return null;
3943
return response.json();
4044
} catch {

src/routes/_authenticated/$service/index.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ export const Route = createFileRoute("/_authenticated/$service/")({
3030
}
3131
const endpoint = interpolateEndpoint(section.endpoint, {});
3232
try {
33-
const response = await fetch(`${service.baseUrl}${endpoint}`, {
34-
headers: {
35-
Authorization: `Bearer ${ctx.session.accessToken}`,
36-
},
37-
});
33+
const isServer = typeof window === "undefined";
34+
const url = isServer
35+
? `${service.baseUrl}${endpoint}`
36+
: `/api/proxy/${service.slug}${endpoint}`;
37+
const headers: Record<string, string> = {};
38+
if (isServer) {
39+
headers.Authorization = `Bearer ${ctx.session.accessToken}`;
40+
}
41+
const response = await fetch(url, { headers });
3842
if (!response.ok) return null;
3943
return response.json();
4044
} catch {

0 commit comments

Comments
 (0)