mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 19:06:34 +00:00
perf(bundle): ETag content-hash — 304 Not Modified when bundle unchanged
This commit is contained in:
parent
890271d4c8
commit
0b3eaa3ef7
2 changed files with 38 additions and 5 deletions
|
|
@ -303,14 +303,20 @@ pub fn poll_claim(server: &str, code: &str) -> (String, String) {
|
|||
|
||||
/// Fetch bundle from server. Returns None on network/HTTP/parse failure.
|
||||
/// On success, also writes the bundle to the on-disk cache.
|
||||
/// Cached ETag from the last bundle fetch. Sent as If-None-Match so the
|
||||
/// server can return 304 when the bundle hasn't changed.
|
||||
static BUNDLE_ETAG: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
|
||||
|
||||
pub fn fetch_bundle(server: &str, key: &str) -> Option<KioskBundle> {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let resp = match client
|
||||
let mut req = client
|
||||
.get(format!("{server}/api/kiosk/bundle"))
|
||||
.header("Authorization", format!("Bearer {key}"))
|
||||
.timeout(Duration::from_secs(10))
|
||||
.send()
|
||||
{
|
||||
.timeout(Duration::from_secs(10));
|
||||
if let Some(etag) = BUNDLE_ETAG.lock().unwrap().as_deref() {
|
||||
req = req.header("If-None-Match", etag);
|
||||
}
|
||||
let resp = match req.send() {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
tracing::warn!("bundle fetch failed: {e}");
|
||||
|
|
@ -318,11 +324,21 @@ pub fn fetch_bundle(server: &str, key: &str) -> Option<KioskBundle> {
|
|||
}
|
||||
};
|
||||
|
||||
// 304 Not Modified — bundle unchanged, use cached.
|
||||
if resp.status().as_u16() == 304 {
|
||||
return load_cached_bundle();
|
||||
}
|
||||
|
||||
if !resp.status().is_success() {
|
||||
tracing::warn!("bundle fetch returned {}", resp.status());
|
||||
return None;
|
||||
}
|
||||
|
||||
// Cache the ETag for next request.
|
||||
if let Some(etag) = resp.headers().get("etag").and_then(|v| v.to_str().ok()) {
|
||||
*BUNDLE_ETAG.lock().unwrap() = Some(etag.to_string());
|
||||
}
|
||||
|
||||
match resp.json::<KioskBundle>() {
|
||||
Ok(b) => {
|
||||
save_bundle(&b);
|
||||
|
|
|
|||
|
|
@ -297,7 +297,24 @@ function registerKioskRoutes(
|
|||
const bundle = generateBundle(repo, secrets, kiosk.id, clusterKey);
|
||||
if (!bundle) throw createError({ statusCode: 404, statusMessage: "Kiosk not found" });
|
||||
|
||||
return bundle;
|
||||
// Content-hash ETag: kiosk sends If-None-Match on subsequent fetches.
|
||||
// If bundle hasn't changed → 304 Not Modified (no body, saves bandwidth).
|
||||
const json = JSON.stringify(bundle);
|
||||
const hash = createHash("sha256").update(json).digest("hex").slice(0, 16);
|
||||
const etag = `"${hash}"`;
|
||||
const ifNoneMatch = getRequestHeader(event, "if-none-match");
|
||||
if (ifNoneMatch === etag) {
|
||||
return new Response(null, { status: 304 });
|
||||
}
|
||||
|
||||
return new Response(json, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"etag": etag,
|
||||
"x-bf-bundle-version": bundle.version,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Heartbeat
|
||||
|
|
|
|||
Loading…
Reference in a new issue