feat: Add Gitea auto-push and fix Tor Shield server mode
Streamlit/MetaBlogizer: - Add 'gitea push <name>' command to both streamlitctl and metablogizerctl - Auto-creates Gitea repo via API if it doesn't exist - Initializes git, commits all files, and pushes to Gitea - Stores repo reference in UCI for future syncs Tor Shield: - Add 'wan_input_allow' option for server preset - Server mode now properly allows WAN inbound (ports 80, 443, 8443) - Uses nftables rules to integrate with OpenWrt firewall4 - Outbound traffic still routed through Tor (kill_switch) - Cleanup nftables rules on stop/disable Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a694241604
commit
364f19d421
@ -38,6 +38,7 @@ Site Commands:
|
||||
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
|
||||
emancipate <name> KISS ULTIME MODE - Full exposure workflow:
|
||||
1. DNS A record (Gandi/OVH)
|
||||
2. Vortex DNS mesh publication
|
||||
@ -409,7 +410,8 @@ cmd_sync() {
|
||||
local name="$1"
|
||||
[ -z "$name" ] && { log_error "Site name required"; return 1; }
|
||||
|
||||
local gitea_repo=$(uci_get site_${name}.gitea_repo)
|
||||
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"
|
||||
@ -434,6 +436,109 @@ cmd_sync() {
|
||||
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
|
||||
local gitea_enabled=$(uci_get main.gitea_enabled)
|
||||
local gitea_url=$(uci_get main.gitea_url)
|
||||
local gitea_user=$(uci_get main.gitea_user)
|
||||
local gitea_token=$(uci_get main.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.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
|
||||
|
||||
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\":false,\"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}"
|
||||
}
|
||||
|
||||
cmd_runtime() {
|
||||
local action="$1"
|
||||
local value="$2"
|
||||
@ -764,6 +869,13 @@ case "${1:-}" in
|
||||
runtime) shift; cmd_runtime "$@" ;;
|
||||
status) shift; cmd_status "$@" ;;
|
||||
install-nginx) shift; cmd_install_nginx "$@" ;;
|
||||
gitea)
|
||||
shift
|
||||
case "${1:-}" in
|
||||
push) shift; cmd_gitea_push "$@" ;;
|
||||
*) echo "Usage: metablogizerctl gitea push <name>"; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
help|--help|-h) usage ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
|
||||
@ -96,6 +96,7 @@ Gitea Integration:
|
||||
gitea setup Configure git credentials
|
||||
gitea clone <name> <repo> Clone app from Gitea repo
|
||||
gitea pull <name> Pull latest from Gitea
|
||||
gitea push <name> Create Gitea repo and push app content
|
||||
|
||||
Exposure:
|
||||
emancipate <name> [domain] KISS ULTIME MODE - Full exposure workflow:
|
||||
@ -1028,6 +1029,99 @@ cmd_gitea_pull() {
|
||||
log_info "Update complete"
|
||||
}
|
||||
|
||||
# Create Gitea repo via API and push local content
|
||||
cmd_gitea_push() {
|
||||
require_root
|
||||
load_config
|
||||
|
||||
local name="$1"
|
||||
[ -z "$name" ] && { log_error "App name required"; return 1; }
|
||||
|
||||
if [ "$gitea_enabled" != "1" ]; then
|
||||
log_error "Gitea integration not enabled"
|
||||
log_info "Enable with: uci set streamlit.gitea.enabled=1 && uci commit streamlit"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "$gitea_token" ]; then
|
||||
log_error "Gitea token not configured"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local app_dir="$APPS_PATH/$name"
|
||||
|
||||
if [ ! -d "$app_dir" ]; then
|
||||
log_error "App '$name' not found at $app_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="streamlit-$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\":\"Streamlit app: ${name}\",\"private\":false,\"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 app directory if needed
|
||||
cd "$app_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 Streamlit 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
|
||||
uci set "${CONFIG}.${name}.repo=${gitea_user}/${repo_name}"
|
||||
uci set "${CONFIG}.${name}.gitea_synced=$(date -Iseconds)"
|
||||
uci commit "$CONFIG"
|
||||
|
||||
log_info "Push complete: ${gitea_url}/${gitea_user}/${repo_name}"
|
||||
}
|
||||
|
||||
# ===========================================
|
||||
# KISS ULTIME MODE - Emancipate
|
||||
# ===========================================
|
||||
@ -1462,7 +1556,8 @@ case "${1:-}" in
|
||||
setup) shift; cmd_gitea_setup "$@" ;;
|
||||
clone) shift; cmd_gitea_clone "$@" ;;
|
||||
pull) shift; cmd_gitea_pull "$@" ;;
|
||||
*) echo "Usage: streamlitctl gitea {setup|clone|pull}"; exit 1 ;;
|
||||
push) shift; cmd_gitea_push "$@" ;;
|
||||
*) echo "Usage: streamlitctl gitea {setup|clone|pull|push}"; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ config preset 'server'
|
||||
option dns_over_tor '1'
|
||||
option kill_switch '1'
|
||||
option lan_proxy '1'
|
||||
option wan_input_allow '1'
|
||||
|
||||
config proxy 'socks'
|
||||
option port '9050'
|
||||
|
||||
@ -161,12 +161,13 @@ EOF
|
||||
}
|
||||
|
||||
setup_iptables() {
|
||||
local mode trans_port dns_port dns_over_tor kill_switch
|
||||
local mode trans_port dns_port dns_over_tor kill_switch wan_input_allow
|
||||
|
||||
config_load "$CONFIG"
|
||||
config_get mode main mode 'transparent'
|
||||
config_get kill_switch main kill_switch '1'
|
||||
config_get dns_over_tor main dns_over_tor '1'
|
||||
config_get wan_input_allow main wan_input_allow '0'
|
||||
config_get trans_port trans port '9040'
|
||||
config_get dns_port trans dns_port '9053'
|
||||
|
||||
@ -186,6 +187,9 @@ setup_iptables() {
|
||||
iptables -t filter -F TOR_SHIELD 2>/dev/null
|
||||
iptables -t filter -X TOR_SHIELD 2>/dev/null
|
||||
|
||||
# Clean up nftables rules for server mode
|
||||
cleanup_nftables_wan
|
||||
|
||||
[ "$mode" = "transparent" ] || return 0
|
||||
|
||||
# Create chains (ignore "already exists" errors)
|
||||
@ -221,10 +225,60 @@ setup_iptables() {
|
||||
iptables -t filter -A OUTPUT -j TOR_SHIELD
|
||||
fi
|
||||
|
||||
# Server mode: Allow WAN inbound traffic (for published services)
|
||||
if [ "$wan_input_allow" = "1" ]; then
|
||||
setup_nftables_wan_allow
|
||||
fi
|
||||
|
||||
# LAN client Tor routing via PREROUTING
|
||||
setup_lan_proxy
|
||||
}
|
||||
|
||||
# nftables helpers for server mode WAN access
|
||||
cleanup_nftables_wan() {
|
||||
# Remove any previous tor-shield nftables rules
|
||||
if command -v nft >/dev/null 2>&1; then
|
||||
nft delete chain inet fw4 tor_shield_wan_in 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
setup_nftables_wan_allow() {
|
||||
# Server mode: Ensure WAN inbound is allowed
|
||||
# This creates nftables rules to allow inbound connections from WAN
|
||||
# Works with OpenWrt firewall4 (nftables-based)
|
||||
|
||||
if ! command -v nft >/dev/null 2>&1; then
|
||||
echo "Warning: nft command not found, WAN input rules may not apply"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if fw4 table exists (OpenWrt firewall4)
|
||||
if ! nft list table inet fw4 >/dev/null 2>&1; then
|
||||
echo "Note: fw4 table not found, using legacy firewall"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create chain for Tor Shield WAN input if not exists
|
||||
nft add chain inet fw4 tor_shield_wan_in 2>/dev/null || true
|
||||
|
||||
# Allow all established/related (responses to outgoing)
|
||||
nft add rule inet fw4 tor_shield_wan_in ct state established,related accept 2>/dev/null || true
|
||||
|
||||
# Allow new inbound on common service ports (HAProxy, HTTPS, HTTP)
|
||||
# These are the ports typically used for published services
|
||||
nft add rule inet fw4 tor_shield_wan_in tcp dport 80 accept 2>/dev/null || true
|
||||
nft add rule inet fw4 tor_shield_wan_in tcp dport 443 accept 2>/dev/null || true
|
||||
nft add rule inet fw4 tor_shield_wan_in tcp dport 8443 accept 2>/dev/null || true
|
||||
|
||||
# Hook into input_wan chain (before drop)
|
||||
# First remove any existing jump to avoid duplicates
|
||||
nft delete rule inet fw4 input_wan jump tor_shield_wan_in 2>/dev/null || true
|
||||
# Add jump at the beginning of input_wan
|
||||
nft insert rule inet fw4 input_wan jump tor_shield_wan_in 2>/dev/null || true
|
||||
|
||||
echo "Server mode: WAN inbound allowed on ports 80, 443, 8443"
|
||||
}
|
||||
|
||||
add_excluded_ip() {
|
||||
iptables -t nat -A TOR_SHIELD -d "$1" -j RETURN
|
||||
}
|
||||
@ -279,6 +333,9 @@ remove_iptables() {
|
||||
done
|
||||
iptables -t filter -F TOR_SHIELD 2>/dev/null
|
||||
iptables -t filter -X TOR_SHIELD 2>/dev/null
|
||||
|
||||
# Clean up nftables rules for server mode
|
||||
cleanup_nftables_wan
|
||||
}
|
||||
|
||||
start_service() {
|
||||
|
||||
@ -123,14 +123,16 @@ cmd_status() {
|
||||
fi
|
||||
|
||||
# Show config details
|
||||
local dns_over_tor kill_switch
|
||||
local dns_over_tor kill_switch wan_input_allow
|
||||
config_get dns_over_tor main dns_over_tor '1'
|
||||
config_get kill_switch main kill_switch '1'
|
||||
config_get wan_input_allow main wan_input_allow '0'
|
||||
|
||||
echo ""
|
||||
echo "Configuration:"
|
||||
echo " DNS over Tor: $([ "$dns_over_tor" = "1" ] && echo "Yes" || echo "No")"
|
||||
echo " Kill Switch: $([ "$kill_switch" = "1" ] && echo "Yes" || echo "No")"
|
||||
echo " WAN Input Allow: $([ "$wan_input_allow" = "1" ] && echo "Yes (Server Mode)" || echo "No")"
|
||||
|
||||
# Bridge status
|
||||
local bridges_enabled
|
||||
@ -151,18 +153,20 @@ cmd_enable() {
|
||||
# Load preset configuration
|
||||
config_load "$CONFIG"
|
||||
|
||||
local preset_mode preset_dns preset_kill preset_bridges preset_lan_proxy
|
||||
local preset_mode preset_dns preset_kill preset_bridges preset_lan_proxy preset_wan_input
|
||||
config_get preset_mode "$preset" mode 'transparent'
|
||||
config_get preset_dns "$preset" dns_over_tor '1'
|
||||
config_get preset_kill "$preset" kill_switch '1'
|
||||
config_get preset_bridges "$preset" use_bridges '0'
|
||||
config_get preset_lan_proxy "$preset" lan_proxy '0'
|
||||
config_get preset_wan_input "$preset" wan_input_allow '0'
|
||||
|
||||
# Apply preset settings
|
||||
uci set tor-shield.main.enabled='1'
|
||||
uci set tor-shield.main.mode="$preset_mode"
|
||||
uci set tor-shield.main.dns_over_tor="$preset_dns"
|
||||
uci set tor-shield.main.kill_switch="$preset_kill"
|
||||
uci set tor-shield.main.wan_input_allow="$preset_wan_input"
|
||||
uci set tor-shield.trans.lan_proxy="$preset_lan_proxy"
|
||||
|
||||
if [ "$preset_bridges" = "1" ]; then
|
||||
|
||||
Loading…
Reference in New Issue
Block a user