package web import ( "encoding/json" "errors" "net/http" "strings" "ymhut-box/server/unified-management/internal/notices" "ymhut-box/server/unified-management/internal/releases" ) func (r *router) handleAdminReleases(w http.ResponseWriter, req *http.Request) { path := cleanPath(req.URL.Path) if strings.HasPrefix(path, "/api/admin/releases/notices") { r.handleAdminReleaseNotices(w, req) return } switch path { case "/api/admin/releases/packages": if req.Method != http.MethodPost { writeError(w, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", errors.New("POST required")) return } if err := req.ParseMultipartForm(256 << 20); err != nil { writeError(w, http.StatusBadRequest, "INVALID_UPLOAD", err) return } file, header, err := req.FormFile("file") if err != nil { writeError(w, http.StatusBadRequest, "FILE_REQUIRED", err) return } defer file.Close() pkg, err := r.releases.SaveUploadedPackage(req, file, releases.UploadOptions{ FileName: firstNonEmpty(req.FormValue("fileName"), header.Filename), Version: req.FormValue("version"), Platform: req.FormValue("platform"), Arch: req.FormValue("arch"), Channel: req.FormValue("channel"), Notes: req.FormValue("notes"), UpdateManifest: req.FormValue("updateManifest") == "true" || req.FormValue("updateManifest") == "1", }, "admin") if err != nil { writeError(w, http.StatusBadRequest, "PACKAGE_UPLOAD_FAILED", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "package": pkg}) case "/api/admin/releases": writeJSON(w, http.StatusOK, map[string]any{"ok": true, "manifest": r.releases.Manifest(req)}) case "/api/admin/releases/legacy-preview": writeJSON(w, http.StatusOK, map[string]any{"ok": true, "updateInfo": r.releases.LegacyUpdateInfo(req), "toolStatus": r.releases.StaticJSON("tool-status.json")}) default: http.NotFound(w, req) } } func (r *router) handleAdminReleaseNotices(w http.ResponseWriter, req *http.Request) { if r.notices == nil { writeError(w, http.StatusNotFound, "NOTICES_DISABLED", errors.New("release notices are not configured")) return } path := cleanPath(req.URL.Path) if req.Method == http.MethodPost && path == "/api/admin/releases/notices/import" { if err := r.notices.Import(req.Context()); err != nil { writeError(w, http.StatusInternalServerError, "NOTICE_IMPORT_FAILED", err) return } items, _ := r.notices.List(100) writeJSON(w, http.StatusOK, map[string]any{"ok": true, "items": items}) return } if req.Method == http.MethodGet && path == "/api/admin/releases/notices" { items, err := r.notices.List(100) if err != nil { writeError(w, http.StatusInternalServerError, "NOTICE_LIST_FAILED", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "items": items}) return } rest := strings.TrimPrefix(path, "/api/admin/releases/notices/") if rest == "" || rest == path { http.NotFound(w, req) return } parts := strings.Split(rest, "/") version := parts[0] if req.Method == http.MethodGet && len(parts) == 1 { doc, err := r.notices.Get(version) if err != nil { writeError(w, http.StatusNotFound, "NOTICE_NOT_FOUND", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "document": doc}) return } if req.Method == http.MethodPut && len(parts) == 1 { var body notices.SaveRequest if err := json.NewDecoder(req.Body).Decode(&body); err != nil { writeError(w, http.StatusBadRequest, "INVALID_PAYLOAD", err) return } doc, err := r.notices.Save(req.Context(), version, body, "admin") if err != nil { writeError(w, http.StatusBadRequest, "NOTICE_SAVE_FAILED", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "document": doc}) return } if req.Method == http.MethodPost && len(parts) == 2 && parts[1] == "validate" { var body notices.SaveRequest if err := json.NewDecoder(req.Body).Decode(&body); err != nil { writeError(w, http.StatusBadRequest, "INVALID_PAYLOAD", err) return } doc, err := r.notices.Validate(req.Context(), version, body) if err != nil { writeError(w, http.StatusBadRequest, "NOTICE_VALIDATE_FAILED", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "document": doc}) return } if req.Method == http.MethodPost && len(parts) == 2 && parts[1] == "restore" { var body struct { RevisionID int64 `json:"revisionId"` } if err := json.NewDecoder(req.Body).Decode(&body); err != nil || body.RevisionID <= 0 { writeError(w, http.StatusBadRequest, "INVALID_PAYLOAD", errors.New("revisionId is required")) return } doc, err := r.notices.Restore(req.Context(), version, body.RevisionID, "admin") if err != nil { writeError(w, http.StatusBadRequest, "NOTICE_RESTORE_FAILED", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "document": doc}) return } http.NotFound(w, req) }