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); } }