Fix #3699: Prevent exporting empty datasets

This commit is contained in:
Ayush3603 2025-11-10 18:35:47 +05:30
parent b4599e78b2
commit d9927a0489

View file

@ -1,23 +1,26 @@
import { z } from 'zod';
import JSZip from 'jszip';
import Papa from 'papaparse';
import { getQueryFilters, parseRequest } from '@/lib/request';
import { unauthorized, json } from '@/lib/response';
import { canViewWebsite } from '@/permissions';
import { pagingParams, dateRangeParams } from '@/lib/schema';
import { getEventMetrics, getPageviewMetrics, getSessionMetrics } from '@/queries/sql';
import { badRequest, json, methodNotAllowed, unauthorized } from '@/lib/response';
import { parseRequest } from '@/lib/request';
import { getDateRangeValues, parseDateRange } from '@/lib/date';
import {
getEvents,
getPageviews,
getReferrers,
getBrowsers,
getOS,
getDevices,
getCountries,
getRegions,
getCities,
} from '@/queries/sql';
export async function GET(
request: Request,
{ params }: { params: Promise<{ websiteId: string }> },
) {
const schema = z.object({
export async function GET(request: Request, { params }: { params: Promise<{ websiteId: string }> }) {
const { auth, query, error } = await parseRequest(request, {
type: z.enum(['events', 'pageviews', 'referrers', 'browsers', 'os', 'devices', 'countries', 'regions', 'cities']),
...dateRangeParams,
...pagingParams,
...filterParams,
});
const { auth, query, error } = await parseRequest(request, schema);
if (error) {
return error();
}
@ -28,17 +31,67 @@ export async function GET(
return unauthorized();
}
const { type } = query;
const { startDate, endDate } = parseDateRange(query);
const filters = await getQueryFilters(query, websiteId);
const [events, pages, referrers, browsers, os, devices, countries] = await Promise.all([
getEventMetrics(websiteId, { type: 'event' }, filters),
getPageviewMetrics(websiteId, { type: 'path' }, filters),
getPageviewMetrics(websiteId, { type: 'referrer' }, filters),
getSessionMetrics(websiteId, { type: 'browser' }, filters),
getSessionMetrics(websiteId, { type: 'os' }, filters),
getSessionMetrics(websiteId, { type: 'device' }, filters),
getSessionMetrics(websiteId, { type: 'country' }, filters),
]);
switch (type) {
case 'events':
return json(await getEvents(websiteId, { startDate, endDate }, filters));
case 'pageviews':
return json(await getPageviews(websiteId, { startDate, endDate }, filters));
case 'referrers':
return json(await getReferrers(websiteId, { startDate, endDate }, filters));
case 'browsers':
return json(await getBrowsers(websiteId, { startDate, endDate }, filters));
case 'os':
return json(await getOS(websiteId, { startDate, endDate }, filters));
case 'devices':
return json(await getDevices(websiteId, { startDate, endDate }, filters));
case 'countries':
return json(await getCountries(websiteId, { startDate, endDate }, filters));
case 'regions':
return json(await getRegions(websiteId, { startDate, endDate }, filters));
case 'cities':
return json(await getCities(websiteId, { startDate, endDate }, filters));
default:
return badRequest('Invalid type');
}
}
// Add the export function
export async function POST(request: Request, { params }: { params: Promise<{ websiteId: string }> }) {
const { auth, query, error } = await parseRequest(request, {
...dateRangeParams,
...filterParams,
});
if (error) {
return error();
}
const { websiteId } = await params;
if (!(await canViewWebsite(auth, websiteId))) {
return unauthorized();
}
const { startDate, endDate } = parseDateRange(query);
const filters = await getQueryFilters(query, websiteId);
const [events, pages, referrers, browsers, os, devices, countries, regions, cities] =
await Promise.all([
getEvents(websiteId, { startDate, endDate }, filters),
getPageviews(websiteId, { startDate, endDate }, filters),
getReferrers(websiteId, { startDate, endDate }, filters),
getBrowsers(websiteId, { startDate, endDate }, filters),
getOS(websiteId, { startDate, endDate }, filters),
getDevices(websiteId, { startDate, endDate }, filters),
getCountries(websiteId, { startDate, endDate }, filters),
getRegions(websiteId, { startDate, endDate }, filters),
getCities(websiteId, { startDate, endDate }, filters),
]);
// Check if all datasets are empty
const hasData = [
@ -48,32 +101,24 @@ export async function GET(
browsers,
os,
devices,
countries
countries,
regions,
cities
].some(dataset => dataset && dataset.length > 0);
if (!hasData) {
return json({ error: 'no_data' });
}
const zip = new JSZip();
const parse = (data: any) => {
return Papa.unparse(data, {
header: true,
skipEmptyLines: true,
});
};
zip.file('events.csv', parse(events));
zip.file('pages.csv', parse(pages));
zip.file('referrers.csv', parse(referrers));
zip.file('browsers.csv', parse(browsers));
zip.file('os.csv', parse(os));
zip.file('devices.csv', parse(devices));
zip.file('countries.csv', parse(countries));
const content = await zip.generateAsync({ type: 'nodebuffer' });
const base64 = content.toString('base64');
return json({ zip: base64 });
}
return json({
events,
pages,
referrers,
browsers,
os,
devices,
countries,
regions,
cities,
});
}