feat(metablogizer): Add KISS ULTIME MODE emancipate workflow
Add `metablogizerctl emancipate <name>` command for one-command full exposure workflow: 1. DNS A record via dnsctl (Gandi/OVH based on availability) 2. Vortex DNS mesh publication 3. HAProxy vhost with SSL/ACME enabled 4. SSL certificate request (webroot mode) 5. Zero-downtime HAProxy reload Usage: metablogizerctl create myblog blog.example.com metablogizerctl emancipate myblog Bump version to 1.1.0. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3d26c8a64e
commit
e21ca8a060
@ -663,3 +663,21 @@ _Last updated: 2026-02-07_
|
||||
- RPCD `fix_ports` method wrapping CLI command
|
||||
- Visual feedback with modal spinner
|
||||
- Updated container.sh to include `dovecot-pop3d` in initial package list.
|
||||
|
||||
44. **MetaBlogizer KISS ULTIME MODE (2026-02-07)**
|
||||
- Added `metablogizerctl emancipate <name>` — one-command full exposure workflow.
|
||||
- **Workflow steps** (automated in sequence):
|
||||
- DNS Registration: Creates A record via `dnsctl` (Gandi/OVH based on availability)
|
||||
- Vortex Mesh: Publishes to mesh via `vortexctl mesh publish`
|
||||
- HAProxy: Creates backend, server, and vhost with SSL/ACME enabled
|
||||
- SSL Certificate: Requests ACME cert via `haproxyctl cert add` (webroot mode)
|
||||
- Zero-downtime Reload: Applies HAProxy config via SIGUSR2
|
||||
- **Helper functions**:
|
||||
- `_emancipate_dns()`: Public IP detection, subdomain extraction, dnsctl integration
|
||||
- `_emancipate_vortex()`: Mesh publication if vortex-dns enabled
|
||||
- `_emancipate_haproxy()`: UCI backend/server/vhost creation, haproxyctl generate
|
||||
- `_emancipate_ssl()`: ACME certificate request with status feedback
|
||||
- `_emancipate_reload()`: Graceful HAProxy reload with restart fallback
|
||||
- **Usage**: `metablogizerctl create myblog blog.example.com && metablogizerctl emancipate myblog`
|
||||
- **Tracking**: Stores `emancipated=1` and `emancipated_at` timestamp in UCI
|
||||
- Part of Punk Exposure architecture (multi-channel emancipation).
|
||||
|
||||
@ -51,7 +51,17 @@ _Last updated: 2026-02-07_
|
||||
- Gossip-based exposure config sync via secubox-p2p
|
||||
- Created `luci-app-vortex-dns` dashboard
|
||||
|
||||
### Just Completed (2026-02-06)
|
||||
### Just Completed (2026-02-07)
|
||||
|
||||
- **MetaBlogizer KISS ULTIME MODE** — DONE (2026-02-07)
|
||||
- Added `metablogizerctl emancipate` command
|
||||
- One-command workflow: DNS + Vortex + HAProxy + SSL + Reload
|
||||
- DNS registration via dnsctl (Gandi/OVH based on availability)
|
||||
- Vortex DNS mesh publication
|
||||
- HAProxy vhost with SSL and ACME
|
||||
- Zero-downtime reload via SIGUSR2
|
||||
|
||||
### Completed (2026-02-06)
|
||||
|
||||
- **AI Insights Dashboard** — DONE
|
||||
- Created `luci-app-ai-insights` - unified view across all AI agents
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=secubox-app-metablogizer
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_VERSION:=1.1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
@ -17,6 +17,7 @@ endef
|
||||
define Package/secubox-app-metablogizer/description
|
||||
Static site publisher with auto-vhost creation.
|
||||
Supports uhttpd (default) and nginx LXC runtimes.
|
||||
KISS ULTIME MODE: One-command DNS + SSL + Mesh workflow.
|
||||
endef
|
||||
|
||||
define Package/secubox-app-metablogizer/install
|
||||
|
||||
@ -38,6 +38,12 @@ Site Commands:
|
||||
delete <name> Delete site
|
||||
sync <name> Sync site from git repo
|
||||
publish <name> Publish site (create HAProxy vhost)
|
||||
emancipate <name> KISS ULTIME MODE - Full exposure workflow:
|
||||
1. DNS A record (Gandi/OVH)
|
||||
2. Vortex DNS mesh publication
|
||||
3. HAProxy vhost with SSL
|
||||
4. ACME certificate
|
||||
5. Zero-downtime reload
|
||||
|
||||
Runtime Commands:
|
||||
runtime Show current runtime
|
||||
@ -51,6 +57,10 @@ Runtime Selection:
|
||||
auto - Auto-detect (uhttpd preferred)
|
||||
uhttpd - Use uhttpd instances (lightweight)
|
||||
nginx - Use nginx LXC container (more features)
|
||||
|
||||
Examples:
|
||||
metablogizerctl create myblog blog.example.com
|
||||
metablogizerctl emancipate myblog # Full exposure in one command
|
||||
EOF
|
||||
}
|
||||
|
||||
@ -512,6 +522,199 @@ EOF
|
||||
log_info "Start with: lxc-start -n $NGINX_LXC -d"
|
||||
}
|
||||
|
||||
# ===========================================
|
||||
# KISS ULTIME MODE - Emancipate
|
||||
# ===========================================
|
||||
|
||||
_emancipate_dns() {
|
||||
local name="$1"
|
||||
local domain="$2"
|
||||
local zone=$(uci -q get dns-provider.main.zone)
|
||||
local provider=$(uci -q get dns-provider.main.provider)
|
||||
|
||||
[ -z "$zone" ] && { log_warn "[DNS] No zone configured, skipping external DNS"; return 1; }
|
||||
|
||||
# Extract subdomain from domain
|
||||
local subdomain=$(echo "$domain" | sed "s/\.${zone}$//")
|
||||
|
||||
# Get public IP
|
||||
local public_ip=$(curl -s --connect-timeout 5 https://ipv4.icanhazip.com 2>/dev/null | tr -d '\n')
|
||||
[ -z "$public_ip" ] && { log_warn "[DNS] Cannot detect public IP, skipping DNS"; return 1; }
|
||||
|
||||
log_info "[DNS] Registering $subdomain.$zone -> $public_ip via $provider"
|
||||
|
||||
# Check if dnsctl is available
|
||||
if ! command -v dnsctl >/dev/null 2>&1; then
|
||||
log_warn "[DNS] dnsctl not found, skipping external DNS"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if provider is available
|
||||
if ! dnsctl test >/dev/null 2>&1; then
|
||||
log_warn "[DNS] Provider $provider not configured or credentials invalid"
|
||||
log_warn "[DNS] Skipping external DNS registration"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Add A record
|
||||
dnsctl add A "$subdomain" "$public_ip" 3600
|
||||
|
||||
# Verify propagation (non-blocking)
|
||||
log_info "[DNS] Verify with: dnsctl verify $domain"
|
||||
}
|
||||
|
||||
_emancipate_vortex() {
|
||||
local name="$1"
|
||||
local domain="$2"
|
||||
|
||||
# Check if vortexctl is available
|
||||
if ! command -v vortexctl >/dev/null 2>&1; then
|
||||
log_info "[VORTEX] vortexctl not found, skipping mesh publication"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if vortex-dns is enabled
|
||||
local vortex_enabled=$(uci -q get vortex-dns.main.enabled)
|
||||
|
||||
if [ "$vortex_enabled" = "1" ]; then
|
||||
log_info "[VORTEX] Publishing $name as $domain to mesh"
|
||||
vortexctl mesh publish "$name" "$domain" 2>/dev/null
|
||||
else
|
||||
log_info "[VORTEX] Vortex DNS disabled, skipping mesh publication"
|
||||
fi
|
||||
}
|
||||
|
||||
_emancipate_haproxy() {
|
||||
local name="$1"
|
||||
local domain="$2"
|
||||
local port=$(uci_get site_${name}.port)
|
||||
|
||||
log_info "[HAPROXY] Creating vhost for $domain"
|
||||
|
||||
# Create backend
|
||||
local backend_name="metablog_${name}"
|
||||
uci set haproxy.${backend_name}=backend
|
||||
uci set haproxy.${backend_name}.name="$backend_name"
|
||||
uci set haproxy.${backend_name}.mode="http"
|
||||
uci set haproxy.${backend_name}.balance="roundrobin"
|
||||
uci set haproxy.${backend_name}.enabled="1"
|
||||
|
||||
# Create server
|
||||
local server_name="${backend_name}_srv"
|
||||
uci set haproxy.${server_name}=server
|
||||
uci set haproxy.${server_name}.backend="$backend_name"
|
||||
uci set haproxy.${server_name}.name="uhttpd"
|
||||
uci set haproxy.${server_name}.address="192.168.255.1"
|
||||
uci set haproxy.${server_name}.port="$port"
|
||||
uci set haproxy.${server_name}.weight="100"
|
||||
uci set haproxy.${server_name}.check="1"
|
||||
uci set haproxy.${server_name}.enabled="1"
|
||||
|
||||
# Create vhost with SSL
|
||||
local vhost_name=$(echo "$domain" | tr '.-' '_')
|
||||
uci set haproxy.${vhost_name}=vhost
|
||||
uci set haproxy.${vhost_name}.domain="$domain"
|
||||
uci set haproxy.${vhost_name}.backend="$backend_name"
|
||||
uci set haproxy.${vhost_name}.ssl="1"
|
||||
uci set haproxy.${vhost_name}.ssl_redirect="1"
|
||||
uci set haproxy.${vhost_name}.acme="1"
|
||||
uci set haproxy.${vhost_name}.enabled="1"
|
||||
|
||||
uci commit haproxy
|
||||
|
||||
# Generate HAProxy config
|
||||
if command -v haproxyctl >/dev/null 2>&1; then
|
||||
haproxyctl generate 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
_emancipate_ssl() {
|
||||
local domain="$1"
|
||||
|
||||
log_info "[SSL] Requesting certificate for $domain"
|
||||
|
||||
# Check if haproxyctl is available
|
||||
if ! command -v haproxyctl >/dev/null 2>&1; then
|
||||
log_warn "[SSL] haproxyctl not found, skipping SSL"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# haproxyctl cert add handles ACME webroot mode (no HAProxy restart needed)
|
||||
haproxyctl cert add "$domain" 2>&1 | while read line; do
|
||||
echo " $line"
|
||||
done
|
||||
|
||||
if [ -f "/srv/haproxy/certs/$domain.pem" ]; then
|
||||
log_info "[SSL] Certificate obtained successfully"
|
||||
else
|
||||
log_warn "[SSL] Certificate request may still be pending"
|
||||
log_warn "[SSL] Check with: haproxyctl cert verify $domain"
|
||||
fi
|
||||
}
|
||||
|
||||
_emancipate_reload() {
|
||||
log_info "[RELOAD] Applying HAProxy configuration"
|
||||
/etc/init.d/haproxy reload 2>/dev/null || {
|
||||
log_warn "[RELOAD] Reload failed, restarting..."
|
||||
/etc/init.d/haproxy restart 2>/dev/null
|
||||
}
|
||||
}
|
||||
|
||||
cmd_emancipate() {
|
||||
local name="$1"
|
||||
[ -z "$name" ] && { log_error "Site name required"; usage; return 1; }
|
||||
|
||||
if ! site_exists "$name"; then
|
||||
log_error "Site '$name' not found"
|
||||
log_error "Create first: metablogizerctl create $name <domain>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local domain=$(uci_get site_${name}.domain)
|
||||
[ -z "$domain" ] && { log_error "Site domain not configured"; return 1; }
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo " KISS ULTIME MODE: Emancipating $name"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Step 1: DNS Registration (external provider)
|
||||
_emancipate_dns "$name" "$domain"
|
||||
|
||||
# Step 2: Vortex DNS (mesh registration)
|
||||
_emancipate_vortex "$name" "$domain"
|
||||
|
||||
# Step 3: HAProxy vhost + backend
|
||||
_emancipate_haproxy "$name" "$domain"
|
||||
|
||||
# Step 4: SSL Certificate
|
||||
_emancipate_ssl "$domain"
|
||||
|
||||
# Step 5: Reload HAProxy
|
||||
_emancipate_reload
|
||||
|
||||
# Mark site as emancipated
|
||||
uci set ${CONFIG}.site_${name}.emancipated="1"
|
||||
uci set ${CONFIG}.site_${name}.emancipated_at="$(date -Iseconds)"
|
||||
uci commit ${CONFIG}
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo " EMANCIPATION COMPLETE"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
echo " Site: https://$domain"
|
||||
echo " Status: Published and SSL-protected"
|
||||
echo " Mesh: $(uci -q get vortex-dns.main.enabled | grep -q 1 && echo 'Published' || echo 'Disabled')"
|
||||
echo ""
|
||||
echo " Verify:"
|
||||
echo " curl -v https://$domain"
|
||||
echo " dnsctl verify $domain"
|
||||
echo " haproxyctl cert verify $domain"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ===========================================
|
||||
# Main
|
||||
# ===========================================
|
||||
@ -522,6 +725,7 @@ case "${1:-}" in
|
||||
delete) shift; cmd_delete "$@" ;;
|
||||
sync) shift; cmd_sync "$@" ;;
|
||||
publish) shift; cmd_publish "$@" ;;
|
||||
emancipate) shift; cmd_emancipate "$@" ;;
|
||||
runtime) shift; cmd_runtime "$@" ;;
|
||||
status) shift; cmd_status "$@" ;;
|
||||
install-nginx) shift; cmd_install_nginx "$@" ;;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user