From d415453336215f59de377a70ded4b3ac6ce431a5 Mon Sep 17 00:00:00 2001
From: Eduardo <6845999+eduardosmaniotto@users.noreply.github.com>
Date: Thu, 10 Sep 2026 10:41:07 -0300
Subject: [PATCH 1/2] Parametrize database credentials via DB_* environment
variables
The postgres admin user/password was effectively hardcoded to
postgres/admin across the compose deployments. The all-in-one Startup
path already supported DB_HOST/DB_ADMIN_USER/DB_ADMIN_PW overrides, but
the compose files never passed them through and the distributed Dapr
secret store ignored them entirely.
- Add shared DatabaseConnectionStringHelper and use it in both the
config-file and the Dapr secret-store connection string providers
- Wire DB_HOST/DB_ADMIN_USER/DB_ADMIN_PW/DB_NAME through all compose
files (all-in-one, traefik, distributed), mapped to POSTGRES_* for
the database container
- Add deploy/*/.env.example templates, gitignore real .env files
- Document compose wiring, first-init semantics and local dev setup
Defaults (postgres/admin/openmu) still apply when unset, so existing
installations keep working without changes.
---
.gitignore | 6 ++
deploy/all-in-one-traefik/.env.example | 25 ++++++
.../docker-compose.prod.yml | 10 ++-
deploy/all-in-one-traefik/docker-compose.yml | 10 ++-
deploy/all-in-one/.env.example | 28 +++++++
deploy/all-in-one/docker-compose.yml | 10 ++-
deploy/distributed/.env.example | 28 +++++++
.../distributed/dapr-components/secrets.json | 1 +
deploy/distributed/docker-compose.yml | 30 ++++++-
.../docs/deployment/startup-parameters.md | 4 +-
docs-website/docs/getting-started/docker.md | 19 +++++
.../docs/getting-started/from-source.md | 81 +++++++++++++++----
...StoreDatabaseConnectionSettingsProvider.cs | 5 +-
...figFileDatabaseConnectionStringProvider.cs | 27 +------
.../DatabaseConnectionStringHelper.cs | 77 ++++++++++++++++++
15 files changed, 301 insertions(+), 60 deletions(-)
create mode 100644 deploy/all-in-one/.env.example
create mode 100644 deploy/distributed/.env.example
create mode 100644 src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs
diff --git a/.gitignore b/.gitignore
index dd969b7806..d7cdada7d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -326,5 +326,11 @@ src/AdminPanel/wwwroot/content/js/app.js.map
# Keys which are generated when the server runs locally.
data-protection-keys/
+# Local environment files with secrets (see deploy/*/.env.example).
+# Templates (*.env.example) stay committable, real values never are.
+.env
+.env.*
+!.env.example
+
# The archive of the network observation, when the server is started from the repository.
captures/
diff --git a/deploy/all-in-one-traefik/.env.example b/deploy/all-in-one-traefik/.env.example
index f81bc7d596..526e6ee165 100644
--- a/deploy/all-in-one-traefik/.env.example
+++ b/deploy/all-in-one-traefik/.env.example
@@ -1 +1,26 @@
DOMAIN=domain.com
+
+# Copy to .env and adjust. Defaults preserve the previous behavior (postgres / admin).
+# Host name of the postgres database as seen by the app containers.
+DB_HOST=database
+
+# User name of the postgres admin account.
+# Must match the user of an already-initialized database volume.
+DB_ADMIN_USER=postgres
+
+# Password of the postgres admin account. Also used as POSTGRES_PASSWORD
+# for the database container on first initialization.
+# IMPORTANT: POSTGRES_PASSWORD is only applied when the dbdata volume is
+# created. Changing it later does NOT change an existing database.
+# Change the password inside postgres first (ALTER ROLE ...),
+# then restart with the new value here.
+# Must match the password of an already-initialized database volume.
+DB_ADMIN_PW=admin
+
+# Name of the postgres database (default openmu).
+DB_NAME=openmu
+
+# Optional bootstrap admin panel user (see docker docs).
+OPENMU_ADMIN_USER=
+OPENMU_ADMIN_PASSWORD=
+OPENMU_ADMIN_TOTP_SECRET=
diff --git a/deploy/all-in-one-traefik/docker-compose.prod.yml b/deploy/all-in-one-traefik/docker-compose.prod.yml
index 6eec1e2b0b..f9a7a5ee70 100644
--- a/deploy/all-in-one-traefik/docker-compose.prod.yml
+++ b/deploy/all-in-one-traefik/docker-compose.prod.yml
@@ -14,7 +14,9 @@ services:
- "44405:44405"
- "55980:55980"
environment:
- DB_HOST: database
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
working_dir: /app/
depends_on:
- database
@@ -28,9 +30,9 @@ services:
image: postgres
container_name: database
environment:
- POSTGRES_PASSWORD: admin
- POSTGRES_DB: openmu
- POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: ${DB_ADMIN_PW:-admin}
+ POSTGRES_DB: ${DB_NAME:-openmu}
+ POSTGRES_USER: ${DB_ADMIN_USER:-postgres}
networks:
- proxy
ports:
diff --git a/deploy/all-in-one-traefik/docker-compose.yml b/deploy/all-in-one-traefik/docker-compose.yml
index 430865c069..9e9ced8d40 100644
--- a/deploy/all-in-one-traefik/docker-compose.yml
+++ b/deploy/all-in-one-traefik/docker-compose.yml
@@ -14,7 +14,9 @@ services:
- "44405:44405"
- "55980:55980"
environment:
- DB_HOST: database
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
# Optional bootstrap admin panel user. Without it, the admin panel is reachable
# without a login until the first user has been created within the panel itself.
OPENMU_ADMIN_USER: ${OPENMU_ADMIN_USER:-}
@@ -37,9 +39,9 @@ services:
image: postgres
container_name: database
environment:
- POSTGRES_PASSWORD: admin
- POSTGRES_DB: openmu
- POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: ${DB_ADMIN_PW:-admin}
+ POSTGRES_DB: ${DB_NAME:-openmu}
+ POSTGRES_USER: ${DB_ADMIN_USER:-postgres}
networks:
- proxy
ports:
diff --git a/deploy/all-in-one/.env.example b/deploy/all-in-one/.env.example
new file mode 100644
index 0000000000..80aa14380b
--- /dev/null
+++ b/deploy/all-in-one/.env.example
@@ -0,0 +1,28 @@
+# Copy this file to .env and adjust the values.
+# If .env is absent, the defaults below (after ':-') apply and
+# the setup behaves exactly as before (postgres / admin).
+
+# Host name of the postgres database as seen by the app containers.
+DB_HOST=database
+
+# User name of the postgres admin account.
+# Must match the user of an already-initialized database volume.
+DB_ADMIN_USER=postgres
+
+# Password of the postgres admin account. Also used as POSTGRES_PASSWORD
+# for the database container on first initialization.
+# IMPORTANT: POSTGRES_PASSWORD is only applied when the dbdata volume is
+# created. Changing it later does NOT change an existing database.
+# Change the password inside postgres first (ALTER ROLE ...),
+# then restart with the new value here.
+# Must match the password of an already-initialized database volume.
+DB_ADMIN_PW=admin
+
+# Name of the postgres database. Must match Database= in
+# src/Persistence/EntityFramework/ConnectionSettings.xml (openmu).
+DB_NAME=openmu
+
+# Optional bootstrap admin panel user (see docker docs).
+OPENMU_ADMIN_USER=
+OPENMU_ADMIN_PASSWORD=
+OPENMU_ADMIN_TOTP_SECRET=
diff --git a/deploy/all-in-one/docker-compose.yml b/deploy/all-in-one/docker-compose.yml
index ec40e40f1c..4a62908364 100644
--- a/deploy/all-in-one/docker-compose.yml
+++ b/deploy/all-in-one/docker-compose.yml
@@ -24,7 +24,9 @@ services:
- "44406:44406"
- "55980:55980"
environment:
- DB_HOST: database
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
ASPNETCORE_URLS: http://+:8080
# Optional bootstrap admin panel user. Without it, the admin panel is reachable
# without a login until the first user has been created within the panel itself.
@@ -43,9 +45,9 @@ services:
image: postgres
container_name: database
environment:
- POSTGRES_PASSWORD: admin
- POSTGRES_DB: openmu
- POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: ${DB_ADMIN_PW:-admin}
+ POSTGRES_DB: ${DB_NAME:-openmu}
+ POSTGRES_USER: ${DB_ADMIN_USER:-postgres}
ports:
- "5432"
volumes:
diff --git a/deploy/distributed/.env.example b/deploy/distributed/.env.example
new file mode 100644
index 0000000000..52e7104396
--- /dev/null
+++ b/deploy/distributed/.env.example
@@ -0,0 +1,28 @@
+# Copy this file to .env and adjust the values.
+# If .env is absent, the defaults below (after ':-') apply and
+# the setup behaves exactly as before (postgres / admin).
+
+# Host name of the postgres database as seen by the app containers.
+DB_HOST=database
+
+# User name of the postgres admin account.
+# Must match the user of an already-initialized database volume.
+DB_ADMIN_USER=postgres
+
+# Password of the postgres admin account. Also used as POSTGRES_PASSWORD
+# for the database container on first initialization, and applied to the
+# Dapr secret store connection strings at runtime.
+# IMPORTANT: POSTGRES_PASSWORD is only applied when the dbdata volume is
+# created. Changing it later does NOT change an existing database.
+# Change the password inside postgres first (ALTER ROLE ...),
+# then restart with the new value here.
+# Must match the password of an already-initialized database volume.
+DB_ADMIN_PW=admin
+
+# Name of the postgres database (default openmu).
+DB_NAME=openmu
+
+# Optional bootstrap admin panel user (see docker docs).
+OPENMU_ADMIN_USER=
+OPENMU_ADMIN_PASSWORD=
+OPENMU_ADMIN_TOTP_SECRET=
diff --git a/deploy/distributed/dapr-components/secrets.json b/deploy/distributed/dapr-components/secrets.json
index 38da013e46..77e9acabdc 100644
--- a/deploy/distributed/dapr-components/secrets.json
+++ b/deploy/distributed/dapr-components/secrets.json
@@ -1,4 +1,5 @@
{
+ "_comment": "Defaults; DB_HOST/DB_ADMIN_USER/DB_ADMIN_PW env vars of the app containers override Server/User Id/Password at runtime. Default password 'admin' keeps working when unset.",
"connectionStrings": {
"MUnique.OpenMU.Persistence.EntityFramework.EntityDataContext": "Server=database;Port=5432;User Id=postgres;Password=admin;Database=openmu;Command Timeout=120;",
"MUnique.OpenMU.Persistence.EntityFramework.TypedContext": "Server=database;Port=5432;User Id=postgres;Password=admin;Database=openmu;Command Timeout=120;",
diff --git a/deploy/distributed/docker-compose.yml b/deploy/distributed/docker-compose.yml
index 715d0a760e..e7c0a6cc7b 100644
--- a/deploy/distributed/docker-compose.yml
+++ b/deploy/distributed/docker-compose.yml
@@ -98,9 +98,9 @@ services:
container_name: postgres
restart: always
environment:
- POSTGRES_PASSWORD: admin
- POSTGRES_DB: openmu
- POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: ${DB_ADMIN_PW:-admin}
+ POSTGRES_DB: ${DB_NAME:-openmu}
+ POSTGRES_USER: ${DB_ADMIN_USER:-postgres}
ports:
- "5432"
volumes:
@@ -123,6 +123,9 @@ services:
environment:
APPID: connectServer
ASPNETCORE_URLS: http://+:8080
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
depends_on:
- rabbit
- zipkin
@@ -149,6 +152,9 @@ services:
- "9464" # Prometheus
environment:
ASPNETCORE_URLS: http://+:8080
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
depends_on:
- zipkin
- database
@@ -174,6 +180,9 @@ services:
- "9464" # Prometheus
environment:
ASPNETCORE_URLS: http://+:8080
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
depends_on:
- chatServer-dapr
- database
@@ -200,6 +209,9 @@ services:
- "9464" # Prometheus
environment:
ASPNETCORE_URLS: http://+:8080
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
depends_on:
- database
- zipkin
@@ -223,6 +235,9 @@ services:
environment:
APPID: chatServer
ASPNETCORE_URLS: http://+:8080
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
ports:
- "55980:55980"
- "50001"
@@ -256,6 +271,9 @@ services:
environment:
ASPNETCORE_URLS: http://+:8080
PATH_BASE: /admin/
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
# Optional bootstrap admin panel user. Without it, the admin panel is reachable
# without a login until the first user has been created within the panel itself.
OPENMU_ADMIN_USER: ${OPENMU_ADMIN_USER:-}
@@ -297,6 +315,9 @@ services:
APPID: gameServer0
PATH_BASE: /gameServer/0/
ASPNETCORE_URLS: http://+:8080
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
depends_on:
- connectServer-dapr
- loginServer-dapr
@@ -336,6 +357,9 @@ services:
APPID: gameServer1
PATH_BASE: /gameServer/1/
ASPNETCORE_URLS: http://+:8080
+ DB_HOST: ${DB_HOST:-database}
+ DB_ADMIN_USER: ${DB_ADMIN_USER:-}
+ DB_ADMIN_PW: ${DB_ADMIN_PW:-}
depends_on:
- connectServer-dapr
- loginServer-dapr
diff --git a/docs-website/docs/deployment/startup-parameters.md b/docs-website/docs/deployment/startup-parameters.md
index f2b7e3c1c4..48619896e9 100644
--- a/docs-website/docs/deployment/startup-parameters.md
+++ b/docs-website/docs/deployment/startup-parameters.md
@@ -54,8 +54,8 @@ These may be helpful when running the server in a container or under Linux.
| `ASPNETCORE_ENVIRONMENT` | If neither a `-resolveIP` parameter nor a `RESOLVE_IP` variable is defined, this variable is considered to find the optimal IP resolver. If the value is `Development`, `loopback` is used, otherwise `public`. |
| `ASPNETCORE_URLS` | Defines the address of the admin panel, e.g. `http://+:80` |
| `DB_HOST` | Host name/address of the postgres database |
-| `DB_ADMIN_USER` | User name of the admin user of the postgres database |
-| `DB_ADMIN_PW` | Password of the admin user of the postgres database |
+| `DB_ADMIN_USER` | User name of the admin user of the postgres database. In docker compose this is also used as `POSTGRES_USER` for the database container (`${DB_ADMIN_USER:-postgres}`). |
+| `DB_ADMIN_PW` | Password of the admin user of the postgres database. In docker compose this is also used as `POSTGRES_PASSWORD` for the database container (`${DB_ADMIN_PW:-admin}`). When unset, the default `admin` keeps working. |
| `Database__AssumeExternallyProvisioned` | When `true`, an already-provisioned (empty) database is kept and only its schema is built via migrations, instead of dropping and recreating it. Default: `false`. See below. |
## Externally provisioned database
diff --git a/docs-website/docs/getting-started/docker.md b/docs-website/docs/getting-started/docker.md
index cae260d6a6..eb05753c15 100644
--- a/docs-website/docs/getting-started/docker.md
+++ b/docs-website/docs/getting-started/docker.md
@@ -66,5 +66,24 @@ environment variables:
| `DB_ADMIN_USER` | The user name of the postgres admin account. If the local configuration file is still configured to use `postgres` for the user name of the admin (first entry in the `ConnectionSettings.xml`), the value of this variable replaces it. |
| `DB_ADMIN_PW` | The password of the postgres admin account. If the local configuration file is still configured to use `admin` for the password of the admin (first entry in the `ConnectionSettings.xml`), the value of this variable replaces it. |
+The `docker-compose.yml` files take these variables (e.g. from a local `.env`
+file, see `deploy/*/.env.example`) and wire them to both sides:
+
+* the `database` service as `POSTGRES_PASSWORD: ${DB_ADMIN_PW:-admin}` and
+ `POSTGRES_USER: ${DB_ADMIN_USER:-postgres}`,
+* the app container(s) as `DB_ADMIN_USER` / `DB_ADMIN_PW` (and
+ `DB_HOST: ${DB_HOST:-database}`).
+
+When the variables are not set, the defaults (`postgres` / `admin`) apply, so
+existing installations keep working without any change. The same override
+applies to the distributed deployment, where the Dapr secret store connection
+strings are adjusted at runtime with the same variables.
+
+Note: `POSTGRES_PASSWORD` is only honored by the official postgres image when
+the `dbdata` volume is created for the first time. Changing `DB_ADMIN_PW`
+later does not change an existing database. Change the password inside
+postgres first (e.g. `ALTER ROLE postgres PASSWORD 'new'`), then restart the
+containers with the new value.
+
More variables and the start parameters are listed under
[Startup parameters and environment variables](../deployment/startup-parameters.md).
diff --git a/docs-website/docs/getting-started/from-source.md b/docs-website/docs/getting-started/from-source.md
index 0c35a17bc3..dec7a1c1c9 100644
--- a/docs-website/docs/getting-started/from-source.md
+++ b/docs-website/docs/getting-started/from-source.md
@@ -6,39 +6,86 @@ description: Build, run and debug OpenMU from the source code.
# Run from source
-Use this way if you want to develop or debug OpenMU. This guide describes it for
-Windows; it runs under Linux and macOS as well.
+Use this way if you want to develop or debug OpenMU. It works on Windows,
+Linux and macOS. Where the steps differ per operating system, both variants
+are shown.
## Requirements
-* Windows 10 or higher (Linux/macOS work too, this guide just isn't written for them)
-* [PostgreSQL](https://www.postgresql.org/download/) installed
-* Visual Studio 2026, with the workloads for *ASP.NET Web development* and
- *.NET Desktop development*. Please keep it up-to-date to prevent issues.
+* Windows 10 or higher, a current Linux distribution, or macOS
+* [PostgreSQL](https://www.postgresql.org/download/) installed, or Docker to
+ run it in a container (see step 3)
+* An IDE: Visual Studio 2026 on Windows (with the workloads for *ASP.NET Web
+ development* and *.NET Desktop development*), Rider, or VS Code with the C#
+ extension. Plain command line with the SDK below works as well. Please keep
+ your tools up to date to prevent issues.
* The Visual Studio extension
[Web Compiler 2022+](https://marketplace.visualstudio.com/items?itemName=Failwyn.WebCompiler64),
- if you plan to edit SCSS files of the admin panel
-* [.NET SDK 10](https://dotnet.microsoft.com/download/dotnet/10.0) — it should
- already be included in Visual Studio 2026
+ if you plan to edit SCSS files of the admin panel (Visual Studio only)
+* [.NET SDK 10](https://dotnet.microsoft.com/download/dotnet/10.0) (already
+ included in Visual Studio 2026)
```powershell
winget install Microsoft.DotNet.SDK.10
```
+ On Linux and macOS, install it through your package manager or the
+ installer from the download page linked above.
* [NodeJS 16+](https://nodejs.org)
```powershell
winget install OpenJS.NodeJS.LTS
```
+ On Linux, install it through your package manager (for example
+ `sudo apt install nodejs`) or from the download page linked above.
* This repository cloned
## Steps
-1. Open the OpenMU solution with Visual Studio.
-2. Right click the solution and select *Restore NuGet Packages*.
-3. Edit `src/Persistence/EntityFramework/ConnectionSettings.xml` so that the
- connection strings are correct. Only the user/password of the **first and
- second** connection string need to be correct — the server will try to create
- the other roles specified by the settings.
-4. Build the solution.
-5. Start `MUnique.OpenMU.Startup`.
+1. Open the OpenMU solution in your IDE.
+2. Restore the NuGet packages: Visual Studio offers *Restore NuGet Packages* on
+ right click of the solution, or run this from the repository root:
+ ```bash
+ dotnet restore
+ ```
+3. Configure the postgres admin credentials (pick one option):
+ * **Recommended: environment variables.** Set `DB_ADMIN_USER` and
+ `DB_ADMIN_PW` to the user/password of your postgres superuser account
+ (leave `DB_HOST` unset to use `localhost`). Only the admin credentials
+ need to be correct. The server creates the database schemas and the
+ other roles (`config`, `account`, `friend`, `guild`) itself.
+ ```bash
+ export DB_ADMIN_USER=postgres
+ export DB_ADMIN_PW='s3cret'
+ ```
+ On Windows (PowerShell) instead:
+ ```powershell
+ $env:DB_ADMIN_USER='postgres'
+ $env:DB_ADMIN_PW='s3cret'
+ ```
+ When the variables are not set, the defaults (`postgres` / `admin`)
+ apply. Avoid `;` in the password, since it separates values in connection
+ strings.
+ * **Alternative:** edit `src/Persistence/EntityFramework/ConnectionSettings.xml`
+ so that the connection strings are correct. Only the user/password of the
+ **first and second** connection string need to be correct. The server
+ will try to create the other roles specified by the settings.
+
+ Whichever option you picked, if you run postgres in Docker instead of installing it, start it with a
+ matching user, password and database name (`openmu`):
+ ```bash
+ docker run -d --name openmu-db \
+ -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD='s3cret' -e POSTGRES_DB=openmu \
+ -p 5432:5432 postgres
+ ```
+4. Build the solution, in your IDE or from the repository root with:
+ ```bash
+ dotnet build src/MUnique.OpenMU.sln
+ ```
+5. Start `MUnique.OpenMU.Startup`. The environment variables from step 3 must
+ be set wherever you start it (same terminal, or your Debug launch profile).
+ From a terminal:
+ ```bash
+ dotnet run --project src/Startup/MUnique.OpenMU.Startup.csproj -- -autostart
+ ```
+ (The `--` passes `-autostart` to the server instead of to `dotnet`.)
* If required, it creates the database schemas and the required roles, and
gives permissions to those roles.
* Optional: you can reinitialize the database by adding the `-reinit`
diff --git a/src/Dapr/Common/SecretStoreDatabaseConnectionSettingsProvider.cs b/src/Dapr/Common/SecretStoreDatabaseConnectionSettingsProvider.cs
index c92dd28e36..12a0362119 100644
--- a/src/Dapr/Common/SecretStoreDatabaseConnectionSettingsProvider.cs
+++ b/src/Dapr/Common/SecretStoreDatabaseConnectionSettingsProvider.cs
@@ -14,6 +14,9 @@ namespace MUnique.OpenMU.Dapr.Common;
///
/// Implementation of which retrieves the settings from the
/// configured Dapr secret storage.
+/// The retrieved connection strings can be influenced by the environment variables DB_HOST, DB_ADMIN_USER and DB_ADMIN_PW,
+/// just like the . When the variables are not set,
+/// the stored values (default password admin) are used unchanged.
///
public class SecretStoreDatabaseConnectionSettingsProvider : IDatabaseConnectionSettingProvider
{
@@ -61,7 +64,7 @@ public Task InitializeAsync(CancellationToken cancellationToken)
var setting = new ConnectionSetting
{
ContextTypeName = contextTypeName,
- ConnectionString = secret.Value.Values.First()!,
+ ConnectionString = DatabaseConnectionStringHelper.ApplyEnvironmentVariables(secret.Value.Values.First()!),
DatabaseEngine = DatabaseEngine.Npgsql,
};
diff --git a/src/Persistence/EntityFramework/ConfigFileDatabaseConnectionStringProvider.cs b/src/Persistence/EntityFramework/ConfigFileDatabaseConnectionStringProvider.cs
index f699928d15..17433a32cc 100644
--- a/src/Persistence/EntityFramework/ConfigFileDatabaseConnectionStringProvider.cs
+++ b/src/Persistence/EntityFramework/ConfigFileDatabaseConnectionStringProvider.cs
@@ -17,10 +17,6 @@ namespace MUnique.OpenMU.Persistence.EntityFramework;
///
public class ConfigFileDatabaseConnectionStringProvider : IDatabaseConnectionSettingProvider
{
- private const string DbHostVariableName = "DB_HOST";
- private const string DbAdminUserVariableName = "DB_ADMIN_USER";
- private const string DbAdminPasswordVariableName = "DB_ADMIN_PW";
-
private readonly string _fileName;
private IDictionary? _settings;
@@ -128,25 +124,6 @@ private IDictionary LoadSettings()
private void ApplyEnvironmentVariables(ConnectionSetting setting)
{
- if (Environment.GetEnvironmentVariable(DbHostVariableName) is { } dbHost
- && !string.IsNullOrEmpty(dbHost))
- {
- setting.ConnectionString = setting.ConnectionString!.Replace("Server=localhost;", $"Server={dbHost};");
- }
-
- if (setting.ConnectionString!.Contains("User Id=postgres;"))
- {
- if (Environment.GetEnvironmentVariable(DbAdminUserVariableName) is { } dbAdminUser
- && !string.IsNullOrEmpty(dbAdminUser))
- {
- setting.ConnectionString = setting.ConnectionString.Replace("User Id=postgres;", $"User Id={dbAdminUser};");
- }
-
- if (Environment.GetEnvironmentVariable(DbAdminPasswordVariableName) is { } dbAdminPassword
- && !string.IsNullOrEmpty(dbAdminPassword))
- {
- setting.ConnectionString = setting.ConnectionString.Replace("Password=admin;", $"Password={dbAdminPassword};");
- }
- }
+ DatabaseConnectionStringHelper.ApplyEnvironmentVariables(setting);
}
-}
\ No newline at end of file
+}
diff --git a/src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs b/src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs
new file mode 100644
index 0000000000..91734044e0
--- /dev/null
+++ b/src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs
@@ -0,0 +1,77 @@
+//
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace MUnique.OpenMU.Persistence.EntityFramework;
+
+using System.Text.RegularExpressions;
+
+///
+/// Shared helper to apply the DB_HOST, DB_ADMIN_USER and DB_ADMIN_PW
+/// environment variables to a connection string.
+/// When the variables are not set (or empty), the connection string is left unchanged,
+/// so the default password admin keeps working and existing databases are unaffected.
+///
+public static partial class DatabaseConnectionStringHelper
+{
+ ///
+ /// Gets the environment variable name for the database host.
+ ///
+ public const string DbHostVariableName = "DB_HOST";
+ ///
+ /// Gets the environment variable name for the admin user.
+ ///
+ public const string DbAdminUserVariableName = "DB_ADMIN_USER";
+ ///
+ /// Gets the environment variable name for the admin password.
+ ///
+ public const string DbAdminPasswordVariableName = "DB_ADMIN_PW";
+
+ ///
+ /// Applies the environment variables to the connection string of the specified setting, in place.
+ ///
+ /// The setting to adjust.
+ public static void ApplyEnvironmentVariables(ConnectionSetting setting)
+ {
+ if (setting.ConnectionString is null)
+ {
+ return;
+ }
+
+ setting.ConnectionString = ApplyEnvironmentVariables(setting.ConnectionString);
+ }
+
+ ///
+ /// Applies the environment variables to the specified connection string.
+ ///
+ /// The connection string.
+ /// The adjusted connection string.
+ public static string ApplyEnvironmentVariables(string connectionString)
+ {
+ if (Environment.GetEnvironmentVariable(DbHostVariableName) is { } dbHost
+ && !string.IsNullOrEmpty(dbHost))
+ {
+ connectionString = ServerRegex().Replace(connectionString, $"Server={dbHost};");
+ }
+
+ if (connectionString.Contains("User Id=postgres;"))
+ {
+ if (Environment.GetEnvironmentVariable(DbAdminUserVariableName) is { } dbAdminUser
+ && !string.IsNullOrEmpty(dbAdminUser))
+ {
+ connectionString = connectionString.Replace("User Id=postgres;", $"User Id={dbAdminUser};");
+ }
+
+ if (Environment.GetEnvironmentVariable(DbAdminPasswordVariableName) is { } dbAdminPassword
+ && !string.IsNullOrEmpty(dbAdminPassword))
+ {
+ connectionString = connectionString.Replace("Password=admin;", $"Password={dbAdminPassword};");
+ }
+ }
+
+ return connectionString;
+ }
+
+ [GeneratedRegex("Server=[^;]+;")]
+ private static partial Regex ServerRegex();
+}
From 02a2d2990246d804f1c2e44b5ebbc5daf89f83bc Mon Sep 17 00:00:00 2001
From: Eduardo <6845999+eduardosmaniotto@users.noreply.github.com>
Date: Thu, 10 Sep 2026 11:05:54 -0300
Subject: [PATCH 2/2] Address codacy warnings
---
.../docs/getting-started/from-source.md | 29 ++++++++++++++-----
.../DatabaseConnectionStringHelper.cs | 12 ++++----
2 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/docs-website/docs/getting-started/from-source.md b/docs-website/docs/getting-started/from-source.md
index dec7a1c1c9..956d72c17b 100644
--- a/docs-website/docs/getting-started/from-source.md
+++ b/docs-website/docs/getting-started/from-source.md
@@ -19,8 +19,8 @@ are shown.
development* and *.NET Desktop development*), Rider, or VS Code with the C#
extension. Plain command line with the SDK below works as well. Please keep
your tools up to date to prevent issues.
-* The Visual Studio extension
- [Web Compiler 2022+](https://marketplace.visualstudio.com/items?itemName=Failwyn.WebCompiler64),
+* The Visual Studio extension [Web Compiler
+ 2022+](https://marketplace.visualstudio.com/items?itemName=Failwyn.WebCompiler64),
if you plan to edit SCSS files of the admin panel (Visual Studio only)
* [.NET SDK 10](https://dotnet.microsoft.com/download/dotnet/10.0) (already
included in Visual Studio 2026)
@@ -42,49 +42,64 @@ are shown.
1. Open the OpenMU solution in your IDE.
2. Restore the NuGet packages: Visual Studio offers *Restore NuGet Packages* on
right click of the solution, or run this from the repository root:
+
```bash
dotnet restore
```
+
3. Configure the postgres admin credentials (pick one option):
* **Recommended: environment variables.** Set `DB_ADMIN_USER` and
`DB_ADMIN_PW` to the user/password of your postgres superuser account
(leave `DB_HOST` unset to use `localhost`). Only the admin credentials
need to be correct. The server creates the database schemas and the
other roles (`config`, `account`, `friend`, `guild`) itself.
+
```bash
export DB_ADMIN_USER=postgres
export DB_ADMIN_PW='s3cret'
```
+
On Windows (PowerShell) instead:
+
```powershell
$env:DB_ADMIN_USER='postgres'
$env:DB_ADMIN_PW='s3cret'
```
+
When the variables are not set, the defaults (`postgres` / `admin`)
apply. Avoid `;` in the password, since it separates values in connection
strings.
- * **Alternative:** edit `src/Persistence/EntityFramework/ConnectionSettings.xml`
- so that the connection strings are correct. Only the user/password of the
+ * **Alternative:** edit
+ `src/Persistence/EntityFramework/ConnectionSettings.xml` so that the
+ connection strings are correct. Only the user/password of the
**first and second** connection string need to be correct. The server
will try to create the other roles specified by the settings.
- Whichever option you picked, if you run postgres in Docker instead of installing it, start it with a
- matching user, password and database name (`openmu`):
+ Whichever option you picked, if you run postgres in Docker instead of
+ installing it, start it with a matching user, password and database
+ name (`openmu`):
+
```bash
docker run -d --name openmu-db \
- -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD='s3cret' -e POSTGRES_DB=openmu \
+ -e POSTGRES_USER=postgres -e POSTGRES_DB=openmu \
+ -e POSTGRES_PASSWORD='s3cret' \
-p 5432:5432 postgres
```
+
4. Build the solution, in your IDE or from the repository root with:
+
```bash
dotnet build src/MUnique.OpenMU.sln
```
+
5. Start `MUnique.OpenMU.Startup`. The environment variables from step 3 must
be set wherever you start it (same terminal, or your Debug launch profile).
From a terminal:
+
```bash
dotnet run --project src/Startup/MUnique.OpenMU.Startup.csproj -- -autostart
```
+
(The `--` passes `-autostart` to the server instead of to `dotnet`.)
* If required, it creates the database schemas and the required roles, and
gives permissions to those roles.
diff --git a/src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs b/src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs
index 91734044e0..d8bce3d844 100644
--- a/src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs
+++ b/src/Persistence/EntityFramework/DatabaseConnectionStringHelper.cs
@@ -17,15 +17,15 @@ public static partial class DatabaseConnectionStringHelper
///
/// Gets the environment variable name for the database host.
///
- public const string DbHostVariableName = "DB_HOST";
+ public static string DbHostVariableName { get; } = "DB_HOST";
///
/// Gets the environment variable name for the admin user.
///
- public const string DbAdminUserVariableName = "DB_ADMIN_USER";
+ public static string DbAdminUserVariableName { get; } = "DB_ADMIN_USER";
///
/// Gets the environment variable name for the admin password.
///
- public const string DbAdminPasswordVariableName = "DB_ADMIN_PW";
+ public static string DbAdminPasswordVariableName { get; } = "DB_ADMIN_PW";
///
/// Applies the environment variables to the connection string of the specified setting, in place.
@@ -54,18 +54,18 @@ public static string ApplyEnvironmentVariables(string connectionString)
connectionString = ServerRegex().Replace(connectionString, $"Server={dbHost};");
}
- if (connectionString.Contains("User Id=postgres;"))
+ if (connectionString.Contains("User Id=postgres;", StringComparison.Ordinal))
{
if (Environment.GetEnvironmentVariable(DbAdminUserVariableName) is { } dbAdminUser
&& !string.IsNullOrEmpty(dbAdminUser))
{
- connectionString = connectionString.Replace("User Id=postgres;", $"User Id={dbAdminUser};");
+ connectionString = connectionString.Replace("User Id=postgres;", $"User Id={dbAdminUser};", StringComparison.Ordinal);
}
if (Environment.GetEnvironmentVariable(DbAdminPasswordVariableName) is { } dbAdminPassword
&& !string.IsNullOrEmpty(dbAdminPassword))
{
- connectionString = connectionString.Replace("Password=admin;", $"Password={dbAdminPassword};");
+ connectionString = connectionString.Replace("Password=admin;", $"Password={dbAdminPassword};", StringComparison.Ordinal);
}
}