chore: refactor chart access
This commit is contained in:
@@ -60,6 +60,68 @@ function utc(date: string | Date) {
|
|||||||
|
|
||||||
const cacher = cacheMiddleware(60);
|
const cacher = cacheMiddleware(60);
|
||||||
|
|
||||||
|
const chartProcedure = publicProcedure.use(
|
||||||
|
async ({ ctx, next, getRawInput }) => {
|
||||||
|
const rawInput = (await getRawInput()) as {
|
||||||
|
projectId: string;
|
||||||
|
shareId?: string;
|
||||||
|
id?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rawInput.shareId) {
|
||||||
|
// Require reportId when shareId provided
|
||||||
|
if (!rawInput.id) {
|
||||||
|
throw new Error('reportId required with shareId');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate share access
|
||||||
|
const shareValidation = await validateShareAccess(
|
||||||
|
rawInput.shareId,
|
||||||
|
rawInput.id,
|
||||||
|
{
|
||||||
|
cookies: ctx.cookies,
|
||||||
|
session: ctx.session?.userId
|
||||||
|
? { userId: ctx.session.userId }
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (!shareValidation.isValid) {
|
||||||
|
throw TRPCAccessError('You do not have access to this share');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch report
|
||||||
|
const report = await getReportById(rawInput.id);
|
||||||
|
if (!report) {
|
||||||
|
throw TRPCAccessError('Report not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({
|
||||||
|
ctx: {
|
||||||
|
report,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular member access check
|
||||||
|
if (!ctx.session?.userId) {
|
||||||
|
throw TRPCAccessError('Authentication required');
|
||||||
|
}
|
||||||
|
const access = await getProjectAccess({
|
||||||
|
projectId: rawInput.projectId,
|
||||||
|
userId: ctx.session.userId,
|
||||||
|
});
|
||||||
|
if (!access) {
|
||||||
|
throw TRPCAccessError('You do not have access to this project');
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({
|
||||||
|
ctx: {
|
||||||
|
report: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const chartRouter = createTRPCRouter({
|
export const chartRouter = createTRPCRouter({
|
||||||
projectCard: protectedProcedure
|
projectCard: protectedProcedure
|
||||||
.use(cacheMiddleware(60 * 5))
|
.use(cacheMiddleware(60 * 5))
|
||||||
@@ -333,7 +395,8 @@ export const chartRouter = createTRPCRouter({
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
funnel: publicProcedure
|
funnel: chartProcedure
|
||||||
|
.use(cacher)
|
||||||
.input(
|
.input(
|
||||||
zReportInput.and(
|
zReportInput.and(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -343,56 +406,15 @@ export const chartRouter = createTRPCRouter({
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
let chartInput = input;
|
const chartInput = ctx.report
|
||||||
|
? {
|
||||||
if (input.shareId) {
|
...ctx.report,
|
||||||
// Require reportId when shareId provided
|
range: input.range ?? ctx.report.range,
|
||||||
if (!input.id) {
|
startDate: input.startDate ?? ctx.report.startDate,
|
||||||
throw new Error('reportId required with shareId');
|
endDate: input.endDate ?? ctx.report.endDate,
|
||||||
}
|
interval: input.interval ?? ctx.report.interval,
|
||||||
|
}
|
||||||
// Validate share access
|
: input;
|
||||||
const shareValidation = await validateShareAccess(
|
|
||||||
input.shareId,
|
|
||||||
input.id,
|
|
||||||
{
|
|
||||||
cookies: ctx.cookies,
|
|
||||||
session: ctx.session?.userId
|
|
||||||
? { userId: ctx.session.userId }
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (!shareValidation.isValid) {
|
|
||||||
throw TRPCAccessError('You do not have access to this share');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch report and merge date overrides
|
|
||||||
const report = await getReportById(input.id);
|
|
||||||
if (!report) {
|
|
||||||
throw TRPCAccessError('Report not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
chartInput = {
|
|
||||||
...report,
|
|
||||||
// Only allow date overrides
|
|
||||||
range: input.range ?? report.range,
|
|
||||||
startDate: input.startDate ?? report.startDate,
|
|
||||||
endDate: input.endDate ?? report.endDate,
|
|
||||||
interval: input.interval ?? report.interval,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Regular member access check
|
|
||||||
if (!ctx.session?.userId) {
|
|
||||||
throw TRPCAccessError('Authentication required');
|
|
||||||
}
|
|
||||||
const access = await getProjectAccess({
|
|
||||||
projectId: input.projectId,
|
|
||||||
userId: ctx.session.userId,
|
|
||||||
});
|
|
||||||
if (!access) {
|
|
||||||
throw TRPCAccessError('You do not have access to this project');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { timezone } = await getSettingsForProject(chartInput.projectId);
|
const { timezone } = await getSettingsForProject(chartInput.projectId);
|
||||||
const currentPeriod = getChartStartEndDate(chartInput, timezone);
|
const currentPeriod = getChartStartEndDate(chartInput, timezone);
|
||||||
@@ -415,7 +437,8 @@ export const chartRouter = createTRPCRouter({
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
conversion: publicProcedure
|
conversion: chartProcedure
|
||||||
|
.use(cacher)
|
||||||
.input(
|
.input(
|
||||||
zReportInput.and(
|
zReportInput.and(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -425,56 +448,15 @@ export const chartRouter = createTRPCRouter({
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
let chartInput = input;
|
const chartInput = ctx.report
|
||||||
|
? {
|
||||||
if (input.shareId) {
|
...ctx.report,
|
||||||
// Require reportId when shareId provided
|
range: input.range ?? ctx.report.range,
|
||||||
if (!input.id) {
|
startDate: input.startDate ?? ctx.report.startDate,
|
||||||
throw new Error('reportId required with shareId');
|
endDate: input.endDate ?? ctx.report.endDate,
|
||||||
}
|
interval: input.interval ?? ctx.report.interval,
|
||||||
|
}
|
||||||
// Validate share access
|
: input;
|
||||||
const shareValidation = await validateShareAccess(
|
|
||||||
input.shareId,
|
|
||||||
input.id,
|
|
||||||
{
|
|
||||||
cookies: ctx.cookies,
|
|
||||||
session: ctx.session?.userId
|
|
||||||
? { userId: ctx.session.userId }
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (!shareValidation.isValid) {
|
|
||||||
throw TRPCAccessError('You do not have access to this share');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch report and merge date overrides
|
|
||||||
const report = await getReportById(input.id);
|
|
||||||
if (!report) {
|
|
||||||
throw TRPCAccessError('Report not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
chartInput = {
|
|
||||||
...report,
|
|
||||||
// Only allow date overrides
|
|
||||||
range: input.range ?? report.range,
|
|
||||||
startDate: input.startDate ?? report.startDate,
|
|
||||||
endDate: input.endDate ?? report.endDate,
|
|
||||||
interval: input.interval ?? report.interval,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Regular member access check
|
|
||||||
if (!ctx.session?.userId) {
|
|
||||||
throw TRPCAccessError('Authentication required');
|
|
||||||
}
|
|
||||||
const access = await getProjectAccess({
|
|
||||||
projectId: input.projectId,
|
|
||||||
userId: ctx.session.userId,
|
|
||||||
});
|
|
||||||
if (!access) {
|
|
||||||
throw TRPCAccessError('You do not have access to this project');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { timezone } = await getSettingsForProject(chartInput.projectId);
|
const { timezone } = await getSettingsForProject(chartInput.projectId);
|
||||||
const currentPeriod = getChartStartEndDate(chartInput, timezone);
|
const currentPeriod = getChartStartEndDate(chartInput, timezone);
|
||||||
@@ -543,8 +525,8 @@ export const chartRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
chart: publicProcedure
|
chart: chartProcedure
|
||||||
// .use(cacher)
|
.use(cacher)
|
||||||
.input(
|
.input(
|
||||||
zReportInput.and(
|
zReportInput.and(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -554,58 +536,23 @@ export const chartRouter = createTRPCRouter({
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
let chartInput = input;
|
|
||||||
console.log('input', input);
|
console.log('input', input);
|
||||||
|
|
||||||
if (input.shareId) {
|
const chartInput = ctx.report
|
||||||
// Require reportId when shareId provided
|
? {
|
||||||
if (!input.id) {
|
...ctx.report,
|
||||||
throw new Error('reportId required with shareId');
|
range: input.range ?? ctx.report.range,
|
||||||
}
|
startDate: input.startDate ?? ctx.report.startDate,
|
||||||
|
endDate: input.endDate ?? ctx.report.endDate,
|
||||||
// Validate share access
|
interval: input.interval ?? ctx.report.interval,
|
||||||
const shareValidation = await validateShareAccess(
|
}
|
||||||
input.shareId,
|
: input;
|
||||||
input.id,
|
|
||||||
ctx,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!shareValidation.isValid) {
|
|
||||||
throw TRPCAccessError('You do not have access to this share');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch report and merge date overrides
|
|
||||||
const report = await getReportById(input.id);
|
|
||||||
if (!report) {
|
|
||||||
throw TRPCAccessError('Report not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
chartInput = {
|
|
||||||
...report,
|
|
||||||
// Only allow date overrides
|
|
||||||
range: input.range ?? report.range,
|
|
||||||
startDate: input.startDate ?? report.startDate,
|
|
||||||
endDate: input.endDate ?? report.endDate,
|
|
||||||
interval: input.interval ?? report.interval,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Regular member access check
|
|
||||||
if (!ctx.session?.userId) {
|
|
||||||
throw TRPCAccessError('Authentication required');
|
|
||||||
}
|
|
||||||
const access = await getProjectAccess({
|
|
||||||
projectId: input.projectId,
|
|
||||||
userId: ctx.session.userId,
|
|
||||||
});
|
|
||||||
if (!access) {
|
|
||||||
throw TRPCAccessError('You do not have access to this project');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChartEngine.execute(chartInput);
|
return ChartEngine.execute(chartInput);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
aggregate: publicProcedure
|
aggregate: chartProcedure
|
||||||
|
.use(cacher)
|
||||||
.input(
|
.input(
|
||||||
zReportInput.and(
|
zReportInput.and(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -615,61 +562,21 @@ export const chartRouter = createTRPCRouter({
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
let chartInput = input;
|
const chartInput = ctx.report
|
||||||
|
? {
|
||||||
if (input.shareId) {
|
...ctx.report,
|
||||||
// Require reportId when shareId provided
|
range: input.range ?? ctx.report.range,
|
||||||
if (!input.id) {
|
startDate: input.startDate ?? ctx.report.startDate,
|
||||||
throw new Error('reportId required with shareId');
|
endDate: input.endDate ?? ctx.report.endDate,
|
||||||
}
|
interval: input.interval ?? ctx.report.interval,
|
||||||
|
}
|
||||||
// Validate share access
|
: input;
|
||||||
const shareValidation = await validateShareAccess(
|
|
||||||
input.shareId,
|
|
||||||
input.id,
|
|
||||||
{
|
|
||||||
cookies: ctx.cookies,
|
|
||||||
session: ctx.session?.userId
|
|
||||||
? { userId: ctx.session.userId }
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (!shareValidation.isValid) {
|
|
||||||
throw TRPCAccessError('You do not have access to this share');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch report and merge date overrides
|
|
||||||
const report = await getReportById(input.id);
|
|
||||||
if (!report) {
|
|
||||||
throw TRPCAccessError('Report not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
chartInput = {
|
|
||||||
...report,
|
|
||||||
// Only allow date overrides
|
|
||||||
range: input.range ?? report.range,
|
|
||||||
startDate: input.startDate ?? report.startDate,
|
|
||||||
endDate: input.endDate ?? report.endDate,
|
|
||||||
interval: input.interval ?? report.interval,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Regular member access check
|
|
||||||
if (!ctx.session?.userId) {
|
|
||||||
throw TRPCAccessError('Authentication required');
|
|
||||||
}
|
|
||||||
const access = await getProjectAccess({
|
|
||||||
projectId: input.projectId,
|
|
||||||
userId: ctx.session.userId,
|
|
||||||
});
|
|
||||||
if (!access) {
|
|
||||||
throw TRPCAccessError('You do not have access to this project');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AggregateChartEngine.execute(chartInput);
|
return AggregateChartEngine.execute(chartInput);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
cohort: publicProcedure
|
cohort: chartProcedure
|
||||||
|
.use(cacher)
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
@@ -685,53 +592,32 @@ export const chartRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
let projectId = input.projectId;
|
const projectId = ctx.report?.projectId ?? input.projectId;
|
||||||
let firstEvent = input.firstEvent;
|
let firstEvent = input.firstEvent;
|
||||||
let secondEvent = input.secondEvent;
|
let secondEvent = input.secondEvent;
|
||||||
let criteria = input.criteria;
|
let criteria = input.criteria;
|
||||||
let dateRange = input.range;
|
const dateRange = ctx.report
|
||||||
let startDate = input.startDate;
|
? (input.range ?? ctx.report.range)
|
||||||
let endDate = input.endDate;
|
: input.range;
|
||||||
let interval = input.interval;
|
const startDate = ctx.report
|
||||||
|
? (input.startDate ?? ctx.report.startDate)
|
||||||
|
: input.startDate;
|
||||||
|
const endDate = ctx.report
|
||||||
|
? (input.endDate ?? ctx.report.endDate)
|
||||||
|
: input.endDate;
|
||||||
|
const interval = ctx.report
|
||||||
|
? (input.interval ?? ctx.report.interval)
|
||||||
|
: input.interval;
|
||||||
|
|
||||||
if (input.shareId) {
|
// Extract events from report series if shared
|
||||||
// Require reportId when shareId provided
|
if (ctx.report) {
|
||||||
if (!input.id) {
|
|
||||||
throw new Error('reportId required with shareId');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate share access
|
|
||||||
const shareValidation = await validateShareAccess(
|
|
||||||
input.shareId,
|
|
||||||
input.id,
|
|
||||||
{
|
|
||||||
cookies: ctx.cookies,
|
|
||||||
session: ctx.session?.userId
|
|
||||||
? { userId: ctx.session.userId }
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (!shareValidation.isValid) {
|
|
||||||
throw TRPCAccessError('You do not have access to this share');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch report and extract events
|
|
||||||
const report = await getReportById(input.id);
|
|
||||||
if (!report) {
|
|
||||||
throw TRPCAccessError('Report not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
projectId = report.projectId;
|
|
||||||
const retentionOptions =
|
const retentionOptions =
|
||||||
report.options?.type === 'retention' ? report.options : undefined;
|
ctx.report.options?.type === 'retention'
|
||||||
|
? ctx.report.options
|
||||||
|
: undefined;
|
||||||
criteria = retentionOptions?.criteria ?? criteria;
|
criteria = retentionOptions?.criteria ?? criteria;
|
||||||
dateRange = input.range ?? report.range;
|
|
||||||
startDate = input.startDate ?? report.startDate;
|
|
||||||
endDate = input.endDate ?? report.endDate;
|
|
||||||
interval = input.interval ?? report.interval;
|
|
||||||
|
|
||||||
// Extract events from report series
|
const eventSeries = onlyReportEvents(ctx.report.series);
|
||||||
const eventSeries = onlyReportEvents(report.series);
|
|
||||||
const extractedFirstEvent = (
|
const extractedFirstEvent = (
|
||||||
eventSeries[0]?.filters?.[0]?.value ?? []
|
eventSeries[0]?.filters?.[0]?.value ?? []
|
||||||
).map(String);
|
).map(String);
|
||||||
@@ -748,18 +634,6 @@ export const chartRouter = createTRPCRouter({
|
|||||||
|
|
||||||
firstEvent = extractedFirstEvent;
|
firstEvent = extractedFirstEvent;
|
||||||
secondEvent = extractedSecondEvent;
|
secondEvent = extractedSecondEvent;
|
||||||
} else {
|
|
||||||
// Regular member access check
|
|
||||||
if (!ctx.session?.userId) {
|
|
||||||
throw TRPCAccessError('Authentication required');
|
|
||||||
}
|
|
||||||
const access = await getProjectAccess({
|
|
||||||
projectId: input.projectId,
|
|
||||||
userId: ctx.session.userId,
|
|
||||||
});
|
|
||||||
if (!access) {
|
|
||||||
throw TRPCAccessError('You do not have access to this project');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { timezone } = await getSettingsForProject(projectId);
|
const { timezone } = await getSettingsForProject(projectId);
|
||||||
|
|||||||
Reference in New Issue
Block a user