swadhinbiswas feat: implement air-gapped mode, UI fixes, and project management integrations · 7/9/2026
e3bd4a1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/bin/bash
# OpenCodeHub Production Setup Script
# ====================================
# Generates secrets, validates config, and starts the production stack.
#
# Usage:
# bash scripts/prod-setup.sh # Interactive setup
# bash scripts/prod-setup.sh --auto # Non-interactive (CI/CD)
# bash scripts/prod-setup.sh --start # Setup + start
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
ENV_FILE=".env"
AUTO_MODE=false
START_AFTER=false
for arg in "$@"; do
case $arg in
--auto) AUTO_MODE=true ;;
--start) START_AFTER=true ;;
esac
done
echo -e "${GREEN}╔══════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ OpenCodeHub Production Setup ║${NC}"
echo -e "${GREEN}╚══════════════════════════════════════════╝${NC}"
echo ""
# ── 1. Generate secrets if .env doesn't exist ────────────────
if [ ! -f "$ENV_FILE" ]; then
echo -e "${YELLOW}No .env found. Generating from .env.example...${NC}"
cp .env.example .env
# Generate all required secrets
JWT_SECRET=$(openssl rand -base64 32)
SESSION_SECRET=$(openssl rand -base64 32)
INTERNAL_HOOK_SECRET=$(openssl rand -hex 32)
CRON_SECRET=$(openssl rand -hex 32)
AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32)
WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32)
RUNNER_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '=' | tr '/+' '_-')
REDIS_PASSWORD=$(openssl rand -base64 32 | tr -d '=' | tr '/+' '_-')
# Replace placeholder values in .env
sed -i "s|JWT_SECRET=.*|JWT_SECRET=$JWT_SECRET|" "$ENV_FILE"
sed -i "s|SESSION_SECRET=.*|SESSION_SECRET=$SESSION_SECRET|" "$ENV_FILE"
sed -i "s|INTERNAL_HOOK_SECRET=.*|INTERNAL_HOOK_SECRET=$INTERNAL_HOOK_SECRET|" "$ENV_FILE"
sed -i "s|CRON_SECRET=.*|CRON_SECRET=$CRON_SECRET|" "$ENV_FILE"
sed -i "s|AI_CONFIG_ENCRYPTION_KEY=.*|AI_CONFIG_ENCRYPTION_KEY=$AI_CONFIG_ENCRYPTION_KEY|" "$ENV_FILE"
sed -i "s|WORKFLOW_SECRET_ENCRYPTION_KEY=.*|WORKFLOW_SECRET_ENCRYPTION_KEY=$WORKFLOW_SECRET_ENCRYPTION_KEY|" "$ENV_FILE"
sed -i "s|RUNNER_SECRET=.*|RUNNER_SECRET=$RUNNER_SECRET|" "$ENV_FILE"
sed -i "s|POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$POSTGRES_PASSWORD|" "$ENV_FILE"
sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=$REDIS_PASSWORD|" "$ENV_FILE"
# Generate backup encryption key
BACKUP_KEY=$(openssl rand -hex 32)
sed -i "s|BACKUP_ENCRYPTION_KEY=.*|BACKUP_ENCRYPTION_KEY=$BACKUP_KEY|" "$ENV_FILE"
echo -e "${GREEN}✓ Secrets generated and written to .env${NC}"
else
echo -e "${GREEN}.env already exists, using existing config${NC}"
fi
# ── 2. Validate required variables ───────────────────────────
echo ""
echo "Validating configuration..."
REQUIRED_VARS=(
"JWT_SECRET"
"INTERNAL_HOOK_SECRET"
"CRON_SECRET"
"AI_CONFIG_ENCRYPTION_KEY"
"WORKFLOW_SECRET_ENCRYPTION_KEY"
"RUNNER_SECRET"
"POSTGRES_PASSWORD"
"REDIS_PASSWORD"
)
MISSING=0
for var in "${REQUIRED_VARS[@]}"; do
value=$(grep "^${var}=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2-)
if [ -z "$value" ] || echo "$value" | grep -q "change-this\|^$\|Set "; then
echo -e "${RED}✗ $var is not set or still has default value${NC}"
MISSING=1
else
echo -e "${GREEN}✓ $var${NC}"
fi
done
if [ "$MISSING" -eq 1 ]; then
echo ""
echo -e "${RED}Some secrets are missing. Run without --auto to fix interactively.${NC}"
if [ "$AUTO_MODE" = false ]; then
echo "Edit .env and re-run this script."
fi
exit 1
fi
# ── 3. Validate SITE_URL ─────────────────────────────────────
SITE_URL=$(grep "^SITE_URL=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2-)
if echo "$SITE_URL" | grep -q "localhost\|127.0.0.1"; then
echo -e "${YELLOW}⚠ SITE_URL is set to localhost — change to your public domain for production${NC}"
if [ "$AUTO_MODE" = false ]; then
read -p "Enter your production domain (e.g., https://git.example.com): " NEW_URL
if [ -n "$NEW_URL" ]; then
sed -i "s|SITE_URL=.*|SITE_URL=$NEW_URL|" "$ENV_FILE"
sed -i "s|SERVER_URL=.*|SERVER_URL=$NEW_URL|" "$ENV_FILE"
sed -i "s|ALLOWED_ORIGINS=.*|ALLOWED_ORIGINS=$NEW_URL|" "$ENV_FILE"
echo -e "${GREEN}✓ SITE_URL updated to $NEW_URL${NC}"
fi
fi
fi
# ── 4. Check Docker ──────────────────────────────────────────
echo ""
if ! command -v docker &> /dev/null; then
echo -e "${RED}Docker is not installed. Please install Docker first.${NC}"
exit 1
fi
if ! docker compose version &> /dev/null; then
echo -e "${RED}Docker Compose V2 is not available.${NC}"
exit 1
fi
echo -e "${GREEN}✓ Docker $(docker --version | awk '{print $3}')${NC}"
echo -e "${GREEN}✓ Docker Compose $(docker compose version | awk '{print $4}')${NC}"
# ── 5. Print summary ─────────────────────────────────────────
echo ""
echo -e "${GREEN}Configuration summary:${NC}"
echo " App: http://localhost:4321 (internal)"
echo " SSH Git: ssh://git@localhost:2222"
echo " Database: PostgreSQL (internal)"
echo " Redis: (internal, password-protected)"
echo " Metrics: http://localhost:4321/api/metrics (internal)"
echo ""
echo -e "${YELLOW}To start with Caddy reverse proxy (recommended for production):${NC}"
echo " docker compose --profile production up -d"
echo ""
echo -e "${YELLOW}To start without Caddy (development/testing):${NC}"
echo " docker compose up -d"
echo ""
echo -e "${YELLOW}To include CI runner:${NC}"
echo " docker compose --profile production --profile with-runner up -d"
echo ""
# ── 6. Start if requested ────────────────────────────────────
if [ "$START_AFTER" = true ]; then
echo "Starting production stack..."
docker compose --profile production up -d
echo ""
echo -e "${GREEN}OpenCodeHub is starting. Check status with:${NC}"
echo " docker compose ps"
echo " docker compose logs -f app"
fi