🚀 feat(photos): add Docker deployment configuration

- Add Dockerfile for photos-backend (port 3039)
- Add Dockerfile for photos-web (port 5019)
- Add docker-entrypoint.sh for database migrations
- Add health endpoint for photos-web
- Add photos services to docker-compose.macmini.yml
- Update CORS_ORIGINS for mana-auth and mana-media
- Update CLAUDE.md with production URLs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-11 18:21:19 +01:00
parent 91f175c8f6
commit 5ce4e42c20
6 changed files with 278 additions and 7 deletions

View file

@ -4,10 +4,10 @@
**Photos** is a unified photo gallery application for the ManaCore ecosystem. It aggregates photos from all apps (Picture, Chat, Contacts, NutriPhi, etc.) via the mana-media service, providing a central place to view, organize, and manage photos.
| App | Port | URL |
|-----|------|-----|
| Backend | 3019 | http://localhost:3019 |
| Web App | 5189 | http://localhost:5189 |
| App | Dev Port | Prod Port | Prod URL |
|-----|----------|-----------|----------|
| Backend | 3019 | 3039 | https://photos-api.mana.how |
| Web App | 5189 | 5019 | https://photos.mana.how |
## Project Structure
@ -15,7 +15,7 @@
apps/photos/
├── apps/
│ ├── backend/ # NestJS API server (@photos/backend)
│ └── web/ # SvelteKit web application (@photos/web) [TODO]
│ └── web/ # SvelteKit web application (@photos/web)
├── packages/
│ └── shared/ # Shared types (@photos/shared)
├── package.json

View file

@ -0,0 +1,88 @@
# Build stage
FROM node:20-alpine AS builder
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
# Copy root workspace files
COPY pnpm-workspace.yaml ./
COPY package.json ./
COPY pnpm-lock.yaml ./
# Copy shared packages (all required dependencies)
COPY packages/shared-errors ./packages/shared-errors
COPY packages/shared-nestjs-auth ./packages/shared-nestjs-auth
COPY packages/shared-nestjs-health ./packages/shared-nestjs-health
COPY packages/shared-nestjs-metrics ./packages/shared-nestjs-metrics
COPY packages/shared-nestjs-setup ./packages/shared-nestjs-setup
COPY packages/shared-tsconfig ./packages/shared-tsconfig
COPY packages/mana-core-nestjs-integration ./packages/mana-core-nestjs-integration
COPY packages/shared-drizzle-config ./packages/shared-drizzle-config
# Copy photos shared package
COPY apps/photos/packages/shared ./apps/photos/packages/shared
# Copy photos backend
COPY apps/photos/apps/backend ./apps/photos/apps/backend
# Install dependencies
RUN pnpm install --frozen-lockfile
# Build shared packages first (in dependency order)
WORKDIR /app/packages/shared-errors
RUN pnpm build
WORKDIR /app/packages/shared-nestjs-auth
RUN pnpm build
WORKDIR /app/packages/shared-nestjs-health
RUN pnpm build
WORKDIR /app/packages/shared-nestjs-metrics
RUN pnpm build
WORKDIR /app/packages/shared-nestjs-setup
RUN pnpm build
WORKDIR /app/packages/mana-core-nestjs-integration
RUN pnpm build
# Build the backend
WORKDIR /app/apps/photos/apps/backend
RUN pnpm build
# Production stage
FROM node:20-alpine AS production
# Install pnpm and postgresql-client for health checks
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate \
&& apk add --no-cache postgresql-client
WORKDIR /app
# Copy everything from builder (including node_modules)
COPY --from=builder /app/pnpm-workspace.yaml ./
COPY --from=builder /app/package.json ./
COPY --from=builder /app/pnpm-lock.yaml ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/packages ./packages
COPY --from=builder /app/apps/photos ./apps/photos
# Copy entrypoint script
COPY apps/photos/apps/backend/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
WORKDIR /app/apps/photos/apps/backend
# Expose port
EXPOSE 3039
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3039/api/v1/health || exit 1
# Run entrypoint script
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node", "dist/main.js"]

