bug fixes

This commit is contained in:
undefined committed 2025-10-23 17:57:29 +08:00
1 parent d0944bdde9
commit 2f2e006eff
10 files changed
+56 -65

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/register",
"version": "1.0.3",
"version": "1.0.4",
"license": "MIT",
"main": "index.js",
"engines": {
+6 -8
View File
@@ -69,21 +69,19 @@ export async function apply(ctx: HydroContext) {
const parallelism = getConfig('parallelism');
async function collectInfo() {
const coll = db.collection('status');
const compilers = await compilerVersions(langs);
const [compilers, size] = await Promise.all([
compilerVersions(langs),
stackSize(),
]);
await coll.updateOne(
{ mid: info.mid, type: 'server' },
{ $set: { compilers } },
{ upsert: true },
);
const size = await stackSize();
await coll.updateOne(
{ mid: info.mid, type: 'server' },
{ $set: { stackSize: size } },
{ $set: { compilers, stackSize: size } },
{ upsert: true },
);
}
ctx.effect(() => {
const taskConsumer = TaskModel.consume({ type: 'judge' }, handle, true, parallelism);
collectInfo();
const dispose = ctx.on('system/setting', () => {
taskConsumer.setConcurrency(getConfig('parallelism'));
collectInfo();
+4 -3
View File
@@ -1,6 +1,6 @@
import path from 'path';
import {
ContestModel, Context, fs, getAlphabeticId, ObjectId, PERM, Schema, STATUS, Types, UserModel,
avatar, ContestModel, Context, fs, getAlphabeticId, ObjectId, PERM, Schema, STATUS, Types, UserModel,
} from 'hydrooj';
const file = fs.readFileSync(path.join(__dirname, 'public/assets/board.html'), 'utf8');
@@ -47,8 +47,9 @@ export async function apply(ctx: Context) {
team_id: `${udoc._id}`,
name: udoc.uname,
organization: udoc.school,
members: [],
coach: '',
members: udoc.members?.split(',').filter((t) => t) || [],
coach: udoc.coach,
badge: { url: avatar(udoc.avatar) },
group: [
...(udoc.group || []),
i.unrank ? 'unofficial' : 'official',
+4 -1
View File
@@ -8,6 +8,9 @@
"postinstall": "node -r @hydrooj/register install.ts"
},
"dependencies": {
"@xcpcio/board-app": "^0.64.5"
"@xcpcio/board-app": "^0.67.0"
},
"devDependencies": {
"@xcpcio/types": "^0.67.0"
}
}
@@ -102,10 +102,13 @@ export function Media(md: MarkdownIt) {
}
if (service === 'pdf') {
if (src.startsWith('file://') || src.startsWith('./')) src += src.includes('?') ? '&noDisposition=1' : '?noDisposition=1';
// A response with has content-disposition header causes the browser to download the file automatically.
// As we cannot control response header from external sites, we block embedding external PDFs.
else return '<p>Embedding an external PDF is no longer supported.</p>';
return `\
<object classid="clsid:${uuid().toUpperCase()}">
<param name="SRC" value="${src}" >
<embed width="100%" style="min-height: 100vh;border: none;" fullscreen="yes" src="${src}">
<param name="SRC" value="${src}">
<embed type="application/pdf" width="100%" style="min-height:100vh;border:none;" fullscreen="yes" src="${src}#toolbar=0&navpanes=0">
<noembed></noembed>
</embed>
</object>`;
@@ -125,14 +125,19 @@ export interface Field {
}
type Result<T extends string, R extends Record<T, Field>> = {
[K in keyof R]: ('text' | 'password' | 'username' | 'domain') extends R[K]['type'] ? string
[K in keyof R]: R[K]['type'] extends ('text' | 'password' | 'username' | 'domain') ? string
: R[K]['type'] extends 'checkbox' ? boolean
: R[K]['type'] extends 'userId' ? number
: R[K]['type'] extends 'user' ? any
: never;
};
export async function prompt<T extends string, R extends Record<T, Field>>(title: string, fields: R): Promise<Result<T, R>> {
interface PromptOptions {
cancelByClickingBack: boolean;
cancelByEsc: boolean;
}
export async function prompt<T extends string, R extends Record<T, Field>>(title: string, fields: R, options?: PromptOptions): Promise<Result<T, R>> {
let valueCache: Result<T, R> = {} as any;
const defaultValues = Object.fromEntries(Object.entries(fields)
.map(([name, field]: [T, Field]) => {
@@ -225,6 +230,8 @@ export async function prompt<T extends string, R extends Record<T, Field>>(title
const res = await new Dialog({
$body: $(div),
$action: [buttonCancel, buttonOk].join('\n'),
cancelByClickingBack: options?.cancelByClickingBack ?? false,
cancelByEsc: options?.cancelByEsc ?? false,
onDispatch(action) {
if (action === 'ok') {
for (const [name, field] of Object.entries(fields) as [string, Field][]) {
@@ -2,12 +2,10 @@ import { browserSupportsWebAuthnAutofill, startAuthentication } from '@simpleweb
import $ from 'jquery';
import responsiveCutoff from 'vj/breakpoints.json';
import DomDialog from 'vj/components/dialog/DomDialog';
import { InfoDialog } from 'vj/components/dialog/index';
import { alert } from 'vj/components/dialog/index';
import Notification from 'vj/components/notification';
import { AutoloadPage } from 'vj/misc/Page';
import {
i18n, mediaQuery, request, tpl,
} from 'vj/utils';
import { i18n, mediaQuery, request } from 'vj/utils';
const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
const signInDialog = DomDialog.getOrConstruct($('.dialog--signin'), {
@@ -18,7 +16,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
let authnInitialized = false;
async function initPasskey() {
if (authnInitialized) return;
if (authnInitialized || !window.isSecureContext) return;
authnInitialized = true;
const support = await browserSupportsWebAuthnAutofill();
if (!support) return;
@@ -79,9 +77,7 @@ const signinDialogPage = new AutoloadPage('signinDialogPage', null, () => {
$('[data-lostpass]').on('click', (e) => {
e.preventDefault();
new InfoDialog({
$body: tpl.typoMsg(i18n('Relax and try to remember your password.')),
}).open();
alert(i18n('Relax and try to remember your password.'));
});
});
@@ -164,6 +164,9 @@ $highlight-button-color = #F6DF45
.row
max-width: 100vw !important;
.data-table td
line-height: 1.5
.col--rank
width: rem(60px)
text-align: center
+14 -34
View File
@@ -1,42 +1,22 @@
import $ from 'jquery';
import React from 'react';
import DomainSelectAutoComplete from 'vj/components/autocomplete/DomainSelectAutoComplete';
import { ActionDialog } from 'vj/components/dialog';
import { prompt } from 'vj/components/dialog';
import { NamedPage } from 'vj/misc/Page';
import { i18n, tpl } from 'vj/utils';
import { i18n } from 'vj/utils';
export default new NamedPage('home_domain', () => {
$(tpl(
<div style={{ display: 'none' }} className="dialog__body--join-domain" id="join-domain-dialog">
<div className="row"><div className="columns">
<h1>{i18n('Join Domain')}</h1>
</div></div>
<div className="row">
<div className="columns">
<label>
{i18n('Domain ID')}
<div className="textbox-container">
<input name="domainId" type="text" className="textbox" data-autofocus />
</div>
</label>
</div>
</div>
</div>,
)).appendTo(document.body);
const selector: any = DomainSelectAutoComplete.getOrConstruct($('#join-domain-dialog [name="domainId"]'));
$(document).on('click', '[id="join-domain-button"]', async () => {
const action = await new ActionDialog({
$body: $('.dialog__body--join-domain > div'),
onDispatch(a) {
if (a === 'ok' && selector.value() === null) {
selector.focus();
return false;
}
return true;
const res = await prompt(i18n('Join Domain'), {
domain: {
type: 'domain',
label: i18n('Domain ID'),
required: true,
autofocus: true,
},
}).open();
if (action !== 'ok') return;
window.location.href = `/domain/join?target=${encodeURIComponent(selector.value()._id.toString())}`;
}, {
cancelByClickingBack: true,
cancelByEsc: true,
});
if (!res?.domain) return;
window.location.href = `/domain/join?target=${encodeURIComponent(res.domain.toString())}`;
});
});
@@ -15,11 +15,12 @@ import {
addSpeculationRules, delay, i18n, pjax, request,
} from 'vj/utils';
const keywords = ['category', 'difficulty', 'namespace'];
const list = [];
const filterTags = {};
const pinned: Record<string, string[]> = { category: [], difficulty: [], namespace: [] };
const selections = { category: {}, difficulty: {}, namespace: {} };
const selectedTags: Record<string, string[]> = { category: [], difficulty: [], namespace: [] };
const pinned: Record<string, string[]> = Object.fromEntries(keywords.map((keyword) => [keyword, []]));
const selections = Object.fromEntries(keywords.map((keyword) => [keyword, {}]));
const selectedTags: Record<string, string[]> = Object.fromEntries(keywords.map((keyword) => [keyword, []]));
let selectedPids: string[] = [];
let clearSelectionHandler: (() => void) | null = null;
@@ -35,7 +36,7 @@ function setDomSelected($dom, selected, icon?) {
}
const parserOptions = {
keywords: ['category', 'difficulty', 'namespace'],
keywords,
offsets: true,
alwaysArray: true,
tokenize: true,
@@ -46,8 +47,7 @@ function writeSelectionToInput() {
const parsedCurrentValue = parser.parse(currentValue, parserOptions) as SearchParserResult;
const q = parser.stringify({
...parsedCurrentValue,
category: selectedTags.category,
difficulty: selectedTags.difficulty,
...keywords.reduce((acc, keyword) => ({ ...acc, [keyword]: selectedTags[keyword] }), {}),
text: parsedCurrentValue.text,
}, parserOptions);
$('[name="q"]').val(q);