🙊 escape sql strings
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Widget } from '@/components/widget';
|
||||
import { escape } from 'sqlstring';
|
||||
|
||||
import { db, getEvents } from '@openpanel/db';
|
||||
|
||||
@@ -21,7 +22,7 @@ export default async function EventConversionsListServer({ projectId }: Props) {
|
||||
}
|
||||
|
||||
const events = await getEvents(
|
||||
`SELECT * FROM events WHERE project_id = '${projectId}' AND name IN (${conversions.map((c) => `'${c.name}'`).join(', ')}) ORDER BY created_at DESC LIMIT 20;`,
|
||||
`SELECT * FROM events WHERE project_id = ${escape(projectId)} AND name IN (${conversions.map((c) => escape(c.name)).join(', ')}) ORDER BY created_at DESC LIMIT 20;`,
|
||||
{
|
||||
profile: true,
|
||||
meta: true,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '@/components/ui/tooltip';
|
||||
import { Widget, WidgetBody, WidgetHead } from '@/components/widget';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { escape } from 'sqlstring';
|
||||
|
||||
import { chQuery } from '@openpanel/db';
|
||||
|
||||
@@ -20,7 +21,7 @@ export default async function ProfileLastSeenServer({ projectId }: Props) {
|
||||
// Days since last event from users
|
||||
// group by days
|
||||
const res = await chQuery<Row>(
|
||||
`SELECT age('days',created_at, now()) as days, count(distinct profile_id) as count FROM events where project_id = '${projectId}' group by days order by days ASC`
|
||||
`SELECT age('days',created_at, now()) as days, count(distinct profile_id) as count FROM events where project_id = ${escape(projectId)} group by days order by days ASC`
|
||||
);
|
||||
|
||||
const take = 18;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Widget, WidgetHead } from '@/components/widget';
|
||||
import { WidgetTable } from '@/components/widget-table';
|
||||
import { getProfileName } from '@/utils/getters';
|
||||
import Link from 'next/link';
|
||||
import { escape } from 'sqlstring';
|
||||
|
||||
import { chQuery, getProfiles } from '@openpanel/db';
|
||||
|
||||
@@ -19,7 +20,7 @@ export default async function ProfileTopServer({
|
||||
// Days since last event from users
|
||||
// group by days
|
||||
const res = await chQuery<{ profile_id: string; count: number }>(
|
||||
`SELECT profile_id, count(*) as count from events where profile_id != '' and project_id = '${projectId}' group by profile_id order by count() DESC LIMIT 10`
|
||||
`SELECT profile_id, count(*) as count from events where profile_id != '' and project_id = ${escape(projectId)} group by profile_id order by count() DESC LIMIT 10`
|
||||
);
|
||||
const profiles = await getProfiles({ ids: res.map((r) => r.profile_id) });
|
||||
const list = res.map((item) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { shortNumber } from '@/hooks/useNumerFormatter';
|
||||
import Link from 'next/link';
|
||||
import { escape } from 'sqlstring';
|
||||
|
||||
import type { IServiceProject } from '@openpanel/db';
|
||||
import { chQuery } from '@openpanel/db';
|
||||
@@ -13,19 +14,19 @@ export async function ProjectCard({
|
||||
}: IServiceProject) {
|
||||
const [chart, [data]] = await Promise.all([
|
||||
chQuery<{ value: number; date: string }>(
|
||||
`SELECT countDistinct(profile_id) as value, toStartOfDay(created_at) as date FROM events WHERE project_id = '${id}' AND name = 'session_start' AND created_at >= now() - interval '1 month' GROUP BY date ORDER BY date ASC`
|
||||
`SELECT countDistinct(profile_id) as value, toStartOfDay(created_at) as date FROM events WHERE project_id = ${escape(id)} AND name = 'session_start' AND created_at >= now() - interval '1 month' GROUP BY date ORDER BY date ASC`
|
||||
),
|
||||
chQuery<{ total: number; month: number; day: number }>(
|
||||
`
|
||||
SELECT
|
||||
(
|
||||
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = '${id}'
|
||||
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = ${escape(id)}
|
||||
) as total,
|
||||
(
|
||||
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = '${id}' AND created_at >= now() - interval '1 month'
|
||||
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = ${escape(id)} AND created_at >= now() - interval '1 month'
|
||||
) as month,
|
||||
(
|
||||
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = '${id}' AND created_at >= now() - interval '1 day'
|
||||
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = ${escape(id)} AND created_at >= now() - interval '1 day'
|
||||
) as day
|
||||
`
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import { round } from '@/utils/math';
|
||||
import { subDays } from 'date-fns';
|
||||
import * as mathjs from 'mathjs';
|
||||
import { repeat, reverse, sort } from 'ramda';
|
||||
import { escape } from 'sqlstring';
|
||||
|
||||
import { alphabetIds, NOT_SET_VALUE } from '@openpanel/constants';
|
||||
import {
|
||||
@@ -430,7 +431,7 @@ export async function getFunnelData({ projectId, ...payload }: IChartInput) {
|
||||
const funnels = payload.events.map((event) => {
|
||||
const { sb, getWhere } = createSqlBuilder();
|
||||
sb.where = getEventFiltersWhereClause(event.filters);
|
||||
sb.where.name = `name = '${event.name}'`;
|
||||
sb.where.name = `name = ${escape(event.name)}`;
|
||||
return getWhere().replace('WHERE ', '');
|
||||
});
|
||||
|
||||
@@ -438,7 +439,7 @@ export async function getFunnelData({ projectId, ...payload }: IChartInput) {
|
||||
session_id,
|
||||
windowFunnel(6048000000000000,'strict_increase')(toUnixTimestamp(created_at), ${funnels.join(', ')}) AS level
|
||||
FROM events
|
||||
WHERE (project_id = '${projectId}' AND created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')
|
||||
WHERE (project_id = ${escape(projectId)} AND created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')
|
||||
GROUP BY session_id`;
|
||||
|
||||
const sql = `SELECT level, count() AS count FROM (${innerSql}) GROUP BY level ORDER BY level DESC`;
|
||||
@@ -446,7 +447,7 @@ export async function getFunnelData({ projectId, ...payload }: IChartInput) {
|
||||
const [funnelRes, sessionRes] = await Promise.all([
|
||||
chQuery<{ level: number; count: number }>(sql),
|
||||
chQuery<{ count: number }>(
|
||||
`SELECT count(name) as count FROM events WHERE project_id = '${projectId}' AND name = 'session_start' AND (created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')`
|
||||
`SELECT count(name) as count FROM events WHERE project_id = ${escape(projectId)} AND name = 'session_start' AND (created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')`
|
||||
),
|
||||
]);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '@/trpc/api/trpc';
|
||||
import { average, max, min, round, sum } from '@/utils/math';
|
||||
import { flatten, map, pipe, prop, sort, uniq } from 'ramda';
|
||||
import { escape } from 'sqlstring';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { chQuery, createSqlBuilder } from '@openpanel/db';
|
||||
@@ -60,7 +61,7 @@ export const chartRouter = createTRPCRouter({
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.query(async ({ input: { projectId } }) => {
|
||||
const events = await chQuery<{ name: string }>(
|
||||
`SELECT DISTINCT name FROM events WHERE project_id = '${projectId}'`
|
||||
`SELECT DISTINCT name FROM events WHERE project_id = ${escape(projectId)}`
|
||||
);
|
||||
|
||||
return [
|
||||
@@ -76,8 +77,8 @@ export const chartRouter = createTRPCRouter({
|
||||
.query(async ({ input: { projectId, event } }) => {
|
||||
const events = await chQuery<{ keys: string[] }>(
|
||||
`SELECT distinct mapKeys(properties) as keys from events where ${
|
||||
event && event !== '*' ? `name = '${event}' AND ` : ''
|
||||
} project_id = '${projectId}';`
|
||||
event && event !== '*' ? `name = ${escape(event)} AND ` : ''
|
||||
} project_id = ${escape(projectId)};`
|
||||
);
|
||||
|
||||
const properties = events
|
||||
@@ -122,14 +123,14 @@ export const chartRouter = createTRPCRouter({
|
||||
)
|
||||
.query(async ({ input: { event, property, projectId } }) => {
|
||||
const { sb, getSql } = createSqlBuilder();
|
||||
sb.where.project_id = `project_id = '${projectId}'`;
|
||||
sb.where.project_id = `project_id = ${escape(projectId)}`;
|
||||
if (event !== '*') {
|
||||
sb.where.event = `name = '${event}'`;
|
||||
sb.where.event = `name = ${escape(event)}`;
|
||||
}
|
||||
if (property.startsWith('properties.')) {
|
||||
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, '${property
|
||||
.replace(/^properties\./, '')
|
||||
.replace('.*.', '.%.')}')) as values`;
|
||||
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, ${escape(
|
||||
property.replace(/^properties\./, '').replace('.*.', '.%.')
|
||||
)})) as values`;
|
||||
} else {
|
||||
sb.select.values = `distinct ${property} as values`;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
publicProcedure,
|
||||
} from '@/trpc/api/trpc';
|
||||
import { flatten, map, pipe, prop, sort, uniq } from 'ramda';
|
||||
import { escape } from 'sqlstring';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { chQuery, createSqlBuilder } from '@openpanel/db';
|
||||
@@ -13,7 +14,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 profiles where project_id = '${projectId}';`
|
||||
`SELECT distinct mapKeys(properties) as keys from profiles where project_id = ${escape(projectId)};`
|
||||
);
|
||||
|
||||
const properties = events
|
||||
@@ -40,11 +41,11 @@ export const profileRouter = createTRPCRouter({
|
||||
.query(async ({ input: { property, projectId } }) => {
|
||||
const { sb, getSql } = createSqlBuilder();
|
||||
sb.from = 'profiles';
|
||||
sb.where.project_id = `project_id = '${projectId}'`;
|
||||
sb.where.project_id = `project_id = ${escape(projectId)}`;
|
||||
if (property.startsWith('properties.')) {
|
||||
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, '${property
|
||||
.replace(/^properties\./, '')
|
||||
.replace('.*.', '.%.')}')) as values`;
|
||||
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, ${escape(
|
||||
property.replace(/^properties\./, '').replace('.*.', '.%.')
|
||||
)})) as values`;
|
||||
} else {
|
||||
sb.select.values = `${property} as values`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user