View file

@ -0,0 +1,9 @@
#!/bin/sh
set -e
echo "📋 Running database migrations..."
npx drizzle-kit push --config drizzle.config.ts --force || echo "⚠️ Migration failed, continuing anyway..."
# Start the application
echo "🚀 Starting Photos Backend..."
exec "$@"

View file

@ -0,0 +1,101 @@
# Build stage
FROM node:20-alpine AS builder
# Build arguments for SvelteKit static env vars
ARG PUBLIC_BACKEND_URL=http://photos-backend:3039
ARG PUBLIC_MANA_CORE_AUTH_URL=http://mana-core-auth:3001
ARG PUBLIC_MANA_MEDIA_URL=http://mana-media:3015
# Set as environment variables for build
ENV PUBLIC_BACKEND_URL=$PUBLIC_BACKEND_URL
ENV PUBLIC_MANA_CORE_AUTH_URL=$PUBLIC_MANA_CORE_AUTH_URL
ENV PUBLIC_MANA_MEDIA_URL=$PUBLIC_MANA_MEDIA_URL
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
# Copy root workspace files
COPY pnpm-workspace.yaml ./
COPY package.json ./
COPY pnpm-lock.yaml ./
# Copy shared packages needed by photos web
COPY packages/shared-types ./packages/shared-types
COPY packages/shared-auth ./packages/shared-auth
COPY packages/shared-auth-ui ./packages/shared-auth-ui
COPY packages/shared-branding ./packages/shared-branding
COPY packages/shared-feedback-service ./packages/shared-feedback-service
COPY packages/shared-feedback-types ./packages/shared-feedback-types
COPY packages/shared-feedback-ui ./packages/shared-feedback-ui
COPY packages/shared-help-content ./packages/shared-help-content
COPY packages/shared-help-types ./packages/shared-help-types
COPY packages/shared-help-ui ./packages/shared-help-ui
COPY packages/shared-i18n ./packages/shared-i18n
COPY packages/shared-icons ./packages/shared-icons
COPY packages/shared-tailwind ./packages/shared-tailwind
COPY packages/shared-theme ./packages/shared-theme
COPY packages/shared-theme-ui ./packages/shared-theme-ui
COPY packages/shared-subscription-types ./packages/shared-subscription-types
COPY packages/shared-subscription-ui ./packages/shared-subscription-ui
COPY packages/shared-profile-ui ./packages/shared-profile-ui
COPY packages/shared-ui ./packages/shared-ui
COPY packages/shared-utils ./packages/shared-utils
COPY packages/shared-tags ./packages/shared-tags
COPY packages/shared-splitscreen ./packages/shared-splitscreen
COPY packages/shared-vite-config ./packages/shared-vite-config
COPY packages/shared-api-client ./packages/shared-api-client
COPY packages/shared-stores ./packages/shared-stores
# Copy photos shared package
COPY apps/photos/packages/shared ./apps/photos/packages/shared
# Copy photos web
COPY apps/photos/apps/web ./apps/photos/apps/web
# Install dependencies
RUN pnpm install --frozen-lockfile
# Build shared packages that need building
WORKDIR /app/packages/shared-vite-config
RUN pnpm build
WORKDIR /app/packages/shared-auth
RUN pnpm build || true
# Build the web app
WORKDIR /app/apps/photos/apps/web
RUN pnpm exec svelte-kit sync
RUN pnpm build
# Production stage
FROM node:20-alpine AS production
# Keep same directory structure as builder so pnpm symlinks resolve correctly
WORKDIR /app/apps/photos/apps/web
# Copy the pnpm store that symlinks point to (at /app/node_modules/.pnpm)
COPY --from=builder /app/node_modules/.pnpm /app/node_modules/.pnpm
# Copy the app's node_modules (contains symlinks to the pnpm store)
COPY --from=builder /app/apps/photos/apps/web/node_modules ./node_modules
# Copy built application
COPY --from=builder /app/apps/photos/apps/web/build ./build
COPY --from=builder /app/apps/photos/apps/web/package.json ./
# Expose port
EXPOSE 5019
# Set environment variables
ENV NODE_ENV=production
ENV PORT=5019
ENV HOST=0.0.0.0
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:5019/health || exit 1
# Run the app
CMD ["node", "build"]

