16 Commits
Author SHA1 Message Date
Bentlybro 9d0a51a265 Fix registration default when enabled by env 2026-10-19 11:33:02 +01:00
Bentlybro 21c831572c Fix project and user deletion, update admin registration toggle 2026-10-19 11:28:32 +01:00
Bentlybro 3d86c0412e Tidy comments 2026-10-19 11:15:05 +01:00
Bentlybro 9a779a4c96 Update registration settings and admin project management 2026-10-19 11:05:09 +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 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 31ea8a5259 Add system-wide registration toggle feature
Introduces a SystemSettings model to manage global configuration, including user registration status. Admins can now enable or disable new user registration from the admin panel, and registration routes respect this setting. UI updates provide controls for toggling registration.
2025-10-03 16:29:24 +01:00
Bentlybro c074804e2e Optimize API user stats queries and filter users with content
Improved APIUser.get_current_stats to use a single efficient query with caching for stats retrieval. Updated manual_review route to only display API users who have submitted content by joining with a content count subquery and filtering out users with zero content.
2025-10-03 15:43:57 +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 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 0f395b3b79 Update api_key.py 2025-09-26 00:20:55 +01:00
Bentlybro dd455d4dd3 Refactor project and moderation rule creation logic
Project and moderation rule creation methods now return dictionaries instead of ORM instances to avoid detached instance issues. Updated related usages in dashboard route and default rules configuration to match new return types and argument names. Also added cascade delete-orphan to APIUser's project relationship.
2025-09-11 09:11:37 +01:00
Bentlybro 52d940dc23 Refactor API user stats and enhance API docs UI
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.
2025-08-26 15:13:28 +01:00
Bentlybro 52f23161db Remove unused SQLAlchemy imports and update flake8 config
Eliminated unnecessary 'flask_sqlalchemy' imports from model files to clean up code. Updated flake8 max line length to 128 in pre-commit config. Improved error response formatting in manual_review route for better readability.
2025-08-25 19:25:48 +01:00
Bentlybro c78017eb3d autopep8 + flake8 + isort
Reorganized imports and formatting across models and routes for better readability and maintainability. Improved consistency in model field definitions, relationship declarations, and route logic. No functional changes, but code is now easier to follow and modify.
2025-08-20 16:19:26 +01:00
Bentlybro 635805353d Initial code! 2025-07-29 12:52:14 +01:00