Shoraky commited on
Commit
200d616
·
1 Parent(s): fddeaff

Optimize profile image caching

Browse files
Files changed (2) hide show
  1. README.md +1 -1
  2. src/profile-images.ts +3 -2
README.md CHANGED
@@ -54,7 +54,7 @@ Node health: `GET /health`. AI health is internal at `http://127.0.0.1:8000/heal
54
 
55
  Run one container with one Python worker: analysis jobs and retry payloads are in memory and are lost on restart. Temporary local files are ephemeral. Completed videos and `results.json` are stored in the private HF dataset; Node stores session metadata and frame indices in Supabase. Model constants are in `app.py`.
56
 
57
- The production database must have the analysis functions and action-review structures used by secret-protected `/internal/analysis` routes. The database also needs the profile columns shown in `../Database/schema.sql` and a private `profile-images` Supabase Storage bucket for JPG, PNG, and WEBP files up to 4 MB. The backend reads and writes images through its service-role client; browsers access them through authenticated API routes.
58
 
59
  Before deploying access-control or deletion changes, run [`../Database/updates/access-permissions.sql`](../Database/updates/access-permissions.sql) in the Supabase SQL Editor. This update adds the admin-origin flag, nullable owner references for retained records, cascading action deletion, and the service-role RPCs used for quota refunds and analysis completion. All service-role reads and writes must remain behind the ownership and admin checks in the API. The full policy is documented in [`../Database/access-permissions.md`](../Database/access-permissions.md).
60
 
 
54
 
55
  Run one container with one Python worker: analysis jobs and retry payloads are in memory and are lost on restart. Temporary local files are ephemeral. Completed videos and `results.json` are stored in the private HF dataset; Node stores session metadata and frame indices in Supabase. Model constants are in `app.py`.
56
 
57
+ The production database must have the analysis functions and action-review structures used by secret-protected `/internal/analysis` routes. The database also needs the profile columns shown in `../Database/schema.sql` and a private `profile-images` Supabase Storage bucket for JPG, PNG, and WEBP files up to 4 MB. The frontend resizes profile photos to at most 1280 pixels and encodes them as WebP before upload. The backend reads and writes images through its service-role client; browsers access them through authenticated API routes with private, versioned browser caching.
58
 
59
  Before deploying access-control or deletion changes, run [`../Database/updates/access-permissions.sql`](../Database/updates/access-permissions.sql) in the Supabase SQL Editor. This update adds the admin-origin flag, nullable owner references for retained records, cascading action deletion, and the service-role RPCs used for quota refunds and analysis completion. All service-role reads and writes must remain behind the ownership and admin checks in the API. The full policy is documented in [`../Database/access-permissions.md`](../Database/access-permissions.md).
60
 
src/profile-images.ts CHANGED
@@ -48,7 +48,8 @@ async function sendImage(res: Response, path: string | null | undefined) {
48
  const type = path.endsWith(".png") ? "image/png" : path.endsWith(".webp") ? "image/webp" : "image/jpeg";
49
  res.set({
50
  "Content-Type": type,
51
- "Cache-Control": "private, max-age=3600",
 
52
  "Cross-Origin-Resource-Policy": "cross-origin",
53
  "X-Content-Type-Options": "nosniff",
54
  });
@@ -65,7 +66,7 @@ async function uploadImage(req: Request, res: Response, owner: Owner, id: string
65
  const path = `${owner}/${id}/${randomUUID()}.${type.extension}`;
66
  const { error: uploadError } = await supabaseAdmin.storage.from(BUCKET).upload(path, bytes, {
67
  contentType: type.mime,
68
- cacheControl: "3600",
69
  upsert: false,
70
  });
71
  if (uploadError) throw new Error(`Unable to upload profile image: ${uploadError.message}`);
 
48
  const type = path.endsWith(".png") ? "image/png" : path.endsWith(".webp") ? "image/webp" : "image/jpeg";
49
  res.set({
50
  "Content-Type": type,
51
+ // Each upload gets a new UUID path, so this response never needs revalidation.
52
+ "Cache-Control": "private, max-age=31536000, immutable",
53
  "Cross-Origin-Resource-Policy": "cross-origin",
54
  "X-Content-Type-Options": "nosniff",
55
  });
 
66
  const path = `${owner}/${id}/${randomUUID()}.${type.extension}`;
67
  const { error: uploadError } = await supabaseAdmin.storage.from(BUCKET).upload(path, bytes, {
68
  contentType: type.mime,
69
+ cacheControl: "31536000",
70
  upsert: false,
71
  });
72
  if (uploadError) throw new Error(`Unable to upload profile image: ${uploadError.message}`);