mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: add avatar provider API
This commit is contained in:
1 parent
2db1570d1b
commit
f1f956202f
42 files changed
+122
-80
No files matched your search
@@ -35,7 +35,6 @@ __langname: 한국어
|
||||
'Role {1} already exists in domain {0}.': '역할 {1}이(가) 이미 {0}에 존재합니다.'
|
||||
'Solved {0} problems, RP: {1} (No. {2})': '총 {0} 문제 해결.RP: {1} (No. {2})'
|
||||
'The value `{1}` of {0} already exists.': '{0} 의 값 `{1}` 이 이미 존재합니다.'
|
||||
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.': '<a href="https://cn.gravatar.com/" target="_blank">Gravatar</a>를 만들어 보세요.'
|
||||
'Yes': 예
|
||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 난이도가 알고리즘에 의해 정해지지 않은 경우에만 적용됩니다.
|
||||
"Split by ', '.": “, ”“,”으로 구분
|
||||
@@ -312,7 +311,6 @@ Formula blocks: 공식 블록
|
||||
fs_upload: 업로드
|
||||
Gender Visibility: 성별 공개
|
||||
Gender: 성
|
||||
Gravatar Email: Gravatar 이메일
|
||||
Hard Deadline: 마감일
|
||||
Hash: 해시
|
||||
Have ALL PERMISSIONS in this domain: 이 그룹에 대한 모든 권한
|
||||
|
||||
@@ -35,7 +35,6 @@ __langname: 简体中文
|
||||
'Role {1} already exists in domain {0}.': '域 {0} 中已存在角色 {1}。'
|
||||
'Solved {0} problems, RP: {1} (No. {2})': '解决了 {0} 道题目,RP: {1} (No. {2})'
|
||||
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
||||
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.': '我们使用 <a href="https://cn.gravatar.com/" target="_blank">Gravatar</a> 服务来呈现您的头像。'
|
||||
'Yes': 是
|
||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
||||
"Split by ',' '.'": 由“, ”或“,”分隔。
|
||||
@@ -81,6 +80,7 @@ Attend Contest: 参加比赛
|
||||
Attend contests: 参加比赛
|
||||
Attended: 已参加
|
||||
author: 作者
|
||||
Avatar: 头像
|
||||
Be Copied: 被复制
|
||||
Begin at: 开始于
|
||||
Begin Date: 开始日期
|
||||
@@ -326,7 +326,6 @@ Formula blocks: 公式区块
|
||||
fs_upload: 上传文件
|
||||
Gender Visibility: 性别可见性
|
||||
Gender: 性别
|
||||
Gravatar Email: Gravatar Email 地址
|
||||
Hard Deadline: 最终截止时间
|
||||
Hash: 散列
|
||||
Have ALL PERMISSIONS in this domain: 在此域中拥有全部权限
|
||||
|
||||
@@ -195,7 +195,6 @@ Forgot password or username?: 忘記密碼或者使用者名?
|
||||
Formula blocks: 公式區塊
|
||||
Gender Visibility: 性別可見性
|
||||
Gender: 性別
|
||||
Gravatar Email: Gravatar Email 地址
|
||||
Hard Deadline: 最終截止時間
|
||||
Have ALL PERMISSIONS in this domain: 在此域中擁有全部許可權
|
||||
Help: 幫助
|
||||
@@ -539,7 +538,6 @@ Visible to registered users: 對註冊使用者可見
|
||||
Visit: 檢視
|
||||
Vote problem solutions: 為題解投票
|
||||
Vote: 投票
|
||||
We use Gravatar to present your avatar icon.: 我們使用 Gravatar 服務來呈現您的頭像。
|
||||
We will check code similarity after this contest.: 我們會在賽後檢查程式碼相似度。
|
||||
WeChat Visibility: 微信可見性
|
||||
WeChat: 微信
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.23.6",
|
||||
"version": "2.23.7",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "dist/loader.js",
|
||||
"typings": "dist/loader.d.ts",
|
||||
|
||||
@@ -13,6 +13,7 @@ export const builtinLib = [
|
||||
'jwt', 'download', 'i18n', 'mail', 'useragent',
|
||||
'crypto', 'misc', 'paginate', 'hash.hydro', 'rank',
|
||||
'validator', 'ui', 'testdataConfig', 'difficulty', 'content',
|
||||
'avatar',
|
||||
];
|
||||
|
||||
export const builtinModel = [
|
||||
|
||||
@@ -14,7 +14,7 @@ import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
|
||||
import {
|
||||
DEFAULT_NODES, PERM, PERMS_BY_FAMILY, PRIV,
|
||||
} from '../model/builtin';
|
||||
import { gravatar } from '../lib/misc';
|
||||
import avatar from '../lib/avatar';
|
||||
import paginate from '../lib/paginate';
|
||||
import {
|
||||
Route, Handler, Types, param, query, post,
|
||||
@@ -297,7 +297,7 @@ class DomainSearchHandler extends Handler {
|
||||
async get(domainId: string, q: string) {
|
||||
const ddocs = await domain.getPrefixSearch(q, 20);
|
||||
for (let i = 0; i < ddocs.length; i++) {
|
||||
ddocs[i].gravatar = ddocs[i].gravatar ? gravatar(ddocs[i].gravatar) : '/img/team_avatar.png';
|
||||
ddocs[i].avatarUrl = ddocs[i].avatar ? avatar(ddocs[i].avatar, 64) : '/img/team_avatar.png';
|
||||
}
|
||||
this.response.body = ddocs;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ import * as bus from '../service/bus';
|
||||
import {
|
||||
Route, Connection, Handler, ConnectionHandler, param, Types,
|
||||
} from '../service/server';
|
||||
import * as misc from '../lib/misc';
|
||||
import { md5 } from '../lib/crypto';
|
||||
import { isPassword, isEmail } from '../lib/validator';
|
||||
import avatar from '../lib/avatar';
|
||||
import * as mail from '../lib/mail';
|
||||
import * as contest from '../model/contest';
|
||||
import message from '../model/message';
|
||||
@@ -24,7 +25,6 @@ import * as discussion from '../model/discussion';
|
||||
import token from '../model/token';
|
||||
import * as training from '../model/training';
|
||||
import { PERM, PRIV } from '../model/builtin';
|
||||
import { isPassword, isEmail } from '../lib/validator';
|
||||
|
||||
const { geoip, useragent } = global.Hydro.lib;
|
||||
|
||||
@@ -309,13 +309,13 @@ class HomeDomainCreateHandler extends Handler {
|
||||
@param('id', Types.Name)
|
||||
@param('name', Types.Title)
|
||||
@param('bulletin', Types.Content)
|
||||
@param('gravatar', Types.Content, true, isEmail)
|
||||
async post(_: string, id: string, name: string, bulletin: string, gravatar: string) {
|
||||
@param('avatar', Types.Content, true, isEmail)
|
||||
async post(_: string, id: string, name: string, bulletin: string, avatar: string) {
|
||||
const doc = await domain.get(id);
|
||||
if (doc) throw new DomainAlreadyExistsError(id);
|
||||
gravatar = gravatar || this.user.gravatar || this.user.mail || 'guest@hydro.local';
|
||||
avatar = avatar || this.user.avatar || `gravatar:${this.user.mail}` || 'gravatar:guest@hydro.local';
|
||||
const domainId = await domain.add(id, this.user._id, name, bulletin);
|
||||
await domain.edit(domainId, { gravatar });
|
||||
await domain.edit(domainId, { avatar });
|
||||
await domain.setUserRole(domainId, this.user._id, 'root');
|
||||
this.response.redirect = this.url('domain_dashboard', { domainId });
|
||||
this.response.body = { domainId };
|
||||
@@ -338,7 +338,7 @@ class HomeMessagesHandler extends Handler {
|
||||
if (!parsed[target]) {
|
||||
parsed[target] = {
|
||||
_id: target,
|
||||
udoc: { ...udict[target], gravatar: misc.gravatar(udict[target].gravatar) },
|
||||
udoc: { ...udict[target], avatarUrl: avatar(udict[target].avatar) },
|
||||
messages: [],
|
||||
};
|
||||
}
|
||||
@@ -358,7 +358,7 @@ class HomeMessagesHandler extends Handler {
|
||||
async postSend(domainId: string, uid: number, content: string) {
|
||||
const udoc = await user.getById('system', uid);
|
||||
if (!udoc) throw new UserNotFoundError(uid);
|
||||
if (udoc.gravatar) udoc.gravatar = misc.gravatar(udoc.gravatar);
|
||||
if (udoc.avatar) udoc.avatarUrl = avatar(udoc.avatar);
|
||||
const mdoc = await message.send(this.user._id, uid, content, message.FLAG_UNREAD);
|
||||
this.back({ mdoc, udoc });
|
||||
}
|
||||
@@ -391,7 +391,7 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
|
||||
async onMessageReceived(uid: number, mdoc: Mdoc) {
|
||||
if (uid !== this.user._id) return;
|
||||
const udoc = await user.getById(this.domainId, mdoc.from);
|
||||
udoc.gravatar_url = misc.gravatar(udoc.gravatar, 64);
|
||||
udoc.avatarUrl = avatar(udoc.avatar, 64);
|
||||
this.send({ udoc, mdoc });
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
UserNotFoundError, SystemError, BlacklistedError,
|
||||
UserFacingError, LoginError,
|
||||
} from '../error';
|
||||
import { User } from '../interface';
|
||||
import {
|
||||
Route, Handler, Types, param, post,
|
||||
} from '../service/server';
|
||||
@@ -17,9 +18,8 @@ import * as system from '../model/system';
|
||||
import { PERM, PRIV } from '../model/builtin';
|
||||
import { isEmail, isPassword, isUname } from '../lib/validator';
|
||||
import { sendMail } from '../lib/mail';
|
||||
import * as misc from '../lib/misc';
|
||||
import paginate from '../lib/paginate';
|
||||
import { User } from '../interface';
|
||||
import avatar from '../lib/avatar';
|
||||
|
||||
class UserLoginHandler extends Handler {
|
||||
async get() {
|
||||
@@ -241,7 +241,7 @@ class UserSearchHandler extends Handler {
|
||||
if (udoc) udocs.push(udoc);
|
||||
if (!exactMatch) udocs.push(...await user.getPrefixList(domainId, q, 20));
|
||||
for (const i in udocs) {
|
||||
udocs[i].gravatar = misc.gravatar(udocs[i].gravatar || '');
|
||||
udocs[i].avatarUrl = avatar(udocs[i].avatar);
|
||||
}
|
||||
this.response.body = udocs;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ export interface DomainDoc extends Record<string, any> {
|
||||
_id: string,
|
||||
owner: number,
|
||||
roles: Dictionary<string>,
|
||||
gravatar: string,
|
||||
avatar: string,
|
||||
bulletin: string,
|
||||
_join?: any,
|
||||
host?: string,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { md5 } from './crypto';
|
||||
|
||||
type AvatarProvider = (src: string, size: number) => string;
|
||||
|
||||
export const providers: Record<string, AvatarProvider> = {
|
||||
gravatar: (email, size) => `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${size || 32}`,
|
||||
qq: (id) => `//q1.qlogo.cn/g?b=qq&nk=${/(\d+)/g.exec(id)[1]}&s=640`,
|
||||
github: (id, size) => `//github.com/${id}.png?size=${Math.min(size, 460)}`,
|
||||
};
|
||||
|
||||
function avatar(src: string, size = 64) {
|
||||
const index = src.indexOf(':');
|
||||
if (index === -1) return providers.gravatar('', size);
|
||||
const [provider, str] = [src.substr(0, index), src.substr(index + 1, src.length)];
|
||||
if (!providers[provider] || !str) return providers.gravatar('', size);
|
||||
return providers[provider](str, size);
|
||||
}
|
||||
|
||||
export default avatar;
|
||||
global.Hydro.lib.avatar = avatar;
|
||||
@@ -1,7 +1,6 @@
|
||||
import AnsiUp from 'ansi_up';
|
||||
import { ObjectID } from 'mongodb';
|
||||
import { size, formatSeconds } from '@hydrooj/utils/lib/utils';
|
||||
import { md5 } from './crypto';
|
||||
|
||||
export { size, formatSeconds } from '@hydrooj/utils/lib/utils';
|
||||
|
||||
@@ -11,10 +10,6 @@ export function ansiToHtml(str: string) {
|
||||
return AU.ansi_to_html(str);
|
||||
}
|
||||
|
||||
export function gravatar(email: string, s = 32) {
|
||||
return `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${s}`;
|
||||
}
|
||||
|
||||
export function datetimeSpan(dt: Date | ObjectID, relative = true, format = '%Y-%m-%d %H:%M:%S') {
|
||||
if (!dt) return 'DATETIME_SPAN_ERROR';
|
||||
if (dt instanceof ObjectID) dt = new Date(dt.generationTime * 1000);
|
||||
@@ -53,5 +48,5 @@ export function* paginate(page: number, numPages: number) {
|
||||
}
|
||||
|
||||
global.Hydro.lib.misc = {
|
||||
gravatar, datetimeSpan, paginate, size, formatSeconds, ansiToHtml,
|
||||
datetimeSpan, paginate, size, formatSeconds, ansiToHtml,
|
||||
};
|
||||
@@ -47,7 +47,7 @@ class DomainModel {
|
||||
name,
|
||||
bulletin,
|
||||
roles: {},
|
||||
gravatar: '',
|
||||
avatar: '',
|
||||
};
|
||||
await bus.serial('domain/create', ddoc);
|
||||
await coll.insertOne(ddoc);
|
||||
|
||||
@@ -107,8 +107,8 @@ PreferenceSetting(
|
||||
);
|
||||
|
||||
AccountSetting(
|
||||
Setting('setting_info', 'gravatar', null, 'text', 'Gravatar Email',
|
||||
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.'),
|
||||
Setting('setting_info', 'avatar', '', 'text', 'Avatar',
|
||||
'Allow using gravatar:email qq:id github:name 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'),
|
||||
@@ -122,7 +122,7 @@ AccountSetting(
|
||||
|
||||
DomainSetting(
|
||||
Setting('setting_domain', 'name', 'New domain', 'text', 'name'),
|
||||
Setting('setting_domain', 'gravatar', '', 'text', 'gravatar', 'Will be used as the domain icon.'),
|
||||
Setting('setting_domain', 'avatar', '', 'text', 'avatar', 'Will be used as the domain icon.'),
|
||||
Setting('setting_domain', 'bulletin', '', 'markdown', 'Bulletin'),
|
||||
Setting('setting_storage', 'host', '', 'text', 'Custom host', null, FLAG_HIDDEN | FLAG_DISABLED),
|
||||
);
|
||||
|
||||
@@ -101,7 +101,7 @@ class UserModel {
|
||||
_id: 0,
|
||||
uname: 'Unknown User',
|
||||
unameLower: 'unknown user',
|
||||
gravatar: 'unknown@hydro.local',
|
||||
avatar: 'gravatar:unknown@hydro.local',
|
||||
mail: 'unknown@hydro.local',
|
||||
mailLower: 'unknown@hydro.local',
|
||||
salt: '',
|
||||
@@ -213,7 +213,7 @@ class UserModel {
|
||||
loginat: new Date(),
|
||||
loginip: regip,
|
||||
priv,
|
||||
gravatar: mail,
|
||||
avatar: `gravatar:${mail}`,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.warn('%o', e);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { DomainDoc } from './loader';
|
||||
import type { DomainDoc, Udoc } from './interface';
|
||||
import domain from './model/domain';
|
||||
import user from './model/user';
|
||||
import problem, { Field, Pdoc } from './model/problem';
|
||||
|
||||
export async function iterateAllDomain(cb: (ddoc: DomainDoc, current?: number, total?: number) => Promise<any>) {
|
||||
@@ -8,6 +9,11 @@ export async function iterateAllDomain(cb: (ddoc: DomainDoc, current?: number, t
|
||||
for (const i in ddocs) await cb(ddocs[i], +i, ddocs.length);
|
||||
}
|
||||
|
||||
export async function iterateAllUser(cb: (udoc: Udoc, current?: number, total?: number) => Promise<any>) {
|
||||
const udocs = await user.getMulti().toArray();
|
||||
for (const i in udocs) await cb(udocs[i], +i, udocs.length);
|
||||
}
|
||||
|
||||
interface PartialPdoc extends Pdoc {
|
||||
[key: string]: any,
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import {
|
||||
CsrfTokenError, InvalidOperationError, MethodNotAllowedError,
|
||||
NotFoundError, HydroError, SystemError,
|
||||
} from '../error';
|
||||
import * as misc from '../lib/misc';
|
||||
import { isContent, isName, isTitle } from '../lib/validator';
|
||||
import avatar from '../lib/avatar';
|
||||
import user from '../model/user';
|
||||
import domain from '../model/domain';
|
||||
import * as system from '../model/system';
|
||||
@@ -322,7 +322,7 @@ export class HandlerCommon {
|
||||
async renderHTML(name: string, context: any): Promise<string> {
|
||||
const UserContext: any = {
|
||||
...this.user,
|
||||
gravatar: misc.gravatar(this.user.gravatar || '', 128),
|
||||
avatar: avatar(this.user.avatar || '', 128),
|
||||
perm: this.user.perm.toString(),
|
||||
viewLang: this.translate('__id'),
|
||||
};
|
||||
@@ -508,6 +508,7 @@ export class Handler extends HandlerCommon {
|
||||
this.user = await user.getById(domainId, this.session.uid, this.session.scope);
|
||||
}
|
||||
if (this.user._id === 0 && this.session.viewLang) this.user.viewLang = this.session.viewLang;
|
||||
this.user.avatarUrl = avatar(this.user.avatar, 128);
|
||||
this.csrfToken = this.getCsrfToken(this.session._id || String.random(32));
|
||||
this.UiContext.csrfToken = this.csrfToken;
|
||||
this.loginMethods = filter(Object.keys(global.Hydro.lib), (str) => str.startsWith('oauth_'))
|
||||
|
||||
@@ -10,12 +10,13 @@ import { Progress } from './ui';
|
||||
import { Rdoc } from './interface';
|
||||
import { Logger } from './logger';
|
||||
import { streamToBuffer } from './utils';
|
||||
import { iterateAllDomain, iterateAllProblem } from './pipelineUtils';
|
||||
import { iterateAllDomain, iterateAllProblem, iterateAllUser } from './pipelineUtils';
|
||||
import gridfs from './service/gridfs';
|
||||
import storage from './service/storage';
|
||||
import db from './service/db';
|
||||
import difficultyAlgorithm from './lib/difficulty';
|
||||
import problem from './model/problem';
|
||||
import user from './model/user';
|
||||
import domain from './model/domain';
|
||||
import * as document from './model/document';
|
||||
import * as system from './model/system';
|
||||
@@ -299,6 +300,13 @@ const scripts: UpgradeScript[] = [
|
||||
});
|
||||
return true;
|
||||
},
|
||||
async function _22_23() {
|
||||
await iterateAllUser(async (udoc) => {
|
||||
if (!udoc.gravatar) return;
|
||||
await user.setById(udoc._id, { avatar: `gravatar:${udoc.gravatar}` }, { gravatar: '' });
|
||||
});
|
||||
return true;
|
||||
},
|
||||
];
|
||||
|
||||
export default scripts;
|
||||
Reference in new issue
Block a user