@@ -2,167 +2,172 @@ import Cloudflare from "cloudflare";
2
2
import { z } from "zod" ;
3
3
4
4
interface CaptureBody {
5
- url : string ;
6
- tags : string [ ] ;
7
- zone : string ;
5
+ url : string ;
6
+ tags : string [ ] ;
7
+ zone : string ;
8
8
}
9
9
10
10
const Purge = z . object ( {
11
- tags : z . array ( z . string ( ) ) ,
11
+ tags : z . array ( z . string ( ) ) ,
12
12
} ) ;
13
13
14
14
const Capture = z . object ( {
15
- url : z . string ( ) . url ( ) ,
16
- tags : z . array ( z . string ( ) ) ,
15
+ url : z . string ( ) . url ( ) ,
16
+ tags : z . array ( z . string ( ) ) ,
17
17
} ) ;
18
18
19
19
function * chunks < T > ( arr : T [ ] , n : number ) {
20
- for ( let i = 0 ; i < arr . length ; i += n ) {
21
- yield arr . slice ( i , i + n ) ;
22
- }
20
+ for ( let i = 0 ; i < arr . length ; i += n ) {
21
+ yield arr . slice ( i , i + n ) ;
22
+ }
23
23
}
24
24
25
25
interface PurgeBody {
26
- tag : string ;
27
- zone ?: string | undefined ;
26
+ tag : string ;
27
+ zone ?: string ;
28
28
}
29
29
30
30
function apiToken ( request : Request , env : Env ) : string {
31
- const auth = request . headers . get ( "Authorization" ) ;
32
- if ( ! auth ) {
33
- throw new Error ( "Missing Authorization header" ) ;
34
- }
35
- const [ scheme , token ] = auth . split ( " " ) ;
36
- if ( scheme !== "Bearer" ) {
37
- throw new Error ( "Authorization scheme is not Bearer" ) ;
38
- }
39
-
40
- // Needs at least `Cache Purge:Purge, Zone:Read" permissions.
41
- if ( token !== env . API_TOKEN ) {
42
- throw new Error ( "Provided token does not match the `API_TOKEN` secret." ) ;
43
- }
44
-
45
- return token ;
31
+ const auth = request . headers . get ( "Authorization" ) ;
32
+ if ( ! auth ) {
33
+ throw new Error ( "Missing Authorization header" ) ;
34
+ }
35
+ const [ scheme , token ] = auth . split ( " " ) ;
36
+ if ( scheme !== "Bearer" ) {
37
+ throw new Error ( "Authorization scheme is not Bearer" ) ;
38
+ }
39
+
40
+ // Needs at least `Cache Purge:Purge, Zone:Read" permissions.
41
+ if ( token !== env . API_TOKEN ) {
42
+ throw new Error ( "Provided token does not match the `API_TOKEN` secret." ) ;
43
+ }
44
+
45
+ return token ;
46
46
}
47
47
48
48
async function handlePurgeRequest (
49
- request : Request ,
50
- env : Env ,
49
+ request : Request ,
50
+ env : Env ,
51
51
) : Promise < Response > {
52
- let token : string ;
53
- try {
54
- token = apiToken ( request , env ) ;
55
- } catch ( e ) {
56
- return Response . json (
57
- {
58
- error : String ( e ) ,
59
- } ,
60
- { status : 401 } ,
61
- ) ;
62
- }
63
-
64
- const client = new Cloudflare ( {
65
- apiToken : token ,
66
- } ) ;
67
-
68
- const { status } = await client . user . tokens . verify ( ) ;
69
-
70
- if ( status !== "active" ) {
71
- return Response . json (
72
- {
73
- error : "Authentication token is not active." ,
74
- } ,
75
- { status : 401 } ,
76
- ) ;
77
- }
78
-
79
- const { tags } = Purge . parse ( await request . json ( ) ) ;
80
- console . debug ( "[Cache Purge Request] Purge Tags" , tags ) ;
81
-
82
- // If no zone is present, then all zones will be purged.
83
- const zone = request . headers . get ( "CF-Worker" ) ?? undefined ;
84
-
85
- const messages = tags . map < MessageSendRequest < PurgeBody > > ( ( tag ) => ( {
86
- body : {
87
- tag,
88
- zone,
89
- } ,
90
- contentType : "json" ,
91
- } ) ) ;
92
-
93
- // sendBatch only allows for a maximum of 100 messages.
94
- const promises : ReturnType < typeof env . CACHE_PURGE_TAG . sendBatch > [ ] = [ ] ;
95
- for ( const messageChunks of chunks ( messages , 100 ) ) {
96
- console . debug (
97
- "[Cache Purge Request] Send Batch" ,
98
- messageChunks . map ( ( { body } ) => body ) ,
99
- ) ;
100
- promises . push ( env . CACHE_PURGE_TAG . sendBatch ( messageChunks ) ) ;
101
- }
102
-
103
- await Promise . all ( promises ) ;
104
-
105
- return new Response ( "" , { status : 202 } ) ;
52
+ let token : string ;
53
+ try {
54
+ token = apiToken ( request , env ) ;
55
+ } catch ( e ) {
56
+ return Response . json (
57
+ {
58
+ error : String ( e ) ,
59
+ } ,
60
+ { status : 401 } ,
61
+ ) ;
62
+ }
63
+
64
+ const client = new Cloudflare ( {
65
+ apiToken : token ,
66
+ } ) ;
67
+
68
+ const { status } = await client . user . tokens . verify ( ) ;
69
+
70
+ if ( status !== "active" ) {
71
+ return Response . json (
72
+ {
73
+ error : "Authentication token is not active." ,
74
+ } ,
75
+ { status : 401 } ,
76
+ ) ;
77
+ }
78
+
79
+ const { tags } = Purge . parse ( await request . json ( ) ) ;
80
+ console . debug ( "[Cache Purge Request] Purge Tags" , tags ) ;
81
+
82
+ // If no zone is present, then all zones will be purged.
83
+ const zone = request . headers . get ( "CF-Worker" ) ?? undefined ;
84
+
85
+ const messages = tags . map < MessageSendRequest < PurgeBody > > ( ( tag ) => {
86
+ const body : PurgeBody = {
87
+ tag,
88
+ } ;
89
+ if ( zone ) {
90
+ body . zone = zone ;
91
+ }
92
+ return {
93
+ body,
94
+ contentType : "json" ,
95
+ } ;
96
+ } ) ;
97
+
98
+ // sendBatch only allows for a maximum of 100 messages.
99
+ const promises : ReturnType < typeof env . CACHE_PURGE_TAG . sendBatch > [ ] = [ ] ;
100
+ for ( const messageChunks of chunks ( messages , 100 ) ) {
101
+ console . debug (
102
+ "[Cache Purge Request] Send Batch" ,
103
+ messageChunks . map ( ( { body } ) => body ) ,
104
+ ) ;
105
+ promises . push ( env . CACHE_PURGE_TAG . sendBatch ( messageChunks ) ) ;
106
+ }
107
+
108
+ await Promise . all ( promises ) ;
109
+
110
+ return new Response ( "" , { status : 202 } ) ;
106
111
}
107
112
108
113
async function handleCaptureRequest (
109
- request : Request ,
110
- env : Env ,
114
+ request : Request ,
115
+ env : Env ,
111
116
) : Promise < Response > {
112
- // Since this worker can be called over the internet, we must at least verify that the token matches the secret,
113
- // but we don't need to verify that it's usable right now.
114
- try {
115
- apiToken ( request , env ) ;
116
- } catch ( e ) {
117
- return Response . json (
118
- {
119
- error : String ( e ) ,
120
- } ,
121
- { status : 401 } ,
122
- ) ;
123
- }
124
-
125
- // If there is no zone on the request,
126
- // then we wont know how to purge the response later.
127
- const zone = request . headers . get ( "CF-Worker" ) ;
128
- if ( ! zone ) {
129
- return Response . json (
130
- {
131
- error : "Missing CF-Worker Header" ,
132
- } ,
133
- { status : 400 } ,
134
- ) ;
135
- }
136
-
137
- const { url, tags } = Capture . parse ( await request . json ( ) ) ;
138
-
139
- const capture : CaptureBody = {
140
- url,
141
- zone,
142
- tags,
143
- } ;
144
-
145
- await env . CACHE_CAPTURE . send ( capture , { contentType : "json" } ) ;
146
-
147
- return new Response ( "" , { status : 202 } ) ;
117
+ // Since this worker can be called over the internet, we must at least verify that the token matches the secret,
118
+ // but we don't need to verify that it's usable right now.
119
+ try {
120
+ apiToken ( request , env ) ;
121
+ } catch ( e ) {
122
+ return Response . json (
123
+ {
124
+ error : String ( e ) ,
125
+ } ,
126
+ { status : 401 } ,
127
+ ) ;
128
+ }
129
+
130
+ // If there is no zone on the request,
131
+ // then we wont know how to purge the response later.
132
+ const zone = request . headers . get ( "CF-Worker" ) ;
133
+ if ( ! zone ) {
134
+ return Response . json (
135
+ {
136
+ error : "Missing CF-Worker Header" ,
137
+ } ,
138
+ { status : 400 } ,
139
+ ) ;
140
+ }
141
+
142
+ const { url, tags } = Capture . parse ( await request . json ( ) ) ;
143
+
144
+ const capture : CaptureBody = {
145
+ url,
146
+ zone,
147
+ tags,
148
+ } ;
149
+
150
+ await env . CACHE_CAPTURE . send ( capture , { contentType : "json" } ) ;
151
+
152
+ return new Response ( "" , { status : 202 } ) ;
148
153
}
149
154
150
155
export default {
151
- async fetch ( request , env ) {
152
- // Remove any tracking params to increase the cache hit rate.
153
- const url = new URL ( request . url ) ;
154
-
155
- if ( request . method !== "POST" ) {
156
- return new Response ( "" , { status : 404 } ) ;
157
- }
158
-
159
- switch ( url . pathname ) {
160
- case "/purge" :
161
- return handlePurgeRequest ( request , env ) ;
162
- case "/capture" :
163
- return handleCaptureRequest ( request , env ) ;
164
- default :
165
- return new Response ( "" , { status : 404 } ) ;
166
- }
167
- } ,
156
+ async fetch ( request , env ) {
157
+ // Remove any tracking params to increase the cache hit rate.
158
+ const url = new URL ( request . url ) ;
159
+
160
+ if ( request . method !== "POST" ) {
161
+ return new Response ( "" , { status : 404 } ) ;
162
+ }
163
+
164
+ switch ( url . pathname ) {
165
+ case "/purge" :
166
+ return handlePurgeRequest ( request , env ) ;
167
+ case "/capture" :
168
+ return handleCaptureRequest ( request , env ) ;
169
+ default :
170
+ return new Response ( "" , { status : 404 } ) ;
171
+ }
172
+ } ,
168
173
} satisfies ExportedHandler < Env > ;
0 commit comments