You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -77,8 +77,8 @@ AWS_ACCESS_KEY_ID=<key-id> \
77
77
AWS_SECRET_ACCESS_KEY=<secret> \
78
78
swift run Server
79
79
```
80
-
Image generation is capped at 15 images per UTC day. The backend stores a stable per-page image key so repeat requests for the same page reuse the existing image instead of generating again. When the daily budget is exhausted for a first-time page request, the server assigns that page a random previously generated image.
81
-
Freshly generated image keys are still partitioned by UTC date under `IMAGE_GEN_PREFIX/YYYY/MM/DD/`, and the stable page cache lives under `IMAGE_GEN_PREFIX/page-cache/`.
80
+
Image generation is capped at 15 images per UTC day. The backend stores a daily per-page image key so repeat requests for the same page on the same UTC day reuse the existing image instead of generating again. When the daily budget is exhausted for a first-time page request, the server assigns that page a random previously generated image.
81
+
Freshly generated image keys are partitioned by UTC date under `IMAGE_GEN_PREFIX/YYYY/MM/DD/`, and daily page cache keys live under `IMAGE_GEN_PREFIX/page-cache/YYYY/MM/DD/`.
82
82
83
83
Point the site generator at the backend API when building the HTML:
Copy file name to clipboardExpand all lines: SPEC.md
+30-27Lines changed: 30 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,8 +4,9 @@
4
4
Implement a web app where:
5
5
- The page loads in the browser as a SwiftWASM app.
6
6
- The app automatically requests an image on the home page, article pages, and paginated archive pages.
7
-
- A same-session revisit of the same page reuses that page's last returned image from client-side session storage when available.
8
-
- The backend persists a stable per-page image key so repeat requests for the same page and request country reuse the existing image instead of generating a new one.
7
+
- A same-session revisit of the same page on the same UTC day reuses that page's last returned image from client-side session storage when available.
8
+
- The backend persists a daily per-page image key so repeat requests for the same page, UTC day, and request country reuse the existing image instead of generating a new one.
9
+
- Returning on the next UTC day uses a new page-cache key, causing the backend to generate or assign a new image for that page.
9
10
- When the daily generation budget is exhausted, the backend returns a random previously generated image instead of requesting a new one.
10
11
- The backend waits for image generation to finish before replying.
11
12
- The final image is rendered from a public S3 HTTPS URL.
@@ -24,16 +25,16 @@ Implement a web app where:
24
25
### 2.2 High-Level Flow
25
26
1. The browser loads the SwiftWASM bundle and page HTML.
26
27
2. The app reads the page context from the mount element.
27
-
3. If session storage already contains an image URL for the same page path and page type, the app reuses that URL and skips the API call.
28
+
3. If session storage already contains an image URL for the same page path, page type, and UTC day, the app reuses that URL and skips the API call.
28
29
4. Otherwise, the app calls `POST <API_URL>` with page context.
29
30
5. The server derives the client IP from proxy forwarding headers, preferring `X-Real-IP` when present and otherwise falling back to `X-Forwarded-For`, then looks up the origin country with `country.is`.
30
-
6. The server validates input and checks for a stable page-cache key derived from page context and resolved country before considering a new generation.
31
+
6. The server validates input and checks for a daily page-cache key derived from the current UTC date, page context, and resolved country before considering a new generation.
31
32
7. If the page-cache key already exists, the server returns that image immediately.
32
33
8. Otherwise, the server counts generated PNG objects already present under the current UTC day prefix in S3 to decide whether its soft daily generation budget has remaining capacity.
33
-
9. If budget remains, the server creates a fresh unique dated image key, builds a country-aware prompt when country lookup succeeded, calls OpenAI, uploads the PNG to S3, writes the same image to the stable page-cache key, and returns `200 OK`.
34
-
10. If the daily budget is exhausted, the server selects a random existing generated PNG from S3, copies it to the stable page-cache key, and returns `200 OK` with that page-cache image instead.
35
-
9. The app swaps the placeholder image source to the returned or cached URL.
36
-
10. On successful API responses, the app stores the returned image URL in session storage for future visits to the same page in the current browser session.
34
+
9. If budget remains, the server creates a fresh unique dated image key, builds a country-aware prompt when country lookup succeeded, calls OpenAI, uploads the PNG to S3, writes the same image to the daily page-cache key, and returns `200 OK`.
35
+
10. If the daily budget is exhausted, the server selects a random existing generated PNG from S3, copies it to the daily page-cache key, and returns `200 OK` with that page-cache image instead.
36
+
11. The app swaps the placeholder image source to the returned or cached URL.
37
+
12. On successful API responses, the app stores the returned image URL in session storage with the current UTC date for future visits to the same page on the same UTC day in the current browser session.
37
38
38
39
### 2.3 Published SwiftWASM Assets
39
40
- The `BytesizedCafe` SwiftWASM package is built into the repo-root `bytesized-cafe-app/` directory.
@@ -58,17 +59,17 @@ The backend derives the public image origin from `GENERATED_IMAGES_BUCKET` and `
58
59
- Freshly generated image:
59
60
-`{IMAGE_GEN_PREFIX}/{YYYY}/{MM}/{DD}/{UUID}-{country-slug}.png` when the request country is known
60
61
-`{IMAGE_GEN_PREFIX}/{YYYY}/{MM}/{DD}/{UUID}.png` when the request country is not known
61
-
-Stable page-cache image:
62
-
-`{IMAGE_GEN_PREFIX}/page-cache/{pageType}/{normalized-page-path}-{country-slug}.png` when the request country is known
63
-
-`{IMAGE_GEN_PREFIX}/page-cache/{pageType}/{normalized-page-path}-anywhere.png` when the request country is not known
62
+
-Daily page-cache image:
63
+
-`{IMAGE_GEN_PREFIX}/page-cache/{YYYY}/{MM}/{DD}/{pageType}/{normalized-page-path}-{country-slug}.png` when the request country is known
64
+
-`{IMAGE_GEN_PREFIX}/page-cache/{YYYY}/{MM}/{DD}/{pageType}/{normalized-page-path}-anywhere.png` when the request country is not known
64
65
- Random fallback image:
65
-
- Prefer an existing PNG under `IMAGE_GEN_PREFIX/` whose key ends in the current request's `-{country-slug}.png`
66
-
- Fall back to any existing PNG under `IMAGE_GEN_PREFIX/` when no country-matching image is available
66
+
- Prefer an existing PNG under the current UTC date prefix whose key ends in the current request's `-{country-slug}.png`
67
+
- Fall back to any existing PNG under the current UTC date prefix when no country-matching image is available
67
68
68
69
Rules:
69
70
- Fresh generation keys must not be derived from page context.
70
-
-Stable page-cache keys must be derived from page context and resolved country.
71
-
- API responses should prefer the stable page-cache key whenever one exists or is created during the request.
71
+
-Daily page-cache keys must be derived from the current UTC date, page context, and resolved country.
72
+
- API responses should prefer the daily page-cache key whenever one exists or is created during the request.
-`url` is the final public image URL and must use the generated-images bucket public origin.
126
-
- The response may return a stable per-page cache key when the page already has an assigned image.
127
+
- The response may return a daily per-page cache key when the page already has an assigned image for the current UTC day.
127
128
- Return `200` only after the image has been uploaded successfully or a random fallback image has been selected successfully.
128
129
- Invalid input returns `4xx`.
129
130
- If the daily budget is exhausted and no fallback image exists, return `503`.
@@ -136,7 +137,7 @@ Rules:
136
137
- Encapsulate S3 operations behind one `S3ImageStore` client object that owns the bucket configuration and AWS client lifecycle for image upload and lookup operations.
137
138
- Resolve the client IP address by preferring `X-Real-IP` when present and otherwise falling back to `X-Forwarded-For`.
138
139
- Look up the request origin country with `https://api.country.is/{ip}` and convert the returned region code into an English country name when available.
139
-
- Derive a stable page-cache key from page context and resolved country, and return it immediately when that object already exists in S3.
140
+
- Derive a daily page-cache key from the current UTC date, page context, and resolved country, and return it immediately when that object already exists in S3.
140
141
- Check the soft daily generation budget by counting PNG objects already present under the current UTC date prefix in S3.
141
142
- Build the public `url`.
142
143
- When budget remains:
@@ -147,21 +148,21 @@ Rules:
147
148
- Fall back to the same prompt structure scoped to somewhere in the world when the client IP or country cannot be resolved.
148
149
- Call the OpenAI image generation API with model `gpt-image-1.5`.
149
150
- Upload the PNG to the generated image key used for the dated generation pool.
150
-
- Upload the same PNG to the stable page-cache key.
151
+
- Upload the same PNG to the daily page-cache key.
151
152
- Return the page-cache `url`.
152
153
- When budget is exhausted:
153
-
- Prefer a random existing generated PNG key from S3 whose key suffix matches the current request country.
154
-
- Fall back to a random existing generated PNG key from S3 when no country-matching key is available.
155
-
- Copy the selected fallback image to the stable page-cache key without calling OpenAI.
154
+
- Prefer a random existing generated PNG key from the current UTC date prefix whose key suffix matches the current request country.
155
+
- Fall back to a random existing generated PNG key from the current UTC date prefix when no country-matching key is available.
156
+
- Copy the selected fallback image to the daily page-cache key without calling OpenAI.
156
157
- Return the page-cache `url`.
157
158
158
159
## 7. Frontend Behavior
159
160
- Show a loading placeholder immediately.
160
161
- Read page context from the mount element.
161
-
- If session storage contains a URL for the same page path and page type, reuse that URL and skip the API call.
162
+
- If session storage contains a URL for the same page path, page type, and UTC day, reuse that URL and skip the API call.
162
163
- Otherwise, start a single `POST` request to the configured API URL.
163
164
- When the request succeeds, swap the placeholder image source to the returned `url`.
164
-
- Persist the returned image URL in session storage keyed to the current page so the next same-session visit of that page can reuse it.
165
+
- Persist the returned image URL in session storage keyed to the current page and UTC day so the next same-session visit of that page can reuse it only until the UTC day changes.
165
166
166
167
## 8. Environment Variables
167
168
@@ -187,14 +188,15 @@ Local repo tooling may provide `BACKEND_HOST` and `BACKEND_PORT` as aliases for
187
188
188
189
## 9. Validation
189
190
The implementation is considered complete when:
190
-
- A same-session revisit of the same page reuses the last returned image URL from session storage without making a new backend request.
191
-
- A backend request for a page that already has a stable page-cache object returns that existing image URL without making a new OpenAI request.
191
+
- A same-session revisit of the same page on the same UTC day reuses the last returned image URL from session storage without making a new backend request.
192
+
- A same-session revisit of the same page after the UTC day changes makes a backend request instead of reusing yesterday's session-storage URL.
193
+
- A backend request for a page that already has a daily page-cache object returns that existing image URL without making a new OpenAI request.
192
194
- The backend returns `200` only after a fresh image upload succeeds or a random fallback image has been selected.
193
195
- When the daily budget is exhausted, the backend returns a random existing generated image instead of making a new OpenAI request.
194
196
- Fresh generations use the request origin country in the prompt when the server can resolve it from the client IP, and otherwise fall back to the generic worldwide prompt.
195
197
- Fresh generations include a country slug suffix in the image key when the request country is known.
196
198
- When the daily budget is exhausted, fallback selection prefers existing images whose keys match the current request country and otherwise falls back to any existing image.
197
-
- The backend persists deterministic per-page cache keys separately from the dated generation pool.
199
+
- The backend persists deterministic daily per-page cache keys separately from the dated generation pool.
198
200
199
201
## 10. Deployment
200
202
@@ -223,3 +225,4 @@ The implementation is considered complete when:
223
225
- The repo's Swift package manifests target Swift tools version `6.3`, the macOS GitHub Actions job installs Swift `6.3.0`, and the SwiftWasm site build uses the compatible `swift-6.3-RELEASE` SDK tag.
224
226
-`Scripts/run-local.sh` provides a one-command local stack for development and opens the local site in the default browser after the backend and static site server are ready.
225
227
- The script rebuilds the `BytesizedCafe` SwiftWASM bundle, regenerates the site with `BYTESIZED_CAFE_API_URL` pointed at a localhost backend, prebuilds the backend to avoid counting SwiftPM compilation against the startup timeout, starts the Hummingbird server, and serves `Output/` over a local static HTTP server.
228
+
-`Scripts/build-bytesized-cafe-app.sh` prefers a SwiftWASM SDK ID matching the active `swift --version` release when multiple WASM SDKs are installed; `SWIFT_WASM_SDK_ID` or `SWIFT_SDK_ID` can still override the auto-detected SDK.
0 commit comments