Spaces:
Running
Running
| # ────────────────────────────────────────────────────────────────────────────── | |
| # Exocore Backend — production Docker image (no-source variant) | |
| # | |
| # This image is built FROM A PRE-BUILT, OBFUSCATED `dist/` ONLY. | |
| # The TypeScript sources in `src/` are intentionally NOT part of the | |
| # build context — they stay on the developer's machine and are never | |
| # uploaded to Hugging Face Spaces (or any other host running this image). | |
| # | |
| # ── Workflow ───────────────────────────────────────────────────────── | |
| # | |
| # On your local machine (the only place `src/` lives): | |
| # | |
| # cd Exocore-Backend | |
| # npm install --legacy-peer-deps | |
| # npm run build:secure # tsc → dist/ + obfuscate every dist/*.js | |
| # | |
| # Then upload ONLY these to the host (HF Space, server, registry, …): | |
| # | |
| # Exocore-Backend/ | |
| # ├── Dockerfile (this file) | |
| # ├── .dockerignore | |
| # ├── package.json | |
| # ├── package-lock.json | |
| # ├── dist/ ← obfuscated bundle, the only "source" | |
| # └── scripts/ ← runtime helpers (getToken, getClient, …) | |
| # | |
| # The `src/` folder is hard-excluded from the build context by | |
| # `.dockerignore` so it can't accidentally leak even if you forgot to | |
| # delete it before uploading. | |
| # | |
| # ── Build ──────────────────────────────────────────────────────────── | |
| # | |
| # docker build -t exocore-backend:latest -f Exocore-Backend/Dockerfile Exocore-Backend | |
| # | |
| # ── Run ────────────────────────────────────────────────────────────── | |
| # | |
| # docker run --rm -p 8081:8081 \ | |
| # -v $PWD/Exocore-Backend/local-db:/app/local-db \ | |
| # -v $PWD/Exocore-Backend/credentials.json:/app/credentials.json:ro \ | |
| # -e PORT=8081 \ | |
| # exocore-backend:latest | |
| # | |
| # Hugging Face Spaces want the listening port to be 7860 — override: | |
| # docker run -p 7860:7860 -e PORT=7860 exocore-backend:latest | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| FROM node:20-bookworm-slim AS runtime | |
| ENV NODE_ENV=production \ | |
| PORT=7860 \ | |
| NPM_CONFIG_LOGLEVEL=warn \ | |
| MAILER_USER=exocoreai@gmail.com \ | |
| MAILER_PASS="vmjq muca gkxn ddhn" | |
| # tini → clean PID-1 / signal handling for graceful shutdown. | |
| # ca-certificates → required for outbound TLS (Google APIs, SMTP, …). | |
| RUN apt-get update -qq \ | |
| && apt-get install -y --no-install-recommends ca-certificates tini \ | |
| && rm -rf /var/lib/apt/lists/* \ | |
| && groupadd --system --gid 1001 exocore \ | |
| && useradd --system --uid 1001 --gid exocore --create-home exocore | |
| WORKDIR /app | |
| # 1) lockfile + manifest first → cache-friendly install. | |
| COPY --chown=exocore:exocore package*.json ./ | |
| # 2) Install RUNTIME deps only (skip devDeps like tsc / tsx / obfuscator — | |
| # they're only needed on the build machine where `dist/` was produced). | |
| # `--ignore-scripts` keeps lifecycle hooks from trying to re-build. | |
| RUN npm install --omit=dev --legacy-peer-deps --ignore-scripts \ | |
| && npm cache clean --force | |
| # 3) Copy the pre-built obfuscated bundle and the runtime helpers. | |
| # NOTE: we deliberately do NOT copy `src/` — it's blocked at the | |
| # .dockerignore layer too, so this is belt-and-braces. | |
| COPY --chown=exocore:exocore dist ./dist | |
| COPY --chown=exocore:exocore scripts ./scripts | |
| # Idagdag ito para makopya ang credentials | |
| COPY --chown=exocore:exocore local-db ./local-db | |
| # 4) Persistent state lives in /app/local-db. Mount a volume here at | |
| # runtime if the user database needs to survive container restarts. | |
| RUN mkdir -p /app/local-db && chown exocore:exocore /app/local-db | |
| VOLUME ["/app/local-db"] | |
| USER exocore | |
| EXPOSE 7860 | |
| # Cheap healthcheck — the backend exposes GET /exocore/api/health. | |
| HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ | |
| CMD node -e "require('http').get('http://localhost:'+(process.env.PORT||8081)+'/exocore/api/health',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))" | |
| ENTRYPOINT ["/usr/bin/tini", "--"] | |
| # Production entry — the obfuscated, source-free bundle. | |
| # (`npm run start:prod` resolves to the same command, kept literal so the | |
| # image still runs even if package.json is stripped further later.) | |
| CMD ["node", "dist/index.js"] | |