Files
Erupe/server/channelserver/repo_mail.go
Houmgaor ecfe58ffb4 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
2026-03-05 18:00:30 +01:00

123 lines
3.0 KiB
Go

package channelserver
import (
dbutil "erupe-ce/common/db"
)
// MailRepository centralizes all database access for the mail table.
type MailRepository struct {
db *dbutil.DB
}
// NewMailRepository creates a new MailRepository.
func NewMailRepository(db *dbutil.DB) *MailRepository {
return &MailRepository{db: db}
}
const mailInsertQuery = `
INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite, is_sys_message)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`
// SendMail inserts a new mail row.
func (r *MailRepository) SendMail(senderID, recipientID uint32, subject, body string, itemID, itemAmount uint16, isGuildInvite, isSystemMessage bool) error {
_, err := r.db.Exec(mailInsertQuery, senderID, recipientID, subject, body, itemID, itemAmount, isGuildInvite, isSystemMessage)
return err
}
// GetListForCharacter loads all non-deleted mail for a character (max 32).
func (r *MailRepository) GetListForCharacter(charID uint32) ([]Mail, error) {
rows, err := r.db.Queryx(`
SELECT
m.id,
m.sender_id,
m.recipient_id,
m.subject,
m.read,
m.attached_item_received,
m.attached_item,
m.attached_item_amount,
m.created_at,
m.is_guild_invite,
m.is_sys_message,
m.deleted,
m.locked,
c.name as sender_name
FROM mail m
JOIN characters c ON c.id = m.sender_id
WHERE recipient_id = $1 AND m.deleted = false
ORDER BY m.created_at DESC, id DESC
LIMIT 32
`, charID)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
var allMail []Mail
for rows.Next() {
var mail Mail
if err := rows.StructScan(&mail); err != nil {
return nil, err
}
allMail = append(allMail, mail)
}
return allMail, nil
}
// GetByID loads a single mail by ID.
func (r *MailRepository) GetByID(id int) (*Mail, error) {
row := r.db.QueryRowx(`
SELECT
m.id,
m.sender_id,
m.recipient_id,
m.subject,
m.read,
m.body,
m.attached_item_received,
m.attached_item,
m.attached_item_amount,
m.created_at,
m.is_guild_invite,
m.is_sys_message,
m.deleted,
m.locked,
c.name as sender_name
FROM mail m
JOIN characters c ON c.id = m.sender_id
WHERE m.id = $1
LIMIT 1
`, id)
mail := &Mail{}
if err := row.StructScan(mail); err != nil {
return nil, err
}
return mail, nil
}
// MarkRead marks a mail as read.
func (r *MailRepository) MarkRead(id int) error {
_, err := r.db.Exec(`UPDATE mail SET read = true WHERE id = $1`, id)
return err
}
// MarkDeleted marks a mail as deleted.
func (r *MailRepository) MarkDeleted(id int) error {
_, err := r.db.Exec(`UPDATE mail SET deleted = true WHERE id = $1`, id)
return err
}
// SetLocked sets the locked state of a mail.
func (r *MailRepository) SetLocked(id int, locked bool) error {
_, err := r.db.Exec(`UPDATE mail SET locked = $1 WHERE id = $2`, locked, id)
return err
}
// MarkItemReceived marks a mail's attached item as received.
func (r *MailRepository) MarkItemReceived(id int) error {
_, err := r.db.Exec(`UPDATE mail SET attached_item_received = TRUE WHERE id = $1`, id)
return err
}