cloudunity commited on
Commit
58addbe
·
verified ·
1 Parent(s): c04baab

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +106 -13
Dockerfile CHANGED
@@ -1,24 +1,52 @@
1
  # syntax=docker/dockerfile:1
2
  # =============================================================================
3
- # Rochester-restricted Bluesky PDS - single Dockerfile, built for HF Spaces.
4
  #
5
- # Listens on port 7860 (HF forwards the public Space URL to it automatically).
6
- #
7
- # On first boot it generates JWT/admin secrets, persists them in /app/data,
8
- # and prints the admin password to the Space's container logs.
9
  #
10
- # Registration is restricted to emails at PDS_EMAIL_ALLOWED_DOMAINS
11
- # (default: rochesterschools.org). No SMTP configured - email verification
12
- # is skipped entirely; the domain check is the only gate.
13
- #
14
- # NOTE: HF Spaces have ephemeral storage unless you enable persistent storage
15
- # in the Space settings. /app/data holds the SQLite db + generated secrets.
16
  # =============================================================================
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  FROM ghcr.io/bluesky-social/pds:0.4
19
 
20
  USER root
21
 
 
 
 
22
  # Write the email-allowlist patcher into the image.
23
  RUN <<'PATCH'
24
  cat > /app/allowlist-patch.cjs <<'EOF'
@@ -163,6 +191,71 @@ echo "================================================================"
163
 
164
  node allowlist-patch.cjs
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  exec node --enable-source-maps index.ts
167
  EOF
168
  chmod +x /usr/local/bin/entrypoint.sh
@@ -201,8 +294,8 @@ ENV PDS_EMAIL_DISABLE_CONFIRMATION_LINK=true
201
  # variable named PDS_HOSTNAME) so the PDS advertises the right endpoint:
202
  # https://<owner>-<space>.hf.space
203
  # Users' handles will be <name>.<that hostname>.
204
- ENV PDS_HOSTNAME=https://cloudunity-gemma-api1.hf.space
205
 
206
  EXPOSE 7860
207
 
208
- ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
 
1
  # syntax=docker/dockerfile:1
2
  # =============================================================================
3
+ # Rochester-restricted Bluesky PDS + full Bluesky web app - single Dockerfile.
4
  #
5
+ # Stage 1 builds this repo's web client (the bsky.app React Native Web SPA).
6
+ # Stage 2 is the official PDS image, patched at boot to (a) restrict
7
+ # registration to @rochesterschools.org and (b) serve the web app at / with an
8
+ # SPA fallback. One container serves both the app UI and the AT Protocol API.
9
  #
10
+ # Listens on port 7860 (HF forwards the public Space URL to it automatically).
 
 
 
 
 
11
  # =============================================================================
12
 
13
+ # ---------------------------------------------------------------- build stage
14
+ FROM node:22-bookworm-slim AS webapp
15
+
16
+ RUN corepack enable
17
+ WORKDIR /build
18
+
19
+ # Copy the whole repo (needed by the Expo/webpack build)
20
+ COPY . .
21
+
22
+ # pnpm install with build scripts allowed (better-sqlite3 etc. are not needed
23
+ # for web, but the lockfile requires the steps to run or be skipped)
24
+ RUN pnpm install --frozen-lockfile || pnpm install --no-frozen-lockfile
25
+
26
+ # Compile i18n catalogs (required by the build; normally done by CI)
27
+ RUN pnpm intl:build || pnpm intl:compile || true
28
+
29
+ # Build the static web app with this PDS baked in as the default service,
30
+ # so sign-in/sign-up default to this server (users can still pick another).
31
+ # On HF Spaces, set a Space variable PDS_HOSTNAME and rebuild so this matches.
32
+ ARG PDS_HOSTNAME=hf.space
33
+ ENV EXPO_PUBLIC_DEFAULT_SERVICE=https://${PDS_HOSTNAME}
34
+ ENV EXPO_PUBLIC_BUNDLE_IDENTIFIER=rochester-pds
35
+ ENV NODE_ENV=production
36
+ RUN pnpm build-web || (npx expo export:web && node ./scripts/post-web-build.js)
37
+
38
+ # post-web-build copies assets into bskyweb/static; for static hosting we serve
39
+ # web-build directly, so just make sure it exists
40
+ RUN test -d web-build
41
+
42
+ # --------------------------------------------------------------- server stage
43
  FROM ghcr.io/bluesky-social/pds:0.4
