releases.go
·
150 строк
·
4.6 KB
| 1 | package handlers |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "html/template" |
| 6 | "strconv" |
| 7 | "strings" |
| 8 | |
| 9 | "github.com/gofiber/fiber/v2" |
| 10 | |
| 11 | "polar/internal/git" |
| 12 | "polar/internal/repo" |
| 13 | "polar/internal/web/middleware" |
| 14 | "polar/internal/web/views" |
| 15 | ) |
| 16 | |
| 17 | type ReleaseViewItem struct { |
| 18 | repo.Release |
| 19 | BodyHTML template.HTML |
| 20 | } |
| 21 | |
| 22 | func (h *Handler) RepoReleasesPage(c *fiber.Ctx) error { |
| 23 | r, diskPath, err := h.loadAuthorizedRepo(c, false) |
| 24 | if err != nil { |
| 25 | return nil |
| 26 | } |
| 27 | if !r.EnableReleases { |
| 28 | return h.renderErrorT(c, fiber.StatusNotFound, "err.not_found", "err.releases_disabled") |
| 29 | } |
| 30 | |
| 31 | releases, _ := h.repoSvc.ListReleases(c.UserContext(), r.ID) |
| 32 | tags, _ := git.ListTags(c.UserContext(), diskPath) |
| 33 | |
| 34 | var releaseItems []ReleaseViewItem |
| 35 | for _, rel := range releases { |
| 36 | rendered := views.RenderMarkdown([]byte(rel.Body)) |
| 37 | releaseItems = append(releaseItems, ReleaseViewItem{ |
| 38 | Release: rel, |
| 39 | BodyHTML: rendered, |
| 40 | }) |
| 41 | } |
| 42 | |
| 43 | viewer := middleware.CurrentUser(c) |
| 44 | data := h.baseData(c, h.tr(c, "releases.title")+" · "+r.OwnerUsername+"/"+r.Name) |
| 45 | data["Repo"] = r |
| 46 | data["Ref"] = r.DefaultBranch |
| 47 | data["ActiveTab"] = "releases" |
| 48 | data["CanWrite"] = h.repoSvc.CanWrite(c.UserContext(), r, viewer) |
| 49 | data["CloneURL"] = h.cloneURL(c, r.OwnerUsername, r.Name) |
| 50 | data["Releases"] = releaseItems |
| 51 | data["TagCount"] = len(tags) |
| 52 | |
| 53 | return h.render(c, fiber.StatusOK, "releases", data) |
| 54 | } |
| 55 | |
| 56 | func (h *Handler) RepoNewReleasePage(c *fiber.Ctx) error { |
| 57 | r, diskPath, err := h.loadAuthorizedRepo(c, true) |
| 58 | if err != nil { |
| 59 | return nil |
| 60 | } |
| 61 | if !r.EnableReleases { |
| 62 | return h.renderErrorT(c, fiber.StatusNotFound, "err.not_found", "err.releases_disabled") |
| 63 | } |
| 64 | |
| 65 | tags, _ := git.ListTags(c.UserContext(), diskPath) |
| 66 | branches, _ := git.ListBranches(c.UserContext(), diskPath) |
| 67 | selectedTag := strings.TrimSpace(c.Query("tag")) |
| 68 | |
| 69 | viewer := middleware.CurrentUser(c) |
| 70 | data := h.baseData(c, h.tr(c, "releases.new")+" · "+r.OwnerUsername+"/"+r.Name) |
| 71 | data["Repo"] = r |
| 72 | data["Ref"] = r.DefaultBranch |
| 73 | data["ActiveTab"] = "releases" |
| 74 | data["CanWrite"] = h.repoSvc.CanWrite(c.UserContext(), r, viewer) |
| 75 | data["CloneURL"] = h.cloneURL(c, r.OwnerUsername, r.Name) |
| 76 | data["Tags"] = tags |
| 77 | data["Branches"] = branches |
| 78 | data["SelectedTag"] = selectedTag |
| 79 | |
| 80 | return h.render(c, fiber.StatusOK, "release_new", data) |
| 81 | } |
| 82 | |
| 83 | func (h *Handler) RepoNewReleaseSubmit(c *fiber.Ctx) error { |
| 84 | r, diskPath, err := h.loadAuthorizedRepo(c, true) |
| 85 | if err != nil { |
| 86 | return nil |
| 87 | } |
| 88 | if !r.EnableReleases { |
| 89 | return h.renderErrorT(c, fiber.StatusNotFound, "err.not_found", "err.releases_disabled") |
| 90 | } |
| 91 | |
| 92 | u := middleware.CurrentUser(c) |
| 93 | tagName := strings.TrimSpace(c.FormValue("tag_name")) |
| 94 | title := strings.TrimSpace(c.FormValue("title")) |
| 95 | body := strings.TrimSpace(c.FormValue("body")) |
| 96 | targetRef := strings.TrimSpace(c.FormValue("target_ref")) |
| 97 | if targetRef == "" { |
| 98 | targetRef = r.DefaultBranch |
| 99 | } |
| 100 | isPrerelease := c.FormValue("is_prerelease") == "true" || c.FormValue("is_prerelease") == "on" || c.FormValue("is_prerelease") == "1" |
| 101 | |
| 102 | if tagName == "" { |
| 103 | h.setFlashT(c, "flash.release_tag_empty") |
| 104 | return c.Redirect(fmt.Sprintf("/%s/%s/releases/new", r.OwnerUsername, r.Name), fiber.StatusSeeOther) |
| 105 | } |
| 106 | |
| 107 | // Create git tag if it doesn't exist yet |
| 108 | existingTags, _ := git.ListTags(c.UserContext(), diskPath) |
| 109 | tagExists := false |
| 110 | for _, t := range existingTags { |
| 111 | if t == tagName { |
| 112 | tagExists = true |
| 113 | break |
| 114 | } |
| 115 | } |
| 116 | if !tagExists { |
| 117 | if err := git.CreateTag(c.UserContext(), diskPath, tagName, targetRef, title); err != nil { |
| 118 | h.setFlashT(c, "flash.release_tag_failed", err.Error()) |
| 119 | return c.Redirect(fmt.Sprintf("/%s/%s/releases/new", r.OwnerUsername, r.Name), fiber.StatusSeeOther) |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | if _, err := h.repoSvc.CreateRelease(c.UserContext(), r.ID, u.ID, tagName, title, body, false, isPrerelease); err != nil { |
| 124 | h.setFlashT(c, "flash.release_create_failed", err.Error()) |
| 125 | return c.Redirect(fmt.Sprintf("/%s/%s/releases/new", r.OwnerUsername, r.Name), fiber.StatusSeeOther) |
| 126 | } |
| 127 | |
| 128 | h.repoSvc.TouchUpdatedAt(c.UserContext(), r.ID) |
| 129 | h.setFlashT(c, "flash.release_published", tagName) |
| 130 | return c.Redirect(fmt.Sprintf("/%s/%s/releases", r.OwnerUsername, r.Name), fiber.StatusSeeOther) |
| 131 | } |
| 132 | |
| 133 | func (h *Handler) RepoDeleteReleaseSubmit(c *fiber.Ctx) error { |
| 134 | r, _, err := h.loadAuthorizedRepo(c, true) |
| 135 | if err != nil { |
| 136 | return nil |
| 137 | } |
| 138 | if !r.EnableReleases { |
| 139 | return h.renderErrorT(c, fiber.StatusNotFound, "err.not_found", "err.releases_disabled") |
| 140 | } |
| 141 | |
| 142 | relID, _ := strconv.ParseInt(c.Params("id"), 10, 64) |
| 143 | if err := h.repoSvc.DeleteRelease(c.UserContext(), relID); err != nil { |
| 144 | h.setFlashT(c, "flash.release_delete_failed", err.Error()) |
| 145 | } else { |
| 146 | h.setFlashT(c, "flash.release_deleted") |
| 147 | } |
| 148 | |
| 149 | return c.Redirect(fmt.Sprintf("/%s/%s/releases", r.OwnerUsername, r.Name), fiber.StatusSeeOther) |
| 150 | } |
| 151 |