From 221cdf8d7dca59b0d9f9a94d2f4ebbf063bc7621 Mon Sep 17 00:00:00 2001 From: undefined Date: Wed, 29 Dec 2021 18:00:53 +0800 Subject: [PATCH] ui: enhance resource load --- packages/hydrooj/package.json | 2 +- packages/hydrooj/src/service/server.ts | 3 +- packages/ui-default/entry.js | 36 ++++--- packages/ui-default/handler.ts | 100 +++++++----------- packages/ui-default/package.json | 2 +- .../ui-default/templates/layout/html5.html | 1 + 6 files changed, 67 insertions(+), 77 deletions(-) diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 8bc90dc4a..6c15235ad 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "3.2.34", + "version": "3.2.35", "bin": "bin/hydrooj.js", "main": "src/loader", "module": "src/loader", diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index 84acfa66b..2fe51c313 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -698,7 +698,7 @@ async function handle(ctx, HandlerClass, checker) { } else if (typeof h.post !== 'function') { throw new MethodNotAllowedError(method); } - } else if (typeof h[method] !== 'function') { + } else if (typeof h[method] !== 'function' && typeof h.all !== 'function') { throw new MethodNotAllowedError(method); } @@ -712,6 +712,7 @@ async function handle(ctx, HandlerClass, checker) { await bail(`handler/before/${HandlerClass.name.replace(/Handler$/, '')}`, h); await bail('handler/before', h); h.args.__method = Date.now(); + if (h.all) await h.all(args); if (h[method]) await h[method](args); h.args.__methodDone = Date.now(); await bail(`handler/before-operation/${HandlerClass.name.replace(/Handler$/, '')}`, h); diff --git a/packages/ui-default/entry.js b/packages/ui-default/entry.js index 5b3026eb7..c64c2e984 100644 --- a/packages/ui-default/entry.js +++ b/packages/ui-default/entry.js @@ -29,20 +29,32 @@ document.addEventListener('DOMContentLoaded', async () => { // eslint-disable-next-line camelcase try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { } - // Locale & langs - const { version, payload } = JSON.parse(localStorage.getItem('hydro-constant') || '{}'); - if (version === UiContext.constantVersion) { - eval(payload[0]); // eslint-disable-line no-eval - payload.shift(); - window.Hydro.preload = payload; - } else { + const local = JSON.parse(localStorage.getItem('hydro-constant') || '{}'); + let { data } = local; + if (local.version !== UiContext.constantVersion) { const res = await fetch(`/constant?version=${UiContext.constantVersion}`); - const data = await res.json(); - eval(data.payload[0]); // eslint-disable-line no-eval - localStorage.setItem('hydro-constant', JSON.stringify(data)); - data.payload.shift(); - window.Hydro.preload = data.payload; + data = await res.json(); + localStorage.setItem('hydro-constant', JSON.stringify({ data, version: UiContext.constantVersion })); } + eval(data[0]); // eslint-disable-line no-eval + data.shift(); + window.Hydro.preload = data; + + const e = document.createElement('style'); + const dark = UiContext.domain.nav_logo_dark || UiContext.nav_logo_dark; + const dark2x = UiContext.domain.nav_logo_dark_2x || UiContext.nav_logo_dark_2x; + e.innerHTML = ` + ${dark ? `.nav__logo { background-image: url(${dark}) !important }` : ''} + ${dark2x ? ` + @media + only screen and (-webkit-min-device-pixel-ratio: 1.5), + only screen and (min-resolution: 1.5dppx), + only screen and (min-resolution: 144dpi) { + .nav__logo, .header--mobile__domain { + background-image: url(${dark2x}) !important + } + }` : ''}`; + document.body.appendChild(e); import('./hydro'); }, false); diff --git a/packages/ui-default/handler.ts b/packages/ui-default/handler.ts index 1654082c7..d470367fd 100644 --- a/packages/ui-default/handler.ts +++ b/packages/ui-default/handler.ts @@ -6,86 +6,45 @@ import { join } from 'path'; import { ObjectID } from 'mongodb'; import * as bus from 'hydrooj/src/service/bus'; import { Route, Handler } from 'hydrooj/src/service/server'; -import * as db from 'hydrooj/src/service/db'; import { PERM } from 'hydrooj/src/model/builtin'; import markdown from './backendlib/markdown'; const { - system, domain, user, setting, problem, contest, + system, user, setting, problem, contest, } = global.Hydro.model; global.Hydro.version.ui = require('./package.json').version; -interface ConstantArgs { - lang: string; - domainId: string; -} - -declare module 'hydrooj/src/interface' { - interface Collections { - cache: { - _id: string; - value: any; - } - } -} - -const cache = {}; -const coll = db.collection('cache'); const pages = Object.keys(global.Hydro.ui.manifest) .filter((file) => file.endsWith('.page.js')) .map((i) => readFileSync(join(global.Hydro.ui.manifest[i], i), 'utf-8')); -async function constant(args: ConstantArgs) { - // CompileLangs - const payload = [`window.LANGS=${JSON.stringify(setting.langs)};`]; +let constant = ''; +let hash = ''; - // Locale - let { lang } = args; - if (!global.Hydro.locales[lang]) lang = system.get('server.language'); - payload[0] += `window.LOCALES=${JSON.stringify(global.Hydro.locales[lang])};`; - - // Extra style - let [nav_logo_dark, nav_logo_dark_2x] = system.getMany([ +function run() { + const payload = [...pages]; + const [logo, logo2x] = system.getMany([ 'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x', ]); - const ddoc = await domain.get(args.domainId); - nav_logo_dark = ddoc.ui?.nav_logo_dark || nav_logo_dark; - nav_logo_dark_2x = ddoc.ui?.nav_logo_dark_2x || nav_logo_dark_2x; - payload[0] += `\ - const e = document.createElement('style'); - e.innerHTML = \`\ - ${nav_logo_dark ? `.nav__logo { background-image: url(${nav_logo_dark}) !important }` : ''} - ${nav_logo_dark_2x ? `\ - @media - only screen and (-webkit-min-device-pixel-ratio: 1.5), - only screen and (min-resolution: 1.5dppx), - only screen and (min-resolution: 144dpi) { - .nav__logo, .header--mobile__domain { - background-image: url(${nav_logo_dark_2x}) !important - } - }` : ''}\`; - document.body.appendChild(e);`; - - payload.push(...pages); + const res = []; + res.push(`window.LANGS=${JSON.stringify(setting.langs)};`); + if (logo) res.push(`UiContext.nav_logo_dark="${logo}";`); + if (logo2x) res.push(`UiContext.nav_logo_dark_2x="${logo2x}";`); + payload.unshift(res.join('\n')); const c = crypto.createHash('sha1'); c.update(JSON.stringify(payload)); const version = c.digest('hex'); - cache[version] = { version, payload }; - await coll.updateOne({ _id: version }, { $set: { value: { version, payload } } }, { upsert: true }); - return version; + constant = JSON.stringify(payload); + hash = version; } - -const versionHandler = async (that) => { - if (that.response.template && !that.UiContext.constantVersion) { - that.UiContext.constantVersion = await constant({ - domainId: that.domainId, - lang: that.session.viewLang || that.user.viewLang, - }); - } +const versionHandler = (that) => { + that.UiContext.constantVersion = hash; }; bus.on('handler/after', versionHandler); bus.on('handler/error', versionHandler); +bus.on('app/started', run); +bus.on('system/setting', run); class WikiHelpHandler extends Handler { noCheckPermView = true; @@ -147,12 +106,28 @@ class MarkdownHandler extends Handler { } } -class UiConstantsHandler extends Handler { +class ResourceHandler extends Handler { noCheckPermView = true; - async get({ version }) { - this.response.body = cache[version] - || (await coll.findOne({ _id: version }))?.value; + async prepare() { + this.response.addHeader('Cache-Control', 'public, max-age=86400'); + } +} + +class UiConstantsHandler extends ResourceHandler { + async all() { + this.response.addHeader('ETag', hash); + this.response.body = constant; + this.response.type = 'application/json'; + } +} + +class LanguageHandler extends ResourceHandler { + async all() { + let lang = this.session.viewLang || this.user.viewLang; + if (!global.Hydro.locales[lang]) lang = system.get('server.language'); + this.response.body = `window.LOCALES=${JSON.stringify(global.Hydro.locales[lang])};`; + this.response.type = 'application/javascript'; } } @@ -202,5 +177,6 @@ global.Hydro.handler.ui = async () => { Route('set_theme', '/set_theme/:id', SetThemeHandler); Route('constant', '/constant', UiConstantsHandler); Route('markdown', '/markdown', MarkdownHandler); + Route('lang', '/l/:lang', LanguageHandler); Route('media', '/media', RichMediaHandler); }; diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 5ffb29aa6..193420c81 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.27.15", + "version": "4.27.16", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", diff --git a/packages/ui-default/templates/layout/html5.html b/packages/ui-default/templates/layout/html5.html index 5f8b883c4..6a88a3659 100644 --- a/packages/ui-default/templates/layout/html5.html +++ b/packages/ui-default/templates/layout/html5.html @@ -26,6 +26,7 @@ {% endblock %} {% if not isIE(handler.request.headers['user-agent']) %} + {% if process.env.DEV %} {% else %}