From a157fc1cc3035f90f6d70b132009ab7360600232 Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Wed, 18 Jun 2025 13:16:57 +0300 Subject: [PATCH] feat(launcher): implement modpack manifest API endpoint --- cmd/server/main.go | 4 +++ internal/api/launcher_handler.go | 37 +++++++++++++++++++++++++ internal/database/modpack_repository.go | 36 +++++++++++++++++++++++- internal/models/launcher.go | 8 ++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 internal/api/launcher_handler.go create mode 100644 internal/models/launcher.go diff --git a/cmd/server/main.go b/cmd/server/main.go index 3eb8f4f..a0c0804 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -26,6 +26,7 @@ func main() { serverPoller := &core.ServerPoller{Repo: serverRepo} modpackHandler := &api.ModpackHandler{ModpackRepo: modpackRepo} + launcherHandler := &api.LauncherHandler{ModpackRepo: modpackRepo} // Запускаем поллер в фоновой горутине go serverPoller.Start(context.Background()) @@ -69,6 +70,9 @@ func main() { r.Post("/join", authHandler.Join) r.Get("/profile/{uuid}", profileHandler.GetProfile) }) + r.Route("/launcher", func(r chi.Router) { + r.Get("/modpacks/{name}/manifest", launcherHandler.GetModpackManifest) + }) // --- Защищенные роуты --- r.Group(func(r chi.Router) { diff --git a/internal/api/launcher_handler.go b/internal/api/launcher_handler.go new file mode 100644 index 0000000..ab9b73d --- /dev/null +++ b/internal/api/launcher_handler.go @@ -0,0 +1,37 @@ +package api + +import ( + "encoding/json" + "errors" + "net/http" + + "gitea.mrixs.me/minecraft-platform/backend/internal/database" + "github.com/go-chi/chi/v5" + "github.com/jackc/pgx/v5" +) + +type LauncherHandler struct { + ModpackRepo *database.ModpackRepository +} + +func (h *LauncherHandler) GetModpackManifest(w http.ResponseWriter, r *http.Request) { + modpackName := chi.URLParam(r, "name") + if modpackName == "" { + http.Error(w, "Modpack name is required", http.StatusBadRequest) + return + } + + manifest, err := h.ModpackRepo.GetModpackManifest(r.Context(), modpackName) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + http.Error(w, "Modpack not found or not active", http.StatusNotFound) + return + } + http.Error(w, "Failed to get modpack manifest", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(manifest) +} diff --git a/internal/database/modpack_repository.go b/internal/database/modpack_repository.go index d3790fb..8da2ce4 100644 --- a/internal/database/modpack_repository.go +++ b/internal/database/modpack_repository.go @@ -10,7 +10,7 @@ import ( ) type ModpackRepository struct { - DB *pgxpool.Pool // <--- ИЗМЕНЕНИЕ + DB *pgxpool.Pool } // CreateModpackTx создает модпак и все его файлы в одной транзакции. @@ -47,3 +47,37 @@ func (r *ModpackRepository) CreateModpackTx(ctx context.Context, modpack *models return tx.Commit(ctx) } + +// GetModpackManifest возвращает список файлов для указанного модпака. +func (r *ModpackRepository) GetModpackManifest(ctx context.Context, modpackName string) ([]models.ManifestEntry, error) { + query := ` + SELECT mf.relative_path, mf.file_hash, mf.file_size + FROM modpack_files mf + JOIN modpacks m ON mf.modpack_id = m.id + WHERE m.name = $1 AND m.is_active = TRUE` + + rows, err := r.DB.Query(ctx, query, modpackName) + if err != nil { + return nil, err + } + defer rows.Close() + + var manifest []models.ManifestEntry + for rows.Next() { + var entry models.ManifestEntry + if err := rows.Scan(&entry.Path, &entry.Hash, &entry.Size); err != nil { + return nil, err + } + manifest = append(manifest, entry) + } + + if len(manifest) == 0 { + var exists bool + err := r.DB.QueryRow(ctx, "SELECT EXISTS(SELECT 1 FROM modpacks WHERE name = $1)", modpackName).Scan(&exists) + if err != nil || !exists { + return nil, pgx.ErrNoRows + } + } + + return manifest, nil +} diff --git a/internal/models/launcher.go b/internal/models/launcher.go new file mode 100644 index 0000000..93824ae --- /dev/null +++ b/internal/models/launcher.go @@ -0,0 +1,8 @@ +package models + +// ManifestEntry представляет один файл в манифесте модпака. +type ManifestEntry struct { + Path string `json:"path"` + Hash string `json:"hash"` + Size int64 `json:"size"` +}