diff --git a/README.md b/README.md index afbd6ec3..df6e3e4b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ **turnierplan.NET** is mostly written in C# using [.NET](https://dotnet.microsoft.com/). This includes the core logic, the backend API and database connection as well as all publicly visible web pages. In addition, it serves the *turnierplan.NET portal*, the client application for authenticated users, based on the [Angular](https://angular.dev/) framework. -Visit the **turnierplan.NET** documentation using the following link: [docs.turnierplan.net](https://docs.turnierplan.net). If you want to install **turnierplan.NET** on your server, please visit the [Installation guide](https://docs.turnierplan.net/installation). +Visit the **turnierplan.NET** documentation using the following link: [docs.turnierplan.net](https://docs.turnierplan.net). If you want to set up your own instance of **turnierplan.NET**, visit the [Installation guide](https://docs.turnierplan.net/installation) for detailed information on a local or cloud deployment. > [!NOTE] > The user interface and documentation are currently only available in German 🇩🇪 diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 00000000..8c154d67 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,3 @@ +# turnierplan.NET · Deployment Scripts + +This directory contains scripts for various deployment methods. Refer to the [documentation](https://docs.turnierplan.net/installation/) for details on how to use the scripts. diff --git a/deploy/azure-terraform/app-insights.tf b/deploy/azure-terraform/app-insights.tf new file mode 100644 index 00000000..fef1597d --- /dev/null +++ b/deploy/azure-terraform/app-insights.tf @@ -0,0 +1,15 @@ +resource "azurerm_log_analytics_workspace" "default" { + name = "log-${var.name}" + location = var.location + resource_group_name = azurerm_resource_group.default.name + sku = "PerGB2018" +} + +resource "azurerm_application_insights" "default" { + name = "appi-${var.name}" + location = var.location + resource_group_name = azurerm_resource_group.default.name + workspace_id = azurerm_log_analytics_workspace.default.id + application_type = "web" + retention_in_days = var.app_insights_retention_in_days +} diff --git a/deploy/azure-terraform/app-service.tf b/deploy/azure-terraform/app-service.tf new file mode 100644 index 00000000..2e43e15c --- /dev/null +++ b/deploy/azure-terraform/app-service.tf @@ -0,0 +1,87 @@ +locals { + app_service_name = "app-${var.name}" + application_url = var.app_service_custom_domain == null ? "https://${local.app_service_name}.azurewebsites.net" : "https://${var.app_service_custom_domain}" +} + +resource "azurerm_service_plan" "default" { + name = "asp-${var.name}" + location = var.location + resource_group_name = azurerm_resource_group.default.name + + os_type = "Linux" + sku_name = var.app_service_plan_sku_name +} + +resource "random_bytes" "identity_sining_key" { + length = 64 +} + +resource "azurerm_linux_web_app" "default" { + name = local.app_service_name + location = var.location + resource_group_name = azurerm_resource_group.default.name + + service_plan_id = azurerm_service_plan.default.id + virtual_network_subnet_id = azurerm_subnet.app.id + + https_only = true + + identity { + type = "SystemAssigned" + } + + site_config { + always_on = true + + application_stack { + docker_registry_url = "https://${var.turnierplan_container_registry}" + docker_image_name = "${var.turnierplan_container_image}:${var.turnierplan_container_version}" + } + } + + app_settings = merge(var.turnierplan_additional_app_settings, { + "Turnierplan__ApplicationUrl" = local.application_url + "Turnierplan__InitialUserName" = var.turnierplan_initial_user + "Turnierplan__InitialUserPassword" = var.turnierplan_initial_password + + "ApplicationInsights__ConnectionString" = azurerm_application_insights.default.connection_string + "Database__ConnectionString" = "Host=${azurerm_postgresql_flexible_server.default.fqdn};Database=${azurerm_postgresql_flexible_server_database.default.name};Username=${azurerm_postgresql_flexible_server.default.administrator_login};Password=${azurerm_postgresql_flexible_server.default.administrator_password}" + "Identity__SigningKey" = random_bytes.identity_sining_key.base64 + + "ImageStorage__Type" = "Azure" + "ImageStorage__StorageAccountName" = azurerm_storage_account.default.name + "ImageStorage__ContainerName" = azurerm_storage_container.images.name + }) +} + +resource "azurerm_role_assignment" "application_blob_storage_contributor" { + role_definition_name = "Storage Blob Data Contributor" + principal_id = azurerm_linux_web_app.default.identity[0].principal_id + scope = azurerm_storage_account.default.id +} + +resource "azurerm_app_service_custom_hostname_binding" "default" { + count = var.app_service_custom_domain == null ? 0 : 1 + + resource_group_name = azurerm_resource_group.default.name + app_service_name = azurerm_linux_web_app.default.name + hostname = var.app_service_custom_domain + + lifecycle { + ignore_changes = [ssl_state, thumbprint] + } +} + +resource "azurerm_app_service_managed_certificate" "default" { + count = var.app_service_custom_domain == null ? 0 : 1 + + custom_hostname_binding_id = azurerm_app_service_custom_hostname_binding.default[0].id +} + +resource "azurerm_app_service_certificate_binding" "example" { + count = var.app_service_custom_domain == null ? 0 : 1 + + hostname_binding_id = azurerm_app_service_custom_hostname_binding.default[0].id + certificate_id = azurerm_app_service_managed_certificate.default[0].id + ssl_state = "SniEnabled" +} diff --git a/deploy/azure-terraform/database.tf b/deploy/azure-terraform/database.tf new file mode 100644 index 00000000..30a039f8 --- /dev/null +++ b/deploy/azure-terraform/database.tf @@ -0,0 +1,39 @@ +resource "random_password" "psql_admin" { + length = 32 + special = false +} + +resource "azurerm_postgresql_flexible_server" "default" { + name = "psql-${var.name}" + location = var.location + resource_group_name = azurerm_resource_group.default.name + + public_network_access_enabled = false + delegated_subnet_id = azurerm_subnet.database.id + private_dns_zone_id = azurerm_private_dns_zone.database.id + + administrator_login = "tpsqladm" + administrator_password = random_password.psql_admin.result + + authentication { + active_directory_auth_enabled = false + password_auth_enabled = true + } + + version = "18" + sku_name = var.postgresql_sku_name + zone = var.postgresql_availability_zone + storage_mb = var.postgresql_storage_size_mb + storage_tier = var.postgresql_storage_tier +} + +resource "azurerm_postgresql_flexible_server_database" "default" { + name = "turnierplan" + server_id = azurerm_postgresql_flexible_server.default.id + charset = var.postgresql_charset + collation = var.postgresql_collation + + lifecycle { + prevent_destroy = true + } +} diff --git a/deploy/azure-terraform/networking.tf b/deploy/azure-terraform/networking.tf new file mode 100644 index 00000000..80487619 --- /dev/null +++ b/deploy/azure-terraform/networking.tf @@ -0,0 +1,50 @@ +resource "azurerm_virtual_network" "default" { + name = "vnet-${var.name}" + location = var.location + resource_group_name = azurerm_resource_group.default.name + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "app" { + name = "subnet-app-${var.name}" + resource_group_name = azurerm_resource_group.default.name + virtual_network_name = azurerm_virtual_network.default.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "app" + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet" "database" { + name = "subnet-database-${var.name}" + resource_group_name = azurerm_resource_group.default.name + virtual_network_name = azurerm_virtual_network.default.name + address_prefixes = ["10.0.2.0/24"] + service_endpoints = ["Microsoft.Storage"] + + delegation { + name = "psql" + service_delegation { + name = "Microsoft.DBforPostgreSQL/flexibleServers" + actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_private_dns_zone" "database" { + name = "dnszone-${var.name}.postgres.database.azure.com" + resource_group_name = azurerm_resource_group.default.name +} + +resource "azurerm_private_dns_zone_virtual_network_link" "database" { + name = "database-vnet-link" + private_dns_zone_name = azurerm_private_dns_zone.database.name + virtual_network_id = azurerm_virtual_network.default.id + resource_group_name = azurerm_resource_group.default.name + depends_on = [azurerm_subnet.database] +} diff --git a/deploy/azure-terraform/outputs.tf b/deploy/azure-terraform/outputs.tf new file mode 100644 index 00000000..3662abcc --- /dev/null +++ b/deploy/azure-terraform/outputs.tf @@ -0,0 +1,4 @@ +output "turnierplan_application_url" { + description = "The application URL of the turnierplan.NET App Service without a trailing slash." + value = local.application_url +} diff --git a/deploy/azure-terraform/resource-group.tf b/deploy/azure-terraform/resource-group.tf new file mode 100644 index 00000000..05d7aa2f --- /dev/null +++ b/deploy/azure-terraform/resource-group.tf @@ -0,0 +1,4 @@ +resource "azurerm_resource_group" "default" { + name = "rg-${var.name}" + location = var.location +} diff --git a/deploy/azure-terraform/storage-account.tf b/deploy/azure-terraform/storage-account.tf new file mode 100644 index 00000000..751746b3 --- /dev/null +++ b/deploy/azure-terraform/storage-account.tf @@ -0,0 +1,21 @@ +resource "azurerm_storage_account" "default" { + name = "st${replace(var.name, "-", "")}" + location = var.location + resource_group_name = azurerm_resource_group.default.name + + access_tier = "Hot" + account_tier = "Standard" + account_replication_type = var.storage_account_replication_type + + public_network_access_enabled = true + + lifecycle { + prevent_destroy = true + } +} + +resource "azurerm_storage_container" "images" { + name = "${var.name}-images" + storage_account_id = azurerm_storage_account.default.id + container_access_type = "blob" +} diff --git a/deploy/azure-terraform/terraform.tf b/deploy/azure-terraform/terraform.tf new file mode 100644 index 00000000..a063d5c4 --- /dev/null +++ b/deploy/azure-terraform/terraform.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.0.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.7.2" + } + } +} diff --git a/deploy/azure-terraform/variables.tf b/deploy/azure-terraform/variables.tf new file mode 100644 index 00000000..e1f42a73 --- /dev/null +++ b/deploy/azure-terraform/variables.tf @@ -0,0 +1,111 @@ +variable "name" { + description = "Name suffix for all resources" + type = string + nullable = false +} + +variable "location" { + description = "Location of the deployed resources" + type = string + nullable = false +} + +variable "turnierplan_container_registry" { + description = "The container registry to pull the image from" + type = string + default = "ghcr.io" + nullable = false +} + +variable "turnierplan_container_image" { + description = "The name of the container image to pull" + type = string + default = "turnierplan-net/turnierplan" + nullable = false +} + +variable "turnierplan_container_version" { + description = "The name and tag of the container image to pull" + type = string + default = "2026.2.0" + nullable = false +} + +variable "turnierplan_initial_user" { + description = "The user name for the initially created admin user" + type = string + nullable = false +} + +variable "turnierplan_initial_password" { + description = "The password to initially set for the created admin user" + type = string + nullable = false +} + +variable "turnierplan_additional_app_settings" { + description = "Additional configuration values for turnierplan.NET" + type = map(string) + nullable = false +} + +variable "app_service_plan_sku_name" { + description = "The SKU name to use for the app service plan" + type = string + nullable = false +} + +variable "app_service_custom_domain" { + description = "The domain name which should be bound to the app service (e.g. 'turnierplan.example.com') or null if no custom domain should be used." + type = string + nullable = true + default = null +} + +variable "app_insights_retention_in_days" { + description = "The retention period for application insights logs" + type = number + nullable = false +} + +variable "storage_account_replication_type" { + description = "The replication type to use for the storage account" + type = string + nullable = false +} + +variable "postgresql_availability_zone" { + description = "The availability zone to deploy the PostgreSQL server in" + type = number + nullable = false +} + +variable "postgresql_sku_name" { + description = "The name of the SKU to use for the PostgreSQL server" + type = string + nullable = false +} + +variable "postgresql_storage_size_mb" { + description = "The storage size in MB for the PostgreSQL server" + type = number + nullable = false +} + +variable "postgresql_storage_tier" { + description = "The storage tier for the PostgreSQL server" + type = string + nullable = false +} + +variable "postgresql_charset" { + description = "The charset to use for the PostgreSQL database" + type = string + nullable = false +} + +variable "postgresql_collation" { + description = "The collation to use for the PostgreSQL database" + type = string + nullable = false +} diff --git a/docs/pages/installation/azure-terraform.md b/docs/pages/installation/azure-terraform.md new file mode 100644 index 00000000..7ade0718 --- /dev/null +++ b/docs/pages/installation/azure-terraform.md @@ -0,0 +1,63 @@ +# Microsoft Azure (Terraform) + +Dieser Artikel beschreibt das Deployment von turnierplan.NET auf Microsoft Azure mithilfe des IaC-Tools Terraform. + +## Architektur + +Die Cloud-Architektur umfasst im Wesentlichen einen Azure App Service sowie eine verwaltete Azure PostgreSQL-Datenbank. Ergänzt werden diese Services von Azure Application Insights zur Erfassung von Telemetriedaten sowie von einem Azure Blob Storage Account, worin die hochgeladenen Bilddateien gespeichert werden. Für die Authentifizierung beim Storage Account verwendet der App Service eine System-Assigned Managed Identity. Um die Datenbank vor Zugriffen aus dem Internet zu schützen, wird für App Service und Datenbank ein virtuelles Netzwerk inklusive Subnetzen verwendet. Der Endnutzer greift direkt per HTTPS auf den Azure App Service zu. Hierbei wird entweder der Standard-Domainname verwendet - oder, optional, ein eigens definierter Domainname. + +![Architektur des Terraform-Moduls](./images/azure-terraform-architecture.drawio.png) + +## Verwendung + +Um das Modul zu verwenden, muss zunächst Terraform installiert sein. Zudem müssen die Provider `hashicorp/azurerm` und `hashicorp/random` verfügbar sein. Mit dem nachfolgenden Terraform-Skript kann das Terraform-Modul direkt aus der [GitHub-Repository](https://github.com/turnierplan-NET/turnierplan.NET-Terraform-Azure) verwendet werden. + +```terraform +module "turnierplan" { + source = "github.com/turnierplan-NET/turnierplan.NET//deploy/azure-terraform?ref=2026.2.0" + + # Use a name with a unique suffix to prevent naming collisions + name = "turnierplan-example" + location = "westeurope" + + # DON'T set a secure admin password here! Run the deployment, then log in using credentials + # specified below and change the admin password to a secure one in the web UI afterwards. + turnierplan_initial_user = "admin" + turnierplan_initial_password = "admin" + + turnierplan_additional_app_settings = { + # Additional settings as defined in the documentation: https://docs.turnierplan.net/configuration + "Turnierplan__InstanceName" = "turnierplan.NET on Azure" + } + + app_service_custom_domain = null + app_service_plan_sku_name = "B1" + app_insights_retention_in_days = 90 + storage_account_replication_type = "GRS" + postgresql_availability_zone = 3 + postgresql_sku_name = "B_Standard_B1ms" + postgresql_storage_size_mb = 32768 + postgresql_storage_tier = "P4" + postgresql_charset = "UTF8" + postgresql_collation = "de_DE.utf8" +} +``` + +Der Wert der Variable `name` wird als Suffix für die Namen aller erstellten Ressourcen verwendet. Um Konflikte bei global eindeutigen Ressourcennamen zu vermeiden, sollte der verwendete `name` möglichst eindeutig sein. Allerdings sollte der `name` nicht zu lang sein, da bei bestimmten Ressourcen auch Längenbegrenzungen für den Namen gelten. + +Standardmäßig ist die turnierplan.NET-Instanz über die App Service-Domain `.azurewebsites.net` erreichbar. Um eine eigene Domain zu verwenden, kann die Variable `app_service_custom_domain` entsprechend konfiguriert werden. Hierdurch wird ein Hostname-Binding mit einem durch Azure verwalteten SSL-Zertifikat konfiguriert. Hierfür muss entsprechend der [Microsoft-Doku](https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-custom-domain) ein entsprechender `CNAME`-Record (und ggf. auch ein `TXT`-Record) für die Domain angelegt werden. + +Unabhängig davon, ob eine eigene Domain konfiguriert wird, stellt das Modul den Output `turnierplan_application_url` bereit, welcher die vollständige URL der turnierplan.NET-Instanz beinhaltet. + +Die folgenden Azure-Ressourcen werden durch das Modul erstellt: + +![Auflistung der Ressourcen in Azure Portal-Darstellung](./images/azure-terraform-resources.png) + +!!! danger + Die PostgreSQL-Datenbank ist *ohne* Hochverfügbarkeit konfiguriert. Es wird nur ein Replika in der spezifizierten Availability Zone (`postgresql_availability_zone`) erstellt - vgl. [Terraform-Doku](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/postgresql_flexible_server) und [Microsoft-Doku](https://learn.microsoft.com/en-us/azure/postgresql/high-availability/concepts-high-availability) + +Nachdem das Deployment alle Ressourcen erstellt hat, kann auf die Weboberfläche mit der Domain des Azure App Service zugegriffen werden. Anschließend ist der Login mit den festgelegten Zugangsdaten möglich. Weitere Schritte sind auf der entsprechenden Seite [Erste Schritte](../getting-started/index.md) der Dokumentation beschrieben. + +## Aktualisierung + +Die verwendete Version von turnierplan.NET kann jederzeit auf eine neuere aktualisiert werden. Etwaige Datenbankmigrationen werden beim ersten Start sequenziell angewandt - auch wenn Versionen übersprungen werden. Allerdings sollten vor jeder Aktualisierung *immer* die [Release-Notes](https://github.com/turnierplan-NET/turnierplan.NET/releases) gelesen werden! Es kann jederzeit nicht-rückwärtskompatible Änderungen geben. diff --git a/docs/pages/installation/docker-compose.md b/docs/pages/installation/docker-compose.md new file mode 100644 index 00000000..f04496ef --- /dev/null +++ b/docs/pages/installation/docker-compose.md @@ -0,0 +1,86 @@ +# Docker Compose + +Die einfachste Möglichkeit, turnierplan.NET auf einem lokalen Rechner, einer VM oder einem dedizierten Server zu installieren, ist mithilfe von Docker Compose. Die nachfolgende YAML-Definition beinhaltet die notwendigen Container für Datenbank sowie den Anwendungsserver: + +```yaml +services: + turnierplan.database: + image: postgres:18 + environment: + - POSTGRES_PASSWORD=P@ssw0rd + - POSTGRES_DB=turnierplan + volumes: + - turnierplan-database-data:/var/lib/postgresql + networks: + - turnierplan + restart: unless-stopped + + turnierplan.application: + image: ghcr.io/turnierplan-net/turnierplan:2026.2.0 + depends_on: + - turnierplan.database + environment: + - Turnierplan__ApplicationUrl=http://localhost + - Database__ConnectionString=Host=turnierplan.database;Database=turnierplan;Username=postgres;Password=P@ssw0rd + volumes: + - turnierplan-application-data:/var/turnierplan + networks: + - turnierplan + restart: unless-stopped + ports: + - '80:8080' + +volumes: + turnierplan-application-data: + turnierplan-database-data: + +networks: + turnierplan: +``` + +Die URL, welche letztendlich für den Zugriff auf turnierplan.NET verwendet wird, sollte in der Umgebungsvariable `Turnierplan__ApplicationUrl` spezifiziert werden. Falls bspw. eine Domain `example.com` verwendet wird, sollte der Wert der Umgebungsvariable `https://example.com` sein. Falls turnierplan.NET im lokalen Netzwerk gehostet wird, könnte der Wert bspw. `http://192.168.0.187` sein. Es muss natürlich das korrekte Protokoll (HTTP vs. HTTPS) verwendet werden. + +Die Volume-Mounts sind im [nachfolgenden Abschnitt](#volume-mounts) näher beschrieben. Je nach Konfiguration kann das Volume-Mount vom turnierlpan.NET-Container auch überflüssig sein. + +Beim ersten Starten der Anwendung werden alle Datenbankmigrationen durchgeführt und es wird ein initialer Administrator-Benutzer angelegt. Die Zugangsdaten werden in den Container-Logs angezeigt. Mit diesen Zugangsdaten kann sich in der Weboberfläche unter [localhost:80](http://localhost:80) (bzw. je nach Konfiguration mit entsprechender Domain) eingeloggt werden. + +!!! danger + **Wichtige Sicherheitshinweise**: + + - Das Datenbankpasswort `POSTGRES_PASSWORD` in der Compose-Datei sollte durch ein zufällig generiertes Passwort ersetzt werden. Dementsprechend muss dann auch der Connection-String der Anwendung angepasst werden. + - Der turnierplan.NET-Server sollte niemals ohne Reverse Proxy mit SSL-Terminierung im Internet erreichbar sein. Hierfür kann z.B. [nginx](https://nginx.org/) verwendet werden. + +## Volume Mounts + +Die turnierplan.NET-Anwendung speichert diverse Dateien in folgendem Verzeichnis innerhalb vom Container: `/var/turnierplan`. Bei einer Standardkonfiguration sollte dieses Verzeichnis in einem Docker Volume oder vergleichbar persistiert werden. Die folgenden Daten werden innerhalb vom o.g. Verzeichnis gespeichert: + +- **Bild-Uploads**: Sofern keine anderweitige Speicherung von Bildern (wie z.B. S3) konfiguriert ist. +- **JWT Signatur-Schlüssel**: Sofern kein Schlüssel via Umgebungsvariable spezifiziert wird, wird ein zufällig generierter Schlüssel hier gespeichert. Siehe auch [Konfiguration der Authentifizierung](../configuration/index.md#authentifizierung). + +## Konfiguration + +turnierplan.NET bietet zahlreiche Konfigurationsmöglichkeiten zur Anbindung von Externen System sowie zur Individualisierung. Alle Optionen können mit Umgebungsvariablen gesetzt werden und sind in der [Konfigurationsanleitung](./configuration) aufgelistet und beschrieben. + +## Erste Schritte + +Die Anmeldung als Administrator erfolgt mit den Zugangsdaten, welche beim ersten Programmstart generiert und in den Container-Logs ausgegeben werden. Weitere Schritte sind auf der entsprechenden Seite [Erste Schritte](../getting-started/index.md) der Dokumentation beschrieben. + +!!! tip + Zum Abrufen der Container-Logs kann der Befehl `docker compose logs turnierplan.application` verwendet werden. + +## Aktualisierung + +Die verwendete Version von turnierplan.NET kann jederzeit auf eine neuere aktualisiert werden. Etwaige Datenbankmigrationen werden beim ersten Start sequenziell angewandt - auch wenn Versionen übersprungen werden. Allerdings sollten vor jeder Aktualisierung *immer* die [Release-Notes](https://github.com/turnierplan-NET/turnierplan.NET/releases) gelesen werden! Es kann jederzeit nicht-rückwärtskompatible Änderungen geben. + +Zudem wird empfohlen, vor jeder Aktualisierung der turnierplan.NET-Anwendung *oder* der verwendeten PostgreSQL-Version ein Datenbankupdate zu erstellen. Dies kann z.B. mit dem [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html)-Tool gemacht werden. + +## Fehlerbehebung + +Nachfolgend beschrieben sind Fehler, welche bei einer Neuinstallation auftreten können. + +### Verbindung per HTTP + +Beim Zugriff auf einen nicht-lokalen turnierplan.NET-Server via HTTP sollte standardmäßig ein Fehler *401 Unauthorized* erscheinen. Dies liegt daran, dass turnierplan.NET für die Authentifizierung nach dem Login Cookies verwendet, welche standardmäßig als *secure* ausgestellt werden. Dies hat zur Folge, dass Browser den Cookie nur bei lokalen Verbindungen oder über HTTPS mitschicken. Um turnierplan.NET dennoch verwenden zu können, muss die `Identity__UseInsecureCookies` auf `true` gesetzt werden. Siehe auch [Konfiguration der Authentifizierung](configuration.md#authentifizierung). + +!!! warning + Die Verwendung von HTTP-Verbindungen über das Internet ist **absolut nicht empfohlen**, da persönliche Daten und Passwörter somit unverschlüsselt übertragen werden würden. Zudem ist nicht ausgeschlossen, dass Teile der Webanwendung nicht korrekt funktionieren, falls diese HTTPS-exklusive Browser-APIs verwenden (bspw. Zwischenablage oder *crypto*). diff --git a/docs/pages/installation/images/azure-terraform-architecture.drawio.png b/docs/pages/installation/images/azure-terraform-architecture.drawio.png new file mode 100644 index 00000000..be3c7aa3 Binary files /dev/null and b/docs/pages/installation/images/azure-terraform-architecture.drawio.png differ diff --git a/docs/pages/installation/images/azure-terraform-resources.png b/docs/pages/installation/images/azure-terraform-resources.png new file mode 100644 index 00000000..3947befb Binary files /dev/null and b/docs/pages/installation/images/azure-terraform-resources.png differ diff --git a/docs/pages/installation/index.md b/docs/pages/installation/index.md index 98950d90..66653243 100644 --- a/docs/pages/installation/index.md +++ b/docs/pages/installation/index.md @@ -4,11 +4,7 @@ icon: lucide/server # Installation -Dieser Artikel beschreibt verschiedene Möglichkeiten, **turnierplan.NET** mithilfe des offiziellen Container-Images bereitzustellen. - -## Container-Image - -Innerhalb der GitHub-Organisation wird für jedes [Release](https://github.com/turnierplan-NET/turnierplan.NET/releases) das offizielle Container-Image veröffentlicht: [ghcr.io/turnierplan-net/turnierplan](https://github.com/turnierplan-NET/turnierplan.NET/pkgs/container/turnierplan) +Dieser Artikel beschreibt verschiedene Möglichkeiten, **turnierplan.NET** mithilfe des offiziellen Container-Images bereitzustellen. Innerhalb der GitHub-Organisation wird für jedes [Release](https://github.com/turnierplan-NET/turnierplan.NET/releases) das offizielle Container-Image veröffentlicht: [ghcr.io/turnierplan-net/turnierplan](https://github.com/turnierplan-NET/turnierplan.NET/pkgs/container/turnierplan) Um turnierplan.NET lokal zu testen, kann der folgende Befehl verwendet werden: @@ -22,86 +18,5 @@ Die Weboberfläche kann über [localhost:80](http://localhost:80) erreicht werde Für ein produktives Deployment gibt es nachfolgende Möglichkeiten basierend auf dem Container-Image. Weitere Methoden werden in der Zukunft ergänzt. -### Docker Compose - -Für die Installation auf einem lokalen Rechner oder einer VM kann im einfachsten Fall Docker Compose verwendet werden. Nachfolgend ein Minimalbeispiel für eine `docker-compose.yaml`: - -```yaml -services: - turnierplan.database: - image: postgres:18 - environment: - - POSTGRES_PASSWORD=P@ssw0rd - - POSTGRES_DB=turnierplan - volumes: - - turnierplan-database-data:/var/lib/postgresql - networks: - - turnierplan - restart: unless-stopped - - turnierplan.application: - image: ghcr.io/turnierplan-net/turnierplan:2026.2.0 - depends_on: - - turnierplan.database - environment: - - Turnierplan__ApplicationUrl=http://localhost - - Database__ConnectionString=Host=turnierplan.database;Database=turnierplan;Username=postgres;Password=P@ssw0rd - volumes: - - turnierplan-application-data:/var/turnierplan - networks: - - turnierplan - restart: unless-stopped - ports: - - '80:8080' - -volumes: - turnierplan-application-data: - turnierplan-database-data: - -networks: - turnierplan: -``` - -Die URL, welche letztendlich für den Zugriff auf turnierplan.NET verwendet wird, sollte in der Umgebungsvariable `Turnierplan__ApplicationUrl` spezifiziert werden. Falls bspw. eine Domain `example.com` verwendet wird, sollte der Wert der Umgebungsvariable `https://example.com` sein. Falls turnierplan.NET im lokalen Netzwerk gehostet wird, könnte der Wert bspw. `http://192.168.0.187` sein. Es muss natürlich das korrekte Protokoll (HTTP vs. HTTPS) verwendet werden. - -Die Volume-Mounts sind im [nachfolgenden Abschnitt](#volume-mounts) näher beschrieben. Je nach Konfiguration kann das Volume-Mount vom turnierlpan.NET-Container auch überflüssig sein. - -Beim ersten Starten der Anwendung werden alle Datenbankmigrationen durchgeführt und es wird ein initialer Administrator-Benutzer angelegt. Die Zugangsdaten werden in den Container-Logs angezeigt. Mit diesen Zugangsdaten kann sich in der Weboberfläche unter [localhost:80](http://localhost:80) (bzw. je nach Konfiguration mit entsprechender Domain) eingeloggt werden. - -!!! danger - **Wichtige Sicherheitshinweise**: - - - Das Datenbankpasswort `POSTGRES_PASSWORD` in der Compose-Datei sollte durch ein zufällig generiertes Passwort ersetzt werden. Dementsprechend muss dann auch der Connection-String der Anwendung angepasst werden. - - Der turnierplan.NET-Server sollte niemals ohne Reverse Proxy mit SSL-Terminierung im Internet erreichbar sein. Hierfür kann z.B. [nginx](https://nginx.org/) verwendet werden. - -## Volume Mounts - -Die turnierplan.NET-Anwendung speichert diverse Dateien in folgendem Verzeichnis innerhalb vom Container: `/var/turnierplan`. Bei einer Standardkonfiguration sollte dieses Verzeichnis in einem Docker Volume oder vergleichbar persistiert werden. Die folgenden Daten werden innerhalb vom o.g. Verzeichnis gespeichert: - -- **Bild-Uploads**: Sofern keine anderweitige Speicherung von Bildern (wie z.B. S3) konfiguriert ist. -- **JWT Signatur-Schlüssel**: Sofern kein Schlüssel via Umgebungsvariable spezifiziert wird, wird ein zufällig generierter Schlüssel hier gespeichert. Siehe auch [Konfiguration der Authentifizierung](../configuration/index.md#authentifizierung). - -## Konfiguration - -turnierplan.NET bietet zahlreiche Konfigurationsmöglichkeiten zur Anbindung von Externen System sowie zur Individualisierung. Alle Optionen können mit Umgebungsvariablen gesetzt werden und sind in der [Konfigurationsanleitung](./configuration) aufgelistet und beschrieben. - -## Erste Schritte - -Die Anmeldung als Administrator erfolgt mit den Zugangsdaten, welche beim ersten Programmstart generiert werden, bzw. in der Konfiguration vorgegeben sind. Weitere Schritte sind auf der entsprechenden Seite [Erste Schritte](../getting-started/index.md) der Dokumentation beschrieben. - -## Aktualisierung - -Die verwendete Version von turnierplan.NET kann jederzeit auf eine neuere aktualisiert werden. Etwaige Datenbankmigrationen werden beim ersten Start sequenziell angewandt - auch wenn Versionen übersprungen werden. Allerdings sollten vor jeder Aktualisierung *immer* die [Release-Notes](https://github.com/turnierplan-NET/turnierplan.NET/releases) gelesen werden! Es kann jederzeit nicht-rückwärtskompatible Änderungen geben. - -Zudem wird empfohlen, vor jeder Aktualisierung der turnierplan.NET-Anwendung *oder* der verwendeten PostgreSQL-Version ein Datenbankupdate zu erstellen. Dies kann z.B. mit dem [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html)-Tool gemacht werden. - -## Fehlerbehebung - -Nachfolgend beschrieben sind Fehler, welche bei einer Neuinstallation auftreten können. - -### Verbindung per HTTP - -Beim Zugriff auf einen nicht-lokalen turnierplan.NET-Server via HTTP sollte standardmäßig ein Fehler *401 Unauthorized* erscheinen. Dies liegt daran, dass turnierplan.NET für die Authentifizierung nach dem Login Cookies verwendet, welche standardmäßig als *secure* ausgestellt werden. Dies hat zur Folge, dass Browser den Cookie nur bei lokalen Verbindungen oder über HTTPS mitschicken. Um turnierplan.NET dennoch verwenden zu können, muss die `Identity__UseInsecureCookies` auf `true` gesetzt werden. Siehe auch [Konfiguration der Authentifizierung](configuration.md#authentifizierung). - -!!! warning - Die Verwendung von HTTP-Verbindungen über das Internet ist **absolut nicht empfohlen**, da persönliche Daten und Passwörter somit unverschlüsselt übertragen werden würden. Zudem ist nicht ausgeschlossen, dass Teile der Webanwendung nicht korrekt funktionieren, falls diese HTTPS-exklusive Browser-APIs verwenden (bspw. Zwischenablage oder *crypto*). +- [Docker Compose](./docker-compose.md) für die installation auf einem dedizierten Server oder einer VM +- [Azure (Terraform)](./azure-terraform.md) für ein Deployment innerhalb einer Microsoft Azure Subscription diff --git a/docs/requirements.txt b/docs/requirements.txt index 32b234e8..4a51f3fd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1 @@ -zensical==0.0.24 +zensical==0.0.33 diff --git a/docs/zensical.toml b/docs/zensical.toml index 592caf7f..38478182 100644 --- a/docs/zensical.toml +++ b/docs/zensical.toml @@ -16,7 +16,11 @@ extra_css = ["assets/turnierplan.css"] nav = [ { "Startseite" = "index.md" }, - { "Installation" = "installation/index.md" }, + { "Installation" = [ + "installation/index.md", + { "Docker Compose" = "installation/docker-compose.md" }, + { "Azure (Terraform)" = "installation/azure-terraform.md" } + ]}, { "Konfiguration" = "configuration/index.md" }, { "Erste Schritte" = "getting-started/index.md" }, { "Releases" = "releases/index.md" } @@ -42,7 +46,6 @@ features = [ "navigation.instant", "navigation.instant.prefetch", "navigation.path", - "navigation.sections", "navigation.top", "navigation.tracking", "search.highlight", diff --git a/misc/bump-version.sh b/misc/bump-version.sh index 75ef8a77..a4843652 100644 --- a/misc/bump-version.sh +++ b/misc/bump-version.sh @@ -33,7 +33,9 @@ update_file () { sed -i -e "s/${current_version//./\\.}/$next_version/g" "../$1" } -update_file "src/version.xml" +update_file "deploy/azure-terraform/variables.tf" +update_file "docs/pages/installation/azure-terraform.md" +update_file "docs/pages/installation/docker-compose.md" update_file "src/Turnierplan.App/Client/package.json" update_file "src/Turnierplan.App/Client/src/environments/environment.prod.ts" -update_file "docs/pages/installation/index.md" +update_file "src/version.xml" \ No newline at end of file