Files
freedoms4-backend-public/full-setup.sh
2026-06-14 11:40:27 +02:00

588 lines
21 KiB
Bash
Executable File

#!/bin/bash
# full-setup.sh — freedoms4 backend automation script
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
# Config (edit these before running)
DB_NAME=""
DB_USER=""
DB_PASS=""
PROSODY_DB_USER=""
PROSODY_DB_PASS="" # must match /etc/prosody/prosody.cfg.lua
DOMAIN=""
CERTBOT_EMAIL=""
API_DIR=""
ENV_FILE=""
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
OTP_FROM="" # for example: no-reply@freedoms4.org
# Must run as root
if [[ $EUID -ne 0 ]]; then
error "Please run as root: sudo bash full-setup.sh"
fi
echo ""
echo -e "${BLUE}================================================${NC}"
echo -e "${BLUE} freedoms4 backend setup${NC}"
echo -e "${BLUE}================================================${NC}"
echo ""
# ── STEP 1 PostgreSQL ──
info "Checking PostgreSQL..."
if command -v psql &>/dev/null; then
success "PostgreSQL is already installed."
else
info "Installing PostgreSQL..."
apt update -qq
apt install -y postgresql postgresql-contrib
systemctl enable --now postgresql
success "PostgreSQL installed and started."
fi
if ! systemctl is-active --quiet postgresql; then
systemctl start postgresql
fi
success "PostgreSQL is running."
# ── STEP 2 Database & user ──
info "Setting up database and user..."
if (cd /tmp && sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" | grep -q 1); then
warn "DB user '${DB_USER}' already exists — resetting password to ensure it matches."
(cd /tmp && sudo -u postgres psql -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASS}';")
else
(cd /tmp && sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}';")
success "Created DB user '${DB_USER}'."
fi
if (cd /tmp && sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" | grep -q 1); then
warn "Database '${DB_NAME}' already exists, skipping."
else
(cd /tmp && sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};")
success "Created database '${DB_NAME}'."
fi
(cd /tmp && sudo -u postgres psql -d "${DB_NAME}") << SQL
-- Users table
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(32) NOT NULL UNIQUE,
email VARCHAR(254) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
blocked BOOLEAN NOT NULL DEFAULT FALSE,
terms_agreed BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Migration: add terms_agreed for existing installs
ALTER TABLE users ADD COLUMN IF NOT EXISTS terms_agreed BOOLEAN NOT NULL DEFAULT FALSE;
CREATE INDEX IF NOT EXISTS idx_users_username ON users (username);
CREATE INDEX IF NOT EXISTS idx_users_email ON users (email);
REVOKE ALL ON TABLE users FROM PUBLIC;
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE users TO ${DB_USER};
GRANT USAGE, SELECT ON SEQUENCE users_id_seq TO ${DB_USER};
-- OTP table for email verification during sign-up
CREATE TABLE IF NOT EXISTS email_otps (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(254) NOT NULL,
otp_hash VARCHAR(255) NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
used BOOLEAN NOT NULL DEFAULT FALSE,
otp_token VARCHAR(64),
token_expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_email_otps_email ON email_otps (email);
CREATE INDEX IF NOT EXISTS idx_email_otps_token ON email_otps (otp_token);
REVOKE ALL ON TABLE email_otps FROM PUBLIC;
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE email_otps TO ${DB_USER};
GRANT USAGE, SELECT ON SEQUENCE email_otps_id_seq TO ${DB_USER};
-- Session tracking table
CREATE TABLE IF NOT EXISTS user_sessions (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
session_id VARCHAR(128) NOT NULL UNIQUE,
logged_in_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
logged_out_at TIMESTAMPTZ,
ip_address VARCHAR(45),
user_agent VARCHAR(512)
);
CREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions (user_id);
CREATE INDEX IF NOT EXISTS idx_user_sessions_session_id ON user_sessions (session_id);
REVOKE ALL ON TABLE user_sessions FROM PUBLIC;
GRANT SELECT, INSERT, UPDATE ON TABLE user_sessions TO ${DB_USER};
GRANT USAGE, SELECT ON SEQUENCE user_sessions_id_seq TO ${DB_USER};
-- Comments table
CREATE TABLE IF NOT EXISTS comments (
id BIGSERIAL PRIMARY KEY,
post_id VARCHAR(512) NOT NULL,
parent_id BIGINT REFERENCES comments(id) ON DELETE SET NULL,
user_id BIGINT REFERENCES users(id) ON DELETE SET NULL,
username VARCHAR(32),
body TEXT,
deleted_by VARCHAR(16),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
ALTER TABLE comments ADD COLUMN IF NOT EXISTS username VARCHAR(32);
UPDATE comments c
SET username = u.username
FROM users u
WHERE c.user_id = u.id AND c.username IS NULL;
CREATE INDEX IF NOT EXISTS idx_comments_post_id ON comments (post_id);
CREATE INDEX IF NOT EXISTS idx_comments_parent_id ON comments (parent_id);
REVOKE ALL ON TABLE comments FROM PUBLIC;
GRANT SELECT, INSERT, UPDATE ON TABLE comments TO ${DB_USER};
GRANT USAGE, SELECT ON SEQUENCE comments_id_seq TO ${DB_USER};
SQL
# ── STEP 2b Grant freedoms4_user access to Prosody DB ──
info "Granting freedoms4_user access to prosody database..."
(cd /tmp && sudo -u postgres psql -d prosody -c "GRANT CONNECT ON DATABASE prosody TO ${DB_USER};")
(cd /tmp && sudo -u postgres psql -d prosody -c "GRANT SELECT, INSERT, UPDATE ON TABLE prosody TO ${DB_USER};")
success "freedoms4_user can read/write prosody table."
# ── STEP 3 Block port 5432 via ufw ──
info "Blocking port 5432 externally via ufw..."
if command -v ufw &>/dev/null; then
if ufw status | head -1 | grep -q "active"; then
ufw deny 5432/tcp
success "ufw: port 5432 blocked — PostgreSQL is localhost-only."
else
ufw --force enable
ufw deny 5432/tcp
success "ufw enabled and port 5432 blocked."
fi
else
warn "ufw not found — please manually ensure port 5432 is not publicly exposed."
fi
# ── STEP 4 PHP 8.2 fpm + pgsql ──
info "Installing PHP 8.2 fpm and pgsql extension..."
apt install -y php8.2-fpm php8.2-pgsql php8.2-apcu
phpenmod -v 8.2 pgsql 2>/dev/null || true
phpenmod -v 8.2 pdo_pgsql 2>/dev/null || true
phpenmod -v 8.2 apcu 2>/dev/null || true
systemctl enable --now php8.2-fpm
systemctl restart php8.2-fpm
if [[ ! -S /run/php/php8.2-fpm.sock ]]; then
error "fpm socket not found at /run/php/php8.2-fpm.sock"
fi
success "php8.2-fpm ready."
# ── STEP 5 Mail server check ──
info "Checking mail server (Postfix) for OTP delivery..."
if ! command -v postfix &>/dev/null; then
warn "Postfix binary not found. Installing..."
apt install -y postfix
# Ensure it is configured as 'Internet Site' for freedoms4.org
debconf-set-selections <<< "postfix postfix/main_mailer_type select Internet Site"
debconf-set-selections <<< "postfix postfix/mailname string freedoms4.org"
dpkg-reconfigure -f noninteractive postfix
success "Postfix installed."
fi
if ! systemctl is-active --quiet postfix; then
systemctl start postfix
success "Postfix started."
else
success "Postfix is running."
fi
# Ensure PHP's sendmail_path points to Postfix's sendmail binary
PHP_INI_FPM="/etc/php/8.2/fpm/php.ini"
SENDMAIL_PATH=$(php8.2 -r "echo ini_get('sendmail_path');" 2>/dev/null || true)
if [[ "$SENDMAIL_PATH" != *"sendmail"* ]]; then
warn "sendmail_path may not be set — adding to ${PHP_INI_FPM}"
if ! grep -q "^sendmail_path" "${PHP_INI_FPM}" 2>/dev/null; then
echo 'sendmail_path = "/usr/sbin/sendmail -t -i"' >> "${PHP_INI_FPM}"
systemctl restart php8.2-fpm
success "sendmail_path set and php8.2-fpm restarted."
fi
else
success "sendmail_path is configured: ${SENDMAIL_PATH}"
fi
# Configure Postfix myhostname / myorigin if not already set
POSTFIX_MAIN="/etc/postfix/main.cf"
if ! grep -q "^myhostname\s*=\s*freedoms4.org" "${POSTFIX_MAIN}" 2>/dev/null; then
info "Setting Postfix myhostname = freedoms4.org ..."
postconf -e "myhostname = freedoms4.org"
postconf -e "myorigin = freedoms4.org"
systemctl reload postfix
success "Postfix myhostname/myorigin updated."
fi
# ── Configure Postfix to use Dovecot SASL for SMTP AUTH (so virtual users can send) ──
info "Configuring Postfix to use Dovecot SASL for SMTP submission..."
# Install libsasl2 if needed
apt install -y libsasl2-modules 2>/dev/null || true
postconf -e "smtpd_sasl_type = dovecot"
postconf -e "smtpd_sasl_path = private/auth"
postconf -e "smtpd_sasl_auth_enable = yes"
postconf -e "smtpd_sasl_security_options = noanonymous"
postconf -e "smtpd_sasl_local_domain = \$myhostname"
postconf -e "broken_sasl_auth_clients = yes"
postconf -e "smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination"
# ── Configure Postfix virtual mailbox delivery (Dovecot LDA) ──
info "Configuring Postfix virtual mailbox delivery via transport_maps..."
# freedoms4.org stays in mydestination so system users (hyzen etc.) keep
# working via local delivery. For site-created virtual users, we use
# transport_maps on a per-address basis to route them to Dovecot LDA.
#
# The critical piece: by default, Postfix checks local_recipient_maps for
# any domain in mydestination and rejects unknown recipients before ever
# consulting transport_maps. We override local_recipient_maps to only list
# actual Unix system accounts (passwd file). Virtual users are not in passwd,
# so Postfix finds no match, skips the reject, and falls through to
# transport_maps where their dovecot entry routes them correctly.
postconf -e "local_recipient_maps ="
postconf -e "dovecot_destination_recipient_limit = 1"
# Initialise the per-user transport map (entries added by email-account-create)
VTRANSPORT_FILE="/etc/postfix/virtual_transport"
if [[ ! -f "${VTRANSPORT_FILE}" ]]; then
touch "${VTRANSPORT_FILE}"
success "Created empty ${VTRANSPORT_FILE}."
fi
postmap "${VTRANSPORT_FILE}"
postconf -e "transport_maps = hash:${VTRANSPORT_FILE}"
success "transport_maps initialised."
# Add dovecot LDA transport to master.cf if not already there.
# Use dovecot-lda (the correct binary name on Debian/Ubuntu).
MASTER_CF="/etc/postfix/master.cf"
if ! grep -q "^dovecot" "${MASTER_CF}"; then
cat >> "${MASTER_CF}" << 'MASTER'
dovecot unix - n n - - pipe
flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -d ${recipient}
MASTER
success "Dovecot LDA transport added to master.cf."
fi
# Enable Dovecot auth-userdb socket for Postfix SASL.
# Use a separate drop-in file instead of injecting into 10-master.conf
# to avoid sed portability issues and keep changes isolated.
DOVECOT_POSTFIX_CONF="/etc/dovecot/conf.d/99-postfix-auth.conf"
if [[ ! -f "${DOVECOT_POSTFIX_CONF}" ]]; then
cat > "${DOVECOT_POSTFIX_CONF}" << 'DOVECOTCONF'
# Dovecot SASL socket for Postfix SMTP AUTH
# Added by full-setup.sh — remove this file to undo
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
DOVECOTCONF
success "Created Dovecot Postfix-auth drop-in at ${DOVECOT_POSTFIX_CONF}."
else
success "Dovecot Postfix-auth drop-in already exists."
fi
systemctl reload postfix
systemctl reload dovecot
success "Postfix SASL + virtual mailbox delivery configured."
# Quick test: send a mail from no-reply@freedoms4.org to itself (appears in /var/mail or mail queue)
info "Sending a Postfix self-test email..."
echo "Postfix OTP relay self-test from full-setup.sh" \
| mail -s "freedoms4 mail test" \
-a "From: ${OTP_FROM}" \
root 2>/dev/null \
&& success "Test email queued (check /var/mail/root or 'mailq')." \
|| warn "mail command not available for self-test — install mailutils if needed."
# ── STEP 6 Generate env file + deploy auth.php ──
info "Creating credentials env file at ${ENV_FILE}..."
# Write env file
mkdir -p "$(dirname ${ENV_FILE})"
cat > "${ENV_FILE}" << ENV
# /etc/freedoms4/auth.env — generated by full-setup.sh on $(date)
# Do NOT commit to version control.
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=${DB_NAME}
DB_USER=${DB_USER}
DB_PASS=${DB_PASS}
PROSODY_DB_NAME=prosody
PROSODY_DB_USER=${PROSODY_DB_USER}
PROSODY_DB_PASS=${PROSODY_DB_PASS}
PROSODY_HOST=freedoms4.org
ENV
chown root:www-data "${ENV_FILE}"
chmod 640 "${ENV_FILE}"
success "Env file written and secured (root:www-data 640)."
info "Deploying auth.php to ${API_DIR}..."
if [[ ! -f "${SCRIPT_DIR}/auth.php" ]]; then
error "auth.php not found in ${SCRIPT_DIR}."
fi
mkdir -p "${API_DIR}"
cp "${SCRIPT_DIR}/auth.php" "${API_DIR}/auth.php"
chown -R www-data:www-data "${API_DIR}"
chmod 640 "${API_DIR}/auth.php"
success "auth.php deployed."
if [[ ! -f "${SCRIPT_DIR}/comments.php" ]]; then
error "comments.php not found in ${SCRIPT_DIR}."
fi
cp "${SCRIPT_DIR}/comments.php" "${API_DIR}/comments.php"
chown www-data:www-data "${API_DIR}/comments.php"
chmod 640 "${API_DIR}/comments.php"
success "comments.php deployed."
if [[ ! -f "${SCRIPT_DIR}/admin.php" ]]; then
error "admin.php not found in ${SCRIPT_DIR}."
fi
cp "${SCRIPT_DIR}/admin.php" "${API_DIR}/admin.php"
chown www-data:www-data "${API_DIR}/admin.php"
chmod 640 "${API_DIR}/admin.php"
success "admin.php deployed."
# ── STEP 6b Virtual mail setup (Dovecot + vmail) ──
info "Setting up virtual mail user and Dovecot passwd-file..."
# Create vmail system user if not exists
if ! id vmail &>/dev/null; then
useradd -r -u 5000 -g mail -d /var/vmail -s /sbin/nologin vmail
success "Created vmail system user (uid 5000)."
else
success "vmail user already exists."
fi
# Create mailbox base directory
mkdir -p /var/vmail
mkdir -p /var/dovecot
chown vmail:mail /var/vmail
chmod 770 /var/vmail
success "/var/vmail ready."
# Create /var/dovecot/users if not exists
if [[ ! -f /var/dovecot/users ]]; then
touch /var/dovecot/users
chown root:root /var/dovecot/users
chmod 644 /var/dovecot/users
success "Created /var/dovecot/users."
else
success "/var/dovecot/users already exists."
fi
# Enable auth-passwdfile in 10-auth.conf (add include if not already there)
if ! grep -q "auth-passwdfile" /etc/dovecot/conf.d/10-auth.conf; then
echo '!include auth-passwdfile.conf.ext' >> /etc/dovecot/conf.d/10-auth.conf
success "Enabled auth-passwdfile.conf.ext in 10-auth.conf."
fi
# Enable auth-passwdfile.conf.ext — uncomment the passdb/userdb blocks
PASSWDFILE="/etc/dovecot/conf.d/auth-passwdfile.conf.ext"
if grep -q "^#passdb" "${PASSWDFILE}" 2>/dev/null || ! grep -q "^passdb" "${PASSWDFILE}" 2>/dev/null; then
cat > "${PASSWDFILE}" << 'DOVECOT'
passdb {
driver = passwd-file
args = scheme=SHA512-CRYPT username_format=%n /var/dovecot/users
}
userdb {
driver = passwd-file
args = username_format=%n /var/dovecot/users
default_fields = uid=vmail gid=mail home=/var/vmail/%n@freedoms4.org/maildir
}
DOVECOT
success "auth-passwdfile.conf.ext configured."
fi
systemctl reload dovecot
success "Dovecot reloaded."
# ── STEP 6c Deploy email-account-create wrapper + sudoers ──
info "Deploying email-account-create wrapper script..."
if [[ ! -f "${SCRIPT_DIR}/email-account-create.sh" ]]; then
error "email-account-create.sh not found in ${SCRIPT_DIR}."
fi
cp "${SCRIPT_DIR}/email-account-create.sh" /usr/local/bin/email-account-create
chown root:root /usr/local/bin/email-account-create
chmod 755 /usr/local/bin/email-account-create
success "email-account-create deployed to /usr/local/bin/"
SUDOERS_EMAIL="/etc/sudoers.d/email-account-create"
echo "www-data ALL=(root) NOPASSWD: /usr/local/bin/email-account-create" > "${SUDOERS_EMAIL}"
chmod 440 "${SUDOERS_EMAIL}"
visudo -cf "${SUDOERS_EMAIL}" && success "sudoers rule for email-account-create installed." \
|| error "sudoers syntax check failed — check ${SUDOERS_EMAIL}"
# ── STEP 6d Deploy email-block wrapper + sudoers ──
info "Deploying email-block wrapper script..."
if [[ ! -f "${SCRIPT_DIR}/email-block.sh" ]]; then
error "email-block.sh not found in ${SCRIPT_DIR}."
fi
cp "${SCRIPT_DIR}/email-block.sh" /usr/local/bin/email-block
chown root:root /usr/local/bin/email-block
chmod 755 /usr/local/bin/email-block
success "email-block deployed to /usr/local/bin/"
# Create backup file if not exists
touch /var/dovecot/users.blocked
chown root:root /var/dovecot/users.blocked
chmod 644 /var/dovecot/users.blocked
success "/var/dovecot/users.blocked ready."
SUDOERS_BLOCK="/etc/sudoers.d/email-block"
echo "www-data ALL=(root) NOPASSWD: /usr/local/bin/email-block" > "${SUDOERS_BLOCK}"
chmod 440 "${SUDOERS_BLOCK}"
visudo -cf "${SUDOERS_BLOCK}" && success "sudoers rule for email-block installed." \
|| error "sudoers syntax check failed — check ${SUDOERS_BLOCK}"
# ── STEP 6e Deploy email-delete wrapper + sudoers ──
info "Deploying email-delete wrapper script..."
if [[ ! -f "${SCRIPT_DIR}/email-delete.sh" ]]; then
error "email-delete.sh not found in ${SCRIPT_DIR}."
fi
cp "${SCRIPT_DIR}/email-delete.sh" /usr/local/bin/email-delete
chown root:root /usr/local/bin/email-delete
chmod 755 /usr/local/bin/email-delete
success "email-delete deployed to /usr/local/bin/"
SUDOERS_DELETE="/etc/sudoers.d/email-delete"
echo "www-data ALL=(root) NOPASSWD: /usr/local/bin/email-delete" > "${SUDOERS_DELETE}"
chmod 440 "${SUDOERS_DELETE}"
visudo -cf "${SUDOERS_DELETE}" && success "sudoers rule for email-delete installed." \
|| error "sudoers syntax check failed — check ${SUDOERS_DELETE}"
# ── STEP 7 Nginx config ──
info "Deploying nginx config for ${DOMAIN}..."
if [[ ! -f "${SCRIPT_DIR}/backend.freedoms4.org" ]]; then
error "backend.freedoms4.org not found in ${SCRIPT_DIR}."
fi
cp "${SCRIPT_DIR}/backend.freedoms4.org" "/etc/nginx/sites-available/${DOMAIN}"
ln -sf "/etc/nginx/sites-available/${DOMAIN}" "/etc/nginx/sites-enabled/${DOMAIN}"
nginx -t || error "Nginx config test failed."
systemctl restart nginx
success "Nginx restarted."
# ── STEP 8 Certbot / SSL ──
info "Checking SSL certificate..."
if ! command -v certbot &>/dev/null; then
apt install -y certbot python3-certbot-nginx
success "Certbot installed."
fi
if [[ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]]; then
warn "Certificate for ${DOMAIN} already exists, skipping issuance."
else
info "Obtaining SSL certificate for ${DOMAIN}..."
certbot --nginx -d "${DOMAIN}" --non-interactive --agree-tos -m "${CERTBOT_EMAIL}"
success "Certificate obtained."
fi
if systemctl list-timers 2>/dev/null | grep -q certbot; then
success "Certbot auto-renewal timer is active."
else
systemctl enable --now certbot.timer 2>/dev/null || true
if ! crontab -l 2>/dev/null | grep -q certbot; then
(crontab -l 2>/dev/null; echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload nginx'") | crontab -
success "Added certbot renewal cron job."
fi
fi
# ── STEP 9 OTP cleanup cron ──
info "Installing OTP cleanup cron (purges rows older than 24 h)..."
OTP_CRON="0 4 * * * psql postgresql://${DB_USER}:${DB_PASS}@127.0.0.1/${DB_NAME} -c \"DELETE FROM email_otps WHERE created_at < NOW() - INTERVAL '24 hours';\" >/dev/null 2>&1"
if ! crontab -l 2>/dev/null | grep -q "email_otps"; then
(crontab -l 2>/dev/null; echo "${OTP_CRON}") | crontab -
success "OTP cleanup cron installed (runs daily at 04:00)."
else
success "OTP cleanup cron already present."
fi
# ── STEP 10 Smoke test ──
info "Running smoke test..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
"https://${DOMAIN}/auth.php" \
-H "Content-Type: application/json" \
-H "Origin: https://freedoms4.org" \
-d '{"action":"login","username":"__probe__","password":"__probe__"}' \
--max-time 10 || true)
if [[ "$HTTP_CODE" == "200" ]]; then
success "Smoke test passed (HTTP 200)."
elif [[ "$HTTP_CODE" == "000" ]]; then
error "Smoke test failed (000) — endpoint unreachable. Run: curl -sv https://${DOMAIN}/auth.php"
else
success "Smoke test: HTTP ${HTTP_CODE} — endpoint is reachable."
fi
# ── Done ──
echo ""
echo -e "${GREEN}================================================${NC}"
echo -e "${GREEN} Setup complete!${NC}"
echo -e "${GREEN}================================================${NC}"
echo ""
echo -e " API endpoint : ${BLUE}https://${DOMAIN}/auth.php${NC}"
echo -e " Auth file : ${BLUE}${API_DIR}/auth.php${NC}"
echo -e " OTP sender : ${BLUE}${OTP_FROM}${NC}"
echo ""
echo ""