mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core: remove problem_send
This commit is contained in:
1 parent
1009893683
commit
f54cf8a9ba
9 files changed
+54
-279
No files matched your search
@@ -1,11 +1,9 @@
|
||||
import db from 'hydrooj/dist/service/db';
|
||||
import {
|
||||
Handler, param, post, Route, Types,
|
||||
Handler, post, Route, Types,
|
||||
} from 'hydrooj/dist/service/server';
|
||||
import { RemoteOnlineJudgeError } from 'hydrooj/dist/error';
|
||||
import crypto from 'crypto';
|
||||
import yaml from 'js-yaml';
|
||||
import superagent from 'superagent';
|
||||
|
||||
function decrypt(encrypted: string) {
|
||||
if (!encrypted) throw new Error();
|
||||
@@ -53,28 +51,8 @@ class DataReportHandler extends Handler {
|
||||
}
|
||||
}
|
||||
|
||||
class ProxySendRequestHandler extends Handler {
|
||||
@param('endpoint', Types.String)
|
||||
@param('domainId', Types.String)
|
||||
@param('url', Types.String)
|
||||
@param('tokenId', Types.String)
|
||||
@param('expire', Types.UnsignedInt)
|
||||
async post(_d: string, endpoint: string, domainId: string, url: string, tokenId: string, expire: number) {
|
||||
let res = await superagent.post(`${url}d/${domainId}/problem/send`)
|
||||
.send({ operation: 'info', token: tokenId })
|
||||
.catch((e) => e);
|
||||
if (res instanceof Error) throw new RemoteOnlineJudgeError(`[Post Target/Send/Info] ${res.message}`);
|
||||
res = await superagent.post(`${endpoint}/problem/receive`).send({
|
||||
operation: 'request', url: `${url}d/${domainId}/problem/send`, tokenId, expire,
|
||||
}).catch((e) => e);
|
||||
if (res instanceof Error) throw new RemoteOnlineJudgeError(`[Post Target/Receive/Request] ${res.message}`);
|
||||
this.response.body = { code: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
export function apply() {
|
||||
Route('data_report', '/center/report', DataReportHandler);
|
||||
Route('proxy_send', '/center/send', ProxySendRequestHandler);
|
||||
}
|
||||
|
||||
global.Hydro.handler.center = apply;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/center",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"main": "package.json",
|
||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.26.29",
|
||||
"version": "2.27.0",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "dist/loader.js",
|
||||
"typings": "dist/loader.d.ts",
|
||||
|
||||
@@ -26,7 +26,7 @@ export const builtinModel = [
|
||||
export const builtinHandler = [
|
||||
'home', 'problem', 'record', 'judge', 'user',
|
||||
'contest', 'training', 'discussion', 'manage', 'import',
|
||||
'misc', 'homework', 'domain', 'remote', 'status',
|
||||
'misc', 'homework', 'domain', 'status',
|
||||
];
|
||||
|
||||
export const builtinScript = [
|
||||
|
||||
@@ -13,7 +13,7 @@ const logger = new Logger('setup');
|
||||
const listenPort = argv.port || 8888;
|
||||
|
||||
async function get(ctx: Context) {
|
||||
ctx.body = `<!DOCTYPE html>
|
||||
ctx.body = `<!DOCTYPE html>
|
||||
<html data-page="setup" data-layout="immersive" class="layout--immersive page--setup nojs">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@@ -74,73 +74,63 @@ async function get(ctx: Context) {
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
ctx.response.type = 'text/html';
|
||||
ctx.response.type = 'text/html';
|
||||
}
|
||||
|
||||
async function post(ctx: Context) {
|
||||
const {
|
||||
host, port, name, username, password,
|
||||
} = ctx.request.body;
|
||||
let mongourl = 'mongodb://';
|
||||
if (username) mongourl += `${username}:${password}@`;
|
||||
mongourl += `${host}:${port}/${name}`;
|
||||
try {
|
||||
const Database = await mongodb.MongoClient.connect(mongourl, {
|
||||
useNewUrlParser: true, useUnifiedTopology: true,
|
||||
});
|
||||
const db = Database.db(name);
|
||||
const coll = db.collection('system');
|
||||
await Promise.all([
|
||||
coll.updateOne(
|
||||
{ _id: 'server.host' },
|
||||
{ $set: { value: ctx.request.host } },
|
||||
{ upsert: true },
|
||||
),
|
||||
coll.updateOne(
|
||||
{ _id: 'server.hostname' },
|
||||
{ $set: { value: ctx.request.hostname } },
|
||||
{ upsert: true },
|
||||
),
|
||||
coll.updateOne(
|
||||
{ _id: 'server.url' },
|
||||
{ $set: { value: ctx.request.href } },
|
||||
{ upsert: true },
|
||||
),
|
||||
coll.updateOne(
|
||||
{ _id: 'server.port' },
|
||||
{ $set: { value: parseInt(listenPort as string, 10) } },
|
||||
{ upsert: true },
|
||||
),
|
||||
]);
|
||||
fs.ensureDirSync(path.resolve(os.homedir(), '.hydro'));
|
||||
fs.writeFileSync(path.resolve(os.homedir(), '.hydro', 'config.json'), JSON.stringify({
|
||||
host, port, name, username, password,
|
||||
}));
|
||||
ctx.body = `<h1>This page will reload in 3 secs.</h1>
|
||||
const {
|
||||
host, port, name, username, password,
|
||||
} = ctx.request.body;
|
||||
let mongourl = 'mongodb://';
|
||||
if (username) mongourl += `${username}:${password}@`;
|
||||
mongourl += `${host}:${port}/${name}`;
|
||||
try {
|
||||
const Database = await mongodb.MongoClient.connect(mongourl, {
|
||||
useNewUrlParser: true, useUnifiedTopology: true,
|
||||
});
|
||||
const db = Database.db(name);
|
||||
const coll = db.collection('system');
|
||||
await Promise.all([
|
||||
coll.updateOne(
|
||||
{ _id: 'server.url' },
|
||||
{ $set: { value: ctx.request.href } },
|
||||
{ upsert: true },
|
||||
),
|
||||
coll.updateOne(
|
||||
{ _id: 'server.port' },
|
||||
{ $set: { value: parseInt(listenPort as string, 10) } },
|
||||
{ upsert: true },
|
||||
),
|
||||
]);
|
||||
fs.ensureDirSync(path.resolve(os.homedir(), '.hydro'));
|
||||
fs.writeFileSync(path.resolve(os.homedir(), '.hydro', 'config.json'), JSON.stringify({
|
||||
host, port, name, username, password,
|
||||
}));
|
||||
ctx.body = `<h1>This page will reload in 3 secs.</h1>
|
||||
<script>
|
||||
setTimeout(function (){
|
||||
window.location.href = '/';
|
||||
}, 3000);
|
||||
</script>`;
|
||||
setTimeout(() => process.exit(0), 500);
|
||||
} catch (e) {
|
||||
ctx.body = `Error connecting to database: ${e.message}\n${e.stack}`;
|
||||
}
|
||||
setTimeout(() => process.exit(0), 500);
|
||||
} catch (e) {
|
||||
ctx.body = `Error connecting to database: ${e.message}\n${e.stack}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function load() {
|
||||
const app = new Koa();
|
||||
const server = http.createServer(app.callback());
|
||||
app.keys = ['Hydro'];
|
||||
app
|
||||
.use(cache(path.join(os.tmpdir(), 'hydro', 'public'), {
|
||||
maxAge: 365 * 24 * 60 * 60,
|
||||
}))
|
||||
.use(Body())
|
||||
.use((ctx) => {
|
||||
if (ctx.request.method.toLowerCase() === 'post') return post(ctx);
|
||||
return get(ctx);
|
||||
});
|
||||
server.listen(listenPort);
|
||||
logger.success('Server listening at: %d', listenPort);
|
||||
const app = new Koa();
|
||||
const server = http.createServer(app.callback());
|
||||
app.keys = ['Hydro'];
|
||||
app
|
||||
.use(cache(path.join(os.tmpdir(), 'hydro', 'public'), {
|
||||
maxAge: 365 * 24 * 60 * 60,
|
||||
}))
|
||||
.use(Body())
|
||||
.use((ctx) => {
|
||||
if (ctx.request.method.toLowerCase() === 'post') return post(ctx);
|
||||
return get(ctx);
|
||||
});
|
||||
server.listen(listenPort);
|
||||
logger.success('Server listening at: %d', listenPort);
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import superagent from 'superagent';
|
||||
import { PassThrough } from 'stream';
|
||||
import { argv } from 'yargs';
|
||||
import { PermissionError, InvalidTokenError, RemoteOnlineJudgeError } from '../error';
|
||||
import { Logger } from '../logger';
|
||||
import { logAndReturn } from '../utils';
|
||||
import { ProblemAdd } from '../lib/ui';
|
||||
import { PERM, PRIV } from '../model/builtin';
|
||||
import token from '../model/token';
|
||||
import problem from '../model/problem';
|
||||
import * as system from '../model/system';
|
||||
import storage from '../model/storage';
|
||||
import {
|
||||
Handler, Types, Route, post,
|
||||
} from '../service/server';
|
||||
|
||||
const logger = new Logger('remote');
|
||||
|
||||
export class ProblemSendHandler extends Handler {
|
||||
async get() {
|
||||
this.checkPerm(PERM.PERM_VIEW_PROBLEM);
|
||||
this.response.template = 'problem_send.html';
|
||||
}
|
||||
|
||||
@post('target', Types.String)
|
||||
@post('pids', Types.Array)
|
||||
async postSend(domainId: string, _target: string, _pids: (number | string)[]) {
|
||||
this.checkPerm(PERM.PERM_VIEW_PROBLEM);
|
||||
const target = _target.split('@');
|
||||
if (target[1].endsWith('/')) target[1] = target[1].substr(0, target[1].length - 1);
|
||||
const getHidden = this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||
const getData = this.user.hasPriv(PRIV.PRIV_READ_PROBLEM_DATA) || this.user.hasPerm(PERM.PERM_READ_PROBLEM_DATA);
|
||||
const pdocs = await problem.getList(domainId, _pids, true, true, ['docId', 'owner', 'hidden']);
|
||||
const pids = new Set<number>();
|
||||
for (const key in pdocs) {
|
||||
pids.add(pdocs[key].docId);
|
||||
if (pdocs[key].hidden && !getHidden && !this.user.own(pdocs[key])) {
|
||||
throw new PermissionError(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||
}
|
||||
if (!getData && !this.user.own(pdocs[key]) && !this.user.hasPriv(PRIV.PRIV_READ_PROBLEM_DATA)) {
|
||||
throw new PermissionError(PERM.PERM_READ_PROBLEM_DATA);
|
||||
}
|
||||
}
|
||||
const [url, expire] = system.getMany(['server.url', 'session.saved_expire_seconds']);
|
||||
const tokenId = await token.createOrUpdate(token.TYPE_EXPORT, expire, { creator: this.user._id, domainId, pids: Array.from(pids) });
|
||||
const res = system.get('server.proxy')
|
||||
? await superagent.post(`${system.get('server.center')}/send`)
|
||||
.send({
|
||||
endpoint: `${target[1]}/d/${target[0]}`,
|
||||
domainId,
|
||||
url,
|
||||
tokenId,
|
||||
expire,
|
||||
}).catch(logAndReturn(logger))
|
||||
: await superagent.post(`${target[1]}/d/${target[0]}/problem/receive`)
|
||||
.send({
|
||||
operation: 'request', url: `${url}d/${domainId}/problem/send`, tokenId, expire,
|
||||
}).catch(logAndReturn(logger));
|
||||
if (res instanceof Error) throw new RemoteOnlineJudgeError(`[Post Target/Request] ${res.message}`);
|
||||
this.back();
|
||||
}
|
||||
|
||||
@post('token', Types.Content)
|
||||
async postInfo(domainId: string, tokenId: string) {
|
||||
const data = await token.get(tokenId, token.TYPE_EXPORT);
|
||||
if (!data) throw new InvalidTokenError(tokenId);
|
||||
const r = {};
|
||||
const pdocs = await problem.getMulti(domainId, { docId: { $in: data.pids } }, ['title', 'docId']).toArray();
|
||||
for (const pdoc of pdocs) r[pdoc.docId] = pdoc.title;
|
||||
this.response.body = { pdocs: r, pids: data.pids };
|
||||
}
|
||||
|
||||
@post('token', Types.Content, true)
|
||||
@post('pids', Types.Array)
|
||||
async postFetch(domainId: string, tokenId: string, pids: number[]) {
|
||||
pids = pids.map((i) => (+i ? +i : i));
|
||||
const pdocs = await problem.getMulti(domainId, { docId: { $in: pids } }, problem.PROJECTION_PUBLIC).toArray();
|
||||
const data = await token.get(tokenId, token.TYPE_EXPORT);
|
||||
if (!data) throw new InvalidTokenError(tokenId);
|
||||
if (pids.filter((pid) => !data.pids.includes(pid)).length) throw new InvalidTokenError(tokenId);
|
||||
const links = [];
|
||||
for (const pdoc of pdocs) {
|
||||
const files = await storage.list(`problem/${domainId}/${pdoc.docId}/`, true);
|
||||
const l = [];
|
||||
for (const file of files) {
|
||||
l.push([
|
||||
file.name,
|
||||
await storage.signDownloadLink(file.prefix + file.name, file.name, false, 'user'),
|
||||
]);
|
||||
}
|
||||
links.push(l);
|
||||
}
|
||||
this.response.body = { pdocs, links };
|
||||
}
|
||||
}
|
||||
|
||||
export class ProblemReceiveHandler extends Handler {
|
||||
noCheckPermView = true;
|
||||
|
||||
async get({ domainId }) {
|
||||
this.checkPerm(PERM.PERM_CREATE_PROBLEM);
|
||||
let requests = await token.getMulti(token.TYPE_IMPORT, { domainId }).toArray();
|
||||
requests = requests.map((request) => {
|
||||
const url = new URL(request.source);
|
||||
request.display = `${url.host}/${url.pathname.split('/')[2]}/`;
|
||||
request.url = `${url.origin}/d/${url.pathname.split('/')[2]}/p/`;
|
||||
return request;
|
||||
});
|
||||
this.response.template = 'problem_receive.html';
|
||||
this.response.body = { requests };
|
||||
this.response.body.path = [
|
||||
['Hydro', 'homepage'],
|
||||
['problem_main', 'problem_main'],
|
||||
['problem_receive', null],
|
||||
];
|
||||
}
|
||||
|
||||
@post('url', Types.String)
|
||||
@post('tokenId', Types.String)
|
||||
@post('expire', Types.UnsignedInt)
|
||||
async postRequest(domainId: string, url: string, tokenId: string, expire: number) {
|
||||
const res = await superagent.post(url)
|
||||
.send({ operation: 'info', token: tokenId })
|
||||
.catch(logAndReturn(logger));
|
||||
if (res instanceof Error) throw new RemoteOnlineJudgeError(`[Post Origin/Info] ${res.message}`);
|
||||
const [id] = await token.add(token.TYPE_IMPORT, expire, {
|
||||
domainId, tokenId, source: url, pdocs: res.body.pdocs, pids: res.body.pids,
|
||||
});
|
||||
this.response.body = { token: id };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async syncFiles(domainId: string, baseUrl: string, pid: number, files: [string, string][]) {
|
||||
for (const file of files) {
|
||||
const content = new PassThrough();
|
||||
superagent.get(file[1].startsWith('/') ? baseUrl + file[1] : file[1]).pipe(content);
|
||||
if (file[0].startsWith('testdata/')) {
|
||||
await problem.addTestdata(domainId, pid, file[0].split('testdata/')[1], content);
|
||||
} else if (file[0].startsWith('additional_file/')) {
|
||||
await problem.addAdditionalFile(domainId, pid, file[0].split('additional_file/')[1], content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@post('tokenId', Types.String)
|
||||
@post('pids', Types.Array, true)
|
||||
async postConfirm(domainId: string, tokenId: string, filterPid?: number[]) {
|
||||
this.checkPerm(PERM.PERM_CREATE_PROBLEM);
|
||||
const data = await token.get(tokenId, token.TYPE_IMPORT);
|
||||
if (!data) throw new InvalidTokenError(tokenId);
|
||||
const res = await superagent.post(data.source).send({
|
||||
operation: 'fetch',
|
||||
token: data.tokenId,
|
||||
pids: filterPid.length ? filterPid.map((i) => (+i ? +i : i)) : data.pids,
|
||||
}).catch(logAndReturn(logger));
|
||||
if (res instanceof Error) throw new RemoteOnlineJudgeError(`[Post Origin/Fetch] ${res.message}`);
|
||||
const pids = [];
|
||||
const { pdocs } = res.body;
|
||||
const files = res.body.links;
|
||||
const source = new URL(data.source);
|
||||
const baseUrl = `${source.protocol}//${source.host}`;
|
||||
const exportToken = await token.get(data.tokenId, token.TYPE_EXPORT);
|
||||
for (let i = 0; i < pdocs.length; i++) {
|
||||
const pdoc = pdocs[i];
|
||||
if (pdoc.pid) {
|
||||
const exist = await problem.get(domainId, pdoc.pid);
|
||||
if (exist) pdoc.pid = undefined;
|
||||
}
|
||||
const pid = await problem.add(domainId, pdoc.pid, pdoc.title, pdoc.content, exportToken?.creator || this.user._id, pdoc.tag);
|
||||
pids.push(pid);
|
||||
this.syncFiles(
|
||||
domainId,
|
||||
exportToken ? baseUrl : `http://127.0.0.1:${argv.port || system.get('server.port')}`,
|
||||
pid, files[i],
|
||||
).catch(logAndReturn(logger));
|
||||
await problem.edit(domainId, pid, { html: pdoc.html });
|
||||
}
|
||||
this.back();
|
||||
this.response.body = { pids };
|
||||
}
|
||||
}
|
||||
|
||||
export async function apply() {
|
||||
ProblemAdd('problem_receive', {}, 'copy', 'Receive From Hydro');
|
||||
Route('problem_receive', '/problem/receive', ProblemReceiveHandler);
|
||||
Route('problem_send', '/problem/send', ProblemSendHandler);
|
||||
}
|
||||
|
||||
global.Hydro.handler.remote = apply;
|
||||
@@ -173,8 +173,6 @@ SystemSetting(
|
||||
Setting('setting_server', 'server.name', 'Hydro', 'text', 'server.name', 'Server Name'),
|
||||
Setting('setting_server', 'server.displayName', 'Hydro', 'text', 'server.name', 'Server Name (Global Display)', FLAG_PRO),
|
||||
Setting('setting_server', 'server.worker', 1, 'number', 'server.worker', 'Server Workers Number', FLAG_PRO),
|
||||
Setting('setting_server', 'server.hostname', 'oj.undefined.moe', 'text', 'server.hostname', 'Server Hostname'),
|
||||
Setting('setting_server', 'server.host', 'oj.undefined.moe', 'text', 'server.host', 'Server Host'),
|
||||
Setting('setting_server', 'server.url', '/', 'text', 'server.url', 'Server BaseURL'),
|
||||
Setting('setting_server', 'server.cdn', '/', 'text', 'server.cdn', 'CDN Prefix'),
|
||||
Setting('setting_server', 'server.port', 8888, 'number', 'server.port', 'Server Port'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.8.46",
|
||||
"version": "4.9.0",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
{{ set(UiContext, {
|
||||
problemId: pdoc.pid|default(pdoc.docId),
|
||||
problemNumId: pdoc.docId,
|
||||
postCopyUrl: url('problem_send'),
|
||||
postPretestUrl: url('problem_pretest', pid=pdoc.docId),
|
||||
codeLang: handler.user.codeLang,
|
||||
codeTemplate: handler.user.codeTemplate,
|
||||
|
||||
Reference in new issue
Block a user