diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index dd005d8..ed3b591 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -18,7 +18,7 @@ jobs: username: ${{ gitea.actor }} password: ${{ secrets.CI_TOKEN }} - - name: Build and Pushz + - name: Build and Push uses: docker/build-push-action@v4 with: context: . @@ -28,5 +28,6 @@ jobs: - name: Deploy to Server run: | # Here we tell the server to pull the new image and restart + echo "${{ secrets.CI_TOKEN }}" | docker login git.ludops.com -u ${{ gitea.actor }} --password-stdin docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d diff --git a/Dockerfile b/Dockerfile index 4be8898..b464928 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,36 +1,37 @@ -# --- STAGE 1: Base & Dependencies --- -FROM node:20-alpine AS base +# --- STAGE 1: Base --- +FROM node:24-alpine AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable WORKDIR /app -# Copy lockfiles and workspace configs -COPY pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json ./ -# Copy all package.jsons to allow pnpm to link workspaces -COPY apps/api/package.json ./apps/api/ -COPY apps/web/package.json ./apps/web/ -COPY packages/shared/package.json ./packages/shared/ - -RUN pnpm install --frozen-lockfile - -# --- STAGE 2: Build --- +# --- STAGE 2: Build everything --- FROM base AS build COPY . . -# This runs 'turbo run build' which should create 'dist' in both apps +RUN pnpm install --frozen-lockfile RUN pnpm turbo run build -# --- STAGE 3: Runner (The tiny production image) --- -FROM node:20-alpine AS runner +# --- STAGE 3: Extract for Production --- +# This "flattens" the api and its node_modules into a standalone folder +RUN pnpm deploy --filter=api --prod /prod/api + +# --- STAGE 4: Runner --- +FROM node:24-alpine AS runner WORKDIR /app -# IMPORTANT: We copy from 'build' stage, not 'base' -# We check if the folders exist before copying to avoid the error you saw -COPY --from=build /app/apps/api/dist ./api +# Copy the standalone API (includes its own local node_modules) +COPY --from=build /prod/api . + +# Copy the web dist into the location the API expects +# Based on your code, it looks for ../web-dist or ./web-dist COPY --from=build /app/apps/web/dist ./web-dist -COPY --from=build /app/node_modules ./node_modules + +# We need the shared package if the API imports it directly as source +# But pnpm deploy usually handles the shared package if it's built COPY --from=build /app/packages/shared ./packages/shared EXPOSE 3000 -# Update this path to wherever your compiled Fastify entry point is -CMD ["node", "api/src/index.ts"] \ No newline at end of file +ENV NODE_ENV=production + +# Since we are now inside the "api" folder essentially: +CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 63991eb..ef51bc3 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -11,26 +11,26 @@ const fastify = Fastify({ logger: true }); // API Route fastify.get('/api/status', async (): Promise => { - return { status: 'OK', version: '1.0.0', database: true }; + return { status: 'OK', version: '1.0.0', database: true }; }); // Serve Frontend (Vite Dist) const webDistPath = process.env.NODE_ENV === 'production' - ? path.join(__dirname, '../web-dist') - : path.join(__dirname, '../../web/dist'); + ? path.join(__dirname, 'web-dist') // Adjusted for the flattened Docker structure + : path.join(__dirname, '../../web/dist'); fastify.register(fastifyStatic, { - root: webDistPath, - prefix: '/', + root: webDistPath, + prefix: '/', }); // SPA Routing: Redirect everything else to index.html fastify.setNotFoundHandler((req, reply) => { - if (req.url.startsWith('/api')) { - reply.code(404).send({ error: 'Not Found' }); - } else { - reply.sendFile('index.html'); - } + if (req.url.startsWith('/api')) { + reply.code(404).send({ error: 'Not Found' }); + } else { + reply.sendFile('index.html'); + } }); fastify.listen({ port: 3000, host: '0.0.0.0' }); \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..b77905a --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,19 @@ +services: + skeleton-app: + image: git.ludops.com/ludops/ludops-skeleton:latest + container_name: ludops-skeleton-app + restart: always + environment: + - NODE_ENV=production + - PORT=3000 + # We point to the shared DB we set up earlier + - DATABASE_URL=postgres://gitea:gitea@ludops-postgres:5432/skeleton_db + networks: + - proxy-tier # For NPM to find us + - infrastructure_infra-network # For us to find the Postgres DB + +networks: + proxy-tier: + external: true + infrastructure_infra-network: + external: true \ No newline at end of file