169 lines
4.6 KiB
Go
169 lines
4.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/orohi/vpn-panel/internal/db"
|
|
"github.com/orohi/vpn-panel/internal/models"
|
|
"github.com/orohi/vpn-panel/internal/nodeclient"
|
|
)
|
|
|
|
func (s *Server) syncNodeConfig(n *models.Node) error {
|
|
cfg, err := db.NodeProtocolConfigPayload(s.DB, n.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nodeclient.PushConfig(n, cfg)
|
|
}
|
|
|
|
func (s *Server) protocols(w http.ResponseWriter, r *http.Request) {
|
|
protocols, err := db.ListProtocolsDetailed(s.DB)
|
|
if err != nil {
|
|
http.Error(w, "db error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
nodes, _ := db.ListNodes(s.DB)
|
|
s.render(w, "protocols.html", pageData{
|
|
"Title": "Протоколы",
|
|
"UserName": s.Auth.CurrentName(r),
|
|
"Protocols": protocols,
|
|
"Nodes": nodes,
|
|
"Active": "protocols",
|
|
"Flash": r.URL.Query().Get("ok"),
|
|
"Error": r.URL.Query().Get("err"),
|
|
})
|
|
}
|
|
|
|
func (s *Server) toggleProtocol(w http.ResponseWriter, r *http.Request) {
|
|
idStr := mux.Vars(r)["id"]
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
http.Error(w, "invalid id", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := db.ToggleProtocol(s.DB, id); err != nil {
|
|
http.Error(w, "db error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/protocols", http.StatusSeeOther)
|
|
}
|
|
|
|
func (s *Server) nodeProtocolAction(w http.ResponseWriter, r *http.Request) {
|
|
nodeID, err := uuid.Parse(mux.Vars(r)["id"])
|
|
if err != nil {
|
|
http.Error(w, "invalid node id", http.StatusBadRequest)
|
|
return
|
|
}
|
|
protocolID, err := uuid.Parse(mux.Vars(r)["protocolID"])
|
|
if err != nil {
|
|
http.Error(w, "invalid protocol id", http.StatusBadRequest)
|
|
return
|
|
}
|
|
action := r.FormValue("action")
|
|
if action == "" {
|
|
_ = r.ParseForm()
|
|
action = r.FormValue("action")
|
|
}
|
|
|
|
n, err := db.GetNode(s.DB, nodeID)
|
|
if err != nil || n == nil {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
p, err := db.GetProtocol(s.DB, protocolID)
|
|
if err != nil || p == nil {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
list, err := db.ListNodeProtocols(s.DB, nodeID)
|
|
if err != nil {
|
|
http.Error(w, "db error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
var current models.NodeProtocol
|
|
for _, item := range list {
|
|
if item.ProtocolID == protocolID {
|
|
current = item
|
|
break
|
|
}
|
|
}
|
|
|
|
installed, enabled := current.Installed, current.Enabled
|
|
switch action {
|
|
case "install":
|
|
installed, enabled = true, true
|
|
case "uninstall":
|
|
installed, enabled = false, false
|
|
case "enable":
|
|
installed, enabled = true, true
|
|
case "disable":
|
|
enabled = false
|
|
if !installed {
|
|
installed = false
|
|
}
|
|
default:
|
|
http.Redirect(w, r, "/admin/nodes/"+nodeID.String()+"?err=bad_action", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
if err := db.SetNodeProtocolFlags(s.DB, nodeID, protocolID, installed, enabled); err != nil {
|
|
log.Printf("set node protocol: %v", err)
|
|
http.Redirect(w, r, "/admin/nodes/"+nodeID.String()+"?err=proto_save", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
// If panel catalog was off and we install/enable — turn protocol on in catalog.
|
|
if (action == "install" || action == "enable") && !p.Enabled {
|
|
_ = db.SetProtocolEnabled(s.DB, protocolID, true)
|
|
}
|
|
if action == "uninstall" {
|
|
// If nowhere else has it installed, turn catalog off.
|
|
detailed, _ := db.ListProtocolsDetailed(s.DB)
|
|
for _, item := range detailed {
|
|
if item.ID == protocolID && len(item.InstalledOn) == 0 {
|
|
_ = db.SetProtocolEnabled(s.DB, protocolID, false)
|
|
}
|
|
}
|
|
}
|
|
|
|
if n.Status == models.NodeStatusOnline {
|
|
if err := s.syncNodeConfig(n); err != nil {
|
|
http.Redirect(w, r, "/admin/nodes/"+nodeID.String()+"?err=sync_failed", http.StatusSeeOther)
|
|
return
|
|
}
|
|
}
|
|
|
|
http.Redirect(w, r, "/admin/nodes/"+nodeID.String()+"?ok=protocol_"+action, http.StatusSeeOther)
|
|
}
|
|
|
|
func (s *Server) nodeSyncProtocols(w http.ResponseWriter, r *http.Request) {
|
|
nodeID, err := uuid.Parse(mux.Vars(r)["id"])
|
|
if err != nil {
|
|
http.Error(w, "invalid id", http.StatusBadRequest)
|
|
return
|
|
}
|
|
n, err := db.GetNode(s.DB, nodeID)
|
|
if err != nil || n == nil {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
hr, err := nodeclient.Health(n)
|
|
if err != nil {
|
|
_ = db.UpdateNodeStatus(s.DB, nodeID, models.NodeStatusOffline, err.Error())
|
|
http.Redirect(w, r, "/admin/nodes/"+nodeID.String()+"?err=offline", http.StatusSeeOther)
|
|
return
|
|
}
|
|
_ = db.MarkNodeSeen(s.DB, nodeID, hr.Version, models.NodeStatusOnline)
|
|
// Push panel → node (desired state). Do not wipe local DB from empty agent.
|
|
if err := s.syncNodeConfig(n); err != nil {
|
|
http.Redirect(w, r, "/admin/nodes/"+nodeID.String()+"?err=sync_failed", http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/nodes/"+nodeID.String()+"?ok=synced", http.StatusSeeOther)
|
|
}
|