mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: add service worker to cache resources (#445)
This commit is contained in:
1 parent
aa5df5346a
commit
fcc4eedd9f
10 files changed
+112
-54
No files matched your search
+2
-2
@@ -33,7 +33,7 @@
|
|||||||
"@types/autocannon": "^7.9.0",
|
"@types/autocannon": "^7.9.0",
|
||||||
"@types/cross-spawn": "^6.0.2",
|
"@types/cross-spawn": "^6.0.2",
|
||||||
"@types/mocha": "^9.1.1",
|
"@types/mocha": "^9.1.1",
|
||||||
"@types/node": "^18.8.4",
|
"@types/node": "^18.11.0",
|
||||||
"@types/semver": "^7.3.12",
|
"@types/semver": "^7.3.12",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.40.0",
|
"@typescript-eslint/eslint-plugin": "^5.40.0",
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"globby": "11.1.0",
|
"globby": "11.1.0",
|
||||||
"latest-version": "7.0.0",
|
"latest-version": "7.0.0",
|
||||||
"mocha": "^10.0.0",
|
"mocha": "^10.1.0",
|
||||||
"mongodb": "^3.7.3",
|
"mongodb": "^3.7.3",
|
||||||
"mongodb-memory-server": "^8.9.3",
|
"mongodb-memory-server": "^8.9.3",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class WorkerService extends Service {
|
|||||||
sleep(300000),
|
sleep(300000),
|
||||||
]);
|
]);
|
||||||
const spent = Date.now() - start;
|
const spent = Date.now() - start;
|
||||||
if (spent > 500) logger.warn('Slow worker task (%d ms): %s', spent, doc);
|
if (spent > 500) logger.warn('Slow worker task (%d ms): %o', spent, doc);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Worker task fail: ', e);
|
logger.error('Worker task fail: ', e);
|
||||||
logger.error('%o', doc);
|
logger.error('%o', doc);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
|
|||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
import webpack from 'webpack';
|
import webpack from 'webpack';
|
||||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
||||||
|
import { WebpackManifestPlugin } from 'webpack-manifest-plugin';
|
||||||
import WebpackBar from 'webpackbar';
|
import WebpackBar from 'webpackbar';
|
||||||
import root from '../utils/root';
|
import root from '../utils/root';
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ export default function (env: { watch?: boolean, production?: boolean, measure?:
|
|||||||
loader: 'esbuild-loader',
|
loader: 'esbuild-loader',
|
||||||
options: {
|
options: {
|
||||||
loader: 'tsx',
|
loader: 'tsx',
|
||||||
target: 'es2015',
|
target: 'chrome80',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -220,7 +221,7 @@ export default function (env: { watch?: boolean, production?: boolean, measure?:
|
|||||||
minifyIdentifiers: true,
|
minifyIdentifiers: true,
|
||||||
treeShaking: true,
|
treeShaking: true,
|
||||||
target: [
|
target: [
|
||||||
'chrome60',
|
'chrome80',
|
||||||
],
|
],
|
||||||
exclude: [/mathmaps/, /\.min\.js$/],
|
exclude: [/mathmaps/, /\.min\.js$/],
|
||||||
})],
|
})],
|
||||||
@@ -240,8 +241,9 @@ export default function (env: { watch?: boolean, production?: boolean, measure?:
|
|||||||
new ExtractCssPlugin({
|
new ExtractCssPlugin({
|
||||||
filename: '[name].css?[fullhash:6]',
|
filename: '[name].css?[fullhash:6]',
|
||||||
}),
|
}),
|
||||||
|
new WebpackManifestPlugin({}),
|
||||||
new FriendlyErrorsPlugin(),
|
new FriendlyErrorsPlugin(),
|
||||||
new webpack.IgnorePlugin({ resourceRegExp: /(^\.\/locale$|mathjax|abcjs)/ }),
|
new webpack.IgnorePlugin({ resourceRegExp: /(^\.\/locale$|mathjax|abcjs|vditor.+\.d\.ts)/ }),
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
patterns: [
|
patterns: [
|
||||||
{ from: root('static') },
|
{ from: root('static') },
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default async function download(filename, targets) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
window.captureException?.(e);
|
window.captureException?.(e);
|
||||||
stopDownload();
|
stopDownload();
|
||||||
Notification.error(i18n('Download Error', [target.filename, e.toString()]));
|
Notification.error(i18n('Download Error: {0} {1}', [target.filename, e.toString()]));
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,6 +161,14 @@ class LanguageHandler extends ResourceHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SWConfigHandler extends ResourceHandler {
|
||||||
|
async get() {
|
||||||
|
this.response.body = {
|
||||||
|
preload: SystemModel.get('ui-default.preload'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SystemConfigSchemaHandler extends Handler {
|
class SystemConfigSchemaHandler extends Handler {
|
||||||
async get() {
|
async get() {
|
||||||
const schema = convert(Schema.intersect(SystemSettings) as any, true);
|
const schema = convert(Schema.intersect(SystemSettings) as any, true);
|
||||||
@@ -227,6 +235,7 @@ export function apply(ctx: Context) {
|
|||||||
ctx.Route('markdown', '/markdown', MarkdownHandler);
|
ctx.Route('markdown', '/markdown', MarkdownHandler);
|
||||||
ctx.Route('config_schema', '/manage/config/schema.json', SystemConfigSchemaHandler, PRIV.PRIV_EDIT_SYSTEM);
|
ctx.Route('config_schema', '/manage/config/schema.json', SystemConfigSchemaHandler, PRIV.PRIV_EDIT_SYSTEM);
|
||||||
ctx.Route('lang', '/l/:lang', LanguageHandler);
|
ctx.Route('lang', '/l/:lang', LanguageHandler);
|
||||||
|
ctx.Route('sw_config', '/sw-config', SWConfigHandler);
|
||||||
ctx.Route('media', '/media', RichMediaHandler);
|
ctx.Route('media', '/media', RichMediaHandler);
|
||||||
ctx.on('app/started', buildUI);
|
ctx.on('app/started', buildUI);
|
||||||
ctx.on('app/started', updateLogo);
|
ctx.on('app/started', updateLogo);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.41.18",
|
"version": "4.42.0",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
@@ -14,14 +14,14 @@
|
|||||||
"@blueprintjs/select": "4.8.3",
|
"@blueprintjs/select": "4.8.3",
|
||||||
"@fontsource/dm-mono": "^4.5.10",
|
"@fontsource/dm-mono": "^4.5.10",
|
||||||
"@fontsource/fira-code": "^4.5.12",
|
"@fontsource/fira-code": "^4.5.12",
|
||||||
"@fontsource/inconsolata": "^4.5.8",
|
"@fontsource/inconsolata": "^4.5.9",
|
||||||
"@fontsource/jetbrains-mono": "^4.5.11",
|
"@fontsource/jetbrains-mono": "^4.5.11",
|
||||||
"@fontsource/pt-mono": "^4.5.11",
|
"@fontsource/pt-mono": "^4.5.11",
|
||||||
"@fontsource/roboto-mono": "^4.5.8",
|
"@fontsource/roboto-mono": "^4.5.8",
|
||||||
"@fontsource/source-code-pro": "^4.5.12",
|
"@fontsource/source-code-pro": "^4.5.12",
|
||||||
"@fontsource/ubuntu-mono": "^4.5.11",
|
"@fontsource/ubuntu-mono": "^4.5.11",
|
||||||
"@hydrooj/utils": "workspace:*",
|
"@hydrooj/utils": "workspace:*",
|
||||||
"@svgr/webpack": "^6.4.0",
|
"@svgr/webpack": "^6.5.0",
|
||||||
"@types/gulp-if": "^0.0.34",
|
"@types/gulp-if": "^0.0.34",
|
||||||
"@types/jquery": "^3.5.14",
|
"@types/jquery": "^3.5.14",
|
||||||
"@types/json-schema": "^7.0.11",
|
"@types/json-schema": "^7.0.11",
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"@vscode/codicons": "^0.0.32",
|
"@vscode/codicons": "^0.0.32",
|
||||||
"autoprefixer": "^10.4.12",
|
"autoprefixer": "^10.4.12",
|
||||||
"browser-update": "^3.3.40",
|
"browser-update": "^3.3.40",
|
||||||
"chalk": "^5.1.0",
|
"chalk": "^5.1.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
@@ -65,10 +65,10 @@
|
|||||||
"matchmedia-polyfill": "^0.3.2",
|
"matchmedia-polyfill": "^0.3.2",
|
||||||
"mini-css-extract-plugin": "^2.6.1",
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"monaco-editor": "^0.34.0",
|
"monaco-editor": "^0.34.1",
|
||||||
"monaco-editor-nls": "^2.0.0",
|
"monaco-editor-nls": "^2.0.0",
|
||||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||||
"monaco-graphql": "^1.1.2",
|
"monaco-graphql": "^1.1.3",
|
||||||
"monaco-themes": "^0.4.2",
|
"monaco-themes": "^0.4.2",
|
||||||
"monaco-yaml": "^4.0.2",
|
"monaco-yaml": "^4.0.2",
|
||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
@@ -112,6 +112,7 @@
|
|||||||
"webpack": "^5.74.0",
|
"webpack": "^5.74.0",
|
||||||
"webpack-bundle-analyzer": "^4.6.1",
|
"webpack-bundle-analyzer": "^4.6.1",
|
||||||
"webpack-dev-server": "^4.11.1",
|
"webpack-dev-server": "^4.11.1",
|
||||||
|
"webpack-manifest-plugin": "^5.0.0",
|
||||||
"webpackbar": "^5.0.2"
|
"webpackbar": "^5.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -17,3 +17,87 @@ self.addEventListener('notificationclick', (event) => {
|
|||||||
return null;
|
return null;
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const PRECACHE = `precache-${process.env.VERSION}`;
|
||||||
|
const DO_NOT_CACHE = ['vditor', '.worker.js', 'fonts', 'i.monaco'];
|
||||||
|
|
||||||
|
function shouldCache(name: string) {
|
||||||
|
if (!name.split('/').pop()) return false;
|
||||||
|
if (!name.split('/').pop().includes('.')) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function shouldPreCache(name: string) {
|
||||||
|
if (!shouldCache(name)) return false;
|
||||||
|
if (DO_NOT_CACHE.filter((i) => name.includes(i)).length) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServiceWorkerConfig {
|
||||||
|
base?: string;
|
||||||
|
targets?: string[];
|
||||||
|
preload?: string;
|
||||||
|
}
|
||||||
|
let config: ServiceWorkerConfig = null;
|
||||||
|
|
||||||
|
async function get(url: string) {
|
||||||
|
const pending = [url, ...(config?.targets || []).map((i) => url.replace(config.base, i))];
|
||||||
|
let response: Promise<Response>;
|
||||||
|
for (const source of pending) {
|
||||||
|
response = fetch(source);
|
||||||
|
try {
|
||||||
|
console.log('From ', source);
|
||||||
|
const r = await response;
|
||||||
|
if (r.ok) return r;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(source, ' Load fail ', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener('install', (event) => event.waitUntil((async () => {
|
||||||
|
const [cache, manifest, cfg] = await Promise.all([
|
||||||
|
caches.open(PRECACHE),
|
||||||
|
fetch('/manifest.json').then((res) => res.json()),
|
||||||
|
fetch('/sw-config').then((res) => res.json()),
|
||||||
|
]);
|
||||||
|
config = cfg;
|
||||||
|
if (process.env.NODE_ENV === 'production' && config?.preload) {
|
||||||
|
const files = Object.values(manifest).filter(shouldPreCache)
|
||||||
|
.map((i: string) => new URL(i, config.preload).toString());
|
||||||
|
await cache.addAll(files); // NOTE: CORS header
|
||||||
|
}
|
||||||
|
self.skipWaiting();
|
||||||
|
})()));
|
||||||
|
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
const valid = [PRECACHE];
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys()
|
||||||
|
.then((names) => Promise.all(
|
||||||
|
names.filter((name) => !valid.includes(name)).map((p) => caches.delete(p)),
|
||||||
|
)).then(() => self.clients.claim()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('fetch', (event: FetchEvent) => {
|
||||||
|
if (!['get', 'head'].includes(event.request.method.toLowerCase())) return;
|
||||||
|
if (!event.request.url.startsWith('http')) return;
|
||||||
|
if (!shouldCache(event.request.url) && config?.base && config?.targets?.length) {
|
||||||
|
if (new URL(event.request.url).origin !== location.origin) return;
|
||||||
|
event.respondWith(get(event.request.url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (process.env.NODE_ENV !== 'production' || !shouldCache(event.request.url)) return;
|
||||||
|
event.respondWith((async () => {
|
||||||
|
const cachedResponse = await caches.match(event.request);
|
||||||
|
if (cachedResponse) return cachedResponse;
|
||||||
|
console.log(`Caching ${event.request.url}`);
|
||||||
|
const [cache, response] = await Promise.all([
|
||||||
|
caches.open(PRECACHE),
|
||||||
|
fetch(event.request),
|
||||||
|
]);
|
||||||
|
if (response.ok) await cache.put(event.request, response.clone());
|
||||||
|
return response;
|
||||||
|
})());
|
||||||
|
});
|
||||||
@@ -80,6 +80,9 @@ formatCode:
|
|||||||
desc: Auto format code
|
desc: Auto format code
|
||||||
category: preference
|
category: preference
|
||||||
value: true
|
value: true
|
||||||
|
preload:
|
||||||
|
type: text
|
||||||
|
default: ''
|
||||||
about:
|
about:
|
||||||
type: markdown
|
type: markdown
|
||||||
name: about
|
name: about
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Hydro",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "\/android-chrome-192x192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image\/png",
|
|
||||||
"density": 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
const base = 'https://hydro.ac';
|
|
||||||
const target = [
|
|
||||||
'https://hydro.ac',
|
|
||||||
'https://us.hydro.ac',
|
|
||||||
];
|
|
||||||
|
|
||||||
(this as any).addEventListener('install', () => {
|
|
||||||
console.log('Service Worker installing');
|
|
||||||
});
|
|
||||||
async function get(url) {
|
|
||||||
let current = 0;
|
|
||||||
let response;
|
|
||||||
while (target[current]) {
|
|
||||||
response = fetch(url.replace(base, target[current]));
|
|
||||||
try {
|
|
||||||
await response;
|
|
||||||
if (current) console.log('From ', target[current]);
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(target[current], ' Load fail ', error);
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
(this as any).addEventListener('fetch', (event) => {
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
if (new URL(event.request.url).origin !== location.origin) return;
|
|
||||||
event.respondWith(get(event.request.url));
|
|
||||||
});
|
|
||||||
Reference in new issue
Block a user