This PR mainly refactors the code by extracting a "core" package which is then used by the "backend" and "frontend" apps. Apart from this the PR also contains several smaller changes like fixing dependabot, upgrading dependencies, aligned "package.json" files, added tests, ... addresses step 1 of #32 closes #136 --------- Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
226 lines
6.7 KiB
JavaScript
226 lines
6.7 KiB
JavaScript
import {
|
|
api,
|
|
gist,
|
|
pin,
|
|
topLangs,
|
|
wakatime,
|
|
} from "@stats-organization/github-readme-stats-core";
|
|
|
|
import { default as authenticate } from "./api-renamed/authenticate.js";
|
|
import { default as deleteUser } from "./api-renamed/delete-user.js";
|
|
import { default as downgrade } from "./api-renamed/downgrade.js";
|
|
import { default as repeatRecent } from "./api-renamed/repeat-recent.js";
|
|
import { default as patInfo } from "./api-renamed/status/pat-info.js";
|
|
import { default as statusUp } from "./api-renamed/status/up.js";
|
|
import { default as userAccess } from "./api-renamed/user-access.js";
|
|
import { default as wakatimeProxy } from "./api-renamed/wakatime-proxy.js";
|
|
import { guardAccess } from "./src/common/access.js";
|
|
import {
|
|
CACHE_TTL,
|
|
resolveCacheSeconds,
|
|
setCacheHeaders,
|
|
setErrorCacheHeaders,
|
|
} from "./src/common/cache.js";
|
|
import { getUserAccessByName, storeRequest } from "./src/common/database.js";
|
|
|
|
const getGuardResult = (query, type, id) => {
|
|
const access = guardAccess({
|
|
id,
|
|
type,
|
|
colors: {
|
|
title_color: query.title_color,
|
|
text_color: query.text_color,
|
|
bg_color: query.bg_color,
|
|
border_color: query.border_color,
|
|
theme: query.theme,
|
|
},
|
|
});
|
|
|
|
if (access.isPassed) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
status: "error - permanent",
|
|
content: access.result,
|
|
};
|
|
};
|
|
|
|
const getUserPat = async (username) => {
|
|
if (!username) {
|
|
return null;
|
|
}
|
|
|
|
const userAccess = await getUserAccessByName(username);
|
|
if (!userAccess?.token) {
|
|
return null;
|
|
}
|
|
|
|
return userAccess.token;
|
|
};
|
|
|
|
export default async (req, res) => {
|
|
const url = new URL(req.url, "https://localhost");
|
|
if (res.send === undefined) {
|
|
// remaining code expects express.js-like request and response objects
|
|
res.send = function (data) {
|
|
if (typeof data === "object") {
|
|
res.setHeader("Content-Type", "application/json");
|
|
res.end(JSON.stringify(data));
|
|
} else if (typeof data === "string") {
|
|
res.end(data);
|
|
} else {
|
|
res.end(String(data));
|
|
}
|
|
};
|
|
req.query = Object.fromEntries(url.searchParams.entries());
|
|
}
|
|
|
|
let result;
|
|
|
|
switch (url.pathname) {
|
|
case "/api": {
|
|
result = getGuardResult(req.query, "username", req.query.username);
|
|
if (!result) {
|
|
const userPat = await getUserPat(req.query.username);
|
|
result = await api(req.query, userPat);
|
|
}
|
|
if (result.status === "error - temporary") {
|
|
setErrorCacheHeaders(res);
|
|
} else {
|
|
const cacheSeconds = resolveCacheSeconds({
|
|
requested: parseInt(req.query.cache_seconds, 10),
|
|
def: CACHE_TTL.STATS_CARD.DEFAULT,
|
|
min: CACHE_TTL.STATS_CARD.MIN,
|
|
max: CACHE_TTL.STATS_CARD.MAX,
|
|
});
|
|
setCacheHeaders(res, cacheSeconds);
|
|
}
|
|
res.setHeader("Content-Type", "image/svg+xml");
|
|
res.end(result.content);
|
|
if (result.status !== "error - permanent") {
|
|
await storeRequest(req);
|
|
}
|
|
break;
|
|
}
|
|
case "/api/gist":
|
|
result =
|
|
getGuardResult(req.query, "gist", req.query.id) ??
|
|
(await gist(req.query));
|
|
if (result.status === "error - temporary") {
|
|
setErrorCacheHeaders(res);
|
|
} else {
|
|
const cacheSeconds = resolveCacheSeconds({
|
|
requested: parseInt(req.query.cache_seconds, 10),
|
|
def: CACHE_TTL.GIST_CARD.DEFAULT,
|
|
min: CACHE_TTL.GIST_CARD.MIN,
|
|
max: CACHE_TTL.GIST_CARD.MAX,
|
|
});
|
|
setCacheHeaders(res, cacheSeconds);
|
|
}
|
|
res.setHeader("Content-Type", "image/svg+xml");
|
|
res.end(result.content);
|
|
if (result.status !== "error - permanent") {
|
|
await storeRequest(req);
|
|
}
|
|
break;
|
|
case "/api/pin": {
|
|
result = getGuardResult(req.query, "username", req.query.username);
|
|
if (!result) {
|
|
const userPat = await getUserPat(req.query.username);
|
|
result = await pin(req.query, userPat);
|
|
}
|
|
if (result.status === "error - temporary") {
|
|
setErrorCacheHeaders(res);
|
|
} else {
|
|
const cacheSeconds = resolveCacheSeconds({
|
|
requested: parseInt(req.query.cache_seconds, 10),
|
|
def: CACHE_TTL.PIN_CARD.DEFAULT,
|
|
min: CACHE_TTL.PIN_CARD.MIN,
|
|
max: CACHE_TTL.PIN_CARD.MAX,
|
|
});
|
|
setCacheHeaders(res, cacheSeconds);
|
|
}
|
|
res.setHeader("Content-Type", "image/svg+xml");
|
|
res.end(result.content);
|
|
if (result.status !== "error - permanent") {
|
|
await storeRequest(req);
|
|
}
|
|
break;
|
|
}
|
|
case "/api/top-langs": {
|
|
result = getGuardResult(req.query, "username", req.query.username);
|
|
if (!result) {
|
|
const userPat = await getUserPat(req.query.username);
|
|
result = await topLangs(req.query, userPat);
|
|
}
|
|
if (result.status === "error - temporary") {
|
|
setErrorCacheHeaders(res);
|
|
} else {
|
|
const cacheSeconds = resolveCacheSeconds({
|
|
requested: parseInt(req.query.cache_seconds, 10),
|
|
def: CACHE_TTL.TOP_LANGS_CARD.DEFAULT,
|
|
min: CACHE_TTL.TOP_LANGS_CARD.MIN,
|
|
max: CACHE_TTL.TOP_LANGS_CARD.MAX,
|
|
});
|
|
setCacheHeaders(res, cacheSeconds);
|
|
}
|
|
res.setHeader("Content-Type", "image/svg+xml");
|
|
res.end(result.content);
|
|
if (result.status !== "error - permanent") {
|
|
await storeRequest(req);
|
|
}
|
|
break;
|
|
}
|
|
case "/api/wakatime":
|
|
result =
|
|
getGuardResult(req.query, "wakatime", req.query.username) ??
|
|
(await wakatime(req.query));
|
|
if (result.status === "error - temporary") {
|
|
setErrorCacheHeaders(res);
|
|
} else {
|
|
const cacheSeconds = resolveCacheSeconds({
|
|
requested: parseInt(req.query.cache_seconds, 10),
|
|
def: CACHE_TTL.WAKATIME_CARD.DEFAULT,
|
|
min: CACHE_TTL.WAKATIME_CARD.MIN,
|
|
max: CACHE_TTL.WAKATIME_CARD.MAX,
|
|
});
|
|
setCacheHeaders(res, cacheSeconds);
|
|
}
|
|
res.setHeader("Content-Type", "image/svg+xml");
|
|
res.end(result.content);
|
|
if (result.status !== "error - permanent") {
|
|
await storeRequest(req);
|
|
}
|
|
break;
|
|
case "/api/wakatime-proxy":
|
|
await wakatimeProxy(req, res);
|
|
break;
|
|
case "/api/repeat-recent":
|
|
await repeatRecent(req, res);
|
|
break;
|
|
case "/api/status/pat-info":
|
|
await patInfo(req, res);
|
|
break;
|
|
case "/api/status/up":
|
|
await statusUp(req, res);
|
|
break;
|
|
case "/api/authenticate":
|
|
await authenticate(req, res);
|
|
break;
|
|
case "/api/delete-user":
|
|
await deleteUser(req, res);
|
|
break;
|
|
case "/api/user-access":
|
|
await userAccess(req, res);
|
|
break;
|
|
case "/api/downgrade":
|
|
await downgrade(req, res);
|
|
break;
|
|
default:
|
|
res.statusCode = 404;
|
|
res.end("Not Found");
|
|
break;
|
|
}
|
|
};
|