1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-07-03 19:06:40 -04:00

move some cruft from serverless API to client

This commit is contained in:
2021-07-09 14:40:23 -04:00
parent b411560a34
commit 523670fa22
9 changed files with 26 additions and 65 deletions

View File

@ -5,9 +5,6 @@ import * as Tracing from "@sentry/tracing"; // eslint-disable-line @typescript-e
import { VercelRequest, VercelResponse } from "@vercel/node"; import { VercelRequest, VercelResponse } from "@vercel/node";
import { Client, query as q } from "faunadb"; import { Client, query as q } from "faunadb";
import fetch from "node-fetch"; import fetch from "node-fetch";
import pluralize from "pluralize";
import numeral from "numeral";
import { DateTime } from "luxon";
import parser from "fast-xml-parser"; import parser from "fast-xml-parser";
import { decode } from "html-entities"; import { decode } from "html-entities";
@ -68,7 +65,7 @@ export default async (req: VercelRequest, res: VercelResponse) => {
} }
}; };
const incrementPageHits = async (slug: string | string[], client: Client) => { const incrementPageHits = async (slug: string | string[], client: Client): Promise<PageStats> => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = await client.query<any>( const result = await client.query<any>(
q.Let( q.Let(
@ -87,18 +84,11 @@ const incrementPageHits = async (slug: string | string[], client: Client) => {
) )
); );
// add formatted hits with comma and pluralized "hit(s)", simpler to do here than in browser
const hits: PageStats = {
...result.data,
pretty_hits: numeral(result.data.hits).format("0,0"),
pretty_unit: pluralize("hit", result.data.hits),
};
// send client the *new* hit count // send client the *new* hit count
return hits; return result.data;
}; };
const getSiteStats = async (client: Client) => { const getSiteStats = async (client: Client): Promise<OverallStats> => {
// get database and RSS results asynchronously // get database and RSS results asynchronously
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const [feed, result] = await Promise.all<{ [key: string]: any }, any>([ const [feed, result] = await Promise.all<{ [key: string]: any }, any>([
@ -123,13 +113,9 @@ const getSiteStats = async (client: Client) => {
if (match) { if (match) {
p.title = decode(match.title); p.title = decode(match.title);
p.url = match.link; p.url = match.link;
p.date = DateTime.fromRFC2822(match.pubDate).toISO(); p.date = new Date(match.pubDate).toISOString();
} }
// it's easier to add comma-separated numbers and proper pluralization here on the backend
p.pretty_hits = numeral(p.hits).format("0,0");
p.pretty_unit = pluralize("hit", p.hits);
// add these hits to running tally // add these hits to running tally
stats.total.hits += p.hits; stats.total.hits += p.hits;
@ -141,9 +127,5 @@ const getSiteStats = async (client: Client) => {
return a.hits > b.hits ? -1 : 1; return a.hits > b.hits ? -1 : 1;
}); });
// do same prettification as above to totals
stats.total.pretty_hits = numeral(stats.total.hits).format("0,0");
stats.total.pretty_unit = pluralize("hit", stats.total.hits);
return stats; return stats;
}; };

View File

@ -4,8 +4,6 @@ import * as Sentry from "@sentry/node";
import * as Tracing from "@sentry/tracing"; // eslint-disable-line @typescript-eslint/no-unused-vars import * as Tracing from "@sentry/tracing"; // eslint-disable-line @typescript-eslint/no-unused-vars
import { VercelRequest, VercelResponse } from "@vercel/node"; import { VercelRequest, VercelResponse } from "@vercel/node";
import { encode } from "html-entities"; import { encode } from "html-entities";
import { DateTime } from "luxon";
import numeral from "numeral";
import { GraphQLClient } from "graphql-request"; import { GraphQLClient } from "graphql-request";
import { gql } from "graphql-tag"; import { gql } from "graphql-tag";
@ -101,9 +99,6 @@ const fetchRepos = async (sort: string, limit: number): Promise<Repository[]> =>
({ node: repo }: { [key: string]: Repository }) => ({ ({ node: repo }: { [key: string]: Repository }) => ({
...repo, ...repo,
description: encode(repo.description), description: encode(repo.description),
stargazerCount_pretty: numeral(repo.stargazerCount).format("0,0"),
forkCount_pretty: numeral(repo.forkCount).format("0,0"),
pushedAt_relative: DateTime.fromISO(repo.pushedAt).toRelative({ locale: "en" }),
}) })
); );

