mirror of
https://github.com/umami-software/umami.git
synced 2026-02-09 07:07:17 +01:00
Handle website delete. Added response helper functions.
This commit is contained in:
parent
0a411a9ad6
commit
c4b75e4aec
31 changed files with 314 additions and 96 deletions
|
|
@ -1,9 +1,9 @@
|
|||
import { parse } from 'cookie';
|
||||
import { verifySecureToken } from './crypto';
|
||||
import { parseSecureToken } from './crypto';
|
||||
import { AUTH_COOKIE_NAME } from './constants';
|
||||
|
||||
export async function verifyAuthToken(req) {
|
||||
const token = parse(req.headers.cookie || '')[AUTH_COOKIE_NAME];
|
||||
|
||||
return verifySecureToken(token);
|
||||
return parseSecureToken(token);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import crypto from 'crypto';
|
||||
import { v5 } from 'uuid';
|
||||
import { v4, v5, validate } from 'uuid';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { JWT, JWE, JWK } from 'jose';
|
||||
|
||||
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
||||
const KEY = JWK.asKey(Buffer.from(secret()));
|
||||
|
||||
export function hash(...args) {
|
||||
|
|
@ -15,11 +14,13 @@ export function secret() {
|
|||
}
|
||||
|
||||
export function uuid(...args) {
|
||||
if (!args.length) return v4();
|
||||
|
||||
return v5(args.join(''), v5(process.env.HASH_SALT, v5.DNS));
|
||||
}
|
||||
|
||||
export function isValidId(s) {
|
||||
return UUID_REGEX.test(s);
|
||||
return validate(s);
|
||||
}
|
||||
|
||||
export function checkPassword(password, hash) {
|
||||
|
|
@ -30,15 +31,24 @@ export async function createToken(payload) {
|
|||
return JWT.sign(payload, KEY);
|
||||
}
|
||||
|
||||
export async function verifyToken(token) {
|
||||
return JWT.verify(token, KEY);
|
||||
export async function parseToken(token) {
|
||||
try {
|
||||
return JWT.verify(token, KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createSecureToken(payload) {
|
||||
return JWE.encrypt(await createToken(payload), KEY);
|
||||
}
|
||||
|
||||
export async function verifySecureToken(token) {
|
||||
const result = await JWE.decrypt(token, KEY);
|
||||
return verifyToken(result.toString());
|
||||
export async function parseSecureToken(token) {
|
||||
try {
|
||||
const result = await JWE.decrypt(token, KEY);
|
||||
|
||||
return parseToken(result.toString());
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
28
lib/db.js
28
lib/db.js
|
|
@ -63,6 +63,21 @@ export async function getWebsites(user_id) {
|
|||
);
|
||||
}
|
||||
|
||||
export async function createWebsite(user_id, data) {
|
||||
return runQuery(
|
||||
prisma.website.create({
|
||||
data: {
|
||||
account: {
|
||||
connect: {
|
||||
user_id,
|
||||
},
|
||||
},
|
||||
...data,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateWebsite(website_id, data) {
|
||||
return runQuery(
|
||||
prisma.website.update({
|
||||
|
|
@ -74,6 +89,19 @@ export async function updateWebsite(website_id, data) {
|
|||
);
|
||||
}
|
||||
|
||||
export async function deleteWebsite(website_id) {
|
||||
return runQuery(
|
||||
/* Prisma bug, does not cascade on non-nullable foreign keys
|
||||
prisma.website.delete({
|
||||
where: {
|
||||
website_id,
|
||||
},
|
||||
}),
|
||||
*/
|
||||
prisma.queryRaw(`delete from website where website_id=$1`, website_id),
|
||||
);
|
||||
}
|
||||
|
||||
export async function createSession(website_id, data) {
|
||||
return runQuery(
|
||||
prisma.session.create({
|
||||
|
|
|
|||
|
|
@ -17,19 +17,23 @@ export function use(middleware) {
|
|||
export const useCors = use(cors());
|
||||
|
||||
export const useSession = use(async (req, res, next) => {
|
||||
try {
|
||||
req.session = await verifySession(req);
|
||||
} catch {
|
||||
const session = await verifySession(req);
|
||||
|
||||
if (!session) {
|
||||
return res.status(400).end();
|
||||
}
|
||||
|
||||
req.session = session;
|
||||
next();
|
||||
});
|
||||
|
||||
export const useAuth = use(async (req, res, next) => {
|
||||
try {
|
||||
req.auth = await verifyAuthToken(req);
|
||||
} catch {
|
||||
const token = await verifyAuthToken(req);
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
req.auth = token;
|
||||
next();
|
||||
});
|
||||
|
|
|
|||
25
lib/response.js
Normal file
25
lib/response.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
export function ok(res, data = {}) {
|
||||
return res.status(200).json(data);
|
||||
}
|
||||
|
||||
export function redirect(res, url) {
|
||||
res.setHeader('Location', url);
|
||||
|
||||
return res.status(303).end();
|
||||
}
|
||||
|
||||
export function badRequest(res) {
|
||||
return res.status(400).end();
|
||||
}
|
||||
|
||||
export function unauthorized(res) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
export function forbidden(res) {
|
||||
return res.status(403).end();
|
||||
}
|
||||
|
||||
export function methodNotAllowed(res) {
|
||||
res.status(405).end();
|
||||
}
|
||||
|
|
@ -1,18 +1,14 @@
|
|||
import { getWebsite, getSession, createSession } from 'lib/db';
|
||||
import { getClientInfo } from 'lib/request';
|
||||
import { uuid, isValidId, verifyToken } from 'lib/crypto';
|
||||
import { uuid, isValidId, parseToken } from 'lib/crypto';
|
||||
|
||||
export async function verifySession(req) {
|
||||
const { payload } = req.body;
|
||||
const { website: website_uuid, hostname, screen, language, session } = payload;
|
||||
|
||||
if (!isValidId(website_uuid)) {
|
||||
throw new Error(`Invalid website: ${website_uuid}`);
|
||||
}
|
||||
const token = await parseToken(session);
|
||||
|
||||
try {
|
||||
return await verifyToken(session);
|
||||
} catch {
|
||||
if (!token || !isValidId(website_uuid) || token.website_uuid !== website_uuid) {
|
||||
const { userAgent, browser, os, ip, country, device } = await getClientInfo(req, payload);
|
||||
|
||||
if (website_uuid) {
|
||||
|
|
@ -50,4 +46,6 @@ export async function verifySession(req) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ function parseQuery(url, params = {}) {
|
|||
|
||||
export const get = (url, params) => apiRequest('get', parseQuery(url, params));
|
||||
|
||||
export const del = (url, params) => apiRequest('delete', parseQuery(url, params));
|
||||
|
||||
export const post = (url, params) => apiRequest('post', url, JSON.stringify(params));
|
||||
|
||||
export const del = (url, params) => apiRequest('del', parseQuery(url, params));
|
||||
export const put = (url, params) => apiRequest('put', url, JSON.stringify(params));
|
||||
|
||||
export const hook = (_this, method, callback) => {
|
||||
const orig = _this[method];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue