mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-11 20:05:25 +00:00
* migrate anonymous access logic out of ee * add anonymous access toggle * handle anon toggle properly based on perms * add forceEnableAnonymousAccess setting * add docs for access settings * change forceEnableAnonymousAccess to be an env var * add FORCE_ENABLE_ANONYMOUS_ACCESS to list in docs * add back the enablePublicAccess setting as deprecated * add changelog entry * fix build errors * add news entry for anonymous access * feedback
103 lines
No EOL
3.5 KiB
TypeScript
103 lines
No EOL
3.5 KiB
TypeScript
'use server';
|
|
|
|
import { sew, withAuth, withOrgMembership } from "@/actions";
|
|
import { OrgRole } from "@sourcebot/db";
|
|
import { prisma } from "@/prisma";
|
|
import { ServiceError } from "@/lib/serviceError";
|
|
import { AnalyticsResponse } from "./types";
|
|
import { hasEntitlement } from "@sourcebot/shared";
|
|
import { ErrorCode } from "@/lib/errorCodes";
|
|
import { StatusCodes } from "http-status-codes";
|
|
|
|
export const getAnalytics = async (domain: string, apiKey: string | undefined = undefined): Promise<AnalyticsResponse | ServiceError> => sew(() =>
|
|
withAuth((userId, _apiKeyHash) =>
|
|
withOrgMembership(userId, domain, async ({ org }) => {
|
|
if (!hasEntitlement("analytics")) {
|
|
return {
|
|
statusCode: StatusCodes.FORBIDDEN,
|
|
errorCode: ErrorCode.INSUFFICIENT_PERMISSIONS,
|
|
message: "Analytics is not available in your current plan",
|
|
} satisfies ServiceError;
|
|
}
|
|
|
|
const rows = await prisma.$queryRaw<AnalyticsResponse>`
|
|
WITH core AS (
|
|
SELECT
|
|
date_trunc('day', "timestamp") AS day,
|
|
date_trunc('week', "timestamp") AS week,
|
|
date_trunc('month', "timestamp") AS month,
|
|
action,
|
|
"actorId"
|
|
FROM "Audit"
|
|
WHERE "orgId" = ${org.id}
|
|
AND action IN (
|
|
'user.performed_code_search',
|
|
'user.performed_find_references',
|
|
'user.performed_goto_definition'
|
|
)
|
|
),
|
|
|
|
periods AS (
|
|
SELECT unnest(array['day', 'week', 'month']) AS period
|
|
),
|
|
|
|
buckets AS (
|
|
SELECT
|
|
generate_series(
|
|
date_trunc('day', (SELECT MIN("timestamp") FROM "Audit" WHERE "orgId" = ${org.id})),
|
|
date_trunc('day', CURRENT_DATE),
|
|
interval '1 day'
|
|
) AS bucket,
|
|
'day' AS period
|
|
UNION ALL
|
|
SELECT
|
|
generate_series(
|
|
date_trunc('week', (SELECT MIN("timestamp") FROM "Audit" WHERE "orgId" = ${org.id})),
|
|
date_trunc('week', CURRENT_DATE),
|
|
interval '1 week'
|
|
),
|
|
'week'
|
|
UNION ALL
|
|
SELECT
|
|
generate_series(
|
|
date_trunc('month', (SELECT MIN("timestamp") FROM "Audit" WHERE "orgId" = ${org.id})),
|
|
date_trunc('month', CURRENT_DATE),
|
|
interval '1 month'
|
|
),
|
|
'month'
|
|
),
|
|
|
|
aggregated AS (
|
|
SELECT
|
|
b.period,
|
|
CASE b.period
|
|
WHEN 'day' THEN c.day
|
|
WHEN 'week' THEN c.week
|
|
ELSE c.month
|
|
END AS bucket,
|
|
COUNT(*) FILTER (WHERE c.action = 'user.performed_code_search') AS code_searches,
|
|
COUNT(*) FILTER (WHERE c.action IN ('user.performed_find_references', 'user.performed_goto_definition')) AS navigations,
|
|
COUNT(DISTINCT c."actorId") AS active_users
|
|
FROM core c
|
|
JOIN LATERAL (
|
|
SELECT unnest(array['day', 'week', 'month']) AS period
|
|
) b ON true
|
|
GROUP BY b.period, bucket
|
|
)
|
|
|
|
SELECT
|
|
b.period,
|
|
b.bucket,
|
|
COALESCE(a.code_searches, 0)::int AS code_searches,
|
|
COALESCE(a.navigations, 0)::int AS navigations,
|
|
COALESCE(a.active_users, 0)::int AS active_users
|
|
FROM buckets b
|
|
LEFT JOIN aggregated a
|
|
ON a.period = b.period AND a.bucket = b.bucket
|
|
ORDER BY b.period, b.bucket;
|
|
`;
|
|
|
|
|
|
return rows;
|
|
}, /* minRequiredRole = */ OrgRole.MEMBER), /* allowAnonymousAccess = */ true, apiKey ? { apiKey, domain } : undefined)
|
|
);
|