Initial Ladill Frontdesk release with deploy pipeline.
Deploy Ladill Frontdesk / deploy (push) Failing after 35s
Test / test (push) Failing after 2m45s

Visitor management app with SSO, kiosk, badges, reports, and Gitea CI deploy to frontdesk.ladill.com.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-27 20:37:15 +00:00
co-authored by Cursor
commit 9e2d79936c
284 changed files with 29134 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
#!/usr/bin/env bash
#
# Provision an nginx vhost for one of Ladill's own service subdomains
# (auth./account./pay./qr./... .ladill.com), reusing the existing
# *.ladill.com wildcard TLS certificate.
#
# The monolith phase needs nothing here — the ladill.com vhost already
# serves *.ladill.com over the wildcard cert. Use this when a service gets
# its OWN app (separate Laravel app dir, or a backend on another host/port),
# so its subdomain stops falling through to the catch-all and routes to the
# real service instead.
#
# Idempotent + safe: writes to sites-available, validates with `nginx -t`,
# and only reloads on success (restores the previous config on failure).
#
# Usage:
# setup-service-subdomain-nginx.sh <subdomain> --app <APP_DIR> [--fpm <SOCK>]
# setup-service-subdomain-nginx.sh <subdomain> --proxy <URL>
#
# Options:
# --app DIR Serve a Laravel app from DIR/public via PHP-FPM.
# --fpm SOCK PHP-FPM socket for --app (default: /run/php/php8.4-fpm-ladill.sock).
# --proxy URL Reverse-proxy the subdomain to URL (e.g. http://127.0.0.1:9001).
# --zone ZONE Apex zone (default: ladill.com).
# --cert NAME Let's Encrypt lineage under /etc/letsencrypt/live (default: ladill-wildcard).
# --dry-run Print the rendered vhost and exit without writing anything.
#
# Examples:
# setup-service-subdomain-nginx.sh auth --app /var/www/auth.ladill.com/current
# setup-service-subdomain-nginx.sh pay --proxy http://127.0.0.1:9002
set -euo pipefail
SUB="${1:-}"; shift || true
[ -n "$SUB" ] || { echo "ERROR: missing <subdomain> (e.g. 'auth')"; exit 2; }
MODE=""; APP_DIR=""; PROXY_URL=""
FPM_SOCK="/run/php/php8.4-fpm-ladill.sock"
ZONE="ladill.com"
CERT="ladill-wildcard"
DRY_RUN=0
while [ $# -gt 0 ]; do
case "$1" in
--app) MODE="app"; APP_DIR="${2:?--app needs a dir}"; shift 2;;
--proxy) MODE="proxy"; PROXY_URL="${2:?--proxy needs a url}"; shift 2;;
--fpm) FPM_SOCK="${2:?}"; shift 2;;
--zone) ZONE="${2:?}"; shift 2;;
--cert) CERT="${2:?}"; shift 2;;
--dry-run) DRY_RUN=1; shift;;
*) echo "ERROR: unknown arg '$1'"; exit 2;;
esac
done
[ -n "$MODE" ] || { echo "ERROR: choose a backend: --app <DIR> or --proxy <URL>"; exit 2; }
FQDN="${SUB}.${ZONE}"
CERT_DIR="/etc/letsencrypt/live/${CERT}"
WEBROOT="${APP_DIR:-/var/www/${ZONE}/current}/public"
if [ "$MODE" = "app" ]; then
BODY=$(cat <<EOF
root ${APP_DIR}/public;
index index.php;
location / { try_files \$uri \$uri/ /index.php?\$query_string; }
location ~ \.php\$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:${FPM_SOCK};
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
}
location ~ /\.(?!well-known).* { deny all; }
location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|webp|ico|woff2?)\$ {
expires 30d; access_log off;
add_header Cache-Control "public, max-age=2592000";
}
EOF
)
else
BODY=$(cat <<EOF
location / {
proxy_pass ${PROXY_URL};
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
EOF
)
fi
VHOST=$(cat <<EOF
# Managed by setup-service-subdomain-nginx.sh — Ladill service subdomain.
server {
listen 80;
listen [::]:80;
server_name ${FQDN};
location ^~ /.well-known/acme-challenge/ { root ${WEBROOT}; allow all; }
location / { return 301 https://\$host\$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ${FQDN};
client_max_body_size 64M;
ssl_certificate ${CERT_DIR}/fullchain.pem;
ssl_certificate_key ${CERT_DIR}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
${BODY}
}
EOF
)
if [ "$DRY_RUN" = "1" ]; then
echo "# --- rendered vhost for ${FQDN} (dry-run, not written) ---"
echo "$VHOST"
exit 0
fi
[ -d "$CERT_DIR" ] || { echo "ERROR: cert lineage not found: $CERT_DIR (run certbot for ${CERT} first)"; exit 1; }
AVAIL="/etc/nginx/sites-available/${FQDN}.conf"
ENABLED="/etc/nginx/sites-enabled/${FQDN}.conf"
if [ -f "$AVAIL" ]; then
BK="${AVAIL}.bak.$(date +%Y%m%d%H%M%S)"
cp -a "$AVAIL" "$BK"
echo ">> Backed up existing vhost to $BK"
fi
printf '%s\n' "$VHOST" > "$AVAIL"
ln -sfn "$AVAIL" "$ENABLED"
if nginx -t; then
systemctl reload nginx
echo ">> ${FQDN} vhost active (${MODE})."
else
echo "ERROR: nginx config test failed — rolling back."
if [ -n "${BK:-}" ] && [ -f "${BK:-}" ]; then
cp -a "$BK" "$AVAIL"
else
rm -f "$AVAIL" "$ENABLED"
fi
nginx -t && systemctl reload nginx || true
exit 1
fi
@@ -0,0 +1,14 @@
[program:ladill-frontdesk-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/ladill-frontdesk/current/artisan queue:work --sleep=3 --tries=3 --timeout=300 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=deploy
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/ladill-frontdesk/shared/storage/logs/worker.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stopwaitsecs=3600