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`
 | 
			
		||||
2. Retry update (Don't forget `npm i`)
 | 
			
		||||
 | 
			
		||||
11.1.0 (2019/04/15)
 | 
			
		||||
-------------------
 | 
			
		||||
### Improvements
 | 
			
		||||
* アイコン未設定時にランダムな画像を表示するように
 | 
			
		||||
* 管理者やモデレーターはレートリミット無効に
 | 
			
		||||
 | 
			
		||||
### Fixes
 | 
			
		||||
* メンションの「あなた」インジケーターが表示されない問題を修正
 | 
			
		||||
* ブロックAPIでエラーが発生する問題を修正
 | 
			
		||||
* プッシュ通知の購読に失敗する問題を修正
 | 
			
		||||
 | 
			
		||||
11.0.3 (2019/04/15)
 | 
			
		||||
-------------------
 | 
			
		||||
### Fixes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
	"name": "misskey",
 | 
			
		||||
	"author": "syuilo <i@syuilo.com>",
 | 
			
		||||
	"version": "11.0.3",
 | 
			
		||||
	"version": "11.1.0",
 | 
			
		||||
	"codename": "daybreak",
 | 
			
		||||
	"repository": {
 | 
			
		||||
		"type": "git",
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +75,7 @@
 | 
			
		|||
		"@types/portscanner": "2.1.0",
 | 
			
		||||
		"@types/pug": "2.0.4",
 | 
			
		||||
		"@types/qrcode": "1.3.2",
 | 
			
		||||
		"@types/random-seed": "0.3.3",
 | 
			
		||||
		"@types/ratelimiter": "2.1.28",
 | 
			
		||||
		"@types/redis": "2.8.12",
 | 
			
		||||
		"@types/rename": "1.0.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +104,7 @@
 | 
			
		|||
		"bootstrap-vue": "2.0.0-rc.13",
 | 
			
		||||
		"bull": "3.7.0",
 | 
			
		||||
		"cafy": "15.1.1",
 | 
			
		||||
		"canvas": "2.4.1",
 | 
			
		||||
		"chai": "4.2.0",
 | 
			
		||||
		"chalk": "2.4.2",
 | 
			
		||||
		"cli-highlight": "2.1.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -188,6 +190,7 @@
 | 
			
		|||
		"pug": "2.0.3",
 | 
			
		||||
		"punycode": "2.1.1",
 | 
			
		||||
		"qrcode": "1.3.3",
 | 
			
		||||
		"random-seed": "0.3.0",
 | 
			
		||||
		"randomcolor": "0.5.4",
 | 
			
		||||
		"ratelimiter": "3.3.0",
 | 
			
		||||
		"recaptcha-promise": "0.1.3",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,9 @@ export default Vue.extend({
 | 
			
		|||
			return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
 | 
			
		||||
		},
 | 
			
		||||
		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: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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 rap from '@prezzemolo/rap';
 | 
			
		||||
import { ensure } from '../../prelude/ensure';
 | 
			
		||||
import config from '../../config';
 | 
			
		||||
 | 
			
		||||
@EntityRepository(User)
 | 
			
		||||
export class UserRepository extends Repository<User> {
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +89,7 @@ export class UserRepository extends Repository<User> {
 | 
			
		|||
			name: user.name,
 | 
			
		||||
			username: user.username,
 | 
			
		||||
			host: user.host,
 | 
			
		||||
			avatarUrl: user.avatarUrl,
 | 
			
		||||
			avatarUrl: user.avatarUrl ? user.avatarUrl : config.url + '/avatar/' + user.id,
 | 
			
		||||
			avatarColor: user.avatarColor,
 | 
			
		||||
			isAdmin: user.isAdmin || 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
 | 
			
		||||
		await limiter(ep, user!).catch(e => {
 | 
			
		||||
			throw new ApiError({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import create from '../../../../services/blocking/create';
 | 
			
		|||
import define from '../../define';
 | 
			
		||||
import { ApiError } from '../../error';
 | 
			
		||||
import { getUser } from '../../common/getters';
 | 
			
		||||
import { Blockings, NoteWatchings } from '../../../../models';
 | 
			
		||||
import { Blockings, NoteWatchings, Users } from '../../../../models';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	stability: 'stable',
 | 
			
		||||
| 
						 | 
				
			
			@ -89,5 +89,5 @@ export default define(meta, async (ps, user) => {
 | 
			
		|||
		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 { ApiError } from '../../error';
 | 
			
		||||
import { getUser } from '../../common/getters';
 | 
			
		||||
import { Blockings } from '../../../../models';
 | 
			
		||||
import { Blockings, Users } from '../../../../models';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	stability: 'stable',
 | 
			
		||||
| 
						 | 
				
			
			@ -84,5 +84,5 @@ export default define(meta, async (ps, user) => {
 | 
			
		|||
	// Delete blocking
 | 
			
		||||
	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) => {
 | 
			
		||||
	// if already subscribed
 | 
			
		||||
	const exist = await SwSubscriptions.findOne({
 | 
			
		||||
		createdAt: new Date(),
 | 
			
		||||
		userId: user.id,
 | 
			
		||||
		endpoint: ps.endpoint,
 | 
			
		||||
		auth: ps.auth,
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +44,7 @@ export default define(meta, async (ps, user) => {
 | 
			
		|||
 | 
			
		||||
	await SwSubscriptions.save({
 | 
			
		||||
		id: genId(),
 | 
			
		||||
		createdAt: new Date(),
 | 
			
		||||
		userId: user.id,
 | 
			
		||||
		endpoint: ps.endpoint,
 | 
			
		||||
		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
 | 
			
		||||
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 => {
 | 
			
		||||
	const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
 | 
			
		||||
	ctx.set('Content-Type', 'image/jpeg');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ import Logger from '../services/logger';
 | 
			
		|||
import { program } from '../argv';
 | 
			
		||||
import { UserProfiles } from '../models';
 | 
			
		||||
import { networkChart } from '../services/chart';
 | 
			
		||||
import { genAvatar } from '../misc/gen-avatar';
 | 
			
		||||
 | 
			
		||||
export const serverLogger = new Logger('server', 'gray', false);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +73,12 @@ router.use(activityPub.routes());
 | 
			
		|||
router.use(nodeinfo.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 => {
 | 
			
		||||
	const profile = await UserProfiles.findOne({
 | 
			
		||||
		emailVerifyCode: ctx.params.code
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -209,7 +209,7 @@ async function deleteOldFile(user: IRemoteUser) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (user.bannerId) {
 | 
			
		||||
		q.andWhere('file.id != :bannerId', { bannerId: user.bannerId })
 | 
			
		||||
		q.andWhere('file.id != :bannerId', { bannerId: user.bannerId });
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	q.orderBy('file.id', 'DESC');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue