Added CORS middleware. Updated umami script.

This commit is contained in:
Mike Cao 2020-07-18 10:36:46 -07:00
parent bb04015b46
commit 58a1c63407
10 changed files with 144 additions and 78 deletions

20
lib/middleware.js Normal file
View File

@ -0,0 +1,20 @@
import cors from 'cors';
export function use(middleware) {
return (req, res) =>
new Promise((resolve, reject) => {
middleware(req, res, result => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
export const allowPost = use(
cors({
origin: '*',
methods: ['POST', 'OPTIONS'],
}),
);

View File

@ -61,48 +61,55 @@ export async function getCountry(req, ip) {
}
export async function parseSessionRequest(req) {
const ip = getIpAddress(req);
const { website_id, screen, language } = req.body;
const { userAgent, browser, os } = getDevice(req);
const country = await getCountry(req, ip);
const session_id = hash(`${website_id}${ip}${userAgent}${os}`);
if (req.method === 'POST') {
const ip = getIpAddress(req);
const { website_id, hostname, screen, language } = req.body;
const { userAgent, browser, os } = getDevice(req);
const country = await getCountry(req, ip);
const session_id = hash(`${website_id}${hostname}${ip}${userAgent}${os}`);
return {
website_id,
session_id,
browser,
os,
screen,
language,
country,
};
return {
website_id,
session_id,
hostname,
browser,
os,
screen,
language,
country,
};
}
return {};
}
export function parseCollectRequest(req) {
const { type, payload } = req.body;
if (req.method === 'POST') {
const { type, payload } = req.body;
if (payload.session) {
const {
url,
referrer,
session: { website_id, session_id, time, hash: validationHash },
} = payload;
if (
validHash(website_id) &&
validHash(session_id) &&
validHash(validationHash) &&
hash(`${website_id}${session_id}${time}`) === validationHash
) {
return {
valid: true,
type,
session_id,
if (payload.session) {
const {
url,
referrer,
};
session: { website_id, session_id, time, hash: validationHash },
} = payload;
if (
validHash(website_id) &&
validHash(session_id) &&
validHash(validationHash) &&
hash(`${website_id}${session_id}${time}`) === validationHash
) {
return {
success: 1,
type,
session_id,
url,
referrer,
};
}
}
}
return { valid: false };
return { success: 0 };
}

View File

@ -29,6 +29,7 @@
"@prisma/client": "2.2.2",
"chart.js": "^2.9.3",
"classnames": "^2.2.6",
"cors": "^2.8.5",
"date-fns": "^2.14.0",
"detect-browser": "^5.1.1",
"dotenv": "^8.2.0",

View File

@ -1,18 +1,21 @@
import { parseCollectRequest } from 'lib/utils';
import { savePageView } from 'lib/db';
import { allowPost } from 'lib/middleware';
export default async (req, res) => {
await allowPost(req, res);
const values = parseCollectRequest(req);
if (values.valid) {
if (values.success) {
const { type, session_id, url, referrer } = values;
if (type === 'pageview') {
await savePageView(session_id, url, referrer);
await savePageView(session_id, url, referrer).catch(() => {
values.success = 0;
});
}
}
res.setHeader('Access-Control-Allow-Origin', '*');
res.status(200).json({ status: values.valid });
res.status(200).json({ success: values.success });
};

View File

@ -1,11 +1,16 @@
import { getWebsite, getSession, createSession } from 'lib/db';
import { hash, parseSessionRequest } from 'lib/utils';
import { allowPost } from 'lib/middleware';
export default async (req, res) => {
let result = { time: Date.now() };
await allowPost(req, res);
let result = { success: 0, time: Date.now() };
const {
website_id,
session_id,
hostname,
browser,
os,
screen,
@ -13,24 +18,32 @@ export default async (req, res) => {
country,
} = await parseSessionRequest(req);
const website = await getWebsite(website_id);
if (website_id && session_id) {
const website = await getWebsite(website_id);
if (website) {
const session = await getSession(session_id);
if (website) {
const session = await getSession(session_id);
if (!session) {
await createSession(website_id, session_id, { browser, os, screen, language, country });
if (!session) {
await createSession(website_id, session_id, {
hostname,
browser,
os,
screen,
language,
country,
});
}
result = {
...result,
success: 1,
session_id,
website_id,
hash: hash(`${website_id}${session_id}${result.time}`),
};
}
result = {
...result,
session_id,
website_id,
hash: hash(`${website_id}${session_id}${result.time}`),
};
}
res.setHeader('Access-Control-Allow-Origin', '*');
res.status(200).json(result);
};

View File

@ -34,6 +34,7 @@ model session {
browser String?
country String?
created_at DateTime? @default(now())
hostname String?
language String?
os String?
screen String?

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -11,16 +11,37 @@ const {
document,
} = window;
function post(url, params) {
return fetch(url, {
const post = (url, params) =>
fetch(url, {
method: 'post',
cache: 'no-cache',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: params,
body: JSON.stringify(params),
}).then(res => res.json());
}
const createSession = data =>
post(`${HOST_URL}/api/session`, data).then(({ success, ...session }) => {
if (success) {
store.setItem(SESSION_VAR, JSON.stringify(session));
return success;
}
});
const getSession = () => JSON.parse(store.getItem(SESSION_VAR));
const pageView = (url, referrer) =>
post(`${HOST_URL}/api/collect`, {
type: 'pageview',
payload: { url, referrer, session: getSession() },
}).then(({ success }) => {
if (!success) {
store.removeItem(SESSION_VAR);
}
return success;
});
const script = document.querySelector('script[data-website-id]');
@ -31,26 +52,12 @@ if (script) {
const referrer = document.referrer;
const screen = `${width}x${height}`;
const url = `${pathname}${search}`;
const data = { website_id, hostname, url, screen, language };
if (!store.getItem(SESSION_VAR)) {
post(`${HOST_URL}/api/session`, {
website_id,
hostname,
url,
screen,
language,
}).then(session => {
store.setItem(SESSION_VAR, JSON.stringify(session));
});
createSession(data).then(success => success && pageView(url, referrer));
} else {
pageView(url, referrer).then(success => !success && createSession(data));
}
post(`${HOST_URL}/api/collect`, {
type: 'pageview',
payload: { url, referrer, session: JSON.parse(store.getItem(SESSION_VAR)) },
}).then(response => {
if (!response.status) {
store.removeItem(SESSION_VAR);
}
});
}
}

View File

@ -1,6 +1,6 @@
create table website (
website_id uuid primary key,
hostname varchar(255) unique not null,
hostname varchar(100) unique not null,
created_at timestamp with time zone default current_timestamp
);
@ -8,6 +8,7 @@ create table session (
session_id uuid primary key,
website_id uuid references website(website_id) on delete cascade,
created_at timestamp with time zone default current_timestamp,
hostname varchar(100),
browser varchar(20),
os varchar(20),
screen varchar(11),

View File

@ -2619,6 +2619,14 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
cosmiconfig@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
@ -5513,7 +5521,7 @@ num2fraction@^1.2.2:
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@ -8386,6 +8394,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
vary@^1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
vendors@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e"