mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
Merge branch 'master' into remove-union
This commit is contained in:
17 files changed
+47
-28
No files matched your search
+3
-2
@@ -305,7 +305,7 @@ const Steps = () => [
|
||||
{
|
||||
init: 'install.preparing',
|
||||
operations: [
|
||||
() => {
|
||||
async () => {
|
||||
if (process.env.IGNORE_BT) return;
|
||||
const res = exec('bt default');
|
||||
if (!res.code) {
|
||||
@@ -314,6 +314,7 @@ const Steps = () => [
|
||||
process.exit(1);
|
||||
} else {
|
||||
log.warn('warn.bt');
|
||||
await sleep(5000);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -407,7 +408,7 @@ ${nixConfBase}`);
|
||||
},
|
||||
{
|
||||
init: 'install.caddy',
|
||||
skip: () => !exec('caddy version').code || installAsJudge || noCaddy,
|
||||
skip: () => !exec('caddy version').code || installAsJudge || noCaddy || !existsSync(`${process.env.HOME}/.hydro/Caddyfile`),
|
||||
hidden: installAsJudge,
|
||||
operations: [
|
||||
'nix-env -iA nixpkgs.caddy',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "4.16.0",
|
||||
"version": "4.16.1",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "src/plugin-api",
|
||||
"module": "src/plugin-api",
|
||||
@@ -25,7 +25,6 @@
|
||||
"adm-zip": "0.5.14",
|
||||
"cac": "^6.7.14",
|
||||
"cordis": "3.18.1",
|
||||
"detect-browser": "^5.3.0",
|
||||
"emoji-regex": "^10.4.0",
|
||||
"emojis-list": "2.1.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
@@ -51,7 +50,8 @@
|
||||
"shorty.js": "^1.0.1",
|
||||
"superagent": "^10.1.0",
|
||||
"tar": "6.2.1",
|
||||
"thirty-two": "^1.0.2"
|
||||
"thirty-two": "^1.0.2",
|
||||
"ua-parser-js": "^1.0.39"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/adm-zip": "^0.4.34",
|
||||
|
||||
@@ -285,6 +285,13 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
|
||||
const pid = (i: number) => String.fromCharCode(65 + i);
|
||||
const escape = (i: string) => i.replace(/[",]/g, '');
|
||||
const unknownSchool = this.translate('Unknown School');
|
||||
const statusMap = {
|
||||
[STATUS.STATUS_ACCEPTED]: 'AC',
|
||||
[STATUS.STATUS_WRONG_ANSWER]: 'WA',
|
||||
[STATUS.STATUS_COMPILE_ERROR]: 'CE',
|
||||
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'TL',
|
||||
[STATUS.STATUS_RUNTIME_ERROR]: 'RT',
|
||||
};
|
||||
const submissions = teams.flatMap((i, idx) => {
|
||||
if (!i.journal) return [];
|
||||
const journal = i.journal.filter((s) => tdoc.pids.includes(s.pid));
|
||||
@@ -292,7 +299,7 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
|
||||
return journal.map((s) => {
|
||||
const id = pid(tdoc.pids.indexOf(s.pid));
|
||||
c[id]++;
|
||||
return `@s ${idx + 1},${id},${c[id]},${time(s.rid)},${s.status === STATUS.STATUS_ACCEPTED ? 'AC' : 'RJ'}`;
|
||||
return `@s ${idx + 1},${id},${c[id]},${time(s.rid)},${statusMap[s.status] || 'RJ'}`;
|
||||
});
|
||||
});
|
||||
const res = [
|
||||
|
||||
@@ -3,6 +3,7 @@ import { generateRegistrationOptions, verifyRegistrationResponse } from '@simple
|
||||
import yaml from 'js-yaml';
|
||||
import { pick } from 'lodash';
|
||||
import { Binary, ObjectId } from 'mongodb';
|
||||
import Parser from 'ua-parser-js';
|
||||
import { Context } from '../context';
|
||||
import {
|
||||
AuthOperationError, BlacklistedError, DomainAlreadyExistsError, InvalidTokenError,
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
import { DomainDoc, MessageDoc, Setting } from '../interface';
|
||||
import avatar, { validate } from '../lib/avatar';
|
||||
import * as mail from '../lib/mail';
|
||||
import * as useragent from '../lib/useragent';
|
||||
import { verifyTFA } from '../lib/verifyTFA';
|
||||
import BlackListModel from '../model/blacklist';
|
||||
import { PERM, PRIV } from '../model/builtin';
|
||||
@@ -187,7 +187,11 @@ class HomeSecurityHandler extends Handler {
|
||||
for (const session of sessions) {
|
||||
session.isCurrent = session._id === this.session._id;
|
||||
session._id = md5(session._id);
|
||||
session.updateUa = useragent.parse(session.updateUa || session.createUa || '');
|
||||
const ua = session.updateUa || session.createUa;
|
||||
if (ua) {
|
||||
const parser = new Parser(ua);
|
||||
session.updateUaInfo = parser.getResult();
|
||||
}
|
||||
session.updateGeoip = this.ctx.geoip?.lookup?.(
|
||||
session.updateIp || session.createIp,
|
||||
this.translate('geoip_locale'),
|
||||
@@ -202,7 +206,6 @@ class HomeSecurityHandler extends Handler {
|
||||
'authenticatorAttachment', 'regat', 'fmt',
|
||||
])),
|
||||
geoipProvider: this.ctx?.geoip?.provider,
|
||||
icon: (str = '') => str.split(' ')[0].toLowerCase(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -271,6 +271,7 @@ class UserRegisterWithCodeHandler extends Handler {
|
||||
if (mailDomain === 'qq.com' && !Number.isNaN(+id)) $set.avatar = `qq:${id}`;
|
||||
if (this.session.viewLang) $set.viewLang = this.session.viewLang;
|
||||
if (Object.keys($set).length) await user.setById(uid, $set);
|
||||
if (Object.keys(this.tdoc.setInDomain || {}).length) await domain.setUserInDomain(domainId, uid, this.tdoc.setInDomain);
|
||||
if (this.tdoc.oauth) await oauth.set(this.tdoc.oauth[1], uid);
|
||||
this.context.HydroContext.user = await user.getById(domainId, uid);
|
||||
this.session.viewLang = '';
|
||||
@@ -453,7 +454,7 @@ class OauthCallbackHandler extends Handler {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const set: Partial<Udoc> = { oauth: args.type };
|
||||
const set: Partial<Udoc> = { ...(r.set || {}), oauth: args.type };
|
||||
if (r.bio) set.bio = r.bio;
|
||||
if (r.viewLang) set.viewLang = r.viewLang;
|
||||
if (r.avatar) set.avatar = r.avatar;
|
||||
@@ -465,6 +466,7 @@ class OauthCallbackHandler extends Handler {
|
||||
username,
|
||||
redirect: this.domain.registerRedirect,
|
||||
set,
|
||||
setInDomain: r.setInDomain,
|
||||
oauth: [args.type, r._id],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -58,6 +58,8 @@ export interface OAuthUserResponse {
|
||||
bio?: string;
|
||||
uname?: string[];
|
||||
viewLang?: string;
|
||||
set?: Record<string, any>;
|
||||
setInDomain?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface Authenticator {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import * as browser from 'detect-browser';
|
||||
|
||||
export function parse(str: string) {
|
||||
return browser.parseUserAgent(str);
|
||||
}
|
||||
@@ -22,6 +22,7 @@ interface MongoConfig {
|
||||
url?: string,
|
||||
uri?: string,
|
||||
prefix?: string,
|
||||
collectionMap?: Record<string, string>,
|
||||
}
|
||||
|
||||
class MongoService {
|
||||
@@ -54,8 +55,9 @@ class MongoService {
|
||||
}
|
||||
|
||||
public collection<K extends keyof Collections>(c: K) {
|
||||
if (this.opts.prefix) return this.db.collection<Collections[K]>(`${this.opts.prefix}.${c}`);
|
||||
return this.db.collection<Collections[K]>(c);
|
||||
let coll = this.opts.prefix ? `${this.opts.prefix}.${c}` : c;
|
||||
if (this.opts.collectionMap?.[coll]) coll = this.opts.collectionMap[coll];
|
||||
return this.db.collection<Collections[K]>(coll);
|
||||
}
|
||||
|
||||
public async fixExpireAfter() {
|
||||
|
||||
@@ -238,7 +238,7 @@ export async function apply(ctx: Context) {
|
||||
return res;
|
||||
},
|
||||
paginate<T>(cursor: FindCursor<T>, page: number, key: string) {
|
||||
return db.paginate(cursor, page, this.ctx.setting.get(`pagination.${key}`));
|
||||
return db.paginate(cursor, page, this.ctx.setting.get(`pagination.${key}`) || 20);
|
||||
},
|
||||
checkPerm(...args: bigint[]) {
|
||||
if (!this.user.hasPerm(...args)) {
|
||||
|
||||
@@ -18,6 +18,7 @@ class OnlyofficeJWTHandler extends Handler {
|
||||
if (SystemModel.get('onlyoffice.externalSign')) {
|
||||
const res = await superagent.get(SystemModel.get('onlyoffice.externalSign')).query({ url });
|
||||
this.response.body = res.body;
|
||||
return;
|
||||
}
|
||||
let path: string;
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/onlyoffice",
|
||||
"version": "1.4.3",
|
||||
"version": "1.4.4",
|
||||
"main": "index.ts",
|
||||
"repository": "git@github.com:hydro-dev/Hydro.git",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
|
||||
@@ -151,7 +151,7 @@ export const xss = new FilterXSS({
|
||||
css: cssFilterOptions,
|
||||
safeAttrValue(tag, name, value) {
|
||||
if (name === 'id') return escapeAttrValue(`xss-id-${value}`);
|
||||
if (name === 'class') return value.split(' ').filter((i) => whitelistClasses.includes(i)).join(' ');
|
||||
if (name === 'class') return value.split(' ').filter((i) => whitelistClasses.includes(i) || i.startsWith('language-')).join(' ');
|
||||
return safeAttrValue(tag, name, value, CssFilter);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -157,6 +157,14 @@ env.addGlobal('set', (obj, key, val) => {
|
||||
env.addGlobal('findSubModule', (prefix) => Object.keys(global.Hydro.ui.template).filter((n) => n.startsWith(prefix)));
|
||||
env.addGlobal('templateExists', (name) => !!global.Hydro.ui.template[name]);
|
||||
|
||||
const platformIconMap = { 'mac os': 'mac' };
|
||||
const supportedPlatformIcons = ['android', 'chromeos', 'ios', 'linux', 'mac', 'mobile', 'windows'];
|
||||
env.addGlobal('platformIcon', (platform) => {
|
||||
const key = platformIconMap[platform?.toLowerCase()] || platform?.toLowerCase();
|
||||
if (supportedPlatformIcons.includes(key)) return key;
|
||||
return 'unknown';
|
||||
});
|
||||
|
||||
const render = (name: string, state: any) => new Promise<string>((resolve, reject) => {
|
||||
env.render(name, {
|
||||
page_name: name.split('.')[0],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.54.3",
|
||||
"version": "4.54.4",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "index.ts",
|
||||
|
||||
@@ -13,7 +13,7 @@ export default new NamedPage('record_detail', async () => {
|
||||
$(document).on('click', '.subtask-case', function () {
|
||||
const text = $(this).find('.message').text();
|
||||
const data = $(this).find('.message').html();
|
||||
if (!text?.trim()) return;
|
||||
if (!text?.trim() || (!text.includes('\n') && text.length < 20)) return;
|
||||
new InfoDialog({
|
||||
$body: tpl(<pre dangerouslySetInnerHTML={{ __html: data }} />),
|
||||
}).open();
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
<li class="sessionlist__item">
|
||||
<div class="media">
|
||||
<div class="media__left medium">
|
||||
<span class="sessionlist__icon icon icon-platform--{{ icon(session.updateUa.os) }}"></span>
|
||||
<span class="sessionlist__icon icon icon-platform--{{ platformIcon(session.updateUaInfo.os.name) }}"></span>
|
||||
</div>
|
||||
<div class="media__body medium typo">
|
||||
<p>{{ _('Last Update At') }}: {{ datetimeSpan(session.updateAt)|safe }}</p>
|
||||
@@ -161,8 +161,8 @@
|
||||
<p>{{ _('Hostname') }}: {{ session.createHost }}</p>
|
||||
{% endif %}
|
||||
<p>{{ _('Location') }}: {{ _(session.updateGeoip.display) }} ({{ session.updateIp }})</p>
|
||||
<p>{{ _('Operating System') }}: {{ session.updateUa.os }}</p>
|
||||
<p>{{ _('Browser') }}: {{ session.updateUa.name }} {{ session.updateUa.version }}</p>
|
||||
<p>{{ _('Operating System') }}: {{ session.updateUaInfo.os.name }} {{ session.updateUaInfo.os.version }}</p>
|
||||
<p>{{ _('Browser') }}: {{ session.updateUaInfo.browser.name }} {{ session.updateUaInfo.browser.version }}</p>
|
||||
{% if session.isCurrent %}
|
||||
<p class="sessionlist__current-session"><span class="icon icon-check"></span> {{ _('This is the current session') }}</p>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<div class="dialog dialog--signin" style="display:none">
|
||||
<div class="dialog__content">
|
||||
{% if model.system.get('server.login') %}
|
||||
{% if model.system.get('server.login') and handler.user.hasPriv(PRIV.PRIV_REGISTER_USER) %}
|
||||
<div class="dialog--signin__bg">
|
||||
<div class="dialog--signin__side">
|
||||
<h1>{{ _("Don't have an account?") }}</h1>
|
||||
<p>{{ _('By signing up a {0} universal account, you can submit code and join discussions in all online judging services provided by us.').format((handler.domain|default({ui:[]})).ui.name|default(model.system.get('server.name'))) }}</p>
|
||||
{% if handler.user.hasPriv(PRIV.PRIV_REGISTER_USER) %}
|
||||
<div><a href="{{ url('user_register') }}" class="inverse rounded button">{{ _('Sign Up Now') }}</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
Reference in new issue
Block a user