feat: MVP two-user chat via embedded SPA (#9)
All checks were successful
check / check (push) Successful in 1m51s
All checks were successful
check / check (push) Successful in 1m51s
Backend: - Session/client UUID model: sessions table (uuid, nick, signing_key), clients table (uuid, session_id, token) with per-client message queues - MOTD delivery as IRC numeric messages (375/372/376) on connect - EnqueueToSession fans out to all clients of a session - EnqueueToClient for targeted delivery (MOTD) - All queries updated for session/client model SPA client: - Long-poll loop (15s timeout) instead of setInterval - IRC message envelope parsing (command/from/to/body) - Display JOIN/PART/NICK/TOPIC/QUIT system messages - Nick change via /nick command - Topic display in header bar - Unread count badges on inactive tabs - Auto-rejoin channels on reconnect (localStorage) - Connection status indicator - Message deduplication by UUID - Channel history loaded on join - /topic command support Closes #9
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -37,35 +38,37 @@ func (hdlr *Handlers) maxBodySize() int64 {
|
||||
return defaultMaxBodySize
|
||||
}
|
||||
|
||||
// authUser extracts the user from the Authorization header.
|
||||
func (hdlr *Handlers) authUser(
|
||||
// authSession extracts the session from the client token.
|
||||
func (hdlr *Handlers) authSession(
|
||||
request *http.Request,
|
||||
) (int64, string, error) {
|
||||
) (int64, int64, string, error) {
|
||||
auth := request.Header.Get("Authorization")
|
||||
if !strings.HasPrefix(auth, "Bearer ") {
|
||||
return 0, "", errUnauthorized
|
||||
return 0, 0, "", errUnauthorized
|
||||
}
|
||||
|
||||
token := strings.TrimPrefix(auth, "Bearer ")
|
||||
if token == "" {
|
||||
return 0, "", errUnauthorized
|
||||
return 0, 0, "", errUnauthorized
|
||||
}
|
||||
|
||||
uid, nick, err := hdlr.params.Database.GetUserByToken(
|
||||
request.Context(), token,
|
||||
)
|
||||
sessionID, clientID, nick, err :=
|
||||
hdlr.params.Database.GetSessionByToken(
|
||||
request.Context(), token,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, "", fmt.Errorf("auth: %w", err)
|
||||
return 0, 0, "", fmt.Errorf("auth: %w", err)
|
||||
}
|
||||
|
||||
return uid, nick, nil
|
||||
return sessionID, clientID, nick, nil
|
||||
}
|
||||
|
||||
func (hdlr *Handlers) requireAuth(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) (int64, string, bool) {
|
||||
uid, nick, err := hdlr.authUser(request)
|
||||
) (int64, int64, string, bool) {
|
||||
sessionID, clientID, nick, err :=
|
||||
hdlr.authSession(request)
|
||||
if err != nil {
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
@@ -73,19 +76,19 @@ func (hdlr *Handlers) requireAuth(
|
||||
http.StatusUnauthorized,
|
||||
)
|
||||
|
||||
return 0, "", false
|
||||
return 0, 0, "", false
|
||||
}
|
||||
|
||||
return uid, nick, true
|
||||
return sessionID, clientID, nick, true
|
||||
}
|
||||
|
||||
// fanOut stores a message and enqueues it to all specified
|
||||
// user IDs, then notifies them.
|
||||
// session IDs, then notifies them.
|
||||
func (hdlr *Handlers) fanOut(
|
||||
request *http.Request,
|
||||
command, from, target string,
|
||||
body json.RawMessage,
|
||||
userIDs []int64,
|
||||
sessionIDs []int64,
|
||||
) (string, error) {
|
||||
dbID, msgUUID, err := hdlr.params.Database.InsertMessage(
|
||||
request.Context(), command, from, target, body, nil,
|
||||
@@ -94,16 +97,16 @@ func (hdlr *Handlers) fanOut(
|
||||
return "", fmt.Errorf("insert message: %w", err)
|
||||
}
|
||||
|
||||
for _, uid := range userIDs {
|
||||
enqErr := hdlr.params.Database.EnqueueMessage(
|
||||
request.Context(), uid, dbID,
|
||||
for _, sid := range sessionIDs {
|
||||
enqErr := hdlr.params.Database.EnqueueToSession(
|
||||
request.Context(), sid, dbID,
|
||||
)
|
||||
if enqErr != nil {
|
||||
hdlr.log.Error("enqueue failed",
|
||||
"error", enqErr, "user_id", uid)
|
||||
"error", enqErr, "session_id", sid)
|
||||
}
|
||||
|
||||
hdlr.broker.Notify(uid)
|
||||
hdlr.broker.Notify(sid)
|
||||
}
|
||||
|
||||
return msgUUID, nil
|
||||
@@ -114,10 +117,10 @@ func (hdlr *Handlers) fanOutSilent(
|
||||
request *http.Request,
|
||||
command, from, target string,
|
||||
body json.RawMessage,
|
||||
userIDs []int64,
|
||||
sessionIDs []int64,
|
||||
) error {
|
||||
_, err := hdlr.fanOut(
|
||||
request, command, from, target, body, userIDs,
|
||||
request, command, from, target, body, sessionIDs,
|
||||
)
|
||||
|
||||
return err
|
||||
@@ -125,16 +128,6 @@ func (hdlr *Handlers) fanOutSilent(
|
||||
|
||||
// HandleCreateSession creates a new user session.
|
||||
func (hdlr *Handlers) HandleCreateSession() http.HandlerFunc {
|
||||
type createRequest struct {
|
||||
Nick string `json:"nick"`
|
||||
}
|
||||
|
||||
type createResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Nick string `json:"nick"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
return func(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
@@ -143,82 +136,174 @@ func (hdlr *Handlers) HandleCreateSession() http.HandlerFunc {
|
||||
writer, request.Body, hdlr.maxBodySize(),
|
||||
)
|
||||
|
||||
var payload createRequest
|
||||
|
||||
err := json.NewDecoder(request.Body).Decode(&payload)
|
||||
if err != nil {
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"invalid request body",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
payload.Nick = strings.TrimSpace(payload.Nick)
|
||||
|
||||
if !validNickRe.MatchString(payload.Nick) {
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"invalid nick format",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
userID, token, err := hdlr.params.Database.CreateUser(
|
||||
request.Context(), payload.Nick,
|
||||
)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "UNIQUE") {
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"nick already taken",
|
||||
http.StatusConflict,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.log.Error(
|
||||
"create user failed", "error", err,
|
||||
)
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"internal error",
|
||||
http.StatusInternalServerError,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.respondJSON(
|
||||
writer, request,
|
||||
&createResponse{
|
||||
ID: userID,
|
||||
Nick: payload.Nick,
|
||||
Token: token,
|
||||
},
|
||||
http.StatusCreated,
|
||||
)
|
||||
hdlr.handleCreateSession(writer, request)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleState returns the current user's info and channels.
|
||||
func (hdlr *Handlers) handleCreateSession(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) {
|
||||
type createRequest struct {
|
||||
Nick string `json:"nick"`
|
||||
}
|
||||
|
||||
var payload createRequest
|
||||
|
||||
err := json.NewDecoder(request.Body).Decode(&payload)
|
||||
if err != nil {
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"invalid request body",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
payload.Nick = strings.TrimSpace(payload.Nick)
|
||||
|
||||
if !validNickRe.MatchString(payload.Nick) {
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"invalid nick format",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
sessionID, clientID, token, err :=
|
||||
hdlr.params.Database.CreateSession(
|
||||
request.Context(), payload.Nick,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.handleCreateSessionError(
|
||||
writer, request, err,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.deliverMOTD(request, clientID, sessionID)
|
||||
|
||||
hdlr.respondJSON(writer, request, map[string]any{
|
||||
"id": sessionID,
|
||||
"nick": payload.Nick,
|
||||
"token": token,
|
||||
}, http.StatusCreated)
|
||||
}
|
||||
|
||||
func (hdlr *Handlers) handleCreateSessionError(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
err error,
|
||||
) {
|
||||
if strings.Contains(err.Error(), "UNIQUE") {
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"nick already taken",
|
||||
http.StatusConflict,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.log.Error(
|
||||
"create session failed", "error", err,
|
||||
)
|
||||
hdlr.respondError(
|
||||
writer, request,
|
||||
"internal error",
|
||||
http.StatusInternalServerError,
|
||||
)
|
||||
}
|
||||
|
||||
// deliverMOTD sends the MOTD as IRC numeric messages to a
|
||||
// new client.
|
||||
func (hdlr *Handlers) deliverMOTD(
|
||||
request *http.Request,
|
||||
clientID, sessionID int64,
|
||||
) {
|
||||
motd := hdlr.params.Config.MOTD
|
||||
serverName := hdlr.params.Config.ServerName
|
||||
|
||||
if serverName == "" {
|
||||
serverName = "chat"
|
||||
}
|
||||
|
||||
if motd == "" {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := request.Context()
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "375", serverName,
|
||||
"- "+serverName+" Message of the Day -",
|
||||
)
|
||||
|
||||
for line := range strings.SplitSeq(motd, "\n") {
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "372", serverName,
|
||||
"- "+line,
|
||||
)
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, "376", serverName,
|
||||
"End of /MOTD command.",
|
||||
)
|
||||
|
||||
hdlr.broker.Notify(sessionID)
|
||||
}
|
||||
|
||||
func (hdlr *Handlers) enqueueNumeric(
|
||||
ctx context.Context,
|
||||
clientID int64,
|
||||
command, serverName, text string,
|
||||
) {
|
||||
body, err := json.Marshal([]string{text})
|
||||
if err != nil {
|
||||
hdlr.log.Error(
|
||||
"marshal numeric body", "error", err,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
dbID, _, insertErr := hdlr.params.Database.InsertMessage(
|
||||
ctx, command, serverName, "",
|
||||
json.RawMessage(body), nil,
|
||||
)
|
||||
if insertErr != nil {
|
||||
hdlr.log.Error(
|
||||
"insert numeric message", "error", insertErr,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_ = hdlr.params.Database.EnqueueToClient(
|
||||
ctx, clientID, dbID,
|
||||
)
|
||||
}
|
||||
|
||||
// HandleState returns the current session's info and
|
||||
// channels.
|
||||
func (hdlr *Handlers) HandleState() http.HandlerFunc {
|
||||
return func(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) {
|
||||
uid, nick, ok := hdlr.requireAuth(writer, request)
|
||||
sessionID, _, nick, ok :=
|
||||
hdlr.requireAuth(writer, request)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
channels, err := hdlr.params.Database.ListChannels(
|
||||
request.Context(), uid,
|
||||
request.Context(), sessionID,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.log.Error(
|
||||
@@ -234,7 +319,7 @@ func (hdlr *Handlers) HandleState() http.HandlerFunc {
|
||||
}
|
||||
|
||||
hdlr.respondJSON(writer, request, map[string]any{
|
||||
"id": uid,
|
||||
"id": sessionID,
|
||||
"nick": nick,
|
||||
"channels": channels,
|
||||
}, http.StatusOK)
|
||||
@@ -247,7 +332,7 @@ func (hdlr *Handlers) HandleListAllChannels() http.HandlerFunc {
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) {
|
||||
_, _, ok := hdlr.requireAuth(writer, request)
|
||||
_, _, _, ok := hdlr.requireAuth(writer, request)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -280,7 +365,7 @@ func (hdlr *Handlers) HandleChannelMembers() http.HandlerFunc {
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) {
|
||||
_, _, ok := hdlr.requireAuth(writer, request)
|
||||
_, _, _, ok := hdlr.requireAuth(writer, request)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -328,7 +413,8 @@ func (hdlr *Handlers) HandleGetMessages() http.HandlerFunc {
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) {
|
||||
uid, _, ok := hdlr.requireAuth(writer, request)
|
||||
sessionID, clientID, _, ok :=
|
||||
hdlr.requireAuth(writer, request)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -349,7 +435,7 @@ func (hdlr *Handlers) HandleGetMessages() http.HandlerFunc {
|
||||
}
|
||||
|
||||
msgs, lastQID, err := hdlr.params.Database.PollMessages(
|
||||
request.Context(), uid,
|
||||
request.Context(), clientID,
|
||||
afterID, pollMessageLimit,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -374,17 +460,20 @@ func (hdlr *Handlers) HandleGetMessages() http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.longPoll(writer, request, uid, afterID, timeout)
|
||||
hdlr.longPoll(
|
||||
writer, request,
|
||||
sessionID, clientID, afterID, timeout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (hdlr *Handlers) longPoll(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid, afterID int64,
|
||||
sessionID, clientID, afterID int64,
|
||||
timeout int,
|
||||
) {
|
||||
waitCh := hdlr.broker.Wait(uid)
|
||||
waitCh := hdlr.broker.Wait(sessionID)
|
||||
|
||||
timer := time.NewTimer(
|
||||
time.Duration(timeout) * time.Second,
|
||||
@@ -396,15 +485,15 @@ func (hdlr *Handlers) longPoll(
|
||||
case <-waitCh:
|
||||
case <-timer.C:
|
||||
case <-request.Context().Done():
|
||||
hdlr.broker.Remove(uid, waitCh)
|
||||
hdlr.broker.Remove(sessionID, waitCh)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.broker.Remove(uid, waitCh)
|
||||
hdlr.broker.Remove(sessionID, waitCh)
|
||||
|
||||
msgs, lastQID, err := hdlr.params.Database.PollMessages(
|
||||
request.Context(), uid,
|
||||
request.Context(), clientID,
|
||||
afterID, pollMessageLimit,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -443,7 +532,8 @@ func (hdlr *Handlers) HandleSendCommand() http.HandlerFunc {
|
||||
writer, request.Body, hdlr.maxBodySize(),
|
||||
)
|
||||
|
||||
uid, nick, ok := hdlr.requireAuth(writer, request)
|
||||
sessionID, _, nick, ok :=
|
||||
hdlr.requireAuth(writer, request)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -492,7 +582,7 @@ func (hdlr *Handlers) HandleSendCommand() http.HandlerFunc {
|
||||
}
|
||||
|
||||
hdlr.dispatchCommand(
|
||||
writer, request, uid, nick,
|
||||
writer, request, sessionID, nick,
|
||||
payload.Command, payload.To,
|
||||
payload.Body, bodyLines,
|
||||
)
|
||||
@@ -502,7 +592,7 @@ func (hdlr *Handlers) HandleSendCommand() http.HandlerFunc {
|
||||
func (hdlr *Handlers) dispatchCommand(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick, command, target string,
|
||||
body json.RawMessage,
|
||||
bodyLines func() []string,
|
||||
@@ -510,20 +600,20 @@ func (hdlr *Handlers) dispatchCommand(
|
||||
switch command {
|
||||
case cmdPrivmsg, "NOTICE":
|
||||
hdlr.handlePrivmsg(
|
||||
writer, request, uid, nick,
|
||||
writer, request, sessionID, nick,
|
||||
command, target, body, bodyLines,
|
||||
)
|
||||
case "JOIN":
|
||||
hdlr.handleJoin(
|
||||
writer, request, uid, nick, target,
|
||||
writer, request, sessionID, nick, target,
|
||||
)
|
||||
case "PART":
|
||||
hdlr.handlePart(
|
||||
writer, request, uid, nick, target, body,
|
||||
writer, request, sessionID, nick, target, body,
|
||||
)
|
||||
case "NICK":
|
||||
hdlr.handleNick(
|
||||
writer, request, uid, nick, bodyLines,
|
||||
writer, request, sessionID, nick, bodyLines,
|
||||
)
|
||||
case "TOPIC":
|
||||
hdlr.handleTopic(
|
||||
@@ -531,7 +621,7 @@ func (hdlr *Handlers) dispatchCommand(
|
||||
)
|
||||
case "QUIT":
|
||||
hdlr.handleQuit(
|
||||
writer, request, uid, nick, body,
|
||||
writer, request, sessionID, nick, body,
|
||||
)
|
||||
case "PING":
|
||||
hdlr.respondJSON(writer, request,
|
||||
@@ -552,7 +642,7 @@ func (hdlr *Handlers) dispatchCommand(
|
||||
func (hdlr *Handlers) handlePrivmsg(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick, command, target string,
|
||||
body json.RawMessage,
|
||||
bodyLines func() []string,
|
||||
@@ -580,7 +670,7 @@ func (hdlr *Handlers) handlePrivmsg(
|
||||
|
||||
if strings.HasPrefix(target, "#") {
|
||||
hdlr.handleChannelMsg(
|
||||
writer, request, uid, nick,
|
||||
writer, request, sessionID, nick,
|
||||
command, target, body,
|
||||
)
|
||||
|
||||
@@ -588,7 +678,7 @@ func (hdlr *Handlers) handlePrivmsg(
|
||||
}
|
||||
|
||||
hdlr.handleDirectMsg(
|
||||
writer, request, uid, nick,
|
||||
writer, request, sessionID, nick,
|
||||
command, target, body,
|
||||
)
|
||||
}
|
||||
@@ -596,7 +686,7 @@ func (hdlr *Handlers) handlePrivmsg(
|
||||
func (hdlr *Handlers) handleChannelMsg(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick, command, target string,
|
||||
body json.RawMessage,
|
||||
) {
|
||||
@@ -614,7 +704,7 @@ func (hdlr *Handlers) handleChannelMsg(
|
||||
}
|
||||
|
||||
isMember, err := hdlr.params.Database.IsChannelMember(
|
||||
request.Context(), chID, uid,
|
||||
request.Context(), chID, sessionID,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.log.Error(
|
||||
@@ -677,11 +767,11 @@ func (hdlr *Handlers) handleChannelMsg(
|
||||
func (hdlr *Handlers) handleDirectMsg(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick, command, target string,
|
||||
body json.RawMessage,
|
||||
) {
|
||||
targetUID, err := hdlr.params.Database.GetUserByNick(
|
||||
targetSID, err := hdlr.params.Database.GetSessionByNick(
|
||||
request.Context(), target,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -694,9 +784,9 @@ func (hdlr *Handlers) handleDirectMsg(
|
||||
return
|
||||
}
|
||||
|
||||
recipients := []int64{targetUID}
|
||||
if targetUID != uid {
|
||||
recipients = append(recipients, uid)
|
||||
recipients := []int64{targetSID}
|
||||
if targetSID != sessionID {
|
||||
recipients = append(recipients, sessionID)
|
||||
}
|
||||
|
||||
msgUUID, err := hdlr.fanOut(
|
||||
@@ -721,7 +811,7 @@ func (hdlr *Handlers) handleDirectMsg(
|
||||
func (hdlr *Handlers) handleJoin(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick, target string,
|
||||
) {
|
||||
if target == "" {
|
||||
@@ -766,7 +856,7 @@ func (hdlr *Handlers) handleJoin(
|
||||
}
|
||||
|
||||
err = hdlr.params.Database.JoinChannel(
|
||||
request.Context(), chID, uid,
|
||||
request.Context(), chID, sessionID,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.log.Error(
|
||||
@@ -800,7 +890,7 @@ func (hdlr *Handlers) handleJoin(
|
||||
func (hdlr *Handlers) handlePart(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick, target string,
|
||||
body json.RawMessage,
|
||||
) {
|
||||
@@ -841,7 +931,7 @@ func (hdlr *Handlers) handlePart(
|
||||
)
|
||||
|
||||
err = hdlr.params.Database.PartChannel(
|
||||
request.Context(), chID, uid,
|
||||
request.Context(), chID, sessionID,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.log.Error(
|
||||
@@ -871,7 +961,7 @@ func (hdlr *Handlers) handlePart(
|
||||
func (hdlr *Handlers) handleNick(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick string,
|
||||
bodyLines func() []string,
|
||||
) {
|
||||
@@ -909,7 +999,7 @@ func (hdlr *Handlers) handleNick(
|
||||
}
|
||||
|
||||
err := hdlr.params.Database.ChangeNick(
|
||||
request.Context(), uid, newNick,
|
||||
request.Context(), sessionID, newNick,
|
||||
)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "UNIQUE") {
|
||||
@@ -934,7 +1024,7 @@ func (hdlr *Handlers) handleNick(
|
||||
return
|
||||
}
|
||||
|
||||
hdlr.broadcastNick(request, uid, nick, newNick)
|
||||
hdlr.broadcastNick(request, sessionID, nick, newNick)
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
map[string]string{
|
||||
@@ -945,15 +1035,15 @@ func (hdlr *Handlers) handleNick(
|
||||
|
||||
func (hdlr *Handlers) broadcastNick(
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
oldNick, newNick string,
|
||||
) {
|
||||
channels, _ := hdlr.params.Database.
|
||||
GetAllChannelMembershipsForUser(
|
||||
request.Context(), uid,
|
||||
GetSessionChannels(
|
||||
request.Context(), sessionID,
|
||||
)
|
||||
|
||||
notified := map[int64]bool{uid: true}
|
||||
notified := map[int64]bool{sessionID: true}
|
||||
|
||||
nickBody, err := json.Marshal([]string{newNick})
|
||||
if err != nil {
|
||||
@@ -969,11 +1059,11 @@ func (hdlr *Handlers) broadcastNick(
|
||||
json.RawMessage(nickBody), nil,
|
||||
)
|
||||
|
||||
_ = hdlr.params.Database.EnqueueMessage(
|
||||
request.Context(), uid, dbID,
|
||||
_ = hdlr.params.Database.EnqueueToSession(
|
||||
request.Context(), sessionID, dbID,
|
||||
)
|
||||
|
||||
hdlr.broker.Notify(uid)
|
||||
hdlr.broker.Notify(sessionID)
|
||||
|
||||
for _, chanInfo := range channels {
|
||||
memberIDs, _ := hdlr.params.Database.
|
||||
@@ -985,7 +1075,7 @@ func (hdlr *Handlers) broadcastNick(
|
||||
if !notified[mid] {
|
||||
notified[mid] = true
|
||||
|
||||
_ = hdlr.params.Database.EnqueueMessage(
|
||||
_ = hdlr.params.Database.EnqueueToSession(
|
||||
request.Context(), mid, dbID,
|
||||
)
|
||||
|
||||
@@ -1077,13 +1167,13 @@ func (hdlr *Handlers) handleTopic(
|
||||
func (hdlr *Handlers) handleQuit(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick string,
|
||||
body json.RawMessage,
|
||||
) {
|
||||
channels, _ := hdlr.params.Database.
|
||||
GetAllChannelMembershipsForUser(
|
||||
request.Context(), uid,
|
||||
GetSessionChannels(
|
||||
request.Context(), sessionID,
|
||||
)
|
||||
|
||||
notified := map[int64]bool{}
|
||||
@@ -1103,10 +1193,10 @@ func (hdlr *Handlers) handleQuit(
|
||||
)
|
||||
|
||||
for _, mid := range memberIDs {
|
||||
if mid != uid && !notified[mid] {
|
||||
if mid != sessionID && !notified[mid] {
|
||||
notified[mid] = true
|
||||
|
||||
_ = hdlr.params.Database.EnqueueMessage(
|
||||
_ = hdlr.params.Database.EnqueueToSession(
|
||||
request.Context(), mid, dbID,
|
||||
)
|
||||
|
||||
@@ -1115,7 +1205,7 @@ func (hdlr *Handlers) handleQuit(
|
||||
}
|
||||
|
||||
_ = hdlr.params.Database.PartChannel(
|
||||
request.Context(), chanInfo.ID, uid,
|
||||
request.Context(), chanInfo.ID, sessionID,
|
||||
)
|
||||
|
||||
_ = hdlr.params.Database.DeleteChannelIfEmpty(
|
||||
@@ -1123,8 +1213,8 @@ func (hdlr *Handlers) handleQuit(
|
||||
)
|
||||
}
|
||||
|
||||
_ = hdlr.params.Database.DeleteUser(
|
||||
request.Context(), uid,
|
||||
_ = hdlr.params.Database.DeleteSession(
|
||||
request.Context(), sessionID,
|
||||
)
|
||||
|
||||
hdlr.respondJSON(writer, request,
|
||||
@@ -1138,7 +1228,8 @@ func (hdlr *Handlers) HandleGetHistory() http.HandlerFunc {
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
) {
|
||||
uid, nick, ok := hdlr.requireAuth(writer, request)
|
||||
sessionID, _, nick, ok :=
|
||||
hdlr.requireAuth(writer, request)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -1155,7 +1246,7 @@ func (hdlr *Handlers) HandleGetHistory() http.HandlerFunc {
|
||||
}
|
||||
|
||||
if !hdlr.canAccessHistory(
|
||||
writer, request, uid, nick, target,
|
||||
writer, request, sessionID, nick, target,
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -1198,12 +1289,12 @@ func (hdlr *Handlers) HandleGetHistory() http.HandlerFunc {
|
||||
func (hdlr *Handlers) canAccessHistory(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
nick, target string,
|
||||
) bool {
|
||||
if strings.HasPrefix(target, "#") {
|
||||
return hdlr.canAccessChannelHistory(
|
||||
writer, request, uid, target,
|
||||
writer, request, sessionID, target,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1225,7 +1316,7 @@ func (hdlr *Handlers) canAccessHistory(
|
||||
func (hdlr *Handlers) canAccessChannelHistory(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uid int64,
|
||||
sessionID int64,
|
||||
target string,
|
||||
) bool {
|
||||
chID, err := hdlr.params.Database.GetChannelByName(
|
||||
@@ -1242,7 +1333,7 @@ func (hdlr *Handlers) canAccessChannelHistory(
|
||||
}
|
||||
|
||||
isMember, err := hdlr.params.Database.IsChannelMember(
|
||||
request.Context(), chID, uid,
|
||||
request.Context(), chID, sessionID,
|
||||
)
|
||||
if err != nil {
|
||||
hdlr.log.Error(
|
||||
|
||||
Reference in New Issue
Block a user