generated from cms/tibi-docs
backend & types
This commit is contained in:
92
api/hooks/backups/post_create.js
Normal file
92
api/hooks/backups/post_create.js
Normal file
@@ -0,0 +1,92 @@
|
||||
;(function () {
|
||||
let backup = context.data
|
||||
const updateLogs = compareAndUpdateEntry(backup?.entry, backup?.collectionName, backup?.versionNr - 1)
|
||||
backup.updateLogs = updateLogs
|
||||
return { data: backup }
|
||||
|
||||
function compareAndUpdateEntry(entry, collectionName, versionNr) {
|
||||
let updateLogs
|
||||
if (versionNr == 0) {
|
||||
updateLogs = getUpdateLogs({}, entry)
|
||||
}
|
||||
delete entry.updateTime
|
||||
let previousEntry = context.db.find("backups", {
|
||||
filter: { entryId: context.data.entryId, versionNr, collectionName },
|
||||
})[0]
|
||||
if (!previousEntry) {
|
||||
console.error("No previous entry found")
|
||||
updateLogs = getUpdateLogs({}, entry)
|
||||
} else {
|
||||
delete previousEntry.updateTime
|
||||
updateLogs = getUpdateLogs(previousEntry.entry, entry)
|
||||
}
|
||||
console.log(context.json.stringify(previousEntry?.entry), context.json.stringify(entry))
|
||||
|
||||
return updateLogs
|
||||
}
|
||||
|
||||
function filterValidObjects(array) {
|
||||
return array.filter((object) => {
|
||||
for (let key in object) {
|
||||
if (typeof object[key] !== "string" && object[key] !== null) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
function getUpdateLogs(oldObj = {}, newObj = {}, path = "") {
|
||||
let updateLogs = []
|
||||
const ignoredKeys = ["id", "insertTime", "updateTime"]
|
||||
const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)])
|
||||
|
||||
allKeys.forEach((key) => {
|
||||
if (ignoredKeys.includes(key)) return
|
||||
|
||||
const newPath = path ? `${path}.${key}` : key
|
||||
const oldVal = oldObj.hasOwnProperty(key) ? oldObj[key] : ""
|
||||
const newVal = newObj.hasOwnProperty(key) ? newObj[key] : ""
|
||||
|
||||
// Handle Arrays
|
||||
if (Array.isArray(oldVal) || Array.isArray(newVal)) {
|
||||
const oldArr = oldVal || []
|
||||
const newArr = newVal || []
|
||||
|
||||
for (let i = 0; i < Math.max(oldArr.length, newArr.length); i++) {
|
||||
const itemPath = `${newPath}[${i}]`
|
||||
|
||||
if (oldArr[i] !== newArr[i]) {
|
||||
if (typeof oldArr[i] === "object" || typeof newArr[i] === "object") {
|
||||
const arrUpdates = getUpdateLogs(oldArr[i], newArr[i], itemPath)
|
||||
updateLogs = updateLogs.concat(arrUpdates)
|
||||
} else {
|
||||
updateLogs.push({
|
||||
field: itemPath,
|
||||
previous: oldArr[i] === undefined ? "" : oldArr[i].toString(),
|
||||
current: newArr[i] === undefined ? "" : newArr[i].toString(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle nested objects but not arrays
|
||||
else if (
|
||||
(typeof oldVal === "object" && oldVal !== null) ||
|
||||
(typeof newVal === "object" && newVal !== null)
|
||||
) {
|
||||
const nestedUpdates = getUpdateLogs(oldVal || {}, newVal || {}, newPath)
|
||||
updateLogs = updateLogs.concat(nestedUpdates)
|
||||
}
|
||||
// Handle primitive types
|
||||
else if (oldVal !== newVal) {
|
||||
updateLogs.push({
|
||||
field: newPath,
|
||||
previous: oldVal.toString(),
|
||||
current: newVal.toString(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return filterValidObjects(updateLogs)
|
||||
}
|
||||
})()
|
||||
5
api/hooks/clear_cache.js
Normal file
5
api/hooks/clear_cache.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var utils = require("./lib/utils")
|
||||
|
||||
;(function () {
|
||||
utils.clearSSRCache()
|
||||
})()
|
||||
@@ -1,6 +1,5 @@
|
||||
const release = "tibi-docs.dirty"
|
||||
const apiClientBaseURL = "/api/"
|
||||
|
||||
// @ts-ignore
|
||||
if (release && typeof context !== "undefined") {
|
||||
context.response.header("X-Release", release)
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
const publishedFilter = {
|
||||
$or: [
|
||||
{ publishDate: { $exists: false } },
|
||||
{ publishDate: null },
|
||||
{
|
||||
publishDate: { $lte: { $date: new Date().toISOString() } },
|
||||
// publishDate: { $lte: new Date() },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const apiSsrBaseURL = "http://localhost:8080/api/v1/_/demo/"
|
||||
const apiSsrBaseURL = "http://localhost:8080/api/v1/_/allkids_erfurt"
|
||||
|
||||
module.exports = {
|
||||
publishedFilter,
|
||||
apiSsrBaseURL,
|
||||
ssrValidatePath: function (/** @type {string} */ path) {
|
||||
ssrValidatePath: function (path) {
|
||||
// validate if path ssr rendering is ok, -1 = NOTFOUND, 0 = NO SSR, 1 = SSR
|
||||
// pe. use context.readCollection("product", {filter: {path: path}}) ... to validate dynamic urls
|
||||
|
||||
@@ -22,16 +10,16 @@ module.exports = {
|
||||
// if (path == "/") return 1
|
||||
|
||||
// // all other sites are in db
|
||||
// path = path?.replace(/^\//, "")
|
||||
|
||||
// filter for path or alternativePaths
|
||||
//path = path?.replace(/^\//, "")
|
||||
console.log("PATH:", path)
|
||||
const resp = context.db.find("content", {
|
||||
filter: {
|
||||
$and: [{ $or: [{ path }, { "alternativePaths.path": path }] }, publishedFilter],
|
||||
$and: [{ path }],
|
||||
},
|
||||
|
||||
selector: { _id: 1 },
|
||||
})
|
||||
console.log("RESP:", resp?.length)
|
||||
if (resp && resp.length) {
|
||||
return 1
|
||||
}
|
||||
@@ -40,4 +28,5 @@ module.exports = {
|
||||
return -1
|
||||
},
|
||||
ssrPublishCheckCollections: ["content"],
|
||||
LIGHTHOUSE_TOKEN: "AIzaSyC0UxHp3-MpJiDL3ws7pEV6lj57bfIc7GQ",
|
||||
}
|
||||
|
||||
15
api/hooks/forms/post_create.js
Normal file
15
api/hooks/forms/post_create.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const { validateFields } = require("./validateFields")
|
||||
;(function () {
|
||||
if (context?.data?.formular?.honey) {
|
||||
throw { status: 400, error: "Bot detection" }
|
||||
}
|
||||
delete context.data.formular.honey
|
||||
/**
|
||||
* @type {Array<[string, FormObj]>}
|
||||
*/
|
||||
let values = Object.entries(context?.data?.formular)
|
||||
let validation = validateFields(values)
|
||||
if (validation.length) {
|
||||
throw { status: 400, error: validation }
|
||||
}
|
||||
})()
|
||||
79
api/hooks/forms/post_return.js
Normal file
79
api/hooks/forms/post_return.js
Normal file
@@ -0,0 +1,79 @@
|
||||
;(function () {
|
||||
/** @type {FormObj} */
|
||||
let formular = context?.data?.formular
|
||||
|
||||
/** @type {TempFormBefore | TempFormAfter} */
|
||||
let tempForm = {}
|
||||
|
||||
let formTitle = `${formular.formTitle}`
|
||||
delete formular.formTitle
|
||||
delete formular["agreement"]
|
||||
delete formular["honey"]
|
||||
|
||||
formular?.formRows?.forEach(
|
||||
/** @param {string} rowName */
|
||||
(rowName) => {
|
||||
tempForm[rowName] = {}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {[string, any]} e
|
||||
* @returns {any}
|
||||
*/
|
||||
let getValue = (e) => {
|
||||
if (e[0].includes("numberLabel")) return e[1][0]
|
||||
return e[1]
|
||||
}
|
||||
|
||||
/** @type {TempFormIndices} */
|
||||
let indices = {}
|
||||
|
||||
Object.entries(formular).forEach((e) => {
|
||||
if (e[0] === "formRows") return
|
||||
|
||||
let key = e[0].split("_")
|
||||
let newKey = key[key.length - 1]
|
||||
|
||||
// @ts-ignore
|
||||
let rowName = formular?.formRows[Number(key[key.length - 2])]
|
||||
let index = isNaN(Number(key[key.length - 3])) ? 100 : Number(key[key.length - 3])
|
||||
|
||||
if (!rowName) return
|
||||
if (!tempForm[rowName]) tempForm[rowName] = {}
|
||||
|
||||
// @ts-ignore
|
||||
if (tempForm[rowName][newKey]) {
|
||||
// @ts-ignore
|
||||
tempForm[rowName][newKey][0] += "\\" + getValue(e)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
tempForm[rowName][newKey] = [getValue(e), newKey[0] === "n", newKey.includes("invalid")]
|
||||
indices[rowName] = { ...indices[rowName], [newKey]: index }
|
||||
}
|
||||
})
|
||||
|
||||
// Now iterate over tempForm to sort fields in each row
|
||||
Object.entries(tempForm).forEach(([rowName, fields]) => {
|
||||
tempForm[rowName] = Object.entries(fields)
|
||||
.sort((a, b) => indices[rowName][a[0]] - indices[rowName][b[0]])
|
||||
.reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {})
|
||||
})
|
||||
|
||||
Object.keys(tempForm).forEach((row) => {
|
||||
tempForm[row] = Object.entries(tempForm[row])
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
delete tempForm[undefined]
|
||||
context.smtp.sendMail({
|
||||
to: "allkids.erfurt@gmail.com",
|
||||
from: "mail@webmakers.de",
|
||||
subject: "AllKids " + formTitle,
|
||||
html: context.tpl.execute(context.fs.readFile("templates/form_mail.html"), {
|
||||
context: context,
|
||||
formularRows: Object.entries(tempForm),
|
||||
formTitle: formTitle,
|
||||
}),
|
||||
})
|
||||
})()
|
||||
80
api/hooks/forms/validateFields.js
Normal file
80
api/hooks/forms/validateFields.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @param {Array<[string, FormObj]>} fieldsArray
|
||||
* @returns {(string | (() => void))[][]}
|
||||
*/
|
||||
function validateFields(fieldsArray) {
|
||||
/**@type {(string | (() => void))[][]} */
|
||||
const errors = []
|
||||
/**@type {number} */
|
||||
let selectedGroup
|
||||
|
||||
const numberRegex = /^[+]?([.]\d+|\d+([.]\d+)?)$/
|
||||
const emailRegex =
|
||||
/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/
|
||||
const dateRegex = /^\d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])$/
|
||||
const timeRegex = /^\d{1,2}:\d{2}-\d{1,2}:\d{2}$/
|
||||
const phoneRegex = /^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*$/
|
||||
|
||||
const wholeBlockInvalid = () => {
|
||||
const blockContainer = document.getElementsByClassName("blockContainer")[0]
|
||||
blockContainer.classList.add("invalidBlocks")
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} value
|
||||
* @param {any} field
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
const validateNumber = (value, field, element) => {
|
||||
if (!numberRegex.test(`${value}`)) {
|
||||
errors.push(["block", () => element.classList.add("border-red")])
|
||||
}
|
||||
}
|
||||
|
||||
fieldsArray.forEach(([field, value]) => {
|
||||
if (field === "blockGroups" || field.includes("numberLabel")) {
|
||||
if (!field.includes("numberLabel")) return
|
||||
// @ts-ignore
|
||||
const [elementValue, element, group] = value
|
||||
|
||||
if (!elementValue) return
|
||||
|
||||
if (selectedGroup !== undefined) {
|
||||
if (group !== selectedGroup) {
|
||||
errors.push(["block", wholeBlockInvalid])
|
||||
} else {
|
||||
validateNumber(elementValue, field, element)
|
||||
}
|
||||
} else {
|
||||
selectedGroup = group
|
||||
validateNumber(elementValue, field, element)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
errors.push(["Eingabe ist erforderlich.", field])
|
||||
} else if (field.includes("number_")) {
|
||||
if (!numberRegex.test(`${value}`)) {
|
||||
errors.push(["Ungültiger numerischer Wert.", field])
|
||||
}
|
||||
} else if (field.includes("agreement_")) {
|
||||
if (value !== true) errors.push(["Bitte das Kontrollkästchen anklicken.", field])
|
||||
} else if (field.includes("Email_")) {
|
||||
if (!emailRegex.test(value)) errors.push(["Ungültiges E-Mail-Format.", field])
|
||||
} else if (field.includes("date_")) {
|
||||
if (!dateRegex.test(value)) errors.push(["Ungültiges Datumsformat.", field])
|
||||
} else if (field.includes("times_")) {
|
||||
if (!timeRegex.test(value)) errors.push(["Ungültiges Zeitformat.", field])
|
||||
} else if (field.includes("Telefon_")) {
|
||||
if (!phoneRegex.test(value)) errors.push(["Ungültiges Telefonnummernformat.", field])
|
||||
}
|
||||
})
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validateFields,
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
16
api/hooks/lighthouse/post_create.js
Normal file
16
api/hooks/lighthouse/post_create.js
Normal file
@@ -0,0 +1,16 @@
|
||||
var { setUpQuery, calculateAverageDynamically, run } = require("../lib/utils")
|
||||
;(function () {
|
||||
let subPaths = context.db.find("lighthouseSubpath")
|
||||
let urls = []
|
||||
for (let i = 0; i < subPaths.length; i++) {
|
||||
urls.push(setUpQuery(subPaths[i].lighthouseSubpath))
|
||||
}
|
||||
let dbObjs = []
|
||||
urls.forEach((url) => {
|
||||
console.log("URL:", url)
|
||||
dbObjs.push(run(url))
|
||||
})
|
||||
let dbObject = calculateAverageDynamically(dbObjs)
|
||||
dbObject.analyzedPaths = [...subPaths].map((subPath) => subPath.lighthouseSubpath)
|
||||
return { data: dbObject }
|
||||
})()
|
||||
@@ -1,18 +0,0 @@
|
||||
;(function () {
|
||||
const request = context.request()
|
||||
//get id of deleted image entry (delete request URL is : .../api/_/demo/medialib/id)
|
||||
const id = request.param("id")
|
||||
|
||||
//find all entrys where there is an image field with the id of this deleted one (image is an foreignKey widget, so stores the id as a string internally)
|
||||
const referencingElements = context.db.find("democol", {
|
||||
filter: {
|
||||
image: id,
|
||||
},
|
||||
})
|
||||
referencingElements.forEach((e) => {
|
||||
//update all those entries to remove the image id from them, since its deleted...
|
||||
context.db.update("democol", e.id, {
|
||||
image: "",
|
||||
})
|
||||
})
|
||||
})()
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
const { ssrValidatePath } = require("../config")
|
||||
const { log } = require("../lib/utils")
|
||||
const { ssrRequest } = require("../lib/ssr-server")
|
||||
const { ssrRequest } = require("../lib/ssr-server.js")
|
||||
|
||||
;(function () {
|
||||
/** @type {HookResponse} */
|
||||
// @ts-ignore
|
||||
let response = null
|
||||
|
||||
const request = context.request()
|
||||
@@ -15,9 +14,6 @@ const { ssrRequest } = require("../lib/ssr-server")
|
||||
|
||||
// add sentry trace id to head
|
||||
const trace_id = context.debug.sentryTraceId()
|
||||
/**
|
||||
* @param {string} content
|
||||
*/
|
||||
function addSentryTrace(content) {
|
||||
return content.replace("</head>", '<meta name="sentry-trace" content="' + trace_id + '" /></head>')
|
||||
}
|
||||
@@ -49,22 +45,12 @@ const { ssrRequest } = require("../lib/ssr-server")
|
||||
},
|
||||
})
|
||||
if (cache && cache.length) {
|
||||
const validUntil = cache[0].validUntil ? new Date(cache[0].validUntil.unixMilli()) : null
|
||||
// context.debug.dump("cache validUntil", validUntil)
|
||||
if (!validUntil || validUntil > new Date()) {
|
||||
// context.debug.dump("using cache")
|
||||
// use cache
|
||||
context.response.header("X-SSR-Cache", "true")
|
||||
throw {
|
||||
status: 200,
|
||||
log: false,
|
||||
html: addSentryTrace(cache[0].content),
|
||||
}
|
||||
} else {
|
||||
// cache is invalid, delete it
|
||||
context.response.header("X-SSR-Cache", "invalid")
|
||||
// @ts-ignore
|
||||
context.db.delete("ssr", cache[0].id)
|
||||
// use cache
|
||||
context.response.header("X-SSR-Cache", "true")
|
||||
throw {
|
||||
status: 200,
|
||||
log: false,
|
||||
html: addSentryTrace(cache[0].content),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +114,7 @@ const { ssrRequest } = require("../lib/ssr-server")
|
||||
} else {
|
||||
cacheIt = true
|
||||
}
|
||||
} catch (/** @type {any} */ e) {
|
||||
} catch (e) {
|
||||
// save error for later insert into html
|
||||
log(e.message)
|
||||
log(e.stack)
|
||||
@@ -149,8 +135,6 @@ const { ssrRequest } = require("../lib/ssr-server")
|
||||
// context.debug.dump("ssr", {
|
||||
path: url,
|
||||
content: tpl,
|
||||
// @ts-ignore
|
||||
validUntil: context.ssrCacheValidUntil,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user