Skip to content

Commit 254a828

Browse files
JakobDevsilverwindGiteaBot
authored
Add API for changing Avatars (#25369)
This adds an API for uploading and Deleting Avatars for of Users, Repos and Organisations. I'm not sure, if this should also be added to the Admin API. Resolves #25344 --------- Co-authored-by: silverwind <[email protected]> Co-authored-by: Giteabot <[email protected]>
1 parent 9fd63aa commit 254a828

File tree

12 files changed

+666
-1
lines changed

12 files changed

+666
-1
lines changed

modules/structs/repo.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,9 @@ type NewIssuePinsAllowed struct {
380380
Issues bool `json:"issues"`
381381
PullRequests bool `json:"pull_requests"`
382382
}
383+
384+
// UpdateRepoAvatarUserOption options when updating the repo avatar
385+
type UpdateRepoAvatarOption struct {
386+
// image must be base64 encoded
387+
Image string `json:"image" binding:"Required"`
388+
}

modules/structs/user.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,9 @@ type RenameUserOption struct {
102102
// unique: true
103103
NewName string `json:"new_username" binding:"Required"`
104104
}
105+
106+
// UpdateUserAvatarUserOption options when updating the user avatar
107+
type UpdateUserAvatarOption struct {
108+
// image must be base64 encoded
109+
Image string `json:"image" binding:"Required"`
110+
}

routers/api/v1/api.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,11 @@ func Routes() *web.Route {
899899
Patch(bind(api.EditHookOption{}), user.EditHook).
900900
Delete(user.DeleteHook)
901901
}, reqWebhooksEnabled())
902+
903+
m.Group("/avatar", func() {
904+
m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar)
905+
m.Delete("", user.DeleteAvatar)
906+
}, reqToken())
902907
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
903908

904909
// Repositories (requires repo scope, org scope)
@@ -1134,6 +1139,10 @@ func Routes() *web.Route {
11341139
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
11351140
m.Get("/activities/feeds", repo.ListRepoActivityFeeds)
11361141
m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed)
1142+
m.Group("/avatar", func() {
1143+
m.Post("", bind(api.UpdateRepoAvatarOption{}), repo.UpdateAvatar)
1144+
m.Delete("", repo.DeleteAvatar)
1145+
}, reqAdmin(), reqToken())
11371146
}, repoAssignment())
11381147
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
11391148

@@ -1314,6 +1323,10 @@ func Routes() *web.Route {
13141323
Patch(bind(api.EditHookOption{}), org.EditHook).
13151324
Delete(org.DeleteHook)
13161325
}, reqToken(), reqOrgOwnership(), reqWebhooksEnabled())
1326+
m.Group("/avatar", func() {
1327+
m.Post("", bind(api.UpdateUserAvatarOption{}), org.UpdateAvatar)
1328+
m.Delete("", org.DeleteAvatar)
1329+
}, reqToken(), reqOrgOwnership())
13171330
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
13181331
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true))
13191332
m.Group("/teams/{teamid}", func() {

routers/api/v1/org/avatar.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package org
5+
6+
import (
7+
"encoding/base64"
8+
"net/http"
9+
10+
"code.gitea.io/gitea/modules/context"
11+
api "code.gitea.io/gitea/modules/structs"
12+
"code.gitea.io/gitea/modules/web"
13+
user_service "code.gitea.io/gitea/services/user"
14+
)
15+
16+
// UpdateAvatarupdates the Avatar of an Organisation
17+
func UpdateAvatar(ctx *context.APIContext) {
18+
// swagger:operation POST /orgs/{org}/avatar organization orgUpdateAvatar
19+
// ---
20+
// summary: Update Avatar
21+
// produces:
22+
// - application/json
23+
// parameters:
24+
// - name: org
25+
// in: path
26+
// description: name of the organization
27+
// type: string
28+
// required: true
29+
// - name: body
30+
// in: body
31+
// schema:
32+
// "$ref": "#/definitions/UpdateUserAvatarOption"
33+
// responses:
34+
// "204":
35+
// "$ref": "#/responses/empty"
36+
form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
37+
38+
content, err := base64.StdEncoding.DecodeString(form.Image)
39+
if err != nil {
40+
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
41+
return
42+
}
43+
44+
err = user_service.UploadAvatar(ctx.Org.Organization.AsUser(), content)
45+
if err != nil {
46+
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
47+
}
48+
49+
ctx.Status(http.StatusNoContent)
50+
}
51+
52+
// DeleteAvatar deletes the Avatar of an Organisation
53+
func DeleteAvatar(ctx *context.APIContext) {
54+
// swagger:operation DELETE /orgs/{org}/avatar organization orgDeleteAvatar
55+
// ---
56+
// summary: Delete Avatar
57+
// produces:
58+
// - application/json
59+
// parameters:
60+
// - name: org
61+
// in: path
62+
// description: name of the organization
63+
// type: string
64+
// required: true
65+
// responses:
66+
// "204":
67+
// "$ref": "#/responses/empty"
68+
err := user_service.DeleteAvatar(ctx.Org.Organization.AsUser())
69+
if err != nil {
70+
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
71+
}
72+
73+
ctx.Status(http.StatusNoContent)
74+
}

routers/api/v1/repo/avatar.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package repo
5+
6+
import (
7+
"encoding/base64"
8+
"net/http"
9+
10+
"code.gitea.io/gitea/modules/context"
11+
api "code.gitea.io/gitea/modules/structs"
12+
"code.gitea.io/gitea/modules/web"
13+
repo_service "code.gitea.io/gitea/services/repository"
14+
)
15+
16+
// UpdateVatar updates the Avatar of an Repo
17+
func UpdateAvatar(ctx *context.APIContext) {
18+
// swagger:operation POST /repos/{owner}/{repo}/avatar repository repoUpdateAvatar
19+
// ---
20+
// summary: Update avatar
21+
// produces:
22+
// - application/json
23+
// parameters:
24+
// - name: owner
25+
// in: path
26+
// description: owner of the repo
27+
// type: string
28+
// required: true
29+
// - name: repo
30+
// in: path
31+
// description: name of the repo
32+
// type: string
33+
// required: true
34+
// - name: body
35+
// in: body
36+
// schema:
37+
// "$ref": "#/definitions/UpdateRepoAvatarOption"
38+
// responses:
39+
// "204":
40+
// "$ref": "#/responses/empty"
41+
form := web.GetForm(ctx).(*api.UpdateRepoAvatarOption)
42+
43+
content, err := base64.StdEncoding.DecodeString(form.Image)
44+
if err != nil {
45+
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
46+
return
47+
}
48+
49+
err = repo_service.UploadAvatar(ctx, ctx.Repo.Repository, content)
50+
if err != nil {
51+
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
52+
}
53+
54+
ctx.Status(http.StatusNoContent)
55+
}
56+
57+
// UpdateAvatar deletes the Avatar of an Repo
58+
func DeleteAvatar(ctx *context.APIContext) {
59+
// swagger:operation DELETE /repos/{owner}/{repo}/avatar repository repoDeleteAvatar
60+
// ---
61+
// summary: Delete avatar
62+
// produces:
63+
// - application/json
64+
// parameters:
65+
// - name: owner
66+
// in: path
67+
// description: owner of the repo
68+
// type: string
69+
// required: true
70+
// - name: repo
71+
// in: path
72+
// description: name of the repo
73+
// type: string
74+
// required: true
75+
// responses:
76+
// "204":
77+
// "$ref": "#/responses/empty"
78+
err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository)
79+
if err != nil {
80+
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
81+
}
82+
83+
ctx.Status(http.StatusNoContent)
84+
}

routers/api/v1/swagger/options.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,10 @@ type swaggerParameterBodies struct {
181181

182182
// in:body
183183
CreatePushMirrorOption api.CreatePushMirrorOption
184+
185+
// in:body
186+
UpdateUserAvatarOptions api.UpdateUserAvatarOption
187+
188+
// in:body
189+
UpdateRepoAvatarOptions api.UpdateRepoAvatarOption
184190
}

routers/api/v1/user/avatar.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package user
5+
6+
import (
7+
"encoding/base64"
8+
"net/http"
9+
10+
"code.gitea.io/gitea/modules/context"
11+
api "code.gitea.io/gitea/modules/structs"
12+
"code.gitea.io/gitea/modules/web"
13+
user_service "code.gitea.io/gitea/services/user"
14+
)
15+
16+
// UpdateAvatar updates the Avatar of an User
17+
func UpdateAvatar(ctx *context.APIContext) {
18+
// swagger:operation POST /user/avatar user userUpdateAvatar
19+
// ---
20+
// summary: Update Avatar
21+
// produces:
22+
// - application/json
23+
// parameters:
24+
// - name: body
25+
// in: body
26+
// schema:
27+
// "$ref": "#/definitions/UpdateUserAvatarOption"
28+
// responses:
29+
// "204":
30+
// "$ref": "#/responses/empty"
31+
form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
32+
33+
content, err := base64.StdEncoding.DecodeString(form.Image)
34+
if err != nil {
35+
ctx.Error(http.StatusBadRequest, "DecodeImage", err)
36+
return
37+
}
38+
39+
err = user_service.UploadAvatar(ctx.Doer, content)
40+
if err != nil {
41+
ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
42+
}
43+
44+
ctx.Status(http.StatusNoContent)
45+
}
46+
47+
// DeleteAvatar deletes the Avatar of an User
48+
func DeleteAvatar(ctx *context.APIContext) {
49+
// swagger:operation DELETE /user/avatar user userDeleteAvatar
50+
// ---
51+
// summary: Delete Avatar
52+
// produces:
53+
// - application/json
54+
// responses:
55+
// "204":
56+
// "$ref": "#/responses/empty"
57+
err := user_service.DeleteAvatar(ctx.Doer)
58+
if err != nil {
59+
ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
60+
}
61+
62+
ctx.Status(http.StatusNoContent)
63+
}

0 commit comments

Comments
 (0)