feat: Google Photos provider backend + API hardening

- Add Google Photos provider: client, models, change detector, capabilities
- Add notification templates (en/ru) for all GP event slots
- Add command templates (en/ru) for GP bot commands
- Register GP in slot/command loaders, capabilities, and seeds
- Harden provider API: validate OAuth credentials on create/update
- Add internal URL rewriting for asset fetches (LAN optimization)
- Fix template renderer to handle missing variables gracefully
- Improve webhook command routing for multi-provider support
- Add provider health check endpoint and watcher improvements
This commit is contained in:
2026-03-25 22:07:03 +03:00
parent 337276113d
commit 307871cae5
73 changed files with 1154 additions and 144 deletions
@@ -11,8 +11,11 @@ from slowapi.middleware import SlowAPIMiddleware
# Ensure app-level loggers are visible
logging.basicConfig(level=logging.INFO)
logging.getLogger("notify_bridge_server").setLevel(logging.DEBUG)
logging.getLogger("notify_bridge_core").setLevel(logging.DEBUG)
from .config import settings as _log_cfg
_log_level = logging.DEBUG if _log_cfg.debug else logging.INFO
logging.getLogger("notify_bridge_server").setLevel(_log_level)
logging.getLogger("notify_bridge_core").setLevel(_log_level)
from .database.engine import init_db
from .database.models import * # noqa: F401,F403 — ensure all models registered
@@ -77,6 +80,24 @@ async def lifespan(app: FastAPI):
app = FastAPI(title="Notify Bridge", version="0.1.0", lifespan=lifespan)
# --- Security headers ---
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request as StarletteRequest
from starlette.responses import Response as StarletteResponse
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: StarletteRequest, call_next):
response: StarletteResponse = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
return response
app.add_middleware(SecurityHeadersMiddleware)
# --- Rate limiting ---
from .auth.routes import limiter
app.state.limiter = limiter