mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-01 23:15:27 +08:00
185 lines
5.0 KiB
Go
185 lines
5.0 KiB
Go
package admin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"ds2api/internal/config"
|
|
)
|
|
|
|
func (h *Handler) listAccounts(w http.ResponseWriter, r *http.Request) {
|
|
page := intFromQuery(r, "page", 1)
|
|
pageSize := intFromQuery(r, "page_size", 10)
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if pageSize < 1 {
|
|
pageSize = 1
|
|
}
|
|
if pageSize > 5000 {
|
|
pageSize = 5000
|
|
}
|
|
accounts := h.Store.Snapshot().Accounts
|
|
reverseAccounts(accounts)
|
|
q := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("q")))
|
|
if q != "" {
|
|
filtered := make([]config.Account, 0, len(accounts))
|
|
for _, acc := range accounts {
|
|
id := strings.ToLower(acc.Identifier())
|
|
if strings.Contains(id, q) ||
|
|
strings.Contains(strings.ToLower(acc.Name), q) ||
|
|
strings.Contains(strings.ToLower(acc.Remark), q) ||
|
|
strings.Contains(strings.ToLower(acc.Email), q) ||
|
|
strings.Contains(strings.ToLower(acc.Mobile), q) {
|
|
filtered = append(filtered, acc)
|
|
}
|
|
}
|
|
accounts = filtered
|
|
}
|
|
total := len(accounts)
|
|
totalPages := 1
|
|
if total > 0 {
|
|
totalPages = (total + pageSize - 1) / pageSize
|
|
}
|
|
start := (page - 1) * pageSize
|
|
if start > total {
|
|
start = total
|
|
}
|
|
end := start + pageSize
|
|
if end > total {
|
|
end = total
|
|
}
|
|
items := make([]map[string]any, 0, end-start)
|
|
for _, acc := range accounts[start:end] {
|
|
testStatus, _ := h.Store.AccountTestStatus(acc.Identifier())
|
|
token := strings.TrimSpace(acc.Token)
|
|
preview := ""
|
|
if token != "" {
|
|
if len(token) > 20 {
|
|
preview = token[:20] + "..."
|
|
} else {
|
|
preview = token
|
|
}
|
|
}
|
|
items = append(items, map[string]any{
|
|
"identifier": acc.Identifier(),
|
|
"name": acc.Name,
|
|
"remark": acc.Remark,
|
|
"email": acc.Email,
|
|
"mobile": acc.Mobile,
|
|
"proxy_id": acc.ProxyID,
|
|
"has_password": acc.Password != "",
|
|
"has_token": token != "",
|
|
"token_preview": preview,
|
|
"test_status": testStatus,
|
|
})
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"items": items, "total": total, "page": page, "page_size": pageSize, "total_pages": totalPages})
|
|
}
|
|
|
|
func (h *Handler) addAccount(w http.ResponseWriter, r *http.Request) {
|
|
var req map[string]any
|
|
_ = json.NewDecoder(r.Body).Decode(&req)
|
|
acc := toAccount(req)
|
|
if acc.Identifier() == "" {
|
|
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": "需要 email 或 mobile"})
|
|
return
|
|
}
|
|
err := h.Store.Update(func(c *config.Config) error {
|
|
if acc.ProxyID != "" {
|
|
if _, ok := findProxyByID(*c, acc.ProxyID); !ok {
|
|
return fmt.Errorf("代理不存在")
|
|
}
|
|
}
|
|
mobileKey := config.CanonicalMobileKey(acc.Mobile)
|
|
for _, a := range c.Accounts {
|
|
if acc.Email != "" && a.Email == acc.Email {
|
|
return fmt.Errorf("邮箱已存在")
|
|
}
|
|
if mobileKey != "" && config.CanonicalMobileKey(a.Mobile) == mobileKey {
|
|
return fmt.Errorf("手机号已存在")
|
|
}
|
|
}
|
|
c.Accounts = append(c.Accounts, acc)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": err.Error()})
|
|
return
|
|
}
|
|
h.Pool.Reset()
|
|
writeJSON(w, http.StatusOK, map[string]any{"success": true, "total_accounts": len(h.Store.Snapshot().Accounts)})
|
|
}
|
|
|
|
func (h *Handler) updateAccount(w http.ResponseWriter, r *http.Request) {
|
|
identifier := chi.URLParam(r, "identifier")
|
|
if decoded, err := url.PathUnescape(identifier); err == nil {
|
|
identifier = decoded
|
|
}
|
|
|
|
var req map[string]any
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": "invalid json"})
|
|
return
|
|
}
|
|
name, nameOK := fieldStringOptional(req, "name")
|
|
remark, remarkOK := fieldStringOptional(req, "remark")
|
|
|
|
err := h.Store.Update(func(c *config.Config) error {
|
|
for i, acc := range c.Accounts {
|
|
if !accountMatchesIdentifier(acc, identifier) {
|
|
continue
|
|
}
|
|
if nameOK {
|
|
c.Accounts[i].Name = name
|
|
}
|
|
if remarkOK {
|
|
c.Accounts[i].Remark = remark
|
|
}
|
|
return nil
|
|
}
|
|
return newRequestError("账号不存在")
|
|
})
|
|
if err != nil {
|
|
if detail, ok := requestErrorDetail(err); ok {
|
|
writeJSON(w, http.StatusNotFound, map[string]any{"detail": detail})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": err.Error()})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"success": true, "total_accounts": len(h.Store.Snapshot().Accounts)})
|
|
}
|
|
|
|
func (h *Handler) deleteAccount(w http.ResponseWriter, r *http.Request) {
|
|
identifier := chi.URLParam(r, "identifier")
|
|
if decoded, err := url.PathUnescape(identifier); err == nil {
|
|
identifier = decoded
|
|
}
|
|
err := h.Store.Update(func(c *config.Config) error {
|
|
idx := -1
|
|
for i, a := range c.Accounts {
|
|
if accountMatchesIdentifier(a, identifier) {
|
|
idx = i
|
|
break
|
|
}
|
|
}
|
|
if idx < 0 {
|
|
return fmt.Errorf("账号不存在")
|
|
}
|
|
c.Accounts = append(c.Accounts[:idx], c.Accounts[idx+1:]...)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
writeJSON(w, http.StatusNotFound, map[string]any{"detail": err.Error()})
|
|
return
|
|
}
|
|
h.Pool.Reset()
|
|
writeJSON(w, http.StatusOK, map[string]any{"success": true, "total_accounts": len(h.Store.Snapshot().Accounts)})
|
|
}
|