Merge branch 'develop'
This commit is contained in:
		
						commit
						7abfcd06da
					
				
					 13 changed files with 123 additions and 16 deletions
				
			
		
							
								
								
									
										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…
	
	Add table
		Add a link
		
	
		Reference in a new issue