diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..db0fb54 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,216 @@ +#!/bin/bash +# ============================================================ +# NextWks — Production Deploy Script +# Idempotent: safe for first-time setup and subsequent updates. +# Run: sudo bash deploy.sh +# ============================================================ +set -euo pipefail + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' +info() { echo -e "${BLUE}[+]${NC} $1"; } +ok() { echo -e "${GREEN}[✓]${NC} $1"; } +warn() { echo -e "${YELLOW}[!]${NC} $1"; } +die() { echo -e "${RED}[✗]${NC} $1"; exit 1; } +header(){ echo -e "\n${BOLD}${CYAN}── $1 ──${NC}"; } + +# ============================================================ +# CONFIG +# ============================================================ +INSTALL_DIR="/opt/nextworkspace" +SRC_DIR="${INSTALL_DIR}/src/core" +CONFIG_DIR="${INSTALL_DIR}/config/nextworkspace" +DATA_DIR="${INSTALL_DIR}/data/core" +CERTS_DIR="${INSTALL_DIR}/data/certs" +LOGS_DIR="${INSTALL_DIR}/logs" +STATIC_DIR="${INSTALL_DIR}/static" +BIN_PATH="${INSTALL_DIR}/core" +CONFIG_PATH="${CONFIG_DIR}/config.yaml" +SERVICE_FILE="/etc/systemd/system/nextwks.service" +SVC_USER="nextwks" +REPO_URL="https://git.lohmar.co.uk/lexton-it/NextWks.git" + +# Must be root +[ "${EUID:-$(id -u)}" -ne 0 ] && die "Run as root: sudo bash deploy.sh" + +# ============================================================ +# PHASE 1: DIRECTORY SCAFFOLD + SERVICE USER +# ============================================================ +header "Phase 1: Directory Structure" + +if ! id "$SVC_USER" &>/dev/null; then + useradd -r -s /usr/sbin/nologin -d /nonexistent "$SVC_USER" + ok "Created service user: $SVC_USER" +else + info "Service user exists: $SVC_USER" +fi + +mkdir -p "$SRC_DIR" "$CONFIG_DIR/apps.d" "$DATA_DIR" "$CERTS_DIR" "$LOGS_DIR" "$STATIC_DIR" +chown -R "$SVC_USER:$SVC_USER" "$DATA_DIR" "$CERTS_DIR" "$LOGS_DIR" "$STATIC_DIR" +ok "Directory tree created at $INSTALL_DIR/" + +# ============================================================ +# PHASE 2: FETCH + BUILD +# ============================================================ +header "Phase 2: Build" + +if [ -d "$SRC_DIR/.git" ]; then + info "Pulling latest from origin..." + cd "$SRC_DIR" + git fetch origin main --quiet + git reset --hard origin/main --quiet + ok "Repo updated" +else + info "Cloning repository..." + git clone --depth 1 "$REPO_URL" "$SRC_DIR" --quiet + cd "$SRC_DIR" + ok "Repo cloned" +fi + +VERSION=$(cat "$SRC_DIR/VERSION" 2>/dev/null || echo "dev") +COMMIT_SHA=$(cd "$SRC_DIR" && git rev-parse --short HEAD) +BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + +if ! command -v go &>/dev/null; then + die "Go is not installed. Run: apt install golang-go" +fi + +info "Building v${VERSION} (${COMMIT_SHA})..." +cd "$SRC_DIR/src" + +go build -ldflags="-s -w \ + -X git.lohmar.co.uk/lexton-it/NextWks/core/version.Version=${VERSION} \ + -X git.lohmar.co.uk/lexton-it/NextWks/core/version.BuildTime=${BUILD_TIME} \ + -X git.lohmar.co.uk/lexton-it/NextWks/core/version.CommitSHA=${COMMIT_SHA}" \ + -o "$BIN_PATH" . + +chmod 755 "$BIN_PATH" +chown "$SVC_USER:$SVC_USER" "$BIN_PATH" +ok "Binary: $BIN_PATH" + +# ============================================================ +# PHASE 3: CONFIG + ASSETS +# ============================================================ +header "Phase 3: Configuration" + +if [ ! -f "$CONFIG_PATH" ]; then + ADMIN_TOKEN=$(openssl rand -hex 32 2>/dev/null || head -c32 /dev/urandom | xxd -p -c32) + SESSION_SECRET=$(openssl rand -hex 32 2>/dev/null || head -c32 /dev/urandom | xxd -p -c32) + + cat > "$CONFIG_PATH" << CONFIGEOF +# NextWks — Production Configuration +# Generated: $(date) +server: + host: "0.0.0.0" + port: 8080 + +admin: + secret_token: "${ADMIN_TOKEN}" + +database: + type: "sqlite" + path: "${DATA_DIR}/nextwks.db" + +authelia: + host: "http://127.0.0.1:9091" + config_path: "/opt/authelia/configuration.yml" + users_db_path: "/opt/authelia/users_database.yml" + +oidc: + issuer_url: "https://app.nextwks.eu/auth" + client_id: "nextwks" + client_secret: "" + redirect_url: "https://app.nextwks.eu/access" + +smtp: + host: "" + port: 587 + username: "" + password: "" + from: "noreply@nextwks.local" + +session: + secret: "${SESSION_SECRET}" + expiry_minutes: 60 + +tls: + enabled: false + domain: "" + email: "" + storage_path: "${CERTS_DIR}" + staging: false +CONFIGEOF + + chmod 600 "$CONFIG_PATH" + chown "$SVC_USER:$SVC_USER" "$CONFIG_PATH" + ok "Config: $CONFIG_PATH" + warn "IMPORTANT: Edit $CONFIG_PATH with your domain, email, and SMTP settings!" +else + ok "Config exists (not overwritten): $CONFIG_PATH" +fi + +if [ -d "$SRC_DIR/app/static" ]; then + cp -r "$SRC_DIR/app/static/"* "$STATIC_DIR/" 2>/dev/null || true + chown -R "$SVC_USER:$SVC_USER" "$STATIC_DIR" + ok "Static assets copied" +fi + +# ============================================================ +# PHASE 4: SYSTEMD +# ============================================================ +header "Phase 4: Systemd" + +cat > "$SERVICE_FILE" << SERVICEEOF +[Unit] +Description=Next Workspace (NextWks) Core +Documentation=https://git.lohmar.co.uk/lexton-it/NextWks +After=network-online.target authelia.service +Wants=network-online.target authelia.service + +[Service] +Type=simple +User=${SVC_USER} +Group=${SVC_USER} +WorkingDirectory=${INSTALL_DIR} +ExecStart=${BIN_PATH} -config ${CONFIG_PATH} +Restart=on-failure +RestartSec=5 +AmbientCapabilities=CAP_NET_BIND_SERVICE +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=${DATA_DIR} ${LOGS_DIR} ${CERTS_DIR} +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +SERVICEEOF + +systemctl daemon-reload +systemctl enable nextwks 2>/dev/null || true +systemctl restart nextwks +ok "Systemd unit installed and service restarted" + +# ============================================================ +# PHASE 5: HEALTH CHECK +# ============================================================ +header "Phase 5: Health Check" + +sleep 2 +if curl -s --max-time 5 http://localhost:8080/api/health 2>/dev/null | grep -q '"status":"ok"'; then + ok "Health check passed — NextWks v${VERSION} (${COMMIT_SHA}) is running" +else + warn "Health check failed — check logs: journalctl -u nextwks -n 30" +fi + +echo "" +echo -e "${GREEN}════════════════════════════════════════${NC}" +echo -e "${GREEN} NextWks v${VERSION} deployed${NC}" +echo -e "${GREEN}════════════════════════════════════════${NC}" +echo "" +info " Binary: ${BIN_PATH}" +info " Config: ${CONFIG_PATH}" +info " Data: ${DATA_DIR}" +info " Logs: journalctl -u nextwks -f" +info " Version: ${VERSION} (${COMMIT_SHA})" +echo "" diff --git a/update.sh b/update.sh index dc8321f..db61153 100755 --- a/update.sh +++ b/update.sh @@ -4,8 +4,8 @@ # Pulls latest code, rebuilds, restarts NextWks only. # Users & auth are managed by Authelia (untouched). # -# Run: cd /opt/nextwks && sudo bash update.sh -# cd /opt/nextwks && sudo bash update.sh --wipe-db +# Run: cd /opt/nextworkspace && sudo bash update.sh +# cd /opt/nextworkspace && sudo bash update.sh --wipe-db # ============================================================ set -euo pipefail @@ -17,7 +17,7 @@ error() { echo -e "${RED}$1${NC}"; } REPO_URL="https://git.lohmar.co.uk/lexton-it/NextWks.git" REPO_DIR="/tmp/nextwks-update" -INSTALL_DIR="/opt/nextwks" +INSTALL_DIR="/opt/nextworkspace" BIN_DIR="${INSTALL_DIR}/bin" DATA_DIR="${INSTALL_DIR}/data" WIPE_DB=false