Deploy backend; app uses the deployed Worker !12

merged merged by cmc on 2026-10-18 01:54 UTC · krz/moonrise:deploy-backend into main

7 files changed, +36 −7

Layout: unified · split

App/README.org +4
@@ -11,3 +11,7 @@ open Moonrise.xcodeproj
1111Simulator: an iOS 26+ runtime is required. Push registration needs a
1212signed build (team =ZCNAX3VL9D=); the APNs device token is logged under
1313subsystem =sh.krz.moonrise=.
14
15Backend: both configurations use https://moonrise-server.audit-labs.workers.dev
16(=MOONRISE_API_URL= in =project.yml=). For a local Worker, set it to
17=http://localhost:8787= and regenerate.
App/project.yml +2 −2
@@ -32,9 +32,9 @@ targets:
3232 settings:
3333 configs:
3434 Debug:
35 MOONRISE_API_URL: http://localhost:8787
35 MOONRISE_API_URL: https://moonrise-server.audit-labs.workers.dev
3636 Release:
37 MOONRISE_API_URL: ""
37 MOONRISE_API_URL: https://moonrise-server.audit-labs.workers.dev
3838 base:
3939 PRODUCT_BUNDLE_IDENTIFIER: sh.krz.moonrise
4040 DEVELOPMENT_TEAM: ZCNAX3VL9D
PLAN.org +11
@@ -374,6 +374,17 @@ MR !11.
374374- Not done: tap a planet or probe for its Library page, Earth–Moon inset,
375375 active missions (Juno, Europa Clipper, JUICE, Psyche, Parker, Lucy).
376376
377** Deployment (2026-10-18)
378- Worker =moonrise-server= at https://moonrise-server.audit-labs.workers.dev,
379 D1 =moonrise=, crons =*/5= (SWPC) and =*/10= (LL2), APNS_KEY set.
380- SWPC polling confirmed in production (=/v1/today= serving Kp).
381- LL2 from Cloudflare returned 429 on the first run: the anonymous limit
382 is per IP and Workers share egress IPs. Needs an LL2 API key
383 (=LL2_API_KEY= secret, supported). Launches stay empty until then.
384- App Debug and Release builds use the deployed URL.
385- The app does not register devices with the backend yet, so no aurora
386 pushes are sent.
387
377388* roadmap
378389** v1: now
379390Sky, System, Sun, Launches, Library core (above).
server/README.org +10
@@ -15,6 +15,9 @@ APNs cannot be reached from =wrangler dev= (no HTTP/2); test pushes
1515against a deployed Worker.
1616
1717* deploy
18Deployed 2026-10-17: https://moonrise-server.audit-labs.workers.dev
19(D1 =moonrise= 54a0c50d-2150-47ae-aa22-449e8204ad7b, APNS_KEY set).
20
1821#+begin_src sh
1922wrangler d1 create moonrise # put the id in wrangler.jsonc
2023wrangler d1 migrations apply moonrise --remote
@@ -22,6 +25,13 @@ wrangler deploy
2225wrangler secret put APNS_KEY < AuthKey_644XS4F2RG.p8
2326#+end_src
2427
28Launch Library 2's anonymous limit is per IP, and Workers share egress IPs,
29so anonymous requests from Cloudflare often get HTTP 429. With an API key:
30
31#+begin_src sh
32wrangler secret put LL2_API_KEY
33#+end_src
34
2535* API
2636| Method and path | Body / result |
2737|-
server/src/index.ts +3 −1
@@ -9,6 +9,8 @@ interface Env {
99 APNS_TEAM_ID: string;
1010 APNS_TOPIC: string;
1111 APNS_KEY: string;
12 /** Optional Launch Library 2 API key. */
13 LL2_API_KEY?: string;
1214}
1315
1416interface DeviceRequest {
@@ -99,7 +101,7 @@ async function launches(env: Env, now: Date): Promise<Response> {
99101async function pollLaunches(env: Env, now: Date): Promise<void> {
100102 let list: Launch[];
101103 try {
102 list = await fetchUpcoming();
104 list = await fetchUpcoming(env.LL2_API_KEY);
103105 } catch (e) {
104106 console.error("ll2", String(e));
105107 await env.DB.prepare("INSERT INTO feed_snapshots (feed, fetched_at, ok) VALUES ('ll2', ?1, 0) ON CONFLICT (feed) DO UPDATE SET fetched_at = ?1, ok = 0").bind(now.toISOString()).run();
server/src/ll2.ts +5 −2
@@ -63,8 +63,11 @@ export function normalize(r: Raw): Launch {
6363 };
6464}
6565
66export async function fetchUpcoming(): Promise<Launch[]> {
67 const res = await fetch(UPCOMING_URL, { headers: { "user-agent": "moonrise-server (sh.krz.moonrise)" } });
66/** `apiKey` lifts the anonymous per-IP limit, which Cloudflare's shared egress IPs often exhaust (HTTP 429). */
67export async function fetchUpcoming(apiKey?: string): Promise<Launch[]> {
68 const headers: Record<string, string> = { "user-agent": "moonrise-server (sh.krz.moonrise)" };
69 if (apiKey) headers.authorization = `Token ${apiKey}`;
70 const res = await fetch(UPCOMING_URL, { headers });
6871 if (!res.ok) throw new Error(`LL2 ${res.status}`);
6972 const body = (await res.json()) as { results: Raw[] };
7073 return body.results.map(normalize);
server/wrangler.jsonc +1 −2
@@ -7,8 +7,7 @@
77 {
88 "binding": "DB",
99 "database_name": "moonrise",
10 // Replace with the id from `wrangler d1 create moonrise` before deploying.
11 "database_id": "00000000-0000-0000-0000-000000000000",
10 "database_id": "54a0c50d-2150-47ae-aa22-449e8204ad7b",
1211 "migrations_dir": "migrations"
1312 }
1413 ],