diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b3881d5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +node_modules +.next +.git +data +*.db +*.db-* +.env +.env.* +!.env.example +coverage +tests +.github +.cursor +**/*.md +!README.md diff --git a/.env.example b/.env.example index 97401f5..c7563cb 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,17 @@ AUTH_SECRET=change-me-to-a-long-random-string # Optional: path to SQLite file (default ./data/rfid.db) # RFID_DB_PATH=./data/rfid.db +<<<<<<< HEAD +# Docker Compose published port (default 3000) +# RFID_PORT=3000 + +# Optional first-boot user when using Docker entrypoint +# CREATE_USER_EMAIL=admin@lab.local +# CREATE_USER_PASSWORD=changeme +# CREATE_USER_NAME=Admin + +======= +>>>>>>> origin/main # Optional OIDC (Authentik, Keycloak, Authelia, etc.) # AUTH_OIDC_ISSUER=https://sso.example.com/application/o/rfid/ # AUTH_OIDC_CLIENT_ID= diff --git a/Dockerfile b/Dockerfile index 62ce9bb..f134ce6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,18 +2,33 @@ FROM node:22-bookworm-slim AS deps WORKDIR /app +<<<<<<< HEAD +RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ \ + && rm -rf /var/lib/apt/lists/* +======= RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/* +>>>>>>> origin/main COPY package.json package-lock.json ./ RUN npm ci FROM node:22-bookworm-slim AS builder WORKDIR /app +<<<<<<< HEAD +RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ \ + && rm -rf /var/lib/apt/lists/* +======= RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/* +>>>>>>> origin/main COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED=1 ENV AUTH_SECRET=build-time-placeholder-secret-32chars +<<<<<<< HEAD +RUN npm run build \ + && npm prune --omit=dev +======= RUN npm run build +>>>>>>> origin/main FROM node:22-bookworm-slim AS runner WORKDIR /app @@ -21,6 +36,36 @@ ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 ENV RFID_DB_PATH=/data/rfid.db ENV PORT=3000 +<<<<<<< HEAD +ENV HOSTNAME=0.0.0.0 +ENV AUTH_TRUST_HOST=true + +RUN apt-get update && apt-get install -y --no-install-recommends libstdc++6 curl \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /data \ + && chown node:node /data + +COPY --from=builder --chown=node:node /app/package.json /app/package-lock.json ./ +COPY --from=builder --chown=node:node /app/node_modules ./node_modules +COPY --from=builder --chown=node:node /app/.next ./.next +COPY --from=builder --chown=node:node /app/public ./public +COPY --from=builder --chown=node:node /app/scripts ./scripts +COPY --from=builder --chown=node:node /app/src ./src +COPY --from=builder --chown=node:node /app/tsconfig.json ./tsconfig.json +# tsx is needed for create-user / entrypoint bootstrap (devDependency pruned above) +RUN npm install --omit=dev tsx@4.20.5 \ + && chown -R node:node /app/node_modules +COPY --chown=node:node docker/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +USER node +EXPOSE 3000 +VOLUME ["/data"] +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD curl -fsS http://127.0.0.1:3000/login >/dev/null || exit 1 + +ENTRYPOINT ["/entrypoint.sh"] +======= RUN apt-get update && apt-get install -y libstdc++6 && rm -rf /var/lib/apt/lists/* RUN mkdir -p /data && chown node:node /data COPY --from=builder /app/package.json /app/package-lock.json ./ @@ -33,4 +78,5 @@ COPY --from=builder /app/tsconfig.json ./tsconfig.json USER node EXPOSE 3000 VOLUME ["/data"] +>>>>>>> origin/main CMD ["npm", "run", "start"] diff --git a/README.md b/README.md index 80e29a1..5cb4656 100644 --- a/README.md +++ b/README.md @@ -86,16 +86,57 @@ GitHub Actions runs lint, typecheck, and tests on every pull request and push to ## Docker +<<<<<<< HEAD +### docker compose (recommended) + +```bash +cp .env.example .env +# set AUTH_SECRET to a long random string + +# optional first user on boot: +# echo 'CREATE_USER_EMAIL=admin@lab.local' >> .env +# echo 'CREATE_USER_PASSWORD=changeme' >> .env + +docker compose up -d --build +``` + +App: http://localhost:3000 — SQLite persists in the `rfid-data` volume. + +Create a user later (if you skipped bootstrap): + +```bash +docker compose exec rfid-database \ + npx tsx scripts/create-user.ts --email you@lab.local --password 'secret' --name You +``` + +Stop / remove (keeps volume): + +```bash +docker compose down +``` + +### Plain docker + +======= +>>>>>>> origin/main ```bash docker build -t rfid-database . docker run --rm -p 3000:3000 \ -e AUTH_SECRET=your-long-secret \ +<<<<<<< HEAD + -e CREATE_USER_EMAIL=admin@lab.local \ + -e CREATE_USER_PASSWORD=changeme \ +======= +>>>>>>> origin/main -v rfid-data:/data \ rfid-database ``` +<<<<<<< HEAD +======= Create the first user against the mounted DB (exec into the container or run `create-user` with `RFID_DB_PATH` pointed at the volume). +>>>>>>> origin/main ## Security Tag dumps often include sector keys. Keep the app behind HTTPS, do not expose it publicly without auth, and treat `data/rfid.db` as sensitive. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..abed71d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +services: + rfid-database: + build: + context: . + dockerfile: Dockerfile + image: rfid-database:local + container_name: rfid-database + restart: unless-stopped + ports: + - "${RFID_PORT:-3000}:3000" + env_file: + - .env + environment: + NODE_ENV: production + RFID_DB_PATH: /data/rfid.db + PORT: "3000" + HOSTNAME: 0.0.0.0 + AUTH_TRUST_HOST: "true" + # Optional bootstrap (only creates if missing): + # CREATE_USER_EMAIL: admin@lab.local + # CREATE_USER_PASSWORD: change-me + # CREATE_USER_NAME: Admin + # Optional OIDC — also set NEXT_PUBLIC_* in .env if using SSO button + # AUTH_OIDC_ISSUER: ${AUTH_OIDC_ISSUER:-} + # AUTH_OIDC_CLIENT_ID: ${AUTH_OIDC_CLIENT_ID:-} + # AUTH_OIDC_CLIENT_SECRET: ${AUTH_OIDC_CLIENT_SECRET:-} + # AUTH_OIDC_NAME: ${AUTH_OIDC_NAME:-SSO} + volumes: + - rfid-data:/data + healthcheck: + test: ["CMD", "curl", "-fsS", "http://127.0.0.1:3000/login"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 25s + +volumes: + rfid-data: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..fdfcd37 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e + +if [ -z "$AUTH_SECRET" ] || [ "$AUTH_SECRET" = "change-me-to-a-long-random-string" ]; then + echo "ERROR: AUTH_SECRET must be set to a long random value." >&2 + exit 1 +fi + +mkdir -p "$(dirname "${RFID_DB_PATH:-/data/rfid.db}")" + +# Optional first-boot user: set CREATE_USER_EMAIL + CREATE_USER_PASSWORD +if [ -n "$CREATE_USER_EMAIL" ] && [ -n "$CREATE_USER_PASSWORD" ]; then + echo "Ensuring user $CREATE_USER_EMAIL exists (ignored if already present)..." + npx tsx scripts/create-user.ts \ + --email "$CREATE_USER_EMAIL" \ + --password "$CREATE_USER_PASSWORD" \ + --name "${CREATE_USER_NAME:-Admin}" \ + || true +fi + +exec "$@"