feat: update after deploy
This commit is contained in:
parent
b9c998f33d
commit
970a0f75c6
46
CLAUDE.md
46
CLAUDE.md
|
|
@ -300,6 +300,52 @@ curl -X POST http://localhost:3000/api/upload \
|
||||||
- **Rate Limits**: 100 requests per hour per key
|
- **Rate Limits**: 100 requests per hour per key
|
||||||
- **Revocation**: Soft delete via `is_active` flag
|
- **Revocation**: Soft delete via `is_active` flag
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
### VPS Infrastructure
|
||||||
|
|
||||||
|
Banatie is deployed as an isolated ecosystem on VPS at `/opt/banatie/`:
|
||||||
|
|
||||||
|
| Service | URL | Container |
|
||||||
|
|---------|-----|-----------|
|
||||||
|
| Landing | https://banatie.app | banatie-landing |
|
||||||
|
| API | https://api.banatie.app | banatie-api |
|
||||||
|
| MinIO Console | https://storage.banatie.app | banatie-minio |
|
||||||
|
| MinIO CDN | https://cdn.banatie.app | banatie-minio |
|
||||||
|
|
||||||
|
### Deploy Scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From project root
|
||||||
|
./scripts/deploy-landing.sh # Deploy landing
|
||||||
|
./scripts/deploy-landing.sh --no-cache # Force rebuild (when deps change)
|
||||||
|
./scripts/deploy-api.sh # Deploy API
|
||||||
|
./scripts/deploy-api.sh --no-cache # Force rebuild
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Configuration Files
|
||||||
|
|
||||||
|
```
|
||||||
|
infrastructure/
|
||||||
|
├── docker-compose.production.yml # VPS docker-compose
|
||||||
|
├── .env.example # Environment variables template
|
||||||
|
└── secrets.env.example # Secrets template
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Production Learnings
|
||||||
|
|
||||||
|
1. **NEXT_PUBLIC_* variables** - Must be set at build time AND runtime for Next.js client-side code
|
||||||
|
2. **pnpm workspaces in Docker** - Symlinks break between stages; use single-stage install with `pnpm --filter`
|
||||||
|
3. **Docker User NS Remapping** - VPS uses UID offset 165536; container UID 1001 → host UID 166537
|
||||||
|
4. **DATABASE_URL encoding** - Special characters like `=` must be URL-encoded (`%3D`)
|
||||||
|
|
||||||
|
### Known Production Issues (Non-Critical)
|
||||||
|
|
||||||
|
1. **Healthcheck showing "unhealthy"** - Alpine images lack curl; services work correctly
|
||||||
|
2. **Next.js cache permission** - `.next/cache` may show EACCES; non-critical for functionality
|
||||||
|
|
||||||
|
See [docs/deployment.md](docs/deployment.md) for full deployment guide.
|
||||||
|
|
||||||
## Development Notes
|
## Development Notes
|
||||||
|
|
||||||
- Uses pnpm workspaces for monorepo management (required >= 8.0.0)
|
- Uses pnpm workspaces for monorepo management (required >= 8.0.0)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
# Banatie Production Deployment Guide
|
||||||
|
|
||||||
|
> Last Updated: December 23, 2025
|
||||||
|
|
||||||
|
This guide covers deploying Banatie to a VPS with Docker. For local development, see [environment.md](./environment.md).
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Banatie is deployed as an isolated ecosystem with:
|
||||||
|
|
||||||
|
- **Landing** (Next.js 15.5.9) → banatie.app
|
||||||
|
- **API** (Express.js) → api.banatie.app
|
||||||
|
- **PostgreSQL** (15-alpine) → Database
|
||||||
|
- **MinIO** (SNMD mode) → Object storage
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- VPS with Docker and Docker Compose
|
||||||
|
- Caddy reverse proxy (or similar) with SSL
|
||||||
|
- DNS records configured
|
||||||
|
- GEMINI_API_KEY from Google AI Studio
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Create directory structure
|
||||||
|
sudo mkdir -p /opt/banatie/{data,logs,scripts}
|
||||||
|
sudo mkdir -p /opt/banatie/data/{postgres,minio,waitlist-logs,api-results,api-uploads}
|
||||||
|
sudo mkdir -p /opt/banatie/data/minio/{drive1,drive2,drive3,drive4}
|
||||||
|
sudo chown -R $USER:$USER /opt/banatie
|
||||||
|
|
||||||
|
# 2. Clone repository
|
||||||
|
git clone <repo> ~/workspace/projects/banatie-service
|
||||||
|
|
||||||
|
# 3. Copy production configs
|
||||||
|
cp ~/workspace/projects/banatie-service/infrastructure/docker-compose.production.yml /opt/banatie/docker-compose.yml
|
||||||
|
cp ~/workspace/projects/banatie-service/infrastructure/.env.example /opt/banatie/.env
|
||||||
|
cp ~/workspace/projects/banatie-service/infrastructure/secrets.env.example /opt/banatie/secrets.env
|
||||||
|
cp ~/workspace/projects/banatie-service/infrastructure/init-db.sql /opt/banatie/scripts/
|
||||||
|
|
||||||
|
# 4. Configure environment
|
||||||
|
nano /opt/banatie/.env # Edit public variables
|
||||||
|
nano /opt/banatie/secrets.env # Generate and add secrets
|
||||||
|
chmod 600 /opt/banatie/secrets.env
|
||||||
|
|
||||||
|
# 5. Build and start
|
||||||
|
cd /opt/banatie
|
||||||
|
docker compose --env-file .env --env-file secrets.env build
|
||||||
|
docker compose --env-file .env --env-file secrets.env up -d
|
||||||
|
|
||||||
|
# 6. Initialize database schema
|
||||||
|
cd ~/workspace/projects/banatie-service
|
||||||
|
pnpm install
|
||||||
|
pnpm --filter @banatie/database db:push
|
||||||
|
|
||||||
|
# 7. Create master API key
|
||||||
|
curl -X POST https://api.banatie.app/api/bootstrap/initial-key
|
||||||
|
# Or use UI: https://banatie.app/admin/master/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deploy Scripts
|
||||||
|
|
||||||
|
Located in `scripts/` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy landing page
|
||||||
|
./scripts/deploy-landing.sh # Normal deploy
|
||||||
|
./scripts/deploy-landing.sh --no-cache # Fresh build (when deps change)
|
||||||
|
|
||||||
|
# Deploy API
|
||||||
|
./scripts/deploy-api.sh # Normal deploy
|
||||||
|
./scripts/deploy-api.sh --no-cache # Fresh build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Files
|
||||||
|
|
||||||
|
### Environment Variables (.env)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NODE_ENV=production
|
||||||
|
PORT=3000
|
||||||
|
POSTGRES_DB=banatie_db
|
||||||
|
POSTGRES_USER=banatie_user
|
||||||
|
DATABASE_URL=postgresql://banatie_user:<password>@banatie-postgres:5432/banatie_db
|
||||||
|
MINIO_ENDPOINT=banatie-minio:9000
|
||||||
|
MINIO_PUBLIC_URL=https://cdn.banatie.app
|
||||||
|
API_PUBLIC_URL=https://api.banatie.app
|
||||||
|
NEXT_PUBLIC_API_URL=https://api.banatie.app
|
||||||
|
CORS_ORIGIN=https://banatie.app,https://api.banatie.app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secrets (secrets.env)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
POSTGRES_PASSWORD=<generated>
|
||||||
|
MINIO_ROOT_USER=banatie_admin
|
||||||
|
MINIO_ROOT_PASSWORD=<generated>
|
||||||
|
MINIO_ACCESS_KEY=banatie_service
|
||||||
|
MINIO_SECRET_KEY=<generated>
|
||||||
|
GEMINI_API_KEY=<your-key>
|
||||||
|
JWT_SECRET=<generated>
|
||||||
|
SESSION_SECRET=<generated>
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate secrets with:
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32 | tr -d '\n\r '
|
||||||
|
```
|
||||||
|
|
||||||
|
## DNS Configuration
|
||||||
|
|
||||||
|
| Type | Name | Value |
|
||||||
|
|------|------|-------|
|
||||||
|
| A | @ | VPS_IP |
|
||||||
|
| CNAME | www | banatie.app |
|
||||||
|
| CNAME | api | banatie.app |
|
||||||
|
| CNAME | storage | banatie.app |
|
||||||
|
| CNAME | cdn | banatie.app |
|
||||||
|
|
||||||
|
## Caddy Configuration
|
||||||
|
|
||||||
|
Add to your Caddyfile:
|
||||||
|
|
||||||
|
```caddy
|
||||||
|
www.banatie.app {
|
||||||
|
redir https://banatie.app{uri} permanent
|
||||||
|
}
|
||||||
|
|
||||||
|
banatie.app {
|
||||||
|
reverse_proxy banatie-landing:3000
|
||||||
|
}
|
||||||
|
|
||||||
|
api.banatie.app {
|
||||||
|
reverse_proxy banatie-api:3000
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.banatie.app {
|
||||||
|
reverse_proxy banatie-minio:9001
|
||||||
|
}
|
||||||
|
|
||||||
|
cdn.banatie.app {
|
||||||
|
reverse_proxy banatie-minio:9000
|
||||||
|
header Access-Control-Allow-Origin "*"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Permission Denied on Volumes
|
||||||
|
|
||||||
|
Docker User Namespace Remapping offsets UIDs by 165536:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fix permissions for Next.js (uid 1001 → 166537)
|
||||||
|
sudo chown -R 166537:166537 /opt/banatie/data/waitlist-logs
|
||||||
|
|
||||||
|
# Fix permissions for API (uid 1001 → 166537)
|
||||||
|
sudo chown -R 166537:166537 /opt/banatie/data/api-results
|
||||||
|
sudo chown -R 166537:166537 /opt/banatie/data/api-uploads
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables Not Applied
|
||||||
|
|
||||||
|
Use `docker compose up -d` instead of `docker restart`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --env-file .env --env-file secrets.env up -d banatie-api
|
||||||
|
```
|
||||||
|
|
||||||
|
### NEXT_PUBLIC_* Variables
|
||||||
|
|
||||||
|
Must be set at both build time AND runtime. Ensure `NEXT_PUBLIC_API_URL` is in .env before building.
|
||||||
|
|
||||||
|
### pnpm Workspace Symlinks in Docker
|
||||||
|
|
||||||
|
The Dockerfiles use simplified single-stage install to avoid symlink issues:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
|
||||||
|
COPY apps/landing ./apps/landing
|
||||||
|
COPY packages/database ./packages/database
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
RUN pnpm --filter @banatie/landing build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Connection Refused
|
||||||
|
|
||||||
|
URL-encode special characters in DATABASE_URL:
|
||||||
|
- `=` → `%3D`
|
||||||
|
- `@` → `%40`
|
||||||
|
- `#` → `%23`
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
### Healthcheck Showing "Unhealthy"
|
||||||
|
|
||||||
|
Alpine images don't have `curl` by default. The healthcheck uses `wget` but may still show unhealthy in some cases. Services work correctly despite this status.
|
||||||
|
|
||||||
|
### Next.js Cache Permission Warning
|
||||||
|
|
||||||
|
```
|
||||||
|
EACCES: permission denied, mkdir '/app/apps/landing/.next/cache'
|
||||||
|
```
|
||||||
|
|
||||||
|
This is non-critical - images still work, just not cached. To fix:
|
||||||
|
```bash
|
||||||
|
sudo chown -R 166537:166537 /opt/banatie/data/landing-cache
|
||||||
|
# And add volume mount for .next/cache
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full VPS Documentation
|
||||||
|
|
||||||
|
For complete VPS setup and infrastructure details, see:
|
||||||
|
- VPS Repository: `VPS/docs/banatie-deployment.md`
|
||||||
|
- Deployment Manual: `VPS/manuals/banatie-service-deployment.md`
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Banatie Production Environment Variables
|
||||||
|
# ==========================================
|
||||||
|
# Copy this file to .env and fill in the values
|
||||||
|
#
|
||||||
|
# Location on VPS: /opt/banatie/.env
|
||||||
|
# Last Updated: December 23, 2025
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# Node.js Configuration
|
||||||
|
# ----------------------------------------
|
||||||
|
NODE_ENV=production
|
||||||
|
PORT=3000
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# PostgreSQL Database
|
||||||
|
# ----------------------------------------
|
||||||
|
POSTGRES_DB=banatie_db
|
||||||
|
POSTGRES_USER=banatie_user
|
||||||
|
# Note: POSTGRES_PASSWORD is in secrets.env
|
||||||
|
|
||||||
|
# DATABASE_URL for application use
|
||||||
|
# IMPORTANT: URL-encode special characters (e.g., = → %3D, @ → %40)
|
||||||
|
# Example: DATABASE_URL=postgresql://banatie_user:MyP%3Dssword@banatie-postgres:5432/banatie_db
|
||||||
|
DATABASE_URL=postgresql://banatie_user:<url-encoded-password>@banatie-postgres:5432/banatie_db
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# MinIO Object Storage
|
||||||
|
# ----------------------------------------
|
||||||
|
MINIO_ENDPOINT=banatie-minio:9000
|
||||||
|
MINIO_BUCKET_NAME=banatie
|
||||||
|
MINIO_USE_SSL=false
|
||||||
|
STORAGE_TYPE=minio
|
||||||
|
|
||||||
|
# Public URL for CDN access (used in API responses)
|
||||||
|
MINIO_PUBLIC_URL=https://cdn.banatie.app
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# API Configuration
|
||||||
|
# ----------------------------------------
|
||||||
|
API_BASE_URL=https://api.banatie.app
|
||||||
|
API_PUBLIC_URL=https://api.banatie.app
|
||||||
|
|
||||||
|
# IMPORTANT: This must be set for Next.js client-side code
|
||||||
|
NEXT_PUBLIC_API_URL=https://api.banatie.app
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# CORS Configuration
|
||||||
|
# ----------------------------------------
|
||||||
|
# Comma-separated list of allowed origins
|
||||||
|
CORS_ORIGIN=https://banatie.app,https://api.banatie.app
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# Multi-tenancy Defaults
|
||||||
|
# ----------------------------------------
|
||||||
|
DEFAULT_ORG_ID=default
|
||||||
|
DEFAULT_PROJECT_ID=main
|
||||||
|
DEFAULT_USER_ID=system
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Banatie Infrastructure
|
||||||
|
|
||||||
|
Production deployment configuration files for VPS.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `docker-compose.production.yml` | Docker Compose for VPS deployment |
|
||||||
|
| `.env.example` | Environment variables template |
|
||||||
|
| `secrets.env.example` | Secrets template (passwords, API keys) |
|
||||||
|
| `init-db.sql` | PostgreSQL initialization (grants permissions) |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy to VPS
|
||||||
|
scp -r infrastructure/* usul-vps:/opt/banatie/
|
||||||
|
|
||||||
|
# On VPS
|
||||||
|
cd /opt/banatie
|
||||||
|
cp docker-compose.production.yml docker-compose.yml
|
||||||
|
cp .env.example .env
|
||||||
|
cp secrets.env.example secrets.env
|
||||||
|
cp init-db.sql scripts/
|
||||||
|
|
||||||
|
# Edit configuration
|
||||||
|
nano .env
|
||||||
|
nano secrets.env
|
||||||
|
chmod 600 secrets.env
|
||||||
|
|
||||||
|
# Deploy
|
||||||
|
docker compose --env-file .env --env-file secrets.env up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## VPS Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/banatie/
|
||||||
|
├── docker-compose.yml # Copy from docker-compose.production.yml
|
||||||
|
├── .env # Copy from .env.example and configure
|
||||||
|
├── secrets.env # Copy from secrets.env.example and configure
|
||||||
|
├── scripts/
|
||||||
|
│ └── init-db.sql # Copy from init-db.sql
|
||||||
|
├── data/
|
||||||
|
│ ├── postgres/ # PostgreSQL data (DO NOT CREATE MANUALLY)
|
||||||
|
│ ├── minio/ # MinIO drives (DO NOT CREATE MANUALLY)
|
||||||
|
│ ├── waitlist-logs/
|
||||||
|
│ ├── api-results/
|
||||||
|
│ └── api-uploads/
|
||||||
|
└── logs/
|
||||||
|
└── api/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. **Docker User NS Remapping**: VPS uses UID offset 165536. Let Docker create data directories.
|
||||||
|
2. **Secrets**: Never commit secrets.env to git. Use `chmod 600`.
|
||||||
|
3. **Database**: Tables are created by Drizzle ORM, not init-db.sql.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- Full guide: [docs/deployment.md](../docs/deployment.md)
|
||||||
|
- VPS docs: VPS repo `docs/banatie-deployment.md`
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
# Banatie Production - VPS Isolated Deployment
|
||||||
|
# ============================================
|
||||||
|
# This is the production docker-compose file used on VPS at /opt/banatie/
|
||||||
|
# Last Updated: December 23, 2025
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# docker compose --env-file .env --env-file secrets.env up -d
|
||||||
|
# docker compose --env-file .env --env-file secrets.env build --no-cache
|
||||||
|
#
|
||||||
|
# Key differences from dev:
|
||||||
|
# - Uses external proxy-network for Caddy integration
|
||||||
|
# - All services isolated in banatie-internal network
|
||||||
|
# - MinIO with 4 drives for full S3 compatibility
|
||||||
|
# - Secrets stored in separate secrets.env file
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ----------------------------------------
|
||||||
|
# API Service - Express.js REST API
|
||||||
|
# ----------------------------------------
|
||||||
|
banatie-api:
|
||||||
|
build:
|
||||||
|
context: /home/usul/workspace/projects/banatie-service
|
||||||
|
dockerfile: apps/api-service/Dockerfile
|
||||||
|
target: production
|
||||||
|
container_name: banatie-api
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- banatie-internal
|
||||||
|
- proxy-network
|
||||||
|
depends_on:
|
||||||
|
banatie-postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
banatie-minio:
|
||||||
|
condition: service_healthy
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
- secrets.env
|
||||||
|
environment:
|
||||||
|
- IS_DOCKER=true
|
||||||
|
- NODE_ENV=production
|
||||||
|
volumes:
|
||||||
|
- ./logs/api:/app/apps/api-service/logs
|
||||||
|
- ./data/api-results:/app/results
|
||||||
|
- ./data/api-uploads:/app/uploads
|
||||||
|
healthcheck:
|
||||||
|
# Note: Alpine images don't have curl by default
|
||||||
|
# Using wget instead, but may still show "unhealthy" - service works correctly
|
||||||
|
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 60s
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# Landing Page - Next.js 15.5.9
|
||||||
|
# ----------------------------------------
|
||||||
|
banatie-landing:
|
||||||
|
build:
|
||||||
|
context: /home/usul/workspace/projects/banatie-service
|
||||||
|
dockerfile: apps/landing/Dockerfile
|
||||||
|
container_name: banatie-landing
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- banatie-internal
|
||||||
|
- proxy-network
|
||||||
|
depends_on:
|
||||||
|
- banatie-postgres
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
- secrets.env
|
||||||
|
environment:
|
||||||
|
- IS_DOCKER=true
|
||||||
|
- NODE_ENV=production
|
||||||
|
- HOSTNAME=0.0.0.0
|
||||||
|
- WAITLIST_LOGS_PATH=/app/waitlist-logs
|
||||||
|
volumes:
|
||||||
|
- ./data/waitlist-logs:/app/waitlist-logs
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# PostgreSQL Database
|
||||||
|
# ----------------------------------------
|
||||||
|
banatie-postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: banatie-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- banatie-internal
|
||||||
|
volumes:
|
||||||
|
- ./data/postgres:/var/lib/postgresql/data
|
||||||
|
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# MinIO Object Storage (S3-compatible)
|
||||||
|
# ----------------------------------------
|
||||||
|
banatie-minio:
|
||||||
|
image: quay.io/minio/minio:latest
|
||||||
|
container_name: banatie-minio
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- banatie-internal
|
||||||
|
- proxy-network
|
||||||
|
volumes:
|
||||||
|
# 4 drives for SNMD mode (full S3 compatibility)
|
||||||
|
- ./data/minio/drive1:/data1
|
||||||
|
- ./data/minio/drive2:/data2
|
||||||
|
- ./data/minio/drive3:/data3
|
||||||
|
- ./data/minio/drive4:/data4
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
|
MINIO_BROWSER_REDIRECT_URL: https://storage.banatie.app
|
||||||
|
MINIO_SERVER_URL: https://cdn.banatie.app
|
||||||
|
command: server /data{1...4} --console-address ":9001"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# Storage Initialization (runs once)
|
||||||
|
# ----------------------------------------
|
||||||
|
banatie-storage-init:
|
||||||
|
image: minio/mc:latest
|
||||||
|
container_name: banatie-storage-init
|
||||||
|
networks:
|
||||||
|
- banatie-internal
|
||||||
|
depends_on:
|
||||||
|
banatie-minio:
|
||||||
|
condition: service_healthy
|
||||||
|
env_file:
|
||||||
|
- secrets.env
|
||||||
|
entrypoint:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
echo '=== MinIO Storage Initialization ==='
|
||||||
|
mc alias set storage http://banatie-minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}
|
||||||
|
mc mb --ignore-existing storage/banatie
|
||||||
|
mc admin user add storage $${MINIO_ACCESS_KEY} $${MINIO_SECRET_KEY} || echo 'User may already exist'
|
||||||
|
mc admin policy attach storage readwrite --user=$${MINIO_ACCESS_KEY} || echo 'Policy may already be attached'
|
||||||
|
cat > /tmp/lifecycle.json <<'LCEOF'
|
||||||
|
{"Rules":[{"ID":"temp-cleanup","Status":"Enabled","Filter":{"Prefix":"temp/"},"Expiration":{"Days":7}}]}
|
||||||
|
LCEOF
|
||||||
|
mc ilm import storage/banatie < /tmp/lifecycle.json || echo 'Lifecycle policy may already exist'
|
||||||
|
echo '=== Storage Initialization Completed ==='
|
||||||
|
exit 0
|
||||||
|
restart: "no"
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# Networks
|
||||||
|
# ----------------------------------------
|
||||||
|
networks:
|
||||||
|
# Internal network for service communication
|
||||||
|
# internal: true means no outbound access
|
||||||
|
banatie-internal:
|
||||||
|
driver: bridge
|
||||||
|
internal: true
|
||||||
|
|
||||||
|
# External network shared with Caddy reverse proxy
|
||||||
|
# Must be created by Caddy's docker-compose first
|
||||||
|
proxy-network:
|
||||||
|
name: services_proxy-network
|
||||||
|
external: true
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
-- Banatie PostgreSQL 15 Permission Fix
|
||||||
|
-- =====================================
|
||||||
|
-- This script runs only on first PostgreSQL startup.
|
||||||
|
-- It grants necessary permissions for the Drizzle ORM to create tables.
|
||||||
|
--
|
||||||
|
-- Note: Actual tables are created by Drizzle ORM during deployment:
|
||||||
|
-- pnpm --filter @banatie/database db:push
|
||||||
|
--
|
||||||
|
-- PostgreSQL 15+ removed default CREATE privileges on public schema for security.
|
||||||
|
-- This script restores those privileges for the service user.
|
||||||
|
|
||||||
|
-- Grant CREATE permission on public schema
|
||||||
|
GRANT CREATE ON SCHEMA public TO banatie_user;
|
||||||
|
GRANT ALL ON SCHEMA public TO banatie_user;
|
||||||
|
|
||||||
|
-- Log completion
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
RAISE NOTICE 'Banatie database initialized. Run db:push to create tables.';
|
||||||
|
END $$;
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Banatie Production Secrets
|
||||||
|
# ==========================
|
||||||
|
# NEVER COMMIT THIS FILE TO GIT!
|
||||||
|
#
|
||||||
|
# Copy this file to secrets.env and generate real values
|
||||||
|
# Location on VPS: /opt/banatie/secrets.env
|
||||||
|
# Permissions: chmod 600 secrets.env
|
||||||
|
#
|
||||||
|
# Last Updated: December 23, 2025
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# PostgreSQL Secrets
|
||||||
|
# ----------------------------------------
|
||||||
|
# Generate: openssl rand -base64 32 | tr -d '\n\r '
|
||||||
|
POSTGRES_PASSWORD=<generate-strong-password>
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# MinIO Root Credentials
|
||||||
|
# ----------------------------------------
|
||||||
|
# Root user for MinIO admin console
|
||||||
|
MINIO_ROOT_USER=banatie_admin
|
||||||
|
# Generate: openssl rand -base64 32 | tr -d '\n\r '
|
||||||
|
MINIO_ROOT_PASSWORD=<generate-strong-password>
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# MinIO Service Account
|
||||||
|
# ----------------------------------------
|
||||||
|
# Service account for API access to MinIO
|
||||||
|
MINIO_ACCESS_KEY=banatie_service
|
||||||
|
# Generate: openssl rand -base64 32 | tr -d '\n\r '
|
||||||
|
MINIO_SECRET_KEY=<generate-strong-password>
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# API Secrets
|
||||||
|
# ----------------------------------------
|
||||||
|
# Google Gemini API key for image generation
|
||||||
|
# Get from: https://aistudio.google.com/app/apikey
|
||||||
|
GEMINI_API_KEY=<your-gemini-api-key>
|
||||||
|
|
||||||
|
# JWT secret for token signing
|
||||||
|
# Generate: openssl rand -base64 64 | tr -d '\n\r '
|
||||||
|
JWT_SECRET=<generate-strong-secret>
|
||||||
|
|
||||||
|
# Session secret for Express sessions
|
||||||
|
# Generate: openssl rand -base64 32 | tr -d '\n\r '
|
||||||
|
SESSION_SECRET=<generate-strong-secret>
|
||||||
|
|
||||||
|
# ----------------------------------------
|
||||||
|
# Quick Generation Script
|
||||||
|
# ----------------------------------------
|
||||||
|
# Run this to generate all secrets:
|
||||||
|
#
|
||||||
|
# echo "POSTGRES_PASSWORD=$(openssl rand -base64 32 | tr -d '\n\r ')"
|
||||||
|
# echo "MINIO_ROOT_USER=banatie_admin"
|
||||||
|
# echo "MINIO_ROOT_PASSWORD=$(openssl rand -base64 32 | tr -d '\n\r ')"
|
||||||
|
# echo "MINIO_ACCESS_KEY=banatie_service"
|
||||||
|
# echo "MINIO_SECRET_KEY=$(openssl rand -base64 32 | tr -d '\n\r ')"
|
||||||
|
# echo "JWT_SECRET=$(openssl rand -base64 64 | tr -d '\n\r ')"
|
||||||
|
# echo "SESSION_SECRET=$(openssl rand -base64 32 | tr -d '\n\r ')"
|
||||||
|
# echo "GEMINI_API_KEY=<add-manually>"
|
||||||
|
|
@ -23,7 +23,8 @@
|
||||||
"clean": "pnpm -r clean && rm -rf node_modules",
|
"clean": "pnpm -r clean && rm -rf node_modules",
|
||||||
"deploy:landing": "./scripts/deploy-landing.sh",
|
"deploy:landing": "./scripts/deploy-landing.sh",
|
||||||
"deploy:landing:no-cache": "./scripts/deploy-landing.sh --no-cache",
|
"deploy:landing:no-cache": "./scripts/deploy-landing.sh --no-cache",
|
||||||
"deploy:api": "./scripts/deploy-landing.sh"
|
"deploy:api": "./scripts/deploy-api.sh",
|
||||||
|
"deploy:api:no-cache": "./scripts/deploy-api.sh --no-cache"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"monorepo",
|
"monorepo",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue