From 2cfbb41cdff0cb4daf63ba28f9326af109988294 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Sun, 24 May 2026 02:34:18 +0200
Subject: [PATCH] refactor: migrate to Forgejo workflows, normalize env file
names, remove GitHub Actions
Co-authored-by: Cursor
---
....yml => main_porta-main-platform-core.yml} | 12 +-
.../workflows/update-requirements-lock.yml | 19 +-
.github/scripts/load_config_key_from_azure.py | 74 -------
.github/workflows/deploy-gcp.yml | 194 ------------------
.github/workflows/int_gateway-int.yml | 121 -----------
.github/workflows/main_gateway-prod.yml | 121 -----------
env-gateway-dev.env => env-dev.env | 0
env-gateway-prod.env | 92 ---------
env-gateway-int.env => env-int.env | 0
env-gateway-prod-forgejo.env => env-prod.env | 0
10 files changed, 13 insertions(+), 620 deletions(-)
rename .forgejo/workflows/{deploy.yml => main_porta-main-platform-core.yml} (81%)
rename {.github => .forgejo}/workflows/update-requirements-lock.yml (67%)
delete mode 100644 .github/scripts/load_config_key_from_azure.py
delete mode 100644 .github/workflows/deploy-gcp.yml
delete mode 100644 .github/workflows/int_gateway-int.yml
delete mode 100644 .github/workflows/main_gateway-prod.yml
rename env-gateway-dev.env => env-dev.env (100%)
delete mode 100644 env-gateway-prod.env
rename env-gateway-int.env => env-int.env (100%)
rename env-gateway-prod-forgejo.env => env-prod.env (100%)
diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/main_porta-main-platform-core.yml
similarity index 81%
rename from .forgejo/workflows/deploy.yml
rename to .forgejo/workflows/main_porta-main-platform-core.yml
index e24e53ac..b32bf566 100644
--- a/.forgejo/workflows/deploy.yml
+++ b/.forgejo/workflows/main_porta-main-platform-core.yml
@@ -22,9 +22,9 @@ jobs:
git remote set-url origin ssh://git@git.poweron.swiss:2222/PowerOn/plattform-core.git
git fetch origin main
git reset --hard origin/main
- test -f env-gateway-prod-forgejo.env
- cp env-gateway-prod-forgejo.env .env
- rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env
+ test -f env-prod.env
+ cp env-prod.env .env
+ rm -f env-dev.env env-int.env env-prod.env
source .venv/bin/activate
pip install -r requirements.txt --no-cache-dir
python -m pytest tests/ --ignore=tests/demo
@@ -49,9 +49,9 @@ jobs:
git remote set-url origin ssh://git@git.poweron.swiss:2222/PowerOn/plattform-core.git
git fetch origin main
git reset --hard origin/main
- test -f env-gateway-prod-forgejo.env
- cp env-gateway-prod-forgejo.env .env
- rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env
+ test -f env-prod.env
+ cp env-prod.env .env
+ rm -f env-dev.env env-int.env env-prod.env
source .venv/bin/activate
pip install -r requirements.txt --no-cache-dir
sudo systemctl restart gateway
diff --git a/.github/workflows/update-requirements-lock.yml b/.forgejo/workflows/update-requirements-lock.yml
similarity index 67%
rename from .github/workflows/update-requirements-lock.yml
rename to .forgejo/workflows/update-requirements-lock.yml
index 3d21839f..367bb9ce 100644
--- a/.github/workflows/update-requirements-lock.yml
+++ b/.forgejo/workflows/update-requirements-lock.yml
@@ -1,7 +1,3 @@
-# Generates requirements.lock from requirements.txt using Python 3.11 (same as build).
-# Run manually (workflow_dispatch) or on changes to requirements.txt.
-# After running, commit the generated requirements.lock so builds use it for fast installs.
-
name: Update requirements.lock
on:
@@ -13,7 +9,6 @@ on:
paths:
- 'requirements.txt'
-# Cancel in-progress runs when a new run is triggered (saves logs/storage)
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
@@ -22,13 +17,13 @@ jobs:
update-lock:
runs-on: ubuntu-latest
permissions:
- contents: write # push requirements.lock
+ contents: write
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v5
with:
python-version: '3.11'
@@ -41,9 +36,9 @@ jobs:
- name: Set environment file
run: |
if [ "${{ github.ref }}" == "refs/heads/int" ]; then
- ENV_FILE="env-gateway-int.env"
+ ENV_FILE="env-int.env"
else
- ENV_FILE="env-gateway-prod.env"
+ ENV_FILE="env-prod.env"
fi
test -f "$ENV_FILE"
cp "$ENV_FILE" .env
@@ -59,8 +54,8 @@ jobs:
- name: Commit and push requirements.lock
run: |
- git config user.name "github-actions[bot]"
- git config user.email "github-actions[bot]@users.noreply.github.com"
+ git config user.name "forgejo-actions[bot]"
+ git config user.email "forgejo-actions[bot]@noreply.git.poweron.swiss"
git add requirements.lock
if git diff --staged --quiet; then
echo "No changes to requirements.lock"
diff --git a/.github/scripts/load_config_key_from_azure.py b/.github/scripts/load_config_key_from_azure.py
deleted file mode 100644
index 08da7be4..00000000
--- a/.github/scripts/load_config_key_from_azure.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2026 Patrick Motsch
-"""Load CONFIG_KEY from Azure App Service for CI pytest (Kudu API + publish profile)."""
-from __future__ import annotations
-
-import base64
-import json
-import os
-import sys
-import urllib.request
-import xml.etree.ElementTree as ET
-
-
-def main() -> None:
- profile_xml = os.environ.get("AZURE_PUBLISH_PROFILE")
- setting_name = os.environ.get("SETTING_NAME", "CONFIG_KEY")
- if not profile_xml:
- print("::error::AZURE_PUBLISH_PROFILE is not set", file=sys.stderr)
- sys.exit(1)
-
- root = ET.fromstring(profile_xml)
- pub = None
- for element in root.findall(".//publishProfile"):
- url = (element.get("publishUrl") or "").lower()
- if "scm" in url:
- pub = element
- break
- if pub is None:
- pub = root.find(".//publishProfile")
- if pub is None:
- print("::error::No publishProfile in publish profile XML", file=sys.stderr)
- sys.exit(1)
-
- host = (pub.get("publishUrl") or "").split(":")[0]
- user = pub.get("userName")
- pwd = pub.get("userPWD")
- if not (host and user and pwd):
- print("::error::Could not parse SCM credentials from publish profile", file=sys.stderr)
- sys.exit(1)
-
- api = f"https://{host}/api/settings"
- req = urllib.request.Request(api)
- cred = base64.b64encode(f"{user}:{pwd}".encode()).decode()
- req.add_header("Authorization", f"Basic {cred}")
- try:
- with urllib.request.urlopen(req, timeout=60) as resp:
- settings = json.load(resp)
- except Exception as exc:
- print(f"::error::Kudu settings request failed: {exc}", file=sys.stderr)
- sys.exit(1)
-
- if not isinstance(settings, dict) or setting_name not in settings:
- preview = sorted(settings.keys())[:25] if isinstance(settings, dict) else []
- print(
- f"::error::{setting_name} not in Azure App Service application settings "
- f"(sample keys: {preview})",
- file=sys.stderr,
- )
- sys.exit(1)
-
- value = settings[setting_name]
- if not value or not str(value).strip():
- print(f"::error::{setting_name} is empty in Azure App Service", file=sys.stderr)
- sys.exit(1)
-
- github_env = os.environ.get("GITHUB_ENV")
- if github_env:
- with open(github_env, "a", encoding="utf-8") as handle:
- handle.write(f"{setting_name}<> $GITHUB_OUTPUT
-
- - name: Set up Python
- uses: actions/setup-python@v6
- with:
- python-version: '3.11'
-
- - name: Set environment file
- run: |
- ENV_FILE="${{ steps.env.outputs.env_file }}"
- test -f "$ENV_FILE"
- cp "$ENV_FILE" .env
- rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env
-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- if [ -f requirements.lock ]; then
- pip install -r requirements.lock --no-cache-dir
- else
- pip install -r requirements.txt --no-cache-dir
- fi
-
- - name: Run tests
- run: python -m pytest tests/ --ignore=tests/demo
-
- deploy:
- runs-on: ubuntu-latest
- needs: test
- permissions:
- contents: read
- id-token: write # Required for Workload Identity Federation
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v5
-
- - name: Determine environment
- id: env
- run: |
- if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
- ENV_TYPE="${{ github.event.inputs.environment }}"
- elif [ "${{ github.ref }}" == "refs/heads/int" ]; then
- ENV_TYPE="int"
- else
- ENV_TYPE="prod"
- fi
- echo "env_type=$ENV_TYPE" >> $GITHUB_OUTPUT
- echo "service_name=gateway-$ENV_TYPE" >> $GITHUB_OUTPUT
- echo "env_file=env-gateway-${ENV_TYPE}.env" >> $GITHUB_OUTPUT
- echo "Determined environment: $ENV_TYPE"
- echo "Service name: gateway-$ENV_TYPE"
- echo "Env file: env-gateway-${ENV_TYPE}.env"
-
- - name: Authenticate to Google Cloud
- uses: google-github-actions/auth@v2
- with:
- credentials_json: ${{ secrets.GCP_SA_KEY }}
- # Alternative: Use Workload Identity Federation (more secure)
- # workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
- # service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
-
- - name: Set up Cloud SDK
- uses: google-github-actions/setup-gcloud@v2
-
- - name: Configure Docker for GCR
- run: |
- gcloud auth configure-docker
-
- - name: Set environment file
- run: |
- cd gateway
- ENV_FILE="${{ steps.env.outputs.env_file }}"
- if [ -f "$ENV_FILE" ]; then
- echo "Using $ENV_FILE"
- cp "$ENV_FILE" .env
- else
- echo "Warning: $ENV_FILE not found, using env-gateway-prod.env as fallback"
- cp env-gateway-prod.env .env
- fi
- # Clean up other env files (optional, for security)
- rm -f env-*.env
-
- - name: Build and push container image
- working-directory: ./gateway
- run: |
- # Build container image using Cloud Build
- # If Dockerfile exists, it will be used; otherwise Cloud Buildpacks will be used
- SERVICE_NAME="${{ steps.env.outputs.service_name }}"
- gcloud builds submit \
- --tag gcr.io/${{ env.PROJECT_ID }}/$SERVICE_NAME:${{ github.sha }} \
- --tag gcr.io/${{ env.PROJECT_ID }}/$SERVICE_NAME:latest \
- --project ${{ env.PROJECT_ID }}
-
- - name: Deploy to Cloud Run
- run: |
- SERVICE_NAME="${{ steps.env.outputs.service_name }}"
- ENV_TYPE="${{ steps.env.outputs.env_type }}"
- gcloud run deploy $SERVICE_NAME \
- --image gcr.io/${{ env.PROJECT_ID }}/$SERVICE_NAME:${{ github.sha }} \
- --region ${{ env.REGION }} \
- --platform managed \
- --allow-unauthenticated \
- --project ${{ env.PROJECT_ID }} \
- --set-env-vars "APP_ENV_TYPE=$ENV_TYPE" \
- --set-secrets "CONFIG_KEY=CONFIG_KEY:latest" \
- --memory 2Gi \
- --cpu 2 \
- --timeout 300 \
- --max-instances 10 \
- --min-instances 1 \
- --port 8000 \
- --service-account ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
-
- - name: Get service URL
- id: service-url
- run: |
- SERVICE_NAME="${{ steps.env.outputs.service_name }}"
- SERVICE_URL=$(gcloud run services describe $SERVICE_NAME \
- --region ${{ env.REGION }} \
- --project ${{ env.PROJECT_ID }} \
- --format 'value(status.url)')
- echo "url=$SERVICE_URL" >> $GITHUB_OUTPUT
-
- - name: Output deployment URL
- run: |
- echo "🚀 Deployment successful!"
- echo "Service URL: ${{ steps.service-url.outputs.url }}"
diff --git a/.github/workflows/int_gateway-int.yml b/.github/workflows/int_gateway-int.yml
deleted file mode 100644
index d9fa4d6a..00000000
--- a/.github/workflows/int_gateway-int.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
-# More GitHub Actions for Azure: https://github.com/Azure/actions
-# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions
-
-name: Build and deploy Python app to Azure Web App - gateway-int
-
-on:
- push:
- branches:
- - int
- workflow_dispatch:
-
-# Cancel in-progress runs when a new run is triggered (saves logs/storage)
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- test:
- runs-on: ubuntu-latest
- environment: Production
- steps:
- - uses: actions/checkout@v5
-
- - name: Set up Python version
- uses: actions/setup-python@v6
- with:
- python-version: '3.11'
-
- - name: Set environment file
- run: |
- test -f env-gateway-int.env
- cp env-gateway-int.env .env
- rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env
-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- if [ -f requirements.lock ]; then
- pip install -r requirements.lock --no-cache-dir
- else
- pip install -r requirements.txt --no-cache-dir
- fi
-
- - name: Load CONFIG_KEY from Azure App Service
- env:
- AZURE_PUBLISH_PROFILE: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_GATEWAY_INT }}
- run: python .github/scripts/load_config_key_from_azure.py
-
- - name: Run tests
- run: python -m pytest tests/ --ignore=tests/demo
-
- build:
- runs-on: ubuntu-latest
- needs: test
- permissions:
- contents: read #This is required for actions/checkout
-
- steps:
- - uses: actions/checkout@v5
-
- - name: Set up Python version
- uses: actions/setup-python@v6
- with:
- python-version: '3.11'
-
- - name: Create and start virtual environment
- run: |
- python -m venv venv
- source venv/bin/activate
-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- if [ -f requirements.lock ]; then
- pip install -r requirements.lock --no-cache-dir
- else
- pip install -r requirements.txt --no-cache-dir
- fi
-
- - name: Zip artifact for deployment
- run: zip release.zip ./* -r
-
- - name: Upload artifact for deployment jobs
- uses: actions/upload-artifact@v6
- with:
- name: python-app
- path: |
- release.zip
- !venv/
- retention-days: 5
-
- deploy:
- runs-on: ubuntu-latest
- needs: build
- environment:
- name: 'Production'
- url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
-
- steps:
- - name: Download artifact from build job
- uses: actions/download-artifact@v7
- with:
- name: python-app
-
- - name: Unzip artifact for deployment
- run: unzip release.zip
-
- - name: Set productive environment
- run: cp env-gateway-int.env .env
-
- - name: Clean up environment files
- run: rm -f env-*.env
-
- - name: 'Deploy to Azure Web App'
- uses: azure/webapps-deploy@v3
- id: deploy-to-webapp
- with:
- app-name: 'gateway-int'
- slot-name: 'Production'
- publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_GATEWAY_INT }}
\ No newline at end of file
diff --git a/.github/workflows/main_gateway-prod.yml b/.github/workflows/main_gateway-prod.yml
deleted file mode 100644
index 60de29ff..00000000
--- a/.github/workflows/main_gateway-prod.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
-# More GitHub Actions for Azure: https://github.com/Azure/actions
-# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions
-
-name: Build and deploy Python app to Azure Web App - gateway-prod
-
-on:
- push:
- branches:
- - main
- workflow_dispatch:
-
-# Cancel in-progress runs when a new run is triggered (saves logs/storage)
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- test:
- runs-on: ubuntu-latest
- environment: Production
- steps:
- - uses: actions/checkout@v5
-
- - name: Set up Python version
- uses: actions/setup-python@v6
- with:
- python-version: '3.11'
-
- - name: Set environment file
- run: |
- test -f env-gateway-prod.env
- cp env-gateway-prod.env .env
- rm -f env-gateway-dev.env env-gateway-int.env env-gateway-prod.env env-gateway-prod-forgejo.env
-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- if [ -f requirements.lock ]; then
- pip install -r requirements.lock --no-cache-dir
- else
- pip install -r requirements.txt --no-cache-dir
- fi
-
- - name: Load CONFIG_KEY from Azure App Service
- env:
- AZURE_PUBLISH_PROFILE: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_GATEWAY_PROD }}
- run: python .github/scripts/load_config_key_from_azure.py
-
- - name: Run tests
- run: python -m pytest tests/ --ignore=tests/demo
-
- build:
- runs-on: ubuntu-latest
- needs: test
- permissions:
- contents: read #This is required for actions/checkout
-
- steps:
- - uses: actions/checkout@v5
-
- - name: Set up Python version
- uses: actions/setup-python@v6
- with:
- python-version: '3.11'
-
- - name: Create and start virtual environment
- run: |
- python -m venv venv
- source venv/bin/activate
-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- if [ -f requirements.lock ]; then
- pip install -r requirements.lock --no-cache-dir
- else
- pip install -r requirements.txt --no-cache-dir
- fi
-
- - name: Zip artifact for deployment
- run: zip release.zip ./* -r
-
- - name: Upload artifact for deployment jobs
- uses: actions/upload-artifact@v6
- with:
- name: python-app
- path: |
- release.zip
- !venv/
- retention-days: 5
-
- deploy:
- runs-on: ubuntu-latest
- needs: build
- environment:
- name: 'Production'
- url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
-
- steps:
- - name: Download artifact from build job
- uses: actions/download-artifact@v7
- with:
- name: python-app
-
- - name: Unzip artifact for deployment
- run: unzip release.zip
-
- - name: Set productive environment
- run: cp env-gateway-prod.env .env
-
- - name: Clean up environment files
- run: rm -f env-*.env
-
- - name: 'Deploy to Azure Web App'
- uses: azure/webapps-deploy@v3
- id: deploy-to-webapp
- with:
- app-name: 'gateway-prod'
- slot-name: 'Production'
- publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_GATEWAY_PROD }}
\ No newline at end of file
diff --git a/env-gateway-dev.env b/env-dev.env
similarity index 100%
rename from env-gateway-dev.env
rename to env-dev.env
diff --git a/env-gateway-prod.env b/env-gateway-prod.env
deleted file mode 100644
index c6979c1c..00000000
--- a/env-gateway-prod.env
+++ /dev/null
@@ -1,92 +0,0 @@
-# Production Environment Configuration
-
-# System Configuration
-APP_ENV_TYPE = prod
-APP_ENV_LABEL = Production Instance
-APP_KEY_SYSVAR = CONFIG_KEY
-APP_INIT_PASS_ADMIN_SECRET = PROD_ENC:Z0FBQUFBQnBDM1Z3UnJRV0sySFlDblpXUlREclREaW1WbUt6bGtQYkdrNkZDOXNOLXFua1hqeFF2RHJnRXJ5VlVGV3hOZm41QjZOMlNTb0duYXNxZi05dXVTc2xDVkx0SVBFLUhncVo5T0VUZHE0UTZLWWw3ck09
-APP_INIT_PASS_EVENT_SECRET = PROD_ENC:Z0FBQUFBQnBDM1Z3QVpIY19DQVZSSzJmc2F0VEZvQlU1cHBhTEgxdHdnR3g4eW01aTEzYTUxc1gxTDR1RVVpSHRXYjV6N1BLZUdCUGlfOW1qdy0xSHFVRkNBcGZvaGlSSkZycXRuUllaWnpyVGRoeFg1dGEyNUk9
-APP_API_URL = https://gateway-prod.poweron.swiss
-APP_COOKIE_SECURE = true
-
-# PostgreSQL DB Host
-DB_HOST=gateway-prod-server.postgres.database.azure.com
-DB_USER=gzxxmcrdhn
-DB_PASSWORD_SECRET = PROD_ENC:Z0FBQUFBQnBDM1Z3Y1JScGxjZG9TdUkwaHRzSHZhRHpNcDV3N1U2TnIwZ21PRG5TWFFfR1k0N3BiRk5WelVadjlnXzVSTDZ6NXFQNFpqbnJ1R3dNVkJocm1zVEgtSk0xaDRiR19zNDBEbVIzSk51ekNlQ0Z3b0U9
-DB_PORT=5432
-
-# Security Configuration
-APP_JWT_KEY_SECRET = PROD_ENC:Z0FBQUFBQnBDM1Z3elhfV0Rnd2pQRjlMdkVwX1FnSmRhSzNZUlV5SVpaWXBNX1hpa2xPZGdMSWpnN2ZINHQxeGZnNHJweU5pZjlyYlY5Qm9zOUZEbl9wUEgtZHZXd1NhR19JSG9kbFU4MnFGQnllbFhRQVphRGQyNHlFVWR5VHQyUUpqN0stUmRuY2QyTi1oalczRHpLTEJqWURjZWs4YjZvT2U5YnFqcXEwdEpxV05fX05QMmtrPQ==
-APP_TOKEN_EXPIRY=300
-
-# CORS Configuration
-APP_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:5176,https://nyla.poweron.swiss,https://nyla-int.poweron.swiss,https://nyla.poweron-center.net,https://nyla-int.poweron-center.net
-
-# Logging configuration
-APP_LOGGING_LOG_LEVEL = DEBUG
-APP_LOGGING_LOG_DIR = /home/site/wwwroot/
-APP_LOGGING_FORMAT = %(asctime)s - %(levelname)s - %(name)s - %(message)s
-APP_LOGGING_DATE_FORMAT = %Y-%m-%d %H:%M:%S
-APP_LOGGING_CONSOLE_ENABLED = True
-APP_LOGGING_FILE_ENABLED = True
-APP_LOGGING_ROTATION_SIZE = 10485760
-APP_LOGGING_BACKUP_COUNT = 5
-
-# OAuth: Auth app (login/JWT) vs Data app (Graph / Google APIs)
-Service_MSFT_AUTH_CLIENT_ID = 840b759a-4d79-4a7a-9598-f3ed204d99d8
-Service_MSFT_AUTH_CLIENT_SECRET = PROD_ENC:Z0FBQUFBQnFBa1kySFR2NjBKM084QTNpeUlyUmM4R0N0SU1BZ2x4MmVTZTVHQkVzRE9GdmFkV041MzhudFhobjU0RWNnd3lqeXpKUXA5aGtNZkhtYU12QjBtX0NjemVmdEZBdC1TbXVBSXJTcF9vMlJXd0ZNRTRKRFBMUXNjTF85eTBxakR4RVNfYmU=
-Service_MSFT_AUTH_REDIRECT_URI = https://gateway-prod.poweron.swiss/api/msft/auth/login/callback
-Service_MSFT_DATA_CLIENT_ID = 840b759a-4d79-4a7a-9598-f3ed204d99d8
-Service_MSFT_DATA_CLIENT_SECRET = PROD_ENC:Z0FBQUFBQnFBa1kyNVU4cVRIZFdjS3l2S1RJVTVlc1ozQ1liZXZDX1VwdFZQUzFtS0N6UWYyeGxkNGNmY1hoaWxEUDBXVU5QR2t3Vi1ZV1A2QkxqbnpobzJwOXdzYTBZaFZYdnNkeDE1VVl0bm4weHFiLXdON2gtZzAwMTkxNWRoZldFM2djSkNHVS0=
-Service_MSFT_DATA_REDIRECT_URI = https://gateway-prod.poweron.swiss/api/msft/auth/connect/callback
-
-Service_GOOGLE_AUTH_CLIENT_ID = 813678306829-3f23dnf1cs4aaftubjfickt46tlmkgjm.apps.googleusercontent.com
-Service_GOOGLE_AUTH_CLIENT_SECRET = PROD_ENC:Z0FBQUFBQnFBa1kyUmJleVpTOF9OaFV3NGVfcWVBX2oxSjUwMWRGOFZRWFRIN1FZRzZ6U3VQMlg5a21RY1drTHh3U254LW4zM1A1cXQ1TTFWYlNoek9hSHJIeE4tbm1wU1lKRXlKNU5HVWI4VGZwTVE0VnJGaV8wZmNvdkVrMjJGeXdmZ3UyNmVXN1E=
-Service_GOOGLE_AUTH_REDIRECT_URI = https://gateway-prod.poweron.swiss/api/google/auth/login/callback
-Service_GOOGLE_DATA_CLIENT_ID = 813678306829-3f23dnf1cs4aaftubjfickt46tlmkgjm.apps.googleusercontent.com
-Service_GOOGLE_DATA_CLIENT_SECRET = PROD_ENC:Z0FBQUFBQnFBa1kyY2pxMDh0U0RqWERianBMTTNtSUZPSzhKUzh4S0RTenR2MmxnRDlvQzJjbDVTczRWLUJtVnhxWTE2MmUxQjJia2xJcVUzVlFlUnpma040NFdHRzVNRUt0OXR0c2JkTkRmQ1RIYllXbXFFaExIQWNycFVHbUxHbmtYOVhOVUV2MFY=
-Service_GOOGLE_DATA_REDIRECT_URI = https://gateway-prod.poweron.swiss/api/google/auth/connect/callback
-
-# ClickUp OAuth (Verbindungen / automation). Create an app in ClickUp: Settings → Apps → API; set redirect URL to Service_CLICKUP_OAUTH_REDIRECT_URI exactly.
-Service_CLICKUP_CLIENT_ID = O3FX3H602A30MQN4I4SBNGJLIDBD5SL4
-Service_CLICKUP_CLIENT_SECRET = PROD_ENC:Z0FBQUFBQnB5dkd6VGw5WDdhdDRsVENSalhSSUV0OFFxbEx0V1l6aktNV0E5Y18xU3JHLUlqMWVJdmxyajAydVZRaDJkZzJOVXhxRV9ROFRZbWxlRjh4c3NtQnRFMmRtZWpzTWVsdngtWldlNXRKTURHQjJCOEt6alMwQlkwOFYyVVJWNURJUGJIZDIxYVlfNnBrMU54M0Q3TVdVbFZqRkJKTUtqa05wUkV4eGZvbXNsVi1nNVdBPQ==
-Service_CLICKUP_OAUTH_REDIRECT_URI = https://gateway-prod.poweron.swiss/api/clickup/auth/connect/callback
-
-# Infomaniak: no OAuth client. Users paste a Personal Access Token (kdrive + mail) per UI.
-
-# Stripe Billing (both end with _SECRET for encryption script)
-STRIPE_SECRET_KEY_SECRET = PROD_ENC:Z0FBQUFBQnB5dkd6aVA3R3VRS3VHMUgzUEVjYkR4eUZKWFhPUzFTTVlHNnBvT3FienNQaUlBWVpPLXJyVGpGMWk4LXktMXphX0J6ZTVESkJxdjNNa3ZJbF9wX2ppYzdjYlF0cmdVamlEWWJDSmJYYkJseHctTlh4dnNoQWs4SG5haVl2TTNDdXpuaFpqeDBtNkFCbUxMa0RaWG14dmxyOEdILTNrZ2licmNpbXVkN2lFSWoxZW1BODNpV0ZTQ0VaeXRmR1d4RjExMlVFS3MtQU9zZXZlZE1mTmY3OWctUXJHdz09
-STRIPE_WEBHOOK_SECRET = PROD_ENC:Z0FBQUFBQnBudkpGNUpTWldsakYydFhFelBrR1lSaWxYT3kyMENOMUljZTJUZHBWcEhhdWVCMzYxZXQ5b3VlTFVRalFiTVdsbGxrdUx0RDFwSEpsOC1sTDJRTEJNQlA3S3ZaQzBtV1h6bWp5VnlMZUgwUlF3cXYxcnljZVE5SWdzLVg3V0syOWRYS08=
-STRIPE_API_VERSION = 2026-01-28.clover
-STRIPE_AUTOMATIC_TAX_ENABLED = false
-STRIPE_TAX_RATE_ID_CH_VAT = txr_1TOQZG8WqlVsabrfFEu49pah
-
-
-# AI configuration
-Connector_AiOpenai_API_SECRET = PROD_ENC:Z0FBQUFBQnFCdlFmcDVyOGNwbVkwWFJCWmFkZS12RkhLaFhLSF9kWWpEZ0d0NDBqV2FnWlpnYmpSckdLSGpjbmh6aHJXVUZxMElwY1MzcVg1MzBOdURUZXhnZ3pqNEZyQ1JWMVA0YmxhNWJlenNpa1A3TjZkYVZSclFONjU4MF9jMTJaS2d0ZDNnXzJKSmhSRVhyckJpTUlDa0RRWHN5cWVkOUJMTUp5aFRHcDV5Z1A1aWhSUnFNOHBJTDFPdzAzcVJ3bmhueTBmVkJDZTdJakhMOEFRdHBvWFduUzdRV2dNQVdpaXdFSVlHMDJ4NnZRUTBZZ3pOakxPLUdjNlNNQnJQMXpfSWR3NmFodDdDbkEtVmRjdVBhMjRWT1NOV1BYbU15VHRSWFR0UVBBMWtKRTRkS25KMFk9
-Connector_AiAnthropic_API_SECRET = PROD_ENC:Z0FBQUFBQnFCdlFmMGhla2xoZWowNjJzc1EzMWJYRXRTcGdWWWctU3hhcXNUbVVaOTJiRFJuSGM5S3ZGZ0M4RFotTGxOQ3loa3l4aVZ2T3FsRVVMck83RTlURFNOdWxHb0JfNVEtRGJ4X193dV9Bd0EtNlVGV0h4SWk2bldfWThxNVVnOGctSkNFR3FXa2pmY2ROcV9EVE1oMndFY1d4MjdLeWtUd0VEeW5CTlFwX2FOcW9DaWVXYWVfMy1ZUnFFUEZnanFOUGZILUpUZU8yUHNSODE3OXBSWVJFNlpBdTJtUT09
-Connector_AiPerplexity_API_SECRET = PROD_ENC:Z0FBQUFBQnFCdlFmRm9saTZuR1VSZV9pQllKRGFURmN4cDNNanpsVFM3TVItdDNtNWdoWC1zVllrLUVPeGZDRXF1S3Rxd0tVUGV6bl9Ob0JMa3U5ZUNlRjRVQ1dRWXZDTXlsRU13b2o2R1paalU4RXB6SWxYVEJPa2NmaDRFdzExRXU1X2VnNDlhQzQ3cTE1RlJrSlB5elRMZ2w3NmxlV2l3PT0=
-Connector_AiTavily_API_SECRET = PROD_ENC:Z0FBQUFBQnFCdlFmZGdyWkJibS03akJtSjF0U2doYXZVVDM1em1kY2ZpRGJISmVCUURfVkw3c2Z3OEFQd1h1SzE0cTExSUtVejRPY3VmWF9XT1ZyS3RxRmVRYktJeDR6OWhYaEM0bkNLVEI1cl9VZ1VFOG9IRTFWc2FUemh0UmNHTGprQ0FweThlSGpSSDAyZmw2YmR0OFREQWxpNERHWm1nPT0=
-Connector_AiPrivateLlm_API_SECRET = PROD_ENC:Z0FBQUFBQnBudkpGanZ6U3pzZWkwXzVPWGtIQ040XzFrTXc5QWRnazdEeEktaUJ0akJmNnEzbWUzNHczLTJfc2dIdzBDY0FTaXZYcDhxNFdNbTNtbEJTb2VRZ0ZYd05hdlNLR1h6SUFzVml2Z1FLY1BjTl90UWozUGxtak1URnhhZmNDRWFTb0dKVUo=
-Connector_AiMistral_API_SECRET = PROD_ENC:Z0FBQUFBQnFCdlFmcEVpVmFuWkk4eTJTc3VtRFg4cE9QU3R5NVg0eVFIR29RSVhmXy1rR0pPTm4wbFhIVFFpckx5UmhvSGxqSWV4S0xoTzdESE55R2k5eHowZEprdGhrbEU3eG5JWGpaNWJIdDRqT05zZGNCQVpXd2xTek1teHRBS3NRU2FuUTlSQ2Q=
-
-Service_MSFT_TENANT_ID = common
-
-# Google Cloud Speech Services configuration
-Connector_GoogleSpeech_API_KEY_SECRET = PROD_ENC:Z0FBQUFBQnBDM1Z4NFQxaF9uN3h1cVB6dnZid1c1R1VfNDlSQ1NHMEVDZWtKanpMQ29CLXc1MXBqRm1hQ0YtWVhaejBMY1ZTOEFEVlpWQ3hrYkFza1E2RDNsYkdMMndNR0VGNTMwVDRGdURJY3hyaVFxVjEtSEYwNHJzeWM3WmlpZW9jU2E3NTgycEV2allqQ3dJRTNyRFAzaDJ6dklKeXpNRkJhYjFzUkptN2dpbkNpMklrcGxuZl9vTkt3T0JvNm1YTXd5UlkwZWptUXdWVFpnV2J4X3J2WUhIUlFkSElFVnlqMnlJRnNHTnlpMWs2R1dZc2ROWjNYZG85cndmd1E5cUZnVmZRYnVjTG43dXFmSWd2bGFfVWFWSmtpWkpndWNlSUNwcnFNU2NqZXFaV0xsY3l3SElLRkVHcHZGZERKV1ltcGhTS0dhTko1VTJLYzNoZjRkSGVEX3dTMWVVTmdDczV5cE1JQUdSbUJGUm11eFhTVjJHbkt0SzB4UG1Dc2xmbnp1Y041Y2RTeWRuWGdmQy1sTGx0MGtnM2VJQ3EyLXViRlNhTU9ybzZkR1N1bXE5SXhlZENWRFpWSGlYOWx4SUQ3UlR0ZEVxQkxNakRUVFRiUmFnbklOalphLUZkRFVVaXBRUk5NZW5PaUZydTFmQkNPSTdTVTNZd0plWXllNVFJdmN4MVcyTGlwMGFtVjBzOGRxR1FjbzhfYW5zdTB0ZEZBTTJhakltazh1dktNMUZsOUItdFdTb1pIaUxySllXNkdlY20zUS0wTnpFNTB2SU5acG1VcXhyaHBmME8takw3RDh5T043T2VGOV92TzNya2pWSlpYVjZDdXlZcjM3a0hPTlhkaW9oQmxqQlpGRFYyTTY4WmZmT3k4Tk1tdXRuSGdTUVpNT2NKenhXb05PdXBfSEdhMTNxNjdpNXlKUUI2YUgydFFPX1VvXzVJb0UxWTU2YVNiNDQ0QndZanhMMHR1cGdHWGhvcEg1QXEtSXZJdTdZUE12ZEVVWkF4QmtsQS1GYnY3SFIxSHlsOGVfcEpGS1A4QUVEQWNEOFZYYlljQ3ByTU03YU16Y0UzUnJQZEprSWNjT1ZXVEtDWi03Y3ZzRVdYUTlabXJISEo5THRHVXVuM0xqbzA4bGVlZVpOMk1QMmptb21tV0pTMlVoOXdWVU95UW1iQmttc2w1RG9mMWwxXzg1T2IxYUVmTUJEZkpUdTFDTzZ3RlBFeUFiX01iRTZNWkNaSG45TkFOM2pzbUJRZ2N0VFpoejJUTG1RODY3TzZpSzVkYUQzaEpfY2pSTkRzU0VpanlkdXVQQmJ2WU5peno4QWNLTDVxZTlhSHI3NnNiM0k0Y3JkQ0xaOU05bGtsQl8zQklvaktWSDZ4aVp2MHlYelJuUDJyTU9CZC1OZjJxNFc1dDcwSUlxaVh1LTMyWWFwU0IwUU9kOUFpMWpnOERtLTh1VmJiNGVwcXBMbU5fMjVZc0hFbmxQT2puSFd1ZGpyTkphLU5sVlBZWWxrWEZrWGJQWmVkN19tZFZfZ1l1V3pSWlA0V0ZxM2lrWnl2NU9WeTdCbDROSmhfeENKTFhMVXk1d195S2JMUFJoRXZjcVo4V2g0MTNKRnZhUE1wRkNPM3FZOGdVazJPeW5PSGpuZnFGTTdJMkRnam5rUlV6NFlqODlIelRYaEN5VjdJNnVwbllNODNCTFRHMWlXbmM1VlRxbXB3Wm9LRjVrQUpjYzRNMThUMWwwSVhBMUlyamtPZnE4R0o4bEdHay1zMjR5RDJkZ1lYRHZaNHVHU2otR3ZpN25LZlEySEU0UmdTNzJGVHNWQXMyb0dVMV9WUE13ODhZWUFaakxGOWZieGNXZkNYRnV5djEyWTZLcmdrajRBLU1rS1Z0VVRkOWlDMU9fMGVmYXFhZXJGMUhpNkdmb2hkbzZ1OWV6VlNmVzNISjVYTFh6SjJNdWR5MWZidE8yVEo2dnRrZXhMRXBPczUwTG13OGhNUVpIQm0zQmRKRnJ0Nl8wNW1Ob0dHRDVpU0NWREV3TkY2SjktdVBkMFU1ZXBmSFpHQ3FHNTRZdTJvaExpZVEtLTU4YTVyeFBpNDdEajZtWUc4c1dBeUJqQ3NIY1NLS0FIMUxGZzZxNFNkOG9ORGNHWWJCVnZuNnJVTEtoQi1mRTZyUl81ZWJJMi1KOGdERzBhNVRZeHRYUUlqY2JvMFlaNHhWMU9pWFFiZjdaLUhkaG15TTBPZVlkS2R5UVdENTI4QVFiY1RJV0ZNZnlpVWxfZmlnN1BXbGdrbjFGUkhzYl9qeHBxVVJacUE4bjZETENHVFpSamh0NVpOM2hMYTZjYzBuS3J0a3hhZGxSM1V5UHd2OTU3ZHY0Yy1xWDBkWUk0Ymp0MWVrS3YzSktKODhQZnY3QTZ1Wm1VZkZJbS1jamdreks1ZlhpQjFOUDFiOHJ2Nm9NcmdTdU5LQXV2RkZWZEFNZnVKUjVwcVY3dDdhQnpmRVJ6SmlvVXpDM0ZiYXh5bGE2X04tTE9qZ3BiTnN3TF9ZaFRxSUpjNjB1dXZBcy1TZHRHTjFjSUR3WUl4cE9VNzB5Rkk4U3Z1SVZYTl9sYXlZVk83UnFrMlVmcnBpam9lRUlCY19DdVJwOXl2TVVDV1pMRFZTZk9MY3Z1eXA0MnhGazc5YllQaWtOeTc4NjlOa2lGY05RRzY1cG9nbGpYelc4c3FicWxWRkg0YzRSamFlQ19zOU14YWJreU9pNDREZVJ3a0REMUxGTzF1XzI1bEF3VXVZRjlBeWFiLXJsOXgza3VZem1WckhWSnVNbDBNcldadU8xQ3RwOTl5NGgtVlR0QklCLWl5WkE4V1FlQTBCOVU1RE9sQlRrYUNZOGdfUmEwbEZvUTFGUEFWVmQ4V1FhOU9VNjZqemRpZm1sUDhZQTJ0YVBRbWZldkF5THV4QXpfdUtNZ0tlcGdSRFM3c0lDOTNQbnBxdmxYYWNpTmI3MW9BMlZIdTQ5RldudHpNQWQ5NDNPLVVTLXVVNzdHZXh4UXpZa3dVa2J4dTFDV1RkYjRnWXU2M3lJekRYWGNMcWU5OVh6U2xZWDh6MmpqcnpiOHlnMjA5S3RFQm1NZjNSM21adkVnTUpSYVhkTzNkNnJCTmljY0x1cl9kMkx3UHhySjZEdHREanZERzNEUTFlTkR0NWlBczAtdmFGTjdZNVpTMlkxV2czYW5RN2lqemg4eUViZDV6RjdKNXdFcUlvcVhoNkJ6eVJkR1pua1hnNzQwOEs2TXJYSlpGcW9qRDU2QjBOWFFtdXBJRkRKbmdZUF9ZSmRPVEtvUjVhLTV1NjdXQjRhS0duaEtJb2FrQnNjUTRvdFMxdkdTNk1NYlFHUFhhYTJ1eUN3WHN4UlJ4UjdrZjY0SzFGYWVFN1k0cGJnc1RjNmFUenR4NHljbVhablZSWHZmUVN3cXRHNjhsX1BSZWEzdTJUZFA0S2pTaU9YMnZIQ1ZPcGhWMFJqZkVEMWRMR1h3SnU0Z2FzZ3VGM3puNzdhVjhaQXNIWHFsbjB0TDVYSFdSNV9rdWhUUUhSZHBGYkJIVDB5SDdlMC13QTVnS0g5Qkg5RGNxSGJlelVndUhPcEQ0QkRKMTJTZUM1OXJhVm0zYjU0OVY2dk9MQVBheklIQXpVNW9Yc0ROVjEzaFZTWmVxYlBWMlNlSzladzJ6TmNuMG5FVVZkN1VZN1pfS2ZHa0lQcE80S24wSnQtVlJVV09OVWJ3M09YMkZpV2ktVF9ENHhKU2dfYUQ2aUVyamk0VHJHQmVfVHU4clpUTFoteW5aSWRPV1M0RDRMTms4NGRoYmJfVE82aUl2X3VieVJOdDhBQmRwdzdnRTVBNzZwaW93dUlZb3ZRYUtOeG9ULWxvNVp5a0haSjdkcUhRb3d6UGIxRUpCVkVYX2d6TkRqQVozUWxkNGFoc1FXYVd2YWNkME9Qclo0bjYxMFRWTy1nbnI5NTBJNzRMMDluUXRKYTFqQUN4d0d5aHVlamN3Tkk3NWJXeXR0TW9BeUg5Vnp4Q2RnZUY3b3AtMDlrNmlrSGR0eGRtbUdUd2lFRWg4MklEeWJHN2wwZEpVSXMxNDNOWjRFS0tPdWxhMmFCckhfRENIY184aEFDZXNrRDl2dHQtQW12UnRuQXJjaDJoTUpiYkNWQUtfRG9GMUZoNWM4UnBYZ29RWWs2NHcyUm5kdTF3Vk1GeFpiRUJLaVZ2UGFjbi1jV3lMV0N2ZDl4VERPN295X01NNG56ZjZkRzZoYUtmY1E5NlVXemx2SnVfb19iSXg0R2M3Mjd1a2JRPT0=
-
-# Feature SyncDelta JIRA configuration
-Feature_SyncDelta_JIRA_DELTA_TOKEN_SECRET = PROD_ENC:Z0FBQUFBQnBDM1Z4d3Z4d2x6N1FhUktMU0RKbkxfY2pTQkRzXzJ6UXVEbDNCaFM3UHMtQVFGYzNmYWs4N0lMM1R2SFJuZTVFVmx6MGVEbXc5U3NOTnY1TWN0ZDNaamlHQWloalM3VldmREJNSHQ1TlVkSVFJMTVhQWVGSVRMTGw4UTBqNGlQZFVuaHp4WUlKemR5UnBXZlh0REJFLXJ4ejR3PT0=
-
-# Teamsbot Browser Bot Service
-TEAMSBOT_BROWSER_BOT_URL = https://cae-poweron-shared.redwater-53d21339.switzerlandnorth.azurecontainerapps.io
-
-# Debug Configuration
-APP_DEBUG_CHAT_WORKFLOW_ENABLED = FALSE
-APP_DEBUG_CHAT_WORKFLOW_DIR = ./test-chat
-APP_DEBUG_ACCOUNTING_SYNC_ENABLED = FALSE
-APP_DEBUG_ACCOUNTING_SYNC_DIR = ./debug/sync
-
-# Azure Communication Services Email Configuration
-MESSAGING_ACS_CONNECTION_STRING = endpoint=https://mailing-poweron-prod.switzerland.communication.azure.com/;accesskey=4UizRfBKBgMhDgQ92IYINM6dJsO1HIeL6W1DvIX9S0GtaS1PjIXqJQQJ99CAACULyCpHwxUcAAAAAZCSuSCt
-MESSAGING_ACS_SENDER_EMAIL = DoNotReply@poweron.swiss
diff --git a/env-gateway-int.env b/env-int.env
similarity index 100%
rename from env-gateway-int.env
rename to env-int.env
diff --git a/env-gateway-prod-forgejo.env b/env-prod.env
similarity index 100%
rename from env-gateway-prod-forgejo.env
rename to env-prod.env