feat: v3.4.0 — OpenPanel plugin public release (42 tools)

OpenPanel product analytics plugin fully reviewed, tested, and published.
Works with both self-hosted and cloud (openpanel.dev) instances.

- 42 tools: event tracking, data export, analytics, project/client management
- All tools use public REST APIs (Track, Export, Insights, Manage)
- Client modes: write (tracking), read (analytics), root (full access)
- Service page with description, setup notes, WordPress plugin download
- Dynamic URL hints in Add Site form
- 62 unit tests
- ENABLED_PLUGINS default now includes openpanel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 17:48:12 +02:00
parent 68c84b3ece
commit 9905a28eb0
33 changed files with 1672 additions and 3207 deletions

View File

@@ -157,7 +157,7 @@ PLUGIN_CREDENTIAL_FIELDS: dict[str, list[dict[str, Any]]] = {
"label": "Client ID",
"type": "text",
"required": True,
"hint": "OpenPanel admin panel → API section",
"hint": "OpenPanel Dashboard → Settings → Clients → Create client with 'root' mode for full access",
},
{
"name": "client_secret",
@@ -166,6 +166,21 @@ PLUGIN_CREDENTIAL_FIELDS: dict[str, list[dict[str, Any]]] = {
"required": True,
"hint": "Generated with your Client ID",
},
{
"name": "project_id",
"label": "Project ID",
"type": "text",
"required": False,
"hint": "From dashboard URL: dashboard.openpanel.dev/{org}/{project-id}/ — sets default for Export & Insights tools",
},
{
"name": "organization_id",
"label": "Organization ID",
"type": "text",
"required": False,
"hint": "From dashboard URL: dashboard.openpanel.dev/{org}/{project-id}/",
"advanced": True,
},
],
"appwrite": [
{
@@ -215,7 +230,7 @@ _HEALTH_ENDPOINTS: dict[str, dict[str, Any]] = {
"gitea": {"path": "/api/v1/user", "method": "GET"},
"n8n": {"path": "/healthz", "method": "GET"},
"supabase": {"path": "/rest/v1/", "method": "GET"},
"openpanel": {"path": "/api/v1/oauth/token", "method": "POST"},
"openpanel": {"path": "/healthcheck", "method": "GET"},
"appwrite": {"path": "/v1/health", "method": "GET"},
"directus": {"path": "/server/health", "method": "GET"},
}
@@ -342,23 +357,13 @@ async def validate_site_connection(
elif plugin_type == "directus":
headers["Authorization"] = f"Bearer {credentials.get('token', '')}"
elif plugin_type == "openpanel":
# OpenPanel uses token exchange — just check that the URL is reachable
pass
headers["openpanel-client-id"] = credentials.get("client_id", "")
headers["openpanel-client-secret"] = credentials.get("client_secret", "")
try:
timeout = aiohttp.ClientTimeout(total=15)
async with aiohttp.ClientSession(timeout=timeout) as session:
if method == "POST" and plugin_type == "openpanel":
async with session.post(
check_url,
json={
"clientId": credentials.get("client_id", ""),
"clientSecret": credentials.get("client_secret", ""),
},
) as resp:
status_code = resp.status
resp_text = await resp.text()
elif method == "POST":
if method == "POST":
async with session.post(check_url, headers=headers) as resp:
status_code = resp.status
resp_text = await resp.text()