diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b3c95f3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +*/node_modules +*/*/node_modules +**/dist +apps/client +.git +.env +*.md diff --git a/CLAUDE.md b/CLAUDE.md index 4f5c0a8..4a5cb7b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,10 +26,15 @@ npm run lint -w @calchat/client # Run ESLint npm run build:apk -w @calchat/client # Build APK locally with EAS ``` +### Shared (packages/shared) +```bash +npm run build -w @calchat/shared # Compile shared types to dist/ +``` + ### Server (apps/server) - Express.js backend ```bash -npm run dev -w @calchat/server # Start dev server with hot reload (tsx watch) -npm run build -w @calchat/server # Compile TypeScript +npm run dev -w @calchat/server # Build shared + start dev server with hot reload (tsx watch) +npm run build -w @calchat/server # Build shared + compile TypeScript npm run start -w @calchat/server # Run compiled server (port 3000) ``` @@ -53,6 +58,7 @@ npm run start -w @calchat/server # Run compiled server (port 3000) | | react-native-logs | Client-side logging | | | tsdav | CalDAV client library | | | ical.js | iCalendar parsing/generation | +| Deployment | Docker | Server containerization (multi-stage build) | | Planned | iCalendar | Event export/import | ## Architecture @@ -318,6 +324,8 @@ src/ ### Shared Package (packages/shared) +The shared package is compiled to `dist/` (CommonJS). All imports must use `@calchat/shared` (NOT `@calchat/shared/src/...`). Server `dev` and `build` scripts automatically build shared first. + ``` src/ ├── index.ts @@ -530,6 +538,14 @@ docker compose up -d # Start Radicale CalDAV server ``` - Radicale: `localhost:5232` +### Server Docker Image +```bash +# Build from repo root: +docker build -f apps/server/docker/Dockerfile -t calchat-server . +docker run -p 3000:3000 --env-file apps/server/.env calchat-server +``` +Multi-stage build: compiles shared + server in build stage, copies only `dist/` and production dependencies to runtime stage. `.dockerignore` excludes `node_modules`, `dist`, `apps/client`, `.git`, `.env`, `*.md`. + ### Environment Variables Server requires `.env` file in `apps/server/`: ``` diff --git a/apps/server/docker/Dockerfile b/apps/server/docker/Dockerfile new file mode 100644 index 0000000..4c2ca36 --- /dev/null +++ b/apps/server/docker/Dockerfile @@ -0,0 +1,29 @@ +FROM node:alpine AS build + +WORKDIR /app + +COPY package.json package-lock.json tsconfig.json ./ +COPY packages/shared/ packages/shared/ +COPY apps/server/ apps/server/ + +RUN npm ci --workspace=@calchat/server --workspace=@calchat/shared --include-workspace-root + +RUN npm run build --workspace=@calchat/shared && \ + npm run build --workspace=@calchat/server + +FROM node:alpine + +WORKDIR /app + +COPY package.json package-lock.json ./ +COPY packages/shared/package.json packages/shared/ +COPY apps/server/package.json apps/server/ + +RUN npm ci --omit=dev --workspace=@calchat/server --workspace=@calchat/shared --include-workspace-root + +COPY --from=build /app/packages/shared/dist/ packages/shared/dist/ +COPY --from=build /app/apps/server/dist/ apps/server/dist/ + +EXPOSE 3000 + +CMD ["node", "apps/server/dist/app.js"] diff --git a/apps/server/package.json b/apps/server/package.json index 13d5f1d..2ee3fe7 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -3,8 +3,8 @@ "version": "1.0.0", "private": true, "scripts": { - "dev": "tsx watch src/app.ts", - "build": "tsc", + "dev": "npm run build --workspace=@calchat/shared && tsx watch src/app.ts", + "build": "npm run build --workspace=@calchat/shared && tsc", "start": "node dist/app.js", "test": "jest" }, diff --git a/apps/server/src/services/CaldavService.ts b/apps/server/src/services/CaldavService.ts index 4e153cf..6db09a4 100644 --- a/apps/server/src/services/CaldavService.ts +++ b/apps/server/src/services/CaldavService.ts @@ -6,9 +6,10 @@ import { CaldavRepository } from "./interfaces/CaldavRepository"; import { CalendarEvent, CreateEventDTO, -} from "@calchat/shared/src/models/CalendarEvent"; + CaldavConfig, + formatDateKey, +} from "@calchat/shared"; import { EventService } from "./EventService"; -import { CaldavConfig, formatDateKey } from "@calchat/shared"; const logger = createLogger("CaldavService"); diff --git a/apps/server/src/services/interfaces/CaldavRepository.ts b/apps/server/src/services/interfaces/CaldavRepository.ts index ffddbdd..ca938de 100644 --- a/apps/server/src/services/interfaces/CaldavRepository.ts +++ b/apps/server/src/services/interfaces/CaldavRepository.ts @@ -1,4 +1,4 @@ -import { CaldavConfig } from "@calchat/shared/src/models/CaldavConfig"; +import { CaldavConfig } from "@calchat/shared"; export interface CaldavRepository { findByUserId(userId: string): Promise; diff --git a/packages/shared/.gitignore b/packages/shared/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/packages/shared/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/packages/shared/package.json b/packages/shared/package.json index 4721b79..18c1bef 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -2,11 +2,14 @@ "name": "@calchat/shared", "version": "1.0.0", "private": true, - "main": "./src/index.ts", - "types": "./src/index.ts", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { - ".": "./src/index.ts", - "./*": "./src/*" + ".": "./dist/index.js", + "./*": "./dist/*" + }, + "scripts": { + "build": "tsc" }, "dependencies": { "rrule": "^2.8.1" diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json index 7c3de64..176c156 100644 --- a/packages/shared/tsconfig.json +++ b/packages/shared/tsconfig.json @@ -1,10 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "composite": true, "declaration": true, - "declarationMap": true, - "module": "ESNext", + "outDir": "dist", + "rootDir": "src", + "module": "CommonJS", "target": "ES2020", "moduleResolution": "Node", "esModuleInterop": true,