Skip to content

Commit

Permalink
Auth and session middleware.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikecao committed Jul 28, 2020
1 parent 590a70c commit d81ee39
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 73 deletions.
36 changes: 10 additions & 26 deletions components/Chart.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
import React, { useState, useMemo, useRef, useEffect } from 'react';
import ChartJS from 'chart.js';
import { format, subDays, subHours, startOfHour } from 'date-fns';
import { get } from 'lib/web';
import { getTimezone, getLocalTime } from 'lib/date';

export default function Chart({ websiteId, startDate, endDate }) {
const [data, setData] = useState();
const canvas = useRef();
const chart = useRef();
const metrics = useMemo(() => {
if (data) {
const points = {};
const now = startOfHour(new Date());

for (let i = 0; i <= 168; i++) {
const d = new Date(subHours(now, 168 - i));
const key = format(d, 'yyyy-MM-dd-HH');
points[key] = { t: startOfHour(d).toISOString(), y: 0 };
}

data.pageviews.forEach(e => {
const key = format(new Date(e.created_at), 'yyyy-MM-dd-HH');
points[key].y += 1;
});

return points;
return data.pageviews.map(({ t, y }) => ({ t: getLocalTime(t), y }));
}
}, [data]);
console.log(metrics);

async function loadData() {
setData(
await get(`/api/website/${websiteId}/pageviews`, {
start_at: startDate,
end_at: endDate,
start_at: +startDate,
end_at: +endDate,
tz: getTimezone(),
}),
);
}
Expand All @@ -47,6 +34,9 @@ export default function Chart({ websiteId, startDate, endDate }) {
label: 'page views',
data: Object.values(metrics),
lineTension: 0,
backgroundColor: 'rgb(38, 128, 235, 0.1)',
borderColor: 'rgb(13, 102, 208, 0.2)',
borderWidth: 1,
},
],
},
Expand All @@ -65,19 +55,13 @@ export default function Chart({ websiteId, startDate, endDate }) {
{
type: 'time',
distribution: 'series',
offset: true,
time: {
unit: 'hour',
displayFormats: {
hour: 'ddd M/DD',
day: 'ddd M/DD',
},
tooltipFormat: 'ddd M/DD hA',
},
ticks: {
autoSkip: true,
minRotation: 0,
maxRotation: 0,
maxTicksLimit: 7,
},
},
],
yAxes: [
Expand Down
8 changes: 8 additions & 0 deletions lib/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { parse } from 'cookie';
import { verifySecureToken } from './crypto';

export default async req => {
const token = parse(req.headers.cookie)['umami.auth'];

return verifySecureToken(token);
};
11 changes: 11 additions & 0 deletions lib/date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import moment from 'moment-timezone';
import { addMinutes } from 'date-fns';

export function getTimezone() {
const tz = moment.tz.guess();
return moment.tz.zone(tz).abbr(new Date().getTimezoneOffset());
}

export function getLocalTime(t) {
return addMinutes(new Date(t), new Date().getTimezoneOffset());
}
36 changes: 36 additions & 0 deletions lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ export async function getWebsite(website_uuid) {
);
}

export async function getWebsites(user_id) {
return runQuery(
prisma.website.findMany({
where: {
user_id,
},
}),
);
}

