diff --git a/hydro/entry/master.ts b/hydro/entry/master.ts index 19ab5252f..e9dafb728 100644 --- a/hydro/entry/master.ts +++ b/hydro/entry/master.ts @@ -59,6 +59,13 @@ export async function load(call) { } } } + for (const postInit of global.Hydro.postInit) { + try { + await postInit(); + } catch (e) { + console.error(e); + } + } pending = []; return await modelSystem.get('server.worker'); } diff --git a/hydro/entry/worker.ts b/hydro/entry/worker.ts index 989540b32..589041e70 100644 --- a/hydro/entry/worker.ts +++ b/hydro/entry/worker.ts @@ -52,6 +52,9 @@ export async function load() { } for (const i of builtinScript) require(`../script/${i}`); await script(pending, fail, active); + for (const postInit of global.Hydro.postInit) { + await postInit(); + } pending = []; await server.start(); setInterval(() => { diff --git a/hydro/handler/homework.ts b/hydro/handler/homework.ts index 9e078373d..79f4e6a8d 100644 --- a/hydro/handler/homework.ts +++ b/hydro/handler/homework.ts @@ -194,6 +194,7 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler { class HomeworkCreateHandler extends HomeworkHandler { async get() { const beginAt = moment().add(1, 'day'); + console.log(beginAt, beginAt.tz(this.user.timeZone)); const penaltySince = beginAt.clone().add(7, 'days'); const path = [ ['Hydro', 'homepage'], diff --git a/hydro/handler/misc.ts b/hydro/handler/misc.ts index 86e00fbd1..7d432cdca 100644 --- a/hydro/handler/misc.ts +++ b/hydro/handler/misc.ts @@ -1,3 +1,4 @@ +/* eslint-disable camelcase */ import { ObjectID } from 'mongodb'; import { PRIV } from '../model/builtin'; import * as system from '../model/system'; @@ -74,18 +75,33 @@ class MarkdownHandler extends Handler { class SockToken extends Handler { async get() { - this.response.body = { token: this.csrfToken }; + this.response.body = { token: this.UIContext.token }; } } class UiSettingsHandler extends Handler { async get() { const [ - header, nav, headerBackground, + header_logo, header_logo_2x, + nav_logo_dark, nav_logo_light, + nav_logo_dark_2x, nav_logo_light_2x, + header_background, header_background_2x, ] = await system.getMany([ - 'ui.header', 'ui.nav', 'ui.headerBackground', + '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', ]); - this.response.body = await this.renderHTML('extra.css', { header, nav, headerBackground }); + this.response.body = await this.renderHTML('extra.css', { + header_logo, + header_logo_2x, + nav_logo_dark, + nav_logo_light, + nav_logo_dark_2x, + nav_logo_light_2x, + header_background, + header_background_2x, + }); this.response.type = 'text/css'; } } diff --git a/hydro/interface.ts b/hydro/interface.ts index 2a23f7f8c..ae8257068 100644 --- a/hydro/interface.ts +++ b/hydro/interface.ts @@ -225,6 +225,7 @@ declare global { ui: any, error: typeof import('./error'), locales: { [id: string]: { [key: string]: string } }, + postInit: Function[], }, nodeModules: any, onDestory: Function[], diff --git a/hydro/loader.ts b/hydro/loader.ts index 4aa734de9..5b7b21f8e 100644 --- a/hydro/loader.ts +++ b/hydro/loader.ts @@ -24,6 +24,7 @@ global.Hydro = { // @ts-ignore error: {}, locales: {}, + postInit: [], }; global.onDestory = []; diff --git a/hydro/model/setting.ts b/hydro/model/setting.ts index adb04180e..1bddc0480 100644 --- a/hydro/model/setting.ts +++ b/hydro/model/setting.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-await-in-loop */ import moment from 'moment-timezone'; import * as builtin from './builtin'; import { Setting as _Setting } from '../interface'; @@ -14,12 +15,12 @@ export const FLAG_HIDDEN = 1; export const FLAG_DISABLED = 2; export const FLAG_SECRET = 4; -export const PREFERENCE_SETTINGS = []; -export const ACCOUNT_SETTINGS = []; -export const DOMAIN_SETTINGS = []; -export const DOMAIN_USER_SETTINGS = []; -export const SYSTEM_SETTINGS = []; -export const SETTINGS = []; +export const PREFERENCE_SETTINGS: _Setting[] = []; +export const ACCOUNT_SETTINGS: _Setting[] = []; +export const DOMAIN_SETTINGS: _Setting[] = []; +export const DOMAIN_USER_SETTINGS: _Setting[] = []; +export const SYSTEM_SETTINGS: _Setting[] = []; +export const SETTINGS: _Setting[] = []; export const SETTINGS_BY_KEY = {}; export const DOMAIN_USER_SETTINGS_BY_KEY = {}; export const DOMAIN_SETTINGS_BY_KEY = {}; @@ -70,7 +71,7 @@ PreferenceSetting( // TODO generate by global.Hydro.locales Setting('setting_display', 'viewLang', builtin.VIEW_LANGS.map((i) => [i.code, i.name]), 'zh_CN', 'select', 'UI Language'), - Setting('setting_display', 'timezone', timezones as [string, string][], + Setting('setting_display', 'timeZone', timezones as [string, string][], 'Asia/Shanghai', 'select', 'Timezone'), Setting('setting_usage', 'codeLang', builtin.LANG_TEXTS, null, 'select', 'Default Code Language'), @@ -118,9 +119,14 @@ SystemSetting( Setting('setting_server', 'server.port', null, 8888, 'number', 'Server Port'), Setting('setting_server', 'server.xff', null, null, 'text', 'IP Header'), Setting('setting_server', 'server.log', null, false, 'boolean', 'Disable Access Log'), - Setting('setting_ui', 'ui.header', null, null, 'text', 'Header Logo'), - Setting('setting_ui', 'ui.headerBackground', null, null, 'text', 'Header Background'), - Setting('setting_ui', 'ui.nav', null, null, 'text', 'Nav Logo'), + 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.png', 'text', 'Header Logo@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)'), + Setting('setting_ui', 'ui.nav_logo_dark_2x', null, '/components/navigation/nav-logo-small@2x_dark.png', 'text', 'Nav Logo@2x(Dark)'), Setting('setting_oauth', 'oauth.githubappid', null, null, 'text', 'Github Oauth AppID'), Setting('setting_oauth', 'oauth.githubsecret', null, null, 'text', 'Github Oauth Secret', null, FLAG_SECRET), Setting('setting_oauth', 'oauth.googleappid', null, null, 'text', 'Google Oauth ClientID', null), @@ -135,6 +141,19 @@ SystemSetting( Setting('setting_constant', 'REPLY_PER_PAGE', null, 50, 'number', 'Reply per Page'), ); +global.Hydro.postInit.push( + async () => { + for (const setting of SYSTEM_SETTINGS) { + if (setting.value) { + const current = await global.Hydro.model.system.get(setting.key); + if (current === null || current === '') { + await global.Hydro.model.system.set(setting.key, setting.value); + } + } + } + }, +); + global.Hydro.model.setting = { Setting, PreferenceSetting, diff --git a/hydro/model/user.ts b/hydro/model/user.ts index f3dc3f867..1eac70c8e 100644 --- a/hydro/model/user.ts +++ b/hydro/model/user.ts @@ -262,8 +262,15 @@ export function setRoles(domainId: string, roles: any) { export async function getRoles(domainId: string) { const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_USER).sort('_id', 1).toArray(); const roles = []; + const r = []; for (const doc of docs) { roles.push({ _id: doc.docId, perm: doc.content }); + r.push(doc.docId); + } + for (const role in BUILTIN_ROLES) { + if (!r.includes(role)) { + roles.push({ _id: role, perm: BUILTIN_ROLES[role].perm }); + } } return roles; } diff --git a/hydro/service/monitor.ts b/hydro/service/monitor.ts index a2e8ebf5e..49b0a477a 100644 --- a/hydro/service/monitor.ts +++ b/hydro/service/monitor.ts @@ -13,14 +13,16 @@ export async function update() { global.Hydro.stat.reqCount = 0; } -export async function postInit() { - const info = await sysinfo.get(); - await coll.updateOne( - { mid: info.mid, type: 'server' }, - { $set: { ...info, updateAt: new Date(), type: 'server' } }, - { upsert: true }, - ); - setInterval(update, 60 * 1000); -} +global.Hydro.postInit.push( + async () => { + const info = await sysinfo.get(); + await coll.updateOne( + { mid: info.mid, type: 'server' }, + { $set: { ...info, updateAt: new Date(), type: 'server' } }, + { upsert: true }, + ); + setInterval(update, 60 * 1000); + }, +); -global.Hydro.service.monitor = { update, postInit }; +global.Hydro.service.monitor = { update }; diff --git a/hydro/service/server.ts b/hydro/service/server.ts index e50701b59..3bc41500f 100644 --- a/hydro/service/server.ts +++ b/hydro/service/server.ts @@ -95,7 +95,6 @@ const Tools: Array<[Converter, Validator, boolean?]> = [ ], ]; -export function param(name: string): MethodDecorator; export function param(name: string, type: Types, validate: Validator): MethodDecorator; export function param(name: string, type?: Types, isOptional?: boolean): MethodDecorator; export function param( @@ -138,7 +137,7 @@ export function param(name: string, ...args: any): MethodDecorator { if (!target.__param[target.constructor.name][funcName]) { target.__param[target.constructor.name][funcName] = [{ name: 'domainId', type: 'string' }]; const originalMethod = obj.value; - obj.value = function func(rawArgs: any) { + obj.value = function validate(rawArgs: any) { const c = []; const arglist: ParamOption[] = this.__param[target.constructor.name][funcName]; for (const item of arglist) { @@ -146,7 +145,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); + throw new ValidationError(item.name, rawArgs[item.name]); } } if (item.convert) c.push(item.convert(rawArgs[item.name])); @@ -163,7 +162,7 @@ export function param(name: string, ...args: any): MethodDecorator { export function requireCsrfToken(target: any, funcName: string, obj: any) { const originalMethod = obj.value; - obj.value = async function func(...args: any[]) { + obj.value = async function checkCsrfToken(...args: any[]) { if (this.getCsrfToken(this.session._id) !== this.args.csrfToken) { throw new CsrfTokenError(this.args.csrfToken); } diff --git a/package.json b/package.json index ebee4a7d3..11eba3722 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.7.3", + "version": "2.7.4", "main": "hydro/loader.ts", "repository": "https://github.com/hydro-dev/Hydro.git", "author": "masnn", diff --git a/templates/error.html b/templates/error.html index fd482c500..0f9553a31 100644 --- a/templates/error.html +++ b/templates/error.html @@ -7,7 +7,7 @@
{{ _(error.message).format(error.params) }}
+{{ _(error.message).format.apply(error.params) }}
{{ _('Technical Information') }}:
{{ _('Type') }}: {{ error.code }}
{{ _('Arguments') }}: diff --git a/templates/extra.css b/templates/extra.css index af040a676..f27b86956 100644 --- a/templates/extra.css +++ b/templates/extra.css @@ -1,10 +1,54 @@ -/* {% if header %} */ +/* {% if header_logo %} */ + .header__logo { - background: url({{ header }}) !important + background: url({{header_logo}}) } + /* {% endif %} */ -/* {% if headerBackground %} */ + +/* {% if header_background %} */ + .header { - background: url({{ headerBackground }}) !important + background: url({{header_background}}) } + /* {% endif %} */ + +/* {% if nav_logo_light %} */ + +.nav__logo, .header--mobile__domain.is--system { + background-image: url({{nav_logo_light}}) +} + +/* {% endif %} */ + +/* {% if nav_logo_dark %} */ + +.nav.floating .nav__logo, .nojs .nav .nav__logo { + background-image: url({{nav_logo_dark}}) !important +} + +/* {% endif %} */ + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx), only screen and (min-resolution: 144dpi) { + /* {% if header_2x %} */ + .header__logo { + background-image: url({{header_2x}}) + } + /* {% endif %} */ + /* {% if header_background_2x %} */ + .header { + background-image: url({{header_background_2x}}) + } + /* {% endif %} */ + /* {% if nav_logo_light_2x %} */ + .nav__logo, .header--mobile__domain.is--system { + background-image: url({{nav_logo_light_2x}}) !important + } + /* {% endif %} */ + /* {% if nav_logo_dark_2x %} */ + .nav.floating .nav__logo, .nojs .nav .nav__logo, .header--mobile__domain.is--system { + background-image: url({{nav_logo_dark_2x}}) !important + } + /* {% endif %} */ +} \ No newline at end of file diff --git a/templates/layout/html5.html b/templates/layout/html5.html index 431735cd1..8c0d521d0 100644 --- a/templates/layout/html5.html +++ b/templates/layout/html5.html @@ -30,7 +30,7 @@ var _htmlNode = document.documentElement; _htmlNode.className = _htmlNode.className.replace(' nojs', ' hasjs'); var UiContext = {{ handler.UIContext|json|safe }}; - var UserContext = {{ currentUser|json|safe }}; + var UserContext = {{ UserContext|json|safe }}; {% endif %} diff --git a/ui/components/header/header.page.styl b/ui/components/header/header.page.styl index a546f22c8..7188d212d 100644 --- a/ui/components/header/header.page.styl +++ b/ui/components/header/header.page.styl @@ -33,15 +33,13 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l display: block width: rem($header-logo-width-real) height: rem($header-logo-height + $header-gap-mini + $nav-item-height) - background: url(header-logo.png) no-repeat + background: no-repeat background-size: rem($header-logo-width-real) rem($header-logo-height-real) background-position: 0 rem($header-gap-mini + $nav-item-height - ($header-logo-height-real - $header-logo-height)) transition: transform .5s, opacity .5s transition-timing-function: ease-out-cubic transform-origin: 0 0 opacity: 1 - +retina() - background-image: url(./header-logo@2x.png) +mobile() display: inline-block @@ -152,9 +150,7 @@ $header-gap-mini-domain = ($header-bg-height-mini - $nav-item-height - $header-l width: $nav-logo-width // no rem height: $nav-logo-height // no rem margin-top: rem(10px) - background: url('../navigation/nav-logo-small_light.png') no-repeat +retina() - background: url('../navigation/nav-logo-small@2x_light.png') no-repeat background-size: $nav-logo-width $nav-logo-height // no rem .header--mobile__system diff --git a/ui/components/navigation/navigation.page.styl b/ui/components/navigation/navigation.page.styl index 540b33860..77d44838b 100644 --- a/ui/components/navigation/navigation.page.styl +++ b/ui/components/navigation/navigation.page.styl @@ -113,11 +113,10 @@ opacity: 0 height: rem($nav-logo-height) margin: rem((($nav-item-height - $nav-logo-height) / 2) 0) - background: url('nav-logo-small_light.png') no-repeat + background: no-repeat transition: width .3s, opacity .3s transition-timing-function: ease-out-cubic +retina() - background: url('nav-logo-small@2x_light.png') no-repeat background-size: $nav-logo-width $nav-logo-height &:hover @@ -155,11 +154,6 @@ background-color: $primary-color color: #FFF - .nav__logo - background-image: url('nav-logo-small_dark.png') - +retina() - background-image: url('nav-logo-small@2x_dark.png') - .nav--shadow.floating box-shadow: 0 0 6px rgba(#000, 0.3)