mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
An MMO server without multiplayer defeats the purpose. PostgreSQL is the right choice and Docker Compose already solves the setup pain. This reverts the common/db wrapper, SQLite schema, config Driver field, modernc.org/sqlite dependency, and all repo type changes while keeping the dashboard, wizard, and CI improvements from the previous commit.
120 lines
3.6 KiB
Go
120 lines
3.6 KiB
Go
package signserver
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
// SignCharacterRepository implements SignCharacterRepo with PostgreSQL.
|
|
type SignCharacterRepository struct {
|
|
db *sqlx.DB
|
|
}
|
|
|
|
// NewSignCharacterRepository creates a new SignCharacterRepository.
|
|
func NewSignCharacterRepository(db *sqlx.DB) *SignCharacterRepository {
|
|
return &SignCharacterRepository{db: db}
|
|
}
|
|
|
|
func (r *SignCharacterRepository) CountNewCharacters(uid uint32) (int, error) {
|
|
var count int
|
|
err := r.db.QueryRow("SELECT COUNT(*) FROM characters WHERE user_id = $1 AND is_new_character = true", uid).Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
func (r *SignCharacterRepository) CreateCharacter(uid uint32, lastLogin uint32) error {
|
|
_, err := r.db.Exec(`
|
|
INSERT INTO characters (
|
|
user_id, is_female, is_new_character, name, unk_desc_string,
|
|
hr, gr, weapon_type, last_login)
|
|
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
|
|
uid, lastLogin,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (r *SignCharacterRepository) GetForUser(uid uint32) ([]character, error) {
|
|
characters := make([]character, 0)
|
|
err := r.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return characters, nil
|
|
}
|
|
|
|
func (r *SignCharacterRepository) IsNewCharacter(cid int) (bool, error) {
|
|
var isNew bool
|
|
err := r.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", cid).Scan(&isNew)
|
|
return isNew, err
|
|
}
|
|
|
|
func (r *SignCharacterRepository) HardDelete(cid int) error {
|
|
_, err := r.db.Exec("DELETE FROM characters WHERE id = $1", cid)
|
|
return err
|
|
}
|
|
|
|
func (r *SignCharacterRepository) SoftDelete(cid int) error {
|
|
_, err := r.db.Exec("UPDATE characters SET deleted = true WHERE id = $1", cid)
|
|
return err
|
|
}
|
|
|
|
// GetFriends returns friends for a character using parameterized queries
|
|
// (fixes the SQL injection vector from the original string-concatenated approach).
|
|
func (r *SignCharacterRepository) GetFriends(charID uint32) ([]members, error) {
|
|
var friendsCSV string
|
|
err := r.db.QueryRow("SELECT friends FROM characters WHERE id=$1", charID).Scan(&friendsCSV)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if friendsCSV == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
friendsSlice := strings.Split(friendsCSV, ",")
|
|
// Filter out empty strings
|
|
ids := make([]string, 0, len(friendsSlice))
|
|
for _, s := range friendsSlice {
|
|
s = strings.TrimSpace(s)
|
|
if s != "" {
|
|
ids = append(ids, s)
|
|
}
|
|
}
|
|
if len(ids) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// Use parameterized ANY($1) instead of string-concatenated WHERE id=X OR id=Y
|
|
friends := make([]members, 0)
|
|
err = r.db.Select(&friends, "SELECT id, name FROM characters WHERE id = ANY($1)", pq.Array(ids))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return friends, nil
|
|
}
|
|
|
|
// GetGuildmates returns guildmates for a character.
|
|
func (r *SignCharacterRepository) GetGuildmates(charID uint32) ([]members, error) {
|
|
var inGuild int
|
|
err := r.db.QueryRow("SELECT count(*) FROM guild_characters WHERE character_id=$1", charID).Scan(&inGuild)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if inGuild == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
var guildID int
|
|
err = r.db.QueryRow("SELECT guild_id FROM guild_characters WHERE character_id=$1", charID).Scan(&guildID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
guildmates := make([]members, 0)
|
|
err = r.db.Select(&guildmates, "SELECT character_id AS id, c.name FROM guild_characters gc JOIN characters c ON c.id = gc.character_id WHERE guild_id=$1 AND character_id!=$2", guildID, charID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return guildmates, nil
|
|
}
|