Files
stats/packages/db/src/services/share.service.ts
2026-02-05 21:24:37 +00:00

280 lines
5.9 KiB
TypeScript

import { db } from '../prisma-client';
import { getProjectAccess } from './access.service';
export function getShareOverviewById(id: string) {
return db.shareOverview.findFirst({
where: {
id,
},
include: {
project: true,
},
});
}
export function getShareByProjectId(projectId: string) {
return db.shareOverview.findUnique({
where: {
projectId,
},
});
}
// Dashboard sharing functions
export function getShareDashboardById(id: string) {
return db.shareDashboard.findFirst({
where: {
id,
},
include: {
dashboard: {
include: {
project: true,
},
},
},
});
}
export function getShareDashboardByDashboardId(dashboardId: string) {
return db.shareDashboard.findUnique({
where: {
dashboardId,
},
});
}
// Report sharing functions
export function getShareReportById(id: string) {
return db.shareReport.findFirst({
where: {
id,
},
include: {
report: {
include: {
project: true,
},
},
},
});
}
export function getShareReportByReportId(reportId: string) {
return db.shareReport.findUnique({
where: {
reportId,
},
});
}
// Validation for secure endpoints
export async function validateReportAccess(
reportId: string,
shareId: string,
shareType: 'dashboard' | 'report',
) {
if (shareType === 'dashboard') {
const share = await db.shareDashboard.findUnique({
where: { id: shareId },
include: {
dashboard: {
include: {
reports: {
where: { id: reportId },
},
},
},
},
});
if (!share || !share.public) {
throw new Error('Share not found or not public');
}
if (!share.dashboard.reports.some((r) => r.id === reportId)) {
throw new Error('Report does not belong to this dashboard');
}
return share;
}
const share = await db.shareReport.findUnique({
where: { id: shareId },
include: {
report: true,
},
});
if (!share || !share.public) {
throw new Error('Share not found or not public');
}
if (share.reportId !== reportId) {
throw new Error('Report ID mismatch');
}
return share;
}
// Unified validation for share access
export async function validateShareAccess(
shareId: string,
reportId: string,
ctx: {
cookies: Record<string, string | undefined>;
session?: { userId?: string | null };
},
): Promise<{ projectId: string; isValid: boolean }> {
// Check ShareDashboard first
const dashboardShare = await db.shareDashboard.findUnique({
where: { id: shareId },
include: {
dashboard: {
include: {
reports: {
where: { id: reportId },
},
},
},
},
});
if (
dashboardShare?.dashboard?.reports &&
dashboardShare.dashboard.reports.length > 0
) {
if (!dashboardShare.public) {
throw new Error('Share not found or not public');
}
const projectId = dashboardShare.projectId;
// If no password is set, share is public and accessible
if (!dashboardShare.password) {
return {
projectId,
isValid: true,
};
}
// If password is set, require cookie OR member access
const hasCookie = !!ctx.cookies[`shared-dashboard-${shareId}`];
const hasMemberAccess =
ctx.session?.userId &&
(await getProjectAccess({
userId: ctx.session.userId,
projectId,
}));
return {
projectId,
isValid: hasCookie || !!hasMemberAccess,
};
}
// Check ShareReport
const reportShare = await db.shareReport.findUnique({
where: { id: shareId, reportId },
include: {
report: true,
},
});
if (reportShare) {
if (!reportShare.public) {
throw new Error('Share not found or not public');
}
const projectId = reportShare.projectId;
// If no password is set, share is public and accessible
if (!reportShare.password) {
return {
projectId,
isValid: true,
};
}
// If password is set, require cookie OR member access
const hasCookie = !!ctx.cookies[`shared-report-${shareId}`];
const hasMemberAccess =
ctx.session?.userId &&
(await getProjectAccess({
userId: ctx.session.userId,
projectId,
}));
return {
projectId,
isValid: hasCookie || !!hasMemberAccess,
};
}
throw new Error('Share not found');
}
// Validation for overview share access
export async function validateOverviewShareAccess(
shareId: string | undefined,
projectId: string,
ctx: {
cookies: Record<string, string | undefined>;
session?: { userId?: string | null };
},
): Promise<{ isValid: boolean }> {
// If shareId is provided, validate share access
if (shareId) {
const share = await db.shareOverview.findUnique({
where: { id: shareId },
});
if (!share || !share.public) {
throw new Error('Share not found or not public');
}
// Verify the share is for the correct project
if (share.projectId !== projectId) {
throw new Error('Project ID mismatch');
}
// If no password is set, share is public and accessible
if (!share.password) {
return {
isValid: true,
};
}
// If password is set, require cookie OR member access
const hasCookie = !!ctx.cookies[`shared-overview-${shareId}`];
const hasMemberAccess =
ctx.session?.userId &&
(await getProjectAccess({
userId: ctx.session.userId,
projectId,
}));
return {
isValid: hasCookie || !!hasMemberAccess,
};
}
// If no shareId, require authenticated user with project access
if (!ctx.session?.userId) {
throw new Error('Authentication required');
}
const access = await getProjectAccess({
userId: ctx.session.userId,
projectId,
});
if (!access) {
throw new Error('You do not have access to this project');
}
return {
isValid: true,
};
}