diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index e13ff301..586b75ad 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -89,22 +89,22 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v5
- - name: "Set up Python 3.12"
+ - name: "Set up Python 3.x"
uses: actions/setup-python@v5
with:
- python-version: "3.12"
+ python-version: "3.x"
- name: "Install requirements"
run: "python3 -m pip install -r requirements.txt"
working-directory: "docs"
- name: "Build documentation"
- run: "python3 -m mkdocs build"
+ run: "zensical build --clean"
working-directory: "docs"
- name: "Setup Pages"
uses: actions/configure-pages@v5
- - name: Upload artifact
+ - name: "Upload artifact"
uses: actions/upload-pages-artifact@v3
with:
path: "docs/site"
- - name: Deploy to GitHub Pages
+ - name: "Deploy to GitHub Pages"
id: deployment
uses: actions/deploy-pages@v4
diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml
index 977cc976..15df5328 100644
--- a/.github/workflows/documentation.yaml
+++ b/.github/workflows/documentation.yaml
@@ -12,13 +12,13 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- - name: "Set up Python 3.12"
+ - name: "Set up Python 3.x"
uses: actions/setup-python@v5
with:
- python-version: "3.12"
+ python-version: "3.x"
- name: "Install requirements"
run: "python3 -m pip install -r requirements.txt"
working-directory: "docs"
- name: "Build documentation"
- run: "python3 -m mkdocs build"
+ run: "zensical build --clean"
working-directory: "docs"
diff --git a/README.md b/README.md
index 8f184447..7053eafe 100644
--- a/README.md
+++ b/README.md
@@ -6,20 +6,12 @@
## Introduction
-**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. Some screenshots can be seen in the [section at the end](#screenshots).
+**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.
-> [!NOTE]
-> The user interface is currently only available in German 🇩🇪
-
-## Installation
-
-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 install **turnierplan.NET** on your server, please visit the [Installation guide](https://docs.turnierplan.net/installation).
-## Documentation
-
-Visit the **turnierplan.NET** documentation using the following link: [docs.turnierplan.net](https://docs.turnierplan.net)
-
-The documentation sources are located in the `docs` directory. See the [docs readme](docs/README.md) for further information on how to edit and build the documentation.
+> [!NOTE]
+> The user interface and documentation are currently only available in German 🇩🇪
## Development
diff --git a/docs/README.md b/docs/README.md
index c8f9dfa2..ba869c23 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,16 +1,16 @@
## turnierplan.NET · Documentation
-This directory contains the source markdown files and mkdocs configuration for the publically available turnierplan.NET documentation: [https://docs.turnierplan.net](https://docs.turnierplan.net).
+This directory contains the turnierplan.NET documentation. The content files use an extended markdown format and are build into static HTML using [zensical](https://zensical.org). The documentation is hosted at [docs.turnierplan.net](https://docs.turnierplan.net).
-In order to build the documentation locally, you must first install Python and [mkdocs](https://www.mkdocs.org):
+In order to build the documentation locally, you must first install Python and `zensical`:
```
pip install -r requirements.txt
```
-Next, you can either view the rendered documentation using the mkdocs-build-in server or you can generate the static website files:
+Next, you can either view the rendered documentation using the zensical built-in server or you can generate the static website files:
```
-python3 -m mkdocs serve # starts a local web server on port 8000
-python3 -m mkdocs build # generates static web site artifacts into the 'site' directory
+python3 -m zensical serve # starts a local web server on port 8000
+python3 -m zensical build # generates static web site artifacts into the 'site' directory
```
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
deleted file mode 100644
index b144ae2f..00000000
--- a/docs/mkdocs.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-site_name: "turnierplan.NET"
-site_author: "turnierplan.NET"
-site_url: "https://docs.turnierplan.net"
-site_description: "The administrator and user documentation for turnierplan.NET"
-
-repo_url: "https://github.com/turnierplan-NET/turnierplan.NET"
-edit_uri: "blob/main/docs/pages/"
-
-copyright: "Copyright © 2026 Elias Hörner"
-
-docs_dir: "pages"
-
-theme:
- name: mkdocs
- color_mode: auto
- user_color_mode_toggle: true
- nav_style: dark
- navigation_depth: 4
- locale: en
-
-nav:
- - Home: "index.md"
- - Installation: "installation.md"
- - About:
- - Releases: "https://github.com/turnierplan-NET/turnierplan.NET/releases"
- - License: "https://github.com/turnierplan-NET/turnierplan.NET/blob/main/LICENSE"
-
-extra_css:
- - "assets/turnierplan.css"
-
-markdown_extensions:
- - admonition:
diff --git a/docs/pages/assets/turnierplan.css b/docs/pages/assets/turnierplan.css
deleted file mode 100644
index ac947a92..00000000
--- a/docs/pages/assets/turnierplan.css
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Some style improvements for the footer
- */
-
-footer hr {
- opacity: 0.2;
-}
-
-footer p {
- font-size: 0.8em;
- margin-bottom: 0.3em;
-}
-
-/*
- * Remove the sidebar on the homepage, copied from the mkdocs.org documentation
- * => https://github.com/mkdocs/mkdocs/blob/2862536793b3c67d9d83c33e0dd6d50a791928f8/docs/css/extra.css#L48
- */
-
-body.homepage > div.container > div.row > div.col-md-3 {
- display: none;
-}
-
-body.homepage > div.container > div.row > div.col-md-9 {
- margin-left: 0;
- flex: 0 0 100%;
- max-width: 100%;
-}
-/*
- * Some additional style changes for the homepage
- */
-
-body.homepage h1 {
- margin-top: 2em;
- margin-bottom: 0.7em;
- text-align: center;
- font-weight: bold;
-}
-
-body.homepage > div.container p:first-of-type {
- text-align: center;
-}
-
-body.homepage > div.container img:first-of-type {
- padding: unset;
- border: unset;
-}
diff --git a/docs/pages/configuration.md b/docs/pages/configuration.md
new file mode 100644
index 00000000..9c8d2c10
--- /dev/null
+++ b/docs/pages/configuration.md
@@ -0,0 +1,7 @@
+---
+icon: lucide/wrench
+---
+
+# Konfiguration
+
+Anleitung für Konfiguration
diff --git a/docs/pages/getting-started.md b/docs/pages/getting-started.md
new file mode 100644
index 00000000..33d5927f
--- /dev/null
+++ b/docs/pages/getting-started.md
@@ -0,0 +1,7 @@
+---
+icon: lucide/rocket
+---
+
+# Erste Schritte
+
+Anleitung für Erste Schritte
diff --git a/docs/pages/img/favicon.ico b/docs/pages/images/favicon.ico
similarity index 100%
rename from docs/pages/img/favicon.ico
rename to docs/pages/images/favicon.ico
diff --git a/docs/pages/images/logo-64.png b/docs/pages/images/logo-64.png
new file mode 100644
index 00000000..ae427211
Binary files /dev/null and b/docs/pages/images/logo-64.png differ
diff --git a/docs/pages/img/logo-192.png b/docs/pages/img/logo-192.png
deleted file mode 100644
index 478d9cad..00000000
Binary files a/docs/pages/img/logo-192.png and /dev/null differ
diff --git a/docs/pages/index.md b/docs/pages/index.md
index b063cdca..a90d4d2c 100644
--- a/docs/pages/index.md
+++ b/docs/pages/index.md
@@ -1,13 +1,28 @@
-# turnierplan.NET
+---
+icon: lucide/house
+---
-**turnierplan.NET** ist a free and open-source web application for football tournaments
+# Startseite
-
-

+turnierplan.NET ist eine **Open-Source Webanwendung zur Organisation von Turnieren** in Fußballvereinen ([GitHub](https://github.com/turnierplan-NET/turnierplan.NET)).
+
+
+- :lucide-land-plot: Flexible Spielpläne
+- :lucide-shield-user: Benutzerverwaltung
+- :lucide-globe: Öffentliche Ansicht für Besucher
+- :lucide-file-text: Export von PDF-Dokumenten
+- :lucide-settings: Zahlreiche Konfigurationsmöglichkeiten
+- :lucide-mail: Verwaltung von Turnieranmeldungen
-
+## Made in Germany
+
+turnierplan.NET wird hauptsächlich von Fußballfreunden aus dem nordöstlichen Baden-Württemberg, Deutschland verwendet und maintained 🇩🇪.
+
+Aus diesem Grund sind die Benutzeroberfläche sowie die Dokumentation aktuell ausschließlich in deutscher Sprache verfügbar. Zudem sind die verwendeten Begriffe und Funktionen speziell für die hierzulande üblichen Turniere und Veranstaltungen ausgelegt. Verbesserungsvorschläge sind jederzeit gerne gesehen und können im [Repo](https://github.com/turnierplan-NET/turnierplan.NET) hinterlassen werden.
+
+Falls turnierplan.NET dennoch für deinen Verein / deine Organisation infrage kommt: Der Quelltext und die Container-Images sind unter der [AGPL-3.0](https://github.com/turnierplan-NET/turnierplan.NET/blob/main/LICENSE) lizenziert und dementsprechend frei verwendbar. Mögliche Vorgehensweisen zum Erstellen eines eigenen Setups sind in der [Installationsanleitung](installation.md) beschrieben.
-## Getting Started
+## Technische Dokumentation
-To set up an instance of **turnierplan.NET** on your own server, visit the [Installation](installation.md) guide.
+Der Großteil der Anwendung ist in C# geschrieben und basiert auf dem [ASP.NET Core](https://dotnet.microsoft.com/en-us/apps/aspnet) Framework. Die primäre Datenbank, welche von turnierplan.NET verwendet wird, ist [PostgreSQL](https://www.postgresql.org/). Bilddateien werden außerhalb der Datenbank als lokale Dateien oder in einem cloud-basierten Blob-Storage gespeichert. Sämtliche administrative Aufgaben wie das Anlegen von Nutzern oder das Planen und Durchführen von Turnieren werden mit dem turnierplan.NET *Portal* erledigt. Dies ist eine [SPA](https://de.wikipedia.org/wiki/Single-Page-Webanwendung) basierend auf dem [Angular](https://angular.dev/) Framework. Öffentlich sichtbare HTML-Seiten werden allerdings direkt von der ASP.NET-Anwendung gerendert und bereitgestellt.
diff --git a/docs/pages/installation.md b/docs/pages/installation.md
index c9bf6655..d3e2594e 100644
--- a/docs/pages/installation.md
+++ b/docs/pages/installation.md
@@ -1,195 +1,7 @@
-# Installation
-
-**turnierplan.NET** comes as a pre-built container image which can be deployed with minimal configuration. The image is available on GitHub: [ghcr.io/turnierplan-net/turnierplan](https://github.com/turnierplan-NET/turnierplan.NET/pkgs/container/turnierplan)
-
-## Getting Started
-
-In the simplest case, you can configure the container to use an in-memory data store. Note that this in-memory store is only meant for quick testing and is obviously not suitable for a production environment.
-
-```shell
-docker run -p 80:8080 -e Database__InMemory="true" ghcr.io/turnierplan-net/turnierplan:latest
-```
-
-You should see the following output. The credentials of the initial admin user are displayed in the container logs. You can now open up
http://localhost in your browser and log in using those credentials.
-
-```
- __ ___ __
- /\ \__ __ /\_ \ /\ \__
- \ \ ,_\ __ __ _ __ ___ /\_\ __ _ __ _____\//\ \ __ ___ ___ __\ \ ,_\
- \ \ \/ /\ \/\ \/\`'__\/' _ `\/\ \ /'__`\/\`'__\/\ '__`\\ \ \ /'__`\ /' _ `\ /' _ `\ /'__`\ \ \/
- \ \ \_\ \ \_\ \ \ \/ /\ \/\ \ \ \/\ __/\ \ \/ \ \ \L\ \\_\ \_/\ \L\.\_/\ \/\ \ __/\ \/\ \/\ __/\ \ \_
- \ \__\\ \____/\ \_\ \ \_\ \_\ \_\ \____\\ \_\ \ \ ,__//\____\ \__/.\_\ \_\ \_\/\_\ \_\ \_\ \____\\ \__\
- \/__/ \/___/ \/_/ \/_/\/_/\/_/\/____/ \/_/ \ \ \/ \/____/\/__/\/_/\/_/\/_/\/_/\/_/\/_/\/____/ \/__/
- \ \_\
- \/_/ v2025.4.0
-
-info: Turnierplan.ImageStorage.Local.LocalImageStorage[0]
- Using the following directory for local image storage: '/var/turnierplan/images'
-info: Turnierplan.App.DatabaseMigrator[0]
- An initial user was created: You can log in using "admin" and the password "53fe6bac-1050-4801-bb11-be2dbd479d66". IMMEDIATELY change this password when running in a production environment!
-info: Microsoft.Hosting.Lifetime[14]
- Now listening on: http://[::]:8080
-info: Microsoft.Hosting.Lifetime[0]
- Application started. Press Ctrl+C to shut down.
-info: Microsoft.Hosting.Lifetime[0]
- Hosting environment: Production
-info: Microsoft.Hosting.Lifetime[0]
- Content root path: /app
-```
-
-## Persisting Data
-
-The application stores the following data in the `/var/turnierplan` directory:
-
-- `/var/turnierplan/identity/jwt-signing-key.bin` - The SHA512 signature key used to sign and verify JWT tokens.
-- `/var/turnierplan/images/**` - If not configured otherwise (see [section below](#storing-images)), this folder will contain all uploaded image files.
-
-Therefore, there should always be a volume mapping for this path.
-
-## Environment Variables
-
-For a basic installation, the following environment variables must be set:
-
-| Environment Variable | Description |
-|-------------------------------|--------------------------------------------------------------|
-| `Turnierplan__ApplicationUrl` | The URL used to access the website. |
-| `Database__ConnectionString` | The PostgreSQL connection string with read/write permission. |
-
-The following environment variables can be set if you want to enable specific features or modify default behavior:
-
-| Environment Variable | Description | Default |
-|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
-| `ApplicationInsights__ConnectionString` | Can be set if you wish that your instance sends telemetry data to [Azure Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview). | - |
-| `Identity__AccessTokenLifetime` | Defines the lifetime of issued JWT access tokens. | `00:03:00` |
-| `Identity__RefreshTokenLifetime` | Defines the lifetime of issued JWT refresh tokens. | `1.00:00:00` |
-| `Turnierplan__InstanceName` | The instance name is displayed in the header/footer of the public pages. If not specified, the string `turnierplan.NET` will be shown instead. | - |
-| `Turnierplan__LogoUrl` | The URL of the custom logo to be displayed in the header of the public pages. If not specified, the turnierplan.NET logo will be shown instead. | - |
-| `Turnierplan__ImprintUrl` | The URL of your external imprint page if you want it to be linked on the public pages. | - |
-| `Turnierplan__PrivacyUrl` | The URL of your external privacy page if you want it to be linked on the public pages. | - |
-| `Turnierplan__ImageMaxSize` | The maximum allowed file size when uploading an image file. The default value equates to 8 MiB (8 · 1024 · 1024) | `8388608` |
-| `Turnierplan__ImageQuality` | Uploaded images are compressed using the `webp` format with the specified quality. A value of `100` will result in lossless compression being uesd. | `80` |
-
-!!! note
- The token lifetimes must be specified as .NET `TimeSpan` strings. For example `00:03:00` means 3 minutes or `1.00:00.00` means 1 day.
-
-## Docker Compose
-
-A minimal recommended configuration for a production environment is shown in the following docker compose example:
-
-```yaml
-services:
- turnierplan.database:
- image: postgres:latest
- environment:
- - POSTGRES_PASSWORD=P@ssw0rd
- - POSTGRES_DB=turnierplan
- volumes:
- - turnierplan-database-data:/var/lib/postgresql/data
- networks:
- - turnierplan
- restart: unless-stopped
-
- turnierplan.app:
- image: ghcr.io/turnierplan-net/turnierplan:latest
- depends_on:
- - turnierplan.database
- environment:
- - Turnierplan__ApplicationUrl=http://localhost
- - Database__ConnectionString=Host=turnierplan.database;Database=turnierplan;Username=postgres;Password=P@ssw0rd
- volumes:
- - turnierplan-app-data:/var/turnierplan
- networks:
- - turnierplan
- restart: unless-stopped
- ports:
- - '80:8080'
-
-volumes:
- turnierplan-database-data:
- turnierplan-app-data:
-
-networks:
- turnierplan:
-```
-
-!!! tip
- It is recommended to not use the latest tag. Rather, pin your docker services to a specific image version.
-
-Feel free to modify the environment variables or add additional ones as described in the [environment variables](#environment-variables) section above.
+---
+icon: lucide/server
+---
-## Storing Images
-
-By default, all uploaded image files are stored in the `/var/turnierplan/images` directory. Whilst this is a very simple solution, it also means that the turnierplan.NET backend will serve all image files which can potentially lead to high load on the application server. Alternatively, you can configure the application to save image files to an external storage service. This way, clients can load the images directly from the external service.
-
-!!! warning
- The differnt implementations do not necessarily use the same folder structure to organizie the files. Because of this, migrating from one image storage implementation to another can be difficult - so choose wisely!
-
-The following implementations are currently available:
-
-- **Local** - The default, which saves images in a local folder as described above
-- **AWS S3** (or compatible)
-- **Azure Blob Storage**
-
-### Configuring AWS S3
-
-To store uploaded images in an AWS S3 or S3-compatible bucket, add the following environment variables to your deployment:
-
-| Environment Variable | Description |
-|---------------------------------|--------------------------------------------------|
-| `ImageStorage__Type` | The image storage type, **must** be `S3`. |
-| `ImageStorage__RegionEndpoint` | The AWS region endpoint, such as `eu-central-1`. |
-| `ImageStorage__ServiceUrl` | The service URL when using a non-AWS S3 bucket. |
-| `ImageStorage__AccessKey` | The access key identifier. |
-| `ImageStorage__AccessKeySecret` | The access key secret. |
-| `ImageStorage__BucketName` | The name of the bucket. |
-
-The access key must have permissions to create, read and delete objects.
-
-The `RegionEndpoint` and `ServiceUrl` variables are *mutually exclusive*. Use the former if you are using an AWS S3 bucket and use the latter if you use a S3-compatible bucket from a third party.
-
-### Configuring Azure Blob Storage
-
-To store uploaded images in an [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs/) container, add the following environment variables to your deployment:
-
-| Environment Variable | Description |
-|------------------------------------|----------------------------------------------|
-| `ImageStorage__Type` | The image storage type, **must** be `Azure`. |
-| `ImageStorage__StorageAccountName` | The name of the storage account. |
-| `ImageStorage__ContainerName` | The name of the blob container. |
-
-By default, a [DefaultAzureCredential](https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) will be used. Therefore, if you use Azure Managed Identities, you won't have to do any further configuration. In addition, this implementation supports two additional means of authentication listed below.
-
-When using Entra ID based authentication, the managed identity / app registration must have permission to create/read/delete blobs in the storage account. This can be achieved by assigning the [Storage Blob Data Contributor](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/storage#storage-blob-data-contributor) role.
-
-#### Access key authentication
-
-Refer to the [documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal) on how to view and manage the access keys.
-
-The following environment variables must be set to enable access key authentication:
-
-| Environment Variable | Description |
-|-------------------------------|--------------------------------------------------|
-| `ImageStorage__UseAccountKey` | Set to `true` to use account key authentication. |
-| `ImageStorage__AccountKey` | The value of the account key. |
-
-#### Client secret authentication
-
-If you have an Entra ID app registration with the necessary permissions on the storage account, you can set the following environment variables to enable client secret authentication:
-
-| Environment Variable | Description |
-|---------------------------------|---------------------------------------------------------|
-| `ImageStorage__UseClientSecret` | Set to `true` to use client credentials authentication. |
-| `ImageStorage__TenantId` | The tenant id where the app registration resides. |
-| `ImageStorage__ClientId` | The client id of the *app registration*. |
-| `ImageStorage__ClientSecret` | The value of the client secret. |
-
-## Troubleshooting
-
-Below are troubleshooting steps for some issues you might encounter during installation.
-
-### Connecting over HTTP
-
-If you are connecting to a remote (non-`localhost`) turnierplan.NET server via HTTP, you should see a *401 Unauthorized* error after logging in with your valid credentials. This is because turnierplan.NET uses secure cookies by default. You can set the `Identity__UseInsecureCookies` environment variable to `true` to change this behavior.
+# Installation
-!!! danger
- Using HTTP is obviously not the way to go if you are connecting over the internet. For local setups this might be fine, though it is still discouraged. Most importantly, it is **not officially supported** because some parts of the client application rely on HTTPS-only browser APIs to work properly (such as clipboard or crypto).
+Anleitung für Installation
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 016bb16d..32b234e8 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1 +1 @@
-mkdocs
+zensical==0.0.24
diff --git a/docs/zensical.toml b/docs/zensical.toml
new file mode 100644
index 00000000..430693f9
--- /dev/null
+++ b/docs/zensical.toml
@@ -0,0 +1,61 @@
+[project]
+
+site_name = "turnierplan.NET Dokumentation"
+site_author = "turnierplan.NET"
+site_url = "https://docs.turnierplan.net/"
+site_description = "Admin- und Benutzerdokumentation für turnierplan.NET"
+
+repo_name = "turnierplan-NET/turnierplan.NET"
+repo_url = "https://github.com/turnierplan-NET/turnierplan.NET"
+edit_uri = "blob/main/docs/pages/"
+
+copyright = "Copyright © 2026 Elias Hörner"
+
+docs_dir = "pages"
+
+nav = [
+ { "Startseite" = "index.md" },
+ { "Installation" = "installation.md" },
+ { "Konfiguration" = "configuration.md" },
+ { "Erste Schritte" = "getting-started.md" }
+]
+
+[project.theme]
+
+favicon = "images/favicon.ico"
+logo = "images/logo-64.png"
+language = "de"
+font = false
+
+features = [
+ "content.action.edit",
+ "content.code.annotate",
+ "content.code.copy",
+ "content.footnote.tooltips",
+ "content.tabs.link",
+ "content.tooltips",
+ "navigation.expand",
+ "navigation.footer",
+ "navigation.indexes",
+ "navigation.instant",
+ "navigation.instant.prefetch",
+ "navigation.path",
+ "navigation.sections",
+ "navigation.top",
+ "navigation.tracking",
+ "search.highlight",
+ # "toc.follow",
+ # "toc.integrate",
+]
+
+[[project.theme.palette]]
+scheme = "default"
+accent = "green"
+toggle.icon = "lucide/sun"
+toggle.name = "In dunklen Modus wechseln"
+
+[[project.theme.palette]]
+scheme = "slate"
+accent = "green"
+toggle.icon = "lucide/moon"
+toggle.name = "In hellen Modus wechseln"