Feat: add password for player

This commit is contained in:
Grail Finder
2025-07-09 12:39:16 +03:00
parent 50d042a19d
commit 502317507b
10 changed files with 38 additions and 41 deletions

View File

@ -8,6 +8,10 @@
<input type="hidden" name="room_id" value={{.}}>
</div>
<div id="login_notice">this name looks available</div>
<label For="password" class="block text-sm font-medium leading-6 text-white-900">password</label>
<div>
<input id="password" name="password" type="password" class="block w-full rounded-md border-0 bg-white py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 text-center"/>
</div>
<div>
<button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Sign in</button>
</div>

View File

@ -2,11 +2,15 @@
<div id="logindiv">
<form class="space-y-6" hx-post="/login" hx-target="#ancestor">
<div>
<label For="username" class="block text-sm font-medium leading-6 text-white-900">tell us your username</label>
<label For="username" class="block text-sm font-medium leading-6 text-white-900">tell us your username (signup|login)</label>
<div class="mt-2">
<input id="username" name="username" hx-target="#login_notice" hx-swap="outerHTML" hx-post="/check/name" hx-trigger="input changed delay:400ms" autocomplete="username" required class="block w-full rounded-md border-0 bg-white py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 text-center"/>
</div>
<div id="login_notice">this name looks available</div>
</div>
<div>
<label For="password" class="block text-sm font-medium leading-6 text-white-900">password</label>
<input id="password" name="password" type="password" class="block w-full rounded-md border-0 bg-white py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 text-center"/>
</div>
<div>
<button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Sign in</button>

View File

@ -1,9 +1,6 @@
{{define "roomlist"}}
<div id="roomlist" hx-get="/" hx-trigger="sse:roomlistupdate" hx-target="#ancestor">
{{range .}}
<p>
{{.ID}}
</p>
<div hx-get="/room-join?id={{.ID}}" hx-target="#ancestor" class="room-item mb-3 p-4 border rounded-lg hover:bg-gray-50 transition-colors">
<div class="flex justify-between items-center">
<div class="room-info">

View File

@ -4,6 +4,8 @@ import (
"context"
"database/sql"
"errors"
"gralias/broker"
"gralias/models"
"gralias/repos"
"log/slog"
"time"
@ -61,7 +63,6 @@ func (cm *CronManager) CleanupRooms() {
cm.log.Error("failed to get players for room", "room_id", room.ID, "err", err)
continue
}
if len(players) == 0 {
cm.log.Info("deleting empty room", "room_id", room.ID)
if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil {
@ -72,7 +73,6 @@ func (cm *CronManager) CleanupRooms() {
}
continue
}
creatorInRoom := false
for _, player := range players {
if player.Username == room.CreatorName {
@ -80,7 +80,6 @@ func (cm *CronManager) CleanupRooms() {
break
}
}
isInactive := false
// If the creator is in the room and the room is more than one hour old, check for inactivity
if creatorInRoom && time.Since(room.CreatedAt) > time.Hour {
@ -104,7 +103,6 @@ func (cm *CronManager) CleanupRooms() {
reason = "inactive"
}
cm.log.Info("deleting room", "room_id", room.ID, "reason", reason)
for _, player := range players {
if player.IsBot {
if err := cm.repo.PlayerDelete(ctx, room.ID); err != nil {
@ -116,14 +114,12 @@ func (cm *CronManager) CleanupRooms() {
}
}
}
if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil {
cm.log.Error("failed to delete room", "room_id", room.ID, "reason", reason, "err", err)
}
if err := cm.repo.SettingsDeleteByRoomID(ctx, room.ID); err != nil {
cm.log.Error("failed to delete settings for room", "room_id", room.ID, "reason", reason, "err", err)
}
// Move to the next room
continue
}
@ -131,6 +127,10 @@ func (cm *CronManager) CleanupRooms() {
if err := tx.Commit(); err != nil {
cm.log.Error("failed to commit transaction", "err", err)
}
broker.Notifier.Notifier <- broker.NotificationEvent{
EventName: models.NotifyRoomListUpdate,
Payload: "",
}
}
func (cm *CronManager) CleanupActions() {

View File

@ -37,7 +37,6 @@ func HandleNameCheck(w http.ResponseWriter, r *http.Request) {
return
}
cleanName := utils.RemoveSpacesFromStr(username)
// allNames := getAllNames()
allNames, err := repo.PlayerListNames(r.Context())
if err != nil {
abortWithError(w, err.Error())
@ -74,10 +73,12 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
abortWithError(w, msg)
return
}
password := r.PostFormValue("password")
var makeplayer bool
roomID := r.PostFormValue("room_id")
// make sure username does not exists
cleanName := utils.RemoveSpacesFromStr(username)
clearPass := utils.RemoveSpacesFromStr(password)
// login user
cookie, err := makeCookie(cleanName, r.RemoteAddr)
if err != nil {
@ -85,15 +86,19 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
abortWithError(w, err.Error())
return
}
http.SetCookie(w, cookie)
// check if that user was already in db
// userstate, err := loadState(cleanName)
userstate, err := repo.PlayerGetByName(r.Context(), cleanName)
if err != nil || userstate == nil {
log.Debug("making new player", "error", err, "state", userstate)
userstate = models.InitPlayer(cleanName)
makeplayer = true
}
if userstate.Password != clearPass {
log.Error("wrong password", "username", cleanName, "password", clearPass)
abortWithError(w, "wrong password")
return
}
http.SetCookie(w, cookie)
fi := &models.FullInfo{
State: userstate,
}
@ -106,20 +111,12 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
abortWithError(w, err.Error())
return
}
// room.PlayerList = append(room.PlayerList, fi.State.Username)
// fi.Room = room
fi.List = nil
fi.State.RoomID = &room.ID
if err := repo.PlayerSetRoomID(r.Context(), room.ID, fi.State.Username); err != nil {
abortWithError(w, err.Error())
return
}
// repo.RoomUpdate()
// save full info instead
// if err := saveFullInfo(r.Context(), fi); err != nil {
// abortWithError(w, err.Error())
// return
// }
} else {
log.Debug("no room_id in login")
// fi.List = listRooms(false)
@ -129,19 +126,15 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) {
return
}
// save state to cache
// if err := saveState(cleanName, userstate); err != nil {
if makeplayer {
userstate.Password = clearPass
if err := repo.PlayerAdd(r.Context(), userstate); err != nil {
// if err := saveFullInfo(r.Context(), fi); err != nil {
log.Error("failed to save state", "error", err)
abortWithError(w, err.Error())
return
}
}
}
// if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
// log.Error("failed to execute base template", "error", err)
// }
http.Redirect(w, r, "/", 302)
}

View File

@ -94,19 +94,16 @@ func HandleExit(w http.ResponseWriter, r *http.Request) {
}
notify(models.NotifyRoomListUpdate, "")
}
// scary to update the whole room
fiToSave := &models.FullInfo{
Room: exitedRoom,
}
if err := repo.PlayerExitRoom(r.Context(), fi.State.Username); err != nil {
log.Error("failed to exit room", "error", err)
abortWithError(w, err.Error())
return
}
if err := saveFullInfo(r.Context(), fiToSave); err != nil {
if err := repo.RoomUpdate(r.Context(), exitedRoom); err != nil {
log.Error("failed to update room", "error", err)
abortWithError(w, err.Error())
return
}
// fi.List = listRooms(false)
fi.List, err = repo.RoomList(r.Context())
if err != nil {
abortWithError(w, err.Error())

View File

@ -21,6 +21,7 @@ CREATE TABLE players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
room_id TEXT, -- nullable
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL DEFAULT '',
team TEXT NOT NULL DEFAULT '', -- 'red' or 'blue'
role TEXT NOT NULL DEFAULT '', -- 'guesser' or 'mime'
is_bot BOOLEAN NOT NULL DEFAULT FALSE,

View File

@ -106,6 +106,7 @@ type Player struct {
ID uint32 `json:"id" db:"id"`
RoomID *string `json:"room_id" db:"room_id"`
Username string `json:"username" db:"username"`
Password string `json:"-" db:"password"`
Team UserTeam `json:"team" db:"team"`
Role UserRole `json:"role" db:"role"`
IsBot bool `json:"is_bot" db:"is_bot"`
@ -153,7 +154,6 @@ type PlayerStats struct {
PlayedAsGuesser int `db:"played_as_guesser"`
}
type Room struct {
ID string `json:"id" db:"id"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
@ -465,7 +465,6 @@ type FullInfo struct {
}
func (f *FullInfo) ExitRoom() *Room {
// f.Room.PlayerList = utils.RemoveFromSlice(f.State.Username, f.Room.PlayerList)
f.Room.RedTeam.Guessers = utils.RemoveFromSlice(f.State.Username, f.Room.RedTeam.Guessers)
f.Room.BlueTeam.Guessers = utils.RemoveFromSlice(f.State.Username, f.Room.BlueTeam.Guessers)
if f.Room.RedTeam.Mime == f.State.Username {
@ -474,7 +473,7 @@ func (f *FullInfo) ExitRoom() *Room {
if f.Room.BlueTeam.Mime == f.State.Username {
f.Room.BlueTeam.Mime = ""
}
// f.State.ExitRoom()
f.State.RoomID = nil
resp := f.Room
f.Room = nil
return resp

View File

@ -32,7 +32,7 @@ func (p *RepoProvider) PlayerListNames(ctx context.Context) ([]string, error) {
func (p *RepoProvider) PlayerGetByName(ctx context.Context, username string) (*models.Player, error) {
var player models.Player
err := sqlx.GetContext(ctx, p.DB, &player, "SELECT id, room_id, username, team, role, is_bot FROM players WHERE username = ?", username)
err := sqlx.GetContext(ctx, p.DB, &player, "SELECT id, room_id, username, team, role, is_bot, password FROM players WHERE username = ?", username)
if err != nil {
return nil, err
}
@ -44,8 +44,8 @@ func (p *RepoProvider) PlayerGetByName(ctx context.Context, username string) (*m
func (p *RepoProvider) PlayerAdd(ctx context.Context, player *models.Player) error {
db := getDB(ctx, p.DB)
_, err := db.ExecContext(ctx, "INSERT INTO players (room_id, username, team, role, is_bot) VALUES (?, ?, ?, ?, ?)",
player.RoomID, player.Username, player.Team, player.Role, player.IsBot)
_, err := db.ExecContext(ctx, "INSERT INTO players (room_id, username, team, role, is_bot, password) VALUES (?, ?, ?, ?, ?, ?)",
player.RoomID, player.Username, player.Team, player.Role, player.IsBot, player.Password)
return err
}

View File

@ -6,8 +6,8 @@ import (
"testing"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/assert"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
)
func setupPlayersTestDB(t *testing.T) (*sqlx.DB, func()) {
@ -19,6 +19,7 @@ func setupPlayersTestDB(t *testing.T) (*sqlx.DB, func()) {
id INTEGER PRIMARY KEY AUTOINCREMENT,
room_id TEXT,
username TEXT,
password TEXT NOT NULL DEFAULT '',
team TEXT,
role TEXT,
is_bot BOOLEAN
@ -106,3 +107,4 @@ func TestPlayersRepo_DeletePlayer(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 0, count)
}