Add Go rewrite with Postgres 17 and Dokploy Docker Compose
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user