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
+202
View File
@@ -0,0 +1,202 @@
package handlers
import (
"net/http"
"net/url"
"strings"
"time"
"amnezia-share/internal/models"
"amnezia-share/internal/share"
"amnezia-share/internal/web"
)
func flashFromQuery(r *http.Request) (ok, errText string) {
return r.URL.Query().Get("ok"), r.URL.Query().Get("err")
}
func redirectWithFlash(w http.ResponseWriter, r *http.Request, a *web.App, path string, ok, errText string) {
u, _ := url.Parse(a.Cfg.AppURL(path))
q := u.Query()
if ok != "" {
q.Set("ok", ok)
}
if errText != "" {
q.Set("err", errText)
}
u.RawQuery = q.Encode()
http.Redirect(w, r, u.String(), http.StatusSeeOther)
}
const adminPerPage = 20
type linkRow struct {
models.ShareLink
Status share.LinkStatus
ActiveConfigs int
}
type adminLinksView struct {
adminLayout
Links []linkRow
StatusFilter string
Page int
Total int
PerPage int
Detail *models.ShareLink
DetailStatus share.LinkStatus
Creations []models.ShareCreation
DeletedCreations []models.ShareCreation
AllowedDurations []int
AllowedServerIDs []int
AllServers []models.ServerInfo
}
// AdminLinksGet lists share links (paginated, filterable by status) and, when
// ?id= is present, shows one link's detail with its creations (share_links.php).
func AdminLinksGet(a *web.App) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ok, errText := flashFromQuery(r)
view := adminLinksView{
adminLayout: newAdminLayout(a, r, "links"),
StatusFilter: r.URL.Query().Get("status"),
Page: queryInt(r, "page", 1),
PerPage: adminPerPage,
AllowedDurations: share.AllowedDurations,
}
view.OK, view.Error = ok, errText
if view.Page < 1 {
view.Page = 1
}
links, err := a.Share.ListLinks(ctx, view.Page, view.PerPage, view.StatusFilter)
if err != nil {
view.Error = err.Error()
}
view.Total, _ = a.Share.CountLinks(ctx, view.StatusFilter)
for _, l := range links {
creations, _ := a.Share.ListCreationsForLink(ctx, l.ID)
view.Links = append(view.Links, linkRow{
ShareLink: l,
Status: share.ClassifyLinkStatus(l, len(creations) > 0),
ActiveConfigs: len(creations),
})
}
if id := queryInt(r, "id", 0); id > 0 {
if link, err := a.Share.LinkByID(ctx, id); err == nil && link != nil {
view.Detail = link
creations, _ := a.Share.ListCreationsForLink(ctx, id)
view.DetailStatus = share.ClassifyLinkStatus(*link, len(creations) > 0)
view.Creations = creations
view.DeletedCreations, _ = a.Share.ListDeletedCreationsForLink(ctx, id)
view.AllowedServerIDs = share.ParseAllowedServerIDs(link.AllowedServerIDs)
view.AllServers = share.BuildGuestServers(ctx, a.Settings, a.Settings.ServerLabels(ctx))
}
}
a.Render(w, r, "page_admin_links", view)
}
}
func parseFormDate(v string) *time.Time {
v = strings.TrimSpace(v)
if v == "" {
return nil
}
layouts := []string{"2006-01-02T15:04", "2006-01-02", time.RFC3339}
for _, layout := range layouts {
if t, err := time.Parse(layout, v); err == nil {
return &t
}
}
return nil
}
// AdminLinksPost handles link create/update/delete/extend/allowed-servers and
// per-creation deletion.
func AdminLinksPost(a *web.App) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
action := r.FormValue("action")
back := "admin/links"
if id := r.FormValue("back_id"); id != "" {
back += "?id=" + url.QueryEscape(id)
}
var err error
var okMsg string
switch action {
case "create":
days := formInt(r, "days", 30)
maxUses := formInt(r, "max_uses", 5)
subUntil := parseFormDate(r.FormValue("subscription_until"))
link, cerr := a.Share.CreateLink(ctx, days, maxUses, subUntil)
err = cerr
if err == nil {
okMsg = "Ссылка создана: #" + itoaHelper(link.ID)
}
case "update":
linkID := formInt(r, "link_id", 0)
validityDays := formInt(r, "validity_days", 30)
floating := r.FormValue("floating_expiry") == "1"
expiresAt := parseFormDate(r.FormValue("expires_at"))
subUntil := parseFormDate(r.FormValue("subscription_until"))
maxUses := formInt(r, "max_uses", 5)
err = a.Share.UpdateLink(ctx, linkID, validityDays, floating, expiresAt, subUntil, maxUses)
if err == nil {
okMsg = "Срок ссылки обновлён."
}
case "delete":
linkID := formInt(r, "link_id", 0)
err = a.Share.DeleteLink(ctx, linkID)
back = "admin/links"
if err == nil {
okMsg = "Ссылка удалена."
}
case "extend":
linkID := formInt(r, "link_id", 0)
days := formInt(r, "days", 30)
err = a.Share.ExtendLinkByDays(ctx, linkID, days)
if err == nil {
okMsg = "Ссылка продлена."
}
case "set_allowed_servers":
linkID := formInt(r, "link_id", 0)
_ = r.ParseForm()
var ids []int
for _, v := range r.Form["server_ids[]"] {
if n := formIntFromString(v, -1); n >= 0 {
ids = append(ids, n)
}
}
err = a.Share.SetAllowedServerIDs(ctx, linkID, ids)
if err == nil {
okMsg = "Список серверов обновлён."
}
case "delete_creation":
linkID := formInt(r, "link_id", 0)
creationID := formInt64(r, "creation_id", 0)
err = a.Share.SoftDeleteCreation(ctx, a.Panel, a.Settings, linkID, creationID)
if err == nil {
okMsg = "Конфигурация удалена."
}
default:
err = errUnknownAction
}
errText := ""
if err != nil {
errText = err.Error()
}
redirectWithFlash(w, r, a, back, okMsg, errText)
}
}