mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
bump version
This commit is contained in:
8 files changed
+75
-52
No files matched your search
@@ -111,6 +111,18 @@ Hydro 用户群:1085853538
|
|||||||
<details>
|
<details>
|
||||||
<summary><h2>更新日志(点击展开)</h2></summary>
|
<summary><h2>更新日志(点击展开)</h2></summary>
|
||||||
|
|
||||||
|
### Hydro 4.14.0 / UI 4.53.0
|
||||||
|
- core: 修复能够越权查看隐藏题目的问题 [security]
|
||||||
|
- ui: 优化提交记录详情富文本展示
|
||||||
|
- ui: 支持头像缓存
|
||||||
|
- ui: 禁止文本越界显示
|
||||||
|
- vjudge: codeforces: 优化提交 ID 获取
|
||||||
|
- ui: 默认禁用 sentry
|
||||||
|
- core: 支持隐藏训练左侧用户栏
|
||||||
|
- core: cli: 支持 eval
|
||||||
|
- ui: 添加实验性 speculation rules 支持
|
||||||
|
- core: 修复训练参加人数计数
|
||||||
|
|
||||||
### Hydro 4.13.4 / UI 4.52.3
|
### Hydro 4.13.4 / UI 4.52.3
|
||||||
- framework: 修复 404 返回页
|
- framework: 修复 404 返回页
|
||||||
- ui: 优化用户权限页布局
|
- ui: 优化用户权限页布局
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/elastic-search",
|
"name": "@hydrooj/elastic-search",
|
||||||
"version": "1.1.5",
|
"version": "1.1.6",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro",
|
"repository": "https://github.com/hydro-dev/Hydro",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "4.13.4",
|
"version": "4.14.0",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "src/plugin-api",
|
"main": "src/plugin-api",
|
||||||
"module": "src/plugin-api",
|
"module": "src/plugin-api",
|
||||||
|
|||||||
@@ -31,9 +31,13 @@ window.UiContext = JSON.parse(window.UiContext);
|
|||||||
window.UserContext = JSON.parse(window.UserContext);
|
window.UserContext = JSON.parse(window.UserContext);
|
||||||
try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { }
|
try { __webpack_public_path__ = UiContext.cdn_prefix; } catch (e) { }
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
const encodedConfig = encodeURIComponent(JSON.stringify(UiContext.SWConfig));
|
navigator.serviceWorker.register('/service-worker.js').then((registration) => {
|
||||||
navigator.serviceWorker.register(`/service-worker.js?config=${encodedConfig}`).then((registration) => {
|
|
||||||
console.log('SW registered: ', registration);
|
console.log('SW registered: ', registration);
|
||||||
|
fetch('/service-worker-config', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(UiContext.SWConfig),
|
||||||
|
});
|
||||||
}).catch((registrationError) => {
|
}).catch((registrationError) => {
|
||||||
console.log('SW registration failed: ', registrationError);
|
console.log('SW registration failed: ', registrationError);
|
||||||
});
|
});
|
||||||
@@ -48,7 +52,7 @@ if (process.env.NODE_ENV === 'production' && UiContext.sentry_dsn) {
|
|||||||
if (!e.isUserFacingError) window._sentryEvents.push(e);
|
if (!e.isUserFacingError) window._sentryEvents.push(e);
|
||||||
};
|
};
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = new URL('sentry.js', __webpack_public_path__).href;
|
script.src = new URL('/sentry.js', __webpack_public_path__).href;
|
||||||
document.body.appendChild(script);
|
document.body.appendChild(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,8 +158,8 @@ export function apply(ctx: Context) {
|
|||||||
`https://${that.request.host}`,
|
`https://${that.request.host}`,
|
||||||
SystemModel.get('server.url'),
|
SystemModel.get('server.url'),
|
||||||
SystemModel.get('server.cdn'),
|
SystemModel.get('server.cdn'),
|
||||||
...((SystemModel.get('server.assets') || '').split(',')).filter((i) => i),
|
|
||||||
],
|
],
|
||||||
|
assets: ((SystemModel.get('ui-default.assets') || '').split(',')).filter((i) => i) || [],
|
||||||
domains: SystemModel.get('ui-default.domains') || [],
|
domains: SystemModel.get('ui-default.domains') || [],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.52.3",
|
"version": "4.53.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",
|
||||||
|
|||||||
@@ -114,19 +114,20 @@ interface ServiceWorkerConfig {
|
|||||||
hosts: string[];
|
hosts: string[];
|
||||||
/** service domains */
|
/** service domains */
|
||||||
domains: string[];
|
domains: string[];
|
||||||
|
/** assets domains */
|
||||||
|
assets: string[];
|
||||||
preload?: string;
|
preload?: string;
|
||||||
}
|
}
|
||||||
let config: ServiceWorkerConfig = null;
|
let config: ServiceWorkerConfig = null;
|
||||||
|
|
||||||
function initConfig() {
|
function initConfig(cfg) {
|
||||||
config = JSON.parse(new URLSearchParams(location.search).get('config'));
|
config = cfg;
|
||||||
config.hosts ||= [];
|
config.hosts ||= [];
|
||||||
config.domains = Array.from(new Set([location.host, ...(config.domains || [])]));
|
config.domains = Array.from(new Set([location.host, ...(config.domains || [])]));
|
||||||
console.log('Config:', config);
|
console.log('Config:', config);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('install', (event) => event.waitUntil((async () => {
|
self.addEventListener('install', (event) => event.waitUntil((async () => {
|
||||||
initConfig();
|
|
||||||
if (process.env.NODE_ENV === 'production' && config?.preload) {
|
if (process.env.NODE_ENV === 'production' && config?.preload) {
|
||||||
const [cache, manifest] = await Promise.all([
|
const [cache, manifest] = await Promise.all([
|
||||||
caches.open(PRECACHE),
|
caches.open(PRECACHE),
|
||||||
@@ -140,7 +141,6 @@ self.addEventListener('install', (event) => event.waitUntil((async () => {
|
|||||||
})()));
|
})()));
|
||||||
|
|
||||||
self.addEventListener('activate', (event) => {
|
self.addEventListener('activate', (event) => {
|
||||||
initConfig();
|
|
||||||
event.waitUntil(self.clients.claim());
|
event.waitUntil(self.clients.claim());
|
||||||
const valid = [PRECACHE];
|
const valid = [PRECACHE];
|
||||||
caches.keys().then((names) => names
|
caches.keys().then((names) => names
|
||||||
@@ -181,23 +181,47 @@ async function get(request: Request) {
|
|||||||
function transformUrl(url: string) {
|
function transformUrl(url: string) {
|
||||||
const urlObject = new URL(url);
|
const urlObject = new URL(url);
|
||||||
const path = urlObject.pathname;
|
const path = urlObject.pathname;
|
||||||
if (path.startsWith('/fs/') || path.includes('X-Amz')) urlObject.search = '';
|
if (path.startsWith('/fs/') || path.toLowerCase().includes('x-amz')) urlObject.search = '';
|
||||||
return urlObject.toString();
|
return urlObject.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cachedRespond(request: Request) {
|
async function cached(request: Request, cacheKey: string, fetchFunc: () => Promise<Response>) {
|
||||||
const url = transformUrl(request.url);
|
const url = transformUrl(request.url);
|
||||||
const cachedResponse = await caches.match(url);
|
const urlObject = new URL(url);
|
||||||
if (cachedResponse) return cachedResponse;
|
const isAsset = config.assets.some((i) => request.url.startsWith(i));
|
||||||
console.log(`Caching ${url}`);
|
const rewritable = !isAsset && config.domains.length > 1
|
||||||
|
&& config.domains.includes(urlObject.hostname)
|
||||||
|
&& urlObject.origin === location.origin;
|
||||||
|
let targets = [url];
|
||||||
|
if (rewritable) {
|
||||||
|
targets = config.domains.map((i) => {
|
||||||
|
const t = new URL(request.url);
|
||||||
|
t.host = i;
|
||||||
|
return transformUrl(t.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldCache(request)) return rewritable ? fetchFunc() : fetch(request);
|
||||||
|
|
||||||
|
const results = await Promise.all(targets.map((i) => caches.match(i)));
|
||||||
|
const found = results.find((i) => i);
|
||||||
|
if (found) {
|
||||||
|
console.debug('Serve from cache %s <- %s', request.url, found.url);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
const [cache, response] = await Promise.all([
|
const [cache, response] = await Promise.all([
|
||||||
caches.open(PRECACHE),
|
caches.open(cacheKey),
|
||||||
get(request),
|
fetchFunc(),
|
||||||
]);
|
]);
|
||||||
|
if (response.status === 206) return response; // partial response cannot be cached
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
console.log(`Cached ${url}`);
|
||||||
cache.put(url, response.clone());
|
cache.put(url, response.clone());
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
console.log(`Failed to cache ${url}`, response);
|
||||||
|
// If response fails, re-fetch the original request to prevent
|
||||||
|
// errors caused by different headers and do not cache them
|
||||||
return fetch(request);
|
return fetch(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +230,11 @@ self.addEventListener('fetch', (event: FetchEvent) => {
|
|||||||
event.respondWith(new Response('pong'));
|
event.respondWith(new Response('pong'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (event.request.url.endsWith('/service-worker-config')) {
|
||||||
|
event.request.json().then((cfg) => initConfig(cfg));
|
||||||
|
event.respondWith(new Response('ok'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (map.get(event.request.url)) {
|
if (map.get(event.request.url)) {
|
||||||
streamsaver(event);
|
streamsaver(event);
|
||||||
return;
|
return;
|
||||||
@@ -213,29 +242,18 @@ self.addEventListener('fetch', (event: FetchEvent) => {
|
|||||||
if (!['get', 'post', 'head'].includes(event.request.method.toLowerCase())) return;
|
if (!['get', 'post', 'head'].includes(event.request.method.toLowerCase())) return;
|
||||||
if (!config) return; // Don't do anything when not initialized
|
if (!config) return; // Don't do anything when not initialized
|
||||||
const url = new URL(event.request.url);
|
const url = new URL(event.request.url);
|
||||||
const rewritable = config.domains.length > 1
|
const isAsset = config.assets.some((i) => event.request.url.startsWith(i));
|
||||||
&& config.domains.includes(url.hostname) && url.origin === location.origin;
|
const rewritable = !isAsset && config.domains.length > 1
|
||||||
|
&& config.domains.includes(url.hostname)
|
||||||
|
&& url.origin === location.origin;
|
||||||
// Only handle whitelisted origins;
|
// Only handle whitelisted origins;
|
||||||
if (!config.hosts.some((i) => event.request.url.startsWith(i))) return;
|
if (!isAsset && !config.hosts.some((i) => event.request.url.startsWith(i))) return;
|
||||||
|
// Do not cache range requests
|
||||||
|
if (event.request.headers.get('range')?.trim()?.length) return;
|
||||||
|
|
||||||
if (shouldCache(event.request)) {
|
event.respondWith((async () => cached(event.request, isAsset ? 'assets' : PRECACHE, () => (rewritable
|
||||||
event.respondWith((async () => {
|
? get(event.request)
|
||||||
if (rewritable) {
|
: fetch(url, {
|
||||||
const targets = config.domains.map((i) => {
|
|
||||||
const t = new URL(event.request.url);
|
|
||||||
t.host = i;
|
|
||||||
return transformUrl(t.toString());
|
|
||||||
});
|
|
||||||
const results = await Promise.all(targets.map((i) => caches.match(i)));
|
|
||||||
return results.find((i) => i) || cachedRespond(event.request);
|
|
||||||
}
|
|
||||||
const transformedUrl = transformUrl(event.request.url);
|
|
||||||
const cachedResponse = await caches.match(transformedUrl);
|
|
||||||
if (cachedResponse) return cachedResponse;
|
|
||||||
console.log(`Caching ${transformedUrl}`);
|
|
||||||
const [cache, response] = await Promise.all([
|
|
||||||
caches.open(PRECACHE),
|
|
||||||
fetch(url, {
|
|
||||||
method: event.request.method,
|
method: event.request.method,
|
||||||
headers: event.request.headers,
|
headers: event.request.headers,
|
||||||
redirect: event.request.redirect,
|
redirect: event.request.redirect,
|
||||||
@@ -243,16 +261,5 @@ self.addEventListener('fetch', (event: FetchEvent) => {
|
|||||||
referrer: event.request.referrer,
|
referrer: event.request.referrer,
|
||||||
referrerPolicy: event.request.referrerPolicy,
|
referrerPolicy: event.request.referrerPolicy,
|
||||||
signal: event.request.signal,
|
signal: event.request.signal,
|
||||||
}), // Fetch from url to prevent opaque response
|
}))))());
|
||||||
]);
|
|
||||||
if (response.ok) {
|
|
||||||
cache.put(transformedUrl, response.clone());
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
console.log(`Failed to cache ${transformedUrl}`, response);
|
|
||||||
// If response fails, re-fetch the original request to prevent
|
|
||||||
// errors caused by different headers and do not cache them
|
|
||||||
return fetch(event.request);
|
|
||||||
})());
|
|
||||||
} else if (rewritable) event.respondWith(get(event.request));
|
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/vjudge",
|
"name": "@hydrooj/vjudge",
|
||||||
"version": "1.9.9",
|
"version": "1.9.10",
|
||||||
"description": "Submit problems to remote oj",
|
"description": "Submit problems to remote oj",
|
||||||
"main": "./src/index.ts",
|
"main": "./src/index.ts",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
|
|||||||
Reference in new issue
Block a user