chore: Docker prod config (Python 3.13, no reload), fix utcnow deprecation, update docs

This commit is contained in:
ZdenekSrotyr 2026-04-08 12:10:47 +02:00
parent 05a1b452e9
commit 92fbb88c15
13 changed files with 29 additions and 1665 deletions

View file

@ -53,7 +53,7 @@ docker compose --profile full up # Include telegram bot
├── scripts/ # Utility + migration scripts
├── config/ # Configuration templates (instance.yaml.example)
├── docs/ # Documentation + metric YAML definitions
└── tests/ # Test suite (704 tests)
└── tests/ # Test suite (633 tests)
```
## Architecture: extract.duckdb Contract

View file

@ -1,4 +1,4 @@
FROM python:3.11-slim
FROM python:3.13-slim
# Install uv for fast dependency management
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

View file

@ -9,7 +9,7 @@ import hashlib
import hmac
import json
import logging
from datetime import datetime
from datetime import datetime, timezone
from fastapi import APIRouter, Request, Response
from fastapi.responses import JSONResponse
@ -52,7 +52,7 @@ def _log_webhook_event(event_data: dict) -> None:
"""Log webhook event to file for debugging/audit."""
try:
WEBHOOK_LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S_%f")
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S_%f")
event_type = event_data.get("webhookEvent", "unknown").replace(":", "_")
filename = f"{timestamp}_{event_type}.json"
filepath = WEBHOOK_LOG_DIR / filename

View file

@ -33,7 +33,7 @@ import sys
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from typing import Iterator
@ -292,7 +292,7 @@ class JiraBackfill:
return None
# Add sync metadata
issue_data["_synced_at"] = datetime.utcnow().isoformat()
issue_data["_synced_at"] = datetime.now(timezone.utc).isoformat()
file_path = self.issues_dir / f"{issue_key}.json"

View file

@ -36,7 +36,7 @@ import sys
import tempfile
import time
from dataclasses import dataclass
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Iterator
@ -151,8 +151,8 @@ class JiraConsistencyChecker:
Set of issue keys from Jira API (ground truth)
"""
# Calculate cutoff date with grace period
cutoff = datetime.utcnow() - timedelta(days=max_age_days)
grace_cutoff = datetime.utcnow() - timedelta(minutes=self.GRACE_PERIOD_MINUTES)
cutoff = datetime.now(timezone.utc) - timedelta(days=max_age_days)
grace_cutoff = datetime.now(timezone.utc) - timedelta(minutes=self.GRACE_PERIOD_MINUTES)
# JQL: fetch issues created after cutoff, but not too recent (grace period)
jira_project = os.environ.get("JIRA_PROJECT", "")
@ -533,7 +533,7 @@ class JiraConsistencyChecker:
# Build report
report = {
"timestamp": datetime.utcnow().isoformat(),
"timestamp": datetime.now(timezone.utc).isoformat(),
"check_type": "incremental" if max_age_days <= 90 else "deep",
"max_age_days": max_age_days,
"duration_seconds": round(duration, 2),

View file

@ -12,7 +12,7 @@ import json
import logging
import os
import tempfile
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
@ -259,7 +259,7 @@ class JiraService:
self.data_dir.mkdir(parents=True, exist_ok=True)
# Add metadata
issue_data["_synced_at"] = datetime.utcnow().isoformat()
issue_data["_synced_at"] = datetime.now(timezone.utc).isoformat()
# Fetch and embed remote links for Parquet transform
issue_key_for_links = issue_data.get("key")
@ -525,7 +525,7 @@ class JiraService:
with issue_json_lock(issues_dir, issue_key):
with open(file_path) as f:
data = json.load(f)
data["_deleted_at"] = datetime.utcnow().isoformat()
data["_deleted_at"] = datetime.now(timezone.utc).isoformat()
# Atomic write: temp file + replace
fd, tmp_path = tempfile.mkstemp(

View file

@ -9,7 +9,7 @@ import json
import logging
import os
import re
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
@ -549,7 +549,7 @@ def transform_remote_links(raw_issue: dict) -> list[dict]:
def get_month_key(dt: datetime | None) -> str:
"""Get month key (YYYY-MM) from datetime, defaulting to current month."""
if dt is None:
dt = datetime.utcnow()
dt = datetime.now(timezone.utc)
return dt.strftime("%Y-%m")

View file

@ -9,7 +9,7 @@ import hashlib
import hmac
import json
import logging
from datetime import datetime
from datetime import datetime, timezone
from flask import Blueprint, abort, jsonify, request
@ -69,7 +69,7 @@ def log_webhook_event(event_data: dict) -> None:
"""
try:
WEBHOOK_LOG_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S_%f")
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S_%f")
event_type = event_data.get("webhookEvent", "unknown").replace(":", "_")
filename = f"{timestamp}_{event_type}.json"
filepath = WEBHOOK_LOG_DIR / filename

View file

@ -0,0 +1,8 @@
# Development overrides — auto-reload + source mount
# This file is auto-loaded by `docker compose up` when present
services:
app:
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
volumes:
- .:/app
- data:/data

View file

@ -1,11 +1,10 @@
services:
app:
build: .
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
command: uvicorn app.main:app --host 0.0.0.0 --port 8000
ports:
- "8000:8000"
volumes:
- .:/app
- data:/data
env_file: .env
environment:

File diff suppressed because it is too large Load diff

View file

@ -1,69 +0,0 @@
# Complete System Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans.
**Goal:** Make the new FastAPI system feature-complete with the old Flask system. Every route, every service function, every template — replicated with the new DuckDB-backed architecture.
**Status:** Infrastructure done (DuckDB, repos, FastAPI skeleton, CLI, Docker). Missing: business logic wiring, web UI, auth providers, 18 routes, 38 service functions.
---
## Part A: Wire sync trigger to DataSyncManager
Files:
- Modify: `app/api/sync.py` (replace stub with real sync)
- Modify: `app/main.py` (add instance config loading)
## Part B: Instance config integration
Files:
- Create: `app/instance_config.py` (load instance.yaml, expose to FastAPI)
- Modify: `app/main.py` (lifespan event loads config)
- Modify: `app/api/health.py` (include data source info)
## Part C: Web UI — Jinja2 templates in FastAPI
Files:
- Create: `app/web/router.py` (ALL web routes: /, /dashboard, /catalog, /login, /corporate-memory, /admin/tables, etc.)
- Copy: `webapp/templates/``app/web/templates/` (adapt for FastAPI)
- Copy: `webapp/static/``app/web/static/`
- Modify: `app/main.py` (mount templates + static)
## Part D: Auth providers (Google OAuth + Email + Password)
Files:
- Create: `app/auth/providers/google.py`
- Create: `app/auth/providers/email.py`
- Create: `app/auth/providers/password.py`
- Modify: `app/auth/router.py` (OAuth callback, magic link, password verify)
## Part E: Missing API endpoints (18 routes)
Files:
- Create: `app/api/catalog.py` (profile, metrics)
- Create: `app/api/telegram.py` (verify, unlink, status)
- Create: `app/api/desktop.py` (scripts, run)
- Create: `app/api/admin.py` (tables discover, registry CRUD)
- Modify: `app/api/memory.py` (add 10 admin governance endpoints)
- Modify: `app/api/sync.py` (add sync-settings, table-subscriptions)
## Part F: Service logic rewiring
Files:
- Rewrite all old service calls to use DuckDB repositories
- Bridge: old corporate_memory_service → KnowledgeRepository
- Bridge: old sync_settings_service → SyncSettingsRepository
- Bridge: old telegram_service → TelegramRepository
## Part G: CLI missing commands + old test fixes
Files:
- Create: `cli/commands/setup.py`
- Create: `cli/commands/server.py`
- Create: `cli/commands/explore.py`
- Fix: old tests to work with new code
## Part H: Full test coverage
- Integration tests for all 40 routes
- E2E Docker test

View file

@ -15,7 +15,7 @@ import math
import os
import re
import tempfile
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
@ -1210,7 +1210,7 @@ def profile_changed_tables(table_names: list[str]) -> dict:
# Write atomically
output = {
"generated_at": datetime.utcnow().isoformat() + "Z",
"generated_at": datetime.now(timezone.utc).isoformat() + "Z",
"version": "1.0",
"tables": merged,
}
@ -1376,7 +1376,7 @@ def main() -> None:
# Build output
output = {
"generated_at": datetime.utcnow().isoformat() + "Z",
"generated_at": datetime.now(timezone.utc).isoformat() + "Z",
"version": "1.0",
"tables": profiles,
}