2025-10-09 04:03:23 +08:00
|
|
|
import { screenshots_task_keys } from '@buster-app/trigger/task-keys';
|
2025-10-09 04:25:28 +08:00
|
|
|
import type { TakeReportScreenshotTrigger } from '@buster-app/trigger/task-schemas';
|
2025-09-25 04:32:50 +08:00
|
|
|
import { checkPermission } from '@buster/access-controls';
|
2025-10-09 04:03:23 +08:00
|
|
|
import {
|
|
|
|
type User,
|
|
|
|
getMetricIdsInReport,
|
|
|
|
getReportFileById,
|
|
|
|
getUserOrganizationId,
|
|
|
|
} from '@buster/database/queries';
|
2025-09-20 23:03:54 +08:00
|
|
|
import {
|
|
|
|
GetReportParamsSchema,
|
|
|
|
GetReportQuerySchema,
|
|
|
|
type GetReportResponse,
|
|
|
|
} from '@buster/server-shared/reports';
|
|
|
|
import { zValidator } from '@hono/zod-validator';
|
2025-10-09 06:11:56 +08:00
|
|
|
import { shouldTakeScreenshot } from '@shared-helpers/screenshots';
|
2025-10-09 04:03:23 +08:00
|
|
|
import { tasks } from '@trigger.dev/sdk';
|
2025-08-03 08:38:50 +08:00
|
|
|
import { Hono } from 'hono';
|
|
|
|
import { HTTPException } from 'hono/http-exception';
|
2025-09-27 05:04:45 +08:00
|
|
|
import { throwUnauthorizedError } from '../../../../shared-helpers/asset-public-access';
|
2025-09-26 02:46:39 +08:00
|
|
|
import { getMetricsInAncestorAssetFromMetricIds } from '../../../../shared-helpers/metric-helpers';
|
2025-08-05 09:27:18 +08:00
|
|
|
import { standardErrorHandler } from '../../../../utils/response';
|
2025-08-03 08:38:50 +08:00
|
|
|
|
2025-08-05 05:37:48 +08:00
|
|
|
export async function getReportHandler(
|
2025-08-03 08:38:50 +08:00
|
|
|
reportId: string,
|
2025-09-26 02:46:39 +08:00
|
|
|
user: User,
|
2025-09-21 01:15:53 +08:00
|
|
|
versionNumber?: number | undefined,
|
|
|
|
password?: string | undefined
|
2025-08-21 11:53:51 +08:00
|
|
|
): Promise<GetReportResponse> {
|
2025-09-26 02:46:39 +08:00
|
|
|
const [report, metricIds] = await Promise.all([
|
|
|
|
getReportFileById({
|
|
|
|
reportId,
|
|
|
|
userId: user.id,
|
|
|
|
versionNumber,
|
|
|
|
}),
|
|
|
|
getMetricIdsInReport({ reportId }),
|
|
|
|
]);
|
2025-08-08 06:42:04 +08:00
|
|
|
|
2025-09-25 04:32:50 +08:00
|
|
|
const permission = await checkPermission({
|
|
|
|
userId: user.id,
|
2025-09-21 01:05:10 +08:00
|
|
|
assetId: reportId,
|
|
|
|
assetType: 'report_file',
|
2025-09-25 04:32:50 +08:00
|
|
|
requiredRole: 'can_view',
|
2025-09-21 01:05:10 +08:00
|
|
|
workspaceSharing: report.workspace_sharing,
|
2025-09-25 04:32:50 +08:00
|
|
|
organizationId: report.organization_id,
|
|
|
|
publiclyAccessible: report.publicly_accessible,
|
|
|
|
publicExpiryDate: report.public_expiry_date ?? undefined,
|
|
|
|
publicPassword: report.public_password ?? undefined,
|
|
|
|
userSuppliedPassword: password,
|
2025-09-21 01:05:10 +08:00
|
|
|
});
|
2025-09-25 04:32:50 +08:00
|
|
|
|
|
|
|
if (!permission.hasAccess || !permission.effectiveRole) {
|
2025-09-27 03:18:31 +08:00
|
|
|
throwUnauthorizedError({
|
|
|
|
publiclyAccessible: report.publicly_accessible ?? false,
|
|
|
|
publicExpiryDate: report.public_expiry_date ?? undefined,
|
|
|
|
publicPassword: report.public_password ?? undefined,
|
|
|
|
userSuppliedPassword: password,
|
|
|
|
});
|
2025-09-25 04:32:50 +08:00
|
|
|
}
|
|
|
|
|
2025-09-26 02:46:39 +08:00
|
|
|
const metrics = await getMetricsInAncestorAssetFromMetricIds(metricIds, user);
|
|
|
|
|
2025-09-25 04:32:50 +08:00
|
|
|
const response: GetReportResponse = {
|
|
|
|
...report,
|
|
|
|
permission: permission.effectiveRole,
|
2025-09-26 02:46:39 +08:00
|
|
|
metrics,
|
2025-09-25 04:32:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
return response;
|
2025-08-03 08:38:50 +08:00
|
|
|
}
|
|
|
|
|
2025-08-05 09:27:18 +08:00
|
|
|
const app = new Hono()
|
2025-09-20 23:03:54 +08:00
|
|
|
.get(
|
|
|
|
'/',
|
|
|
|
zValidator('param', GetReportParamsSchema),
|
|
|
|
zValidator('query', GetReportQuerySchema),
|
|
|
|
async (c) => {
|
|
|
|
const { id: reportId } = c.req.valid('param');
|
|
|
|
const query = c.req.valid('query');
|
2025-09-21 00:37:50 +08:00
|
|
|
const { password, version_number: versionNumber } = query;
|
2025-09-20 23:03:54 +08:00
|
|
|
const user = c.get('busterUser');
|
2025-08-03 10:15:48 +08:00
|
|
|
|
2025-09-20 23:03:54 +08:00
|
|
|
if (!reportId) {
|
|
|
|
throw new HTTPException(404, { message: 'Report ID is required' });
|
|
|
|
}
|
2025-08-03 10:15:48 +08:00
|
|
|
|
2025-09-20 23:03:54 +08:00
|
|
|
const response: GetReportResponse = await getReportHandler(
|
|
|
|
reportId,
|
|
|
|
user,
|
2025-09-21 00:37:50 +08:00
|
|
|
versionNumber,
|
|
|
|
password
|
2025-09-20 23:03:54 +08:00
|
|
|
);
|
2025-10-09 04:03:23 +08:00
|
|
|
|
2025-10-09 06:55:44 +08:00
|
|
|
const tag = `take-report-screenshot-${reportId}`;
|
2025-10-09 06:11:56 +08:00
|
|
|
|
|
|
|
if (
|
|
|
|
await shouldTakeScreenshot({
|
|
|
|
tag,
|
|
|
|
key: screenshots_task_keys.take_report_screenshot,
|
|
|
|
context: c,
|
|
|
|
})
|
|
|
|
) {
|
|
|
|
tasks.trigger(
|
|
|
|
screenshots_task_keys.take_report_screenshot,
|
|
|
|
{
|
|
|
|
reportId,
|
|
|
|
organizationId: (await getUserOrganizationId(user.id))?.organizationId || '',
|
|
|
|
accessToken: c.get('accessToken'),
|
|
|
|
} satisfies TakeReportScreenshotTrigger,
|
2025-10-09 22:13:05 +08:00
|
|
|
{ tags: [tag], idempotencyKey: tag }
|
2025-10-09 06:11:56 +08:00
|
|
|
);
|
|
|
|
}
|
2025-10-09 04:03:23 +08:00
|
|
|
|
2025-09-20 23:03:54 +08:00
|
|
|
return c.json(response);
|
|
|
|
}
|
|
|
|
)
|
2025-08-05 09:27:18 +08:00
|
|
|
.onError(standardErrorHandler);
|
2025-08-03 08:38:50 +08:00
|
|
|
|
|
|
|
export default app;
|