diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2da7556 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +node_modules +server/node_modules +web/node_modules +server/.env +web/.env +server/dev.db +server/*.db +server/*.db-journal +**/.DS_Store diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30bd623 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6ceb6b6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,64 @@ +FROM node:20-bookworm-slim AS server-deps +WORKDIR /app/server + +COPY server/package.json server/package-lock.json ./ +COPY server/scripts ./scripts +COPY server/prisma ./prisma + +RUN npm ci --no-audit --no-fund + + +FROM server-deps AS server-build +WORKDIR /app/server + +COPY server/tsconfig.json ./tsconfig.json +COPY server/src ./src + +RUN npm run build + + +FROM node:20-bookworm-slim AS server-runtime +WORKDIR /app/server + +COPY server/package.json server/package-lock.json ./ +COPY server/scripts ./scripts +COPY server/prisma ./prisma + +RUN npm ci --omit=dev --no-audit --no-fund + +COPY --from=server-build /app/server/dist ./dist + +ENV NODE_ENV=production +ENV HOST=0.0.0.0 +ENV PORT=8787 +EXPOSE 8787 + +CMD ["npm", "run", "start"] + + +FROM node:20-bookworm-slim AS web-deps +WORKDIR /app/web + +COPY web/package.json web/package-lock.json ./ +RUN npm ci --no-audit --no-fund + + +FROM web-deps AS web-build +WORKDIR /app/web + +COPY web/ ./ + +ARG VITE_API_BASE_URL=/api +ARG VITE_ADMIN_TOKEN= +ENV VITE_API_BASE_URL=${VITE_API_BASE_URL} +ENV VITE_ADMIN_TOKEN=${VITE_ADMIN_TOKEN} + +RUN npm run build + + +FROM nginx:1.27-alpine AS web-runtime + +COPY --from=web-build /app/web/dist /usr/share/nginx/html +COPY dist/default.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 diff --git a/dist/default.conf b/dist/default.conf new file mode 100644 index 0000000..db8a43a --- /dev/null +++ b/dist/default.conf @@ -0,0 +1,22 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location = /api { + return 307 /api/; + } + + location /api/ { + proxy_pass http://server:8787/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/docker-compose.example.yml b/docker-compose.example.yml new file mode 100644 index 0000000..a8db2b5 --- /dev/null +++ b/docker-compose.example.yml @@ -0,0 +1,37 @@ +services: + server: + build: + context: . + dockerfile: Dockerfile + target: server-runtime + environment: + HOST: 0.0.0.0 + PORT: 8787 + DATABASE_URL: file:/data/dev.db + # Set ADMIN_TOKEN only when you actually want token auth enabled. + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} + XAI_API_KEY: ${XAI_API_KEY:-} + EXA_API_KEY: ${EXA_API_KEY:-} + volumes: + - sybil_data:/data + expose: + - "8787" + restart: unless-stopped + + web: + build: + context: . + dockerfile: Dockerfile + target: web-runtime + args: + VITE_API_BASE_URL: ${VITE_API_BASE_URL:-/api} + VITE_ADMIN_TOKEN: ${VITE_ADMIN_TOKEN:-} + depends_on: + - server + ports: + - "5173:80" + restart: unless-stopped + +volumes: + sybil_data: diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index 218e1f0..ccafd32 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -91,7 +91,7 @@ type CompletionResponse = { }; }; -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8787"; +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "/api"; const ENV_ADMIN_TOKEN = (import.meta.env.VITE_ADMIN_TOKEN as string | undefined)?.trim() || null; let authToken: string | null = ENV_ADMIN_TOKEN;