diff --git a/deploy.sh b/deploy.sh
index 39c411e2..0ebc3ee0 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -2,22 +2,10 @@
set -e
# ─────────────────────────────────────────────────────────────────────────────
-# Semblance — deploy script
-# Builds frontend locally, rsyncs dist/ to server, restarts Docker services.
-#
-# Server: optical-dev.oliver.solution (Ubuntu 24.04, Apache2, Docker)
-# SSH alias: optical-dev (see ~/.ssh/config)
-#
-# First-time setup on server (run once manually):
-# ssh optical-dev
-# sudo mkdir -p /opt/semblance && sudo chown vadym.samoilenko:vadym.samoilenko /opt/semblance
-# git clone git@bitbucket.org:zlalani/semblance-dev.git /opt/semblance
-# cp /opt/semblance/backend/.env.example /opt/semblance/backend/.env
-# nano /opt/semblance/backend/.env # fill in real values
-# cd /opt/semblance && docker compose up -d --build
+# Semblance — deploy script (run directly on server)
+# /opt/semblance/deploy.sh
# ─────────────────────────────────────────────────────────────────────────────
-SSH_HOST="optical-dev"
DEPLOY_DIR="/opt/semblance"
FRONTEND_DEST="/var/www/html/semblance"
@@ -25,78 +13,63 @@ echo "======================================"
echo "Semblance — starting deployment"
echo "======================================"
-# ── Pre-flight: backend/.env must exist on server ────────────────────────────
+cd "$DEPLOY_DIR"
+
+# ── Pre-flight ────────────────────────────────────────────────────────────────
echo ""
-echo "[pre-flight] Checking backend/.env on server..."
-if ! ssh "$SSH_HOST" "test -f $DEPLOY_DIR/backend/.env"; then
- echo ""
- echo "ERROR: $DEPLOY_DIR/backend/.env not found on server."
- echo "Run on server:"
- echo " cp $DEPLOY_DIR/backend/.env.example $DEPLOY_DIR/backend/.env"
- echo " nano $DEPLOY_DIR/backend/.env"
+echo "[pre-flight] Checking backend/.env..."
+if [ ! -f backend/.env ]; then
+ echo "ERROR: backend/.env not found."
+ echo " cp backend/.env.example backend/.env && nano backend/.env"
exit 1
fi
for VAR in SECRET_KEY JWT_SECRET_KEY OPENAI_API_KEY GEMINI_API_KEY; do
- if ! ssh "$SSH_HOST" "grep -q '^${VAR}=.\+' $DEPLOY_DIR/backend/.env 2>/dev/null"; then
- echo "ERROR: $VAR is not set in backend/.env on server"
+ if ! grep -q "^${VAR}=.\+" backend/.env 2>/dev/null; then
+ echo "ERROR: $VAR is not set in backend/.env"
exit 1
fi
done
-echo "✓ backend/.env present and required vars set"
+echo "✓ backend/.env OK"
-# ── Step 1: Pull latest code on server ───────────────────────────────────────
+# ── Step 1: Pull latest code ──────────────────────────────────────────────────
echo ""
-echo "[1/5] Pulling latest code on server..."
-ssh "$SSH_HOST" "cd $DEPLOY_DIR && git pull"
+echo "[1/4] Pulling latest code..."
+git pull
-# ── Step 2: Build frontend locally ───────────────────────────────────────────
+# ── Step 2: Build frontend (Node in Docker) ───────────────────────────────────
echo ""
-echo "[2/5] Building frontend locally..."
-cp .env.production .env
-npm install --silent
-npm run build
-echo "✓ Frontend built (dist/)"
-
-# ── Step 3: Deploy frontend to server ────────────────────────────────────────
-echo ""
-echo "[3/5] Deploying frontend to server..."
-ssh "$SSH_HOST" "sudo mkdir -p $FRONTEND_DEST"
-rsync -az --delete dist/ "$SSH_HOST:$FRONTEND_DEST/"
-ssh "$SSH_HOST" "sudo chown -R www-data:www-data $FRONTEND_DEST"
+echo "[2/4] Building frontend..."
+sudo mkdir -p "$FRONTEND_DEST"
+docker compose --profile build run --rm frontend
+sudo chown -R www-data:www-data "$FRONTEND_DEST"
echo "✓ Frontend deployed to $FRONTEND_DEST"
-# ── Step 4: Ensure Apache proxy config for semblance ─────────────────────────
+# ── Step 3: Ensure Apache proxy config ───────────────────────────────────────
echo ""
-echo "[4/5] Ensuring Apache proxy config..."
-ssh "$SSH_HOST" "
- CONF='/etc/apache2/sites-enabled/optical-dev.oliver.solutions.conf'
+echo "[3/4] Ensuring Apache proxy config..."
+CONF="/etc/apache2/sites-enabled/optical-dev.oliver.solutions.conf"
- if grep -q 'semblance_back' \"\$CONF\" 2>/dev/null; then
- echo '✓ Apache semblance config already present'
- else
- echo 'Adding semblance blocks to Apache config...'
- sudo sed -i 's|| # ----------------------------------------------------------------\n # Semblance — Quart/Hypercorn backend at :5137\n # ----------------------------------------------------------------\n\n # WebSocket (Socket.IO)\n RewriteCond %{HTTP:Upgrade} websocket [NC]\n RewriteCond %{HTTP:Connection} upgrade [NC]\n RewriteRule ^/semblance_back/socket.io/(.*) ws://127.0.0.1:5137/socket.io/\$1 [P,L]\n\n # REST API\n ProxyPass /semblance_back/api/ http://127.0.0.1:5137/api/\n ProxyPassReverse /semblance_back/api/ http://127.0.0.1:5137/api/\n\n # Socket.IO HTTP polling fallback\n ProxyPass /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n ProxyPassReverse /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n\n # Semblance SPA — served at /semblance/\n Alias /semblance $FRONTEND_DEST\n \n Options -Indexes +FollowSymLinks\n AllowOverride None\n Require all granted\n RewriteEngine On\n RewriteBase /semblance/\n RewriteCond %{REQUEST_FILENAME} !-f\n RewriteCond %{REQUEST_FILENAME} !-d\n RewriteRule ^ index.html [L]\n \n\n|' \"\$CONF\"
- sudo apache2ctl configtest && sudo systemctl reload apache2
- echo '✓ Apache config updated and reloaded'
- fi
-"
+if sudo grep -q "semblance_back" "$CONF" 2>/dev/null; then
+ echo "✓ Apache semblance config already present"
+else
+ echo "Adding semblance blocks to Apache config..."
+ sudo sed -i "s|| # ----------------------------------------------------------------\n # Semblance — Quart/Hypercorn backend at :5137\n # ----------------------------------------------------------------\n\n # WebSocket (Socket.IO)\n RewriteCond %{HTTP:Upgrade} websocket [NC]\n RewriteCond %{HTTP:Connection} upgrade [NC]\n RewriteRule ^/semblance_back/socket.io/(.*) ws://127.0.0.1:5137/socket.io/\$1 [P,L]\n\n # REST API\n ProxyPass /semblance_back/api/ http://127.0.0.1:5137/api/\n ProxyPassReverse /semblance_back/api/ http://127.0.0.1:5137/api/\n\n # Socket.IO HTTP polling fallback\n ProxyPass /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n ProxyPassReverse /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n\n # Semblance SPA\n Alias /semblance $FRONTEND_DEST\n \n Options -Indexes +FollowSymLinks\n AllowOverride None\n Require all granted\n RewriteEngine On\n RewriteBase /semblance/\n RewriteCond %{REQUEST_FILENAME} !-f\n RewriteCond %{REQUEST_FILENAME} !-d\n RewriteRule ^ index.html [L]\n \n\n|" "$CONF"
+ sudo apache2ctl configtest && sudo systemctl reload apache2
+ echo "✓ Apache config updated and reloaded"
+fi
-# ── Step 5: Rebuild and restart Docker services ───────────────────────────────
+# ── Step 4: Rebuild and restart backend + mongo ───────────────────────────────
echo ""
-echo "[5/5] Rebuilding and restarting Docker services..."
-ssh "$SSH_HOST" "
- cd $DEPLOY_DIR
- docker compose pull mongo 2>/dev/null || true
- docker compose up -d --build
- echo ''
- echo 'Container status:'
- docker compose ps
-"
+echo "[4/4] Rebuilding and restarting Docker services..."
+docker compose up -d --build
+
+echo ""
+echo "Container status:"
+docker compose ps
echo ""
echo "======================================"
echo "Deployment complete!"
-echo " Frontend: https://optical-dev.oliver.solution/semblance/"
-echo " API: https://optical-dev.oliver.solution/semblance_back/api/"
+echo " https://optical-dev.oliver.solution/semblance/"
echo "======================================"
diff --git a/docker-compose.yml b/docker-compose.yml
index 656f3bda..74aec9ad 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,18 @@
services:
+ frontend:
+ image: node:20-alpine
+ working_dir: /app
+ volumes:
+ - .:/app
+ - /var/www/html/semblance:/app/dist-out
+ command: >
+ sh -c "cp .env.production .env &&
+ npm ci --silent &&
+ npm run build &&
+ cp -r dist/* /app/dist-out/"
+ profiles:
+ - build
+
mongo:
image: mongo:7
restart: unless-stopped
diff --git a/server-deploy.sh b/server-deploy.sh
deleted file mode 100755
index 645beeea..00000000
--- a/server-deploy.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-set -e
-
-# ─────────────────────────────────────────────────────────────────────────────
-# Semblance — server-side deploy script
-# Run this DIRECTLY ON THE SERVER: /opt/semblance/server-deploy.sh
-#
-# Does NOT build the frontend (Node not on server).
-# Frontend must be deployed from local Mac via: ./deploy.sh
-# ─────────────────────────────────────────────────────────────────────────────
-
-DEPLOY_DIR="/opt/semblance"
-FRONTEND_DEST="/var/www/html/semblance"
-
-echo "======================================"
-echo "Semblance — server-side deployment"
-echo "======================================"
-
-cd "$DEPLOY_DIR"
-
-# ── Pre-flight ────────────────────────────────────────────────────────────────
-if [ ! -f backend/.env ]; then
- echo "ERROR: backend/.env not found."
- echo " cp backend/.env.example backend/.env && nano backend/.env"
- exit 1
-fi
-
-for VAR in SECRET_KEY JWT_SECRET_KEY OPENAI_API_KEY GEMINI_API_KEY; do
- if ! grep -q "^${VAR}=.\+" backend/.env 2>/dev/null; then
- echo "ERROR: $VAR is not set in backend/.env"
- exit 1
- fi
-done
-echo "✓ backend/.env OK"
-
-# ── Step 1: Pull latest code ──────────────────────────────────────────────────
-echo ""
-echo "[1/3] Pulling latest code..."
-git pull
-
-# ── Step 2: Ensure frontend dir exists ───────────────────────────────────────
-echo ""
-echo "[2/3] Ensuring frontend directory..."
-sudo mkdir -p "$FRONTEND_DEST"
-sudo chown -R www-data:www-data "$FRONTEND_DEST"
-echo "✓ $FRONTEND_DEST ready"
-
-# ── Step 3: Rebuild and restart Docker services ───────────────────────────────
-echo ""
-echo "[3/3] Rebuilding and restarting Docker services..."
-docker compose up -d --build
-
-echo ""
-echo "Container status:"
-docker compose ps
-
-echo ""
-echo "======================================"
-echo "Server deployment complete!"
-echo " Backend: http://127.0.0.1:5137"
-echo ""
-echo "NOTE: Frontend not deployed (requires local build)."
-echo " Run ./deploy.sh from your Mac to deploy the frontend."
-echo "======================================"