mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui-next: inject i18n, code-langs & version (#1193)
This commit is contained in:
1 parent
b9118113cb
commit
a8ec204a04
2 files changed
+95
-9
No files matched your search
@@ -3,10 +3,10 @@ import path from 'path';
|
||||
import esbuild from 'esbuild';
|
||||
import c2k from 'koa2-connect/ts';
|
||||
import { createServer, type Plugin } from 'vite';
|
||||
import { serializer } from '@hydrooj/framework';
|
||||
import { HandlerCommon, serializer } from '@hydrooj/framework';
|
||||
import {
|
||||
Context, Handler, Logger,
|
||||
NotFoundError, param, sha1, size, Types,
|
||||
NotFoundError, param, SettingModel, sha1, size, Types,
|
||||
} from 'hydrooj';
|
||||
|
||||
const logger = new Logger('ui-next');
|
||||
@@ -112,6 +112,50 @@ function addFile(name: string, content: string) {
|
||||
hashes[name] = sha1(content).substring(0, 8);
|
||||
}
|
||||
|
||||
async function buildI18n() {
|
||||
const localeList: Record<string, { name: string, flag: string }> = {};
|
||||
for (const lang in global.Hydro.locales) {
|
||||
if (!/^[a-zA-Z_]+$/.test(lang)) continue;
|
||||
if (!global.Hydro.locales[lang].__interface) continue;
|
||||
addFile(`lang-${lang}.js`, `window.HydroLocale=${JSON.stringify(global.Hydro.locales[lang][Symbol.for('iterate')])};`);
|
||||
const id = global.Hydro.locales[lang].__id;
|
||||
if (id) localeList[id] = { name: global.Hydro.locales[lang].__langname, flag: global.Hydro.locales[lang].__flag };
|
||||
}
|
||||
addFile('locale-list.js', `window.HydroLocaleList=${JSON.stringify(localeList)};`);
|
||||
}
|
||||
|
||||
async function buildCodeLangs() {
|
||||
addFile('code-langs.js', `window.HydroCodeLangs=${JSON.stringify(SettingModel.langs)};`);
|
||||
}
|
||||
|
||||
async function buildVersions() {
|
||||
const versions: Record<string, string> = { ...global.Hydro.version };
|
||||
try {
|
||||
const { simpleGit } = require('simple-git') as typeof import('simple-git');
|
||||
const fetchAddonVersion = async (name: string, addonPath: string) => {
|
||||
try {
|
||||
const git = simpleGit(addonPath);
|
||||
const [log, status] = await Promise.all([git.log(), git.status()]);
|
||||
if (log.all.length > 0) {
|
||||
let hash = log.all[0].hash.substring(0, 7);
|
||||
if (!status.isClean()) hash += '-dirty';
|
||||
versions[name] = versions[name] ? `${versions[name]}-${hash}` : hash;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug('Could not get git hash for addon %s: %o', name, e);
|
||||
}
|
||||
};
|
||||
await Promise.all(
|
||||
Object.entries(global.addons)
|
||||
.filter(([name]) => name !== 'hydrooj') // already handled in loader.ts
|
||||
.map(([name, addonPath]) => fetchAddonVersion(name, addonPath as string)),
|
||||
);
|
||||
} catch (e) {
|
||||
logger.debug('simple-git not available: %o', e);
|
||||
}
|
||||
addFile('versions.js', `window.HydroVersions=${JSON.stringify(versions)};`);
|
||||
}
|
||||
|
||||
class UiNextConstantHandler extends Handler {
|
||||
noCheckPermView = true;
|
||||
|
||||
@@ -219,10 +263,32 @@ export async function buildPlugins() {
|
||||
}
|
||||
}
|
||||
|
||||
const HASH_FALLBACK = '00000000';
|
||||
|
||||
const getViewLang = (handler: HandlerCommon) => handler.user?.viewLang || handler.session?.viewLang || 'zh';
|
||||
|
||||
const injectedScripts = (resolve: (name: string) => string, viewLang: string) => [
|
||||
'code-langs.js',
|
||||
'locale-list.js',
|
||||
`lang-${viewLang}.js`,
|
||||
'versions.js',
|
||||
].map((name) => `<script src="${resolve(name)}"></script>`);
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
if (process.env.HYDRO_CLI) return;
|
||||
|
||||
ctx.Route('ui_next_constants', '/plugins/:version/:name', UiNextConstantHandler);
|
||||
|
||||
if (process.env.DEV) {
|
||||
ctx.on('app/started', async () => {
|
||||
await buildI18n();
|
||||
await buildCodeLangs();
|
||||
await buildVersions();
|
||||
});
|
||||
ctx.on('app/i18n/update', buildI18n);
|
||||
ctx.on('system/setting-loaded', buildCodeLangs);
|
||||
ctx.on('system/setting', buildCodeLangs);
|
||||
|
||||
const vite = await createServer({
|
||||
root: __dirname,
|
||||
clearScreen: false,
|
||||
@@ -265,7 +331,13 @@ export async function apply(ctx: Context) {
|
||||
route_map: ctx.server.routeMap,
|
||||
endpoint: ctx.setting.get('server.url') || undefined,
|
||||
}, serializer(false, context.handler));
|
||||
const htmlToRender = html.replace(INJECT_MARKER, buildInject(serialized));
|
||||
const ts = Date.now();
|
||||
const devAssetUrl = (name: string) => `/plugins/0/${name}?_=${ts}`;
|
||||
const injectHtml = [
|
||||
buildInject(serialized),
|
||||
...injectedScripts(devAssetUrl, getViewLang(context.handler)),
|
||||
].join('\n');
|
||||
const htmlToRender = html.replace(INJECT_MARKER, injectHtml);
|
||||
return await vite.transformIndexHtml(context.handler.context.req.url!, htmlToRender);
|
||||
},
|
||||
});
|
||||
@@ -275,7 +347,14 @@ export async function apply(ctx: Context) {
|
||||
await vite.close().catch((e) => console.error(e));
|
||||
};
|
||||
} else {
|
||||
ctx.Route('ui_next_constants', '/plugins/:version/:name', UiNextConstantHandler);
|
||||
const build = async () => {
|
||||
await buildPlugins();
|
||||
await buildI18n();
|
||||
await buildCodeLangs();
|
||||
await buildVersions();
|
||||
};
|
||||
ctx.on('app/started', build);
|
||||
|
||||
ctx.server.registerRenderer('next', {
|
||||
name: 'next',
|
||||
accept: [],
|
||||
@@ -298,19 +377,25 @@ export async function apply(ctx: Context) {
|
||||
url: context.handler.context.req.url!,
|
||||
route_map: ctx.server.routeMap,
|
||||
endpoint: ctx.setting.get('server.url') || undefined,
|
||||
plugins_url: `/plugins/${hashes['plugins.js'] || '00000000'}/plugins.js`,
|
||||
plugins_url: `/plugins/${hashes['plugins.js'] || HASH_FALLBACK}/plugins.js`,
|
||||
}, serializer(false, context.handler));
|
||||
return html.replace(INJECT_MARKER, buildInject(serialized));
|
||||
const prodAssetUrl = (name: string) => `/plugins/${hashes[name] || HASH_FALLBACK}/${name}`;
|
||||
const injectHtml = [
|
||||
buildInject(serialized),
|
||||
...injectedScripts(prodAssetUrl, getViewLang(context.handler)),
|
||||
].join('\n');
|
||||
return html.replace(INJECT_MARKER, injectHtml);
|
||||
},
|
||||
});
|
||||
ctx.on('app/started', buildPlugins);
|
||||
const debouncedBuild = ctx.debounce(buildPlugins, 2000);
|
||||
const debouncedBuild = ctx.debounce(build, 2000);
|
||||
const triggerHotUpdate = (filePath?: string) => {
|
||||
if (filePath && !filePath.includes('/ui/')) return;
|
||||
debouncedBuild();
|
||||
};
|
||||
ctx.on('app/watch/change', triggerHotUpdate);
|
||||
ctx.on('app/watch/unlink', triggerHotUpdate);
|
||||
ctx.on('system/setting', () => debouncedBuild());
|
||||
ctx.on('system/setting-loaded', buildCodeLangs);
|
||||
ctx.on('system/setting', debouncedBuild);
|
||||
ctx.on('app/i18n/update', debouncedBuild);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
"esbuild": "0.25.2",
|
||||
"is-relative-url": "^4.1.0",
|
||||
"path-to-regexp": "^8.4.2",
|
||||
"simple-git": "^3.36.0",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8"
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user