diff options
| author | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
| commit | 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch) | |
| tree | b9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/app/api/reports | |
| download | umami-main.tar.xz umami-main.zip | |
Created from https://vercel.com/new
Diffstat (limited to 'src/app/api/reports')
| -rw-r--r-- | src/app/api/reports/[reportId]/route.ts | 80 | ||||
| -rw-r--r-- | src/app/api/reports/attribution/route.ts | 26 | ||||
| -rw-r--r-- | src/app/api/reports/breakdown/route.ts | 26 | ||||
| -rw-r--r-- | src/app/api/reports/funnel/route.ts | 26 | ||||
| -rw-r--r-- | src/app/api/reports/goal/route.ts | 26 | ||||
| -rw-r--r-- | src/app/api/reports/journey/route.ts | 25 | ||||
| -rw-r--r-- | src/app/api/reports/retention/route.ts | 26 | ||||
| -rw-r--r-- | src/app/api/reports/revenue/route.ts | 26 | ||||
| -rw-r--r-- | src/app/api/reports/route.ts | 73 | ||||
| -rw-r--r-- | src/app/api/reports/utm/route.ts | 37 |
10 files changed, 371 insertions, 0 deletions
diff --git a/src/app/api/reports/[reportId]/route.ts b/src/app/api/reports/[reportId]/route.ts new file mode 100644 index 0000000..1f22c62 --- /dev/null +++ b/src/app/api/reports/[reportId]/route.ts @@ -0,0 +1,80 @@ +import { parseRequest } from '@/lib/request'; +import { json, notFound, ok, unauthorized } from '@/lib/response'; +import { reportSchema } from '@/lib/schema'; +import { canDeleteWebsite, canUpdateWebsite, canViewReport } from '@/permissions'; +import { deleteReport, getReport, updateReport } from '@/queries/prisma'; + +export async function GET(request: Request, { params }: { params: Promise<{ reportId: string }> }) { + const { auth, error } = await parseRequest(request); + + if (error) { + return error(); + } + + const { reportId } = await params; + + const report = await getReport(reportId); + + if (!(await canViewReport(auth, report))) { + return unauthorized(); + } + + return json(report); +} + +export async function POST( + request: Request, + { params }: { params: Promise<{ reportId: string }> }, +) { + const { auth, body, error } = await parseRequest(request, reportSchema); + + if (error) { + return error(); + } + + const { reportId } = await params; + const { websiteId, type, name, description, parameters } = body; + + const report = await getReport(reportId); + + if (!report) { + return notFound(); + } + + if (!(await canUpdateWebsite(auth, websiteId))) { + return unauthorized(); + } + + const result = await updateReport(reportId, { + websiteId, + userId: auth.user.id, + type, + name, + description, + parameters, + } as any); + + return json(result); +} + +export async function DELETE( + request: Request, + { params }: { params: Promise<{ reportId: string }> }, +) { + const { auth, error } = await parseRequest(request); + + if (error) { + return error(); + } + + const { reportId } = await params; + const report = await getReport(reportId); + + if (!(await canDeleteWebsite(auth, report.websiteId))) { + return unauthorized(); + } + + await deleteReport(reportId); + + return ok(); +} diff --git a/src/app/api/reports/attribution/route.ts b/src/app/api/reports/attribution/route.ts new file mode 100644 index 0000000..bd7d86d --- /dev/null +++ b/src/app/api/reports/attribution/route.ts @@ -0,0 +1,26 @@ +import { getQueryFilters, parseRequest, setWebsiteDate } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { type AttributionParameters, getAttribution } from '@/queries/sql/reports/getAttribution'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const parameters = await setWebsiteDate(websiteId, body.parameters); + const filters = await getQueryFilters(body.filters, websiteId); + + const data = await getAttribution(websiteId, parameters as AttributionParameters, filters); + + return json(data); +} diff --git a/src/app/api/reports/breakdown/route.ts b/src/app/api/reports/breakdown/route.ts new file mode 100644 index 0000000..3c59314 --- /dev/null +++ b/src/app/api/reports/breakdown/route.ts @@ -0,0 +1,26 @@ +import { getQueryFilters, parseRequest, setWebsiteDate } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { type BreakdownParameters, getBreakdown } from '@/queries/sql'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const parameters = await setWebsiteDate(websiteId, body.parameters); + const filters = await getQueryFilters(body.filters, websiteId); + + const data = await getBreakdown(websiteId, parameters as BreakdownParameters, filters); + + return json(data); +} diff --git a/src/app/api/reports/funnel/route.ts b/src/app/api/reports/funnel/route.ts new file mode 100644 index 0000000..c13f6f1 --- /dev/null +++ b/src/app/api/reports/funnel/route.ts @@ -0,0 +1,26 @@ +import { getQueryFilters, parseRequest, setWebsiteDate } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { type FunnelParameters, getFunnel } from '@/queries/sql'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const parameters = await setWebsiteDate(websiteId, body.parameters); + const filters = await getQueryFilters(body.filters, websiteId); + + const data = await getFunnel(websiteId, parameters as FunnelParameters, filters); + + return json(data); +} diff --git a/src/app/api/reports/goal/route.ts b/src/app/api/reports/goal/route.ts new file mode 100644 index 0000000..3bd0415 --- /dev/null +++ b/src/app/api/reports/goal/route.ts @@ -0,0 +1,26 @@ +import { getQueryFilters, parseRequest, setWebsiteDate } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { type GoalParameters, getGoal } from '@/queries/sql/reports/getGoal'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const parameters = await setWebsiteDate(websiteId, body.parameters); + const filters = await getQueryFilters(body.filters, websiteId); + + const data = await getGoal(websiteId, parameters as GoalParameters, filters); + + return json(data); +} diff --git a/src/app/api/reports/journey/route.ts b/src/app/api/reports/journey/route.ts new file mode 100644 index 0000000..29e8531 --- /dev/null +++ b/src/app/api/reports/journey/route.ts @@ -0,0 +1,25 @@ +import { getQueryFilters, parseRequest } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { getJourney } from '@/queries/sql'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId, parameters, filters } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const queryFilters = await getQueryFilters(filters, websiteId); + + const data = await getJourney(websiteId, parameters, queryFilters); + + return json(data); +} diff --git a/src/app/api/reports/retention/route.ts b/src/app/api/reports/retention/route.ts new file mode 100644 index 0000000..d1a7d69 --- /dev/null +++ b/src/app/api/reports/retention/route.ts @@ -0,0 +1,26 @@ +import { getQueryFilters, parseRequest, setWebsiteDate } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { getRetention, type RetentionParameters } from '@/queries/sql'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const filters = await getQueryFilters(body.filters, websiteId); + const parameters = await setWebsiteDate(websiteId, body.parameters); + + const data = await getRetention(websiteId, parameters as RetentionParameters, filters); + + return json(data); +} diff --git a/src/app/api/reports/revenue/route.ts b/src/app/api/reports/revenue/route.ts new file mode 100644 index 0000000..6a55661 --- /dev/null +++ b/src/app/api/reports/revenue/route.ts @@ -0,0 +1,26 @@ +import { getQueryFilters, parseRequest, setWebsiteDate } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { getRevenue, type RevenuParameters } from '@/queries/sql/reports/getRevenue'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const parameters = await setWebsiteDate(websiteId, body.parameters); + const filters = await getQueryFilters(body.filters, websiteId); + + const data = await getRevenue(websiteId, parameters as RevenuParameters, filters); + + return json(data); +} diff --git a/src/app/api/reports/route.ts b/src/app/api/reports/route.ts new file mode 100644 index 0000000..b0a4135 --- /dev/null +++ b/src/app/api/reports/route.ts @@ -0,0 +1,73 @@ +import { z } from 'zod'; +import { uuid } from '@/lib/crypto'; +import { parseRequest } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { pagingParams, reportSchema, reportTypeParam } from '@/lib/schema'; +import { canUpdateWebsite, canViewWebsite } from '@/permissions'; +import { createReport, getReports } from '@/queries/prisma'; + +export async function GET(request: Request) { + const schema = z.object({ + websiteId: z.uuid(), + type: reportTypeParam.optional(), + ...pagingParams, + }); + + const { auth, query, error } = await parseRequest(request, schema); + + if (error) { + return error(); + } + + const { page, search, pageSize, websiteId, type } = query; + const filters = { + page, + pageSize, + search, + }; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const data = await getReports( + { + where: { + websiteId, + type, + website: { + deletedAt: null, + }, + }, + }, + filters, + ); + + return json(data); +} + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportSchema); + + if (error) { + return error(); + } + + const { websiteId, type, name, description, parameters } = body; + + if (!(await canUpdateWebsite(auth, websiteId))) { + return unauthorized(); + } + + const result = await createReport({ + id: uuid(), + userId: auth.user.id, + websiteId, + type, + name, + description: description || '', + parameters, + }); + + return json(result); +} diff --git a/src/app/api/reports/utm/route.ts b/src/app/api/reports/utm/route.ts new file mode 100644 index 0000000..577fdab --- /dev/null +++ b/src/app/api/reports/utm/route.ts @@ -0,0 +1,37 @@ +import { UTM_PARAMS } from '@/lib/constants'; +import { getQueryFilters, parseRequest, setWebsiteDate } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { reportResultSchema } from '@/lib/schema'; +import { canViewWebsite } from '@/permissions'; +import { getUTM, type UTMParameters } from '@/queries/sql'; + +export async function POST(request: Request) { + const { auth, body, error } = await parseRequest(request, reportResultSchema); + + if (error) { + return error(); + } + + const { websiteId } = body; + + if (!(await canViewWebsite(auth, websiteId))) { + return unauthorized(); + } + + const filters = await getQueryFilters(body.filters, websiteId); + const parameters = await setWebsiteDate(websiteId, body.parameters); + + const data = { + utm_source: [], + utm_medium: [], + utm_campaign: [], + utm_term: [], + utm_content: [], + }; + + for (const key of UTM_PARAMS) { + data[key] = await getUTM(websiteId, { column: key, ...parameters } as UTMParameters, filters); + } + + return json(data); +} |