add new user set avatar and profile background

This commit is contained in:
gitGnome
2026-04-28 11:16:37 +02:00
parent 6f7a913e64
commit 8697cd2632
10 changed files with 203 additions and 228 deletions
+1 -1
View File
@@ -3,13 +3,13 @@ module go-socket
go 1.26 go 1.26
require ( require (
github.com/BurntSushi/toml v1.6.0
github.com/coder/websocket v1.8.14 github.com/coder/websocket v1.8.14
github.com/jackc/pgx/v5 v5.8.0 github.com/jackc/pgx/v5 v5.8.0
golang.org/x/crypto v0.49.0 golang.org/x/crypto v0.49.0
) )
require ( require (
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect github.com/go-ini/ini v1.67.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
+33 -3
View File
@@ -83,7 +83,10 @@
<button data-form="get-user-profilebg" onclick="showForm('get-user-profilebg')">GET /user/profilebg</button> <button data-form="get-user-profilebg" onclick="showForm('get-user-profilebg')">GET /user/profilebg</button>
<button data-form="del-user" class="warn" onclick="showForm('del-user')">DELETE /user</button> <button data-form="del-user" class="warn" onclick="showForm('del-user')">DELETE /user</button>
<button data-form="del-connection" class="warn" onclick="showForm('del-connection')">DELETE /connection</button> <button data-form="del-connection" class="warn" onclick="showForm('del-connection')">DELETE /connection</button>
<button data-form="msg-user" onclick="showForm('msg-user')">POST /message</button> <button data-form="msg-user" onclick="showForm('msg-user')">POST /connection/message</button>
<button data-form="hub-create" onclick="showForm('hub-create')">POST /hub</button>
<button data-form="hub-join" onclick="showForm('hub-join')">PUT /hub/join</button>
<button data-form="channel-message" onclick="showForm('channel-message')">POST /channel/message</button>
<button data-form="websocket" onclick="showForm('websocket')">WS /ws</button> <button data-form="websocket" onclick="showForm('websocket')">WS /ws</button>
</div> </div>
@@ -220,7 +223,7 @@
<div class="form-actions"><button class="send" onclick="submit('del-connection')">Send</button></div> <div class="form-actions"><button class="send" onclick="submit('del-connection')">Send</button></div>
</div> </div>
<!-- POST /message --> <!-- POST /connection/message -->
<div class="form-content" id="fc-msg-user"> <div class="form-content" id="fc-msg-user">
<div class="field"><label>token</label><input id="mu-token" placeholder=""></div> <div class="field"><label>token</label><input id="mu-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="mu-connectionid" placeholder="UUID"></div> <div class="field"><label>connectionid</label><input id="mu-connectionid" placeholder="UUID"></div>
@@ -229,6 +232,30 @@
<div class="form-actions"><button class="send" onclick="submit('msg-user')">Send</button></div> <div class="form-actions"><button class="send" onclick="submit('msg-user')">Send</button></div>
</div> </div>
<!-- POST /hub -->
<div class="form-content" id="fc-hub-create">
<div class="field"><label>token</label><input id="hc-token" placeholder=""></div>
<div class="field"><label>hubname</label><input id="hc-hubname" placeholder="name of new hub"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-create')">Send</button></div>
</div>
<!-- PUT /hub/join -->
<div class="form-content" id="fc-hub-join">
<div class="field"><label>token</label><input id="hj-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hj-hubid" placeholder="UUID"><span class="hint">sent as header</span></div>
<div class="form-actions"><button class="send" onclick="submit('hub-join')">Send</button></div>
</div>
<!-- POST /channel/message -->
<div class="form-content" id="fc-channel-message">
<div class="field"><label>token</label><input id="cm-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="cm-hubid" placeholder="UUID"></div>
<div class="field"><label>channelid</label><input id="cm-channelid" placeholder="UUID"></div>
<div class="field"><label>msgContent</label><input id="cm-msgContent" placeholder="message text (optional if file set)"></div>
<div class="field"><label>attachedFile</label><input id="cm-attachedFile" placeholder="key from POST /file (optional)"></div>
<div class="form-actions"><button class="send" onclick="submit('channel-message')">Send</button></div>
</div>
<!-- WS /ws --> <!-- WS /ws -->
<div class="form-content" id="fc-websocket"> <div class="form-content" id="fc-websocket">
<div class="form-actions" style="margin-bottom:10px"> <div class="form-actions" style="margin-bottom:10px">
@@ -267,7 +294,10 @@
'get-connection-messages': { method:'GET', path:'/connection/messages', title:'GET /connection/messages — message history', fields:[{id:'gcm-token',dest:'header',name:'token'},{id:'gcm-connectionid',dest:'query',name:'connectionid'},{id:'gcm-messages',dest:'query',name:'messages'},{id:'gcm-before',dest:'query',name:'before'}] }, 'get-connection-messages': { method:'GET', path:'/connection/messages', title:'GET /connection/messages — message history', fields:[{id:'gcm-token',dest:'header',name:'token'},{id:'gcm-connectionid',dest:'query',name:'connectionid'},{id:'gcm-messages',dest:'query',name:'messages'},{id:'gcm-before',dest:'query',name:'before'}] },
'del-user': { method:'DELETE', path:'/user', title:'DELETE /user — delete own account', fields:[{id:'du-token',dest:'header',name:'token'}] }, 'del-user': { method:'DELETE', path:'/user', title:'DELETE /user — delete own account', fields:[{id:'du-token',dest:'header',name:'token'}] },
'del-connection': { method:'DELETE', path:'/connection', title:'DELETE /connection — delete a connection', fields:[{id:'dc-token',dest:'header',name:'token'},{id:'dc-connectionid',dest:'query',name:'connectionid'}] }, 'del-connection': { method:'DELETE', path:'/connection', title:'DELETE /connection — delete a connection', fields:[{id:'dc-token',dest:'header',name:'token'},{id:'dc-connectionid',dest:'query',name:'connectionid'}] },
'msg-user': { method:'POST', path:'/message', title:'POST /message — send direct message', fields:[{id:'mu-token',dest:'header',name:'token'},{id:'mu-connectionid',dest:'body',name:'connectionid'},{id:'mu-msgContent',dest:'body',name:'msgContent'},{id:'mu-attachedFile',dest:'body',name:'attachedFile'}] }, 'msg-user': { method:'POST', path:'/connection/message', title:'POST /connection/message — send direct message', fields:[{id:'mu-token',dest:'header',name:'token'},{id:'mu-connectionid',dest:'body',name:'connectionid'},{id:'mu-msgContent',dest:'body',name:'msgContent'},{id:'mu-attachedFile',dest:'body',name:'attachedFile'}] },
'hub-create': { method:'POST', path:'/hub', title:'POST /hub — create a new hub', fields:[{id:'hc-token',dest:'header',name:'token'},{id:'hc-hubname',dest:'body',name:'hubname'}] },
'hub-join': { method:'PUT', path:'/hub/join', title:'PUT /hub/join — join hub (hubid as header)', fields:[{id:'hj-token',dest:'header',name:'token'},{id:'hj-hubid',dest:'header',name:'hubid'}] },
'channel-message': { method:'POST', path:'/channel/message', title:'POST /channel/message — send hub channel msg',fields:[{id:'cm-token',dest:'header',name:'token'},{id:'cm-hubid',dest:'body',name:'hubid'},{id:'cm-channelid',dest:'body',name:'channelid'},{id:'cm-msgContent',dest:'body',name:'msgContent'},{id:'cm-attachedFile',dest:'body',name:'attachedFile'}] },
'mod-user-avatar': { title:'PATCH /user/avatar — set avatar image' }, 'mod-user-avatar': { title:'PATCH /user/avatar — set avatar image' },
'mod-user-profilebg': { title:'PATCH /user/profilebg — set profile background' }, 'mod-user-profilebg': { title:'PATCH /user/profilebg — set profile background' },
'file-upload': { title:'POST /file — upload file (multipart)' }, 'file-upload': { title:'POST /file — upload file (multipart)' },
+6 -3
View File
@@ -38,8 +38,8 @@ func main() {
http.HandleFunc("DELETE /user", withCORS(httpRequest.HandleUserDelete)) http.HandleFunc("DELETE /user", withCORS(httpRequest.HandleUserDelete))
http.HandleFunc("GET /user", withCORS(httpRequest.HandleUserGetUser)) http.HandleFunc("GET /user", withCORS(httpRequest.HandleUserGetUser))
http.HandleFunc("PATCH /user/profile", withCORS(httpRequest.HandleUserModProfile)) http.HandleFunc("PATCH /user/profile", withCORS(httpRequest.HandleUserModProfile))
http.HandleFunc("PATCH /user/avatar", withCORS(httpRequest.HandleUserModAvatar)) http.HandleFunc("PATCH /user/avatar", withCORS(httpRequest.HandleSetUserAvatar))
http.HandleFunc("PATCH /user/profilebg", withCORS(httpRequest.HandleUserModProfileBg)) http.HandleFunc("PATCH /user/profilebg", withCORS(httpRequest.HandleSetUserProfileBg))
http.HandleFunc("GET /user/avatar", withCORS(httpRequest.HandleGetUserAvatar)) http.HandleFunc("GET /user/avatar", withCORS(httpRequest.HandleGetUserAvatar))
http.HandleFunc("GET /user/profilebg", withCORS(httpRequest.HandleGetUserProfileBg)) http.HandleFunc("GET /user/profilebg", withCORS(httpRequest.HandleGetUserProfileBg))
@@ -56,7 +56,10 @@ func main() {
http.HandleFunc("POST /file", withCORS(httpRequest.HandleAttachmentFileUpload)) http.HandleFunc("POST /file", withCORS(httpRequest.HandleAttachmentFileUpload))
http.HandleFunc("GET /file", withCORS(httpRequest.HandleAttachmentFileDownload)) http.HandleFunc("GET /file", withCORS(httpRequest.HandleAttachmentFileDownload))
http.HandleFunc("POST /message", withCORS(httpRequest.HandleDm)) http.HandleFunc("POST /hub", withCORS(httpRequest.HandleHubCreate))
http.HandleFunc("PUT /hub/join", withCORS(httpRequest.HandleHubJoin))
http.HandleFunc("POST /connection/message", withCORS(httpRequest.HandleDm))
http.HandleFunc("GET /ws", wsServer.ServeWsConnection) http.HandleFunc("GET /ws", wsServer.ServeWsConnection)
log.Println("beep boop; server server started") log.Println("beep boop; server server started")
+1
View File
@@ -33,6 +33,7 @@ type configFile struct {
MaxRequestWithProfileBgBytes uint32 `toml:"max_request_with_profile_bg_bytes"` MaxRequestWithProfileBgBytes uint32 `toml:"max_request_with_profile_bg_bytes"`
FileProcessingPartBytes uint64 `toml:"file_processing_part_bytes"` FileProcessingPartBytes uint64 `toml:"file_processing_part_bytes"`
FileProcessingThreads uint `toml:"file_processing_threads"` FileProcessingThreads uint `toml:"file_processing_threads"`
FileStorageBucketName string `toml:"file_storage_bucket_name"`
FileDownloadLinkTtl time.Duration `toml:"file_download_link_ttl"` FileDownloadLinkTtl time.Duration `toml:"file_download_link_ttl"`
} }
+139 -8
View File
@@ -2,6 +2,8 @@ package httpRequest
import ( import (
json2 "encoding/json" json2 "encoding/json"
"go-socket/packages/postgresql"
"go-socket/packages/types"
"net/http" "net/http"
"strings" "strings"
@@ -42,7 +44,7 @@ func HandleAttachmentFileUpload(response http.ResponseWriter, request *http.Requ
defer file.Close() defer file.Close()
contentType := header.Header.Get("Content-Type") contentType := header.Header.Get("Content-Type")
key := minio.GetKey(minio.GetKeyOptions{ key := minio.GetKey(&minio.GetKeyOptions{
ConnectionId: conn.Id, ConnectionId: conn.Id,
MimeType: contentType, MimeType: contentType,
UploadType: minio.ConnectionFile, UploadType: minio.ConnectionFile,
@@ -60,6 +62,62 @@ func HandleAttachmentFileUpload(response http.ResponseWriter, request *http.Requ
response.Write([]byte(key)) response.Write([]byte(key))
} }
func HandleSetUserAvatar(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, avatar) {
return
}
ctx := request.Context()
user, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
file, header, err := request.FormFile("file")
if err != nil {
http.Error(response, "missing file", http.StatusBadRequest)
return
}
defer file.Close()
isImg, contentType, err := isImage(file)
if err != nil || !isImg {
http.Error(response, "invalid file", http.StatusBadRequest)
return
}
key := minio.GetKey(&minio.GetKeyOptions{
MimeType: contentType,
UploadType: minio.UserAvatar,
UserId: user.Id,
})
err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
"originalName": header.Filename,
"uploaderId": user.Id.String(),
})
if err != nil {
http.Error(response, "upload failed", http.StatusInternalServerError)
return
}
if user.AvatarUrl != "" {
if err = minio.Delete(ctx, user.AvatarUrl); err != nil {
minio.Delete(ctx, key)
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
}
user.AvatarUrl = key
err = postgresql.UserUpdateProfile(ctx, user, &types.UserProfileUpdateList{Avatar: true})
if err != nil {
http.Error(response, "failed to update user avatar", http.StatusInternalServerError)
minio.Delete(ctx, user.AvatarUrl)
return
}
response.WriteHeader(http.StatusCreated)
}
func HandleGetUserAvatar(response http.ResponseWriter, request *http.Request) { func HandleGetUserAvatar(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, normal) { if !validCheckWithResponseOnFail(&response, request, normal) {
return return
@@ -85,18 +143,82 @@ func HandleGetUserAvatar(response http.ResponseWriter, request *http.Request) {
} }
if target.AvatarUrl == "" { if target.AvatarUrl == "" {
http.Error(response, "no avatar", http.StatusNotFound) http.Error(response, "user have no avatar", http.StatusNoContent)
return return
} }
url, _, err := minio.GetDownloadUrlAndMetadata(ctx, target.AvatarUrl) url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, target.AvatarUrl)
if err != nil { if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
json, err := json2.Marshal(map[string]any{
"url": url.String(),
"metadata": meta,
})
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
}
response.WriteHeader(http.StatusOK) response.WriteHeader(http.StatusOK)
response.Write([]byte(url.String())) response.Write(json)
}
func HandleSetUserProfileBg(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, profileBg) {
return
}
ctx := request.Context()
user, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
file, header, err := request.FormFile("file")
if err != nil {
http.Error(response, "missing file", http.StatusBadRequest)
return
}
defer file.Close()
isImg, contentType, err := isImage(file)
if err != nil || !isImg {
http.Error(response, "invalid file", http.StatusBadRequest)
return
}
key := minio.GetKey(&minio.GetKeyOptions{
MimeType: contentType,
UploadType: minio.UserProfileBg,
UserId: user.Id,
})
err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
"originalName": header.Filename,
"uploaderId": user.Id.String(),
})
if err != nil {
http.Error(response, "upload failed", http.StatusInternalServerError)
return
}
if user.ProfileBgUrl != "" {
if err = minio.Delete(ctx, user.ProfileBgUrl); err != nil {
minio.Delete(ctx, key)
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
}
user.ProfileBgUrl = key
err = postgresql.UserUpdateProfile(ctx, user, &types.UserProfileUpdateList{ProfileBg: true})
if err != nil {
http.Error(response, "failed to update user profile background", http.StatusInternalServerError)
minio.Delete(ctx, user.ProfileBgUrl)
return
}
response.WriteHeader(http.StatusCreated)
} }
func HandleGetUserProfileBg(response http.ResponseWriter, request *http.Request) { func HandleGetUserProfileBg(response http.ResponseWriter, request *http.Request) {
@@ -105,7 +227,8 @@ func HandleGetUserProfileBg(response http.ResponseWriter, request *http.Request)
} }
ctx := request.Context() ctx := request.Context()
if _, err := getUserByToken(ctx, request.Header.Get("token")); err != nil { _, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized) http.Error(response, "invalid token", http.StatusUnauthorized)
return return
} }
@@ -123,18 +246,26 @@ func HandleGetUserProfileBg(response http.ResponseWriter, request *http.Request)
} }
if target.ProfileBgUrl == "" { if target.ProfileBgUrl == "" {
http.Error(response, "no profile background", http.StatusNotFound) http.Error(response, "user have no profile background", http.StatusNoContent)
return return
} }
url, _, err := minio.GetDownloadUrlAndMetadata(ctx, target.ProfileBgUrl) url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, target.ProfileBgUrl)
if err != nil { if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
json, err := json2.Marshal(map[string]any{
"url": url.String(),
"metadata": meta,
})
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
}
response.WriteHeader(http.StatusOK) response.WriteHeader(http.StatusOK)
response.Write([]byte(url.String())) response.Write(json)
} }
func HandleAttachmentFileDownload(response http.ResponseWriter, request *http.Request) { func HandleAttachmentFileDownload(response http.ResponseWriter, request *http.Request) {
+19 -19
View File
@@ -90,22 +90,22 @@ func getHubUserIfValidWithResponseOnFail(ctx context.Context, response http.Resp
return user, hubUser, hub, nil return user, hubUser, hub, nil
} }
func getHubChannelIfValidWithResponseOnFail(ctx context.Context, response http.ResponseWriter, hub *types.Hub, hubUser *types.HubUser, channelId string) ( //func getHubChannelIfValidWithResponseOnFail(ctx context.Context, response http.ResponseWriter, hub *types.Hub, hubUser *types.HubUser, channelId string) (
*types.HubChannel, error) { // *types.HubChannel, error) {
channelUuid, err := convertions.StringToUuid(channelId) // channelUuid, err := convertions.StringToUuid(channelId)
if err != nil { // if err != nil {
http.Error(response, "invalid channelid", http.StatusBadRequest) // http.Error(response, "invalid channelid", http.StatusBadRequest)
return nil, errors.New("invalid channelid") // return nil, errors.New("invalid channelid")
} // }
channel, ok := hub.Channels[channelUuid] // channel, ok := hub.Channels[channelUuid]
if !ok { // if !ok {
http.Error(response, "invalid channelid", http.StatusBadRequest) // http.Error(response, "invalid channelid", http.StatusBadRequest)
return nil, errors.New("invalid channelid") // return nil, errors.New("invalid channelid")
} // }
//
if !haveHubUserPermissionsOnChannel(types.CachedUserCanView, hubUser, channel) { // if !haveHubUserPermissionsOnChannel(types.CachedUserCanView, hubUser, channel) {
return nil, errors.New("invalid channelid") // return nil, errors.New("invalid channelid")
} // }
//
return channel, nil // return channel, nil
} //}
-61
View File
@@ -4,10 +4,8 @@ import (
"net/http" "net/http"
"time" "time"
"go-socket/packages/Enums/WsEventType"
"go-socket/packages/cache" "go-socket/packages/cache"
"go-socket/packages/types" "go-socket/packages/types"
"go-socket/packages/wsServer"
"github.com/google/uuid" "github.com/google/uuid"
) )
@@ -134,65 +132,6 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
cache.SaveHub(hub) cache.SaveHub(hub)
} }
func HandleChannelSendMessage(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, normal) {
return
}
ctx := request.Context()
user, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid"))
if err != nil {
return
}
msgContent := request.FormValue("msgContent")
attachedFile := request.FormValue("attachedFile")
if msgContent == "" && attachedFile == "" {
http.Error(response, "empty msgContent", http.StatusBadRequest)
return
}
channel, err := getHubChannelIfValidWithResponseOnFail(ctx, response, hub, hubUser, request.FormValue("channelid"))
if err != nil {
return
}
if !haveHubUserPermissionsOnChannel(types.CachedUserCanMessage, hubUser, channel) {
http.Error(response, "cannot send messages here", http.StatusUnauthorized)
return
}
message := &types.Message{
Id: uuid.New(),
AttachedFile: "",
Content: msgContent,
Sender: user.Id,
Receiver: channel.Id,
CreatedAt: time.Now(),
}
channel.Mu.RLock()
recipients := make([]uuid.UUID, 0, len(channel.UsersCachedPermissions))
for id, userCachedPerms := range channel.UsersCachedPermissions {
if userCachedPerms.CanReadHistory() && id != user.Id {
recipients = append(recipients, id)
}
}
channel.Mu.RUnlock()
for _, id := range recipients {
targetUser, err := cache.GetUserById(id)
if err != nil {
// todo Add to postgres in future
continue
}
wsServer.WsSendMessageCloseIfTimeout(targetUser, types.WsEventMessage{
Type: WsEventType.HubMessage,
Event: message,
})
}
}
func HandleHubJoin(response http.ResponseWriter, request *http.Request) { func HandleHubJoin(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, normal) { if !validCheckWithResponseOnFail(&response, request, normal) {
return return
+2 -131
View File
@@ -5,11 +5,8 @@ import (
"net/http" "net/http"
"time" "time"
"go-socket/packages/config"
"go-socket/packages/convertions"
"go-socket/packages/minio"
"go-socket/packages/cache" "go-socket/packages/cache"
"go-socket/packages/convertions"
"go-socket/packages/passwords" "go-socket/packages/passwords"
"go-socket/packages/postgresql" "go-socket/packages/postgresql"
"go-socket/packages/tokens" "go-socket/packages/tokens"
@@ -152,7 +149,7 @@ func HandleUserModProfile(response http.ResponseWriter, request *http.Request) {
return return
} }
var updateList types.UserProfileUpdateList updateList := &types.UserProfileUpdateList{}
if pronouns := request.FormValue("pronouns"); pronouns != "" { if pronouns := request.FormValue("pronouns"); pronouns != "" {
if len(pronouns) > 32 { if len(pronouns) > 32 {
@@ -190,132 +187,6 @@ func HandleUserModProfile(response http.ResponseWriter, request *http.Request) {
response.WriteHeader(http.StatusAccepted) response.WriteHeader(http.StatusAccepted)
} }
func HandleUserModAvatar(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, avatar) {
return
}
ctx := request.Context()
user, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
request.Body = http.MaxBytesReader(response, request.Body, int64(config.MaxRequestWithAvatarBytes))
if err = request.ParseMultipartForm(int64(config.MaxRequestBytes)); err != nil {
http.Error(response, "invalid multipart form", http.StatusBadRequest)
return
}
file, header, err := request.FormFile("file")
if err != nil {
http.Error(response, "missing file", http.StatusBadRequest)
return
}
defer file.Close()
isImg, contentType, err := isImage(file)
if err != nil || !isImg {
http.Error(response, "invalid file", http.StatusBadRequest)
return
}
if user.AvatarUrl != "" {
err = minio.Delete(ctx, user.AvatarUrl)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
}
key := minio.GetKey(minio.GetKeyOptions{
UserId: user.Id,
MimeType: contentType,
UploadType: minio.UserAvatar,
})
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
"originalName": header.Filename,
"uploaderId": user.Id.String(),
}); err != nil {
http.Error(response, "upload failed", http.StatusInternalServerError)
return
}
user.AvatarUrl = key
err = postgresql.UserUpdateProfile(ctx, user, types.UserProfileUpdateList{Avatar: true})
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusAccepted)
}
func HandleUserModProfileBg(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, profileBg) {
return
}
ctx := request.Context()
user, err := getUserByToken(ctx, request.Header.Get("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
request.Body = http.MaxBytesReader(response, request.Body, int64(config.MaxRequestWithProfileBgBytes))
if err = request.ParseMultipartForm(int64(config.MaxRequestBytes)); err != nil {
http.Error(response, "invalid multipart form", http.StatusBadRequest)
return
}
file, header, err := request.FormFile("file")
if err != nil {
http.Error(response, "missing file", http.StatusBadRequest)
return
}
defer file.Close()
isImg, contentType, err := isImage(file)
if err != nil || !isImg {
http.Error(response, "invalid file", http.StatusBadRequest)
return
}
if user.ProfileBgUrl != "" {
err = minio.Delete(ctx, user.ProfileBgUrl)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
}
key := minio.GetKey(minio.GetKeyOptions{
UserId: user.Id,
MimeType: contentType,
UploadType: minio.UserProfileBg,
})
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
"originalName": header.Filename,
"uploaderId": user.Id.String(),
}); err != nil {
http.Error(response, "upload failed", http.StatusInternalServerError)
return
}
user.ProfileBgUrl = key
err = postgresql.UserUpdateProfile(ctx, user, types.UserProfileUpdateList{ProfileBg: true})
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusAccepted)
}
func HandleUserGetUser(response http.ResponseWriter, request *http.Request) { func HandleUserGetUser(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(&response, request, normal) { if !validCheckWithResponseOnFail(&response, request, normal) {
return return
+1 -1
View File
@@ -43,7 +43,7 @@ type GetKeyOptions struct {
UploadType DataType UploadType DataType
} }
func GetKey(opts GetKeyOptions) string { func GetKey(opts *GetKeyOptions) string {
extensions, err := mime.ExtensionsByType(opts.MimeType) extensions, err := mime.ExtensionsByType(opts.MimeType)
if err != nil || len(extensions) == 0 { if err != nil || len(extensions) == 0 {
extensions = []string{".unknown"} extensions = []string{".unknown"}
+1 -1
View File
@@ -113,7 +113,7 @@ func UserGetById(ctx context.Context, user *types.User) error {
return err return err
} }
func UserUpdateProfile(ctx context.Context, user *types.User, updateList types.UserProfileUpdateList) error { func UserUpdateProfile(ctx context.Context, user *types.User, updateList *types.UserProfileUpdateList) error {
setClauses := make([]string, 0, 3) setClauses := make([]string, 0, 3)
args := make([]any, 0, 4) args := make([]any, 0, 4)
argIdx := 1 argIdx := 1