ops: add Caddyfile + landing + DB url

This commit is contained in:
Claude 2026-05-30 22:05:11 +02:00
parent 7e583d262b
commit bbcd8ffe41
4 changed files with 128 additions and 1 deletions

67
deploy/Caddyfile Normal file
View file

@ -0,0 +1,67 @@
{
# HTTP/1.1 + HTTP/2 + HTTP/3 (QUIC). Cloudflare serves h3 to browsers;
# origin↔CF is TCP, but enabling h3 here is harmless + future-proof.
servers {
protocols h1 h2 h3
}
}
# Shared security headers. (No strict CSP yet — the app uses inline
# scripts; we'll add a nonce-based CSP later.)
(sechdrs) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}
# All sites use the Cloudflare Origin cert (valid to 2041, no ACME/renewal).
(cfcert) {
tls /etc/ssl/cannabuddy/origin.crt /etc/ssl/cannabuddy/origin.key
}
# --- Marketing landing (apex) ---
cannabuddy.eu, www.cannabuddy.eu {
import cfcert
import sechdrs
encode zstd gzip
root * /var/www/cannabuddy-landing
file_server
}
# --- App (PWA) --- tenant resolved by login, not subdomain
app.cannabuddy.eu {
import cfcert
import sechdrs
encode zstd gzip
# MQTT-over-WebSocket → Mosquitto ws listener (localhost)
@mqtt path /mqtt /mqtt/*
reverse_proxy @mqtt 127.0.0.1:8083
# everything else → Astro SSR (Node/pm2)
reverse_proxy 127.0.0.1:7502
}
# --- Superadmin console (same app, host-routed; lock behind CF Access) ---
admin.cannabuddy.eu {
import cfcert
import sechdrs
encode zstd gzip
reverse_proxy 127.0.0.1:7502
}
# --- Forgejo ---
git.cannabuddy.eu {
import cfcert
import sechdrs
reverse_proxy 127.0.0.1:3000
}
# --- Status (Uptime Kuma) ---
status.cannabuddy.eu {
import cfcert
import sechdrs
reverse_proxy 127.0.0.1:3001
}

33
deploy/landing/index.html Normal file
View file

@ -0,0 +1,33 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<meta name="theme-color" content="#0f1419">
<title>CannaBuddy</title>
<style>
:root{ --bg:#131820; --surface:#1a2027; --text:#e2e5e9; --muted:#8b9199; --green:#46b46e }
*{box-sizing:border-box}
body{margin:0;min-height:100vh;background:var(--bg);color:var(--text);
font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
display:flex;align-items:center;justify-content:center;text-align:center;padding:24px;-webkit-font-smoothing:antialiased}
.card{max-width:440px}
.dot{display:inline-block;width:10px;height:10px;border-radius:50%;background:var(--green);
box-shadow:0 0 0 0 rgba(70,180,110,.6);animation:p 2s infinite}
@keyframes p{0%{box-shadow:0 0 0 0 rgba(70,180,110,.5)}70%{box-shadow:0 0 0 14px rgba(70,180,110,0)}100%{box-shadow:0 0 0 0 rgba(70,180,110,0)}}
h1{font-size:2rem;margin:.4em 0 .2em;letter-spacing:-.01em}
.g{color:var(--green)}
p{color:var(--muted);line-height:1.5}
a{color:var(--green);text-decoration:none}
</style>
</head>
<body>
<div class="card">
<span class="dot"></span>
<h1>Canna<span class="g">Buddy</span></h1>
<p>Hydroponik-Monitoring für Cannabis Social Clubs.<br>Die Plattform wird gerade aufgebaut.</p>
<p><a href="https://app.cannabuddy.eu">→ Zur App</a></p>
</div>
</body>
</html>

View file

@ -51,7 +51,8 @@ _Last updated: 2026-05-30 (new-VPS bring-up)._
- Uses: nightly encrypted `pg_dump` backups → R2; later image uploads (presigned URLs). - Uses: nightly encrypted `pg_dump` backups → R2; later image uploads (presigned URLs).
## App secrets — to generate fresh on the new box (do NOT reuse old) ## App secrets — to generate fresh on the new box (do NOT reuse old)
- `DATABASE_URL` (TimescaleDB role + password) — _TODO_ - **`DATABASE_URL`** = `postgres://hbcloud:095bb2ad7b25355592243d10c46e1e39cc15129cd71c5d53@127.0.0.1:5432/cannabuddy`
(also in `/etc/cannabuddy/cloud.env` on the box, root 0600; TimescaleDB 2.27.1 ext enabled)
- Better-Auth secret (`BETTER_AUTH_SECRET`) — _TODO (openssl rand -hex 32)_ - Better-Auth secret (`BETTER_AUTH_SECRET`) — _TODO (openssl rand -hex 32)_
- NanoMQ ↔ cloud auth-hook shared secret / device-token signing — _TODO_ - NanoMQ ↔ cloud auth-hook shared secret / device-token signing — _TODO_
- Forgejo: admin creds + CI runner registration token + deploy SSH key — _TODO_ - Forgejo: admin creds + CI runner registration token + deploy SSH key — _TODO_

26
scripts/create-db.sh Normal file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Create the app DB + role + TimescaleDB extension, write the env file.
set -euo pipefail
DBPASS=$(openssl rand -hex 24)
# role (create or reset password)
sudo -u postgres psql -c "CREATE ROLE hbcloud LOGIN PASSWORD '$DBPASS';" 2>/dev/null \
|| sudo -u postgres psql -c "ALTER ROLE hbcloud LOGIN PASSWORD '$DBPASS';"
# database
sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='cannabuddy'" | grep -q 1 \
|| sudo -u postgres createdb -O hbcloud cannabuddy
# timescaledb extension
sudo -u postgres psql -d cannabuddy -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS timescaledb;" >/dev/null
# env file (root-only) — the app + go-auth read DATABASE_URL from here
mkdir -p /etc/cannabuddy
touch /etc/cannabuddy/cloud.env && chmod 600 /etc/cannabuddy/cloud.env
grep -q '^DATABASE_URL=' /etc/cannabuddy/cloud.env 2>/dev/null \
&& sed -i "s#^DATABASE_URL=.*#DATABASE_URL=postgres://hbcloud:$DBPASS@127.0.0.1:5432/cannabuddy#" /etc/cannabuddy/cloud.env \
|| echo "DATABASE_URL=postgres://hbcloud:$DBPASS@127.0.0.1:5432/cannabuddy" >> /etc/cannabuddy/cloud.env
echo "OK — DB cannabuddy + role hbcloud ready, timescaledb extension on"
sudo -u postgres psql -d cannabuddy -tAc "SELECT extname, extversion FROM pg_extension WHERE extname='timescaledb';"
echo "DATABASE_URL=postgres://hbcloud:$DBPASS@127.0.0.1:5432/cannabuddy"