use std::sync::{Arc, Mutex};
use foundation_auth::shared::jwt::JwtSigningKey;
use foundation_db::core::storage_provider::AsyncQueryStore;
use foundation_db::{StorageBackend, StorageProvider};
use worker::{event, Context, Env, Request, Response, Result as WorkerResult};
use crate::core::context::KeychainContext;
use crate::core::router::route;
use crate::core::store::apply_schema;
static CONTEXT: Mutex<Option<Arc<KeychainContext>>> = Mutex::new(None);
const D1_BINDING: &str = "KEYCHAIN_DB";
const KV_BINDING: &str = "KEYCHAIN_KV";
const SIGNING_KEY_KV_KEY: &str = "jwt_signing_key";
#[event(fetch)]
pub async fn fetch(mut req: Request, env: Env, _ctx: Context) -> WorkerResult<Response> {
let ctx = match get_or_init_context(&env).await {
Ok(ctx) => ctx,
Err(e) => return Response::error(format!("keychain init failed: {e}"), 500),
};
let method = req.method().to_string().to_uppercase();
let path = req.path();
let bearer = req
.headers()
.get("Authorization")
.ok()
.flatten()
.and_then(|h| h.strip_prefix("Bearer ").map(|t| t.trim().to_string()));
let body = req.text().await.unwrap_or_default();
let (status, json) = match route(&ctx, &method, &path, &body, bearer.as_deref()).await {
Ok((status, json)) => (status, json),
Err(err) => (err.status(), err.to_json()),
};
Ok(Response::from_json(&json)?.with_status(status))
}
async fn get_or_init_context(env: &Env) -> Result<Arc<KeychainContext>, String> {
if let Some(ctx) = CONTEXT.lock().unwrap().as_ref() {
return Ok(Arc::clone(ctx));
}
let ctx = Arc::new(build_context(env).await?);
let mut guard = CONTEXT.lock().unwrap();
if let Some(existing) = guard.as_ref() {
return Ok(Arc::clone(existing));
}
*guard = Some(Arc::clone(&ctx));
Ok(ctx)
}
async fn build_context(env: &Env) -> Result<KeychainContext, String> {
let d1 = env.d1(D1_BINDING).map_err(|e| e.to_string())?;
let db: foundation_db::D1Database = d1.into();
let provider = StorageProvider::new(StorageBackend::D1Wasm {
db: Arc::new(db),
table_prefix: String::new(),
})
.map_err(|e| e.to_string())?;
provider
.init_schema_async()
.await
.map_err(|e| format!("schema init: {e:?}"))?;
let store: Arc<dyn AsyncQueryStore> = Arc::new(provider);
apply_schema(store.as_ref())
.await
.map_err(|e| format!("vault schema: {e}"))?;
let signing_key = load_or_create_signing_key(env).await?;
Ok(KeychainContext::with_signing_key(store, signing_key))
}
async fn load_or_create_signing_key(env: &Env) -> Result<JwtSigningKey, String> {
let kv = env.kv(KV_BINDING).map_err(|e| format!("kv bind: {e}"))?;
if let Ok(Some(pem)) = kv.get(SIGNING_KEY_KV_KEY).text().await {
if let Ok(key) = JwtSigningKey::from_pem(&pem) {
return Ok(key);
}
}
let key = JwtSigningKey::generate_ed25519();
let pem = key.to_pem().map_err(|e| format!("key pem: {e}"))?;
kv.put(SIGNING_KEY_KV_KEY, pem)
.map_err(|e| format!("kv put: {e}"))?
.execute()
.await
.map_err(|e| format!("kv store: {e}"))?;
Ok(key)
}