aboutsummaryrefslogtreecommitdiff
path: root/src/lib/request.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/request.ts')
-rw-r--r--src/lib/request.ts145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/lib/request.ts b/src/lib/request.ts
new file mode 100644
index 0000000..42c4490
--- /dev/null
+++ b/src/lib/request.ts
@@ -0,0 +1,145 @@
+import { z } from 'zod';
+import { checkAuth } from '@/lib/auth';
+import { DEFAULT_PAGE_SIZE, FILTER_COLUMNS } from '@/lib/constants';
+import { getAllowedUnits, getMinimumUnit, maxDate, parseDateRange } from '@/lib/date';
+import { fetchWebsite } from '@/lib/load';
+import { filtersArrayToObject } from '@/lib/params';
+import { badRequest, unauthorized } from '@/lib/response';
+import type { QueryFilters } from '@/lib/types';
+import { getWebsiteSegment } from '@/queries/prisma';
+
+export async function parseRequest(
+ request: Request,
+ schema?: any,
+ options?: { skipAuth: boolean },
+): Promise<any> {
+ const url = new URL(request.url);
+ let query = Object.fromEntries(url.searchParams);
+ let body = await getJsonBody(request);
+ let error: () => undefined | undefined;
+ let auth = null;
+
+ if (schema) {
+ const isGet = request.method === 'GET';
+ const result = schema.safeParse(isGet ? query : body);
+
+ if (!result.success) {
+ error = () => badRequest(z.treeifyError(result.error));
+ } else if (isGet) {
+ query = result.data;
+ } else {
+ body = result.data;
+ }
+ }
+
+ if (!options?.skipAuth && !error) {
+ auth = await checkAuth(request);
+
+ if (!auth) {
+ error = () => unauthorized();
+ }
+ }
+
+ return { url, query, body, auth, error };
+}
+
+export async function getJsonBody(request: Request) {
+ try {
+ return await request.clone().json();
+ } catch {
+ return undefined;
+ }
+}
+
+export function getRequestDateRange(query: Record<string, string>) {
+ const { startAt, endAt, unit, timezone } = query;
+
+ const startDate = new Date(+startAt);
+ const endDate = new Date(+endAt);
+
+ return {
+ startDate,
+ endDate,
+ timezone,
+ unit: getAllowedUnits(startDate, endDate).includes(unit)
+ ? unit
+ : getMinimumUnit(startDate, endDate),
+ };
+}
+
+export function getRequestFilters(query: Record<string, any>) {
+ const result: Record<string, any> = {};
+
+ for (const key of Object.keys(FILTER_COLUMNS)) {
+ const value = query[key];
+ if (value !== undefined) {
+ result[key] = value;
+ }
+ }
+
+ return result;
+}
+
+export async function setWebsiteDate(websiteId: string, data: Record<string, any>) {
+ const website = await fetchWebsite(websiteId);
+
+ if (website?.resetAt) {
+ data.startDate = maxDate(data.startDate, new Date(website?.resetAt));
+ }
+
+ return data;
+}
+
+export async function getQueryFilters(
+ params: Record<string, any>,
+ websiteId?: string,
+): Promise<QueryFilters> {
+ const dateRange = getRequestDateRange(params);
+ const filters = getRequestFilters(params);
+
+ if (websiteId) {
+ await setWebsiteDate(websiteId, dateRange);
+
+ if (params.segment) {
+ const segmentParams = (await getWebsiteSegment(websiteId, params.segment))
+ ?.parameters as Record<string, any>;
+
+ Object.assign(filters, filtersArrayToObject(segmentParams.filters));
+ }
+
+ if (params.cohort) {
+ const cohortParams = (await getWebsiteSegment(websiteId, params.cohort))
+ ?.parameters as Record<string, any>;
+
+ const { startDate, endDate } = parseDateRange(cohortParams.dateRange);
+
+ const cohortFilters = cohortParams.filters.map(({ name, ...props }) => ({
+ ...props,
+ name: `cohort_${name}`,
+ }));
+
+ cohortFilters.push({
+ name: `cohort_${cohortParams.action.type}`,
+ operator: 'eq',
+ value: cohortParams.action.value,
+ });
+
+ Object.assign(filters, {
+ ...filtersArrayToObject(cohortFilters),
+ cohort_startDate: startDate,
+ cohort_endDate: endDate,
+ });
+ }
+ }
+
+ return {
+ ...dateRange,
+ ...filters,
+ page: params?.page,
+ pageSize: params?.pageSize ? params?.pageSize || DEFAULT_PAGE_SIZE : undefined,
+ orderBy: params?.orderBy,
+ sortDescending: params?.sortDescending,
+ search: params?.search,
+ compare: params?.compare,
+ };
+}