From b074e08f34b107a1a5251fa2a96657ef15fd8834 Mon Sep 17 00:00:00 2001
From: JakobDev <jakobdev@gmx.de>
Date: Sat, 23 Nov 2024 10:33:55 +0100
Subject: [PATCH 1/2] Improve Swagger documentation for user endpoints

---
 routers/api/v1/org/org.go                 |   4 +
 routers/api/v1/org/team.go                |   4 +
 routers/api/v1/repo/issue_stopwatch.go    |   4 +
 routers/api/v1/repo/issue_tracked_time.go |   4 +
 routers/api/v1/repo/repo.go               |   4 +
 routers/api/v1/user/action.go             |  28 ++
 routers/api/v1/user/app.go                |  24 ++
 routers/api/v1/user/avatar.go             |   8 +
 routers/api/v1/user/email.go              |  12 +
 routers/api/v1/user/follower.go           |  22 +-
 routers/api/v1/user/gpg_key.go            |  22 ++
 routers/api/v1/user/hook.go               |  20 ++
 routers/api/v1/user/key.go                |  14 +
 routers/api/v1/user/quota.go              |  10 +
 routers/api/v1/user/repo.go               |   4 +
 routers/api/v1/user/runners.go            |   4 +
 routers/api/v1/user/settings.go           |   8 +
 routers/api/v1/user/star.go               |  16 +
 routers/api/v1/user/user.go               |  16 +
 routers/api/v1/user/watch.go              |   4 +
 services/context/api.go                   |  11 +
 templates/swagger/v1_json.tmpl            | 362 ++++++++++++++++++++++
 tests/integration/swagger_test.go         | 100 ++++++
 23 files changed, 703 insertions(+), 2 deletions(-)
 create mode 100644 tests/integration/swagger_test.go

diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go
index 95c8c25d8e..3623b85b96 100644
--- a/routers/api/v1/org/org.go
+++ b/routers/api/v1/org/org.go
@@ -68,6 +68,10 @@ func ListMyOrgs(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/OrganizationList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index da4fc13ea1..bf28d54c42 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -91,6 +91,10 @@ func ListUserTeams(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/TeamList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{
 		ListOptions: utils.GetListOptions(ctx),
diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go
index d9054e8f77..dd61967ed0 100644
--- a/routers/api/v1/repo/issue_stopwatch.go
+++ b/routers/api/v1/repo/issue_stopwatch.go
@@ -217,6 +217,10 @@ func GetStopwatches(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/StopWatchList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	sws, err := issues_model.GetUserStopwatches(ctx, ctx.Doer.ID, utils.GetListOptions(ctx))
 	if err != nil {
diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index f83855efac..3d8abfa5f3 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -599,6 +599,10 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/TrackedTimeList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	opts := &issues_model.FindTrackedTimesOptions{
 		ListOptions: utils.GetListOptions(ctx),
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 2c4a65934d..93d40d2dbf 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -304,6 +304,10 @@ func Create(ctx *context.APIContext) {
 	//     "$ref": "#/responses/Repository"
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "409":
 	//     description: The repository with the same name already exists.
 	//   "413":
diff --git a/routers/api/v1/user/action.go b/routers/api/v1/user/action.go
index bf78c2c864..ec5289fdb0 100644
--- a/routers/api/v1/user/action.go
+++ b/routers/api/v1/user/action.go
@@ -44,6 +44,10 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
 	//     description: response when updating a secret
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -88,6 +92,10 @@ func DeleteSecret(ctx *context.APIContext) {
 	//     description: delete one secret of the user
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -132,6 +140,10 @@ func CreateVariable(ctx *context.APIContext) {
 	//     description: response when creating a variable
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -191,6 +203,10 @@ func UpdateVariable(ctx *context.APIContext) {
 	//     description: response when updating a variable
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -244,6 +260,10 @@ func DeleteVariable(ctx *context.APIContext) {
 	//     description: response when deleting a variable
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -279,6 +299,10 @@ func GetVariable(ctx *context.APIContext) {
 	//			"$ref": "#/responses/ActionVariable"
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -326,6 +350,10 @@ func ListVariables(ctx *context.APIContext) {
 	//			"$ref": "#/responses/VariableList"
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index d5b20f7703..c4fb2ea38d 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -46,6 +46,8 @@ func ListAccessTokens(ctx *context.APIContext) {
 	//     "$ref": "#/responses/AccessTokenList"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
 
 	opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)}
 
@@ -95,6 +97,8 @@ func CreateAccessToken(ctx *context.APIContext) {
 	//     "$ref": "#/responses/error"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
 
 	form := web.GetForm(ctx).(*api.CreateAccessTokenOption)
 
@@ -224,6 +228,10 @@ func CreateOauth2Application(ctx *context.APIContext) {
 	//     "$ref": "#/responses/OAuth2Application"
 	//   "400":
 	//     "$ref": "#/responses/error"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
 
@@ -266,6 +274,10 @@ func ListOauth2Applications(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/OAuth2ApplicationList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	apps, total, err := db.FindAndCount[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{
 		ListOptions: utils.GetListOptions(ctx),
@@ -303,6 +315,10 @@ func DeleteOauth2Application(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	appID := ctx.ParamsInt64(":id")
@@ -335,6 +351,10 @@ func GetOauth2Application(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/OAuth2Application"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	appID := ctx.ParamsInt64(":id")
@@ -379,6 +399,10 @@ func UpdateOauth2Application(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/OAuth2Application"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	appID := ctx.ParamsInt64(":id")
diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go
index 30ccb63587..d3833a32bb 100644
--- a/routers/api/v1/user/avatar.go
+++ b/routers/api/v1/user/avatar.go
@@ -28,6 +28,10 @@ func UpdateAvatar(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
 
 	content, err := base64.StdEncoding.DecodeString(form.Image)
@@ -55,6 +59,10 @@ func DeleteAvatar(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	err := user_service.DeleteAvatar(ctx, ctx.Doer)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go
index 1e9d0984df..6bd7e10dd8 100644
--- a/routers/api/v1/user/email.go
+++ b/routers/api/v1/user/email.go
@@ -27,6 +27,10 @@ func ListEmails(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/EmailList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	emails, err := user_model.GetEmailAddresses(ctx, ctx.Doer.ID)
 	if err != nil {
@@ -55,6 +59,10 @@ func AddEmail(ctx *context.APIContext) {
 	// responses:
 	//   '201':
 	//     "$ref": "#/responses/EmailList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "422":
 	//     "$ref": "#/responses/validationError"
 
@@ -112,6 +120,10 @@ func DeleteEmail(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go
index 1ba7346b8c..784e2325a3 100644
--- a/routers/api/v1/user/follower.go
+++ b/routers/api/v1/user/follower.go
@@ -53,6 +53,10 @@ func ListMyFollowers(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/UserList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	listUserFollowers(ctx, ctx.Doer)
 }
@@ -117,6 +121,10 @@ func ListMyFollowing(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/UserList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	listUserFollowing(ctx, ctx.Doer)
 }
@@ -173,6 +181,10 @@ func CheckMyFollowing(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -222,10 +234,12 @@ func Follow(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
-	//   "404":
-	//     "$ref": "#/responses/notFound"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
+	//   "404":
+	//     "$ref": "#/responses/notFound"
 
 	if err := user_model.FollowUser(ctx, ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
 		if errors.Is(err, user_model.ErrBlockedByUser) {
@@ -252,6 +266,10 @@ func Unfollow(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go
index 5a2f995e1b..2fe4eb8e78 100644
--- a/routers/api/v1/user/gpg_key.go
+++ b/routers/api/v1/user/gpg_key.go
@@ -92,6 +92,10 @@ func ListMyGPGKeys(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/GPGKeyList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	listGPGKeys(ctx, ctx.Doer.ID, utils.GetListOptions(ctx))
 }
@@ -113,6 +117,10 @@ func GetGPGKey(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/GPGKey"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -164,6 +172,10 @@ func GetVerificationToken(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/string"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -183,6 +195,10 @@ func VerifyUserGPGKey(ctx *context.APIContext) {
 	// responses:
 	//   "201":
 	//     "$ref": "#/responses/GPGKey"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	//   "422":
@@ -244,6 +260,10 @@ func CreateGPGKey(ctx *context.APIContext) {
 	// responses:
 	//   "201":
 	//     "$ref": "#/responses/GPGKey"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	//   "422":
@@ -270,6 +290,8 @@ func DeleteGPGKey(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 	//   "404":
diff --git a/routers/api/v1/user/hook.go b/routers/api/v1/user/hook.go
index 9d9ca5bf01..47b6498d85 100644
--- a/routers/api/v1/user/hook.go
+++ b/routers/api/v1/user/hook.go
@@ -32,6 +32,10 @@ func ListHooks(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/HookList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	utils.ListOwnerHooks(
 		ctx,
@@ -56,6 +60,10 @@ func GetHook(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/Hook"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	hook, err := utils.GetOwnerHook(ctx, ctx.Doer.ID, ctx.ParamsInt64("id"))
 	if err != nil {
@@ -93,6 +101,10 @@ func CreateHook(ctx *context.APIContext) {
 	// responses:
 	//   "201":
 	//     "$ref": "#/responses/Hook"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	utils.AddOwnerHook(
 		ctx,
@@ -124,6 +136,10 @@ func EditHook(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/Hook"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	utils.EditOwnerHook(
 		ctx,
@@ -150,6 +166,10 @@ func DeleteHook(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	utils.DeleteOwnerHook(
 		ctx,
diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go
index d9456e7ec6..1b4ba0a40f 100644
--- a/routers/api/v1/user/key.go
+++ b/routers/api/v1/user/key.go
@@ -121,6 +121,10 @@ func ListMyPublicKeys(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/PublicKeyList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	listPublicKeys(ctx, ctx.Doer)
 }
@@ -176,6 +180,10 @@ func GetPublicKey(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/PublicKey"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -240,6 +248,10 @@ func CreatePublicKey(ctx *context.APIContext) {
 	// responses:
 	//   "201":
 	//     "$ref": "#/responses/PublicKey"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "422":
 	//     "$ref": "#/responses/validationError"
 
@@ -264,6 +276,8 @@ func DeletePublicKey(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 	//   "404":
diff --git a/routers/api/v1/user/quota.go b/routers/api/v1/user/quota.go
index 573d7b7fbc..ab2881b355 100644
--- a/routers/api/v1/user/quota.go
+++ b/routers/api/v1/user/quota.go
@@ -18,6 +18,8 @@ func GetQuota(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/QuotaInfo"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 
@@ -34,6 +36,8 @@ func CheckQuota(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/boolean"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 	//   "422":
@@ -61,6 +65,8 @@ func ListQuotaAttachments(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/QuotaUsedAttachmentList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 
@@ -86,6 +92,8 @@ func ListQuotaPackages(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/QuotaUsedPackageList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 
@@ -111,6 +119,8 @@ func ListQuotaArtifacts(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/QuotaUsedArtifactList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 
diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go
index 3b304c30b0..3b0654ef13 100644
--- a/routers/api/v1/user/repo.go
+++ b/routers/api/v1/user/repo.go
@@ -106,6 +106,10 @@ func ListMyRepos(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/RepositoryList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "422":
 	//     "$ref": "#/responses/validationError"
 
diff --git a/routers/api/v1/user/runners.go b/routers/api/v1/user/runners.go
index 899218473e..dc4c187ffe 100644
--- a/routers/api/v1/user/runners.go
+++ b/routers/api/v1/user/runners.go
@@ -21,6 +21,10 @@ func GetRegistrationToken(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/RegistrationToken"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
 }
diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go
index bfd24013db..173f06e474 100644
--- a/routers/api/v1/user/settings.go
+++ b/routers/api/v1/user/settings.go
@@ -24,6 +24,10 @@ func GetUserSettings(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/UserSettings"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	ctx.JSON(http.StatusOK, convert.User2UserSettings(ctx.Doer))
 }
 
@@ -42,6 +46,10 @@ func UpdateUserSettings(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/UserSettings"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	form := web.GetForm(ctx).(*api.UserSettingsOptions)
 
diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go
index cb9e05f791..be84b13204 100644
--- a/routers/api/v1/user/star.go
+++ b/routers/api/v1/user/star.go
@@ -96,6 +96,10 @@ func GetMyStarredRepos(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/RepositoryList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	repos, err := getStarredRepos(ctx, ctx.Doer, true, utils.GetListOptions(ctx))
 	if err != nil {
@@ -125,6 +129,10 @@ func IsStarring(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -154,6 +162,10 @@ func Star(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
@@ -185,6 +197,10 @@ func Unstar(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index 4fa3f25ad7..da1250b283 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -137,6 +137,10 @@ func GetAuthenticatedUser(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/User"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	ctx.JSON(http.StatusOK, convert.ToUser(ctx, ctx.Doer, ctx.Doer))
 }
@@ -244,6 +248,10 @@ func ListBlockedUsers(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/BlockedUserList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	utils.ListUserBlockedUsers(ctx, ctx.Doer)
 }
@@ -264,6 +272,10 @@ func BlockUser(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	//   "422":
@@ -293,6 +305,10 @@ func UnblockUser(ctx *context.APIContext) {
 	// responses:
 	//   "204":
 	//     "$ref": "#/responses/empty"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 	//   "422":
diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go
index 706f4cc66b..dc27a38a03 100644
--- a/routers/api/v1/user/watch.go
+++ b/routers/api/v1/user/watch.go
@@ -91,6 +91,10 @@ func GetMyWatchedRepos(ctx *context.APIContext) {
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/RepositoryList"
+	//   "401":
+	//     "$ref": "#/responses/unauthorized"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
 
 	repos, total, err := getWatchedRepos(ctx, ctx.Doer, true, utils.GetListOptions(ctx))
 	if err != nil {
diff --git a/services/context/api.go b/services/context/api.go
index c560994258..396ceb520f 100644
--- a/services/context/api.go
+++ b/services/context/api.go
@@ -99,6 +99,17 @@ type swaggerAPIInvalidTopicsError struct {
 // swagger:response empty
 type APIEmpty struct{}
 
+type APIUnauthorizedError struct {
+	APIError
+}
+
+// APIUnauthorizedError is a unauthorized error response
+// swagger:response unauthorized
+type swaggerAPUnauthorizedError struct {
+	// in:body
+	Body APIUnauthorizedError `json:"body"`
+}
+
 type APIForbiddenError struct {
 	APIError
 }
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 3e3838ccc2..d939e5d469 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -17315,6 +17315,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/User"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -17332,6 +17338,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/RegistrationToken"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -17375,6 +17387,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17408,6 +17426,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17445,6 +17469,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17477,6 +17507,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17520,6 +17556,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17563,6 +17605,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17596,6 +17644,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17629,6 +17683,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/OAuth2ApplicationList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -17657,6 +17717,12 @@
           },
           "400": {
             "$ref": "#/responses/error"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -17685,6 +17751,12 @@
           "200": {
             "$ref": "#/responses/OAuth2Application"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17713,6 +17785,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17749,6 +17827,12 @@
           "200": {
             "$ref": "#/responses/OAuth2Application"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17777,6 +17861,12 @@
         "responses": {
           "204": {
             "$ref": "#/responses/empty"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -17792,6 +17882,12 @@
         "responses": {
           "204": {
             "$ref": "#/responses/empty"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -17819,6 +17915,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           },
@@ -17841,6 +17943,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/EmailList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -17866,6 +17974,12 @@
           "201": {
             "$ref": "#/responses/EmailList"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "422": {
             "$ref": "#/responses/validationError"
           }
@@ -17893,6 +18007,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -17926,6 +18046,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/UserList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -17957,6 +18083,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/UserList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -17981,6 +18113,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18005,6 +18143,9 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           },
@@ -18032,6 +18173,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18052,6 +18199,12 @@
           "200": {
             "$ref": "#/responses/string"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18075,6 +18228,12 @@
           "201": {
             "$ref": "#/responses/GPGKey"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           },
@@ -18111,6 +18270,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/GPGKeyList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -18139,6 +18304,12 @@
           "201": {
             "$ref": "#/responses/GPGKey"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           },
@@ -18172,6 +18343,12 @@
           "200": {
             "$ref": "#/responses/GPGKey"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18200,6 +18377,9 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           },
@@ -18236,6 +18416,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/HookList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -18264,6 +18450,12 @@
         "responses": {
           "201": {
             "$ref": "#/responses/Hook"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -18291,6 +18483,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/Hook"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -18316,6 +18514,12 @@
         "responses": {
           "204": {
             "$ref": "#/responses/empty"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -18351,6 +18555,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/Hook"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -18388,6 +18598,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/PublicKeyList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -18416,6 +18632,12 @@
           "201": {
             "$ref": "#/responses/PublicKey"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "422": {
             "$ref": "#/responses/validationError"
           }
@@ -18446,6 +18668,12 @@
           "200": {
             "$ref": "#/responses/PublicKey"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18474,6 +18702,9 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           },
@@ -18510,6 +18741,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/BlockedUserList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -18542,6 +18779,12 @@
           "200": {
             "$ref": "#/responses/OrganizationList"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18562,6 +18805,9 @@
           "200": {
             "$ref": "#/responses/QuotaInfo"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           }
@@ -18596,6 +18842,9 @@
           "200": {
             "$ref": "#/responses/QuotaUsedArtifactList"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           }
@@ -18630,6 +18879,9 @@
           "200": {
             "$ref": "#/responses/QuotaUsedAttachmentList"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           }
@@ -18650,6 +18902,9 @@
           "200": {
             "$ref": "#/responses/boolean"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           },
@@ -18687,6 +18942,9 @@
           "200": {
             "$ref": "#/responses/QuotaUsedPackageList"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
           "403": {
             "$ref": "#/responses/forbidden"
           }
@@ -18727,6 +18985,12 @@
           "200": {
             "$ref": "#/responses/RepositoryList"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "422": {
             "$ref": "#/responses/validationError"
           }
@@ -18761,6 +19025,12 @@
           "400": {
             "$ref": "#/responses/error"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "409": {
             "description": "The repository with the same name already exists."
           },
@@ -18786,6 +19056,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/UserSettings"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       },
@@ -18810,6 +19086,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/UserSettings"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -18841,6 +19123,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/RepositoryList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -18872,6 +19160,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18903,6 +19197,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18934,6 +19234,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           }
@@ -18970,6 +19276,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/StopWatchList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -19001,6 +19313,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/RepositoryList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -19032,6 +19350,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/TeamList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -19077,6 +19401,12 @@
         "responses": {
           "200": {
             "$ref": "#/responses/TrackedTimeList"
+          },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
           }
         }
       }
@@ -19104,6 +19434,12 @@
           "204": {
             "$ref": "#/responses/empty"
           },
+          "401": {
+            "$ref": "#/responses/unauthorized"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
           "404": {
             "$ref": "#/responses/notFound"
           },
@@ -19726,6 +20062,9 @@
           },
           "403": {
             "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
           }
         }
       },
@@ -19766,6 +20105,9 @@
           },
           "403": {
             "$ref": "#/responses/forbidden"
+          },
+          "404": {
+            "$ref": "#/responses/notFound"
           }
         }
       }
@@ -19912,6 +20254,20 @@
       },
       "x-go-package": "code.gitea.io/gitea/services/context"
     },
+    "APIUnauthorizedError": {
+      "type": "object",
+      "properties": {
+        "message": {
+          "type": "string",
+          "x-go-name": "Message"
+        },
+        "url": {
+          "type": "string",
+          "x-go-name": "URL"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/services/context"
+    },
     "APIValidationError": {
       "type": "object",
       "properties": {
@@ -28488,6 +28844,12 @@
         "type": "string"
       }
     },
+    "unauthorized": {
+      "description": "APIUnauthorizedError is a unauthorized error response",
+      "schema": {
+        "$ref": "#/definitions/APIUnauthorizedError"
+      }
+    },
     "validationError": {
       "description": "APIValidationError is error format response related to input validation",
       "schema": {
diff --git a/tests/integration/swagger_test.go b/tests/integration/swagger_test.go
new file mode 100644
index 0000000000..584601f7f9
--- /dev/null
+++ b/tests/integration/swagger_test.go
@@ -0,0 +1,100 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+	"net/http"
+	"strings"
+	"testing"
+
+	"code.gitea.io/gitea/modules/json"
+	"code.gitea.io/gitea/tests"
+
+	swagger_spec "github.com/go-openapi/spec"
+	"github.com/stretchr/testify/require"
+)
+
+func getSwagger(t *testing.T) *swagger_spec.Swagger {
+	t.Helper()
+
+	resp := MakeRequest(t, NewRequest(t, "GET", "/swagger.v1.json"), http.StatusOK)
+
+	swagger := new(swagger_spec.Swagger)
+
+	decoder := json.NewDecoder(resp.Body)
+	require.NoError(t, decoder.Decode(swagger))
+
+	return swagger
+}
+
+func checkSwaggerMethodResponse(t *testing.T, path string, method *swagger_spec.Operation, name string, statusCode int, responseType string) {
+	t.Helper()
+
+	if method == nil {
+		return
+	}
+
+	val, ok := method.Responses.StatusCodeResponses[statusCode]
+	if !ok {
+		t.Errorf("%s %s is missing response status code %d in swagger", name, path, statusCode)
+		return
+	}
+
+	if responseType != val.Ref.String() {
+		t.Errorf("%s %s has %s response type for %d in swagger (expected %s)", name, path, val.Ref.String(), statusCode, responseType)
+	}
+}
+
+func checkSwaggerPathResponse(t *testing.T, paths map[string]swagger_spec.PathItem, pathMatch string, statusCode int, responseType string) {
+	t.Helper()
+
+	for pathName, pathData := range paths {
+		if pathName != pathMatch {
+			continue
+		}
+
+		checkSwaggerMethodResponse(t, pathName, pathData.Get, "GET", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Put, "PUT", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Post, "POST", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Patch, "PATCH", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Delete, "DELETE", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Options, "OPTIONS", statusCode, responseType)
+
+		return
+	}
+}
+
+func checkSwaggerRouteResponse(t *testing.T, paths map[string]swagger_spec.PathItem, prefix string, statusCode int, responseType string) {
+	t.Helper()
+
+	for pathName, pathData := range paths {
+		if !strings.HasPrefix(pathName, prefix) {
+			continue
+		}
+
+		checkSwaggerMethodResponse(t, pathName, pathData.Get, "GET", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Put, "PUT", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Post, "POST", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Patch, "PATCH", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Delete, "DELETE", statusCode, responseType)
+		checkSwaggerMethodResponse(t, pathName, pathData.Options, "OPTIONS", statusCode, responseType)
+	}
+}
+
+func TestSwaggerUserRoute(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	swagger := getSwagger(t)
+
+	checkSwaggerPathResponse(t, swagger.Paths.Paths, "/user", http.StatusUnauthorized, "#/responses/unauthorized")
+	checkSwaggerRouteResponse(t, swagger.Paths.Paths, "/user/", http.StatusUnauthorized, "#/responses/unauthorized")
+}
+
+func TestSwaggerUsersRoute(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	swagger := getSwagger(t)
+
+	checkSwaggerRouteResponse(t, swagger.Paths.Paths, "/users/{username}", http.StatusNotFound, "#/responses/notFound")
+}

From 76fb2afc40c52beeed0551454c04335f48ecf0c1 Mon Sep 17 00:00:00 2001
From: JakobDev <jakobdev@gmx.de>
Date: Thu, 28 Nov 2024 19:36:55 +0100
Subject: [PATCH 2/2] Run make tidy

---
 go.mod | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/go.mod b/go.mod
index 0a71fd1cb9..7213ac6333 100644
--- a/go.mod
+++ b/go.mod
@@ -45,6 +45,7 @@ require (
 	github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
 	github.com/go-git/go-git/v5 v5.11.0
 	github.com/go-ldap/ldap/v3 v3.4.6
+	github.com/go-openapi/spec v0.20.14
 	github.com/go-sql-driver/mysql v1.8.1
 	github.com/go-swagger/go-swagger v0.30.5
 	github.com/go-testfixtures/testfixtures/v3 v3.12.0
@@ -186,7 +187,6 @@ require (
 	github.com/go-openapi/jsonreference v0.20.4 // indirect
 	github.com/go-openapi/loads v0.21.5 // indirect
 	github.com/go-openapi/runtime v0.26.2 // indirect
-	github.com/go-openapi/spec v0.20.14 // indirect
 	github.com/go-openapi/strfmt v0.22.0 // indirect
 	github.com/go-openapi/swag v0.22.7 // indirect
 	github.com/go-openapi/validate v0.22.6 // indirect