|
|
|
/* eslint-disable no-await-in-loop */
|
|
|
|
import { Logger } from 'hydrooj/dist/logger';
|
|
|
|
import db from 'hydrooj/dist/service/db';
|
|
|
|
import * as bus from 'hydrooj/dist/service/bus';
|
|
|
|
import * as Judge from 'hydrooj/dist/handler/judge';
|
|
|
|
import TaskModel from 'hydrooj/dist/model/task';
|
|
|
|
import DomainModel from 'hydrooj/dist/model/domain';
|
|
|
|
import ProblemModel from 'hydrooj/dist/model/problem';
|
|
|
|
import { sleep } from '@hydrooj/utils/lib/utils';
|
|
|
|
import providers from './providers/index';
|
|
|
|
import { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
|
|
|
|
|
|
|
|
const coll = db.collection('vjudge');
|
|
|
|
const Pool = {};
|
|
|
|
const logger = new Logger('vjudge');
|
|
|
|
const syncing = {};
|
|
|
|
|
|
|
|
class Service {
|
|
|
|
api: IBasicProvider;
|
|
|
|
|
|
|
|
constructor(public Provider: BasicProvider, public account: RemoteAccount) {
|
|
|
|
this.api = new Provider(account, async (data) => {
|
|
|
|
await coll.updateOne({ _id: account._id }, { $set: data });
|
|
|
|
});
|
|
|
|
this.main();
|
|
|
|
}
|
|
|
|
|
|
|
|
async judge(task) {
|
|
|
|
const rid = await this.api.submitProblem(task.target, task.lang, task.code, task);
|
|
|
|
const next = (payload) => Judge.next({ ...payload, rid: task.rid });
|
|
|
|
const end = (payload) => Judge.end({ ...payload, rid: task.rid });
|
|
|
|
await this.api.waitForSubmission(rid, next, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
async sync(domainId: string, resync = false) {
|
|
|
|
let page = 1;
|
|
|
|
let pids = await this.api.listProblem(page, resync);
|
|
|
|
while (pids.length) {
|
|
|
|
logger.info(`${domainId}: Syncing page ${page}`);
|
|
|
|
for (const pid of pids) {
|
|
|
|
if (syncing[`${domainId}/${pid}`] || await ProblemModel.get(domainId, pid)) continue;
|
|
|
|
syncing[`${domainId}/${pid}`] = true;
|
|
|
|
try {
|
|
|
|
const res = await this.api.getProblem(pid);
|
|
|
|
if (!res) continue;
|
|
|
|
const id = await ProblemModel.add(domainId, pid, res.title, res.content, 1, res.tag, false);
|
|
|
|
for (const key in res.files) {
|
|
|
|
await ProblemModel.addAdditionalFile(domainId, id, key, res.files[key]);
|
|
|
|
}
|
|
|
|
for (const key in res.data) {
|
|
|
|
await ProblemModel.addTestdata(domainId, id, key, res.data[key]);
|
|
|
|
}
|
|
|
|
logger.info(`${domainId}: problem ${id} sync done`);
|
|
|
|
} finally {
|
|
|
|
delete syncing[`${domainId}/${pid}`];
|
|
|
|
}
|
|
|
|
await sleep(5000);
|
|
|
|
}
|
|
|
|
page++;
|
|
|
|
pids = await this.api.listProblem(page, resync);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async main() {
|
|
|
|
const login = await this.api.ensureLogin();
|
|
|
|
if (login) logger.info(`${this.account.type}/${this.account.handle}: logged in`);
|
|
|
|
else return;
|
|
|
|
TaskModel.consume({ type: 'remotejudge', subType: this.account.type }, this.judge.bind(this));
|
|
|
|
const ddocs = await DomainModel.getMulti({ mount: this.account.type }).toArray();
|
|
|
|
for (const ddoc of ddocs) {
|
|
|
|
if (!ddoc.syncDone) await this.sync(ddoc._id, false);
|
|
|
|
else await this.sync(ddoc._id, true);
|
|
|
|
await DomainModel.edit(ddoc._id, { syncDone: true });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function loadAccounts() {
|
|
|
|
const accounts = await coll.find().toArray();
|
|
|
|
for (const account of accounts) {
|
|
|
|
if (!providers[account.type]) continue;
|
|
|
|
Pool[`${account.type}/${account.handle}`] = new Service(providers[account.type], account);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bus.on('app/started', loadAccounts);
|