Tududi: add task tracke application
This commit is contained in:
@@ -732,6 +732,10 @@ access_control:
|
||||
subject: "group:admins"
|
||||
policy: "two_factor"
|
||||
|
||||
- domain: "bifrost.vakhrushev.me"
|
||||
subject: "group:admins"
|
||||
policy: "two_factor"
|
||||
|
||||
- domain: "wanderbase.vakhrushev.me"
|
||||
subject: "group:admins"
|
||||
policy: "two_factor"
|
||||
@@ -1343,6 +1347,18 @@ identity_providers:
|
||||
|
||||
## Authorization Policies which can be utilized by clients. The 'policy_name' is an arbitrary value that you pick
|
||||
## which is utilized as the value for the 'authorization_policy' on the client.
|
||||
## Claims Policies (Authelia 4.39+): по умолчанию Authelia НЕ кладёт email/
|
||||
## профильные claims в ID Token — только в UserInfo. tududi читает claims
|
||||
## ТОЛЬКО из ID Token (userinfo не вызывает), поэтому копируем их в id_token
|
||||
## явно (при условии, что соответствующий scope выдан клиенту).
|
||||
claims_policies:
|
||||
tududi_claims:
|
||||
id_token:
|
||||
- "email"
|
||||
- "email_verified"
|
||||
- "name"
|
||||
- "preferred_username"
|
||||
|
||||
authorization_policies:
|
||||
outline_policy:
|
||||
rules:
|
||||
@@ -1401,6 +1417,25 @@ identity_providers:
|
||||
userinfo_signed_response_alg: "none"
|
||||
token_endpoint_auth_method: "client_secret_basic"
|
||||
|
||||
- client_name: "Tududi"
|
||||
client_id: "{{ oidc__tududi__client_id }}"
|
||||
client_secret: "{{ oidc__tududi__client_secret }}"
|
||||
redirect_uris:
|
||||
- "https://tududi.vakhrushev.me/api/oidc/callback/authelia"
|
||||
scopes:
|
||||
- "openid"
|
||||
- "profile"
|
||||
- "email"
|
||||
response_types:
|
||||
- "code"
|
||||
grant_types:
|
||||
- "authorization_code"
|
||||
access_token_signed_response_alg: "none"
|
||||
userinfo_signed_response_alg: "none"
|
||||
token_endpoint_auth_method: "client_secret_basic"
|
||||
authorization_policy: "one_factor"
|
||||
claims_policy: "tududi_claims"
|
||||
|
||||
- client_name: "Wakapi"
|
||||
client_id: "{{ oidc__wakapi__client_id }}"
|
||||
client_secret: "{{ oidc__wakapi__client_secret }}"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
{# Конфиг Bifrost — ИСТОЧНИК ПРАВДЫ. Рендерится в config_dir/config.json (app-dir
|
||||
Bifrost смонтирован на /app/data), Bifrost читает его при старте.
|
||||
source_of_truth=config.json: на каждом старте секции этого файла авторитетно
|
||||
перезаписывают состояние в config.db (git выигрывает у правок через UI).
|
||||
|
||||
Раскладка каталогов (по требованию «конфиги/данные раздельно»):
|
||||
- config_dir -> /app/data : сам config.json (app-dir Bifrost по умолчанию);
|
||||
- data_dir -> /app/store : config.db + logs.db (пути заданы явно ниже).
|
||||
|
||||
Несекретные настройки (base URL провайдера, имя модели) заданы здесь прямо
|
||||
строками — они больше нигде не переиспользуются. Секреты (ключ DeepSeek,
|
||||
encryption_key) рендерятся строками из vars/secrets.yml, поэтому готовый
|
||||
config.json — секретный файл (плейбук пишет его на 0600).
|
||||
|
||||
Провайдер deepseek заведён как КАСТОМНЫЙ OpenAI-совместимый
|
||||
(base_provider_type=openai + network_config.base_url) — нативного провайдера
|
||||
deepseek у Bifrost нет. ВАЖНО: Bifrost сам дописывает к base_url путь
|
||||
/v1/chat/completions, поэтому base_url задаём БЕЗ хвостового /v1.
|
||||
|
||||
Роутинг — декларативно через governance.routing_rules (требует включённого
|
||||
config_store). tududi ходит на drop-in эндпоинт /openai и шлёт ЗАХАРДКОЖЕННОЕ
|
||||
имя модели gpt-4o-mini «голым». Правило ловит его по cel и подменяет на
|
||||
реальную модель deepseek/deepseek-v4-pro. Фолбэка нет — единственный провайдер.
|
||||
Чтобы сменить бэкенд-модель, правим ТОЛЬКО этот файл, конфиг tududi не трогаем.
|
||||
|
||||
Эти Jinja-комментарии в готовый config.json не попадают (на выходе чистый JSON). #}
|
||||
{
|
||||
"$schema": "https://www.getbifrost.ai/schema",
|
||||
"encryption_key": {{ bifrost_encryption_key | to_json }},
|
||||
"client": {
|
||||
"enable_logging": true
|
||||
},
|
||||
"providers": {
|
||||
"deepseek": {
|
||||
"keys": [
|
||||
{ "name": "deepseek-primary", "value": {{ bifrost_deepseek_api_key | to_json }}, "models": ["*"], "weight": 1.0 }
|
||||
],
|
||||
"network_config": { "base_url": "https://api.deepseek.com", "max_retries": 2 },
|
||||
"custom_provider_config": {
|
||||
"base_provider_type": "openai",
|
||||
"allowed_requests": { "chat_completion": true, "chat_completion_stream": true }
|
||||
}
|
||||
}
|
||||
},
|
||||
"config_store": {
|
||||
"enabled": true,
|
||||
"type": "sqlite",
|
||||
"config": { "path": "/app/store/config.db" },
|
||||
"source_of_truth": "config.json"
|
||||
},
|
||||
"logs_store": {
|
||||
"enabled": true,
|
||||
"type": "sqlite",
|
||||
"config": { "path": "/app/store/logs.db" }
|
||||
},
|
||||
"governance": {
|
||||
"routing_rules": [
|
||||
{
|
||||
"id": "tududi-gpt4omini-to-deepseek-pro",
|
||||
"name": "Tududi gpt-4o-mini -> DeepSeek v4 Pro",
|
||||
"enabled": true,
|
||||
"cel_expression": "model == 'gpt-4o-mini'",
|
||||
"targets": [
|
||||
{ "provider": "deepseek", "model": "deepseek-v4-pro", "weight": 1 }
|
||||
],
|
||||
"scope": "global",
|
||||
"priority": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# Bifrost — LLM-gateway (github.com/maximhq/bifrost). Единый OpenAI-совместимый
|
||||
# шлюз для AI-фич других приложений (сейчас — tududi). За ним провайдер DeepSeek.
|
||||
# Клиенты ходят в него по имени bifrost_app:8080 в общей web_proxy_network и про
|
||||
# провайдера/модель ничего не знают — роутинг решает config.json.
|
||||
# See versions: https://hub.docker.com/r/maximhq/bifrost/tags
|
||||
#
|
||||
# В контейнере Bifrost слушает 8080 (дефолт). Наружу host-порт НЕ пробрасываем:
|
||||
# веб-UI (Dashboard + Logs) и API доступны только через Caddy
|
||||
# (bifrost.vakhrushev.me, за Authelia forward_auth) и внутри web_proxy_network.
|
||||
#
|
||||
# App-dir Bifrost по умолчанию — /app/data; туда монтируется config_dir с
|
||||
# config.json (источник правды, с секретами, 0600). SQLite-сторы config.db /
|
||||
# logs.db вынесены в data_dir -> /app/store (пути заданы явно в config.json).
|
||||
|
||||
services:
|
||||
|
||||
bifrost_app:
|
||||
image: maximhq/bifrost:v1.6.2 # проверь актуальный стабильный тег в releases
|
||||
container_name: bifrost_app
|
||||
restart: unless-stopped
|
||||
user: "{{ owner_create_result.uid }}:{{ owner_create_result.group }}"
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
volumes:
|
||||
# App-dir: config.json (рендерит плейбук).
|
||||
- "{{ config_dir }}:/app/data"
|
||||
# Сторы, создаёт и держит сам Bifrost — бекапить не нужно (config.db
|
||||
# восстанавливается из config.json, logs.db — просто логи запросов).
|
||||
- "{{ data_dir }}:/app/store"
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
||||
@@ -152,6 +152,17 @@ memos.vakhrushev.me {
|
||||
}
|
||||
|
||||
# tududi сам логинит пользователей через OIDC (Authelia), поэтому здесь без
|
||||
# forward_auth — иначе двойная аутентификация.
|
||||
tududi.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
import access_log
|
||||
|
||||
reverse_proxy {
|
||||
to tududi_app:3002
|
||||
}
|
||||
}
|
||||
|
||||
remembos.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
import access_log
|
||||
|
||||
@@ -233,3 +244,17 @@ goaccess.vakhrushev.me {
|
||||
}
|
||||
|
||||
# Bifrost — внутренний LLM-шлюз. Веб-UI (дашборд + LLM-логи + ключи) закрыт за
|
||||
# Authelia (2FA). Сам API tududi дёргает не через Caddy, а по web_proxy_network.
|
||||
bifrost.vakhrushev.me {
|
||||
tls anwinged@ya.ru
|
||||
import access_log
|
||||
|
||||
forward_auth authelia_app:9091 {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||
}
|
||||
|
||||
reverse_proxy bifrost_app:8080
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Tududi — планировщик задач (github.com/chrisvel/tududi).
|
||||
# See versions: https://hub.docker.com/r/chrisvel/tududi/tags
|
||||
#
|
||||
# Образ стартует entrypoint'ом от root, chown'ит /app/backend на PUID:PGID и
|
||||
# сбрасывает права через su-exec — поэтому НЕ задаём `user:`, только PUID/PGID
|
||||
# (совпадают с системным пользователем tududi, владельцем смонтированных каталогов).
|
||||
#
|
||||
# Внутренний порт — 3002 (frontend отдаётся тем же backend'ом). Caddy проксирует
|
||||
# tududi.vakhrushev.me -> tududi_app:3002. Аутентификация — собственный OIDC-логин
|
||||
# tududi к Authelia (НЕ forward_auth), поэтому в Caddy домен открыт.
|
||||
|
||||
services:
|
||||
|
||||
tududi_app:
|
||||
image: chrisvel/tududi:1.1.1
|
||||
container_name: tududi_app
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- "web_proxy_network"
|
||||
volumes:
|
||||
- "{{ db_dir }}:/app/backend/db"
|
||||
- "{{ uploads_dir }}:/app/backend/uploads"
|
||||
environment:
|
||||
- PUID={{ app_owner_uid }}
|
||||
- PGID={{ app_owner_gid }}
|
||||
|
||||
- NODE_ENV=production
|
||||
- PORT=3002
|
||||
- DB_FILE=db/production.sqlite3
|
||||
- TUDUDI_UPLOAD_PATH=/app/backend/uploads
|
||||
|
||||
- BASE_URL=https://tududi.vakhrushev.me
|
||||
- TUDUDI_ALLOWED_ORIGINS=https://tududi.vakhrushev.me
|
||||
- TUDUDI_TRUST_PROXY=true
|
||||
|
||||
- TUDUDI_SESSION_SECRET={{ tududi_session_secret }}
|
||||
# Чистый OIDC: сид-админ не создаётся (TUDUDI_USER_EMAIL/PASSWORD не заданы),
|
||||
# первый вход через Authelia сам заводит пользователя и выдаёт ему admin по
|
||||
# OIDC_ADMIN_EMAIL_DOMAINS. Локальный парольный вход выключен.
|
||||
- PASSWORD_AUTH_ENABLED=false
|
||||
|
||||
# OIDC (Authelia). Redirect URI регистрируется в Authelia как
|
||||
# https://tududi.vakhrushev.me/api/oidc/callback/{OIDC_PROVIDER_SLUG}.
|
||||
- OIDC_ENABLED=true
|
||||
- OIDC_PROVIDER_NAME=Authelia
|
||||
- OIDC_PROVIDER_SLUG=authelia
|
||||
- OIDC_ISSUER_URL=https://auth.vakhrushev.me
|
||||
- OIDC_CLIENT_ID={{ tududi_oidc_client_id }}
|
||||
- OIDC_CLIENT_SECRET={{ tududi_oidc_client_secret }}
|
||||
- OIDC_SCOPE=openid profile email
|
||||
- OIDC_AUTO_PROVISION=true
|
||||
# Домен email, чьим пользователям при auto-provision выдаётся роль админа
|
||||
# (должен совпадать с доменом email в id_token от Authelia).
|
||||
- OIDC_ADMIN_EMAIL_DOMAINS=vakhrushev.me
|
||||
|
||||
# Почта (Yandex Cloud Postbox, те же секреты, что у outline/authelia).
|
||||
- ENABLE_EMAIL=true
|
||||
- EMAIL_SMTP_HOST={{ postbox_host }}
|
||||
- EMAIL_SMTP_PORT={{ postbox_port }}
|
||||
- EMAIL_SMTP_SECURE=false
|
||||
- EMAIL_SMTP_USERNAME={{ postbox_user }}
|
||||
- EMAIL_SMTP_PASSWORD={{ postbox_pass }}
|
||||
- EMAIL_FROM_ADDRESS=tududi@vakhrushev.me
|
||||
- EMAIL_FROM_NAME=Tududi
|
||||
|
||||
# AI-фичи через Bifrost (drop-in OpenAI-эндпоинт в общей сети — НЕ localhost,
|
||||
# это сам контейнер). Модель у tududi захардкожена как gpt-4o-mini; Bifrost
|
||||
# по governance-правилу роутит её на deepseek-v4-pro. openai-node сам
|
||||
# подхватывает OPENAI_BASE_URL.
|
||||
- OPENAI_API_KEY={{ tududi_openai_api_key }}
|
||||
- OPENAI_BASE_URL=http://bifrost_app:8080/openai
|
||||
|
||||
networks:
|
||||
web_proxy_network:
|
||||
external: true
|
||||
Reference in New Issue
Block a user