diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dc54ef7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.turbo +*.tsbuildinfo +.git +.claude +.gitea diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..9af48d6 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + push: + branches: [main] + +env: + REGISTRY: gitea.thewrightserver.net + IMAGE_PREFIX: gitea.thewrightserver.net/josh/aihostingtycoon + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Log in to Gitea registry + run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} -u josh --password-stdin + + - name: Build and push web image + run: | + docker build -f apps/web/Dockerfile -t ${{ env.IMAGE_PREFIX }}/web:latest -t ${{ env.IMAGE_PREFIX }}/web:${{ github.sha }} . + docker push ${{ env.IMAGE_PREFIX }}/web:latest + docker push ${{ env.IMAGE_PREFIX }}/web:${{ github.sha }} + + - name: Build and push server image + run: | + docker build -f apps/server/Dockerfile -t ${{ env.IMAGE_PREFIX }}/server:latest -t ${{ env.IMAGE_PREFIX }}/server:${{ github.sha }} . + docker push ${{ env.IMAGE_PREFIX }}/server:latest + docker push ${{ env.IMAGE_PREFIX }}/server:${{ github.sha }} diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile new file mode 100644 index 0000000..4e2bdb1 --- /dev/null +++ b/apps/server/Dockerfile @@ -0,0 +1,29 @@ +FROM node:22-slim AS base +RUN corepack enable && corepack prepare pnpm@10.33.0 --activate +WORKDIR /app + +FROM base AS deps +COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ +COPY packages/shared/package.json packages/shared/ +COPY packages/tsconfig/package.json packages/tsconfig/ +COPY apps/server/package.json apps/server/ +RUN pnpm install --frozen-lockfile --prod=false + +FROM base AS build +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/packages/shared/node_modules ./packages/shared/node_modules +COPY --from=deps /app/apps/server/node_modules ./apps/server/node_modules +COPY . . +RUN pnpm --filter @ai-tycoon/shared build && \ + pnpm --filter @ai-tycoon/server typecheck + +FROM base AS production +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/packages/shared/node_modules ./packages/shared/node_modules +COPY --from=deps /app/apps/server/node_modules ./apps/server/node_modules +COPY packages/shared ./packages/shared +COPY packages/tsconfig ./packages/tsconfig +COPY apps/server ./apps/server +ENV NODE_ENV=production +EXPOSE 3001 +CMD ["node", "--import", "tsx", "apps/server/src/index.ts"] diff --git a/apps/server/package.json b/apps/server/package.json index 5faeaf0..6e19c9d 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "dev": "tsx watch src/index.ts", - "build": "tsc && tsx src/index.ts", + "build": "tsc", + "start": "node --import tsx src/index.ts", "typecheck": "tsc --noEmit", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 6f127ea..8af8ace 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -10,7 +10,9 @@ const app = new Hono(); app.use('*', logger()); app.use('*', cors({ - origin: ['http://localhost:5173', 'http://localhost:5174', 'http://localhost:5175', 'http://localhost:5178'], + origin: process.env.CORS_ORIGIN + ? process.env.CORS_ORIGIN.split(',') + : ['http://localhost:5173', 'http://localhost:5174', 'http://localhost:5175', 'http://localhost:5178'], allowMethods: ['GET', 'POST', 'PUT', 'DELETE'], allowHeaders: ['Content-Type', 'Authorization'], })); diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 0000000..a743dbc --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,28 @@ +FROM node:22-slim AS base +RUN corepack enable && corepack prepare pnpm@10.33.0 --activate +WORKDIR /app + +FROM base AS deps +COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ +COPY packages/shared/package.json packages/shared/ +COPY packages/tsconfig/package.json packages/tsconfig/ +COPY packages/game-engine/package.json packages/game-engine/ +COPY apps/web/package.json apps/web/ +RUN pnpm install --frozen-lockfile + +FROM base AS build +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/packages/shared/node_modules ./packages/shared/node_modules +COPY --from=deps /app/packages/game-engine/node_modules ./packages/game-engine/node_modules +COPY --from=deps /app/apps/web/node_modules ./apps/web/node_modules +COPY . . +ARG VITE_API_URL=/api +ENV VITE_API_URL=$VITE_API_URL +RUN pnpm --filter @ai-tycoon/shared build && \ + pnpm --filter @ai-tycoon/game-engine build && \ + pnpm --filter @ai-tycoon/web build + +FROM nginx:alpine +COPY --from=build /app/apps/web/dist /usr/share/nginx/html +COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf new file mode 100644 index 0000000..e864e54 --- /dev/null +++ b/apps/web/nginx.conf @@ -0,0 +1,14 @@ +server { + listen 80; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d5aa355 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + web: + image: gitea.thewrightserver.net/josh/aihostingtycoon/web:latest + ports: + - "80:80" + depends_on: + - server + restart: unless-stopped + + server: + image: gitea.thewrightserver.net/josh/aihostingtycoon/server:latest + ports: + - "3001:3001" + environment: + - DATABASE_URL=postgresql://aitycoon:aitycoon@db:5432/aitycoon + - PORT=3001 + - CORS_ORIGIN=* + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + db: + image: postgres:17-alpine + environment: + - POSTGRES_USER=aitycoon + - POSTGRES_PASSWORD=aitycoon + - POSTGRES_DB=aitycoon + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U aitycoon"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + +volumes: + pgdata: diff --git a/packages/game-engine/package.json b/packages/game-engine/package.json index 831ac85..2a4f128 100644 --- a/packages/game-engine/package.json +++ b/packages/game-engine/package.json @@ -6,6 +6,7 @@ "main": "./src/index.ts", "types": "./src/index.ts", "scripts": { + "build": "tsc", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/packages/shared/package.json b/packages/shared/package.json index ab288a8..fb1ffd2 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -6,6 +6,7 @@ "main": "./src/index.ts", "types": "./src/index.ts", "scripts": { + "build": "tsc", "typecheck": "tsc --noEmit" }, "devDependencies": {