secubox-openwrt/package/secubox/secubox-app-metablogizer/files/usr/sbin/metablogizerctl
CyberMind-FR 54113d8454 feat(gitea): Create repositories as private by default
Changed default visibility from public to private for new Gitea
repositories created by metablogizerctl and streamlitctl.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-10 08:22:33 +01:00

956 lines
26 KiB
Bash

#!/bin/sh
# SecuBox MetaBlogizer - Static Site Publisher
# Supports uhttpd (default) and nginx LXC runtime
# Copyright (C) 2025 CyberMind.fr
CONFIG="metablogizer"
SITES_ROOT="/srv/metablogizer/sites"
NGINX_LXC="metablogizer-nginx"
LXC_PATH="/srv/lxc"
PORT_BASE=8900
. /lib/functions.sh
log_info() { echo "[INFO] $*"; logger -t metablogizer "$*"; }
log_warn() { echo "[WARN] $*" >&2; }
log_error() { echo "[ERROR] $*" >&2; }
uci_get() { uci -q get ${CONFIG}.$1; }
uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; }
fix_permissions() {
local dir="$1"
[ -d "$dir" ] || return 1
chmod 755 "$dir"
find "$dir" -type d -exec chmod 755 {} \;
find "$dir" -type f -exec chmod 644 {} \;
}
usage() {
cat <<'EOF'
MetaBlogizer - Static Site Publisher
Usage: metablogizerctl <command> [options]
Site Commands:
list List all sites
create <name> <domain> [repo] Create new site
delete <name> Delete site
sync <name> Sync site from git repo
publish <name> Publish site (create HAProxy vhost)
gitea push <name> Create Gitea repo and push site content
gitea init-all Initialize Gitea repos for all existing sites
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
runtime set <uhttpd|nginx> Set runtime preference
Management:
status Show overall status
install-nginx Install nginx LXC container (optional)
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
}
# ===========================================
# Runtime Detection
# ===========================================
has_uhttpd() { [ -x /etc/init.d/uhttpd ]; }
has_nginx_lxc() {
command -v lxc-info >/dev/null 2>&1 && \
[ -d "$LXC_PATH/$NGINX_LXC/rootfs" ]
}
detect_runtime() {
local configured=$(uci_get main.runtime)
case "$configured" in
uhttpd)
if has_uhttpd; then
echo "uhttpd"
else
log_error "uhttpd requested but not available"
return 1
fi
;;
nginx)
if has_nginx_lxc; then
echo "nginx"
else
log_error "nginx LXC requested but not installed"
return 1
fi
;;
auto|*)
# Prefer uhttpd (lighter), fall back to nginx
if has_uhttpd; then
echo "uhttpd"
elif has_nginx_lxc; then
echo "nginx"
else
log_error "No runtime available"
return 1
fi
;;
esac
}
# ===========================================
# Site Management
# ===========================================
get_next_port() {
local port=$PORT_BASE
while uci show uhttpd 2>/dev/null | grep -q "listen_http='0.0.0.0:$port'"; do
port=$((port + 1))
done
echo $port
}
# Convert site name to UCI section name (hyphens -> underscores)
get_section() {
echo "site_$(echo "$1" | tr '-' '_')"
}
site_exists() {
local name="$1"
local section=$(get_section "$name")
uci -q get ${CONFIG}.${section} >/dev/null 2>&1
}
cmd_list() {
echo "MetaBlogizer Sites:"
echo "==================="
local runtime=$(detect_runtime 2>/dev/null)
echo "Runtime: ${runtime:-none}"
echo ""
config_load "$CONFIG"
local found=0
_print_site() {
local section="$1"
local name domain port enabled gitea_repo
config_get name "$section" name
config_get domain "$section" domain
config_get port "$section" port
config_get enabled "$section" enabled "0"
config_get gitea_repo "$section" gitea_repo ""
[ -z "$name" ] && return
local status="disabled"
[ "$enabled" = "1" ] && status="enabled"
local dir_status="missing"
[ -d "$SITES_ROOT/$name" ] && dir_status="exists"
printf " %-15s %-25s :%-5s [%s] %s\n" "$name" "$domain" "$port" "$status" "$dir_status"
found=1
}
config_foreach _print_site site
[ "$found" = "0" ] && echo " No sites configured"
}
cmd_create() {
local name="$1"
local domain="$2"
local gitea_repo="$3"
[ -z "$name" ] && { log_error "Site name required"; return 1; }
[ -z "$domain" ] && { log_error "Domain required"; return 1; }
# Sanitize name
name=$(echo "$name" | tr -cd 'a-z0-9_-')
if site_exists "$name"; then
log_error "Site '$name' already exists"
return 1
fi
local runtime=$(detect_runtime) || return 1
local port=$(get_next_port)
log_info "Creating site: $name ($domain) on port $port using $runtime"
# Create site directory with proper permissions
mkdir -p "$SITES_ROOT/$name"
chmod 755 "$SITES_ROOT/$name"
# Create placeholder index
cat > "$SITES_ROOT/$name/index.html" <<EOF
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>$name</title>
<meta property="og:title" content="$name">
<meta property="og:url" content="https://$domain">
<meta property="og:type" content="website">
<style>
body { font-family: system-ui; max-width: 800px; margin: 50px auto; padding: 20px; }
h1 { color: #3b82f6; }
</style>
</head>
<body>
<h1>$name</h1>
<p>Site published with MetaBlogizer</p>
<p><a href="https://$domain">https://$domain</a></p>
</body>
</html>
EOF
chmod 644 "$SITES_ROOT/$name/index.html"
# Clone from Gitea if repo specified
if [ -n "$gitea_repo" ]; then
local gitea_url=$(uci_get main.gitea_url)
[ -z "$gitea_url" ] && gitea_url="http://localhost:3000"
log_info "Cloning from $gitea_url/$gitea_repo..."
rm -rf "$SITES_ROOT/$name"
git clone "$gitea_url/$gitea_repo.git" "$SITES_ROOT/$name" 2>/dev/null || {
log_warn "Git clone failed, using placeholder"
mkdir -p "$SITES_ROOT/$name"
}
fi
# Always fix permissions for web serving
fix_permissions "$SITES_ROOT/$name"
# Configure runtime
case "$runtime" in
uhttpd)
_create_uhttpd_site "$name" "$port"
;;
nginx)
_create_nginx_site "$name"
;;
esac
# Save site config
uci set ${CONFIG}.site_${name}=site
uci set ${CONFIG}.site_${name}.name="$name"
uci set ${CONFIG}.site_${name}.domain="$domain"
uci set ${CONFIG}.site_${name}.port="$port"
uci set ${CONFIG}.site_${name}.runtime="$runtime"
[ -n "$gitea_repo" ] && uci set ${CONFIG}.site_${name}.gitea_repo="$gitea_repo"
uci set ${CONFIG}.site_${name}.enabled="1"
uci commit ${CONFIG}
log_info "Site created: $name"
log_info "Directory: $SITES_ROOT/$name"
log_info "Local URL: http://localhost:$port"
# Auto-push to Gitea if enabled
local gitea_token_cfg=$(uci_get main.gitea_token)
if [ -n "$gitea_token_cfg" ]; then
log_info "Auto-pushing to Gitea..."
cmd_gitea_push "$name"
fi
echo ""
echo "Next: Run 'metablogizerctl publish $name' to create HAProxy vhost"
}
_create_uhttpd_site() {
local name="$1"
local port="$2"
log_info "Creating uhttpd instance for $name on port $port"
uci set uhttpd.metablog_${name}=uhttpd
uci set uhttpd.metablog_${name}.listen_http="0.0.0.0:$port"
uci set uhttpd.metablog_${name}.home="$SITES_ROOT/$name"
uci set uhttpd.metablog_${name}.index_page="index.html"
uci set uhttpd.metablog_${name}.error_page="/index.html"
uci commit uhttpd
/etc/init.d/uhttpd reload 2>/dev/null || /etc/init.d/uhttpd restart
}
_create_nginx_site() {
local name="$1"
if ! has_nginx_lxc; then
log_error "nginx LXC not installed. Run: metablogizerctl install-nginx"
return 1
fi
log_info "Creating nginx config for $name"
local nginx_conf="$LXC_PATH/$NGINX_LXC/rootfs/etc/nginx/sites.d"
mkdir -p "$nginx_conf"
cat > "$nginx_conf/metablog-$name.conf" <<EOF
location /$name/ {
alias /srv/sites/$name/;
index index.html;
try_files \$uri \$uri/ /index.html;
}
EOF
# Reload nginx in container
lxc-attach -n "$NGINX_LXC" -- nginx -s reload 2>/dev/null || true
}
cmd_publish() {
local name="$1"
[ -z "$name" ] && { log_error "Site name required"; return 1; }
if ! site_exists "$name"; then
log_error "Site '$name' not found"
return 1
fi
local domain=$(uci_get site_${name}.domain)
local port=$(uci_get site_${name}.port)
[ -z "$domain" ] && { log_error "Site domain not configured"; return 1; }
log_info "Publishing $name to $domain"
# Create HAProxy 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 HAProxy 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 HAProxy vhost
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
# Regenerate HAProxy config
/usr/sbin/haproxyctl generate 2>/dev/null
/etc/init.d/haproxy reload 2>/dev/null
log_info "Site published!"
echo ""
echo "URL: https://$domain"
echo ""
echo "To request SSL certificate:"
echo " haproxyctl cert add $domain"
# Auto-package for P2P distribution
if [ -x /usr/sbin/secubox-content-pkg ]; then
log_info "Packaging site for P2P distribution..."
/usr/sbin/secubox-content-pkg site "$name" "$domain" 2>/dev/null && \
log_info "Site packaged for mesh distribution"
fi
}
cmd_delete() {
local name="$1"
[ -z "$name" ] && { log_error "Site name required"; return 1; }
log_info "Deleting site: $name"
# Remove uhttpd instance
uci delete uhttpd.metablog_${name} 2>/dev/null
uci commit uhttpd
/etc/init.d/uhttpd reload 2>/dev/null
# Remove HAProxy config
local domain=$(uci_get site_${name}.domain)
if [ -n "$domain" ]; then
local vhost_name=$(echo "$domain" | tr '.-' '_')
uci delete haproxy.${vhost_name} 2>/dev/null
uci delete haproxy.metablog_${name} 2>/dev/null
uci delete haproxy.metablog_${name}_srv 2>/dev/null
uci commit haproxy
/usr/sbin/haproxyctl generate 2>/dev/null
/etc/init.d/haproxy reload 2>/dev/null
fi
# Remove site config
uci delete ${CONFIG}.site_${name} 2>/dev/null
uci commit ${CONFIG}
# Optionally remove files
if [ -d "$SITES_ROOT/$name" ]; then
echo "Site directory: $SITES_ROOT/$name"
echo "Remove manually if desired: rm -rf $SITES_ROOT/$name"
fi
log_info "Site deleted"
}
cmd_sync() {
local name="$1"
[ -z "$name" ] && { log_error "Site name required"; return 1; }
local section=$(get_section "$name")
local gitea_repo=$(uci_get ${section}.gitea_repo)
[ -z "$gitea_repo" ] && { log_error "No git repo configured for $name"; return 1; }
local site_dir="$SITES_ROOT/$name"
[ ! -d "$site_dir" ] && { log_error "Site directory not found"; return 1; }
log_info "Syncing $name from git..."
cd "$site_dir"
if [ -d ".git" ]; then
git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || git pull
else
local gitea_url=$(uci_get main.gitea_url)
[ -z "$gitea_url" ] && gitea_url="http://localhost:3000"
git clone "$gitea_url/$gitea_repo.git" /tmp/metablog-sync-$$
cp -r /tmp/metablog-sync-$$/* "$site_dir/"
rm -rf /tmp/metablog-sync-$$
fi
# Fix permissions for web serving
fix_permissions "$site_dir"
log_info "Sync complete"
}
# Create Gitea repo via API and push local site content
cmd_gitea_push() {
local name="$1"
[ -z "$name" ] && { log_error "Site name required"; return 1; }
if ! site_exists "$name"; then
log_error "Site '$name' not found"
return 1
fi
# Load Gitea config from dedicated gitea section
local gitea_enabled=$(uci_get gitea.enabled)
local gitea_url=$(uci_get gitea.url)
local gitea_user=$(uci_get gitea.user)
local gitea_token=$(uci_get gitea.token)
[ -z "$gitea_url" ] && gitea_url="http://localhost:3000"
if [ -z "$gitea_token" ]; then
log_error "Gitea token not configured"
log_info "Configure with:"
log_info " uci set metablogizer.gitea=gitea"
log_info " uci set metablogizer.gitea.enabled=1"
log_info " uci set metablogizer.gitea.url='http://192.168.255.1:3001'"
log_info " uci set metablogizer.gitea.user='admin'"
log_info " uci set metablogizer.gitea.token='your-token'"
log_info " uci commit metablogizer"
return 1
fi
local site_dir="$SITES_ROOT/$name"
if [ ! -d "$site_dir" ]; then
log_error "Site '$name' not found at $site_dir"
return 1
fi
local gitea_host=$(echo "$gitea_url" | sed 's|^https\?://||' | sed 's|/.*||')
local gitea_proto=$(echo "$gitea_url" | grep -q '^https' && echo "https" || echo "http")
local repo_name="metablog-$name"
log_info "Creating Gitea repository: $repo_name"
# Check if repo exists, create if not
local repo_check=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token $gitea_token" \
"${gitea_url}/api/v1/repos/${gitea_user}/${repo_name}" 2>/dev/null)
if [ "$repo_check" != "200" ]; then
log_info "Repository doesn't exist, creating..."
local create_result=$(curl -s -X POST \
-H "Authorization: token $gitea_token" \
-H "Content-Type: application/json" \
-d "{\"name\":\"${repo_name}\",\"description\":\"MetaBlogizer site: ${name}\",\"private\":true,\"auto_init\":false}" \
"${gitea_url}/api/v1/user/repos" 2>/dev/null)
if ! echo "$create_result" | grep -q "\"name\":"; then
log_error "Failed to create repository"
log_error "Response: $create_result"
return 1
fi
log_info "Repository created: ${gitea_user}/${repo_name}"
else
log_info "Repository exists: ${gitea_user}/${repo_name}"
fi
# Initialize git in site directory if needed
cd "$site_dir"
if [ ! -d ".git" ]; then
log_info "Initializing git repository..."
git init
git config user.name "$gitea_user"
git config user.email "${gitea_user}@localhost"
fi
# Set remote
local remote_url="${gitea_proto}://${gitea_user}:${gitea_token}@${gitea_host}/${gitea_user}/${repo_name}.git"
git remote remove origin 2>/dev/null
git remote add origin "$remote_url"
# Add, commit and push
log_info "Adding files and committing..."
git add -A
git commit -m "Auto-push from SecuBox MetaBlogizer at $(date -Iseconds)" 2>/dev/null || \
log_info "No changes to commit"
log_info "Pushing to Gitea..."
git push -u origin HEAD:main --force 2>&1 || {
# Try master branch as fallback
git push -u origin HEAD:master --force 2>&1 || {
log_error "Failed to push to Gitea"
return 1
}
}
# Save repo reference in UCI
local section=$(get_section "$name")
uci set "${CONFIG}.${section}.gitea_repo=${gitea_user}/${repo_name}"
uci set "${CONFIG}.${section}.gitea_synced=$(date -Iseconds)"
uci commit "$CONFIG"
log_info "Push complete: ${gitea_url}/${gitea_user}/${repo_name}"
}
# Initialize Gitea for all existing sites
cmd_gitea_init_all() {
local gitea_token=$(uci_get main.gitea_token)
if [ -z "$gitea_token" ]; then
log_error "Gitea token not configured"
log_info "Configure with:"
log_info " uci set metablogizer.main.gitea_url='http://192.168.255.1:3000'"
log_info " uci set metablogizer.main.gitea_user='admin'"
log_info " uci set metablogizer.main.gitea_token='your-token'"
log_info " uci commit metablogizer"
return 1
fi
log_info "Initializing Gitea repositories for all sites..."
echo ""
local success=0
local failed=0
# Load config and iterate over sites
config_load "$CONFIG"
_init_site_gitea() {
local section="$1"
local name
config_get name "$section" name
[ -z "$name" ] && return
# Check if site directory exists
if [ ! -d "$SITES_ROOT/$name" ]; then
log_warn "[$name] Site directory not found, skipping"
return
fi
# Check if already has a repo configured
local existing_repo
config_get existing_repo "$section" gitea_repo
if [ -n "$existing_repo" ]; then
log_info "[$name] Already linked to $existing_repo, syncing..."
else
log_info "[$name] Creating Gitea repository..."
fi
if cmd_gitea_push "$name"; then
success=$((success + 1))
else
failed=$((failed + 1))
fi
echo ""
}
config_foreach _init_site_gitea site
echo "========================================"
echo "Gitea initialization complete"
echo " Success: $success"
echo " Failed: $failed"
echo "========================================"
}
cmd_runtime() {
local action="$1"
local value="$2"
if [ "$action" = "set" ]; then
case "$value" in
uhttpd|nginx|auto)
uci_set main.runtime "$value"
log_info "Runtime set to: $value"
;;
*)
log_error "Invalid runtime: $value (use uhttpd, nginx, or auto)"
return 1
;;
esac
else
local configured=$(uci_get main.runtime)
local detected=$(detect_runtime 2>/dev/null)
echo "Configured: ${configured:-auto}"
echo "Detected: ${detected:-none}"
echo ""
echo "Available:"
has_uhttpd && echo " - uhttpd (installed)" || echo " - uhttpd (not available)"
has_nginx_lxc && echo " - nginx LXC (installed)" || echo " - nginx LXC (not installed)"
fi
}
cmd_status() {
echo "MetaBlogizer Status"
echo "==================="
local enabled=$(uci_get main.enabled)
local runtime=$(detect_runtime 2>/dev/null)
local sites_count=$(uci show $CONFIG 2>/dev/null | grep -c "=site")
echo "Enabled: $([ "$enabled" = "1" ] && echo "yes" || echo "no")"
echo "Runtime: ${runtime:-none}"
echo "Sites: $sites_count"
echo "Sites Root: $SITES_ROOT"
echo ""
cmd_list
}
cmd_install_nginx() {
log_info "Installing nginx LXC container..."
command -v lxc-start >/dev/null 2>&1 || {
log_error "LXC not installed. Install with: opkg install lxc lxc-common"
return 1
}
local rootfs="$LXC_PATH/$NGINX_LXC/rootfs"
mkdir -p "$LXC_PATH/$NGINX_LXC"
# Download Alpine
local arch="aarch64"
case "$(uname -m)" in
x86_64) arch="x86_64" ;;
armv7l) arch="armv7" ;;
esac
log_info "Downloading Alpine Linux..."
wget -q -O /tmp/alpine-nginx.tar.gz \
"https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/$arch/alpine-minirootfs-3.19.0-$arch.tar.gz" || {
log_error "Failed to download Alpine"
return 1
}
mkdir -p "$rootfs"
tar xzf /tmp/alpine-nginx.tar.gz -C "$rootfs"
rm -f /tmp/alpine-nginx.tar.gz
# Configure
echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf"
# Install nginx
chroot "$rootfs" /bin/sh -c "apk update && apk add --no-cache nginx"
# Create LXC config
cat > "$LXC_PATH/$NGINX_LXC/config" <<EOF
lxc.uts.name = $NGINX_LXC
lxc.rootfs.path = dir:$rootfs
lxc.net.0.type = none
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.mount.entry = $SITES_ROOT srv/sites none bind,create=dir 0 0
lxc.cap.drop = sys_admin sys_module mac_admin mac_override
lxc.init.cmd = /usr/sbin/nginx -g 'daemon off;'
EOF
log_info "nginx LXC installed"
log_info "Start with: lxc-start -n $NGINX_LXC -d"
}
# ===========================================
# KISS ULTIME MODE - Emancipate
# ===========================================
_emancipate_dns() {
local name="$1"
local domain="$2"
local default_zone=$(uci -q get dns-provider.main.zone)
local provider=$(uci -q get dns-provider.main.provider)
local vortex_wildcard=$(uci -q get vortex-dns.master.wildcard_domain)
# 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
# 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; }
# Detect zone from domain suffix (try known zones)
local zone=""
local subdomain=""
for z in "secubox.in" "maegia.tv" "cybermind.fr"; do
if echo "$domain" | grep -q "\.${z}$"; then
zone="$z"
subdomain=$(echo "$domain" | sed "s/\.${z}$//")
break
elif [ "$domain" = "$z" ]; then
zone="$z"
subdomain="@"
break
fi
done
# Fallback to default zone if no match
if [ -z "$zone" ]; then
zone="$default_zone"
subdomain=$(echo "$domain" | sed "s/\.${zone}$//")
fi
[ -z "$zone" ] && { log_warn "[DNS] No zone detected, skipping external DNS"; return 1; }
log_info "[DNS] Registering $subdomain.$zone -> $public_ip via $provider"
# Register on the published domain's zone
dnsctl -z "$zone" add A "$subdomain" "$public_ip" 3600
# Also register on vortex node subdomain (e.g., bday.gk2.secubox.in)
if [ -n "$vortex_wildcard" ]; then
local vortex_zone=$(echo "$vortex_wildcard" | sed 's/^[^.]*\.//')
local vortex_node=$(echo "$vortex_wildcard" | cut -d. -f1)
local vortex_subdomain="${name}.${vortex_node}"
log_info "[DNS] Registering $vortex_subdomain.$vortex_zone -> $public_ip (vortex node)"
dnsctl -z "$vortex_zone" add A "$vortex_subdomain" "$public_ip" 3600
fi
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"
# Generate fresh config
haproxyctl generate 2>/dev/null
# Always restart for clean state with new vhosts/certs
log_info "[RELOAD] Restarting HAProxy for clean state..."
/etc/init.d/haproxy restart 2>/dev/null
sleep 1
# Verify HAProxy is running
if pgrep haproxy >/dev/null 2>&1; then
log_info "[RELOAD] HAProxy restarted successfully"
else
log_warn "[RELOAD] HAProxy may not have started properly"
fi
}
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 section=$(get_section "$name")
local domain=$(uci_get ${section}.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}.${section}.emancipated="1"
uci set ${CONFIG}.${section}.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
# ===========================================
case "${1:-}" in
list) shift; cmd_list "$@" ;;
create) shift; cmd_create "$@" ;;
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 "$@" ;;
gitea)
shift
case "${1:-}" in
push) shift; cmd_gitea_push "$@" ;;
init-all) shift; cmd_gitea_init_all "$@" ;;
*) echo "Usage: metablogizerctl gitea {push|init-all} [name]"; exit 1 ;;
esac
;;
help|--help|-h) usage ;;
*) usage ;;
esac