Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Caddy terminates HTTPS (certificates from Let's Encrypt, renewed automatically) and forwards to TaskForge.
|
||||
{DOMAIN} {
|
||||
encode zstd gzip
|
||||
reverse_proxy 127.0.0.1:8080
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds TaskForge and deploys it to a droplet. Run from Git Bash (or any shell with jai, ssh and scp):
|
||||
#
|
||||
# deploy/deploy.sh root@<droplet-ip> tasks.example.com first time (sets up the droplet for that domain)
|
||||
# deploy/deploy.sh root@<droplet-ip> later updates
|
||||
#
|
||||
# The server is cross-compiled to a Linux object file here and linked on the droplet (which has libc etc.).
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${1:?usage: deploy.sh user@host [domain]}"
|
||||
DOMAIN="${2:-}"
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
jai build.jai - web release
|
||||
jai build.jai - server-linux
|
||||
|
||||
ssh "$HOST" 'mkdir -p /opt/taskforge/staging'
|
||||
scp server/bin/taskforge-server.o web/index.html web/taskforge.js web/taskforge.wasm web/fonts.bin \
|
||||
deploy/setup-droplet.sh deploy/taskforge.service deploy/Caddyfile \
|
||||
"$HOST":/opt/taskforge/staging/
|
||||
|
||||
ssh "$HOST" "set -e
|
||||
cd /opt/taskforge/staging
|
||||
if [ ! -f /etc/systemd/system/taskforge.service ]; then
|
||||
if [ -z '$DOMAIN' ]; then echo 'First deploy: pass your domain as the second argument.'; exit 1; fi
|
||||
bash setup-droplet.sh '$DOMAIN'
|
||||
fi
|
||||
cc taskforge-server.o -o taskforge-server -no-pie -lm -ldl -lpthread
|
||||
install -m 755 taskforge-server /opt/taskforge/bin/taskforge-server
|
||||
install -m 644 index.html taskforge.js taskforge.wasm fonts.bin /opt/taskforge/web/
|
||||
systemctl restart taskforge
|
||||
sleep 1
|
||||
systemctl --no-pager --lines=5 status taskforge"
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
# One-time setup of a fresh Ubuntu droplet (22.04 or 24.04) for TaskForge.
|
||||
# deploy.sh runs this automatically on the first deploy. By hand, on the droplet as root:
|
||||
# bash setup-droplet.sh tasks.example.com (with taskforge.service and Caddyfile next to it)
|
||||
#
|
||||
# Installs: SQLite, a C toolchain (to link the server), Caddy (HTTPS via Let's Encrypt), a firewall,
|
||||
# a systemd service and a daily database backup. Point your domain's A record at the droplet first.
|
||||
set -euo pipefail
|
||||
|
||||
DOMAIN="${1:?usage: setup-droplet.sh <domain>}"
|
||||
|
||||
apt-get update
|
||||
apt-get install -y libsqlite3-0 sqlite3 build-essential curl gnupg ufw \
|
||||
debian-keyring debian-archive-keyring apt-transport-https
|
||||
|
||||
# Caddy from its official repository.
|
||||
if ! command -v caddy >/dev/null; then
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list
|
||||
apt-get update
|
||||
apt-get install -y caddy
|
||||
fi
|
||||
|
||||
# Service account and directories.
|
||||
id taskforge >/dev/null 2>&1 || useradd --system --home-dir /var/lib/taskforge --shell /usr/sbin/nologin taskforge
|
||||
mkdir -p /opt/taskforge/bin /opt/taskforge/web /opt/taskforge/staging /var/lib/taskforge /var/backups/taskforge
|
||||
chown taskforge:taskforge /var/lib/taskforge
|
||||
chmod 750 /var/lib/taskforge /var/backups/taskforge
|
||||
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
install -m 644 "$HERE/taskforge.service" /etc/systemd/system/taskforge.service
|
||||
sed "s/{DOMAIN}/$DOMAIN/" "$HERE/Caddyfile" > /etc/caddy/Caddyfile
|
||||
|
||||
# Daily consistent backup (safe while the server runs), kept for 14 days.
|
||||
cat > /etc/cron.daily/taskforge-backup <<'EOF'
|
||||
#!/bin/sh
|
||||
sqlite3 /var/lib/taskforge/taskforge.db ".backup '/var/backups/taskforge/taskforge-$(date +%F).db'"
|
||||
find /var/backups/taskforge -name 'taskforge-*.db' -mtime +14 -delete
|
||||
EOF
|
||||
chmod 755 /etc/cron.daily/taskforge-backup
|
||||
|
||||
ufw allow OpenSSH
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
ufw --force enable
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable taskforge
|
||||
systemctl reload caddy || systemctl restart caddy
|
||||
|
||||
echo
|
||||
echo "Droplet set up for https://$DOMAIN"
|
||||
echo "After the server binary is installed, create the first admin on the droplet:"
|
||||
echo " sudo -u taskforge /opt/taskforge/bin/taskforge-server --db /var/lib/taskforge/taskforge.db add-user <username> \"<Your Name>\" --admin"
|
||||
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
Description=TaskForge server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=taskforge
|
||||
Group=taskforge
|
||||
WorkingDirectory=/var/lib/taskforge
|
||||
ExecStart=/opt/taskforge/bin/taskforge-server --db /var/lib/taskforge/taskforge.db --web /opt/taskforge/web --listen 127.0.0.1 --port 8080
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
# Hardening: the server can only write its database directory.
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ReadWritePaths=/var/lib/taskforge
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user