Compare commits

...

4 Commits

Author SHA1 Message Date
efim 5cab5d88d9 feat: simplest room expiration 2023-12-01 04:14:23 +00:00
efim a502ee72a0 fix: request context to rooms manager 2023-12-01 04:13:10 +00:00
efim cc33c3f742 feat: expire & prolong sessions 2023-12-01 04:09:17 +00:00
efim e0bd77fe3b fix: page names 2023-11-30 04:17:54 +00:00
5 changed files with 51 additions and 21 deletions

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"log" "log"
"math/rand" "math/rand"
"time"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
) )
@ -50,13 +51,14 @@ func (r *Room) UnmarshalBinary(data []byte) error {
// let's check whether it will be possible to save nested structs // let's check whether it will be possible to save nested structs
type RoomManager interface { type RoomManager interface {
Get(roomName string) (Room, bool, error) Get(ctx context.Context, roomName string) (Room, bool, error)
Save(room Room) error Save(ctx context.Context, room Room) error
Update(ctx context.Context, roomName string, f func(fromRoom Room) (toRoom Room)) error Update(ctx context.Context, roomName string, f func(fromRoom Room) (toRoom Room)) error
Subscribe(ctx context.Context, roomName string) <-chan Room Subscribe(ctx context.Context, roomName string) <-chan Room
} }
const roomRedisPrefix = "room" const roomRedisPrefix = "room"
const roomTtl = 24 * time.Hour
func roomNameToRedisId(roomName string) string { func roomNameToRedisId(roomName string) string {
return fmt.Sprintf("%s:%s", roomRedisPrefix, roomName) return fmt.Sprintf("%s:%s", roomRedisPrefix, roomName)
@ -66,9 +68,9 @@ type RedisRM struct {
Rdb *redis.Client Rdb *redis.Client
} }
func (redisRM RedisRM) Get(roomName string) (Room, bool, error) { func (redisRM RedisRM) Get(ctx context.Context, roomName string) (Room, bool, error) {
var readRoom Room var readRoom Room
err := redisRM.Rdb.Get(context.TODO(), roomNameToRedisId(roomName)).Scan(&readRoom) err := redisRM.Rdb.Get(ctx, roomNameToRedisId(roomName)).Scan(&readRoom)
if err == redis.Nil { if err == redis.Nil {
return Room{}, false, nil return Room{}, false, nil
} }
@ -155,7 +157,7 @@ func (redisRM RedisRM) Update(ctx context.Context, roomName string, f func(fromR
_, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error { _, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
log.Printf(">> about to Set %s to %v", roomName, room) log.Printf(">> about to Set %s to %v", roomName, room)
pipe.Set(ctx, roomKey, &room, 0) pipe.Set(ctx, roomKey, &room, roomTtl)
return nil return nil
}) })
@ -177,7 +179,7 @@ func (redisRM RedisRM) Update(ctx context.Context, roomName string, f func(fromR
return errors.New("update reached maximum amount of retries") return errors.New("update reached maximum amount of retries")
} }
func (redisRM RedisRM) Save(room Room) error { func (redisRM RedisRM) Save(ctx context.Context, room Room) error {
err := redisRM.Rdb.Set(context.TODO(), roomNameToRedisId(room.Name), &room, 0).Err() // maybe even set expiration? err := redisRM.Rdb.Set(ctx, roomNameToRedisId(room.Name), &room, roomTtl).Err()
return err return err
} }

View File

@ -34,6 +34,7 @@ func indexPageRoute(
) http.HandlerFunc { ) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session, err := getRequestSession(r, sessionSM) session, err := getRequestSession(r, sessionSM)
log.Printf("/ the session i got is %+v", session)
if err != nil { if err != nil {
log.Printf("/ session not found, means should render the login section %s", err) log.Printf("/ session not found, means should render the login section %s", err)
// TODO return error i guess // TODO return error i guess
@ -53,7 +54,7 @@ func indexPageRoute(
data := pageData{ data := pageData{
Base: baseData{ Base: baseData{
Title: "hello base template title", Title: "Some Automoderation: simple automation",
}, },
Header: headerData{ Header: headerData{
Title: session.RoomId, Title: session.RoomId,

View File

@ -71,7 +71,7 @@ func getRequestSession(r *http.Request,
if err != nil { if err != nil {
return sessions.SessionData{}, ErrAuthCookieValueInvalid return sessions.SessionData{}, ErrAuthCookieValueInvalid
} }
session := sessionsM.Get(sessionId) session := sessionsM.Get(r.Context(), sessionId)
if session == (sessions.SessionData{}) { if session == (sessions.SessionData{}) {
return sessions.SessionData{}, ErrAuthSessionNotFound return sessions.SessionData{}, ErrAuthSessionNotFound
} }
@ -115,7 +115,7 @@ func renderLoginPage(w http.ResponseWriter, roomName string, isRoomExisting bool
title := "Some Automoderation: Join room or create one" title := "Some Automoderation: Join room or create one"
if roomName != "" { if roomName != "" {
title = fmt.Sprintf("Some Automoderation: create or join '%s' room", roomName) title = fmt.Sprintf("Some Automoderation: join room '%s'", roomName)
} }
data := pageData{ data := pageData{
Base: baseData{ Base: baseData{
@ -146,7 +146,7 @@ func createRoomHandler(templateFs *embed.FS,
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
} }
roomName := r.PostFormValue("roomName") roomName := r.PostFormValue("roomName")
_, exists, _ := roomsM.Get(roomName) _, exists, _ := roomsM.Get(r.Context(), roomName)
if exists { if exists {
// TODO return anouther error notice // TODO return anouther error notice
log.Printf("error, room name occupied %s", roomName) log.Printf("error, room name occupied %s", roomName)
@ -171,12 +171,13 @@ func createRoomHandler(templateFs *embed.FS,
AllKnownPeople: map[rooms.PersonId]rooms.Person{ AllKnownPeople: map[rooms.PersonId]rooms.Person{
person.Id: person}, person.Id: person},
} }
err = roomsM.Save(newRoom) err = roomsM.Save(r.Context(), newRoom)
if err != nil { if err != nil {
log.Printf("what am i to do? error saving room %s", err) log.Printf("what am i to do? error saving room %s", err)
// todo return error notice somehow // todo return error notice somehow
} }
newSession, err := sessionSM.Save(r.Context(), newRoom.Name, person.Id) newSession, err := sessionSM.Save(r.Context(), newRoom.Name, person.Id)
log.Printf("saving session %v", newSession)
if err != nil { if err != nil {
log.Printf("what am i to do? error saving session %s", err) log.Printf("what am i to do? error saving session %s", err)
// todo return error notice somehow // todo return error notice somehow
@ -187,6 +188,7 @@ func createRoomHandler(templateFs *embed.FS,
Secure: true, Secure: true,
HttpOnly: true, HttpOnly: true,
Path: "/", Path: "/",
Expires: time.Now().Add(newSession.ExpireIn),
}) })
w.Header().Add("HX-Redirect", fmt.Sprintf("/room/%s", newRoom.Name)) w.Header().Add("HX-Redirect", fmt.Sprintf("/room/%s", newRoom.Name))
} }
@ -203,7 +205,7 @@ func checkRoomName(templateFs *embed.FS,
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
} }
roomName := r.PostFormValue("roomName") roomName := r.PostFormValue("roomName")
_, isFound, err := roomsM.Get(roomName) _, isFound, err := roomsM.Get(r.Context(), roomName)
if err != nil { if err != nil {
log.Printf("/login/room-name-check error finding room %s\n", err) log.Printf("/login/room-name-check error finding room %s\n", err)
} }
@ -229,7 +231,7 @@ func joinRoomHandler(templateFs *embed.FS,
personPass := r.PostFormValue("personalPassword") personPass := r.PostFormValue("personalPassword")
// a) get room data // a) get room data
room, _, err := roomsM.Get(roomName) room, _, err := roomsM.Get(r.Context(), roomName)
if err != nil { if err != nil {
log.Printf("/login/join error getting room %s", roomName) log.Printf("/login/join error getting room %s", roomName)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -308,6 +310,7 @@ func joinRoomHandler(templateFs *embed.FS,
} }
newSession, err := sessionSM.Save(r.Context(), room.Name, person.Id) newSession, err := sessionSM.Save(r.Context(), room.Name, person.Id)
log.Printf("saving session %v", newSession)
if err != nil { if err != nil {
log.Printf("/login/submit > error saving session %s", err) log.Printf("/login/submit > error saving session %s", err)
} }
@ -317,6 +320,7 @@ func joinRoomHandler(templateFs *embed.FS,
Secure: true, Secure: true,
HttpOnly: true, HttpOnly: true,
Path: "/", Path: "/",
Expires: time.Now().Add(newSession.ExpireIn),
}) })
log.Printf("is is %+v. room things %s & %s, personal things %s and %s. \n found room %+v", log.Printf("is is %+v. room things %s & %s, personal things %s and %s. \n found room %+v",
newSession, roomName, roomPass, personName, personPass, room, newSession, roomName, roomPass, personName, personPass, room,

View File

@ -223,7 +223,7 @@ func roomPageRoute(
// check session, // check session,
session, err := getRequestSession(r, sessionSM) session, err := getRequestSession(r, sessionSM)
room, found, err := roomsM.Get(roomName) room, found, err := roomsM.Get(r.Context(), roomName)
if err != nil || session.RoomId != roomName { if err != nil || session.RoomId != roomName {
log.Printf("not authed with session %+v | error %s, but for wrong room, trying to access %s", session, err, roomName) log.Printf("not authed with session %+v | error %s, but for wrong room, trying to access %s", session, err, roomName)
renderLoginPage(w, roomName, found) renderLoginPage(w, roomName, found)
@ -268,7 +268,7 @@ func roomPageRoute(
} }
data := pageData{ data := pageData{
Base: baseData{ Base: baseData{
Title: "room-lala-from-base", Title: fmt.Sprintf("Some Automoderation: discussion in '%s'", room.Name),
LoggedIn: true, LoggedIn: true,
}, },
Content: contentData, Content: contentData,

View File

@ -16,15 +16,18 @@ type SessionData struct {
SessionId int `redis:"session_id"` SessionId int `redis:"session_id"`
RoomId string `redis:"room_id"` RoomId string `redis:"room_id"`
PersonId rooms.PersonId `redis:"person_id"` PersonId rooms.PersonId `redis:"person_id"`
ExpireIn time.Duration `redis:"-"`
} }
type SessionManagement interface { type SessionManagement interface {
Get(sessionId int) SessionData Get(ctx context.Context, sessionId int) SessionData
Save(ctx context.Context, roomName string, personId rooms.PersonId) (SessionData, error) Save(ctx context.Context, roomName string, personId rooms.PersonId) (SessionData, error)
Remove(ctx context.Context, sessionId int) error Remove(ctx context.Context, sessionId int) error
} }
const sessionPrefix = "session" const sessionPrefix = "session"
const SessionTtl = 3 * time.Hour
const SessionProlongationWindow = time.Hour
func sessionIdToKey(sessionId int) string { func sessionIdToKey(sessionId int) string {
return fmt.Sprintf("%s:%d", sessionPrefix, sessionId) return fmt.Sprintf("%s:%d", sessionPrefix, sessionId)
@ -34,15 +37,34 @@ type RedisSM struct {
Rdb *redis.Client Rdb *redis.Client
} }
func (redisSM RedisSM) Get(sessionId int) SessionData { func (redisSM RedisSM) Get(ctx context.Context, sessionId int) SessionData {
var foundSession SessionData var foundSession SessionData
redisKey := sessionIdToKey(sessionId) redisKey := sessionIdToKey(sessionId)
err := redisSM.Rdb.HGetAll(context.TODO(), redisKey).Scan(&foundSession) err := redisSM.Rdb.HGetAll(ctx, redisKey).Scan(&foundSession)
if err != nil { if err != nil {
log.Printf("> error reading %s : %s", redisKey, err) log.Printf("> error reading %s : %s", redisKey, err)
return SessionData{} return SessionData{}
} }
log.Printf("> successfully found %d %+v", sessionId, foundSession) log.Printf("> successfully found %d %+v", sessionId, foundSession)
ttl, err := redisSM.Rdb.TTL(ctx, redisKey).Result()
if err != nil {
log.Printf("> error getting ttl for session %+v", foundSession)
return foundSession
}
if ttl == -2 {
log.Printf("> ttl indicates session doesn't exist for session %+v", foundSession)
return SessionData{}
}
if ttl < SessionProlongationWindow {
err = redisSM.Rdb.Expire(ctx, redisKey, SessionTtl).Err()
if err != nil {
log.Printf("> error updating ttl for session %+v", foundSession)
return foundSession
} else {
ttl = SessionTtl
}
}
foundSession.ExpireIn = ttl
return foundSession return foundSession
} }
func (redisSM RedisSM) Save(ctx context.Context, roomName string, personId rooms.PersonId) (SessionData, error) { func (redisSM RedisSM) Save(ctx context.Context, roomName string, personId rooms.PersonId) (SessionData, error) {
@ -51,9 +73,10 @@ func (redisSM RedisSM) Save(ctx context.Context, roomName string, personId rooms
SessionId: randId, SessionId: randId,
RoomId: roomName, RoomId: roomName,
PersonId: personId, PersonId: personId,
ExpireIn: SessionTtl,
} }
err := redisSM.Rdb.HSet(ctx, sessionIdToKey(randId), newSession).Err() err := redisSM.Rdb.HSet(ctx, sessionIdToKey(randId), newSession).Err()
redisSM.Rdb.Expire(ctx, sessionIdToKey(randId), 24 * time.Hour) redisSM.Rdb.Expire(ctx, sessionIdToKey(randId), SessionTtl)
if err != nil { if err != nil {
log.Printf("> error! saving session %+v %s", newSession, err) log.Printf("> error! saving session %+v %s", newSession, err)
@ -69,7 +92,7 @@ func (redisSM RedisSM) Remove(ctx context.Context, sessionId int) error {
type DummySM struct{} type DummySM struct{}
func (d DummySM) Get(sessionId int) SessionData { func (d DummySM) Get(ctx context.Context, sessionId int) SessionData {
log.Printf("get dummy session by %d", sessionId) log.Printf("get dummy session by %d", sessionId)
return SessionData{} return SessionData{}
} }