mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core: init graphql
This commit is contained in:
1 parent
2097c7257f
commit
c39c406535
16 files changed
+131
-23
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.32.35",
|
||||
"version": "2.33.0",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "src/loader",
|
||||
"module": "src/loader",
|
||||
@@ -19,6 +19,7 @@
|
||||
"cookies": "^0.8.0",
|
||||
"detect-browser": "^5.2.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"graphql": "^15.5.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"koa": "^2.13.1",
|
||||
"koa-body": "^4.2.0",
|
||||
|
||||
@@ -26,7 +26,7 @@ export const builtinModel = [
|
||||
export const builtinHandler = [
|
||||
'home', 'problem', 'record', 'judge', 'user',
|
||||
'contest', 'training', 'discussion', 'manage', 'import',
|
||||
'misc', 'homework', 'domain', 'status',
|
||||
'misc', 'homework', 'domain', 'status', 'api',
|
||||
];
|
||||
|
||||
export const builtinScript = [
|
||||
|
||||
@@ -78,5 +78,7 @@ export async function load(call: Entry) {
|
||||
await modelSystem.set('db.ver', dbVer);
|
||||
}
|
||||
await bus.serial('app/started');
|
||||
return await modelSystem.get('server.worker');
|
||||
return typeof argv.options.worker === 'number'
|
||||
? argv.options.worker
|
||||
: await modelSystem.get('server.worker');
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import graphql from 'graphql';
|
||||
import UserModel from '../model/user';
|
||||
import { Handler, Route } from '../service/server';
|
||||
|
||||
const schemaStr = `\
|
||||
type Query {
|
||||
user(id: Int, uname: String, mail: String): User
|
||||
problem(id: Int): String
|
||||
}
|
||||
|
||||
type User {
|
||||
_id: Int!
|
||||
uname: String!
|
||||
mail: String!
|
||||
}`;
|
||||
|
||||
const schema = graphql.buildSchema(schemaStr);
|
||||
const root = {
|
||||
user: (arg, ctx) => {
|
||||
if (arg.id) return UserModel.getById(ctx.domainId, arg.id);
|
||||
if (arg.mail) return UserModel.getByEmail(ctx.domainId, arg.mail);
|
||||
if (arg.uname) return UserModel.getByUname(ctx.domainId, arg.uname);
|
||||
return ctx.user;
|
||||
},
|
||||
problem: async (arg, ctx, node) => {
|
||||
},
|
||||
};
|
||||
|
||||
class ApiHandler extends Handler {
|
||||
async get() {
|
||||
const q = decodeURIComponent(this.ctx.request.querystring);
|
||||
if (q === 'schema') {
|
||||
this.response.body = { schema: schemaStr };
|
||||
} else if (q) {
|
||||
this.response.type = 'application/json';
|
||||
this.response.body = await graphql.graphql(schema, q, root, this);
|
||||
} else this.response.template = 'api.html';
|
||||
}
|
||||
|
||||
async post() {
|
||||
this.response.type = 'application/json';
|
||||
this.response.body = await graphql.graphql(schema, this.args.query, root, this, this.args.variables);
|
||||
}
|
||||
}
|
||||
|
||||
export function apply() {
|
||||
Route('api', '/api', ApiHandler);
|
||||
}
|
||||
|
||||
global.Hydro.handler.api = apply;
|
||||
@@ -246,7 +246,7 @@ class RecordMainConnectionHandler extends ConnectionHandler {
|
||||
else {
|
||||
this.send({
|
||||
html: await this.renderHTML('record_main_tr.html', {
|
||||
rdoc, udoc, pdoc, tdoc,
|
||||
rdoc, udoc, pdoc, tdoc, all: this.all,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -439,7 +439,7 @@ export class Handler extends HandlerCommon {
|
||||
query: ctx.query,
|
||||
path: ctx.path,
|
||||
params: ctx.params,
|
||||
referer: ctx.request.headers.referer,
|
||||
referer: ctx.request.headers.referer || '',
|
||||
json: (ctx.request.headers.accept || '').includes('application/json'),
|
||||
};
|
||||
this.response = {
|
||||
@@ -679,11 +679,9 @@ async function handle(ctx, HandlerClass, checker) {
|
||||
h.domainId = args.domainId;
|
||||
try {
|
||||
const method = ctx.method.toLowerCase();
|
||||
let operation: string;
|
||||
if (method === 'post' && ctx.request.body.operation) {
|
||||
operation = `_${ctx.request.body.operation}`
|
||||
.replace(/_([a-z])/gm, (s) => s[1].toUpperCase());
|
||||
}
|
||||
const operation = (method === 'post' && ctx.request.body.operation)
|
||||
? `_${ctx.request.body.operation}`.replace(/_([a-z])/gm, (s) => s[1].toUpperCase())
|
||||
: '';
|
||||
|
||||
await bus.serial('handler/create', h);
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ require.extensions['.js'] = function loader(module, filename) {
|
||||
const content = fs.readFileSync(filename, 'utf-8');
|
||||
return module._compile(content, filename);
|
||||
};
|
||||
require.extensions['.ts'] = function loader(module, filename) {
|
||||
return module._compile(transform(filename), filename);
|
||||
};
|
||||
const main = require('./main.js');
|
||||
|
||||
if (!module.parent) main();
|
||||
@@ -2,8 +2,8 @@ import { Page } from './Page';
|
||||
|
||||
export default class PageLoader {
|
||||
constructor() {
|
||||
const pages = require.context('../pages/', true, /\.page\.jsx?$/i);
|
||||
const components = require.context('../components/', true, /\.page\.jsx?$/i);
|
||||
const pages = require.context('../pages/', true, /\.page\.[jt]sx?$/i);
|
||||
const components = require.context('../components/', true, /\.page\.[jt]sx?$/i);
|
||||
this.pageInstances = [
|
||||
...pages.keys().map((key) => {
|
||||
const page = pages(key).default;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.17.8",
|
||||
"version": "4.18.0",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
@@ -41,6 +41,7 @@
|
||||
"file-loader": "^6.2.0",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"graphiql": "^1.4.2",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-cli": "^2.3.0",
|
||||
"gulp-iconfont": "^11.0.0",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.graphiql-container .doc-explorer-title, .graphiql-container .history-title
|
||||
padding: 2px !important
|
||||
@@ -0,0 +1,45 @@
|
||||
import { NamedPage } from 'vj/misc/Page';
|
||||
import request from 'vj/utils/request';
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import 'graphiql/graphiql.css';
|
||||
|
||||
const Logo = () => <span>Hydro API Console </span>;
|
||||
|
||||
// @ts-ignore
|
||||
|
||||
const page = new NamedPage('api', async () => {
|
||||
const [{ default: GraphiQL }, { buildSchema }, res] = await Promise.all([
|
||||
import('graphiql'),
|
||||
import('graphql'),
|
||||
request.get('/api?schema'),
|
||||
]);
|
||||
// @ts-ignore
|
||||
GraphiQL.Logo = Logo;
|
||||
console.log(res);
|
||||
const App = () => (
|
||||
<GraphiQL
|
||||
schema={buildSchema(res.schema)}
|
||||
style={{ height: '100vh' }}
|
||||
fetcher={async (graphQLParams) => {
|
||||
const data = await fetch(
|
||||
'/api',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(graphQLParams),
|
||||
credentials: 'same-origin',
|
||||
},
|
||||
);
|
||||
return data.json().catch(() => data.text());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
render(<App />, document.getElementById('graphiql'));
|
||||
});
|
||||
|
||||
export default page;
|
||||
@@ -0,0 +1,4 @@
|
||||
{% extends "layout/basic.html" %}
|
||||
{% block content %}
|
||||
<div id="graphiql" style="height:90vh;"></div>
|
||||
{% endblock %}
|
||||
@@ -1,9 +1,12 @@
|
||||
{% macro render_status_td(rdoc, rid_key='_id', class='') %}
|
||||
{% macro render_status_td(rdoc, rid_key='_id', class='', allDomain = false) %}
|
||||
<td class="col--status record-status--border {{ class }} {{ model.builtin.STATUS_CODES[rdoc.status] }}">
|
||||
<div class="col--status__text">
|
||||
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc.status] }}"></span>
|
||||
<span style="color: {{ utils.status.getScoreColor(rdoc.score|default(0)) }}">{{ rdoc.score|default(0) }}</span>
|
||||
<a href="{{ url('record_detail', rid=rdoc[rid_key]) }}" class="record-status--text {{ model.builtin.STATUS_CODES[rdoc.status] }}">
|
||||
<a
|
||||
href="{{ url('record_detail', rid=rdoc[rid_key], domainId=rdoc.domainId) if allDomain else url('record_detail', rid=rdoc[rid_key]) }}"
|
||||
class="record-status--text {{ model.builtin.STATUS_CODES[rdoc.status] }}"
|
||||
>
|
||||
{{ model.builtin.STATUS_TEXTS[rdoc.status] }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
<h1 class="section__title">{{ tdoc.title }}</h1>
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item">{{ _('Status') }}: {{ _(model.contest.statusText(tdoc)) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item bp3-icon-standard bp3-icon-book">{{ _(model.contest.RULES[tdoc.rule].TEXT) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item">{{ _(model.contest.statusText(tdoc)) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item icon icon-award">{{ _(model.contest.RULES[tdoc.rule].TEXT) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item">{{ _('Start at') }}: {{ contest.render_time(tdoc.beginAt) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item">{{ _('End at') }}: {{ contest.render_time(tdoc.endAt) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item icon icon-schedule">{{ contest.render_duration(tdoc) }} {{ _('hour(s)') }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item">{{ _('Host') }}: {{ user.render_inline(udict[tdoc.owner], badge=false) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item">{{ _('Partic.') }}: {{ tdoc['attend']|default(0) }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item icon icon-user--multiple">{{ tdoc['attend']|default(0) }}</span>
|
||||
{% if attended %}
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item bp3-icon-standard bp3-icon-tick">{{ _('Attended') }}</span>
|
||||
<span class="bp3-tag bp3-large bp3-minimal problem__tag-item icon icon-check">{{ _('Attended') }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{% set no_path_section = true %}
|
||||
{% extends "layout/basic.html" %}
|
||||
{% block content %}
|
||||
<div class="error__container clearfix">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{% import "components/record.html" as record with context %}
|
||||
{% import "components/problem.html" as problem with context %}
|
||||
<tr data-rid="{{ rdoc['_id'] }}">
|
||||
{{ record.render_status_td(rdoc) }}
|
||||
{{ record.render_status_td(rdoc, allDomain = all) }}
|
||||
<td class="col--problem col--problem-name">
|
||||
{% if handler.user.hasPerm(perm.PERM_REJUDGE) %}
|
||||
<form class="form--inline" method="post" action="{{ url('record_detail', rid=rdoc._id) }}">
|
||||
<form class="form--inline" method="post" action="{{ url('record_detail', rid=rdoc._id, domainId=rdoc.domainId) }}">
|
||||
<input type="hidden" name="operation" value="rejudge">
|
||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
||||
<button type="submit" class="link text-maroon lighter">
|
||||
|
||||
Reference in new issue
Block a user