feat: make backend Docker-ready and web Vercel-ready

- Switch @bytelyst/* deps from link: to private Gitea registry (^0.x)
- Add .npmrc pointing to gitea.bytelyst.com private npm registry
- Rewrite backend/Dockerfile: monorepo root context, pnpm workspace,
  correct EXPOSE 4018, CMD node dist/backend/src/bootstrap.js
- Move vercel.json to repo root with pnpm filter build commands
- Remove web/Dockerfile and web/nginx.conf (web is Vercel-only)
- Remove web service from docker-compose.yml (backend Docker only)
- Document GITEA_NPM_TOKEN requirement in .env.example
- Fix start script path: dist/backend/src/bootstrap.js (rootDir: "..")

PREREQUISITE: Set GITEA_NPM_TOKEN and run pnpm install to regenerate
pnpm-lock.yaml before first Docker build.

Vercel settings: Root Directory = repo root, add GITEA_NPM_TOKEN env var.
Docker build: GITEA_NPM_TOKEN=<token> docker compose build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Saravana Achu Mac 2026-04-05 19:05:35 -07:00
parent a256571480
commit 5e07ac040d
10 changed files with 87 additions and 101 deletions

View File

@ -1,3 +1,7 @@
# Private npm registry for @bytelyst/* packages (Gitea)
# Required for: pnpm install (dev + CI), Docker builds, Vercel builds
GITEA_NPM_TOKEN=
# Shared product identity # Shared product identity
PRODUCT_ID=invttrdg PRODUCT_ID=invttrdg
PRODUCT_DISPLAY_NAME=ByteLyst Trading PRODUCT_DISPLAY_NAME=ByteLyst Trading

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
@bytelyst:registry=https://gitea.bytelyst.com/api/packages/ByteLyst/npm/
//gitea.bytelyst.com/api/packages/ByteLyst/npm/:_authToken=${GITEA_NPM_TOKEN}

View File

@ -1,35 +1,54 @@
# Build context: learning_ai_invt_trdg/ (monorepo root)
# docker-compose passes GITEA_NPM_TOKEN as a build arg for the private @bytelyst registry
#
# --- Stage 1: Build --- # --- Stage 1: Build ---
FROM node:18-alpine AS builder FROM node:18-alpine AS builder
RUN corepack enable && corepack prepare pnpm@10.6.5 --activate
WORKDIR /app WORKDIR /app
# Install build dependencies ARG GITEA_NPM_TOKEN
COPY package*.json ./ ENV GITEA_NPM_TOKEN=${GITEA_NPM_TOKEN}
RUN npm ci
# Copy source and compile # Copy workspace root files first (layer cache)
COPY . . # NOTE: After switching @bytelyst/* deps from link: to registry, run:
RUN npm run build # GITEA_NPM_TOKEN=<token> pnpm install
# to regenerate pnpm-lock.yaml, then restore --frozen-lockfile here.
COPY .npmrc pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY package.json ./package.json
COPY backend/package.json ./backend/package.json
# Install backend deps only
RUN pnpm install --filter @bytelyst/trading-backend
# Copy source (backend + shared types used by tsconfig rootDir "..")
COPY backend/ ./backend/
COPY shared/ ./shared/
WORKDIR /app/backend
RUN pnpm run build
# --- Stage 2: Production --- # --- Stage 2: Production ---
FROM node:18-alpine FROM node:18-alpine
RUN corepack enable && corepack prepare pnpm@10.6.5 --activate
WORKDIR /app WORKDIR /app
# Copy only production dependencies ARG GITEA_NPM_TOKEN
COPY package*.json ./ ENV GITEA_NPM_TOKEN=${GITEA_NPM_TOKEN}
RUN npm ci --omit=dev
# Copy compiled files from builder COPY .npmrc pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY --from=builder /app/dist ./dist COPY package.json ./package.json
COPY backend/package.json ./backend/package.json
RUN pnpm install --filter @bytelyst/trading-backend --prod
COPY --from=builder /app/backend/dist ./backend/dist
# Ensure the node user owns the app directory
RUN chown -R node:node /app RUN chown -R node:node /app
# Expose the API port for the dashboard
EXPOSE 5000
# Use non-root user for security
USER node USER node
CMD ["node", "dist/index.js"] WORKDIR /app/backend
EXPOSE 4018
CMD ["node", "dist/backend/src/bootstrap.js"]

View File

@ -9,7 +9,7 @@
"dev": "node --import tsx src/bootstrap.ts", "dev": "node --import tsx src/bootstrap.ts",
"build": "tsc", "build": "tsc",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"start": "node dist/bootstrap.js", "start": "node dist/backend/src/bootstrap.js",
"check:schema-contract": "node --import tsx verifySchemaContract.ts", "check:schema-contract": "node --import tsx verifySchemaContract.ts",
"check:rls-policies": "node --import tsx verifyRlsPolicies.ts", "check:rls-policies": "node --import tsx verifyRlsPolicies.ts",
"check:secret-hygiene": "node --import tsx verifySecretHygiene.ts", "check:secret-hygiene": "node --import tsx verifySecretHygiene.ts",
@ -53,10 +53,10 @@
"@azure/cosmos": "^4.3.0", "@azure/cosmos": "^4.3.0",
"@azure/identity": "^4.10.0", "@azure/identity": "^4.10.0",
"@azure/keyvault-secrets": "^4.9.0", "@azure/keyvault-secrets": "^4.9.0",
"@bytelyst/auth": "link:../../../learning_ai/learning_ai_common_plat/packages/auth", "@bytelyst/auth": "^0.1.0",
"@bytelyst/config": "link:../../../learning_ai/learning_ai_common_plat/packages/config", "@bytelyst/config": "^0.1.0",
"@bytelyst/cosmos": "link:../../../learning_ai/learning_ai_common_plat/packages/cosmos", "@bytelyst/cosmos": "^0.1.0",
"@bytelyst/llm": "link:../../../learning_ai/learning_ai_common_plat/packages/llm", "@bytelyst/llm": "^0.1.0",
"@alpacahq/alpaca-trade-api": "^3.1.3", "@alpacahq/alpaca-trade-api": "^3.1.3",
"@supabase/supabase-js": "^2.90.1", "@supabase/supabase-js": "^2.90.1",
"@types/cors": "^2.8.19", "@types/cors": "^2.8.19",

View File

@ -3,19 +3,11 @@ version: '3.9'
services: services:
backend: backend:
build: build:
context: ./backend context: .
dockerfile: backend/Dockerfile
args:
GITEA_NPM_TOKEN: ${GITEA_NPM_TOKEN}
env_file: env_file:
- .env - .env
ports: ports:
- '4018:4018' - '4018:4018'
web:
build:
context: ./web
env_file:
- .env
ports:
- '3048:3048'
depends_on:
- backend

View File

@ -13,10 +13,10 @@
"verify": "./scripts/verify.sh" "verify": "./scripts/verify.sh"
}, },
"dependencies": { "dependencies": {
"@bytelyst/kill-switch-client": "link:../../learning_ai/learning_ai_common_plat/packages/kill-switch-client", "@bytelyst/kill-switch-client": "^0.1.0",
"@bytelyst/react-auth": "link:../../learning_ai/learning_ai_common_plat/packages/react-auth", "@bytelyst/react-auth": "^0.1.1",
"@bytelyst/react-native-platform-sdk": "link:../../learning_ai/learning_ai_common_plat/packages/react-native-platform-sdk", "@bytelyst/react-native-platform-sdk": "^1.0.0",
"@bytelyst/telemetry-client": "link:../../learning_ai/learning_ai_common_plat/packages/telemetry-client" "@bytelyst/telemetry-client": "^0.1.0"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5.9.3" "typescript": "^5.9.3"

11
vercel.json Normal file
View File

@ -0,0 +1,11 @@
{
"installCommand": "pnpm install --filter @bytelyst/trading-web",
"buildCommand": "pnpm --filter @bytelyst/trading-web run build",
"outputDirectory": "web/dist",
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}

View File

@ -1,34 +0,0 @@
# --- Stage 1: Build ---
FROM node:18-alpine AS builder
WORKDIR /app
# Install build dependencies
COPY package*.json ./
RUN npm install
# Copy source and build
# Copy source and build
COPY . .
# Build-time environment variables
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_ANON_KEY
ARG VITE_API_URL
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY
ENV VITE_API_URL=$VITE_API_URL
RUN npm run build
# --- Stage 2: Serve ---
FROM nginx:stable-alpine
# Copy static assets from builder
COPY --from=builder /app/dist /usr/share/nginx/html
# Expose port 80
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -18,9 +18,9 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@bytelyst/kill-switch-client": "link:../../../learning_ai/learning_ai_common_plat/packages/kill-switch-client", "@bytelyst/kill-switch-client": "^0.1.0",
"@bytelyst/react-auth": "link:../../../learning_ai/learning_ai_common_plat/packages/react-auth", "@bytelyst/react-auth": "^0.1.1",
"@bytelyst/telemetry-client": "link:../../../learning_ai/learning_ai_common_plat/packages/telemetry-client", "@bytelyst/telemetry-client": "^0.1.0",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",

View File

@ -1,8 +0,0 @@
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
]
}