From f1aae5e8c2cfb25bea50325d5063325dc955e8af Mon Sep 17 00:00:00 2001 From: lowlighter <22963968+lowlighter@users.noreply.github.com> Date: Thu, 29 Oct 2020 19:59:22 +0100 Subject: [PATCH] Improve placeholder generation --- src/app.mjs | 4 +- src/html/app.js | 57 ++-------------------------- src/metrics.mjs | 99 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 81 insertions(+), 79 deletions(-) diff --git a/src/app.mjs b/src/app.mjs index 4d368f68..7977d939 100644 --- a/src/app.mjs +++ b/src/app.mjs @@ -62,7 +62,7 @@ app.get("/prism.yaml.min.js", limiter, (req, res) => res.sendFile(`${conf.node_modules}/prismjs/components/prism-yaml.min.js`)) app.get("/prism.markdown.min.js", limiter, (req, res) => res.sendFile(`${conf.node_modules}/prismjs/components/prism-markdown.min.js`)) app.get("/style.prism.css", limiter, (req, res) => res.sendFile(`${conf.node_modules}/prismjs/themes/prism-tomorrow.css`)) - app.get("/placeholder.svg", limiter, async (req, res) => { + app.get("/placeholder.json", limiter, async (req, res) => { const template = req.query.template || conf.settings.templates.default if (!(template in Templates)) return res.sendStatus(404) @@ -73,7 +73,7 @@ const {token, user} = req.query if (token) { if (actions.flush.has(token)) { - console.debug(`metrics/app/${user} > flushed cache`) + console.debug(`metrics/app/${actions.flush.get(token)} > flushed cache`) cache.del(actions.flush.get(token)) return res.sendStatus(200) } diff --git a/src/html/app.js b/src/html/app.js index aa4ae0d8..29edc5f3 100644 --- a/src/html/app.js +++ b/src/html/app.js @@ -73,6 +73,7 @@ //Plugins options const options = Object.entries(this.plugins.options) .filter(([key, value]) => `${value}`.length) + .filter(([key, value]) => this.plugins.enabled[key.split(".")[0]]) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) //Template const template = (this.templates.selected !== templates[0]) ? [`template=${this.templates.selected}`] : [] @@ -115,61 +116,9 @@ methods:{ //Load and render image async load() { - //Load template - const template = this.templates.selected - if (!this.templates.loaded[template]) { - const {data:{image, style, fonts}} = await axios.get(`/placeholder.svg?template=${template}`) - this.templates.loaded[template] = {image, style, fonts} - } - const {image = "", style = "", fonts = ""} = this.templates.loaded[this.templates.selected] || {} - if (!image) - return this.templates.placeholder = "#" - //Proxifier - const proxify = (target) => typeof target === "object" ? new Proxy(target, { - get(target, property) { - //Primitive conversion - if (property === Symbol.toPrimitive) - return () => "##" - //Iterables - if (property === Symbol.iterator) - return Reflect.get(target, property) - //Plugins should not be proxified by default as they can be toggled by user - if (/^plugins$/.test(property)) - return Reflect.get(target, property) - //Consider no errors on plugins - if (/^error/.test(property)) - return undefined - //Proxify recursively - return proxify(property in target ? Reflect.get(target, property) : {}) - } - }) : target - //Placeholder data - const data = { - style, - fonts, - s(_, letter) { return letter === "y" ? "ies" : "s" }, - base:this.plugins.enabled.base, - meta:{version:"0.0.0", author:"lowlighter", placeholder:true}, - user:proxify({name:`############`, websiteUrl:`########################`}), - computed:proxify({ - avatar:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==", - registration:"## years ago", - calendar:new Array(14).fill({color:"#ebedf0"}), - licenses:{favorite:`########`}, - plugins:Object.fromEntries(Object.entries(this.plugins.enabled).filter(([key, enabled]) => (key !== "base")&&(enabled)).map(([key]) => { - return [key, proxify({ - music:{provider:"########", tracks:new Array(4).fill({name:"##########", artist:"######", artwork:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg=="})}, - pagespeed:{scores:["Performance", "Accessibility", "Best Practices", "SEO"].map(title => ({title, score:NaN}))}, - followup:{issues:{count:0}, pr:{count:0}}, - habits:{indents:{style:`########`}}, - languages:{favorites:new Array(7).fill(null).map((_, x) => ({x, name:`######`, color:"#ebedf0", value:1/(x+1)}))}, - }[key]||{})] - })), - token:{scopes:[]}, - }), - } //Render placeholder - this.templates.placeholder = this.serialize(ejs.render(image, data)) + const url = this.url.replace(new RegExp(`${this.user}(\\?|$)`), "placeholder$1") + this.templates.placeholder = this.serialize((await axios.get(url)).data) this.generated.content = "" }, //Generate metrics and flush cache diff --git a/src/metrics.mjs b/src/metrics.mjs index 09ff9486..5353e015 100644 --- a/src/metrics.mjs +++ b/src/metrics.mjs @@ -23,37 +23,43 @@ if ((!(template in Templates))||(!(template in conf.templates))||((conf.settings.templates.enabled.length)&&(!conf.settings.templates.enabled.includes(template)))) throw new Error("unsupported template") const {query, image, style, fonts} = conf.templates[template] - - //Query data from GitHub API - console.debug(`metrics/compute/${login} > graphql query`) - const data = await graphql(query - .replace(/[$]login/, `"${login}"`) - .replace(/[$]repositories/, `${repositories}`) - .replace(/[$]calendar.to/, `"${(new Date()).toISOString()}"`) - .replace(/[$]calendar.from/, `"${(new Date(Date.now()-14*24*60*60*1000)).toISOString()}"`) - ) + const data = {base:{}} //Base parts - data.base = {} if (("base" in q)&&(!q.base)) conf.settings.plugins.base.parts.map(part => q[`base.${part}`] = false) for (const part of conf.settings.plugins.base.parts) data.base[part] = (`base.${part}` in q) ? !!q[`base.${part}`] : true - //Compute metrics - console.debug(`metrics/compute/${login} > compute`) - const computer = Templates[template].default || Templates[template] - await computer({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, format, shuffle}}) - const promised = await Promise.all(pending) + //Placeholder + if (login === "placeholder") + placeholder({data, conf, q}) + //Compute + else { + //Query data from GitHub API + console.debug(`metrics/compute/${login} > graphql query`) + Object.assign(data, await graphql(query + .replace(/[$]login/, `"${login}"`) + .replace(/[$]repositories/, `${repositories}`) + .replace(/[$]calendar.to/, `"${(new Date()).toISOString()}"`) + .replace(/[$]calendar.from/, `"${(new Date(Date.now()-14*24*60*60*1000)).toISOString()}"`) + )) - //Check plugins errors - if (conf.settings.debug) - for (const {name, result = null} of promised) - console.debug(`plugin ${name} ${result ? result.error ? "failed" : "success" : "ignored"} : ${JSON.stringify(result).replace(/^(.{888}).+/, "$1...")}`) - if (die) { - const errors = promised.filter(({result = null}) => !!result?.error).length - if (errors) - throw new Error(`${errors} error${s(errors)} found...`) + //Compute metrics + console.debug(`metrics/compute/${login} > compute`) + const computer = Templates[template].default || Templates[template] + await computer({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, format, shuffle}}) + const promised = await Promise.all(pending) + + //Check plugins errors + if (conf.settings.debug) + for (const {name, result = null} of promised) + console.debug(`plugin ${name} ${result ? result.error ? "failed" : "success" : "ignored"} : ${JSON.stringify(result).replace(/^(.{888}).+/, "$1...")}`) + if (die) { + const errors = promised.filter(({result = null}) => !!result?.error).length + if (errors) + throw new Error(`${errors} error${s(errors)} found...`) + } } //Template rendering @@ -98,3 +104,50 @@ } return array } + +/** Placeholder generator */ + function placeholder({data, conf, q}) { + //Proxifier + const proxify = (target) => typeof target === "object" ? new Proxy(target, { + get(target, property) { + //Primitive conversion + if (property === Symbol.toPrimitive) + return () => "##" + //Iterables + if (property === Symbol.iterator) + return Reflect.get(target, property) + //Plugins should not be proxified by default as they can be toggled by user + if (/^plugins$/.test(property)) + return Reflect.get(target, property) + //Consider no errors on plugins + if (/^error/.test(property)) + return undefined + //Proxify recursively + return proxify(property in target ? Reflect.get(target, property) : {}) + } + }) : target + //Enabled plugins + const enabled = Object.entries(conf.settings.plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key).filter(key => (key in q)&&(q[key])) + //Placeholder data + Object.assign(data, { + s(_, letter) { return letter === "y" ? "ies" : "s" }, + meta:{version:conf.package.version, author:conf.package.author, placeholder:true}, + user:proxify({name:`############`, websiteUrl:`########################`}), + computed:proxify({ + avatar:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==", + registration:"## years ago", + calendar:new Array(14).fill({color:"#ebedf0"}), + licenses:{favorite:`########`}, + plugins:Object.fromEntries(enabled.map(key => + [key, proxify({ + music:{provider:"########", tracks:new Array("music.limit" in q ? Math.max(Number(q["music.limit"])||0, 0) : 4).fill({name:"##########", artist:"######", artwork:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg=="})}, + pagespeed:{scores:["Performance", "Accessibility", "Best Practices", "SEO"].map(title => ({title, score:NaN}))}, + followup:{issues:{count:0}, pr:{count:0}}, + habits:{indents:{style:`########`}}, + languages:{favorites:new Array(7).fill(null).map((_, x) => ({x, name:`######`, color:"#ebedf0", value:1/(x+1)}))}, + }[key]??{})] + )), + token:{scopes:[]}, + }), + }) + } \ No newline at end of file