package admin import ( "net/http" "strings" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/mattnite/forgejo-tickets/internal/forgejo" "github.com/mattnite/forgejo-tickets/internal/models" "github.com/rs/zerolog/log" ) type RepoHandler struct { deps Dependencies } func (h *RepoHandler) List(c *gin.Context) { var repos []models.Repo if err := h.deps.DB.Order("name ASC").Find(&repos).Error; err != nil { log.Error().Err(err).Msg("list repos error") h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusInternalServerError, "Failed to load repos") return } h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/list", map[string]interface{}{ "Repos": repos, "BaseURL": h.deps.Config.BaseURL, }) } func (h *RepoHandler) NewForm(c *gin.Context) { h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/new", nil) } func (h *RepoHandler) Create(c *gin.Context) { name := strings.TrimSpace(c.PostForm("name")) slug := strings.TrimSpace(c.PostForm("slug")) forgejoOwner := strings.TrimSpace(c.PostForm("forgejo_owner")) forgejoRepo := strings.TrimSpace(c.PostForm("forgejo_repo")) active := c.PostForm("active") == "on" if name == "" || slug == "" || forgejoOwner == "" || forgejoRepo == "" { h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/new", map[string]interface{}{ "Error": "All fields are required", "Name": name, "Slug": slug, "ForgejoOwner": forgejoOwner, "ForgejoRepo": forgejoRepo, }) return } // Verify the bot user has write access to the Forgejo repo if err := h.deps.ForgejoClient.CheckRepoPermission(forgejoOwner, forgejoRepo); err != nil { log.Error().Err(err).Msg("repo permission check failed") h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/new", map[string]interface{}{ "Error": "Forgejo repo check failed: " + err.Error(), "Name": name, "Slug": slug, "ForgejoOwner": forgejoOwner, "ForgejoRepo": forgejoRepo, }) return } webhookSecret, err := forgejo.GenerateWebhookSecret() if err != nil { log.Error().Err(err).Msg("generate webhook secret error") h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/new", map[string]interface{}{ "Error": "Failed to generate webhook secret", "Name": name, "Slug": slug, "ForgejoOwner": forgejoOwner, "ForgejoRepo": forgejoRepo, }) return } repo := models.Repo{ Name: name, Slug: slug, ForgejoOwner: forgejoOwner, ForgejoRepo: forgejoRepo, WebhookSecret: webhookSecret, Active: active, } if ssoKey := strings.TrimSpace(c.PostForm("sso_public_key")); ssoKey != "" { repo.SSOPublicKey = &ssoKey } if err := h.deps.DB.Create(&repo).Error; err != nil { log.Error().Err(err).Msg("create repo error") h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/new", map[string]interface{}{ "Error": "Failed to create repo: " + err.Error(), "Name": name, "Slug": slug, "ForgejoOwner": forgejoOwner, "ForgejoRepo": forgejoRepo, }) return } c.Redirect(http.StatusSeeOther, "/repos/"+repo.ID.String()+"/edit") } func (h *RepoHandler) EditForm(c *gin.Context) { repoID, err := uuid.Parse(c.Param("id")) if err != nil { h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusBadRequest, "Invalid repo ID") return } var repo models.Repo if err := h.deps.DB.First(&repo, "id = ?", repoID).Error; err != nil { h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusNotFound, "Repo not found") return } // Check bot write access to the Forgejo repo var repoPermErr string if err := h.deps.ForgejoClient.CheckRepoPermission(repo.ForgejoOwner, repo.ForgejoRepo); err != nil { repoPermErr = err.Error() } h.deps.Renderer.Render(c.Writer, c.Request, "admin/repos/edit", map[string]interface{}{ "Repo": repo, "BaseURL": h.deps.Config.BaseURL, "ForgejoURL": h.deps.Config.ForgejoURL, "RepoPermErr": repoPermErr, }) } func (h *RepoHandler) Update(c *gin.Context) { repoID, err := uuid.Parse(c.Param("id")) if err != nil { h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusBadRequest, "Invalid repo ID") return } name := strings.TrimSpace(c.PostForm("name")) slug := strings.TrimSpace(c.PostForm("slug")) forgejoOwner := strings.TrimSpace(c.PostForm("forgejo_owner")) forgejoRepo := strings.TrimSpace(c.PostForm("forgejo_repo")) active := c.PostForm("active") == "on" var ssoPublicKey *string if ssoKey := strings.TrimSpace(c.PostForm("sso_public_key")); ssoKey != "" { ssoPublicKey = &ssoKey } if err := h.deps.DB.Model(&models.Repo{}).Where("id = ?", repoID).Updates(map[string]interface{}{ "name": name, "slug": slug, "forgejo_owner": forgejoOwner, "forgejo_repo": forgejoRepo, "active": active, "sso_public_key": ssoPublicKey, }).Error; err != nil { log.Error().Err(err).Msg("update repo error") h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusInternalServerError, "Failed to update repo") return } c.Redirect(http.StatusSeeOther, "/repos/"+repoID.String()+"/edit") }