added docker setup
This commit is contained in:
parent
dd4d57fa1b
commit
89cf92eaf5
|
|
@ -0,0 +1,51 @@
|
|||
# Dependencies
|
||||
node_modules
|
||||
*/node_modules
|
||||
|
||||
# Build output (will be built in container)
|
||||
frontend/dist
|
||||
backend/dist
|
||||
|
||||
# Development files
|
||||
.git
|
||||
.gitignore
|
||||
.vscode
|
||||
.idea
|
||||
*.md
|
||||
!docker/README.md
|
||||
|
||||
# Environment files (use Docker environment instead)
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
backend/.env
|
||||
frontend/.env
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Test files
|
||||
*.test.ts
|
||||
*.test.tsx
|
||||
*.spec.ts
|
||||
*.spec.tsx
|
||||
__tests__
|
||||
coverage
|
||||
|
||||
# Docker files in wrong location
|
||||
docker-compose.yml
|
||||
|
||||
# Uploads and backups (mounted as volumes)
|
||||
uploads
|
||||
backups
|
||||
backend/uploads
|
||||
backend/backups
|
||||
|
||||
# Prisma migrations (included, but not dev db)
|
||||
*.db
|
||||
*.db-journal
|
||||
|
|
@ -66,6 +66,20 @@ app.use('/api', contractHistory_routes_js_1.default);
|
|||
app.get('/api/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
// Production: Serve frontend static files
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const publicPath = path_1.default.join(process.cwd(), 'public');
|
||||
// Serve static files
|
||||
app.use(express_1.default.static(publicPath));
|
||||
// SPA fallback: serve index.html for all non-API routes
|
||||
app.get('*', (req, res, next) => {
|
||||
// Skip API routes
|
||||
if (req.path.startsWith('/api')) {
|
||||
return next();
|
||||
}
|
||||
res.sendFile(path_1.default.join(publicPath, 'index.html'));
|
||||
});
|
||||
}
|
||||
// Error handling
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,sDAA8B;AAC9B,gDAAwB;AACxB,gDAAwB;AACxB,oDAA4B;AAE5B,6EAAiD;AACjD,qFAAyD;AACzD,mFAAuD;AACvD,qFAAyD;AACzD,qFAAyD;AACzD,+EAAmD;AACnD,mGAAuE;AACvE,qFAAyD;AACzD,qFAAyD;AACzD,2GAA8E;AAC9E,uGAA0E;AAC1E,qFAAyD;AACzD,iFAAqD;AACrD,6EAAiD;AACjD,iFAAqD;AACrD,uFAA2D;AAC3D,qGAAyE;AACzE,6FAAiE;AACjE,yFAA6D;AAC7D,+FAAmE;AACnE,2FAA+D;AAC/D,mFAAuD;AACvD,mGAAuE;AAEvE,gBAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AACtB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAEtC,aAAa;AACb,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,GAAE,CAAC,CAAC;AAChB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,iBAAO,CAAC,MAAM,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;AAE7E,SAAS;AACT,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAU,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,2BAAa,CAAC,CAAC;AACzC,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,4BAAc,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,yBAAW,CAAC,CAAC;AACpC,GAAG,CAAC,GAAG,CAAC,wBAAwB,EAAE,mCAAqB,CAAC,CAAC;AACzD,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,2BAA2B,EAAE,uCAAwB,CAAC,CAAC;AAC/D,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,qCAAsB,CAAC,CAAC;AAC3D,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,0BAAY,CAAC,CAAC;AACtC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,wBAAU,CAAC,CAAC;AAClC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,0BAAY,CAAC,CAAC;AACrC,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,6BAAe,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,0BAA0B,EAAE,oCAAsB,CAAC,CAAC;AAC5D,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,gCAAkB,CAAC,CAAC;AACpC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,8BAAgB,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAAE,iCAAmB,CAAC,CAAC;AACrD,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,+BAAiB,CAAC,CAAC;AACnC,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,2BAAa,CAAC,CAAC;AAC9C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,mCAAqB,CAAC,CAAC;AAEvC,eAAe;AACf,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAClC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,iBAAiB;AACjB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAU,EAAE,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAE,EAAE;IAC9F,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,sDAA8B;AAC9B,gDAAwB;AACxB,gDAAwB;AACxB,oDAA4B;AAE5B,6EAAiD;AACjD,qFAAyD;AACzD,mFAAuD;AACvD,qFAAyD;AACzD,qFAAyD;AACzD,+EAAmD;AACnD,mGAAuE;AACvE,qFAAyD;AACzD,qFAAyD;AACzD,2GAA8E;AAC9E,uGAA0E;AAC1E,qFAAyD;AACzD,iFAAqD;AACrD,6EAAiD;AACjD,iFAAqD;AACrD,uFAA2D;AAC3D,qGAAyE;AACzE,6FAAiE;AACjE,yFAA6D;AAC7D,+FAAmE;AACnE,2FAA+D;AAC/D,mFAAuD;AACvD,mGAAuE;AAEvE,gBAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AACtB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAEtC,aAAa;AACb,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,GAAE,CAAC,CAAC;AAChB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,iBAAO,CAAC,MAAM,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;AAE7E,SAAS;AACT,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAU,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,2BAAa,CAAC,CAAC;AACzC,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,4BAAc,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,yBAAW,CAAC,CAAC;AACpC,GAAG,CAAC,GAAG,CAAC,wBAAwB,EAAE,mCAAqB,CAAC,CAAC;AACzD,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,2BAA2B,EAAE,uCAAwB,CAAC,CAAC;AAC/D,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,qCAAsB,CAAC,CAAC;AAC3D,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,4BAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,0BAAY,CAAC,CAAC;AACtC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,wBAAU,CAAC,CAAC;AAClC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,0BAAY,CAAC,CAAC;AACrC,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,6BAAe,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,0BAA0B,EAAE,oCAAsB,CAAC,CAAC;AAC5D,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,gCAAkB,CAAC,CAAC;AACpC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,8BAAgB,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAAE,iCAAmB,CAAC,CAAC;AACrD,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,+BAAiB,CAAC,CAAC;AACnC,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,2BAAa,CAAC,CAAC;AAC9C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,mCAAqB,CAAC,CAAC;AAEvC,eAAe;AACf,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAClC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,0CAA0C;AAC1C,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IAC1C,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IAEtD,qBAAqB;IACrB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAEpC,wDAAwD;IACxD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9B,kBAAkB;QAClB,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,GAAG,CAAC,QAAQ,CAAC,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,iBAAiB;AACjB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAU,EAAE,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAE,EAAE;IAC9F,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC"}
|
||||
|
|
@ -69,6 +69,23 @@ app.get('/api/health', (req, res) => {
|
|||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// Production: Serve frontend static files
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const publicPath = path.join(process.cwd(), 'public');
|
||||
|
||||
// Serve static files
|
||||
app.use(express.static(publicPath));
|
||||
|
||||
// SPA fallback: serve index.html for all non-API routes
|
||||
app.get('*', (req, res, next) => {
|
||||
// Skip API routes
|
||||
if (req.path.startsWith('/api')) {
|
||||
return next();
|
||||
}
|
||||
res.sendFile(path.join(publicPath, 'index.html'));
|
||||
});
|
||||
}
|
||||
|
||||
// Error handling
|
||||
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
console.error(err.stack);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# OpenCRM Docker Environment
|
||||
# ============================
|
||||
# Copy this file to .env and adjust the values
|
||||
|
||||
# Domain (for Caddy SSL certificate)
|
||||
# Use your actual domain, e.g., crm.example.com
|
||||
DOMAIN=localhost
|
||||
|
||||
# Database
|
||||
DB_ROOT_PASSWORD=change-this-root-password
|
||||
DB_NAME=opencrm
|
||||
DB_USER=opencrm
|
||||
DB_PASSWORD=change-this-password
|
||||
|
||||
# JWT Authentication
|
||||
# Generate with: openssl rand -base64 32
|
||||
JWT_SECRET=change-this-to-a-secure-random-string
|
||||
JWT_EXPIRES_IN=7d
|
||||
|
||||
# Encryption Key (for portal credentials)
|
||||
# Generate with: openssl rand -hex 32
|
||||
ENCRYPTION_KEY=change-this-to-a-32-byte-hex-key
|
||||
|
||||
# First Install: Set to "true" to seed database on first startup
|
||||
# After first successful start, set back to "false"
|
||||
RUN_SEED=true
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# OpenCRM Caddyfile
|
||||
# ===================
|
||||
# Replace {$DOMAIN} with your actual domain or use environment variable
|
||||
# For local development without SSL, use localhost:80
|
||||
|
||||
{$DOMAIN:localhost} {
|
||||
# Reverse proxy to OpenCRM app
|
||||
reverse_proxy app:3001
|
||||
|
||||
# Logging
|
||||
log {
|
||||
output stdout
|
||||
format console
|
||||
}
|
||||
|
||||
# Security headers
|
||||
header {
|
||||
# Clickjacking protection
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
# XSS protection
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
# Referrer policy
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
}
|
||||
|
||||
# Gzip compression
|
||||
encode gzip
|
||||
|
||||
# Handle file uploads (increase body limit)
|
||||
request_body {
|
||||
max_size 50MB
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
# ===================================
|
||||
# Stage 1: Build Frontend
|
||||
# ===================================
|
||||
FROM node:20-alpine AS frontend-builder
|
||||
|
||||
WORKDIR /app/frontend
|
||||
|
||||
# Copy package files
|
||||
COPY frontend/package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Copy frontend source
|
||||
COPY frontend/ ./
|
||||
|
||||
# Build frontend
|
||||
RUN npm run build
|
||||
|
||||
# ===================================
|
||||
# Stage 2: Build Backend
|
||||
# ===================================
|
||||
FROM node:20-alpine AS backend-builder
|
||||
|
||||
WORKDIR /app/backend
|
||||
|
||||
# Install OpenSSL for Prisma
|
||||
RUN apk add --no-cache openssl
|
||||
|
||||
# Copy package files
|
||||
COPY backend/package*.json ./
|
||||
|
||||
# Install dependencies (including dev for build)
|
||||
RUN npm ci
|
||||
|
||||
# Copy backend source
|
||||
COPY backend/ ./
|
||||
|
||||
# Generate Prisma client
|
||||
RUN npx prisma generate
|
||||
|
||||
# Build TypeScript
|
||||
RUN npm run build
|
||||
|
||||
# ===================================
|
||||
# Stage 3: Production Runtime
|
||||
# ===================================
|
||||
FROM node:20-alpine AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install OpenSSL for Prisma runtime and netcat for DB health check
|
||||
RUN apk add --no-cache openssl netcat-openbsd
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S opencrm -u 1001 -G nodejs
|
||||
|
||||
# Copy backend package files
|
||||
COPY backend/package*.json ./
|
||||
|
||||
# Install production dependencies + tsx for seed script
|
||||
RUN npm ci --omit=dev && npm install tsx
|
||||
|
||||
# Copy Prisma schema and generate client
|
||||
COPY backend/prisma ./prisma
|
||||
RUN npx prisma generate
|
||||
|
||||
# Copy built backend
|
||||
COPY --from=backend-builder /app/backend/dist ./dist
|
||||
|
||||
# Copy built frontend to public directory
|
||||
COPY --from=frontend-builder /app/frontend/dist ./public
|
||||
|
||||
# Copy seed file for factory reset
|
||||
COPY backend/prisma/seed.ts ./prisma/
|
||||
|
||||
# Copy entrypoint script
|
||||
COPY docker/entrypoint.sh ./entrypoint.sh
|
||||
|
||||
# Create directories for uploads and backups
|
||||
RUN mkdir -p uploads backups && \
|
||||
chmod +x entrypoint.sh && \
|
||||
chown -R opencrm:nodejs /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER opencrm
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3001
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:3001/api/health || exit 1
|
||||
|
||||
# Start the application with entrypoint
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
# OpenCRM Docker Deployment
|
||||
|
||||
## Schnellstart
|
||||
|
||||
1. **Umgebungsvariablen konfigurieren:**
|
||||
```bash
|
||||
cd docker
|
||||
cp .env.example .env
|
||||
nano .env # Sichere Werte setzen
|
||||
```
|
||||
|
||||
2. **Container starten (erster Start mit RUN_SEED=true):**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Beim ersten Start wird automatisch:
|
||||
- Auf die Datenbank gewartet
|
||||
- Migrationen ausgeführt
|
||||
- Seed-Daten geladen (wenn `RUN_SEED=true`)
|
||||
|
||||
3. **Nach erfolgreicher Installation:**
|
||||
```bash
|
||||
# RUN_SEED in .env auf false setzen
|
||||
sed -i 's/RUN_SEED=true/RUN_SEED=false/' .env
|
||||
```
|
||||
|
||||
4. **Anwendung aufrufen:**
|
||||
- Mit Domain: `https://your-domain.com`
|
||||
- Lokal: `http://localhost`
|
||||
|
||||
5. **Login:**
|
||||
- E-Mail: `admin@admin.com`
|
||||
- Passwort: `admin`
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Caddy │
|
||||
│ (SSL/TLS) │
|
||||
│ :80/:443 │
|
||||
└──────┬──────┘
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ OpenCRM │
|
||||
│ (Node.js) │
|
||||
│ :3001 │
|
||||
└──────┬──────┘
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ MariaDB │
|
||||
│ :3306 │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Befehle
|
||||
|
||||
### Container verwalten
|
||||
```bash
|
||||
# Starten
|
||||
docker compose up -d
|
||||
|
||||
# Stoppen
|
||||
docker compose down
|
||||
|
||||
# Logs anzeigen
|
||||
docker compose logs -f app
|
||||
|
||||
# Neustart
|
||||
docker compose restart app
|
||||
```
|
||||
|
||||
### Datenbank
|
||||
```bash
|
||||
# Migration ausführen
|
||||
docker compose exec app npx prisma migrate deploy
|
||||
|
||||
# Seed-Daten laden
|
||||
docker compose exec app npx tsx prisma/seed.ts
|
||||
|
||||
# Prisma Studio (Datenbank-UI)
|
||||
docker compose exec app npx prisma studio
|
||||
```
|
||||
|
||||
### Backup & Restore
|
||||
```bash
|
||||
# Backup-Verzeichnis ist unter /app/backups gemountet
|
||||
# Backups werden über die Anwendung erstellt/wiederhergestellt
|
||||
```
|
||||
|
||||
### Update
|
||||
```bash
|
||||
# Image neu bauen und Container aktualisieren
|
||||
docker compose build --no-cache
|
||||
docker compose up -d
|
||||
# Migrationen werden automatisch beim Start ausgeführt
|
||||
```
|
||||
|
||||
## Volumes
|
||||
|
||||
| Volume | Beschreibung |
|
||||
|--------|--------------|
|
||||
| `mariadb_data` | Datenbank-Dateien |
|
||||
| `uploads_data` | Hochgeladene Dokumente |
|
||||
| `backups_data` | Backup-Dateien |
|
||||
| `caddy_data` | SSL-Zertifikate |
|
||||
| `caddy_config` | Caddy-Konfiguration |
|
||||
|
||||
## SSL-Zertifikat
|
||||
|
||||
Caddy holt automatisch ein Let's Encrypt Zertifikat wenn:
|
||||
- Die Domain in `.env` korrekt gesetzt ist
|
||||
- Port 80 und 443 von außen erreichbar sind
|
||||
- DNS auf den Server zeigt
|
||||
|
||||
Für lokale Entwicklung mit `DOMAIN=localhost` wird ein selbstsigniertes Zertifikat verwendet.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container startet nicht
|
||||
```bash
|
||||
docker compose logs app
|
||||
```
|
||||
|
||||
### Datenbank-Verbindung fehlgeschlagen
|
||||
```bash
|
||||
# Warten bis MariaDB bereit ist
|
||||
docker compose logs db
|
||||
```
|
||||
|
||||
### SSL-Zertifikat Probleme
|
||||
```bash
|
||||
docker compose logs caddy
|
||||
# Caddy-Daten zurücksetzen
|
||||
docker compose down
|
||||
docker volume rm opencrm_caddy_data opencrm_caddy_config
|
||||
docker compose up -d
|
||||
```
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
# ===================================
|
||||
# MariaDB Database
|
||||
# ===================================
|
||||
db:
|
||||
image: mariadb:10.11
|
||||
container_name: opencrm-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpassword}
|
||||
MYSQL_DATABASE: ${DB_NAME:-opencrm}
|
||||
MYSQL_USER: ${DB_USER:-opencrm}
|
||||
MYSQL_PASSWORD: ${DB_PASSWORD:-opencrm123}
|
||||
volumes:
|
||||
- mariadb_data:/var/lib/mysql
|
||||
networks:
|
||||
- opencrm-network
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
start_period: 10s
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
# ===================================
|
||||
# OpenCRM Application
|
||||
# ===================================
|
||||
app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
container_name: opencrm-app
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3001
|
||||
DATABASE_URL: mysql://${DB_USER:-opencrm}:${DB_PASSWORD:-opencrm123}@db:3306/${DB_NAME:-opencrm}
|
||||
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
|
||||
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-7d}
|
||||
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?ENCRYPTION_KEY is required}
|
||||
RUN_SEED: ${RUN_SEED:-false}
|
||||
volumes:
|
||||
- uploads_data:/app/uploads
|
||||
- backups_data:/app/backups
|
||||
networks:
|
||||
- opencrm-network
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/api/health"]
|
||||
start_period: 60s
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# ===================================
|
||||
# Caddy Reverse Proxy (with auto SSL)
|
||||
# ===================================
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
container_name: opencrm-caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
networks:
|
||||
- opencrm-network
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_healthy
|
||||
|
||||
networks:
|
||||
opencrm-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
mariadb_data:
|
||||
uploads_data:
|
||||
backups_data:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "=== OpenCRM Startup ==="
|
||||
|
||||
# Wait for database to be ready
|
||||
echo "Waiting for database connection..."
|
||||
MAX_RETRIES=30
|
||||
RETRY_COUNT=0
|
||||
|
||||
while ! nc -z db 3306 2>/dev/null; do
|
||||
RETRY_COUNT=$((RETRY_COUNT + 1))
|
||||
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
|
||||
echo "Error: Database not available after $MAX_RETRIES attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo " Attempt $RETRY_COUNT/$MAX_RETRIES - waiting..."
|
||||
sleep 2
|
||||
done
|
||||
echo "Database is ready!"
|
||||
|
||||
# Run migrations
|
||||
echo "Running database migrations..."
|
||||
npx prisma migrate deploy
|
||||
|
||||
# Seed database if RUN_SEED is set (first install)
|
||||
if [ "$RUN_SEED" = "true" ]; then
|
||||
echo "Seeding database..."
|
||||
npx tsx prisma/seed.ts
|
||||
fi
|
||||
|
||||
# Start the application
|
||||
echo "Starting OpenCRM server..."
|
||||
exec node dist/index.js
|
||||
Loading…
Reference in New Issue