From a8f945ae8ee427ea59da2b7f62aae849dafc499d Mon Sep 17 00:00:00 2001 From: Satsuki Yanagi <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 5 Jul 2019 02:48:32 +0900 Subject: [PATCH 1/7] Update node version (#5101) --- .node-version | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.node-version b/.node-version index bb0aefa18c..b65ea2e839 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v12.1.0 +v12.6.0 diff --git a/Dockerfile b/Dockerfile index 40ad068a49..ab8be6c682 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12.5-alpine AS base +FROM node:12.6-alpine AS base ENV NODE_ENV=production From d5caf22d8c4f1509fed7cb5e3ef5237e24c677fd Mon Sep 17 00:00:00 2001 From: Satsuki Yanagi <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 5 Jul 2019 07:45:00 +0900 Subject: [PATCH 2/7] Update Emojis immediately (#5106) --- src/server/api/endpoints/admin/emoji/add.ts | 3 +++ src/server/api/endpoints/admin/emoji/remove.ts | 3 +++ src/server/api/endpoints/admin/emoji/update.ts | 3 +++ src/server/api/endpoints/meta.ts | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/server/api/endpoints/admin/emoji/add.ts b/src/server/api/endpoints/admin/emoji/add.ts index c26e8dd04d..5ba00afde8 100644 --- a/src/server/api/endpoints/admin/emoji/add.ts +++ b/src/server/api/endpoints/admin/emoji/add.ts @@ -3,6 +3,7 @@ import define from '../../../define'; import { detectUrlMine } from '../../../../../misc/detect-url-mine'; import { Emojis } from '../../../../../models'; import { genId } from '../../../../../misc/gen-id'; +import { getConnection } from 'typeorm'; export const meta = { desc: { @@ -43,6 +44,8 @@ export default define(meta, async (ps) => { type, }); + await getConnection().queryResultCache!.remove(['meta_emojis']); + return { id: emoji.id }; diff --git a/src/server/api/endpoints/admin/emoji/remove.ts b/src/server/api/endpoints/admin/emoji/remove.ts index 316834b884..3ebf933bc6 100644 --- a/src/server/api/endpoints/admin/emoji/remove.ts +++ b/src/server/api/endpoints/admin/emoji/remove.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import define from '../../../define'; import { ID } from '../../../../../misc/cafy-id'; import { Emojis } from '../../../../../models'; +import { getConnection } from 'typeorm'; export const meta = { desc: { @@ -26,4 +27,6 @@ export default define(meta, async (ps) => { if (emoji == null) throw new Error('emoji not found'); await Emojis.delete(emoji.id); + + await getConnection().queryResultCache!.remove(['meta_emojis']); }); diff --git a/src/server/api/endpoints/admin/emoji/update.ts b/src/server/api/endpoints/admin/emoji/update.ts index 48b4a4ee23..f8bc638fcf 100644 --- a/src/server/api/endpoints/admin/emoji/update.ts +++ b/src/server/api/endpoints/admin/emoji/update.ts @@ -3,6 +3,7 @@ import define from '../../../define'; import { detectUrlMine } from '../../../../../misc/detect-url-mine'; import { ID } from '../../../../../misc/cafy-id'; import { Emojis } from '../../../../../models'; +import { getConnection } from 'typeorm'; export const meta = { desc: { @@ -47,4 +48,6 @@ export default define(meta, async (ps) => { url: ps.url, type, }); + + await getConnection().queryResultCache!.remove(['meta_emojis']); }); diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index a3390a011d..a754a885ab 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -95,7 +95,7 @@ export const meta = { export default define(meta, async (ps, me) => { const instance = await fetchMeta(true); - const emojis = await Emojis.find({ where: { host: null }, cache: 3600000 }); // 1 hour + const emojis = await Emojis.find({ where: { host: null }, cache: { id: 'meta_emojis', milliseconds: 3600000 } }); // 1 hour const response: any = { maintainerName: instance.maintainerName, From 114523e69e30a90bc7bc043254cfc89e3a523c46 Mon Sep 17 00:00:00 2001 From: Satsuki Yanagi <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 5 Jul 2019 07:48:12 +0900 Subject: [PATCH 3/7] Fix WebAuthn login (#5103) --- .../app/common/views/components/signin.vue | 16 +++-- src/server/api/endpoints/i/2fa/getkeys.ts | 67 ------------------- src/server/api/private/signin.ts | 42 +++++++++++- 3 files changed, 51 insertions(+), 74 deletions(-) delete mode 100644 src/server/api/endpoints/i/2fa/getkeys.ts diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue index 53cc62c333..8498a1dc3e 100644 --- a/src/client/app/common/views/components/signin.vue +++ b/src/client/app/common/views/components/signin.vue @@ -107,9 +107,8 @@ export default Vue.extend({ })), timeout: 60 * 1000 } - }).catch(err => { + }).catch(() => { this.queryingKey = false; - console.warn(err); return Promise.reject(null); }).then(credential => { this.queryingKey = false; @@ -127,8 +126,7 @@ export default Vue.extend({ localStorage.setItem('i', res.i); location.reload(); }).catch(err => { - if(err === null) return; - console.error(err); + if (err === null) return; this.$root.dialog({ type: 'error', text: this.$t('login-failed') @@ -142,7 +140,7 @@ export default Vue.extend({ if (!this.totpLogin && this.user && this.user.twoFactorEnabled) { if (window.PublicKeyCredential && this.user.securityKeys) { - this.$root.api('i/2fa/getkeys', { + this.$root.api('signin', { username: this.username, password: this.password }).then(res => { @@ -150,6 +148,14 @@ export default Vue.extend({ this.signing = false; this.challengeData = res; return this.queryKey(); + }).catch(() => { + this.$root.dialog({ + type: 'error', + text: this.$t('login-failed') + }); + this.challengeData = null; + this.totpLogin = false; + this.signing = false; }); } else { this.totpLogin = true; diff --git a/src/server/api/endpoints/i/2fa/getkeys.ts b/src/server/api/endpoints/i/2fa/getkeys.ts deleted file mode 100644 index bb1585d795..0000000000 --- a/src/server/api/endpoints/i/2fa/getkeys.ts +++ /dev/null @@ -1,67 +0,0 @@ -import $ from 'cafy'; -import * as bcrypt from 'bcryptjs'; -import * as crypto from 'crypto'; -import define from '../../../define'; -import { UserProfiles, UserSecurityKeys, AttestationChallenges } from '../../../../../models'; -import { ensure } from '../../../../../prelude/ensure'; -import { promisify } from 'util'; -import { hash } from '../../../2fa'; -import { genId } from '../../../../../misc/gen-id'; - -export const meta = { - requireCredential: true, - - secure: true, - - params: { - password: { - validator: $.str - } - } -}; - -const randomBytes = promisify(crypto.randomBytes); - -export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne(user.id).then(ensure); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - const keys = await UserSecurityKeys.find({ - userId: user.id - }); - - if (keys.length === 0) { - throw new Error('no keys found'); - } - - // 32 byte challenge - const entropy = await randomBytes(32); - const challenge = entropy.toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); - - const challengeId = genId(); - - await AttestationChallenges.save({ - userId: user.id, - id: challengeId, - challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), - createdAt: new Date(), - registrationChallenge: false - }); - - return { - challenge, - challengeId, - securityKeys: keys.map(key => ({ - id: key.id - })) - }; -}); diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts index cd9fe5bb9d..bc9346d088 100644 --- a/src/server/api/private/signin.ts +++ b/src/server/api/private/signin.ts @@ -9,6 +9,7 @@ import { ILocalUser } from '../../../models/entities/user'; import { genId } from '../../../misc/gen-id'; import { ensure } from '../../../prelude/ensure'; import { verifyLogin, hash } from '../2fa'; +import { randomBytes } from 'crypto'; export default async (ctx: Koa.BaseContext) => { ctx.set('Access-Control-Allow-Origin', config.url); @@ -99,7 +100,7 @@ export default async (ctx: Koa.BaseContext) => { }); return; } - } else { + } else if (body.credentialId) { const clientDataJSON = Buffer.from(body.clientDataJSON, 'hex'); const clientData = JSON.parse(clientDataJSON.toString('utf-8')); const challenge = await AttestationChallenges.findOne({ @@ -131,7 +132,7 @@ export default async (ctx: Koa.BaseContext) => { const securityKey = await UserSecurityKeys.findOne({ id: Buffer.from( body.credentialId - .replace(/\-/g, '+') + .replace(/-/g, '+') .replace(/_/g, '/'), 'base64' ).toString('hex') @@ -161,7 +162,44 @@ export default async (ctx: Koa.BaseContext) => { }); return; } + } else { + const keys = await UserSecurityKeys.find({ + userId: user.id + }); + + if (keys.length === 0) { + await fail(403, { + error: 'no keys found' + }); + } + + // 32 byte challenge + const challenge = randomBytes(32).toString('base64') + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); + + const challengeId = genId(); + + await AttestationChallenges.save({ + userId: user.id, + id: challengeId, + challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), + createdAt: new Date(), + registrationChallenge: false + }); + + ctx.body = { + challenge, + challengeId, + securityKeys: keys.map(key => ({ + id: key.id + })) + }; + ctx.status = 200; + return; } await fail(); + return; }; From 6a53ccf814adfbb894551c7a74866c7fc34a0182 Mon Sep 17 00:00:00 2001 From: Satsuki Yanagi <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 5 Jul 2019 08:43:56 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5?= =?UTF-8?q?=E3=82=BF=E3=82=B0=E3=81=AE=E3=83=88=E3=83=AC=E3=83=B3=E3=83=89?= =?UTF-8?q?=E3=81=AE=E8=A8=88=E7=AE=97=E3=82=925=E5=88=86=E5=8D=98?= =?UTF-8?q?=E4=BD=8D=E3=81=A7=E4=B8=B8=E3=82=81=E3=82=8B=20(#5107)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/endpoints/hashtags/trend.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/server/api/endpoints/hashtags/trend.ts b/src/server/api/endpoints/hashtags/trend.ts index 3154461e5a..a004732f81 100644 --- a/src/server/api/endpoints/hashtags/trend.ts +++ b/src/server/api/endpoints/hashtags/trend.ts @@ -54,8 +54,11 @@ export default define(meta, async () => { const instance = await fetchMeta(true); const hiddenTags = instance.hiddenTags.map(t => t.toLowerCase()); + const now = new Date(); // 5分単位で丸めた現在日時 + now.setMinutes(Math.round(now.getMinutes() / 5) * 5, 0, 0); + const tagNotes = await Notes.createQueryBuilder('note') - .where(`note.createdAt > :date`, { date: new Date(Date.now() - rangeA) }) + .where(`note.createdAt > :date`, { date: new Date(now.getTime() - rangeA) }) .andWhere(`note.tags != '{}'`) .select(['note.tags', 'note.userId']) .cache(60000) // 1 min @@ -106,8 +109,8 @@ export default define(meta, async () => { countPromises.push(Promise.all(hots.map(tag => Notes.createQueryBuilder('note') .select('count(distinct note.userId)') .where(':tag = ANY(note.tags)', { tag: tag }) - .andWhere('note.createdAt < :lt', { lt: new Date(Date.now() - (interval * i)) }) - .andWhere('note.createdAt > :gt', { gt: new Date(Date.now() - (interval * (i + 1))) }) + .andWhere('note.createdAt < :lt', { lt: new Date(now.getTime() - (interval * i)) }) + .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - (interval * (i + 1))) }) .cache(60000) // 1 min .getRawOne() .then(x => parseInt(x.count, 10)) @@ -119,7 +122,7 @@ export default define(meta, async () => { const totalCounts = await Promise.all(hots.map(tag => Notes.createQueryBuilder('note') .select('count(distinct note.userId)') .where(':tag = ANY(note.tags)', { tag: tag }) - .andWhere('note.createdAt > :gt', { gt: new Date(Date.now() - (interval * range)) }) + .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - (interval * range)) }) .cache(60000) // 1 min .getRawOne() .then(x => parseInt(x.count, 10)) From 26f2ae093bcb2e9eba703e9f865c264beb04dc99 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri, 5 Jul 2019 17:44:23 +0900 Subject: [PATCH 5/7] =?UTF-8?q?APNG=E3=81=A7=E3=82=82MIME=20type=E3=81=AFi?= =?UTF-8?q?mage/png=E3=81=AB=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=20(#5100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * APNGでもMIME typeはimage/pngにするように * Revert "APNGでもMIME typeはimage/pngにするように" This reverts commit e579eb2bf44f526cabfa9bd4adc6b4fa84727e3b. * APNGはファイル送信時のみimage/pngにするように --- src/server/file/send-drive-file.ts | 2 +- src/services/drive/add-file.ts | 2 ++ src/services/drive/image-processor.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts index 5da3d79eb5..44e88be8ae 100644 --- a/src/server/file/send-drive-file.ts +++ b/src/server/file/send-drive-file.ts @@ -42,7 +42,7 @@ export default async function(ctx: Koa.BaseContext) { ctx.set('Content-Disposition', contentDisposition('inline', `${rename(file.name, { suffix: '-thumb', extname: '.jpeg' })}`)); ctx.body = InternalStorage.read(key); } else if (isWebpublic) { - ctx.set('Content-Type', file.type); + ctx.set('Content-Type', file.type === 'image/apng' ? 'image/png' : file.type); ctx.set('Content-Disposition', contentDisposition('inline', `${rename(file.name, { suffix: '-web' })}`)); ctx.body = InternalStorage.read(key); } else { diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index e7b1e2a812..2f0d5d6265 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -207,6 +207,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool * Upload to ObjectStorage */ async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { + if (type === 'image/apng') type = 'image/png'; + const meta = await fetchMeta(); const minio = new Minio.Client({ diff --git a/src/services/drive/image-processor.ts b/src/services/drive/image-processor.ts index 919d9ce138..4b8db0e0c8 100644 --- a/src/services/drive/image-processor.ts +++ b/src/services/drive/image-processor.ts @@ -97,6 +97,6 @@ export async function convertToApng(path: string): Promise { return { data, ext: 'apng', - type: 'image/vnd.mozilla.apng' + type: 'image/apng' }; } From 411f038f34ad0283c5c24285a06701276aa4f6b9 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri, 5 Jul 2019 17:58:54 +0900 Subject: [PATCH 6/7] =?UTF-8?q?Fix:=20=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=81=8C=E3=81=9F=E3=81=BE=E3=81=AB=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=97=E3=81=BE?= =?UTF-8?q?=E3=81=86=20(#5105)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/app/common/views/components/emoji.vue | 16 +++++++++++++++- .../common/views/components/reaction-icon.vue | 7 ++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/client/app/common/views/components/emoji.vue b/src/client/app/common/views/components/emoji.vue index 0cc78a337b..20c1ea1a80 100644 --- a/src/client/app/common/views/components/emoji.vue +++ b/src/client/app/common/views/components/emoji.vue @@ -55,6 +55,20 @@ export default Vue.extend({ } }, + watch: { + customEmojis() { + if (this.name) { + const customEmoji = this.customEmojis.find(x => x.name == this.name); + if (customEmoji) { + this.customEmoji = customEmoji; + this.url = this.$store.state.device.disableShowingAnimatedImages + ? getStaticImageUrl(customEmoji.url) + : customEmoji.url; + } + } + }, + }, + created() { if (this.name) { const customEmoji = this.customEmojis.find(x => x.name == this.name); @@ -80,7 +94,7 @@ export default Vue.extend({ this.url = `${twemojiBase}/2/svg/${codes.join('-')}.svg`; } - } + }, }); diff --git a/src/client/app/common/views/components/reaction-icon.vue b/src/client/app/common/views/components/reaction-icon.vue index 022d57dc44..afe51d7833 100644 --- a/src/client/app/common/views/components/reaction-icon.vue +++ b/src/client/app/common/views/components/reaction-icon.vue @@ -15,9 +15,14 @@ export default Vue.extend({ }, data() { return { - customEmojis: (this.$root.getMetaSync() || { emojis: [] }).emojis || [] + customEmojis: [] }; }, + created() { + this.$root.getMeta().then(meta => { + if (meta && meta.emojis) this.customEmojis = meta.emojis; + }); + }, computed: { str(): any { switch (this.reaction) { From e2d59293fb204d0c41b4289b16b764ef11a3da43 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 5 Jul 2019 18:02:29 +0900 Subject: [PATCH 7/7] 11.24.1 --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12861d31ba..7783896256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,15 @@ npm i -g ts-node npm run migrate ``` +11.24.1 (2019/07/05) +-------------------- +### 🐛Fixes +* WebAuthnでログインできない問題を修正 +* 絵文字の変更事項のmetaへの反映が最大1時間遅延される問題を修正 +* ハッシュタグのトレンドの計算を5分単位で丸めるように +* APNGでもMIME typeはimage/pngにするように +* カスタム絵文字リアクションがたまに文字になってしまう問題を修正 + 11.24.0 (2019/07/05) -------------------- 注意: このアップデート後に、`node built/tools/accept-migration Init 1000000000000`してください。 diff --git a/package.json b/package.json index bd0d551ea1..e5d2806998 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "11.24.0", + "version": "11.24.1", "codename": "daybreak", "repository": { "type": "git",