diff --git a/apps/web/public/content/dub.md b/apps/web/public/content/dub.md new file mode 100644 index 0000000..48e461c --- /dev/null +++ b/apps/web/public/content/dub.md @@ -0,0 +1,22 @@ +# Connect Dub to Tinybird + +1. Open the Dub UI and go to the Settings > Webhooks page. + +2. Select "Create Webhook". + +3. In Tinybird, go to Tokens, and copy the `append` token. + +4. Back in Dub, paste the Events API URL as your webhook URL. Use the query parameter `name=dub` and `token=`. For example: + +``` +https://api.tinybird.co/v0/events?name=dub&token=TOKEN +``` + +5. Select the checkboxes for the events you want to send to Tinybird, and select "Create webhook". + +6. You're done. Dub will now push events to Tinybird via the [Events API](https://tinybird.co/docs/get-data-in/ingest-apis/events-api). + + +## See also + +* [Events API](https://tinybird.co/docs/get-data-in/ingest-apis/events-api). diff --git a/apps/web/public/dub_favicon.png b/apps/web/public/dub_favicon.png new file mode 100644 index 0000000..7aa285a Binary files /dev/null and b/apps/web/public/dub_favicon.png differ diff --git a/apps/web/src/components/tools/dub/dashboard.tsx b/apps/web/src/components/tools/dub/dashboard.tsx new file mode 100644 index 0000000..8060297 --- /dev/null +++ b/apps/web/src/components/tools/dub/dashboard.tsx @@ -0,0 +1,35 @@ +"use client" + +import { useQueryState } from 'nuqs' +import { useEffect } from 'react' + +export default function DubDashboard() { + const [token] = useQueryState('token') + + useEffect(() => { + async function fetchMetrics() { + if (!token) return + + try { + const [] = await Promise.all([ + ]) + } catch (error) { + console.error('Failed to fetch metrics:', error) + } + } + + fetchMetrics() + }, [token]) + + return ( +
+ {/* Metrics Row */} +
+
+ + {/* Charts Grid */} +
+
+
+ ) +} diff --git a/apps/web/src/components/tools/dub/readme.tsx b/apps/web/src/components/tools/dub/readme.tsx new file mode 100644 index 0000000..9405e5b --- /dev/null +++ b/apps/web/src/components/tools/dub/readme.tsx @@ -0,0 +1,19 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { Markdown } from '@/components/markdown'; +import { getMarkdownContent } from '@/lib/markdown'; + +export default function DubReadme() { + const [content, setContent] = useState(''); + + useEffect(() => { + getMarkdownContent('dub').then(setContent); + }, []); + + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/apps/web/src/lib/constants.ts b/apps/web/src/lib/constants.ts index 91500af..020421d 100644 --- a/apps/web/src/lib/constants.ts +++ b/apps/web/src/lib/constants.ts @@ -127,6 +127,15 @@ export const TOOLS: Record = { icon: '👀', icon_url: '/sentry_favicon.ico' }, + // Links + dub: { + id: 'dub', + ds: 'dub', + name: 'Dub', + description: 'Link infrastructure', + icon: '👀', + icon_url: '/dub_favicon.png' + }, }; export const TOOL_IMPORTS = { @@ -182,6 +191,10 @@ export const TOOL_IMPORTS = { Dashboard: dynamic(() => import('@/components/tools/pagerduty/dashboard')), Readme: dynamic(() => import('@/components/tools/pagerduty/readme')), }, + dub: { + Dashboard: dynamic(() => import('@/components/tools/dub/dashboard')), + Readme: dynamic(() => import('@/components/tools/dub/readme')), + }, } as const; export type ToolId = keyof typeof TOOL_IMPORTS; \ No newline at end of file diff --git a/tinybird/datasources/dub.datasource b/tinybird/datasources/dub.datasource new file mode 100644 index 0000000..78bfa34 --- /dev/null +++ b/tinybird/datasources/dub.datasource @@ -0,0 +1,13 @@ +TOKEN "append" APPEND +TOKEN "read" READ + +TAGS "dub" + +SCHEMA > + `event_time` DateTime64(3) `json:$.tinybirdIngestTime` DEFAULT now(), + `event_type` String `json:$.event` DEFAULT 'unknown', + `event` JSON(max_dynamic_types=2, max_dynamic_paths=16) `json:$` DEFAULT '{}' + +ENGINE "MergeTree" +ENGINE_PARTITION_KEY "toYYYYMM(event_time)" +ENGINE_SORTING_KEY "event_time"