-
Notifications
You must be signed in to change notification settings - Fork 293
Expand file tree
/
Copy pathsetup.sh
More file actions
executable file
·471 lines (431 loc) · 20.8 KB
/
setup.sh
File metadata and controls
executable file
·471 lines (431 loc) · 20.8 KB
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
#!/usr/bin/env bash
# ────────────────────────────────────────────────
# Clawith — First-time Setup Script
# Sets up backend, frontend, database, and seed data.
# ────────────────────────────────────────────────
set -e
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; CYAN='\033[0;36m'; NC='\033[0m'
ROOT="$(cd "$(dirname "$0")" && pwd)"
# Parse arguments
INSTALL_DEV=false
for arg in "$@"; do
case $arg in
--dev) INSTALL_DEV=true ;;
esac
done
# --- Helper: detect server IP ---
get_server_ip() {
# Try hostname -I (Linux), then ifconfig (macOS), then fallback
local ip
ip=$(hostname -I 2>/dev/null | awk '{print $1}')
[ -z "$ip" ] && ip=$(ifconfig 2>/dev/null | grep 'inet ' | grep -v '127.0.0.1' | head -1 | awk '{print $2}')
[ -z "$ip" ] && ip="<your-server-ip>"
echo "$ip"
}
# --- Check Python version (>= 3.12 required) ---
PYTHON_BIN="${PYTHON_BIN:-python3}"
if command -v "$PYTHON_BIN" &>/dev/null; then
PY_VER=$("$PYTHON_BIN" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
PY_MAJOR=$(echo "$PY_VER" | cut -d. -f1)
PY_MINOR=$(echo "$PY_VER" | cut -d. -f2)
if [ "$PY_MAJOR" -lt 3 ] || ([ "$PY_MAJOR" -eq 3 ] && [ "$PY_MINOR" -lt 12 ]); then
echo -e "${RED}Python $PY_VER detected, but Clawith requires Python >= 3.12.${NC}"
echo ""
echo " Please install Python 3.12+:"
echo " Ubuntu: sudo apt install python3.12 python3.12-venv"
echo " CentOS: sudo dnf install python3.12"
echo " macOS: brew install python@3.12"
echo " Conda: conda create -n clawith python=3.12"
echo ""
echo " Or set PYTHON_BIN to point to a valid python3.12+ binary:"
echo " PYTHON_BIN=/path/to/python3.12 bash setup.sh"
exit 1
fi
fi
# --- Optional package mirror overrides ---
PIP_INSTALL_ARGS=()
if [ -n "${CLAWITH_PIP_INDEX_URL:-}" ]; then
PIP_INSTALL_ARGS+=(--index-url "$CLAWITH_PIP_INDEX_URL")
fi
if [ -n "${CLAWITH_PIP_TRUSTED_HOST:-}" ]; then
PIP_INSTALL_ARGS+=(--trusted-host "$CLAWITH_PIP_TRUSTED_HOST")
fi
NPM_MIRROR="--registry https://registry.npmmirror.com"
echo ""
echo -e "${CYAN}═══════════════════════════════════════${NC}"
echo -e "${CYAN} 🦞 Clawith — First-time Setup${NC}"
echo -e "${CYAN}═══════════════════════════════════════${NC}"
echo ""
# ── 1. Environment file ──────────────────────────
echo -e "${YELLOW}[1/6]${NC} Checking environment file..."
if [ ! -f "$ROOT/.env" ]; then
cp "$ROOT/.env.example" "$ROOT/.env"
echo -e " ${GREEN}✓${NC} Created .env from .env.example"
echo -e " ${YELLOW}⚠${NC} Please edit .env to set SECRET_KEY and JWT_SECRET_KEY before production use."
else
echo -e " ${GREEN}✓${NC} .env already exists"
fi
# ── 2. PostgreSQL setup ──────────────────────────
echo ""
echo -e "${YELLOW}[2/6]${NC} Setting up PostgreSQL..."
# --- Helper: find psql binary ---
find_psql() {
# Check PATH first
if command -v psql &>/dev/null; then
command -v psql
return 0
fi
# Search common non-standard locations
local search_paths=(
"/www/server/pgsql/bin"
"/usr/local/pgsql/bin"
"/usr/lib/postgresql/15/bin"
"/usr/lib/postgresql/14/bin"
"/usr/lib/postgresql/16/bin"
"/opt/homebrew/opt/postgresql@15/bin"
"/opt/homebrew/opt/postgresql/bin"
)
for dir in "${search_paths[@]}"; do
if [ -x "$dir/psql" ]; then
echo "$dir"
return 0
fi
done
return 1
}
# --- Helper: find a free port starting from $1 ---
find_free_port() {
local port=$1
while ss -tlnp 2>/dev/null | grep -q ":${port} " || \
lsof -iTCP:${port} -sTCP:LISTEN 2>/dev/null | grep -q LISTEN; do
echo -e " ${YELLOW}⚠${NC} Port $port is in use, trying $((port+1))..."
port=$((port+1))
done
echo "$port"
}
PG_PORT=5432
PG_MANAGED_BY_US=false
if PG_BIN_DIR=$(find_psql 2>/dev/null); then
# If find_psql returned a directory (not a full path), add to PATH
if [ -d "$PG_BIN_DIR" ]; then
export PATH="$PG_BIN_DIR:$PATH"
fi
echo -e " ${GREEN}✓${NC} Found psql: $(which psql)"
# Check if PG is running and we can connect
if pg_isready -h localhost -p 5432 -q 2>/dev/null; then
echo -e " ${GREEN}✓${NC} PostgreSQL is running on port 5432"
PG_PORT=5432
# Try to create role and database
ROLE_EXISTS=false
if psql -h localhost -p $PG_PORT -U "$USER" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='clawith'" 2>/dev/null | grep -q 1; then
ROLE_EXISTS=true
echo -e " ${GREEN}✓${NC} Role 'clawith' already exists"
elif sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='clawith'" 2>/dev/null | grep -q 1; then
ROLE_EXISTS=true
echo -e " ${GREEN}✓${NC} Role 'clawith' already exists"
fi
if [ "$ROLE_EXISTS" = false ]; then
# Try 1: as current user
if createuser -h localhost -p $PG_PORT clawith 2>/dev/null; then
psql -h localhost -p $PG_PORT -U "$USER" -d postgres -c "ALTER ROLE clawith WITH LOGIN PASSWORD 'clawith';" &>/dev/null
echo -e " ${GREEN}✓${NC} Created PostgreSQL role: clawith"
# Try 2: via sudo -u postgres (standard Linux setup)
elif sudo -u postgres createuser clawith 2>/dev/null && \
sudo -u postgres psql -c "ALTER ROLE clawith WITH LOGIN PASSWORD 'clawith';" &>/dev/null; then
echo -e " ${GREEN}✓${NC} Created PostgreSQL role: clawith (via sudo)"
else
echo -e " ${YELLOW}⚠${NC} Could not create role in existing PG — will init a local instance"
PG_BIN_DIR="" # Force local PG setup below
fi
fi
if [ -n "$PG_BIN_DIR" ] || command -v psql &>/dev/null; then
DB_EXISTS=false
if psql -h localhost -p $PG_PORT -U "$USER" -lqt 2>/dev/null | cut -d\| -f1 | grep -qw clawith; then
DB_EXISTS=true
elif sudo -u postgres psql -lqt 2>/dev/null | cut -d\| -f1 | grep -qw clawith; then
DB_EXISTS=true
fi
if [ "$DB_EXISTS" = true ]; then
echo -e " ${GREEN}✓${NC} Database 'clawith' already exists"
else
if createdb -h localhost -p $PG_PORT -O clawith clawith 2>/dev/null || \
sudo -u postgres createdb -O clawith clawith 2>/dev/null; then
echo -e " ${GREEN}✓${NC} Created database: clawith"
fi
fi
fi
else
echo -e " ${YELLOW}⚠${NC} PostgreSQL binaries found but service is not running on port 5432"
echo " Will set up a local instance..."
PG_BIN_DIR="" # Force local PG setup below
fi
fi
# --- Local PG instance: install + initdb if needed ---
if [ -z "$PG_BIN_DIR" ] && ! (PGPASSWORD=clawith psql -h localhost -p 5432 -U clawith -d clawith -c "SELECT 1" &>/dev/null); then
echo -e " ${CYAN}↓${NC} No usable PostgreSQL found — setting up a local instance..."
PG_MANAGED_BY_US=true
PGDATA="$ROOT/.pgdata"
PG_LOCAL="$ROOT/.pg"
# Strategy 1: Install via system package manager (most reliable)
if [ ! -x "$PG_LOCAL/bin/psql" ]; then
INSTALLED_VIA_PKG=false
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
if [ "$OS" = "darwin" ]; then
if command -v brew &>/dev/null; then
echo " Installing PostgreSQL via Homebrew..."
brew install postgresql@15 2>/dev/null && brew services start postgresql@15 2>/dev/null && INSTALLED_VIA_PKG=true
else
echo -e " ${YELLOW}⚠${NC} On macOS, please install Homebrew first, then:"
echo " brew install postgresql@15 && brew services start postgresql@15"
echo " Then re-run: bash setup.sh"
exit 1
fi
elif [ "$OS" = "linux" ]; then
# Check if sudo is available
CAN_SUDO=false
if command -v sudo &>/dev/null; then
if sudo -n true 2>/dev/null || (echo "" | sudo -S true 2>/dev/null); then
CAN_SUDO=true
fi
fi
if [ "$CAN_SUDO" = true ]; then
if command -v apt-get &>/dev/null; then
echo " Installing PostgreSQL via apt..."
sudo apt-get update -qq 2>/dev/null
sudo apt-get install -y -qq postgresql postgresql-client 2>/dev/null && INSTALLED_VIA_PKG=true
elif command -v yum &>/dev/null; then
echo " Installing PostgreSQL via yum..."
sudo yum install -y -q postgresql-server postgresql 2>/dev/null && \
sudo postgresql-setup --initdb 2>/dev/null; \
sudo systemctl start postgresql 2>/dev/null && INSTALLED_VIA_PKG=true
elif command -v dnf &>/dev/null; then
echo " Installing PostgreSQL via dnf..."
sudo dnf install -y -q postgresql-server postgresql 2>/dev/null && \
sudo postgresql-setup --initdb 2>/dev/null; \
sudo systemctl start postgresql 2>/dev/null && INSTALLED_VIA_PKG=true
fi
fi
fi
if [ "$INSTALLED_VIA_PKG" = true ]; then
echo -e " ${GREEN}✓${NC} PostgreSQL installed via package manager"
# Re-find psql after install
if PG_BIN_DIR=$(find_psql 2>/dev/null); then
if [ -d "$PG_BIN_DIR" ]; then
export PATH="$PG_BIN_DIR:$PATH"
fi
fi
# Wait for PG to be ready
for i in $(seq 1 10); do
if pg_isready -h localhost -p 5432 -q 2>/dev/null; then
PG_PORT=5432
break
fi
sleep 1
done
# Create role and database
if command -v psql &>/dev/null; then
if ! psql -h localhost -p $PG_PORT -U postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='clawith'" 2>/dev/null | grep -q 1; then
sudo -u postgres createuser clawith 2>/dev/null || createuser -h localhost -p $PG_PORT clawith 2>/dev/null || true
sudo -u postgres psql -c "ALTER ROLE clawith WITH LOGIN PASSWORD 'clawith';" 2>/dev/null || \
psql -h localhost -p $PG_PORT -U postgres -c "ALTER ROLE clawith WITH LOGIN PASSWORD 'clawith';" 2>/dev/null || true
echo -e " ${GREEN}✓${NC} Created role: clawith"
fi
if ! psql -h localhost -p $PG_PORT -U postgres -lqt 2>/dev/null | cut -d\| -f1 | grep -qw clawith; then
sudo -u postgres createdb -O clawith clawith 2>/dev/null || createdb -h localhost -p $PG_PORT -O clawith clawith 2>/dev/null || true
echo -e " ${GREEN}✓${NC} Created database: clawith"
fi
PG_MANAGED_BY_US=false # System manages PG now
fi
fi
fi
# Strategy 2: Use system PG binaries from non-standard paths for user-space initdb
if [ "$INSTALLED_VIA_PKG" != true ] || [ "$PG_MANAGED_BY_US" = true ]; then
if [ ! -x "$PG_LOCAL/bin/initdb" ]; then
# Try to link from common system paths
for dir in /www/server/pgsql /usr/local/pgsql /usr/lib/postgresql/15 /usr/lib/postgresql/14 /usr/lib/postgresql/16; do
if [ -x "$dir/bin/initdb" ]; then
mkdir -p "$PG_LOCAL"
ln -sf "$dir/bin" "$PG_LOCAL/bin" 2>/dev/null || cp -r "$dir/bin" "$PG_LOCAL/bin" 2>/dev/null
if [ -d "$dir/lib" ]; then
ln -sf "$dir/lib" "$PG_LOCAL/lib" 2>/dev/null || cp -r "$dir/lib" "$PG_LOCAL/lib" 2>/dev/null
fi
if [ -d "$dir/share" ]; then
ln -sf "$dir/share" "$PG_LOCAL/share" 2>/dev/null || cp -r "$dir/share" "$PG_LOCAL/share" 2>/dev/null
fi
echo -e " ${GREEN}✓${NC} Found system PG binaries at $dir"
break
fi
done
fi
if [ -x "$PG_LOCAL/bin/initdb" ]; then
export PATH="$PG_LOCAL/bin:$PATH"
export LD_LIBRARY_PATH="$PG_LOCAL/lib:${LD_LIBRARY_PATH:-}"
# Find a free port
PG_PORT=$(find_free_port 5432)
# Initialize data directory
if [ ! -f "$PGDATA/PG_VERSION" ]; then
echo " Initializing database cluster..."
initdb -D "$PGDATA" -U postgres --auth=trust -E UTF8 --locale=C >/dev/null 2>&1
# Configure port (handle both GNU and BSD sed)
sed -i "s/#port = 5432/port = $PG_PORT/" "$PGDATA/postgresql.conf" 2>/dev/null || \
sed -i '' "s/#port = 5432/port = $PG_PORT/" "$PGDATA/postgresql.conf" 2>/dev/null
sed -i "s/#listen_addresses = 'localhost'/listen_addresses = 'localhost'/" "$PGDATA/postgresql.conf" 2>/dev/null || \
sed -i '' "s/#listen_addresses = 'localhost'/listen_addresses = 'localhost'/" "$PGDATA/postgresql.conf" 2>/dev/null
echo -e " ${GREEN}✓${NC} Database cluster initialized (port $PG_PORT)"
else
# Read configured port from existing cluster
PG_PORT=$(grep "^port = " "$PGDATA/postgresql.conf" 2>/dev/null | awk '{print $3}')
PG_PORT=${PG_PORT:-5432}
echo -e " ${GREEN}✓${NC} Existing data directory found (port $PG_PORT)"
fi
# Start PostgreSQL
if ! pg_isready -h localhost -p "$PG_PORT" -q 2>/dev/null; then
pg_ctl -D "$PGDATA" -l "$PGDATA/pg.log" start >/dev/null 2>&1
sleep 2
if pg_isready -h localhost -p "$PG_PORT" -q 2>/dev/null; then
echo -e " ${GREEN}✓${NC} PostgreSQL started on port $PG_PORT"
else
echo -e " ${RED}✗${NC} Failed to start PostgreSQL. Check $PGDATA/pg.log"
exit 1
fi
else
echo -e " ${GREEN}✓${NC} PostgreSQL already running on port $PG_PORT"
fi
# Create role and database
if ! psql -h localhost -p "$PG_PORT" -U postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='clawith'" 2>/dev/null | grep -q 1; then
createuser -h localhost -p "$PG_PORT" -U postgres clawith 2>/dev/null || true
psql -h localhost -p "$PG_PORT" -U postgres -c "ALTER ROLE clawith WITH LOGIN PASSWORD 'clawith';" &>/dev/null
echo -e " ${GREEN}✓${NC} Created role: clawith"
fi
if ! psql -h localhost -p "$PG_PORT" -U postgres -lqt 2>/dev/null | cut -d\| -f1 | grep -qw clawith; then
createdb -h localhost -p "$PG_PORT" -U postgres -O clawith clawith 2>/dev/null
echo -e " ${GREEN}✓${NC} Created database: clawith"
fi
else
echo -e " ${RED}✗${NC} Could not set up PostgreSQL automatically."
echo ""
echo " Please install PostgreSQL manually:"
echo ""
echo " Ubuntu/Debian: sudo apt install postgresql"
echo " CentOS/RHEL: sudo yum install postgresql-server"
echo " macOS: brew install postgresql@15"
echo ""
echo " Then re-run: bash setup.sh"
exit 1
fi
fi
fi
# Ensure DATABASE_URL is correct in .env
DB_URL="postgresql+asyncpg://clawith:clawith@localhost:${PG_PORT}/clawith?ssl=disable"
if grep -q "^DATABASE_URL=" "$ROOT/.env" 2>/dev/null; then
# Update existing DATABASE_URL
sed -i "s|^DATABASE_URL=.*|DATABASE_URL=${DB_URL}|" "$ROOT/.env" 2>/dev/null || \
sed -i '' "s|^DATABASE_URL=.*|DATABASE_URL=${DB_URL}|" "$ROOT/.env" 2>/dev/null
elif grep -q "^# DATABASE_URL=" "$ROOT/.env" 2>/dev/null; then
# Uncomment and set
sed -i "s|^# DATABASE_URL=.*|DATABASE_URL=${DB_URL}|" "$ROOT/.env" 2>/dev/null || \
sed -i '' "s|^# DATABASE_URL=.*|DATABASE_URL=${DB_URL}|" "$ROOT/.env" 2>/dev/null
else
echo "DATABASE_URL=${DB_URL}" >> "$ROOT/.env"
fi
echo -e " ${GREEN}✓${NC} DATABASE_URL set (port $PG_PORT)"
# ── 3. Backend setup ─────────────────────────────
echo ""
echo -e "${YELLOW}[3/6]${NC} Setting up backend..."
cd "$ROOT/backend"
if [ ! -d ".venv" ]; then
echo " Creating Python virtual environment..."
$PYTHON_BIN -m venv .venv
echo -e " ${GREEN}✓${NC} Virtual environment created"
fi
if [ "$INSTALL_DEV" = true ]; then
PIP_TARGET=".[dev]"
echo " Installing dependencies with dev extras (this may take 2-5 minutes)..."
else
PIP_TARGET="."
echo " Installing dependencies (this may take 1-2 minutes)..."
fi
if .venv/bin/pip install -e "$PIP_TARGET" "${PIP_INSTALL_ARGS[@]}" 2>&1; then
echo -e " ${GREEN}✓${NC} Backend dependencies installed"
else
echo -e " ${RED}✗${NC} Failed to install backend dependencies."
echo " Try manually: cd backend && .venv/bin/pip install -e '$PIP_TARGET'"
exit 1
fi
# ── 4. Frontend setup ────────────────────────────
echo ""
echo -e "${YELLOW}[4/6]${NC} Setting up frontend..."
cd "$ROOT/frontend"
if [ ! -d "node_modules" ]; then
if ! command -v npm &>/dev/null; then
echo -e " ${YELLOW}⚠${NC} npm not found. Skipping frontend dependency install."
echo -e " ${YELLOW}⚠${NC} Install Node.js 20+ to enable frontend dev server."
echo -e " ${YELLOW}⚠${NC} You can still use pre-built dist/ or Docker for frontend."
else
echo " Installing npm packages..."
npm install --silent $NPM_MIRROR 2>&1 | tail -1
echo -e " ${GREEN}✓${NC} Frontend dependencies installed"
fi
else
echo -e " ${GREEN}✓${NC} Frontend dependencies already installed"
fi
# ── 5. Database setup ────────────────────────────
echo ""
echo -e "${YELLOW}[5/6]${NC} Setting up database..."
cd "$ROOT/backend"
# Source .env for DATABASE_URL
if [ -f "$ROOT/.env" ]; then
set -a
source "$ROOT/.env"
set +a
fi
# ── 6. Seed data ─────────────────────────────────
echo ""
echo -e "${YELLOW}[6/6]${NC} Running database seed..."
if .venv/bin/python seed.py 2>&1 | while IFS= read -r line; do echo " $line"; done; then
echo ""
else
echo ""
echo -e " ${RED}✗ Seed failed.${NC}"
echo " Common fixes:"
echo " 1. Make sure PostgreSQL is running"
echo " 2. Set DATABASE_URL in .env, e.g.:"
echo " DATABASE_URL=postgresql+asyncpg://clawith:clawith@localhost:5432/clawith?ssl=disable"
echo " 3. Create the database first:"
echo " createdb clawith"
echo " 4. If you see 'Ident authentication failed', configure pg_hba.conf:"
echo " Add this line BEFORE other host rules:"
echo " host all clawith 127.0.0.1/32 md5"
echo " Then reload: sudo systemctl reload postgresql"
echo ""
echo " After fixing, re-run: bash setup.sh"
exit 1
fi
# ── Summary ──────────────────────────────────────
SERVER_IP=$(get_server_ip)
echo ""
echo -e "${GREEN}═══════════════════════════════════════${NC}"
echo -e "${GREEN} 🎉 Setup complete!${NC}"
echo -e "${GREEN}═══════════════════════════════════════${NC}"
echo ""
echo " To start the application:"
echo ""
echo -e " ${CYAN}Option A: One-command start${NC}"
echo " bash restart.sh"
echo ""
echo -e " ${CYAN}Option B: Manual start${NC}"
echo " # Terminal 1 — Backend"
echo " cd backend && .venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8008"
echo ""
echo " # Terminal 2 — Frontend"
echo " cd frontend && npx vite --host 0.0.0.0 --port 3008"
echo ""
echo -e " ${CYAN}Option C: Docker${NC}"
echo " docker compose up -d"
echo ""
echo -e " ${CYAN}Access URLs:${NC}"
echo " Local: http://localhost:3008"
echo " Network: http://${SERVER_IP}:3008"
echo ""
echo " The first user to register becomes the platform admin."
echo ""