From 773bd91232ea880ba83841a3d2492c7c794d3ef0 Mon Sep 17 00:00:00 2001
From: eyad-hussein <eyad.hussein@ejust.edu.eg>
Date: Wed, 10 Jul 2024 10:32:46 +0300
Subject: [PATCH] api: create endpoint for changing project status

---
 routers/api/v1/api.go         | 32 +++++++++++++++++++++++++++++++-
 routers/api/v1/org/project.go | 22 ++++++++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index be72565a16..90ba816b65 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -811,6 +811,28 @@ func checkDeprecatedAuthMethods(ctx *context.APIContext) {
 	}
 }
 
+func reqUnitAccess(unitType unit.Type, accessMode perm.AccessMode, ignoreGlobal bool) func(ctx *context.APIContext) {
+	return func(ctx *context.APIContext) {
+		// only check global disabled units when ignoreGlobal is false
+		if !ignoreGlobal && unitType.UnitGlobalDisabled() {
+			ctx.NotFound("Repo unit is is disabled: "+unitType.LogString(), nil)
+			return
+		}
+
+		if ctx.ContextUser == nil {
+			ctx.NotFound("ContextUser is nil", nil)
+			return
+		}
+
+		if ctx.ContextUser.IsOrganization() {
+			if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode {
+				ctx.NotFound("ContextUser is org but doer has no access to unit", nil)
+				return
+			}
+		}
+	}
+}
+
 // Routes registers all v1 APIs routes to web application.
 func Routes() *web.Router {
 	m := web.NewRouter()
@@ -960,8 +982,16 @@ func Routes() *web.Router {
 
 				m.Group("/projects", func() {
 					m.Post("", bind(api.CreateProjectOption{}), org.CreateProject)
+					m.Group("/{id}", func() {
+						m.Post("/{action:open|close}", org.ChangeProjectStatus)
+					})
 				})
-			}, context.UserAssignmentAPI())
+			}, context.UserAssignmentAPI(), reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.APIContext) {
+				if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
+					ctx.NotFound("NewProject", nil)
+					return
+				}
+			})
 		}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
 
 		// Users (requires user scope)
diff --git a/routers/api/v1/org/project.go b/routers/api/v1/org/project.go
index a7344cfa4d..9415727b28 100644
--- a/routers/api/v1/org/project.go
+++ b/routers/api/v1/org/project.go
@@ -10,6 +10,7 @@ import (
 	"code.gitea.io/gitea/services/context"
 )
 
+// CreateProject creates a new project
 func CreateProject(ctx *context.APIContext) {
 	form := web.GetForm(ctx).(*api.CreateProjectOption)
 
@@ -37,3 +38,24 @@ func CreateProject(ctx *context.APIContext) {
 
 	ctx.JSON(http.StatusCreated, map[string]int64{"id": project.ID})
 }
+
+// ChangeProjectStatus updates the status of a project between "open" and "close"
+func ChangeProjectStatus(ctx *context.APIContext) {
+	var toClose bool
+	switch ctx.PathParam(":action") {
+	case "open":
+		toClose = false
+	case "close":
+		toClose = true
+	default:
+		ctx.NotFound("ChangeProjectStatus", nil)
+		return
+	}
+	id := ctx.PathParamInt64(":id")
+
+	if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, 0, id, toClose); err != nil {
+		ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
+		return
+	}
+	ctx.JSON(http.StatusOK, map[string]any{"message": "project status updated successfully"})
+}