user no stays in cache forever, fix some hubs bugs and add new enpoints

This commit is contained in:
2026-05-04 14:03:02 +02:00
parent 22e2d18810
commit 015c79bf09
14 changed files with 260 additions and 34 deletions
BIN
View File
Binary file not shown.
+5 -1
View File
@@ -69,6 +69,10 @@ func main() {
http.HandleFunc("PUT /hub/join", withCORS(httpRequest.HandleHubJoin)) http.HandleFunc("PUT /hub/join", withCORS(httpRequest.HandleHubJoin))
http.HandleFunc("PATCH /hub/name", withCORS(httpRequest.HandleHubSetName)) http.HandleFunc("PATCH /hub/name", withCORS(httpRequest.HandleHubSetName))
http.HandleFunc("PATCH /hub/color", withCORS(httpRequest.HandleHubSetColor)) http.HandleFunc("PATCH /hub/color", withCORS(httpRequest.HandleHubSetColor))
http.HandleFunc("PATCH /hub/icon", withCORS(httpRequest.HandleHubSetIcon))
http.HandleFunc("GET /hub/icon", withCORS(httpRequest.HandleGetHubIcon))
http.HandleFunc("PATCH /hub/bg", withCORS(httpRequest.HandleHubSetBg))
http.HandleFunc("GET /hub/bg", withCORS(httpRequest.HandleGetHubBg))
http.HandleFunc("DELETE /hub", withCORS(httpRequest.HandleHubRemove)) http.HandleFunc("DELETE /hub", withCORS(httpRequest.HandleHubRemove))
http.HandleFunc("PATCH /hub/usercolorallowed", withCORS(httpRequest.HandleHubToggleUserColorAllowed)) http.HandleFunc("PATCH /hub/usercolorallowed", withCORS(httpRequest.HandleHubToggleUserColorAllowed))
http.HandleFunc("DELETE /hub/user", withCORS(httpRequest.HandleHubUserRemove)) http.HandleFunc("DELETE /hub/user", withCORS(httpRequest.HandleHubUserRemove))
@@ -93,7 +97,7 @@ func main() {
http.HandleFunc("POST /connection/message", withCORS(httpRequest.HandleDm)) http.HandleFunc("POST /connection/message", withCORS(httpRequest.HandleDm))
http.HandleFunc("GET /ws", wsServer.ServeWsConnection) http.HandleFunc("GET /ws", wsServer.ServeWsConnection)
http.Handle("GET /client/", http.StripPrefix("/client/", http.FileServer(http.Dir("machine-client")))) http.Handle("GET /client/", http.StripPrefix("/client/", http.FileServer(http.Dir("test-client"))))
log.Println("beep boop; server server started") log.Println("beep boop; server server started")
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(int(config.Port)), nil)) log.Fatal(http.ListenAndServe(":"+strconv.Itoa(int(config.Port)), nil))
+44 -16
View File
@@ -29,18 +29,12 @@ func HandleAttachmentFileUpload(response http.ResponseWriter, request *http.Requ
return return
} }
request.Body = http.MaxBytesReader(response, request.Body, int64(config.MaxRequestWithFileBytes))
if err = request.ParseMultipartForm(int64(config.MaxRequestBytes)); err != nil { if err = request.ParseMultipartForm(int64(config.MaxRequestBytes)); err != nil {
http.Error(response, "invalid multipart form", http.StatusBadRequest) http.Error(response, "invalid multipart form", http.StatusBadRequest)
return return
} }
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user) target := request.FormValue("target_id")
if !ok {
return
}
file, header, err := request.FormFile("file") file, header, err := request.FormFile("file")
if err != nil { if err != nil {
http.Error(response, "missing file", http.StatusBadRequest) http.Error(response, "missing file", http.StatusBadRequest)
@@ -49,11 +43,31 @@ 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{ var key string
ConnectionId: conn.Id,
MimeType: contentType, if conn, ok := getConnection(ctx, target, user); ok {
UploadType: minio.ConnectionFile, key = minio.GetKey(&minio.GetKeyOptions{
}) ConnectionId: conn.Id,
MimeType: contentType,
UploadType: minio.ConnectionFile,
})
} else if channel, ok := getChannelFromUser(user, target); ok {
channel.Mu.RLock()
perms := channel.UsersCachedPermissions[user.Id]
channel.Mu.RUnlock()
if !perms.CanMessage() {
http.Error(response, "forbidden", http.StatusForbidden)
return
}
key = minio.GetKey(&minio.GetKeyOptions{
ChannelId: channel.Id,
MimeType: contentType,
UploadType: minio.HubChannelFile,
})
} else {
http.Error(response, "cannot find target", http.StatusBadRequest)
return
}
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{ if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
"originalName": header.Filename, "originalName": header.Filename,
@@ -322,13 +336,27 @@ func HandleAttachmentFileDownload(response http.ResponseWriter, request *http.Re
return return
} }
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user) target := request.URL.Query().Get("target_id")
if !ok { key := request.URL.Query().Get("key")
var validPrefix string
if conn, ok := getConnection(ctx, target, user); ok {
validPrefix = string(minio.ConnectionFilePrefix) + conn.Id.String() + "/"
} else if channel, ok := getChannelFromUser(user, target); ok {
channel.Mu.RLock()
perms := channel.UsersCachedPermissions[user.Id]
channel.Mu.RUnlock()
if !perms.CanReadHistory() {
http.Error(response, "forbidden", http.StatusForbidden)
return
}
validPrefix = string(minio.HubChannelFilePrefix) + channel.Id.String() + "/"
} else {
http.Error(response, "cannot find target", http.StatusBadRequest)
return return
} }
key := request.URL.Query().Get("key") if !strings.HasPrefix(key, validPrefix) {
if !strings.HasPrefix(key, string(minio.ConnectionFilePrefix)+conn.Id.String()+"/") {
http.Error(response, "no such file", http.StatusUnauthorized) http.Error(response, "no such file", http.StatusUnauthorized)
return return
} }
+39
View File
@@ -36,6 +36,45 @@ func getUserByToken(ctx context.Context, token string) (*types.User, error) {
return getUserById(ctx, userId) return getUserById(ctx, userId)
} }
func getConnection(ctx context.Context, connectionIdStr string, user *types.User) (*types.Connection, bool) {
connectionId, err := convertions.StringToUuid(connectionIdStr)
if err != nil {
return nil, false
}
if conn, ok := cache.GetConnection(user, connectionId); ok {
return conn, true
}
conn, err := postgresql.ConnectionGetById(ctx, connectionId)
if err != nil {
return nil, false
}
if conn.RequestorId != user.Id && conn.RecipientId != user.Id {
return nil, false
}
user.Mu.Lock()
user.Connections[conn.Id] = conn
user.Mu.Unlock()
return conn, true
}
func getChannelFromUser(user *types.User, channelIdStr string) (*types.HubChannel, bool) {
channelId, err := convertions.StringToUuid(channelIdStr)
if err != nil {
return nil, false
}
user.Mu.RLock()
defer user.Mu.RUnlock()
for _, hub := range user.Hubs {
hub.Mu.RLock()
ch, ok := hub.Channels[channelId]
hub.Mu.RUnlock()
if ok {
return ch, true
}
}
return nil, false
}
func getConnectionWithResponseOnFail(response http.ResponseWriter, connectionIdStr string, user *types.User) (*types.Connection, bool) { func getConnectionWithResponseOnFail(response http.ResponseWriter, connectionIdStr string, user *types.User) (*types.Connection, bool) {
connectionId, err := convertions.StringToUuid(connectionIdStr) connectionId, err := convertions.StringToUuid(connectionIdStr)
if err != nil { if err != nil {
+2
View File
@@ -41,6 +41,8 @@ func validCheckWithResponseOnFail(response http.ResponseWriter, request *http.Re
maxSize = int64(config.MaxRequestBytes) maxSize = int64(config.MaxRequestBytes)
} }
request.Body = http.MaxBytesReader(response, request.Body, maxSize)
if request.ContentLength > maxSize { if request.ContentLength > maxSize {
io.Copy(io.Discard, request.Body) io.Copy(io.Discard, request.Body)
http.Error(response, "Request too large", http.StatusRequestEntityTooLarge) http.Error(response, "Request too large", http.StatusRequestEntityTooLarge)
+69 -1
View File
@@ -551,6 +551,74 @@ func HandleHubSetBg(response http.ResponseWriter, request *http.Request) {
response.WriteHeader(http.StatusCreated) response.WriteHeader(http.StatusCreated)
} }
func HandleGetHubIcon(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, _, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
if hub.IconUrl == "" {
http.Error(response, "hub has no icon", http.StatusNoContent)
return
}
url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, hub.IconUrl)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
iconData, err := json.Marshal(map[string]any{
"url": url.String(),
"metadata": meta,
})
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(iconData)
}
func HandleGetHubBg(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
_, _, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
if hub.BgUrl == "" {
http.Error(response, "hub has no background", http.StatusNoContent)
return
}
url, meta, err := minio.GetDownloadUrlAndMetadata(ctx, hub.BgUrl)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
bgData, err := json.Marshal(map[string]any{
"url": url.String(),
"metadata": meta,
})
if err != nil {
http.Error(response, "json error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(bgData)
}
func HandleHubRemove(response http.ResponseWriter, request *http.Request) { func HandleHubRemove(response http.ResponseWriter, request *http.Request) {
_, hub, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveHub) _, hub, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveHub)
if !ok { if !ok {
@@ -852,7 +920,7 @@ func HandleRoleSetColor(response http.ResponseWriter, request *http.Request) {
} }
color, err := convertions.StringToRgba(request.FormValue("new_color")) color, err := convertions.StringToRgba(request.FormValue("new_color"))
if err != nil { if err != nil {
http.Error(response, "invalid newcolor", http.StatusBadRequest) http.Error(response, "invalid new_color", http.StatusBadRequest)
return return
} }
+3 -1
View File
@@ -26,17 +26,19 @@ const (
UserProfileBg UserProfileBg
HubIcon HubIcon
HubBackground HubBackground
ChannelIcon
) )
type DataTypePrefix string type DataTypePrefix string
const ( const (
ConnectionFilePrefix DataTypePrefix = "connection/" ConnectionFilePrefix DataTypePrefix = "connection/"
HubChannelFilePrefix DataTypePrefix = "hub/" HubChannelFilePrefix DataTypePrefix = "hubChannel/"
UserAvatarPrefix DataTypePrefix = "userAvatar/" UserAvatarPrefix DataTypePrefix = "userAvatar/"
UserProfileBgPrefix DataTypePrefix = "userProfileBg/" UserProfileBgPrefix DataTypePrefix = "userProfileBg/"
HubIconPrefix DataTypePrefix = "hubIcon/" HubIconPrefix DataTypePrefix = "hubIcon/"
HubBackgroundPrefix DataTypePrefix = "hubBackground/" HubBackgroundPrefix DataTypePrefix = "hubBackground/"
ChannelIconPrefix DataTypePrefix = "channelIcon/"
) )
type GetKeyOptions struct { type GetKeyOptions struct {
+13
View File
@@ -181,6 +181,19 @@ func ConnectionDelete(ctx context.Context, conn *types.Connection) error {
return err return err
} }
func ConnectionGetById(ctx context.Context, id uuid.UUID) (*types.Connection, error) {
conn := types.NewConn()
err := dbConn.QueryRow(ctx, `
SELECT id, requestor_id, recipient_id, state, created_at
FROM user_connections
WHERE id = $1
`, id).Scan(&conn.Id, &conn.RequestorId, &conn.RecipientId, &conn.State, &conn.CreatedAt)
if err != nil {
return nil, err
}
return conn, nil
}
func ConnectionsGetBelongingToUser(ctx context.Context, user *types.User) error { func ConnectionsGetBelongingToUser(ctx context.Context, user *types.User) error {
rows, err := dbConn.Query(ctx, ` rows, err := dbConn.Query(ctx, `
SELECT id, requestor_id, recipient_id, state, created_at SELECT id, requestor_id, recipient_id, state, created_at
+7 -8
View File
@@ -38,7 +38,7 @@ type User struct {
WsConn *websocket.Conn `json:"-"` WsConn *websocket.Conn `json:"-"`
Id uuid.UUID `json:"-"` Id uuid.UUID `json:"-"`
Connections map[uuid.UUID]*Connection `json:"-"` Connections map[uuid.UUID]*Connection `json:"-"`
Hubs map[uuid.UUID]*Hub `json:"-"` Hubs map[uuid.UUID]*Hub `json:"hubs-to-delete"`
Color Rgba `json:"color"` Color Rgba `json:"color"`
} }
@@ -249,7 +249,7 @@ const (
CachedUserCanMessage CachedUserCanMessage
) )
const CachedUserPermissionsAll = CachedUserCanMessage | CachedUserCanReadHistory | CachedUserCanReadHistory const CachedUserPermissionsAll = CachedUserCanView | CachedUserCanReadHistory | CachedUserCanMessage
func (p *CachedUserPermissions) SetCanView() { *p |= CachedUserCanView } func (p *CachedUserPermissions) SetCanView() { *p |= CachedUserCanView }
func (p *CachedUserPermissions) ClearCanView() { *p &^= CachedUserCanView } func (p *CachedUserPermissions) ClearCanView() { *p &^= CachedUserCanView }
@@ -287,12 +287,11 @@ func NewHub() *Hub {
} }
type HubRole struct { type HubRole struct {
Name string `json:"role"` Name string `json:"role"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Permissions Permissions `json:"permissions"` Permissions Permissions `json:"permissions"`
Color Rgba `json:"color"` Color Rgba `json:"color"`
Id uint8 `json:"id"` Id uint8 `json:"id"`
BoundedGroup uint8 `json:"boundedGroup"` // BoundedGroup 0 for global
} }
func (h *HubRole) GrantPermission(r Permissions) { func (h *HubRole) GrantPermission(r Permissions) {
+1 -1
View File
@@ -32,7 +32,7 @@ func ServeWsConnection(responseWriter http.ResponseWriter, request *http.Request
var user = types.User{WsConn: connection} var user = types.User{WsConn: connection}
var isAuthenticated bool var isAuthenticated bool
var ignoreCache bool var ignoreCache bool = true
defer func() { closeConnection(&user, ignoreCache) }() defer func() { closeConnection(&user, ignoreCache) }()
for { for {
@@ -53,6 +53,10 @@
<button data-form="get-users" onclick="showForm('get-users')">GET /users</button> <button data-form="get-users" onclick="showForm('get-users')">GET /users</button>
<button data-form="hub-set-name" onclick="showForm('hub-set-name')">PATCH /hub/name</button> <button data-form="hub-set-name" onclick="showForm('hub-set-name')">PATCH /hub/name</button>
<button data-form="hub-set-color" onclick="showForm('hub-set-color')">PATCH /hub/color</button> <button data-form="hub-set-color" onclick="showForm('hub-set-color')">PATCH /hub/color</button>
<button data-form="hub-set-icon" onclick="showForm('hub-set-icon')">PATCH /hub/icon</button>
<button data-form="get-hub-icon" onclick="showForm('get-hub-icon')">GET /hub/icon</button>
<button data-form="hub-set-bg" onclick="showForm('hub-set-bg')">PATCH /hub/bg</button>
<button data-form="get-hub-bg" onclick="showForm('get-hub-bg')">GET /hub/bg</button>
<button data-form="hub-toggle-color" onclick="showForm('hub-toggle-color')">PATCH /hub/usercolorallowed</button> <button data-form="hub-toggle-color" onclick="showForm('hub-toggle-color')">PATCH /hub/usercolorallowed</button>
<button data-form="hub-user-rename" onclick="showForm('hub-user-rename')">PATCH /hub/user/name</button> <button data-form="hub-user-rename" onclick="showForm('hub-user-rename')">PATCH /hub/user/name</button>
<button data-form="hub-self-rename" onclick="showForm('hub-self-rename')">PATCH /hub/self/name</button> <button data-form="hub-self-rename" onclick="showForm('hub-self-rename')">PATCH /hub/self/name</button>
@@ -178,7 +182,7 @@
<!-- GET /file --> <!-- GET /file -->
<div class="form-content" id="fc-file-download"> <div class="form-content" id="fc-file-download">
<div class="field"><label>token</label><input id="fd-token" placeholder=""></div> <div class="field"><label>token</label><input id="fd-token" placeholder=""></div>
<div class="field"><label>connectionid</label><input id="fd-connectionid" placeholder="UUID"></div> <div class="field"><label>target_id</label><input id="fd-targetid" placeholder="connection or hub UUID"></div>
<div class="field"><label>key</label><input id="fd-key" placeholder="returned by upload"></div> <div class="field"><label>key</label><input id="fd-key" placeholder="returned by upload"></div>
<div class="form-actions"><button class="send" onclick="submitFileDownload()">Send</button></div> <div class="form-actions"><button class="send" onclick="submitFileDownload()">Send</button></div>
</div> </div>
@@ -286,6 +290,36 @@
<div class="form-actions"><button class="send" onclick="submit('hub-set-color')">Send</button></div> <div class="form-actions"><button class="send" onclick="submit('hub-set-color')">Send</button></div>
</div> </div>
<!-- PATCH /hub/icon -->
<div class="form-content" id="fc-hub-set-icon">
<div class="field"><label>token</label><input id="hsi-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hsi-hubid" placeholder="UUID"></div>
<div class="field"><label>file</label><input id="hsi-file" type="file"></div>
<div class="form-actions"><button class="send" onclick="submitHubIconUpload()">Send</button></div>
</div>
<!-- GET /hub/icon -->
<div class="form-content" id="fc-get-hub-icon">
<div class="field"><label>token</label><input id="ghi-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="ghi-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-icon')">Send</button></div>
</div>
<!-- PATCH /hub/bg -->
<div class="form-content" id="fc-hub-set-bg">
<div class="field"><label>token</label><input id="hsb-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="hsb-hubid" placeholder="UUID"></div>
<div class="field"><label>file</label><input id="hsb-file" type="file"></div>
<div class="form-actions"><button class="send" onclick="submitHubBgUpload()">Send</button></div>
</div>
<!-- GET /hub/bg -->
<div class="form-content" id="fc-get-hub-bg">
<div class="field"><label>token</label><input id="ghbg-token" placeholder=""></div>
<div class="field"><label>hubid</label><input id="ghbg-hubid" placeholder="UUID"></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-bg')">Send</button></div>
</div>
<!-- DELETE /hub --> <!-- DELETE /hub -->
<div class="form-content" id="fc-hub-remove"> <div class="form-content" id="fc-hub-remove">
<div class="field"><label>token</label><input id="hr-token" placeholder=""></div> <div class="field"><label>token</label><input id="hr-token" placeholder=""></div>
@@ -541,6 +575,10 @@
'get-users': { method:'GET', path:'/users', title:'GET /users — get multiple users by IDs', fields:[{id:'gus-token',dest:'header',name:'token'},{id:'gus-targetids',dest:'query',name:'target_ids'}] }, 'get-users': { method:'GET', path:'/users', title:'GET /users — get multiple users by IDs', fields:[{id:'gus-token',dest:'header',name:'token'},{id:'gus-targetids',dest:'query',name:'target_ids'}] },
'hub-set-name': { method:'PATCH', path:'/hub/name', title:'PATCH /hub/name — set hub name', fields:[{id:'hsn-token',dest:'header',name:'token'},{id:'hsn-hubid',dest:'header',name:'hub_id'},{id:'hsn-newname',dest:'body',name:'new_name'}] }, 'hub-set-name': { method:'PATCH', path:'/hub/name', title:'PATCH /hub/name — set hub name', fields:[{id:'hsn-token',dest:'header',name:'token'},{id:'hsn-hubid',dest:'header',name:'hub_id'},{id:'hsn-newname',dest:'body',name:'new_name'}] },
'hub-set-color': { method:'PATCH', path:'/hub/color', title:'PATCH /hub/color — set hub color (R,G,B,A)', fields:[{id:'hsc-token',dest:'header',name:'token'},{id:'hsc-hubid',dest:'header',name:'hub_id'},{id:'hsc-newcolor',dest:'body',name:'new_color'}] }, 'hub-set-color': { method:'PATCH', path:'/hub/color', title:'PATCH /hub/color — set hub color (R,G,B,A)', fields:[{id:'hsc-token',dest:'header',name:'token'},{id:'hsc-hubid',dest:'header',name:'hub_id'},{id:'hsc-newcolor',dest:'body',name:'new_color'}] },
'hub-set-icon': { title:'PATCH /hub/icon — set hub icon image' },
'get-hub-icon': { method:'GET', path:'/hub/icon', title:'GET /hub/icon — get hub icon URL', fields:[{id:'ghi-token',dest:'header',name:'token'},{id:'ghi-hubid',dest:'header',name:'hub_id'}] },
'hub-set-bg': { title:'PATCH /hub/bg — set hub background image' },
'get-hub-bg': { method:'GET', path:'/hub/bg', title:'GET /hub/bg — get hub background URL', fields:[{id:'ghbg-token',dest:'header',name:'token'},{id:'ghbg-hubid',dest:'header',name:'hub_id'}] },
'hub-remove': { method:'DELETE', path:'/hub', title:'DELETE /hub — remove hub', fields:[{id:'hr-token',dest:'header',name:'token'},{id:'hr-hubid',dest:'header',name:'hub_id'}] }, 'hub-remove': { method:'DELETE', path:'/hub', title:'DELETE /hub — remove hub', fields:[{id:'hr-token',dest:'header',name:'token'},{id:'hr-hubid',dest:'header',name:'hub_id'}] },
'hub-toggle-color': { method:'PATCH', path:'/hub/usercolorallowed', title:'PATCH /hub/usercolorallowed — toggle user color allowed', fields:[{id:'htc-token',dest:'header',name:'token'},{id:'htc-hubid',dest:'header',name:'hub_id'}] }, 'hub-toggle-color': { method:'PATCH', path:'/hub/usercolorallowed', title:'PATCH /hub/usercolorallowed — toggle user color allowed', fields:[{id:'htc-token',dest:'header',name:'token'},{id:'htc-hubid',dest:'header',name:'hub_id'}] },
'hub-user-remove': { method:'DELETE', path:'/hub/user', title:'DELETE /hub/user — remove user from hub', fields:[{id:'hur-token',dest:'header',name:'token'},{id:'hur-hubid',dest:'header',name:'hub_id'},{id:'hur-targetid',dest:'query',name:'target_id'}] }, 'hub-user-remove': { method:'DELETE', path:'/hub/user', title:'DELETE /hub/user — remove user from hub', fields:[{id:'hur-token',dest:'header',name:'token'},{id:'hur-hubid',dest:'header',name:'hub_id'},{id:'hur-targetid',dest:'query',name:'target_id'}] },
@@ -778,9 +816,9 @@
async function submitFileDownload() { async function submitFileDownload() {
const token = document.getElementById('fd-token').value; const token = document.getElementById('fd-token').value;
const connectionid = document.getElementById('fd-connectionid').value; const targetId = document.getElementById('fd-targetid').value;
const key = document.getElementById('fd-key').value; const key = document.getElementById('fd-key').value;
const query = new URLSearchParams({ connection_id: connectionid, key }); const query = new URLSearchParams({ target_id: targetId, key });
log('GET /file', query.toString(), 'log-info'); log('GET /file', query.toString(), 'log-info');
try { try {
const resp = await fetch(baseUrl() + '/file?' + query.toString(), { method: 'GET', headers: { token } }); const resp = await fetch(baseUrl() + '/file?' + query.toString(), { method: 'GET', headers: { token } });
@@ -845,6 +883,36 @@
} catch(e) { log('HTTP ERR', e.message, 'log-err'); } } catch(e) { log('HTTP ERR', e.message, 'log-err'); }
} }
async function submitHubIconUpload() {
const token = document.getElementById('hsi-token').value;
const hubid = document.getElementById('hsi-hubid').value;
const fileInput = document.getElementById('hsi-file');
if (!fileInput.files.length) { log('HTTP ERR', 'no file selected', 'log-err'); return; }
const form = new FormData();
form.append('file', fileInput.files[0]);
log('PATCH /hub/icon', 'file=' + fileInput.files[0].name, 'log-info');
try {
const resp = await fetch(baseUrl() + '/hub/icon', { method: 'PATCH', headers: { token, hub_id: hubid }, body: form });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitHubBgUpload() {
const token = document.getElementById('hsb-token').value;
const hubid = document.getElementById('hsb-hubid').value;
const fileInput = document.getElementById('hsb-file');
if (!fileInput.files.length) { log('HTTP ERR', 'no file selected', 'log-err'); return; }
const form = new FormData();
form.append('file', fileInput.files[0]);
log('PATCH /hub/bg', 'file=' + fileInput.files[0].name, 'log-info');
try {
const resp = await fetch(baseUrl() + '/hub/bg', { method: 'PATCH', headers: { token, hub_id: hubid }, body: form });
const text = await resp.text();
log('HTTP ' + resp.status, text, resp.ok ? 'log-http' : 'log-err');
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
}
async function submitGetUserProfileBg() { async function submitGetUserProfileBg() {
const token = document.getElementById('gpb-token').value; const token = document.getElementById('gpb-token').value;
const userid = document.getElementById('gpb-userid').value; const userid = document.getElementById('gpb-userid').value;
+6 -3
View File
@@ -1,8 +1,11 @@
check hubs attachment working
add option to change join role
add channel icons
make hub persistent
when user not ws connected collect count of unread messages for each conn (add db table in future) when user not ws connected collect count of unread messages for each conn (add db table in future)
setting avatar and profilebg fix cache
check when mutex needed
change api endpoints and body/url/header to follow same case
user banners user banners