mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
298 lines
14 KiB
TypeScript
298 lines
14 KiB
TypeScript
/* eslint-disable max-len */
|
|
/* eslint-disable no-await-in-loop */
|
|
import yaml from 'js-yaml';
|
|
import { Dictionary } from 'lodash';
|
|
import moment from 'moment-timezone';
|
|
import { parseLang } from '@hydrooj/utils/lib/lang';
|
|
import { retry } from '@hydrooj/utils/lib/utils';
|
|
import { Setting as _Setting } from '../interface';
|
|
import { Logger } from '../logger';
|
|
import * as bus from '../service/bus';
|
|
import * as builtin from './builtin';
|
|
|
|
type SettingDict = Dictionary<_Setting>;
|
|
|
|
const logger = new Logger('model/setting');
|
|
const countries = moment.tz.countries();
|
|
const tzs = new Set();
|
|
for (const country of countries) {
|
|
const tz = moment.tz.zonesForCountry(country);
|
|
for (const t of tz) tzs.add(t);
|
|
}
|
|
const timezones = Array.from(tzs).sort().map((tz) => [tz, tz]) as [string, string][];
|
|
const langRange: Dictionary<string> = {};
|
|
|
|
for (const lang in global.Hydro.locales) {
|
|
langRange[lang] = global.Hydro.locales[lang].__langname;
|
|
}
|
|
|
|
export const FLAG_HIDDEN = 1;
|
|
export const FLAG_DISABLED = 2;
|
|
export const FLAG_SECRET = 4;
|
|
export const FLAG_PRO = 8;
|
|
|
|
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: SettingDict = {};
|
|
export const DOMAIN_USER_SETTINGS_BY_KEY: SettingDict = {};
|
|
export const DOMAIN_SETTINGS_BY_KEY: SettingDict = {};
|
|
export const SYSTEM_SETTINGS_BY_KEY: SettingDict = {};
|
|
|
|
// eslint-disable-next-line max-len
|
|
export type SettingType = 'text' | 'yaml' | 'number' | 'float' | 'markdown' | 'password' | 'boolean' | 'textarea' | [string, string][] | Record<string, string>;
|
|
|
|
export const Setting = (
|
|
family: string, key: string, value: any = null,
|
|
type: SettingType = 'text', name = '', desc = '', flag = 0,
|
|
): _Setting => {
|
|
let subType = '';
|
|
if (type === 'yaml' && typeof value !== 'string') {
|
|
value = yaml.dump(value);
|
|
type = 'textarea';
|
|
subType = 'yaml';
|
|
}
|
|
return {
|
|
family,
|
|
key,
|
|
value,
|
|
name,
|
|
desc,
|
|
flag,
|
|
subType,
|
|
type: typeof type === 'object' ? 'select' : type,
|
|
range: typeof type === 'object' ? type : null,
|
|
};
|
|
};
|
|
|
|
export const PreferenceSetting = (...settings: _Setting[]) => {
|
|
for (const setting of settings) {
|
|
if (PREFERENCE_SETTINGS.find((s) => s.key === setting.key)) logger.warn(`Duplicate setting key: ${setting.key}`);
|
|
PREFERENCE_SETTINGS.push(setting);
|
|
SETTINGS.push(setting);
|
|
SETTINGS_BY_KEY[setting.key] = setting;
|
|
}
|
|
};
|
|
export const AccountSetting = (...settings: _Setting[]) => {
|
|
for (const setting of settings) {
|
|
if (ACCOUNT_SETTINGS.find((s) => s.key === setting.key)) logger.warn(`Duplicate setting key: ${setting.key}`);
|
|
ACCOUNT_SETTINGS.push(setting);
|
|
SETTINGS.push(setting);
|
|
SETTINGS_BY_KEY[setting.key] = setting;
|
|
}
|
|
};
|
|
export const DomainUserSetting = (...settings: _Setting[]) => {
|
|
for (const setting of settings) {
|
|
if (DOMAIN_USER_SETTINGS.find((s) => s.key === setting.key)) logger.warn(`Duplicate setting key: ${setting.key}`);
|
|
DOMAIN_USER_SETTINGS.push(setting);
|
|
DOMAIN_USER_SETTINGS_BY_KEY[setting.key] = setting;
|
|
}
|
|
};
|
|
export const DomainSetting = (...settings: _Setting[]) => {
|
|
for (const setting of settings) {
|
|
if (DOMAIN_SETTINGS.find((s) => s.key === setting.key)) logger.warn(`Duplicate setting key: ${setting.key}`);
|
|
DOMAIN_SETTINGS.push(setting);
|
|
DOMAIN_SETTINGS_BY_KEY[setting.key] = setting;
|
|
}
|
|
};
|
|
export const SystemSetting = (...settings: _Setting[]) => {
|
|
for (const setting of settings) {
|
|
if (SYSTEM_SETTINGS.find((s) => s.key === setting.key)) logger.warn(`Duplicate setting key: ${setting.key}`);
|
|
SYSTEM_SETTINGS.push(setting);
|
|
SYSTEM_SETTINGS_BY_KEY[setting.key] = setting;
|
|
}
|
|
};
|
|
|
|
const LangSettingNode = {
|
|
family: 'setting_usage',
|
|
key: 'codeLang',
|
|
value: '',
|
|
name: 'codeLang',
|
|
desc: 'Default Code Language',
|
|
flag: 0,
|
|
subType: '',
|
|
type: 'select',
|
|
range: {},
|
|
};
|
|
const ServerLangSettingNode = {
|
|
family: 'setting_server',
|
|
key: 'preference.codeLang',
|
|
value: '',
|
|
name: 'preference.codeLang',
|
|
desc: 'Default Code Language',
|
|
flag: 0,
|
|
subType: '',
|
|
type: 'select',
|
|
range: {},
|
|
};
|
|
|
|
PreferenceSetting(
|
|
Setting('setting_display', 'viewLang', null, langRange, 'UI Language'),
|
|
Setting('setting_display', 'skipAnimate', false, 'boolean', 'Skip Animation'),
|
|
Setting('setting_display', 'timeZone', 'Asia/Shanghai', timezones, 'Timezone'),
|
|
LangSettingNode,
|
|
Setting('setting_usage', 'codeTemplate', '', 'textarea', 'Default Code Template',
|
|
'If left blank, the built-in template of the corresponding language will be used.'),
|
|
);
|
|
|
|
AccountSetting(
|
|
Setting('setting_info', 'avatar', '', 'text', 'Avatar',
|
|
'Allow using gravatar:email qq:id github:name url:link format.'),
|
|
Setting('setting_info', 'qq', null, 'text', 'QQ'),
|
|
Setting('setting_info', 'gender', builtin.USER_GENDER_OTHER, builtin.USER_GENDER_RANGE, 'Gender'),
|
|
Setting('setting_info', 'bio', null, 'markdown', 'Bio'),
|
|
Setting('setting_info', 'school', '', 'text', 'School'),
|
|
Setting('setting_info', 'studentId', '', 'text', 'Student ID'),
|
|
Setting('setting_info', 'phone', null, 'text', 'Phone', null, FLAG_DISABLED),
|
|
Setting('setting_customize', 'backgroundImage',
|
|
'/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image',
|
|
'Choose the background image in your profile page.'),
|
|
Setting('setting_storage', 'unreadMsg', 0, 'number', 'Unread Message Count', null, FLAG_DISABLED | FLAG_HIDDEN),
|
|
Setting('setting_storage', 'badge', '', 'text', 'badge info', null, FLAG_DISABLED | FLAG_HIDDEN),
|
|
);
|
|
|
|
DomainSetting(
|
|
Setting('setting_domain', 'name', 'New domain', 'text', 'name'),
|
|
Setting('setting_domain', 'avatar', '', 'text', 'avatar', 'Will be used as the domain icon.'),
|
|
Setting('setting_domain', 'share', '', 'text', 'Share problem with domain (* for any)'),
|
|
Setting('setting_domain', 'bulletin', '', 'markdown', 'Bulletin'),
|
|
Setting('setting_domain', 'langs', '', 'text', 'Allowed langs', null),
|
|
Setting('setting_storage', 'host', '', 'text', 'Custom host', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
);
|
|
|
|
DomainUserSetting(
|
|
Setting('setting_info', 'displayName', null, 'text', 'Display Name'),
|
|
Setting('setting_storage', 'nAccept', 0, 'number', 'nAccept', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
Setting('setting_storage', 'nSubmit', 0, 'number', 'nSubmit', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
Setting('setting_storage', 'nLike', 0, 'number', 'nLike', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
Setting('setting_storage', 'rp', 0, 'number', 'RP', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
Setting('setting_storage', 'rpInfo', '', 'text'/* JSON */, 'RP Detail', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
Setting('setting_storage', 'rpdelta', 0, 'number', 'RP.delta', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
Setting('setting_storage', 'rank', 0, 'number', 'Rank', null, FLAG_DISABLED | FLAG_HIDDEN),
|
|
Setting('setting_storage', 'level', 0, 'number', 'level', null, FLAG_HIDDEN | FLAG_DISABLED),
|
|
);
|
|
|
|
const ignoreUA = [
|
|
'bingbot',
|
|
'Gatus',
|
|
'Googlebot',
|
|
'Uptime',
|
|
'YandexBot',
|
|
].join('\n');
|
|
|
|
SystemSetting(
|
|
Setting('setting_smtp', 'smtp.user', null, 'text', 'smtp.user', 'SMTP Username'),
|
|
Setting('setting_smtp', 'smtp.pass', null, 'password', 'smtp.pass', 'SMTP Password', FLAG_SECRET),
|
|
Setting('setting_smtp', 'smtp.host', null, 'text', 'smtp.host', 'SMTP Server Host'),
|
|
Setting('setting_smtp', 'smtp.port', 465, 'number', 'smtp.port', 'SMTP Server Port'),
|
|
Setting('setting_smtp', 'smtp.from', null, 'text', 'smtp.from', 'Mail From'),
|
|
Setting('setting_smtp', 'smtp.secure', false, 'boolean', 'smtp.secure', 'SSL'),
|
|
Setting('setting_smtp', 'smtp.verify', true, 'boolean', 'smtp.verify', 'Verify register email'),
|
|
Setting('setting_server', 'server.center', 'https://hydro.ac/center', 'text', 'server.center', '', FLAG_HIDDEN),
|
|
Setting('setting_server', 'server.name', 'Hydro', 'text', 'server.name', 'Server Name'),
|
|
Setting('setting_server', 'server.displayName', 'Hydro', 'text', 'server.name', 'Server Name (Global Display)', FLAG_PRO),
|
|
Setting('setting_server', 'server.url', '/', 'text', 'server.url', 'Server BaseURL'),
|
|
Setting('setting_server', 'server.upload', '256m', 'text', 'server.upload', 'Max upload file size'),
|
|
Setting('setting_server', 'server.cdn', '/', 'text', 'server.cdn', 'CDN Prefix'),
|
|
Setting('setting_server', 'server.port', 8888, 'number', 'server.port', 'Server Port'),
|
|
Setting('setting_server', 'server.xff', null, 'text', 'server.xff', 'IP Header'),
|
|
Setting('setting_server', 'server.xhost', null, 'text', 'server.xhost', 'Hostname Header'),
|
|
Setting('setting_server', 'server.language', 'zh_CN', langRange, 'server.language', 'Default display language'),
|
|
Setting('setting_server', 'server.login', true, 'boolean', 'server.login', 'Allow builtin-login', FLAG_PRO),
|
|
Setting('setting_server', 'server.message', true, 'boolean', 'server.message', 'Allow users send messages'),
|
|
Setting('setting_server', 'server.blog', true, 'boolean', 'server.blog', 'Allow users post blog'),
|
|
Setting('setting_server', 'server.checkUpdate', true, 'boolean', 'server.checkUpdate', 'Daily update check'),
|
|
Setting('setting_server', 'server.ignoreUA', ignoreUA, 'textarea', 'server.ignoreUA', 'ignoredUA'),
|
|
ServerLangSettingNode,
|
|
Setting('setting_limits', 'limit.problem_files_max', 100, 'number', 'limit.problem_files_max', 'Max files per problem'),
|
|
Setting('setting_limits', 'limit.problem_files_max_size', 256 * 1024 * 1024, 'number', 'limit.problem_files_max_size', 'Max files size per problem'),
|
|
Setting('setting_limits', 'limit.user_files', 100, 'number', 'limit.user_files', 'Max files for user'),
|
|
Setting('setting_limits', 'limit.user_files_size', 128 * 1024 * 1024, 'number', 'limit.user_files_size', 'Max total file size for user'),
|
|
Setting('setting_limits', 'limit.contest_files', 100, 'number', 'limit.contest_files', 'Max files for contest'),
|
|
Setting('setting_limits', 'limit.contest_files_size', 128 * 1024 * 1024, 'number', 'limit.contest_files_size', 'Max total file size for contest'),
|
|
Setting('setting_limits', 'limit.submission', 60, 'number', 'limit.submission', 'Max submission count per minute'),
|
|
Setting('setting_limits', 'limit.submission_user', 15, 'number', 'limit.submission_user', 'Max submission count per user per minute'),
|
|
Setting('setting_limits', 'limit.pretest', 60, 'number', 'limit.pretest', 'Max pretest count per minute'),
|
|
Setting('setting_basic', 'avatar.gravatar_url', '//cn.gravatar.com/avatar/', 'text', 'avatar.gravatar_url', 'Gravatar URL Prefix'),
|
|
Setting('setting_basic', 'default.priv', builtin.PRIV.PRIV_DEFAULT, 'number', 'default.priv', 'Default Privilege', FLAG_PRO),
|
|
Setting('setting_basic', 'discussion.nodes', builtin.DEFAULT_NODES, 'yaml', 'discussion.nodes', 'Discussion Nodes'),
|
|
Setting('setting_basic', 'problem.categories', builtin.CATEGORIES, 'yaml', 'problem.categories', 'Problem Categories'),
|
|
Setting('setting_basic', 'pagination.problem', 100, 'number', 'pagination.problem', 'Problems per page'),
|
|
Setting('setting_basic', 'pagination.contest', 20, 'number', 'pagination.contest', 'Contests per page'),
|
|
Setting('setting_basic', 'pagination.discussion', 50, 'number', 'pagination.discussion', 'Discussions per page'),
|
|
Setting('setting_basic', 'pagination.record', 100, 'number', 'pagination.record', 'Records per page'),
|
|
Setting('setting_basic', 'pagination.solution', 20, 'number', 'pagination.solution', 'Solutions per page'),
|
|
Setting('setting_basic', 'pagination.training', 10, 'number', 'pagination.training', 'Trainings per page'),
|
|
Setting('setting_basic', 'pagination.reply', 50, 'number', 'pagination.reply', 'Replies per page'),
|
|
Setting('setting_session', 'session.keys', [String.random(32)], 'text', 'session.keys', 'session.keys', FLAG_HIDDEN),
|
|
Setting('setting_session', 'session.secure', false, 'boolean', 'session.secure', 'session.secure', FLAG_HIDDEN),
|
|
Setting('setting_session', 'session.saved_expire_seconds', 3600 * 24 * 30,
|
|
'number', 'session.saved_expire_seconds', 'Saved session expire seconds'),
|
|
Setting('setting_session', 'session.unsaved_expire_seconds', 3600 * 3,
|
|
'number', 'session.unsaved_expire_seconds', 'Unsaved session expire seconds'),
|
|
Setting('setting_storage', 'db.ver', 0, 'number', 'db.ver', 'Database version', FLAG_DISABLED | FLAG_HIDDEN),
|
|
Setting('setting_storage', 'installid', String.random(64), 'text', 'installid', 'Installation ID', FLAG_HIDDEN | FLAG_DISABLED),
|
|
);
|
|
|
|
// eslint-disable-next-line import/no-mutable-exports
|
|
export let langs = {};
|
|
|
|
bus.on('app/started', async () => {
|
|
logger.debug('Ensuring settings');
|
|
const system = global.Hydro.model.system;
|
|
for (const setting of SYSTEM_SETTINGS) {
|
|
if (setting.value) {
|
|
const current = await global.Hydro.service.db.collection('system').findOne({ _id: setting.key });
|
|
if (!current || current.value == null || current.value === '') {
|
|
await retry(system.set, setting.key, setting.value);
|
|
}
|
|
}
|
|
}
|
|
try {
|
|
langs = parseLang(system.get('hydrooj.langs'));
|
|
global.Hydro.model.setting.langs = langs;
|
|
const range = {};
|
|
for (const key in langs) range[key] = langs[key].display;
|
|
LangSettingNode.range = range;
|
|
ServerLangSettingNode.range = range;
|
|
} catch (e) { /* Ignore */ }
|
|
});
|
|
|
|
bus.on('system/setting', (args) => {
|
|
if (args.hydrooj?.langs) {
|
|
langs = parseLang(args.hydrooj.langs);
|
|
global.Hydro.model.setting.langs = langs;
|
|
const range = {};
|
|
for (const key in langs) range[key] = langs[key].display;
|
|
LangSettingNode.range = range;
|
|
ServerLangSettingNode.range = range;
|
|
}
|
|
});
|
|
|
|
global.Hydro.model.setting = {
|
|
Setting,
|
|
PreferenceSetting,
|
|
AccountSetting,
|
|
DomainSetting,
|
|
DomainUserSetting,
|
|
SystemSetting,
|
|
FLAG_HIDDEN,
|
|
FLAG_DISABLED,
|
|
FLAG_SECRET,
|
|
FLAG_PRO,
|
|
PREFERENCE_SETTINGS,
|
|
ACCOUNT_SETTINGS,
|
|
SETTINGS,
|
|
SETTINGS_BY_KEY,
|
|
SYSTEM_SETTINGS,
|
|
SYSTEM_SETTINGS_BY_KEY,
|
|
DOMAIN_SETTINGS,
|
|
DOMAIN_SETTINGS_BY_KEY,
|
|
DOMAIN_USER_SETTINGS,
|
|
DOMAIN_USER_SETTINGS_BY_KEY,
|
|
langs,
|
|
};
|