Ng eventually #1
+11
-2
@@ -18,10 +18,19 @@
|
||||
FESTIPOD_SHARED_WALLET_PASSWORD=
|
||||
|
||||
# Chemin vers le fichier portefeuille partagé (.ngw), absolu ou relatif à la
|
||||
# racine. Servi en téléchargement à /shared-wallet.ngw — par le build de l'app,
|
||||
# et par le serveur du harness pendant les tests.
|
||||
# racine. Servi en téléchargement à /shared-wallet.ngw. C'est la forme du DEV
|
||||
# LOCAL et du harness de TESTS : le fichier vit sur le disque de la machine.
|
||||
FESTIPOD_SHARED_WALLET_FILE=/chemin/absolu/vers/festipod-wallet.ngw
|
||||
|
||||
# Contenu du portefeuille partagé (.ngw), encodé en base64 — deuxième source
|
||||
# pour le même fichier. C'est la forme des DÉPLOIEMENTS (conteneur) : *.ngw est
|
||||
# gitignoré, donc `COPY . .` n'en embarque aucun et rien n'en monte un ; le
|
||||
# fichier n'étant pas un secret (l'app le sert à quiconque ouvre l'app), il
|
||||
# voyage comme une variable de config. `FESTIPOD_SHARED_WALLET_FILE` est
|
||||
# prioritaire quand les deux sont renseignées — voir le commentaire dans
|
||||
# src/index.ts. Générer la valeur avec, p.ex., `base64 -w0 festipod-wallet.ngw`.
|
||||
FESTIPOD_SHARED_WALLET_FILE_BASE64=
|
||||
|
||||
# ── Seed automatique (opt-in) ──────────────────────────────────────────────
|
||||
# Non vide => l'app amorce des données de démo dans un wallet VIDE au 1er login.
|
||||
# OFF par défaut : laisser vide en usage normal.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Doc-debt — tech-stack
|
||||
|
||||
> Presence of a block = doc to update. Processed → delete the block; no blocks left → delete this file.
|
||||
> One block = one "big change": `why` + `files` + `verify` (leaves to review).
|
||||
|
||||
## Raw markers (consolidate into blocks, then delete)
|
||||
- TOUCHED src/index.ts @2026-08-17 (session 0b064e8b-1717-421f-a20e-a4318ad217b1)
|
||||
+1
-1
@@ -3,7 +3,7 @@ FROM oven/bun:1-alpine AS base
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies with pnpm.
|
||||
# - git: the @ng-eventually/client polyfill is a git+https (public Gitea) dependency → no auth.
|
||||
# - git: @ng-eventually/polyfill is a git+https (public Gitea) dependency → no auth.
|
||||
# - nodejs + npm: pnpm is a Node CLI; we pin the exact pnpm version via `npm i -g`
|
||||
# (Alpine's nodejs package does not bundle corepack).
|
||||
# The `bun` npm peer (pulled by bun-plugin-tailwind) is approved to build in package.json
|
||||
|
||||
+53
-3
@@ -3,6 +3,24 @@ import index from "./index.html";
|
||||
|
||||
const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
||||
|
||||
// Strict base64 check (not a mere `Buffer.from` attempt, which silently drops invalid
|
||||
// characters instead of failing): reject anything that is not a well-formed base64 body
|
||||
// before decoding, so a typo'd env var is reported instead of served as 810 garbage bytes.
|
||||
const BASE64_SHAPE = /^[A-Za-z0-9+/]+={0,2}$/;
|
||||
|
||||
function decodeBase64WalletOrThrow(raw: string) {
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed.length === 0 || trimmed.length % 4 !== 0 || !BASE64_SHAPE.test(trimmed)) {
|
||||
throw new Error("not valid base64 (bad characters, or length not a multiple of 4)");
|
||||
}
|
||||
// Web `atob` (not Node's `Buffer`, whose `ArrayBufferLike` generic doesn't line up
|
||||
// with `Response`'s `BodyInit`) — decodes to a binary string, rebuilt into bytes below.
|
||||
const binary = atob(trimmed);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
const server = serve({
|
||||
port,
|
||||
routes: {
|
||||
@@ -54,12 +72,44 @@ const server = serve({
|
||||
}),
|
||||
|
||||
// The shared wallet file — the `fileUrl` the app hands the SDK, when configured.
|
||||
//
|
||||
// Two sources, read fresh on every request (never captured at module evaluation):
|
||||
// - FESTIPOD_SHARED_WALLET_FILE: a filesystem path. What local dev and the test
|
||||
// harness set today — a file sitting at the working-copy root.
|
||||
// - FESTIPOD_SHARED_WALLET_FILE_BASE64: the file's bytes, base64-encoded. What a
|
||||
// container sets instead, since *.ngw is gitignored and nothing mounts one there.
|
||||
//
|
||||
// Precedence: FILE wins whenever it is set, even if the path turns out unreadable —
|
||||
// it is NOT "whichever resolves". This keeps dev/test behaviour byte-for-byte
|
||||
// unchanged (they set only FILE, never BASE64) and makes the rule predictable: a
|
||||
// deployment picks exactly one variable to set, and setting both is a leftover, not
|
||||
// an intentional fallback chain.
|
||||
"/shared-wallet.ngw": async () => {
|
||||
const p = process.env.FESTIPOD_SHARED_WALLET_FILE;
|
||||
if (p) {
|
||||
const file = Bun.file(p);
|
||||
const path = process.env.FESTIPOD_SHARED_WALLET_FILE;
|
||||
if (path) {
|
||||
const file = Bun.file(path);
|
||||
if (await file.exists()) return new Response(file);
|
||||
return new Response("No shared wallet file configured.", { status: 404 });
|
||||
}
|
||||
|
||||
const encoded = process.env.FESTIPOD_SHARED_WALLET_FILE_BASE64;
|
||||
if (encoded) {
|
||||
// Malformed must fail loudly: a 404 here would look identical to "not
|
||||
// configured", which is exactly the confusion this project is removing.
|
||||
try {
|
||||
const bytes = decodeBase64WalletOrThrow(encoded);
|
||||
return new Response(bytes, {
|
||||
headers: { "Content-Type": "application/octet-stream" },
|
||||
});
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
return new Response(
|
||||
`FESTIPOD_SHARED_WALLET_FILE_BASE64 is set but ${message}.`,
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response("No shared wallet file configured.", { status: 404 });
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user