diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml new file mode 100644 index 00000000..eb67becd --- /dev/null +++ b/.github/workflows/documentation.yaml @@ -0,0 +1,52 @@ +on: + push: + branches: + - main + paths: + - "docs/**" + pull_request: + branches: + - main + paths: + - "docs/**" + types: [opened, synchronize, reopened] + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + documentation: + name: "Documentation" + runs-on: ubuntu-24.04 + env: + DEPLOY_TO_GITHUB_PAGES: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v5 + - name: "Set up Python 3.12" + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: "Install requirements" + run: "python3 -m pip install -r requirements.txt" + working-directory: "docs" + - name: "Build documentation" + run: "python3 -m mkdocs build" + working-directory: "docs" + - name: "Setup Pages" + uses: actions/configure-pages@v5 + if: ${{ env.DEPLOY_TO_GITHUB_PAGES == 'true' }} + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + if: ${{ env.DEPLOY_TO_GITHUB_PAGES == 'true' }} + with: + path: "docs/site" + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + if: ${{ env.DEPLOY_TO_GITHUB_PAGES == 'true' }} diff --git a/README.md b/README.md index 4ce20359..5160c5c1 100644 --- a/README.md +++ b/README.md @@ -2,172 +2,28 @@ *An open-source tournament planning application for football clubs* -[![.github/workflows/validate.yaml](https://github.com/turnierplan-NET/turnierplan.NET/actions/workflows/validate.yaml/badge.svg)](https://github.com/turnierplan-NET/turnierplan.NET/actions/workflows/validate.yaml) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=bugs)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=coverage)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) +[![.github/workflows/validate.yaml](https://github.com/turnierplan-NET/turnierplan.NET/actions/workflows/validate.yaml/badge.svg)](https://github.com/turnierplan-NET/turnierplan.NET/actions/workflows/validate.yaml) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=bugs)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=turnierplan-NET_turnierplan.NET&metric=coverage)](https://sonarcloud.io/summary/new_code?id=turnierplan-NET_turnierplan.NET) [![NuGet Version](https://img.shields.io/nuget/v/Turnierplan.Adapter)](https://www.nuget.org/packages/Turnierplan.Adapter) ## 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). -This readme describes how to deploy the application using the pre-built containers or how to get a local development environment up and running. - > [!NOTE] > The user interface is currently only available in German 🇩🇪 -## Deployment - -**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) - -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 *not stable* for production! - -```shell -docker run -p 80:8080 -e Turnierplan__ApplicationUrl="http://localhost" -e Database__InMemory="true" ghcr.io/turnierplan-net/turnierplan:latest -``` - -A PostgreSQL database can be configured by specifying the `Database__ConnectionString` environment variable: - -```shell -docker run -p 80:8080 -e Turnierplan__ApplicationUrl="http://localhost" -e Database__ConnectionString="" ghcr.io/turnierplan-net/turnierplan:latest -``` - -The credentials of the initial admin user are displayed in the container logs. - -> [!CAUTION] -> In a production environment, you should immediately change the administrator password to a secure one! - -### Persisting Data - -To persist the **turnierplan.NET** application data, create a Docker volume mapping to the `/var/turnierplan` folder inside the container. - -> [!CAUTION] -> This folder contains the JWT signing key for issued access/refresh tokens. - -### 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, `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. | - | - -> 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 Example - -You can use the following docker compose file to get a complete instance running on your machine: - -```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: -``` +## Installation -> [!TIP] -> It is recommended to *not* use the `latest` tag. Rather, pin your docker services to a specific image version. - -### Storing images in AWS S3 - -If you prefer 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. - -> [!NOTE] -> 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. - -### Storing images in Azure Blob Storage - -If you prefer to store uploaded images in Microsoft [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs/), 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. - -#### Authenticating using access key - -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. | - -#### Authenticate using client secret - -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. | +If you want to install **turnierplan.NET** on your server, please visit the [Installation guide](https://docs.turnierplan.net/installation). ## Documentation -The developer documentation of **turnierplan.NET** is located in the `docs` directory of this repository. Information about the container images and how to build them can be found in the [README.md in the docker folder](./docker/README.md). +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. ## Development -First, you need to install the following tools: +This section describes how to set up the development environment. First, you need to install the following tools: - .NET 10.0 SDK - node.js v24.x and npm @@ -177,26 +33,10 @@ To run the application from source, follow these steps: 1. Open the `src/Turnierplan.slnx` solution and navigate to the docker compose file located under `Solution Items`. Run the `turnierplan.database` docker compose service. This will start up the PostgreSQL database for local development. 2. Navigate to the `Turnierplan.App` project and run the `Turnierplan.App` launch configuration. This will start the backend using port `45000`. -3. Open a terminal and navigate to the `src/Turnierplan.App/Client` directory. Run `npm install` to install the node dependencies. Next, you can start the client application by typing `npm run start`. Note that this will only work if the backend application has previously been run because the client app startup depends on OpenAPI files generated by the backend build process. +3. Open a terminal and navigate to the `src/Turnierplan.App/Client` directory. Run `npm install` to install the node dependencies. Next, you can start the client application by typing `npm run start`. 4. Access the client application using [http://localhost:45001](http://localhost:45001) and log in using default credentials. The user name is `admin` and the password is `P@ssw0rd`. When running locally, the API documentation can be viewed by opening [http://localhost:45000/scalar](http://localhost:45000/scalar). -## Screenshots - -Below are some screenshots of the application: - -1. *Creating a new tournament:* - ![Screenshot that shows the create tournament page](images/create-tournament.png) -2. *Exporting a tournament to PDF document:* - ![Screenshot that shows the PDF export page](images/export-pdf.png) -3. *Viewing a timetable of multiple tournaments:* - ![Screenshot that shows the folder timetable page](images/folder-timetable.png) -4. *Reporting the outcome of a match:* - ![Screenshow that shows the match reporting dialog](images/report-match.png) - -## Turnierplan.Adapter - -If you want to use the **turnierplan.NET** API programatically in a .NET environment, you can use the `Turnierplan.Adapter` [NuGet package](https://www.nuget.org/packages/Turnierplan.Adapter) which contains all model classes and an abstraction layer to easily query the API endpoints. - -Please refer to the [package readme](src/Turnierplan.Adapter/README.md) for details and usage examples. +> [!NOTE] +> The solution must be built first before the client application can be started. This is because the client application startup depends on OpenAPI files generated during the solution build. diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..1320f90e --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +site diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..c8f9dfa2 --- /dev/null +++ b/docs/README.md @@ -0,0 +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). + +In order to build the documentation locally, you must first install Python and [mkdocs](https://www.mkdocs.org): + +``` +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: + +``` +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 +``` diff --git a/docs/image-credit.md b/docs/image-credit.md deleted file mode 100644 index f46cffe6..00000000 --- a/docs/image-credit.md +++ /dev/null @@ -1,10 +0,0 @@ -# Image Credit - -**All vector illustrations** -Local source files: `src/Turnierplan.App/Client/src/assets/illustration/*.svg` -Original link: https://undraw.co - -**Identity pages image** -Local source file: `src/Turnierplan.App/Client/src/assets/identity-2025.04.jpg` -Created on: `2023-04-09` by Elias Hörner -Original link: N/A diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 00000000..b144ae2f --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,32 @@ +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 new file mode 100644 index 00000000..ac947a92 --- /dev/null +++ b/docs/pages/assets/turnierplan.css @@ -0,0 +1,46 @@ +/* + * 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/img/favicon.ico b/docs/pages/img/favicon.ico new file mode 100644 index 00000000..e4cb444d Binary files /dev/null and b/docs/pages/img/favicon.ico differ diff --git a/docs/pages/img/logo-192.png b/docs/pages/img/logo-192.png new file mode 100644 index 00000000..478d9cad Binary files /dev/null and b/docs/pages/img/logo-192.png differ diff --git a/docs/pages/index.md b/docs/pages/index.md new file mode 100644 index 00000000..b063cdca --- /dev/null +++ b/docs/pages/index.md @@ -0,0 +1,13 @@ +# turnierplan.NET + +**turnierplan.NET** ist a free and open-source web application for football tournaments + +
+ +
+ +
+ +## Getting Started + +To set up an instance of **turnierplan.NET** on your own server, visit the [Installation](installation.md) guide. diff --git a/docs/pages/installation.md b/docs/pages/installation.md new file mode 100644 index 00000000..7d88cb90 --- /dev/null +++ b/docs/pages/installation.md @@ -0,0 +1,182 @@ +# 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. | - | + +!!! 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. + +## 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. | diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..016bb16d --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +mkdocs diff --git a/images/create-tournament.png b/images/create-tournament.png deleted file mode 100644 index ead43262..00000000 Binary files a/images/create-tournament.png and /dev/null differ diff --git a/images/export-pdf.png b/images/export-pdf.png deleted file mode 100644 index 551917a8..00000000 Binary files a/images/export-pdf.png and /dev/null differ diff --git a/images/folder-timetable.png b/images/folder-timetable.png deleted file mode 100644 index 758d29be..00000000 Binary files a/images/folder-timetable.png and /dev/null differ diff --git a/images/report-match.png b/images/report-match.png deleted file mode 100644 index 072717e8..00000000 Binary files a/images/report-match.png and /dev/null differ