256 lines
6.8 KiB
Go
256 lines
6.8 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"amnezia-share/internal/settings"
|
|
"amnezia-share/internal/share"
|
|
"amnezia-share/internal/web"
|
|
)
|
|
|
|
type serverRow struct {
|
|
ID int
|
|
Title string
|
|
Protocols []string
|
|
Flag string
|
|
Speed string
|
|
Disabled bool
|
|
PanelName string
|
|
PanelHost string
|
|
}
|
|
|
|
type adminServersView struct {
|
|
adminLayout
|
|
Rows []serverRow
|
|
AllProtocols []string
|
|
PanelReachable bool
|
|
PanelErr string
|
|
}
|
|
|
|
// AdminServersGet lists the known panel servers with their editable labels,
|
|
// allowed protocols, flag emoji, speed badge and disabled state
|
|
// (admin/server_labels.php).
|
|
func AdminServersGet(a *web.App) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
ok, errText := flashFromQuery(r)
|
|
view := adminServersView{
|
|
adminLayout: newAdminLayout(a, r, "servers"),
|
|
AllProtocols: share.GuestProtocols,
|
|
}
|
|
view.OK, view.Error = ok, errText
|
|
|
|
labels := a.Settings.ServerLabels(ctx)
|
|
protosMap := a.Settings.JSONMapStringSlice(ctx, settings.KeyProtocolsJSON)
|
|
flagsMap := a.Settings.JSONMapString(ctx, settings.KeyFlagsJSON)
|
|
speedsMap := a.Settings.JSONMapString(ctx, settings.KeySpeedsJSON)
|
|
disabledMap := a.Settings.DisabledServers(ctx)
|
|
|
|
baseURL := a.Settings.PanelURL(ctx)
|
|
token := a.Settings.PanelToken(ctx)
|
|
panelServers := map[int]struct{ Name, Host string }{}
|
|
if baseURL != "" && token != "" {
|
|
list, err := a.Panel.ListServers(ctx, baseURL, token)
|
|
if err != nil {
|
|
view.PanelErr = err.Error()
|
|
} else {
|
|
view.PanelReachable = true
|
|
for _, s := range list {
|
|
panelServers[s.ID] = struct{ Name, Host string }{s.Name, s.Host}
|
|
if _, known := labels[s.ID]; !known {
|
|
labels[s.ID] = s.Name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ids := make([]int, 0, len(labels))
|
|
for id := range labels {
|
|
ids = append(ids, id)
|
|
}
|
|
sort.Ints(ids)
|
|
|
|
for _, id := range ids {
|
|
row := serverRow{
|
|
ID: id,
|
|
Title: labels[id],
|
|
Protocols: protosMap[id],
|
|
Flag: flagsMap[id],
|
|
Speed: speedsMap[id],
|
|
Disabled: disabledMap[id],
|
|
}
|
|
if len(row.Protocols) == 0 {
|
|
row.Protocols = append([]string(nil), share.DefaultServerProtocols...)
|
|
}
|
|
if p, ok := panelServers[id]; ok {
|
|
row.PanelName, row.PanelHost = p.Name, p.Host
|
|
}
|
|
view.Rows = append(view.Rows, row)
|
|
}
|
|
|
|
a.Render(w, r, "page_admin_servers", view)
|
|
}
|
|
}
|
|
|
|
func protocolSelected(list []string, proto string) bool {
|
|
for _, p := range list {
|
|
if p == proto {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func upsertServerLabel(ctx context.Context, a *web.App, id int, title string) error {
|
|
title = strings.TrimSpace(title)
|
|
if title == "" {
|
|
return nil
|
|
}
|
|
_, err := a.Pool.Exec(ctx, `
|
|
INSERT INTO panel_server_labels (panel_server_id, title, updated_at)
|
|
VALUES ($1,$2,NOW())
|
|
ON CONFLICT (panel_server_id) DO UPDATE SET title=EXCLUDED.title, updated_at=NOW()`,
|
|
id, title)
|
|
return err
|
|
}
|
|
|
|
func intMapToJSON[T any](m map[int]T) string {
|
|
out := make(map[string]T, len(m))
|
|
for k, v := range m {
|
|
out[strconv.Itoa(k)] = v
|
|
}
|
|
b, _ := json.Marshal(out)
|
|
return string(b)
|
|
}
|
|
|
|
func intSetToJSON(m map[int]bool) string {
|
|
ids := make([]int, 0, len(m))
|
|
for k, v := range m {
|
|
if v {
|
|
ids = append(ids, k)
|
|
}
|
|
}
|
|
sort.Ints(ids)
|
|
b, _ := json.Marshal(ids)
|
|
return string(b)
|
|
}
|
|
|
|
func saveServerJSONSettings(ctx context.Context, a *web.App, protosMap map[int][]string, flagsMap, speedsMap map[int]string, disabledSet map[int]bool) error {
|
|
if err := a.Settings.Set(ctx, settings.KeyProtocolsJSON, intMapToJSON(protosMap)); err != nil {
|
|
return err
|
|
}
|
|
if err := a.Settings.Set(ctx, settings.KeyFlagsJSON, intMapToJSON(flagsMap)); err != nil {
|
|
return err
|
|
}
|
|
if err := a.Settings.Set(ctx, settings.KeySpeedsJSON, intMapToJSON(speedsMap)); err != nil {
|
|
return err
|
|
}
|
|
return a.Settings.Set(ctx, settings.KeyDisabledServers, intSetToJSON(disabledSet))
|
|
}
|
|
|
|
// AdminServersPost saves the whole server table, adds a new server id, or
|
|
// removes one server's label row.
|
|
func AdminServersPost(a *web.App) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
_ = r.ParseForm()
|
|
var err error
|
|
var okMsg string
|
|
|
|
switch r.FormValue("action") {
|
|
case "save":
|
|
protosMap := a.Settings.JSONMapStringSlice(ctx, settings.KeyProtocolsJSON)
|
|
flagsMap := a.Settings.JSONMapString(ctx, settings.KeyFlagsJSON)
|
|
speedsMap := a.Settings.JSONMapString(ctx, settings.KeySpeedsJSON)
|
|
disabledSet := a.Settings.DisabledServers(ctx)
|
|
|
|
for _, idStr := range r.Form["server_ids"] {
|
|
id, perr := strconv.Atoi(strings.TrimSpace(idStr))
|
|
if perr != nil {
|
|
continue
|
|
}
|
|
title := r.FormValue(fmt.Sprintf("servers[%d][title]", id))
|
|
if uerr := upsertServerLabel(ctx, a, id, title); uerr != nil {
|
|
err = uerr
|
|
}
|
|
protos := r.Form[fmt.Sprintf("servers[%d][protocols][]", id)]
|
|
if len(protos) > 0 {
|
|
protosMap[id] = protos
|
|
} else {
|
|
delete(protosMap, id)
|
|
}
|
|
flag := strings.TrimSpace(r.FormValue(fmt.Sprintf("servers[%d][flag]", id)))
|
|
if flag != "" {
|
|
flagsMap[id] = flag
|
|
} else {
|
|
delete(flagsMap, id)
|
|
}
|
|
speed := strings.TrimSpace(r.FormValue(fmt.Sprintf("servers[%d][speed]", id)))
|
|
if speed != "" {
|
|
speedsMap[id] = speed
|
|
} else {
|
|
delete(speedsMap, id)
|
|
}
|
|
if r.FormValue(fmt.Sprintf("servers[%d][disabled]", id)) == "1" {
|
|
disabledSet[id] = true
|
|
} else {
|
|
delete(disabledSet, id)
|
|
}
|
|
}
|
|
|
|
if err == nil {
|
|
err = saveServerJSONSettings(ctx, a, protosMap, flagsMap, speedsMap, disabledSet)
|
|
}
|
|
if err == nil {
|
|
okMsg = "Настройки серверов сохранены."
|
|
}
|
|
|
|
case "add":
|
|
id := formInt(r, "new_server_id", 0)
|
|
title := r.FormValue("new_server_title")
|
|
if id <= 0 || strings.TrimSpace(title) == "" {
|
|
err = errUnknownAction
|
|
break
|
|
}
|
|
err = upsertServerLabel(ctx, a, id, title)
|
|
if err == nil {
|
|
okMsg = "Сервер добавлен."
|
|
}
|
|
|
|
case "delete_label":
|
|
id := formInt(r, "server_id", 0)
|
|
_, derr := a.Pool.Exec(ctx, `DELETE FROM panel_server_labels WHERE panel_server_id=$1`, id)
|
|
err = derr
|
|
if err == nil {
|
|
protosMap := a.Settings.JSONMapStringSlice(ctx, settings.KeyProtocolsJSON)
|
|
flagsMap := a.Settings.JSONMapString(ctx, settings.KeyFlagsJSON)
|
|
speedsMap := a.Settings.JSONMapString(ctx, settings.KeySpeedsJSON)
|
|
disabledSet := a.Settings.DisabledServers(ctx)
|
|
delete(protosMap, id)
|
|
delete(flagsMap, id)
|
|
delete(speedsMap, id)
|
|
delete(disabledSet, id)
|
|
err = saveServerJSONSettings(ctx, a, protosMap, flagsMap, speedsMap, disabledSet)
|
|
}
|
|
if err == nil {
|
|
okMsg = "Сервер удалён из списка."
|
|
}
|
|
|
|
default:
|
|
err = errUnknownAction
|
|
}
|
|
|
|
errText := ""
|
|
if err != nil {
|
|
errText = err.Error()
|
|
}
|
|
redirectWithFlash(w, r, a, "admin/servers", okMsg, errText)
|
|
}
|
|
}
|