“我的域”功能

This commit is contained in:
undefined committed 2020-07-14 19:02:19 +08:00
1 parent d4e849a133
commit eaed0e84f5
11 files changed
+122 -27

No files matched your search

+20 -4
View File
@@ -241,6 +241,10 @@ class UserChangemailWithCodeHandler extends Handler {
class HomeDomainHandler extends Handler {
async get() {
const path = [
['Hydro', 'homepage'],
['home_domain', null],
];
const dudict = await domain.getDictUserByDomainId(this.user._id);
const dids = Object.keys(dudict);
const ddocs = await domain.getMulti({ _id: { $in: dids } }).toArray();
@@ -252,19 +256,27 @@ class HomeDomainHandler extends Handler {
|| udoc.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN);
}
this.response.template = 'home_domain.html';
this.response.body = { ddocs, dudict, canManage };
this.response.body = {
ddocs, dudict, canManage, path,
};
}
}
class HomeDomainCreateHandler extends Handler {
async get() {
this.response.body = 'home_domain_create.html';
this.response.body = {
path: [
['Hydro', 'homepage'],
['domain_create', null],
],
};
this.response.template = 'domain_create.html';
}
@param('id', Types.String)
@param('name', Types.String, isTitle)
@param('bulletin', Types.String, isContent)
@param('gravatar', Types.String, isEmail, true)
@param('gravatar', Types.String, true, isEmail)
async post(_: string, id: string, name: string, bulletin: string, gravatar: string) {
gravatar = gravatar || this.user.gravatar || this.user.mail || 'guest@hydro.local';
const domainId = await domain.add(id, this.user._id, name, bulletin);
@@ -342,9 +354,13 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
class HomeFileHandler extends Handler {
async get() {
const path = [
['Hydro', 'homepage'],
['domain_file', null],
];
const ufdocs = await file.getMulti({ owner: this.user._id }).toArray();
this.response.template = 'home_file.html';
this.response.body = { ufdocs };
this.response.body = { ufdocs, path };
}
@param('ufid', Types.ObjectID)
+1 -1
View File
@@ -127,7 +127,7 @@ class UiSettingsHandler extends Handler {
'ui.header_logo', 'ui.header_logo_2x',
'ui.nav_logo_dark', 'ui.nav_logo_light',
'ui.nav_logo_dark_2x', 'ui.nav_logo_light_2x',
'ui.headerBackground', 'ui.headerBackground2x',
'ui.header_background', 'ui.header_background2x',
]);
this.response.body = await this.renderHTML('extra.css', {
header_logo,
+1 -1
View File
@@ -361,7 +361,7 @@ class ProblemDataUploadHandler extends ProblemManageHandler {
async post({ domainId }) {
let md5;
if (!this.request.files.file) throw new BadRequestError();
await problem.setTestdata(
this.pdoc = await problem.setTestdata(
domainId, this.pdoc.docId, this.request.files.file.path,
);
if (this.pdoc.data && typeof this.pdoc.data === 'object') {
+1 -1
View File
@@ -13,7 +13,7 @@ export async function add(domainId: string, owner: number, name: string, bulleti
return domainId;
}
export function get(domainId: string): Promise<DomainDoc> {
export function get(domainId: string): Promise<DomainDoc | null> {
return coll.findOne({ _id: domainId });
}
+2 -2
View File
@@ -28,7 +28,7 @@ export async function add(
domainId: string, pid: string = null, title: string, content: string, owner: number,
tag: string[] = [], category: string[] = [], data: ObjectID = null, hidden = false,
) {
const d = await domain.inc(domainId, 'pidCounter', 1);
const d = await domain.inc(domainId, 'pid_counter', 1);
if (!pid) pid = d.pidCounter.toString();
return await document.add(
domainId, content, owner, document.TYPE_PROBLEM, d.pidCounter, null, null,
@@ -153,7 +153,7 @@ export function setStar(domainId: string, pid: number, uid: number, star: boolea
export async function setTestdata(domainId: string, _id: number, filePath: string) {
const pdoc = await get(domainId, _id);
const config = await readConfig(filePath);
const id = await file.add(filePath, 'data.zip');
const id = await file.add(filePath, 'data.zip', 1);
if (pdoc.data && typeof pdoc.data === 'object') file.dec(pdoc.data);
return await edit(domainId, _id, { data: id, config });
}
+2 -1
View File
@@ -99,6 +99,7 @@ DomainSetting(
Setting('setting_domain', 'name', null, 'New domain', 'text', 'name'),
Setting('setting_domain', 'gravatar', null, '', 'text', 'gravatar', 'Will be used as the domain icon.'),
Setting('setting_domain', 'bulletin', null, '', 'markdown', 'Bulletin'),
Setting('storage', 'pid_counter', null, 0, 'number', 'Problem ID Counter', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('storage', 'nAccept', null, 0, 'number', 'nAccept', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('storage', 'nSubmit', null, 0, 'number', 'nSubmit', null, FLAG_HIDDEN | FLAG_DISABLED),
Setting('storage', 'nLike', null, 0, 'number', 'nLike', null, FLAG_HIDDEN | FLAG_DISABLED),
@@ -126,7 +127,7 @@ SystemSetting(
Setting('setting_ui', 'ui.header_logo', null, '/components/header/header-logo.png', 'text', 'Header Logo'),
Setting('setting_ui', 'ui.header_logo_2x', null, '/components/header/header-logo@2x.png', 'text', 'Header Logo@2x'),
Setting('setting_ui', 'ui.header_background', null, '/components/header/header-background.png', 'text', 'Header Background'),
Setting('setting_ui', 'ui.header_background_2x', null, '/components/header/header-background@2x.png', 'text', 'Header Logo@2x'),
Setting('setting_ui', 'ui.header_background_2x', null, '/components/header/header-background@2x.png', 'text', 'Header Background@2x'),
Setting('setting_ui', 'ui.nav_logo_light', null, '/components/navigation/nav-logo-small_light.png', 'text', 'Nav Logo(Light)'),
Setting('setting_ui', 'ui.nav_logo_dark', null, '/components/navigation/nav-logo-small_dark.png', 'text', 'Nav Logo(Dark)'),
Setting('setting_ui', 'ui.nav_logo_light_2x', null, '/components/navigation/nav-logo-small@2x_light.png', 'text', 'Nav Logo@2x(Light)'),
+1 -1
View File
@@ -10,7 +10,7 @@ export async function get(_id: string) {
return null;
}
export async function getMany(keys: string[]): Promise<Dictionary<any>> {
export async function getMany(keys: string[]): Promise<any[]> {
const docs = await coll.find({ _id: { $in: keys } }).toArray();
const dict = {};
const res = [];
+30 -15
View File
@@ -12,21 +12,22 @@ import Body from 'koa-body';
import Router from 'koa-router';
import cache from 'koa-static-cache';
import sockjs from 'sockjs';
import { lrucache } from '../utils';
import { Udoc, DomainDoc } from '../interface';
import {
UserNotFoundError, BlacklistedError, PermissionError,
UserFacingError, ValidationError, PrivilegeError,
CsrfTokenError, InvalidOperationError, MethodNotAllowedError, NotFoundError,
} from '../error';
import { render } from '../lib/template';
import * as misc from '../lib/misc';
import * as user from '../model/user';
import * as domain from '../model/domain';
import * as system from '../model/system';
import * as blacklist from '../model/blacklist';
import * as token from '../model/token';
import * as opcount from '../model/opcount';
import {
UserNotFoundError, BlacklistedError, PermissionError,
UserFacingError, ValidationError, PrivilegeError,
CsrfTokenError, InvalidOperationError, MethodNotAllowedError,
} from '../error';
import hash from '../lib/hash.hydro';
import { lrucache } from '../utils';
import { Udoc } from '../interface';
let enableLog = true;
@@ -145,7 +146,7 @@ export function param(name: string, ...args: any): MethodDecorator {
if (!rawArgs[item.name]) throw new ValidationError(item.name);
if (item.validate) {
if (!item.validate(rawArgs[item.name])) {
throw new ValidationError(item.name, rawArgs[item.name]);
throw new ValidationError(item.name);
}
}
if (item.convert) c.push(item.convert(rawArgs[item.name]));
@@ -223,6 +224,8 @@ export class Handler {
user: Udoc;
domain: DomainDoc;
constructor(ctx: Koa.Context) {
this.ctx = ctx;
this.request = {
@@ -263,7 +266,7 @@ export class Handler {
async renderHTML(name: string, context: any): Promise<string> {
if (enableLog) console.time(name);
const UserContext = { ...this.user, gravatar: misc.gravatar(this.user.gravatar, 128) };
const UserContext = { ...this.user, gravatar: misc.gravatar(this.user.gravatar || '', 128) };
const res = await render(name, {
handler: this,
UserContext,
@@ -326,7 +329,7 @@ export class Handler {
let res = '#';
const args: any = { ...kwargs };
try {
if (this.args.domainId !== 'system') {
if (this.args.domainId !== 'system' || args.domainId) {
name += '_with_domainId';
args.domainId = args.domainId || this.args.domainId;
}
@@ -372,14 +375,25 @@ export class Handler {
async init({ domainId }) {
const xff = await system.get('server.xff');
if (xff) this.request.ip = this.request.headers[xff.toLowerCase()];
await Promise.all([
[this.domain] = await Promise.all([
domain.get(domainId),
this.getSession(),
this.getBdoc(),
]);
if (!this.domain) {
this.args.domainId = 'system';
[this.user, this.UIContext.token] = await Promise.all([
user.getById('system', this.session.uid),
token.createOrUpdate(
token.TYPE_TOKEN, 600, { uid: this.session.uid, domainId },
),
]);
throw new NotFoundError(domainId);
}
[this.user, this.UIContext.token] = await Promise.all([
user.getById(domainId, this.session.uid),
token.createOrUpdate(
token.TYPE_TOKEN, 300, { uid: this.session.uid },
token.TYPE_TOKEN, 600, { uid: this.session.uid, domainId },
),
]);
this.csrfToken = this.getCsrfToken(this.session._id || String.random(32));
@@ -640,11 +654,11 @@ export class ConnectionHandler {
}
}
url(name, kwargs = {}) {
url(name: string, kwargs = {}) {
let res = '#';
const args: any = { ...kwargs };
try {
if (this.args.domainId !== 'system') {
if (this.args.domainId !== 'system' || args.domainId) {
name += '_with_domainId';
args.domainId = args.domainId || this.args.domainId;
}
@@ -676,8 +690,9 @@ export class ConnectionHandler {
try {
this.session = await token.get(this.request.params.token, token.TYPE_TOKEN, true);
} catch (e) {
this.session = { uid: 0 };
this.session = { uid: 0, domainId: 'system' };
}
this.args.domainId = this.session.domainId;
const bdoc = await blacklist.get(this.request.ip);
if (bdoc) throw new BlacklistedError(this.request.ip);
this.user = await user.getById(domainId, this.session.uid);
+10
View File
@@ -29,6 +29,16 @@
name:'name',
required:true
}) }}
{{ form.form_textarea({
label:'Bulletin',
name:'bulletin',
markdown:true,
required:true
}) }}
{{ form.form_text({
label:'Gravatar',
name:'gravatar'
}) }}
<div class="row"><div class="columns">
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
<button type="submit" class="rounded primary button">
+53
View File
@@ -0,0 +1,53 @@
{% extends "layout/home_base.html" %}
{% block home_content %}
<div class="section">
<div class="section__header">
<h1 class="section__title">{{ _('My Domains') }}</h1>
<div class="section__tools">
{% if handler.user.hasPriv(model.builtin.PRIV.PRIV_CREATE_DOMAIN) %}
<a class="primary rounded button" href="{{ url('home_domain_create') }}"><span class="icon icon-add"></span> {{ _('Create Domain') }}</a>
{% endif %}
<a class="tool-button" href="{{ url('wiki', page='help', anchor='domain') }}"><span class="icon icon-help"></span> {{ _("What's domain?") }}</a>
</div>
</div>
<div class="section__body no-padding">
<table class="data-table">
<colgroup>
<col class="col--icon">
<col class="col--name">
<col class="col--role">
<col class="col--action">
</colgroup>
<thead>
<tr>
<th class="col--icon">&nbsp;</th>
<th class="col--name">{{ _('Name') }} ({{ _('ID') }})</th>
<th class="col--role">{{ _('My Role') }}</th>
<th class="col--action">{{ _('Action') }}</th>
</tr>
</thead>
<tbody>
{% for ddoc in ddocs %}
<tr>
<td class="col--icon">
<img src="{{ gravatar(ddoc.gravatar) }}" width="32" height="32">
</td>
<td class="col--name">
{{ ddoc['name'] }} ({{ ddoc['_id'] }})
</td>
<td class="col--role">
{{ dudict[ddoc._id].role|default('default') }}
</td>
<td class="col--action">
<a class="typo-a" href="{{ url('homepage', domainId=ddoc['_id']) }}">{{ _('Visit') }}</a>
{% if canManage[ddoc['_id']] %}
· <a class="typo-a" href="{{ url('domain_manage', domainId=ddoc['_id']) }}">{{ _('Manage') }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
+1 -1
View File
@@ -12,7 +12,7 @@ export default class Sock {
init() {
this.sock = new SockJS(this.url);
this.sock.onopen = () => {
if (this.reconnect) Notification.warn(i18n('Reconnected to the server.'));
if (this.reconnect) Notification.info(i18n('Reconnected to the server.'));
if (this.onopen) this.onopen(this.reconnect);
};
this.sock.onclose = (code, reason) => {