Compare commits

..

No commits in common. "834d1e62a003ff8382a304438a7396977605ae37" and "8439519d8e78a3cd312974c40567f6477cd68176" have entirely different histories.

7 changed files with 40 additions and 219 deletions

View File

@ -34,11 +34,10 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: go mod download run: go mod download
- name: Lint Codebase - name: Lint Codebase
continue-on-error: true
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v6
with: with:
args: --timeout=10m
version: latest version: latest
args: --config .golangci.yml
- name: Test - name: Test
run: go test -v run: go test -v
- name: Build - name: Build

View File

@ -1,8 +0,0 @@
run:
timeout: 10m
severity:
default-severity: error
rules:
- linters:
- unused
severity: info

View File

@ -19,7 +19,6 @@ package account
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"github.com/pagefaultgames/rogueserver/db" "github.com/pagefaultgames/rogueserver/db"
@ -29,7 +28,7 @@ import (
func Logout(token []byte) error { func Logout(token []byte) error {
err := db.RemoveSessionFromToken(token) err := db.RemoveSessionFromToken(token)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if err == sql.ErrNoRows {
return fmt.Errorf("token not found") return fmt.Errorf("token not found")
} }

View File

@ -21,11 +21,12 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"net/http"
"github.com/pagefaultgames/rogueserver/api/account" "github.com/pagefaultgames/rogueserver/api/account"
"github.com/pagefaultgames/rogueserver/api/daily" "github.com/pagefaultgames/rogueserver/api/daily"
"github.com/pagefaultgames/rogueserver/db" "github.com/pagefaultgames/rogueserver/db"
"log"
"net/http"
) )
func Init(mux *http.ServeMux) error { func Init(mux *http.ServeMux) error {
@ -48,17 +49,14 @@ func Init(mux *http.ServeMux) error {
mux.HandleFunc("GET /game/classicsessioncount", handleGameClassicSessionCount) mux.HandleFunc("GET /game/classicsessioncount", handleGameClassicSessionCount)
// savedata // savedata
mux.HandleFunc("GET /savedata/get", legacyHandleGetSaveData) mux.HandleFunc("GET /savedata/get", handleGetSaveData)
mux.HandleFunc("POST /savedata/update", legacyHandleSaveData) mux.HandleFunc("POST /savedata/update", handleSaveData)
mux.HandleFunc("GET /savedata/delete", legacyHandleSaveData) // TODO use deleteSystemSave mux.HandleFunc("GET /savedata/delete", handleSaveData) // TODO use deleteSystemSave
mux.HandleFunc("POST /savedata/clear", legacyHandleSaveData) // TODO use clearSessionData mux.HandleFunc("POST /savedata/clear", handleSaveData) // TODO use clearSessionData
mux.HandleFunc("GET /savedata/newclear", legacyHandleNewClear) mux.HandleFunc("GET /savedata/newclear", handleNewClear)
// new session // new session
mux.HandleFunc("POST /savedata/updateall", handleUpdateAll) mux.HandleFunc("POST /savedata/updateall", handleUpdateAll)
mux.HandleFunc("POST /savedata/verify", handleSessionVerify)
mux.HandleFunc("GET /savedata/system", handleGetSystemData)
mux.HandleFunc("GET /savedata/session", handleGetSessionData)
// daily // daily
mux.HandleFunc("GET /daily/seed", handleDailySeed) mux.HandleFunc("GET /daily/seed", handleDailySeed)

View File

@ -146,54 +146,8 @@ func handleGameClassicSessionCount(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(strconv.Itoa(classicSessionCount))) _, _ = w.Write([]byte(strconv.Itoa(classicSessionCount)))
} }
func handleGetSessionData(w http.ResponseWriter, r *http.Request) { func handleGetSaveData(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r) token, uuid, err := tokenAndUuidFromRequest(r)
if err != nil {
httpError(w, r, err, http.StatusBadRequest)
return
}
var slot int
if r.URL.Query().Has("slot") {
slot, err = strconv.Atoi(r.URL.Query().Get("slot"))
if err != nil {
httpError(w, r, err, http.StatusBadRequest)
return
}
}
var clientSessionId string
if r.URL.Query().Has("clientSessionId") {
clientSessionId = r.URL.Query().Get("clientSessionId")
} else {
httpError(w, r, fmt.Errorf("missing clientSessionId"), http.StatusBadRequest)
}
err = db.UpdateActiveSession(uuid, clientSessionId)
if err != nil {
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
return
}
var save any
save, err = savedata.Get(uuid, 1, slot)
if errors.Is(err, sql.ErrNoRows) {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
if err != nil {
httpError(w, r, err, http.StatusInternalServerError)
return
}
jsonResponse(w, r, save)
}
const legacyClientSessionId = "LEGACY_CLIENT"
func legacyHandleGetSaveData(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusBadRequest) httpError(w, r, err, http.StatusBadRequest)
return return
@ -219,7 +173,7 @@ func legacyHandleGetSaveData(w http.ResponseWriter, r *http.Request) {
var save any var save any
if datatype == 0 { if datatype == 0 {
err = db.UpdateActiveSession(uuid, legacyClientSessionId) // we dont have a client id err = db.UpdateActiveSession(uuid, token)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest) httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
return return
@ -242,7 +196,7 @@ func legacyHandleGetSaveData(w http.ResponseWriter, r *http.Request) {
// FIXME UNFINISHED!!! // FIXME UNFINISHED!!!
func clearSessionData(w http.ResponseWriter, r *http.Request) { func clearSessionData(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r) token, uuid, err := tokenAndUuidFromRequest(r)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusBadRequest) httpError(w, r, err, http.StatusBadRequest)
return return
@ -268,7 +222,7 @@ func clearSessionData(w http.ResponseWriter, r *http.Request) {
save = session save = session
var active bool var active bool
active, err = db.IsActiveSession(uuid, legacyClientSessionId) //TODO unfinished, read token from query active, err = db.IsActiveSession(token)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest) httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
return return
@ -330,7 +284,7 @@ func clearSessionData(w http.ResponseWriter, r *http.Request) {
// FIXME UNFINISHED!!! // FIXME UNFINISHED!!!
func deleteSystemSave(w http.ResponseWriter, r *http.Request) { func deleteSystemSave(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r) token, uuid, err := tokenAndUuidFromRequest(r)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusBadRequest) httpError(w, r, err, http.StatusBadRequest)
return return
@ -355,7 +309,7 @@ func deleteSystemSave(w http.ResponseWriter, r *http.Request) {
} }
var active bool var active bool
active, err = db.IsActiveSession(uuid, legacyClientSessionId) //TODO unfinished, read token from query active, err = db.IsActiveSession(token)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusInternalServerError) httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusInternalServerError)
return return
@ -409,8 +363,8 @@ func deleteSystemSave(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
func legacyHandleSaveData(w http.ResponseWriter, r *http.Request) { func handleSaveData(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r) token, uuid, err := tokenAndUuidFromRequest(r)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusBadRequest) httpError(w, r, err, http.StatusBadRequest)
return return
@ -434,14 +388,6 @@ func legacyHandleSaveData(w http.ResponseWriter, r *http.Request) {
} }
} }
var clientSessionId string
if r.URL.Query().Has("clientSessionId") {
clientSessionId = r.URL.Query().Get("clientSessionId")
}
if clientSessionId == "" {
clientSessionId = legacyClientSessionId
}
var save any var save any
// /savedata/get and /savedata/delete specify datatype, but don't expect data in body // /savedata/get and /savedata/delete specify datatype, but don't expect data in body
if r.URL.Path != "/savedata/get" && r.URL.Path != "/savedata/delete" { if r.URL.Path != "/savedata/get" && r.URL.Path != "/savedata/delete" {
@ -470,14 +416,14 @@ func legacyHandleSaveData(w http.ResponseWriter, r *http.Request) {
var active bool var active bool
if r.URL.Path == "/savedata/get" { if r.URL.Path == "/savedata/get" {
if datatype == 0 { if datatype == 0 {
err = db.UpdateActiveSession(uuid, clientSessionId) err = db.UpdateActiveSession(uuid, token)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest) httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
return return
} }
} }
} else { } else {
active, err = db.IsActiveSession(uuid, clientSessionId) active, err = db.IsActiveSession(token)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest) httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
return return
@ -571,28 +517,20 @@ func legacyHandleSaveData(w http.ResponseWriter, r *http.Request) {
} }
type CombinedSaveData struct { type CombinedSaveData struct {
System defs.SystemSaveData `json:"system"` System defs.SystemSaveData `json:"system"`
Session defs.SessionSaveData `json:"session"` Session defs.SessionSaveData `json:"session"`
SessionSlotId int `json:"sessionSlotId"` SessionSlotId int `json:"sessionSlotId"`
ClientSessionId string `json:"clientSessionId"`
} }
// TODO wrap this in a transaction // TODO wrap this in a transaction
func handleUpdateAll(w http.ResponseWriter, r *http.Request) { func handleUpdateAll(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r) var token []byte
token, uuid, err := tokenAndUuidFromRequest(r)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusBadRequest) httpError(w, r, err, http.StatusBadRequest)
return return
} }
var clientSessionId string
if r.URL.Query().Has("clientSessionId") {
clientSessionId = r.URL.Query().Get("clientSessionId")
}
if clientSessionId == "" {
clientSessionId = legacyClientSessionId
}
var data CombinedSaveData var data CombinedSaveData
err = json.NewDecoder(r.Body).Decode(&data) err = json.NewDecoder(r.Body).Decode(&data)
if err != nil { if err != nil {
@ -601,7 +539,7 @@ func handleUpdateAll(w http.ResponseWriter, r *http.Request) {
} }
var active bool var active bool
active, err = db.IsActiveSession(uuid, clientSessionId) active, err = db.IsActiveSession(token)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest) httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
return return
@ -646,98 +584,7 @@ func handleUpdateAll(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
type SessionVerifyResponse struct { func handleNewClear(w http.ResponseWriter, r *http.Request) {
Valid bool `json:"valid"`
SessionData *defs.SessionSaveData `json:"sessionData"`
}
type SessionVerifyRequest struct {
ClientSessionId string `json:"clientSessionId"`
Slot int `json:"slot"`
}
func handleSessionVerify(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r)
if err != nil {
httpError(w, r, err, http.StatusBadRequest)
return
}
var input SessionVerifyRequest
err = json.NewDecoder(r.Body).Decode(&input)
if err != nil {
httpError(w, r, fmt.Errorf("failed to decode request body: %s", err), http.StatusBadRequest)
return
}
var active bool
active, err = db.IsActiveSession(uuid, input.ClientSessionId)
if err != nil {
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
return
}
response := SessionVerifyResponse{
Valid: active,
}
// not valid, send server state
if !active {
err = db.UpdateActiveSession(uuid, input.ClientSessionId)
if err != nil {
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
return
}
var storedSaveData defs.SessionSaveData
storedSaveData, err = db.ReadSessionSaveData(uuid, input.Slot)
if err != nil {
httpError(w, r, fmt.Errorf("failed to read session save data: %s", err), http.StatusInternalServerError)
return
}
response.SessionData = &storedSaveData
}
jsonResponse(w, r, response)
}
func handleGetSystemData(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r)
if err != nil {
httpError(w, r, err, http.StatusBadRequest)
return
}
var clientSessionId string
if r.URL.Query().Has("clientSessionId") {
clientSessionId = r.URL.Query().Get("clientSessionId")
} else {
httpError(w, r, fmt.Errorf("missing clientSessionId"), http.StatusBadRequest)
}
err = db.UpdateActiveSession(uuid, clientSessionId)
if err != nil {
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
return
}
var save any //TODO this is always system save data
save, err = savedata.Get(uuid, 0, 0)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
http.Error(w, err.Error(), http.StatusNotFound)
} else {
httpError(w, r, err, http.StatusInternalServerError)
}
return
}
jsonResponse(w, r, save)
}
func legacyHandleNewClear(w http.ResponseWriter, r *http.Request) {
uuid, err := uuidFromRequest(r) uuid, err := uuidFromRequest(r)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusBadRequest) httpError(w, r, err, http.StatusBadRequest)
@ -763,6 +610,7 @@ func legacyHandleNewClear(w http.ResponseWriter, r *http.Request) {
} }
// daily // daily
func handleDailySeed(w http.ResponseWriter, r *http.Request) { func handleDailySeed(w http.ResponseWriter, r *http.Request) {
seed, err := db.GetDailyRunSeed() seed, err := db.GetDailyRunSeed()
if err != nil { if err != nil {

View File

@ -18,8 +18,6 @@
package db package db
import ( import (
"database/sql"
"errors"
"fmt" "fmt"
"slices" "slices"
@ -42,6 +40,11 @@ func AddAccountSession(username string, token []byte) error {
return err return err
} }
_, err = handle.Exec("UPDATE sessions s JOIN accounts a ON a.uuid = s.uuid SET s.active = 1 WHERE a.username = ? AND a.lastLoggedIn IS NULL", username)
if err != nil {
return err
}
_, err = handle.Exec("UPDATE accounts SET lastLoggedIn = UTC_TIMESTAMP() WHERE username = ?", username) _, err = handle.Exec("UPDATE accounts SET lastLoggedIn = UTC_TIMESTAMP() WHERE username = ?", username)
if err != nil { if err != nil {
return err return err
@ -210,28 +213,18 @@ func UpdateTrainerIds(trainerId, secretId int, uuid []byte) error {
return nil return nil
} }
func IsActiveSession(uuid []byte, clientSessionId string) (bool, error) { func IsActiveSession(token []byte) (bool, error) {
var storedId string var active int
err := handle.QueryRow("SELECT clientSessionId FROM activeClientSessions WHERE sessions.uuid = ?", uuid).Scan(&storedId) err := handle.QueryRow("SELECT `active` FROM sessions WHERE token = ?", token).Scan(&active)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
return false, err return false, err
} }
if storedId == "" {
err = UpdateActiveSession(uuid, clientSessionId)
if err != nil {
return false, err
}
return true, nil
}
return storedId == clientSessionId, nil return active == 1, nil
} }
func UpdateActiveSession(uuid []byte, clientSessionId string) error { func UpdateActiveSession(uuid []byte, token []byte) error {
_, err := handle.Exec("REPLACE INTO activeClientSessions VALUES (?, ?)", uuid, clientSessionId) _, err := handle.Exec("UPDATE sessions SET `active` = CASE WHEN token = ? THEN 1 ELSE 0 END WHERE uuid = ?", token, uuid)
if err != nil { if err != nil {
return err return err
} }

View File

@ -145,8 +145,6 @@ func Init(username, password, protocol, address, database string) error {
func setupDb(tx *sql.Tx) error { func setupDb(tx *sql.Tx) error {
queries := []string{ queries := []string{
// MIGRATION 000
`CREATE TABLE IF NOT EXISTS accounts (uuid BINARY(16) NOT NULL PRIMARY KEY, username VARCHAR(16) UNIQUE NOT NULL, hash BINARY(32) NOT NULL, salt BINARY(16) NOT NULL, registered TIMESTAMP NOT NULL, lastLoggedIn TIMESTAMP DEFAULT NULL, lastActivity TIMESTAMP DEFAULT NULL, banned TINYINT(1) NOT NULL DEFAULT 0, trainerId SMALLINT(5) UNSIGNED DEFAULT 0, secretId SMALLINT(5) UNSIGNED DEFAULT 0)`, `CREATE TABLE IF NOT EXISTS accounts (uuid BINARY(16) NOT NULL PRIMARY KEY, username VARCHAR(16) UNIQUE NOT NULL, hash BINARY(32) NOT NULL, salt BINARY(16) NOT NULL, registered TIMESTAMP NOT NULL, lastLoggedIn TIMESTAMP DEFAULT NULL, lastActivity TIMESTAMP DEFAULT NULL, banned TINYINT(1) NOT NULL DEFAULT 0, trainerId SMALLINT(5) UNSIGNED DEFAULT 0, secretId SMALLINT(5) UNSIGNED DEFAULT 0)`,
`CREATE INDEX IF NOT EXISTS accountsByActivity ON accounts (lastActivity)`, `CREATE INDEX IF NOT EXISTS accountsByActivity ON accounts (lastActivity)`,
@ -170,12 +168,6 @@ func setupDb(tx *sql.Tx) error {
`CREATE TABLE IF NOT EXISTS systemSaveData (uuid BINARY(16) PRIMARY KEY, data LONGBLOB, timestamp TIMESTAMP)`, `CREATE TABLE IF NOT EXISTS systemSaveData (uuid BINARY(16) PRIMARY KEY, data LONGBLOB, timestamp TIMESTAMP)`,
`CREATE TABLE IF NOT EXISTS sessionSaveData (uuid BINARY(16), slot TINYINT, data LONGBLOB, timestamp TIMESTAMP, PRIMARY KEY (uuid, slot))`, `CREATE TABLE IF NOT EXISTS sessionSaveData (uuid BINARY(16), slot TINYINT, data LONGBLOB, timestamp TIMESTAMP, PRIMARY KEY (uuid, slot))`,
// ----------------------------------
// MIGRATION 001
`ALTER TABLE sessions DROP COLUMN IF EXISTS active`,
`CREATE TABLE IF NOT EXISTS activeClientSessions (uuid BINARY(16) NOT NULL PRIMARY KEY, clientSessionId VARCHAR(32) NOT NULL, FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)`,
} }
for _, q := range queries { for _, q := range queries {