feat(project): SSE push updates for project board pages (#7)
This commit was merged in pull request #7.
This commit is contained in:
@@ -875,6 +875,7 @@ func Routes() *web.Router {
|
||||
}))
|
||||
}
|
||||
|
||||
m.AfterRouting(common.SessionTagMiddleware())
|
||||
m.AfterRouting(context.APIContexter())
|
||||
m.AfterRouting(checkDeprecatedAuthMethods)
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ func DeleteOrgProject(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteProjectByID(ctx, project.ID); err != nil {
|
||||
if err := project_service.DeleteProject(ctx, project.ID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -468,7 +468,7 @@ func CreateOrgProjectColumn(ctx *context.APIContext) {
|
||||
CreatorID: ctx.Doer.ID,
|
||||
}
|
||||
|
||||
if err := project_model.NewColumn(ctx, column); err != nil {
|
||||
if err := project_service.CreateColumn(ctx, column); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -545,7 +545,7 @@ func EditOrgProjectColumn(ctx *context.APIContext) {
|
||||
column.Sorting = int8(*form.Sorting)
|
||||
}
|
||||
|
||||
if err := project_model.UpdateColumn(ctx, column); err != nil {
|
||||
if err := project_service.EditColumn(ctx, column); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -592,7 +592,7 @@ func DeleteOrgProjectColumn(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteColumnByID(ctx, column.ID); err != nil {
|
||||
if err := project_service.DeleteColumn(ctx, column.ID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -811,7 +811,7 @@ func assignIssueToOrgProjectColumn(ctx *context.APIContext, add bool) {
|
||||
newProjectIDs = append(newProjectIDs, id)
|
||||
}
|
||||
}
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -821,7 +821,7 @@ func assignIssueToOrgProjectColumn(ctx *context.APIContext, add bool) {
|
||||
newProjectIDs := make([]int64, len(currentProjectIDs)+1)
|
||||
copy(newProjectIDs, currentProjectIDs)
|
||||
newProjectIDs[len(currentProjectIDs)] = column.ProjectID
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
project_service "code.gitea.io/gitea/services/projects"
|
||||
)
|
||||
|
||||
// buildSearchIssuesRepoIDs builds the list of repository IDs for issue search based on query parameters.
|
||||
@@ -915,7 +916,7 @@ func EditIssue(ctx *context.APIContext) {
|
||||
|
||||
// Update projects if provided
|
||||
if canWrite && form.Projects != nil {
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, *form.Projects); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, *form.Projects); err != nil {
|
||||
if errors.Is(err, util.ErrPermissionDenied) || errors.Is(err, util.ErrNotExist) {
|
||||
ctx.APIError(http.StatusBadRequest, err)
|
||||
} else {
|
||||
|
||||
@@ -371,7 +371,7 @@ func DeleteProject(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteProjectByID(ctx, project.ID); err != nil {
|
||||
if err := project_service.DeleteProject(ctx, project.ID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -505,7 +505,7 @@ func CreateProjectColumn(ctx *context.APIContext) {
|
||||
CreatorID: ctx.Doer.ID,
|
||||
}
|
||||
|
||||
if err := project_model.NewColumn(ctx, column); err != nil {
|
||||
if err := project_service.CreateColumn(ctx, column); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -587,7 +587,7 @@ func EditProjectColumn(ctx *context.APIContext) {
|
||||
column.Sorting = int8(*form.Sorting)
|
||||
}
|
||||
|
||||
if err := project_model.UpdateColumn(ctx, column); err != nil {
|
||||
if err := project_service.EditColumn(ctx, column); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -639,7 +639,7 @@ func DeleteProjectColumn(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteColumnByID(ctx, column.ID); err != nil {
|
||||
if err := project_service.DeleteColumn(ctx, column.ID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -876,7 +876,7 @@ func assignIssueToProjectColumn(ctx *context.APIContext, add bool) {
|
||||
newProjectIDs = append(newProjectIDs, id)
|
||||
}
|
||||
}
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -888,7 +888,7 @@ func assignIssueToProjectColumn(ctx *context.APIContext, add bool) {
|
||||
newProjectIDs := make([]int64, len(currentProjectIDs)+1)
|
||||
copy(newProjectIDs, currentProjectIDs)
|
||||
newProjectIDs[len(currentProjectIDs)] = column.ProjectID
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ func DeleteUserProject(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteProjectByID(ctx, project.ID); err != nil {
|
||||
if err := project_service.DeleteProject(ctx, project.ID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -494,7 +494,7 @@ func CreateUserProjectColumn(ctx *context.APIContext) {
|
||||
CreatorID: ctx.Doer.ID,
|
||||
}
|
||||
|
||||
if err := project_model.NewColumn(ctx, column); err != nil {
|
||||
if err := project_service.CreateColumn(ctx, column); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -575,7 +575,7 @@ func EditUserProjectColumn(ctx *context.APIContext) {
|
||||
column.Sorting = int8(*form.Sorting)
|
||||
}
|
||||
|
||||
if err := project_model.UpdateColumn(ctx, column); err != nil {
|
||||
if err := project_service.EditColumn(ctx, column); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -626,7 +626,7 @@ func DeleteUserProjectColumn(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteColumnByID(ctx, column.ID); err != nil {
|
||||
if err := project_service.DeleteColumn(ctx, column.ID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -847,7 +847,7 @@ func assignIssueToUserProjectColumn(ctx *context.APIContext, add bool) {
|
||||
newProjectIDs = append(newProjectIDs, id)
|
||||
}
|
||||
}
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@@ -857,7 +857,7 @@ func assignIssueToUserProjectColumn(ctx *context.APIContext, add bool) {
|
||||
newProjectIDs := make([]int64, len(currentProjectIDs)+1)
|
||||
copy(newProjectIDs, currentProjectIDs)
|
||||
newProjectIDs[len(currentProjectIDs)] = column.ProjectID
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, newProjectIDs); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/services/project_events"
|
||||
)
|
||||
|
||||
// SessionTagHeader is the HTTP header browser tabs use to broadcast a
|
||||
// per-page-load identifier with every mutation request. The server
|
||||
// echoes it back inside SSE event payloads so the originating tab
|
||||
// can suppress its own event after applying the optimistic update.
|
||||
const SessionTagHeader = "X-Session-Tag"
|
||||
|
||||
// SessionTagMiddleware decorates each incoming request's context with
|
||||
// the X-Session-Tag header value when present. Service- and model-
|
||||
// layer publishers read the value via project_events.SessionTagFromContext.
|
||||
//
|
||||
// Empty / missing headers are a no-op.
|
||||
func SessionTagMiddleware() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tag := r.Header.Get(SessionTagHeader)
|
||||
if tag != "" {
|
||||
ctx := project_events.WithSessionTag(r.Context(), tag)
|
||||
r = r.WithContext(ctx)
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func DeleteProject(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteProjectByID(ctx, p.ID); err != nil {
|
||||
if err := project_service.DeleteProject(ctx, p.ID); err != nil {
|
||||
ctx.Flash.Error("DeleteProjectByID: " + err.Error())
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success"))
|
||||
@@ -283,7 +283,7 @@ func EditProjectPost(ctx *context.Context) {
|
||||
p.Title = form.Title
|
||||
p.Description = form.Content
|
||||
p.CardType = form.CardType
|
||||
if err = project_model.UpdateProject(ctx, p); err != nil {
|
||||
if err = project_service.UpdateProject(ctx, p, project_service.UpdateProjectOptions{}); err != nil {
|
||||
ctx.ServerError("UpdateProjects", err)
|
||||
return
|
||||
}
|
||||
@@ -496,7 +496,7 @@ func DeleteProjectColumn(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteColumnByID(ctx, ctx.PathParamInt64("columnID")); err != nil {
|
||||
if err := project_service.DeleteColumn(ctx, ctx.PathParamInt64("columnID")); err != nil {
|
||||
ctx.ServerError("DeleteProjectColumnByID", err)
|
||||
return
|
||||
}
|
||||
@@ -514,7 +514,7 @@ func AddColumnToProjectPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.NewColumn(ctx, &project_model.Column{
|
||||
if err := project_service.CreateColumn(ctx, &project_model.Column{
|
||||
ProjectID: project.ID,
|
||||
Title: form.Title,
|
||||
Color: form.Color,
|
||||
@@ -567,7 +567,7 @@ func EditProjectColumn(ctx *context.Context) {
|
||||
column.Sorting = form.Sorting
|
||||
}
|
||||
|
||||
if err := project_model.UpdateColumn(ctx, column); err != nil {
|
||||
if err := project_service.EditColumn(ctx, column); err != nil {
|
||||
ctx.ServerError("UpdateProjectColumn", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ func DeleteProject(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteProjectByID(ctx, p.ID); err != nil {
|
||||
if err := project_service.DeleteProject(ctx, p.ID); err != nil {
|
||||
ctx.Flash.Error("DeleteProjectByID: " + err.Error())
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success"))
|
||||
@@ -264,7 +264,7 @@ func EditProjectPost(ctx *context.Context) {
|
||||
p.Title = form.Title
|
||||
p.Description = form.Content
|
||||
p.CardType = form.CardType
|
||||
if err = project_model.UpdateProject(ctx, p); err != nil {
|
||||
if err = project_service.UpdateProject(ctx, p, project_service.UpdateProjectOptions{}); err != nil {
|
||||
ctx.ServerError("UpdateProjects", err)
|
||||
return
|
||||
}
|
||||
@@ -459,7 +459,7 @@ func UpdateIssueProject(ctx *context.Context) {
|
||||
projectIDs = filteredIDs
|
||||
var failedIssues []int64
|
||||
for _, issue := range issues {
|
||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectIDs); err != nil {
|
||||
if err := project_service.AssignOrRemoveProjects(ctx, issue, ctx.Doer, projectIDs); err != nil {
|
||||
if errors.Is(err, util.ErrPermissionDenied) {
|
||||
failedIssues = append(failedIssues, issue.ID)
|
||||
continue
|
||||
@@ -569,7 +569,7 @@ func DeleteProjectColumn(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.DeleteColumnByID(ctx, ctx.PathParamInt64("columnID")); err != nil {
|
||||
if err := project_service.DeleteColumn(ctx, ctx.PathParamInt64("columnID")); err != nil {
|
||||
ctx.ServerError("DeleteProjectColumnByID", err)
|
||||
return
|
||||
}
|
||||
@@ -597,7 +597,7 @@ func AddColumnToProjectPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.NewColumn(ctx, &project_model.Column{
|
||||
if err := project_service.CreateColumn(ctx, &project_model.Column{
|
||||
ProjectID: project.ID,
|
||||
Title: form.Title,
|
||||
Color: form.Color,
|
||||
@@ -672,7 +672,7 @@ func EditProjectColumn(ctx *context.Context) {
|
||||
column.Sorting = form.Sorting
|
||||
}
|
||||
|
||||
if err := project_model.UpdateColumn(ctx, column); err != nil {
|
||||
if err := project_service.EditColumn(ctx, column); err != nil {
|
||||
ctx.ServerError("UpdateProjectColumn", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
project_service "code.gitea.io/gitea/services/projects"
|
||||
)
|
||||
|
||||
// MoveColumns moves or keeps columns in a project and sorts them inside that project
|
||||
@@ -39,7 +40,7 @@ func MoveColumns(ctx *context.Context) {
|
||||
sortedColumnIDs[column.Sorting] = column.ColumnID
|
||||
}
|
||||
|
||||
if err = project_model.MoveColumnsOnProject(ctx, project, sortedColumnIDs); err != nil {
|
||||
if err = project_service.ReorderColumns(ctx, project, sortedColumnIDs); err != nil {
|
||||
ctx.ServerError("MoveColumnsOnProject", err)
|
||||
return
|
||||
}
|
||||
|
||||
+1
-1
@@ -295,7 +295,7 @@ func Routes() *web.Router {
|
||||
routes.Get("/ssh_info", misc.SSHInfo)
|
||||
routes.Get("/api/healthz", healthcheck.Check)
|
||||
|
||||
mid = append(mid, common.MustInitSessioner(), context.Contexter())
|
||||
mid = append(mid, common.SessionTagMiddleware(), common.MustInitSessioner(), context.Contexter())
|
||||
|
||||
// Get user from session if logged in.
|
||||
webAuth := newWebAuthMiddleware()
|
||||
|
||||
Reference in New Issue
Block a user