The full-key reveal modal and its click/copy handlers referenced the
plaintext key display that was dropped when keys became hashed. They
matched no elements (harmless no-ops), so remove the orphaned modal
markup and handlers; toggle/delete handlers are untouched.
Security / correctness
- Privilege escalation: rule/api-key/project-update routes now gate on
can_manage_members instead of is_member; plain members could previously
rewrite or disable moderation rules and delete API keys.
- Project.is_owner() AttributeError on non-admin API-user deletion (route
+ template) replaced with user_id comparison.
- Flask-Limiter on /auth/login, /auth/register, /auth/change-password,
and /api/moderate (per API key id).
- Remove silent SECRET_KEY fallback: docker-compose fails hard if unset;
production config validator refuses to boot without SECRET_KEY and
OPENAI_API_KEY.
- Require verified email in Google OAuth auto-link flow.
- User-supplied regex now evaluated via the regex package with a 1s
timeout, closing the ReDoS vector.
- Cap batched AI chunk count (max 40) and concurrent workers (200 -> 10).
- openai>=2.32 (from 1.55.3 so reasoning_effort kwarg works); add regex,
Flask-Limiter, gunicorn, eventlet, psycogreen; drop the stdlib-shadowing
pypi asyncio package.
Production runtime
- New wsgi.py: eventlet.monkey_patch + psycogreen.patch_psycopg so DB
queries cooperate with the event loop; sets SOCKETIO_ASYNC_MODE=eventlet
for app startup.
- Dockerfiles run as non-root app user and CMD gunicorn with
--worker-class eventlet. run.py refuses FLASK_CONFIG=production so a
misconfigured deploy fails loudly.
- cloudbuild.yaml stops managing env vars/secrets on deploy — it was
about to clobber secret-backed env vars with plain ones. Runtime config
is now owned by the Cloud Run service.
SocketIO async-mode fix (was causing full site lockup during API calls)
- async_mode picked via SOCKETIO_ASYNC_MODE env var, default "threading".
The previous sys.modules auto-detect false-positived because
flask_socketio / python-engineio import eventlet on load whenever it is
installed, pinning a non-monkey-patched eventlet server in front of
blocking stdlib I/O.
Real-time dashboard updates
- New content_received WebSocket event fires when the API accepts a
submission; UI renders a "Processing…" row immediately and updates it
in place when moderation_update arrives.
- Fix stale updated_at timestamp in moderation_update payloads.
Batched AI rule evaluation
- Replace the per-rule parallel fan-out (N OpenAI calls) with a single
batched call evaluating all AI-prompt rules at once. On a 6-rule
default project this drops safe-content cost from 5-12s to ~1-4s.
- response_format=json_object + max_completion_tokens=4000 so the model
emits strict JSON and stops at the close-brace.
- OPENAI_REASONING_EFFORT config: minimal/low/medium/high for gpt-5 /
o-series; accepts "none" on gpt-5.4. Works around the gpt-5-nano case
where reasoning tokens ate the full output budget and returned empty
responses.
Client errors
- Malformed / non-UTF-8 JSON bodies now return 400 with a clear message
about UTF-8 encoding instead of a generic 500.
Logging
- Single INFO line per moderation: the existing
"Content <id>: decision in Zs" summary.
- Per-stage timing, websocket scheduling, and batched-AI call details
moved to DEBUG so they are available when diagnosing latency without
cluttering steady-state logs.
Added HTML escaping in rules.js to prevent XSS when displaying rule details and editing forms. Updated manual_review/index.html to use DOM manipulation instead of HTML string concatenation for selected content in the bulk action modal, improving security and robustness.
Added a formatNumber function to display statistics with comma separators for better readability. Updated updateStatistics to handle numbers with commas and improved error handling.
Introduces Discord webhook notifications for flagged or rejected content. Adds project-level settings for Discord notifications, including enabling/disabling and specifying a webhook URL. Implements backend logic to send notifications and a frontend UI for configuring and testing Discord webhooks in project settings.
Introduces hidden CSRF token fields in profile, API keys, and rules templates, and updates related JavaScript to include the CSRF token in AJAX request headers. Configures CSRF header names in Flask app settings. Also updates the database service to return all moderation rules, not just active ones, and expands Content Security Policy sources for analytics and source maps.
Enhanced AI moderator to default to approval on malformed responses and apply a confidence threshold before rejecting content. Updated user registration to avoid detached session errors. Fixed project ID extraction in dashboard members JS. Removed cache-busting query parameters from static asset links in templates for more consistent asset loading.
Introduces a language selector UI and supporting JavaScript to toggle between Python, cURL, and JavaScript code examples in the API documentation. Improves usability by allowing users to view code samples in their preferred language.
Added CSS rules to improve visibility of .table-info rows and cells in dark mode. Updated cache-busting version for dark-mode.css in base.html to ensure latest styles are loaded.
APIUser model now computes stats directly from the database for accuracy, with new properties for current counts and days active. API documentation UI is improved with consistent code block styling, theme-aware code blocks, copy buttons, and example cards for cURL, JavaScript, and Python. Manual review templates updated to use new APIUser properties for displaying stats.
Replaces monolithic base.css with modular CSS files for layout, dark mode, tables, forms, modals, and responsive utilities. Moves theme initialization script from base.js to base.html to prevent dark mode flash on page load. Updates base.html to reference new CSS files and removes the old base.css import.
Applied consistent import formatting in Python route files, improved string formatting in AI moderator service, and standardized whitespace and indentation across CSS, JS, and HTML template files for better readability and maintainability. Updated pre-commit configuration to include isort, autopep8, flake8, and additional hooks for code quality and formatting enforcement.
Introduces a route to redirect to API user details by external user ID, allowing easier navigation for users with external identifiers. Enhances the content modal with improved metadata formatting, dark mode styling, and a button to view a user's other content when available. Updates database service to support metadata on content creation.
Introduces a mobile sidebar with overlay for authenticated users, refines dark mode styling for pagination and sidebar, and updates JS to support theme toggling and sidebar behavior on mobile and desktop. Also adds cache-busting query parameters to static asset links and improves responsive layout handling.
Moved dashboard title and actions to dedicated template blocks for better structure. Improved statistics card layout and spacing in CSS, and removed the Moderation Statistics section from the admin index template.
Introduces dark mode support with a toggle button, including persistent theme preference and comprehensive CSS for dark mode styling. Adds a sidebar collapse/expand feature with persistent state and responsive behavior. Refactors dashboard templates to use unified page header blocks for consistent layout and action buttons. Updates documentation with environment setup, API usage, and deployment notes.
Introduces search functionality for content IDs and content data in the dashboard, updates filters and pagination to preserve search and content type parameters, and adds UI elements and JavaScript handlers for copying content IDs. Improves moderation appeal workflow by making content IDs more accessible and visible throughout the dashboard.
Introduces a card displaying the average processing time for content items on the dashboard. Updates JS and HTML to calculate and show this statistic, and improves content preview and metadata display for better readability. Also fixes timezone handling in invitation expiry check.
Raised the per-page content limit from 20 to 25 in dashboard.py and updated frontend logic to enforce a maximum of 25 displayed items. Refactored ResultCache to use a shared cache across instances, added request-level cache summary reporting, and enhanced cache cleanup and logging. ModerationOrchestrator now logs cache usage details for each moderation request.