mirror of
https://github.com/umami-software/umami.git
synced 2025-12-08 05:12:36 +01:00
Merge branch 'dev' of https://github.com/umami-software/umami into dev
# Conflicts: # lib/middleware.ts # pages/api/users/[id]/index.ts # pages/api/users/index.ts # pages/api/websites/[id]/active.ts # pages/api/websites/[id]/eventdata.ts # pages/api/websites/[id]/events.ts # pages/api/websites/[id]/index.ts # pages/api/websites/[id]/metrics.ts # pages/api/websites/[id]/pageviews.ts # pages/api/websites/[id]/reset.ts # pages/api/websites/[id]/stats.ts # yarn.lock
This commit is contained in:
commit
f3879c92e1
212 changed files with 2642 additions and 2841 deletions
|
|
@ -7,9 +7,9 @@ import {
|
|||
methodNotAllowed,
|
||||
getRandomChars,
|
||||
} from 'next-basics';
|
||||
import redis from '@umami/redis-client';
|
||||
import { getUser, User } from 'queries';
|
||||
import { secret } from 'lib/crypto';
|
||||
import redis from 'lib/redis';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { methodNotAllowed, ok } from 'next-basics';
|
||||
import redis from '@umami/redis-client';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import redis from 'lib/redis';
|
||||
import { getAuthToken } from 'lib/auth';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import ipaddr from 'ipaddr.js';
|
|||
import { createToken, unauthorized, send, badRequest, forbidden } from 'next-basics';
|
||||
import { savePageView, saveEvent } from 'queries';
|
||||
import { useCors, useSession } from 'lib/middleware';
|
||||
import { getJsonBody, getIpAddress } from 'lib/request';
|
||||
import { getJsonBody, getIpAddress } from 'lib/detect';
|
||||
import { secret } from 'lib/crypto';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
|||
|
||||
const { type, payload } = getJsonBody(req);
|
||||
|
||||
const { referrer, event_name: eventName, event_data: eventData } = payload;
|
||||
const { referrer, eventName, eventData } = payload;
|
||||
let { url } = payload;
|
||||
|
||||
// Validate eventData is JSON
|
||||
|
|
@ -39,6 +39,11 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
|
|||
return badRequest(res, 'Event Data must be in the form of a JSON Object.');
|
||||
}
|
||||
|
||||
// Validate eventData is less than 100kB
|
||||
if (eventData && new TextEncoder().encode(eventData).length / 1024 > 100) {
|
||||
return badRequest(res, 'Event Data exceeds maximum size of 100 kB.');
|
||||
}
|
||||
|
||||
const ignoreIps = process.env.IGNORE_IP;
|
||||
const ignoreHostnames = process.env.IGNORE_HOSTNAME;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { NextApiResponse } from 'next';
|
|||
import { RealtimeUpdate } from 'lib/types';
|
||||
|
||||
export interface InitUpdateRequestQuery {
|
||||
start_at: string;
|
||||
startAt: string;
|
||||
}
|
||||
|
||||
export default async (
|
||||
|
|
@ -18,7 +18,7 @@ export default async (
|
|||
await useAuth(req, res);
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const { start_at } = req.query;
|
||||
const { startAt } = req.query;
|
||||
|
||||
const token = req.headers[SHARE_TOKEN_HEADER];
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ export default async (
|
|||
|
||||
const { websites } = parseToken(token, secret());
|
||||
|
||||
const data = await getRealtimeData(websites, new Date(+start_at));
|
||||
const data = await getRealtimeData(websites, new Date(+startAt));
|
||||
|
||||
return ok(res, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,10 @@ export default async (
|
|||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
const { id: teamId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!(await canViewTeam(userId, teamId))) {
|
||||
if (!(await canViewTeam(req.auth, teamId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +35,7 @@ export default async (
|
|||
if (req.method === 'POST') {
|
||||
const { name } = req.body;
|
||||
|
||||
if (!(await canUpdateTeam(userId, teamId))) {
|
||||
if (!(await canUpdateTeam(req.auth, teamId))) {
|
||||
return unauthorized(res, 'You must be the owner of this team.');
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +45,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
if (!(await canDeleteTeam(userId, teamId))) {
|
||||
if (!(await canDeleteTeam(req.auth, teamId))) {
|
||||
return unauthorized(res, 'You must be the owner of this team.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export interface TeamUserRequestQuery {
|
|||
|
||||
export interface TeamUserRequestBody {
|
||||
email: string;
|
||||
role_id: string;
|
||||
team_user_id?: string;
|
||||
roleId: string;
|
||||
teamUserId?: string;
|
||||
}
|
||||
|
||||
export default async (
|
||||
|
|
@ -21,13 +21,10 @@ export default async (
|
|||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
const { id: teamId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!(await canViewTeam(userId, teamId))) {
|
||||
if (!(await canViewTeam(req.auth, teamId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
@ -37,11 +34,11 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (!(await canUpdateTeam(userId, teamId))) {
|
||||
if (!(await canUpdateTeam(req.auth, teamId))) {
|
||||
return unauthorized(res, 'You must be the owner of this team.');
|
||||
}
|
||||
|
||||
const { email, role_id: roleId } = req.body;
|
||||
const { email, roleId: roleId } = req.body;
|
||||
|
||||
// Check for User
|
||||
const user = await getUser({ username: email });
|
||||
|
|
@ -56,12 +53,12 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
if (await canUpdateTeam(userId, teamId)) {
|
||||
if (await canUpdateTeam(req.auth, teamId)) {
|
||||
return unauthorized(res, 'You must be the owner of this team.');
|
||||
}
|
||||
const { team_user_id } = req.body;
|
||||
const { teamUserId } = req.body;
|
||||
|
||||
await deleteTeamUser(team_user_id);
|
||||
await deleteTeamUser(teamUserId);
|
||||
|
||||
return ok(res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,10 @@ export default async (
|
|||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
const { id: teamId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (await canViewTeam(userId, teamId)) {
|
||||
if (await canViewTeam(req.auth, teamId)) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (!(await canCreateTeam(userId))) {
|
||||
if (!(await canCreateTeam(req.auth))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canUpdateUser, canViewUser } from 'lib/auth';
|
||||
import { canDeleteUser, canUpdateUser, canViewUser } from 'lib/auth';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
|
|
@ -26,7 +26,7 @@ export default async (
|
|||
const { id } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (await canViewUser(userId, id)) {
|
||||
if (!(await canViewUser(req.auth, id))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (await canUpdateUser(userId, id)) {
|
||||
if (!(await canUpdateUser(req.auth, id))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +46,8 @@ export default async (
|
|||
|
||||
const data: any = {};
|
||||
|
||||
if (password) {
|
||||
// Only admin can change these fields
|
||||
if (password && isAdmin) {
|
||||
data.password = hashPassword(password);
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
if (!isAdmin) {
|
||||
if (!(await canDeleteUser(req.auth))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ export interface UserPasswordRequestQuery {
|
|||
}
|
||||
|
||||
export interface UserPasswordRequestBody {
|
||||
current_password: string;
|
||||
new_password: string;
|
||||
currentPassword: string;
|
||||
newPassword: string;
|
||||
}
|
||||
|
||||
export default async (
|
||||
|
|
@ -27,24 +27,21 @@ export default async (
|
|||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const { current_password, new_password } = req.body;
|
||||
const { currentPassword, newPassword } = req.body;
|
||||
const { id } = req.query;
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (canUpdateUser(userId, id)) {
|
||||
if (!(await canUpdateUser(req.auth, id))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const user = await getUser({ id });
|
||||
const user = await getUser({ id }, { includePassword: true });
|
||||
|
||||
if (!checkPassword(current_password, user.password)) {
|
||||
if (!checkPassword(currentPassword, user.password)) {
|
||||
return badRequest(res, 'Current password is incorrect');
|
||||
}
|
||||
|
||||
const password = hashPassword(new_password);
|
||||
const password = hashPassword(newPassword);
|
||||
|
||||
const updated = await updateUser({ password }, { id });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { Prisma } from '@prisma/client';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canCreateWebsite } from 'lib/auth';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok } from 'next-basics';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createWebsite, getUserWebsites } from 'queries';
|
||||
|
||||
export interface WebsitesRequestQuery {}
|
||||
|
|
@ -35,6 +36,10 @@ export default async (
|
|||
if (req.method === 'POST') {
|
||||
const { name, domain, shareId, teamId } = req.body;
|
||||
|
||||
if (!(await canCreateWebsite(req.auth, teamId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const data: Prisma.WebsiteUncheckedCreateInput = {
|
||||
id: uuid(),
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canCreateUser, canViewUsers } from 'lib/auth';
|
||||
import { ROLES } from 'lib/constants';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { ROLES } from 'lib/constants';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, hashPassword, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createUser, getUser, getUsers, User } from 'queries';
|
||||
|
|
@ -18,12 +19,8 @@ export default async (
|
|||
) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const {
|
||||
user: { isAdmin },
|
||||
} = req.auth;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!isAdmin) {
|
||||
if (!(await canViewUsers(req.auth))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +30,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (!isAdmin) {
|
||||
if (!(await canCreateUser(req.auth))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { NextApiRequestQueryBody, WebsiteActive } from 'lib/types';
|
||||
import { WebsiteActive } from 'lib/types';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
|
|
@ -16,15 +17,10 @@ export default async (
|
|||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
const { user, shareToken } = req.auth;
|
||||
const userId = user?.id;
|
||||
const websiteId = req.query.id;
|
||||
const shared = shareToken?.websiteId === websiteId;
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const canView = await canViewWebsite(userId, websiteId);
|
||||
|
||||
if (!canView && !shared) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ export interface WebsiteEventDataRequestQuery {
|
|||
}
|
||||
|
||||
export interface WebsiteEventDataRequestBody {
|
||||
start_at: string;
|
||||
end_at: string;
|
||||
event_name: string;
|
||||
startAt: string;
|
||||
endAt: string;
|
||||
eventName: string;
|
||||
columns: { [key: string]: 'count' | 'max' | 'min' | 'avg' | 'sum' };
|
||||
filters?: { [key: string]: any };
|
||||
}
|
||||
|
|
@ -24,22 +24,17 @@ export default async (
|
|||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const canView = canViewWebsite(userId, websiteId);
|
||||
|
||||
if (!canView) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const { start_at, end_at, event_name: eventName, columns, filters } = req.body;
|
||||
const { startAt, endAt, eventName, columns, filters } = req.body;
|
||||
|
||||
const startDate = new Date(+start_at);
|
||||
const endDate = new Date(+end_at);
|
||||
const startDate = new Date(+startAt);
|
||||
const endDate = new Date(+endAt);
|
||||
|
||||
const events = await getEventData(websiteId, {
|
||||
startDate,
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ const unitTypes = ['year', 'month', 'hour', 'day'];
|
|||
|
||||
export interface WebsiteEventsRequestQuery {
|
||||
id: string;
|
||||
start_at: string;
|
||||
end_at: string;
|
||||
startAt: string;
|
||||
endAt: string;
|
||||
unit: string;
|
||||
tz: string;
|
||||
timezone: string;
|
||||
url: string;
|
||||
event_name: string;
|
||||
eventName: string;
|
||||
}
|
||||
|
||||
export default async (
|
||||
|
|
@ -25,32 +25,27 @@ export default async (
|
|||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
const { id: websiteId, start_at, end_at, unit, tz, url, event_name } = req.query;
|
||||
const { user, shareToken } = req.auth;
|
||||
const userId = user?.id;
|
||||
const shared = shareToken?.websiteId === websiteId;
|
||||
const { id: websiteId, startAt, endAt, unit, timezone, url, eventName } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const canView = canViewWebsite(userId, websiteId);
|
||||
|
||||
if (!canView && !shared) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
if (!moment.tz.zone(tz) || !unitTypes.includes(unit)) {
|
||||
if (!moment.tz.zone(timezone) || !unitTypes.includes(unit)) {
|
||||
return badRequest(res);
|
||||
}
|
||||
const startDate = new Date(+start_at);
|
||||
const endDate = new Date(+end_at);
|
||||
const startDate = new Date(+startAt);
|
||||
const endDate = new Date(+endAt);
|
||||
|
||||
const events = await getEventMetrics(websiteId, {
|
||||
startDate,
|
||||
endDate,
|
||||
timezone: tz,
|
||||
timezone,
|
||||
unit,
|
||||
filters: {
|
||||
url,
|
||||
eventName: event_name,
|
||||
eventName,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -22,15 +22,10 @@ export default async (
|
|||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
const { user, shareToken } = req.auth;
|
||||
const userId = user?.id;
|
||||
const websiteId = req.query.id;
|
||||
const shared = shareToken?.websiteId === websiteId;
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const canView = await canViewWebsite(userId, websiteId);
|
||||
|
||||
if (!canView && !shared) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
@ -40,9 +35,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const canUpdate = await canUpdateWebsite(userId, websiteId);
|
||||
|
||||
if (!canUpdate) {
|
||||
if (!(await canUpdateWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
@ -60,9 +53,7 @@ export default async (
|
|||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
const canDelete = await canDeleteWebsite(userId, websiteId);
|
||||
|
||||
if (!canDelete) {
|
||||
if (!(await canDeleteWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ function getColumn(type) {
|
|||
export interface WebsiteMetricsRequestQuery {
|
||||
id: string;
|
||||
type: string;
|
||||
start_at: number;
|
||||
end_at: number;
|
||||
startAt: number;
|
||||
endAt: number;
|
||||
url: string;
|
||||
referrer: string;
|
||||
os: string;
|
||||
|
|
@ -58,8 +58,8 @@ export default async (
|
|||
const {
|
||||
id: websiteId,
|
||||
type,
|
||||
start_at,
|
||||
end_at,
|
||||
startAt,
|
||||
endAt,
|
||||
url,
|
||||
referrer,
|
||||
os,
|
||||
|
|
@ -67,19 +67,14 @@ export default async (
|
|||
device,
|
||||
country,
|
||||
} = req.query;
|
||||
const { user, shareToken } = req.auth;
|
||||
const userId = user?.id;
|
||||
const shared = shareToken?.websiteId === websiteId;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const canView = await canViewWebsite(userId, websiteId);
|
||||
|
||||
if (!canView && !shared) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const startDate = new Date(+start_at);
|
||||
const endDate = new Date(+end_at);
|
||||
const startDate = new Date(+startAt);
|
||||
const endDate = new Date(+endAt);
|
||||
|
||||
if (sessionColumns.includes(type)) {
|
||||
let data = await getSessionMetrics(websiteId, {
|
||||
|
|
@ -136,7 +131,7 @@ export default async (
|
|||
browser: type !== 'browser' ? browser : undefined,
|
||||
device: type !== 'device' ? device : undefined,
|
||||
country: type !== 'country' ? country : undefined,
|
||||
event_url: type !== 'url' && table === 'event' ? url : undefined,
|
||||
eventUrl: type !== 'url' && table === 'event' ? url : undefined,
|
||||
query: type === 'query' && table !== 'event' ? true : undefined,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import moment from 'moment-timezone';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { NextApiRequestQueryBody, WebsitePageviews } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { getPageviewStats } from 'queries';
|
||||
|
||||
const unitTypes = ['year', 'month', 'hour', 'day'];
|
||||
|
|
@ -11,10 +11,10 @@ const unitTypes = ['year', 'month', 'hour', 'day'];
|
|||
export interface WebsitePageviewRequestQuery {
|
||||
id: string;
|
||||
websiteId: string;
|
||||
start_at: number;
|
||||
end_at: number;
|
||||
startAt: number;
|
||||
endAt: number;
|
||||
unit: string;
|
||||
tz: string;
|
||||
timezone: string;
|
||||
url?: string;
|
||||
referrer?: string;
|
||||
os?: string;
|
||||
|
|
@ -32,10 +32,10 @@ export default async (
|
|||
|
||||
const {
|
||||
id: websiteId,
|
||||
start_at,
|
||||
end_at,
|
||||
startAt,
|
||||
endAt,
|
||||
unit,
|
||||
tz,
|
||||
timezone,
|
||||
url,
|
||||
referrer,
|
||||
os,
|
||||
|
|
@ -43,21 +43,16 @@ export default async (
|
|||
device,
|
||||
country,
|
||||
} = req.query;
|
||||
const { user, shareToken } = req.auth;
|
||||
const userId = user?.id;
|
||||
const shared = shareToken?.websiteId === websiteId;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const canView = await canViewWebsite(userId, websiteId);
|
||||
|
||||
if (!canView && !shared) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const startDate = new Date(+start_at);
|
||||
const endDate = new Date(+end_at);
|
||||
const startDate = new Date(+startAt);
|
||||
const endDate = new Date(+endAt);
|
||||
|
||||
if (!moment.tz.zone(tz) || !unitTypes.includes(unit)) {
|
||||
if (!moment.tz.zone(timezone) || !unitTypes.includes(unit)) {
|
||||
return badRequest(res);
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +60,7 @@ export default async (
|
|||
getPageviewStats(websiteId, {
|
||||
startDate,
|
||||
endDate,
|
||||
timezone: tz,
|
||||
timezone,
|
||||
unit,
|
||||
count: '*',
|
||||
filters: {
|
||||
|
|
@ -80,7 +75,7 @@ export default async (
|
|||
getPageviewStats(websiteId, {
|
||||
startDate,
|
||||
endDate,
|
||||
timezone: tz,
|
||||
timezone,
|
||||
unit,
|
||||
count: 'distinct pageview.',
|
||||
filters: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canUpdateWebsite } from 'lib/auth';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
|
|
@ -16,15 +16,10 @@ export default async (
|
|||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
const {
|
||||
user: { id: userId },
|
||||
} = req.auth;
|
||||
const { id: websiteId } = req.query;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const canUpdate = await canUpdateWebsite(userId, websiteId);
|
||||
|
||||
if (!canUpdate) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { NextApiRequestQueryBody, WebsiteStats } from 'lib/types';
|
||||
import { WebsiteStats } from 'lib/types';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canViewWebsite } from 'lib/auth';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { NextApiResponse } from 'next';
|
||||
|
|
@ -8,8 +9,8 @@ import { getWebsiteStats } from 'queries';
|
|||
export interface WebsiteStatsRequestQuery {
|
||||
id: string;
|
||||
type: string;
|
||||
start_at: number;
|
||||
end_at: number;
|
||||
startAt: number;
|
||||
endAt: number;
|
||||
url: string;
|
||||
referrer: string;
|
||||
os: string;
|
||||
|
|
@ -25,34 +26,19 @@ export default async (
|
|||
await useCors(req, res);
|
||||
await useAuth(req, res);
|
||||
|
||||
const {
|
||||
id: websiteId,
|
||||
start_at,
|
||||
end_at,
|
||||
url,
|
||||
referrer,
|
||||
os,
|
||||
browser,
|
||||
device,
|
||||
country,
|
||||
} = req.query;
|
||||
const { user, shareToken } = req.auth;
|
||||
const userId = user?.id;
|
||||
const shared = shareToken?.websiteId === websiteId;
|
||||
const { id: websiteId, startAt, endAt, url, referrer, os, browser, device, country } = req.query;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const canView = await canViewWebsite(userId, websiteId);
|
||||
|
||||
if (!canView && !shared) {
|
||||
if (!(await canViewWebsite(req.auth, websiteId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const startDate = new Date(+start_at);
|
||||
const endDate = new Date(+end_at);
|
||||
const startDate = new Date(+startAt);
|
||||
const endDate = new Date(+endAt);
|
||||
|
||||
const distance = end_at - start_at;
|
||||
const prevStartDate = new Date(+start_at - distance);
|
||||
const prevEndDate = new Date(+end_at - distance);
|
||||
const distance = endAt - startAt;
|
||||
const prevStartDate = new Date(+startAt - distance);
|
||||
const prevEndDate = new Date(+endAt - distance);
|
||||
|
||||
const metrics = await getWebsiteStats(websiteId, {
|
||||
startDate,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Prisma } from '@prisma/client';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { canCreateWebsite } from 'lib/auth';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { useAuth, useCors } from 'lib/middleware';
|
||||
import { NextApiRequestQueryBody } from 'lib/types';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { methodNotAllowed, ok } from 'next-basics';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'next-basics';
|
||||
import { createWebsite, getUserWebsites } from 'queries';
|
||||
|
||||
export interface WebsitesRequestQuery {}
|
||||
|
|
@ -35,7 +35,11 @@ export default async (
|
|||
if (req.method === 'POST') {
|
||||
const { name, domain, shareId, teamId } = req.body;
|
||||
|
||||
const data: Prisma.WebsiteUncheckedCreateInput = {
|
||||
if (!(await canCreateWebsite(req.auth, teamId))) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const data: any = {
|
||||
id: uuid(),
|
||||
name,
|
||||
domain,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue