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,252 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"amnezia-share/internal/i18n"
|
||||
"amnezia-share/internal/member"
|
||||
"amnezia-share/internal/models"
|
||||
"amnezia-share/internal/share"
|
||||
"amnezia-share/internal/web"
|
||||
)
|
||||
|
||||
func registerCabinet(a *web.App, mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /cabinet", a.RequireMember(CabinetGet(a)))
|
||||
mux.HandleFunc("POST /cabinet", a.RequireMember(a.CSRFProtect(CabinetPost(a))))
|
||||
mux.HandleFunc("GET /cabinet/login", CabinetLoginForm(a))
|
||||
mux.HandleFunc("POST /cabinet/login", a.CSRFProtect(CabinetLoginSubmit(a)))
|
||||
mux.HandleFunc("GET /cabinet/register", CabinetRegisterForm(a))
|
||||
mux.HandleFunc("POST /cabinet/register", a.CSRFProtect(CabinetRegisterSubmit(a)))
|
||||
mux.HandleFunc("GET /cabinet/logout", CabinetLogout(a))
|
||||
mux.HandleFunc("GET /cabinet/servers", CabinetServers(a))
|
||||
mux.HandleFunc("GET /cabinet/download", CabinetDownload(a))
|
||||
}
|
||||
|
||||
type cabinetView struct {
|
||||
Member *models.Member
|
||||
Sub *models.MemberSubscription
|
||||
SubLabel string
|
||||
ConfigsRemaining int
|
||||
Servers []models.ServerInfo
|
||||
Bundles []share.Bundle
|
||||
Maintenance bool
|
||||
Expired bool
|
||||
LimitReached bool
|
||||
OK string
|
||||
Error string
|
||||
}
|
||||
|
||||
func renderCabinet(a *web.App, w http.ResponseWriter, r *http.Request, okMsg, errText string) {
|
||||
ctx := r.Context()
|
||||
m := a.CurrentMember(r)
|
||||
sub, err := a.Member.EnsureSubscription(ctx, a.Settings, m.ID)
|
||||
if err != nil {
|
||||
errText = err.Error()
|
||||
}
|
||||
bundles, _ := a.Member.Bundles(ctx, m.ID)
|
||||
servers := share.BuildGuestServers(ctx, a.Settings, a.Settings.ServerLabels(ctx))
|
||||
|
||||
view := cabinetView{
|
||||
Member: m,
|
||||
Sub: sub,
|
||||
Servers: servers,
|
||||
Bundles: bundles,
|
||||
Maintenance: a.Settings.Maintenance(ctx),
|
||||
OK: okMsg,
|
||||
Error: errText,
|
||||
}
|
||||
if sub != nil {
|
||||
view.Expired = member.SubscriptionExpired(*sub)
|
||||
view.LimitReached = sub.ConfigCount >= sub.MaxConfigs
|
||||
view.SubLabel = member.SubscriptionExpiresLabel(*sub)
|
||||
view.ConfigsRemaining = sub.MaxConfigs - sub.ConfigCount
|
||||
}
|
||||
a.Render(w, r, "page_cabinet", view)
|
||||
}
|
||||
|
||||
// CabinetGet renders the member portal dashboard (cabinet.php GET).
|
||||
func CabinetGet(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
renderCabinet(a, w, r, "", "")
|
||||
}
|
||||
}
|
||||
|
||||
// CabinetPost handles member actions: create / migrate / renew / delete
|
||||
// (cabinet.php POST).
|
||||
func CabinetPost(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
m := a.CurrentMember(r)
|
||||
lang := i18n.Resolve(r, a.Sessions)
|
||||
maintenance := a.Settings.Maintenance(ctx)
|
||||
|
||||
var actionErr error
|
||||
var okMsg string
|
||||
switch r.FormValue("action") {
|
||||
case "create":
|
||||
if maintenance {
|
||||
actionErr = errors.New(i18n.T(lang, "err_maintenance_mode", nil))
|
||||
break
|
||||
}
|
||||
serverID := formInt(r, "server_id", -1)
|
||||
protocol := strings.TrimSpace(r.FormValue("protocol"))
|
||||
_, err := a.Member.TryAddConfig(ctx, a.Panel, a.Settings, m.ID, serverID, protocol)
|
||||
if err != nil {
|
||||
actionErr = err
|
||||
break
|
||||
}
|
||||
okMsg = "Готово! Конфигурация создана."
|
||||
case "migrate":
|
||||
if maintenance {
|
||||
actionErr = errors.New(i18n.T(lang, "err_maintenance_mode", nil))
|
||||
break
|
||||
}
|
||||
configID := formInt64(r, "config_id", 0)
|
||||
newServerID := formInt(r, "new_server_id", -1)
|
||||
newProtocol := strings.TrimSpace(r.FormValue("new_protocol"))
|
||||
if err := a.Member.TryMigrate(ctx, a.Panel, a.Settings, m.ID, configID, newServerID, newProtocol); err != nil {
|
||||
actionErr = err
|
||||
break
|
||||
}
|
||||
okMsg = i18n.T(lang, "ok_migrated", nil)
|
||||
case "renew":
|
||||
code := r.FormValue("code")
|
||||
res, err := a.Member.TryRedeemRenewal(ctx, m.ID, code)
|
||||
if err != nil {
|
||||
actionErr = err
|
||||
break
|
||||
}
|
||||
until := ""
|
||||
if res.Sub != nil {
|
||||
until = member.SubscriptionExpiresLabel(*res.Sub)
|
||||
}
|
||||
okMsg = i18n.T(lang, "ok_renewed", map[string]string{"days": fmt.Sprint(res.AddDays), "until": until})
|
||||
case "delete":
|
||||
configID := formInt64(r, "config_id", 0)
|
||||
if err := a.Member.SoftDelete(ctx, a.Panel, a.Settings, m.ID, configID); err != nil {
|
||||
actionErr = err
|
||||
break
|
||||
}
|
||||
okMsg = "Конфигурация удалена."
|
||||
default:
|
||||
actionErr = errors.New("Неизвестное действие.")
|
||||
}
|
||||
|
||||
errText := ""
|
||||
if actionErr != nil {
|
||||
errText = i18n.TranslateGuestError(lang, actionErr.Error())
|
||||
}
|
||||
if wantsJSON(r) {
|
||||
if errText != "" {
|
||||
writeJSONError(w, http.StatusBadRequest, errText)
|
||||
return
|
||||
}
|
||||
writeJSONOK(w, map[string]any{"message": okMsg})
|
||||
return
|
||||
}
|
||||
renderCabinet(a, w, r, okMsg, errText)
|
||||
}
|
||||
}
|
||||
|
||||
type cabinetAuthView struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
// CabinetLoginForm renders the member login form.
|
||||
func CabinetLoginForm(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if a.CurrentMember(r) != nil {
|
||||
redirectTo(w, r, a, "cabinet")
|
||||
return
|
||||
}
|
||||
a.Render(w, r, "page_cabinet_auth", cabinetAuthView{})
|
||||
}
|
||||
}
|
||||
|
||||
// CabinetLoginSubmit verifies member credentials and signs them in.
|
||||
func CabinetLoginSubmit(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
username := strings.TrimSpace(r.FormValue("username"))
|
||||
password := r.FormValue("password")
|
||||
m, err := a.Member.Login(ctx, username, password)
|
||||
if err != nil {
|
||||
a.Render(w, r, "page_cabinet_auth", cabinetAuthView{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
_ = a.Sessions.RenewToken(ctx)
|
||||
a.Sessions.Put(ctx, web.SessionMemberID, m.ID)
|
||||
redirectTo(w, r, a, "cabinet")
|
||||
}
|
||||
}
|
||||
|
||||
// CabinetRegisterForm renders the member self-registration form.
|
||||
func CabinetRegisterForm(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if a.CurrentMember(r) != nil {
|
||||
redirectTo(w, r, a, "cabinet")
|
||||
return
|
||||
}
|
||||
a.Render(w, r, "page_cabinet_auth", cabinetAuthView{})
|
||||
}
|
||||
}
|
||||
|
||||
// CabinetRegisterSubmit creates a new member account and signs them in.
|
||||
func CabinetRegisterSubmit(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
username := strings.TrimSpace(r.FormValue("username"))
|
||||
password := r.FormValue("password")
|
||||
email := strings.TrimSpace(r.FormValue("email"))
|
||||
m, err := a.Member.Register(ctx, username, password, email)
|
||||
if err != nil {
|
||||
a.Render(w, r, "page_cabinet_auth", cabinetAuthView{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
if _, err := a.Member.EnsureSubscription(ctx, a.Settings, m.ID); err != nil {
|
||||
a.Render(w, r, "page_cabinet_auth", cabinetAuthView{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
_ = a.Sessions.RenewToken(ctx)
|
||||
a.Sessions.Put(ctx, web.SessionMemberID, m.ID)
|
||||
redirectTo(w, r, a, "cabinet")
|
||||
}
|
||||
}
|
||||
|
||||
// CabinetLogout clears the member session.
|
||||
func CabinetLogout(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
a.Sessions.Remove(r.Context(), web.SessionMemberID)
|
||||
_ = a.Sessions.RenewToken(r.Context())
|
||||
redirectTo(w, r, a, "cabinet/login")
|
||||
}
|
||||
}
|
||||
|
||||
// CabinetServers returns the server catalog as JSON for the member portal.
|
||||
func CabinetServers(a *web.App) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
servers := share.BuildGuestServers(ctx, a.Settings, a.Settings.ServerLabels(ctx))
|
||||
writeJSONOK(w, map[string]any{"servers": servers})
|
||||
}
|
||||
}
|
||||
|
||||
// CabinetDownload streams a single downloadable artifact for one of the
|
||||
// member's configs.
|
||||
func CabinetDownload(a *web.App) http.HandlerFunc {
|
||||
return a.RequireMember(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
m := a.CurrentMember(r)
|
||||
configID := int64(queryInt(r, "cre", 0))
|
||||
part := r.URL.Query().Get("part")
|
||||
fp, err := a.Member.DownloadPayload(ctx, m.ID, configID, part)
|
||||
if err != nil || fp == nil {
|
||||
http.Error(w, "Файл не найден.", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
serveFilePart(w, fp)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user