v0.0.2: pre-productie (Bootstrap 5.3.3, LightPdf klant-PDF, server-setup en release-script)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
bffa053c05
commit
7d302dc405
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Op de server, als gebruiker crm (tools/release.sh roept dit aan): een geüploade versie installeren en actief maken.
|
||||
#
|
||||
# bash /srv/vanemous-crm/releases/vX.Y.Z/server/activate.sh vX.Y.Z
|
||||
#
|
||||
# Eigen venv per versie; vóór een versiewissel gaat er een kopie van de database naar data/backups/ (migraties
|
||||
# draaien bij het starten). Start de nieuwe versie niet, dan gaat `current` terug naar de vorige.
|
||||
# De laatste 5 versies blijven staan, dus terugrollen is `tools/release.sh crm@server vOUD`.
|
||||
set -euo pipefail
|
||||
|
||||
TAG="${1:?gebruik: activate.sh vX.Y.Z}"
|
||||
ROOT=/srv/vanemous-crm
|
||||
APP=vanemous-crm
|
||||
target="$ROOT/releases/$TAG"
|
||||
|
||||
say() { printf '\033[38;2;185;89;19m▸\033[0m %s\n' "$*"; }
|
||||
fail() { printf 'Fout: %s\n' "$*" >&2; exit 1; }
|
||||
current_version() { [ -L "$ROOT/current" ] && basename "$(readlink "$ROOT/current")" || true; }
|
||||
|
||||
[ "$(id -un)" = crm ] || fail "draai dit als gebruiker crm"
|
||||
[ -f "$target/app.py" ] || fail "$target bestaat niet of is onvolledig"
|
||||
|
||||
if [ ! -f "$target/.installed" ]; then
|
||||
say "venv en afhankelijkheden voor $TAG"
|
||||
python3 -m venv "$target/.venv"
|
||||
"$target/.venv/bin/pip" install --quiet --upgrade pip
|
||||
"$target/.venv/bin/pip" install --quiet -r "$target/requirements.txt" gunicorn
|
||||
"$target/.venv/bin/python" -m compileall -q "$target"/*.py
|
||||
touch "$target/.installed"
|
||||
fi
|
||||
|
||||
previous="$(current_version)"
|
||||
if [ -f "$ROOT/data/crm.db" ] && [ "$previous" != "$TAG" ]; then
|
||||
backup="$ROOT/data/backups/crm-$(date +%Y%m%d-%H%M%S)-${previous:-leeg}-naar-$TAG.db"
|
||||
sqlite3 "$ROOT/data/crm.db" ".backup '$backup'"
|
||||
say "database geback-upt: $(basename "$backup")"
|
||||
fi
|
||||
|
||||
ln -sfn "$target" "$ROOT/current"
|
||||
sudo /usr/bin/systemctl restart $APP
|
||||
|
||||
# even wachten en controleren of de app antwoordt
|
||||
ok=
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
sleep 1
|
||||
if curl -fsS -o /dev/null http://127.0.0.1:5050/login; then ok=1; break; fi
|
||||
done
|
||||
if [ -z "$ok" ]; then
|
||||
if [ -n "$previous" ] && [ "$previous" != "$TAG" ]; then
|
||||
ln -sfn "$ROOT/releases/$previous" "$ROOT/current"
|
||||
sudo /usr/bin/systemctl restart $APP
|
||||
fail "$TAG start niet; terug naar $previous. Log: sudo journalctl -u $APP -n 50"
|
||||
fi
|
||||
fail "$TAG start niet. Log: sudo journalctl -u $APP -n 50"
|
||||
fi
|
||||
say "actief: $TAG${previous:+ (was $previous)}"
|
||||
|
||||
# oude versies opruimen (de 5 nieuwste blijven)
|
||||
ls -1d "$ROOT"/releases/v*/ 2>/dev/null | xargs -n1 basename | sort -V | head -n -5 | while read -r old; do
|
||||
[ "$old" = "$TAG" ] || rm -rf "${ROOT:?}/releases/$old"
|
||||
done
|
||||
@@ -0,0 +1,25 @@
|
||||
# Van Emous CRM achter nginx (server/setup.sh vult __DOMAIN__ in; certbot voegt HTTPS en de doorverwijzing toe).
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name __DOMAIN__;
|
||||
|
||||
# uploads tot 25 MB (MAX_CONTENT_LENGTH in app.py)
|
||||
client_max_body_size 25m;
|
||||
|
||||
# css, js en de 3D-engine direct door nginx; steeds even navragen zodat een nieuwe release meteen zichtbaar is
|
||||
location /static/ {
|
||||
alias /srv/vanemous-crm/current/static/;
|
||||
add_header Cache-Control "no-cache";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5050;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
}
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
# Eenmalig op een nieuwe Ubuntu-server (getest voor 26.04 LTS, OVHcloud-image): alles klaarzetten voor de CRM.
|
||||
#
|
||||
# scp -r server ubuntu@SERVER:
|
||||
# ssh ubuntu@SERVER sudo bash server/setup.sh crm.voorbeeld.nl jij@voorbeeld.nl
|
||||
#
|
||||
# Daarna rolt tools/release.sh (op je eigen machine) een versie uit naar crm@SERVER.
|
||||
# Opnieuw draaien kan: elke stap controleert wat er al is.
|
||||
#
|
||||
# Wat het doet: pakketten (Python, nginx, certbot, rsync, sqlite3), tijdzone Europe/Amsterdam, gebruiker `crm`
|
||||
# met /srv/vanemous-crm, SSH-toegang voor releases (zelfde sleutels als de sudo-gebruiker), systemd-dienst,
|
||||
# nginx-site met HTTPS (Let's Encrypt), firewall (alleen SSH, 80, 443), automatische beveiligingsupdates,
|
||||
# cron voor e-mail ophalen (elke 10 min) en een dagelijkse databaseback-up (7 dagen rond).
|
||||
set -euo pipefail
|
||||
|
||||
DOMAIN="${1:-}"
|
||||
EMAIL="${2:-}"
|
||||
APP=vanemous-crm
|
||||
ROOT=/srv/$APP
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
say() { printf '\033[38;2;185;89;19m▸\033[0m %s\n' "$*"; }
|
||||
fail() { printf 'Fout: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[ "$(id -u)" = 0 ] || fail "draai dit met sudo"
|
||||
[ -n "$DOMAIN" ] && [ -n "$EMAIL" ] || fail "gebruik: sudo bash setup.sh <domein> <e-mail voor Let's Encrypt>"
|
||||
|
||||
# --- pakketten ------------------------------------------------------------------------
|
||||
say "pakketten installeren"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -q
|
||||
apt-get upgrade -yq
|
||||
apt-get install -yq python3 python3-venv python3-pip nginx certbot python3-certbot-nginx \
|
||||
rsync sqlite3 ufw unattended-upgrades cron curl
|
||||
python3 -c 'import sys; assert sys.version_info >= (3, 11), sys.version' || fail "Python 3.11 of nieuwer nodig"
|
||||
|
||||
say "tijdzone Europe/Amsterdam"
|
||||
timedatectl set-timezone Europe/Amsterdam
|
||||
|
||||
say "automatische beveiligingsupdates aan"
|
||||
dpkg-reconfigure -f noninteractive unattended-upgrades
|
||||
|
||||
# --- gebruiker en mappen -----------------------------------------------------------------
|
||||
if ! id crm >/dev/null 2>&1; then
|
||||
say "gebruiker crm aanmaken"
|
||||
useradd --system --create-home --home-dir "$ROOT" --shell /bin/bash crm
|
||||
fi
|
||||
install -d -o crm -g crm -m 755 "$ROOT" "$ROOT/releases"
|
||||
install -d -o crm -g crm -m 700 "$ROOT/data" "$ROOT/data/backups"
|
||||
|
||||
# SSH-sleutels van de gebruiker die sudo deed (bij OVH: ubuntu), zodat releases als crm kunnen inloggen.
|
||||
src_user="${SUDO_USER:-ubuntu}"
|
||||
src_keys="$(getent passwd "$src_user" | cut -d: -f6)/.ssh/authorized_keys"
|
||||
install -d -o crm -g crm -m 700 "$ROOT/.ssh"
|
||||
if [ -f "$src_keys" ]; then
|
||||
cat "$src_keys" "$ROOT/.ssh/authorized_keys" 2>/dev/null | sort -u > "$ROOT/.ssh/authorized_keys.new"
|
||||
mv "$ROOT/.ssh/authorized_keys.new" "$ROOT/.ssh/authorized_keys"
|
||||
chown crm:crm "$ROOT/.ssh/authorized_keys"; chmod 600 "$ROOT/.ssh/authorized_keys"
|
||||
say "SSH-sleutels van $src_user toegevoegd aan crm"
|
||||
else
|
||||
say "let op: geen $src_keys; zet zelf een sleutel in $ROOT/.ssh/authorized_keys"
|
||||
fi
|
||||
|
||||
# crm mag alleen de eigen dienst herstarten
|
||||
cat > /etc/sudoers.d/$APP <<EOF
|
||||
crm ALL=(root) NOPASSWD: /usr/bin/systemctl restart $APP, /usr/bin/systemctl is-active $APP, /usr/bin/journalctl -u $APP *
|
||||
EOF
|
||||
chmod 440 /etc/sudoers.d/$APP
|
||||
visudo -cf /etc/sudoers.d/$APP >/dev/null
|
||||
|
||||
# --- systemd -----------------------------------------------------------------------------
|
||||
say "systemd-dienst $APP"
|
||||
install -m 644 "$HERE/$APP.service" /etc/systemd/system/$APP.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable $APP >/dev/null 2>&1
|
||||
# starten gebeurt bij de eerste release (er is nog geen code)
|
||||
|
||||
# --- nginx -------------------------------------------------------------------------------
|
||||
say "nginx voor $DOMAIN"
|
||||
if [ ! -f /etc/nginx/sites-available/$APP ] || ! grep -q "listen 443" /etc/nginx/sites-available/$APP; then
|
||||
sed "s/__DOMAIN__/$DOMAIN/g" "$HERE/nginx-site.conf" > /etc/nginx/sites-available/$APP
|
||||
fi
|
||||
ln -sfn /etc/nginx/sites-available/$APP /etc/nginx/sites-enabled/$APP
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
|
||||
# --- firewall ------------------------------------------------------------------------------
|
||||
say "firewall: alleen SSH, HTTP en HTTPS"
|
||||
ufw allow OpenSSH >/dev/null
|
||||
ufw allow 'Nginx Full' >/dev/null
|
||||
ufw --force enable >/dev/null
|
||||
|
||||
# --- HTTPS -------------------------------------------------------------------------------
|
||||
say "certificaat aanvragen (DNS van $DOMAIN moet al naar deze server wijzen)"
|
||||
if certbot --nginx -d "$DOMAIN" -m "$EMAIL" --agree-tos --non-interactive --redirect; then
|
||||
say "HTTPS actief; verlengen gaat automatisch (certbot.timer)"
|
||||
else
|
||||
say "let op: certificaat mislukt. Klopt de DNS? Daarna opnieuw: sudo certbot --nginx -d $DOMAIN -m $EMAIL --agree-tos --redirect"
|
||||
fi
|
||||
|
||||
# --- cron: e-mail en back-up -------------------------------------------------------------------
|
||||
say "cron: e-mail elke 10 minuten, database-back-up elke nacht"
|
||||
cat > /etc/cron.d/$APP <<EOF
|
||||
# Van Emous CRM (server/setup.sh)
|
||||
*/10 * * * * crm [ -d $ROOT/current ] && cd $ROOT/current && CRM_DATA=$ROOT/data .venv/bin/python app.py sync 2>&1 | logger -t $APP-sync
|
||||
15 3 * * * crm [ -f $ROOT/data/crm.db ] && sqlite3 $ROOT/data/crm.db ".backup '$ROOT/data/backups/nacht-\$(date +\%a).db'"
|
||||
EOF
|
||||
chmod 644 /etc/cron.d/$APP
|
||||
|
||||
say "klaar. Rol nu vanaf je eigen machine een versie uit: tools/release.sh crm@$DOMAIN"
|
||||
@@ -0,0 +1,26 @@
|
||||
# Van Emous CRM: gunicorn achter nginx (server/setup.sh installeert dit naar /etc/systemd/system/).
|
||||
[Unit]
|
||||
Description=Van Emous CRM
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
User=crm
|
||||
Group=crm
|
||||
WorkingDirectory=/srv/vanemous-crm/current
|
||||
Environment=CRM_DATA=/srv/vanemous-crm/data
|
||||
Environment=CRM_PROXY=1
|
||||
ExecStart=/srv/vanemous-crm/current/.venv/bin/gunicorn --workers 2 --bind 127.0.0.1:5050 \
|
||||
--timeout 120 --access-logfile - --error-logfile - app:app
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
# afscherming: alleen de datamap is schrijfbaar
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/srv/vanemous-crm/data
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user