4
api/types/hits.d.ts vendored
View File

@ -4,15 +4,11 @@ type PageStats = {
date?: string; date?: string;
slug: string; slug: string;
hits: number; hits: number;
pretty_hits: string;
pretty_unit: string;
}; };
type OverallStats = { type OverallStats = {
total: { total: {
hits: number; hits: number;
pretty_hits?: string;
pretty_unit?: string;
}; };
pages: PageStats[]; pages: PageStats[];
}; };

View File

@ -7,9 +7,6 @@ type Repository = {
name: string; name: string;
}; };
stargazerCount: number; stargazerCount: number;
stargazerCount_pretty?: string;
forkCount: number; forkCount: number;
forkCount_pretty?: string;
pushedAt: string; pushedAt: string;
pushedAt_relative?: string;
}; };

View File

@ -1,4 +1,5 @@
import fetch from "cross-fetch"; import fetch from "cross-fetch";
import numeral from "numeral";
import * as queryString from "query-string"; import * as queryString from "query-string";
// don't continue if there isn't a span#meta-hits element on this page // don't continue if there isn't a span#meta-hits element on this page
@ -18,14 +19,17 @@ if (wrapper) {
fetch(queryString.stringifyUrl({ url: "/api/hits/", query: { slug: slug } })) fetch(queryString.stringifyUrl({ url: "/api/hits/", query: { slug: slug } }))
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
if (typeof data.hits !== "undefined") { if (data.hits) {
// finally inject the hits and hide the loading spinner // finally inject the hits and hide the loading spinner
const spinner = document.getElementById("hit-spinner"); const spinner = document.getElementById("hit-spinner");
const counter = document.getElementById("hit-counter"); const counter = document.getElementById("hit-counter");
const hitsComma = numeral(data.hits).format("0,0");
const hitsPlural = data.hits === 1 ? "hit" : "hits";
if (spinner) spinner.style.display = "none"; if (spinner) spinner.style.display = "none";
if (counter) counter.appendChild(document.createTextNode(data.pretty_hits)); if (counter) counter.appendChild(document.createTextNode(hitsComma));
wrapper.title = data.pretty_hits + " " + data.pretty_unit; wrapper.title = hitsComma + " " + hitsPlural;
} else { } else {
// something went horribly wrong, initiate coverup // something went horribly wrong, initiate coverup
wrapper.style.display = "none"; wrapper.style.display = "none";

View File

@ -1,9 +1,14 @@
import fetch from "cross-fetch"; import fetch from "cross-fetch";
import numeral from "numeral";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime.js";
// don't continue if there isn't a span#meta-hits element on this page // don't continue if there isn't a span#meta-hits element on this page
const wrapper = document.getElementById("github-cards"); const wrapper = document.getElementById("github-cards");
if (wrapper) { if (wrapper) {
dayjs.extend(relativeTime);
fetch("/api/projects/?top") fetch("/api/projects/?top")
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((data) => {
@ -24,7 +29,7 @@ if (wrapper) {
html += ` html += `
<div class="repo-meta"> <div class="repo-meta">
<svg viewBox="0 0 16 16" height="16" width="16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg> <svg viewBox="0 0 16 16" height="16" width="16"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
<span>${repo.stargazerCount_pretty}</span> <span>${numeral(repo.stargazerCount).format("0,0")}</span>
</div>`; </div>`;
} }
@ -32,13 +37,13 @@ if (wrapper) {
html += ` html += `
<div class="repo-meta"> <div class="repo-meta">
<svg viewBox="0 0 16 16" height="16" width="16"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg> <svg viewBox="0 0 16 16" height="16" width="16"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
<span>${repo.forkCount_pretty}</span> <span>${numeral(repo.forkCount).format("0,0")}</span>
</div>`; </div>`;
} }
html += ` html += `
<div class="repo-meta"> <div class="repo-meta">
<span title="${repo.pushedAt}">Updated ${repo.pushedAt_relative}</span> <span title="${dayjs(repo.pushedAt).format("MMM D, YYYY h:mm A")}">Updated ${dayjs(repo.pushedAt).fromNow()}</span>
</div>`; </div>`;
const div = document.createElement("div"); const div = document.createElement("div");

View File

@ -12,7 +12,7 @@ import imageminSvgo from "imagemin-svgo";
gulp.task("default", gulp.series( gulp.task("default", gulp.series(
clean, clean,
npx("webpack", ["--mode", "production", "--progress", "profile"]), npx("webpack", ["--mode", "production"]),
npx("hugo"), npx("hugo"),
gulp.parallel( gulp.parallel(
optimizeHtml, optimizeHtml,

View File

@ -35,6 +35,7 @@
"@sentry/node": "^6.8.0", "@sentry/node": "^6.8.0",
"@sentry/tracing": "^6.8.0", "@sentry/tracing": "^6.8.0",
"cross-fetch": "3.1.4", "cross-fetch": "3.1.4",
"dayjs": "1.10.6",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"fast-xml-parser": "^3.19.0", "fast-xml-parser": "^3.19.0",
"faunadb": "^4.3.0", "faunadb": "^4.3.0",
@ -42,11 +43,9 @@
"graphql-request": "^3.4.0", "graphql-request": "^3.4.0",
"graphql-tag": "^2.12.5", "graphql-tag": "^2.12.5",
"html-entities": "^2.3.2", "html-entities": "^2.3.2",
"luxon": "^1.27.0",
"modern-normalize": "1.1.0", "modern-normalize": "1.1.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"pluralize": "^8.0.0",
"query-string": "7.0.1", "query-string": "7.0.1",
"twemoji": "13.1.0", "twemoji": "13.1.0",
"twemoji-emojis": "14.1.0" "twemoji-emojis": "14.1.0"
@ -55,10 +54,8 @@
"@babel/cli": "^7.14.5", "@babel/cli": "^7.14.5",
"@babel/core": "^7.14.6", "@babel/core": "^7.14.6",
"@babel/preset-env": "^7.14.7", "@babel/preset-env": "^7.14.7",
"@types/luxon": "^1.27.1",
"@types/node-fetch": "^2.5.11", "@types/node-fetch": "^2.5.11",
"@types/numeral": "^2.0.1", "@types/numeral": "^2.0.1",
"@types/pluralize": "^0.0.29",
"@types/twemoji": "^12.1.1", "@types/twemoji": "^12.1.1",
"@typescript-eslint/eslint-plugin": "^4.28.2", "@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2", "@typescript-eslint/parser": "^4.28.2",

View File

@ -1176,11 +1176,6 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/luxon@^1.27.1":
version "1.27.1"
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096"
integrity sha512-cPiXpOvPFDr2edMnOXlz3UBDApwUfR+cpizvxCy0n3vp9bz/qe8BWzHPIEFcy+ogUOyjKuCISgyq77ELZPmkkg==
"@types/mdast@^3.0.0": "@types/mdast@^3.0.0":
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.4.tgz#8ee6b5200751b6cadb9a043ca39612693ad6cb9e" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.4.tgz#8ee6b5200751b6cadb9a043ca39612693ad6cb9e"
@ -1226,11 +1221,6 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/pluralize@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/pluralize/-/pluralize-0.0.29.tgz#6ffa33ed1fc8813c469b859681d09707eb40d03c"
integrity sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA==
"@types/q@^1.5.1": "@types/q@^1.5.1":
version "1.5.5" version "1.5.5"
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
@ -3090,6 +3080,11 @@ d@1, d@^1.0.1:
es5-ext "^0.10.50" es5-ext "^0.10.50"
type "^1.0.1" type "^1.0.1"
dayjs@1.10.6:
version "1.10.6"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63"
integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -6590,11 +6585,6 @@ lru_map@^0.3.3:
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=
luxon@^1.27.0:
version "1.27.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.27.0.tgz#ae10c69113d85dab8f15f5e8390d0cbeddf4f00f"
integrity sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA==
make-dir@^1.0.0, make-dir@^1.2.0: make-dir@^1.0.0, make-dir@^1.2.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@ -7844,11 +7834,6 @@ plur@^3.0.1:
dependencies: dependencies:
irregular-plurals "^2.0.0" irregular-plurals "^2.0.0"
pluralize@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
pngquant-bin@^6.0.0: pngquant-bin@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/pngquant-bin/-/pngquant-bin-6.0.0.tgz#aff0d7e61095feb96ced379ad8c7294ad3dd1712" resolved "https://registry.yarnpkg.com/pngquant-bin/-/pngquant-bin-6.0.0.tgz#aff0d7e61095feb96ced379ad8c7294ad3dd1712"