feat(v3.13.0): settings live DB reads, remove Directus/Appwrite, admin stats

Settings fixes:
- MAX_SITES_PER_USER, USER_RATE_LIMIT_PER_MIN/HR now read from DB settings
  table (DB > ENV > default), so dashboard/settings changes apply without
  restart. Sync cache refreshed on every save or delete.
- /api/me reports the live DB value for max_sites_per_user.

Admin improvements:
- Admin users bypass per-user rate limiting entirely (role=admin or ADMIN_EMAILS).
- Admin Overview now shows platform stats: registered users, new users (7d),
  total user sites, available tools.

Plugin cleanup:
- Appwrite and Directus plugins removed from the active registry (8 plugins
  now: WordPress, WooCommerce, WordPress Specialist, Gitea, n8n, Supabase,
  OpenPanel, Coolify). Plugin code is retained for future re-enabling.
- Settings page plugin visibility list updated to match.

Mobile onboarding:
- Stepper steps on narrow viewports stack vertically with correct full border
  and rounded corners on each step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 23:33:20 +02:00
parent f203ca88de
commit 43fd2201a0
223 changed files with 36183 additions and 4115 deletions

View File

@@ -37,7 +37,7 @@ logger = logging.getLogger(__name__)
_DEFAULT_DATA_DIR = "/app/data" if Path("/app").exists() else "./data"
# Schema version — increment when adding migrations
SCHEMA_VERSION = 13
SCHEMA_VERSION = 14
# Initial schema DDL
_SCHEMA_SQL = """\
@@ -281,6 +281,23 @@ _MIGRATIONS: dict[int, str] = {
# callers don't have to pass `model=...` every time.
"ALTER TABLE site_provider_keys ADD COLUMN default_model TEXT;\n"
),
14: (
# F.19.2.2: every user API key gets full-tier scope by design.
# Tool visibility is gated per-site via ``sites.tool_scope`` and
# per-tool toggles, not per-key. Older keys were issued before
# the F.19.2.0 tier system added ``editor`` / ``settings`` /
# ``install`` between ``read`` and ``write``, so their stored
# scope string ("read write admin" or narrower) doesn't list the
# new tiers explicitly. The universal-tier closure already maps
# ``admin`` to every tier, so functionally these keys see every
# tool — but enumerating the scope names explicitly keeps the
# DB consistent with the dashboard preset dropdown and removes
# any risk if a future change checks scope names directly
# without going through UNIVERSAL_SCOPE_TIERS.
"UPDATE user_api_keys "
"SET scopes = 'read editor settings install write admin' "
"WHERE scopes IN ('read write admin', 'read write', 'admin');\n"
),
}
@@ -759,6 +776,27 @@ class Database:
)
return row["cnt"] if row else 0
async def count_all_users(self) -> int:
"""Return total number of registered users."""
row = await self.fetchone("SELECT COUNT(*) AS cnt FROM users")
return row["cnt"] if row else 0
async def count_all_sites(self) -> int:
"""Return total number of user-owned sites across all users."""
row = await self.fetchone("SELECT COUNT(*) AS cnt FROM sites")
return row["cnt"] if row else 0
async def count_recent_users(self, days: int = 7) -> int:
"""Return number of users registered in the last N days."""
import time
cutoff = time.time() - days * 86400
row = await self.fetchone(
"SELECT COUNT(*) AS cnt FROM users WHERE created_at > ?",
(cutoff,),
)
return row["cnt"] if row else 0
# ------------------------------------------------------------------
# Settings CRUD (Phase 4C.3)
# ------------------------------------------------------------------