192 lines
5.2 KiB
Go
192 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
// ✅ umbenannt, damit es nicht mit models.go kollidiert
|
|
func modelsWriteJSON(w http.ResponseWriter, status int, v any) {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(v)
|
|
}
|
|
|
|
func modelsReadJSON(r *http.Request, v any) error {
|
|
if r.Body == nil {
|
|
return errors.New("missing body")
|
|
}
|
|
defer r.Body.Close()
|
|
return json.NewDecoder(r.Body).Decode(v)
|
|
}
|
|
|
|
type parseReq struct {
|
|
Input string `json:"input"`
|
|
}
|
|
|
|
func parseModelFromURL(raw string) (ParsedModelDTO, error) {
|
|
in := strings.TrimSpace(raw)
|
|
if in == "" {
|
|
return ParsedModelDTO{}, errors.New("Bitte eine URL eingeben.")
|
|
}
|
|
|
|
// scheme ergänzen, falls User "chaturbate.com/xyz" eingibt
|
|
if !strings.Contains(in, "://") {
|
|
in = "https://" + in
|
|
}
|
|
|
|
u, err := url.Parse(in)
|
|
if err != nil || u.Scheme == "" || u.Hostname() == "" {
|
|
return ParsedModelDTO{}, errors.New("Ungültige URL.")
|
|
}
|
|
|
|
host := strings.ToLower(u.Hostname())
|
|
host = strings.TrimPrefix(host, "www.")
|
|
|
|
// ModelKey aus Pfad/Fragment ableiten
|
|
path := strings.Trim(u.Path, "/")
|
|
segs := strings.Split(path, "/")
|
|
|
|
skip := map[string]bool{
|
|
"models": true, "model": true, "profile": true, "users": true, "user": true,
|
|
}
|
|
|
|
var key string
|
|
for _, s := range segs {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" || skip[strings.ToLower(s)] {
|
|
continue
|
|
}
|
|
key = s
|
|
break
|
|
}
|
|
if key == "" && strings.TrimSpace(u.Fragment) != "" {
|
|
key = strings.Trim(strings.TrimSpace(u.Fragment), "/")
|
|
}
|
|
if key == "" {
|
|
return ParsedModelDTO{}, errors.New("Konnte keinen Modelnamen aus der URL ableiten.")
|
|
}
|
|
|
|
// URL-decode + kleines Sanitizing
|
|
if dec, err := url.PathUnescape(key); err == nil {
|
|
key = dec
|
|
}
|
|
key = strings.TrimPrefix(strings.TrimSpace(key), "@")
|
|
key = strings.Map(func(r rune) rune {
|
|
switch {
|
|
case r >= 'a' && r <= 'z':
|
|
return r
|
|
case r >= 'A' && r <= 'Z':
|
|
return r
|
|
case r >= '0' && r <= '9':
|
|
return r
|
|
case r == '_' || r == '-' || r == '.':
|
|
return r
|
|
default:
|
|
return -1
|
|
}
|
|
}, key)
|
|
if key == "" {
|
|
return ParsedModelDTO{}, errors.New("Ungültiger Modelname in URL.")
|
|
}
|
|
|
|
return ParsedModelDTO{
|
|
Input: u.String(), // ✅ speicherst du als URL
|
|
IsURL: true,
|
|
Host: host,
|
|
Path: u.Path,
|
|
ModelKey: key, // ✅ kommt IMMER aus URL
|
|
}, nil
|
|
}
|
|
|
|
func RegisterModelAPI(mux *http.ServeMux, store *ModelStore) {
|
|
|
|
// ✅ NEU: Parse-Endpoint (nur URL erlaubt)
|
|
mux.HandleFunc("/api/models/parse", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
modelsWriteJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
|
return
|
|
}
|
|
var req parseReq
|
|
if err := modelsReadJSON(r, &req); err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
dto, err := parseModelFromURL(req.Input)
|
|
if err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
modelsWriteJSON(w, http.StatusOK, dto)
|
|
})
|
|
|
|
mux.HandleFunc("/api/models/list", func(w http.ResponseWriter, r *http.Request) {
|
|
modelsWriteJSON(w, http.StatusOK, store.List())
|
|
})
|
|
|
|
mux.HandleFunc("/api/models/upsert", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
modelsWriteJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
|
return
|
|
}
|
|
var req ParsedModelDTO
|
|
if err := modelsReadJSON(r, &req); err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// ✅ Server-seitig: nur URL akzeptieren (wird zusätzlich im Store geprüft)
|
|
if !req.IsURL {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": "Nur URL erlaubt."})
|
|
return
|
|
}
|
|
|
|
m, err := store.UpsertFromParsed(req)
|
|
if err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
modelsWriteJSON(w, http.StatusOK, m)
|
|
})
|
|
|
|
mux.HandleFunc("/api/models/flags", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
modelsWriteJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
|
return
|
|
}
|
|
var req ModelFlagsPatch
|
|
if err := modelsReadJSON(r, &req); err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
m, err := store.PatchFlags(req)
|
|
if err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
modelsWriteJSON(w, http.StatusOK, m)
|
|
})
|
|
|
|
mux.HandleFunc("/api/models/delete", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
modelsWriteJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
|
return
|
|
}
|
|
var req struct {
|
|
ID string `json:"id"`
|
|
}
|
|
if err := modelsReadJSON(r, &req); err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
if err := store.Delete(req.ID); err != nil {
|
|
modelsWriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
return
|
|
}
|
|
modelsWriteJSON(w, http.StatusOK, map[string]any{"ok": true})
|
|
})
|
|
}
|