Add Go rewrite with Postgres 17 and Dokploy Docker Compose

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-27 08:21:21 +03:00
co-authored by Cursor
parent bac78dd3fc
commit 99a27f00be
67 changed files with 17864 additions and 0 deletions
+255
View File
@@ -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)
}
}