chore:little fixes and formating and linting and patches

This commit is contained in:
2026-03-31 15:50:54 +02:00
parent a1ce71ffb6
commit 9b197abcfa
815 changed files with 22960 additions and 8982 deletions

View File

@@ -1,3 +1,3 @@
export { getProjectAccess } from './src/access';
export * from './src/root';
export * from './src/trpc';
export { getProjectAccess } from './src/access';

View File

@@ -1,5 +1,5 @@
export {
getClientAccess,
getOrganizationAccess,
getProjectAccess,
getClientAccess,
} from '@openpanel/db';

View File

@@ -1,7 +1,5 @@
import { z } from 'zod';
import { db } from '@openpanel/db';
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure } from '../trpc';
export const chatRouter = createTRPCRouter({

View File

@@ -1,10 +1,8 @@
import crypto from 'node:crypto';
import { z } from 'zod';
import { hashPassword } from '@openpanel/common/server';
import type { Prisma } from '@openpanel/db';
import { db } from '@openpanel/db';
import { hashPassword } from '@openpanel/common/server';
import { z } from 'zod';
import { getClientAccess } from '../access';
import { TRPCAccessError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
@@ -14,7 +12,7 @@ export const clientRouter = createTRPCRouter({
.input(
z.object({
projectId: z.string(),
}),
})
)
.query(async ({ input }) => {
return db.client.findMany({
@@ -28,7 +26,7 @@ export const clientRouter = createTRPCRouter({
z.object({
id: z.string(),
name: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const access = await getClientAccess({
@@ -56,7 +54,7 @@ export const clientRouter = createTRPCRouter({
projectId: z.string(),
organizationId: z.string(),
type: z.enum(['read', 'write', 'root']).optional(),
}),
})
)
.mutation(async ({ input }) => {
const secret = `sec_${crypto.randomBytes(10).toString('hex')}`;
@@ -79,7 +77,7 @@ export const clientRouter = createTRPCRouter({
.input(
z.object({
id: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const access = await getClientAccess({

View File

@@ -1,6 +1,4 @@
import { PrismaError } from 'prisma-error-enum';
import { z } from 'zod';
import type { Prisma } from '@openpanel/db';
import {
db,
getDashboardById,
@@ -8,8 +6,8 @@ import {
getId,
getProjectById,
} from '@openpanel/db';
import type { Prisma } from '@openpanel/db';
import { PrismaError } from 'prisma-error-enum';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import { TRPCAccessError, TRPCNotFoundError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
@@ -19,7 +17,7 @@ export const dashboardRouter = createTRPCRouter({
.input(
z.object({
projectId: z.string(),
}),
})
)
.query(({ input }) => {
return getDashboardsByProjectId(input.projectId);
@@ -29,7 +27,7 @@ export const dashboardRouter = createTRPCRouter({
z.object({
id: z.string(),
projectId: z.string(),
}),
})
)
.query(async ({ input, ctx }) => {
const access = await getProjectAccess({
@@ -54,7 +52,7 @@ export const dashboardRouter = createTRPCRouter({
z.object({
name: z.string(),
projectId: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const access = await getProjectAccess({
@@ -86,7 +84,7 @@ export const dashboardRouter = createTRPCRouter({
z.object({
id: z.string(),
name: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const dashboard = await db.dashboard.findUniqueOrThrow({
@@ -118,7 +116,7 @@ export const dashboardRouter = createTRPCRouter({
z.object({
id: z.string(),
forceDelete: z.boolean().optional(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const dashboard = await db.dashboard.findUniqueOrThrow({
@@ -157,7 +155,7 @@ export const dashboardRouter = createTRPCRouter({
switch (error.code) {
case PrismaError.ForeignConstraintViolation:
throw new Error(
'Cannot delete dashboard with associated reports',
'Cannot delete dashboard with associated reports'
);
default:
throw new Error('Unknown error deleting dashboard');

View File

@@ -12,7 +12,7 @@ export const emailRouter = createTRPCRouter({
email: z.string().email(),
category: z.string(),
token: z.string(),
}),
})
)
.mutation(async ({ input }) => {
const { email, category, token } = input;
@@ -41,7 +41,7 @@ export const emailRouter = createTRPCRouter({
}),
getPreferences: protectedProcedure.query(async ({ ctx }) => {
if (!ctx.session.userId || !ctx.session.user?.email) {
if (!(ctx.session.userId && ctx.session.user?.email)) {
throw new Error('User not authenticated');
}
@@ -72,10 +72,10 @@ export const emailRouter = createTRPCRouter({
.input(
z.object({
categories: z.record(z.string(), z.boolean()),
}),
})
)
.mutation(async ({ input, ctx }) => {
if (!ctx.session.userId || !ctx.session.user?.email) {
if (!(ctx.session.userId && ctx.session.user?.email)) {
throw new Error('User not authenticated');
}

View File

@@ -1,11 +1,4 @@
import { TRPCError } from '@trpc/server';
import sqlstring from 'sqlstring';
import { z } from 'zod';
import {
type IServiceProfile,
type IServiceSession,
TABLE_NAMES,
chQuery,
convertClickhouseDateToJs,
db,
@@ -15,16 +8,21 @@ import {
getEventList,
getEventMetasCached,
getSettingsForProject,
type IServiceProfile,
type IServiceSession,
pagesService,
sessionService,
TABLE_NAMES,
} from '@openpanel/db';
import {
zChartEventFilter,
zRange,
zTimeInterval,
} from '@openpanel/validation';
import { TRPCError } from '@trpc/server';
import { clone } from 'ramda';
import sqlstring from 'sqlstring';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import { TRPCAccessError } from '../errors';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
@@ -38,7 +36,7 @@ export const eventRouter = createTRPCRouter({
icon: z.string().optional(),
color: z.string().optional(),
conversion: z.boolean().optional(),
}),
})
)
.mutation(
async ({ input: { projectId, name, icon, color, conversion } }) => {
@@ -53,7 +51,7 @@ export const eventRouter = createTRPCRouter({
create: { projectId, name, icon, color, conversion },
update: { icon, color, conversion },
});
},
}
),
byId: protectedProcedure
@@ -62,7 +60,7 @@ export const eventRouter = createTRPCRouter({
id: z.string(),
projectId: z.string(),
createdAt: z.date().optional(),
}),
})
)
.query(async ({ input: { id, projectId, createdAt } }) => {
const res = await eventService.getById({
@@ -87,7 +85,7 @@ export const eventRouter = createTRPCRouter({
id: z.string(),
projectId: z.string(),
createdAt: z.date().optional(),
}),
})
)
.query(async ({ input: { id, projectId, createdAt } }) => {
const res = await eventService.getById({
@@ -129,7 +127,7 @@ export const eventRouter = createTRPCRouter({
endDate: z.date().optional(),
events: z.array(z.string()).optional(),
columnVisibility: z.record(z.string(), z.boolean()).optional(),
}),
})
)
.query(async ({ input: { columnVisibility, ...input } }) => {
const items = await getEventList({
@@ -191,7 +189,7 @@ export const eventRouter = createTRPCRouter({
endDate: z.date().optional(),
events: z.array(z.string()).optional(),
columnVisibility: z.record(z.string(), z.boolean()).optional(),
}),
})
)
.query(async ({ input: { columnVisibility, ...input } }) => {
const conversions = await getConversionEventNames(input.projectId);
@@ -266,7 +264,7 @@ export const eventRouter = createTRPCRouter({
projectId: z.string(),
cursor: z.number().optional(),
limit: z.number().default(8),
}),
})
)
.query(async ({ input: { projectId, cursor, limit }, ctx }) => {
if (ctx.session.userId) {
@@ -298,12 +296,12 @@ export const eventRouter = createTRPCRouter({
path: string;
created_at: string;
}>(
`SELECT * FROM ${TABLE_NAMES.events_bots} WHERE project_id = ${sqlstring.escape(projectId)} ORDER BY created_at DESC LIMIT ${limit} OFFSET ${(cursor ?? 0) * limit}`,
`SELECT * FROM ${TABLE_NAMES.events_bots} WHERE project_id = ${sqlstring.escape(projectId)} ORDER BY created_at DESC LIMIT ${limit} OFFSET ${(cursor ?? 0) * limit}`
),
chQuery<{
count: number;
}>(
`SELECT count(*) as count FROM ${TABLE_NAMES.events_bots} WHERE project_id = ${sqlstring.escape(projectId)}`,
`SELECT count(*) as count FROM ${TABLE_NAMES.events_bots} WHERE project_id = ${sqlstring.escape(projectId)}`
),
]);
@@ -325,7 +323,7 @@ export const eventRouter = createTRPCRouter({
search: z.string().optional(),
range: zRange,
interval: zTimeInterval,
}),
})
)
.query(async ({ input }) => {
const { timezone } = await getSettingsForProject(input.projectId);
@@ -346,7 +344,7 @@ export const eventRouter = createTRPCRouter({
projectId: z.string(),
range: zRange,
interval: zTimeInterval,
}),
})
)
.query(async ({ input }) => {
const { timezone } = await getSettingsForProject(input.projectId);
@@ -366,7 +364,7 @@ export const eventRouter = createTRPCRouter({
projectId: z.string(),
range: zRange,
interval: zTimeInterval,
}),
})
)
.query(async ({ input }) => {
const { timezone } = await getSettingsForProject(input.projectId);
@@ -378,8 +376,7 @@ export const eventRouter = createTRPCRouter({
const prevEnd = new Date(startMs - 1);
const prevStart = new Date(prevEnd.getTime() - duration);
const fmt = (d: Date) =>
d.toISOString().slice(0, 19).replace('T', ' ');
const fmt = (d: Date) => d.toISOString().slice(0, 19).replace('T', ' ');
return pagesService.getTopPages({
projectId: input.projectId,
@@ -397,7 +394,7 @@ export const eventRouter = createTRPCRouter({
interval: zTimeInterval,
origin: z.string(),
path: z.string(),
}),
})
)
.query(async ({ input }) => {
const { timezone } = await getSettingsForProject(input.projectId);
@@ -417,15 +414,17 @@ export const eventRouter = createTRPCRouter({
.input(
z.object({
projectId: z.string(),
}),
})
)
.query(async ({ input }) => {
const res = await chQuery<{ origin: string }>(
`SELECT DISTINCT origin, count(id) as count FROM ${TABLE_NAMES.events} WHERE project_id = ${sqlstring.escape(
input.projectId,
)} AND origin IS NOT NULL AND origin != '' AND toDate(created_at) > now() - INTERVAL 30 DAY GROUP BY origin ORDER BY count DESC LIMIT 3`,
input.projectId
)} AND origin IS NOT NULL AND origin != '' AND toDate(created_at) > now() - INTERVAL 30 DAY GROUP BY origin ORDER BY count DESC LIMIT 3`
);
return res.filter((item) => item.origin && !item.origin.includes('localhost:'));
return res.filter(
(item) => item.origin && !item.origin.includes('localhost:')
);
}),
});

View File

@@ -55,11 +55,9 @@ export const groupRouter = createTRPCRouter({
return getGroupById(id, projectId);
}),
create: protectedProcedure
.input(zCreateGroup)
.mutation(({ input }) => {
return createGroup(input);
}),
create: protectedProcedure.input(zCreateGroup).mutation(({ input }) => {
return createGroup(input);
}),
update: protectedProcedure
.input(zUpdateGroup)

View File

@@ -1,9 +1,7 @@
import { z } from 'zod';
import { db } from '@openpanel/db';
import { importQueue } from '@openpanel/queue';
import { zCreateImport } from '@openpanel/validation';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import {
TRPCAccessError,
@@ -69,7 +67,7 @@ export const importRouter = createTRPCRouter({
if (!access || (typeof access !== 'boolean' && access.level === 'read')) {
throw TRPCAccessError(
'You do not have permission to create imports for this project',
'You do not have permission to create imports for this project'
);
}
@@ -85,13 +83,13 @@ export const importRouter = createTRPCRouter({
if (!organization) {
throw TRPCNotFoundError(
'Could not start import, organization not found',
'Could not start import, organization not found'
);
}
if (!organization.isActive) {
throw TRPCBadRequestError(
'You cannot start an import without an active subscription!',
'You cannot start an import without an active subscription!'
);
}
@@ -140,7 +138,7 @@ export const importRouter = createTRPCRouter({
if (!access || (typeof access !== 'boolean' && access.level === 'read')) {
throw TRPCAccessError(
'You do not have permission to delete imports for this project',
'You do not have permission to delete imports for this project'
);
}
@@ -174,7 +172,7 @@ export const importRouter = createTRPCRouter({
if (!access || (typeof access !== 'boolean' && access.level === 'read')) {
throw TRPCAccessError(
'You do not have permission to retry imports for this project',
'You do not have permission to retry imports for this project'
);
}

View File

@@ -10,7 +10,7 @@ export const insightRouter = createTRPCRouter({
z.object({
projectId: z.string(),
limit: z.number().min(1).max(100).optional().default(50),
}),
})
)
.query(async ({ input: { projectId, limit }, ctx }) => {
const access = await getProjectAccess({
@@ -71,7 +71,7 @@ export const insightRouter = createTRPCRouter({
z.object({
projectId: z.string(),
limit: z.number().min(1).max(500).optional().default(200),
}),
})
)
.query(async ({ input: { projectId, limit }, ctx }) => {
const access = await getProjectAccess({

View File

@@ -1,18 +1,16 @@
import { z } from 'zod';
import { BASE_INTEGRATIONS, db } from '@openpanel/db';
import { getSlackInstallUrl } from '@openpanel/integrations/src/slack';
import { validate as validateJavaScriptTemplate } from '@openpanel/js-runtime';
import {
type ISlackConfig,
zCreateDiscordIntegration,
zCreateSlackIntegration,
zCreateWebhookIntegration,
} from '@openpanel/validation';
import { z } from 'zod';
import { getOrganizationAccess } from '../access';
import { TRPCAccessError, TRPCBadRequestError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
import { validate as validateJavaScriptTemplate } from '@openpanel/js-runtime';
export const integrationRouter = createTRPCRouter({
get: protectedProcedure
@@ -101,11 +99,11 @@ export const integrationRouter = createTRPCRouter({
input.config.javascriptTemplate
) {
const validation = validateJavaScriptTemplate(
input.config.javascriptTemplate,
input.config.javascriptTemplate
);
if (!validation.valid) {
throw TRPCBadRequestError(
`Invalid JavaScript template: ${validation.error}`,
`Invalid JavaScript template: ${validation.error}`
);
}
}

View File

@@ -1,15 +1,13 @@
import { z } from 'zod';
import {
APP_NOTIFICATION_INTEGRATION_ID,
BASE_INTEGRATIONS,
EMAIL_NOTIFICATION_INTEGRATION_ID,
db,
EMAIL_NOTIFICATION_INTEGRATION_ID,
getNotificationRulesByProjectId,
isBaseIntegration,
} from '@openpanel/db';
import { zCreateNotificationRule } from '@openpanel/validation';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import { TRPCAccessError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
@@ -89,10 +87,10 @@ export const notificationRouter = createTRPCRouter({
name: input.name,
projectId: input.projectId,
sendToApp: !!input.integrations.find(
(id) => id === APP_NOTIFICATION_INTEGRATION_ID,
(id) => id === APP_NOTIFICATION_INTEGRATION_ID
),
sendToEmail: !!input.integrations.find(
(id) => id === EMAIL_NOTIFICATION_INTEGRATION_ID,
(id) => id === EMAIL_NOTIFICATION_INTEGRATION_ID
),
integrations: {
set: input.integrations
@@ -110,10 +108,10 @@ export const notificationRouter = createTRPCRouter({
name: input.name,
projectId: input.projectId,
sendToApp: !!input.integrations.find(
(id) => id === APP_NOTIFICATION_INTEGRATION_ID,
(id) => id === APP_NOTIFICATION_INTEGRATION_ID
),
sendToEmail: !!input.integrations.find(
(id) => id === EMAIL_NOTIFICATION_INTEGRATION_ID,
(id) => id === EMAIL_NOTIFICATION_INTEGRATION_ID
),
integrations: {
connect: input.integrations

View File

@@ -1,18 +1,16 @@
import crypto from 'node:crypto';
import type { z } from 'zod';
import { stripTrailingSlash } from '@openpanel/common';
import { db, getId, getOrganizationById, getUserById } from '@openpanel/db';
import type { IServiceUser, ProjectType } from '@openpanel/db';
import { zOnboardingProject } from '@openpanel/validation';
import { hashPassword } from '@openpanel/common/server';
import type { IServiceUser, ProjectType } from '@openpanel/db';
import { db, getId, getOrganizationById, getUserById } from '@openpanel/db';
import { zOnboardingProject } from '@openpanel/validation';
import { addDays } from 'date-fns';
import type { z } from 'zod';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
async function createOrGetOrganization(
input: z.infer<typeof zOnboardingProject>,
user: IServiceUser,
user: IServiceUser
) {
if (input.organizationId) {
return await getOrganizationById(input.organizationId);
@@ -75,9 +73,15 @@ export const onboardingRouter = createTRPCRouter({
.input(zOnboardingProject)
.mutation(async ({ input, ctx }) => {
const types: ProjectType[] = [];
if (input.website) types.push('website');
if (input.app) types.push('app');
if (input.backend) types.push('backend');
if (input.website) {
types.push('website');
}
if (input.app) {
types.push('app');
}
if (input.backend) {
types.push('backend');
}
const user = await getUserById(ctx.session.userId);
const organization = await createOrGetOrganization(input, user);

View File

@@ -1,5 +1,4 @@
import { z } from 'zod';
import { generateSecureId } from '@openpanel/common/server';
import {
connectUserToOrganization,
db,
@@ -9,11 +8,10 @@ import {
getOrganizationById,
getOrganizations,
} from '@openpanel/db';
import { zEditOrganization, zInviteUser } from '@openpanel/validation';
import { generateSecureId } from '@openpanel/common/server';
import { sendEmail } from '@openpanel/email';
import { zEditOrganization, zInviteUser } from '@openpanel/validation';
import { addDays } from 'date-fns';
import { z } from 'zod';
import { getOrganizationAccess } from '../access';
import { TRPCAccessError, TRPCBadRequestError } from '../errors';
import {
@@ -88,7 +86,7 @@ export const organizationRouter = createTRPCRouter({
if (alreadyMember && userExists) {
throw TRPCBadRequestError(
'User is already a member of the organization',
'User is already a member of the organization'
);
}
@@ -101,7 +99,7 @@ export const organizationRouter = createTRPCRouter({
if (alreadyInvited) {
throw TRPCBadRequestError(
'User is already invited to the organization',
'User is already invited to the organization'
);
}
@@ -153,7 +151,7 @@ export const organizationRouter = createTRPCRouter({
.input(
z.object({
inviteId: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const invite = await db.invite.findUniqueOrThrow({
@@ -184,7 +182,7 @@ export const organizationRouter = createTRPCRouter({
organizationId: z.string(),
userId: z.string(),
id: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const exists = await db.member.count({
@@ -230,7 +228,7 @@ export const organizationRouter = createTRPCRouter({
userId: z.string(),
organizationId: z.string(),
access: z.array(z.string()),
}),
})
)
.mutation(async ({ input, ctx }) => {
if (input.userId === ctx.session.userId) {
@@ -257,7 +255,7 @@ export const organizationRouter = createTRPCRouter({
data: input.access.map((projectId) => ({
userId: input.userId,
organizationId: input.organizationId,
projectId: projectId,
projectId,
level: 'read',
})),
}),
@@ -281,7 +279,7 @@ export const organizationRouter = createTRPCRouter({
rateLimitMiddleware({
max: 5,
windowMs: 30_000,
}),
})
)
.input(z.object({ inviteId: z.string().optional() }))
.query(async ({ input }) => {

View File

@@ -1,5 +1,4 @@
import {
TABLE_NAMES,
ch,
clix,
eventBuffer,
@@ -9,6 +8,7 @@ import {
getOrganizationSubscriptionChartEndDate,
getSettingsForProject,
overviewService,
TABLE_NAMES,
validateOverviewShareAccess,
zGetMapDataInput,
zGetMetricsInput,
@@ -70,7 +70,7 @@ const overviewProcedure = publicProcedure.use(
}
return next();
},
}
);
function getCurrentAndPrevious<
@@ -85,14 +85,14 @@ function getCurrentAndPrevious<
const previous = getChartPrevStartEndDate(current);
return async <R>(
fn: (input: T & { startDate: string; endDate: string }) => Promise<R>,
fn: (input: T & { startDate: string; endDate: string }) => Promise<R>
): Promise<{
current: R;
previous: R | null;
}> => {
const endDate = await getOrganizationSubscriptionChartEndDate(
input.projectId,
current.endDate,
current.endDate
);
if (endDate) {
current.endDate = endDate;
@@ -164,7 +164,7 @@ export const overviewRouter = createTRPCRouter({
.fill(
clix.exp('toStartOfMinute(now() - INTERVAL 30 MINUTE)'),
clix.exp('toStartOfMinute(now())'),
clix.exp('INTERVAL 1 MINUTE'),
clix.exp('INTERVAL 1 MINUTE')
);
// Get referrers per minute for the last 30 minutes
@@ -251,7 +251,7 @@ export const overviewRouter = createTRPCRouter({
endDate: z.string().nullish(),
range: zRange,
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -259,7 +259,7 @@ export const overviewRouter = createTRPCRouter({
const { current, previous } = await getCurrentAndPrevious(
{ ...input, timezone },
true,
timezone,
timezone
)(overviewService.getMetrics.bind(overviewService));
return {
metrics: {
@@ -298,7 +298,7 @@ export const overviewRouter = createTRPCRouter({
range: zRange,
mode: z.enum(['page', 'entry', 'exit', 'bot']),
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -306,7 +306,7 @@ export const overviewRouter = createTRPCRouter({
const { current } = await getCurrentAndPrevious(
{ ...input },
false,
timezone,
timezone
)(async (input) => {
if (input.mode === 'page') {
return overviewService.getTopPages({ ...input, timezone });
@@ -333,7 +333,7 @@ export const overviewRouter = createTRPCRouter({
endDate: z.string().nullish(),
range: zRange,
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -343,7 +343,7 @@ export const overviewRouter = createTRPCRouter({
const { current } = await getCurrentAndPrevious(
{ ...input, timezone },
false,
timezone,
timezone
)(overviewService.getTopGeneric.bind(overviewService));
return current;
@@ -358,7 +358,7 @@ export const overviewRouter = createTRPCRouter({
endDate: z.string().nullish(),
range: zRange,
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -366,7 +366,7 @@ export const overviewRouter = createTRPCRouter({
const { current } = await getCurrentAndPrevious(
{ ...input, timezone },
false,
timezone,
timezone
)(overviewService.getTopGenericSeries.bind(overviewService));
return current;
@@ -380,7 +380,7 @@ export const overviewRouter = createTRPCRouter({
range: zRange,
steps: z.number().min(2).max(10).default(5).optional(),
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -388,7 +388,7 @@ export const overviewRouter = createTRPCRouter({
const { current } = await getCurrentAndPrevious(
{ ...input, timezone },
false,
timezone,
timezone
)(async (input) => {
return overviewService.getUserJourney({
...input,
@@ -407,7 +407,7 @@ export const overviewRouter = createTRPCRouter({
endDate: z.string().nullish(),
range: zRange,
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -415,7 +415,7 @@ export const overviewRouter = createTRPCRouter({
const { current } = await getCurrentAndPrevious(
{ ...input, timezone },
false,
timezone,
timezone
)(overviewService.getTopEvents.bind(overviewService));
return current;
@@ -426,7 +426,7 @@ export const overviewRouter = createTRPCRouter({
z.object({
projectId: z.string(),
shareId: z.string().optional(),
}),
})
)
.query(async ({ input }) => {
return getConversionEventNames(input.projectId);
@@ -439,7 +439,7 @@ export const overviewRouter = createTRPCRouter({
endDate: z.string().nullish(),
range: zRange,
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -447,7 +447,7 @@ export const overviewRouter = createTRPCRouter({
const { current } = await getCurrentAndPrevious(
{ ...input, timezone },
false,
timezone,
timezone
)(overviewService.getTopLinkOut.bind(overviewService));
return current;
@@ -460,7 +460,7 @@ export const overviewRouter = createTRPCRouter({
endDate: z.string().nullish(),
range: zRange,
shareId: z.string().optional(),
}),
})
)
.use(cacher)
.query(async ({ input }) => {
@@ -468,7 +468,7 @@ export const overviewRouter = createTRPCRouter({
const { current } = await getCurrentAndPrevious(
{ ...input, timezone },
false,
timezone,
timezone
)(overviewService.getMapData.bind(overviewService));
return current;

View File

@@ -1,9 +1,4 @@
import { flatten, map, pipe, prop, sort, uniq } from 'ramda';
import sqlstring from 'sqlstring';
import { z } from 'zod';
import {
TABLE_NAMES,
chQuery,
createSqlBuilder,
getProfileById,
@@ -11,8 +6,11 @@ import {
getProfileListCount,
getProfileMetrics,
getProfiles,
TABLE_NAMES,
} from '@openpanel/db';
import { flatten, map, pipe, prop, sort, uniq } from 'ramda';
import sqlstring from 'sqlstring';
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure } from '../trpc';
export const profileRouter = createTRPCRouter({
@@ -32,7 +30,7 @@ export const profileRouter = createTRPCRouter({
.input(z.object({ profileId: z.string(), projectId: z.string() }))
.query(async ({ input: { profileId, projectId } }) => {
return chQuery<{ count: number; date: string }>(
`SELECT count(*) as count, toStartOfDay(created_at) as date FROM ${TABLE_NAMES.events} WHERE project_id = ${sqlstring.escape(projectId)} and profile_id = ${sqlstring.escape(profileId)} GROUP BY date ORDER BY date DESC`,
`SELECT count(*) as count, toStartOfDay(created_at) as date FROM ${TABLE_NAMES.events} WHERE project_id = ${sqlstring.escape(projectId)} and profile_id = ${sqlstring.escape(profileId)} GROUP BY date ORDER BY date DESC`
);
}),
@@ -40,7 +38,7 @@ export const profileRouter = createTRPCRouter({
.input(z.object({ profileId: z.string(), projectId: z.string() }))
.query(async ({ input: { profileId, projectId } }) => {
return chQuery<{ count: number; name: string }>(
`SELECT count(*) as count, name FROM ${TABLE_NAMES.events} WHERE name NOT IN ('screen_view', 'session_start', 'session_end') AND project_id = ${sqlstring.escape(projectId)} and profile_id = ${sqlstring.escape(profileId)} GROUP BY name ORDER BY count DESC`,
`SELECT count(*) as count, name FROM ${TABLE_NAMES.events} WHERE name NOT IN ('screen_view', 'session_start', 'session_end') AND project_id = ${sqlstring.escape(projectId)} and profile_id = ${sqlstring.escape(profileId)} GROUP BY name ORDER BY count DESC`
);
}),
@@ -48,7 +46,7 @@ export const profileRouter = createTRPCRouter({
.input(z.object({ profileId: z.string(), projectId: z.string() }))
.query(async ({ input: { profileId, projectId } }) => {
return chQuery<{ count: number; path: string }>(
`SELECT count(*) as count, path FROM ${TABLE_NAMES.events} WHERE name = 'screen_view' AND project_id = ${sqlstring.escape(projectId)} and profile_id = ${sqlstring.escape(profileId)} GROUP BY path ORDER BY count DESC LIMIT 10`,
`SELECT count(*) as count, path FROM ${TABLE_NAMES.events} WHERE name = 'screen_view' AND project_id = ${sqlstring.escape(projectId)} and profile_id = ${sqlstring.escape(profileId)} GROUP BY path ORDER BY count DESC LIMIT 10`
);
}),
@@ -56,7 +54,7 @@ export const profileRouter = createTRPCRouter({
.input(z.object({ projectId: z.string() }))
.query(async ({ input: { projectId } }) => {
const events = await chQuery<{ keys: string[] }>(
`SELECT distinct mapKeys(properties) as keys from ${TABLE_NAMES.profiles} where project_id = ${sqlstring.escape(projectId)};`,
`SELECT distinct mapKeys(properties) as keys from ${TABLE_NAMES.profiles} where project_id = ${sqlstring.escape(projectId)};`
);
const properties = events
@@ -69,7 +67,7 @@ export const profileRouter = createTRPCRouter({
return pipe(
sort<string>((a, b) => a.length - b.length),
uniq,
uniq
)(properties);
}),
@@ -81,7 +79,7 @@ export const profileRouter = createTRPCRouter({
take: z.number().default(50),
search: z.string().optional(),
isExternal: z.boolean().optional(),
}),
})
)
.query(async ({ input }) => {
const [data, count] = await Promise.all([
@@ -103,7 +101,7 @@ export const profileRouter = createTRPCRouter({
projectId: z.string(),
cursor: z.number().optional(),
take: z.number().default(50),
}),
})
)
.query(async ({ input: { projectId, cursor, take } }) => {
const res = await chQuery<{ profile_id: string; count: number }>(
@@ -115,11 +113,11 @@ export const profileRouter = createTRPCRouter({
AND project_id = ${sqlstring.escape(projectId)}
GROUP BY profile_id
ORDER BY count() DESC
LIMIT ${take} ${cursor ? `OFFSET ${cursor * take}` : ''}`,
LIMIT ${take} ${cursor ? `OFFSET ${cursor * take}` : ''}`
);
const profiles = await getProfiles(
res.map((r) => r.profile_id),
projectId,
projectId
);
const data = res
@@ -146,7 +144,7 @@ export const profileRouter = createTRPCRouter({
z.object({
property: z.string(),
projectId: z.string(),
}),
})
)
.query(async ({ input: { property, projectId } }) => {
const { sb, getSql } = createSqlBuilder();
@@ -154,7 +152,7 @@ export const profileRouter = createTRPCRouter({
sb.where.project_id = `project_id = ${sqlstring.escape(projectId)}`;
if (property.startsWith('properties.')) {
sb.select.values = `distinct arrayMap(x -> trim(x), mapValues(mapExtractKeyLike(properties, ${sqlstring.escape(
property.replace(/^properties\./, '').replace('.*.', '.%.'),
property.replace(/^properties\./, '').replace('.*.', '.%.')
)}))) as values`;
} else {
sb.select.values = `${property} as values`;
@@ -166,7 +164,7 @@ export const profileRouter = createTRPCRouter({
(data: typeof profiles) => map(prop('values'), data),
flatten,
uniq,
sort((a, b) => a.length - b.length),
sort((a, b) => a.length - b.length)
)(profiles);
return {

View File

@@ -1,19 +1,18 @@
import { z } from 'zod';
import crypto from 'node:crypto';
import { stripTrailingSlash } from '@openpanel/common';
import { hashPassword } from '@openpanel/common/server';
import {
type Prisma,
db,
getClientByIdCached,
getId,
getProjectByIdCached,
getProjectWithClients,
getProjectsByOrganizationId,
getProjectWithClients,
type Prisma,
} from '@openpanel/db';
import { zOnboardingProject, zProject } from '@openpanel/validation';
import { addHours } from 'date-fns';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import { TRPCAccessError, TRPCBadRequestError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
@@ -23,7 +22,7 @@ export const projectRouter = createTRPCRouter({
.input(
z.object({
projectId: z.string(),
}),
})
)
.query(async ({ input: { projectId }, ctx }) => {
const access = await getProjectAccess({
@@ -42,10 +41,12 @@ export const projectRouter = createTRPCRouter({
.input(
z.object({
organizationId: z.string().nullable(),
}),
})
)
.query(async ({ input: { organizationId } }) => {
if (organizationId === null) return [];
if (organizationId === null) {
return [];
}
return getProjectsByOrganizationId(organizationId);
}),
@@ -151,7 +152,7 @@ export const projectRouter = createTRPCRouter({
.input(
z.object({
projectId: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const access = await getProjectAccess({
@@ -178,7 +179,7 @@ export const projectRouter = createTRPCRouter({
.input(
z.object({
projectId: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const access = await getProjectAccess({

View File

@@ -112,7 +112,7 @@ interface CoordinatePoint {
long: number;
lat: number;
count: number;
};
}
function mergeByRadius(
points: CoordinatePoint[],

View File

@@ -1,8 +1,6 @@
import { z } from 'zod';
import { db, getChartStartEndDate, getSettingsForProject } from '@openpanel/db';
import { zCreateReference, zRange } from '@openpanel/validation';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import { TRPCAccessError } from '../errors';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
@@ -13,7 +11,7 @@ export const referenceRouter = createTRPCRouter({
z.object({
projectId: z.string(),
cursor: z.number().optional(),
}),
})
)
.query(async ({ input: { projectId, cursor }, ctx }) => {
const access = await getProjectAccess({
@@ -46,7 +44,7 @@ export const referenceRouter = createTRPCRouter({
date: new Date(datetime),
},
});
},
}
),
update: protectedProcedure
.input(
@@ -55,7 +53,7 @@ export const referenceRouter = createTRPCRouter({
title: z.string(),
description: z.string().nullish(),
datetime: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
const existing = await db.reference.findUniqueOrThrow({
@@ -111,7 +109,7 @@ export const referenceRouter = createTRPCRouter({
startDate: z.string().nullish(),
endDate: z.string().nullish(),
range: zRange,
}),
})
)
.query(async ({ input: { projectId, ...input } }) => {
const { timezone } = await getSettingsForProject(projectId);

View File

@@ -1,8 +1,6 @@
import { z } from 'zod';
import { db, getReportById, getReportsByDashboardId } from '@openpanel/db';
import { zReport } from '@openpanel/validation';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import { TRPCAccessError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
@@ -13,7 +11,7 @@ export const reportRouter = createTRPCRouter({
z.object({
dashboardId: z.string(),
projectId: z.string(),
}),
})
)
.query(async ({ input: { dashboardId, projectId }, ctx }) => {
return getReportsByDashboardId(dashboardId);
@@ -23,7 +21,7 @@ export const reportRouter = createTRPCRouter({
z.object({
report: zReport.omit({ projectId: true }),
dashboardId: z.string(),
}),
})
)
.mutation(async ({ input: { report, dashboardId }, ctx }) => {
const dashboard = await db.dashboard.findUniqueOrThrow({
@@ -65,7 +63,7 @@ export const reportRouter = createTRPCRouter({
z.object({
reportId: z.string(),
report: zReport.omit({ projectId: true }),
}),
})
)
.mutation(async ({ input: { report, reportId }, ctx }) => {
const dbReport = await db.report.findUniqueOrThrow({
@@ -107,7 +105,7 @@ export const reportRouter = createTRPCRouter({
.input(
z.object({
reportId: z.string(),
}),
})
)
.mutation(async ({ input: { reportId }, ctx }) => {
const report = await db.report.findUniqueOrThrow({
@@ -135,7 +133,7 @@ export const reportRouter = createTRPCRouter({
.input(
z.object({
reportId: z.string(),
}),
})
)
.mutation(async ({ input: { reportId }, ctx }) => {
const report = await db.report.findUniqueOrThrow({
@@ -176,7 +174,7 @@ export const reportRouter = createTRPCRouter({
.input(
z.object({
reportId: z.string(),
}),
})
)
.query(async ({ input: { reportId }, ctx }) => {
return getReportById(reportId);
@@ -195,7 +193,7 @@ export const reportRouter = createTRPCRouter({
maxW: z.number().optional(),
maxH: z.number().optional(),
}),
}),
})
)
.mutation(async ({ input: { reportId, layout }, ctx }) => {
const report = await db.report.findUniqueOrThrow({
@@ -216,10 +214,10 @@ export const reportRouter = createTRPCRouter({
// Upsert the layout (create if doesn't exist, update if it does)
return db.reportLayout.upsert({
where: {
reportId: reportId,
reportId,
},
create: {
reportId: reportId,
reportId,
x: layout.x,
y: layout.y,
w: layout.w,
@@ -246,12 +244,12 @@ export const reportRouter = createTRPCRouter({
z.object({
dashboardId: z.string(),
projectId: z.string(),
}),
})
)
.query(async ({ input: { dashboardId, projectId }, ctx }) => {
const access = await getProjectAccess({
userId: ctx.session.userId,
projectId: projectId,
projectId,
});
if (!access) {
@@ -261,7 +259,7 @@ export const reportRouter = createTRPCRouter({
return db.reportLayout.findMany({
where: {
report: {
dashboardId: dashboardId,
dashboardId,
},
},
include: {
@@ -274,12 +272,12 @@ export const reportRouter = createTRPCRouter({
z.object({
dashboardId: z.string(),
projectId: z.string(),
}),
})
)
.mutation(async ({ input: { dashboardId, projectId }, ctx }) => {
const access = await getProjectAccess({
userId: ctx.session.userId,
projectId: projectId,
projectId,
});
if (!access) {
@@ -290,7 +288,7 @@ export const reportRouter = createTRPCRouter({
return db.reportLayout.deleteMany({
where: {
report: {
dashboardId: dashboardId,
dashboardId,
},
},
});

View File

@@ -1,11 +1,8 @@
import ShortUniqueId from 'short-unique-id';
import { hashPassword } from '@openpanel/auth';
import {
db,
getReportById,
getReportsByDashboardId,
getShareDashboardById,
getShareReportById,
transformReport,
} from '@openpanel/db';
import {
@@ -13,8 +10,7 @@ import {
zShareOverview,
zShareReport,
} from '@openpanel/validation';
import { hashPassword } from '@openpanel/auth';
import ShortUniqueId from 'short-unique-id';
import { z } from 'zod';
import { getProjectAccess } from '../access';
import { TRPCAccessError, TRPCNotFoundError } from '../errors';
@@ -32,8 +28,8 @@ export const shareRouter = createTRPCRouter({
.or(
z.object({
shareId: z.string(),
}),
),
})
)
)
.query(async ({ input, ctx }) => {
const share = await db.shareOverview.findUnique({
@@ -108,8 +104,8 @@ export const shareRouter = createTRPCRouter({
.or(
z.object({
shareId: z.string(),
}),
),
})
)
)
.query(async ({ input, ctx }) => {
const share = await db.shareDashboard.findUnique({
@@ -192,12 +188,12 @@ export const shareRouter = createTRPCRouter({
.input(
z.object({
shareId: z.string(),
}),
})
)
.query(async ({ input, ctx }) => {
const share = await getShareDashboardById(input.shareId);
if (!share || !share.public) {
if (!(share && share.public)) {
throw TRPCNotFoundError('Dashboard share not found');
}
@@ -220,8 +216,8 @@ export const shareRouter = createTRPCRouter({
.or(
z.object({
shareId: z.string(),
}),
),
})
)
)
.query(async ({ input, ctx }) => {
const share = await db.shareReport.findUnique({

View File

@@ -12,9 +12,8 @@ import {
getProducts,
reactivateSubscription,
} from '@openpanel/payments';
import { zCheckout } from '@openpanel/validation';
import { getCache } from '@openpanel/redis';
import { zCheckout } from '@openpanel/validation';
import { subDays } from 'date-fns';
import { z } from 'zod';
import { TRPCBadRequestError } from '../errors';
@@ -68,7 +67,7 @@ export const subscriptionRouter = createTRPCRouter({
await changeSubscription(
organization.subscriptionId,
input.productId,
input.productId
);
return null;
}
@@ -117,7 +116,7 @@ export const subscriptionRouter = createTRPCRouter({
.input(
z.object({
organizationId: z.string(),
}),
})
)
.query(async ({ input }) => {
const organization = await db.organization.findUniqueOrThrow({

View File

@@ -1,8 +1,6 @@
import { z } from 'zod';
import { db } from '@openpanel/db';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure } from '../trpc';
export const userRouter = createTRPCRouter({
update: protectedProcedure
@@ -10,7 +8,7 @@ export const userRouter = createTRPCRouter({
z.object({
firstName: z.string(),
lastName: z.string(),
}),
})
)
.mutation(async ({ input, ctx }) => {
return db.user.update({
@@ -28,7 +26,7 @@ export const userRouter = createTRPCRouter({
z.object({
sameSite: z.enum(['lax', 'strict', 'none']),
domain: z.string(),
}),
})
)
.mutation(async ({ ctx, input }) => {
ctx.setCookie('debugCookie', new Date().toISOString(), {
@@ -44,7 +42,7 @@ export const userRouter = createTRPCRouter({
z.object({
sameSite: z.enum(['lax', 'strict', 'none']),
domain: z.string(),
}),
})
)
.query(async ({ ctx, input }) => {
ctx.setCookie('debugCookie', new Date().toISOString(), {

View File

@@ -1,22 +1,15 @@
import ShortUniqueId from 'short-unique-id';
import { z } from 'zod';
import {
TABLE_NAMES,
ch,
clix,
db,
eventBuffer,
getSettingsForProject,
TABLE_NAMES,
} from '@openpanel/db';
import { getCache } from '@openpanel/redis';
import {
zCounterWidgetOptions,
zRealtimeWidgetOptions,
zWidgetOptions,
zWidgetType,
} from '@openpanel/validation';
import { zWidgetOptions, zWidgetType } from '@openpanel/validation';
import ShortUniqueId from 'short-unique-id';
import { z } from 'zod';
import { TRPCNotFoundError } from '../errors';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
@@ -28,7 +21,7 @@ async function findWidgetByType(projectId: string, type: string) {
where: { projectId },
});
return widgets.find(
(w) => (w.options as z.infer<typeof zWidgetOptions>)?.type === type,
(w) => (w.options as z.infer<typeof zWidgetOptions>)?.type === type
);
}
@@ -54,7 +47,7 @@ export const widgetRouter = createTRPCRouter({
organizationId: z.string(),
type: zWidgetType,
enabled: z.boolean(),
}),
})
)
.mutation(async ({ input }) => {
const existing = await findWidgetByType(input.projectId, input.type);
@@ -95,12 +88,12 @@ export const widgetRouter = createTRPCRouter({
projectId: z.string(),
organizationId: z.string(),
options: zWidgetOptions,
}),
})
)
.mutation(async ({ input }) => {
const existing = await findWidgetByType(
input.projectId,
input.options.type,
input.options.type
);
if (existing) {
@@ -131,7 +124,7 @@ export const widgetRouter = createTRPCRouter({
},
});
if (!widget || !widget.public) {
if (!(widget && widget.public)) {
throw TRPCNotFoundError('Widget not found');
}
@@ -154,7 +147,7 @@ export const widgetRouter = createTRPCRouter({
},
});
if (!widget || !widget.public) {
if (!(widget && widget.public)) {
throw TRPCNotFoundError('Widget not found');
}
@@ -179,7 +172,7 @@ export const widgetRouter = createTRPCRouter({
const result = await uniqueVisitorsQuery.execute();
return result[0]?.count || 0;
},
}
);
return {
@@ -206,7 +199,7 @@ export const widgetRouter = createTRPCRouter({
},
});
if (!widget || !widget.public) {
if (!(widget && widget.public)) {
throw TRPCNotFoundError('Widget not found');
}
@@ -245,7 +238,7 @@ export const widgetRouter = createTRPCRouter({
.fill(
clix.exp('toStartOfMinute(now() - INTERVAL 30 MINUTE)'),
clix.exp('toStartOfMinute(now())'),
clix.exp('INTERVAL 1 MINUTE'),
clix.exp('INTERVAL 1 MINUTE')
);
// Conditionally fetch countries

View File

@@ -1,17 +1,16 @@
import { TRPCError, initTRPC } from '@trpc/server';
import type { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';
import { has } from 'ramda';
import superjson from 'superjson';
import { ZodError } from 'zod';
import { COOKIE_OPTIONS, type SessionValidationResult } from '@openpanel/auth';
import { runWithAlsSession } from '@openpanel/db';
import { getRedisCache } from '@openpanel/redis';
import type { ISetCookie } from '@openpanel/validation';
import { initTRPC, TRPCError } from '@trpc/server';
import type { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';
import {
createTrpcRedisLimiter,
defaultFingerPrint,
} from '@trpc-limiter/redis';
import { has } from 'ramda';
import superjson from 'superjson';
import { ZodError } from 'zod';
import { getOrganizationAccess, getProjectAccess } from './access';
import { TRPCAccessError } from './errors';
@@ -34,8 +33,7 @@ export const rateLimitMiddleware = ({
export async function createContext({ req, res }: CreateFastifyContextOptions) {
const cookies = (req as any).cookies as Record<string, string | undefined>;
const setCookie: ISetCookie = (key, value, options) => {
// @ts-ignore
res.setCookie(key, value, {
(res as any).setCookie(key, value, {
maxAge: options.maxAge,
signed: options.signed,
...COOKIE_OPTIONS,
@@ -44,7 +42,7 @@ export async function createContext({ req, res }: CreateFastifyContextOptions) {
if (process.env.NODE_ENV !== 'production') {
await new Promise((res) =>
setTimeout(() => res(1), Math.min(Math.random() * 500, 200)),
setTimeout(() => res(1), Math.min(Math.random() * 500, 200))
);
}
@@ -151,7 +149,7 @@ const loggerMiddleware = t.middleware(
});
}
return next();
},
}
);
const sessionScopeMiddleware = t.middleware(async ({ ctx, next }) => {
@@ -175,7 +173,7 @@ const middlewareMarker = 'middlewareMarker' as 'middlewareMarker' & {
};
export const cacheMiddleware = (
cbOrTtl: number | ((input: any, opts: { path: string }) => number),
cbOrTtl: number | ((input: any, opts: { path: string }) => number)
) =>
t.middleware(async ({ ctx, next, path, type, getRawInput, input }) => {
const ttl =
@@ -189,7 +187,7 @@ export const cacheMiddleware = (
}
let key = `trpc:${path}:`;
if (rawInput) {
key += JSON.stringify(rawInput).replace(/\"/g, "'");
key += JSON.stringify(rawInput).replace(/"/g, "'");
}
const cache = await getRedisCache().getJson(key);
if (cache && process.env.NODE_ENV === 'production') {
@@ -208,7 +206,7 @@ export const cacheMiddleware = (
key,
ttl,
// @ts-expect-error
result.data,
result.data
);
}
return result;

View File

@@ -1,3 +1,3 @@
import { getSharedVitestConfig } from '../../vitest.shared';
export default getSharedVitestConfig({ __dirname });
export default getSharedVitestConfig({ __dirname: import.meta.dirname });