Backend: - Add file_handlers.go: presigned upload/download for item attachments - Add item_files.go: item file and thumbnail DB operations - Add migration 011: item_files table and thumbnail_key column - Update items/projects/relationships DB with extended field support - Update routes: React SPA serving from web/dist, file upload endpoints - Update auth handlers and middleware for cookie + bearer token auth - Remove Go HTML templates (replaced by React SPA) - Update storage client for presigned URL generation Frontend: - Add TagInput component for tag/keyword entry - Add SVG assets for Silo branding and UI icons - Update API client and types for file uploads, auth, extended fields - Update AuthContext for session-based auth flow - Update LoginPage, ProjectsPage, SchemasPage, SettingsPage - Fix tsconfig.node.json Deployment: - Update config.prod.yaml: single-binary SPA layout at /opt/silo - Update silod.service: ReadOnlyPaths for /opt/silo - Add scripts/deploy.sh: build, package, ship, migrate, start - Update docker-compose.yaml and Dockerfile - Add frontend-spec.md design document
155 lines
4.6 KiB
Bash
Executable File
155 lines
4.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# Deploy Silo to silo.kindred.internal
|
|
#
|
|
# Usage: ./scripts/deploy.sh [host]
|
|
# host defaults to silo.kindred.internal
|
|
#
|
|
# Prerequisites:
|
|
# - SSH access to the target host
|
|
# - /etc/silo/silod.env must exist on target with credentials filled in
|
|
# - PostgreSQL reachable from target at psql.kindred.internal
|
|
# - MinIO reachable from target at minio.kindred.internal
|
|
|
|
set -euo pipefail
|
|
|
|
TARGET="${1:-silo.kindred.internal}"
|
|
DEPLOY_DIR="/opt/silo"
|
|
CONFIG_DIR="/etc/silo"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="${SCRIPT_DIR}/.."
|
|
|
|
echo "=== Silo Deploy to ${TARGET} ==="
|
|
|
|
# --- Build locally ---
|
|
echo "[1/6] Building Go binary..."
|
|
cd "$PROJECT_DIR"
|
|
GOOS=linux GOARCH=amd64 go build -o silod ./cmd/silod
|
|
|
|
echo "[2/6] Building React frontend..."
|
|
cd "$PROJECT_DIR/web"
|
|
npm run build
|
|
|
|
# --- Package ---
|
|
echo "[3/6] Packaging..."
|
|
STAGING=$(mktemp -d)
|
|
trap "rm -rf $STAGING" EXIT
|
|
|
|
mkdir -p "$STAGING/bin"
|
|
mkdir -p "$STAGING/web"
|
|
mkdir -p "$STAGING/schemas"
|
|
mkdir -p "$STAGING/migrations"
|
|
|
|
cp "$PROJECT_DIR/silod" "$STAGING/bin/silod"
|
|
cp -r "$PROJECT_DIR/web/dist" "$STAGING/web/dist"
|
|
cp "$PROJECT_DIR/schemas/"*.yaml "$STAGING/schemas/"
|
|
cp "$PROJECT_DIR/migrations/"*.sql "$STAGING/migrations/"
|
|
cp "$PROJECT_DIR/deployments/config.prod.yaml" "$STAGING/config.yaml"
|
|
cp "$PROJECT_DIR/deployments/systemd/silod.service" "$STAGING/silod.service"
|
|
cp "$PROJECT_DIR/deployments/systemd/silod.env.example" "$STAGING/silod.env.example"
|
|
|
|
TARBALL=$(mktemp --suffix=.tar.gz)
|
|
tar -czf "$TARBALL" -C "$STAGING" .
|
|
echo " Package: $(du -h "$TARBALL" | cut -f1)"
|
|
|
|
# --- Deploy ---
|
|
echo "[4/6] Uploading to ${TARGET}..."
|
|
scp "$TARBALL" "${TARGET}:/tmp/silo-deploy.tar.gz"
|
|
|
|
echo "[5/6] Installing on ${TARGET}..."
|
|
ssh "$TARGET" bash -s <<'REMOTE'
|
|
set -euo pipefail
|
|
|
|
DEPLOY_DIR="/opt/silo"
|
|
CONFIG_DIR="/etc/silo"
|
|
|
|
# Create directories
|
|
sudo mkdir -p "$DEPLOY_DIR/bin" "$DEPLOY_DIR/web" "$DEPLOY_DIR/schemas" "$DEPLOY_DIR/migrations"
|
|
sudo mkdir -p "$CONFIG_DIR"
|
|
|
|
# Stop service if running
|
|
if systemctl is-active --quiet silod 2>/dev/null; then
|
|
echo " Stopping silod..."
|
|
sudo systemctl stop silod
|
|
fi
|
|
|
|
# Extract
|
|
echo " Extracting..."
|
|
sudo tar -xzf /tmp/silo-deploy.tar.gz -C "$DEPLOY_DIR"
|
|
sudo chmod +x "$DEPLOY_DIR/bin/silod"
|
|
rm -f /tmp/silo-deploy.tar.gz
|
|
|
|
# Install config if not present (don't overwrite existing)
|
|
if [ ! -f "$CONFIG_DIR/config.yaml" ]; then
|
|
echo " Installing default config..."
|
|
sudo cp "$DEPLOY_DIR/config.yaml" "$CONFIG_DIR/config.yaml" 2>/dev/null || true
|
|
fi
|
|
|
|
# Install env template if not present
|
|
if [ ! -f "$CONFIG_DIR/silod.env" ]; then
|
|
echo " WARNING: /etc/silo/silod.env does not exist!"
|
|
echo " Copying template..."
|
|
sudo cp "$DEPLOY_DIR/silod.env.example" "$CONFIG_DIR/silod.env"
|
|
echo " Edit /etc/silo/silod.env with your credentials before starting the service."
|
|
fi
|
|
|
|
# Install systemd service
|
|
echo " Installing systemd service..."
|
|
sudo cp "$DEPLOY_DIR/silod.service" /etc/systemd/system/silod.service
|
|
|
|
# Set ownership
|
|
sudo chown -R silo:silo "$DEPLOY_DIR" 2>/dev/null || true
|
|
sudo chmod 600 "$CONFIG_DIR/silod.env" 2>/dev/null || true
|
|
|
|
echo " Files installed to $DEPLOY_DIR"
|
|
REMOTE
|
|
|
|
echo "[6/6] Running migrations and starting service..."
|
|
ssh "$TARGET" bash -s <<'REMOTE'
|
|
set -euo pipefail
|
|
|
|
DEPLOY_DIR="/opt/silo"
|
|
CONFIG_DIR="/etc/silo"
|
|
|
|
# Source env for migration
|
|
if [ -f "$CONFIG_DIR/silod.env" ]; then
|
|
set -a
|
|
source "$CONFIG_DIR/silod.env"
|
|
set +a
|
|
fi
|
|
|
|
# Run migrations
|
|
if command -v psql &>/dev/null && [ -n "${SILO_DB_PASSWORD:-}" ]; then
|
|
echo " Running migrations..."
|
|
for f in "$DEPLOY_DIR/migrations/"*.sql; do
|
|
echo " $(basename "$f")"
|
|
PGPASSWORD="$SILO_DB_PASSWORD" psql \
|
|
-h psql.kindred.internal -p 5432 \
|
|
-U silo -d silo \
|
|
-f "$f" -q 2>&1 | grep -v "already exists" || true
|
|
done
|
|
echo " Migrations complete."
|
|
else
|
|
echo " WARNING: psql not available or SILO_DB_PASSWORD not set, skipping migrations."
|
|
echo " Run migrations manually: PGPASSWORD=... psql -h psql.kindred.internal -U silo -d silo -f /opt/silo/migrations/NNN_name.sql"
|
|
fi
|
|
|
|
# Start service
|
|
echo " Starting silod..."
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable silod
|
|
sudo systemctl start silod
|
|
sleep 2
|
|
if systemctl is-active --quiet silod; then
|
|
echo " silod is running!"
|
|
else
|
|
echo " ERROR: silod failed to start. Check: journalctl -u silod -n 50"
|
|
exit 1
|
|
fi
|
|
REMOTE
|
|
|
|
echo ""
|
|
echo "=== Deploy complete ==="
|
|
echo " Backend: https://${TARGET} (port 8080)"
|
|
echo " React SPA served from /opt/silo/web/dist/"
|
|
echo " Logs: ssh ${TARGET} journalctl -u silod -f"
|