127 lines
4.3 KiB
Go
127 lines
4.3 KiB
Go
package dnf90
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
appkit "longheng.io/server/internal/platform/app"
|
|
httpx "longheng.io/server/internal/platform/httpx"
|
|
"longheng.io/server/internal/services/dnfbridge"
|
|
)
|
|
|
|
// companionPanelAdminPath is the local-only endpoint used by the GM tool's
|
|
// "支援兵属性" page. It is intentionally unauthenticated: the admin mux binds
|
|
// to loopback, like the existing /local endpoints, and the page is served by
|
|
// the local GM tool.
|
|
const companionPanelAdminPath = "/local/companion-panel"
|
|
|
|
func registerCompanionPanelAdmin(env *appkit.Env, bridge *dnfbridge.Service) {
|
|
if env == nil || env.AdminMux == nil || bridge == nil {
|
|
return
|
|
}
|
|
env.AdminMux.HandleFunc(companionPanelAdminPath, func(w http.ResponseWriter, r *http.Request) {
|
|
applyCompanionPanelCORS(w)
|
|
if r.Method == http.MethodOptions {
|
|
w.WriteHeader(http.StatusNoContent)
|
|
return
|
|
}
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
characterID := strings.TrimSpace(r.URL.Query().Get("character_id"))
|
|
if characterID == "" {
|
|
characterID = strings.TrimSpace(r.URL.Query().Get("characterId"))
|
|
}
|
|
values, found, err := bridge.CompanionPanelValues(r.Context(), characterID)
|
|
if err != nil {
|
|
httpx.WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
httpx.WriteJSON(w, http.StatusOK, map[string]any{
|
|
"character_id": characterID,
|
|
"found": found,
|
|
"values": values,
|
|
})
|
|
case http.MethodPost:
|
|
var body struct {
|
|
CharacterID string `json:"character_id"`
|
|
Values map[string]string `json:"values"`
|
|
}
|
|
decoder := json.NewDecoder(io.LimitReader(r.Body, 1<<20))
|
|
if err := decoder.Decode(&body); err != nil {
|
|
httpx.WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
if strings.TrimSpace(body.CharacterID) == "" {
|
|
httpx.WriteJSON(w, http.StatusBadRequest, map[string]string{
|
|
"error": "character_id is required",
|
|
})
|
|
return
|
|
}
|
|
if err := bridge.SaveCompanionPanelValues(r.Context(), body.CharacterID, body.Values); err != nil {
|
|
httpx.WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
values, found, _ := bridge.CompanionPanelValues(r.Context(), body.CharacterID)
|
|
httpx.WriteJSON(w, http.StatusOK, map[string]any{
|
|
"ok": true,
|
|
"character_id": body.CharacterID,
|
|
"found": found,
|
|
"values": values,
|
|
})
|
|
default:
|
|
w.Header().Set("Allow", "GET, POST, OPTIONS")
|
|
httpx.WriteJSON(w, http.StatusMethodNotAllowed, map[string]string{
|
|
"error": "method not allowed",
|
|
})
|
|
}
|
|
})
|
|
|
|
// Base-estimate generation: PVF growth + worn equipment + weapon forging.
|
|
// Enchant cards, amplification, reinforcement, quality, sets, titles, auras
|
|
// and pets are intentionally not modelled; the GM page marks the result as
|
|
// an estimate and lets the operator correct it.
|
|
env.AdminMux.HandleFunc(companionPanelGeneratePath, func(w http.ResponseWriter, r *http.Request) {
|
|
applyCompanionPanelCORS(w)
|
|
if r.Method == http.MethodOptions {
|
|
w.WriteHeader(http.StatusNoContent)
|
|
return
|
|
}
|
|
if r.Method != http.MethodPost {
|
|
w.Header().Set("Allow", "POST, OPTIONS")
|
|
httpx.WriteJSON(w, http.StatusMethodNotAllowed, map[string]string{
|
|
"error": "method not allowed",
|
|
})
|
|
return
|
|
}
|
|
var body struct {
|
|
CharacterID string `json:"character_id"`
|
|
}
|
|
if err := json.NewDecoder(io.LimitReader(r.Body, 1<<20)).Decode(&body); err != nil {
|
|
httpx.WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
values, err := bridge.GenerateCompanionPanelSnapshot(r.Context(), body.CharacterID)
|
|
if err != nil {
|
|
httpx.WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
httpx.WriteJSON(w, http.StatusOK, map[string]any{
|
|
"ok": true,
|
|
"character_id": body.CharacterID,
|
|
"estimate": true,
|
|
"values": values,
|
|
})
|
|
})
|
|
}
|
|
|
|
const companionPanelGeneratePath = "/local/companion-panel/generate"
|
|
|
|
func applyCompanionPanelCORS(w http.ResponseWriter) {
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-Admin-Token")
|
|
w.Header().Set("Access-Control-Allow-Private-Network", "true")
|
|
}
|