database: Use specialized db connection struct

This commit is contained in:
Palapeli
2026-04-05 04:04:35 -04:00
parent d70b0ee335
commit 9606722b61
22 changed files with 155 additions and 191 deletions

View File

@@ -6,7 +6,6 @@ import (
"net/http"
"strconv"
"time"
"wwfc/database"
"wwfc/gpcm"
"wwfc/logging"
@@ -102,7 +101,7 @@ func handleBanImpl(r *http.Request) (bool, string, int) {
logging.Notice("API:"+moderator, "Ban profile:", aurora.Cyan(req.ProfileID), "TOS:", aurora.Cyan(req.Tos), "Length:", aurora.Cyan(length), "Reason:", aurora.BrightCyan(req.Reason), "Reason (Hidden):", aurora.BrightCyan(req.ReasonHidden))
if !database.BanUser(pool, ctx, req.ProfileID, req.Tos, length, req.Reason, req.ReasonHidden, moderator) {
if !db.BanUser(req.ProfileID, req.Tos, length, req.Reason, req.ReasonHidden, moderator) {
return false, "Failed to ban user", http.StatusInternalServerError
}

View File

@@ -1,16 +1,13 @@
package api
import (
"context"
"fmt"
"wwfc/common"
"github.com/jackc/pgx/v4/pgxpool"
"wwfc/database"
)
var (
ctx = context.Background()
pool *pgxpool.Pool
db database.Connection
apiSecret string
)
@@ -21,16 +18,7 @@ func StartServer(reload bool) {
apiSecret = config.APISecret
// Start SQL
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
dbConf, err := pgxpool.ParseConfig(dbString)
if err != nil {
panic(err)
}
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
if err != nil {
panic(err)
}
db = database.Start(config)
}
func Shutdown() {

View File

@@ -5,7 +5,6 @@ import (
"io"
"net/http"
"strconv"
"wwfc/database"
)
func HandleUnban(w http.ResponseWriter, r *http.Request) {
@@ -72,7 +71,7 @@ func handleUnbanImpl(r *http.Request) (bool, string, int) {
return false, "pid missing or 0 in request", http.StatusBadRequest
}
if !database.UnbanUser(pool, ctx, req.ProfileID) {
if !db.UnbanUser(req.ProfileID) {
return false, "Failed to unban user", http.StatusInternalServerError
}

37
database/connection.go Normal file
View File

@@ -0,0 +1,37 @@
package database
import (
"context"
"fmt"
"wwfc/common"
"github.com/jackc/pgx/v4/pgxpool"
)
type Connection struct {
pool *pgxpool.Pool
ctx context.Context
}
func Start(config common.Config) Connection {
conn := Connection{
ctx: context.Background(),
}
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
dbConf, err := pgxpool.ParseConfig(dbString)
if err != nil {
panic(err)
}
conn.pool, err = pgxpool.ConnectConfig(conn.ctx, dbConf)
if err != nil {
panic(err)
}
return conn
}
func (c *Connection) Close() {
c.pool.Close()
}

View File

@@ -1,10 +1,7 @@
package database
import (
"context"
"time"
"github.com/jackc/pgx/v4/pgxpool"
)
const (
@@ -13,17 +10,17 @@ const (
queryGsUpdatePublicData = `UPDATE gamestats_public_data SET pdata = $4, modified_time = CURRENT_TIMESTAMP WHERE profile_id = $1 AND dindex = $2 AND ptype = $3 RETURNING modified_time`
)
func GetGameStatsPublicData(pool *pgxpool.Pool, ctx context.Context, profileId uint32, dindex string, ptype string) (modifiedTime time.Time, publicData string, err error) {
err = pool.QueryRow(ctx, queryGsGetPublicData, profileId, dindex, ptype).Scan(&modifiedTime, &publicData)
func (c *Connection) GetGameStatsPublicData(profileId uint32, dindex string, ptype string) (modifiedTime time.Time, publicData string, err error) {
err = c.pool.QueryRow(c.ctx, queryGsGetPublicData, profileId, dindex, ptype).Scan(&modifiedTime, &publicData)
return
}
func CreateGameStatsPublicData(pool *pgxpool.Pool, ctx context.Context, profileId uint32, dindex string, ptype string, publicData string) (modifiedTime time.Time, err error) {
err = pool.QueryRow(ctx, queryGsInsertPublicData, profileId, dindex, ptype, publicData).Scan(&modifiedTime)
func (c *Connection) CreateGameStatsPublicData(profileId uint32, dindex string, ptype string, publicData string) (modifiedTime time.Time, err error) {
err = c.pool.QueryRow(c.ctx, queryGsInsertPublicData, profileId, dindex, ptype, publicData).Scan(&modifiedTime)
return
}
func UpdateGameStatsPublicData(pool *pgxpool.Pool, ctx context.Context, profileId uint32, dindex string, ptype string, publicData string) (modifiedTime time.Time, err error) {
err = pool.QueryRow(ctx, queryGsUpdatePublicData, profileId, dindex, ptype, publicData).Scan(&modifiedTime)
func (c *Connection) UpdateGameStatsPublicData(profileId uint32, dindex string, ptype string, publicData string) (modifiedTime time.Time, err error) {
err = c.pool.QueryRow(c.ctx, queryGsUpdatePublicData, profileId, dindex, ptype, publicData).Scan(&modifiedTime)
return
}

View File

@@ -1,7 +1,6 @@
package database
import (
"context"
"errors"
"fmt"
"time"
@@ -9,7 +8,6 @@ import (
"wwfc/logging"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
)
@@ -43,9 +41,9 @@ var (
ErrProfileBannedTOS = errors.New("profile is banned for violating the Terms of Service")
)
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string, profileId uint32, defaultKey bool, ngDeviceId uint32, ipAddress string, ingamesn string, deviceAuth bool) (User, error) {
func (c *Connection) LoginUserToGPCM(userId uint64, gsbrcd string, profileId uint32, defaultKey bool, ngDeviceId uint32, ipAddress string, ingamesn string, deviceAuth bool) (User, error) {
var exists bool
err := pool.QueryRow(ctx, DoesUserExist, userId, gsbrcd).Scan(&exists)
err := c.pool.QueryRow(c.ctx, DoesUserExist, userId, gsbrcd).Scan(&exists)
if err != nil {
return User{}, err
}
@@ -67,7 +65,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
user.Email = user.UniqueNick + "@nds"
// Create the GPCM account
err := user.CreateUser(pool, ctx)
err := c.CreateUser(&user)
if err != nil {
logging.Error("DATABASE", "Error creating user:", aurora.Cyan(userId), aurora.Cyan(gsbrcd), aurora.Cyan(user.ProfileId), "\nerror:", err.Error())
return User{}, err
@@ -79,7 +77,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
var lastName *string
var allowDefaultKeys bool
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress, &allowDefaultKeys)
err := c.pool.QueryRow(c.ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress, &allowDefaultKeys)
if err != nil {
return User{}, err
}
@@ -106,7 +104,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
if !validDeviceId && id == 0 {
// Replace the 0 with the actual device ID
user.NgDeviceId[index] = ngDeviceId
_, err = pool.Exec(ctx, UpdateUserNGDeviceID, user.ProfileId, user.NgDeviceId)
_, err = c.pool.Exec(c.ctx, UpdateUserNGDeviceID, user.ProfileId, user.NgDeviceId)
validDeviceId = true
}
@@ -126,7 +124,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
}
user.NgDeviceId = append(user.NgDeviceId, ngDeviceId)
_, err = pool.Exec(ctx, UpdateUserNGDeviceID, user.ProfileId, user.NgDeviceId)
_, err = c.pool.Exec(c.ctx, UpdateUserNGDeviceID, user.ProfileId, user.NgDeviceId)
} else if deviceAuth && !validDeviceId && ngDeviceId == 0 {
if len(user.NgDeviceId) > 0 && !common.GetConfig().AllowConnectWithoutDeviceID {
logging.Error("DATABASE", "NG device ID not provided for profile", aurora.Cyan(user.ProfileId), "- expected one of {", deviceIdList[:len(deviceIdList)-2], "} but got", aurora.Cyan("00000000"))
@@ -139,7 +137,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
}
if profileId != 0 && user.ProfileId != profileId {
err := user.UpdateProfileID(pool, ctx, profileId)
err := c.UpdateProfileID(&user, profileId)
if err != nil {
logging.Warn("DATABASE", "Could not update", aurora.Cyan(userId), aurora.Cyan(gsbrcd), "profile ID from", aurora.Cyan(user.ProfileId), "to", aurora.Cyan(profileId))
} else {
@@ -152,14 +150,14 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
// This should be set if the user already knows its own profile ID
if profileId != 0 && user.LastName == "" {
user.UpdateProfile(pool, ctx, map[string]string{
c.UpdateProfile(&user, map[string]string{
"lastname": "000000000" + gsbrcd,
})
}
// Update the user's last IP address and ingamesn
if deviceAuth {
_, err = pool.Exec(ctx, UpdateUserLastIPAddress, user.ProfileId, ipAddress, ingamesn)
_, err = c.pool.Exec(c.ctx, UpdateUserLastIPAddress, user.ProfileId, ipAddress, ingamesn)
if err != nil {
return User{}, err
}
@@ -176,7 +174,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
var bannedDeviceIdList []uint32
var banReason string
timeNow := time.Now().UTC()
err = pool.QueryRow(ctx, SearchUserBan, user.NgDeviceId, user.ProfileId, ipAddress, *lastIPAddress, timeNow).Scan(&banExists, &banTOS, &bannedDeviceIdList, &banReason)
err = c.pool.QueryRow(c.ctx, SearchUserBan, user.NgDeviceId, user.ProfileId, ipAddress, *lastIPAddress, timeNow).Scan(&banExists, &banTOS, &bannedDeviceIdList, &banReason)
if err != nil {
if err != pgx.ErrNoRows {
@@ -220,7 +218,7 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
return user, nil
}
func LoginUserToGameStats(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string) (User, error) {
func (c *Connection) LoginUserToGameStats(userId uint64, gsbrcd string) (User, error) {
user := User{
UserId: userId,
GsbrCode: gsbrcd,
@@ -231,7 +229,7 @@ func LoginUserToGameStats(pool *pgxpool.Pool, ctx context.Context, userId uint64
var lastIPAddress *string
var allowDefaultKeys bool
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress, &allowDefaultKeys)
err := c.pool.QueryRow(c.ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress, &allowDefaultKeys)
if err != nil {
return User{}, err
}

View File

@@ -1,11 +1,9 @@
package database
import (
"context"
"wwfc/common"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
)
type MarioKartWiiTopTenRanking struct {
@@ -56,9 +54,9 @@ const (
"SET regionid = EXCLUDED.regionid, score = EXCLUDED.score, playerinfo = EXCLUDED.playerinfo, ghost = EXCLUDED.ghost, upload_time = CURRENT_TIMESTAMP"
)
func GetMarioKartWiiTopTenRankings(pool *pgxpool.Pool, ctx context.Context, regionId common.MarioKartWiiLeaderboardRegionId,
func (c *Connection) GetMarioKartWiiTopTenRankings(regionId common.MarioKartWiiLeaderboardRegionId,
courseId common.MarioKartWiiCourseId) ([]MarioKartWiiTopTenRanking, error) {
rows, err := pool.Query(ctx, getTopTenRankingsQuery, regionId, courseId)
rows, err := c.pool.Query(c.ctx, getTopTenRankingsQuery, regionId, courseId)
if err != nil {
return nil, err
}
@@ -81,8 +79,8 @@ func GetMarioKartWiiTopTenRankings(pool *pgxpool.Pool, ctx context.Context, regi
return topTenRankings, nil
}
func GetMarioKartWiiGhostData(pool *pgxpool.Pool, ctx context.Context, courseId common.MarioKartWiiCourseId, time int) (int, error) {
row := pool.QueryRow(ctx, getGhostDataQuery, courseId, time)
func (c *Connection) GetMarioKartWiiGhostData(courseId common.MarioKartWiiCourseId, time int) (int, error) {
row := c.pool.QueryRow(c.ctx, getGhostDataQuery, courseId, time)
var fileId int
if err := row.Scan(&fileId); err != nil {
@@ -92,9 +90,9 @@ func GetMarioKartWiiGhostData(pool *pgxpool.Pool, ctx context.Context, courseId
return fileId, nil
}
func GetMarioKartWiiStoredGhostData(pool *pgxpool.Pool, ctx context.Context, regionId common.MarioKartWiiLeaderboardRegionId,
func (c *Connection) GetMarioKartWiiStoredGhostData(regionId common.MarioKartWiiLeaderboardRegionId,
courseId common.MarioKartWiiCourseId) (int, int, error) {
row := pool.QueryRow(ctx, getStoredGhostDataQuery, regionId, courseId)
row := c.pool.QueryRow(c.ctx, getStoredGhostDataQuery, regionId, courseId)
var pid int
var fileId int
@@ -105,8 +103,8 @@ func GetMarioKartWiiStoredGhostData(pool *pgxpool.Pool, ctx context.Context, reg
return pid, fileId, nil
}
func GetMarioKartWiiFile(pool *pgxpool.Pool, ctx context.Context, fileId int) ([]byte, error) {
row := pool.QueryRow(ctx, getFileQuery, fileId)
func (c *Connection) GetMarioKartWiiFile(fileId int) ([]byte, error) {
row := c.pool.QueryRow(c.ctx, getFileQuery, fileId)
var file []byte
if err := row.Scan(&file); err != nil {
@@ -116,9 +114,8 @@ func GetMarioKartWiiFile(pool *pgxpool.Pool, ctx context.Context, fileId int) ([
return file, nil
}
func GetMarioKartWiiGhostFile(pool *pgxpool.Pool, ctx context.Context, courseId common.MarioKartWiiCourseId,
time int, pid int) ([]byte, error) {
row := pool.QueryRow(ctx, getGhostFileQuery, courseId, time, pid)
func (c *Connection) GetMarioKartWiiGhostFile(courseId common.MarioKartWiiCourseId, time int, pid int) ([]byte, error) {
row := c.pool.QueryRow(c.ctx, getGhostFileQuery, courseId, time, pid)
var ghost []byte
if err := row.Scan(&ghost); err != nil {
@@ -128,17 +125,17 @@ func GetMarioKartWiiGhostFile(pool *pgxpool.Pool, ctx context.Context, courseId
return ghost, nil
}
func InsertMarioKartWiiGhostFile(pool *pgxpool.Pool, ctx context.Context, regionId common.MarioKartWiiLeaderboardRegionId,
func (c *Connection) InsertMarioKartWiiGhostFile(regionId common.MarioKartWiiLeaderboardRegionId,
courseId common.MarioKartWiiCourseId, score int, pid int, playerInfo string, ghost []byte) error {
_, err := pool.Exec(ctx, insertGhostFileStatement, regionId, courseId, score, pid, playerInfo, ghost)
_, err := c.pool.Exec(c.ctx, insertGhostFileStatement, regionId, courseId, score, pid, playerInfo, ghost)
return err
}
// Mario Kart Wii friend info functions for API compatibility
func GetMKWFriendInfo(pool *pgxpool.Pool, ctx context.Context, profileId uint32) string {
records, err := GetSakeRecords(pool, ctx, 1687, []int32{int32(profileId)}, "FriendInfo", nil, []string{"info"}, "")
func (c *Connection) GetMKWFriendInfo(profileId uint32) string {
records, err := c.GetSakeRecords(1687, []int32{int32(profileId)}, "FriendInfo", nil, []string{"info"}, "")
if err != nil || len(records) == 0 {
return ""
}
@@ -151,8 +148,8 @@ func GetMKWFriendInfo(pool *pgxpool.Pool, ctx context.Context, profileId uint32)
return infoField.Value
}
func UpdateMKWFriendInfo(pool *pgxpool.Pool, ctx context.Context, profileId uint32, info string) {
records, err := GetSakeRecords(pool, ctx, 1687, []int32{int32(profileId)}, "FriendInfo", nil, []string{"info"}, "")
func (c *Connection) UpdateMKWFriendInfo(profileId uint32, info string) {
records, err := c.GetSakeRecords(1687, []int32{int32(profileId)}, "FriendInfo", nil, []string{"info"}, "")
if err == pgx.ErrNoRows || (err == nil && len(records) == 0) {
// No existing record, insert new one
record := SakeRecord{
@@ -166,14 +163,14 @@ func UpdateMKWFriendInfo(pool *pgxpool.Pool, ctx context.Context, profileId uint
},
},
}
_, err = InsertSakeRecord(pool, ctx, record)
_, err = c.InsertSakeRecord(record)
} else if err == nil {
// Update existing record
records[0].Fields["info"] = SakeField{
Type: SakeFieldTypeBinaryData,
Value: info,
}
err = UpdateSakeRecord(pool, ctx, records[0], int32(profileId))
err = c.UpdateSakeRecord(records[0], int32(profileId))
}

View File

@@ -1,7 +1,6 @@
package database
import (
"context"
"encoding/json"
"errors"
"wwfc/filter"
@@ -63,6 +62,13 @@ const (
VALUES ($1, $2, $3, $4)
RETURNING record_id`
deleteSakeRecordQuery = `
DELETE FROM sake_records
WHERE game_id = $1
AND table_id = $2
AND record_id = $3
AND owner_id = $4`
checkMaxSakeRecordsQuery = `
SELECT COUNT(*)
FROM sake_records
@@ -84,7 +90,7 @@ func parseSakeFieldsFromJson(fieldsJson []byte) (map[string]SakeField, error) {
return fields, nil
}
func GetSakeRecords(pool *pgxpool.Pool, ctx context.Context, gameId int, ownerIds []int32, tableId string, recordIds []int32, fields []string, filterExpr string) ([]SakeRecord, error) {
func (c *Connection) GetSakeRecords(gameId int, ownerIds []int32, tableId string, recordIds []int32, fields []string, filterExpr string) ([]SakeRecord, error) {
if fields == nil {
fields = []string{}
}
@@ -103,7 +109,7 @@ func GetSakeRecords(pool *pgxpool.Pool, ctx context.Context, gameId int, ownerId
}
var filterQuery string
err = pool.AcquireFunc(ctx, func(conn *pgxpool.Conn) error {
err = c.pool.AcquireFunc(c.ctx, func(conn *pgxpool.Conn) error {
filterQuery, err = createSqlFilter(conn.Conn().PgConn(), tree)
return err
})
@@ -116,7 +122,7 @@ func GetSakeRecords(pool *pgxpool.Pool, ctx context.Context, gameId int, ownerId
query += " AND (" + filterQuery + ")"
}
rows, err := pool.Query(ctx, query, gameId, tableId, ownerIds, recordIds)
rows, err := c.pool.Query(c.ctx, query, gameId, tableId, ownerIds, recordIds)
if err != nil {
return nil, err
}
@@ -144,13 +150,13 @@ func GetSakeRecords(pool *pgxpool.Pool, ctx context.Context, gameId int, ownerId
return records, nil
}
func UpdateSakeRecord(pool *pgxpool.Pool, ctx context.Context, record SakeRecord, ownerId int32) error {
func (c *Connection) UpdateSakeRecord(record SakeRecord, ownerId int32) error {
fieldsJson, err := json.Marshal(record.Fields)
if err != nil {
return err
}
var existingOwnerId int32
err = pool.QueryRow(ctx, updateSakeRecordQuery, record.GameId, record.TableId, record.RecordId, ownerId, fieldsJson).Scan(&existingOwnerId)
err = c.pool.QueryRow(c.ctx, updateSakeRecordQuery, record.GameId, record.TableId, record.RecordId, ownerId, fieldsJson).Scan(&existingOwnerId)
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && pgErr.Code == pgerrcode.CheckViolation {
@@ -165,14 +171,14 @@ func UpdateSakeRecord(pool *pgxpool.Pool, ctx context.Context, record SakeRecord
return nil
}
func InsertSakeRecord(pool *pgxpool.Pool, ctx context.Context, record SakeRecord) (recordId int32, err error) {
func (c *Connection) InsertSakeRecord(record SakeRecord) (recordId int32, err error) {
fieldsJson, err := json.Marshal(record.Fields)
if err != nil {
return 0, err
}
for i := 0; i < 10; i++ {
err = pool.QueryRow(ctx, insertSakeRecordQuery, record.GameId, record.TableId, record.OwnerId, fieldsJson).Scan(&recordId)
err = c.pool.QueryRow(c.ctx, insertSakeRecordQuery, record.GameId, record.TableId, record.OwnerId, fieldsJson).Scan(&recordId)
if err == nil {
break
}
@@ -188,9 +194,9 @@ func InsertSakeRecord(pool *pgxpool.Pool, ctx context.Context, record SakeRecord
return recordId, err
}
func IsMaxSakeRecordsReached(pool *pgxpool.Pool, ctx context.Context, profileId uint32, maxRecords int) (bool, error) {
func (c *Connection) IsMaxSakeRecordsReached(profileId uint32, maxRecords int) (bool, error) {
var count int
err := pool.QueryRow(ctx, checkMaxSakeRecordsQuery, profileId).Scan(&count)
err := c.pool.QueryRow(c.ctx, checkMaxSakeRecordsQuery, profileId).Scan(&count)
if err != nil {
return false, err
}

View File

@@ -1,13 +1,7 @@
package database
import (
"context"
"github.com/jackc/pgx/v4/pgxpool"
)
func UpdateTables(pool *pgxpool.Pool, ctx context.Context) {
pool.Exec(ctx, `
func (c *Connection) UpdateTables() {
_, _ = c.pool.Exec(c.ctx, `
ALTER TABLE ONLY public.users
ADD IF NOT EXISTS last_ip_address character varying DEFAULT ''::character varying,
@@ -23,7 +17,7 @@ func UpdateTables(pool *pgxpool.Pool, ctx context.Context) {
ADD IF NOT EXISTS allow_default_keys boolean DEFAULT false;
`)
pool.Exec(ctx, `
_, _ = c.pool.Exec(c.ctx, `
DO $$
BEGIN
@@ -35,7 +29,7 @@ func UpdateTables(pool *pgxpool.Pool, ctx context.Context) {
`)
pool.Exec(ctx, `
_, _ = c.pool.Exec(c.ctx, `
ALTER TABLE ONLY public.mario_kart_wii_sake
ADD IF NOT EXISTS id serial PRIMARY KEY,

View File

@@ -1,12 +1,9 @@
package database
import (
"context"
"errors"
"math/rand"
"time"
"github.com/jackc/pgx/v4/pgxpool"
)
const (
@@ -48,9 +45,9 @@ var (
ErrReservedProfileIDRange = errors.New("profile ID is in reserved range")
)
func (user *User) CreateUser(pool *pgxpool.Pool, ctx context.Context) error {
func (c *Connection) CreateUser(user *User) error {
if user.ProfileId == 0 {
return pool.QueryRow(ctx, InsertUser, user.UserId, user.GsbrCode, "", user.NgDeviceId, user.Email, user.UniqueNick).Scan(&user.ProfileId)
return c.pool.QueryRow(c.ctx, InsertUser, user.UserId, user.GsbrCode, "", user.NgDeviceId, user.Email, user.UniqueNick).Scan(&user.ProfileId)
}
if user.ProfileId >= 1000000000 {
@@ -58,7 +55,7 @@ func (user *User) CreateUser(pool *pgxpool.Pool, ctx context.Context) error {
}
var exists bool
err := pool.QueryRow(ctx, IsProfileIDInUse, user.ProfileId).Scan(&exists)
err := c.pool.QueryRow(c.ctx, IsProfileIDInUse, user.ProfileId).Scan(&exists)
if err != nil {
return err
}
@@ -67,17 +64,17 @@ func (user *User) CreateUser(pool *pgxpool.Pool, ctx context.Context) error {
return ErrProfileIDInUse
}
_, err = pool.Exec(ctx, InsertUserWithProfileID, user.ProfileId, user.UserId, user.GsbrCode, "", user.NgDeviceId, user.Email, user.UniqueNick)
_, err = c.pool.Exec(c.ctx, InsertUserWithProfileID, user.ProfileId, user.UserId, user.GsbrCode, "", user.NgDeviceId, user.Email, user.UniqueNick)
return err
}
func (user *User) UpdateProfileID(pool *pgxpool.Pool, ctx context.Context, newProfileId uint32) error {
func (c *Connection) UpdateProfileID(user *User, newProfileId uint32) error {
if newProfileId >= 1000000000 {
return ErrReservedProfileIDRange
}
var exists bool
err := pool.QueryRow(ctx, IsProfileIDInUse, newProfileId).Scan(&exists)
err := c.pool.QueryRow(c.ctx, IsProfileIDInUse, newProfileId).Scan(&exists)
if err != nil {
return err
}
@@ -86,7 +83,7 @@ func (user *User) UpdateProfileID(pool *pgxpool.Pool, ctx context.Context, newPr
return ErrProfileIDInUse
}
_, err = pool.Exec(ctx, UpdateUserProfileID, user.UserId, user.GsbrCode, newProfileId)
_, err = c.pool.Exec(c.ctx, UpdateUserProfileID, user.UserId, user.GsbrCode, newProfileId)
if err == nil {
user.ProfileId = newProfileId
}
@@ -99,7 +96,7 @@ func GetUniqueUserID() uint64 {
return uint64(rand.Int63n(0x80000000000))
}
func (user *User) UpdateProfile(pool *pgxpool.Pool, ctx context.Context, data map[string]string) {
func (c *Connection) UpdateProfile(user *User, data map[string]string) {
firstName, firstNameExists := data["firstname"]
lastName, lastNameExists := data["lastname"]
openHost, openHostExists := data["wl:oh"]
@@ -108,7 +105,7 @@ func (user *User) UpdateProfile(pool *pgxpool.Pool, ctx context.Context, data ma
openHostBool = true
}
_, err := pool.Exec(ctx, UpdateUserTable, user.ProfileId, firstName, firstNameExists, lastName, lastNameExists, openHostBool, openHostExists)
_, err := c.pool.Exec(c.ctx, UpdateUserTable, user.ProfileId, firstName, firstNameExists, lastName, lastNameExists, openHostBool, openHostExists)
if err != nil {
panic(err)
}
@@ -126,9 +123,9 @@ func (user *User) UpdateProfile(pool *pgxpool.Pool, ctx context.Context, data ma
}
}
func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (User, bool) {
func (c *Connection) GetProfile(profileId uint32) (User, bool) {
user := User{}
row := pool.QueryRow(ctx, GetUser, profileId)
row := c.pool.QueryRow(c.ctx, GetUser, profileId)
err := row.Scan(&user.UserId, &user.GsbrCode, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName, &user.OpenHost, &user.LastIPAddress, &user.LastInGameSn)
if err != nil {
return User{}, false
@@ -138,9 +135,9 @@ func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (User
return user, true
}
func ClearProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (User, bool) {
func (c *Connection) ClearProfile(profileId uint32) (User, bool) {
user := User{}
row := pool.QueryRow(ctx, ClearProfileQuery, profileId)
row := c.pool.QueryRow(c.ctx, ClearProfileQuery, profileId)
err := row.Scan(&user.UserId, &user.GsbrCode, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName, &user.OpenHost, &user.LastIPAddress, &user.LastInGameSn)
if err != nil {
@@ -151,12 +148,12 @@ func ClearProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (Us
return user, true
}
func BanUser(pool *pgxpool.Pool, ctx context.Context, profileId uint32, tos bool, length time.Duration, reason string, reasonHidden string, moderator string) bool {
_, err := pool.Exec(ctx, UpdateUserBan, profileId, time.Now().UTC(), time.Now().UTC().Add(length), reason, reasonHidden, moderator, tos)
func (c *Connection) BanUser(profileId uint32, tos bool, length time.Duration, reason string, reasonHidden string, moderator string) bool {
_, err := c.pool.Exec(c.ctx, UpdateUserBan, profileId, time.Now().UTC(), time.Now().UTC().Add(length), reason, reasonHidden, moderator, tos)
return err == nil
}
func UnbanUser(pool *pgxpool.Pool, ctx context.Context, profileId uint32) bool {
_, err := pool.Exec(ctx, DisableUserBan, profileId)
func (c *Connection) UnbanUser(profileId uint32) bool {
_, err := c.pool.Exec(c.ctx, DisableUserBan, profileId)
return err == nil
}

View File

@@ -5,7 +5,6 @@ import (
"strconv"
"time"
"wwfc/common"
"wwfc/database"
"wwfc/gpcm"
"wwfc/logging"
@@ -77,7 +76,7 @@ func (g *GameStatsSession) authp(command common.GameSpyCommand) {
return
}
g.User, err = database.LoginUserToGameStats(pool, ctx, userId, gsbrcd)
g.User, err = db.LoginUserToGameStats(userId, gsbrcd)
if err != nil {
logging.Error(g.ModuleName, "Error logging in user:", err.Error())
g.Write(errorCmd)

View File

@@ -3,7 +3,6 @@ package gamestats
import (
"strconv"
"wwfc/common"
"wwfc/database"
"wwfc/logging"
"github.com/jackc/pgx/v4"
@@ -54,7 +53,7 @@ func (g *GameStatsSession) getpd(command common.GameSpyCommand) {
logging.Info(g.ModuleName, "Get public data: PID:", aurora.Cyan(profileId), "Index:", aurora.Cyan(dindex), "Type:", aurora.Cyan(ptype))
modifiedTime, data, err := database.GetGameStatsPublicData(pool, ctx, uint32(profileId), dindex, ptype)
modifiedTime, data, err := db.GetGameStatsPublicData(uint32(profileId), dindex, ptype)
if err != nil {
if err != pgx.ErrNoRows {
logging.Error(g.ModuleName, "GetGameStatsPublicData returned", err)

View File

@@ -1,9 +1,7 @@
package gamestats
import (
"context"
"encoding/gob"
"fmt"
"os"
"strings"
"wwfc/common"
@@ -11,7 +9,6 @@ import (
"wwfc/gpcm"
"wwfc/logging"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/linkdata/deadlock"
"github.com/logrusorgru/aurora/v3"
)
@@ -37,8 +34,7 @@ type GameStatsSession struct {
}
var (
ctx = context.Background()
pool *pgxpool.Pool
db database.Connection
serverName string
webSalt string
@@ -57,16 +53,7 @@ func StartServer(reload bool) {
common.ReadGameList()
// Start SQL
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
dbConf, err := pgxpool.ParseConfig(dbString)
if err != nil {
panic(err)
}
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
if err != nil {
panic(err)
}
db = database.Start(config)
if reload {
// Load state
@@ -114,7 +101,7 @@ func Shutdown() {
panic(err)
}
pool.Close()
db.Close()
logging.Notice("GSTATS", "Saved", aurora.Cyan(len(sessionsByConnIndex)), "sessions")
}

View File

@@ -5,7 +5,6 @@ import (
"strings"
"time"
"wwfc/common"
"wwfc/database"
"wwfc/logging"
"github.com/jackc/pgx/v4"
@@ -78,7 +77,7 @@ func (g *GameStatsSession) setpd(command common.GameSpyCommand) {
}
var modifiedTime time.Time
_, _, err := database.GetGameStatsPublicData(pool, ctx, g.User.ProfileId, dindex, ptype)
_, _, err := db.GetGameStatsPublicData(g.User.ProfileId, dindex, ptype)
if err != nil {
if err != pgx.ErrNoRows {
logging.Error(g.ModuleName, "GetGameStatsPublicData returned", err)
@@ -86,14 +85,14 @@ func (g *GameStatsSession) setpd(command common.GameSpyCommand) {
return
}
modifiedTime, err = database.CreateGameStatsPublicData(pool, ctx, g.User.ProfileId, dindex, ptype, newData)
modifiedTime, err = db.CreateGameStatsPublicData(g.User.ProfileId, dindex, ptype, newData)
if err != nil {
logging.Error(g.ModuleName, "GetGameStatsPublicData returned", err)
g.Write(errMsg)
return
}
} else {
modifiedTime, err = database.UpdateGameStatsPublicData(pool, ctx, g.User.ProfileId, dindex, ptype, newData)
modifiedTime, err = db.UpdateGameStatsPublicData(g.User.ProfileId, dindex, ptype, newData)
if err != nil {
logging.Error(g.ModuleName, "UpdateGameStatsPublicData returned", err)
g.Write(errMsg)

View File

@@ -456,7 +456,7 @@ func (g *GameSpySession) performLoginWithDatabase(userId uint64, gsbrCode string
ipAddress = ipAddress[:strings.Index(ipAddress, ":")]
}
user, err := database.LoginUserToGPCM(pool, ctx, userId, gsbrCode, profileId, defaultKey, deviceId, ipAddress, g.InGameName, deviceAuth)
user, err := db.LoginUserToGPCM(userId, gsbrCode, profileId, defaultKey, deviceId, ipAddress, g.InGameName, deviceAuth)
g.User = user
if err != nil {

View File

@@ -1,9 +1,7 @@
package gpcm
import (
"context"
"encoding/gob"
"fmt"
"os"
"strings"
"wwfc/common"
@@ -11,12 +9,11 @@ import (
"wwfc/logging"
"wwfc/qr2"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/linkdata/deadlock"
"github.com/logrusorgru/aurora/v3"
)
var ServerName = "gpcm"
const ServerName = "gpcm"
type GameSpySession struct {
ConnIndex uint64
@@ -60,8 +57,8 @@ type GameSpySession struct {
}
var (
ctx = context.Background()
pool *pgxpool.Pool
db database.Connection
// I would use a sync.Map instead of the map mutex combo, but this performs better.
sessions = map[uint32]*GameSpySession{}
sessionsByConnIndex = map[uint64]*GameSpySession{}
@@ -77,18 +74,8 @@ func StartServer(reload bool) {
config := common.GetConfig()
// Start SQL
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
dbConf, err := pgxpool.ParseConfig(dbString)
if err != nil {
panic(err)
}
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
if err != nil {
panic(err)
}
database.UpdateTables(pool, ctx)
db = database.Start(config)
db.UpdateTables()
allowDefaultDolphinKeys = config.AllowDefaultDolphinKeys
@@ -108,7 +95,11 @@ func Shutdown() {
if err != nil {
logging.Error("GPCM", "Failed to save state:", err)
}
db.Close()
logging.Notice("GPCM", "Saved", aurora.Cyan(len(sessions)), "sessions")
}
func CloseConnection(index uint64) {

View File

@@ -30,7 +30,7 @@ func (g *GameSpySession) getProfile(command common.GameSpyCommand) {
mutex.Unlock()
} else {
mutex.Unlock()
user, ok = database.GetProfile(pool, ctx, uint32(profileId))
user, ok = db.GetProfile(uint32(profileId))
if !ok {
// The profile info was requested on is invalid.
g.replyError(ErrGetProfileBadProfile)
@@ -91,7 +91,7 @@ func (g *GameSpySession) updateProfile(command common.GameSpyCommand) {
}
}
g.User.UpdateProfile(pool, ctx, command.OtherValues)
db.UpdateProfile(&g.User, command.OtherValues)
}
func VerifyPlayerSearch(profileId uint32, sessionKey int32, gameName string) (string, bool) {

View File

@@ -1,20 +1,17 @@
package race
import (
"context"
"fmt"
"net/http"
"strings"
"wwfc/common"
"wwfc/database"
"wwfc/logging"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
)
var (
ctx = context.Background()
pool *pgxpool.Pool
db database.Connection
)
func StartServer(reload bool) {
@@ -24,19 +21,11 @@ func StartServer(reload bool) {
common.ReadGameList()
// Start SQL
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
dbConf, err := pgxpool.ParseConfig(dbString)
if err != nil {
panic(err)
}
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
if err != nil {
panic(err)
}
db = database.Start(config)
}
func Shutdown() {
db.Close()
}
func HandleRequest(responseWriter http.ResponseWriter, request *http.Request) {

View File

@@ -8,7 +8,6 @@ import (
"strconv"
"strings"
"wwfc/common"
"wwfc/database"
"wwfc/logging"
"github.com/logrusorgru/aurora/v3"
@@ -144,7 +143,7 @@ func handleGetTopTenRankingsRequest(moduleName string, responseWriter http.Respo
return
}
topTenRankings, err := database.GetMarioKartWiiTopTenRankings(pool, ctx, regionId, courseId)
topTenRankings, err := db.GetMarioKartWiiTopTenRankings(regionId, courseId)
if err != nil {
logging.Error(moduleName, "Failed to get the Top 10 rankings:", err)
writeErrorResponse(raceServiceResultDatabaseError, responseWriter)

View File

@@ -1,20 +1,17 @@
package sake
import (
"context"
"fmt"
"net/http"
"strings"
"wwfc/common"
"wwfc/database"
"wwfc/logging"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
)
var (
ctx = context.Background()
pool *pgxpool.Pool
db database.Connection
)
func StartServer(reload bool) {
@@ -24,19 +21,11 @@ func StartServer(reload bool) {
common.ReadGameList()
// Start SQL
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
dbConf, err := pgxpool.ParseConfig(dbString)
if err != nil {
panic(err)
}
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
if err != nil {
panic(err)
}
db = database.Start(config)
}
func Shutdown() {
db.Close()
}
func HandleRequest(w http.ResponseWriter, r *http.Request) {

View File

@@ -85,7 +85,7 @@ func getMarioKartWiiGhostDataRecord(moduleName string, request StorageRequestCom
return []database.SakeRecord{}, false
}
fileId, err := database.GetMarioKartWiiGhostData(pool, ctx, courseId, time)
fileId, err := db.GetMarioKartWiiGhostData(courseId, time)
if err != nil {
logging.Error(moduleName, "mariokartwii/GhostData: Failed to get the ghost data from the database:", err)
return []database.SakeRecord{}, false
@@ -157,7 +157,7 @@ func getMarioKartWiiStoredGhostDataRecord(moduleName string, request StorageRequ
regionId = common.Worldwide
}
pid, fileId, err := database.GetMarioKartWiiStoredGhostData(pool, ctx, regionId, courseId)
pid, fileId, err := db.GetMarioKartWiiStoredGhostData(regionId, courseId)
if err != nil {
logging.Error(moduleName, "mariokartwii/StoredGhostData: Failed to get the stored ghost data from the database:", err)
return []database.SakeRecord{}, false
@@ -200,7 +200,7 @@ func handleMarioKartWiiFileDownloadRequest(moduleName string, responseWriter htt
return
}
file, err := database.GetMarioKartWiiFile(pool, ctx, fileId)
file, err := db.GetMarioKartWiiFile(fileId)
if err != nil {
logging.Error(moduleName, "Failed to get the file from the database:", err)
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultServerError))
@@ -259,7 +259,7 @@ func handleMarioKartWiiGhostDownloadRequest(moduleName string, responseWriter ht
return
}
ghost, err := database.GetMarioKartWiiGhostFile(pool, ctx, courseId, time, pid)
ghost, err := db.GetMarioKartWiiGhostFile(courseId, time, pid)
if err != nil {
logging.Error(moduleName, "Failed to get a ghost file from the database:", err)
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultServerError))
@@ -395,7 +395,7 @@ func handleMarioKartWiiGhostUploadRequest(moduleName string, responseWriter http
ghostFile = nil
}
err = database.InsertMarioKartWiiGhostFile(pool, ctx, regionId, courseId, score, pid, playerInfo, []byte(ghostData))
err = db.InsertMarioKartWiiGhostFile(regionId, courseId, score, pid, playerInfo, []byte(ghostData))
if err != nil {
logging.Error(moduleName, "Failed to insert the ghost file into the database:", err)
responseWriter.Header().Set(SakeFileResultHeader, strconv.Itoa(SakeFileResultServerError))

View File

@@ -261,7 +261,7 @@ func getRequestIdentity(moduleName string, request StorageRequestCommon) (uint32
}
func createRecord(moduleName string, profileId uint32, gameInfo common.GameInfo, request StorageRequestCommon) StorageResponseBody {
if reached, err := database.IsMaxSakeRecordsReached(pool, ctx, profileId, MaxSakeRecordsPerProfile); err != nil {
if reached, err := db.IsMaxSakeRecordsReached(profileId, MaxSakeRecordsPerProfile); err != nil {
logging.Error(moduleName, "Failed to check max sake records:", err)
return StorageResponseBody{CreateRecordResponse: &CreateRecordResponse{
CreateRecordResult: ResultDatabaseUnavailable,
@@ -311,7 +311,7 @@ func createRecord(moduleName string, profileId uint32, gameInfo common.GameInfo,
record.OwnerId = int32(profileId)
// TODO: Limit number of records or fields a user can have
recordId, err := database.InsertSakeRecord(pool, ctx, record)
recordId, err := db.InsertSakeRecord(record)
if err != nil {
logging.Error(moduleName, "Failed to insert sake record into the database:", err)
return StorageResponseBody{CreateRecordResponse: &CreateRecordResponse{
@@ -349,7 +349,7 @@ func getMyRecords(moduleName string, profileId uint32, gameInfo common.GameInfo,
}}
}
records, err := database.GetSakeRecords(pool, ctx, gameInfo.GameID, []int32{int32(profileId)}, request.TableID, nil, request.Fields.String, request.Filter)
records, err := db.GetSakeRecords(gameInfo.GameID, []int32{int32(profileId)}, request.TableID, nil, request.Fields.String, request.Filter)
if err != nil {
logging.Error(moduleName, "Failed to get sake records from the database:", err)
if err == pgx.ErrNoRows {
@@ -410,7 +410,7 @@ func updateRecord(moduleName string, profileId uint32, gameInfo common.GameInfo,
record.RecordId = int32(request.RecordID)
record.OwnerId = int32(profileId)
err := database.UpdateSakeRecord(pool, ctx, record, int32(profileId))
err := db.UpdateSakeRecord(record, int32(profileId))
if err != nil {
logging.Error(moduleName, "Failed to update sake record in the database:", err)
if err == database.ErrSakeNotOwned {
@@ -467,7 +467,7 @@ func searchForRecords(moduleName string, profileId uint32, gameInfo common.GameI
}
var err error
records, err = database.GetSakeRecords(pool, ctx, gameInfo.GameID, ownerIds, request.TableID, nil, request.Fields.String, request.Filter)
records, err = db.GetSakeRecords(gameInfo.GameID, ownerIds, request.TableID, nil, request.Fields.String, request.Filter)
if err != nil {
logging.Error(moduleName, "Failed to get sake records from the database:", err)
return StorageResponseBody{SearchForRecordsResponse: &SearchForRecordsResponse{