Home / Function/ AuditLogs() — supabase Function Reference

AuditLogs() — supabase Function Reference

Architecture documentation for the AuditLogs() function in AuditLogs.tsx from the supabase codebase.

Entity Profile

Relationship Graph

Source Code

apps/studio/components/interfaces/Account/AuditLogs.tsx lines 21–300

export const AuditLogs = () => {
  const currentTime = dayjs().utc().set('millisecond', 0)

  const [search, setSearch] = useState('')
  const debouncedSearch = useDebounce(search, 500)

  const [dateSortDesc, setDateSortDesc] = useState(true)
  const [dateRange, setDateRange] = useState({
    from: currentTime.subtract(1, 'day').toISOString(),
    to: currentTime.toISOString(),
  })

  const [selectedLog, setSelectedLog] = useState<AuditLog>()
  const [filters, setFilters] = useState<{ projects: string[] }>({
    projects: [], // project_ref[]
  })

  const {
    data: projectsData,
    isLoading: isLoadingProjects,
    isFetching,
    isFetchingNextPage,
    hasNextPage,
    fetchNextPage,
  } = useProjectsInfiniteQuery(
    { search: search.length === 0 ? search : debouncedSearch },
    { placeholderData: keepPreviousData }
  )
  const projects =
    useMemo(() => projectsData?.pages.flatMap((page) => page.projects), [projectsData?.pages]) || []

  const { data: organizations } = useOrganizationsQuery()
  const {
    data,
    error,
    isPending: isLoading,
    isSuccess,
    isError,
    isRefetching,
    refetch,
  } = useProfileAuditLogsQuery(
    {
      iso_timestamp_start: dateRange.from,
      iso_timestamp_end: dateRange.to,
    },
    {
      retry: false,
    }
  )

  const logs = data?.result ?? []
  const sortedLogs = logs
    ?.sort((a, b) =>
      dateSortDesc
        ? Number(new Date(b.occurred_at)) - Number(new Date(a.occurred_at))
        : Number(new Date(a.occurred_at)) - Number(new Date(b.occurred_at))
    )
    ?.filter((log) => {
      if (filters.projects.length > 0) {
        return filters.projects.includes(
          log.target.metadata.project_ref || log.target.metadata.ref || ''
        )
      } else {
        return log
      }
    })

  // This feature depends on the subscription tier of the user. Free user can view logs up to 1 day
  // in the past. The API limits the logs to maximum of 1 day and 5 minutes so when the page is
  // viewed for more than 5 minutes, the call parameters needs to be updated. This also works with
  // higher tiers (7 days of logs).The user will see a loading shimmer.
  useEffect(() => {
    const duration = dayjs(dateRange.from).diff(dayjs(dateRange.to))
    const interval = setInterval(() => {
      const currentTime = dayjs().utc().set('millisecond', 0)
      setDateRange({
        from: currentTime.add(duration).toISOString(),
        to: currentTime.toISOString(),
      })
    }, 5 * 60000)

    return () => clearInterval(interval)
  }, [dateRange.from, dateRange.to])

  return (
    <>
      <div className="space-y-4 flex flex-col pb-8">
        <div className="flex flex-col md:flex-row md:items-center justify-between">
          <div className="flex items-center space-x-2">
            <p className="text-xs prose">Filter by</p>
            <FilterPopover
              name="Projects"
              options={projects ?? []}
              labelKey="name"
              valueKey="ref"
              activeOptions={filters.projects}
              onSaveFilters={(values) => setFilters({ ...filters, projects: values })}
              search={search}
              setSearch={setSearch}
              hasNextPage={hasNextPage}
              isLoading={isLoadingProjects}
              isFetching={isFetching}
              isFetchingNextPage={isFetchingNextPage}
              fetchNextPage={fetchNextPage}
            />
            <LogsDatePicker
              hideWarnings
              value={dateRange}
              onSubmit={(value) => setDateRange(value)}
              helpers={[
                {
                  text: 'Last 1 hour',
                  calcFrom: () => dayjs().subtract(1, 'hour').toISOString(),
                  calcTo: () => dayjs().toISOString(),
                },
                {
                  text: 'Last 3 hours',
                  calcFrom: () => dayjs().subtract(3, 'hour').toISOString(),
                  calcTo: () => dayjs().toISOString(),
                },

                {
                  text: 'Last 6 hours',
                  calcFrom: () => dayjs().subtract(6, 'hour').toISOString(),
                  calcTo: () => dayjs().toISOString(),
                },
                {
                  text: 'Last 12 hours',
                  calcFrom: () => dayjs().subtract(12, 'hour').toISOString(),
                  calcTo: () => dayjs().toISOString(),
                },
                {
                  text: 'Last 24 hours',
                  calcFrom: () => dayjs().subtract(1, 'day').toISOString(),
                  calcTo: () => dayjs().toISOString(),
                },
              ]}
            />
            {isSuccess && (
              <>
                <div className="h-[20px] border-r border-strong !ml-4 !mr-2" />
                <p className="prose text-xs">Viewing {sortedLogs.length} logs in total</p>
              </>
            )}
          </div>
          <Button
            type="default"
            disabled={isLoading || isRefetching}
            icon={<RefreshCw className={isRefetching ? 'animate-spin' : ''} />}
            onClick={() => refetch()}
          >
            {isRefetching ? 'Refreshing' : 'Refresh'}
          </Button>
        </div>

        {isLoading && (
          <div className="space-y-2">
            <ShimmeringLoader />
            <ShimmeringLoader className="w-3/4" />
            <ShimmeringLoader className="w-1/2" />
          </div>
        )}

        {isError && <AlertError error={error} subject="Failed to retrieve audit logs" />}

        {isSuccess && (
          <>
            {logs.length === 0 ? (
              <div className="bg-surface-100 border rounded p-4 flex items-center justify-between">
                <p className="prose text-sm">You do not have any audit logs available yet</p>
              </div>
            ) : logs.length > 0 && sortedLogs.length === 0 ? (
              <div className="bg-surface-100 border rounded p-4 flex items-center justify-between">
                <p className="prose text-sm">No audit logs found based on the filters applied</p>
              </div>
            ) : (
              <div className="overflow-hidden md:overflow-auto overflow-x-scroll">
                <Table
                  head={[
                    <Table.th key="action" className="py-2">
                      Action
                    </Table.th>,
                    <Table.th key="target" className="py-2">
                      Target
                    </Table.th>,
                    <Table.th key="date" className="py-2">
                      <div className="flex items-center space-x-2">
                        <p>Date</p>
                        <ButtonTooltip
                          type="text"
                          className="px-1"
                          icon={
                            dateSortDesc ? (
                              <ArrowDown strokeWidth={1.5} size={14} />
                            ) : (
                              <ArrowUp strokeWidth={1.5} size={14} />
                            )
                          }
                          onClick={() => setDateSortDesc(!dateSortDesc)}
                          tooltip={{
                            content: {
                              side: 'bottom',
                              text: dateSortDesc ? 'Sort latest first' : 'Sort earliest first',
                            },
                          }}
                        />
                      </div>
                    </Table.th>,
                    <Table.th key="actions" className="py-2"></Table.th>,
                  ]}
                  body={
                    sortedLogs?.map((log) => {
                      const logProjectRef =
                        log.target.metadata.project_ref || log.target.metadata.ref
                      const logOrgSlug = log.target.metadata.org_slug || log.target.metadata.slug

                      const project = projects?.find((project) => project.ref === logProjectRef)
                      const organization = organizations?.find((org) => org.slug === logOrgSlug)

                      const hasStatusCode = log.action.metadata[0]?.status !== undefined

                      return (
                        <Table.tr
                          key={log.occurred_at}
                          onClick={() => setSelectedLog(log)}
                          className="cursor-pointer hover:!bg-alternative transition duration-100"
                        >
                          <Table.td className="max-w-[250px]">
                            <div className="flex items-center space-x-2">
                              {hasStatusCode && (
                                <p className="bg-surface-200 rounded px-1 flex items-center justify-center text-xs font-mono border">
                                  {log.action.metadata[0].status}
                                </p>
                              )}
                              <p className="truncate" title={log.action.name}>
                                {log.action.name}
                              </p>
                            </div>
                          </Table.td>
                          <Table.td>
                            <p
                              className="text-foreground-light max-w-[230px] truncate"
                              title={project?.name ?? organization?.name ?? '-'}
                            >
                              {project?.name
                                ? 'Project: '
                                : organization?.name
                                  ? 'Organization: '
                                  : null}
                              {project?.name ?? organization?.name ?? '-'}
                            </p>
                            <p
                              className="text-foreground-light text-xs mt-0.5 truncate"
                              title={logProjectRef ?? logOrgSlug ?? ''}
                            >
                              {logProjectRef ? 'Ref: ' : logOrgSlug ? 'Slug: ' : null}
                              {logProjectRef ?? logOrgSlug}
                            </p>
                          </Table.td>
                          <Table.td>
                            <TimestampInfo className="text-sm" utcTimestamp={log.occurred_at} />
                          </Table.td>
                          <Table.td align="right">
                            <Button type="default">View details</Button>
                          </Table.td>
                        </Table.tr>
                      )
                    }) ?? []
                  }
                />
              </div>
            )}
          </>
        )}
      </div>

      <LogDetailsPanel selectedLog={selectedLog} onClose={() => setSelectedLog(undefined)} />
    </>
  )
}

Subdomains

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free