Skip to content

Commit 998d764

Browse files
authored
refactor: auth boundaries and runtime state (#23)
* Add shared auth cache backend * Remove legacy rate-limit cache config * Remove unused auth cache exports * Hide fixed-window counter internals * Simplify auth session and cache state * Centralize auth session identities * Improve club invitation code page * Refactor auth boundaries and runtime state * Stabilize admin invitation code e2e
1 parent 682b6a4 commit 998d764

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1395
-509
lines changed

.vscode/launch.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Next.js: debug server-side",
6+
"type": "node-terminal",
7+
"request": "launch",
8+
"command": "pnpm dev",
9+
"env": {
10+
"NODE_OPTIONS": "--inspect"
11+
}
12+
},
13+
{
14+
"name": "Next.js: debug client-side",
15+
"type": "chrome",
16+
"request": "launch",
17+
"url": "http://localhost:3000"
18+
},
19+
{
20+
"name": "Next.js: debug full stack",
21+
"type": "node",
22+
"request": "launch",
23+
"program": "${workspaceFolder}/node_modules/next/dist/bin/next",
24+
"runtimeArgs": ["--inspect"],
25+
"skipFiles": ["<node_internals>/**"],
26+
"serverReadyAction": {
27+
"action": "debugWithEdge",
28+
"killOnServerStop": true,
29+
"pattern": "- Local:.+(https?://.+)",
30+
"uriFormat": "%s",
31+
"webRoot": "${workspaceFolder}"
32+
}
33+
}
34+
]
35+
}

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ Optional:
2525
NEXTAUTH_URL=http://localhost:3000
2626
NEXTAUTH_SECRET=...
2727
LOG_LEVEL=<trace|debug|info|warn|error|fatal|silent>
28-
RATE_LIMIT_PROVIDER=<disabled|memory|redis|upstash>
29-
RATE_LIMIT_REDIS_URL=redis://...
30-
UPSTASH_REDIS_REST_URL=...
31-
UPSTASH_REDIS_REST_TOKEN=...
28+
CACHE_PROVIDER=<disabled|memory|redis|upstash>
29+
CACHE_REDIS_URL=redis://...
30+
CACHE_UPSTASH_REST_URL=...
31+
CACHE_UPSTASH_REST_TOKEN=...
3232
```
3333

3434
Generate a strong secret:

app/(protected)/clubs/[clubId]/(overview)/_lib.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { forbidden, notFound } from "next/navigation";
22
import { cache } from "react";
33

44
import { FlashToast } from "@/components/ui/flash-toast";
5-
import { getCurrentUser } from "@/lib/auth/server";
5+
import { requireCurrentUser } from "@/lib/auth/server";
66
import { canViewClub } from "@/lib/clubs/permissions";
77
import { findClubDetail } from "@/lib/clubs/repository";
88

@@ -15,10 +15,7 @@ export function readMessage(value: string | string[] | undefined) {
1515
}
1616

1717
export const loadClubOverviewContext = cache(async (clubId: string) => {
18-
const currentUser = await getCurrentUser();
19-
if (!currentUser) {
20-
return null;
21-
}
18+
const currentUser = await requireCurrentUser();
2219

2320
const club = await findClubDetail(clubId, currentUser.id);
2421

app/(protected)/clubs/[clubId]/(overview)/board/page.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,7 @@ export default async function ClubBoardPage({
1313
searchParams,
1414
}: ClubBoardPageProps) {
1515
const [{ clubId }, paramsData] = await Promise.all([params, searchParams]);
16-
const context = await loadClubOverviewContext(clubId);
17-
18-
if (!context) {
19-
return null;
20-
}
21-
22-
const { club } = context;
16+
const { club } = await loadClubOverviewContext(clubId);
2317
const books = await listClubBooks(clubId);
2418

2519
return (

app/(protected)/clubs/[clubId]/(overview)/layout.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,7 @@ export default async function ClubOverviewLayout({
2323
params,
2424
}: ClubOverviewLayoutProps) {
2525
const { clubId } = await params;
26-
const context = await loadClubOverviewContext(clubId);
27-
28-
if (!context) {
29-
return null;
30-
}
31-
32-
const { club } = context;
26+
const { club } = await loadClubOverviewContext(clubId);
3327

3428
return (
3529
<div className="space-y-6">

app/(protected)/clubs/[clubId]/(overview)/members/page.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,7 @@ export default async function ClubMembersPage({
2020
searchParams,
2121
}: ClubMembersPageProps) {
2222
const [{ clubId }, paramsData] = await Promise.all([params, searchParams]);
23-
const context = await loadClubOverviewContext(clubId);
24-
25-
if (!context) {
26-
return null;
27-
}
28-
29-
const { club, currentUser } = context;
23+
const { club, currentUser } = await loadClubOverviewContext(clubId);
3024

3125
if (!isClubMember(club.currentUserRole)) {
3226
forbidden();

app/(protected)/clubs/[clubId]/manage/_lib.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { forbidden, notFound } from "next/navigation";
33
import { cache } from "react";
44

55
import { FlashToast } from "@/components/ui/flash-toast";
6-
import { getCurrentUser } from "@/lib/auth/server";
6+
import { requireCurrentUser } from "@/lib/auth/server";
77
import { isClubAdmin } from "@/lib/clubs/permissions";
88
import { findClubDetail } from "@/lib/clubs/repository";
99

@@ -16,10 +16,7 @@ export function readMessage(value: string | string[] | undefined) {
1616
}
1717

1818
export const loadManageClubContext = cache(async (clubId: string) => {
19-
const currentUser = await getCurrentUser();
20-
if (!currentUser) {
21-
return null;
22-
}
19+
const currentUser = await requireCurrentUser();
2320

2421
const club = await findClubDetail(clubId, currentUser.id);
2522

app/(protected)/clubs/[clubId]/manage/board/page.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,7 @@ export default async function ClubManageBoardPage({
1818
searchParams,
1919
}: ClubManageBoardPageProps) {
2020
const [{ clubId }, paramsData] = await Promise.all([params, searchParams]);
21-
const context = await loadManageClubContext(clubId);
22-
23-
if (!context) {
24-
return null;
25-
}
26-
27-
const { club, currentUser } = context;
21+
const { club, currentUser } = await loadManageClubContext(clubId);
2822
const returnTo = createManageSectionHref({
2923
clubId,
3024
section: "board",

app/(protected)/clubs/[clubId]/manage/invite/page.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,7 @@ export default async function ClubManageInvitePage({
2424
searchParams,
2525
}: ClubManageInvitePageProps) {
2626
const [{ clubId }, paramsData] = await Promise.all([params, searchParams]);
27-
const context = await loadManageClubContext(clubId);
28-
29-
if (!context) {
30-
return null;
31-
}
32-
33-
const { club, currentUser } = context;
27+
const { club, currentUser } = await loadManageClubContext(clubId);
3428

3529
if (club.visibility !== "PRIVATE") {
3630
redirect(createManageEntryHref(clubId));

app/(protected)/clubs/[clubId]/manage/layout.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,7 @@ export default async function ClubManageLayout({
2727
params,
2828
}: ClubManageLayoutProps) {
2929
const { clubId } = await params;
30-
const context = await loadManageClubContext(clubId);
31-
32-
if (!context) {
33-
return null;
34-
}
35-
36-
const { club } = context;
30+
const { club } = await loadManageClubContext(clubId);
3731

3832
return (
3933
<div className="mx-auto max-w-6xl space-y-6">

0 commit comments

Comments
 (0)