chore(F.4e): remove env-based site loading, unify OAuth Clients, update docs
- Remove env-based site discovery from SiteManager and ProjectManager - Unify admin and user OAuth Clients into single role-based page - Update all documentation to reflect DB-based site management - Clean up env.example, README, ARCHITECTURE docs - Improve Projects page empty state with link to My Sites - Remove obsolete env discovery tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,8 @@ Uses REST APIs:
|
||||
- Export API (GET /export/events, /export/charts) for raw data export
|
||||
- Insights API (GET /insights/:projectId/*) for analytics queries
|
||||
|
||||
Note: project_id is optional if configured in environment variables.
|
||||
When not provided, the default project_id from OPENPANEL_SITE1_PROJECT_ID is used.
|
||||
Note: project_id is optional if configured in the site settings.
|
||||
When not provided, the default project_id from the site configuration is used.
|
||||
|
||||
Requires 'read' or 'root' mode client for Export API.
|
||||
"""
|
||||
|
||||
@@ -23,6 +23,6 @@ def get_project_id(client: OpenPanelClient, project_id: str | None) -> str:
|
||||
return client.default_project_id
|
||||
raise ValueError(
|
||||
"project_id is required. Either provide it as a parameter or configure "
|
||||
"OPENPANEL_SITE1_PROJECT_ID in environment variables. "
|
||||
"the project_id field when adding the site in the dashboard. "
|
||||
"You can find your Project ID in OpenPanel Dashboard → Project Settings."
|
||||
)
|
||||
|
||||
@@ -483,6 +483,8 @@ async def delete_user_factor(client: SupabaseClient, user_id: str, factor_id: st
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
|
||||
|
||||
async def search_users(
|
||||
client: SupabaseClient, query: str, page: int = 1, per_page: int = 50
|
||||
) -> str:
|
||||
|
||||
@@ -67,18 +67,17 @@ class WordPressClient:
|
||||
# Validate required parameters
|
||||
if not site_url:
|
||||
raise ConfigurationError(
|
||||
"Site URL is not configured. "
|
||||
"Please set the URL environment variable (e.g., WORDPRESS_SITE1_URL)."
|
||||
"Site URL is not configured. " "Please add or update the site in the dashboard."
|
||||
)
|
||||
if not username:
|
||||
raise ConfigurationError(
|
||||
"Username is not configured. "
|
||||
"Please set the USERNAME environment variable (e.g., WORDPRESS_SITE1_USERNAME)."
|
||||
"Please update the site credentials in the dashboard."
|
||||
)
|
||||
if not app_password:
|
||||
raise ConfigurationError(
|
||||
"App password is not configured. "
|
||||
"Please set the APP_PASSWORD environment variable (e.g., WORDPRESS_SITE1_APP_PASSWORD)."
|
||||
"Please update the site credentials in the dashboard."
|
||||
)
|
||||
|
||||
self.site_url = site_url.rstrip("/")
|
||||
|
||||
@@ -77,12 +77,8 @@ class WPCLIManager:
|
||||
try:
|
||||
# First, test if we have Docker socket access
|
||||
test_process = await asyncio.create_subprocess_exec(
|
||||
"docker",
|
||||
"version",
|
||||
"--format",
|
||||
"{{.Server.Version}}",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
"docker", "version", "--format", "{{.Server.Version}}",
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
|
||||
test_stdout, test_stderr = await asyncio.wait_for(
|
||||
@@ -103,15 +99,10 @@ class WPCLIManager:
|
||||
|
||||
# Now check for our specific container using exact name match
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"docker",
|
||||
"ps",
|
||||
"--all",
|
||||
"--filter",
|
||||
f"name=^{self.container_name}$",
|
||||
"--format",
|
||||
"{{.Names}}|{{.Status}}",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
"docker", "ps", "--all",
|
||||
"--filter", f"name=^{self.container_name}$",
|
||||
"--format", "{{.Names}}|{{.Status}}",
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
|
||||
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=5.0)
|
||||
@@ -126,13 +117,8 @@ class WPCLIManager:
|
||||
if not output:
|
||||
# Container not found - get list of available containers for helpful error
|
||||
list_process = await asyncio.create_subprocess_exec(
|
||||
"docker",
|
||||
"ps",
|
||||
"--all",
|
||||
"--format",
|
||||
"{{.Names}}",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
"docker", "ps", "--all", "--format", "{{.Names}}",
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
list_stdout, _ = await list_process.communicate()
|
||||
available = list_stdout.decode().strip().split("\n") if list_stdout else []
|
||||
@@ -184,14 +170,9 @@ class WPCLIManager:
|
||||
try:
|
||||
# Try to run wp --version
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"docker",
|
||||
"exec",
|
||||
self.container_name,
|
||||
"wp",
|
||||
"--version",
|
||||
"--allow-root",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
"docker", "exec", self.container_name,
|
||||
"wp", "--version", "--allow-root",
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
|
||||
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=5.0)
|
||||
@@ -310,9 +291,7 @@ class WPCLIManager:
|
||||
)
|
||||
|
||||
# 4. Build docker exec command (split command into args to prevent shell injection)
|
||||
cmd_parts = (
|
||||
["docker", "exec", self.container_name, "wp"] + command.split() + ["--allow-root"]
|
||||
)
|
||||
cmd_parts = ["docker", "exec", self.container_name, "wp"] + command.split() + ["--allow-root"]
|
||||
|
||||
self.logger.info(f"Executing: {' '.join(cmd_parts)}")
|
||||
|
||||
@@ -603,29 +582,17 @@ class WPCLIManager:
|
||||
try:
|
||||
# Try GNU stat first, fall back to BSD stat
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"docker",
|
||||
"exec",
|
||||
self.container_name,
|
||||
"stat",
|
||||
"-c",
|
||||
"%s",
|
||||
export_path,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
"docker", "exec", self.container_name,
|
||||
"stat", "-c", "%s", export_path,
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, _ = await asyncio.wait_for(process.communicate(), timeout=5.0)
|
||||
if process.returncode != 0:
|
||||
# BSD stat fallback
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"docker",
|
||||
"exec",
|
||||
self.container_name,
|
||||
"stat",
|
||||
"-f",
|
||||
"%z",
|
||||
export_path,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
"docker", "exec", self.container_name,
|
||||
"stat", "-f", "%z", export_path,
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, _ = await asyncio.wait_for(process.communicate(), timeout=5.0)
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class WordPressAdvancedPlugin(BasePlugin):
|
||||
if not container_name:
|
||||
raise ValueError(
|
||||
"WordPress Advanced plugin requires 'container' configuration. "
|
||||
"Please set WORDPRESS_ADVANCED_SITE1_CONTAINER in environment variables."
|
||||
"Please set the 'container' field when adding the site in the dashboard."
|
||||
)
|
||||
|
||||
# Import WP-CLI manager
|
||||
|
||||
Reference in New Issue
Block a user