backend & types

This commit is contained in:
2024-01-27 18:58:35 +00:00
parent 91bfa0864d
commit 0b4a474180
219 changed files with 5211 additions and 12325 deletions

View File

@@ -6,44 +6,15 @@ const { apiSsrBaseURL, ssrPublishCheckCollections } = require("../config")
*
* @param {string} cacheKey
* @param {string} endpoint
* @param {string} query
* @param {ApiOptions} options
* @returns {ApiResult<any>}
* @returns {ApiResult}
*/
function ssrRequest(cacheKey, endpoint, query, options) {
let url = endpoint + (query ? "?" + query : "")
if (ssrPublishCheckCollections.includes(endpoint)) {
// @ts-ignore
let validUntil = context.ssrCacheValidUntil
// check in db for publish date to invalidate cache
const _optionsPublishSearch = Object.assign(
{},
{ filter: options?.filter },
{
selector: { publishDate: 1 },
// projection: null,
}
)
const publishSearch = context.db.find(endpoint, _optionsPublishSearch)
publishSearch?.forEach((item) => {
const publishDate = item.publishDate ? new Date(item.publishDate.unixMilli()) : null
if (publishDate && publishDate > new Date()) {
// entry has a publish date in the future, set global validUntil
if (validUntil == null || validUntil > publishDate) {
validUntil = publishDate
}
}
})
// @ts-ignore
context.ssrCacheValidUntil = validUntil
}
// console.log("############ FETCHING ", apiSsrBaseURL + url)
const response = context.http.fetch(apiSsrBaseURL + url, {
const response = context.http.fetch(apiSsrBaseURL + "/" + url, {
method: options.method,
headers: options.headers,
})

View File

@@ -34,7 +34,10 @@ function obj2str(obj) {
// fetch polyfill
// [MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com/)
const _f = function (url, options) {
const _f = function (
/** @type {string | URL} */ url,
/** @type {{ method?: any; credentials?: any; headers?: any; body?: any; }} */ options
) {
if (typeof XMLHttpRequest === "undefined") {
return Promise.resolve(null)
}
@@ -99,14 +102,18 @@ const _fetch = typeof fetch === "undefined" ? (typeof window === "undefined" ? _
*
* @param {string} endpoint
* @param {ApiOptions} options
* @returns {Promise<ApiResult>}
* @param {any} body
* @returns {Promise<ApiResult<any>>}
*/
function apiRequest(endpoint, options) {
function apiRequest(endpoint, options, body) {
// TODO cache only for GET
// first check cache if on client
const cacheKey = obj2str({ endpoint: endpoint, options: options })
options.method = options?.method || "GET"
// @ts-ignore
if (typeof window !== "undefined" && window.__SSR_CACHE__) {
if (typeof window !== "undefined" && window.__SSR_CACHE__ && options?.method === "GET") {
// @ts-ignore
const cache = window.__SSR_CACHE__[cacheKey]
console.log("SSR:", cacheKey, cache)
@@ -115,7 +122,7 @@ function apiRequest(endpoint, options) {
}
}
let method = "GET"
let method = options?.method || "GET"
let query = "&count=1"
if (options?.filter) query += "&filter=" + encodeURIComponent(JSON.stringify(options.filter))
@@ -137,7 +144,7 @@ function apiRequest(endpoint, options) {
if (options?.headers) headers = { ...headers, ...options.headers }
if (typeof window === "undefined") {
if (typeof window === "undefined" && method === "GET") {
// server
// reference via context from get hook to tree shake in client
@@ -147,12 +154,18 @@ function apiRequest(endpoint, options) {
} else {
// client
let url = endpoint + (query ? "?" + query : "")
console.log("fetch", apiClientBaseURL + url)
return _fetch(apiClientBaseURL + url, {
console.log("URL:", url)
const requestOptions = {
method,
mode: "cors",
headers,
}).then((response) => {
}
if (method === "POST" || method === "PUT") {
requestOptions.body = JSON.stringify(body)
}
return _fetch(apiClientBaseURL + url, requestOptions).then((response) => {
return response?.json().then((json) => {
if (response?.status < 200 || response?.status >= 400) {
return Promise.reject({ response, data: json })

View File

@@ -6,6 +6,39 @@ function log(str) {
console.log(JSON.stringify(str, undefined, 4))
}
/**
* convert object to string
* @param {any} obj object
*/
function obj2str(obj) {
if (Array.isArray(obj)) {
return JSON.stringify(
obj.map(function (idx) {
return obj2str(idx)
})
)
} else if (typeof obj === "object" && obj !== null) {
var elements = Object.keys(obj)
.sort()
.map(function (key) {
var val = obj2str(obj[key])
if (val) {
return key + ":" + val
}
})
var elementsCleaned = []
for (var i = 0; i < elements.length; i++) {
if (elements[i]) elementsCleaned.push(elements[i])
}
return "{" + elementsCleaned.join("|") + "}"
}
if (obj) return obj
}
var { LIGHTHOUSE_TOKEN } = require("../config")
/**
* clear SSR cache
*/
@@ -14,7 +47,103 @@ function clearSSRCache() {
context.response.header("X-SSR-Cleared", info.removed)
}
function calculateAverageDynamically(dbObjs) {
const sumObj = {}
let count = 0
dbObjs.forEach((obj) => {
accumulate(obj, sumObj)
count++
})
function accumulate(sourceObj, targetObj) {
for (const key in sourceObj) {
if (typeof sourceObj[key] === "number") {
targetObj[key] = (targetObj[key] || 0) + sourceObj[key]
} else if (typeof sourceObj[key] === "object" && sourceObj[key] !== null) {
targetObj[key] = targetObj[key] || {}
accumulate(sourceObj[key], targetObj[key])
}
}
}
function average(targetObj) {
for (const key in targetObj) {
if (typeof targetObj[key] === "number") {
targetObj[key] = targetObj[key] / count
} else if (typeof targetObj[key] === "object") {
average(targetObj[key])
}
}
}
average(sumObj)
return sumObj
}
function run(url) {
const response = context.http
.fetch(url, {
timeout: 300,
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.body.json()
// needs enough traffic to be collected
const cruxMetrics = {
"First Contentful Paint": response?.loadingExperience?.metrics?.FIRST_CONTENTFUL_PAINT_MS?.category,
"First Input Delay": response?.loadingExperience?.metrics?.FIRST_INPUT_DELAY_MS?.category,
}
const lighthouse = response.lighthouseResult
const lighthouseMetrics = {
FCPS: lighthouse.audits["first-contentful-paint"].score * 100,
FCPV: lighthouse.audits["first-contentful-paint"].numericValue / 1000,
FMPS: lighthouse.audits["first-meaningful-paint"].score * 100,
FMPV: lighthouse.audits["first-meaningful-paint"].numericValue / 1000,
SIS: lighthouse.audits["speed-index"].score * 100,
SIV: lighthouse.audits["speed-index"].numericValue / 1000,
TTIS: lighthouse.audits["interactive"].score * 100,
TTIV: lighthouse.audits["interactive"].numericValue / 1000,
FPIDS: lighthouse.audits["max-potential-fid"].score * 100,
FPIDV: lighthouse.audits["max-potential-fid"].numericValue / 1000,
}
let dbObject = {
cruxMetrics,
lighthouseMetrics,
performance: Math.round(lighthouse.categories.performance.score * 100),
accessibility: Math.round(lighthouse.categories.accessibility.score * 100),
bestPractices: Math.round(lighthouse.categories["best-practices"].score * 100),
seo: Math.round(lighthouse.categories.seo.score * 100),
}
return dbObject
}
function setUpQuery(subPath = "/") {
const api = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed"
let params = `category=performance&category=accessibility&category=best-practices&category=seo`
const parameters = {
url: encodeURIComponent(`https://allkids-erfurt.de/${subPath}`),
key: LIGHTHOUSE_TOKEN,
}
let query = `${api}?`
for (let key in parameters) {
query += `${key}=${parameters[key]}&`
}
query += params // Append other parameters without URL encoding
return query
}
module.exports = {
log,
clearSSRCache,
obj2str,
run,
setUpQuery,
calculateAverageDynamically,
}