Skip to content

Commit 7dbe93a

Browse files
committed
unlist custom sql
1 parent 46bc7ec commit 7dbe93a

File tree

2 files changed

+2
-230
lines changed

2 files changed

+2
-230
lines changed

apps/api/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { autumnHandler } from 'autumn-js/elysia';
77
import { Elysia } from 'elysia';
88
import { logger } from './lib/logger';
99
import { assistant } from './routes/assistant';
10-
import { customSQL } from './routes/custom-sql';
10+
// import { customSQL } from './routes/custom-sql';
1111
import { exportRoute } from './routes/export';
1212
import { health } from './routes/health';
1313
import { publicApi } from './routes/public';
@@ -45,7 +45,7 @@ const app = new Elysia()
4545
})
4646
)
4747
.use(query)
48-
.use(customSQL)
48+
// .use(customSQL)
4949
.use(assistant)
5050
.use(exportRoute)
5151
.all('/trpc/*', ({ request }) => {

apps/docs/content/docs/api.mdx

Lines changed: 0 additions & 228 deletions
Original file line numberDiff line numberDiff line change
@@ -377,234 +377,6 @@ Compile a query to see the generated SQL without executing it. Useful for debugg
377377
}`}
378378
/>
379379

380-
## Custom SQL API
381-
382-
<Callout type="info">
383-
**New Feature!** 🚀 Use `properties.X` syntax for easy JSON property extraction. Write `properties.browser_name` instead of `JSONExtractString(properties, 'browser_name')`. Supports type annotations like `properties.user_id:int`, `properties.is_active:bool`.
384-
</Callout>
385-
386-
### Execute Custom SQL Queries
387-
388-
Execute custom SQL queries directly against your ClickHouse analytics database with enterprise-grade security.
389-
390-
<CodeBlock
391-
language="http"
392-
code="POST /v1/custom-sql/execute"
393-
/>
394-
395-
**Authentication:**
396-
- Requires API key with `write:custom-sql` or `read:analytics` scope
397-
- API key must have `read:data` access to the specified `clientId`
398-
399-
**Request:**
400-
401-
<CodeBlock
402-
language="json"
403-
code={`{
404-
"query": "SELECT count() as events FROM analytics.events WHERE time >= now() - INTERVAL 7 DAY",
405-
"clientId": "your-client-id",
406-
"parameters": {
407-
"customParam": "value"
408-
}
409-
}`}
410-
/>
411-
412-
**Example with Properties.X Syntax:**
413-
414-
<CodeBlock
415-
language="json"
416-
code={`{
417-
"query": "SELECT properties.browser_name AS browser_name, properties.user_id:int AS user_id, count() as events FROM analytics.custom_events WHERE properties.browser_name IS NOT NULL GROUP BY properties.browser_name, properties.user_id:int ORDER BY events DESC LIMIT 10",
418-
"clientId": "your-client-id"
419-
}`}
420-
/>
421-
422-
**Headers:**
423-
424-
<CodeBlock
425-
language="bash"
426-
code={`curl -X POST https://api.databuddy.cc/v1/custom-sql/execute \\
427-
-H "Content-Type: application/json" \\
428-
-H "x-api-key: dbdy_your_api_key_here" \\
429-
-d '{"query": "SELECT count() FROM analytics.events", "clientId": "client_123"}'`}
430-
/>
431-
432-
**Available Tables:**
433-
- `analytics.events` - All tracked events and page views
434-
- `analytics.errors` - JavaScript errors and exceptions
435-
- `analytics.custom_events` - Custom event tracking data
436-
- `analytics.web_vitals` - Core Web Vitals performance metrics
437-
438-
**Properties.X Syntax Helper:**
439-
Databuddy provides a convenient syntax helper that automatically converts `properties.X` notation to ClickHouse `JSONExtract` functions:
440-
441-
```sql
442-
-- Instead of writing:
443-
SELECT JSONExtractString(properties, 'browser_name') AS browser_name FROM analytics.events
444-
445-
-- You can simply write:
446-
SELECT properties.browser_name AS browser_name FROM analytics.events
447-
```
448-
449-
<Callout type="warn">
450-
**Important:** Always use `AS` aliases when selecting properties to get clean column names in your response. Without aliases, you'll get ugly column names like `"JSONExtractString(properties, 'browser_name')"` instead of `"browser_name"`.
451-
</Callout>
452-
453-
**Supported Type Annotations:**
454-
- `properties.property_name``JSONExtractString(properties, 'property_name')` (default)
455-
- `properties.user_id:int``JSONExtractInt(properties, 'user_id')`
456-
- `properties.is_active:bool``JSONExtractBool(properties, 'is_active')`
457-
- `properties.score:float``JSONExtractFloat(properties, 'score')`
458-
- `properties.metadata:raw``JSONExtractRaw(properties, 'metadata')`
459-
460-
**Security Features:**
461-
- **Automatic client isolation** - All queries automatically filtered by your API key's client access
462-
- **Smart WHERE injection** - Client filtering added to your queries without breaking syntax
463-
- SQL injection prevention with parameterized queries
464-
- Query complexity limits (max 5 SELECT statements, 2 UNION operations, 5000 characters)
465-
- Forbidden operations blocked (INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, etc.)
466-
- Attack pattern detection for known injection techniques
467-
- Comments and multiple statements not allowed
468-
- Balanced parentheses validation
469-
- Properties.X syntax automatically transformed to secure JSONExtract calls
470-
471-
**Response:**
472-
473-
<CodeBlock
474-
language="json"
475-
code={`{
476-
"success": true,
477-
"data": [
478-
{
479-
"events": 15420
480-
}
481-
],
482-
"meta": {
483-
"rowCount": 1,
484-
"columns": ["events"],
485-
"executionTime": 0.045,
486-
"rowsRead": 15420,
487-
"clientId": "client_123",
488-
"apiKeyId": "key_456"
489-
}
490-
}`}
491-
/>
492-
493-
### Get Custom SQL Schema
494-
495-
Get information about allowed tables, operations, and security constraints.
496-
497-
<CodeBlock
498-
language="http"
499-
code="GET /v1/custom-sql/schema"
500-
/>
501-
502-
**Response:**
503-
504-
<CodeBlock
505-
language="json"
506-
code={`{
507-
"success": true,
508-
"schema": {
509-
"allowedTables": [
510-
"analytics.events",
511-
"analytics.errors",
512-
"analytics.custom_events",
513-
"analytics.web_vitals"
514-
],
515-
"allowedOperations": [
516-
"SELECT", "WITH", "FROM", "WHERE", "GROUP BY", "ORDER BY", "HAVING", "LIMIT", "OFFSET", "JOIN", "LEFT JOIN", "RIGHT JOIN", "INNER JOIN", "UNION", "UNION ALL", "JSONExtract", "JSONExtractString", "JSONExtractInt", "JSONExtractFloat", "JSONExtractBool", "JSONExtractRaw", "CASE", "WHEN", "THEN", "ELSE", "END", "AS", "AND", "OR", "NOT", "IN", "EXISTS", "BETWEEN", "LIKE", "ILIKE"
517-
],
518-
"forbiddenOperations": [
519-
"INSERT", "UPDATE", "DELETE", "DROP", "CREATE", "ALTER", "TRUNCATE", "REPLACE", "MERGE", "CALL", "EXEC", "EXECUTE", "DECLARE", "SET", "USE", "SHOW", "DESCRIBE", "EXPLAIN", "ANALYZE", "OPTIMIZE", "REPAIR", "LOCK", "UNLOCK", "GRANT", "REVOKE", "COMMIT", "ROLLBACK", "SAVEPOINT", "RELEASE", "START TRANSACTION", "BEGIN", "INFORMATION_SCHEMA", "SYSTEM", "ADMIN", "SUPER", "FILE", "PROCESS", "RELOAD", "SHUTDOWN", "REFERENCES", "INDEX", "TRIGGER", "EVENT", "ROUTINE"
520-
],
521-
"maxQueryLength": 5000,
522-
"maxNestedSelects": 5,
523-
"maxUnionOperations": 2,
524-
"parameterization": {
525-
"clientIdParameter": "{clientId:String}",
526-
"required": "All queries must use parameterized client filtering"
527-
},
528-
"propertiesSyntax": {
529-
"description": "Automatic JSONExtract transformation from properties.X syntax",
530-
"syntax": "properties.property_name[:type]",
531-
"supportedTypes": ["string", "int", "float", "bool", "raw"],
532-
"examples": [
533-
"properties.browser_name",
534-
"properties.user_id:int",
535-
"properties.is_active:bool",
536-
"properties.metadata:raw"
537-
],
538-
"defaultType": "string (JSONExtractString)"
539-
}
540-
}
541-
}`}
542-
/>
543-
544-
### Get Query Examples
545-
546-
Get example queries to help you get started with custom SQL.
547-
548-
<CodeBlock
549-
language="http"
550-
code="GET /v1/custom-sql/examples"
551-
/>
552-
553-
**Response:**
554-
555-
<CodeBlock
556-
language="json"
557-
code={`{
558-
"success": true,
559-
"examples": [
560-
{
561-
"name": "Monthly Events Count",
562-
"description": "Get monthly event counts for your client",
563-
"query": "SELECT toStartOfMonth(time) as month_start, count() as event_count FROM analytics.events WHERE time >= now() - INTERVAL 6 MONTH GROUP BY month_start ORDER BY month_start DESC"
564-
},
565-
{
566-
"name": "Top Pages by Views",
567-
"description": "Get most popular pages",
568-
"query": "SELECT path, count() as page_views, uniq(session_id) as unique_sessions FROM analytics.events WHERE time >= now() - INTERVAL 30 DAY AND event_name = 'page_view' GROUP BY path ORDER BY page_views DESC LIMIT 10"
569-
},
570-
{
571-
"name": "Browser Analytics (with properties.X syntax)",
572-
"description": "Analyze browser usage using properties.X syntax",
573-
"query": "SELECT properties.browser_name, count() as events, uniq(anonymous_id) as unique_users FROM analytics.events WHERE time >= now() - INTERVAL 7 DAY AND properties.browser_name IS NOT NULL GROUP BY properties.browser_name ORDER BY events DESC"
574-
},
575-
{
576-
"name": "User Analytics with Typed Properties",
577-
"description": "Analyze user behavior with typed property extraction",
578-
"query": "SELECT properties.user_id:int as user_id, properties.is_premium:bool as is_premium, properties.session_duration:float as session_duration, count() as total_events FROM analytics.events WHERE time >= now() - INTERVAL 30 DAY AND properties.user_id:int IS NOT NULL GROUP BY properties.user_id:int, properties.is_premium:bool, properties.session_duration:float ORDER BY total_events DESC LIMIT 20"
579-
},
580-
{
581-
"name": "Error Events Analysis",
582-
"description": "Analyze error events",
583-
"query": "SELECT url, count() as error_count FROM analytics.errors WHERE time >= now() - INTERVAL 7 DAY GROUP BY url ORDER BY error_count DESC LIMIT 10"
584-
}
585-
]
586-
}`}
587-
/>
588-
589-
**Error Responses:**
590-
591-
<CodeBlock
592-
language="json"
593-
code={`{
594-
"success": false,
595-
"error": "API key does not have access to client ID: client_123",
596-
"code": "CLIENT_ACCESS_DENIED"
597-
}`}
598-
/>
599-
600-
**Common Error Codes:**
601-
- `AUTH_REQUIRED` - API key missing or invalid
602-
- `INSUFFICIENT_SCOPE` - API key lacks required scopes
603-
- `CLIENT_ACCESS_DENIED` - No access to specified clientId
604-
- `FORBIDDEN_OPERATION` - Query contains forbidden SQL operations
605-
- `QUERY_TOO_COMPLEX` - Query exceeds complexity limits
606-
- `ATTACK_PATTERN_DETECTED` - Query matches known attack patterns
607-
608380
## AI Assistant
609381

610382
### Stream Analytics Query

0 commit comments

Comments
 (0)