mirror of
https://github.com/umami-software/umami.git
synced 2026-02-07 22:27:16 +01:00
Convert /api/users.
This commit is contained in:
parent
090abcff81
commit
baa3851fb4
61 changed files with 1064 additions and 70 deletions
|
|
@ -1,16 +1,64 @@
|
|||
import { Report } from '@prisma/client';
|
||||
import { getClient } from '@umami/redis-client';
|
||||
import { getClient, redisEnabled } from '@umami/redis-client';
|
||||
import debug from 'debug';
|
||||
import { PERMISSIONS, ROLE_PERMISSIONS, SHARE_TOKEN_HEADER } from 'lib/constants';
|
||||
import { PERMISSIONS, ROLE_PERMISSIONS, ROLES, SHARE_TOKEN_HEADER } from 'lib/constants';
|
||||
import { secret } from 'lib/crypto';
|
||||
import { NextApiRequest } from 'next';
|
||||
import { createSecureToken, ensureArray, getRandomChars, parseToken } from 'next-basics';
|
||||
import { getTeamUser, getWebsite } from 'queries';
|
||||
import {
|
||||
createSecureToken,
|
||||
ensureArray,
|
||||
getRandomChars,
|
||||
parseSecureToken,
|
||||
parseToken,
|
||||
} from 'next-basics';
|
||||
import { getTeamUser, getUser, getWebsite } from 'queries';
|
||||
import { Auth } from './types';
|
||||
|
||||
const log = debug('umami:auth');
|
||||
const cloudMode = process.env.CLOUD_MODE;
|
||||
|
||||
export async function checkAuth(request: Request) {
|
||||
const token = request.headers.get('authorization')?.split(' ')?.[1];
|
||||
const payload = parseSecureToken(token, secret());
|
||||
const shareToken = await parseShareToken(request as any);
|
||||
|
||||
let user = null;
|
||||
const { userId, authKey, grant } = payload || {};
|
||||
|
||||
if (userId) {
|
||||
user = await getUser(userId);
|
||||
} else if (redisEnabled && authKey) {
|
||||
const redis = getClient();
|
||||
|
||||
const key = await redis.get(authKey);
|
||||
|
||||
if (key?.userId) {
|
||||
user = await getUser(key.userId);
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
log('checkAuth:', { token, shareToken, payload, user, grant });
|
||||
}
|
||||
|
||||
if (!user?.id && !shareToken) {
|
||||
log('checkAuth: User not authorized');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
user.isAdmin = user.role === ROLES.admin;
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
grant,
|
||||
token,
|
||||
shareToken,
|
||||
authKey,
|
||||
};
|
||||
}
|
||||
|
||||
export async function saveAuth(data: any, expire = 0) {
|
||||
const authKey = `auth:${getRandomChars(32)}`;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,28 @@
|
|||
import { NextApiRequest } from 'next';
|
||||
import { ZodObject } from 'zod';
|
||||
import { getAllowedUnits, getMinimumUnit } from './date';
|
||||
import { getWebsiteDateRange } from '../queries';
|
||||
import { FILTER_COLUMNS } from 'lib/constants';
|
||||
|
||||
export async function getRequestDateRange(req: NextApiRequest) {
|
||||
const { websiteId, startAt, endAt, unit } = req.query;
|
||||
export async function getJsonBody(request: Request) {
|
||||
try {
|
||||
return await request.clone().json();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkRequest(request: Request, schema: ZodObject<any>) {
|
||||
const url = new URL(request.url);
|
||||
const query = Object.fromEntries(url.searchParams);
|
||||
const body = await getJsonBody(request);
|
||||
|
||||
const result = schema.safeParse(request.method === 'GET' ? query : body);
|
||||
|
||||
return { query, body, error: result.error };
|
||||
}
|
||||
|
||||
export async function getRequestDateRange(query: Record<string, any>) {
|
||||
const { websiteId, startAt, endAt, unit } = query;
|
||||
|
||||
// All-time
|
||||
if (+startAt === 0 && +endAt === 1) {
|
||||
|
|
@ -31,9 +49,9 @@ export async function getRequestDateRange(req: NextApiRequest) {
|
|||
};
|
||||
}
|
||||
|
||||
export function getRequestFilters(req: NextApiRequest) {
|
||||
export function getRequestFilters(query: Record<string, any>) {
|
||||
return Object.keys(FILTER_COLUMNS).reduce((obj, key) => {
|
||||
const value = req.query[key];
|
||||
const value = query[key];
|
||||
|
||||
if (value !== undefined) {
|
||||
obj[key] = value;
|
||||
|
|
|
|||
21
src/lib/response.ts
Normal file
21
src/lib/response.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { serializeError } from 'serialize-error';
|
||||
|
||||
export function ok() {
|
||||
return Response.json({ ok: true });
|
||||
}
|
||||
|
||||
export function json(data: any) {
|
||||
return Response.json(data);
|
||||
}
|
||||
|
||||
export function badRequest(message?: any) {
|
||||
return Response.json({ error: 'Bad request', message }, { status: 400 });
|
||||
}
|
||||
|
||||
export function unauthorized() {
|
||||
return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
export function serverError(error: any) {
|
||||
return Response.json({ error: 'Server error', message: serializeError(error), status: 500 });
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
import { z } from 'zod';
|
||||
import * as yup from 'yup';
|
||||
import { isValidTimezone } from 'lib/date';
|
||||
import { UNIT_TYPES } from './constants';
|
||||
|
||||
export const dateRange = {
|
||||
startAt: yup.number().integer().required(),
|
||||
|
|
@ -11,3 +14,18 @@ export const pageInfo = {
|
|||
pageSize: yup.number().integer().positive().min(1).max(200),
|
||||
orderBy: yup.string(),
|
||||
};
|
||||
|
||||
export const pagingParams = {
|
||||
page: z.coerce.number().int().positive(),
|
||||
pageSize: z.coerce.number().int().positive(),
|
||||
orderBy: z.string().optional(),
|
||||
query: z.string().optional(),
|
||||
};
|
||||
|
||||
export const timezone = z.string().refine(value => isValidTimezone(value), {
|
||||
message: 'Invalid timezone',
|
||||
});
|
||||
|
||||
export const unit = z.string().refine(value => UNIT_TYPES.includes(value), {
|
||||
message: 'Invalid unit',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export type KafkaTopic = ObjectValues<typeof KAFKA_TOPIC>;
|
|||
export type ReportType = ObjectValues<typeof REPORT_TYPES>;
|
||||
|
||||
export interface PageParams {
|
||||
query?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
orderBy?: string;
|
||||
|
|
@ -43,7 +43,7 @@ export interface PageResult<T> {
|
|||
|
||||
export interface PagedQueryResult<T> {
|
||||
result: PageResult<T>;
|
||||
query: any;
|
||||
search: any;
|
||||
params: PageParams;
|
||||
setParams: Dispatch<SetStateAction<T | PageParams>>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue