Various fixes and improvements (#1643)

- allow repo names to be case-insensitive
- improve backend error handling on DB get errors (record not found ->
404, else -> 500)
- replace magic numbers of http response codes
- unify the look and feel of cancel / save buttons on forms and view
them in one line

---------

Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
qwerty287 2023-03-19 13:52:58 +01:00 committed by GitHub
parent 42a115e19e
commit ade8e6d010
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 230 additions and 179 deletions

View file

@ -21,7 +21,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session" "github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
"github.com/woodpecker-ci/woodpecker/server/store" "github.com/woodpecker-ci/woodpecker/server/store"
@ -45,7 +44,7 @@ func GetAgent(c *gin.Context) {
agent, err := store.FromContext(c).AgentFind(agentID) agent, err := store.FromContext(c).AgentFind(agentID)
if err != nil { if err != nil {
c.String(http.StatusNotFound, "Cannot find agent. %s", err) handleDbGetError(c, err)
return return
} }
c.JSON(http.StatusOK, agent) c.JSON(http.StatusOK, agent)
@ -69,7 +68,7 @@ func PatchAgent(c *gin.Context) {
agent, err := _store.AgentFind(agentID) agent, err := _store.AgentFind(agentID)
if err != nil { if err != nil {
c.AbortWithStatus(http.StatusNotFound) handleDbGetError(c, err)
return return
} }
agent.Name = in.Name agent.Name = in.Name
@ -121,12 +120,12 @@ func DeleteAgent(c *gin.Context) {
agent, err := _store.AgentFind(agentID) agent, err := _store.AgentFind(agentID)
if err != nil { if err != nil {
c.String(http.StatusNotFound, "Cannot find user. %s", err) handleDbGetError(c, err)
return return
} }
if err = _store.AgentDelete(agent); err != nil { if err = _store.AgentDelete(agent); err != nil {
c.String(http.StatusInternalServerError, "Error deleting user. %s", err) c.String(http.StatusInternalServerError, "Error deleting user. %s", err)
return return
} }
c.String(http.StatusOK, "") c.String(http.StatusNoContent, "")
} }

View file

@ -19,10 +19,12 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/woodpecker-ci/woodpecker/server/store/types"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -36,7 +38,11 @@ func GetBadge(c *gin.Context) {
_store := store.FromContext(c) _store := store.FromContext(c)
repo, err := _store.GetRepoName(c.Param("owner") + "/" + c.Param("name")) repo, err := _store.GetRepoName(c.Param("owner") + "/" + c.Param("name"))
if err != nil || !repo.IsActive { if err != nil || !repo.IsActive {
c.AbortWithStatus(404) if err == nil || errors.Is(err, types.RecordNotExist) {
c.AbortWithStatus(http.StatusNotFound)
return
}
_ = c.AbortWithError(http.StatusInternalServerError, err)
return return
} }
@ -63,17 +69,17 @@ func GetCC(c *gin.Context) {
_store := store.FromContext(c) _store := store.FromContext(c)
repo, err := _store.GetRepoName(c.Param("owner") + "/" + c.Param("name")) repo, err := _store.GetRepoName(c.Param("owner") + "/" + c.Param("name"))
if err != nil { if err != nil {
c.AbortWithStatus(404) handleDbGetError(c, err)
return return
} }
pipelines, err := _store.GetPipelineList(repo, 1) pipelines, err := _store.GetPipelineList(repo, 1)
if err != nil || len(pipelines) == 0 { if err != nil || len(pipelines) == 0 {
c.AbortWithStatus(404) c.AbortWithStatus(http.StatusNotFound)
return return
} }
url := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, pipelines[0].Number) url := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, pipelines[0].Number)
cc := ccmenu.New(repo, pipelines[0], url) cc := ccmenu.New(repo, pipelines[0], url)
c.XML(200, cc) c.XML(http.StatusOK, cc)
} }

View file

@ -34,16 +34,16 @@ func GetCron(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
id, err := strconv.ParseInt(c.Param("cron"), 10, 64) id, err := strconv.ParseInt(c.Param("cron"), 10, 64)
if err != nil { if err != nil {
c.String(400, "Error parsing cron id. %s", err) c.String(http.StatusBadRequest, "Error parsing cron id. %s", err)
return return
} }
cron, err := store.FromContext(c).CronFind(repo, id) cron, err := store.FromContext(c).CronFind(repo, id)
if err != nil { if err != nil {
c.String(404, "Error getting cron %q. %s", id, err) handleDbGetError(c, err)
return return
} }
c.JSON(200, cron) c.JSON(http.StatusOK, cron)
} }
// RunCron starts a cron job now. // RunCron starts a cron job now.
@ -52,13 +52,13 @@ func RunCron(c *gin.Context) {
_store := store.FromContext(c) _store := store.FromContext(c)
id, err := strconv.ParseInt(c.Param("cron"), 10, 64) id, err := strconv.ParseInt(c.Param("cron"), 10, 64)
if err != nil { if err != nil {
c.String(400, "Error parsing cron id. %s", err) c.String(http.StatusBadRequest, "Error parsing cron id. %s", err)
return return
} }
cron, err := _store.CronFind(repo, id) cron, err := _store.CronFind(repo, id)
if err != nil { if err != nil {
c.String(http.StatusNotFound, "Error getting cron %q. %s", id, err) handleDbGetError(c, err)
return return
} }
@ -74,14 +74,14 @@ func RunCron(c *gin.Context) {
return return
} }
c.JSON(200, pl) c.JSON(http.StatusOK, pl)
} }
// PostCron persists the cron job to the database. // PostCron persists the cron job to the database.
func PostCron(c *gin.Context) { func PostCron(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
user := session.User(c) user := session.User(c)
store := store.FromContext(c) _store := store.FromContext(c)
forge := server.Config.Services.Forge forge := server.Config.Services.Forge
in := new(model.Cron) in := new(model.Cron)
@ -97,13 +97,13 @@ func PostCron(c *gin.Context) {
Branch: in.Branch, Branch: in.Branch,
} }
if err := cron.Validate(); err != nil { if err := cron.Validate(); err != nil {
c.String(400, "Error inserting cron. validate failed: %s", err) c.String(http.StatusUnprocessableEntity, "Error inserting cron. validate failed: %s", err)
return return
} }
nextExec, err := cronScheduler.CalcNewNext(in.Schedule, time.Now()) nextExec, err := cronScheduler.CalcNewNext(in.Schedule, time.Now())
if err != nil { if err != nil {
c.String(400, "Error inserting cron. schedule could not parsed: %s", err) c.String(http.StatusBadRequest, "Error inserting cron. schedule could not parsed: %s", err)
return return
} }
cron.NextExec = nextExec.Unix() cron.NextExec = nextExec.Unix()
@ -112,28 +112,28 @@ func PostCron(c *gin.Context) {
// check if branch exists on forge // check if branch exists on forge
_, err := forge.BranchHead(c, user, repo, in.Branch) _, err := forge.BranchHead(c, user, repo, in.Branch)
if err != nil { if err != nil {
c.String(400, "Error inserting cron. branch not resolved: %s", err) c.String(http.StatusBadRequest, "Error inserting cron. branch not resolved: %s", err)
return return
} }
} }
if err := store.CronCreate(cron); err != nil { if err := _store.CronCreate(cron); err != nil {
c.String(500, "Error inserting cron %q. %s", in.Name, err) c.String(http.StatusInternalServerError, "Error inserting cron %q. %s", in.Name, err)
return return
} }
c.JSON(200, cron) c.JSON(http.StatusOK, cron)
} }
// PatchCron updates the cron job in the database. // PatchCron updates the cron job in the database.
func PatchCron(c *gin.Context) { func PatchCron(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
user := session.User(c) user := session.User(c)
store := store.FromContext(c) _store := store.FromContext(c)
forge := server.Config.Services.Forge forge := server.Config.Services.Forge
id, err := strconv.ParseInt(c.Param("cron"), 10, 64) id, err := strconv.ParseInt(c.Param("cron"), 10, 64)
if err != nil { if err != nil {
c.String(400, "Error parsing cron id. %s", err) c.String(http.StatusBadRequest, "Error parsing cron id. %s", err)
return return
} }
@ -144,16 +144,16 @@ func PatchCron(c *gin.Context) {
return return
} }
cron, err := store.CronFind(repo, id) cron, err := _store.CronFind(repo, id)
if err != nil { if err != nil {
c.String(404, "Error getting cron %d. %s", id, err) handleDbGetError(c, err)
return return
} }
if in.Branch != "" { if in.Branch != "" {
// check if branch exists on forge // check if branch exists on forge
_, err := forge.BranchHead(c, user, repo, in.Branch) _, err := forge.BranchHead(c, user, repo, in.Branch)
if err != nil { if err != nil {
c.String(400, "Error inserting cron. branch not resolved: %s", err) c.String(http.StatusBadRequest, "Error inserting cron. branch not resolved: %s", err)
return return
} }
cron.Branch = in.Branch cron.Branch = in.Branch
@ -162,7 +162,7 @@ func PatchCron(c *gin.Context) {
cron.Schedule = in.Schedule cron.Schedule = in.Schedule
nextExec, err := cronScheduler.CalcNewNext(in.Schedule, time.Now()) nextExec, err := cronScheduler.CalcNewNext(in.Schedule, time.Now())
if err != nil { if err != nil {
c.String(400, "Error inserting cron. schedule could not parsed: %s", err) c.String(http.StatusBadRequest, "Error inserting cron. schedule could not parsed: %s", err)
return return
} }
cron.NextExec = nextExec.Unix() cron.NextExec = nextExec.Unix()
@ -173,14 +173,14 @@ func PatchCron(c *gin.Context) {
cron.CreatorID = user.ID cron.CreatorID = user.ID
if err := cron.Validate(); err != nil { if err := cron.Validate(); err != nil {
c.String(400, "Error inserting cron. validate failed: %s", err) c.String(http.StatusUnprocessableEntity, "Error inserting cron. validate failed: %s", err)
return return
} }
if err := store.CronUpdate(repo, cron); err != nil { if err := _store.CronUpdate(repo, cron); err != nil {
c.String(500, "Error updating cron %q. %s", in.Name, err) c.String(http.StatusInternalServerError, "Error updating cron %q. %s", in.Name, err)
return return
} }
c.JSON(200, cron) c.JSON(http.StatusOK, cron)
} }
// GetCronList gets the cron job list from the database and writes // GetCronList gets the cron job list from the database and writes
@ -189,10 +189,10 @@ func GetCronList(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
list, err := store.FromContext(c).CronList(repo) list, err := store.FromContext(c).CronList(repo)
if err != nil { if err != nil {
c.String(500, "Error getting cron list. %s", err) c.String(http.StatusInternalServerError, "Error getting cron list. %s", err)
return return
} }
c.JSON(200, list) c.JSON(http.StatusOK, list)
} }
// DeleteCron deletes the named cron job from the database. // DeleteCron deletes the named cron job from the database.
@ -200,12 +200,12 @@ func DeleteCron(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
id, err := strconv.ParseInt(c.Param("cron"), 10, 64) id, err := strconv.ParseInt(c.Param("cron"), 10, 64)
if err != nil { if err != nil {
c.String(400, "Error parsing cron id. %s", err) c.String(http.StatusBadRequest, "Error parsing cron id. %s", err)
return return
} }
if err := store.FromContext(c).CronDelete(repo, id); err != nil { if err := store.FromContext(c).CronDelete(repo, id); err != nil {
c.String(500, "Error deleting cron %d. %s", id, err) c.String(http.StatusInternalServerError, "Error deleting cron %d. %s", id, err)
return return
} }
c.String(204, "") c.String(http.StatusNoContent, "")
} }

View file

@ -23,7 +23,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session" "github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
"github.com/woodpecker-ci/woodpecker/server/store" "github.com/woodpecker-ci/woodpecker/server/store"
) )
@ -40,7 +39,7 @@ func FileList(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
pipeline, err := _store.GetPipelineNumber(repo, num) pipeline, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) handleDbGetError(c, err)
return return
} }
@ -79,7 +78,7 @@ func FileGet(c *gin.Context) {
pipeline, err := _store.GetPipelineNumber(repo, num) pipeline, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) handleDbGetError(c, err)
return return
} }
@ -91,18 +90,18 @@ func FileGet(c *gin.Context) {
file, err := _store.FileFind(step, name) file, err := _store.FileFind(step, name)
if err != nil { if err != nil {
c.String(404, "Error getting file %q. %s", name, err) c.String(http.StatusNotFound, "Error getting file %q. %s", name, err)
return return
} }
if !raw { if !raw {
c.JSON(200, file) c.JSON(http.StatusOK, file)
return return
} }
rc, err := _store.FileRead(step, file.Name) rc, err := _store.FileRead(step, file.Name)
if err != nil { if err != nil {
c.String(404, "Error getting file stream %q. %s", name, err) c.String(http.StatusNotFound, "Error getting file stream %q. %s", name, err)
return return
} }
defer rc.Close() defer rc.Close()

View file

@ -17,10 +17,9 @@ package api
import ( import (
"net/http" "net/http"
"github.com/gin-gonic/gin"
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/gin-gonic/gin"
) )
// GetGlobalSecretList gets the global secret list from // GetGlobalSecretList gets the global secret list from
@ -45,10 +44,10 @@ func GetGlobalSecret(c *gin.Context) {
name := c.Param("secret") name := c.Param("secret")
secret, err := server.Config.Services.Secrets.GlobalSecretFind(name) secret, err := server.Config.Services.Secrets.GlobalSecretFind(name)
if err != nil { if err != nil {
c.String(404, "Error getting global secret %q. %s", name, err) handleDbGetError(c, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// PostGlobalSecret persists a global secret to the database. // PostGlobalSecret persists a global secret to the database.
@ -66,14 +65,14 @@ func PostGlobalSecret(c *gin.Context) {
PluginsOnly: in.PluginsOnly, PluginsOnly: in.PluginsOnly,
} }
if err := secret.Validate(); err != nil { if err := secret.Validate(); err != nil {
c.String(400, "Error inserting global secret. %s", err) c.String(http.StatusBadRequest, "Error inserting global secret. %s", err)
return return
} }
if err := server.Config.Services.Secrets.GlobalSecretCreate(secret); err != nil { if err := server.Config.Services.Secrets.GlobalSecretCreate(secret); err != nil {
c.String(500, "Error inserting global secret %q. %s", in.Name, err) c.String(http.StatusInternalServerError, "Error inserting global secret %q. %s", in.Name, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// PatchGlobalSecret updates a global secret in the database. // PatchGlobalSecret updates a global secret in the database.
@ -89,7 +88,7 @@ func PatchGlobalSecret(c *gin.Context) {
secret, err := server.Config.Services.Secrets.GlobalSecretFind(name) secret, err := server.Config.Services.Secrets.GlobalSecretFind(name)
if err != nil { if err != nil {
c.String(404, "Error getting global secret %q. %s", name, err) handleDbGetError(c, err)
return return
} }
if in.Value != "" { if in.Value != "" {
@ -104,22 +103,22 @@ func PatchGlobalSecret(c *gin.Context) {
secret.PluginsOnly = in.PluginsOnly secret.PluginsOnly = in.PluginsOnly
if err := secret.Validate(); err != nil { if err := secret.Validate(); err != nil {
c.String(400, "Error updating global secret. %s", err) c.String(http.StatusBadRequest, "Error updating global secret. %s", err)
return return
} }
if err := server.Config.Services.Secrets.GlobalSecretUpdate(secret); err != nil { if err := server.Config.Services.Secrets.GlobalSecretUpdate(secret); err != nil {
c.String(500, "Error updating global secret %q. %s", in.Name, err) c.String(http.StatusInternalServerError, "Error updating global secret %q. %s", in.Name, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// DeleteGlobalSecret deletes the named global secret from the database. // DeleteGlobalSecret deletes the named global secret from the database.
func DeleteGlobalSecret(c *gin.Context) { func DeleteGlobalSecret(c *gin.Context) {
name := c.Param("secret") name := c.Param("secret")
if err := server.Config.Services.Secrets.GlobalSecretDelete(name); err != nil { if err := server.Config.Services.Secrets.GlobalSecretDelete(name); err != nil {
c.String(500, "Error deleting global secret %q. %s", name, err) c.String(http.StatusInternalServerError, "Error deleting global secret %q. %s", name, err)
return return
} }
c.String(204, "") c.String(http.StatusNoContent, "")
} }

View file

@ -20,6 +20,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/woodpecker-ci/woodpecker/server/store/types"
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
"github.com/woodpecker-ci/woodpecker/server/forge" "github.com/woodpecker-ci/woodpecker/server/forge"
@ -40,6 +41,14 @@ func handlePipelineErr(c *gin.Context, err error) {
} }
} }
func handleDbGetError(c *gin.Context, err error) {
if errors.Is(err, types.RecordNotExist) {
c.AbortWithStatus(http.StatusNotFound)
return
}
_ = c.AbortWithError(http.StatusInternalServerError, err)
}
// if the forge has a refresh token, the current access token may be stale. // if the forge has a refresh token, the current access token may be stale.
// Therefore, we should refresh prior to dispatching the job. // Therefore, we should refresh prior to dispatching the job.
func refreshUserToken(c *gin.Context, user *model.User) { func refreshUserToken(c *gin.Context, user *model.User) {

View file

@ -42,7 +42,7 @@ func init() {
} }
func GetQueueInfo(c *gin.Context) { func GetQueueInfo(c *gin.Context) {
c.IndentedJSON(200, c.IndentedJSON(http.StatusOK,
server.Config.Services.Queue.Info(c), server.Config.Services.Queue.Info(c),
) )
} }
@ -179,6 +179,6 @@ func PostHook(c *gin.Context) {
if err != nil { if err != nil {
handlePipelineErr(c, err) handlePipelineErr(c, err)
} else { } else {
c.JSON(200, pl) c.JSON(http.StatusOK, pl)
} }
} }

View file

@ -16,12 +16,14 @@ package api
import ( import (
"encoding/base32" "encoding/base32"
"errors"
"net/http" "net/http"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/woodpecker-ci/woodpecker/server/store/types"
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
@ -52,7 +54,7 @@ func HandleAuth(c *gin.Context) {
tmpuser, err := server.Config.Services.Forge.Login(c, c.Writer, c.Request) tmpuser, err := server.Config.Services.Forge.Login(c, c.Writer, c.Request)
if err != nil { if err != nil {
log.Error().Msgf("cannot authenticate user. %s", err) log.Error().Msgf("cannot authenticate user. %s", err)
c.Redirect(303, "/login?error=oauth_error") c.Redirect(http.StatusSeeOther, "/login?error=oauth_error")
return return
} }
// this will happen when the user is redirected by the forge as // this will happen when the user is redirected by the forge as
@ -65,10 +67,15 @@ func HandleAuth(c *gin.Context) {
// get the user from the database // get the user from the database
u, err := _store.GetUserLogin(tmpuser.Login) u, err := _store.GetUserLogin(tmpuser.Login)
if err != nil { if err != nil {
if !errors.Is(err, types.RecordNotExist) {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
// if self-registration is disabled we should return a not authorized error // if self-registration is disabled we should return a not authorized error
if !config.Open && !config.IsAdmin(tmpuser) { if !config.Open && !config.IsAdmin(tmpuser) {
log.Error().Msgf("cannot register %s. registration closed", tmpuser.Login) log.Error().Msgf("cannot register %s. registration closed", tmpuser.Login)
c.Redirect(303, "/login?error=access_denied") c.Redirect(http.StatusSeeOther, "/login?error=access_denied")
return return
} }
@ -98,7 +105,7 @@ func HandleAuth(c *gin.Context) {
// insert the user into the database // insert the user into the database
if err := _store.CreateUser(u); err != nil { if err := _store.CreateUser(u); err != nil {
log.Error().Msgf("cannot insert %s. %s", u.Login, err) log.Error().Msgf("cannot insert %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error") c.Redirect(http.StatusSeeOther, "/login?error=internal_error")
return return
} }
} }
@ -116,14 +123,14 @@ func HandleAuth(c *gin.Context) {
teams, terr := server.Config.Services.Forge.Teams(c, u) teams, terr := server.Config.Services.Forge.Teams(c, u)
if terr != nil || !config.IsMember(teams) { if terr != nil || !config.IsMember(teams) {
log.Error().Msgf("cannot verify team membership for %s.", u.Login) log.Error().Msgf("cannot verify team membership for %s.", u.Login)
c.Redirect(303, "/login?error=access_denied") c.Redirect(http.StatusSeeOther, "/login?error=access_denied")
return return
} }
} }
if err := _store.UpdateUser(u); err != nil { if err := _store.UpdateUser(u); err != nil {
log.Error().Msgf("cannot update %s. %s", u.Login, err) log.Error().Msgf("cannot update %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error") c.Redirect(http.StatusSeeOther, "/login?error=internal_error")
return return
} }
@ -131,19 +138,19 @@ func HandleAuth(c *gin.Context) {
tokenString, err := token.New(token.SessToken, u.Login).SignExpires(u.Hash, exp) tokenString, err := token.New(token.SessToken, u.Login).SignExpires(u.Hash, exp)
if err != nil { if err != nil {
log.Error().Msgf("cannot create token for %s. %s", u.Login, err) log.Error().Msgf("cannot create token for %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error") c.Redirect(http.StatusSeeOther, "/login?error=internal_error")
return return
} }
httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenString) httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenString)
c.Redirect(303, "/") c.Redirect(http.StatusSeeOther, "/")
} }
func GetLogout(c *gin.Context) { func GetLogout(c *gin.Context) {
httputil.DelCookie(c.Writer, c.Request, "user_sess") httputil.DelCookie(c.Writer, c.Request, "user_sess")
httputil.DelCookie(c.Writer, c.Request, "user_last") httputil.DelCookie(c.Writer, c.Request, "user_last")
c.Redirect(303, "/") c.Redirect(http.StatusSeeOther, "/")
} }
func GetLoginToken(c *gin.Context) { func GetLoginToken(c *gin.Context) {

View file

@ -17,10 +17,9 @@ package api
import ( import (
"net/http" "net/http"
"github.com/gin-gonic/gin"
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/gin-gonic/gin"
) )
// GetOrgSecret gets the named organization secret from the database // GetOrgSecret gets the named organization secret from the database
@ -32,10 +31,10 @@ func GetOrgSecret(c *gin.Context) {
) )
secret, err := server.Config.Services.Secrets.OrgSecretFind(owner, name) secret, err := server.Config.Services.Secrets.OrgSecretFind(owner, name)
if err != nil { if err != nil {
c.String(404, "Error getting org %q secret %q. %s", owner, name, err) handleDbGetError(c, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// GetOrgSecretList gest the organization secret list from // GetOrgSecretList gest the organization secret list from
@ -73,14 +72,14 @@ func PostOrgSecret(c *gin.Context) {
PluginsOnly: in.PluginsOnly, PluginsOnly: in.PluginsOnly,
} }
if err := secret.Validate(); err != nil { if err := secret.Validate(); err != nil {
c.String(400, "Error inserting org %q secret. %s", owner, err) c.String(http.StatusUnprocessableEntity, "Error inserting org %q secret. %s", owner, err)
return return
} }
if err := server.Config.Services.Secrets.OrgSecretCreate(owner, secret); err != nil { if err := server.Config.Services.Secrets.OrgSecretCreate(owner, secret); err != nil {
c.String(500, "Error inserting org %q secret %q. %s", owner, in.Name, err) c.String(http.StatusInternalServerError, "Error inserting org %q secret %q. %s", owner, in.Name, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// PatchOrgSecret updates an organization secret in the database. // PatchOrgSecret updates an organization secret in the database.
@ -99,7 +98,7 @@ func PatchOrgSecret(c *gin.Context) {
secret, err := server.Config.Services.Secrets.OrgSecretFind(owner, name) secret, err := server.Config.Services.Secrets.OrgSecretFind(owner, name)
if err != nil { if err != nil {
c.String(404, "Error getting org %q secret %q. %s", owner, name, err) handleDbGetError(c, err)
return return
} }
if in.Value != "" { if in.Value != "" {
@ -114,14 +113,14 @@ func PatchOrgSecret(c *gin.Context) {
secret.PluginsOnly = in.PluginsOnly secret.PluginsOnly = in.PluginsOnly
if err := secret.Validate(); err != nil { if err := secret.Validate(); err != nil {
c.String(400, "Error updating org %q secret. %s", owner, err) c.String(http.StatusUnprocessableEntity, "Error updating org %q secret. %s", owner, err)
return return
} }
if err := server.Config.Services.Secrets.OrgSecretUpdate(owner, secret); err != nil { if err := server.Config.Services.Secrets.OrgSecretUpdate(owner, secret); err != nil {
c.String(500, "Error updating org %q secret %q. %s", owner, in.Name, err) c.String(http.StatusInternalServerError, "Error updating org %q secret %q. %s", owner, in.Name, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// DeleteOrgSecret deletes the named organization secret from the database. // DeleteOrgSecret deletes the named organization secret from the database.
@ -131,8 +130,8 @@ func DeleteOrgSecret(c *gin.Context) {
name = c.Param("secret") name = c.Param("secret")
) )
if err := server.Config.Services.Secrets.OrgSecretDelete(owner, name); err != nil { if err := server.Config.Services.Secrets.OrgSecretDelete(owner, name); err != nil {
c.String(500, "Error deleting org %q secret %q. %s", owner, name, err) c.String(http.StatusInternalServerError, "Error deleting org %q secret %q. %s", owner, name, err)
return return
} }
c.String(204, "") c.String(http.StatusNoContent, "")
} }

View file

@ -148,7 +148,7 @@ func GetPipelineLast(c *gin.Context) {
pl, err := _store.GetPipelineLast(repo, branch) pl, err := _store.GetPipelineLast(repo, branch)
if err != nil { if err != nil {
c.String(http.StatusInternalServerError, err.Error()) handleDbGetError(c, err)
return return
} }
@ -176,19 +176,19 @@ func GetPipelineLogs(c *gin.Context) {
pl, err := _store.GetPipelineNumber(repo, num) pl, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(404, err) handleDbGetError(c, err)
return return
} }
step, err := _store.StepChild(pl, ppid, name) step, err := _store.StepChild(pl, ppid, name)
if err != nil { if err != nil {
_ = c.AbortWithError(404, err) handleDbGetError(c, err)
return return
} }
rc, err := _store.LogFind(step) rc, err := _store.LogFind(step)
if err != nil { if err != nil {
_ = c.AbortWithError(404, err) handleDbGetError(c, err)
return return
} }
@ -211,19 +211,19 @@ func GetStepLogs(c *gin.Context) {
pl, err := _store.GetPipelineNumber(repo, num) pl, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusNotFound, err) handleDbGetError(c, err)
return return
} }
step, err := _store.StepFind(pl, pid) step, err := _store.StepFind(pl, pid)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusNotFound, err) handleDbGetError(c, err)
return return
} }
rc, err := _store.LogFind(step) rc, err := _store.LogFind(step)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusNotFound, err) handleDbGetError(c, err)
return return
} }
@ -246,7 +246,7 @@ func GetPipelineConfig(c *gin.Context) {
pl, err := _store.GetPipelineNumber(repo, num) pl, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err) handleDbGetError(c, err)
return return
} }
@ -267,7 +267,7 @@ func CancelPipeline(c *gin.Context) {
pl, err := _store.GetPipelineNumber(repo, num) pl, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(http.StatusNotFound, err) handleDbGetError(c, err)
return return
} }
@ -289,7 +289,7 @@ func PostApproval(c *gin.Context) {
pl, err := _store.GetPipelineNumber(repo, num) pl, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(404, err) handleDbGetError(c, err)
return return
} }
@ -297,7 +297,7 @@ func PostApproval(c *gin.Context) {
if err != nil { if err != nil {
handlePipelineErr(c, err) handlePipelineErr(c, err)
} else { } else {
c.JSON(200, newpipeline) c.JSON(http.StatusOK, newpipeline)
} }
} }
@ -320,17 +320,17 @@ func PostDecline(c *gin.Context) {
if err != nil { if err != nil {
handlePipelineErr(c, err) handlePipelineErr(c, err)
} else { } else {
c.JSON(200, pl) c.JSON(http.StatusOK, pl)
} }
} }
func GetPipelineQueue(c *gin.Context) { func GetPipelineQueue(c *gin.Context) {
out, err := store.FromContext(c).GetPipelineQueue() out, err := store.FromContext(c).GetPipelineQueue()
if err != nil { if err != nil {
c.String(500, "Error getting pipeline queue. %s", err) c.String(http.StatusInternalServerError, "Error getting pipeline queue. %s", err)
return return
} }
c.JSON(200, out) c.JSON(http.StatusOK, out)
} }
// PostPipeline restarts a pipeline optional with altered event, deploy or environment // PostPipeline restarts a pipeline optional with altered event, deploy or environment
@ -346,15 +346,21 @@ func PostPipeline(c *gin.Context) {
user, err := _store.GetUser(repo.UserID) user, err := _store.GetUser(repo.UserID)
if err != nil { if err != nil {
log.Error().Msgf("failure to find repo owner %s. %s", repo.FullName, err) if errors.Is(err, types.RecordNotExist) {
_ = c.AbortWithError(500, err) c.AbortWithStatus(http.StatusNotFound)
return
}
_ = c.AbortWithError(http.StatusInternalServerError, err)
return return
} }
pl, err := _store.GetPipelineNumber(repo, num) pl, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
log.Error().Msgf("failure to get pipeline %d. %s", num, err) if errors.Is(err, types.RecordNotExist) {
_ = c.AbortWithError(404, err) c.AbortWithStatus(http.StatusNotFound)
return
}
_ = c.AbortWithError(http.StatusInternalServerError, err)
return return
} }
@ -394,7 +400,7 @@ func PostPipeline(c *gin.Context) {
if err != nil { if err != nil {
handlePipelineErr(c, err) handlePipelineErr(c, err)
} else { } else {
c.JSON(200, newpipeline) c.JSON(http.StatusOK, newpipeline)
} }
} }
@ -407,19 +413,19 @@ func DeletePipelineLogs(c *gin.Context) {
pl, err := _store.GetPipelineNumber(repo, num) pl, err := _store.GetPipelineNumber(repo, num)
if err != nil { if err != nil {
_ = c.AbortWithError(404, err) handleDbGetError(c, err)
return return
} }
steps, err := _store.StepList(pl) steps, err := _store.StepList(pl)
if err != nil { if err != nil {
_ = c.AbortWithError(404, err) _ = c.AbortWithError(http.StatusNotFound, err)
return return
} }
switch pl.Status { switch pl.Status {
case model.StatusRunning, model.StatusPending: case model.StatusRunning, model.StatusPending:
c.String(400, "Cannot delete logs for a pending or running pipeline") c.String(http.StatusUnprocessableEntity, "Cannot delete logs for a pending or running pipeline")
return return
} }
@ -432,11 +438,11 @@ func DeletePipelineLogs(c *gin.Context) {
} }
} }
if err != nil { if err != nil {
c.String(400, "There was a problem deleting your logs. %s", err) c.String(http.StatusInternalServerError, "There was a problem deleting your logs. %s", err)
return return
} }
c.String(204, "") c.String(http.StatusNoContent, "")
} }
var deleteStr = `[ var deleteStr = `[

View file

@ -15,7 +15,6 @@
package api package api
import ( import (
"errors"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -23,7 +22,6 @@ import (
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session" "github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
"github.com/woodpecker-ci/woodpecker/server/store/types"
) )
// GetRegistry gets the name registry from the database and writes // GetRegistry gets the name registry from the database and writes
@ -35,7 +33,7 @@ func GetRegistry(c *gin.Context) {
) )
registry, err := server.Config.Services.Registries.RegistryFind(repo, name) registry, err := server.Config.Services.Registries.RegistryFind(repo, name)
if err != nil { if err != nil {
c.String(404, "Error getting registry %q. %s", name, err) handleDbGetError(c, err)
return return
} }
c.JSON(200, registry.Copy()) c.JSON(200, registry.Copy())
@ -59,14 +57,14 @@ func PostRegistry(c *gin.Context) {
Email: in.Email, Email: in.Email,
} }
if err := registry.Validate(); err != nil { if err := registry.Validate(); err != nil {
c.String(400, "Error inserting registry. %s", err) c.String(http.StatusBadRequest, "Error inserting registry. %s", err)
return return
} }
if err := server.Config.Services.Registries.RegistryCreate(repo, registry); err != nil { if err := server.Config.Services.Registries.RegistryCreate(repo, registry); err != nil {
c.String(500, "Error inserting registry %q. %s", in.Address, err) c.String(http.StatusInternalServerError, "Error inserting registry %q. %s", in.Address, err)
return return
} }
c.JSON(200, in.Copy()) c.JSON(http.StatusOK, in.Copy())
} }
// PatchRegistry updates the registry in the database. // PatchRegistry updates the registry in the database.
@ -85,7 +83,7 @@ func PatchRegistry(c *gin.Context) {
registry, err := server.Config.Services.Registries.RegistryFind(repo, name) registry, err := server.Config.Services.Registries.RegistryFind(repo, name)
if err != nil { if err != nil {
c.String(404, "Error getting registry %q. %s", name, err) handleDbGetError(c, err)
return return
} }
if in.Username != "" { if in.Username != "" {
@ -102,14 +100,14 @@ func PatchRegistry(c *gin.Context) {
} }
if err := registry.Validate(); err != nil { if err := registry.Validate(); err != nil {
c.String(400, "Error updating registry. %s", err) c.String(http.StatusUnprocessableEntity, "Error updating registry. %s", err)
return return
} }
if err := server.Config.Services.Registries.RegistryUpdate(repo, registry); err != nil { if err := server.Config.Services.Registries.RegistryUpdate(repo, registry); err != nil {
c.String(500, "Error updating registry %q. %s", in.Address, err) c.String(http.StatusInternalServerError, "Error updating registry %q. %s", in.Address, err)
return return
} }
c.JSON(200, in.Copy()) c.JSON(http.StatusOK, in.Copy())
} }
// GetRegistryList gets the registry list from the database and writes // GetRegistryList gets the registry list from the database and writes
@ -118,7 +116,7 @@ func GetRegistryList(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
list, err := server.Config.Services.Registries.RegistryList(repo) list, err := server.Config.Services.Registries.RegistryList(repo)
if err != nil { if err != nil {
c.String(500, "Error getting registry list. %s", err) c.String(http.StatusInternalServerError, "Error getting registry list. %s", err)
return return
} }
// copy the registry detail to remove the sensitive // copy the registry detail to remove the sensitive
@ -126,7 +124,7 @@ func GetRegistryList(c *gin.Context) {
for i, registry := range list { for i, registry := range list {
list[i] = registry.Copy() list[i] = registry.Copy()
} }
c.JSON(200, list) c.JSON(http.StatusOK, list)
} }
// DeleteRegistry deletes the named registry from the database. // DeleteRegistry deletes the named registry from the database.
@ -137,12 +135,8 @@ func DeleteRegistry(c *gin.Context) {
) )
err := server.Config.Services.Registries.RegistryDelete(repo, name) err := server.Config.Services.Registries.RegistryDelete(repo, name)
if err != nil { if err != nil {
if errors.Is(err, types.RecordNotExist) { c.String(http.StatusInternalServerError, "Error deleting registry %q. %s", name, err)
c.String(404, "no records found, cannot delete registry")
return
}
c.String(500, "Error deleting registry %q. %s", name, err)
return return
} }
c.String(204, "") c.String(http.StatusNoContent, "")
} }

View file

@ -263,7 +263,7 @@ func RepairRepo(c *gin.Context) {
t := token.New(token.HookToken, repo.FullName) t := token.New(token.HookToken, repo.FullName)
sig, err := t.Sign(repo.Hash) sig, err := t.Sign(repo.Hash)
if err != nil { if err != nil {
c.String(500, err.Error()) c.String(http.StatusInternalServerError, err.Error())
return return
} }
@ -301,7 +301,7 @@ func RepairRepo(c *gin.Context) {
log.Trace().Err(err).Msgf("deactivate repo '%s' to repair failed", repo.FullName) log.Trace().Err(err).Msgf("deactivate repo '%s' to repair failed", repo.FullName)
} }
if err := forge.Activate(c, user, repo, link); err != nil { if err := forge.Activate(c, user, repo, link); err != nil {
c.String(500, err.Error()) c.String(http.StatusInternalServerError, err.Error())
return return
} }
@ -355,7 +355,7 @@ func MoveRepo(c *gin.Context) {
t := token.New(token.HookToken, repo.FullName) t := token.New(token.HookToken, repo.FullName)
sig, err := t.Sign(repo.Hash) sig, err := t.Sign(repo.Hash)
if err != nil { if err != nil {
c.String(500, err.Error()) c.String(http.StatusInternalServerError, err.Error())
return return
} }
@ -371,7 +371,7 @@ func MoveRepo(c *gin.Context) {
log.Trace().Err(err).Msgf("deactivate repo '%s' for move to activate later, got an error", repo.FullName) log.Trace().Err(err).Msgf("deactivate repo '%s' for move to activate later, got an error", repo.FullName)
} }
if err := forge.Activate(c, user, repo, link); err != nil { if err := forge.Activate(c, user, repo, link); err != nil {
c.String(500, err.Error()) c.String(http.StatusInternalServerError, err.Error())
return return
} }
c.Writer.WriteHeader(http.StatusOK) c.Writer.WriteHeader(http.StatusOK)

View file

@ -19,7 +19,6 @@ import (
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session" "github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
@ -34,10 +33,10 @@ func GetSecret(c *gin.Context) {
) )
secret, err := server.Config.Services.Secrets.SecretFind(repo, name) secret, err := server.Config.Services.Secrets.SecretFind(repo, name)
if err != nil { if err != nil {
c.String(404, "Error getting secret %q. %s", name, err) handleDbGetError(c, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// PostSecret persists the secret to the database. // PostSecret persists the secret to the database.
@ -58,14 +57,14 @@ func PostSecret(c *gin.Context) {
PluginsOnly: in.PluginsOnly, PluginsOnly: in.PluginsOnly,
} }
if err := secret.Validate(); err != nil { if err := secret.Validate(); err != nil {
c.String(400, "Error inserting secret. %s", err) c.String(http.StatusUnprocessableEntity, "Error inserting secret. %s", err)
return return
} }
if err := server.Config.Services.Secrets.SecretCreate(repo, secret); err != nil { if err := server.Config.Services.Secrets.SecretCreate(repo, secret); err != nil {
c.String(500, "Error inserting secret %q. %s", in.Name, err) c.String(http.StatusInternalServerError, "Error inserting secret %q. %s", in.Name, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// PatchSecret updates the secret in the database. // PatchSecret updates the secret in the database.
@ -84,7 +83,7 @@ func PatchSecret(c *gin.Context) {
secret, err := server.Config.Services.Secrets.SecretFind(repo, name) secret, err := server.Config.Services.Secrets.SecretFind(repo, name)
if err != nil { if err != nil {
c.String(404, "Error getting secret %q. %s", name, err) handleDbGetError(c, err)
return return
} }
if in.Value != "" { if in.Value != "" {
@ -99,14 +98,14 @@ func PatchSecret(c *gin.Context) {
secret.PluginsOnly = in.PluginsOnly secret.PluginsOnly = in.PluginsOnly
if err := secret.Validate(); err != nil { if err := secret.Validate(); err != nil {
c.String(400, "Error updating secret. %s", err) c.String(http.StatusUnprocessableEntity, "Error updating secret. %s", err)
return return
} }
if err := server.Config.Services.Secrets.SecretUpdate(repo, secret); err != nil { if err := server.Config.Services.Secrets.SecretUpdate(repo, secret); err != nil {
c.String(500, "Error updating secret %q. %s", in.Name, err) c.String(http.StatusInternalServerError, "Error updating secret %q. %s", in.Name, err)
return return
} }
c.JSON(200, secret.Copy()) c.JSON(http.StatusOK, secret.Copy())
} }
// GetSecretList gets the secret list from the database and writes // GetSecretList gets the secret list from the database and writes
@ -115,7 +114,7 @@ func GetSecretList(c *gin.Context) {
repo := session.Repo(c) repo := session.Repo(c)
list, err := server.Config.Services.Secrets.SecretList(repo) list, err := server.Config.Services.Secrets.SecretList(repo)
if err != nil { if err != nil {
c.String(500, "Error getting secret list. %s", err) c.String(http.StatusInternalServerError, "Error getting secret list. %s", err)
return return
} }
// copy the secret detail to remove the sensitive // copy the secret detail to remove the sensitive
@ -123,7 +122,7 @@ func GetSecretList(c *gin.Context) {
for i, secret := range list { for i, secret := range list {
list[i] = secret.Copy() list[i] = secret.Copy()
} }
c.JSON(200, list) c.JSON(http.StatusOK, list)
} }
// DeleteSecret deletes the named secret from the database. // DeleteSecret deletes the named secret from the database.
@ -133,8 +132,8 @@ func DeleteSecret(c *gin.Context) {
name = c.Param("secret") name = c.Param("secret")
) )
if err := server.Config.Services.Secrets.SecretDelete(repo, name); err != nil { if err := server.Config.Services.Secrets.SecretDelete(repo, name); err != nil {
c.String(500, "Error deleting secret %q. %s", name, err) c.String(http.StatusInternalServerError, "Error deleting secret %q. %s", name, err)
return return
} }
c.String(204, "") c.String(http.StatusNoContent, "")
} }

View file

@ -37,5 +37,5 @@ func GetSignaturePublicKey(c *gin.Context) {
Bytes: b, Bytes: b,
} }
c.String(200, "%s", pem.EncodeToMemory(block)) c.String(http.StatusOK, "%s", pem.EncodeToMemory(block))
} }

View file

@ -48,7 +48,7 @@ func EventStreamSSE(c *gin.Context) {
flusher, ok := rw.(http.Flusher) flusher, ok := rw.(http.Flusher)
if !ok { if !ok {
c.String(500, "Streaming not supported") c.String(http.StatusInternalServerError, "Streaming not supported")
return return
} }
@ -131,7 +131,7 @@ func LogStreamSSE(c *gin.Context) {
flusher, ok := rw.(http.Flusher) flusher, ok := rw.(http.Flusher)
if !ok { if !ok {
c.String(500, "Streaming not supported") c.String(http.StatusInternalServerError, "Streaming not supported")
return return
} }

View file

@ -33,7 +33,7 @@ import (
) )
func GetSelf(c *gin.Context) { func GetSelf(c *gin.Context) {
c.JSON(200, session.User(c)) c.JSON(http.StatusOK, session.User(c))
} }
func GetFeed(c *gin.Context) { func GetFeed(c *gin.Context) {
@ -70,19 +70,19 @@ func GetFeed(c *gin.Context) {
if latest { if latest {
feed, err := _store.RepoListLatest(user) feed, err := _store.RepoListLatest(user)
if err != nil { if err != nil {
c.String(500, "Error fetching feed. %s", err) c.String(http.StatusInternalServerError, "Error fetching feed. %s", err)
} else { } else {
c.JSON(200, feed) c.JSON(http.StatusOK, feed)
} }
return return
} }
feed, err := _store.UserFeed(user) feed, err := _store.UserFeed(user)
if err != nil { if err != nil {
c.String(500, "Error fetching user feed. %s", err) c.String(http.StatusInternalServerError, "Error fetching user feed. %s", err)
return return
} }
c.JSON(200, feed) c.JSON(http.StatusOK, feed)
} }
func GetRepos(c *gin.Context) { func GetRepos(c *gin.Context) {
@ -119,7 +119,7 @@ func GetRepos(c *gin.Context) {
repos, err := _store.RepoList(user, true) repos, err := _store.RepoList(user, true)
if err != nil { if err != nil {
c.String(500, "Error fetching repository list. %s", err) c.String(http.StatusInternalServerError, "Error fetching repository list. %s", err)
return return
} }
@ -155,7 +155,7 @@ func DeleteToken(c *gin.Context) {
securecookie.GenerateRandomKey(32), securecookie.GenerateRandomKey(32),
) )
if err := _store.UpdateUser(user); err != nil { if err := _store.UpdateUser(user); err != nil {
c.String(500, "Error revoking tokens. %s", err) c.String(http.StatusInternalServerError, "Error revoking tokens. %s", err)
return return
} }

View file

@ -20,7 +20,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/store" "github.com/woodpecker-ci/woodpecker/server/store"
) )
@ -37,7 +36,7 @@ func GetUsers(c *gin.Context) {
func GetUser(c *gin.Context) { func GetUser(c *gin.Context) {
user, err := store.FromContext(c).GetUserLogin(c.Param("login")) user, err := store.FromContext(c).GetUserLogin(c.Param("login"))
if err != nil { if err != nil {
c.String(404, "Cannot find user. %s", err) handleDbGetError(c, err)
return return
} }
c.JSON(200, user) c.JSON(200, user)
@ -55,7 +54,7 @@ func PatchUser(c *gin.Context) {
user, err := _store.GetUserLogin(c.Param("login")) user, err := _store.GetUserLogin(c.Param("login"))
if err != nil { if err != nil {
c.AbortWithStatus(http.StatusNotFound) handleDbGetError(c, err)
return return
} }

View file

@ -28,15 +28,15 @@ import (
// Health endpoint returns a 500 if the server state is unhealthy. // Health endpoint returns a 500 if the server state is unhealthy.
func Health(c *gin.Context) { func Health(c *gin.Context) {
if err := store.FromContext(c).Ping(); err != nil { if err := store.FromContext(c).Ping(); err != nil {
c.String(500, err.Error()) c.String(http.StatusInternalServerError, err.Error())
return return
} }
c.String(200, "") c.String(http.StatusOK, "")
} }
// Version endpoint returns the server version and build information. // Version endpoint returns the server version and build information.
func Version(c *gin.Context) { func Version(c *gin.Context) {
c.JSON(200, gin.H{ c.JSON(http.StatusOK, gin.H{
"source": "https://github.com/woodpecker-ci/woodpecker", "source": "https://github.com/woodpecker-ci/woodpecker",
"version": version.String(), "version": version.String(),
}) })
@ -44,7 +44,7 @@ func Version(c *gin.Context) {
// LogLevel endpoint returns the current logging level // LogLevel endpoint returns the current logging level
func LogLevel(c *gin.Context) { func LogLevel(c *gin.Context) {
c.JSON(200, gin.H{ c.JSON(http.StatusOK, gin.H{
"log-level": zerolog.GlobalLevel().String(), "log-level": zerolog.GlobalLevel().String(),
}) })
} }
@ -67,5 +67,5 @@ func SetLogLevel(c *gin.Context) {
log.Log().Msgf("log level set to %s", lvl.String()) log.Log().Msgf("log level set to %s", lvl.String())
zerolog.SetGlobalLevel(lvl) zerolog.SetGlobalLevel(lvl)
c.JSON(200, logLevel) c.JSON(http.StatusOK, logLevel)
} }

View file

@ -15,11 +15,13 @@
package session package session
import ( import (
"errors"
"net/http" "net/http"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/woodpecker-ci/woodpecker/server/store/types"
"github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
@ -62,7 +64,11 @@ func SetRepo() gin.HandlerFunc {
) )
if user != nil { if user != nil {
c.AbortWithStatus(http.StatusNotFound) if errors.Is(err, types.RecordNotExist) {
c.AbortWithStatus(http.StatusNotFound)
return
}
_ = c.AbortWithError(http.StatusInternalServerError, err)
} else { } else {
c.AbortWithStatus(http.StatusUnauthorized) c.AbortWithStatus(http.StatusUnauthorized)
} }

View file

@ -16,6 +16,7 @@ package datastore
import ( import (
"errors" "errors"
"strings"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"xorm.io/builder" "xorm.io/builder"
@ -72,7 +73,7 @@ func (s storage) GetRepoName(fullName string) (*model.Repo, error) {
func (s storage) getRepoName(e *xorm.Session, fullName string) (*model.Repo, error) { func (s storage) getRepoName(e *xorm.Session, fullName string) (*model.Repo, error) {
repo := new(model.Repo) repo := new(model.Repo)
return repo, wrapGet(e.Where("repo_full_name = ?", fullName).Get(repo)) return repo, wrapGet(e.Where("LOWER(repo_full_name) = ?", strings.ToLower(fullName)).Get(repo))
} }
func (s storage) GetRepoCount() (int64, error) { func (s storage) GetRepoCount() (int64, error) {

View file

@ -103,6 +103,22 @@ func TestRepos(t *testing.T) {
g.Assert(repo.Name).Equal(getrepo.Name) g.Assert(repo.Name).Equal(getrepo.Name)
}) })
g.It("Should Get a Repo by Name (case-insensitive)", func() {
repo := model.Repo{
UserID: 1,
FullName: "bradrydzewski/TEST",
Owner: "bradrydzewski",
Name: "TEST",
}
g.Assert(store.CreateRepo(&repo)).IsNil()
getrepo, err := store.GetRepoName("Bradrydzewski/test")
g.Assert(err).IsNil()
g.Assert(repo.ID).Equal(getrepo.ID)
g.Assert(repo.UserID).Equal(getrepo.UserID)
g.Assert(repo.Owner).Equal(getrepo.Owner)
g.Assert(repo.Name).Equal(getrepo.Name)
})
g.It("Should Enforce Unique Repo Name", func() { g.It("Should Enforce Unique Repo Name", func() {
repo1 := model.Repo{ repo1 := model.Repo{
UserID: 1, UserID: 1,

View file

@ -358,6 +358,7 @@
}, },
"capacity": { "capacity": {
"capacity": "Capacity", "capacity": "Capacity",
"desc": "The max amount of parallel pipelines executed by this agent.",
"badge": "capacity" "badge": "capacity"
}, },
"version": "Version", "version": "Version",

View file

@ -78,7 +78,7 @@
:label="$t('admin.settings.agents.capacity.capacity')" :label="$t('admin.settings.agents.capacity.capacity')"
docs-url="docs/next/administration/agent-config#woodpecker_max_procs" docs-url="docs/next/administration/agent-config#woodpecker_max_procs"
> >
<span class="text-color-alt">The max amount of parallel pipelines executed by this agent.</span> <span class="text-color-alt">{{ $t('admin.settings.agents.capacity.desc') }}</span>
<TextField :model-value="selectedAgent.capacity?.toString()" disabled /> <TextField :model-value="selectedAgent.capacity?.toString()" disabled />
</InputField> </InputField>
@ -98,11 +98,15 @@
</InputField> </InputField>
</template> </template>
<Button <div class="flex gap-2">
:is-loading="isSaving" <Button type="button" color="gray" :text="$t('cancel')" @click="selectedAgent = undefined" />
type="submit" <Button
:text="isEditingAgent ? $t('admin.settings.agents.save') : $t('admin.settings.agents.add')" :is-loading="isSaving"
/> type="submit"
color="green"
:text="isEditingAgent ? $t('admin.settings.agents.save') : $t('admin.settings.agents.add')"
/>
</div>
</form> </form>
</div> </div>
</Panel> </Panel>

View file

@ -27,7 +27,8 @@
<IconButton <IconButton
icon="edit" icon="edit"
:title="$t('admin.settings.users.edit_user')" :title="$t('admin.settings.users.edit_user')"
class="ml-2 w-8 h-8" class="w-8 h-8"
:class="{ 'ml-auto': !user.admin, 'ml-2': user.admin }"
@click="editUser(user)" @click="editUser(user)"
/> />
<IconButton <IconButton

View file

@ -74,12 +74,15 @@
<span v-else class="text-color">{{ $t('repo.settings.crons.not_executed_yet') }}</span> <span v-else class="text-color">{{ $t('repo.settings.crons.not_executed_yet') }}</span>
</div> </div>
<Button <div class="flex gap-2">
type="submit" <Button type="button" color="gray" :text="$t('cancel')" @click="selectedCron = undefined" />
color="green" <Button
:is-loading="isSaving" type="submit"
:text="isEditingCron ? $t('repo.settings.crons.save') : $t('repo.settings.crons.add')" color="green"
/> :is-loading="isSaving"
:text="isEditingCron ? $t('repo.settings.crons.save') : $t('repo.settings.crons.add')"
/>
</div>
</form> </form>
</div> </div>
</Panel> </Panel>

View file

@ -65,12 +65,15 @@
<TextField v-model="selectedRegistry.password" :placeholder="$t('password')" required /> <TextField v-model="selectedRegistry.password" :placeholder="$t('password')" required />
</InputField> </InputField>
<Button <div class="flex gap-2">
type="submit" <Button type="button" color="gray" :text="$t('cancel')" @click="selectedRegistry = undefined" />
color="green" <Button
:is-loading="isSaving" type="submit"
:text="isEditingRegistry ? $t('repo.settings.registries.save') : $t('repo.settings.registries.add')" color="green"
/> :is-loading="isSaving"
:text="isEditingRegistry ? $t('repo.settings.registries.save') : $t('repo.settings.registries.add')"
/>
</div>
</form> </form>
</div> </div>
</Panel> </Panel>

View file

@ -24,7 +24,7 @@
<CheckboxesField v-model="innerValue.event" :options="secretEventsOptions" /> <CheckboxesField v-model="innerValue.event" :options="secretEventsOptions" />
</InputField> </InputField>
<div class="flex gap-2 justify-center"> <div class="flex gap-2">
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" /> <Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" />
<Button <Button
type="submit" type="submit"