chore: add pnpm workspace configuration for apps and packages
This commit is contained in:
@@ -112,24 +112,6 @@ services:
|
||||
networks:
|
||||
- dreamchat-network
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:23.0
|
||||
restart: unless-stopped
|
||||
command: start-dev
|
||||
environment:
|
||||
KEYCLOAK_ADMIN: admin
|
||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||
KC_DB: postgres
|
||||
KC_DB_URL: jdbc:postgresql://db:5432/keycloak
|
||||
KC_DB_USERNAME: postgres
|
||||
KC_DB_PASSWORD: postgres
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- db
|
||||
networks:
|
||||
- dreamchat-network
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
|
||||
@@ -138,6 +120,8 @@ networks:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
**Note:** Keycloak is configured as an external service. Set `KEYCLOAK_URL` in your environment to point to your external Keycloak instance.
|
||||
|
||||
### .devcontainer/Dockerfile
|
||||
|
||||
```dockerfile
|
||||
@@ -274,15 +258,15 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# Frontend
|
||||
# Frontend (static file server)
|
||||
# Note: External reverse proxy expected for SSL and routing
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/frontend/Dockerfile
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "3001:3000"
|
||||
environment:
|
||||
- VITE_API_URL=/api
|
||||
- VITE_WS_URL=/ws
|
||||
@@ -319,23 +303,6 @@ services:
|
||||
networks:
|
||||
- dreamchat-network
|
||||
|
||||
# Nginx Reverse Proxy (optional)
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||
- model-cache:/model-cache:ro
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
networks:
|
||||
- dreamchat-network
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
redis-data:
|
||||
@@ -400,7 +367,7 @@ KEYCLOAK_CLIENT_SECRET=your_keycloak_secret
|
||||
|
||||
```dockerfile
|
||||
# apps/backend/Dockerfile
|
||||
FROM node:20-alpine AS base
|
||||
FROM node:24-alpine AS base
|
||||
RUN npm install -g pnpm@8
|
||||
|
||||
FROM base AS dependencies
|
||||
@@ -456,7 +423,7 @@ CMD ["node", "dist/main.js"]
|
||||
|
||||
```dockerfile
|
||||
# apps/frontend/Dockerfile
|
||||
FROM node:20-alpine AS base
|
||||
FROM node:24-alpine AS base
|
||||
RUN npm install -g pnpm@8
|
||||
|
||||
FROM base AS dependencies
|
||||
@@ -486,46 +453,59 @@ RUN pnpm --filter @dreamchat/shared build
|
||||
# Build frontend
|
||||
RUN pnpm --filter @dreamchat/frontend build
|
||||
|
||||
# Production with Nginx
|
||||
FROM nginx:alpine
|
||||
# Production stage - using serve for static files
|
||||
# External reverse proxy (nginx/traefik/etc.) expected
|
||||
FROM node:24-alpine AS production
|
||||
WORKDIR /app
|
||||
|
||||
# Install serve
|
||||
RUN npm install -g serve
|
||||
|
||||
# Copy built assets
|
||||
COPY --from=build /app/apps/frontend/dist /usr/share/nginx/html
|
||||
COPY --from=build /app/apps/frontend/dist ./dist
|
||||
|
||||
# Copy nginx config
|
||||
COPY apps/frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs
|
||||
RUN adduser -S nodejs -u 1001
|
||||
USER nodejs
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
# Serve static files
|
||||
# Note: External reverse proxy should handle:
|
||||
# - SSL/TLS termination
|
||||
# - Path routing (/api -> backend, / -> frontend)
|
||||
# - WebSocket proxying
|
||||
CMD ["serve", "-s", "dist", "-l", "3000"]
|
||||
```
|
||||
|
||||
### frontend/nginx.conf
|
||||
### External Reverse Proxy Configuration
|
||||
|
||||
The frontend container serves static files on port 3000. An external reverse proxy is expected to handle:
|
||||
|
||||
- **SSL/TLS termination**
|
||||
- **Path routing**:
|
||||
- `/api/*` → Backend (port 3000)
|
||||
- `/ws` → Backend WebSocket (port 3000)
|
||||
- `/*` → Frontend (port 3001)
|
||||
- **Static file caching**
|
||||
|
||||
Example nginx configuration:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
listen 443 ssl;
|
||||
server_name dreamchat.example.com;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
# API proxy
|
||||
location /api {
|
||||
proxy_pass http://backend:3000/api;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# WebSocket proxy
|
||||
@@ -534,45 +514,110 @@ server {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Static files
|
||||
# Frontend static files
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
# Health check
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
proxy_pass http://frontend:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Keycloak Configuration
|
||||
## Keycloak Configuration (External)
|
||||
|
||||
### Initial Setup
|
||||
Keycloak is configured as an external service with support for group/role/attribute-based authorization and auto-user creation.
|
||||
|
||||
1. Access Keycloak admin console: `http://localhost:8080/admin`
|
||||
2. Login with admin credentials
|
||||
3. Create new realm: `dreamchat`
|
||||
4. Create client: `dreamchat-backend`
|
||||
### Prerequisites
|
||||
|
||||
1. Have a running Keycloak instance (self-hosted or managed)
|
||||
2. Configure the following environment variables in `.env`:
|
||||
|
||||
```bash
|
||||
# Basic Keycloak settings
|
||||
KEYCLOAK_ENABLED=true
|
||||
KEYCLOAK_URL=http://your-keycloak-server:8080
|
||||
KEYCLOAK_REALM=dreamchat
|
||||
KEYCLOAK_CLIENT_ID=dreamchat-backend
|
||||
KEYCLOAK_CLIENT_SECRET=your_keycloak_secret
|
||||
|
||||
# Authorization settings (optional but recommended)
|
||||
KEYCLOAK_REQUIRED_GROUP=dreamchat-users
|
||||
KEYCLOAK_REQUIRED_ROLE=dreamchat-access
|
||||
KEYCLOAK_REQUIRED_CLIENT_ROLE=user
|
||||
KEYCLOAK_REQUIRED_ATTRIBUTE=approved:true
|
||||
|
||||
# Auto-create users
|
||||
KEYCLOAK_AUTO_CREATE_USER=true
|
||||
KEYCLOAK_DEFAULT_USER_ROLE=USER
|
||||
```
|
||||
|
||||
### Keycloak Realm Setup
|
||||
|
||||
1. Access your Keycloak admin console
|
||||
2. Create new realm: `dreamchat`
|
||||
3. Create client: `dreamchat-backend`
|
||||
- Client authentication: ON
|
||||
- Authorization: ON
|
||||
- Valid redirect URIs: `http://localhost:3000/*`
|
||||
- Web origins: `http://localhost:3000`
|
||||
|
||||
5. Create client: `dreamchat-frontend`
|
||||
4. Create client: `dreamchat-frontend`
|
||||
- Client authentication: OFF
|
||||
- Valid redirect URIs: `http://localhost:5173/*`
|
||||
- Web origins: `http://localhost:5173`
|
||||
|
||||
### Authorization Configuration
|
||||
|
||||
You can restrict access based on:
|
||||
|
||||
**1. Group Membership**
|
||||
```bash
|
||||
KEYCLOAK_REQUIRED_GROUP=dreamchat-users
|
||||
```
|
||||
Users must be members of this Keycloak group to access the application.
|
||||
|
||||
**2. Realm Role**
|
||||
```bash
|
||||
KEYCLOAK_REQUIRED_ROLE=dreamchat-access
|
||||
```
|
||||
Users must have this realm-level role.
|
||||
|
||||
**3. Client Role**
|
||||
```bash
|
||||
KEYCLOAK_REQUIRED_CLIENT_ROLE=user
|
||||
```
|
||||
Users must have this role for the `dreamchat-backend` client.
|
||||
|
||||
**4. User Attribute**
|
||||
```bash
|
||||
KEYCLOAK_REQUIRED_ATTRIBUTE=department:engineering
|
||||
# or
|
||||
KEYCLOAK_REQUIRED_ATTRIBUTE=approved:true
|
||||
```
|
||||
Users must have this attribute with the specified value.
|
||||
|
||||
### User Auto-Creation
|
||||
|
||||
When `KEYCLOAK_AUTO_CREATE_USER=true`:
|
||||
- Users are automatically created in the database on first Keycloak login
|
||||
- Username is derived from Keycloak preferred_username
|
||||
- Email is taken from Keycloak email claim
|
||||
- Role is set to `KEYCLOAK_DEFAULT_USER_ROLE` (default: USER)
|
||||
- The `keycloakSub` field links the local user to Keycloak
|
||||
|
||||
When `KEYCLOAK_AUTO_CREATE_USER=false`:
|
||||
- Only existing local users can log in via Keycloak
|
||||
- The `keycloakSub` must match between Keycloak and local user
|
||||
|
||||
### Example Keycloak Group/Role Setup
|
||||
|
||||
1. Create a group: `dreamchat-users`
|
||||
2. Create a realm role: `dreamchat-access`
|
||||
3. Assign the group and/or role to users who should have access
|
||||
4. Configure `KEYCLOAK_REQUIRED_GROUP` and/or `KEYCLOAK_REQUIRED_ROLE`
|
||||
|
||||
### realm-export.json (Optional)
|
||||
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user