Skip to content

Commit 5c8df29

Browse files
KevenWMarkhamclaude
andcommitted
feat(docker): add persistent storage and Desktop/VPS sync
- Add persistent volumes for uploads, transcripts, exports - Create docker-compose.override.yml for development with hot reload - Fix Dockerfile build order (packages first, then app) - Add backup/restore/sync scripts for Desktop <-> VPS consistency - Fix export package tsconfig to include DOM lib - Update .gitignore for data directories 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 82d990a commit 5c8df29

File tree

12 files changed

+415
-28
lines changed

12 files changed

+415
-28
lines changed

.env.production.example

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
11
# Production Environment Variables
2-
# Copy this to .env.production and fill in your values
2+
# Copy to .env.production and fill in values
33

4-
# Database Configuration
4+
# PostgreSQL Database
55
POSTGRES_USER=postgres
66
POSTGRES_PASSWORD=your_secure_password_here
77
POSTGRES_DB=transcript_parser
88

9-
# N8N Database (separate database in same PostgreSQL instance)
10-
N8N_DB=n8n
9+
# Gemini API
10+
VITE_GEMINI_API_KEY=your_gemini_api_key_here
1111

12-
# N8N Configuration
12+
# N8N Workflow Automation
1313
N8N_USER=admin
14-
N8N_PASSWORD=your_secure_n8n_password_here
15-
N8N_HOST=n8n.smarthaven.ai.com
14+
N8N_PASSWORD=your_n8n_password_here
15+
N8N_HOST=n8n.yourdomain.com
16+
N8N_DB=n8n
1617

17-
# Timezone
18+
# General
1819
TIMEZONE=America/New_York
1920

20-
# Application Configuration
21-
NODE_ENV=production
22-
PORT=3000
23-
24-
# Gemini AI API Key
25-
VITE_GEMINI_API_KEY=your_gemini_api_key_here
26-
27-
# Domain Configuration
28-
DOMAIN=smarthaven.ai.com
21+
# Access Codes (comma-separated)
22+
VITE_VALID_ACCESS_CODES=code1,code2,code3

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,13 @@ playwright-report/
7575
# E2E test screenshots
7676
tests/e2e/*.png
7777
tests/e2e/*.webm
78+
79+
# Docker data volumes (keep structure, ignore content)
80+
data/*
81+
!data/.gitkeep
82+
!data/*/.gitkeep
83+
backups/
84+
85+
# FFmpeg temp files
86+
public/ffmpeg-core*.js
87+
public/ffmpeg-core*.wasm

Dockerfile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ RUN pnpm install --frozen-lockfile
1818
# Copy source code
1919
COPY . .
2020

21-
# Build all packages
22-
RUN pnpm build
21+
# Build workspace packages first (types, ui, ai-services, export, etc.)
22+
RUN pnpm -r --filter "@transcript-parser/*" build
23+
24+
# Build main application
25+
RUN pnpm build:web
2326

2427
# Stage 2: Production image
2528
FROM node:20-alpine AS production

data/.gitkeep

Whitespace-only changes.

docker-compose.override.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Development overrides for docker-compose
2+
# This file is automatically merged with docker-compose.yml when running `docker-compose up`
3+
# For production, use: docker-compose -f docker-compose.yml up
4+
5+
services:
6+
# Development app with hot reloading
7+
app:
8+
build:
9+
context: .
10+
dockerfile: Dockerfile
11+
target: builder # Use builder stage for dev
12+
container_name: transcript-parser-dev
13+
ports:
14+
- "5173:5173" # Vite dev server
15+
- "3000:3000"
16+
environment:
17+
- NODE_ENV=development
18+
- VITE_DEV_SERVER_HOST=0.0.0.0
19+
- UPLOAD_DIR=/app/data/uploads
20+
- TRANSCRIPT_DIR=/app/data/transcripts
21+
- EXPORT_DIR=/app/data/exports
22+
volumes:
23+
# Source code with hot reloading (bind mounts)
24+
- ./src:/app/src:delegated
25+
- ./packages:/app/packages:delegated
26+
- ./public:/app/public:delegated
27+
# Persistent data volumes (consistent with VPS)
28+
- app-uploads:/app/data/uploads
29+
- app-transcripts:/app/data/transcripts
30+
- app-exports:/app/data/exports
31+
# Local data directory for easy access
32+
- ./data:/app/data-local
33+
# Exclude node_modules from bind mount
34+
- /app/node_modules
35+
command: ["pnpm", "dev", "--host", "0.0.0.0"]
36+
healthcheck:
37+
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5173"]
38+
interval: 30s
39+
timeout: 10s
40+
retries: 3
41+
start_period: 60s
42+
43+
# Development audio server with source mounting
44+
audio-server:
45+
volumes:
46+
- ./server:/app/server:delegated
47+
- /app/node_modules
48+
environment:
49+
- NODE_ENV=development
50+
- CORS_ORIGIN=*
51+
52+
# Use local PostgreSQL data for development
53+
postgres:
54+
ports:
55+
- "5432:5432" # Expose for local tools
56+
volumes:
57+
- ./data/postgres:/var/lib/postgresql/data # Local bind for easy backup
58+
59+
# N8N for local workflow testing
60+
n8n:
61+
environment:
62+
- N8N_BASIC_AUTH_ACTIVE=false # Disable auth for local dev
63+
volumes:
64+
- ./data/n8n:/home/node/.n8n # Local bind for easy backup
65+
66+
# Use bind mounts for development data access
67+
volumes:
68+
app-uploads:
69+
driver: local
70+
driver_opts:
71+
type: none
72+
o: bind
73+
device: ${PWD}/data/uploads
74+
app-transcripts:
75+
driver: local
76+
driver_opts:
77+
type: none
78+
o: bind
79+
device: ${PWD}/data/transcripts
80+
app-exports:
81+
driver: local
82+
driver_opts:
83+
type: none
84+
o: bind
85+
device: ${PWD}/data/exports

docker-compose.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: '3.8'
2-
31
services:
42
# PostgreSQL Database (shared by app and N8N)
53
postgres:
@@ -35,8 +33,15 @@ services:
3533
- NODE_ENV=production
3634
- PORT=3000
3735
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-changeme}@postgres:5432/${POSTGRES_DB:-transcript_parser}
36+
- UPLOAD_DIR=/app/data/uploads
37+
- TRANSCRIPT_DIR=/app/data/transcripts
38+
- EXPORT_DIR=/app/data/exports
3839
env_file:
3940
- .env.production
41+
volumes:
42+
- app-uploads:/app/data/uploads
43+
- app-transcripts:/app/data/transcripts
44+
- app-exports:/app/data/exports
4045
depends_on:
4146
postgres:
4247
condition: service_healthy
@@ -140,6 +145,12 @@ volumes:
140145
driver: local
141146
n8n-data:
142147
driver: local
148+
app-uploads:
149+
driver: local
150+
app-transcripts:
151+
driver: local
152+
app-exports:
153+
driver: local
143154

144155
networks:
145156
app-network:

