diff --git a/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx b/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx
index 860fd0db191..703f54b10b4 100644
--- a/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx
+++ b/apps/studio/components/interfaces/Auth/Users/UserLogs.tsx
@@ -1,11 +1,17 @@
-import { Admonition, cn, Separator } from 'ui'
-import { PANEL_PADDING } from './UserPanel'
-import { User } from 'data/auth/users-query'
-import { UserHeader } from './UserHeader'
-import useLogsPreview from 'hooks/analytics/useLogsPreview'
+import dayjs from 'dayjs'
+import { RefreshCw } from 'lucide-react'
+import Link from 'next/link'
+import { useEffect } from 'react'
+
import { useParams } from 'common'
import { LOGS_TABLES } from 'components/interfaces/Settings/Logs/Logs.constants'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
+import { User } from 'data/auth/users-query'
+import useLogsPreview from 'hooks/analytics/useLogsPreview'
+import { Button, cn, CriticalIcon, Separator } from 'ui'
+import { Admonition } from 'ui-patterns'
+import { UserHeader } from './UserHeader'
+import { PANEL_PADDING } from './UserPanel'
interface UserLogsProps {
user: User
@@ -13,14 +19,25 @@ interface UserLogsProps {
export const UserLogs = ({ user }: UserLogsProps) => {
const { ref } = useParams()
- const { logData: authLogs, isLoading: isLoadingAuthLogs } = useLogsPreview(
- ref as string,
- LOGS_TABLES.auth,
- {
- 'metadata.user_id': user.id,
- }
- )
- console.log({ authLogs })
+
+ const {
+ logData: authLogs,
+ isSuccess: isSuccessAuthLogs,
+ isLoading: isLoadingAuthLogs,
+ filters,
+ refresh,
+ setFilters,
+ } = useLogsPreview({
+ projectRef: ref as string,
+ table: LOGS_TABLES.auth,
+ filterOverride: { search_query: user.id },
+ limit: 5,
+ })
+
+ useEffect(() => {
+ if (user.id) setFilters({ ...filters, search_query: user.id })
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [user.id])
return (
@@ -32,10 +49,47 @@ export const UserLogs = ({ user }: UserLogsProps) => {
Authentication logs
- Latest logs from authentication for this user
+ Latest logs from authentication for this user in the past hour
- {isLoadingAuthLogs ? (
+
+
+
+
+
+
+
+
}
+ onClick={() => refresh()}
+ >
+ Refresh
+
+
+
+ {isLoadingAuthLogs && !isSuccessAuthLogs ? (
) : authLogs.length === 0 ? (
{
description="Auth events such as logging in will be shown here"
/>
) : (
- Hello
+
+
+ {authLogs.map((log) => {
+ const status = ((log.status ?? '-') as any).toString()
+ const formattedTime = dayjs(log.timestamp).format('DD MMM HH:mm:ss')
+ const is400 = status.startsWith('4')
+ const is500 = status.startsWith('5')
+
+ return (
+
+
+ {formattedTime}
+
+
+
+ {(is400 || is500) && (
+
+ )}
+ {status}
+
+
+
{`${log.path} | ${log.msg}`}
+
+ )
+ })}
+
+
+
)}
diff --git a/apps/studio/components/interfaces/Auth/Users/Users.utils.tsx b/apps/studio/components/interfaces/Auth/Users/Users.utils.tsx
index bc5fbcdde4b..9581edb3ecf 100644
--- a/apps/studio/components/interfaces/Auth/Users/Users.utils.tsx
+++ b/apps/studio/components/interfaces/Auth/Users/Users.utils.tsx
@@ -117,6 +117,7 @@ const phoneProviders = providers.phone.map((x) => {
export function getDisplayName(user: User, fallback = '-'): string {
const {
+ custom_claims,
displayName,
display_name,
fullName,
@@ -132,8 +133,15 @@ export function getDisplayName(user: User, fallback = '-'): string {
first_name,
} = user.raw_user_meta_data ?? {}
- const last = familyName || family_name || surname || lastName || last_name
- const first = givenName || given_name || firstName || first_name
+ const last =
+ (custom_claims as any)?.last_name ||
+ familyName ||
+ family_name ||
+ surname ||
+ lastName ||
+ last_name
+ const first =
+ (custom_claims as any)?.first_name || givenName || given_name || firstName || first_name
return (
displayName ||
diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts
index 946c9c32017..d8ffa517b39 100644
--- a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts
+++ b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts
@@ -135,7 +135,7 @@ const genWhereStatement = (table: LogsTableName, filters: Filters) => {
}
}
-export const genDefaultQuery = (table: LogsTableName, filters: Filters) => {
+export const genDefaultQuery = (table: LogsTableName, filters: Filters, limit: number = 100) => {
const where = genWhereStatement(table, filters)
const joins = genCrossJoinUnnests(table)
const orderBy = 'order by timestamp desc'
@@ -150,7 +150,7 @@ from edge_logs
${joins}
${where}
${orderBy}
-limit 100;
+limit ${limit};
`
}
return `select id, identifier, timestamp, event_message, request.method, request.path, response.status_code
@@ -158,7 +158,7 @@ limit 100;
${joins}
${where}
${orderBy}
- limit 100
+ limit ${limit}
`
case 'postgres_logs':
@@ -169,14 +169,14 @@ from postgres_logs
${joins}
${where}
${orderBy}
-limit 100
+limit ${limit}
`
}
return `select identifier, postgres_logs.timestamp, id, event_message, parsed.error_severity from ${table}
${joins}
${where}
${orderBy}
- limit 100
+ limit ${limit}
`
case 'function_logs':
@@ -184,7 +184,7 @@ limit 100
${joins}
${where}
${orderBy}
- limit 100
+ limit ${limit}
`
case 'auth_logs':
@@ -192,7 +192,7 @@ limit 100
${joins}
${where}
${orderBy}
- limit 100
+ limit ${limit}
`
case 'function_edge_logs':
@@ -200,16 +200,16 @@ limit 100
${joins}
${where}
${orderBy}
- limit 100
+ limit ${limit}
`
case 'supavisor_logs':
- return `select id, ${table}.timestamp, event_message from ${table} ${joins} ${where} ${orderBy} limit 100`
+ return `select id, ${table}.timestamp, event_message from ${table} ${joins} ${where} ${orderBy} limit ${limit}`
default:
return `select id, ${table}.timestamp, event_message from ${table}
${where}
${orderBy}
- limit 100
+ limit ${limit}
`
}
}
diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx
index acee66e0bd2..309954a6cb7 100644
--- a/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx
+++ b/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx
@@ -69,7 +69,7 @@ export const LogsPreviewer = ({
setFilters,
refresh,
setParams,
- } = useLogsPreview(projectRef as string, table, filterOverride)
+ } = useLogsPreview({ projectRef, table, filterOverride })
const { showUpgradePrompt, setShowUpgradePrompt } = useUpgradePrompt(
params.iso_timestamp_start as string
diff --git a/apps/studio/data/analytics/auth-logs-query.ts b/apps/studio/data/analytics/auth-logs-query.ts
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/apps/studio/data/analytics/keys.ts b/apps/studio/data/analytics/keys.ts
index 5f1e97e656d..bc2d012cccd 100644
--- a/apps/studio/data/analytics/keys.ts
+++ b/apps/studio/data/analytics/keys.ts
@@ -1,3 +1,6 @@
+import { LogsTableName } from 'components/interfaces/Settings/Logs/Logs.constants'
+import { Filters } from 'components/interfaces/Settings/Logs/Logs.types'
+
export const analyticsKeys = {
// warehouse
warehouseQuery: (projectRef: string, query: string) =>
@@ -151,6 +154,12 @@ export const analyticsKeys = {
usageApiRequestsCount: (projectRef: string | undefined) =>
['projects', projectRef, 'usage.api-requests-count'] as const,
+ previewLogs: (projectRef: string | undefined, table: LogsTableName, filters: Filters) => [
+ 'projects',
+ projectRef,
+ table,
+ filters,
+ ],
}
function isoDateStringToDate(isoDateString: string | undefined): string | undefined {
diff --git a/apps/studio/hooks/analytics/useLogsPreview.tsx b/apps/studio/hooks/analytics/useLogsPreview.tsx
index 30b14092d2d..e3b61acfeb3 100644
--- a/apps/studio/hooks/analytics/useLogsPreview.tsx
+++ b/apps/studio/hooks/analytics/useLogsPreview.tsx
@@ -31,6 +31,7 @@ interface LogsPreviewHook {
logData: LogData[]
error: string | Object | null
newCount: number
+ isSuccess: boolean
isLoading: boolean
isLoadingOlder: boolean
filters: Filters
@@ -42,11 +43,17 @@ interface LogsPreviewHook {
setFilters: (filters: Filters | ((previous: Filters) => Filters)) => void
setParams: Dispatch>
}
-function useLogsPreview(
- projectRef: string,
- table: LogsTableName,
+function useLogsPreview({
+ projectRef,
+ table,
+ filterOverride,
+ limit,
+}: {
+ projectRef: string
+ table: LogsTableName
filterOverride?: Filters
-): LogsPreviewHook {
+ limit?: number
+}): LogsPreviewHook {
const defaultHelper = getDefaultHelper(PREVIEWER_DATEPICKER_HELPERS)
const [latestRefresh, setLatestRefresh] = useState(new Date().toISOString())
@@ -55,7 +62,7 @@ function useLogsPreview(
const [params, setParams] = useState({
project: projectRef,
- sql: genDefaultQuery(table, filters),
+ sql: genDefaultQuery(table, filters, limit),
iso_timestamp_start: defaultHelper.calcFrom(),
iso_timestamp_end: defaultHelper.calcTo(),
})
@@ -68,10 +75,9 @@ function useLogsPreview(
refresh()
}, [JSON.stringify(filters)])
- const queryParamsKey = genQueryParams(params as any)
-
const {
data,
+ isSuccess,
isLoading,
isRefetching,
error: rqError,
@@ -79,7 +85,7 @@ function useLogsPreview(
isFetchingNextPage,
refetch,
} = useInfiniteQuery(
- ['projects', projectRef, 'logs', queryParamsKey],
+ ['projects', projectRef, 'logs', params],
({ signal, pageParam }) => {
const uri = `${API_URL}/projects/${projectRef}/analytics/endpoints/logs.all?${genQueryParams({
...params,
@@ -171,7 +177,7 @@ function useLogsPreview(
)
const refresh = async () => {
- const generatedSql = genDefaultQuery(table, filters)
+ const generatedSql = genDefaultQuery(table, filters, limit)
setParams((prev) => ({ ...prev, sql: generatedSql }))
setLatestRefresh(new Date().toISOString())
refreshEventChart()
@@ -207,6 +213,7 @@ function useLogsPreview(
return {
newCount,
logData,
+ isSuccess,
isLoading: isLoading || isRefetching,
isLoadingOlder: isFetchingNextPage,
error: error || eventChartError,