Merge branch 'develop'
This commit is contained in:
commit
7abfcd06da
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -5,6 +5,17 @@ If you encounter any problems with updating, please try the following:
|
||||||
1. `npm run clean` or `npm run cleanall`
|
1. `npm run clean` or `npm run cleanall`
|
||||||
2. Retry update (Don't forget `npm i`)
|
2. Retry update (Don't forget `npm i`)
|
||||||
|
|
||||||
|
11.1.0 (2019/04/15)
|
||||||
|
-------------------
|
||||||
|
### Improvements
|
||||||
|
* アイコン未設定時にランダムな画像を表示するように
|
||||||
|
* 管理者やモデレーターはレートリミット無効に
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* メンションの「あなた」インジケーターが表示されない問題を修正
|
||||||
|
* ブロックAPIでエラーが発生する問題を修正
|
||||||
|
* プッシュ通知の購読に失敗する問題を修正
|
||||||
|
|
||||||
11.0.3 (2019/04/15)
|
11.0.3 (2019/04/15)
|
||||||
-------------------
|
-------------------
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "11.0.3",
|
"version": "11.1.0",
|
||||||
"codename": "daybreak",
|
"codename": "daybreak",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -75,6 +75,7 @@
|
||||||
"@types/portscanner": "2.1.0",
|
"@types/portscanner": "2.1.0",
|
||||||
"@types/pug": "2.0.4",
|
"@types/pug": "2.0.4",
|
||||||
"@types/qrcode": "1.3.2",
|
"@types/qrcode": "1.3.2",
|
||||||
|
"@types/random-seed": "0.3.3",
|
||||||
"@types/ratelimiter": "2.1.28",
|
"@types/ratelimiter": "2.1.28",
|
||||||
"@types/redis": "2.8.12",
|
"@types/redis": "2.8.12",
|
||||||
"@types/rename": "1.0.1",
|
"@types/rename": "1.0.1",
|
||||||
|
@ -103,6 +104,7 @@
|
||||||
"bootstrap-vue": "2.0.0-rc.13",
|
"bootstrap-vue": "2.0.0-rc.13",
|
||||||
"bull": "3.7.0",
|
"bull": "3.7.0",
|
||||||
"cafy": "15.1.1",
|
"cafy": "15.1.1",
|
||||||
|
"canvas": "2.4.1",
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"cli-highlight": "2.1.0",
|
"cli-highlight": "2.1.0",
|
||||||
|
@ -188,6 +190,7 @@
|
||||||
"pug": "2.0.3",
|
"pug": "2.0.3",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
"qrcode": "1.3.3",
|
"qrcode": "1.3.3",
|
||||||
|
"random-seed": "0.3.0",
|
||||||
"randomcolor": "0.5.4",
|
"randomcolor": "0.5.4",
|
||||||
"ratelimiter": "3.3.0",
|
"ratelimiter": "3.3.0",
|
||||||
"recaptcha-promise": "0.1.3",
|
"recaptcha-promise": "0.1.3",
|
||||||
|
|
|
@ -36,7 +36,9 @@ export default Vue.extend({
|
||||||
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
|
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
|
||||||
},
|
},
|
||||||
isMe(): boolean {
|
isMe(): boolean {
|
||||||
return this.$store.getters.isSignedIn && this.canonical.toLowerCase() === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase();
|
return this.$store.getters.isSignedIn && (
|
||||||
|
`@${this.username}@${toUnicode(this.host)}` === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
89
src/misc/gen-avatar.ts
Normal file
89
src/misc/gen-avatar.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/**
|
||||||
|
* Random avatar generator
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createCanvas } from 'canvas';
|
||||||
|
import * as gen from 'random-seed';
|
||||||
|
|
||||||
|
const size = 512; // px
|
||||||
|
const n = 5; // resolution
|
||||||
|
const margin = (size / n) / 1.5;
|
||||||
|
const colors = [
|
||||||
|
'#e57373',
|
||||||
|
'#F06292',
|
||||||
|
'#BA68C8',
|
||||||
|
'#9575CD',
|
||||||
|
'#7986CB',
|
||||||
|
'#64B5F6',
|
||||||
|
'#4FC3F7',
|
||||||
|
'#4DD0E1',
|
||||||
|
'#4DB6AC',
|
||||||
|
'#81C784',
|
||||||
|
'#8BC34A',
|
||||||
|
'#AFB42B',
|
||||||
|
'#F57F17',
|
||||||
|
'#FF5722',
|
||||||
|
'#795548',
|
||||||
|
'#455A64',
|
||||||
|
];
|
||||||
|
const bg = '#e9e9e9';
|
||||||
|
|
||||||
|
const actualSize = size - (margin * 2);
|
||||||
|
const cellSize = actualSize / n;
|
||||||
|
const sideN = Math.floor(n / 2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate buffer of random avatar by seed
|
||||||
|
*/
|
||||||
|
export function genAvatar(seed: string) {
|
||||||
|
const rand = gen.create(seed);
|
||||||
|
const canvas = createCanvas(size, size);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
ctx.fillStyle = bg;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillRect(0, 0, size, size);
|
||||||
|
|
||||||
|
ctx.fillStyle = colors[rand(colors.length)];
|
||||||
|
|
||||||
|
// side bitmap (filled by false)
|
||||||
|
const side: boolean[][] = new Array(sideN);
|
||||||
|
for (let i = 0; i < side.length; i++) {
|
||||||
|
side[i] = new Array(n).fill(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1*n (filled by false)
|
||||||
|
const center: boolean[] = new Array(n).fill(false);
|
||||||
|
|
||||||
|
// tslint:disable-next-line:prefer-for-of
|
||||||
|
for (let x = 0; x < side.length; x++) {
|
||||||
|
for (let y = 0; y < side[x].length; y++) {
|
||||||
|
side[x][y] = rand(3) === 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < center.length; i++) {
|
||||||
|
center[i] = rand(3) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
for (let x = 0; x < n; x++) {
|
||||||
|
for (let y = 0; y < n; y++) {
|
||||||
|
const isXCenter = x === ((n - 1) / 2);
|
||||||
|
if (isXCenter && !center[y]) continue;
|
||||||
|
|
||||||
|
const isLeftSide = x < ((n - 1) / 2);
|
||||||
|
if (isLeftSide && !side[x][y]) continue;
|
||||||
|
|
||||||
|
const isRightSide = x > ((n - 1) / 2);
|
||||||
|
if (isRightSide && !side[sideN - (x - sideN)][y]) continue;
|
||||||
|
|
||||||
|
const actualX = margin + (cellSize * x);
|
||||||
|
const actualY = margin + (cellSize * y);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillRect(actualX, actualY, cellSize, cellSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas.toBuffer();
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import { User, ILocalUser, IRemoteUser } from '../entities/user';
|
||||||
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
|
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
|
||||||
import rap from '@prezzemolo/rap';
|
import rap from '@prezzemolo/rap';
|
||||||
import { ensure } from '../../prelude/ensure';
|
import { ensure } from '../../prelude/ensure';
|
||||||
|
import config from '../../config';
|
||||||
|
|
||||||
@EntityRepository(User)
|
@EntityRepository(User)
|
||||||
export class UserRepository extends Repository<User> {
|
export class UserRepository extends Repository<User> {
|
||||||
|
@ -88,7 +89,7 @@ export class UserRepository extends Repository<User> {
|
||||||
name: user.name,
|
name: user.name,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
host: user.host,
|
host: user.host,
|
||||||
avatarUrl: user.avatarUrl,
|
avatarUrl: user.avatarUrl ? user.avatarUrl : config.url + '/avatar/' + user.id,
|
||||||
avatarColor: user.avatarColor,
|
avatarColor: user.avatarColor,
|
||||||
isAdmin: user.isAdmin || undefined,
|
isAdmin: user.isAdmin || undefined,
|
||||||
isBot: user.isBot || undefined,
|
isBot: user.isBot || undefined,
|
||||||
|
|
|
@ -59,7 +59,7 @@ export default async (endpoint: string, user: User | null | undefined, app: App
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep.meta.requireCredential && ep.meta.limit) {
|
if (ep.meta.requireCredential && ep.meta.limit && !user!.isAdmin && !user!.isModerator) {
|
||||||
// Rate limit
|
// Rate limit
|
||||||
await limiter(ep, user!).catch(e => {
|
await limiter(ep, user!).catch(e => {
|
||||||
throw new ApiError({
|
throw new ApiError({
|
||||||
|
|
|
@ -5,7 +5,7 @@ import create from '../../../../services/blocking/create';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { ApiError } from '../../error';
|
import { ApiError } from '../../error';
|
||||||
import { getUser } from '../../common/getters';
|
import { getUser } from '../../common/getters';
|
||||||
import { Blockings, NoteWatchings } from '../../../../models';
|
import { Blockings, NoteWatchings, Users } from '../../../../models';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
stability: 'stable',
|
stability: 'stable',
|
||||||
|
@ -89,5 +89,5 @@ export default define(meta, async (ps, user) => {
|
||||||
noteUserId: blockee.id
|
noteUserId: blockee.id
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Blockings.pack(blockee.id, user);
|
return await Users.pack(blockee.id, user);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import deleteBlocking from '../../../../services/blocking/delete';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { ApiError } from '../../error';
|
import { ApiError } from '../../error';
|
||||||
import { getUser } from '../../common/getters';
|
import { getUser } from '../../common/getters';
|
||||||
import { Blockings } from '../../../../models';
|
import { Blockings, Users } from '../../../../models';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
stability: 'stable',
|
stability: 'stable',
|
||||||
|
@ -84,5 +84,5 @@ export default define(meta, async (ps, user) => {
|
||||||
// Delete blocking
|
// Delete blocking
|
||||||
await deleteBlocking(blocker, blockee);
|
await deleteBlocking(blocker, blockee);
|
||||||
|
|
||||||
return await Blockings.pack(blockee.id, user);
|
return await Users.pack(blockee.id, user);
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,7 +27,6 @@ export const meta = {
|
||||||
export default define(meta, async (ps, user) => {
|
export default define(meta, async (ps, user) => {
|
||||||
// if already subscribed
|
// if already subscribed
|
||||||
const exist = await SwSubscriptions.findOne({
|
const exist = await SwSubscriptions.findOne({
|
||||||
createdAt: new Date(),
|
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
endpoint: ps.endpoint,
|
endpoint: ps.endpoint,
|
||||||
auth: ps.auth,
|
auth: ps.auth,
|
||||||
|
@ -45,6 +44,7 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
await SwSubscriptions.save({
|
await SwSubscriptions.save({
|
||||||
id: genId(),
|
id: genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
endpoint: ps.endpoint,
|
endpoint: ps.endpoint,
|
||||||
auth: ps.auth,
|
auth: ps.auth,
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB |
|
@ -21,12 +21,6 @@ app.use(async (ctx, next) => {
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
router.get('/default-avatar.jpg', ctx => {
|
|
||||||
const file = fs.createReadStream(`${__dirname}/assets/avatar.jpg`);
|
|
||||||
ctx.set('Content-Type', 'image/jpeg');
|
|
||||||
ctx.body = file;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/app-default.jpg', ctx => {
|
router.get('/app-default.jpg', ctx => {
|
||||||
const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
|
const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
|
||||||
ctx.set('Content-Type', 'image/jpeg');
|
ctx.set('Content-Type', 'image/jpeg');
|
||||||
|
|
|
@ -25,6 +25,7 @@ import Logger from '../services/logger';
|
||||||
import { program } from '../argv';
|
import { program } from '../argv';
|
||||||
import { UserProfiles } from '../models';
|
import { UserProfiles } from '../models';
|
||||||
import { networkChart } from '../services/chart';
|
import { networkChart } from '../services/chart';
|
||||||
|
import { genAvatar } from '../misc/gen-avatar';
|
||||||
|
|
||||||
export const serverLogger = new Logger('server', 'gray', false);
|
export const serverLogger = new Logger('server', 'gray', false);
|
||||||
|
|
||||||
|
@ -72,6 +73,12 @@ router.use(activityPub.routes());
|
||||||
router.use(nodeinfo.routes());
|
router.use(nodeinfo.routes());
|
||||||
router.use(wellKnown.routes());
|
router.use(wellKnown.routes());
|
||||||
|
|
||||||
|
router.get('/avatar/:x', ctx => {
|
||||||
|
const avatar = genAvatar(ctx.params.x);
|
||||||
|
ctx.set('Content-Type', 'image/png');
|
||||||
|
ctx.body = avatar;
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/verify-email/:code', async ctx => {
|
router.get('/verify-email/:code', async ctx => {
|
||||||
const profile = await UserProfiles.findOne({
|
const profile = await UserProfiles.findOne({
|
||||||
emailVerifyCode: ctx.params.code
|
emailVerifyCode: ctx.params.code
|
||||||
|
|
|
@ -209,7 +209,7 @@ async function deleteOldFile(user: IRemoteUser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.bannerId) {
|
if (user.bannerId) {
|
||||||
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId })
|
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId });
|
||||||
}
|
}
|
||||||
|
|
||||||
q.orderBy('file.id', 'DESC');
|
q.orderBy('file.id', 'DESC');
|
||||||
|
|
Loading…
Reference in a new issue