add messages persistancy, channels does not persist

This commit is contained in:
cos
2026-05-07 10:27:39 +02:00
parent bcefea3cf5
commit 916463234f
5 changed files with 128 additions and 0 deletions
BIN
View File
Binary file not shown.
+1
View File
@@ -61,6 +61,7 @@ func main() {
http.HandleFunc("POST /hub", withCORS(httpRequest.HandleHubCreate)) http.HandleFunc("POST /hub", withCORS(httpRequest.HandleHubCreate))
http.HandleFunc("GET /hub", withCORS(httpRequest.GetHubData)) http.HandleFunc("GET /hub", withCORS(httpRequest.GetHubData))
http.HandleFunc("POST /hub/channel/message", withCORS(httpRequest.HandleHubMessage)) http.HandleFunc("POST /hub/channel/message", withCORS(httpRequest.HandleHubMessage))
http.HandleFunc("GET /hub/channel/messages", withCORS(httpRequest.HandleHubChannelGetMessages))
http.HandleFunc("GET /hub/channel", withCORS(httpRequest.GetChannelData)) http.HandleFunc("GET /hub/channel", withCORS(httpRequest.GetChannelData))
http.HandleFunc("GET /hubs", withCORS(httpRequest.HandleGetHubs)) http.HandleFunc("GET /hubs", withCORS(httpRequest.HandleGetHubs))
http.HandleFunc("GET /hubs/channels", withCORS(httpRequest.HandleGetChannels)) http.HandleFunc("GET /hubs/channels", withCORS(httpRequest.HandleGetChannels))
+76
View File
@@ -3,6 +3,7 @@ package httpRequest
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"go-socket/packages/config"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@@ -317,6 +318,81 @@ func HandleHubMessage(response http.ResponseWriter, request *http.Request) {
response.WriteHeader(http.StatusCreated) response.WriteHeader(http.StatusCreated)
} }
func HandleHubChannelGetMessages(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) {
return
}
ctx := request.Context()
user, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return
}
channel, err := getHubChannelIfValidWithResponseOnFail(ctx, response, hub, hubUser, request.FormValue("channel_id"))
if err != nil {
return
}
channel.Mu.RLock()
canReadHistory := channel.UsersCachedPermissions[user.Id].CanReadHistory()
channel.Mu.RUnlock()
if !canReadHistory {
http.Error(response, "forbidden", http.StatusForbidden)
return
}
before, err := convertions.StringToTimestamp(request.URL.Query().Get("before"))
if err != nil {
before = time.Now()
}
messagesCap, err := convertions.StringToUint32(request.URL.Query().Get("messages"))
if err != nil {
messagesCap = config.MaxDirectMsgCache
}
buffer, bufferSize := channel.GetSortedMessagesBuff()
var validBufCount uint32
for validBufCount < bufferSize && buffer[validBufCount].CreatedAt.Before(before) {
validBufCount++
}
var messages []*types.Message
if validBufCount >= messagesCap {
start := validBufCount - messagesCap
messages = make([]*types.Message, messagesCap)
for i := uint32(0); i < messagesCap; i++ {
messages[i] = buffer[start+i]
}
} else {
remaining := messagesCap - validBufCount
cutoff := before
if validBufCount > 0 {
cutoff = buffer[0].CreatedAt
}
dbMessages, err := postgresql.HubChannelGetMessagesBefore(ctx, cutoff, channel.Id, remaining)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
messages = make([]*types.Message, 0, uint32(len(dbMessages))+validBufCount)
messages = append(messages, dbMessages...)
for i := uint32(0); i < validBufCount; i++ {
messages = append(messages, buffer[i])
}
}
data, err := json.Marshal(messages)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
response.Write(data)
}
func HandleGetChannels(response http.ResponseWriter, request *http.Request) { func HandleGetChannels(response http.ResponseWriter, request *http.Request) {
if !validCheckWithResponseOnFail(response, request, normal) { if !validCheckWithResponseOnFail(response, request, normal) {
return return
+29
View File
@@ -578,6 +578,35 @@ func HubChannelMessageGet(ctx context.Context, message *types.Message) error {
`, message.Id).Scan(&message.Sender, &message.Receiver, &message.CreatedAt, &message.Content, &message.AttachedFile) `, message.Id).Scan(&message.Sender, &message.Receiver, &message.CreatedAt, &message.Content, &message.AttachedFile)
} }
func HubChannelGetMessagesBefore(ctx context.Context, before time.Time, channelId uuid.UUID, cap uint32) ([]*types.Message, error) {
rows, err := dbConn.Query(ctx, `
SELECT id, sender_id, receiver_id, created_at, content, attached_file
FROM (
SELECT id, sender_id, receiver_id, created_at, content, attached_file
FROM hub_channel_messages
WHERE receiver_id = $1
AND created_at < $2
ORDER BY created_at DESC
LIMIT $3
) sub
ORDER BY created_at ASC
`, channelId, before, cap)
if err != nil {
return nil, err
}
defer rows.Close()
messages := make([]*types.Message, 0, cap)
for rows.Next() {
msg := &types.Message{}
if err = rows.Scan(&msg.Id, &msg.Sender, &msg.Receiver, &msg.CreatedAt, &msg.Content, &msg.AttachedFile); err != nil {
return nil, err
}
messages = append(messages, msg)
}
return messages, rows.Err()
}
func HubUpdate(ctx context.Context, hub *types.Hub, updateList *types.HubUpdate) error { func HubUpdate(ctx context.Context, hub *types.Hub, updateList *types.HubUpdate) error {
setClauses := make([]string, 0, 6) setClauses := make([]string, 0, 6)
args := make([]any, 0, 7) args := make([]any, 0, 7)
+22
View File
@@ -43,6 +43,7 @@
<button data-form="msg-user" onclick="showForm('msg-user')">POST /connection/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-create" onclick="showForm('hub-create')">POST /hub</button>
<button data-form="hub-message" onclick="showForm('hub-message')">POST /hub/channel/message</button> <button data-form="hub-message" onclick="showForm('hub-message')">POST /hub/channel/message</button>
<button data-form="get-hub-channel-messages" onclick="showForm('get-hub-channel-messages')">GET /hub/channel/messages</button>
<button data-form="hub-join" onclick="showForm('hub-join')">PUT /hub/join</button> <button data-form="hub-join" onclick="showForm('hub-join')">PUT /hub/join</button>
<button data-form="get-hubs" onclick="showForm('get-hubs')">GET /hubs</button> <button data-form="get-hubs" onclick="showForm('get-hubs')">GET /hubs</button>
<button data-form="get-hub-data" onclick="showForm('get-hub-data')">GET /hub</button> <button data-form="get-hub-data" onclick="showForm('get-hub-data')">GET /hub</button>
@@ -51,6 +52,7 @@
<button data-form="get-hub-users" onclick="showForm('get-hub-users')">GET /hubs/users</button> <button data-form="get-hub-users" onclick="showForm('get-hub-users')">GET /hubs/users</button>
<button data-form="get-hub-roles" onclick="showForm('get-hub-roles')">GET /hubs/roles</button> <button data-form="get-hub-roles" onclick="showForm('get-hub-roles')">GET /hubs/roles</button>
<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-joinrole" onclick="showForm('hub-set-joinrole')">PATCH /hub/joinrole</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="hub-set-icon" onclick="showForm('hub-set-icon')">PATCH /hub/icon</button>
@@ -242,6 +244,16 @@
<div class="form-actions"><button class="send" onclick="submit('hub-message')">Send</button></div> <div class="form-actions"><button class="send" onclick="submit('hub-message')">Send</button></div>
</div> </div>
<!-- GET /hub/channel/messages -->
<div class="form-content" id="fc-get-hub-channel-messages">
<div class="field"><label>token</label><input id="ghcm-token" placeholder=""></div>
<div class="field"><label>hub_id</label><input id="ghcm-hubid" placeholder="UUID"><span class="hint">sent as header</span></div>
<div class="field"><label>channel_id</label><input id="ghcm-channelid" placeholder="UUID"></div>
<div class="field"><label>messages</label><input id="ghcm-messages" placeholder="count (optional)"></div>
<div class="field"><label>before</label><input id="ghcm-before" placeholder="RFC3339 (optional)"><span class="hint">e.g. 2025-01-01T00:00:00Z</span></div>
<div class="form-actions"><button class="send" onclick="submit('get-hub-channel-messages')">Send</button></div>
</div>
<!-- PUT /hub/join --> <!-- PUT /hub/join -->
<div class="form-content" id="fc-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>token</label><input id="hj-token" placeholder=""></div>
@@ -276,6 +288,14 @@
<div class="form-actions"><button class="send" onclick="submit('get-hub-users')">Send</button></div> <div class="form-actions"><button class="send" onclick="submit('get-hub-users')">Send</button></div>
</div> </div>
<!-- PATCH /hub/joinrole -->
<div class="form-content" id="fc-hub-set-joinrole">
<div class="field"><label>token</label><input id="hsjr-token" placeholder=""></div>
<div class="field"><label>hub_id</label><input id="hsjr-hubid" placeholder="UUID"></div>
<div class="field"><label>new_role_id</label><input id="hsjr-roleid" placeholder="uint8"></div>
<div class="form-actions"><button class="send" onclick="submit('hub-set-joinrole')">Send</button></div>
</div>
<!-- PATCH /hub/name --> <!-- PATCH /hub/name -->
<div class="form-content" id="fc-hub-set-name"> <div class="form-content" id="fc-hub-set-name">
<div class="field"><label>token</label><input id="hsn-token" placeholder=""></div> <div class="field"><label>token</label><input id="hsn-token" placeholder=""></div>
@@ -584,6 +604,7 @@
'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:'connection_id'},{id:'mu-msgContent',dest:'body',name:'msg_content'},{id:'mu-attachedFile',dest:'body',name:'attached_file'}] }, '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:'connection_id'},{id:'mu-msgContent',dest:'body',name:'msg_content'},{id:'mu-attachedFile',dest:'body',name:'attached_file'}] },
'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:'hub_name'}] }, '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:'hub_name'}] },
'hub-message': { method:'POST', path:'/hub/channel/message', title:'POST /hub/channel/message — send hub channel message', fields:[{id:'hm-token',dest:'header',name:'token'},{id:'hm-hubid',dest:'header',name:'hub_id'},{id:'hm-channelid',dest:'header',name:'channel_id'},{id:'hm-msgContent',dest:'body',name:'msg_content'},{id:'hm-attachedFile',dest:'body',name:'attached_file'}] }, 'hub-message': { method:'POST', path:'/hub/channel/message', title:'POST /hub/channel/message — send hub channel message', fields:[{id:'hm-token',dest:'header',name:'token'},{id:'hm-hubid',dest:'header',name:'hub_id'},{id:'hm-channelid',dest:'header',name:'channel_id'},{id:'hm-msgContent',dest:'body',name:'msg_content'},{id:'hm-attachedFile',dest:'body',name:'attached_file'}] },
'get-hub-channel-messages':{ method:'GET', path:'/hub/channel/messages', title:'GET /hub/channel/messages — channel message history', fields:[{id:'ghcm-token',dest:'header',name:'token'},{id:'ghcm-hubid',dest:'header',name:'hub_id'},{id:'ghcm-channelid',dest:'query',name:'channel_id'},{id:'ghcm-messages',dest:'query',name:'messages'},{id:'ghcm-before',dest:'query',name:'before'}] },
'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:'hub_id'}] }, '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:'hub_id'}] },
'get-hubs': { method:'GET', path:'/hubs', title:'GET /hubs — get own hubs', fields:[{id:'gh-token',dest:'header',name:'token'}] }, 'get-hubs': { method:'GET', path:'/hubs', title:'GET /hubs — get own hubs', fields:[{id:'gh-token',dest:'header',name:'token'}] },
'get-hub-data': { method:'GET', path:'/hub', title:'GET /hub — get hub data', fields:[{id:'ghd-token',dest:'header',name:'token'},{id:'ghd-hubid',dest:'header',name:'hub_id'}] }, 'get-hub-data': { method:'GET', path:'/hub', title:'GET /hub — get hub data', fields:[{id:'ghd-token',dest:'header',name:'token'},{id:'ghd-hubid',dest:'header',name:'hub_id'}] },
@@ -592,6 +613,7 @@
'get-hub-users': { method:'GET', path:'/hubs/users', title:'GET /hubs/users — get hub users (excludes self)', fields:[{id:'ghu-token',dest:'header',name:'token'},{id:'ghu-hubid',dest:'header',name:'hub_id'}] }, 'get-hub-users': { method:'GET', path:'/hubs/users', title:'GET /hubs/users — get hub users (excludes self)', fields:[{id:'ghu-token',dest:'header',name:'token'},{id:'ghu-hubid',dest:'header',name:'hub_id'}] },
'get-hub-roles': { method:'GET', path:'/hubs/roles', title:'GET /hubs/roles — get hub roles', fields:[{id:'ghr-token',dest:'header',name:'token'},{id:'ghr-hubid',dest:'header',name:'hub_id'}] }, 'get-hub-roles': { method:'GET', path:'/hubs/roles', title:'GET /hubs/roles — get hub roles', fields:[{id:'ghr-token',dest:'header',name:'token'},{id:'ghr-hubid',dest:'header',name:'hub_id'}] },
'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-joinrole': { method:'PATCH', path:'/hub/joinrole', title:'PATCH /hub/joinrole — set default join role', fields:[{id:'hsjr-token',dest:'header',name:'token'},{id:'hsjr-hubid',dest:'header',name:'hub_id'},{id:'hsjr-roleid',dest:'body',name:'new_role_id'}] },
'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' }, 'hub-set-icon': { title:'PATCH /hub/icon — set hub icon image' },