export async function createSession(website_id, data) {
return runQuery(
prisma.session.create({
Expand Down Expand Up @@ -126,3 +136,29 @@ export async function getPageviews(website_id, start_at, end_at) {
}),
);
}

export async function getPageviewData(
website_id,
start_at,
end_at,
timezone = 'utc',
unit = 'day',
count = '*',
) {
return runQuery(
prisma.queryRaw(
`
select date_trunc('${unit}', created_at at time zone '${timezone}') t,
count(${count}) y
from pageview
where website_id=$1
and created_at between $2 and $3
group by 1
order by 1
`,
website_id,
start_at,
end_at,
),
);
}
20 changes: 20 additions & 0 deletions lib/middleware.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import cors from 'cors';
import session from './session';
import auth from './auth';

export function use(middleware) {
return (req, res) =>
Expand All @@ -13,3 +15,21 @@ export function use(middleware) {
}

export const useCors = use(cors());

export const useSession = use(async (req, res, next) => {
try {
req.session = await session(req);
} catch {
return res.status(400).end();
}
next();
});

export const useAuth = use(async (req, res, next) => {
try {
req.auth = await auth(req);
} catch {
return res.status(401).end();
}
next();
});
File renamed without changes.
4 changes: 3 additions & 1 deletion lib/session.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getWebsite, getSession, createSession } from 'lib/db';
import { getCountry, getDevice, getIpAddress } from 'lib/utils';
import { getCountry, getDevice, getIpAddress } from 'lib/request';
import { uuid, isValidId, verifyToken } from 'lib/crypto';

export default async req => {
Expand Down Expand Up @@ -46,6 +46,8 @@ export default async req => {
session_id,
session_uuid,
};
} else {
throw new Error(`Invalid website: ${website_uuid}`);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@
"is-localhost-ip": "^1.4.0",
"jose": "^1.27.2",
"maxmind": "^4.1.3",
"moment-timezone": "^0.5.31",
"next": "9.3.5",
"next-cookies": "^2.0.3",
"node-fetch": "^2.6.0",
"promise-polyfill": "^8.1.3",
"react": "16.13.1",
Expand Down
7 changes: 3 additions & 4 deletions pages/api/collect.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { savePageView, saveEvent } from 'lib/db';
import { useCors } from 'lib/middleware';
import checkSession from 'lib/session';
import { useCors, useSession } from 'lib/middleware';
import { createToken } from 'lib/crypto';

export default async (req, res) => {
await useCors(req, res);
await useSession(req, res);

const session = await checkSession(req);

const { session } = req;
const token = await createToken(session);
const { website_id, session_id } = session;
const { type, payload } = req.body;
Expand Down
12 changes: 12 additions & 0 deletions pages/api/website.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getWebsites } from 'lib/db';
import { useAuth } from 'lib/middleware';

export default async (req, res) => {
await useAuth(req, res);

const { user_id } = req.auth;

const websites = await getWebsites(user_id);

res.status(200).json({ websites });
};
15 changes: 10 additions & 5 deletions pages/api/website/[id]/pageviews.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { getPageviews } from 'lib/db';
import { getPageviewData } from 'lib/db';
import { useAuth } from 'lib/middleware';

export default async (req, res) => {
console.log(req.query);
const { id, start_at, end_at } = req.query;
await useAuth(req, res);

const pageviews = await getPageviews(+id, new Date(+start_at), new Date(+end_at));
const { id, start_at, end_at, tz } = req.query;

res.status(200).json({ pageviews });
const [pageviews, uniques] = await Promise.all([
getPageviewData(+id, new Date(+start_at), new Date(+end_at), tz, 'day', '*'),
getPageviewData(+id, new Date(+start_at), new Date(+end_at), tz, 'day', 'distinct session_id'),
]);

res.status(200).json({ pageviews, uniques });
};
13 changes: 13 additions & 0 deletions pages/api/website/[id]/summary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getPageviews } from 'lib/db';
import { useAuth } from 'lib/middleware';

export default async (req, res) => {
await useAuth(req, res);

console.log(req.query);
const { id, start_at, end_at } = req.query;

const pageviews = await getPageviews(+id, new Date(+start_at), new Date(+end_at));

res.status(200).json({ pageviews });
};
13 changes: 6 additions & 7 deletions pages/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import Link from 'next/link';
import cookies from 'next-cookies';
import { parse } from 'cookie';
import Layout from 'components/Layout';
import Chart from 'components/Chart';
import { verifySecureToken } from 'lib/crypto';
import { subDays, endOfDay } from 'date-fns';

export default function HomePage({ username }) {
return (
Expand All @@ -14,8 +15,8 @@ export default function HomePage({ username }) {
<div>
<Chart
websiteId={3}
startDate={Date.now() - 1000 * 60 * 60 * 24 * 7}
endDate={Date.now()}
startDate={subDays(endOfDay(new Date()), 6)}
endDate={endOfDay(new Date())}
/>
</div>
<Link href="/logout">
Expand All @@ -25,8 +26,8 @@ export default function HomePage({ username }) {
);
}

export async function getServerSideProps(context) {
const token = cookies(context)['umami.auth'];
export async function getServerSideProps({ req, res }) {
const token = parse(req.headers.cookie)['umami.auth'];

try {
const payload = await verifySecureToken(token);
Expand All @@ -37,8 +38,6 @@ export async function getServerSideProps(context) {
},
};
} catch {
const { res } = context;

res.statusCode = 303;
res.setHeader('Location', '/login');
res.end();
Expand Down
38 changes: 9 additions & 29 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1374,11 +1374,6 @@
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==

"@types/cookie@^0.3.3":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==

"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
Expand Down Expand Up @@ -1414,11 +1409,6 @@
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==

"@types/object-assign@^4.0.30":
version "4.0.30"
resolved "https://registry.yarnpkg.com/@types/object-assign/-/object-assign-4.0.30.tgz#8949371d5a99f4381ee0f1df0a9b7a187e07e652"
integrity sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI=

"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
Expand Down Expand Up @@ -2662,7 +2652,7 @@ convert-source-map@^0.3.3:
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA=

cookie@^0.4.0, cookie@^0.4.1:
cookie@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
Expand Down Expand Up @@ -5511,7 +5501,14 @@ mkdirp@^1.0.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==

moment@^2.10.2:
moment-timezone@^0.5.31:
version "0.5.31"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05"
integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==
dependencies:
moment ">= 2.9.0"

"moment@>= 2.9.0", moment@^2.10.2:
version "2.27.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
Expand Down Expand Up @@ -5596,13 +5593,6 @@ neo-async@^2.5.0, neo-async@^2.6.1:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==

next-cookies@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/next-cookies/-/next-cookies-2.0.3.tgz#5a3eabcb6afa9b4d4ade69dfaaad749d16cd4a9a"
integrity sha512-YVCQzwZx+sz+KqLO4y9niHH9jjz6jajlEQbAKfsYVT6DOfngb/0k5l6vFK4rmpExVug96pGag8OBsdSRL9FZhQ==
dependencies:
universal-cookie "^4.0.2"

next-tick@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
Expand Down Expand Up @@ -8719,16 +8709,6 @@ unist-util-visit@^2.0.0:
unist-util-is "^4.0.0"
unist-util-visit-parents "^3.0.0"

universal-cookie@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.3.tgz#c2fa59127260e6ad21ef3e0cdd66ad453cbc41f6"
integrity sha512-YbEHRs7bYOBTIWedTR9koVEe2mXrq+xdjTJZcoKJK/pQaE6ni28ak2AKXFpevb+X6w3iU5SXzWDiJkmpDRb9qw==
dependencies:
"@types/cookie" "^0.3.3"
"@types/object-assign" "^4.0.30"
cookie "^0.4.0"
object-assign "^4.1.1"

unquote@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
Expand Down

0 comments on commit d81ee39

Please sign in to comment.