feat: add SQLite support, setup wizard enhancements, and live dashboard

Add zero-dependency SQLite mode so users can run Erupe without
PostgreSQL. A transparent db.DB wrapper auto-translates PostgreSQL
SQL ($N placeholders, now(), ::casts, ILIKE, public. prefix,
TRUNCATE) for SQLite at runtime — all 28 repo files use the wrapper
with no per-query changes needed.

Setup wizard gains two new steps: quest file detection with download
link, and gameplay presets (solo/small/community/rebalanced). The API
server gets a /dashboard endpoint with auto-refreshing stats.

CI release workflow now builds and pushes Docker images to GHCR
alongside binary artifacts on tag push.

Key changes:
- common/db: DB/Tx wrapper with 6 SQL translation rules
- server/migrations/sqlite: full SQLite schema (0001-0005)
- config: Database.Driver field ("postgres" or "sqlite")
- main.go: SQLite connection with WAL mode, single writer
- server/setup: quest check + preset selection steps
- server/api: /dashboard with live stats
- .github/workflows: Docker in release, deduplicate docker.yml
This commit is contained in:
Houmgaor
2026-03-05 18:00:30 +01:00
parent 03adb21e99
commit ecfe58ffb4
86 changed files with 2326 additions and 356 deletions

View File

@@ -170,8 +170,8 @@ func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket
if err == nil {
if itemType == 17 {
if err := addPointNetcafe(s, int(quantity)); err != nil {
s.logger.Error("Failed to add cafe bonus netcafe points", zap.Error(err))
}
s.logger.Error("Failed to add cafe bonus netcafe points", zap.Error(err))
}
}
}
if err := s.server.cafeRepo.AcceptBonus(cbID, s.charID); err != nil {

View File

@@ -1,10 +1,10 @@
package channelserver
import (
cfg "erupe-ce/config"
"erupe-ce/common/mhfcourse"
"erupe-ce/network/mhfpacket"
"errors"
"erupe-ce/common/mhfcourse"
cfg "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"testing"
"time"
)

View File

@@ -140,8 +140,8 @@ func handleMsgMhfAcquireDistItem(s *Session, p mhfpacket.MHFPacket) {
switch item.ItemType {
case 17:
if err := addPointNetcafe(s, int(item.Quantity)); err != nil {
s.logger.Error("Failed to add dist item netcafe points", zap.Error(err))
}
s.logger.Error("Failed to add dist item netcafe points", zap.Error(err))
}
case 19:
if err := s.server.userRepo.AddPremiumCoins(s.userID, item.Quantity); err != nil {
s.logger.Error("Failed to update gacha premium", zap.Error(err))

View File

@@ -3,9 +3,9 @@ package channelserver
import (
"testing"
cfg "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"time"
cfg "erupe-ce/config"
)
func TestHandleMsgMhfGetUdInfo(t *testing.T) {

View File

@@ -4,8 +4,8 @@ import (
"testing"
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
"erupe-ce/common/stringsupport"
"erupe-ce/network/mhfpacket"
)
// --- handleMsgMhfOperateGuild tests ---

View File

@@ -297,7 +297,7 @@ func TestPostGuildScout_Success(t *testing.T) {
func TestPostGuildScout_AlreadyInvited(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, Recruiter: true},
membership: &GuildMember{GuildID: 10, Recruiter: true},
createAppErr: ErrAlreadyInvited,
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}

View File

@@ -6,8 +6,8 @@ import (
"testing"
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
cfg "erupe-ce/config"
"erupe-ce/network/mhfpacket"
)
func TestHandleMsgMhfLoadLegendDispatch(t *testing.T) {

View File

@@ -156,13 +156,13 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
// |5|1|1|7|9|3|0|0|0|0|0|null|
// |8|1|100|7|1|4|1000|0|0|0|0|null|
// |9|1|100|7|2|5|9000|0|0|0|0|null|
bf.WriteUint8(ge.EntryType)
bf.WriteUint32(ge.ID)
bf.WriteUint8(ge.ItemType)
bf.WriteUint32(ge.ItemNumber)
bf.WriteUint16(ge.ItemQuantity)
bf.WriteUint8(ge.EntryType)
bf.WriteUint32(ge.ID)
bf.WriteUint8(ge.ItemType)
bf.WriteUint32(ge.ItemNumber)
bf.WriteUint16(ge.ItemQuantity)
var weightPr uint16
if gachaType >= 4 { // If box
if gachaType >= 4 { // If box
weightPr = 1
} else {
weightPr = uint16(ge.Weight / divisor)

View File

@@ -3,16 +3,16 @@ package channelserver
import (
"fmt"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// AchievementRepository centralizes all database access for the achievements table.
type AchievementRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewAchievementRepository creates a new AchievementRepository.
func NewAchievementRepository(db *sqlx.DB) *AchievementRepository {
func NewAchievementRepository(db *dbutil.DB) *AchievementRepository {
return &AchievementRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -11,7 +12,7 @@ func setupAchievementRepo(t *testing.T) (*AchievementRepository, *sqlx.DB, uint3
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "ach_test_user")
charID := CreateTestCharacter(t, db, userID, "AchChar")
repo := NewAchievementRepository(db)
repo := NewAchievementRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -1,16 +1,16 @@
package channelserver
import (
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// CafeRepository centralizes all database access for cafe-related tables.
type CafeRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewCafeRepository creates a new CafeRepository.
func NewCafeRepository(db *sqlx.DB) *CafeRepository {
func NewCafeRepository(db *dbutil.DB) *CafeRepository {
return &CafeRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -11,7 +12,7 @@ func setupCafeRepo(t *testing.T) (*CafeRepository, *sqlx.DB, uint32) {
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "cafe_test_user")
charID := CreateTestCharacter(t, db, userID, "CafeChar")
repo := NewCafeRepository(db)
repo := NewCafeRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -4,16 +4,16 @@ import (
"database/sql"
"time"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// CharacterRepository centralizes all database access for the characters table.
type CharacterRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewCharacterRepository creates a new CharacterRepository.
func NewCharacterRepository(db *sqlx.DB) *CharacterRepository {
func NewCharacterRepository(db *dbutil.DB) *CharacterRepository {
return &CharacterRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"time"
@@ -12,7 +13,7 @@ func setupCharRepo(t *testing.T) (*CharacterRepository, *sqlx.DB, uint32) {
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "repo_test_user")
charID := CreateTestCharacter(t, db, userID, "RepoChar")
repo := NewCharacterRepository(db)
repo := NewCharacterRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -1,17 +1,17 @@
package channelserver
import (
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// DistributionRepository centralizes all database access for the distribution,
// distribution_items, and distributions_accepted tables.
type DistributionRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewDistributionRepository creates a new DistributionRepository.
func NewDistributionRepository(db *sqlx.DB) *DistributionRepository {
func NewDistributionRepository(db *dbutil.DB) *DistributionRepository {
return &DistributionRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -11,7 +12,7 @@ func setupDistributionRepo(t *testing.T) (*DistributionRepository, *sqlx.DB, uin
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "dist_test_user")
charID := CreateTestCharacter(t, db, userID, "DistChar")
repo := NewDistributionRepository(db)
repo := NewDistributionRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -1,16 +1,16 @@
package channelserver
import (
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// DivaRepository centralizes all database access for diva defense events.
type DivaRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewDivaRepository creates a new DivaRepository.
func NewDivaRepository(db *sqlx.DB) *DivaRepository {
func NewDivaRepository(db *dbutil.DB) *DivaRepository {
return &DivaRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -9,7 +10,7 @@ import (
func setupDivaRepo(t *testing.T) (*DivaRepository, *sqlx.DB) {
t.Helper()
db := SetupTestDB(t)
repo := NewDivaRepository(db)
repo := NewDivaRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"time"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// EventQuest represents a row from the event_quests table.
@@ -22,11 +22,11 @@ type EventQuest struct {
// EventRepository centralizes all database access for event-related tables.
type EventRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewEventRepository creates a new EventRepository.
func NewEventRepository(db *sqlx.DB) *EventRepository {
func NewEventRepository(db *dbutil.DB) *EventRepository {
return &EventRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"time"
@@ -10,7 +11,7 @@ import (
func setupEventRepo(t *testing.T) (*EventRepository, *sqlx.DB) {
t.Helper()
db := SetupTestDB(t)
repo := NewEventRepository(db)
repo := NewEventRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db
}

View File

@@ -4,17 +4,17 @@ import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// FestaRepository centralizes all database access for festa-related tables
// (events, festa_registrations, festa_submissions, festa_prizes, festa_prizes_accepted, festa_trials, guild_characters).
type FestaRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewFestaRepository creates a new FestaRepository.
func NewFestaRepository(db *sqlx.DB) *FestaRepository {
func NewFestaRepository(db *dbutil.DB) *FestaRepository {
return &FestaRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"time"
@@ -13,7 +14,7 @@ func setupFestaRepo(t *testing.T) (*FestaRepository, *sqlx.DB, uint32, uint32) {
userID := CreateTestUser(t, db, "festa_test_user")
charID := CreateTestCharacter(t, db, userID, "FestaChar")
guildID := CreateTestGuild(t, db, charID, "FestaGuild")
repo := NewFestaRepository(db)
repo := NewFestaRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID, guildID
}

View File

@@ -5,17 +5,17 @@ import (
"errors"
"time"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// GachaRepository centralizes all database access for gacha-related tables
// (gacha_shop, gacha_entries, gacha_items, gacha_stepup, gacha_box).
type GachaRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewGachaRepository creates a new GachaRepository.
func NewGachaRepository(db *sqlx.DB) *GachaRepository {
func NewGachaRepository(db *dbutil.DB) *GachaRepository {
return &GachaRepository{db: db}
}

View File

@@ -3,6 +3,7 @@ package channelserver
import (
"database/sql"
"errors"
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -13,7 +14,7 @@ func setupGachaRepo(t *testing.T) (*GachaRepository, *sqlx.DB, uint32) {
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "gacha_test_user")
charID := CreateTestCharacter(t, db, userID, "GachaChar")
repo := NewGachaRepository(db)
repo := NewGachaRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -3,16 +3,16 @@ package channelserver
import (
"fmt"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// GoocooRepository centralizes all database access for the goocoo table.
type GoocooRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewGoocooRepository creates a new GoocooRepository.
func NewGoocooRepository(db *sqlx.DB) *GoocooRepository {
func NewGoocooRepository(db *dbutil.DB) *GoocooRepository {
return &GoocooRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -11,7 +12,7 @@ func setupGoocooRepo(t *testing.T) (*GoocooRepository, *sqlx.DB, uint32) {
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "goocoo_test_user")
charID := CreateTestCharacter(t, db, userID, "GoocooChar")
repo := NewGoocooRepository(db)
repo := NewGoocooRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -6,17 +6,19 @@ import (
"errors"
"fmt"
dbutil "erupe-ce/common/db"
"github.com/jmoiron/sqlx"
)
// GuildRepository centralizes all database access for guild-related tables
// (guilds, guild_characters, guild_applications).
type GuildRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewGuildRepository creates a new GuildRepository.
func NewGuildRepository(db *sqlx.DB) *GuildRepository {
func NewGuildRepository(db *dbutil.DB) *GuildRepository {
return &GuildRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"fmt"
"testing"
"time"
@@ -13,7 +14,7 @@ func setupGuildRepo(t *testing.T) (*GuildRepository, *sqlx.DB, uint32, uint32) {
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "guild_test_user")
charID := CreateTestCharacter(t, db, userID, "GuildLeader")
repo := NewGuildRepository(db)
repo := NewGuildRepository(dbutil.Wrap(db))
guildID := CreateTestGuild(t, db, charID, "TestGuild")
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, guildID, charID
@@ -82,7 +83,7 @@ func TestGetByCharIDNotFound(t *testing.T) {
func TestCreate(t *testing.T) {
db := SetupTestDB(t)
defer TeardownTestDB(t, db)
repo := NewGuildRepository(db)
repo := NewGuildRepository(dbutil.Wrap(db))
userID := CreateTestUser(t, db, "create_guild_user")
charID := CreateTestCharacter(t, db, userID, "CreateLeader")

View File

@@ -3,17 +3,17 @@ package channelserver
import (
"fmt"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// HouseRepository centralizes all database access for house-related tables
// (user_binary house columns, warehouse, titles).
type HouseRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewHouseRepository creates a new HouseRepository.
func NewHouseRepository(db *sqlx.DB) *HouseRepository {
func NewHouseRepository(db *dbutil.DB) *HouseRepository {
return &HouseRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -12,7 +13,7 @@ func setupHouseRepo(t *testing.T) (*HouseRepository, *sqlx.DB, uint32) {
userID := CreateTestUser(t, db, "house_test_user")
charID := CreateTestCharacter(t, db, userID, "HouseChar")
CreateTestUserBinary(t, db, charID)
repo := NewHouseRepository(db)
repo := NewHouseRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -1,16 +1,16 @@
package channelserver
import (
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// MailRepository centralizes all database access for the mail table.
type MailRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewMailRepository creates a new MailRepository.
func NewMailRepository(db *sqlx.DB) *MailRepository {
func NewMailRepository(db *dbutil.DB) *MailRepository {
return &MailRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -13,7 +14,7 @@ func setupMailRepo(t *testing.T) (*MailRepository, *sqlx.DB, uint32, uint32) {
senderID := CreateTestCharacter(t, db, userID, "Sender")
userID2 := CreateTestUser(t, db, "mail_recipient")
recipientID := CreateTestCharacter(t, db, userID2, "Recipient")
repo := NewMailRepository(db)
repo := NewMailRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, senderID, recipientID
}

View File

@@ -4,16 +4,16 @@ import (
"fmt"
"time"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// MercenaryRepository centralizes database access for mercenary/rasta/airou sequences and queries.
type MercenaryRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewMercenaryRepository creates a new MercenaryRepository.
func NewMercenaryRepository(db *sqlx.DB) *MercenaryRepository {
func NewMercenaryRepository(db *dbutil.DB) *MercenaryRepository {
return &MercenaryRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -12,7 +13,7 @@ func setupMercenaryRepo(t *testing.T) (*MercenaryRepository, *sqlx.DB, uint32, u
userID := CreateTestUser(t, db, "merc_test_user")
charID := CreateTestCharacter(t, db, userID, "MercChar")
guildID := CreateTestGuild(t, db, charID, "MercGuild")
repo := NewMercenaryRepository(db)
repo := NewMercenaryRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID, guildID
}

View File

@@ -3,16 +3,16 @@ package channelserver
import (
"fmt"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// MiscRepository centralizes database access for miscellaneous game tables.
type MiscRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewMiscRepository creates a new MiscRepository.
func NewMiscRepository(db *sqlx.DB) *MiscRepository {
func NewMiscRepository(db *dbutil.DB) *MiscRepository {
return &MiscRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -9,7 +10,7 @@ import (
func setupMiscRepo(t *testing.T) (*MiscRepository, *sqlx.DB) {
t.Helper()
db := SetupTestDB(t)
repo := NewMiscRepository(db)
repo := NewMiscRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db
}

View File

@@ -304,15 +304,15 @@ type mockGuildRepo struct {
deletedPostID uint32
// Alliance
alliance *GuildAlliance
getAllianceErr error
createAllianceErr error
deleteAllianceErr error
removeAllyErr error
setAllianceRecruitErr error
deletedAllianceID uint32
removedAllyArgs []uint32
allianceRecruitingSet *bool
alliance *GuildAlliance
getAllianceErr error
createAllianceErr error
deleteAllianceErr error
removeAllyErr error
setAllianceRecruitErr error
deletedAllianceID uint32
removedAllyArgs []uint32
allianceRecruitingSet *bool
// Cooking
meals []*GuildMeal
@@ -552,28 +552,28 @@ func (m *mockGuildRepo) Create(_ uint32, _ string) (int32, error) { return 0, ni
func (m *mockGuildRepo) CreateApplicationWithMail(_, _, _ uint32, _ GuildApplicationType, _, _ uint32, _, _ string) error {
return nil
}
func (m *mockGuildRepo) CancelInvitation(_, _ uint32) error { return nil }
func (m *mockGuildRepo) ArrangeCharacters(_ []uint32) error { return nil }
func (m *mockGuildRepo) GetItemBox(_ uint32) ([]byte, error) { return nil, nil }
func (m *mockGuildRepo) SaveItemBox(_ uint32, _ []byte) error { return nil }
func (m *mockGuildRepo) SetRecruiting(_ uint32, _ bool) error { return nil }
func (m *mockGuildRepo) SetPugiOutfits(_ uint32, _ uint32) error { return nil }
func (m *mockGuildRepo) SetRecruiter(_ uint32, _ bool) error { return nil }
func (m *mockGuildRepo) AddMemberDailyRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) ExchangeEventRP(_ uint32, _ uint16) (uint32, error) { return 0, nil }
func (m *mockGuildRepo) AddRankRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) AddEventRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) GetRoomRP(_ uint32) (uint16, error) { return 0, nil }
func (m *mockGuildRepo) SetRoomRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) AddRoomRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) SetRoomExpiry(_ uint32, _ time.Time) error { return nil }
func (m *mockGuildRepo) UpdatePost(_ uint32, _, _ string) error { return nil }
func (m *mockGuildRepo) UpdatePostStamp(_, _ uint32) error { return nil }
func (m *mockGuildRepo) GetPostLikedBy(_ uint32) (string, error) { return "", nil }
func (m *mockGuildRepo) SetPostLikedBy(_ uint32, _ string) error { return nil }
func (m *mockGuildRepo) CountNewPosts(_ uint32, _ time.Time) (int, error) { return 0, nil }
func (m *mockGuildRepo) ListAlliances() ([]*GuildAlliance, error) { return nil, nil }
func (m *mockGuildRepo) ClearTreasureHunt(_ uint32) error { return nil }
func (m *mockGuildRepo) CancelInvitation(_, _ uint32) error { return nil }
func (m *mockGuildRepo) ArrangeCharacters(_ []uint32) error { return nil }
func (m *mockGuildRepo) GetItemBox(_ uint32) ([]byte, error) { return nil, nil }
func (m *mockGuildRepo) SaveItemBox(_ uint32, _ []byte) error { return nil }
func (m *mockGuildRepo) SetRecruiting(_ uint32, _ bool) error { return nil }
func (m *mockGuildRepo) SetPugiOutfits(_ uint32, _ uint32) error { return nil }
func (m *mockGuildRepo) SetRecruiter(_ uint32, _ bool) error { return nil }
func (m *mockGuildRepo) AddMemberDailyRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) ExchangeEventRP(_ uint32, _ uint16) (uint32, error) { return 0, nil }
func (m *mockGuildRepo) AddRankRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) AddEventRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) GetRoomRP(_ uint32) (uint16, error) { return 0, nil }
func (m *mockGuildRepo) SetRoomRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) AddRoomRP(_ uint32, _ uint16) error { return nil }
func (m *mockGuildRepo) SetRoomExpiry(_ uint32, _ time.Time) error { return nil }
func (m *mockGuildRepo) UpdatePost(_ uint32, _, _ string) error { return nil }
func (m *mockGuildRepo) UpdatePostStamp(_, _ uint32) error { return nil }
func (m *mockGuildRepo) GetPostLikedBy(_ uint32) (string, error) { return "", nil }
func (m *mockGuildRepo) SetPostLikedBy(_ uint32, _ string) error { return nil }
func (m *mockGuildRepo) CountNewPosts(_ uint32, _ time.Time) (int, error) { return 0, nil }
func (m *mockGuildRepo) ListAlliances() ([]*GuildAlliance, error) { return nil, nil }
func (m *mockGuildRepo) ClearTreasureHunt(_ uint32) error { return nil }
func (m *mockGuildRepo) InsertKillLog(_ uint32, _ int, _ uint8, _ time.Time) error { return nil }
func (m *mockGuildRepo) ListInvitedCharacters(_ uint32) ([]*ScoutedCharacter, error) {
return nil, nil
@@ -816,7 +816,6 @@ type mockGachaRepo struct {
allEntries []GachaEntry
allEntriesErr error
weightDivisor float64
}
func (m *mockGachaRepo) GetEntryForTransaction(_ uint32, _ uint8) (uint8, uint16, int, error) {
@@ -957,26 +956,26 @@ type mockTowerRepo struct {
gemsErr error
updatedGems string
progress TenrouiraiProgressData
progressErr error
scores []TenrouiraiCharScore
scoresErr error
guildRP uint32
guildRPErr error
page int
donated int
pageRPErr error
advanceErr error
advanceCalled bool
donateErr error
donatedRP uint16
progress TenrouiraiProgressData
progressErr error
scores []TenrouiraiCharScore
scoresErr error
guildRP uint32
guildRPErr error
page int
donated int
pageRPErr error
advanceErr error
advanceCalled bool
donateErr error
donatedRP uint16
}
func (m *mockTowerRepo) GetTowerData(_ uint32) (TowerData, error) { return m.towerData, m.towerDataErr }
func (m *mockTowerRepo) GetSkills(_ uint32) (string, error) { return m.skills, m.skillsErr }
func (m *mockTowerRepo) UpdateSkills(_ uint32, _ string, _ int32) error { return nil }
func (m *mockTowerRepo) GetTowerData(_ uint32) (TowerData, error) { return m.towerData, m.towerDataErr }
func (m *mockTowerRepo) GetSkills(_ uint32) (string, error) { return m.skills, m.skillsErr }
func (m *mockTowerRepo) UpdateSkills(_ uint32, _ string, _ int32) error { return nil }
func (m *mockTowerRepo) UpdateProgress(_ uint32, _, _, _, _ int32) error { return nil }
func (m *mockTowerRepo) GetGems(_ uint32) (string, error) { return m.gems, m.gemsErr }
func (m *mockTowerRepo) GetGems(_ uint32) (string, error) { return m.gems, m.gemsErr }
func (m *mockTowerRepo) UpdateGems(_ uint32, gems string) error {
m.updatedGems = gems
return nil
@@ -1003,21 +1002,21 @@ func (m *mockTowerRepo) DonateGuildTowerRP(_ uint32, rp uint16) error {
// --- mockFestaRepo ---
type mockFestaRepo struct {
events []FestaEvent
eventsErr error
teamSouls uint32
teamErr error
trials []FestaTrial
trialsErr error
topGuild FestaGuildRanking
topErr error
topWindow FestaGuildRanking
topWinErr error
charSouls uint32
charErr error
hasClaimed bool
prizes []Prize
prizesErr error
events []FestaEvent
eventsErr error
teamSouls uint32
teamErr error
trials []FestaTrial
trialsErr error
topGuild FestaGuildRanking
topErr error
topWindow FestaGuildRanking
topWinErr error
charSouls uint32
charErr error
hasClaimed bool
prizes []Prize
prizesErr error
cleanupErr error
cleanupCalled bool
@@ -1035,8 +1034,8 @@ func (m *mockFestaRepo) InsertEvent(start uint32) error {
m.insertedStart = start
return m.insertErr
}
func (m *mockFestaRepo) GetFestaEvents() ([]FestaEvent, error) { return m.events, m.eventsErr }
func (m *mockFestaRepo) GetTeamSouls(_ string) (uint32, error) { return m.teamSouls, m.teamErr }
func (m *mockFestaRepo) GetFestaEvents() ([]FestaEvent, error) { return m.events, m.eventsErr }
func (m *mockFestaRepo) GetTeamSouls(_ string) (uint32, error) { return m.teamSouls, m.teamErr }
func (m *mockFestaRepo) GetTrialsWithMonopoly() ([]FestaTrial, error) {
return m.trials, m.trialsErr
}
@@ -1046,15 +1045,15 @@ func (m *mockFestaRepo) GetTopGuildForTrial(_ uint16) (FestaGuildRanking, error)
func (m *mockFestaRepo) GetTopGuildInWindow(_, _ uint32) (FestaGuildRanking, error) {
return m.topWindow, m.topWinErr
}
func (m *mockFestaRepo) GetCharSouls(_ uint32) (uint32, error) { return m.charSouls, m.charErr }
func (m *mockFestaRepo) HasClaimedMainPrize(_ uint32) bool { return m.hasClaimed }
func (m *mockFestaRepo) VoteTrial(_ uint32, _ uint32) error { return nil }
func (m *mockFestaRepo) RegisterGuild(_ uint32, _ string) error { return nil }
func (m *mockFestaRepo) GetCharSouls(_ uint32) (uint32, error) { return m.charSouls, m.charErr }
func (m *mockFestaRepo) HasClaimedMainPrize(_ uint32) bool { return m.hasClaimed }
func (m *mockFestaRepo) VoteTrial(_ uint32, _ uint32) error { return nil }
func (m *mockFestaRepo) RegisterGuild(_ uint32, _ string) error { return nil }
func (m *mockFestaRepo) SubmitSouls(_, _ uint32, souls []uint16) error {
m.submittedSouls = souls
return m.submitErr
}
func (m *mockFestaRepo) ClaimPrize(_ uint32, _ uint32) error { return nil }
func (m *mockFestaRepo) ClaimPrize(_ uint32, _ uint32) error { return nil }
func (m *mockFestaRepo) ListPrizes(_ uint32, _ string) ([]Prize, error) {
return m.prizes, m.prizesErr
}
@@ -1078,9 +1077,9 @@ type mockDivaRepo struct {
eventsErr error
}
func (m *mockDivaRepo) DeleteEvents() error { return nil }
func (m *mockDivaRepo) InsertEvent(_ uint32) error { return nil }
func (m *mockDivaRepo) GetEvents() ([]DivaEvent, error) { return m.events, m.eventsErr }
func (m *mockDivaRepo) DeleteEvents() error { return nil }
func (m *mockDivaRepo) InsertEvent(_ uint32) error { return nil }
func (m *mockDivaRepo) GetEvents() ([]DivaEvent, error) { return m.events, m.eventsErr }
// --- mockEventRepo ---
@@ -1122,20 +1121,20 @@ func (m *mockMiscRepo) UpsertTrendWeapon(_ uint16, _ uint8) error { return nil }
// --- mockMercenaryRepo ---
type mockMercenaryRepo struct {
nextRastaID uint32
rastaIDErr error
nextAirouID uint32
airouIDErr error
loans []MercenaryLoan
loansErr error
catUsages []GuildHuntCatUsage
catUsagesErr error
guildAirou [][]byte
nextRastaID uint32
rastaIDErr error
nextAirouID uint32
airouIDErr error
loans []MercenaryLoan
loansErr error
catUsages []GuildHuntCatUsage
catUsagesErr error
guildAirou [][]byte
guildAirouErr error
}
func (m *mockMercenaryRepo) NextRastaID() (uint32, error) { return m.nextRastaID, m.rastaIDErr }
func (m *mockMercenaryRepo) NextAirouID() (uint32, error) { return m.nextAirouID, m.airouIDErr }
func (m *mockMercenaryRepo) NextRastaID() (uint32, error) { return m.nextRastaID, m.rastaIDErr }
func (m *mockMercenaryRepo) NextAirouID() (uint32, error) { return m.nextAirouID, m.airouIDErr }
func (m *mockMercenaryRepo) GetMercenaryLoans(_ uint32) ([]MercenaryLoan, error) {
return m.loans, m.loansErr
}
@@ -1149,17 +1148,17 @@ func (m *mockMercenaryRepo) GetGuildAirou(_ uint32) ([][]byte, error) {
// --- mockCafeRepo ---
type mockCafeRepo struct {
bonuses []CafeBonus
bonusesErr error
claimable []CafeBonus
claimableErr error
bonuses []CafeBonus
bonusesErr error
claimable []CafeBonus
claimableErr error
bonusItemType uint32
bonusItemQty uint32
bonusItemErr error
}
func (m *mockCafeRepo) ResetAccepted(_ uint32) error { return nil }
func (m *mockCafeRepo) GetBonuses(_ uint32) ([]CafeBonus, error) { return m.bonuses, m.bonusesErr }
func (m *mockCafeRepo) ResetAccepted(_ uint32) error { return nil }
func (m *mockCafeRepo) GetBonuses(_ uint32) ([]CafeBonus, error) { return m.bonuses, m.bonusesErr }
func (m *mockCafeRepo) GetClaimable(_ uint32, _ int64) ([]CafeBonus, error) {
return m.claimable, m.claimableErr
}

View File

@@ -3,16 +3,16 @@ package channelserver
import (
"fmt"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// RengokuRepository centralizes all database access for the rengoku_score table.
type RengokuRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewRengokuRepository creates a new RengokuRepository.
func NewRengokuRepository(db *sqlx.DB) *RengokuRepository {
func NewRengokuRepository(db *dbutil.DB) *RengokuRepository {
return &RengokuRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -12,7 +13,7 @@ func setupRengokuRepo(t *testing.T) (*RengokuRepository, *sqlx.DB, uint32, uint3
userID := CreateTestUser(t, db, "rengoku_test_user")
charID := CreateTestCharacter(t, db, userID, "RengokuChar")
guildID := CreateTestGuild(t, db, charID, "RengokuGuild")
repo := NewRengokuRepository(db)
repo := NewRengokuRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID, guildID
}

View File

@@ -3,16 +3,16 @@ package channelserver
import (
"fmt"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// ScenarioRepository centralizes all database access for the scenario_counter table.
type ScenarioRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewScenarioRepository creates a new ScenarioRepository.
func NewScenarioRepository(db *sqlx.DB) *ScenarioRepository {
func NewScenarioRepository(db *dbutil.DB) *ScenarioRepository {
return &ScenarioRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -9,7 +10,7 @@ import (
func setupScenarioRepo(t *testing.T) (*ScenarioRepository, *sqlx.DB) {
t.Helper()
db := SetupTestDB(t)
repo := NewScenarioRepository(db)
repo := NewScenarioRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db
}

View File

@@ -1,16 +1,16 @@
package channelserver
import (
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// SessionRepository centralizes all database access for sign_sessions and servers tables.
type SessionRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewSessionRepository creates a new SessionRepository.
func NewSessionRepository(db *sqlx.DB) *SessionRepository {
func NewSessionRepository(db *dbutil.DB) *SessionRepository {
return &SessionRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -13,7 +14,7 @@ func setupSessionRepo(t *testing.T) (*SessionRepository, *sqlx.DB, uint32, uint3
charID := CreateTestCharacter(t, db, userID, "SessionChar")
token := "test_token_12345"
sessionID := CreateTestSignSession(t, db, userID, token)
repo := NewSessionRepository(db)
repo := NewSessionRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, userID, charID, sessionID, token
}

View File

@@ -1,16 +1,16 @@
package channelserver
import (
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// ShopRepository centralizes all database access for shop-related tables.
type ShopRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewShopRepository creates a new ShopRepository.
func NewShopRepository(db *sqlx.DB) *ShopRepository {
func NewShopRepository(db *dbutil.DB) *ShopRepository {
return &ShopRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -11,7 +12,7 @@ func setupShopRepo(t *testing.T) (*ShopRepository, *sqlx.DB, uint32) {
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "shop_test_user")
charID := CreateTestCharacter(t, db, userID, "ShopChar")
repo := NewShopRepository(db)
repo := NewShopRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -4,16 +4,16 @@ import (
"fmt"
"time"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// StampRepository centralizes all database access for the stamps table.
type StampRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewStampRepository creates a new StampRepository.
func NewStampRepository(db *sqlx.DB) *StampRepository {
func NewStampRepository(db *dbutil.DB) *StampRepository {
return &StampRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"time"
@@ -12,7 +13,7 @@ func setupStampRepo(t *testing.T) (*StampRepository, *sqlx.DB, uint32) {
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "stamp_test_user")
charID := CreateTestCharacter(t, db, userID, "StampChar")
repo := NewStampRepository(db)
repo := NewStampRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID
}

View File

@@ -3,17 +3,17 @@ package channelserver
import (
"fmt"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// TowerRepository centralizes all database access for tower-related tables
// (tower, guilds tower columns, guild_characters tower columns).
type TowerRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewTowerRepository creates a new TowerRepository.
func NewTowerRepository(db *sqlx.DB) *TowerRepository {
func NewTowerRepository(db *dbutil.DB) *TowerRepository {
return &TowerRepository{db: db}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
dbutil "erupe-ce/common/db"
"testing"
"github.com/jmoiron/sqlx"
@@ -17,7 +18,7 @@ func setupTowerRepo(t *testing.T) (*TowerRepository, *sqlx.DB, uint32, uint32) {
if _, err := db.Exec("INSERT INTO guild_characters (guild_id, character_id) VALUES ($1, $2)", guildID, charID); err != nil {
t.Fatalf("Failed to add char to guild: %v", err)
}
repo := NewTowerRepository(db)
repo := NewTowerRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, charID, guildID
}

View File

@@ -4,16 +4,16 @@ import (
"database/sql"
"time"
"github.com/jmoiron/sqlx"
dbutil "erupe-ce/common/db"
)
// UserRepository centralizes all database access for the users table.
type UserRepository struct {
db *sqlx.DB
db *dbutil.DB
}
// NewUserRepository creates a new UserRepository.
func NewUserRepository(db *sqlx.DB) *UserRepository {
func NewUserRepository(db *dbutil.DB) *UserRepository {
return &UserRepository{db: db}
}

View File

@@ -2,6 +2,7 @@ package channelserver
import (
"database/sql"
dbutil "erupe-ce/common/db"
"testing"
"time"
@@ -12,7 +13,7 @@ func setupUserRepo(t *testing.T) (*UserRepository, *sqlx.DB, uint32) {
t.Helper()
db := SetupTestDB(t)
userID := CreateTestUser(t, db, "user_repo_test")
repo := NewUserRepository(db)
repo := NewUserRepository(dbutil.Wrap(db))
t.Cleanup(func() { TeardownTestDB(t, db) })
return repo, db, userID
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
"time"
dbutil "erupe-ce/common/db"
"erupe-ce/common/mhfitem"
cfg "erupe-ce/config"
"erupe-ce/network/clientctx"
@@ -597,18 +598,19 @@ func createTestServerWithDB(t *testing.T, db *sqlx.DB) *Server {
server.logger = logger
// Initialize repositories
server.charRepo = NewCharacterRepository(db)
server.guildRepo = NewGuildRepository(db)
server.userRepo = NewUserRepository(db)
server.gachaRepo = NewGachaRepository(db)
server.houseRepo = NewHouseRepository(db)
server.festaRepo = NewFestaRepository(db)
server.towerRepo = NewTowerRepository(db)
server.rengokuRepo = NewRengokuRepository(db)
server.mailRepo = NewMailRepository(db)
server.stampRepo = NewStampRepository(db)
server.distRepo = NewDistributionRepository(db)
server.sessionRepo = NewSessionRepository(db)
wdb := dbutil.Wrap(db)
server.charRepo = NewCharacterRepository(wdb)
server.guildRepo = NewGuildRepository(wdb)
server.userRepo = NewUserRepository(wdb)
server.gachaRepo = NewGachaRepository(wdb)
server.houseRepo = NewHouseRepository(wdb)
server.festaRepo = NewFestaRepository(wdb)
server.towerRepo = NewTowerRepository(wdb)
server.rengokuRepo = NewRengokuRepository(wdb)
server.mailRepo = NewMailRepository(wdb)
server.stampRepo = NewStampRepository(wdb)
server.distRepo = NewDistributionRepository(wdb)
server.sessionRepo = NewSessionRepository(wdb)
return server
}

View File

@@ -11,6 +11,7 @@ import (
"time"
"erupe-ce/common/byteframe"
dbutil "erupe-ce/common/db"
cfg "erupe-ce/config"
"erupe-ce/network"
"erupe-ce/network/binpacket"
@@ -142,27 +143,28 @@ func NewServer(config *Config) *Server {
handlerTable: buildHandlerTable(),
}
s.charRepo = NewCharacterRepository(config.DB)
s.guildRepo = NewGuildRepository(config.DB)
s.userRepo = NewUserRepository(config.DB)
s.gachaRepo = NewGachaRepository(config.DB)
s.houseRepo = NewHouseRepository(config.DB)
s.festaRepo = NewFestaRepository(config.DB)
s.towerRepo = NewTowerRepository(config.DB)
s.rengokuRepo = NewRengokuRepository(config.DB)
s.mailRepo = NewMailRepository(config.DB)
s.stampRepo = NewStampRepository(config.DB)
s.distRepo = NewDistributionRepository(config.DB)
s.sessionRepo = NewSessionRepository(config.DB)
s.eventRepo = NewEventRepository(config.DB)
s.achievementRepo = NewAchievementRepository(config.DB)
s.shopRepo = NewShopRepository(config.DB)
s.cafeRepo = NewCafeRepository(config.DB)
s.goocooRepo = NewGoocooRepository(config.DB)
s.divaRepo = NewDivaRepository(config.DB)
s.miscRepo = NewMiscRepository(config.DB)
s.scenarioRepo = NewScenarioRepository(config.DB)
s.mercenaryRepo = NewMercenaryRepository(config.DB)
wdb := dbutil.Wrap(config.DB)
s.charRepo = NewCharacterRepository(wdb)
s.guildRepo = NewGuildRepository(wdb)
s.userRepo = NewUserRepository(wdb)
s.gachaRepo = NewGachaRepository(wdb)
s.houseRepo = NewHouseRepository(wdb)
s.festaRepo = NewFestaRepository(wdb)
s.towerRepo = NewTowerRepository(wdb)
s.rengokuRepo = NewRengokuRepository(wdb)
s.mailRepo = NewMailRepository(wdb)
s.stampRepo = NewStampRepository(wdb)
s.distRepo = NewDistributionRepository(wdb)
s.sessionRepo = NewSessionRepository(wdb)
s.eventRepo = NewEventRepository(wdb)
s.achievementRepo = NewAchievementRepository(wdb)
s.shopRepo = NewShopRepository(wdb)
s.cafeRepo = NewCafeRepository(wdb)
s.goocooRepo = NewGoocooRepository(wdb)
s.divaRepo = NewDivaRepository(wdb)
s.miscRepo = NewMiscRepository(wdb)
s.scenarioRepo = NewScenarioRepository(wdb)
s.mercenaryRepo = NewMercenaryRepository(wdb)
s.mailService = NewMailService(s.mailRepo, s.guildRepo, s.logger)
s.guildService = NewGuildService(s.guildRepo, s.mailService, s.charRepo, s.logger)

View File

@@ -8,6 +8,7 @@ import (
"testing"
"time"
dbutil "erupe-ce/common/db"
"erupe-ce/server/channelserver/compression/nullcomp"
"erupe-ce/server/migrations"
"github.com/jmoiron/sqlx"
@@ -330,25 +331,26 @@ func CreateTestGachaItem(t *testing.T, db *sqlx.DB, entryID uint32, itemType uin
// Use this in integration tests instead of setting s.server.db directly.
func SetTestDB(s *Server, db *sqlx.DB) {
s.db = db
s.charRepo = NewCharacterRepository(db)
s.guildRepo = NewGuildRepository(db)
s.userRepo = NewUserRepository(db)
s.gachaRepo = NewGachaRepository(db)
s.houseRepo = NewHouseRepository(db)
s.festaRepo = NewFestaRepository(db)
s.towerRepo = NewTowerRepository(db)
s.rengokuRepo = NewRengokuRepository(db)
s.mailRepo = NewMailRepository(db)
s.stampRepo = NewStampRepository(db)
s.distRepo = NewDistributionRepository(db)
s.sessionRepo = NewSessionRepository(db)
s.eventRepo = NewEventRepository(db)
s.achievementRepo = NewAchievementRepository(db)
s.shopRepo = NewShopRepository(db)
s.cafeRepo = NewCafeRepository(db)
s.goocooRepo = NewGoocooRepository(db)
s.divaRepo = NewDivaRepository(db)
s.miscRepo = NewMiscRepository(db)
s.scenarioRepo = NewScenarioRepository(db)
s.mercenaryRepo = NewMercenaryRepository(db)
wdb := dbutil.Wrap(db)
s.charRepo = NewCharacterRepository(wdb)
s.guildRepo = NewGuildRepository(wdb)
s.userRepo = NewUserRepository(wdb)
s.gachaRepo = NewGachaRepository(wdb)
s.houseRepo = NewHouseRepository(wdb)
s.festaRepo = NewFestaRepository(wdb)
s.towerRepo = NewTowerRepository(wdb)
s.rengokuRepo = NewRengokuRepository(wdb)
s.mailRepo = NewMailRepository(wdb)
s.stampRepo = NewStampRepository(wdb)
s.distRepo = NewDistributionRepository(wdb)
s.sessionRepo = NewSessionRepository(wdb)
s.eventRepo = NewEventRepository(wdb)
s.achievementRepo = NewAchievementRepository(wdb)
s.shopRepo = NewShopRepository(wdb)
s.cafeRepo = NewCafeRepository(wdb)
s.goocooRepo = NewGoocooRepository(wdb)
s.divaRepo = NewDivaRepository(wdb)
s.miscRepo = NewMiscRepository(wdb)
s.scenarioRepo = NewScenarioRepository(wdb)
s.mercenaryRepo = NewMercenaryRepository(wdb)
}