mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
“我的域”功能
This commit is contained in:
1 parent
d4e849a133
commit
eaed0e84f5
11 files changed
+122
-27
No files matched your search
+20
-4
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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)'),
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"> </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 %}
|
||||
@@ -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) => {
|
||||
|
||||
Reference in new issue
Block a user