packages/export/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"compilerOptions": {
33
"target": "ES2020",
44
"module": "ESNext",
5-
"lib": ["ES2020"],
5+
"lib": ["ES2020", "DOM"],
66
"moduleResolution": "bundler",
77
"skipLibCheck": true,
88
"strict": true,

pnpm-lock.yaml

Lines changed: 0 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/docker-backup.ps1

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Docker Volume Backup Script for Windows
2+
# Backs up persistent data for Desktop <-> VPS sync
3+
# Usage: .\scripts\docker-backup.ps1 [-BackupName <name>]
4+
5+
param(
6+
[string]$BackupName = "backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
7+
)
8+
9+
$ErrorActionPreference = "Stop"
10+
11+
$BackupDir = ".\backups"
12+
$DataDir = ".\data"
13+
14+
# Create backup directory
15+
New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null
16+
17+
Write-Host "=== Docker Volume Backup ===" -ForegroundColor Cyan
18+
Write-Host "Backup name: $BackupName"
19+
Write-Host ""
20+
21+
function Backup-Directory {
22+
param([string]$Dir, [string]$Name)
23+
24+
$sourcePath = Join-Path $DataDir $Dir
25+
$archivePath = Join-Path $BackupDir "$BackupName-$Name.zip"
26+
27+
if (Test-Path $sourcePath) {
28+
Write-Host "Backing up $Dir..."
29+
Compress-Archive -Path $sourcePath -DestinationPath $archivePath -Force
30+
Write-Host " -> Created: $BackupName-$Name.zip" -ForegroundColor Green
31+
} else {
32+
Write-Host " -> Skipped: $Dir (not found)" -ForegroundColor Yellow
33+
}
34+
}
35+
36+
# Backup all directories
37+
Backup-Directory -Dir "postgres" -Name "postgres"
38+
Backup-Directory -Dir "n8n" -Name "n8n"
39+
Backup-Directory -Dir "uploads" -Name "uploads"
40+
Backup-Directory -Dir "transcripts" -Name "transcripts"
41+
Backup-Directory -Dir "exports" -Name "exports"
42+
43+
# Create manifest
44+
$manifest = @{
45+
name = $BackupName
46+
created = (Get-Date -Format "o")
47+
hostname = $env:COMPUTERNAME
48+
files = @(
49+
"$BackupName-postgres.zip",
50+
"$BackupName-n8n.zip",
51+
"$BackupName-uploads.zip",
52+
"$BackupName-transcripts.zip",
53+
"$BackupName-exports.zip"
54+
)
55+
} | ConvertTo-Json
56+
57+
$manifestPath = Join-Path $BackupDir "$BackupName-manifest.json"
58+
$manifest | Out-File -FilePath $manifestPath -Encoding UTF8
59+
60+
Write-Host ""
61+
Write-Host "=== Backup Complete ===" -ForegroundColor Cyan
62+
Write-Host "Location: $BackupDir"
63+
Write-Host "Manifest: $BackupName-manifest.json"
64+
Write-Host ""
65+
Write-Host "To transfer to VPS:" -ForegroundColor Yellow
66+
Write-Host " scp $BackupDir\$BackupName-*.zip user@vps:/path/to/backups/"

scripts/docker-backup.sh

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/bin/bash
2+
# Docker Volume Backup Script
3+
# Backs up persistent data for Desktop <-> VPS sync
4+
# Usage: ./scripts/docker-backup.sh [backup_name]
5+
6+
set -e
7+
8+
BACKUP_NAME="${1:-backup-$(date +%Y%m%d-%H%M%S)}"
9+
BACKUP_DIR="./backups"
10+
DATA_DIR="./data"
11+
12+
# Create backup directory
13+
mkdir -p "$BACKUP_DIR"
14+
15+
echo "=== Docker Volume Backup ==="
16+
echo "Backup name: $BACKUP_NAME"
17+
echo ""
18+
19+
# Function to backup a directory
20+
backup_dir() {
21+
local dir="$1"
22+
local name="$2"
23+
24+
if [ -d "$DATA_DIR/$dir" ]; then
25+
echo "Backing up $dir..."
26+
tar -czf "$BACKUP_DIR/${BACKUP_NAME}-${name}.tar.gz" -C "$DATA_DIR" "$dir"
27+
echo " -> Created: ${BACKUP_NAME}-${name}.tar.gz"
28+
else
29+
echo " -> Skipped: $dir (not found)"
30+
fi
31+
}
32+
33+
# Backup PostgreSQL data
34+
backup_dir "postgres" "postgres"
35+
36+
# Backup N8N workflows
37+
backup_dir "n8n" "n8n"
38+
39+
# Backup app data
40+
backup_dir "uploads" "uploads"
41+
backup_dir "transcripts" "transcripts"
42+
backup_dir "exports" "exports"
43+
44+
# Create manifest
45+
cat > "$BACKUP_DIR/${BACKUP_NAME}-manifest.json" << EOF
46+
{
47+
"name": "$BACKUP_NAME",
48+
"created": "$(date -Iseconds)",
49+
"hostname": "$(hostname)",
50+
"files": [
51+
"${BACKUP_NAME}-postgres.tar.gz",
52+
"${BACKUP_NAME}-n8n.tar.gz",
53+
"${BACKUP_NAME}-uploads.tar.gz",
54+
"${BACKUP_NAME}-transcripts.tar.gz",
55+
"${BACKUP_NAME}-exports.tar.gz"
56+
]
57+
}
58+
EOF
59+
60+
echo ""
61+
echo "=== Backup Complete ==="
62+
echo "Location: $BACKUP_DIR"
63+
echo "Manifest: ${BACKUP_NAME}-manifest.json"
64+
echo ""
65+
echo "To transfer to VPS:"
66+
echo " scp $BACKUP_DIR/${BACKUP_NAME}-*.tar.gz user@vps:/path/to/backups/"
67+
echo ""
68+
echo "To restore on VPS:"
69+
echo " ./scripts/docker-restore.sh $BACKUP_NAME"

0 commit comments

Comments
 (0)