Add report files proxy router and integrate with existing API

This commit is contained in:
dal 2025-08-18 13:54:43 -06:00
parent 1d107f4125
commit 978c6b684b
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
2 changed files with 133 additions and 1 deletions

View File

@ -1,8 +1,9 @@
import type { Context } from 'hono';
import { chatsProxyRouter } from './chats';
import { messagesProxyRouter } from './messages';
import { reportFilesProxyRouter } from './report-files';
type SupportedTables = 'messages' | 'chats';
type SupportedTables = 'messages' | 'chats' | 'report_files';
const proxyRouter: Record<
SupportedTables,
@ -10,6 +11,7 @@ const proxyRouter: Record<
> = {
messages: messagesProxyRouter,
chats: chatsProxyRouter,
report_files: reportFilesProxyRouter,
};
export default proxyRouter;

View File

@ -0,0 +1,130 @@
import { markdownToPlatejs } from '@buster/server-utils/report';
import type { Context } from 'hono';
import { errorResponse } from '../../../utils/response';
import { createProxiedResponse, extractParamFromWhere } from './_helpers';
// Types for Electric SQL response format
interface ElectricDataRow<T> {
value: T;
key: string;
headers: {
last?: boolean;
relation: string[];
operation: string;
lsn: string;
op_position: number;
txids: number[];
};
}
interface ElectricControlMessage {
headers: {
control: string;
global_last_seen_lsn?: string;
};
}
type ElectricResponse<T> = Array<ElectricDataRow<T> | ElectricControlMessage>;
// Type for report_files table row
interface ReportFileRow {
id: string;
name: string;
content: string; // This is what we'll transform
organization_id: string;
created_by: string;
created_at: string;
updated_at: string;
deleted_at?: string | null;
publicly_accessible: boolean;
publicly_enabled_by?: string | null;
public_expiry_date?: string | null;
version_history: Record<
string,
{
content: string;
updated_at: string;
version_number: number;
}
>;
public_password?: string | null;
workspace_sharing: string;
workspace_sharing_enabled_by?: string | null;
workspace_sharing_enabled_at?: string | null;
}
/**
* Transform the content field from markdown to PlateJS format
*/
export async function transformReportFilesResponse(response: Response): Promise<Response> {
const data = (await response.json()) as ElectricResponse<ReportFileRow>;
// Transform content field for data rows
const transformedData = await Promise.all(
data.map(async (item) => {
// Type guard to check if this is a data row (not a control message)
if ('value' in item && item.value.content) {
const { elements, error } = await markdownToPlatejs(item.value.content);
if (error) {
console.error('Error transforming report content:', error);
// Keep original content if transformation fails
return item;
}
// Replace content with PlateJS elements
return {
...item,
value: {
...item.value,
content: JSON.stringify(elements),
},
};
}
// Return control messages and other data unchanged
return item;
})
);
// Return new response with same headers
return new Response(JSON.stringify(transformedData), {
headers: response.headers,
status: response.status,
statusText: response.statusText,
});
}
export const reportFilesProxyRouter = async (url: URL, _userId: string, c: Context) => {
const reportId = extractParamFromWhere(url, 'id');
if (!reportId) {
throw errorResponse('Report ID (id) is required', 403);
}
// TODO: Implement proper access control for reports
// Should check:
// 1. If user created the report (createdBy === userId)
// 2. If user is in the same organization as the report
// 3. If workspace sharing is enabled ('can_view' or 'can_edit')
// 4. If report has individual permissions for the user
// 5. If report is publicly accessible
//
// const userHasAccess = await canUserAccessReport({
// userId: c.get('supabaseUser').id,
// reportId,
// });
//
// if (!userHasAccess) {
// throw errorResponse('You do not have access to this report', 403);
// }
// For now, allow access with a warning
console.warn(
`TODO: Implement access control for report ${reportId} for user ${c.get('supabaseUser').id}`
);
// Fetch the response and transform it
const response = await createProxiedResponse(url);
return await transformReportFilesResponse(response);
};