diff --git a/internal/api/v2/remotes.go b/internal/api/v2/remotes.go index 838532d..aad0b58 100644 --- a/internal/api/v2/remotes.go +++ b/internal/api/v2/remotes.go @@ -141,6 +141,20 @@ func (h *RemotesHandler) update(w http.ResponseWriter, r *http.Request) { return } remote.Name = name + if remote.RepoType == "" && oldKnown { + remote.RepoType = existing.RepoType + } + if remote.RepoType == "" { + remote.RepoType = models.RepoTypeRemote + } + if !remote.RepoType.Valid() { + http.Error(w, fmt.Sprintf("invalid repo type: %q", remote.RepoType), http.StatusBadRequest) + return + } + if remote.RepoType == models.RepoTypeRemote && remote.BaseURL == "" { + http.Error(w, "base_url is required for remote repositories", http.StatusBadRequest) + return + } if err := remote.ValidateMirrorlist(); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return diff --git a/internal/api/v2/remotes_ttl_test.go b/internal/api/v2/remotes_ttl_test.go index 47a990b..39548d3 100644 --- a/internal/api/v2/remotes_ttl_test.go +++ b/internal/api/v2/remotes_ttl_test.go @@ -172,3 +172,45 @@ func TestQuarantineDaysValidation(t *testing.T) { } } } + +func TestRepoTypeKeptOnUpdate(t *testing.T) { + if testDSN == "" { + t.Skip("Docker unavailable") + } + db, err := database.New(testDSN) + if err != nil { + t.Fatal(err) + } + defer db.Close() + h := NewRemotesHandler(db, nil, nil).Routes() + repoType := func(name string) string { + t.Helper() + r, err := db.GetRemote(context.Background(), name) + if err != nil { + t.Fatalf("get %s: %v", name, err) + } + return string(r.RepoType) + } + const body = `"package_type":"rpm","base_url":"https://example.com/repo"` + for i, s := range []struct { + method, path, json string + code int + name, want string + }{ + {"POST", "/", `{"name":"rt-remote",` + body + `}`, 201, "rt-remote", "remote"}, + {"PUT", "/rt-remote", `{` + body + `}`, 200, "rt-remote", "remote"}, + {"POST", "/", `{"name":"rt-local","package_type":"rpm","repo_type":"local"}`, 201, "rt-local", "local"}, + {"PUT", "/rt-local", `{"package_type":"rpm","description":"x"}`, 200, "rt-local", "local"}, + {"PUT", "/rt-local", `{"package_type":"rpm","repo_type":""}`, 200, "rt-local", "local"}, + {"PUT", "/rt-local", `{"package_type":"rpm","repo_type":"bogus"}`, 400, "rt-local", "local"}, + {"PUT", "/rt-local", `{"package_type":"rpm","repo_type":"remote"}`, 400, "rt-local", "local"}, + {"PUT", "/rt-remote", `{"package_type":"rpm","repo_type":"local"}`, 200, "rt-remote", "local"}, + } { + if c := do(t, h, s.method, s.path, s.json); c != s.code { + t.Fatalf("step %d %s %s = %d, want %d", i, s.method, s.path, c, s.code) + } + if got := repoType(s.name); got != s.want { + t.Errorf("step %d: repo_type = %q, want %q", i, got, s.want) + } + } +} diff --git a/migrations/0011_remote_repo_type_repair.sql b/migrations/0011_remote_repo_type_repair.sql new file mode 100644 index 0000000..9c4d7b2 --- /dev/null +++ b/migrations/0011_remote_repo_type_repair.sql @@ -0,0 +1 @@ +UPDATE remotes SET repo_type = CASE WHEN COALESCE(base_url, '') = '' THEN 'local' ELSE 'remote' END WHERE repo_type = '' OR repo_type IS NULL;