CJHauser commited on
Commit
c293b5b
·
verified ·
1 Parent(s): a751927

Create Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +239 -0
Dockerfile ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM oven/bun:1-slim
2
+
3
+ ARG DEBIAN_FRONTEND=noninteractive
4
+ RUN apt-get update && apt-get install -y --no-install-recommends \
5
+ git ca-certificates curl sqlite3 rsync python3 \
6
+ && rm -rf /var/lib/apt/lists/*
7
+
8
+ WORKDIR /app
9
+
10
+ # Clone app
11
+ ARG LUMIVERSE_REPO=https://github.com/prolix-oc/Lumiverse.git
12
+ ARG LUMIVERSE_REF=staging
13
+ RUN git clone --depth 1 --branch "${LUMIVERSE_REF}" "${LUMIVERSE_REPO}" .
14
+
15
+ # Patch auth rewrite for HF reverse proxy
16
+ RUN cat > /tmp/patch-auth-rewrite.mjs <<'PATCH'
17
+ import { readFileSync, writeFileSync } from "fs";
18
+
19
+ const path = "src/app.ts";
20
+ const src = readFileSync(path, "utf8");
21
+
22
+ const before = `app.on(["POST", "GET"], "/api/auth/*", (c) => {
23
+ const host = c.req.header("host");
24
+ if (host) {
25
+ const url = new URL(c.req.url);
26
+ const rewritten = new URL(url.pathname + url.search, \`http://\${host}\`);
27
+ return auth.handler(new Request(rewritten.toString(), c.req.raw));
28
+ }
29
+ return auth.handler(c.req.raw);
30
+ });`;
31
+
32
+ const after = `app.on(["POST", "GET"], "/api/auth/*", (c) => {
33
+ const host = c.req.header("x-forwarded-host") || c.req.header("host");
34
+ const proto = c.req.header("x-forwarded-proto") || "http";
35
+
36
+ if (host) {
37
+ const url = new URL(c.req.url);
38
+ const rewritten = new URL(url.pathname + url.search, \`\${proto}://\${host}\`);
39
+ return auth.handler(new Request(rewritten.toString(), c.req.raw));
40
+ }
41
+ return auth.handler(c.req.raw);
42
+ });`;
43
+
44
+ if (!src.includes(before)) {
45
+ console.error("[patch] Expected auth rewrite block not found in src/app.ts");
46
+ process.exit(1);
47
+ }
48
+
49
+ writeFileSync(path, src.replace(before, after), "utf8");
50
+ console.log("[patch] Patched src/app.ts for x-forwarded-proto/host");
51
+ PATCH
52
+
53
+ RUN bun /tmp/patch-auth-rewrite.mjs \
54
+ && grep -n "x-forwarded-proto" src/app.ts >/dev/null
55
+
56
+ # Install deps
57
+ RUN rm -f package-lock.json && bun install --production
58
+
59
+ WORKDIR /app/frontend
60
+ RUN rm -f package-lock.json && bun install && bun run build
61
+ # neutralize service worker to avoid cache loops
62
+ RUN printf "self.addEventListener('install',e=>self.skipWaiting());self.addEventListener('activate',e=>e.waitUntil(self.clients.claim()));\n" > /app/frontend/dist/sw.js
63
+ RUN test -f /app/frontend/dist/index.html
64
+
65
+ WORKDIR /app
66
+ ENV NODE_ENV=production
67
+ ENV PORT=7860
68
+ ENV DATA_DIR=/app/data
69
+ ENV FRONTEND_DIR=/app/frontend/dist
70
+ ENV TRUST_ANY_ORIGIN=true
71
+
72
+ # Aggressive recovery script
73
+ RUN cat > /app/start.sh <<'SH'
74
+ #!/usr/bin/env sh
75
+ set -eu
76
+
77
+ PERSIST_DIR="${DATA_DIR:-/app/data}"
78
+ WORK_DIR="/tmp/lumi-recovery"
79
+ RECOVERY_DIR="$PERSIST_DIR/recovery"
80
+ DB_NAME="lumiverse.db"
81
+
82
+ mkdir -p "$WORK_DIR" "$RECOVERY_DIR"
83
+
84
+ pick_corrupt() {
85
+ for f in \
86
+ "$PERSIST_DIR/lumiverse.db.corrupt-eeeeeeeeeee" \
87
+ "$PERSIST_DIR/lumiverse.db.corrupt-20260413-213108" \
88
+ "$PERSIST_DIR/lumiverse.db.corrupt"* \
89
+ do
90
+ [ -f "$f" ] && { echo "$f"; return 0; }
91
+ done
92
+ return 1
93
+ }
94
+
95
+ CORRUPT_SRC="$(pick_corrupt || true)"
96
+ OUT_DB="$RECOVERY_DIR/$DB_NAME"
97
+ LIVE_DB="$PERSIST_DIR/$DB_NAME"
98
+
99
+ copy_backup() {
100
+ src="$1"
101
+ base="$(basename "$src")"
102
+ cp "$src" "$RECOVERY_DIR/$base.bak"
103
+ cp "$src" "$WORK_DIR/$base"
104
+ }
105
+
106
+ try_integrity() {
107
+ db="$1"
108
+ sqlite3 "$db" "PRAGMA integrity_check;" 2>/dev/null | grep -qx "ok"
109
+ }
110
+
111
+ try_recover() {
112
+ src="$1"
113
+ dst="$2"
114
+ echo "[recover] Trying .recover from $(basename "$src")"
115
+ if sqlite3 "$src" ".recover" | sqlite3 "$dst"; then
116
+ if try_integrity "$dst"; then
117
+ echo "[recover] .recover succeeded"
118
+ return 0
119
+ fi
120
+ fi
121
+ return 1
122
+ }
123
+
124
+ try_dump() {
125
+ src="$1"
126
+ dst="$2"
127
+ echo "[recover] Trying .dump from $(basename "$src")"
128
+ if sqlite3 "$src" ".dump" > "$WORK_DIR/dump.sql" 2>/dev/null; then
129
+ if [ -s "$WORK_DIR/dump.sql" ]; then
130
+ if sqlite3 "$dst" < "$WORK_DIR/dump.sql" 2>/dev/null; then
131
+ if try_integrity "$dst"; then
132
+ echo "[recover] .dump succeeded"
133
+ return 0
134
+ fi
135
+ fi
136
+ fi
137
+ fi
138
+ return 1
139
+ }
140
+
141
+ try_schema_heal() {
142
+ src="$1"
143
+ dst="$2"
144
+ echo "[recover] Trying schema extraction"
145
+ # Extract table names from sqlite_master if possible
146
+ sqlite3 "$src" "SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';" \
147
+ > "$WORK_DIR/schema.tsv" 2>/dev/null || true
148
+
149
+ if [ ! -s "$WORK_DIR/schema.tsv" ]; then
150
+ return 1
151
+ fi
152
+
153
+ # Recreate schema from the statements we could read.
154
+ while IFS='|' read -r name sql; do
155
+ [ -n "${sql:-}" ] || continue
156
+ echo "$sql;" >> "$WORK_DIR/schema.sql"
157
+ done < "$WORK_DIR/schema.tsv"
158
+
159
+ if [ ! -s "$WORK_DIR/schema.sql" ]; then
160
+ return 1
161
+ fi
162
+
163
+ sqlite3 "$dst" < "$WORK_DIR/schema.sql" 2>/dev/null || return 1
164
+ if try_integrity "$dst"; then
165
+ echo "[recover] schema-only restore succeeded"
166
+ return 0
167
+ fi
168
+ return 1
169
+ }
170
+
171
+ recover_attempt() {
172
+ src="$1"
173
+ rm -f "$OUT_DB" "$WORK_DIR/recovered.db" "$WORK_DIR/dump.sql" "$WORK_DIR/schema.sql" "$WORK_DIR/schema.tsv" 2>/dev/null || true
174
+
175
+ copy_backup "$src"
176
+
177
+ if try_integrity "$WORK_DIR/$(basename "$src")"; then
178
+ echo "[recover] Source file is already healthy"
179
+ cp "$WORK_DIR/$(basename "$src")" "$OUT_DB"
180
+ return 0
181
+ fi
182
+
183
+ if try_recover "$WORK_DIR/$(basename "$src")" "$OUT_DB"; then
184
+ return 0
185
+ fi
186
+
187
+ rm -f "$OUT_DB" 2>/dev/null || true
188
+ if try_dump "$WORK_DIR/$(basename "$src")" "$OUT_DB"; then
189
+ return 0
190
+ fi
191
+
192
+ rm -f "$OUT_DB" 2>/dev/null || true
193
+ if try_schema_heal "$WORK_DIR/$(basename "$src")" "$OUT_DB"; then
194
+ return 0
195
+ fi
196
+
197
+ return 1
198
+ }
199
+
200
+ if [ -n "${CORRUPT_SRC:-}" ]; then
201
+ echo "[recover] Source candidate: $CORRUPT_SRC"
202
+ if recover_attempt "$CORRUPT_SRC"; then
203
+ echo "[recover] Recovery completed: $OUT_DB"
204
+ cp "$OUT_DB" "$LIVE_DB"
205
+ rm -f "$LIVE_DB-wal" "$LIVE_DB-shm" 2>/dev/null || true
206
+ else
207
+ echo "[recover] All recovery attempts failed"
208
+ fi
209
+ else
210
+ echo "[recover] No corrupt DB found"
211
+ fi
212
+
213
+ # If recovery produced a DB, use it as the app DB.
214
+ if [ -f "$LIVE_DB" ]; then
215
+ CHECK="$(sqlite3 "$LIVE_DB" "PRAGMA integrity_check;" 2>/dev/null || true)"
216
+ if [ "$CHECK" = "ok" ]; then
217
+ echo "[db-check] Recovered/live DB integrity ok"
218
+ else
219
+ echo "[db-check] Live DB still not healthy; app may create a fresh one"
220
+ rm -f "$LIVE_DB" "$LIVE_DB-wal" "$LIVE_DB-shm" 2>/dev/null || true
221
+ fi
222
+ fi
223
+
224
+ # Make the app use persistent storage as normal.
225
+ export DATA_DIR="$PERSIST_DIR"
226
+
227
+ exec bun run src/index.ts
228
+ SH
229
+
230
+ RUN chmod +x /app/start.sh
231
+
232
+ USER root
233
+ RUN mkdir -p /app/data && chown -R bun:bun /app/data
234
+
235
+ EXPOSE 7860
236
+ VOLUME /app/data
237
+
238
+ USER bun
239
+ CMD ["/app/start.sh"]