My nix-darwin and NixOS config
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: modularise Caddy and add SMTP test email support

- Extract Caddy into dedicated reusable module
- Refactor PDS to use shared Caddy service
- Enable automatic restart for Caddy
- Add Resend API test email during setup
- Require curl as setup dependency

+83 -17
+1
hosts/server/default.nix
··· 8 8 ./minimal-hardware.nix 9 9 ../../modules/common.nix 10 10 ../../modules/users.nix 11 + ../../modules/caddy.nix 11 12 ../../modules/pds.nix 12 13 ../../profiles/server-hardened.nix 13 14 ];
+33
modules/caddy.nix
··· 1 + ############################################################################## 2 + # Caddy reverse proxy module — Generic HTTP reverse proxy configuration. 3 + # 4 + # This module provides a minimal Caddy configuration for reverse proxying 5 + # HTTP services. Designed to work with Cloudflare Tunnel (or other external 6 + # TLS terminators), so automatic HTTPS is disabled. 7 + # 8 + # Usage: 9 + # Import this module and configure services.caddy.virtualHosts as needed. 10 + # For PDS, the configuration is handled by modules/pds.nix. 11 + ############################################################################## 12 + { config, lib, pkgs, ... }: 13 + 14 + { 15 + # ── Caddy service ───────────────────────────────────────────────────────────── 16 + services.caddy = { 17 + enable = true; 18 + 19 + # Global config: disable automatic HTTPS since external service (like 20 + # Cloudflare) handles TLS. Caddy only receives plain HTTP from tunnel. 21 + globalConfig = '' 22 + auto_https off 23 + ''; 24 + }; 25 + 26 + # Keep Caddy service running even if it crashes. 27 + systemd.services.caddy = { 28 + serviceConfig = { 29 + Restart = "always"; 30 + RestartSec = "5s"; 31 + }; 32 + }; 33 + }
+8 -13
modules/pds.nix
··· 107 107 # Listens on localhost:caddyPort only — never exposed publicly. 108 108 # Cloudflare handles TLS; Caddy receives plain HTTP from the tunnel daemon. 109 109 # Using http:// prefix disables Caddy's automatic HTTPS / ACME entirely. 110 - services.caddy = { 111 - enable = true; 112 - # Global config: disable automatic HTTPS since Cloudflare handles TLS. 113 - globalConfig = '' 114 - auto_https off 110 + # 111 + # Note: Caddy service itself is enabled by modules/caddy.nix. 112 + services.caddy.virtualHosts."http://127.0.0.1:${caddyPort}" = { 113 + extraConfig = '' 114 + ${ageAssuranceBlocks} 115 + handle { 116 + reverse_proxy http://127.0.0.1:${pdsPort} 117 + } 115 118 ''; 116 - virtualHosts."http://127.0.0.1:${caddyPort}" = { 117 - extraConfig = '' 118 - ${ageAssuranceBlocks} 119 - handle { 120 - reverse_proxy http://127.0.0.1:${pdsPort} 121 - } 122 - ''; 123 - }; 124 119 }; 125 120 126 121 # ── Cloudflare tunnel ─────────────────────────────────────────────────────────
+41 -4
scripts/pds-setup.sh
··· 89 89 grep "${key}\s*=" "$SETTINGS" | grep -o '"[^"]*"\|[0-9]\+' | head -1 | tr -d '"' 90 90 } 91 91 92 + # Send a test email via Resend API 93 + # Args: $1=API_KEY, $2=FROM_ADDRESS, $3=TO_ADDRESS 94 + send_test_email() { 95 + local api_key="$1" from="$2" to="$3" 96 + 97 + local response 98 + response=$(curl -s -X POST 'https://api.resend.com/emails' \ 99 + -H "Authorization: Bearer ${api_key}" \ 100 + -H 'Content-Type: application/json' \ 101 + -d "{ 102 + \"from\": \"${from}\", 103 + \"to\": [\"${to}\"], 104 + \"subject\": \"PDS Setup Complete! 🎉\", 105 + \"html\": \"<h2>Your PDS is ready to deploy!</h2><p>SMTP email is working correctly. You can now proceed with deployment.</p><p><strong>Next steps:</strong></p><ul><li>Commit your changes</li><li>Deploy to your server</li><li>Add DNS records</li></ul>\" 106 + }" 2>&1) 107 + 108 + if echo "$response" | grep -q '"id"'; then 109 + ok "Test email sent to $to" 110 + else 111 + warn "Test email failed: $response" 112 + fi 113 + } 114 + 92 115 # ── Step 1: Prerequisites ───────────────────────────────────────────────────── 93 116 94 117 log "Step 1/9 — Prerequisites" 95 118 96 119 missing=() 97 - for cmd in openssl nix git; do command -v "$cmd" &>/dev/null || missing+=("$cmd"); done 120 + for cmd in openssl nix git curl; do command -v "$cmd" &>/dev/null || missing+=("$cmd"); done 98 121 (( ${#missing[@]} == 0 )) || fail "Missing commands: ${missing[*]}" 99 122 100 123 if command -v cloudflared &>/dev/null; then ··· 166 189 [[ -n "$SMTP_URL" ]] && read -rp " FROM address (blank to skip): " SMTP_FROM 167 190 fi 168 191 169 - [[ -n "$SMTP_URL" ]] \ 170 - && ok "SMTP: $SMTP_FROM via $SMTP_URL" \ 171 - || warn "SMTP skipped" 192 + if [[ -n "$SMTP_URL" ]]; then 193 + ok "SMTP: $SMTP_FROM via $SMTP_URL" 194 + 195 + # Send test email if using Resend 196 + if [[ "$SMTP_URL" == *"resend"* ]]; then 197 + # Extract API key from SMTP URL: smtps://resend:<API_KEY>@smtp.resend.com:465/ 198 + RESEND_API_KEY=$(echo "$SMTP_URL" | sed -n 's|.*resend:\([^@]*\)@.*|\1|p') 199 + 200 + if [[ -n "$RESEND_API_KEY" ]] && [[ -n "$SMTP_FROM" ]] && [[ -n "$CUR_EMAIL" ]]; then 201 + echo 202 + section "Sending test email..." 203 + send_test_email "$RESEND_API_KEY" "$SMTP_FROM" "$CUR_EMAIL" 204 + fi 205 + fi 206 + else 207 + warn "SMTP skipped" 208 + fi 172 209 173 210 # ── Step 3: PDS runtime secrets ─────────────────────────────────────────────── 174 211