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 esbuild from 'esbuild';
|
||||||
import c2k from 'koa2-connect/ts';
|
import c2k from 'koa2-connect/ts';
|
||||||
import { createServer, type Plugin } from 'vite';
|
import { createServer, type Plugin } from 'vite';
|
||||||
import { serializer } from '@hydrooj/framework';
|
import { HandlerCommon, serializer } from '@hydrooj/framework';
|
||||||
import {
|
import {
|
||||||
Context, Handler, Logger,
|
Context, Handler, Logger,
|
||||||
NotFoundError, param, sha1, size, Types,
|
NotFoundError, param, SettingModel, sha1, size, Types,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
|
||||||
const logger = new Logger('ui-next');
|
const logger = new Logger('ui-next');
|
||||||
@@ -112,6 +112,50 @@ function addFile(name: string, content: string) {
|
|||||||
hashes[name] = sha1(content).substring(0, 8);
|
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 {
|
class UiNextConstantHandler extends Handler {
|
||||||
noCheckPermView = true;
|
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) {
|
export async function apply(ctx: Context) {
|
||||||
if (process.env.HYDRO_CLI) return;
|
if (process.env.HYDRO_CLI) return;
|
||||||
|
|
||||||
|
ctx.Route('ui_next_constants', '/plugins/:version/:name', UiNextConstantHandler);
|
||||||
|
|
||||||
if (process.env.DEV) {
|
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({
|
const vite = await createServer({
|
||||||
root: __dirname,
|
root: __dirname,
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
@@ -265,7 +331,13 @@ export async function apply(ctx: Context) {
|
|||||||
route_map: ctx.server.routeMap,
|
route_map: ctx.server.routeMap,
|
||||||
endpoint: ctx.setting.get('server.url') || undefined,
|
endpoint: ctx.setting.get('server.url') || undefined,
|
||||||
}, serializer(false, context.handler));
|
}, 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);
|
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));
|
await vite.close().catch((e) => console.error(e));
|
||||||
};
|
};
|
||||||
} else {
|
} 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', {
|
ctx.server.registerRenderer('next', {
|
||||||
name: 'next',
|
name: 'next',
|
||||||
accept: [],
|
accept: [],
|
||||||
@@ -298,19 +377,25 @@ export async function apply(ctx: Context) {
|
|||||||
url: context.handler.context.req.url!,
|
url: context.handler.context.req.url!,
|
||||||
route_map: ctx.server.routeMap,
|
route_map: ctx.server.routeMap,
|
||||||
endpoint: ctx.setting.get('server.url') || undefined,
|
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));
|
}, 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(build, 2000);
|
||||||
const debouncedBuild = ctx.debounce(buildPlugins, 2000);
|
|
||||||
const triggerHotUpdate = (filePath?: string) => {
|
const triggerHotUpdate = (filePath?: string) => {
|
||||||
if (filePath && !filePath.includes('/ui/')) return;
|
if (filePath && !filePath.includes('/ui/')) return;
|
||||||
debouncedBuild();
|
debouncedBuild();
|
||||||
};
|
};
|
||||||
ctx.on('app/watch/change', triggerHotUpdate);
|
ctx.on('app/watch/change', triggerHotUpdate);
|
||||||
ctx.on('app/watch/unlink', 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",
|
"esbuild": "0.25.2",
|
||||||
"is-relative-url": "^4.1.0",
|
"is-relative-url": "^4.1.0",
|
||||||
"path-to-regexp": "^8.4.2",
|
"path-to-regexp": "^8.4.2",
|
||||||
|
"simple-git": "^3.36.0",
|
||||||
"react": "^19.2.8",
|
"react": "^19.2.8",
|
||||||
"react-dom": "^19.2.8"
|
"react-dom": "^19.2.8"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user