79 Commits
Author SHA1 Message Date
Bentlybro 21c831572c Fix project and user deletion, update admin registration toggle 2026-10-19 11:28:32 +01:00
Bentlybro e876ff796a Store API keys hashed instead of in plaintext
Keys are now stored as a SHA-256 digest in the existing key column (64
chars, no schema change). Lookups hash the presented key; rows still
holding plaintext are matched and upgraded to their hash on first use.
The full key is shown once at creation and the dashboard displays a
masked placeholder thereafter.
2026-07-06 13:41:53 +01:00
Bentlybro 6642f71b70 Fix stale docs and remove dead code paths
- run.py and the __init__ SocketIO comment referenced the eventlet
  worker; production uses gthread. Copy-pasting the old run.py guidance
  would have redeployed the exact config wsgi.py warns against.
- README/CLAUDE tech-stack tables said Flask 2.3.3 (actual: 3.1.3);
  CLAUDE architecture tree listed a rule_cache.py that doesn't exist.
- Drop the per-request token count that was computed and stored on the
  content object but never read (the AI paths recount as needed).
- Remove the conflicting-AI-decisions manual-review branch: the pipeline
  early-exits on the first AI match, so it could never see >1 AI result.
- Read/reset the process-global cache-store counter under the cache lock
  so it can't tear against the locked increment.
2026-07-06 13:09:24 +01:00
Bentlybro 99a752f152 Harden moderation persistence and cache correctness
- Make API-key and API-user usage counters atomic (single UPDATE ...
  SET col = col + 1) so concurrent moderations for the same key/user
  don't lose increments to a read-modify-write race.
- Persist each moderation outcome (content status + user counters +
  result rows) in a single transaction via save_moderation_outcome,
  replacing three independent commits plus a no-op commit that could
  leave a decided status with no result rows on partial failure.
- Include rule.action in the batched AI cache fingerprint and clear the
  shared result cache when a rule is created/updated/toggled/deleted, so
  edits take effect immediately instead of after the 1h TTL.
- Flag (not approve) content that exceeds the per-request chunk cap:
  partial coverage must not yield a clean approval.
2026-07-06 13:00:25 +01:00
Bentlybro b92bdf694e Phase 1: architectural fixes (concurrency, async/sync, data integrity)
Concurrency / performance
- Drop the global threading.RLock in DatabaseService. It was wrapping every
  _safe_execute and turning the 8-wide thread pool into a 1-wide one;
  Flask-SQLAlchemy's scoped_session already gives each thread its own
  session, so the lock was protecting nothing while serialising every DB
  op in the process.
- moderate_content now awaits _process_rules and _handle_no_matches; both
  push the blocking OpenAI call to asyncio.to_thread so the event loop
  stays responsive while waiting on the model.
- DiscordNotifier shares a single module-level requests.Session (instead
  of one per instance) and exposes an async wrapper that runs the POST
  via asyncio.to_thread, so the moderation request doesn't pin its loop
  on Discord latency / 429 retry backoff.
- load_user simplified to a single User.query.get(user_id). Previously it
  spun a fresh ThreadPoolExecutor and asyncio event loop on every
  authenticated request just to await an async wrapper around the same
  primary-key lookup.
- ResultCache now LRU-evicts the oldest entries when at capacity with no
  expired entries to free, instead of silently dropping new writes.
  _cleanup_expired_entries now sets _last_cleanup_time so the periodic
  throttle applies even when called from the hot-path fallback.

Data integrity
- APIUser gained UniqueConstraint(project_id, external_user_id).
  get_or_create_api_user catches IntegrityError, rolls back, and reads the
  winner's row so concurrent submits for the same external user no longer
  produce duplicate APIUser rows that split stats.
- ProjectMember gained UniqueConstraint(project_id, user_id). The
  invitation accept flow catches the IntegrityError and treats it as a
  benign duplicate-click.
- New scripts/dedup_before_unique_constraints.py to merge any pre-existing
  duplicate rows so the new constraints can be added cleanly. Dry-run by
  default, --apply to commit.
- Fix the API-user stats detached-instance bug: increment_api_user_stats
  in the DB service now does query + mutation + commit in one
  _safe_execute call. Previously the orchestrator mutated a detached ORM
  instance outside any session and the changes silently never persisted,
  so per-user counters drifted.
- Manual-review decision flow now commits the moderation result first,
  then atomically commits the APIUser stat bump in the same request
  context (instead of relying on the detached-instance pattern).

Correctness
- ContentType enum is now {text, markdown, html} to match the route's
  whitelist. Previously the schema declared text/image/video/audio while
  the route accepted text/markdown/html, so markdown/html submissions
  died with a Pydantic 400 before reaching the route.