44
 
45
  USER root
46
 
47
+ # Web app bundle
48
+ COPY --from=webapp /build/web-build /app/web
49
+
50
  # Write the email-allowlist patcher into the image.
51
  RUN <<'PATCH'
52
  cat > /app/allowlist-patch.cjs <<'EOF'
 
191
 
192
  node allowlist-patch.cjs
193
 
194
+ # Serve the bundled Bluesky web app at / with an SPA fallback. Patched into
195
+ # basic-routes.js so the middleware is registered before the '/' banner route;
196
+ # the fallback skips API paths (/xrpc, /.well-known, /tls-check, files with
197
+ # extensions) so the PDS API keeps working. Idempotent.
198
+ node - <<'WEBAPP'
199
+ const fs = require('node:fs')
200
+ const path = require('node:path')
201
+ const MARKER = '__ROCHESTER_WEB_APP__'
202
+ function findBasicRoutes(dir) {
203
+ try {
204
+ for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
205
+ const full = path.join(dir, entry.name)
206
+ if (entry.isDirectory()) {
207
+ const found = findBasicRoutes(full)
208
+ if (found) return found
209
+ } else if (entry.name === 'basic-routes.js') {
210
+ return full
211
+ }
212
+ }
213
+ } catch {}
214
+ return null
215
+ }
216
+ const target = findBasicRoutes('/app/node_modules') || findBasicRoutes('/app/node_modules/.pnpm')
217
+ if (!target) {
218
+ console.error('[webapp] could not locate basic-routes.js')
219
+ process.exit(1)
220
+ }
221
+ let src = fs.readFileSync(target, 'utf8')
222
+ if (src.includes(MARKER)) {
223
+ console.log('[webapp] already patched, skipping')
224
+ process.exit(0)
225
+ }
226
+ const anchor = 'const router = Router()'
227
+ if (!src.includes(anchor)) {
228
+ console.error('[webapp] injection point not found in basic-routes.js')
229
+ process.exit(1)
230
+ }
231
+ if (!src.includes("from 'express'") || !src.includes("import { Router }")) {
232
+ console.error('[webapp] unexpected imports in basic-routes.js')
233
+ process.exit(1)
234
+ }
235
+ const injection = [
236
+ anchor,
237
+ ' // ' + MARKER + ' start',
238
+ " const webDir = '/app/web'",
239
+ ' router.use(express.static(webDir, {index: \'index.html\', maxAge: \'1h\'}))',
240
+ ' router.get(\'*\', function (req, res, next) {',
241
+ " if (req.path.startsWith('/xrpc/') || req.path.startsWith('/.') || req.path.startsWith('/tls-check')) return next()",
242
+ ' if (path.extname(req.path)) return next()',
243
+ " res.sendFile(path.join(webDir, 'index.html'))",
244
+ ' })',
245
+ ' // ' + MARKER + ' end',
246
+ ].join('\n')
247
+ src = src.replace(anchor, injection)
248
+ if (!src.match(/^import path\b/m)) {
249
+ src = "import path from 'node:path'\n" + src
250
+ }
251
+ // express.static needs the default export; basic-routes only imports {Router}
252
+ if (!src.match(/^import express\b/m)) {
253
+ src = "import express from 'express'\n" + src
254
+ }
255
+ fs.writeFileSync(target, src)
256
+ console.log('[webapp] web app serving patched into ' + target)
257
+ WEBAPP
258
+
259
  exec node --enable-source-maps index.ts
260
  EOF
261
  chmod +x /usr/local/bin/entrypoint.sh
 
294
  # variable named PDS_HOSTNAME) so the PDS advertises the right endpoint:
295
  # https://<owner>-<space>.hf.space
296
  # Users' handles will be <name>.<that hostname>.
297
+ ENV PDS_HOSTNAME=cloudunity-gemma-api1.hf.space
298
 
299
  EXPOSE 7860
300
 
301
+ ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]