Skip to content

Instantly share code, notes, and snippets.

@diegslva
Last active September 9, 2025 13:54
Show Gist options
  • Select an option

  • Save diegslva/3250d8da91f65de91c0d2958d971eea7 to your computer and use it in GitHub Desktop.

Select an option

Save diegslva/3250d8da91f65de91c0d2958d971eea7 to your computer and use it in GitHub Desktop.
#!/bin/bash
# ═══════════════════════════════════════════════════════════════════════════════
# guistorm-manager.sh - Gerenciador AutomΓ‘tico de SubdomΓ­nios
# Author: Diego L.S. - github.com/dieglsva
# Data: 16/01/2025
# DescriΓ§Γ£o: Adiciona, remove e gerencia subdomΓ­nios no Caddy + Go Apps
# ═══════════════════════════════════════════════════════════════════════════════
set -e
# ═══════════════════════════════════════════════════════════════════════════════
# CONFIGURAÇÕES GLOBAIS
# ═══════════════════════════════════════════════════════════════════════════════
readonly CADDY_CONFIG="/etc/caddy/Caddyfile"
readonly CADDY_BACKUP_DIR="/etc/caddy/backups"
readonly APPS_DIR="/opt/apps"
readonly SYSTEMD_DIR="/etc/systemd/system"
readonly LOG_DIR="/var/log/guistorm"
readonly DOMAIN_BASE="guistorm.io"
readonly EMAIL="dieg.slva@gmail.com"
# Cores para output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m'
# PrΓ³xima porta disponΓ­vel (incrementa automaticamente)
PORT_FILE="/etc/caddy/.next_port"
[[ ! -f "$PORT_FILE" ]] && echo "8090" > "$PORT_FILE"
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÕES DE UTILIDADE
# ═══════════════════════════════════════════════════════════════════════════════
log_info() { echo -e "${GREEN}[βœ“]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[β†’]${NC} $1"; }
log_error() { echo -e "${RED}[βœ—]${NC} $1"; }
log_blue() { echo -e "${BLUE}[β—†]${NC} $1"; }
log_cyan() { echo -e "${CYAN}[β˜…]${NC} $1"; }
show_header() {
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN} GUISTORM.IO - GERENCIADOR DE SUBDOMÍNIOS${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
echo ""
}
# Verificar se estΓ‘ rodando como root
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "Este script precisa ser executado como root!"
exit 1
fi
}
# Backup do Caddyfile
backup_caddy() {
mkdir -p "$CADDY_BACKUP_DIR"
local backup_file="$CADDY_BACKUP_DIR/Caddyfile.$(date +%Y%m%d_%H%M%S).bak"
cp "$CADDY_CONFIG" "$backup_file"
log_info "Backup criado: $backup_file"
}
# Validar nome do subdomΓ­nio
validate_subdomain() {
local subdomain=$1
if [[ ! "$subdomain" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]]; then
log_error "Nome de subdomΓ­nio invΓ‘lido! Use apenas: a-z, 0-9 e hΓ­fens"
return 1
fi
return 0
}
# Obter prΓ³xima porta disponΓ­vel
get_next_port() {
local current_port=$(cat "$PORT_FILE")
echo "$current_port"
echo $((current_port + 1)) > "$PORT_FILE"
}
# ═══════════════════════════════════════════════════════════════════════════════
# INICIALIZAR CADDYFILE COM SNIPPETS (se necessΓ‘rio)
# ═══════════════════════════════════════════════════════════════════════════════
init_caddy_snippets() {
if ! grep -q "(security_headers)" "$CADDY_CONFIG" 2>/dev/null; then
log_warn "Inicializando snippets no Caddyfile..."
# Criar backup do original
backup_caddy
# Adicionar snippets no inΓ­cio do arquivo
cat > "$CADDY_CONFIG.tmp" << 'EOF'
# ═══════════════════════════════════════════════════════════════════════════════
# CONFIGURAÇÃO GLOBAL - GUISTORM.IO
# Gerenciado por: guistorm-manager.sh
# ═══════════════════════════════════════════════════════════════════════════════
{
email dieg.slva@gmail.com
servers {
protocols h1 h2 h3
strict_sni_host on
}
}
# ═══════════════════════════════════════════════════════════════════════════════
# SNIPPETS REUTILIZÁVEIS
# ═══════════════════════════════════════════════════════════════════════════════
# Headers de seguranΓ§a padrΓ£o
(security_headers) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
-X-Powered-By
}
}
# CompressΓ£o otimizada
(compression) {
encode {
gzip 9
zstd
match {
header Content-Type text/*
header Content-Type application/json*
header Content-Type application/javascript*
header Content-Type application/xml*
}
}
}
# Rate limiting padrΓ£o
(rate_limiting) {
rate_limit {
zone dynamic {
key {remote_host}
events 100
window 60s
}
}
}
# Logging padrΓ£o
(logging) {
log {
output file /var/log/caddy/{args.0}.log {
roll_size 50mb
roll_keep 5
roll_keep_for 720h
}
format json
}
}
# Proxy backend padrΓ£o
(backend_proxy) {
reverse_proxy localhost:{args.0} {
health_uri /health
health_interval 30s
health_timeout 5s
health_status 200
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-Host {host}
transport http {
dial_timeout 30s
read_timeout 90s
write_timeout 90s
}
}
}
# WebSocket support
(websocket) {
@websocket {
header Connection *Upgrade*
header Upgrade websocket
}
handle @websocket {
reverse_proxy localhost:{args.0} {
header_up Upgrade {http.request.header.Upgrade}
header_up Connection "upgrade"
}
}
}
# ═══════════════════════════════════════════════════════════════════════════════
# DOMÍNIOS CONFIGURADOS
# ═══════════════════════════════════════════════════════════════════════════════
EOF
# Adicionar configuraΓ§Γ΅es existentes
if [[ -f "$CADDY_CONFIG" ]]; then
# Extrair apenas as configuraΓ§Γ΅es de domΓ­nio (sem os headers globais)
grep -A 1000 "^[a-z].*\.guistorm\.io {" "$CADDY_CONFIG" >> "$CADDY_CONFIG.tmp" 2>/dev/null || true
fi
mv "$CADDY_CONFIG.tmp" "$CADDY_CONFIG"
log_info "Snippets inicializados com sucesso!"
fi
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: ADICIONAR SUBDOMÍNIO
# ═══════════════════════════════════════════════════════════════════════════════
add_subdomain() {
local subdomain=$1
local port=${2:-$(get_next_port)}
local type=${3:-"backend"} # backend, websocket, static
# Validar entrada
validate_subdomain "$subdomain" || return 1
# Verificar se jΓ‘ existe
if grep -q "^$subdomain\.$DOMAIN_BASE {" "$CADDY_CONFIG" 2>/dev/null; then
log_error "SubdomΓ­nio $subdomain.$DOMAIN_BASE jΓ‘ existe!"
return 1
fi
log_info "Adicionando subdomΓ­nio: $subdomain.$DOMAIN_BASE (porta: $port)"
# Backup antes de modificar
backup_caddy
# Adicionar configuraΓ§Γ£o baseada no tipo
case "$type" in
"backend")
cat >> "$CADDY_CONFIG" << EOF
# $subdomain.$DOMAIN_BASE - Backend API
# Adicionado em: $(date '+%Y-%m-%d %H:%M:%S')
$subdomain.$DOMAIN_BASE {
import security_headers
import compression
import rate_limiting
import logging $subdomain
import backend_proxy $port
}
EOF
;;
"websocket")
cat >> "$CADDY_CONFIG" << EOF
# $subdomain.$DOMAIN_BASE - WebSocket Service
# Adicionado em: $(date '+%Y-%m-%d %H:%M:%S')
$subdomain.$DOMAIN_BASE {
import security_headers
import compression
import rate_limiting
import logging $subdomain
import websocket $port
import backend_proxy $port
}
EOF
;;
"static")
# Criar diretΓ³rio para arquivos estΓ‘ticos
local static_dir="/var/www/$subdomain"
mkdir -p "$static_dir"
chown -R www-data:www-data "$static_dir"
cat >> "$CADDY_CONFIG" << EOF
# $subdomain.$DOMAIN_BASE - Static Website
# Adicionado em: $(date '+%Y-%m-%d %H:%M:%S')
$subdomain.$DOMAIN_BASE {
import security_headers
import compression
import logging $subdomain
root * /var/www/$subdomain
try_files {path} /index.html
file_server
}
EOF
# Criar index.html de exemplo
cat > "$static_dir/index.html" << EOF
<!DOCTYPE html>
<html>
<head>
<title>$subdomain.$DOMAIN_BASE</title>
<style>
body {
font-family: -apple-system, system-ui, sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
margin: 0;
}
.container {
text-align: center;
padding: 40px;
}
h1 { font-size: 3em; margin-bottom: 10px; }
p { font-size: 1.2em; opacity: 0.9; }
</style>
</head>
<body>
<div class="container">
<h1>πŸš€ $subdomain.$DOMAIN_BASE</h1>
<p>Site configurado com sucesso!</p>
<p><small>Criado em: $(date '+%Y-%m-%d %H:%M:%S')</small></p>
</div>
</body>
</html>
EOF
;;
esac
# Criar aplicaΓ§Γ£o Go se for backend/websocket
if [[ "$type" != "static" ]]; then
create_go_app "$subdomain" "$port" "$type"
fi
# Validar e recarregar Caddy
if caddy validate --config "$CADDY_CONFIG" &>/dev/null; then
systemctl reload caddy
log_info "Caddy recarregado com sucesso!"
echo ""
log_cyan "SubdomΓ­nio adicionado com sucesso!"
echo -e "${GREEN}URL:${NC} https://$subdomain.$DOMAIN_BASE"
echo -e "${GREEN}Porta:${NC} $port"
echo -e "${GREEN}Tipo:${NC} $type"
if [[ "$type" != "static" ]]; then
echo -e "${GREEN}ServiΓ§o:${NC} $subdomain-app.service"
echo ""
echo -e "${YELLOW}Comandos ΓΊteis:${NC}"
echo " β€’ Status: systemctl status $subdomain-app"
echo " β€’ Logs: journalctl -u $subdomain-app -f"
echo " β€’ Editar: nano $APPS_DIR/$subdomain/main.go"
else
echo -e "${GREEN}DiretΓ³rio:${NC} /var/www/$subdomain"
fi
else
log_error "Erro na configuraΓ§Γ£o do Caddy! Revertendo..."
cp "$CADDY_BACKUP_DIR/$(ls -t $CADDY_BACKUP_DIR | head -1)" "$CADDY_CONFIG"
return 1
fi
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: CRIAR APLICAÇÃO GO
# ═══════════════════════════════════════════════════════════════════════════════
create_go_app() {
local subdomain=$1
local port=$2
local type=$3
local app_dir="$APPS_DIR/$subdomain"
log_info "Criando aplicaΓ§Γ£o Go para $subdomain..."
mkdir -p "$app_dir"
# Criar aplicaΓ§Γ£o baseada no tipo
if [[ "$type" == "websocket" ]]; then
cat > "$app_dir/main.go" << EOF
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // Ajustar em produΓ§Γ£o
},
}
type Message struct {
Type string \`json:"type"\`
Content string \`json:"content"\`
Timestamp time.Time \`json:"timestamp"\`
}
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Erro no upgrade: %v", err)
return
}
defer conn.Close()
log.Printf("Cliente conectado: %s", r.RemoteAddr)
// Echo server exemplo
for {
var msg Message
err := conn.ReadJSON(&msg)
if err != nil {
log.Printf("Cliente desconectado: %v", err)
break
}
msg.Timestamp = time.Now()
msg.Type = "echo"
if err := conn.WriteJSON(msg); err != nil {
log.Printf("Erro ao enviar: %v", err)
break
}
}
}
func handleHome(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, \`
<!DOCTYPE html>
<html>
<head>
<title>$subdomain WebSocket</title>
</head>
<body>
<h1>WebSocket Service: $subdomain.$DOMAIN_BASE</h1>
<p>Conecte via: wss://$subdomain.$DOMAIN_BASE/ws</p>
</body>
</html>
\`)
}
func handleHealth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"status": "healthy",
"service": "$subdomain",
"type": "websocket",
})
}
func main() {
http.HandleFunc("/", handleHome)
http.HandleFunc("/ws", handleWebSocket)
http.HandleFunc("/health", handleHealth)
log.Printf("[βœ“] $subdomain WebSocket rodando na porta $port")
log.Fatal(http.ListenAndServe(":$port", nil))
}
EOF
else
# Backend API padrΓ£o
cat > "$app_dir/main.go" << EOF
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"runtime"
"time"
)
type Response struct {
Service string \`json:"service"\`
Subdomain string \`json:"subdomain"\`
Status string \`json:"status"\`
Timestamp time.Time \`json:"timestamp"\`
Version string \`json:"version"\`
GoVersion string \`json:"go_version"\`
Uptime string \`json:"uptime"\`
}
var startTime = time.Now()
func handleRoot(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
response := Response{
Service: "$subdomain-service",
Subdomain: "$subdomain.$DOMAIN_BASE",
Status: "operational",
Timestamp: time.Now(),
Version: "1.0.0",
GoVersion: runtime.Version(),
Uptime: time.Since(startTime).String(),
}
json.NewEncoder(w).Encode(response)
}
func handleHealth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "healthy",
"uptime": time.Since(startTime).Seconds(),
})
}
func handleInfo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, \`
<!DOCTYPE html>
<html>
<head>
<title>$subdomain.$DOMAIN_BASE</title>
<style>
body {
font-family: -apple-system, system-ui, sans-serif;
max-width: 800px;
margin: 100px auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
h1 { font-size: 2.5em; }
.info {
background: rgba(255,255,255,0.1);
padding: 20px;
border-radius: 10px;
margin-top: 20px;
}
code {
background: rgba(0,0,0,0.3);
padding: 2px 8px;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>πŸš€ $subdomain Service</h1>
<div class="info">
<p><strong>DomΓ­nio:</strong> $subdomain.$DOMAIN_BASE</p>
<p><strong>Status:</strong> βœ… Operacional</p>
<p><strong>API Endpoint:</strong> <code>GET /api</code></p>
<p><strong>Health Check:</strong> <code>GET /health</code></p>
<p><strong>Go Version:</strong> %s</p>
<p><strong>Uptime:</strong> %s</p>
</div>
</body>
</html>
\`, runtime.Version(), time.Since(startTime))
}
func main() {
// Rotas
http.HandleFunc("/", handleRoot)
http.HandleFunc("/api", handleRoot)
http.HandleFunc("/health", handleHealth)
http.HandleFunc("/info", handleInfo)
// Middleware de logging
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
http.DefaultServeMux.ServeHTTP(w, r)
log.Printf("%s %s %s %v", r.Method, r.URL.Path, r.RemoteAddr, time.Since(start))
})
log.Printf("[βœ“] $subdomain service rodando na porta $port")
log.Printf("[β†’] URL: https://$subdomain.$DOMAIN_BASE")
log.Fatal(http.ListenAndServe(":$port", handler))
}
EOF
fi
# Compilar aplicaΓ§Γ£o
cd "$app_dir"
go mod init "$subdomain" 2>/dev/null || true
if [[ "$type" == "websocket" ]]; then
go get github.com/gorilla/websocket 2>/dev/null || true
fi
go build -o app main.go
# Criar systemd service
cat > "$SYSTEMD_DIR/$subdomain-app.service" << EOF
[Unit]
Description=$subdomain Service - $type
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=$app_dir
ExecStart=$app_dir/app
Restart=always
RestartSec=10
# Limites
LimitNOFILE=65536
LimitNPROC=4096
# VariΓ‘veis de ambiente
Environment="GO_ENV=production"
Environment="SERVICE_NAME=$subdomain"
Environment="PORT=$port"
[Install]
WantedBy=multi-user.target
EOF
# Iniciar serviΓ§o
systemctl daemon-reload
systemctl enable "$subdomain-app.service"
systemctl start "$subdomain-app.service"
log_info "AplicaΓ§Γ£o Go criada e iniciada!"
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: REMOVER SUBDOMÍNIO
# ═══════════════════════════════════════════════════════════════════════════════
remove_subdomain() {
local subdomain=$1
if [[ -z "$subdomain" ]]; then
log_error "Por favor, especifique o subdomΓ­nio a remover"
return 1
fi
# Verificar se existe
if ! grep -q "^$subdomain\.$DOMAIN_BASE {" "$CADDY_CONFIG"; then
log_error "SubdomΓ­nio $subdomain.$DOMAIN_BASE nΓ£o encontrado!"
return 1
fi
log_warn "Removendo subdomΓ­nio: $subdomain.$DOMAIN_BASE"
# Confirmar
echo -e "${YELLOW}⚠️ Esta ação irÑ:${NC}"
echo " β€’ Remover configuraΓ§Γ£o do Caddy"
echo " β€’ Parar e remover o serviΓ§o (se existir)"
echo " β€’ Remover aplicaΓ§Γ£o em $APPS_DIR/$subdomain"
echo " β€’ Remover diretΓ³rio web em /var/www/$subdomain (se existir)"
echo ""
read -p "Confirmar remoΓ§Γ£o? (s/N): " confirm
if [[ "$confirm" != "s" ]]; then
log_info "OperaΓ§Γ£o cancelada"
return 0
fi
# Backup antes de remover
backup_caddy
# Remover do Caddyfile (remove o bloco inteiro)
sed -i "/^# $subdomain\.$DOMAIN_BASE/,/^$/d" "$CADDY_CONFIG"
sed -i "/^$subdomain\.$DOMAIN_BASE {/,/^}/d" "$CADDY_CONFIG"
# Parar e remover serviΓ§o se existir
if systemctl list-units --all | grep -q "$subdomain-app.service"; then
systemctl stop "$subdomain-app.service" 2>/dev/null || true
systemctl disable "$subdomain-app.service" 2>/dev/null || true
rm -f "$SYSTEMD_DIR/$subdomain-app.service"
systemctl daemon-reload
log_info "ServiΓ§o removido"
fi
# Remover aplicaΓ§Γ£o
if [[ -d "$APPS_DIR/$subdomain" ]]; then
rm -rf "$APPS_DIR/$subdomain"
log_info "AplicaΓ§Γ£o removida"
fi
# Remover diretΓ³rio web se existir
if [[ -d "/var/www/$subdomain" ]]; then
rm -rf "/var/www/$subdomain"
log_info "DiretΓ³rio web removido"
fi
# Recarregar Caddy
systemctl reload caddy
log_info "SubdomΓ­nio $subdomain.$DOMAIN_BASE removido com sucesso!"
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: LISTAR SUBDOMÍNIOS
# ═══════════════════════════════════════════════════════════════════════════════
list_subdomains() {
log_cyan "SubdomΓ­nios configurados:"
echo ""
# Extrair subdomΓ­nios do Caddyfile
local subdomains=$(grep -E "^[a-z0-9-]+\.guistorm\.io {" "$CADDY_CONFIG" | sed 's/ {//' | sort)
if [[ -z "$subdomains" ]]; then
log_warn "Nenhum subdomΓ­nio configurado"
return
fi
printf "%-40s %-10s %-15s\n" "DOMÍNIO" "PORTA" "STATUS"
printf "%-40s %-10s %-15s\n" "-------" "-----" "------"
while IFS= read -r domain; do
# Extrair porta do reverse_proxy
local port=$(grep -A 10 "^$domain {" "$CADDY_CONFIG" | grep -oP 'localhost:\K\d+' | head -1)
# Verificar status do serviΓ§o
local service_name=$(echo "$domain" | cut -d'.' -f1)
local status="N/A"
if [[ -d "/var/www/$service_name" ]]; then
status="Static"
elif systemctl is-active "$service_name-app" &>/dev/null; then
status="βœ… Running"
elif systemctl list-units --all | grep -q "$service_name-app.service"; then
status="❌ Stopped"
else
status="πŸ“ No service"
fi
printf "%-40s %-10s %-15s\n" "$domain" "${port:-static}" "$status"
done <<< "$subdomains"
echo ""
# Mostrar estatΓ­sticas
local total=$(echo "$subdomains" | wc -l)
local running=$(systemctl list-units --state=running | grep -c "app.service" || echo 0)
echo -e "${CYAN}──────────────────────────────────────────────────────────${NC}"
echo -e "Total: $total | Rodando: $running | PrΓ³xima porta: $(cat $PORT_FILE)"
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: STATUS DETALHADO
# ═══════════════════════════════════════════════════════════════════════════════
show_status() {
local subdomain=$1
if [[ -z "$subdomain" ]]; then
# Status geral
log_cyan "Status Geral do Sistema"
echo ""
echo -e "${YELLOW}[β†’] Caddy:${NC}"
systemctl status caddy --no-pager | head -5
echo ""
echo -e "${YELLOW}[β†’] Certificados SSL:${NC}"
caddy list-certificates 2>/dev/null | grep "guistorm.io" || echo " Nenhum certificado ativo"
echo ""
echo -e "${YELLOW}[β†’] Recursos:${NC}"
free -h | grep Mem | awk '{print " MemΓ³ria: " $3 " / " $2}'
df -h / | tail -1 | awk '{print " Disco: " $3 " / " $2}'
echo ""
list_subdomains
else
# Status especΓ­fico
if ! grep -q "^$subdomain\.$DOMAIN_BASE {" "$CADDY_CONFIG"; then
log_error "SubdomΓ­nio $subdomain.$DOMAIN_BASE nΓ£o encontrado!"
return 1
fi
log_cyan "Status: $subdomain.$DOMAIN_BASE"
echo ""
# ConfiguraΓ§Γ£o Caddy
echo -e "${YELLOW}[β†’] ConfiguraΓ§Γ£o Caddy:${NC}"
grep -A 8 "^$subdomain\.$DOMAIN_BASE {" "$CADDY_CONFIG"
# ServiΓ§o
if systemctl list-units --all | grep -q "$subdomain-app.service"; then
echo ""
echo -e "${YELLOW}[β†’] ServiΓ§o:${NC}"
systemctl status "$subdomain-app.service" --no-pager | head -10
echo ""
echo -e "${YELLOW}[β†’] Últimos logs:${NC}"
journalctl -u "$subdomain-app.service" -n 5 --no-pager
fi
# Teste de conectividade
echo ""
echo -e "${YELLOW}[β†’] Teste de conectividade:${NC}"
local port=$(grep -A 10 "^$subdomain\.$DOMAIN_BASE {" "$CADDY_CONFIG" | grep -oP 'localhost:\K\d+' | head -1)
if [[ -n "$port" ]]; then
if curl -s "http://localhost:$port/health" > /dev/null 2>&1; then
echo -e " ${GREEN}βœ… Health check OK${NC}"
else
echo -e " ${RED}❌ Health check falhou${NC}"
fi
fi
fi
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: EDITAR APLICAÇÃO
# ═══════════════════════════════════════════════════════════════════════════════
edit_app() {
local subdomain=$1
if [[ -z "$subdomain" ]]; then
log_error "Por favor, especifique o subdomΓ­nio"
return 1
fi
local app_file="$APPS_DIR/$subdomain/main.go"
if [[ ! -f "$app_file" ]]; then
log_error "AplicaΓ§Γ£o nΓ£o encontrada: $app_file"
return 1
fi
log_info "Editando: $app_file"
nano "$app_file"
echo ""
read -p "Recompilar e reiniciar o serviΓ§o? (s/N): " confirm
if [[ "$confirm" == "s" ]]; then
cd "$APPS_DIR/$subdomain"
go build -o app main.go
systemctl restart "$subdomain-app.service"
log_info "ServiΓ§o reiniciado!"
fi
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: MENU INTERATIVO
# ═══════════════════════════════════════════════════════════════════════════════
show_menu() {
clear
show_header
echo -e "${CYAN}Escolha uma opΓ§Γ£o:${NC}"
echo ""
echo " 1) πŸ“ Adicionar novo subdomΓ­nio"
echo " 2) πŸ—‘οΈ Remover subdomΓ­nio"
echo " 3) πŸ“‹ Listar subdomΓ­nios"
echo " 4) πŸ“Š Status detalhado"
echo " 5) ✏️ Editar aplicação"
echo " 6) πŸ”„ Recarregar Caddy"
echo " 7) πŸ“– Ver logs do Caddy"
echo " 8) πŸ”§ Validar configuraΓ§Γ£o"
echo " 9) πŸ“¦ Backup completo"
echo " 0) πŸšͺ Sair"
echo ""
echo -e "${CYAN}─────────────────────────────────────────────────${NC}"
read -p "OpΓ§Γ£o: " choice
case $choice in
1)
read -p "Nome do subdomΓ­nio (ex: api): " subdomain
echo "Tipo de serviΓ§o:"
echo " 1) Backend API (padrΓ£o)"
echo " 2) WebSocket"
echo " 3) Site estΓ‘tico"
read -p "Escolha (1-3): " type_choice
case $type_choice in
2) type="websocket" ;;
3) type="static" ;;
*) type="backend" ;;
esac
add_subdomain "$subdomain" "" "$type"
;;
2)
list_subdomains
echo ""
read -p "Nome do subdomΓ­nio para remover: " subdomain
remove_subdomain "$subdomain"
;;
3)
list_subdomains
;;
4)
read -p "SubdomΓ­nio especΓ­fico (ou Enter para geral): " subdomain
show_status "$subdomain"
;;
5)
list_subdomains
echo ""
read -p "Nome do subdomΓ­nio para editar: " subdomain
edit_app "$subdomain"
;;
6)
systemctl reload caddy
log_info "Caddy recarregado!"
;;
7)
journalctl -u caddy -f
;;
8)
if caddy validate --config "$CADDY_CONFIG"; then
log_info "ConfiguraΓ§Γ£o vΓ‘lida!"
else
log_error "ConfiguraΓ§Γ£o invΓ‘lida!"
fi
;;
9)
backup_caddy
tar -czf "/root/guistorm-backup-$(date +%Y%m%d_%H%M%S).tar.gz" \
"$CADDY_CONFIG" "$APPS_DIR" "$SYSTEMD_DIR"/*-app.service 2>/dev/null
log_info "Backup completo criado em /root/"
;;
0)
echo ""
log_info "AtΓ© logo!"
exit 0
;;
*)
log_error "OpΓ§Γ£o invΓ‘lida!"
;;
esac
echo ""
read -p "Pressione Enter para continuar..."
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: MODO CLI (Linha de Comando)
# ═══════════════════════════════════════════════════════════════════════════════
handle_cli() {
case "$1" in
add)
shift
add_subdomain "$@"
;;
remove|rm)
shift
remove_subdomain "$@"
;;
list|ls)
list_subdomains
;;
status)
shift
show_status "$@"
;;
edit)
shift
edit_app "$@"
;;
reload)
systemctl reload caddy
log_info "Caddy recarregado!"
;;
validate)
caddy validate --config "$CADDY_CONFIG"
;;
help|--help|-h)
show_help
;;
*)
log_error "Comando invΓ‘lido: $1"
show_help
exit 1
;;
esac
}
# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO: AJUDA
# ═══════════════════════════════════════════════════════════════════════════════
show_help() {
echo "Uso: $0 [comando] [opΓ§Γ΅es]"
echo ""
echo "Comandos:"
echo " add <nome> [porta] [tipo] - Adicionar subdomΓ­nio"
echo " remove <nome> - Remover subdomΓ­nio"
echo " list - Listar subdomΓ­nios"
echo " status [nome] - Mostrar status"
echo " edit <nome> - Editar aplicaΓ§Γ£o"
echo " reload - Recarregar Caddy"
echo " validate - Validar configuraΓ§Γ£o"
echo " help - Mostrar esta ajuda"
echo ""
echo "Tipos de serviΓ§o: backend (padrΓ£o), websocket, static"
echo ""
echo "Exemplos:"
echo " $0 add api # Adiciona api.guistorm.io"
echo " $0 add chat 8090 websocket # Adiciona chat com WebSocket"
echo " $0 add docs 0 static # Adiciona site estΓ‘tico"
echo " $0 remove api # Remove api.guistorm.io"
echo " $0 list # Lista todos os subdomΓ­nios"
}
# ═══════════════════════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════════════════════
main() {
# Verificar root
check_root
# Inicializar snippets se necessΓ‘rio
init_caddy_snippets
# Criar diretΓ³rios necessΓ‘rios
mkdir -p "$APPS_DIR" "$LOG_DIR" "$CADDY_BACKUP_DIR"
# Modo de execuΓ§Γ£o
if [[ $# -eq 0 ]]; then
# Menu interativo
while true; do
show_menu
done
else
# Modo CLI
handle_cli "$@"
fi
}
# Executar
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment