235 lines
5.2 KiB
Go
235 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"envguard/internal/models"
|
|
)
|
|
|
|
var (
|
|
services = []models.Service{}
|
|
history = []models.HistoryEntry{}
|
|
mu sync.Mutex
|
|
dataFile = "services.json"
|
|
historyFile = "history.json"
|
|
apiToken = "ENVGUARD_SECRET_TOKEN"
|
|
)
|
|
|
|
func main() {
|
|
if err := loadServices(); err != nil {
|
|
log.Printf("⚠️ No se pudo cargar services.json, usando defaults: %v", err)
|
|
services = []models.Service{
|
|
{Name: "auth-service"},
|
|
{Name: "payments-api"},
|
|
{Name: "user-db"},
|
|
{Name: "notifications"},
|
|
{Name: "front-web"},
|
|
}
|
|
} else {
|
|
fmt.Printf("✅ %d servicios cargados desde disco\n", len(services))
|
|
}
|
|
|
|
if err := loadHistory(); err != nil {
|
|
log.Printf("⚠️ No se pudo cargar history.json: %v", err)
|
|
} else {
|
|
fmt.Printf("📜 %d entradas de historial cargadas\n", len(history))
|
|
}
|
|
|
|
http.HandleFunc("/services", authMiddleware(handleServices))
|
|
http.HandleFunc("/history", authMiddleware(handleHistory))
|
|
http.HandleFunc("/lock", authMiddleware(handleLock))
|
|
http.HandleFunc("/unlock", authMiddleware(handleUnlock))
|
|
|
|
fmt.Println("🚦 Lock Server corriendo en :8080")
|
|
if err := http.ListenAndServe(":8080", nil); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func loadServices() error {
|
|
file, err := os.Open(dataFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
return json.NewDecoder(file).Decode(&services)
|
|
}
|
|
|
|
func saveServices() error {
|
|
file, err := os.Create(dataFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
encoder := json.NewEncoder(file)
|
|
encoder.SetIndent("", " ")
|
|
return encoder.Encode(services)
|
|
}
|
|
|
|
func loadHistory() error {
|
|
file, err := os.Open(historyFile)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
return json.NewDecoder(file).Decode(&history)
|
|
}
|
|
|
|
func saveHistory() error {
|
|
file, err := os.Create(historyFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
encoder := json.NewEncoder(file)
|
|
encoder.SetIndent("", " ")
|
|
return encoder.Encode(history)
|
|
}
|
|
|
|
func handleServices(w http.ResponseWriter, r *http.Request) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(services)
|
|
}
|
|
|
|
func handleLock(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req models.LockRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
for i := range services {
|
|
if services[i].Name == req.ServiceName {
|
|
if services[i].IsLocked {
|
|
http.Error(w, "Service already locked", http.StatusConflict)
|
|
return
|
|
}
|
|
services[i].IsLocked = true
|
|
services[i].LockedBy = req.User
|
|
services[i].LockedAt = time.Now()
|
|
|
|
if err := saveServices(); err != nil {
|
|
log.Printf("Error guardando estado: %v", err)
|
|
}
|
|
|
|
// Add to history
|
|
history = append(history, models.HistoryEntry{
|
|
ServiceName: req.ServiceName,
|
|
Action: "LOCK",
|
|
User: req.User,
|
|
Timestamp: time.Now(),
|
|
})
|
|
saveHistory()
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
}
|
|
|
|
http.Error(w, "Service not found", http.StatusNotFound)
|
|
}
|
|
|
|
func handleUnlock(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req models.UnlockRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
for i := range services {
|
|
if services[i].Name == req.ServiceName {
|
|
if !services[i].IsLocked {
|
|
http.Error(w, "Service is not locked", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if services[i].LockedBy != req.User {
|
|
http.Error(w, "You cannot unlock a service locked by someone else", http.StatusForbidden)
|
|
return
|
|
}
|
|
services[i].IsLocked = false
|
|
services[i].LockedBy = ""
|
|
services[i].LockedAt = time.Time{}
|
|
|
|
if err := saveServices(); err != nil {
|
|
log.Printf("Error guardando estado: %v", err)
|
|
}
|
|
|
|
// Add to history
|
|
history = append(history, models.HistoryEntry{
|
|
ServiceName: req.ServiceName,
|
|
Action: "UNLOCK",
|
|
User: req.User,
|
|
Timestamp: time.Now(),
|
|
})
|
|
saveHistory()
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
}
|
|
|
|
http.Error(w, "Service not found", http.StatusNotFound)
|
|
}
|
|
|
|
func handleHistory(w http.ResponseWriter, r *http.Request) {
|
|
serviceName := r.URL.Query().Get("service")
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
var result []models.HistoryEntry
|
|
for _, h := range history {
|
|
if serviceName == "" || h.ServiceName == serviceName {
|
|
result = append(result, h)
|
|
}
|
|
}
|
|
|
|
// Reverse order (newest first)
|
|
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
|
|
result[i], result[j] = result[j], result[i]
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(result)
|
|
}
|
|
|
|
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
token := r.Header.Get("X-API-Key")
|
|
if token != apiToken {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
next(w, r)
|
|
}
|
|
}
|