Skip to content

Commit a8e613c

Browse files
aster-voidclaude
andcommitted
treewide: add Cloudflare cache purge on content updates
Automatically purges entire Cloudflare zone cache when articles, projects, or members are created/updated/deleted. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent f0b482b commit a8e613c

File tree

6 files changed

+61
-8
lines changed

6 files changed

+61
-8
lines changed

.env.sample

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ S3_PUBLIC_URL=http://localhost:9000/dev
1414

1515
# Set to "true" to bypass auth (dev only!)
1616
UNSAFE_DISABLE_AUTH=true
17+
18+
# Cloudflare cache purge (leave empty to disable)
19+
CLOUDFLARE_ZONE_ID=""
20+
CLOUDFLARE_API_TOKEN=""

src/lib/data/private/articles.remote.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
updateArticle,
1111
} from "$lib/server/database/articles.server";
1212
import { requireUtCodeMember } from "$lib/server/database/auth.server";
13+
import { purgeCache } from "$lib/server/services/cloudflare/cache.server";
1314

1415
// Re-export getMembers from canonical source
1516
export { getMembers } from "./members.remote";
@@ -37,7 +38,9 @@ export const saveArticle = command(
3738
}),
3839
async (data) => {
3940
await requireUtCodeMember();
40-
return await createArticle(data);
41+
const result = await createArticle(data);
42+
purgeCache().catch(console.error);
43+
return result;
4144
},
4245
);
4346

@@ -57,21 +60,28 @@ export const editArticle = command(
5760
}),
5861
async ({ id, data }) => {
5962
await requireUtCodeMember();
60-
return await updateArticle(id, data);
63+
const result = await updateArticle(id, data);
64+
purgeCache().catch(console.error);
65+
return result;
6166
},
6267
);
6368

6469
export const removeArticle = command(v.string(), async (id) => {
6570
await requireUtCodeMember();
6671
await deleteArticle(id);
72+
purgeCache().catch(console.error);
6773
});
6874

6975
export const publish = command(v.string(), async (id) => {
7076
await requireUtCodeMember();
71-
return await publishArticle(id);
77+
const result = await publishArticle(id);
78+
purgeCache().catch(console.error);
79+
return result;
7280
});
7381

7482
export const unpublish = command(v.string(), async (id) => {
7583
await requireUtCodeMember();
76-
return await unpublishArticle(id);
84+
const result = await unpublishArticle(id);
85+
purgeCache().catch(console.error);
86+
return result;
7787
});

src/lib/data/private/members.remote.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
listMembers,
99
updateMember,
1010
} from "$lib/server/database/members.server";
11+
import { purgeCache } from "$lib/server/services/cloudflare/cache.server";
1112

1213
export const getMembers = query(async () => {
1314
await requireUtCodeMember();
@@ -29,7 +30,9 @@ export const saveMember = command(
2930
}),
3031
async (data) => {
3132
await requireUtCodeMember();
32-
return await createMember(data);
33+
const result = await createMember(data);
34+
purgeCache().catch(console.error);
35+
return result;
3336
},
3437
);
3538

@@ -46,11 +49,14 @@ export const editMember = command(
4649
}),
4750
async ({ id, data }) => {
4851
await requireUtCodeMember();
49-
return await updateMember(id, data);
52+
const result = await updateMember(id, data);
53+
purgeCache().catch(console.error);
54+
return result;
5055
},
5156
);
5257

5358
export const removeMember = command(v.string(), async (id) => {
5459
await requireUtCodeMember();
5560
await deleteMember(id);
61+
purgeCache().catch(console.error);
5662
});

src/lib/data/private/projects.remote.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
transferLead as serverTransferLead,
1212
updateProject,
1313
} from "$lib/server/database/projects.server";
14+
import { purgeCache } from "$lib/server/services/cloudflare/cache.server";
1415
import type { ProjectCategory, ProjectRole } from "$lib/shared/models/schema";
1516

1617
// Re-export getMembers from canonical source
@@ -56,7 +57,9 @@ export const saveProject = command(
5657
}),
5758
async ({ data, leadMemberId }) => {
5859
await requireUtCodeMember();
59-
return await createProject(data, leadMemberId);
60+
const result = await createProject(data, leadMemberId);
61+
purgeCache().catch(console.error);
62+
return result;
6063
},
6164
);
6265

@@ -76,13 +79,16 @@ export const editProject = command(
7679
}),
7780
async ({ id, data }) => {
7881
await requireUtCodeMember();
79-
return await updateProject(id, data);
82+
const result = await updateProject(id, data);
83+
purgeCache().catch(console.error);
84+
return result;
8085
},
8186
);
8287

8388
export const removeProject = command(v.string(), async (id) => {
8489
await requireUtCodeMember();
8590
await deleteProject(id);
91+
purgeCache().catch(console.error);
8692
});
8793

8894
export const addMember = command(
@@ -94,6 +100,7 @@ export const addMember = command(
94100
async ({ projectId, memberId, role }) => {
95101
await requireUtCodeMember();
96102
await addProjectMember(projectId, memberId, role);
103+
purgeCache().catch(console.error);
97104
},
98105
);
99106

@@ -105,6 +112,7 @@ export const removeMember = command(
105112
async ({ projectId, memberId }) => {
106113
await requireUtCodeMember();
107114
await removeProjectMember(projectId, memberId);
115+
purgeCache().catch(console.error);
108116
},
109117
);
110118

@@ -129,5 +137,6 @@ export const transferLead = command(
129137
}
130138

131139
await serverTransferLead(projectId, currentLead.memberId, newLeadMemberId);
140+
purgeCache().catch(console.error);
132141
},
133142
);

src/lib/env/env.server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ const Env = v.object({
1313
S3_BUCKET: v.string(),
1414
S3_PUBLIC_URL: v.string(),
1515
UNSAFE_DISABLE_AUTH: v.optional(v.picklist(["true"])),
16+
17+
CLOUDFLARE_ZONE_ID: v.string(),
18+
CLOUDFLARE_API_TOKEN: v.string(),
1619
});
1720

1821
export const env = v.parse(Env, process.env);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { env } from "$lib/env/env.server";
2+
3+
const CLOUDFLARE_API_BASE = "https://api.cloudflare.com/client/v4";
4+
5+
export async function purgeCache(): Promise<void> {
6+
if (!env.CLOUDFLARE_ZONE_ID || !env.CLOUDFLARE_API_TOKEN) return;
7+
8+
const res = await fetch(`${CLOUDFLARE_API_BASE}/zones/${env.CLOUDFLARE_ZONE_ID}/purge_cache`, {
9+
method: "POST",
10+
headers: {
11+
Authorization: `Bearer ${env.CLOUDFLARE_API_TOKEN}`,
12+
"Content-Type": "application/json",
13+
},
14+
body: JSON.stringify({ purge_everything: true }),
15+
});
16+
17+
if (!res.ok) {
18+
const body = await res.text();
19+
throw new Error(`Cloudflare cache purge failed: ${res.status} ${body}`);
20+
}
21+
}

0 commit comments

Comments
 (0)