mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 13:34:53 +08:00
+167
-1
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -36,6 +37,8 @@ func (s *Server) handleAdminIndex(w http.ResponseWriter, r *http.Request) {
|
||||
"/v1/resources",
|
||||
"/openapi.yaml",
|
||||
"/admin/schedules",
|
||||
"/admin/translation/tasks",
|
||||
"/admin/translation/handoff",
|
||||
},
|
||||
Controls: []string{
|
||||
"/admin/control/reload",
|
||||
@@ -50,6 +53,7 @@ func (s *Server) handleAdminIndex(w http.ResponseWriter, r *http.Request) {
|
||||
"/admin/control/schedule-remove",
|
||||
"/admin/control/schedule-run",
|
||||
"/admin/control/translation-task-update",
|
||||
"/admin/control/translation-worker-run",
|
||||
"/admin/control/translation-proofread",
|
||||
},
|
||||
}
|
||||
@@ -82,6 +86,10 @@ func (s *Server) handleAdminControl(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleAdminTranslationTaskUpdate(w, r)
|
||||
return
|
||||
}
|
||||
if action == "translation-worker-run" {
|
||||
s.handleAdminTranslationWorkerRun(w, r)
|
||||
return
|
||||
}
|
||||
if action == "translation-proofread" {
|
||||
s.handleAdminTranslationProofread(w, r)
|
||||
return
|
||||
@@ -187,6 +195,34 @@ func (s *Server) handleAdminTranslationTaskUpdate(w http.ResponseWriter, r *http
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminTranslationWorkerRun(w http.ResponseWriter, r *http.Request) {
|
||||
backend, ok := s.backend.(TranslationBackend)
|
||||
if !ok || backend == nil {
|
||||
writeErrorJSON(w, http.StatusServiceUnavailable, "translation_backend_unavailable", "Rust bat translation backend is unavailable")
|
||||
return
|
||||
}
|
||||
var params backendrpc.TranslationWorkerRunParams
|
||||
if !decodeAdminTranslationJSON(w, r, ¶ms) {
|
||||
return
|
||||
}
|
||||
if err := validateTranslationWorkerRunParams(params); err != nil {
|
||||
writeErrorJSON(w, http.StatusBadRequest, "invalid_translation_params", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := backend.TranslationWorkerRun(r.Context(), params)
|
||||
if err != nil {
|
||||
s.writeControlBackendError(w, "translation-worker-run", err)
|
||||
return
|
||||
}
|
||||
writeNoStoreJSON(w, http.StatusAccepted, AdminControlResponse{
|
||||
Service: "bat-api",
|
||||
Action: "translation-worker-run",
|
||||
RPCMethod: "translation.worker.run",
|
||||
Status: "accepted",
|
||||
Result: result,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminTranslationProofread(w http.ResponseWriter, r *http.Request) {
|
||||
backend, ok := s.backend.(TranslationBackend)
|
||||
if !ok || backend == nil {
|
||||
@@ -207,6 +243,63 @@ func (s *Server) handleAdminTranslationProofread(w http.ResponseWriter, r *http.
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminTranslationTasks(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
||||
return
|
||||
}
|
||||
if !s.requireAdminToken(w, r) {
|
||||
return
|
||||
}
|
||||
backend, ok := s.backend.(TranslationBackend)
|
||||
if !ok || backend == nil {
|
||||
writeErrorJSON(w, http.StatusServiceUnavailable, "translation_backend_unavailable", "Rust bat translation backend is unavailable")
|
||||
return
|
||||
}
|
||||
params, err := translationTaskListParams(r)
|
||||
if err != nil {
|
||||
writeErrorJSON(w, http.StatusBadRequest, "invalid_translation_query", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := backend.TranslationTasks(r.Context(), params)
|
||||
if err != nil {
|
||||
s.writeControlBackendError(w, "translation-tasks", err)
|
||||
return
|
||||
}
|
||||
if r.Method == http.MethodHead {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
writeNoStoreJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminTranslationHandoff(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
||||
return
|
||||
}
|
||||
if !s.requireAdminToken(w, r) {
|
||||
return
|
||||
}
|
||||
backend, ok := s.backend.(TranslationBackend)
|
||||
if !ok || backend == nil {
|
||||
writeErrorJSON(w, http.StatusServiceUnavailable, "translation_backend_unavailable", "Rust bat translation backend is unavailable")
|
||||
return
|
||||
}
|
||||
result, err := backend.TranslationHandoff(r.Context())
|
||||
if err != nil {
|
||||
s.writeControlBackendError(w, "translation-handoff", err)
|
||||
return
|
||||
}
|
||||
if r.Method == http.MethodHead {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
writeNoStoreJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminSchedules(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
||||
@@ -254,6 +347,63 @@ func scheduleListParams(r *http.Request) (backendrpc.ScheduleListParams, error)
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func translationTaskListParams(r *http.Request) (backendrpc.TranslationTaskListParams, error) {
|
||||
query := r.URL.Query()
|
||||
params := backendrpc.TranslationTaskListParams{
|
||||
TaskID: strings.TrimSpace(query.Get("task_id")),
|
||||
ReleaseID: firstTrimmedQuery(query, "release_id", "official_release_id"),
|
||||
Destination: strings.TrimSpace(query.Get("destination")),
|
||||
PathPattern: strings.TrimSpace(query.Get("path_pattern")),
|
||||
ArchiveEntry: strings.TrimSpace(query.Get("archive_entry")),
|
||||
Status: firstTrimmedQuery(query, "status", "task_status"),
|
||||
WorkerStatus: strings.TrimSpace(query.Get("worker_status")),
|
||||
ParseStatus: strings.TrimSpace(query.Get("parse_status")),
|
||||
Format: firstTrimmedQuery(query, "format", "text_unit_format"),
|
||||
}
|
||||
if raw := strings.TrimSpace(query.Get("offset")); raw != "" {
|
||||
offset, err := strconv.ParseUint(raw, 10, 64)
|
||||
if err != nil {
|
||||
return backendrpc.TranslationTaskListParams{}, errors.New("offset must be a non-negative integer")
|
||||
}
|
||||
params.Offset = &offset
|
||||
}
|
||||
if raw := strings.TrimSpace(query.Get("limit")); raw != "" {
|
||||
limit, err := strconv.ParseUint(raw, 10, 64)
|
||||
if err != nil || limit == 0 || limit > 1000 {
|
||||
return backendrpc.TranslationTaskListParams{}, errors.New("limit must be in 1..=1000")
|
||||
}
|
||||
params.Limit = &limit
|
||||
}
|
||||
if raw := strings.TrimSpace(query.Get("has_reason")); raw != "" {
|
||||
hasReason, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return backendrpc.TranslationTaskListParams{}, errors.New("has_reason must be a boolean")
|
||||
}
|
||||
params.HasReason = &hasReason
|
||||
}
|
||||
if raw := strings.TrimSpace(query.Get("has_failure_reason")); raw != "" {
|
||||
hasFailureReason, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return backendrpc.TranslationTaskListParams{}, errors.New("has_failure_reason must be a boolean")
|
||||
}
|
||||
params.HasFailureReason = &hasFailureReason
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func firstTrimmedQuery(query url.Values, keys ...string) string {
|
||||
for _, key := range keys {
|
||||
values := query[key]
|
||||
if len(values) == 0 {
|
||||
continue
|
||||
}
|
||||
if value := strings.TrimSpace(values[0]); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminScheduleControl(w http.ResponseWriter, r *http.Request, action string) {
|
||||
backend, ok := s.backend.(ScheduleBackend)
|
||||
if !ok || backend == nil {
|
||||
@@ -325,7 +475,23 @@ func decodeAdminScheduleJSON(w http.ResponseWriter, r *http.Request, target any)
|
||||
}
|
||||
|
||||
func decodeAdminTranslationJSON(w http.ResponseWriter, r *http.Request, target any) bool {
|
||||
return decodeAdminJSON(w, r, target, "invalid_translation_params", "translation task request")
|
||||
return decodeAdminJSON(w, r, target, "invalid_translation_params", "translation request")
|
||||
}
|
||||
|
||||
func validateTranslationWorkerRunParams(params backendrpc.TranslationWorkerRunParams) error {
|
||||
if params.Concurrency != nil && (*params.Concurrency < 1 || *params.Concurrency > 256) {
|
||||
return errors.New("translation worker concurrency must be in 1..=256")
|
||||
}
|
||||
if params.MaxAttempts != nil && *params.MaxAttempts == 0 {
|
||||
return errors.New("translation worker max_attempts must be greater than 0")
|
||||
}
|
||||
if params.LeaseSeconds != nil && *params.LeaseSeconds == 0 {
|
||||
return errors.New("translation worker lease_seconds must be greater than 0")
|
||||
}
|
||||
if params.MaxTasks != nil && *params.MaxTasks == 0 {
|
||||
return errors.New("translation worker max_tasks must be greater than 0")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeAdminJSON(w http.ResponseWriter, r *http.Request, target any, errorCode string, subject string) bool {
|
||||
|
||||
Reference in New Issue
Block a user