Skip to content

Commit 715f506

Browse files
committed
logs table
1 parent 890af4c commit 715f506

File tree

4 files changed

+310
-3
lines changed

4 files changed

+310
-3
lines changed

apps/web/src/components/tools/auth0/dashboard.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ import {
1818
SelectValue,
1919
} from "@/components/ui/select"
2020
import { Separator } from "@/components/ui/separator"
21-
import { Card, CardContent } from "@/components/ui/card"
21+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
2222
import { Button } from "@/components/ui/button"
2323
import { UserRetentionChart, UserRetentionDataPoint } from './user-retention-chart'
24+
import { LogsTable } from './logs-table'
2425

2526
interface ConversionData {
2627
new_signups: number
@@ -66,6 +67,15 @@ interface ConversionRateResult {
6667
data: { conversion_rate: number }[]
6768
}
6869

70+
interface LogEntry {
71+
timestamp: string
72+
type: string
73+
description: string
74+
id: string
75+
connection: string
76+
application: string
77+
}
78+
6979
export default function Auth0Dashboard() {
7080
const [token] = useQueryState('token')
7181
const [dateRange, setDateRange] = useState<DateRange>({
@@ -91,6 +101,7 @@ export default function Auth0Dashboard() {
91101
const [connections, setConnections] = useState<Array<{ connection_id: string, connection_name: string }>>([])
92102
const [timeRange, setTimeRange] = useState<'hourly' | 'daily' | 'monthly'>('daily')
93103
const [userRetentionData, setUserRetentionData] = useState<UserRetentionDataPoint[]>([])
104+
const [logs, setLogs] = useState<LogEntry[]>([])
94105

95106
useEffect(() => {
96107
async function fetchInitialData() {
@@ -154,7 +165,8 @@ export default function Auth0Dashboard() {
154165
dauComparisonResult,
155166
authMechResult,
156167
dailySignupsResult,
157-
dailyLoginFailsResult
168+
dailyLoginFailsResult,
169+
logsResult
158170
] = await Promise.all([
159171
pipe<UsersResult>(token, 'auth0_users_total', selectedApp !== 'all' || selectedConnection !== 'all' ? {
160172
time_range: timeRange,
@@ -176,7 +188,13 @@ export default function Auth0Dashboard() {
176188
}) : Promise.resolve({ data: [] }),
177189
pipe<{ data: AuthMechDataPoint[] }>(token, 'auth0_mech_usage', params),
178190
pipe<{ data: DailySignupsDataPoint[] }>(token, 'auth0_daily_signups', params),
179-
pipe<{ data: DailyLoginFailsDataPoint[] }>(token, 'auth0_daily_login_fails', params)
191+
pipe<{ data: DailyLoginFailsDataPoint[] }>(token, 'auth0_daily_login_fails', params),
192+
pipe<{ data: LogEntry[] }>(token, 'auth0_logs', {
193+
date_from: fromDate,
194+
date_to: toDate,
195+
...(selectedApp !== 'all' && { client_id: selectedApp }),
196+
...(selectedConnection !== 'all' && { connection_id: selectedConnection })
197+
})
180198
])
181199

182200
setSummaryMetrics({
@@ -194,6 +212,7 @@ export default function Auth0Dashboard() {
194212
setDailySignupsData(dailySignupsResult?.data ?? [])
195213
setDailyLoginFailsData(dailyLoginFailsResult?.data ?? [])
196214
setUserRetentionData(userRetentionTimeSeriesResult?.data ?? [])
215+
setLogs(logsResult?.data ?? [])
197216
} catch (error) {
198217
console.error('Failed to fetch metrics:', error)
199218
}
@@ -347,6 +366,14 @@ export default function Auth0Dashboard() {
347366
className="h-[300px]"
348367
/>
349368
</div>
369+
<Card>
370+
<CardHeader>
371+
<CardTitle>Logs</CardTitle>
372+
</CardHeader>
373+
<CardContent>
374+
<LogsTable data={logs} />
375+
</CardContent>
376+
</Card>
350377

351378
</div>
352379
)
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import {
2+
Table,
3+
TableBody,
4+
TableCell,
5+
TableHead,
6+
TableHeader,
7+
TableRow,
8+
} from "@/components/ui/table"
9+
import { format } from "date-fns"
10+
11+
interface LogEntry {
12+
event_time: string
13+
event_type: string
14+
description: string
15+
id: string
16+
connection: string
17+
application: string
18+
}
19+
20+
interface LogsTableProps {
21+
data: LogEntry[]
22+
}
23+
24+
const EVENT_TYPE_NAMES: Record<string, string> = {
25+
'api_limit': 'Rate Limit on APIs',
26+
'appi': 'API Peak Performance Initiated',
27+
'ciba_exchange_failed': 'Failed CIBA Exchange',
28+
'ciba_exchange_succeeded': 'Successful CIBA Exchange',
29+
'ciba_start_failed': 'Failed CIBA Start',
30+
'ciba_start_succeeded': 'Successful CIBA Start',
31+
'cls': 'Code/Link Sent',
32+
'cs': 'Code Sent',
33+
'depnote': 'Deprecation Notice',
34+
'f': 'Failed Login',
35+
'fc': 'Failed by Connector',
36+
'fce': 'Failed Change Email',
37+
'fco': 'Failed by CORS',
38+
'fcoa': 'Failed Cross-Origin Authentication',
39+
'fcp': 'Failed Change Password',
40+
'fcph': 'Failed Post Change Password Hook',
41+
'fcpn': 'Failed Change Phone Number',
42+
'fcpr': 'Failed Change Password Request',
43+
'fcpro': 'Failed Connector Provisioning',
44+
'fcu': 'Failed Change Username',
45+
'fd': 'Failed Delegation',
46+
'fdeac': 'Failed Device Activation',
47+
'fdeaz': 'Failed Device Authorization',
48+
'fdecc': 'User Canceled Device Confirmation',
49+
'fdu': 'Failed User Deletion',
50+
'feacft': 'Failed Authorization Code Exchange',
51+
'feccft': 'Failed Client Credentials Exchange',
52+
'fede': 'Failed Device Code Exchange',
53+
'fens': 'Failed Native Social Login Exchange',
54+
'feoobft': 'Failed OOB Challenge Exchange',
55+
'feotpft': 'Failed OTP Challenge Exchange',
56+
'fepft': 'Failed Password Exchange',
57+
'fepotpft': 'Failed Passwordless OTP Exchange',
58+
'fercft': 'Failed MFA Recovery Code Exchange',
59+
'ferrt': 'Failed Rotating Refresh Token Exchange',
60+
'fertft': 'Failed Refresh Token Exchange',
61+
'fi': 'Failed Invite Accept',
62+
'flo': 'Failed Logout',
63+
'fn': 'Failed Notification',
64+
'fp': 'Failed Login (Wrong Password)',
65+
'fpar': 'Failed Pushed Authorization',
66+
'fs': 'Failed Signup',
67+
'fsa': 'Failed Silent Auth',
68+
'fu': 'Failed Login (Invalid Email)',
69+
'fui': 'Failed Users Import',
70+
'fv': 'Failed Verification Email',
71+
'fvr': 'Failed Verification Email Request',
72+
's': 'Success Login',
73+
'sapi': 'Success API Operation',
74+
'sce': 'Success Change Email',
75+
'scoa': 'Success Cross-Origin Authentication',
76+
'scp': 'Success Change Password',
77+
'scpn': 'Success Change Phone Number',
78+
'scpr': 'Success Change Password Request',
79+
'scu': 'Success Change Username',
80+
'sd': 'Success Delegation',
81+
'sdu': 'Success User Deletion',
82+
'seacft': 'Success Authorization Code Exchange',
83+
'seccft': 'Success Client Credentials Exchange',
84+
'sede': 'Success Device Code Exchange',
85+
'sens': 'Success Native Social Login',
86+
'seoobft': 'Success OOB Challenge Exchange',
87+
'seotpft': 'Success OTP Challenge Exchange',
88+
'sepft': 'Success Password Exchange',
89+
'sertft': 'Success Refresh Token Exchange',
90+
'si': 'Success Invite Accept',
91+
'slo': 'Success Logout',
92+
'ss': 'Success Signup',
93+
'ssa': 'Success Silent Auth',
94+
'sui': 'Success Users Import',
95+
'sv': 'Success Verification Email',
96+
'svr': 'Success Verification Email Request',
97+
'w': 'Warning During Login',
98+
'wum': 'Warning User Management'
99+
}
100+
101+
export function LogsTable({ data }: LogsTableProps) {
102+
return (
103+
<Table>
104+
<TableHeader>
105+
<TableRow>
106+
<TableHead className="w-[180px]">Timestamp</TableHead>
107+
<TableHead>Type</TableHead>
108+
<TableHead>Description</TableHead>
109+
<TableHead>ID</TableHead>
110+
<TableHead>Connection</TableHead>
111+
<TableHead>Application</TableHead>
112+
</TableRow>
113+
</TableHeader>
114+
<TableBody>
115+
{data.map((log) => (
116+
<TableRow key={log.id}>
117+
<TableCell className="whitespace-nowrap">{format(new Date(log.event_time), 'yyyy-MM-dd HH:mm:ss')}</TableCell>
118+
<TableCell className="font-medium truncate max-w-[150px]">
119+
{EVENT_TYPE_NAMES[log.event_type] || log.event_type}
120+
</TableCell>
121+
<TableCell className="truncate max-w-[300px]">{log.description}</TableCell>
122+
<TableCell className="font-mono text-xs truncate max-w-[200px]">{log.id}</TableCell>
123+
<TableCell className="truncate max-w-[150px]">{log.connection || 'N/A'}</TableCell>
124+
<TableCell className="truncate max-w-[150px]">{log.application || 'N/A'}</TableCell>
125+
</TableRow>
126+
))}
127+
</TableBody>
128+
</Table>
129+
)
130+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import * as React from "react"
2+
3+
import { cn } from "@/lib/utils"
4+
5+
const Table = React.forwardRef<
6+
HTMLTableElement,
7+
React.HTMLAttributes<HTMLTableElement>
8+
>(({ className, ...props }, ref) => (
9+
<div className="relative w-full overflow-auto">
10+
<table
11+
ref={ref}
12+
className={cn("w-full caption-bottom text-sm", className)}
13+
{...props}
14+
/>
15+
</div>
16+
))
17+
Table.displayName = "Table"
18+
19+
const TableHeader = React.forwardRef<
20+
HTMLTableSectionElement,
21+
React.HTMLAttributes<HTMLTableSectionElement>
22+
>(({ className, ...props }, ref) => (
23+
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
24+
))
25+
TableHeader.displayName = "TableHeader"
26+
27+
const TableBody = React.forwardRef<
28+
HTMLTableSectionElement,
29+
React.HTMLAttributes<HTMLTableSectionElement>
30+
>(({ className, ...props }, ref) => (
31+
<tbody
32+
ref={ref}
33+
className={cn("[&_tr:last-child]:border-0", className)}
34+
{...props}
35+
/>
36+
))
37+
TableBody.displayName = "TableBody"
38+
39+
const TableFooter = React.forwardRef<
40+
HTMLTableSectionElement,
41+
React.HTMLAttributes<HTMLTableSectionElement>
42+
>(({ className, ...props }, ref) => (
43+
<tfoot
44+
ref={ref}
45+
className={cn(
46+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
47+
className
48+
)}
49+
{...props}
50+
/>
51+
))
52+
TableFooter.displayName = "TableFooter"
53+
54+
const TableRow = React.forwardRef<
55+
HTMLTableRowElement,
56+
React.HTMLAttributes<HTMLTableRowElement>
57+
>(({ className, ...props }, ref) => (
58+
<tr
59+
ref={ref}
60+
className={cn(
61+
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
62+
className
63+
)}
64+
{...props}
65+
/>
66+
))
67+
TableRow.displayName = "TableRow"
68+
69+
const TableHead = React.forwardRef<
70+
HTMLTableCellElement,
71+
React.ThHTMLAttributes<HTMLTableCellElement>
72+
>(({ className, ...props }, ref) => (
73+
<th
74+
ref={ref}
75+
className={cn(
76+
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
77+
className
78+
)}
79+
{...props}
80+
/>
81+
))
82+
TableHead.displayName = "TableHead"
83+
84+
const TableCell = React.forwardRef<
85+
HTMLTableCellElement,
86+
React.TdHTMLAttributes<HTMLTableCellElement>
87+
>(({ className, ...props }, ref) => (
88+
<td
89+
ref={ref}
90+
className={cn(
91+
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
92+
className
93+
)}
94+
{...props}
95+
/>
96+
))
97+
TableCell.displayName = "TableCell"
98+
99+
const TableCaption = React.forwardRef<
100+
HTMLTableCaptionElement,
101+
React.HTMLAttributes<HTMLTableCaptionElement>
102+
>(({ className, ...props }, ref) => (
103+
<caption
104+
ref={ref}
105+
className={cn("mt-4 text-sm text-muted-foreground", className)}
106+
{...props}
107+
/>
108+
))
109+
TableCaption.displayName = "TableCaption"
110+
111+
export {
112+
Table,
113+
TableHeader,
114+
TableBody,
115+
TableFooter,
116+
TableHead,
117+
TableRow,
118+
TableCell,
119+
TableCaption,
120+
}

tinybird/pipes/auth0_logs.pipe

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
TOKEN "read" READ
2+
3+
TAGS "auth0"
4+
5+
NODE get_logs
6+
SQL >
7+
%
8+
SELECT
9+
event.data.date as event_time,
10+
event_type,
11+
event.data.description::String as description,
12+
event.data.log_id::String as id,
13+
event.data.connection::String as connection,
14+
event.data.client_name::String as application
15+
FROM auth0
16+
WHERE 1
17+
{% if defined(client_name) %}
18+
AND event.data.client_name::String = {{String(client_name)}}
19+
{% end %}
20+
{% if defined(connection) %}
21+
AND event.data.connection::String = {{String(connection)}}
22+
{% end %}
23+
{% if defined(event_type) %}
24+
AND event.data.event_type::String = {{String(event_type)}}
25+
{% end %}
26+
and event_time >= {{DateTime(date_from, '2024-01-01 00:00:00')}}
27+
and event_time <= {{DateTime(date_to, '2024-01-01 23:59:59')}}
28+
order by event_time desc
29+
LIMIT {{Int32(page_size, 100)}}
30+
OFFSET {{Int32(page, 0) * Int32(page_size, 100)}}

0 commit comments

Comments
 (0)