203 lines
5.5 KiB
Go
203 lines
5.5 KiB
Go
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)
|
|
}
|
|
}
|