feat: T4b/T4c wave 8 - weather history, phenology, honey chain, Telegram base

- Weather history from Open-Meteo behind an adapter: worker loop
  phenology-weather with backoff, Retry-After, User-Agent, chunked
  writes and forecast-to-archive correction
- Phenology: growing degree sum per DWD, phase from reports, GTS and
  calendar, calibration from previous years, daily job step,
  SeasonService reads the stored state
- Honey chain: prefill for extractions and batches (proposals only),
  plausibility and withdrawal warnings, Extraction lock, chain events
  and batch.created webhook from HoneyBatchRecorded
- Telegram base: schema, NotificationDelivery outbox, hardened bot
  client, startup check without crash, no-network-outside-loops guard
- ARCHITECTURE.md: sections 11.11-11.13, assumptions 42-46, env table

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-14 20:00:46 +02:00
1 parent 22382d53a2
commit 1971b2d9c4
90 files changed
+8431 -155

No files matched your search

@@ -33,3 +33,18 @@ data:
# bleibt SSO deaktiviert, /auth/sso/status meldet dann enabled: false.
OIDC_ISSUER_URL: "https://login.microsoftonline.com/<tenant-id>/v2.0"
OIDC_REDIRECT_URI: "https://api.apicore.example.com/api/v1/auth/sso/callback"
# Wetterquelle (Pflanzenentwicklung, Wetteranzeige). Nur die freie
# Open-Meteo-API; nur https:-URLs, ein unbekannter Quellenname oder eine
# http:-URL stoppt den Start. Standardwerte gelten ohne Eintrag. Der
# optionale Schluessel OPEN_METEO_API_KEY steht im Secret.
WEATHER_HISTORY_SOURCE: "open-meteo"
OPEN_METEO_FORECAST_URL: "https://api.open-meteo.com/v1/forecast"
OPEN_METEO_ARCHIVE_URL: "https://archive-api.open-meteo.com/v1/archive"
# Optional: Kontakt (E-Mail oder URL) im User-Agent der Wetteranfragen,
# "ApiCore/<Version> (+<Kontakt>)". Ohne Wert: "(self-hosted beekeeping app)".
# WEATHER_USER_AGENT_CONTACT: ""
# Telegram-Erinnerungen (optional). Name des Bots ohne @ (5-32 Zeichen:
# Buchstaben, Ziffern, Unterstrich) fuer den Deep Link; API und Worker lesen
# ihn aus dieser ConfigMap. Das Token steht NICHT hier, siehe Kommentar in
# secret.example.yaml (Secret apicore-telegram, nur Worker).
# TELEGRAM_BOT_USERNAME: "<bot-username>"
@@ -37,3 +37,20 @@ stringData:
# "Calendars.ReadWrite" hinzufuegen (Admin-Consent noetig).
OIDC_CLIENT_ID: "change-me-entra-client-id"
OIDC_CLIENT_SECRET: "change-me-entra-client-secret"
# Optionaler Schluessel der Open-Meteo-API (kommerzieller Tarif). Leer
# lassen, solange die freie API genutzt wird; dann wird kein apikey gesendet.
OPEN_METEO_API_KEY: ""
# Telegram-Bot-Token (optional) - NICHT in diesem Secret, sondern im eigenen
# Secret apicore-telegram, das nur der Worker einbindet (Secret mit
# TELEGRAM_BOT_TOKEN, envFrom/secretKeyRef mit optional: true). Der API-Pod
# darf es nicht einbinden. Der Inhaber legt den Bot bei BotFather und das
# Secret selbst an; ohne Token laeuft Telegram nicht, alles andere schon.
#
# apiVersion: v1
# kind: Secret
# metadata:
# name: apicore-telegram
# type: Opaque
# stringData:
# TELEGRAM_BOT_TOKEN: "<token-from-botfather>"
@@ -19,3 +19,9 @@ data:
TRUST_PROXY: "loopback, 10.42.0.0/16, 173.245.48.0/20, 103.21.244.0/22, 103.22.200.0/22, 103.31.4.0/22, 141.101.64.0/18, 108.162.192.0/18, 190.93.240.0/20, 188.114.96.0/20, 197.234.240.0/22, 198.41.128.0/17, 162.158.0.0/15, 104.16.0.0/13, 104.24.0.0/14, 172.64.0.0/13, 131.0.72.0/22, 2400:cb00::/32, 2606:4700::/32, 2803:f800::/32, 2405:b500::/32, 2405:8100::/32, 2a06:98c0::/29, 2c0f:f248::/32"
OIDC_ISSUER_URL: "https://login.microsoftonline.com/<tenant-id>/v2.0"
OIDC_REDIRECT_URI: "https://api.apicore.example.com/api/v1/auth/sso/callback"
WEATHER_HISTORY_SOURCE: "open-meteo"
OPEN_METEO_FORECAST_URL: "https://api.open-meteo.com/v1/forecast"
OPEN_METEO_ARCHIVE_URL: "https://archive-api.open-meteo.com/v1/archive"
# Telegram: Name des Bots (ohne @) fuer den Deep Link, sobald der Bot bei
# BotFather angelegt ist. Das Token liegt im Secret apicore-telegram (nur Worker).
# TELEGRAM_BOT_USERNAME: "<bot-username>"