View file

@ -0,0 +1,10 @@
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async () => {
return json({
status: 'ok',
service: 'photos-web',
timestamp: new Date().toISOString(),
});
};

View file

@ -105,7 +105,7 @@ services:
SMTP_USER: ${SMTP_USER:-94cde5002@smtp-brevo.com}
SMTP_PASSWORD: ${SMTP_PASSWORD}
SMTP_FROM: Mana <noreply@mana.how>
CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://todo.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://storage.mana.how,https://presi.mana.how,https://nutriphi.mana.how,https://skilltree.mana.how,https://matrix.mana.how,https://element.mana.how,https://link.mana.how,https://playground.mana.how
CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://todo.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://storage.mana.how,https://presi.mana.how,https://nutriphi.mana.how,https://skilltree.mana.how,https://photos.mana.how,https://matrix.mana.how,https://element.mana.how,https://link.mana.how,https://playground.mana.how
DUCKDB_PATH: /data/analytics/metrics.duckdb
SYNAPSE_OIDC_CLIENT_SECRET: ${SYNAPSE_OIDC_CLIENT_SECRET:-}
volumes:
@ -234,7 +234,7 @@ services:
S3_PUBLIC_URL: https://media.mana.how
MATRIX_HOMESERVER_URL: https://matrix.mana.how
PUBLIC_URL: https://media.mana.how/api/v1
CORS_ORIGINS: https://mana.how,https://nutriphi.mana.how,https://contacts.mana.how,https://chat.mana.how,https://storage.mana.how
CORS_ORIGINS: https://mana.how,https://nutriphi.mana.how,https://contacts.mana.how,https://chat.mana.how,https://storage.mana.how,https://photos.mana.how
ports:
- "3015:3015"
healthcheck:
@ -485,6 +485,37 @@ services:
retries: 3
start_period: 40s
photos-backend:
build:
context: .
dockerfile: apps/photos/apps/backend/Dockerfile
image: photos-backend:local
container_name: mana-app-photos-backend
restart: always
depends_on:
mana-auth:
condition: service_healthy
mana-media:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3039
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/photos
DB_HOST: postgres
DB_PORT: 5432
DB_USER: postgres
MANA_CORE_AUTH_URL: http://mana-auth:3001
MANA_MEDIA_URL: http://mana-media:3015
CORS_ORIGINS: https://photos.mana.how,https://mana.how
ports:
- "3039:3039"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3039/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ============================================
# Tier 4: Matrix Stack (Ports 4000-4099)
# ============================================
@ -1107,6 +1138,38 @@ services:
retries: 3
start_period: 40s
photos-web:
build:
context: .
dockerfile: apps/photos/apps/web/Dockerfile
args:
PUBLIC_BACKEND_URL: http://photos-backend:3039
PUBLIC_MANA_CORE_AUTH_URL: http://mana-auth:3001
PUBLIC_MANA_MEDIA_URL: http://mana-media:3015
image: photos-web:local
container_name: mana-app-photos-web
restart: always
depends_on:
photos-backend:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 5019
PUBLIC_BACKEND_URL: http://photos-backend:3039
PUBLIC_MANA_CORE_AUTH_URL: http://mana-auth:3001
PUBLIC_MANA_MEDIA_URL: http://mana-media:3015
PUBLIC_BACKEND_URL_CLIENT: https://photos-api.mana.how
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.mana.how
PUBLIC_MANA_MEDIA_URL_CLIENT: https://media.mana.how
ports:
- "5019:5019"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5019/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
mana-llm:
build:
context: ./services/mana-llm