- Project invitation emails are normalised to lowercase on store and
  compared case-insensitively on accept/decline. Mixed-case stored
  invitations were silently locking recipients out of accepting their
  own invite.
- Admin create_user lowercases the email and requires 8-char passwords
  (matching the public registration policy). The previous 6-char floor
  was weaker than the public path despite admin-created accounts often
  being privileged.
- AI moderator response parsing now uses (content or "").strip() — OpenAI
  returns content=None on safety refusals and the raw .strip() raised
  AttributeError that the surrounding except didn't catch.
2026-04-21 16:14:43 +01:00
Bentlybro 478ca18ad8 Phase 0 hardening + batched AI moderation + websocket/perf fixes
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.
2026-04-21 16:01:18 +01:00
Bentlybro 258d587151 Increase ThreadPoolExecutor max_workers to 200
Raised the maximum number of workers from 150 to 200 in ai_moderator.py and rule_processor.py to improve parallel processing performance for chunk and rule analysis.
2025-10-23 10:44:08 +01:00
Bentlybro f0a263acfd Increase ThreadPoolExecutor max_workers to 150
Raised the maximum number of workers from 100 to 150 in ai_moderator.py and rule_processor.py to improve parallel processing performance for chunk and rule analysis.
2025-10-22 17:03:04 +01:00
Bentlybro 7e4ca4efce Reduce ThreadPoolExecutor max_workers to 100
Lowered the maximum number of workers in ThreadPoolExecutor from 200 to 100 in ai_moderator.py and rule_processor.py to optimize resource usage and prevent potential overload during parallel processing.
2025-10-22 16:54:40 +01:00
Bentlybro 1d82daefc9 Increase concurrency and DB pool sizes for scalability
Raised ThreadPoolExecutor worker limits from 50 to 200 in AI moderation and rule processing to improve OpenAI API concurrency. Increased SQLAlchemy pool_size to 50, pool_timeout to 60, max_overflow to 100, and DB_THREAD_POOL_WORKERS to 100 for better database performance under load.
2025-10-22 16:23:12 +01:00
Bentlybro 464fb0e02b Increase concurrency and cache limits for AI moderation
Raised thread pool worker limits from 10 to 50 in AI moderation and rule processing for improved parallelism under load. Increased max cache size in result_cache.py from 10k to 50k entries to handle higher concurrency. Updated OpenAI client connection pool and limits for better burst handling. Increased max chunk size for content processing to 150k characters for efficiency with large context models.
2025-10-22 15:47:25 +01:00
Bentlybro bede582d8e Optimize content status count queries for projects
Refactored content statistics queries in dashboard routes to use a single optimized database query via db_service.get_content_counts_by_status. The new implementation reduces multiple queries to a single grouped query, improving performance when retrieving content status counts for projects.
2025-10-17 10:37:10 +01:00
Bentlybro 398630fcac Refactor logging for early exit in chunk rejection
Simplifies the logging statement when a chunk is rejected by calculating the number of remaining chunks in a variable and using it in the log message. This improves readability and maintainability.
2025-10-16 18:22:09 +01:00
Bentlybro c5ad918538 Improve auth redirects, AI moderation, and cache handling
Redirect authenticated users from login/register to dashboard. Increase AI moderator chunk size and add early exit on rejection for faster moderation. Expand result cache size and TTL, refine aggressive cleanup to only remove expired entries, and add warnings when cache is full but no expired entries are found.
2025-10-16 18:11:47 +01:00
Bentlybro b26a25b29e Update cache info logic in moderation orchestrator
Refines the cache information display by checking for stored cache results instead of relying solely on request time and cache hits. This provides more accurate feedback about cache usage in moderation requests.
2025-10-16 17:26:06 +01:00
Bentlybro 194b621c99 Refactor chunking debug log in ai_moderator.py
Improved readability of the debug log statement by calculating the number of chunks in a separate variable before logging. No functional changes to chunking logic.
2025-10-16 17:09:33 +01:00
Bentlybro 3a26a500d4 Enable Flask context in parallel AI moderation tasks
Introduces a context wrapper in AIModerator to ensure Flask application context is available during parallel chunk processing. Also updates logging for chunking and refines cache info reporting in ModerationOrchestrator for improved clarity and accuracy.
2025-10-16 17:05:40 +01:00
Bentlybro 9e632da8e6 Parallelize chunk moderation and restrict connection indicator
Updated ai_moderator.py to process content chunks in parallel using ThreadPoolExecutor for improved performance. Modified base.html to display the real-time connection indicator only for authenticated users.
2025-10-14 15:39:36 +01:00
Bentlybro da8df91810 Remove excessive logging from AI moderator service
Eliminated several info and error log statements related to content size, chunking, and API call details to reduce log verbosity and potential exposure of sensitive content. This streamlines the logging output and focuses on essential warnings and errors.
2025-10-14 15:20:11 +01:00
Bentlybro b72d7eed45 Enforce character-based chunking for large custom prompts
Adds a hard character count limit (50,000 chars) for content analyzed with custom prompts to address issues with inaccurate token counting. Content exceeding this limit is split by character count to ensure safe processing, improving reliability when tiktoken misestimates token usage.
2025-10-14 15:15:00 +01:00
Bentlybro 9775c63b3c Improve logging for OpenAI API cost investigation
Replaces token count debug logs with more detailed error logs including content length, prompt length, message lengths, content type, and a preview of the content. This aids in identifying cost-related issues with OpenAI API usage.
2025-10-14 15:09:11 +01:00
Bentlybro d71ee9def5 Add validation and logging for custom prompt size
Introduces a check to raise a ValueError if a custom moderation prompt is too large to allow sufficient space for content and output. Adds detailed logging for prompt and content token counts to aid debugging and prevent exceeding model context window limits.
2025-10-14 14:59:25 +01:00
Bentlybro 0f7c4629dd Make token calculation more conservative in AIModerator
Adjusts the calculation of available tokens for content to use a larger safety margin and buffer, accounting for tokenizer inaccuracies and large custom prompts. Adds logging for unusually large prompts and enforces a hard cap on content tokens to prevent exceeding model context limits.
2025-10-14 14:45:07 +01:00
Bentlybro 02c44343f5 Reduce default max output tokens for OpenAI
Changed the default value for OPENAI_MAX_OUTPUT_TOKENS from 128000 to 500 in AIModerator. This likely addresses performance or cost concerns by limiting the maximum number of output tokens generated.
2025-10-14 14:33:13 +01:00
Bentlybro 1bbd830910 Refine AI moderator token calculation and config
Improved the token calculation logic in AIModerator to better estimate prompt and message overhead, ensuring more accurate content token allocation. Updated OPENAI_MAX_OUTPUT_TOKENS default in config.py to 500, reflecting the typical size of AI moderator responses.
2025-10-14 14:22:27 +01:00
Bentlybro 46329bd618 Increase shared cache TTL to 1 hour
The shared cache time-to-live has been increased from 30 minutes to 1 hour to allow cached results to persist longer.
2025-10-14 13:40:50 +01:00
Bentlybro 041a0c21b9 Update AI error message in moderation response
Replaces the detailed AI error message with a generic internal error message in the moderation response to avoid exposing internal exception details.
2025-10-07 23:41:11 +01:00
Bentlybro cc0f906d85 Update moderation_orchestrator.py 2025-10-07 23:11:53 +01:00
Bentlybro 5222d1a942 Remove 'GDPR compliance' from user data deletion labels
Updated docstrings, comments, and UI labels to remove the '(GDPR compliance)' note from user data deletion actions. This change clarifies the purpose of these actions without referencing GDPR specifically.
2025-10-06 22:23:27 +01:00
Bentlybro acc6c0de7e Add GDPR-compliant user data deletion features
Implements GDPR-compliant user data deletion in the admin and manual review interfaces, including backend service methods and UI modals for confirmation. Adds search and deletion endpoints, updates templates for user data management, and improves quick search functionality for API users. Also corrects the repository URL in the installation guide.
2025-10-06 22:09:38 +01:00
Bentlybro 84eafb7e5a Replace print statements with logging
Refactored app initialization, WebSocket routes, and moderation notifier to use Python's logging module instead of print statements for better log management and consistency. This improves log level control and integration with Flask's logger.
2025-10-04 11:52:39 +01:00
Bentlybro c8522fba14 Refactor token limit calculation for AI moderation
Replaces static max_content_tokens logic with dynamic calculation based on prompt size, ensuring content fits within the model's context window. Updates default context window from 400,000 to 272,000 tokens in both config and service. Improves chunking and moderation handling for custom prompts and default flows.
2025-10-03 22:39:35 +01:00
Bentlybro a9b464507a Remove rule cache and query moderation rules from DB
Eliminates the RuleCache class and its usage, updating moderation logic to fetch rules directly from the database for each request. Simplifies cache management and system health reporting by removing rule cache statistics.
2025-10-03 18:58:31 +01:00
Bentlybro 426333a3cb Add active users count to stats response
The stats dictionary now includes 'active_users', showing the count of users with is_active=True. This provides more detailed user metrics in the database service.
2025-10-03 16:56:30 +01:00
Bentlybro 1e6df701ee Update Discord embed fields and title logic
Removes the 'Moderator' field from the embed and refines the embed title generation to better reflect content status, including specific titles for rejected and flagged content.
2025-10-03 16:16:19 +01:00
Bentlybro bb90945f17 Refactor manual review to be project-specific
Manual review routes, templates, and links now require a project context, restricting access and displaying flagged content per project. Navigation and Discord notification links have been updated to reflect project-specific manual review pages. API Users and review content views now validate project membership and provide improved navigation between project and manual review dashboards.
2025-10-03 16:01:30 +01:00
Bentlybro a958f48631 Add Discord notification support for moderation alerts
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.
2025-10-03 15:40:21 +01:00
Bentlybro e2b86cce6d Fix thread-local API key handling in OpenAIClient
Replaces class-level API key storage with thread-local storage to ensure thread safety when managing OpenAI client instances. Also updates client creation and usage to reference the thread-local API key and client.
2025-10-03 09:25:47 +01:00
seer-by-sentry[bot] bc556104dc Refactor: Remove OpenAI client instance and use thread-local client 2025-10-03 08:15:17 +00:00
Bentlybro ab6935e50b Handle OpenAI InternalServerError in moderator service
Added openai.InternalServerError to exception handling and retry logic in AIModerator. This improves robustness against transient server errors from the OpenAI API by treating them similarly to connection and timeout errors.
2025-10-02 21:57:15 +01:00
Bentlybro e53e74660e Add Google OAuth env vars and improve OpenAI client thread safety
Added GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to .env.example for Google OAuth configuration. Refactored OpenAIClient to use thread-local storage for client instances, improving thread safety in multi-threaded environments.
2025-10-02 21:46:37 +01:00
Bentlybro 6e47011f3c Improve AI rule parallel execution and error handling
Refactored parallel execution of AI rules to increase timeout to 60 seconds, add granular error logging for individual futures, and handle timeouts by collecting completed results and cancelling unfinished tasks. This enhances reliability and provides better diagnostics for rule processing failures.
2025-10-02 20:47:50 +01:00
Bentlybro 95437ac6da Add error status handling for content moderation
Introduces 'error' as a content status throughout the backend and dashboard templates. Moderation errors are now tracked and saved to the database, and the UI displays error status with a distinct badge. This improves visibility and tracking of failed moderation attempts.
2025-10-02 20:43:43 +01:00
Bentlybro d508936556 Add error tracking and reporting to admin and API
Introduces an in-memory error tracker service for monitoring and debugging. Integrates error tracking into API and moderation logic, logs errors with details, and displays error statistics and recent errors in the admin system health page for improved visibility and diagnostics.
2025-10-02 20:07:59 +01:00
Bentlybro 03a68e07d5 Increase CPU usage interval and share rule cache
Changed the CPU usage interval in system health endpoint from 0.1 to 0.5 seconds for more accurate readings. Updated RuleCache to use shared class-level caches for rules and timestamps, improving cache consistency across instances.
2025-10-02 14:36:36 +01:00
Bentlybro 96bac25a2c Add system health monitoring to admin panel
Introduces a system health page in the admin panel displaying process metrics, cache stats, and database pool info. Adds a /health endpoint for basic service checks, optimizes cache management in result_cache and rule_cache, and updates requirements to include psutil.
2025-10-02 13:57:42 +01:00
Bentlybro af916c1ea8 Optimize user profile data loading and stats aggregation
Replaces loading full user projects with a lightweight stats aggregation to prevent OOM errors on large datasets. Adds get_user_with_project_stats to database_service and updates profile route and template to use aggregated counts for projects, API keys, and content.
2025-10-02 12:02:45 +01:00
Bentlybro 2a98fc7228 Add Google OAuth login and user linking
Integrates Google OAuth for user authentication, including login, account creation, and linking to existing accounts. Updates User model to support Google ID, adds relevant database service methods, and modifies login template to include Google sign-in. Also adds necessary configuration for Google OAuth and middleware for proxy headers.
2025-09-30 13:11:00 +01:00
Bentlybro 0ea68698ce Add retry logic and improve error handling for OpenAI API
Introduces a _retry_api_call method with exponential backoff for OpenAI API connection errors and applies it to all API calls. Error handling is updated to approve content for manual review on API errors or connection failures, with improved logging and structured error categories for moderation results.
2025-09-29 18:59:04 +01:00
Bentlybro b2a7ca4fe6 Fix API key usage update to use fresh DB instance
The update_api_key_usage method now fetches a fresh APIKey instance from the database before incrementing usage, ensuring changes are committed to the correct object. This prevents issues with stale or detached instances.
2025-09-26 00:31:30 +01:00