202 lines
5.8 KiB
Go
202 lines
5.8 KiB
Go
package public
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/mattnite/forgejo-tickets/internal/auth"
|
|
"github.com/mattnite/forgejo-tickets/internal/forgejo"
|
|
"github.com/mattnite/forgejo-tickets/internal/models"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type TicketHandler struct {
|
|
deps Dependencies
|
|
}
|
|
|
|
func (h *TicketHandler) List(c *gin.Context) {
|
|
user := auth.CurrentUser(c)
|
|
|
|
var tickets []models.Ticket
|
|
if err := h.deps.DB.Preload("Repo").Where("user_id = ?", user.ID).Order("created_at DESC").Limit(50).Find(&tickets).Error; err != nil {
|
|
log.Error().Err(err).Msg("list tickets error")
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusInternalServerError, "Failed to load tickets")
|
|
return
|
|
}
|
|
|
|
h.deps.Renderer.Render(c.Writer, c.Request, "tickets/list", map[string]interface{}{
|
|
"Tickets": tickets,
|
|
})
|
|
}
|
|
|
|
func (h *TicketHandler) NewForm(c *gin.Context) {
|
|
var repos []models.Repo
|
|
if err := h.deps.DB.Where("active = ?", true).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 products")
|
|
return
|
|
}
|
|
|
|
h.deps.Renderer.Render(c.Writer, c.Request, "tickets/new", map[string]interface{}{
|
|
"Repos": repos,
|
|
})
|
|
}
|
|
|
|
func (h *TicketHandler) Create(c *gin.Context) {
|
|
user := auth.CurrentUser(c)
|
|
|
|
repoID, err := uuid.Parse(c.PostForm("repo_id"))
|
|
if err != nil {
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusBadRequest, "Invalid product selection")
|
|
return
|
|
}
|
|
|
|
title := c.PostForm("title")
|
|
description := c.PostForm("description")
|
|
|
|
if title == "" || description == "" {
|
|
var repos []models.Repo
|
|
h.deps.DB.Where("active = ?", true).Order("name ASC").Find(&repos)
|
|
h.deps.Renderer.Render(c.Writer, c.Request, "tickets/new", map[string]interface{}{
|
|
"Repos": repos,
|
|
"Error": "Title and description are required",
|
|
"Title": title,
|
|
"Description": description,
|
|
"RepoID": repoID.String(),
|
|
})
|
|
return
|
|
}
|
|
|
|
ticket := models.Ticket{
|
|
UserID: user.ID,
|
|
RepoID: repoID,
|
|
Title: title,
|
|
Description: description,
|
|
}
|
|
|
|
if err := h.deps.DB.Create(&ticket).Error; err != nil {
|
|
log.Error().Err(err).Msg("create ticket error")
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusInternalServerError, "Failed to create ticket")
|
|
return
|
|
}
|
|
|
|
// Async Forgejo issue creation
|
|
var repo models.Repo
|
|
if err := h.deps.DB.First(&repo, "id = ?", repoID).Error; err == nil {
|
|
go func() {
|
|
issue, err := h.deps.ForgejoClient.CreateIssue(repo.ForgejoOwner, repo.ForgejoRepo, forgejo.CreateIssueRequest{
|
|
Title: title,
|
|
Body: description + "\n\n---\n*Submitted by: " + user.Email + "*",
|
|
})
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("forgejo create issue error for ticket %s", ticket.ID)
|
|
return
|
|
}
|
|
h.deps.DB.Model(&ticket).Update("forgejo_issue_number", issue.Number)
|
|
}()
|
|
}
|
|
|
|
c.Redirect(http.StatusSeeOther, "/tickets/"+ticket.ID.String())
|
|
}
|
|
|
|
func (h *TicketHandler) Detail(c *gin.Context) {
|
|
user := auth.CurrentUser(c)
|
|
|
|
ticketID, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusBadRequest, "Invalid ticket ID")
|
|
return
|
|
}
|
|
|
|
var ticket models.Ticket
|
|
if err := h.deps.DB.First(&ticket, "id = ?", ticketID).Error; err != nil {
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusNotFound, "Ticket not found")
|
|
return
|
|
}
|
|
|
|
if ticket.UserID != user.ID {
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusForbidden, "Access denied")
|
|
return
|
|
}
|
|
|
|
var repo models.Repo
|
|
h.deps.DB.First(&repo, "id = ?", ticket.RepoID)
|
|
|
|
var comments []struct {
|
|
models.TicketComment
|
|
UserName string
|
|
UserEmail string
|
|
}
|
|
h.deps.DB.Table("ticket_comments").
|
|
Select("ticket_comments.*, users.name as user_name, users.email as user_email").
|
|
Joins("JOIN users ON users.id = ticket_comments.user_id").
|
|
Where("ticket_comments.ticket_id = ?", ticket.ID).
|
|
Order("ticket_comments.created_at ASC").
|
|
Scan(&comments)
|
|
|
|
h.deps.Renderer.Render(c.Writer, c.Request, "tickets/detail", map[string]interface{}{
|
|
"Ticket": ticket,
|
|
"Repo": repo,
|
|
"Comments": comments,
|
|
})
|
|
}
|
|
|
|
func (h *TicketHandler) AddComment(c *gin.Context) {
|
|
user := auth.CurrentUser(c)
|
|
|
|
ticketID, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusBadRequest, "Invalid ticket ID")
|
|
return
|
|
}
|
|
|
|
var ticket models.Ticket
|
|
if err := h.deps.DB.First(&ticket, "id = ?", ticketID).Error; err != nil {
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusNotFound, "Ticket not found")
|
|
return
|
|
}
|
|
|
|
if ticket.UserID != user.ID {
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusForbidden, "Access denied")
|
|
return
|
|
}
|
|
|
|
body := c.PostForm("body")
|
|
if body == "" {
|
|
c.Redirect(http.StatusSeeOther, "/tickets/"+ticket.ID.String())
|
|
return
|
|
}
|
|
|
|
comment := models.TicketComment{
|
|
TicketID: ticket.ID,
|
|
UserID: user.ID,
|
|
Body: body,
|
|
}
|
|
|
|
if err := h.deps.DB.Create(&comment).Error; err != nil {
|
|
log.Error().Err(err).Msg("create comment error")
|
|
h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusInternalServerError, "Failed to add comment")
|
|
return
|
|
}
|
|
|
|
// Async sync to Forgejo
|
|
if ticket.ForgejoIssueNumber != nil {
|
|
var repo models.Repo
|
|
if err := h.deps.DB.First(&repo, "id = ?", ticket.RepoID).Error; err == nil {
|
|
go func() {
|
|
forgejoComment, err := h.deps.ForgejoClient.CreateComment(repo.ForgejoOwner, repo.ForgejoRepo, *ticket.ForgejoIssueNumber, forgejo.CreateCommentRequest{
|
|
Body: body + "\n\n---\n*Comment by: " + user.Email + "*",
|
|
})
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("forgejo create comment error")
|
|
return
|
|
}
|
|
h.deps.DB.Model(&comment).Update("forgejo_comment_id", forgejoComment.ID)
|
|
}()
|
|
}
|
|
}
|
|
|
|
c.Redirect(http.StatusSeeOther, "/tickets/"+ticket.ID.String())
|
|
}
|