mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
12
README.md
12
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
|
||||
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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.
|
||||
|
||||
24
main.go
24
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
|
||||
|
||||
@@ -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{}{
|
||||
|
||||
@@ -145,10 +145,20 @@ h1{font-size:1.75rem;margin-bottom:.5rem;color:#e94560;text-align:center}
|
||||
</div>
|
||||
<div style="font-size:.75rem;color:#666;margin-top:.3rem">Use 127.0.0.1 for local play, or auto-detect for LAN/internet play.</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Client Mode</label>
|
||||
<select id="srv-client-mode"></select>
|
||||
<div style="font-size:.75rem;color:#666;margin-top:.3rem">Must match your game client version. ZZ is the latest.</div>
|
||||
<div class="field-row">
|
||||
<div class="field">
|
||||
<label>Client Mode</label>
|
||||
<select id="srv-client-mode"></select>
|
||||
<div style="font-size:.75rem;color:#666;margin-top:.3rem">Must match your game client version. ZZ is the latest.</div>
|
||||
</div>
|
||||
<div class="field field-sm">
|
||||
<label>Language</label>
|
||||
<select id="srv-language">
|
||||
<option value="jp" selected>jp</option>
|
||||
<option value="en">en</option>
|
||||
</select>
|
||||
<div style="font-size:.75rem;color:#666;margin-top:.3rem">Game text language.</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="checkbox" style="margin-top:1rem"><input type="checkbox" id="srv-auto-create" checked> Auto-create accounts (recommended for private servers)</label>
|
||||
<div class="actions">
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user