@@ -74,6 +74,7 @@ interface Env {
7474 WORDPRESS_STATE_BUCKET : R2Bucket
7575 WORDPRESS_ADMIN_PASSWORD ?: string
7676 WORDPRESS_AUTH_SECRET ?: string
77+ WORDPRESS_OPERATOR_TOKEN ?: string
7778}
7879
7980export default {
@@ -82,6 +83,7 @@ export default {
8283 if ( staticResponse ) return staticResponse
8384 const route = routeWorkerRequest ( request )
8485 const coordinator = env . WORDPRESS_STATE . getByName ( "default" )
86+ if ( route . kind === "operator-reset" ) return resetCanonicalWordPress ( request , env , coordinator )
8587 if ( route . kind === "probe" ) {
8688 if ( route . phase === "canonical-session" ) return canonicalSessionProbe ( env . WORDPRESS_STATE_BUCKET , await coordinatorCall < { pointer : MarkdownPointer | null } > ( coordinator , request . url , "state" ) )
8789 return runBootProbe ( route . phase , env . WORDPRESS_STATE_BUCKET )
@@ -135,6 +137,27 @@ interface Runtime {
135137let cachedRuntime : { baseRevision : string ; promise : Promise < Runtime > } | undefined
136138const LEASE_ACQUISITION_TIMEOUT_MS = 100_000
137139
140+ async function resetCanonicalWordPress ( request : Request , env : Env , coordinator : DurableObjectStub ) : Promise < Response > {
141+ if ( request . method !== "POST" ) return new Response ( "Canonical reset requires POST." , { status : 405 } )
142+ const authorization = request . headers . get ( "authorization" )
143+ if ( ! env . WORDPRESS_OPERATOR_TOKEN || ! authorization || ! await secretsMatch ( authorization , `Bearer ${ env . WORDPRESS_OPERATOR_TOKEN } ` ) ) {
144+ return new Response ( "Canonical reset authorization failed." , { status : 401 } )
145+ }
146+ const response = await coordinator . fetch ( new Request ( coordinatorUrl ( request . url , "reset" ) , { method : "POST" , headers : { "content-type" : "application/json" } , body : "{}" } ) )
147+ if ( response . ok ) await discardCachedRuntime ( )
148+ return response
149+ }
150+
151+ async function secretsMatch ( left : string , right : string ) : Promise < boolean > {
152+ const encoder = new TextEncoder ( )
153+ const [ leftHash , rightHash ] = await Promise . all ( [ crypto . subtle . digest ( "SHA-256" , encoder . encode ( left ) ) , crypto . subtle . digest ( "SHA-256" , encoder . encode ( right ) ) ] )
154+ const leftBytes = new Uint8Array ( leftHash )
155+ const rightBytes = new Uint8Array ( rightHash )
156+ let difference = 0
157+ for ( let index = 0 ; index < leftBytes . length ; index ++ ) difference |= leftBytes [ index ] ^ rightBytes [ index ]
158+ return difference === 0
159+ }
160+
138161async function runCoordinatedWordPressRequest ( request : Request , env : Env , coordinator : DurableObjectStub , route : "wordpress" | "health" | "r2-mutate" | "canonical-auth" ) : Promise < Response > {
139162 if ( route === "r2-mutate" && request . method !== "POST" ) return new Response ( "WordPress state mutation requires POST." , { status : 405 } )
140163 let lease = await acquireLease ( coordinator , request . url )
0 commit comments