From bcdc4e0b7e8628d750f88f18ee2832092d0c2a31 Mon Sep 17 00:00:00 2001 From: Houmgaor Date: Mon, 23 Feb 2026 23:39:49 +0100 Subject: [PATCH] fix(setup): reduce friction in installation procedure - Add Language default ("jp") so missing field no longer produces empty string, and include language selector in setup wizard - Add --setup flag to re-run wizard even when config.json exists, providing a recovery path for corrupted configs - Auto-apply seed data on fresh databases so users who skip the wizard still get shops, events, and gacha - Fix stale docs referencing non-existent init/setup.sh and schemas/patch-schema/ in docker/README, CONTRIBUTING, and README --- CONTRIBUTING.md | 23 ++++++++--------------- README.md | 12 ++++-------- config/config.go | 1 + docker/README.md | 20 ++++++++------------ main.go | 24 ++++++++++++++++++++++++ server/setup/wizard.go | 6 ++++++ server/setup/wizard.html | 20 ++++++++++++++++---- 7 files changed, 67 insertions(+), 39 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc1206563..e691b457f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -153,25 +153,18 @@ func TestYourFunction(t *testing.T) { ## Database Schema Changes -### Patch Schemas (Development) +Erupe uses an embedded auto-migrating schema system in `server/migrations/`. -When actively developing new features that require schema changes: +When adding schema changes: -1. Create a new file in `schemas/patch-schema/` with format: `NN_description.sql` -2. Increment the number from the last patch -3. Test the migration on a clean database -4. Document what the patch does in comments +1. Create a new file in `server/migrations/sql/` with format: `NNNN_description.sql` (e.g. `0002_add_new_table.sql`) +2. Increment the number from the last migration +3. Test the migration on both a fresh and existing database +4. Document what the migration does in SQL comments -**Important**: Patch schemas are temporary and may change during development. +Migrations run automatically on startup in order. Each runs in its own transaction and is tracked in the `schema_version` table. -### Update Schemas (Production) - -For release-ready schema changes: - -1. Consolidate patch schemas into update schemas -2. Create a new file in appropriate schema directory -3. Update schema version tracking -4. Test migration paths from previous versions +For seed/demo data (shops, events, gacha), add files to `server/migrations/seed/`. Seed data is applied automatically on fresh databases and can be re-applied via the setup wizard. ## Documentation Requirements diff --git a/README.md b/README.md index 099f9a543..2435485fe 100644 --- a/README.md +++ b/README.md @@ -197,14 +197,10 @@ Multiple channel servers can run simultaneously, organized by world types: Newbi ## Database Schemas -Erupe uses a structured schema system: +Erupe uses an embedded auto-migrating schema system. Migrations in [server/migrations/sql/](./server/migrations/sql/) are applied automatically on startup — no manual SQL steps needed. -- **Initialization Schema**: Bootstraps database to version 9.1.0 -- **Update Schemas**: Production-ready updates for new releases -- **Patch Schemas**: Development updates (subject to change) -- **Seed Data**: Demo templates for shops, distributions, events, and gacha in [server/migrations/seed/](./server/migrations/seed/) - -**Note**: Only use patch schemas if you're following active development. They get consolidated into update schemas on release. +- **Migrations**: Numbered SQL files (`0001_init.sql`, `0002_*.sql`, ...) tracked in a `schema_version` table +- **Seed Data**: Demo templates for shops, distributions, events, and gacha in [server/migrations/seed/](./server/migrations/seed/) — applied automatically on fresh databases ## Development @@ -237,7 +233,7 @@ go test -v -race ./... # Check for race conditions (mandatory before merging ### Database schema errors -- Ensure all patch files are applied in order +- Schema migrations run automatically on startup — check the server logs for migration errors - Check PostgreSQL logs for detailed error messages - Verify database user has sufficient privileges diff --git a/config/config.go b/config/config.go index cfff625f7..8555fe701 100644 --- a/config/config.go +++ b/config/config.go @@ -342,6 +342,7 @@ func getOutboundIP4() (net.IP, error) { // config.json (just database credentials) produces a fully working server. func registerDefaults() { // Top-level settings + viper.SetDefault("Language", "jp") viper.SetDefault("BinPath", "bin") viper.SetDefault("HideLoginNotice", true) viper.SetDefault("LoginNotices", []string{ diff --git a/docker/README.md b/docker/README.md index c7208675f..2ec1c381e 100644 --- a/docker/README.md +++ b/docker/README.md @@ -19,7 +19,7 @@ docker compose up ``` -The database is automatically initialized and patched on first start via `init/setup.sh`. +The database schema is automatically applied on first start via the embedded migration system. pgAdmin is available at `http://localhost:5050` (default login: `user@pgadmin.com` / `password`). @@ -45,18 +45,14 @@ To delete all persistent data, remove these directories after stopping: ## Updating -After pulling new changes: +After pulling new changes, rebuild and restart. Schema migrations are applied automatically on startup. -1. Check for new patch schemas in `schemas/patch-schema/` — apply them via pgAdmin or `psql` into the running database container. - -2. Rebuild and restart: - - ```bash - docker compose down - docker compose build - docker compose up - ``` +```bash +docker compose down +docker compose build +docker compose up +``` ## Troubleshooting -**Postgres won't populate on Windows**: `init/setup.sh` must use LF line endings, not CRLF. Open it in your editor and convert. +**Postgres won't start on Windows**: Ensure `docker/db-data/` doesn't contain stale data from a different PostgreSQL version. Delete it and restart to reinitialize. diff --git a/main.go b/main.go index a88ee3dfa..84c077887 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( cfg "erupe-ce/config" + "flag" "fmt" "net" "os" @@ -71,6 +72,9 @@ func setupDiscordBot(config *cfg.Config, logger *zap.Logger) *discordbot.Discord } func main() { + runSetup := flag.Bool("setup", false, "Launch the setup wizard (even if config.json exists)") + flag.Parse() + var err error var zapLogger *zap.Logger @@ -79,6 +83,13 @@ func main() { defer func() { _ = zapLogger.Sync() }() logger := zapLogger.Named("main") + if *runSetup { + logger.Info("Launching setup wizard (--setup)") + if err := setup.Run(logger.Named("setup"), 8080); err != nil { + logger.Fatal("Setup wizard failed", zap.Error(err)) + } + } + config, cfgErr := cfg.LoadConfig() if cfgErr != nil { if _, err := os.Stat("config.json"); os.IsNotExist(err) { @@ -156,6 +167,7 @@ func main() { logger.Info("Database: Started successfully") // Run database migrations + verBefore, _ := migrations.Version(db) applied, migErr := migrations.Migrate(db, logger.Named("migrations")) if migErr != nil { preventClose(config, fmt.Sprintf("Database migration failed: %s", migErr.Error())) @@ -165,6 +177,18 @@ func main() { logger.Info(fmt.Sprintf("Database: Applied %d migration(s), now at version %d", applied, ver)) } + // Auto-apply seed data on a fresh database so users who skip the wizard + // still get shops, events, and gacha. Seed files use ON CONFLICT DO NOTHING + // so this is safe to run even if data already exists. + if verBefore == 0 && applied > 0 { + seedApplied, seedErr := migrations.ApplySeedData(db, logger.Named("migrations")) + if seedErr != nil { + logger.Warn(fmt.Sprintf("Seed data failed: %s", seedErr.Error())) + } else if seedApplied > 0 { + logger.Info(fmt.Sprintf("Database: Applied %d seed data file(s)", seedApplied)) + } + } + // Pre-compute all server IDs this instance will own, so we only // delete our own rows (safe for multi-instance on the same DB). var ownedServerIDs []string diff --git a/server/setup/wizard.go b/server/setup/wizard.go index 21f748af9..ca05771bc 100644 --- a/server/setup/wizard.go +++ b/server/setup/wizard.go @@ -26,6 +26,7 @@ type FinishRequest struct { DBPassword string `json:"dbPassword"` DBName string `json:"dbName"` Host string `json:"host"` + Language string `json:"language"` ClientMode string `json:"clientMode"` AutoCreateAccount bool `json:"autoCreateAccount"` } @@ -33,8 +34,13 @@ type FinishRequest struct { // buildDefaultConfig produces a minimal config map with only user-provided values. // All other settings are filled by Viper's registered defaults at load time. func buildDefaultConfig(req FinishRequest) map[string]interface{} { + lang := req.Language + if lang == "" { + lang = "jp" + } return map[string]interface{}{ "Host": req.Host, + "Language": lang, "ClientMode": req.ClientMode, "AutoCreateAccount": req.AutoCreateAccount, "Database": map[string]interface{}{ diff --git a/server/setup/wizard.html b/server/setup/wizard.html index 01c3e4742..be089c012 100644 --- a/server/setup/wizard.html +++ b/server/setup/wizard.html @@ -145,10 +145,20 @@ h1{font-size:1.75rem;margin-bottom:.5rem;color:#e94560;text-align:center}
Use 127.0.0.1 for local play, or auto-detect for LAN/internet play.
-
- - -
Must match your game client version. ZZ is the latest.
+
+
+ + +
Must match your game client version. ZZ is the latest.
+
+
+ + +
Game text language.
+
@@ -339,6 +349,7 @@ function buildReview() { ['Database Password', masked], ['Database Name', document.getElementById('db-name').value], ['Server Host', document.getElementById('srv-host').value], + ['Language', document.getElementById('srv-language').value], ['Client Mode', document.getElementById('srv-client-mode').value], ['Auto-create Accounts', document.getElementById('srv-auto-create').checked ? 'Yes' : 'No'], ]; @@ -362,6 +373,7 @@ async function finish() { dbPassword: document.getElementById('db-password').value, dbName: document.getElementById('db-name').value, host: document.getElementById('srv-host').value, + language: document.getElementById('srv-language').value, clientMode: document.getElementById('srv-client-mode').value, autoCreateAccount: document.getElementById('srv-auto-create').checked, })