user no stays in cache forever, fix some hubs bugs and add new enpoints
This commit is contained in:
@@ -69,6 +69,10 @@ func main() {
|
||||
http.HandleFunc("PUT /hub/join", withCORS(httpRequest.HandleHubJoin))
|
||||
http.HandleFunc("PATCH /hub/name", withCORS(httpRequest.HandleHubSetName))
|
||||
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("PATCH /hub/usercolorallowed", withCORS(httpRequest.HandleHubToggleUserColorAllowed))
|
||||
http.HandleFunc("DELETE /hub/user", withCORS(httpRequest.HandleHubUserRemove))
|
||||
@@ -93,7 +97,7 @@ func main() {
|
||||
|
||||
http.HandleFunc("POST /connection/message", withCORS(httpRequest.HandleDm))
|
||||
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.Fatal(http.ListenAndServe(":"+strconv.Itoa(int(config.Port)), nil))
|
||||
|
||||
@@ -29,18 +29,12 @@ func HandleAttachmentFileUpload(response http.ResponseWriter, request *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
request.Body = http.MaxBytesReader(response, request.Body, int64(config.MaxRequestWithFileBytes))
|
||||
|
||||
if err = request.ParseMultipartForm(int64(config.MaxRequestBytes)); err != nil {
|
||||
http.Error(response, "invalid multipart form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
target := request.FormValue("target_id")
|
||||
file, header, err := request.FormFile("file")
|
||||
if err != nil {
|
||||
http.Error(response, "missing file", http.StatusBadRequest)
|
||||
@@ -49,11 +43,31 @@ func HandleAttachmentFileUpload(response http.ResponseWriter, request *http.Requ
|
||||
defer file.Close()
|
||||
|
||||
contentType := header.Header.Get("Content-Type")
|
||||
key := minio.GetKey(&minio.GetKeyOptions{
|
||||
ConnectionId: conn.Id,
|
||||
MimeType: contentType,
|
||||
UploadType: minio.ConnectionFile,
|
||||
})
|
||||
var key string
|
||||
|
||||
if conn, ok := getConnection(ctx, target, user); ok {
|
||||
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{
|
||||
"originalName": header.Filename,
|
||||
@@ -322,13 +336,27 @@ func HandleAttachmentFileDownload(response http.ResponseWriter, request *http.Re
|
||||
return
|
||||
}
|
||||
|
||||
conn, ok := getConnectionWithResponseOnFail(response, request.FormValue("connectionid"), user)
|
||||
if !ok {
|
||||
target := request.URL.Query().Get("target_id")
|
||||
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
|
||||
}
|
||||
|
||||
key := request.URL.Query().Get("key")
|
||||
if !strings.HasPrefix(key, string(minio.ConnectionFilePrefix)+conn.Id.String()+"/") {
|
||||
if !strings.HasPrefix(key, validPrefix) {
|
||||
http.Error(response, "no such file", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -36,6 +36,45 @@ func getUserByToken(ctx context.Context, token string) (*types.User, error) {
|
||||
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) {
|
||||
connectionId, err := convertions.StringToUuid(connectionIdStr)
|
||||
if err != nil {
|
||||
|
||||
@@ -41,6 +41,8 @@ func validCheckWithResponseOnFail(response http.ResponseWriter, request *http.Re
|
||||
maxSize = int64(config.MaxRequestBytes)
|
||||
}
|
||||
|
||||
request.Body = http.MaxBytesReader(response, request.Body, maxSize)
|
||||
|
||||
if request.ContentLength > maxSize {
|
||||
io.Copy(io.Discard, request.Body)
|
||||
http.Error(response, "Request too large", http.StatusRequestEntityTooLarge)
|
||||
|
||||
@@ -551,6 +551,74 @@ func HandleHubSetBg(response http.ResponseWriter, request *http.Request) {
|
||||
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) {
|
||||
_, hub, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveHub)
|
||||
if !ok {
|
||||
@@ -852,7 +920,7 @@ func HandleRoleSetColor(response http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
color, err := convertions.StringToRgba(request.FormValue("new_color"))
|
||||
if err != nil {
|
||||
http.Error(response, "invalid newcolor", http.StatusBadRequest)
|
||||
http.Error(response, "invalid new_color", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -26,17 +26,19 @@ const (
|
||||
UserProfileBg
|
||||
HubIcon
|
||||
HubBackground
|
||||
ChannelIcon
|
||||
)
|
||||
|
||||
type DataTypePrefix string
|
||||
|
||||
const (
|
||||
ConnectionFilePrefix DataTypePrefix = "connection/"
|
||||
HubChannelFilePrefix DataTypePrefix = "hub/"
|
||||
HubChannelFilePrefix DataTypePrefix = "hubChannel/"
|
||||
UserAvatarPrefix DataTypePrefix = "userAvatar/"
|
||||
UserProfileBgPrefix DataTypePrefix = "userProfileBg/"
|
||||
HubIconPrefix DataTypePrefix = "hubIcon/"
|
||||
HubBackgroundPrefix DataTypePrefix = "hubBackground/"
|
||||
ChannelIconPrefix DataTypePrefix = "channelIcon/"
|
||||
)
|
||||
|
||||
type GetKeyOptions struct {
|
||||
|
||||
@@ -181,6 +181,19 @@ func ConnectionDelete(ctx context.Context, conn *types.Connection) error {
|
||||
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 {
|
||||
rows, err := dbConn.Query(ctx, `
|
||||
SELECT id, requestor_id, recipient_id, state, created_at
|
||||
|
||||
@@ -38,7 +38,7 @@ type User struct {
|
||||
WsConn *websocket.Conn `json:"-"`
|
||||
Id uuid.UUID `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"`
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ const (
|
||||
CachedUserCanMessage
|
||||
)
|
||||
|
||||
const CachedUserPermissionsAll = CachedUserCanMessage | CachedUserCanReadHistory | CachedUserCanReadHistory
|
||||
const CachedUserPermissionsAll = CachedUserCanView | CachedUserCanReadHistory | CachedUserCanMessage
|
||||
|
||||
func (p *CachedUserPermissions) SetCanView() { *p |= CachedUserCanView }
|
||||
func (p *CachedUserPermissions) ClearCanView() { *p &^= CachedUserCanView }
|
||||
@@ -287,12 +287,11 @@ func NewHub() *Hub {
|
||||
}
|
||||
|
||||
type HubRole struct {
|
||||
Name string `json:"role"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Permissions Permissions `json:"permissions"`
|
||||
Color Rgba `json:"color"`
|
||||
Id uint8 `json:"id"`
|
||||
BoundedGroup uint8 `json:"boundedGroup"` // BoundedGroup 0 for global
|
||||
Name string `json:"role"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Permissions Permissions `json:"permissions"`
|
||||
Color Rgba `json:"color"`
|
||||
Id uint8 `json:"id"`
|
||||
}
|
||||
|
||||
func (h *HubRole) GrantPermission(r Permissions) {
|
||||
|
||||
@@ -32,7 +32,7 @@ func ServeWsConnection(responseWriter http.ResponseWriter, request *http.Request
|
||||
|
||||
var user = types.User{WsConn: connection}
|
||||
var isAuthenticated bool
|
||||
var ignoreCache bool
|
||||
var ignoreCache bool = true
|
||||
|
||||
defer func() { closeConnection(&user, ignoreCache) }()
|
||||
for {
|
||||
|
||||
@@ -53,6 +53,10 @@
|
||||
<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-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-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>
|
||||
@@ -178,7 +182,7 @@
|
||||
<!-- GET /file -->
|
||||
<div class="form-content" id="fc-file-download">
|
||||
<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="form-actions"><button class="send" onclick="submitFileDownload()">Send</button></div>
|
||||
</div>
|
||||
@@ -286,6 +290,36 @@
|
||||
<div class="form-actions"><button class="send" onclick="submit('hub-set-color')">Send</button></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 -->
|
||||
<div class="form-content" id="fc-hub-remove">
|
||||
<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'}] },
|
||||
'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-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-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'}] },
|
||||
@@ -778,9 +816,9 @@
|
||||
|
||||
async function submitFileDownload() {
|
||||
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 query = new URLSearchParams({ connection_id: connectionid, key });
|
||||
const query = new URLSearchParams({ target_id: targetId, key });
|
||||
log('GET /file', query.toString(), 'log-info');
|
||||
try {
|
||||
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'); }
|
||||
}
|
||||
|
||||
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() {
|
||||
const token = document.getElementById('gpb-token').value;
|
||||
const userid = document.getElementById('gpb-userid').value;
|
||||
@@ -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)
|
||||
|
||||
setting avatar and profilebg
|
||||
check when mutex needed
|
||||
change api endpoints and body/url/header to follow same case
|
||||
fix cache
|
||||
|
||||
user banners
|
||||
|
||||
|
||||
Reference in New Issue
Block a user