From ca07459f5e80d7602c7f68df431bed4df9a8fd81 Mon Sep 17 00:00:00 2001 From: _ Date: Mon, 9 Oct 2023 12:36:25 +0900 Subject: [PATCH 001/127] =?UTF-8?q?fix(backend):=20=E3=83=80=E3=82=A4?= =?UTF-8?q?=E3=83=AC=E3=82=AF=E3=83=88=E6=8A=95=E7=A8=BF=E3=81=8C=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E4=B8=8A=E3=81=AB?= =?UTF-8?q?=E6=AD=A3=E5=B8=B8=E3=81=AB=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20(#11993)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DMをredisにpushするように * add test * add CHANGELOG * Update NoteCreateService.ts * lint * :v: * 前のバージョンから発生した問題ではないため不要 --------- Co-authored-by: syuilo --- .../backend/src/core/NoteCreateService.ts | 58 +++--- .../src/server/api/endpoints/users/notes.ts | 1 + packages/backend/test/e2e/timelines.ts | 175 ++++++++++++++++-- packages/shared/.eslintrc.js | 3 + 4 files changed, 193 insertions(+), 44 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 4a454e79e7..277875a19f 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -494,11 +494,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Increment notes count (user) this.incNotesCountOfUser(user); - if (data.visibility === 'specified') { - // TODO? - } else { - this.pushToTl(note, user); - } + this.pushToTl(note, user); this.antennaService.addNoteToAntennas(note, user); @@ -861,24 +857,34 @@ export class NoteCreateService implements OnApplicationShutdown { } } else { // TODO: キャッシュ? - const followings = await this.followingsRepository.find({ - where: { - followeeId: user.id, - followerHost: IsNull(), - isFollowerHibernated: false, - }, - select: ['followerId', 'withReplies'], - }); + // eslint-disable-next-line prefer-const + let [followings, userListMemberships] = await Promise.all([ + this.followingsRepository.find({ + where: { + followeeId: user.id, + followerHost: IsNull(), + isFollowerHibernated: false, + }, + select: ['followerId', 'withReplies'], + }), + this.userListMembershipsRepository.find({ + where: { + userId: user.id, + }, + select: ['userListId', 'userListUserId', 'withReplies'], + }), + ]); - const userListMemberships = await this.userListMembershipsRepository.find({ - where: { - userId: user.id, - }, - select: ['userListId', 'withReplies'], - }); + if (note.visibility === 'followers') { + // TODO: 重そうだから何とかしたい Set 使う? + userListMemberships = userListMemberships.filter(x => followings.some(f => f.followerId === x.userListUserId)); + } // TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする for (const following of followings) { + // 基本的にvisibleUserIdsには自身のidが含まれている前提であること + if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue; + // 自分自身以外への返信 if (note.replyId && note.replyUserId !== note.userId) { if (!following.withReplies) continue; @@ -899,13 +905,13 @@ export class NoteCreateService implements OnApplicationShutdown { } } - // TODO - //if (note.visibility === 'followers') { - // // TODO: 重そうだから何とかしたい Set 使う? - // userLists = userLists.filter(x => followings.some(f => f.followerId === x.userListUserId)); - //} - for (const userListMembership of userListMemberships) { + // ダイレクトのとき、そのリストが対象外のユーザーの場合 + if ( + note.visibility === 'specified' && + !note.visibleUserIds.some(v => v === userListMembership.userListUserId) + ) continue; + // 自分自身以外への返信 if (note.replyId && note.replyUserId !== note.userId) { if (!userListMembership.withReplies) continue; @@ -926,7 +932,7 @@ export class NoteCreateService implements OnApplicationShutdown { } } - { // 自分自身のHTL + if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL redisPipeline.xadd( `homeTimeline:${user.id}`, 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index d4f9186e3a..5736aad5f9 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -136,6 +136,7 @@ export default class extends Endpoint { // eslint- } } + if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false; if (note.visibility === 'followers' && !isFollowing) return false; return true; diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 6de58f0146..05209c9024 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -3,6 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +// How to run: +// pnpm jest -- e2e/timelines.ts + process.env.NODE_ENV = 'test'; process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING = 'true'; @@ -378,6 +381,104 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); + + test.concurrent('自分の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); + }); + + test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); + }); + + test.concurrent('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + + test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + + test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + const aliceNote = await post(alice, { text: 'ok', visibility: 'specified', visibleUserIds: [bob.id], replyId: bobNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); + }); + + /* TODO + test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok'); + }); + */ + + // ↑の挙動が理想だけど実装が面倒かも + test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); }); describe('Local TL', () => { @@ -630,7 +731,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); - /* 未実装 test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -645,23 +745,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); - */ - - test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれるが隠される', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); - await sleep(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); - assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, null); - }); test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); @@ -778,6 +861,38 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); }, 1000 * 10); + + test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); + }); + + test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('/users/lists/push', { listId: list.id, userId: carol.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); }); describe('User TL', () => { @@ -951,6 +1066,30 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote3.id), true); }); + + test.concurrent('自身の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: alice.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + }); + + test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); }); // TODO: リノートミュート済みユーザーのテスト diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 1ecad7ab75..c578894f60 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -38,6 +38,9 @@ module.exports = { 'before': true, 'after': true, }], + 'brace-style': ['error', '1tbs', { + 'allowSingleLine': true, + }], 'padded-blocks': ['error', 'never'], /* TODO: path aliasを使わないとwarnする 'no-restricted-imports': ['warn', { From fce557715b68b608a6ff02538a7b5ca2e438264b Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 12:37:04 +0900 Subject: [PATCH 002/127] New Crowdin updates (#11980) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (German) * New translations ja-jp.yml (English) * New translations ja-jp.yml (German) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Chinese Traditional) --- locales/de-DE.yml | 10 ++++++++++ locales/en-US.yml | 10 ++++++++++ locales/it-IT.yml | 14 ++++++++++---- locales/zh-TW.yml | 10 ++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/locales/de-DE.yml b/locales/de-DE.yml index e8a1da4821..08a216cbe5 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1129,6 +1129,12 @@ fileAttachedOnly: "Nur Notizen mit Dateien" showRepliesToOthersInTimeline: "Antworten in Chronik anzeigen" hideRepliesToOthersInTimeline: "Antworten nicht in Chronik anzeigen" externalServices: "Externe Dienste" +impressum: "Impressum" +impressumUrl: "Impressums-URL" +impressumDescription: "In manchen Ländern, wie Deutschland und dessen Umgebung, ist die Angabe von Betreiberinformationen (ein Impressum) bei kommerziellem Betrieb zwingend." +privacyPolicy: "Datenschutzerklärung" +privacyPolicyUrl: "Datenschutzerklärungs-URL" +tosAndPrivacyPolicy: "Nutzungsbedingungen und Datenschutzerklärung" _announcement: forExistingUsers: "Nur für existierende Nutzer" forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt." @@ -1527,6 +1533,10 @@ _ad: reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen" hide: "Ausblenden" timezoneinfo: "Der Wochentag wird durch die Serverzeitzone bestimmt." + adsSettings: "Werbeeinstellungen" + notesPerOneAd: "Werbeintervall während Echtzeitaktualisierung (Notizen pro Werbung)" + setZeroToDisable: "Setze dies auf 0, um Werbung während Echtzeitaktualisierung zu deaktivieren" + adsTooClose: "Durch den momentan sehr niedrigen Werbeintervall kann es zu einer starken Verschlechterung der Benutzererfahrung kommen." _forgotPassword: enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst." ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator." diff --git a/locales/en-US.yml b/locales/en-US.yml index 827c04bd34..2d0b291415 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1129,6 +1129,12 @@ fileAttachedOnly: "Only notes with files" showRepliesToOthersInTimeline: "Show replies to others in TL" hideRepliesToOthersInTimeline: "Hide replies to others from TL" externalServices: "External Services" +impressum: "Impressum" +impressumUrl: "Impressum URL" +impressumDescription: "In some countries, like germany, the inclusion of operator contact information (an Impressum) is legally required for commercial websites." +privacyPolicy: "Privacy Policy" +privacyPolicyUrl: "Privacy Policy URL" +tosAndPrivacyPolicy: "Terms of Service and Privacy Policy" _announcement: forExistingUsers: "Existing users only" forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it." @@ -1527,6 +1533,10 @@ _ad: reduceFrequencyOfThisAd: "Show this ad less" hide: "Hide" timezoneinfo: "The day of the week is determined from the server's timezone." + adsSettings: "Ad settings" + notesPerOneAd: "Real-time update ad placement interval (Notes per ad)" + setZeroToDisable: "Set this value to 0 to disable real-time update ads" + adsTooClose: "The current ad interval may significantly worsen the user experience due to being too low." _forgotPassword: enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it." ifNoEmail: "If you did not use an email during registration, please contact the instance administrator instead." diff --git a/locales/it-IT.yml b/locales/it-IT.yml index a28d243566..710fc32ba2 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -64,7 +64,7 @@ reply: "Rispondi" loadMore: "Mostra di più" showMore: "Espandi" showLess: "Comprimi" -youGotNewFollower: "Ti sta seguendo" +youGotNewFollower: "Adesso ti segue" receiveFollowRequest: "Hai ricevuto una richiesta di follow" followRequestAccepted: "Ha accettato la tua richiesta di follow" mention: "Menzioni" @@ -336,7 +336,7 @@ instanceName: "Nome dell'istanza" instanceDescription: "Descrizione dell'istanza" maintainerName: "Nome dell'amministratore" maintainerEmail: "Indirizzo e-mail dell'amministratore" -tosUrl: "URL dei termini del servizio e della privacy" +tosUrl: "URL delle condizioni d'uso" thisYear: "Anno" thisMonth: "Mese" today: "Oggi" @@ -1129,6 +1129,12 @@ fileAttachedOnly: "Con file in allegato" showRepliesToOthersInTimeline: "Risposte altrui nella TL" hideRepliesToOthersInTimeline: "Nascondi Riposte altrui nella TL" externalServices: "Servizi esterni" +impressum: "Dichiarazione di proprietà" +impressumUrl: "URL della dichiarazione di proprietà" +impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)." +privacyPolicy: "Informativa sulla privacy" +privacyPolicyUrl: "URL della informativa privacy" +tosAndPrivacyPolicy: "Condizioni d'uso e informativa sulla privacy" _announcement: forExistingUsers: "Solo ai profili attuali" forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio." @@ -1538,7 +1544,7 @@ _gallery: unlike: "Non mi piace più" _email: _follow: - title: "Ha iniziato a seguirti" + title: "Adesso ti segue" _receiveFollowRequest: title: "Hai ricevuto una richiesta di follow" _plugin: @@ -2019,7 +2025,7 @@ _notification: youGotReply: "{name} ti ha risposto" youGotQuote: "{name} ha citato la tua Nota e ha detto" youRenoted: "{name} ha rinotato" - youWereFollowed: "Ha iniziato a seguirti" + youWereFollowed: "Adesso ti segue" youReceivedFollowRequest: "Hai ricevuto una richiesta di follow" yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata" pollEnded: "Risultati del sondaggio." diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 5e62cb1b7b..4cc336e35c 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1129,6 +1129,12 @@ fileAttachedOnly: "包含附件" showRepliesToOthersInTimeline: "在時間軸上顯示給其他人的回覆" hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆" externalServices: "外部服務" +impressum: "營運者資訊" +impressumUrl: "營運者資訊網址" +impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。" +privacyPolicy: "隱私政策" +privacyPolicyUrl: "隱私政策網址" +tosAndPrivacyPolicy: "服務條款和隱私政策" _announcement: forExistingUsers: "僅限既有的使用者" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" @@ -1527,6 +1533,10 @@ _ad: reduceFrequencyOfThisAd: "降低此廣告的頻率 " hide: "隱藏" timezoneinfo: "星期幾是由伺服器的時區指定的。" + adsSettings: "廣告投放設定" + notesPerOneAd: "即時更新中投放廣告的間隔(貼文數)" + setZeroToDisable: "設為 0 則在即時更新時不投放廣告" + adsTooClose: "由於廣告投放的間隔極短,可能會嚴重影響使用者體驗。" _forgotPassword: enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。" ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 " From 1564651bf628a8114543e542e5b188648e9f2aca Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 12:37:21 +0900 Subject: [PATCH 003/127] 2023.10.0-beta.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e4fe4cc7f..41bae0fa7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.9", + "version": "2023.10.0-beta.10", "codename": "nasubi", "repository": { "type": "git", From a2d3544a080597349dfd3e77d2160b077012c070 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 13:22:58 +0900 Subject: [PATCH 004/127] refactor(backend): better argument name --- packages/backend/src/core/FeaturedService.ts | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts index 62b50ed38d..9330d1a840 100644 --- a/packages/backend/src/core/FeaturedService.ts +++ b/packages/backend/src/core/FeaturedService.ts @@ -43,15 +43,15 @@ export class FeaturedService { } @bindThis - private async getRankingOf(name: string, windowRange: number, limit: number): Promise { + private async getRankingOf(name: string, windowRange: number, threshold: number): Promise { const currentWindow = this.getCurrentWindow(windowRange); const previousWindow = currentWindow - 1; const [currentRankingResult, previousRankingResult] = await Promise.all([ this.redisClient.zrange( - `${name}:${currentWindow}`, 0, limit, 'REV', 'WITHSCORES'), + `${name}:${currentWindow}`, 0, threshold, 'REV', 'WITHSCORES'), this.redisClient.zrange( - `${name}:${previousWindow}`, 0, limit, 'REV', 'WITHSCORES'), + `${name}:${previousWindow}`, 0, threshold, 'REV', 'WITHSCORES'), ]); const ranking = new Map(); @@ -95,22 +95,22 @@ export class FeaturedService { } @bindThis - public getGlobalNotesRanking(limit: number): Promise { - return this.getRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, limit); + public getGlobalNotesRanking(threshold: number): Promise { + return this.getRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, threshold); } @bindThis - public getInChannelNotesRanking(channelId: MiNote['channelId'], limit: number): Promise { - return this.getRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, limit); + public getInChannelNotesRanking(channelId: MiNote['channelId'], threshold: number): Promise { + return this.getRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, threshold); } @bindThis - public getPerUserNotesRanking(userId: MiUser['id'], limit: number): Promise { - return this.getRankingOf(`featuredPerUserNotesRanking:${userId}`, PER_USER_NOTES_RANKING_WINDOW, limit); + public getPerUserNotesRanking(userId: MiUser['id'], threshold: number): Promise { + return this.getRankingOf(`featuredPerUserNotesRanking:${userId}`, PER_USER_NOTES_RANKING_WINDOW, threshold); } @bindThis - public getHashtagsRanking(limit: number): Promise { - return this.getRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, limit); + public getHashtagsRanking(threshold: number): Promise { + return this.getRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, threshold); } } From 4f20c871860d0f62aef77bc716c3cc2677d4a592 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 13:32:41 +0900 Subject: [PATCH 005/127] lint fixes --- packages/backend/src/core/QueryService.ts | 101 ++++++++++-------- .../src/core/entities/RoleEntityService.ts | 7 +- .../src/server/ActivityPubServerService.ts | 7 +- .../server/api/endpoints/admin/roles/users.ts | 7 +- .../server/api/endpoints/channels/search.ts | 7 +- .../server/api/endpoints/notes/children.ts | 21 ++-- .../server/api/endpoints/notes/mentions.ts | 7 +- .../endpoints/notes/polls/recommendation.ts | 7 +- .../src/server/api/endpoints/roles/users.ts | 7 +- .../users/search-by-username-and-host.ts | 7 +- .../src/server/api/endpoints/users/search.ts | 21 ++-- packages/misskey-js/etc/misskey-js.api.md | 7 +- packages/misskey-js/src/api.ts | 3 +- packages/misskey-js/src/consts.ts | 2 +- packages/misskey-js/src/entities.ts | 3 + packages/sw/src/scripts/operations.ts | 2 +- 16 files changed, 124 insertions(+), 92 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 18bd49286e..4575917bf6 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -76,13 +76,15 @@ export class QueryService { // 投稿の引用元の作者にブロックされていない q .andWhere(`note.userId NOT IN (${ blockingQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserId IS NULL') - .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.replyUserId IS NULL') + .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`); })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserId IS NULL') - .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.renoteUserId IS NULL') + .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`); })); q.setParameters(blockingQuery.getParameters()); @@ -112,9 +114,10 @@ export class QueryService { .where('threadMuted.userId = :userId', { userId: me.id }); q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); - q.andWhere(new Brackets(qb => { qb - .where('note.threadId IS NULL') - .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); + q.andWhere(new Brackets(qb => { + qb + .where('note.threadId IS NULL') + .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); })); q.setParameters(mutedQuery.getParameters()); @@ -139,26 +142,31 @@ export class QueryService { // 投稿の引用元の作者をミュートしていない q .andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserId IS NULL') - .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.replyUserId IS NULL') + .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserId IS NULL') - .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.renoteUserId IS NULL') + .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); })) // mute instances - .andWhere(new Brackets(qb => { qb - .andWhere('note.userHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); + .andWhere(new Brackets(qb => { + qb + .andWhere('note.userHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); })) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); + .andWhere(new Brackets(qb => { + qb + .where('note.replyUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); + .andWhere(new Brackets(qb => { + qb + .where('note.renoteUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); })); q.setParameters(mutingQuery.getParameters()); @@ -180,36 +188,41 @@ export class QueryService { public generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: MiUser['id'] } | null): void { // This code must always be synchronized with the checks in Notes.isVisibleForMe. if (me == null) { - q.andWhere(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); + q.andWhere(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); })); } else { const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :meId'); - q.andWhere(new Brackets(qb => { qb + q.andWhere(new Brackets(qb => { + qb // 公開投稿である - .where(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })) + .where(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })) // または 自分自身 - .orWhere('note.userId = :meId') + .orWhere('note.userId = :meId') // または 自分宛て - .orWhere(':meId = ANY(note.visibleUserIds)') - .orWhere(':meId = ANY(note.mentions)') - .orWhere(new Brackets(qb => { qb - // または フォロワー宛ての投稿であり、 - .where('note.visibility = \'followers\'') - .andWhere(new Brackets(qb => { qb - // 自分がフォロワーである - .where(`note.userId IN (${ followingQuery.getQuery() })`) - // または 自分の投稿へのリプライ - .orWhere('note.replyUserId = :meId'); + .orWhere(':meId = ANY(note.visibleUserIds)') + .orWhere(':meId = ANY(note.mentions)') + .orWhere(new Brackets(qb => { + qb + // または フォロワー宛ての投稿であり、 + .where('note.visibility = \'followers\'') + .andWhere(new Brackets(qb => { + qb + // 自分がフォロワーである + .where(`note.userId IN (${ followingQuery.getQuery() })`) + // または 自分の投稿へのリプライ + .orWhere('note.replyUserId = :meId'); + })); })); - })); })); q.setParameters({ meId: me.id }); diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 23e82561d6..79375a7008 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -33,9 +33,10 @@ export class RoleEntityService { const assignedCount = await this.roleAssignmentsRepository.createQueryBuilder('assign') .where('assign.roleId = :roleId', { roleId: role.id }) - .andWhere(new Brackets(qb => { qb - .where('assign.expiresAt IS NULL') - .orWhere('assign.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('assign.expiresAt IS NULL') + .orWhere('assign.expiresAt > :now', { now: new Date() }); })) .getCount(); diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 2428fa2792..a7f6f82daf 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -379,9 +379,10 @@ export class ActivityPubServerService { if (page) { const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) .andWhere('note.userId = :userId', { userId: user.id }) - .andWhere(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); + .andWhere(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); })) .andWhere('note.localOnly = FALSE'); diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index b1772be777..ef5627bc9a 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -61,9 +61,10 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) .andWhere('assign.roleId = :roleId', { roleId: role.id }) - .andWhere(new Brackets(qb => { qb - .where('assign.expiresAt IS NULL') - .orWhere('assign.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('assign.expiresAt IS NULL') + .orWhere('assign.expiresAt > :now', { now: new Date() }); })) .innerJoinAndSelect('assign.user', 'user'); diff --git a/packages/backend/src/server/api/endpoints/channels/search.ts b/packages/backend/src/server/api/endpoints/channels/search.ts index 65df45706b..9c78a94844 100644 --- a/packages/backend/src/server/api/endpoints/channels/search.ts +++ b/packages/backend/src/server/api/endpoints/channels/search.ts @@ -55,9 +55,10 @@ export default class extends Endpoint { // eslint- if (ps.query !== '') { if (ps.type === 'nameAndDescription') { - query.andWhere(new Brackets(qb => { qb - .where('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }) - .orWhere('channel.description ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); + query.andWhere(new Brackets(qb => { + qb + .where('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }) + .orWhere('channel.description ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); })); } else { query.andWhere('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 1a82a4b5d7..1e569d9806 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -49,16 +49,19 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where('note.replyId = :noteId', { noteId: ps.noteId }) - .orWhere(new Brackets(qb => { qb - .where('note.renoteId = :noteId', { noteId: ps.noteId }) - .andWhere(new Brackets(qb => { qb - .where('note.text IS NOT NULL') - .orWhere('note.fileIds != \'{}\'') - .orWhere('note.hasPoll = TRUE'); + .andWhere(new Brackets(qb => { + qb + .where('note.replyId = :noteId', { noteId: ps.noteId }) + .orWhere(new Brackets(qb => { + qb + .where('note.renoteId = :noteId', { noteId: ps.noteId }) + .andWhere(new Brackets(qb => { + qb + .where('note.text IS NOT NULL') + .orWhere('note.fileIds != \'{}\'') + .orWhere('note.hasPoll = TRUE'); + })); })); - })); })) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 65e7bd8cd5..6fab024d17 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -59,9 +59,10 @@ export default class extends Endpoint { // eslint- .where('following.followerId = :followerId', { followerId: me.id }); const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where(`'{"${me.id}"}' <@ note.mentions`) - .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); + .andWhere(new Brackets(qb => { + qb + .where(`'{"${me.id}"}' <@ note.mentions`) + .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); })) // Avoid scanning primary key index .orderBy('CONCAT(note.id)', 'DESC') diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 29190af62a..986201e950 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -57,9 +57,10 @@ export default class extends Endpoint { // eslint- .where('poll.userHost IS NULL') .andWhere('poll.userId != :meId', { meId: me.id }) .andWhere('poll.noteVisibility = \'public\'') - .andWhere(new Brackets(qb => { qb - .where('poll.expiresAt IS NULL') - .orWhere('poll.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('poll.expiresAt IS NULL') + .orWhere('poll.expiresAt > :now', { now: new Date() }); })); //#region exclude arleady voted polls diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts index 37aac908b5..caaa3735e9 100644 --- a/packages/backend/src/server/api/endpoints/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -62,9 +62,10 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) .andWhere('assign.roleId = :roleId', { roleId: role.id }) - .andWhere(new Brackets(qb => { qb - .where('assign.expiresAt IS NULL') - .orWhere('assign.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('assign.expiresAt IS NULL') + .orWhere('assign.expiresAt > :now', { now: new Date() }); })) .innerJoinAndSelect('assign.user', 'user'); diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 74408cc64a..4bf25d9fbb 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -92,9 +92,10 @@ export default class extends Endpoint { // eslint- .andWhere(`user.id IN (${ followingQuery.getQuery() })`) .andWhere('user.id != :meId', { meId: me.id }) .andWhere('user.isSuspended = FALSE') - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })); query.setParameters(followingQuery.getParameters()); diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index aff5b98779..32b5c12372 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -64,9 +64,10 @@ export default class extends Endpoint { // eslint- if (isUsername) { const usernameQuery = this.usersRepository.createQueryBuilder('user') .where('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' }) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) .andWhere('user.isSuspended = FALSE'); @@ -91,9 +92,10 @@ export default class extends Endpoint { // eslint- qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' }); } })) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) .andWhere('user.isSuspended = FALSE'); @@ -122,9 +124,10 @@ export default class extends Endpoint { // eslint- const query = this.usersRepository.createQueryBuilder('user') .where(`user.id IN (${ profQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) .andWhere('user.isSuspended = FALSE') .setParameters(profQuery.getParameters()); diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 09662073ed..1a0bbeac78 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2755,6 +2755,9 @@ type Notification_2 = { invitation: UserGroup; user: User; userId: User['id']; +} | { + type: 'achievementEarned'; + achievement: string; } | { type: 'app'; header?: string | null; @@ -2765,7 +2768,7 @@ type Notification_2 = { }); // @public (undocumented) -export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app"]; +export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "achievementEarned"]; // @public (undocumented) type OriginType = 'combined' | 'local' | 'remote'; @@ -2981,7 +2984,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:630:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:107:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:597:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:600:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts index 974cb35ace..9415e692e3 100644 --- a/packages/misskey-js/src/api.ts +++ b/packages/misskey-js/src/api.ts @@ -67,8 +67,7 @@ export class APIClient { IsCaseMatched extends true ? GetCaseResult : IsCaseMatched extends true ? GetCaseResult : Endpoints[E]['res']['$switch']['$default'] - : Endpoints[E]['res']> - { + : Endpoints[E]['res']> { const promise = new Promise((resolve, reject) => { this.fetch(`${this.origin}/api/${endpoint}`, { method: 'POST', diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index ccc55537d2..c4ddead823 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'achievementEarned'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index b02f3e1f9b..aed242d8aa 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -277,6 +277,9 @@ export type Notification = { invitation: UserGroup; user: User; userId: User['id']; +} | { + type: 'achievementEarned'; + achievement: string; } | { type: 'app'; header?: string | null; diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts index be4f066b5f..0cbf4c7953 100644 --- a/packages/sw/src/scripts/operations.ts +++ b/packages/sw/src/scripts/operations.ts @@ -15,7 +15,7 @@ import { getUrlWithLoginId } from '@/scripts/login-id.js'; export const cli = new Misskey.api.APIClient({ origin, fetch: (...args): Promise => fetch(...args) }); export async function api(endpoint: E, userId?: string, options?: O): Promise>> { - let account: { token: string; id: string } | void; + let account: { token: string; id: string } | void = undefined; if (userId) { account = await getAccountFromId(userId); From 3a4039e2e1920d7aebd90792f47a84c5b748f2af Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 15:34:03 +0900 Subject: [PATCH 006/127] refactor --- packages/backend/src/core/FeaturedService.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts index 9330d1a840..cccbbd95cb 100644 --- a/packages/backend/src/core/FeaturedService.ts +++ b/packages/backend/src/core/FeaturedService.ts @@ -47,12 +47,12 @@ export class FeaturedService { const currentWindow = this.getCurrentWindow(windowRange); const previousWindow = currentWindow - 1; - const [currentRankingResult, previousRankingResult] = await Promise.all([ - this.redisClient.zrange( - `${name}:${currentWindow}`, 0, threshold, 'REV', 'WITHSCORES'), - this.redisClient.zrange( - `${name}:${previousWindow}`, 0, threshold, 'REV', 'WITHSCORES'), - ]); + const redisPipeline = this.redisClient.pipeline(); + redisPipeline.zrange( + `${name}:${currentWindow}`, 0, threshold, 'REV', 'WITHSCORES'); + redisPipeline.zrange( + `${name}:${previousWindow}`, 0, threshold, 'REV', 'WITHSCORES'); + const [currentRankingResult, previousRankingResult] = await redisPipeline.exec().then(result => result ? result.map(r => r[1] as string[]) : [[], []]); const ranking = new Map(); for (let i = 0; i < currentRankingResult.length; i += 2) { From 19a507633e18784a303adacbd416c709c128b69f Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 15:37:58 +0900 Subject: [PATCH 007/127] lint fixes --- .../src/pages/page-editor/page-editor.vue | 3 +-- packages/frontend/src/scripts/theme.ts | 24 +++++++------------ 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index 377267bdbc..dc749c292e 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -286,8 +286,7 @@ definePageMetadata(computed(() => { let title = i18n.ts._pages.newPage; if (props.initPageId) { title = i18n.ts._pages.editPage; - } - else if (props.initPageName && props.initUser) { + } else if (props.initPageName && props.initUser) { title = i18n.ts._pages.readPage; } return { diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts index 1c924e774f..b6383487c9 100644 --- a/packages/frontend/src/scripts/theme.ts +++ b/packages/frontend/src/scripts/theme.ts @@ -5,7 +5,11 @@ import { ref } from 'vue'; import tinycolor from 'tinycolor2'; -import { globalEvents } from '@/events'; +import { deepClone } from './clone.js'; +import { globalEvents } from '@/events.js'; +import lightTheme from '@/themes/_light.json5'; +import darkTheme from '@/themes/_dark.json5'; +import { miLocalStorage } from '@/local-storage.js'; export type Theme = { id: string; @@ -16,11 +20,6 @@ export type Theme = { props: Record; }; -import lightTheme from '@/themes/_light.json5'; -import darkTheme from '@/themes/_dark.json5'; -import { deepClone } from './clone'; -import { miLocalStorage } from '@/local-storage.js'; - export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); export const getBuiltinThemes = () => Promise.all( @@ -101,18 +100,11 @@ export function applyTheme(theme: Theme, persist = true) { function compile(theme: Theme): Record { function getColor(val: string): tinycolor.Instance { - // ref (prop) - if (val[0] === '@') { + if (val[0] === '@') { // ref (prop) return getColor(theme.props[val.substring(1)]); - } - - // ref (const) - else if (val[0] === '$') { + } else if (val[0] === '$') { // ref (const) return getColor(theme.props[val]); - } - - // func - else if (val[0] === ':') { + } else if (val[0] === ':') { // func const parts = val.split('<'); const func = parts.shift().substring(1); const arg = parseFloat(parts.shift()); From 0f367da84b65c4ca2b3bb5af152df261d6de1260 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 16:47:46 +0900 Subject: [PATCH 008/127] =?UTF-8?q?fix(backend):=20TL=E3=82=92=E9=80=94?= =?UTF-8?q?=E4=B8=AD=E3=81=BE=E3=81=A7=E3=81=97=E3=81=8B=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E3=81=93=E3=81=A8?= =?UTF-8?q?=E3=81=8C=E3=81=82=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #11404 --- CHANGELOG.md | 1 + packages/backend/src/core/AntennaService.ts | 3 +++ packages/backend/src/core/NoteCreateService.ts | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e655bef8b7..ba34554ba2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - Fix: 連合なしアンケートに投票をするとUpdateがリモートに配信されてしまうのを修正 - Fix: nodeinfoにおいてCORS用のヘッダーが設定されていないのを修正 - Fix: 同じ種類のTLのストリーミングを複数接続できない問題を修正 +- Fix: アンテナTLを途中までしかページネーションできなくなることがある問題を修正 ## 2023.9.3 ### General diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 95712b35b7..b64120772c 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -77,6 +77,9 @@ export class AntennaService implements OnApplicationShutdown { @bindThis public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise { + // リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、3分以内に投稿されたもののみを追加する + if (Date.now() - note.createdAt.getTime() > 1000 * 60 * 3) return; + const antennas = await this.getAntennas(); const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const))); const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 277875a19f..65beb9f970 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -822,6 +822,10 @@ export class NoteCreateService implements OnApplicationShutdown { @bindThis private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) { + // リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、3分以内に投稿されたもののみを追加する + // TODO: https://github.com/misskey-dev/misskey/issues/11404#issuecomment-1752480890 をやる + if (note.userHost != null && (Date.now() - note.createdAt.getTime()) > 1000 * 60 * 3) return; + const meta = await this.metaService.fetch(); const redisPipeline = this.redisForTimelines.pipeline(); From 0680ea3a78597d95464291d1fb9e703b46b7b1de Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 16:54:27 +0900 Subject: [PATCH 009/127] =?UTF-8?q?fix(backend):=20users/notes=20=E3=81=A7?= =?UTF-8?q?=E9=80=94=E4=B8=AD=E3=81=BE=E3=81=A7=E3=81=97=E3=81=8B=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E3=81=93?= =?UTF-8?q?=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/users/notes.ts | 76 ++++++++++--------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 5736aad5f9..62b2119a2c 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -144,45 +144,47 @@ export default class extends Endpoint { // eslint- timeline.sort((a, b) => a.id > b.id ? -1 : 1); - return await this.noteEntityService.packMany(timeline, me); - } else { - // fallback to database - - //#region Construct query - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.userId = :userId', { userId: ps.userId }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('note.channel', 'channel') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); - - if (!ps.withChannelNotes) { - query.andWhere('note.channelId IS NULL'); + if (timeline.length > 0) { + return await this.noteEntityService.packMany(timeline, me); } - - this.queryService.generateVisibilityQuery(query, me); - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :userId', { userId: ps.userId }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - //#endregion - - const timeline = await query.limit(ps.limit).getMany(); - - return await this.noteEntityService.packMany(timeline, me); } + + // fallback to database + + //#region Construct query + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.userId = :userId', { userId: ps.userId }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('note.channel', 'channel') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); + + if (!ps.withChannelNotes) { + query.andWhere('note.channelId IS NULL'); + } + + this.queryService.generateVisibilityQuery(query, me); + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :userId', { userId: ps.userId }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + //#endregion + + const timeline = await query.limit(ps.limit).getMany(); + + return await this.noteEntityService.packMany(timeline, me); }); } } From b3d6334b5c60381e1356e512922efc0d34ebebce Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 16:56:51 +0900 Subject: [PATCH 010/127] 2023.10.0-beta.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 41bae0fa7a..86eb4db91b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.10", + "version": "2023.10.0-beta.11", "codename": "nasubi", "repository": { "type": "git", From 7066d61730e1172e154f81bfa76f436cc9586e6a Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 17:41:54 +0900 Subject: [PATCH 011/127] fix --- packages/backend/src/core/QueryService.ts | 2 +- packages/backend/src/server/api/endpoints/users/notes.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 4575917bf6..60333a5b91 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -124,7 +124,7 @@ export class QueryService { } @bindThis - public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: MiUser): void { + public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 62b2119a2c..35b22af1da 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -166,6 +166,10 @@ export default class extends Endpoint { // eslint- } this.queryService.generateVisibilityQuery(query, me); + if (me) { + this.queryService.generateMutedUserQuery(query, me, { id: ps.userId }); + this.queryService.generateBlockedUserQuery(query, me); + } if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); From 6ff98846e62eec3e6a4c88d658c7e98971195b34 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 17:48:09 +0900 Subject: [PATCH 012/127] =?UTF-8?q?fix(backend):=20=E3=80=8C=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E4=BB=98=E3=81=8D=E3=81=AE=E3=81=BF?= =?UTF-8?q?=E3=80=8D=E3=81=AETL=E3=81=A7=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E7=84=A1=E3=81=97=E3=81=AE=E6=96=B0=E7=9D=80=E3=83=8E?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #11939 --- CHANGELOG.md | 1 + .../src/server/api/stream/channels/global-timeline.ts | 4 ++++ .../backend/src/server/api/stream/channels/home-timeline.ts | 4 ++++ .../src/server/api/stream/channels/hybrid-timeline.ts | 4 ++++ .../src/server/api/stream/channels/local-timeline.ts | 4 ++++ .../backend/src/server/api/stream/channels/user-list.ts | 6 +++++- packages/frontend/src/components/MkTimeline.vue | 2 -- 7 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba34554ba2..bea6501209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ - Fix: nodeinfoにおいてCORS用のヘッダーが設定されていないのを修正 - Fix: 同じ種類のTLのストリーミングを複数接続できない問題を修正 - Fix: アンテナTLを途中までしかページネーションできなくなることがある問題を修正 +- Fix: 「ファイル付きのみ」のTLでファイル無しの新着ノートが流れる問題を修正 ## 2023.9.3 ### General diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 552506fbbe..03f2dff62b 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -19,6 +19,7 @@ class GlobalTimelineChannel extends Channel { public static shouldShare = false; public static requireCredential = false; private withRenotes: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +39,7 @@ class GlobalTimelineChannel extends Channel { if (!policies.gtlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -45,6 +47,8 @@ class GlobalTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.visibility !== 'public') return; if (note.channelId != null) return; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index e377246f34..24be590504 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -17,6 +17,7 @@ class HomeTimelineChannel extends Channel { public static shouldShare = false; public static requireCredential = true; private withRenotes: boolean; + private withFiles: boolean; constructor( private noteEntityService: NoteEntityService, @@ -31,12 +32,15 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { this.withRenotes = params.withRenotes ?? true; + this.withFiles = params.withFiles ?? false; this.subscriber.on('notesStream', this.onNote); } @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.channelId) { if (!this.followingChannels.has(note.channelId)) return; } else { diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 348be9c7e4..d5f5d54e46 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel { public static shouldShare = false; public static requireCredential = true; private withRenotes: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +39,7 @@ class HybridTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -45,6 +47,8 @@ class HybridTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + // チャンネルの投稿ではなく、自分自身の投稿 または // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または // チャンネルの投稿ではなく、全体公開のローカルの投稿 または diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 849cbfa560..94c22f8915 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel { public static shouldShare = false; public static requireCredential = false; private withRenotes: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -37,6 +38,7 @@ class LocalTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -44,6 +46,8 @@ class LocalTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.user.host !== null) return; if (note.visibility !== 'public') return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 03f7760d8e..240822d9ab 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -18,8 +18,9 @@ class UserListChannel extends Channel { public static shouldShare = false; public static requireCredential = false; private listId: string; - public membershipsMap: Record | undefined> = {}; + private membershipsMap: Record | undefined> = {}; private listUsersClock: NodeJS.Timeout; + private withFiles: boolean; constructor( private userListsRepository: UserListsRepository, @@ -37,6 +38,7 @@ class UserListChannel extends Channel { @bindThis public async init(params: any) { this.listId = params.listId as string; + this.withFiles = params.withFiles ?? false; // Check existence and owner const listExist = await this.userListsRepository.exist({ @@ -76,6 +78,8 @@ class UserListChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (!Object.hasOwn(this.membershipsMap, note.userId)) return; if (['followers', 'specified'].includes(note.visibility)) { diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 4e71c048b2..45dedd5042 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -138,12 +138,10 @@ if (props.src === 'antenna') { } else if (props.src === 'list') { endpoint = 'notes/user-list-timeline'; query = { - withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }; connection = stream.useChannel('userList', { - withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }); From 04971ca5654ce900429de29388910324e89926c3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 18:13:53 +0900 Subject: [PATCH 013/127] =?UTF-8?q?perf(backend):=20untilDate/sinceDate?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E6=99=82=E3=81=AE=E3=82=AF=E3=82=A8=E3=83=AA?= =?UTF-8?q?=E3=83=91=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E=E3=83=B3=E3=82=B9?= =?UTF-8?q?=E3=82=92=E5=90=91=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/QueryService.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 60333a5b91..50d1d2e65b 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import type { MiUser } from '@/models/User.js'; import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import type { SelectQueryBuilder } from 'typeorm'; @Injectable() @@ -34,6 +35,8 @@ export class QueryService { @Inject(DI.renoteMutingsRepository) private renoteMutingsRepository: RenoteMutingsRepository, + + private idService: IdService, ) { } @@ -49,15 +52,15 @@ export class QueryService { q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); q.orderBy(`${q.alias}.id`, 'DESC'); } else if (sinceDate && untilDate) { - q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); - q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); - q.orderBy(`${q.alias}.createdAt`, 'DESC'); + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); + q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); + q.orderBy(`${q.alias}.id`, 'DESC'); } else if (sinceDate) { - q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); - q.orderBy(`${q.alias}.createdAt`, 'ASC'); + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); + q.orderBy(`${q.alias}.id`, 'ASC'); } else if (untilDate) { - q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); - q.orderBy(`${q.alias}.createdAt`, 'DESC'); + q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); + q.orderBy(`${q.alias}.id`, 'DESC'); } else { q.orderBy(`${q.alias}.id`, 'DESC'); } From 7473b2854fb53ccd5612254887202259113d2c21 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 18:14:38 +0900 Subject: [PATCH 014/127] =?UTF-8?q?fix(backend):=20users/notes=E3=81=A7sin?= =?UTF-8?q?ceId=E6=8C=87=E5=AE=9A=E6=99=82=E3=81=AB=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=83=99=E3=83=BC=E3=82=B9=E3=81=AB=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E3=83=90=E3=83=83=E3=82=AF=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/users/notes.ts | 126 +++++++++--------- 1 file changed, 65 insertions(+), 61 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 35b22af1da..4a3d2e5be4 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -72,80 +72,84 @@ export default class extends Endpoint { // eslint- private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const [ - userIdsWhoMeMuting, - ] = me ? await Promise.all([ - this.cacheService.userMutingsCache.fetch(me.id), - ]) : [new Set()]; + const isRangeSpecified = (ps.sinceId != null || ps.sinceDate != null) && (ps.untilId != null || ps.untilDate != null); - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 + if (isRangeSpecified || !(ps.sinceId != null || ps.sinceDate != null)) { + const [ + userIdsWhoMeMuting, + ] = me ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(me.id), + ]) : [new Set()]; - const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([ - this.redisForTimelines.xrevrange( - ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)), - ps.withReplies - ? this.redisForTimelines.xrevrange( - `userTimelineWithReplies:${ps.userId}`, + const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 + + const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([ + this.redisForTimelines.xrevrange( + ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)) - : Promise.resolve([]), - ps.withChannelNotes - ? this.redisForTimelines.xrevrange( - `userTimelineWithChannel:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)) - : Promise.resolve([]), - ]); + ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)), + ps.withReplies + ? this.redisForTimelines.xrevrange( + `userTimelineWithReplies:${ps.userId}`, + ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', + ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', + 'COUNT', limit, + ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)) + : Promise.resolve([]), + ps.withChannelNotes + ? this.redisForTimelines.xrevrange( + `userTimelineWithChannel:${ps.userId}`, + ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', + ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', + 'COUNT', limit, + ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)) + : Promise.resolve([]), + ]); - let noteIds = Array.from(new Set([ - ...noteIdsRes, - ...repliesNoteIdsRes, - ...channelNoteIdsRes, - ])); - noteIds.sort((a, b) => a > b ? -1 : 1); - noteIds = noteIds.slice(0, ps.limit); + let noteIds = Array.from(new Set([ + ...noteIdsRes, + ...repliesNoteIdsRes, + ...channelNoteIdsRes, + ])); + noteIds.sort((a, b) => a > b ? -1 : 1); + noteIds = noteIds.slice(0, ps.limit); - if (noteIds.length > 0) { - const isFollowing = me ? me.id === ps.userId || Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId) : false; + if (noteIds.length > 0) { + const isFollowing = me ? me.id === ps.userId || Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId) : false; - const query = this.notesRepository.createQueryBuilder('note') - .where('note.id IN (:...noteIds)', { noteIds: noteIds }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('note.channel', 'channel'); + const query = this.notesRepository.createQueryBuilder('note') + .where('note.id IN (:...noteIds)', { noteIds: noteIds }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); - let timeline = await query.getMany(); + let timeline = await query.getMany(); - timeline = timeline.filter(note => { - if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false; + timeline = timeline.filter(note => { + if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false; - if (note.renoteId) { - if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) { - if (ps.withRenotes === false) return false; + if (note.renoteId) { + if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) { + if (ps.withRenotes === false) return false; + } } + + if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false; + if (note.visibility === 'followers' && !isFollowing) return false; + + return true; + }); + + timeline.sort((a, b) => a.id > b.id ? -1 : 1); + + if (timeline.length > 0) { + return await this.noteEntityService.packMany(timeline, me); } - - if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false; - if (note.visibility === 'followers' && !isFollowing) return false; - - return true; - }); - - timeline.sort((a, b) => a.id > b.id ? -1 : 1); - - if (timeline.length > 0) { - return await this.noteEntityService.packMany(timeline, me); } } From aafe80c12119e17684e0274bcbd89569a4122e75 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 18:48:43 +0900 Subject: [PATCH 015/127] 2023.10.0-beta.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 86eb4db91b..862a1b2657 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.11", + "version": "2023.10.0-beta.12", "codename": "nasubi", "repository": { "type": "git", From 0bb0c329087149355a9968e7c142b9ecdf1023ec Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 20:31:39 +0900 Subject: [PATCH 016/127] =?UTF-8?q?enhance(backend):=20Redis=E3=81=B8?= =?UTF-8?q?=E3=81=AETL=E3=81=AE=E6=A7=8B=E7=AF=89=E3=82=92List=E3=81=A7?= =?UTF-8?q?=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #11404 --- packages/backend/src/core/AntennaService.ts | 12 +- packages/backend/src/core/CoreModule.ts | 6 + .../backend/src/core/NoteCreateService.ts | 110 +++------------ .../backend/src/core/RedisTimelineService.ts | 80 +++++++++++ packages/backend/src/core/RoleService.ts | 9 +- .../server/api/endpoints/antennas/notes.ts | 16 +-- .../server/api/endpoints/channels/timeline.ts | 125 +++++++++--------- .../api/endpoints/notes/hybrid-timeline.ts | 28 ++-- .../api/endpoints/notes/local-timeline.ts | 21 ++- .../server/api/endpoints/notes/timeline.ts | 19 ++- .../api/endpoints/notes/user-list-timeline.ts | 19 ++- .../src/server/api/endpoints/roles/notes.ts | 14 +- .../src/server/api/endpoints/users/notes.ts | 43 ++---- 13 files changed, 239 insertions(+), 263 deletions(-) create mode 100644 packages/backend/src/core/RedisTimelineService.ts diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index b64120772c..ca7624b1d4 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -16,6 +16,7 @@ import type { AntennasRepository, UserListMembershipsRepository } from '@/models import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -38,6 +39,7 @@ export class AntennaService implements OnApplicationShutdown { private utilityService: UtilityService, private globalEventService: GlobalEventService, + private redisTimelineService: RedisTimelineService, ) { this.antennasFetched = false; this.antennas = []; @@ -77,9 +79,6 @@ export class AntennaService implements OnApplicationShutdown { @bindThis public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise { - // リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、3分以内に投稿されたもののみを追加する - if (Date.now() - note.createdAt.getTime() > 1000 * 60 * 3) return; - const antennas = await this.getAntennas(); const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const))); const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna); @@ -87,12 +86,7 @@ export class AntennaService implements OnApplicationShutdown { const redisPipeline = this.redisForTimelines.pipeline(); for (const antenna of matchedAntennas) { - redisPipeline.xadd( - `antennaTimeline:${antenna.id}`, - 'MAXLEN', '~', '200', - '*', - 'note', note.id); - + this.redisTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline); this.globalEventService.publishAntennaStream(antenna.id, 'note', note); } diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 1984d9e6c2..0dc025d998 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -61,6 +61,7 @@ import { FileInfoService } from './FileInfoService.js'; import { SearchService } from './SearchService.js'; import { ClipService } from './ClipService.js'; import { FeaturedService } from './FeaturedService.js'; +import { RedisTimelineService } from './RedisTimelineService.js'; import { ChartLoggerService } from './chart/ChartLoggerService.js'; import FederationChart from './chart/charts/federation.js'; import NotesChart from './chart/charts/notes.js'; @@ -189,6 +190,7 @@ const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: Fi const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService }; const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService }; const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService }; +const $RedisTimelineService: Provider = { provide: 'RedisTimelineService', useExisting: RedisTimelineService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -321,6 +323,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SearchService, ClipService, FeaturedService, + RedisTimelineService, ChartLoggerService, FederationChart, NotesChart, @@ -446,6 +449,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SearchService, $ClipService, $FeaturedService, + $RedisTimelineService, $ChartLoggerService, $FederationChart, $NotesChart, @@ -572,6 +576,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SearchService, ClipService, FeaturedService, + RedisTimelineService, FederationChart, NotesChart, UsersChart, @@ -696,6 +701,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SearchService, $ClipService, $FeaturedService, + $RedisTimelineService, $FederationChart, $NotesChart, $UsersChart, diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 65beb9f970..2a73467122 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -54,6 +54,7 @@ import { RoleService } from '@/core/RoleService.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; import { FeaturedService } from '@/core/FeaturedService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -194,6 +195,7 @@ export class NoteCreateService implements OnApplicationShutdown { private idService: IdService, private globalEventService: GlobalEventService, private queueService: QueueService, + private redisTimelineService: RedisTimelineService, private noteReadService: NoteReadService, private notificationService: NotificationService, private relayService: RelayService, @@ -347,14 +349,6 @@ export class NoteCreateService implements OnApplicationShutdown { const note = await this.insertNote(user, data, tags, emojis, mentionedUsers); - if (data.channel) { - this.redisForTimelines.xadd( - `channelTimeline:${data.channel.id}`, - 'MAXLEN', '~', this.config.perChannelMaxNoteCacheCount.toString(), - '*', - 'note', note.id); - } - setImmediate('post created', { signal: this.#shutdownController.signal }).then( () => this.postNoteCreated(note, user, data, silent, tags!, mentionedUsers!), () => { /* aborted, ignore this */ }, @@ -822,20 +816,14 @@ export class NoteCreateService implements OnApplicationShutdown { @bindThis private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) { - // リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、3分以内に投稿されたもののみを追加する - // TODO: https://github.com/misskey-dev/misskey/issues/11404#issuecomment-1752480890 をやる - if (note.userHost != null && (Date.now() - note.createdAt.getTime()) > 1000 * 60 * 3) return; - const meta = await this.metaService.fetch(); - const redisPipeline = this.redisForTimelines.pipeline(); + const r = this.redisForTimelines.pipeline(); if (note.channelId) { - redisPipeline.xadd( - `userTimelineWithChannel:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r); + + this.redisTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); const channelFollowings = await this.channelFollowingsRepository.find({ where: { @@ -845,18 +833,9 @@ export class NoteCreateService implements OnApplicationShutdown { }); for (const channelFollowing of channelFollowings) { - redisPipeline.xadd( - `homeTimeline:${channelFollowing.followerId}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${channelFollowing.followerId}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } } else { @@ -894,18 +873,9 @@ export class NoteCreateService implements OnApplicationShutdown { if (!following.withReplies) continue; } - redisPipeline.xadd( - `homeTimeline:${following.followerId}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${following.followerId}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } @@ -921,72 +891,32 @@ export class NoteCreateService implements OnApplicationShutdown { if (!userListMembership.withReplies) continue; } - redisPipeline.xadd( - `userListTimeline:${userListMembership.userListId}`, - 'MAXLEN', '~', meta.perUserListTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `userListTimelineWithFiles:${userListMembership.userListId}`, - 'MAXLEN', '~', (meta.perUserListTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r); } } if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL - redisPipeline.xadd( - `homeTimeline:${user.id}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${user.id}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } // 自分自身以外への返信 if (note.replyId && note.replyUserId !== note.userId) { - redisPipeline.xadd( - `userTimelineWithReplies:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); } else { - redisPipeline.xadd( - `userTimeline:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `userTimelineWithFiles:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? (meta.perLocalUserUserTimelineCacheMax / 2).toString() : (meta.perRemoteUserUserTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r); } if (note.visibility === 'public' && note.userHost == null) { - redisPipeline.xadd( - 'localTimeline', - 'MAXLEN', '~', '1000', - '*', - 'note', note.id); - + this.redisTimelineService.push('localTimeline', note.id, 1000, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - 'localTimelineWithFiles', - 'MAXLEN', '~', '500', - '*', - 'note', note.id); + this.redisTimelineService.push('localTimelineWithFiles', note.id, 500, r); } } } @@ -998,7 +928,7 @@ export class NoteCreateService implements OnApplicationShutdown { } } - redisPipeline.exec(); + r.exec(); } @bindThis diff --git a/packages/backend/src/core/RedisTimelineService.ts b/packages/backend/src/core/RedisTimelineService.ts new file mode 100644 index 0000000000..f0ca2726d6 --- /dev/null +++ b/packages/backend/src/core/RedisTimelineService.ts @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; + +@Injectable() +export class RedisTimelineService { + constructor( + @Inject(DI.redisForTimelines) + private redisForTimelines: Redis.Redis, + + private idService: IdService, + ) { + } + + @bindThis + public push(tl: string, id: string, maxlen: number, pipeline: Redis.ChainableCommander) { + // リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、 + // 3分以内に投稿されたものでない場合、Redisにある最古のIDより新しい場合のみ追加する + if (this.idService.parse(id).date.getTime() > Date.now() - 1000 * 60 * 3) { + pipeline.lpush('list:' + tl, id); + if (Math.random() < 0.1) { // 10%の確率でトリム + pipeline.ltrim('list:' + tl, 0, maxlen - 1); + } + } else { + // 末尾のIDを取得 + this.redisForTimelines.lindex('list:' + tl, -1).then(lastId => { + if (lastId == null || (this.idService.parse(id).date.getTime() > this.idService.parse(lastId).date.getTime())) { + this.redisForTimelines.lpush('list:' + tl, id); + } else { + Promise.resolve(); + } + }); + } + } + + @bindThis + public get(name: string, untilId?: string | null, sinceId?: string | null) { + if (untilId && sinceId) { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.filter(id => id > untilId && id < sinceId).sort((a, b) => a > b ? -1 : 1)); + } else if (untilId) { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.filter(id => id > untilId).sort((a, b) => a > b ? -1 : 1)); + } else if (sinceId) { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.filter(id => id < sinceId).sort((a, b) => a < b ? -1 : 1)); + } else { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.sort((a, b) => a > b ? -1 : 1)); + } + } + + @bindThis + public getMulti(name: string[], untilId?: string | null, sinceId?: string | null): Promise { + const pipeline = this.redisForTimelines.pipeline(); + for (const n of name) { + pipeline.lrange('list:' + n, 0, -1); + } + return pipeline.exec().then(res => { + if (res == null) return []; + const tls = res.map(r => r[1] as string[]); + return tls.map(ids => + (untilId && sinceId) + ? ids.filter(id => id > untilId && id < sinceId).sort((a, b) => a > b ? -1 : 1) + : untilId + ? ids.filter(id => id > untilId).sort((a, b) => a > b ? -1 : 1) + : sinceId + ? ids.filter(id => id < sinceId).sort((a, b) => a < b ? -1 : 1) + : ids.sort((a, b) => a > b ? -1 : 1), + ); + }); + } +} diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index f2bd9de5ee..2c3547e4ac 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -20,6 +20,7 @@ import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import type { Packed } from '@/misc/json-schema.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; export type RolePolicies = { @@ -102,6 +103,7 @@ export class RoleService implements OnApplicationShutdown { private globalEventService: GlobalEventService, private idService: IdService, private moderationLogService: ModerationLogService, + private redisTimelineService: RedisTimelineService, ) { //this.onMessage = this.onMessage.bind(this); @@ -472,12 +474,7 @@ export class RoleService implements OnApplicationShutdown { const redisPipeline = this.redisClient.pipeline(); for (const role of roles) { - redisPipeline.xadd( - `roleTimeline:${role.id}`, - 'MAXLEN', '~', '1000', - '*', - 'note', note.id); - + this.redisTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline); this.globalEventService.publishRoleTimelineStream(role.id, 'note', note); } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index b563f704a8..7d6841563a 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -12,6 +12,7 @@ import { NoteReadService } from '@/core/NoteReadService.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -69,8 +70,12 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private queryService: QueryService, private noteReadService: NoteReadService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, userId: me.id, @@ -85,15 +90,8 @@ export default class extends Endpoint { // eslint- lastUsedAt: new Date(), }); - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - - const noteIds = await this.redisForTimelines.xrevrange( - `antennaTimeline:${antenna.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)); - + let noteIds = await this.redisTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; } diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index f0b14d4fd8..e063e0e2fc 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -12,6 +12,9 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; +import { isUserRelated } from '@/misc/is-user-related.js'; +import { CacheService } from '@/core/CacheService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -66,9 +69,15 @@ export default class extends Endpoint { // eslint- private idService: IdService, private noteEntityService: NoteEntityService, private queryService: QueryService, + private redisTimelineService: RedisTimelineService, + private cacheService: CacheService, private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const isRangeSpecified = untilId != null && sinceId != null; + const channel = await this.channelsRepository.findOneBy({ id: ps.channelId, }); @@ -77,68 +86,66 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchChannel); } - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - noteIdsRes = await this.redisForTimelines.xrevrange( - `channelTimeline:${channel.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - } - - // redis から取得していないとき・取得数が足りないとき - if (noteIdsRes.length < limit) { - //#region Construct query - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.channelId = :channelId', { channelId: channel.id }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('note.channel', 'channel'); - - if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - } - //#endregion - - timeline = await query.limit(ps.limit).getMany(); - } else { - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); - - if (noteIds.length === 0) { - return []; - } - - //#region Construct query - const query = this.notesRepository.createQueryBuilder('note') - .where('note.id IN (:...noteIds)', { noteIds: noteIds }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('note.channel', 'channel'); - - if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - } - //#endregion - - timeline = await query.getMany(); - timeline.sort((a, b) => a.id > b.id ? -1 : 1); - } - if (me) this.activeUsersChart.read(me); + if (isRangeSpecified || sinceId == null) { + const [ + userIdsWhoMeMuting, + ] = me ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(me.id), + ]) : [new Set()]; + + let noteIds = await this.redisTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); + + if (noteIds.length > 0) { + const query = this.notesRepository.createQueryBuilder('note') + .where('note.id IN (:...noteIds)', { noteIds: noteIds }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); + + let timeline = await query.getMany(); + + timeline = timeline.filter(note => { + if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false; + + return true; + }); + + // TODO: フィルタで件数が減った場合の埋め合わせ処理 + + timeline.sort((a, b) => a.id > b.id ? -1 : 1); + + if (timeline.length > 0) { + return await this.noteEntityService.packMany(timeline, me); + } + } + } + + //#region fallback to database + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.channelId = :channelId', { channelId: channel.id }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); + + if (me) { + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + } + //#endregion + + const timeline = await query.limit(ps.limit).getMany(); + return await this.noteEntityService.packMany(timeline, me); + //#endregion }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index d5b706e543..a05269794c 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -15,6 +15,7 @@ import { RoleService } from '@/core/RoleService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { CacheService } from '@/core/CacheService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -72,8 +73,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const policies = await this.roleService.getUserPolicies(me.id); if (!policies.ltlAvailable) { throw new ApiError(meta.errors.stlDisabled); @@ -89,27 +94,10 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - - const redisPipeline = this.redisForTimelines.pipeline(); - redisPipeline.xrevrange( + const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([ ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ); - redisPipeline.xrevrange( ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ); - const [htlNoteIds, ltlNoteIds] = await redisPipeline.exec().then(res => res ? [ - (res[0][1] as string[][]).map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId), - (res[1][1] as string[][]).map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId), - ] : []); + ], untilId, sinceId); let noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); noteIds.sort((a, b) => a > b ? -1 : 1); @@ -128,7 +116,7 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (note.userId === me.id) { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index b9305e8e26..c8ea003ba6 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -15,6 +15,7 @@ import { RoleService } from '@/core/RoleService.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -68,8 +69,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const policies = await this.roleService.getUserPolicies(me ? me.id : null); if (!policies.ltlAvailable) { throw new ApiError(meta.errors.ltlDisabled); @@ -85,16 +90,8 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]) : [new Set(), new Set(), new Set()]; - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - - const noteIds = await this.redisForTimelines.xrevrange( - ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)); + let noteIds = await this.redisTimelineService.get(ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -109,7 +106,7 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (me && (note.userId === me.id)) { @@ -127,6 +124,8 @@ export default class extends Endpoint { // eslint- return true; }); + // TODO: フィルタした結果件数が足りなかった場合の対応 + timeline.sort((a, b) => a.id > b.id ? -1 : 1); process.nextTick(() => { diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index cd2ec8fe29..cd45135f99 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; export const meta = { tags: ['notes'], @@ -62,8 +63,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const [ followings, userIdsWhoMeMuting, @@ -76,16 +81,8 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - - const noteIds = await this.redisForTimelines.xrevrange( - ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)); + let noteIds = await this.redisTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -100,7 +97,7 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (note.userId === me.id) { diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 2eec6297c0..96c8ff5b1b 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js'; import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -79,8 +80,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private cacheService: CacheService, private idService: IdService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const list = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, @@ -100,16 +105,8 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - - const noteIds = await this.redisForTimelines.xrevrange( - ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)); + let noteIds = await this.redisTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -124,7 +121,7 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (note.userId === me.id) { diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 3f2b31c02f..366c3a7cc1 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -11,6 +11,7 @@ import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -65,8 +66,12 @@ export default class extends Endpoint { // eslint- private idService: IdService, private noteEntityService: NoteEntityService, private queryService: QueryService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const role = await this.rolesRepository.findOneBy({ id: ps.roleId, isPublic: true, @@ -78,14 +83,9 @@ export default class extends Endpoint { // eslint- if (!role.isExplorable) { return []; } - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - const noteIds = await this.redisForTimelines.xrevrange( - `roleTimeline:${role.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)); + let noteIds = await this.redisTimelineService.get(`roleTimeline:${role.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 4a3d2e5be4..30cd5b791f 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -14,6 +14,7 @@ import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { QueryService } from '@/core/QueryService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -70,42 +71,24 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private cacheService: CacheService, private idService: IdService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const isRangeSpecified = (ps.sinceId != null || ps.sinceDate != null) && (ps.untilId != null || ps.untilDate != null); + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const isRangeSpecified = untilId != null && sinceId != null; - if (isRangeSpecified || !(ps.sinceId != null || ps.sinceDate != null)) { + if (isRangeSpecified || sinceId == null) { const [ userIdsWhoMeMuting, ] = me ? await Promise.all([ this.cacheService.userMutingsCache.fetch(me.id), ]) : [new Set()]; - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([ - this.redisForTimelines.xrevrange( - ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)), - ps.withReplies - ? this.redisForTimelines.xrevrange( - `userTimelineWithReplies:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)) - : Promise.resolve([]), - ps.withChannelNotes - ? this.redisForTimelines.xrevrange( - `userTimelineWithChannel:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit, - ).then(res => res.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId)) - : Promise.resolve([]), + this.redisTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId), + ps.withReplies ? this.redisTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), + ps.withChannelNotes ? this.redisTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), ]); let noteIds = Array.from(new Set([ @@ -145,6 +128,8 @@ export default class extends Endpoint { // eslint- return true; }); + // TODO: フィルタで件数が減った場合の埋め合わせ処理 + timeline.sort((a, b) => a.id > b.id ? -1 : 1); if (timeline.length > 0) { @@ -153,9 +138,7 @@ export default class extends Endpoint { // eslint- } } - // fallback to database - - //#region Construct query + //#region fallback to database const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: ps.userId }) .innerJoinAndSelect('note.user', 'user') @@ -188,11 +171,11 @@ export default class extends Endpoint { // eslint- qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); })); } - //#endregion const timeline = await query.limit(ps.limit).getMany(); return await this.noteEntityService.packMany(timeline, me); + //#endregion }); } } From 11c9e193a46a731a1b4a09571b973fb49201018c Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 20:47:49 +0900 Subject: [PATCH 017/127] =?UTF-8?q?fix(backend):=20Misskey=E3=81=AE?= =?UTF-8?q?=E3=83=90=E3=83=83=E3=82=AF=E3=82=A8=E3=83=B3=E3=83=89=E3=83=97?= =?UTF-8?q?=E3=83=AD=E3=82=BB=E3=82=B9=E3=81=8C=E7=B5=82=E4=BA=86=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #10995 --- CHANGELOG.md | 1 + packages/backend/src/boot/common.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bea6501209..d0c3af806a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - Fix: 同じ種類のTLのストリーミングを複数接続できない問題を修正 - Fix: アンテナTLを途中までしかページネーションできなくなることがある問題を修正 - Fix: 「ファイル付きのみ」のTLでファイル無しの新着ノートが流れる問題を修正 +- Fix: プロセスが終了しない、あるいは非常に時間がかかる問題を修正 ## 2023.9.3 ### General diff --git a/packages/backend/src/boot/common.ts b/packages/backend/src/boot/common.ts index 4783a2b2da..df10ab1e3d 100644 --- a/packages/backend/src/boot/common.ts +++ b/packages/backend/src/boot/common.ts @@ -17,7 +17,6 @@ export async function server() { const app = await NestFactory.createApplicationContext(MainModule, { logger: new NestLogger(), }); - app.enableShutdownHooks(); const serverService = app.get(ServerService); await serverService.launch(); @@ -35,7 +34,6 @@ export async function jobQueue() { const jobQueue = await NestFactory.createApplicationContext(QueueProcessorModule, { logger: new NestLogger(), }); - jobQueue.enableShutdownHooks(); jobQueue.get(QueueProcessorService).start(); jobQueue.get(ChartManagementService).start(); From 13dbfef9f8c9b4af5cd70fa92233c6987e68b995 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 20:55:40 +0900 Subject: [PATCH 018/127] update deps --- package.json | 2 +- packages/backend/package.json | 8 +- packages/frontend/package.json | 14 +- packages/misskey-js/package.json | 4 +- packages/sw/package.json | 2 +- pnpm-lock.yaml | 549 +++++++++++++++---------------- 6 files changed, 286 insertions(+), 293 deletions(-) diff --git a/package.json b/package.json index 862a1b2657..fd034a6e2f 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@typescript-eslint/parser": "6.7.4", "cross-env": "7.0.3", "cypress": "13.3.0", - "eslint": "8.50.0", + "eslint": "8.51.0", "start-server-and-test": "2.0.1" }, "optionalDependencies": { diff --git a/packages/backend/package.json b/packages/backend/package.json index 2f0cf10374..796445ab23 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -124,13 +124,13 @@ "nanoid": "5.0.1", "nested-property": "4.0.0", "node-fetch": "3.3.2", - "nodemailer": "6.9.5", + "nodemailer": "6.9.6", "nsfwjs": "2.4.2", "oauth": "0.10.0", "oauth2orize": "1.11.1", "oauth2orize-pkce": "0.1.2", "os-utils": "0.0.14", - "otpauth": "9.1.4", + "otpauth": "9.1.5", "parse5": "7.1.2", "pg": "8.11.3", "pkce-challenge": "4.0.1", @@ -189,7 +189,7 @@ "@types/jsrsasign": "10.5.9", "@types/mime-types": "2.1.2", "@types/ms": "0.7.32", - "@types/node": "20.8.2", + "@types/node": "20.8.3", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.11", "@types/oauth": "0.9.2", @@ -216,7 +216,7 @@ "@typescript-eslint/parser": "6.7.4", "aws-sdk-client-mock": "3.0.0", "cross-env": "7.0.3", - "eslint": "8.50.0", + "eslint": "8.51.0", "eslint-plugin-import": "2.28.1", "execa": "8.0.1", "jest": "29.7.0", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index dd213b3d7d..7a822e0fdf 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -38,7 +38,7 @@ "chartjs-chart-matrix": "2.0.1", "chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-zoom": "2.0.1", - "chromatic": "7.2.2", + "chromatic": "7.2.3", "compare-versions": "6.1.0", "cropperjs": "2.0.0-beta.4", "date-fns": "2.30.0", @@ -57,7 +57,7 @@ "prismjs": "1.29.0", "punycode": "2.3.0", "querystring": "0.2.1", - "rollup": "4.0.0", + "rollup": "4.0.2", "sanitize-html": "2.11.0", "sass": "1.69.0", "strict-event-emitter-types": "2.0.0", @@ -101,12 +101,12 @@ "@types/estree": "1.0.2", "@types/matter-js": "0.19.1", "@types/micromatch": "4.0.3", - "@types/node": "20.8.2", + "@types/node": "20.8.3", "@types/punycode": "2.1.0", "@types/sanitize-html": "2.9.1", "@types/throttle-debounce": "5.0.0", "@types/tinycolor2": "1.4.4", - "@types/uuid": "9.0.4", + "@types/uuid": "9.0.5", "@types/websocket": "1.0.7", "@types/ws": "8.5.6", "@typescript-eslint/eslint-plugin": "6.7.4", @@ -116,7 +116,7 @@ "acorn": "8.10.0", "cross-env": "7.0.3", "cypress": "13.3.0", - "eslint": "8.50.0", + "eslint": "8.51.0", "eslint-plugin-import": "2.28.1", "eslint-plugin-vue": "9.17.0", "fast-glob": "3.3.1", @@ -135,7 +135,7 @@ "vite-plugin-turbosnap": "1.0.3", "vitest": "0.34.6", "vitest-fetch-mock": "0.2.2", - "vue-eslint-parser": "9.3.1", - "vue-tsc": "1.8.15" + "vue-eslint-parser": "9.3.2", + "vue-tsc": "1.8.18" } } diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 1aefc898f5..ae04058eb9 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -23,10 +23,10 @@ "@microsoft/api-extractor": "7.38.0", "@swc/jest": "0.2.29", "@types/jest": "29.5.5", - "@types/node": "20.8.2", + "@types/node": "20.8.3", "@typescript-eslint/eslint-plugin": "6.7.4", "@typescript-eslint/parser": "6.7.4", - "eslint": "8.50.0", + "eslint": "8.51.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", diff --git a/packages/sw/package.json b/packages/sw/package.json index 7d4c16c164..fdab706510 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -16,7 +16,7 @@ "devDependencies": { "@typescript-eslint/parser": "6.7.4", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", - "eslint": "8.50.0", + "eslint": "8.51.0", "eslint-plugin-import": "2.28.1", "typescript": "5.2.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49ac89d59c..31fcd8688a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,10 +37,10 @@ importers: devDependencies: '@typescript-eslint/eslint-plugin': specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: 6.7.4 - version: 6.7.4(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) cross-env: specifier: 7.0.3 version: 7.0.3 @@ -48,8 +48,8 @@ importers: specifier: 13.3.0 version: 13.3.0 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 start-server-and-test: specifier: 2.0.1 version: 2.0.1 @@ -261,8 +261,8 @@ importers: specifier: 3.3.2 version: 3.3.2 nodemailer: - specifier: 6.9.5 - version: 6.9.5 + specifier: 6.9.6 + version: 6.9.6 nsfwjs: specifier: 2.4.2 version: 2.4.2(@tensorflow/tfjs@4.4.0) @@ -279,8 +279,8 @@ importers: specifier: 0.0.14 version: 0.0.14 otpauth: - specifier: 9.1.4 - version: 9.1.4 + specifier: 9.1.5 + version: 9.1.5 parse5: specifier: 7.1.2 version: 7.1.2 @@ -539,8 +539,8 @@ importers: specifier: 0.7.32 version: 0.7.32 '@types/node': - specifier: 20.8.2 - version: 20.8.2 + specifier: 20.8.3 + version: 20.8.3 '@types/node-fetch': specifier: 3.0.3 version: 3.0.3 @@ -609,10 +609,10 @@ importers: version: 8.5.6 '@typescript-eslint/eslint-plugin': specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: 6.7.4 - version: 6.7.4(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) aws-sdk-client-mock: specifier: 3.0.0 version: 3.0.0 @@ -620,17 +620,17 @@ importers: specifier: 7.0.3 version: 7.0.3 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0) execa: specifier: 8.0.1 version: 8.0.1 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.8.2) + version: 29.7.0(@types/node@20.8.3) jest-mock: specifier: 29.7.0 version: 29.7.0 @@ -648,16 +648,16 @@ importers: version: 2.1.1 '@rollup/plugin-alias': specifier: 5.0.1 - version: 5.0.1(rollup@4.0.0) + version: 5.0.1(rollup@4.0.2) '@rollup/plugin-json': specifier: 6.0.1 - version: 6.0.1(rollup@4.0.0) + version: 6.0.1(rollup@4.0.2) '@rollup/plugin-replace': specifier: 5.0.3 - version: 5.0.3(rollup@4.0.0) + version: 5.0.3(rollup@4.0.2) '@rollup/pluginutils': specifier: 5.0.5 - version: 5.0.5(rollup@4.0.0) + version: 5.0.5(rollup@4.0.2) '@syuilo/aiscript': specifier: 0.16.0 version: 0.16.0 @@ -669,7 +669,7 @@ importers: version: 4.4.0(vite@4.4.11)(vue@3.3.4) '@vue-macros/reactivity-transform': specifier: 0.3.23 - version: 0.3.23(rollup@4.0.0)(vue@3.3.4) + version: 0.3.23(rollup@4.0.2)(vue@3.3.4) '@vue/compiler-sfc': specifier: 3.3.4 version: 3.3.4 @@ -707,8 +707,8 @@ importers: specifier: 2.0.1 version: 2.0.1(chart.js@4.4.0) chromatic: - specifier: 7.2.2 - version: 7.2.2 + specifier: 7.2.3 + version: 7.2.3 compare-versions: specifier: 6.1.0 version: 6.1.0 @@ -764,8 +764,8 @@ importers: specifier: 0.2.1 version: 0.2.1 rollup: - specifier: 4.0.0 - version: 4.0.0 + specifier: 4.0.2 + version: 4.0.2 sanitize-html: specifier: 2.11.0 version: 2.11.0 @@ -810,7 +810,7 @@ importers: version: 1.8.1 vite: specifier: 4.4.11 - version: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + version: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) vue: specifier: 3.3.4 version: 3.3.4 @@ -859,7 +859,7 @@ importers: version: 7.4.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) '@storybook/react-vite': specifier: 7.4.6 - version: 7.4.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.0.0)(typescript@5.2.2)(vite@4.4.11) + version: 7.4.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.0.2)(typescript@5.2.2)(vite@4.4.11) '@storybook/testing-library': specifier: 0.2.2 version: 0.2.2 @@ -891,8 +891,8 @@ importers: specifier: 4.0.3 version: 4.0.3 '@types/node': - specifier: 20.8.2 - version: 20.8.2 + specifier: 20.8.3 + version: 20.8.3 '@types/punycode': specifier: 2.1.0 version: 2.1.0 @@ -906,8 +906,8 @@ importers: specifier: 1.4.4 version: 1.4.4 '@types/uuid': - specifier: 9.0.4 - version: 9.0.4 + specifier: 9.0.5 + version: 9.0.5 '@types/websocket': specifier: 1.0.7 version: 1.0.7 @@ -916,10 +916,10 @@ importers: version: 8.5.6 '@typescript-eslint/eslint-plugin': specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: 6.7.4 - version: 6.7.4(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) '@vitest/coverage-v8': specifier: 0.34.6 version: 0.34.6(vitest@0.34.6) @@ -936,14 +936,14 @@ importers: specifier: 13.3.0 version: 13.3.0 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0) eslint-plugin-vue: specifier: 9.17.0 - version: 9.17.0(eslint@8.50.0) + version: 9.17.0(eslint@8.51.0) fast-glob: specifier: 3.3.1 version: 3.3.1 @@ -993,11 +993,11 @@ importers: specifier: 0.2.2 version: 0.2.2(vitest@0.34.6) vue-eslint-parser: - specifier: 9.3.1 - version: 9.3.1(eslint@8.50.0) + specifier: 9.3.2 + version: 9.3.2(eslint@8.51.0) vue-tsc: - specifier: 1.8.15 - version: 1.8.15(typescript@5.2.2) + specifier: 1.8.18 + version: 1.8.18(typescript@5.2.2) packages/misskey-js: dependencies: @@ -1016,7 +1016,7 @@ importers: devDependencies: '@microsoft/api-extractor': specifier: 7.38.0 - version: 7.38.0(@types/node@20.8.2) + version: 7.38.0(@types/node@20.8.3) '@swc/jest': specifier: 0.2.29 version: 0.2.29(@swc/core@1.3.92) @@ -1024,20 +1024,20 @@ importers: specifier: 29.5.5 version: 29.5.5 '@types/node': - specifier: 20.8.2 - version: 20.8.2 + specifier: 20.8.3 + version: 20.8.3 '@typescript-eslint/eslint-plugin': specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: 6.7.4 - version: 6.7.4(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.8.2) + version: 29.7.0(@types/node@20.8.3) jest-fetch-mock: specifier: 3.0.3 version: 3.0.3 @@ -1068,16 +1068,16 @@ importers: devDependencies: '@typescript-eslint/parser': specifier: 6.7.4 - version: 6.7.4(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.67 version: /@types/serviceworker@0.0.67 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0) typescript: specifier: 5.2.2 version: 5.2.2 @@ -3686,13 +3686,13 @@ packages: dev: false optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.51.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.50.0 + eslint: 8.51.0 eslint-visitor-keys: 3.4.3 dev: true @@ -3718,8 +3718,8 @@ packages: - supports-color dev: true - /@eslint/js@8.50.0: - resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} + /@eslint/js@8.51.0: + resolution: {integrity: sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -4008,7 +4008,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -4029,14 +4029,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.7.1 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.8.2) + jest-config: 29.7.0(@types/node@20.8.3) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4071,7 +4071,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 jest-mock: 29.7.0 dev: true @@ -4098,7 +4098,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.8.2 + '@types/node': 20.8.3 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4131,7 +4131,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 20.8.2 + '@types/node': 20.8.3 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -4225,7 +4225,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.8.2 + '@types/node': 20.8.3 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true @@ -4237,7 +4237,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.8.2 + '@types/node': 20.8.3 '@types/yargs': 17.0.19 chalk: 4.1.2 dev: true @@ -4256,7 +4256,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.2.2) typescript: 5.2.2 - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) dev: true /@jridgewell/gen-mapping@0.3.2: @@ -4341,24 +4341,24 @@ packages: react: 18.2.0 dev: true - /@microsoft/api-extractor-model@7.28.2(@types/node@20.8.2): + /@microsoft/api-extractor-model@7.28.2(@types/node@20.8.3): resolution: {integrity: sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.61.0(@types/node@20.8.2) + '@rushstack/node-core-library': 3.61.0(@types/node@20.8.3) transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.38.0(@types/node@20.8.2): + /@microsoft/api-extractor@7.38.0(@types/node@20.8.3): resolution: {integrity: sha512-e1LhZYnfw+JEebuY2bzhw0imDCl1nwjSThTrQqBXl40hrVo6xm3j/1EpUr89QyzgjqmAwek2ZkIVZbrhaR+cqg==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.28.2(@types/node@20.8.2) + '@microsoft/api-extractor-model': 7.28.2(@types/node@20.8.3) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.61.0(@types/node@20.8.2) + '@rushstack/node-core-library': 3.61.0(@types/node@20.8.3) '@rushstack/rig-package': 0.5.1 '@rushstack/ts-command-line': 4.16.1 colors: 1.2.5 @@ -5189,7 +5189,7 @@ packages: '@babel/runtime': 7.22.10 dev: true - /@rollup/plugin-alias@5.0.1(rollup@4.0.0): + /@rollup/plugin-alias@5.0.1(rollup@4.0.2): resolution: {integrity: sha512-JObvbWdOHoMy9W7SU0lvGhDtWq9PllP5mjpAy+TUslZG/WzOId9u80Hsqq1vCUn9pFJ0cxpdcnAv+QzU2zFH3Q==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5198,11 +5198,11 @@ packages: rollup: optional: true dependencies: - rollup: 4.0.0 + rollup: 4.0.2 slash: 4.0.0 dev: false - /@rollup/plugin-json@6.0.1(rollup@4.0.0): + /@rollup/plugin-json@6.0.1(rollup@4.0.2): resolution: {integrity: sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5211,11 +5211,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.5(rollup@4.0.0) - rollup: 4.0.0 + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) + rollup: 4.0.2 dev: false - /@rollup/plugin-replace@5.0.3(rollup@4.0.0): + /@rollup/plugin-replace@5.0.3(rollup@4.0.2): resolution: {integrity: sha512-je7fu05B800IrMlWjb2wzJcdXzHYW46iTipfChnBDbIbDXhASZs27W1B58T2Yf45jZtJUONegpbce+9Ut2Ti/Q==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5224,12 +5224,12 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.5(rollup@4.0.0) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) magic-string: 0.27.0 - rollup: 4.0.0 + rollup: 4.0.2 dev: false - /@rollup/pluginutils@5.0.5(rollup@4.0.0): + /@rollup/pluginutils@5.0.5(rollup@4.0.2): resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==} engines: {node: '>=14.0.0'} peerDependencies: @@ -5241,86 +5241,93 @@ packages: '@types/estree': 1.0.2 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 4.0.0 + rollup: 4.0.2 - /@rollup/rollup-android-arm-eabi@4.0.0: - resolution: {integrity: sha512-rN3qt1JzOx0v7JWyK68zkb3yf1k1f1OhhHR0i7vLlGlediTtM3FKsOkestQN6HwJ9nEaP3KxPHxH5Xv7yr6f4w==} + /@rollup/rollup-android-arm-eabi@4.0.2: + resolution: {integrity: sha512-xDvk1pT4vaPU2BOLy0MqHMdYZyntqpaBf8RhBiezlqG9OjY8F50TyctHo8znigYKd+QCFhCmlmXHOL/LoaOl3w==} cpu: [arm] os: [android] requiresBuild: true optional: true - /@rollup/rollup-android-arm64@4.0.0: - resolution: {integrity: sha512-dcdg6Zp2bqIS/+2FHhdSS+lbcySufP2fYYoXkDa4W6uHE22L15psftdQZtFhxvvqRWPD1HsK0xIj5f07zuujkg==} + /@rollup/rollup-android-arm64@4.0.2: + resolution: {integrity: sha512-lqCglytY3E6raze27DD9VQJWohbwCxzqs9aSHcj5X/8hJpzZfNdbsr4Ja9Hqp6iPyF53+5PtPx0pKRlkSvlHZg==} cpu: [arm64] os: [android] requiresBuild: true optional: true - /@rollup/rollup-darwin-arm64@4.0.0: - resolution: {integrity: sha512-mOz75DpOOHGk4+xYbh1E23vmSOrOqskTwq9s/e2Z46eYbTZ0+s/UVoS42cLG8dUe6enF2Xh3hTtiIEzLhO9kmA==} + /@rollup/rollup-darwin-arm64@4.0.2: + resolution: {integrity: sha512-nkBKItS6E6CCzvRwgiKad+j+1ibmL7SIInj7oqMWmdkCjiSX6VeVZw2mLlRKIUL+JjsBgpATTfo7BiAXc1v0jA==} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@rollup/rollup-darwin-x64@4.0.0: - resolution: {integrity: sha512-rEBuHQ2ejl9gb0//19F88gR7Z9HY2kcCX8jT5LhCHqGqAvlloETXO1FD7DKEdqGz98UtJy6pVAxxeVBN4tlWag==} + /@rollup/rollup-darwin-x64@4.0.2: + resolution: {integrity: sha512-vX2C8xvWPIbpEgQht95+dY6BReKAvtDgPDGi0XN0kWJKkm4WdNmq5dnwscv/zxvi+n6jUTBhs6GtpkkWT4q8Gg==} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.0.0: - resolution: {integrity: sha512-W4Elp0SGWqWOkdgoYniOp6ERrhHYRfMPikUZmnU/kAdLXQ9p0M0meF648Z6Y7ClHJr8pIQpcCdmr7E2h8Kn7Fw==} + /@rollup/rollup-linux-arm-gnueabihf@4.0.2: + resolution: {integrity: sha512-DVFIfcHOjgmeHOAqji4xNz2wczt1Bmzy9MwBZKBa83SjBVO/i38VHDR+9ixo8QpBOiEagmNw12DucG+v55tCrg==} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.0.0: - resolution: {integrity: sha512-/BTevM/UKprMJgFse0nm+YXQ83iDqArru+k3kZtQlvaNMWdkLcyscOP8SwWPpR0CJuLlXr8Gtpps+EgH3TUqLA==} + /@rollup/rollup-linux-arm64-gnu@4.0.2: + resolution: {integrity: sha512-GCK/a9ItUxPI0V5hQEJjH4JtOJO90GF2Hja7TO+EZ8rmkGvEi8/ZDMhXmcuDpQT7/PWrTT9RvnG8snMd5SrhBQ==} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-x64-gnu@4.0.0: - resolution: {integrity: sha512-Pz2FD/4FUZM98+rcpuGAJgatW5/dW/pXXrbanjtir38EYqqmdVc0odHwqlQ+KFY2C5P+B6PJO5vom8PmJQLdug==} + /@rollup/rollup-linux-arm64-musl@4.0.2: + resolution: {integrity: sha512-cLuBp7rOjIB1R2j/VazjCmHC7liWUur2e9mFflLJBAWCkrZ+X0+QwHLvOQakIwDymungzAKv6W9kHZnTp/Mqrg==} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.0.2: + resolution: {integrity: sha512-Zqw4iVnJr2naoyQus0yLy7sLtisCQcpdMKUCeXPBjkJtpiflRime/TMojbnl8O3oxUAj92mxr+t7im/RbgA20w==} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-linux-x64-musl@4.0.0: - resolution: {integrity: sha512-Xs2tOshU5MD7nK5WnaSBUwiFdBlMtyKdXOOnBno4IRbDIyrjLtx9lnSIO47FNP0LtpGfyOcsK/lE/ZsLlnXyIg==} + /@rollup/rollup-linux-x64-musl@4.0.2: + resolution: {integrity: sha512-jJRU9TyUD/iMqjf8aLAp7XiN3pIj5v6Qcu+cdzBfVTKDD0Fvua4oUoK8eVJ9ZuKBEQKt3WdlcwJXFkpmMLk6kg==} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.0.0: - resolution: {integrity: sha512-h2r04SsqVMbmaIRSMN3HKQLYpKewJ7rWQx1SwEZQMeXRkecWFBBNOfoB3iMlvvUfc3VUOonR/3Dm/Op6yOD2Lg==} + /@rollup/rollup-win32-arm64-msvc@4.0.2: + resolution: {integrity: sha512-ZkS2NixCxHKC4zbOnw64ztEGGDVIYP6nKkGBfOAxEPW71Sji9v8z3yaHNuae/JHPwXA+14oDefnOuVfxl59SmQ==} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.0.0: - resolution: {integrity: sha512-1pl05L51RbVLnqZTEpbgG2RxeS7VLysF7vhU8v1EOAMqbLzko64r8+S2SxsNDKODsgusFqHO8rc3w+G9VUjodw==} + /@rollup/rollup-win32-ia32-msvc@4.0.2: + resolution: {integrity: sha512-3SKjj+tvnZ0oZq2BKB+fI+DqYI83VrRzk7eed8tJkxeZ4zxJZcLSE8YDQLYGq1tZAnAX+H076RHHB4gTZXsQzw==} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@rollup/rollup-win32-x64-msvc@4.0.0: - resolution: {integrity: sha512-GDi4TkL95/J0ven1wt+q2cfdg1k9UEIQiF58lSC36KUdA0xtlqgLPEDlNAhu6NTXJ491eiZ71lQbLu1D7hlz9w==} + /@rollup/rollup-win32-x64-msvc@4.0.2: + resolution: {integrity: sha512-MBdJIOxRauKkry7t2q+rTHa3aWjVez2eioWg+etRVS3dE4tChhmt5oqZYr48R6bPmcwEhxQr96gVRfeQrLbqng==} cpu: [x64] os: [win32] requiresBuild: true optional: true - /@rushstack/node-core-library@3.61.0(@types/node@20.8.2): + /@rushstack/node-core-library@3.61.0(@types/node@20.8.3): resolution: {integrity: sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==} peerDependencies: '@types/node': '*' @@ -5328,7 +5335,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 colors: 1.2.5 fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -6347,7 +6354,7 @@ packages: remark-slug: 6.1.0 rollup: 3.29.4 typescript: 5.2.2 - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) transitivePeerDependencies: - encoding - supports-color @@ -6717,7 +6724,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/react-vite@7.4.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.0.0)(typescript@5.2.2)(vite@4.4.11): + /@storybook/react-vite@7.4.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.0.2)(typescript@5.2.2)(vite@4.4.11): resolution: {integrity: sha512-jkjnrf3FxzR5wcmebXRPflrsM4WIDjWyW/NVFJwxi5PeIOk7fE7/QAPrm4NFRUu2Q7DeuH3oLKsw8bigvUI9RA==} engines: {node: '>=16'} peerDependencies: @@ -6726,7 +6733,7 @@ packages: vite: ^3.0.0 || ^4.0.0 dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.4.11) - '@rollup/pluginutils': 5.0.5(rollup@4.0.0) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) '@storybook/builder-vite': 7.4.6(typescript@5.2.2)(vite@4.4.11) '@storybook/react': 7.4.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) '@vitejs/plugin-react': 3.1.0(vite@4.4.11) @@ -6735,7 +6742,7 @@ packages: react: 18.2.0 react-docgen: 6.0.0-alpha.3 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -6875,7 +6882,7 @@ packages: magic-string: 0.30.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) vue-docgen-api: 4.64.1(vue@3.3.4) transitivePeerDependencies: - '@preact/preset-vite' @@ -6906,7 +6913,7 @@ packages: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.3.4 - vue-component-type-helpers: 1.8.15 + vue-component-type-helpers: 1.8.18 transitivePeerDependencies: - encoding - supports-color @@ -7411,7 +7418,7 @@ packages: /@types/accepts@1.3.5: resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/archiver@5.3.3: @@ -7465,7 +7472,7 @@ packages: resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} dependencies: '@types/connect': 3.4.35 - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/braces@3.0.1: @@ -7477,7 +7484,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 20.8.2 + '@types/node': 20.8.3 '@types/responselike': 1.0.0 dev: false @@ -7510,7 +7517,7 @@ packages: /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/content-disposition@0.5.6: @@ -7524,7 +7531,7 @@ packages: /@types/cross-spawn@6.0.2: resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/debug@4.1.7: @@ -7578,7 +7585,7 @@ packages: /@types/express-serve-static-core@4.17.33: resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -7599,20 +7606,20 @@ packages: /@types/fluent-ffmpeg@2.1.22: resolution: {integrity: sha512-ZZPDDrDOb2Ahp5fxZzuw64f0rCcviv+SDuCyJ1PIF/UFn9wNHtb/bY8Dj/2nrbQ7SNsGI7gaO2wJVkkU2HBcMg==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/hast@2.3.4: @@ -7627,7 +7634,7 @@ packages: /@types/http-link-header@1.0.3: resolution: {integrity: sha512-y8HkoD/vyid+5MrJ3aas0FvU3/BVBGcyG9kgxL0Zn4JwstA8CglFPnrR0RuzOjRCXwqzL5uxWC2IO7Ub0rMU2A==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/istanbul-lib-coverage@2.0.4: @@ -7671,7 +7678,7 @@ packages: /@types/jsdom@21.1.3: resolution: {integrity: sha512-1zzqSP+iHJYV4lB3lZhNBa012pubABkj9yG/GuXuf6LZH1cSPIJBqFDrm5JX65HHt6VOnNYdTui/0ySerRbMgA==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -7695,7 +7702,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: false /@types/lodash@4.14.191: @@ -7744,7 +7751,7 @@ packages: /@types/node-fetch@2.6.4: resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 form-data: 3.0.1 /@types/node-fetch@3.0.3: @@ -7761,13 +7768,13 @@ packages: resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==} dev: true - /@types/node@20.8.2: - resolution: {integrity: sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==} + /@types/node@20.8.3: + resolution: {integrity: sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==} /@types/nodemailer@6.4.11: resolution: {integrity: sha512-Ld2c0frwpGT4VseuoeboCXQ7UJIkK3X7Lx/4YsZEiUHtHsthWAOCYtf6PAiLhMtfwV0cWJRabLBS3+LD8x6Nrw==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/normalize-package-data@2.4.1: @@ -7784,13 +7791,13 @@ packages: resolution: {integrity: sha512-U3L0c4eQA6lTSZRgW4LYfhKlR084Aw19akmYHrMdYzaqg9mQDfc2b/1iyqm9+1FJDEnVS5ONi5fxdDrB4/7CpQ==} dependencies: '@types/express': 4.17.17 - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/oauth@0.9.2: resolution: {integrity: sha512-Nu3/abQ6yR9VlsCdX3aiGsWFkj6OJvJqDvg/36t8Gwf2mFXdBZXPDN3K+2yfeA6Lo2m1Q12F8Qil9TZ48nWhOQ==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/offscreencanvas@2019.3.0: @@ -7806,7 +7813,7 @@ packages: /@types/pg@8.10.3: resolution: {integrity: sha512-BACzsw64lCZesclRpZGu55tnqgFAYcrCBP92xLh1KLypZLCOsvJTSTgaoFVTy3lCys/aZTQzfeDxtjwrvdzL2g==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 pg-protocol: 1.6.0 pg-types: 4.0.1 dev: true @@ -7830,7 +7837,7 @@ packages: /@types/qrcode@1.5.2: resolution: {integrity: sha512-W4KDz75m7rJjFbyCctzCtRzZUj+PrUHV+YjqDp50sSRezTbrtEAIq2iTzC6lISARl3qw+8IlcCyljdcVJE0Wug==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/qs@6.9.7: @@ -7860,7 +7867,7 @@ packages: /@types/readdir-glob@1.1.1: resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/rename@1.0.5: @@ -7870,7 +7877,7 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: false /@types/sanitize-html@2.9.1: @@ -7896,7 +7903,7 @@ packages: resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 3.0.1 - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/serviceworker@0.0.67: @@ -7906,7 +7913,7 @@ packages: /@types/set-cookie-parser@2.4.3: resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/sharp@0.32.0: @@ -7962,20 +7969,20 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true - /@types/uuid@9.0.4: - resolution: {integrity: sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==} + /@types/uuid@9.0.5: + resolution: {integrity: sha512-xfHdwa1FMJ082prjSJpoEI57GZITiQz10r3vEJCHa2khEFQjKy91aWKz6+zybzssCvXUwE1LQWgWVwZ4nYUvHQ==} dev: true /@types/vary@1.1.1: resolution: {integrity: sha512-XL8U62BpXBMMuFzFBYsWekQwo+dqcyN117IwFVMCkBCvc6HY1ODdRKNA0JHxnuTM5lX3kpqsnBH5OuEeXSN3aA==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/web-push@3.6.1: resolution: {integrity: sha512-Zu6Iju7c4IlE8I8eEeFLYRb7XFqvHFmWWAYr1cmug9EX3c6CDarxIXWN/GO0sxjbJLkHPwozUzp6cLdXsrq7Ew==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/webgl-ext@0.0.30: @@ -7986,13 +7993,13 @@ packages: /@types/websocket@1.0.7: resolution: {integrity: sha512-62Omr8U0PO+hgjLCpPnMsmjh2/FRwIGOktZHyYAUzooEJotwkXHMp7vCacdYi8haxBNOiw9bc2HIHI+b/MPNjA==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/ws@8.5.6: resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /@types/yargs-parser@21.0.0: @@ -8015,11 +8022,11 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true optional: true - /@typescript-eslint/eslint-plugin@6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/eslint-plugin@6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8031,13 +8038,13 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.4(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/scope-manager': 6.7.4 - '@typescript-eslint/type-utils': 6.7.4(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/type-utils': 6.7.4(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.4(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.7.4 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 + eslint: 8.51.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 @@ -8048,7 +8055,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.7.4(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/parser@6.7.4(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8063,7 +8070,7 @@ packages: '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.7.4 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 + eslint: 8.51.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -8077,7 +8084,7 @@ packages: '@typescript-eslint/visitor-keys': 6.7.4 dev: true - /@typescript-eslint/type-utils@6.7.4(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/type-utils@6.7.4(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8088,9 +8095,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.4(eslint@8.51.0)(typescript@5.2.2) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 + eslint: 8.51.0 ts-api-utils: 1.0.1(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -8123,19 +8130,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.7.4(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/utils@6.7.4(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 6.7.4 '@typescript-eslint/types': 6.7.4 '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) - eslint: 8.50.0 + eslint: 8.51.0 semver: 7.5.4 transitivePeerDependencies: - supports-color @@ -8161,7 +8168,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.22.11) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) transitivePeerDependencies: - supports-color dev: true @@ -8173,7 +8180,7 @@ packages: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) vue: 3.3.4 /@vitest/coverage-v8@0.34.6(vitest@0.34.6): @@ -8235,25 +8242,25 @@ packages: pretty-format: 29.7.0 dev: true - /@volar/language-core@1.10.0: - resolution: {integrity: sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==} + /@volar/language-core@1.10.3: + resolution: {integrity: sha512-7Qgwu9bWUHN+cLrOkCbIVBkL+RVPREhvY07wY89dGxi4mY9mQCsUVRRp64F61lX7Nc27meMnvy0sWlzY0x6oQQ==} dependencies: - '@volar/source-map': 1.10.0 + '@volar/source-map': 1.10.3 dev: true - /@volar/source-map@1.10.0: - resolution: {integrity: sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==} + /@volar/source-map@1.10.3: + resolution: {integrity: sha512-QE9nwK3xsdBQGongHnC9SCR0itx7xUKQFsUDn5HbZY3pHpyXxdY1hSBG0eh9mE+aTKoM4KlqMvrb+19Tv9vS1Q==} dependencies: muggle-string: 0.3.1 dev: true - /@volar/typescript@1.10.0: - resolution: {integrity: sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==} + /@volar/typescript@1.10.3: + resolution: {integrity: sha512-n0ar6xGYpRoSvgGMetm/JXP0QAXx+NOUvxCaWCfCjiFivQRSLJeydYDijhoGBUl5KSKosqq9In5L3e/m2TqTcQ==} dependencies: - '@volar/language-core': 1.10.0 + '@volar/language-core': 1.10.3 dev: true - /@vue-macros/common@1.8.0(rollup@4.0.0)(vue@3.3.4): + /@vue-macros/common@1.8.0(rollup@4.0.2)(vue@3.3.4): resolution: {integrity: sha512-auDJJzE0z3uRe3867e0DsqcseKImktNf5ojCZgUKqiVxb2yTlwlgOVAYCgoep9oITqxkXQymSvFeKhedi8PhaA==} engines: {node: '>=16.14.0'} peerDependencies: @@ -8263,9 +8270,9 @@ packages: optional: true dependencies: '@babel/types': 7.22.17 - '@rollup/pluginutils': 5.0.5(rollup@4.0.0) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) '@vue/compiler-sfc': 3.3.4 - ast-kit: 0.11.2(rollup@4.0.0) + ast-kit: 0.11.2(rollup@4.0.2) local-pkg: 0.4.3 magic-string-ast: 0.3.0 vue: 3.3.4 @@ -8273,14 +8280,14 @@ packages: - rollup dev: false - /@vue-macros/reactivity-transform@0.3.23(rollup@4.0.0)(vue@3.3.4): + /@vue-macros/reactivity-transform@0.3.23(rollup@4.0.2)(vue@3.3.4): resolution: {integrity: sha512-SubIg1GsNpQdIDJusrcA2FWBgwSY+4jmL0j6SJ6PU85r3rlS+uDhn6AUkqxeZRAdmJnrbGHXDyWUdygOZmWrSg==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 dependencies: '@babel/parser': 7.22.16 - '@vue-macros/common': 1.8.0(rollup@4.0.0)(vue@3.3.4) + '@vue-macros/common': 1.8.0(rollup@4.0.2)(vue@3.3.4) '@vue/compiler-core': 3.3.4 '@vue/shared': 3.3.4 magic-string: 0.30.3 @@ -8324,20 +8331,20 @@ packages: '@vue/compiler-dom': 3.3.4 '@vue/shared': 3.3.4 - /@vue/language-core@1.8.15(typescript@5.2.2): - resolution: {integrity: sha512-zche5Aw8kkvp3YaghuLiOZyVIpoWHjSQ0EfjxGSsqHOPMamdCoa9x3HtbenpR38UMUoKJ88wiWuiOrV3B/Yq+A==} + /@vue/language-core@1.8.18(typescript@5.2.2): + resolution: {integrity: sha512-byTi+mwSL7XnVRtfWE3MJy3HQryoVSQ3lymauXviegn3G1wwwlSOUljzQe3w5PyesOnBEIxYoavfKzMJnExrBA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@volar/language-core': 1.10.0 - '@volar/source-map': 1.10.0 + '@volar/language-core': 1.10.3 + '@volar/source-map': 1.10.3 '@vue/compiler-dom': 3.3.4 '@vue/reactivity': 3.3.4 '@vue/shared': 3.3.4 - minimatch: 9.0.2 + minimatch: 9.0.3 muggle-string: 0.3.1 typescript: 5.2.2 vue-template-compiler: 2.7.14 @@ -8394,11 +8401,11 @@ packages: '@vue/server-renderer': 3.3.4(vue@3.3.4) dev: true - /@vue/typescript@1.8.15(typescript@5.2.2): - resolution: {integrity: sha512-qWyanQKXOsK84S8rP7QBrqsvUdQ0nZABZmTjXMpb3ox4Bp5IbkscREA3OPUrkgl64mAxwwCzIWcOc3BPTCPjQw==} + /@vue/typescript@1.8.18(typescript@5.2.2): + resolution: {integrity: sha512-3M+lu+DUwJW0fNwd/rLE0FenmELxcC6zxgm/YZ25jSTi+uNGj9L5XvXvf20guC69gQvZ+cg49tTxbepfFVuNNQ==} dependencies: - '@volar/typescript': 1.10.0 - '@vue/language-core': 1.8.15(typescript@5.2.2) + '@volar/typescript': 1.10.3 + '@vue/language-core': 1.8.18(typescript@5.2.2) transitivePeerDependencies: - typescript dev: true @@ -8855,12 +8862,12 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true - /ast-kit@0.11.2(rollup@4.0.0): + /ast-kit@0.11.2(rollup@4.0.2): resolution: {integrity: sha512-Q0DjXK4ApbVoIf9GLyCo252tUH44iTnD/hiJ2TQaJeydYWSpKk0sI34+WMel8S9Wt5pbLgG02oJ+gkgX5DV3sQ==} engines: {node: '>=16.14.0'} dependencies: '@babel/parser': 7.22.16 - '@rollup/pluginutils': 5.0.5(rollup@4.0.0) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) pathe: 1.1.1 transitivePeerDependencies: - rollup @@ -9692,8 +9699,8 @@ packages: engines: {node: '>=10'} requiresBuild: true - /chromatic@7.2.2: - resolution: {integrity: sha512-o9EIMV/EAe6bI7osYi4DfD1zuVovYR/vrY8CXNB5OdcT+alpHZmEZ4+ysTrvL9Bgk6zP/z/2YMVz5ZYdV/gagA==} + /chromatic@7.2.3: + resolution: {integrity: sha512-UEcHB1nkPoHWoRybPzv6BOVqPr7PqDNuz3u8NCRg7KJciouOb20HjiUQx4Dh9mgA7JUsb2WeGHE2SG/0fHH0PA==} hasBin: true dev: false @@ -10030,7 +10037,7 @@ packages: readable-stream: 3.6.0 dev: false - /create-jest@29.7.0(@types/node@20.8.2): + /create-jest@29.7.0(@types/node@20.8.3): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10039,7 +10046,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.8.2) + jest-config: 29.7.0(@types/node@20.8.3) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -11054,7 +11061,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.4)(eslint-import-resolver-node@0.3.7)(eslint@8.50.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.4)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -11075,15 +11082,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.4(eslint@8.51.0)(typescript@5.2.2) debug: 3.2.7(supports-color@5.5.0) - eslint: 8.50.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.50.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -11093,16 +11100,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.7.4(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.4(eslint@8.51.0)(typescript@5.2.2) array-includes: 3.1.6 array.prototype.findlastindex: 1.2.2 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7(supports-color@5.5.0) doctrine: 2.1.0 - eslint: 8.50.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.4)(eslint-import-resolver-node@0.3.7)(eslint@8.50.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.4)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -11118,19 +11125,19 @@ packages: - supports-color dev: true - /eslint-plugin-vue@9.17.0(eslint@8.50.0): + /eslint-plugin-vue@9.17.0(eslint@8.51.0): resolution: {integrity: sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) - eslint: 8.50.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + eslint: 8.51.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.13 semver: 7.5.4 - vue-eslint-parser: 9.3.1(eslint@8.50.0) + vue-eslint-parser: 9.3.2(eslint@8.51.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -11140,14 +11147,6 @@ packages: resolution: {integrity: sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==} dev: true - /eslint-scope@7.2.0: - resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -11156,25 +11155,20 @@ packages: estraverse: 5.3.0 dev: true - /eslint-visitor-keys@3.4.1: - resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.50.0: - resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} + /eslint@8.51.0: + resolution: {integrity: sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.50.0 + '@eslint/js': 8.51.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -11212,15 +11206,6 @@ packages: - supports-color dev: true - /espree@9.5.2: - resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) - eslint-visitor-keys: 3.4.3 - dev: true - /espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13259,7 +13244,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 chalk: 4.1.2 co: 4.6.0 dedent: 1.3.0 @@ -13280,7 +13265,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.8.2): + /jest-cli@29.7.0(@types/node@20.8.3): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13294,10 +13279,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.8.2) + create-jest: 29.7.0(@types/node@20.8.3) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.8.2) + jest-config: 29.7.0(@types/node@20.8.3) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.6.2 @@ -13308,7 +13293,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.8.2): + /jest-config@29.7.0(@types/node@20.8.3): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -13323,7 +13308,7 @@ packages: '@babel/core': 7.22.11 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 babel-jest: 29.7.0(@babel/core@7.22.11) chalk: 4.1.2 ci-info: 3.7.1 @@ -13403,7 +13388,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -13433,7 +13418,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 20.8.2 + '@types/node': 20.8.3 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -13494,7 +13479,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.8.2 + '@types/node': 20.8.3 dev: true /jest-mock@29.7.0: @@ -13502,7 +13487,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 jest-util: 29.7.0 dev: true @@ -13557,7 +13542,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -13588,7 +13573,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -13640,7 +13625,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.11 @@ -13665,7 +13650,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -13684,13 +13669,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.8.2): + /jest@29.7.0(@types/node@20.8.3): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13703,7 +13688,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.8.2) + jest-cli: 29.7.0(@types/node@20.8.3) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -13966,8 +13951,8 @@ packages: resolution: {integrity: sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw==} dev: false - /jssha@3.3.0: - resolution: {integrity: sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w==} + /jssha@3.3.1: + resolution: {integrity: sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==} dev: false /jstransformer@1.0.0: @@ -14530,6 +14515,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -14988,8 +14980,8 @@ packages: /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} - /nodemailer@6.9.5: - resolution: {integrity: sha512-/dmdWo62XjumuLc5+AYQZeiRj+PRR8y8qKtFCOyuOl1k/hckZd8durUUHs/ucKx6/8kN+wFxqKJlQ/LK/qR5FA==} + /nodemailer@6.9.6: + resolution: {integrity: sha512-s7pDtWwe5fLMkQUhw8TkWB/wnZ7SRdd9HRZslq/s24hlZvBP3j32N/ETLmnqTpmj4xoBZL9fOWyCIZ7r2HORHg==} engines: {node: '>=6.0.0'} dev: false @@ -15325,10 +15317,10 @@ packages: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} dev: true - /otpauth@9.1.4: - resolution: {integrity: sha512-T6T0E1WlzwKWESq8K0Ja47u01XjmDmRY/AiUoMAc6xZI/OsTsD4cqBrfpt2WfJ29W5pRiWkuUuyHdNQl0/Ic+Q==} + /otpauth@9.1.5: + resolution: {integrity: sha512-mnic91MZxvj04Ir7FN8Xi6wF3FU8D+s6M5p6FQaSS91/csKswoOI9Dk7kKSnGFAoBYgGTTO+OWScV0nJuzrbPg==} dependencies: - jssha: 3.3.0 + jssha: 3.3.1 dev: false /outvariant@1.4.0: @@ -17165,22 +17157,23 @@ packages: optionalDependencies: fsevents: 2.3.2 - /rollup@4.0.0: - resolution: {integrity: sha512-dtlkoIdp/g2glVlQb6FzhMAMzhMYVIJ3KLGjhWKkwz/ambEuHeVZ7Eg6GALhHZOsDRD+ZWSjnUikZXPyb22puQ==} + /rollup@4.0.2: + resolution: {integrity: sha512-MCScu4usMPCeVFaiLcgMDaBQeYi1z6vpWxz0r0hq0Hv77Y2YuOTZldkuNJ54BdYBH3e+nkrk6j0Rre/NLDBYzg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.0.0 - '@rollup/rollup-android-arm64': 4.0.0 - '@rollup/rollup-darwin-arm64': 4.0.0 - '@rollup/rollup-darwin-x64': 4.0.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.0.0 - '@rollup/rollup-linux-arm64-gnu': 4.0.0 - '@rollup/rollup-linux-x64-gnu': 4.0.0 - '@rollup/rollup-linux-x64-musl': 4.0.0 - '@rollup/rollup-win32-arm64-msvc': 4.0.0 - '@rollup/rollup-win32-ia32-msvc': 4.0.0 - '@rollup/rollup-win32-x64-msvc': 4.0.0 + '@rollup/rollup-android-arm-eabi': 4.0.2 + '@rollup/rollup-android-arm64': 4.0.2 + '@rollup/rollup-darwin-arm64': 4.0.2 + '@rollup/rollup-darwin-x64': 4.0.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.0.2 + '@rollup/rollup-linux-arm64-gnu': 4.0.2 + '@rollup/rollup-linux-arm64-musl': 4.0.2 + '@rollup/rollup-linux-x64-gnu': 4.0.2 + '@rollup/rollup-linux-x64-musl': 4.0.2 + '@rollup/rollup-win32-arm64-msvc': 4.0.2 + '@rollup/rollup-win32-ia32-msvc': 4.0.2 + '@rollup/rollup-win32-x64-msvc': 4.0.2 fsevents: 2.3.2 /rrweb-cssom@0.6.0: @@ -19028,7 +19021,7 @@ packages: core-util-is: 1.0.2 extsprintf: 1.3.0 - /vite-node@0.34.6(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0): + /vite-node@0.34.6(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0): resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19038,7 +19031,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) transitivePeerDependencies: - '@types/node' - less @@ -19054,7 +19047,7 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0): + /vite@4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0): resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -19082,7 +19075,7 @@ packages: terser: optional: true dependencies: - '@types/node': 20.8.2 + '@types/node': 20.8.3 esbuild: 0.18.17 postcss: 8.4.31 rollup: 3.29.4 @@ -19136,7 +19129,7 @@ packages: dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 - '@types/node': 20.8.2 + '@types/node': 20.8.3 '@vitest/expect': 0.34.6 '@vitest/runner': 0.34.6 '@vitest/snapshot': 0.34.6 @@ -19156,8 +19149,8 @@ packages: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.4.11(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) - vite-node: 0.34.6(@types/node@20.8.2)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite-node: 0.34.6(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -19173,8 +19166,8 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} - /vue-component-type-helpers@1.8.15: - resolution: {integrity: sha512-RKiPRKW4BdwgmQ9vaNkHYKAThdTbgU4TOphVyyzqxRwsOJOoRIrb+vB49XLvs5CKPNrvxMXZMwPe5FyJCqFWyg==} + /vue-component-type-helpers@1.8.18: + resolution: {integrity: sha512-SklLIg782E5Ff0qdE68AUrRBhT2YGW97edBewNEjCWCw+RSETcGOjA8m1/6T68CXkymWBSk+KDpPXqIGthqCDg==} dev: true /vue-demi@0.13.11(vue@3.3.4): @@ -19210,17 +19203,17 @@ packages: - vue dev: true - /vue-eslint-parser@9.3.1(eslint@8.50.0): - resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} + /vue-eslint-parser@9.3.2(eslint@8.51.0): + resolution: {integrity: sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' dependencies: debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 - eslint-scope: 7.2.0 - eslint-visitor-keys: 3.4.1 - espree: 9.5.2 + eslint: 8.51.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 esquery: 1.4.2 lodash: 4.17.21 semver: 7.5.4 @@ -19252,14 +19245,14 @@ packages: he: 1.2.0 dev: true - /vue-tsc@1.8.15(typescript@5.2.2): - resolution: {integrity: sha512-4DoB3LUj7IToLmggoCxRiFG+QU5lem0nv03m1ocqugXA9rSVoTOEoYYaP8vu8b99Eh+/cCVdYOeIAQ+RsgUYUw==} + /vue-tsc@1.8.18(typescript@5.2.2): + resolution: {integrity: sha512-AwQxBB9SZX308TLL1932P1JByuMsXC2jLfRBGt8SBdm1e3cXkDlFaXUAqibfKnoQ1ZC2zO2NSbeBNdSjOcdvJw==} hasBin: true peerDependencies: typescript: '*' dependencies: - '@vue/language-core': 1.8.15(typescript@5.2.2) - '@vue/typescript': 1.8.15(typescript@5.2.2) + '@vue/language-core': 1.8.18(typescript@5.2.2) + '@vue/typescript': 1.8.18(typescript@5.2.2) semver: 7.5.4 typescript: 5.2.2 dev: true From 457b880ebaab8811461796200769a9073f38f3c4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 20:55:53 +0900 Subject: [PATCH 019/127] 2023.10.0-beta.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd034a6e2f..b5dcedc5c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.12", + "version": "2023.10.0-beta.13", "codename": "nasubi", "repository": { "type": "git", From edf847d966fddc348a1617643bb4b945fb636a43 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 21:23:07 +0900 Subject: [PATCH 020/127] fix of 0bb0c32908 --- .../backend/src/core/RedisTimelineService.ts | 16 ++++++++-------- .../src/server/api/endpoints/antennas/notes.ts | 4 ++-- .../server/api/endpoints/channels/timeline.ts | 4 ++-- .../api/endpoints/notes/hybrid-timeline.ts | 4 ++-- .../server/api/endpoints/notes/local-timeline.ts | 4 ++-- .../src/server/api/endpoints/notes/timeline.ts | 4 ++-- .../api/endpoints/notes/user-list-timeline.ts | 4 ++-- .../src/server/api/endpoints/roles/notes.ts | 4 ++-- .../src/server/api/endpoints/users/notes.ts | 4 ++-- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/core/RedisTimelineService.ts b/packages/backend/src/core/RedisTimelineService.ts index f0ca2726d6..2f110c014e 100644 --- a/packages/backend/src/core/RedisTimelineService.ts +++ b/packages/backend/src/core/RedisTimelineService.ts @@ -44,16 +44,16 @@ export class RedisTimelineService { public get(name: string, untilId?: string | null, sinceId?: string | null) { if (untilId && sinceId) { return this.redisForTimelines.lrange('list:' + name, 0, -1) - .then(ids => ids.filter(id => id > untilId && id < sinceId).sort((a, b) => a > b ? -1 : 1)); + .then(ids => ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1)); } else if (untilId) { return this.redisForTimelines.lrange('list:' + name, 0, -1) - .then(ids => ids.filter(id => id > untilId).sort((a, b) => a > b ? -1 : 1)); + .then(ids => ids.filter(id => id < untilId).sort((a, b) => a > b ? -1 : 1)); } else if (sinceId) { return this.redisForTimelines.lrange('list:' + name, 0, -1) - .then(ids => ids.filter(id => id < sinceId).sort((a, b) => a < b ? -1 : 1)); + .then(ids => ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1)); } else { return this.redisForTimelines.lrange('list:' + name, 0, -1) - .then(ids => ids.sort((a, b) => a > b ? -1 : 1)); + .then(ids => ids.sort((a, b) => a < b ? -1 : 1)); } } @@ -68,12 +68,12 @@ export class RedisTimelineService { const tls = res.map(r => r[1] as string[]); return tls.map(ids => (untilId && sinceId) - ? ids.filter(id => id > untilId && id < sinceId).sort((a, b) => a > b ? -1 : 1) + ? ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1) : untilId - ? ids.filter(id => id > untilId).sort((a, b) => a > b ? -1 : 1) + ? ids.filter(id => id < untilId).sort((a, b) => a > b ? -1 : 1) : sinceId - ? ids.filter(id => id < sinceId).sort((a, b) => a < b ? -1 : 1) - : ids.sort((a, b) => a > b ? -1 : 1), + ? ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1) + : ids.sort((a, b) => a < b ? -1 : 1), ); }); } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 7d6841563a..1a78bf26df 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -73,8 +73,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index e063e0e2fc..4385c698eb 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -74,8 +74,8 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const isRangeSpecified = untilId != null && sinceId != null; const channel = await this.channelsRepository.findOneBy({ diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index a05269794c..1b77285d47 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -76,8 +76,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const policies = await this.roleService.getUserPolicies(me.id); if (!policies.ltlAvailable) { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index c8ea003ba6..2357f32d5e 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -72,8 +72,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const policies = await this.roleService.getUserPolicies(me ? me.id : null); if (!policies.ltlAvailable) { diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index cd45135f99..760d52c9db 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -66,8 +66,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const [ followings, diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 96c8ff5b1b..f7ee58264e 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -83,8 +83,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const list = await this.userListsRepository.findOneBy({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 366c3a7cc1..0db51abc55 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -69,8 +69,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const role = await this.rolesRepository.findOneBy({ id: ps.roleId, diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 30cd5b791f..d2a65142a5 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -74,8 +74,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const isRangeSpecified = untilId != null && sinceId != null; if (isRangeSpecified || sinceId == null) { From fc777be7bc279a5c04153d838223327ec69b1266 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 21:23:18 +0900 Subject: [PATCH 021/127] 2023.10.0-beta.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b5dcedc5c0..adbd811351 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.13", + "version": "2023.10.0-beta.14", "codename": "nasubi", "repository": { "type": "git", From 8ab364029142991b4e71030cdce070650c13a6ea Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 21:52:31 +0900 Subject: [PATCH 022/127] fix of 0bb0c32908 --- packages/backend/src/core/RedisTimelineService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/RedisTimelineService.ts b/packages/backend/src/core/RedisTimelineService.ts index 2f110c014e..94541759cc 100644 --- a/packages/backend/src/core/RedisTimelineService.ts +++ b/packages/backend/src/core/RedisTimelineService.ts @@ -53,7 +53,7 @@ export class RedisTimelineService { .then(ids => ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1)); } else { return this.redisForTimelines.lrange('list:' + name, 0, -1) - .then(ids => ids.sort((a, b) => a < b ? -1 : 1)); + .then(ids => ids.sort((a, b) => a > b ? -1 : 1)); } } @@ -73,7 +73,7 @@ export class RedisTimelineService { ? ids.filter(id => id < untilId).sort((a, b) => a > b ? -1 : 1) : sinceId ? ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1) - : ids.sort((a, b) => a < b ? -1 : 1), + : ids.sort((a, b) => a > b ? -1 : 1), ); }); } From 4eb9e50a362a58a408b49b00cc20c9a1ded6a2e3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 9 Oct 2023 21:52:43 +0900 Subject: [PATCH 023/127] 2023.10.0-beta.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index adbd811351..270385425d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.14", + "version": "2023.10.0-beta.15", "codename": "nasubi", "repository": { "type": "git", From 9f33ce1cd0eafb744647dab33f3e8087800dd3b4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 10 Oct 2023 09:45:40 +0900 Subject: [PATCH 024/127] fix of 0bb0c32908 --- packages/backend/src/server/api/endpoints/antennas/notes.ts | 6 +++++- packages/backend/test/utils.ts | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 1a78bf26df..6d69971e30 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -109,7 +109,11 @@ export default class extends Endpoint { // eslint- this.queryService.generateBlockedUserQuery(query, me); const notes = await query.getMany(); - notes.sort((a, b) => a.id > b.id ? -1 : 1); + if (sinceId != null && untilId == null) { + notes.sort((a, b) => a.id < b.id ? -1 : 1); + } else { + notes.sort((a, b) => a.id > b.id ? -1 : 1); + } if (notes.length > 0) { this.noteReadService.read(me.id, notes); diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 738635dae2..97118d73c0 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -457,6 +457,7 @@ export async function testPaginationConsistency id + ':' + createdAt), expected.map(({ id, createdAt }) => id + ':' + createdAt)); } + */ // 3. untilId指定+limitで取得してつなぎ合わせた結果が期待通りになっていること if (ordering === 'desc') { From af1087aed4afd0a34206faaab93f7598bc10e4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:43:43 +0900 Subject: [PATCH 025/127] =?UTF-8?q?Feat:=E3=80=8C=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=AE=E8=A9=B3=E7=B4=B0=E3=80=8D=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=82=92=E8=BF=BD=E5=8A=A0=20(#11995)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (add) ファイルビューア * Update Changelog * 既存のAPIを利用 * run api extratctor * Change i18n * (add) ページに関する説明を追加 * Update CHANGELOG * (fix) design, classes --- CHANGELOG.md | 4 + locales/index.d.ts | 9 + locales/ja-JP.yml | 9 + .../endpoints/drive/files/attached-notes.ts | 12 +- .../frontend/src/components/MkDrive.file.vue | 5 +- .../frontend/src/pages/drive.file.info.vue | 302 ++++++++++++++++++ .../frontend/src/pages/drive.file.notes.vue | 33 ++ packages/frontend/src/pages/drive.file.vue | 52 +++ packages/frontend/src/router.ts | 4 + .../src/scripts/get-drive-file-menu.ts | 7 +- 10 files changed, 432 insertions(+), 5 deletions(-) create mode 100644 packages/frontend/src/pages/drive.file.info.vue create mode 100644 packages/frontend/src/pages/drive.file.notes.vue create mode 100644 packages/frontend/src/pages/drive.file.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index d0c3af806a..4b6f208d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ ### Changes - API: users/notes, notes/local-timeline で fileType 指定はできなくなりました - API: notes/featured でページネーションは他APIと同様 untilId を使って行うようになりました +- API: drive/files/attached-notes がページネーションに対応しました ### General - Feat: ユーザーごとに他ユーザーへの返信をタイムラインに含めるか設定可能になりました @@ -31,6 +32,9 @@ - Feat: タイムラインがリアルタイム更新中に広告を挿入できるようになりました - デフォルトは無効 - 頻度はコントロールパネルから設定できます。運営中のサーバーのTLの流速を見て、最適な値を指定してください。 +- Feat: 「ファイルの詳細」ページを追加 + - ドライブのファイルの拡大プレビューができるように + - ファイルが添付されたノートの一覧が表示できるように - Enhance: ソフトワードミュートとハードワードミュートは統合されました - Enhance: モデレーションログ機能の強化 - Enhance: ローカリゼーションの更新 diff --git a/locales/index.d.ts b/locales/index.d.ts index 8a429e3b66..5227caa083 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2294,6 +2294,15 @@ export interface Locale { "deleteAd": string; "updateAd": string; }; + "_fileViewer": { + "title": string; + "type": string; + "size": string; + "url": string; + "uploadedAt": string; + "attachedNotes": string; + "thisPageCanBeSeenFromTheAuthor": string; + }; } declare const locales: { [lang: string]: Locale; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 52e06e720d..bf179ca733 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2206,3 +2206,12 @@ _moderationLogTypes: createAd: "広告を作成" deleteAd: "広告を削除" updateAd: "広告を更新" + +_fileViewer: + title: "ファイルの詳細" + type: "ファイルタイプ" + size: "ファイルサイズ" + url: "URL" + uploadedAt: "追加日" + attachedNotes: "添付されているノート" + thisPageCanBeSeenFromTheAuthor: "このページは、このファイルをアップロードしたユーザーしか閲覧できません。" diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 779231a856..14a13b09c9 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -6,6 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { NotesRepository, DriveFilesRepository } from '@/models/_.js'; +import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -41,6 +42,9 @@ export const meta = { export const paramDef = { type: 'object', properties: { + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, fileId: { type: 'string', format: 'misskey:id' }, }, required: ['fileId'], @@ -56,6 +60,7 @@ export default class extends Endpoint { // eslint- private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { // Fetch file @@ -68,9 +73,10 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchFile); } - const notes = await this.notesRepository.createQueryBuilder('note') - .where(':file = ANY(note.fileIds)', { file: file.id }) - .getMany(); + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); + query.andWhere(':file = ANY(note.fileIds)', { file: file.id }); + + const notes = await query.limit(ps.limit).getMany(); return await this.noteEntityService.packMany(notes, me, { detail: true, diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index e3f96724d9..96704996f9 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -45,8 +45,11 @@ import bytes from '@/filters/bytes.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; +import { useRouter } from '@/router.js'; import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js'; +const router = useRouter(); + const props = withDefaults(defineProps<{ file: Misskey.entities.DriveFile; folder: Misskey.entities.DriveFolder | null; @@ -71,7 +74,7 @@ function onClick(ev: MouseEvent) { if (props.selectMode) { emit('chosen', props.file); } else { - os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + router.push(`/my/drive/file/${props.file.id}`); } } diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue new file mode 100644 index 0000000000..ae9256b8e3 --- /dev/null +++ b/packages/frontend/src/pages/drive.file.info.vue @@ -0,0 +1,302 @@ + + + + + + + diff --git a/packages/frontend/src/pages/drive.file.notes.vue b/packages/frontend/src/pages/drive.file.notes.vue new file mode 100644 index 0000000000..ee1a0ee9b0 --- /dev/null +++ b/packages/frontend/src/pages/drive.file.notes.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/packages/frontend/src/pages/drive.file.vue b/packages/frontend/src/pages/drive.file.vue new file mode 100644 index 0000000000..2c1e5d20a7 --- /dev/null +++ b/packages/frontend/src/pages/drive.file.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 294f66aaaf..6c33d0d8ee 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -467,6 +467,10 @@ export const routes = [{ path: '/my/drive', component: page(() => import('./pages/drive.vue')), loginRequired: true, +}, { + path: '/my/drive/file/:fileId', + component: page(() => import('./pages/drive.file.vue')), + loginRequired: true, }, { path: '/my/follow-requests', component: page(() => import('./pages/follow-requests.vue')), diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts index 0964108249..8b2144a22f 100644 --- a/packages/frontend/src/scripts/get-drive-file-menu.ts +++ b/packages/frontend/src/scripts/get-drive-file-menu.ts @@ -27,7 +27,7 @@ function rename(file: Misskey.entities.DriveFile) { function describe(file: Misskey.entities.DriveFile) { os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { - default: file.comment != null ? file.comment : '', + default: file.comment ?? '', file: file, }, { done: caption => { @@ -112,6 +112,11 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss text: i18n.ts.download, icon: 'ti ti-download', download: file.name, + }, null, { + type: 'link', + to: `/my/drive/file/${file.id}`, + text: i18n.ts._fileViewer.title, + icon: 'ti ti-file', }, null, { text: i18n.ts.delete, icon: 'ti ti-trash', From d94380780f35670cc02581e1d2251349cc9f14de Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 10 Oct 2023 10:47:11 +0900 Subject: [PATCH 026/127] Update CHANGELOG.md --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b6f208d03..cc3bd80259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,9 +32,6 @@ - Feat: タイムラインがリアルタイム更新中に広告を挿入できるようになりました - デフォルトは無効 - 頻度はコントロールパネルから設定できます。運営中のサーバーのTLの流速を見て、最適な値を指定してください。 -- Feat: 「ファイルの詳細」ページを追加 - - ドライブのファイルの拡大プレビューができるように - - ファイルが添付されたノートの一覧が表示できるように - Enhance: ソフトワードミュートとハードワードミュートは統合されました - Enhance: モデレーションログ機能の強化 - Enhance: ローカリゼーションの更新 @@ -43,6 +40,9 @@ - Fix: ユーザーリストTLにチャンネル投稿が含まれる問題を修正 ### Client +- Feat: 「ファイルの詳細」ページを追加 + - ドライブのファイルの拡大プレビューができるように + - ファイルが添付されたノートの一覧が表示できるように - Enhance: 二要素認証のバックアップコード一覧をテキストファイルでダウンロード可能に - Enhance: 動画再生時のデフォルトボリュームを30%に - Fix: リアクションしたユーザ一覧のUIが稀に左上に残ってしまう不具合を修正 From 9dd0f8c39beea48aa8741aeae1f116e02c987ffc Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 10 Oct 2023 16:25:06 +0900 Subject: [PATCH 027/127] clean up --- packages/frontend/src/pages/user-list-timeline.vue | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 83244876fe..0fc7b62d82 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -61,20 +61,7 @@ function settings() { router.push(`/my/lists/${props.listId}`); } -async function timetravel() { - const { canceled, result: date } = await os.inputDate({ - title: i18n.ts.date, - }); - if (canceled) return; - - tlEl.timetravel(date); -} - const headerActions = $computed(() => list ? [{ - icon: 'ti ti-calendar-time', - text: i18n.ts.jumpToSpecifiedDate, - handler: timetravel, -}, { icon: 'ti ti-settings', text: i18n.ts.settings, handler: settings, From cf6e53b2ac1153f4cf346dcea5cf44f00ca0a645 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 10 Oct 2023 16:26:48 +0900 Subject: [PATCH 028/127] update deps --- package.json | 4 +- packages/backend/package.json | 10 +- packages/frontend/package.json | 8 +- packages/misskey-js/package.json | 6 +- packages/sw/package.json | 2 +- pnpm-lock.yaml | 335 ++++++++++++++++--------------- 6 files changed, 185 insertions(+), 180 deletions(-) diff --git a/package.json b/package.json index 270385425d..a2ac96d43f 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "typescript": "5.2.2" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "6.7.4", - "@typescript-eslint/parser": "6.7.4", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", "cross-env": "7.0.3", "cypress": "13.3.0", "eslint": "8.51.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index 796445ab23..bc24b4938c 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -86,7 +86,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.2", - "bullmq": "4.12.2", + "bullmq": "4.12.3", "cacheable-lookup": "7.0.0", "cbor": "9.0.1", "chalk": "5.3.0", @@ -189,13 +189,13 @@ "@types/jsrsasign": "10.5.9", "@types/mime-types": "2.1.2", "@types/ms": "0.7.32", - "@types/node": "20.8.3", + "@types/node": "20.8.4", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.11", "@types/oauth": "0.9.2", "@types/oauth2orize": "1.11.1", "@types/oauth2orize-pkce": "0.1.0", - "@types/pg": "8.10.3", + "@types/pg": "8.10.4", "@types/pug": "2.0.7", "@types/punycode": "2.1.0", "@types/qrcode": "1.5.2", @@ -212,8 +212,8 @@ "@types/vary": "1.1.1", "@types/web-push": "3.6.1", "@types/ws": "8.5.6", - "@typescript-eslint/eslint-plugin": "6.7.4", - "@typescript-eslint/parser": "6.7.4", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", "aws-sdk-client-mock": "3.0.0", "cross-env": "7.0.3", "eslint": "8.51.0", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 7a822e0fdf..8b7604f311 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -59,7 +59,7 @@ "querystring": "0.2.1", "rollup": "4.0.2", "sanitize-html": "2.11.0", - "sass": "1.69.0", + "sass": "1.69.1", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", "three": "0.157.0", @@ -101,7 +101,7 @@ "@types/estree": "1.0.2", "@types/matter-js": "0.19.1", "@types/micromatch": "4.0.3", - "@types/node": "20.8.3", + "@types/node": "20.8.4", "@types/punycode": "2.1.0", "@types/sanitize-html": "2.9.1", "@types/throttle-debounce": "5.0.0", @@ -109,8 +109,8 @@ "@types/uuid": "9.0.5", "@types/websocket": "1.0.7", "@types/ws": "8.5.6", - "@typescript-eslint/eslint-plugin": "6.7.4", - "@typescript-eslint/parser": "6.7.4", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", "@vitest/coverage-v8": "0.34.6", "@vue/runtime-core": "3.3.4", "acorn": "8.10.0", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index ae04058eb9..1b27380019 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -23,9 +23,9 @@ "@microsoft/api-extractor": "7.38.0", "@swc/jest": "0.2.29", "@types/jest": "29.5.5", - "@types/node": "20.8.3", - "@typescript-eslint/eslint-plugin": "6.7.4", - "@typescript-eslint/parser": "6.7.4", + "@types/node": "20.8.4", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", "eslint": "8.51.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", diff --git a/packages/sw/package.json b/packages/sw/package.json index fdab706510..24878a6e2f 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -14,7 +14,7 @@ "misskey-js": "workspace:*" }, "devDependencies": { - "@typescript-eslint/parser": "6.7.4", + "@typescript-eslint/parser": "6.7.5", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", "eslint": "8.51.0", "eslint-plugin-import": "2.28.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31fcd8688a..5466d7b208 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,11 +36,11 @@ importers: version: 4.4.0 devDependencies: '@typescript-eslint/eslint-plugin': - specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.4 - version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) cross-env: specifier: 7.0.3 version: 7.0.3 @@ -147,8 +147,8 @@ importers: specifier: 1.20.2 version: 1.20.2 bullmq: - specifier: 4.12.2 - version: 4.12.2 + specifier: 4.12.3 + version: 4.12.3 cacheable-lookup: specifier: 7.0.0 version: 7.0.0 @@ -539,8 +539,8 @@ importers: specifier: 0.7.32 version: 0.7.32 '@types/node': - specifier: 20.8.3 - version: 20.8.3 + specifier: 20.8.4 + version: 20.8.4 '@types/node-fetch': specifier: 3.0.3 version: 3.0.3 @@ -557,8 +557,8 @@ importers: specifier: 0.1.0 version: 0.1.0 '@types/pg': - specifier: 8.10.3 - version: 8.10.3 + specifier: 8.10.4 + version: 8.10.4 '@types/pug': specifier: 2.0.7 version: 2.0.7 @@ -608,11 +608,11 @@ importers: specifier: 8.5.6 version: 8.5.6 '@typescript-eslint/eslint-plugin': - specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.4 - version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) aws-sdk-client-mock: specifier: 3.0.0 version: 3.0.0 @@ -624,13 +624,13 @@ importers: version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0) + version: 2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0) execa: specifier: 8.0.1 version: 8.0.1 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.8.3) + version: 29.7.0(@types/node@20.8.4) jest-mock: specifier: 29.7.0 version: 29.7.0 @@ -770,8 +770,8 @@ importers: specifier: 2.11.0 version: 2.11.0 sass: - specifier: 1.69.0 - version: 1.69.0 + specifier: 1.69.1 + version: 1.69.1 strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 @@ -810,7 +810,7 @@ importers: version: 1.8.1 vite: specifier: 4.4.11 - version: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + version: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) vue: specifier: 3.3.4 version: 3.3.4 @@ -891,8 +891,8 @@ importers: specifier: 4.0.3 version: 4.0.3 '@types/node': - specifier: 20.8.3 - version: 20.8.3 + specifier: 20.8.4 + version: 20.8.4 '@types/punycode': specifier: 2.1.0 version: 2.1.0 @@ -915,11 +915,11 @@ importers: specifier: 8.5.6 version: 8.5.6 '@typescript-eslint/eslint-plugin': - specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.4 - version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) '@vitest/coverage-v8': specifier: 0.34.6 version: 0.34.6(vitest@0.34.6) @@ -940,7 +940,7 @@ importers: version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0) + version: 2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0) eslint-plugin-vue: specifier: 9.17.0 version: 9.17.0(eslint@8.51.0) @@ -988,7 +988,7 @@ importers: version: 1.0.3 vitest: specifier: 0.34.6 - version: 0.34.6(happy-dom@10.0.3)(sass@1.69.0)(terser@5.21.0) + version: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) vitest-fetch-mock: specifier: 0.2.2 version: 0.2.2(vitest@0.34.6) @@ -1016,7 +1016,7 @@ importers: devDependencies: '@microsoft/api-extractor': specifier: 7.38.0 - version: 7.38.0(@types/node@20.8.3) + version: 7.38.0(@types/node@20.8.4) '@swc/jest': specifier: 0.2.29 version: 0.2.29(@swc/core@1.3.92) @@ -1024,20 +1024,20 @@ importers: specifier: 29.5.5 version: 29.5.5 '@types/node': - specifier: 20.8.3 - version: 20.8.3 + specifier: 20.8.4 + version: 20.8.4 '@typescript-eslint/eslint-plugin': - specifier: 6.7.4 - version: 6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.4 - version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) eslint: specifier: 8.51.0 version: 8.51.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.8.3) + version: 29.7.0(@types/node@20.8.4) jest-fetch-mock: specifier: 3.0.3 version: 3.0.3 @@ -1067,8 +1067,8 @@ importers: version: link:../misskey-js devDependencies: '@typescript-eslint/parser': - specifier: 6.7.4 - version: 6.7.4(eslint@8.51.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.67 version: /@types/serviceworker@0.0.67 @@ -1077,7 +1077,7 @@ importers: version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0) + version: 2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0) typescript: specifier: 5.2.2 version: 5.2.2 @@ -4008,7 +4008,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -4029,14 +4029,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.7.1 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.8.3) + jest-config: 29.7.0(@types/node@20.8.4) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4071,7 +4071,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 jest-mock: 29.7.0 dev: true @@ -4098,7 +4098,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.8.3 + '@types/node': 20.8.4 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4131,7 +4131,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 20.8.3 + '@types/node': 20.8.4 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -4225,7 +4225,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.8.3 + '@types/node': 20.8.4 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true @@ -4237,7 +4237,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.8.3 + '@types/node': 20.8.4 '@types/yargs': 17.0.19 chalk: 4.1.2 dev: true @@ -4256,7 +4256,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.2.2) typescript: 5.2.2 - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) dev: true /@jridgewell/gen-mapping@0.3.2: @@ -4341,24 +4341,24 @@ packages: react: 18.2.0 dev: true - /@microsoft/api-extractor-model@7.28.2(@types/node@20.8.3): + /@microsoft/api-extractor-model@7.28.2(@types/node@20.8.4): resolution: {integrity: sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.61.0(@types/node@20.8.3) + '@rushstack/node-core-library': 3.61.0(@types/node@20.8.4) transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.38.0(@types/node@20.8.3): + /@microsoft/api-extractor@7.38.0(@types/node@20.8.4): resolution: {integrity: sha512-e1LhZYnfw+JEebuY2bzhw0imDCl1nwjSThTrQqBXl40hrVo6xm3j/1EpUr89QyzgjqmAwek2ZkIVZbrhaR+cqg==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.28.2(@types/node@20.8.3) + '@microsoft/api-extractor-model': 7.28.2(@types/node@20.8.4) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.61.0(@types/node@20.8.3) + '@rushstack/node-core-library': 3.61.0(@types/node@20.8.4) '@rushstack/rig-package': 0.5.1 '@rushstack/ts-command-line': 4.16.1 colors: 1.2.5 @@ -5327,7 +5327,7 @@ packages: requiresBuild: true optional: true - /@rushstack/node-core-library@3.61.0(@types/node@20.8.3): + /@rushstack/node-core-library@3.61.0(@types/node@20.8.4): resolution: {integrity: sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==} peerDependencies: '@types/node': '*' @@ -5335,7 +5335,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 colors: 1.2.5 fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -6354,7 +6354,7 @@ packages: remark-slug: 6.1.0 rollup: 3.29.4 typescript: 5.2.2 - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - encoding - supports-color @@ -6742,7 +6742,7 @@ packages: react: 18.2.0 react-docgen: 6.0.0-alpha.3 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -6882,7 +6882,7 @@ packages: magic-string: 0.30.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) vue-docgen-api: 4.64.1(vue@3.3.4) transitivePeerDependencies: - '@preact/preset-vite' @@ -7370,7 +7370,7 @@ packages: dom-accessibility-api: 0.5.16 lodash: 4.17.21 redent: 3.0.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.0)(terser@5.21.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) dev: true /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0): @@ -7418,7 +7418,7 @@ packages: /@types/accepts@1.3.5: resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/archiver@5.3.3: @@ -7472,7 +7472,7 @@ packages: resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} dependencies: '@types/connect': 3.4.35 - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/braces@3.0.1: @@ -7484,7 +7484,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 20.8.3 + '@types/node': 20.8.4 '@types/responselike': 1.0.0 dev: false @@ -7517,7 +7517,7 @@ packages: /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/content-disposition@0.5.6: @@ -7531,7 +7531,7 @@ packages: /@types/cross-spawn@6.0.2: resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/debug@4.1.7: @@ -7585,7 +7585,7 @@ packages: /@types/express-serve-static-core@4.17.33: resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -7606,20 +7606,20 @@ packages: /@types/fluent-ffmpeg@2.1.22: resolution: {integrity: sha512-ZZPDDrDOb2Ahp5fxZzuw64f0rCcviv+SDuCyJ1PIF/UFn9wNHtb/bY8Dj/2nrbQ7SNsGI7gaO2wJVkkU2HBcMg==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/hast@2.3.4: @@ -7634,7 +7634,7 @@ packages: /@types/http-link-header@1.0.3: resolution: {integrity: sha512-y8HkoD/vyid+5MrJ3aas0FvU3/BVBGcyG9kgxL0Zn4JwstA8CglFPnrR0RuzOjRCXwqzL5uxWC2IO7Ub0rMU2A==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/istanbul-lib-coverage@2.0.4: @@ -7678,7 +7678,7 @@ packages: /@types/jsdom@21.1.3: resolution: {integrity: sha512-1zzqSP+iHJYV4lB3lZhNBa012pubABkj9yG/GuXuf6LZH1cSPIJBqFDrm5JX65HHt6VOnNYdTui/0ySerRbMgA==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -7702,7 +7702,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: false /@types/lodash@4.14.191: @@ -7751,7 +7751,7 @@ packages: /@types/node-fetch@2.6.4: resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 form-data: 3.0.1 /@types/node-fetch@3.0.3: @@ -7768,13 +7768,15 @@ packages: resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==} dev: true - /@types/node@20.8.3: - resolution: {integrity: sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==} + /@types/node@20.8.4: + resolution: {integrity: sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==} + dependencies: + undici-types: 5.25.3 /@types/nodemailer@6.4.11: resolution: {integrity: sha512-Ld2c0frwpGT4VseuoeboCXQ7UJIkK3X7Lx/4YsZEiUHtHsthWAOCYtf6PAiLhMtfwV0cWJRabLBS3+LD8x6Nrw==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/normalize-package-data@2.4.1: @@ -7791,13 +7793,13 @@ packages: resolution: {integrity: sha512-U3L0c4eQA6lTSZRgW4LYfhKlR084Aw19akmYHrMdYzaqg9mQDfc2b/1iyqm9+1FJDEnVS5ONi5fxdDrB4/7CpQ==} dependencies: '@types/express': 4.17.17 - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/oauth@0.9.2: resolution: {integrity: sha512-Nu3/abQ6yR9VlsCdX3aiGsWFkj6OJvJqDvg/36t8Gwf2mFXdBZXPDN3K+2yfeA6Lo2m1Q12F8Qil9TZ48nWhOQ==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/offscreencanvas@2019.3.0: @@ -7810,10 +7812,10 @@ packages: requiresBuild: true dev: false - /@types/pg@8.10.3: - resolution: {integrity: sha512-BACzsw64lCZesclRpZGu55tnqgFAYcrCBP92xLh1KLypZLCOsvJTSTgaoFVTy3lCys/aZTQzfeDxtjwrvdzL2g==} + /@types/pg@8.10.4: + resolution: {integrity: sha512-6cxJPHzhlJxqAMkWl2w3KubTEM0UjGC0UrtIToa9J/CEuRFJ2bquKt+g9MhYBN9n1+U6UZZ8CW6Z4oLx/Tvh/w==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 pg-protocol: 1.6.0 pg-types: 4.0.1 dev: true @@ -7837,7 +7839,7 @@ packages: /@types/qrcode@1.5.2: resolution: {integrity: sha512-W4KDz75m7rJjFbyCctzCtRzZUj+PrUHV+YjqDp50sSRezTbrtEAIq2iTzC6lISARl3qw+8IlcCyljdcVJE0Wug==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/qs@6.9.7: @@ -7867,7 +7869,7 @@ packages: /@types/readdir-glob@1.1.1: resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/rename@1.0.5: @@ -7877,7 +7879,7 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: false /@types/sanitize-html@2.9.1: @@ -7903,7 +7905,7 @@ packages: resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 3.0.1 - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/serviceworker@0.0.67: @@ -7913,7 +7915,7 @@ packages: /@types/set-cookie-parser@2.4.3: resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/sharp@0.32.0: @@ -7976,13 +7978,13 @@ packages: /@types/vary@1.1.1: resolution: {integrity: sha512-XL8U62BpXBMMuFzFBYsWekQwo+dqcyN117IwFVMCkBCvc6HY1ODdRKNA0JHxnuTM5lX3kpqsnBH5OuEeXSN3aA==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/web-push@3.6.1: resolution: {integrity: sha512-Zu6Iju7c4IlE8I8eEeFLYRb7XFqvHFmWWAYr1cmug9EX3c6CDarxIXWN/GO0sxjbJLkHPwozUzp6cLdXsrq7Ew==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/webgl-ext@0.0.30: @@ -7993,13 +7995,13 @@ packages: /@types/websocket@1.0.7: resolution: {integrity: sha512-62Omr8U0PO+hgjLCpPnMsmjh2/FRwIGOktZHyYAUzooEJotwkXHMp7vCacdYi8haxBNOiw9bc2HIHI+b/MPNjA==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/ws@8.5.6: resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /@types/yargs-parser@21.0.0: @@ -8022,12 +8024,12 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true optional: true - /@typescript-eslint/eslint-plugin@6.7.4(@typescript-eslint/parser@6.7.4)(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==} + /@typescript-eslint/eslint-plugin@6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -8038,11 +8040,11 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 6.7.4(eslint@8.51.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.7.4 - '@typescript-eslint/type-utils': 6.7.4(eslint@8.51.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.4(eslint@8.51.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.7.4 + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/type-utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@8.1.1) eslint: 8.51.0 graphemer: 1.4.0 @@ -8055,8 +8057,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.7.4(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==} + /@typescript-eslint/parser@6.7.5(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8065,10 +8067,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.7.4 - '@typescript-eslint/types': 6.7.4 - '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.7.4 + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@8.1.1) eslint: 8.51.0 typescript: 5.2.2 @@ -8076,16 +8078,16 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@6.7.4: - resolution: {integrity: sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==} + /@typescript-eslint/scope-manager@6.7.5: + resolution: {integrity: sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.4 - '@typescript-eslint/visitor-keys': 6.7.4 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/visitor-keys': 6.7.5 dev: true - /@typescript-eslint/type-utils@6.7.4(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==} + /@typescript-eslint/type-utils@6.7.5(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8094,8 +8096,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.4(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) debug: 4.3.4(supports-color@8.1.1) eslint: 8.51.0 ts-api-utils: 1.0.1(typescript@5.2.2) @@ -8104,13 +8106,13 @@ packages: - supports-color dev: true - /@typescript-eslint/types@6.7.4: - resolution: {integrity: sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==} + /@typescript-eslint/types@6.7.5: + resolution: {integrity: sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.7.4(typescript@5.2.2): - resolution: {integrity: sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==} + /@typescript-eslint/typescript-estree@6.7.5(typescript@5.2.2): + resolution: {integrity: sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -8118,8 +8120,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.7.4 - '@typescript-eslint/visitor-keys': 6.7.4 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 @@ -8130,8 +8132,8 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.7.4(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==} + /@typescript-eslint/utils@6.7.5(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8139,9 +8141,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.3 - '@typescript-eslint/scope-manager': 6.7.4 - '@typescript-eslint/types': 6.7.4 - '@typescript-eslint/typescript-estree': 6.7.4(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) eslint: 8.51.0 semver: 7.5.4 transitivePeerDependencies: @@ -8149,11 +8151,11 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys@6.7.4: - resolution: {integrity: sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==} + /@typescript-eslint/visitor-keys@6.7.5: + resolution: {integrity: sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/types': 6.7.5 eslint-visitor-keys: 3.4.3 dev: true @@ -8168,7 +8170,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.22.11) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - supports-color dev: true @@ -8180,7 +8182,7 @@ packages: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) vue: 3.3.4 /@vitest/coverage-v8@0.34.6(vitest@0.34.6): @@ -8199,7 +8201,7 @@ packages: std-env: 3.3.3 test-exclude: 6.0.0 v8-to-istanbul: 9.1.0 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.0)(terser@5.21.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - supports-color dev: true @@ -9331,8 +9333,8 @@ packages: dependencies: node-gyp-build: 4.6.0 - /bullmq@4.12.2: - resolution: {integrity: sha512-0YhOtg1lvdqBtYQgh7NNKisFckbxKaHSDqifXvx90OAa5qa6sqNoFbvOgaUitRsAiJgg2fDdT1DnMdQHsQiCeQ==} + /bullmq@4.12.3: + resolution: {integrity: sha512-4uPp4NQTALFF+eFK7g8VJM+rt0aiduQdzBomgiEO1OK4OE+TdgC6cjGXooKI/asuB8iDhSZ+pSnGYy5Xyr6qRA==} dependencies: cron-parser: 4.8.1 glob: 8.1.0 @@ -10037,7 +10039,7 @@ packages: readable-stream: 3.6.0 dev: false - /create-jest@29.7.0(@types/node@20.8.3): + /create-jest@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10046,7 +10048,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.8.3) + jest-config: 29.7.0(@types/node@20.8.4) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -11061,7 +11063,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.4)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -11082,7 +11084,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.7.4(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) debug: 3.2.7(supports-color@5.5.0) eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 @@ -11090,7 +11092,7 @@ packages: - supports-color dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.4)(eslint@8.51.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -11100,7 +11102,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.7.4(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) array-includes: 3.1.6 array.prototype.findlastindex: 1.2.2 array.prototype.flat: 1.3.1 @@ -11109,7 +11111,7 @@ packages: doctrine: 2.1.0 eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.4)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -13244,7 +13246,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.3.0 @@ -13265,7 +13267,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.8.3): + /jest-cli@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13279,10 +13281,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.8.3) + create-jest: 29.7.0(@types/node@20.8.4) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.8.3) + jest-config: 29.7.0(@types/node@20.8.4) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.6.2 @@ -13293,7 +13295,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.8.3): + /jest-config@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -13308,7 +13310,7 @@ packages: '@babel/core': 7.22.11 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 babel-jest: 29.7.0(@babel/core@7.22.11) chalk: 4.1.2 ci-info: 3.7.1 @@ -13388,7 +13390,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -13418,7 +13420,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 20.8.3 + '@types/node': 20.8.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -13479,7 +13481,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.8.3 + '@types/node': 20.8.4 dev: true /jest-mock@29.7.0: @@ -13487,7 +13489,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 jest-util: 29.7.0 dev: true @@ -13542,7 +13544,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -13573,7 +13575,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -13625,7 +13627,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.11 @@ -13650,7 +13652,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -13669,13 +13671,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.8.3): + /jest@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13688,7 +13690,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.8.3) + jest-cli: 29.7.0(@types/node@20.8.4) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -17251,8 +17253,8 @@ packages: postcss: 8.4.31 dev: false - /sass@1.69.0: - resolution: {integrity: sha512-l3bbFpfTOGgQZCLU/gvm1lbsQ5mC/WnLz3djL2v4WCJBDrWm58PO+jgngcGRNnKUh6wSsdm50YaovTqskZ0xDQ==} + /sass@1.69.1: + resolution: {integrity: sha512-nc969GvTVz38oqKgYYVHM/Iq7Yl33IILy5uqaH2CWSiSUmRCvw+UR7tA3845Sp4BD5ykCUimvrT3k1EjTwpVUA==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -18752,6 +18754,9 @@ packages: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} dev: true + /undici-types@5.25.3: + resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} + /undici@5.22.1: resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} engines: {node: '>=14.0'} @@ -19021,7 +19026,7 @@ packages: core-util-is: 1.0.2 extsprintf: 1.3.0 - /vite-node@0.34.6(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0): + /vite-node@0.34.6(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0): resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19031,7 +19036,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - '@types/node' - less @@ -19047,7 +19052,7 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0): + /vite@4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0): resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -19075,11 +19080,11 @@ packages: terser: optional: true dependencies: - '@types/node': 20.8.3 + '@types/node': 20.8.4 esbuild: 0.18.17 postcss: 8.4.31 rollup: 3.29.4 - sass: 1.69.0 + sass: 1.69.1 terser: 5.21.0 optionalDependencies: fsevents: 2.3.2 @@ -19091,12 +19096,12 @@ packages: vitest: '>=0.16.0' dependencies: cross-fetch: 3.1.5 - vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.0)(terser@5.21.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - encoding dev: true - /vitest@0.34.6(happy-dom@10.0.3)(sass@1.69.0)(terser@5.21.0): + /vitest@0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0): resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19129,7 +19134,7 @@ packages: dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 - '@types/node': 20.8.3 + '@types/node': 20.8.4 '@vitest/expect': 0.34.6 '@vitest/runner': 0.34.6 '@vitest/snapshot': 0.34.6 @@ -19149,8 +19154,8 @@ packages: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.4.11(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) - vite-node: 0.34.6(@types/node@20.8.3)(sass@1.69.0)(terser@5.21.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) + vite-node: 0.34.6(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less From d81c8337753eee781125a3532a1e6adf671df35e Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 10 Oct 2023 16:34:22 +0900 Subject: [PATCH 029/127] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc3bd80259..abb9ca8909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,6 @@ ### Changes - API: users/notes, notes/local-timeline で fileType 指定はできなくなりました - API: notes/featured でページネーションは他APIと同様 untilId を使って行うようになりました -- API: drive/files/attached-notes がページネーションに対応しました ### General - Feat: ユーザーごとに他ユーザーへの返信をタイムラインに含めるか設定可能になりました @@ -48,6 +47,7 @@ - Fix: リアクションしたユーザ一覧のUIが稀に左上に残ってしまう不具合を修正 ### Server +- Enhance: drive/files/attached-notes がページネーションに対応しました - Enhance: タイムライン取得時のパフォーマンスを大幅に向上 - Enhance: ハイライト取得時のパフォーマンスを大幅に向上 - Enhance: トレンドハッシュタグ取得時のパフォーマンスを大幅に向上 From f5e72f7d3ed05b55406b45f59d8169bfeb353559 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 10 Oct 2023 18:08:54 +0900 Subject: [PATCH 030/127] =?UTF-8?q?:art:=20CW=E3=83=9C=E3=82=BF=E3=83=B3?= =?UTF-8?q?=E3=82=92=E5=A4=A7=E3=81=8D=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/MkCwButton.vue | 23 ++++--------------- packages/frontend/src/components/MkNote.vue | 2 +- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue index 54c2159356..0cdaf7c9bd 100644 --- a/packages/frontend/src/components/MkCwButton.vue +++ b/packages/frontend/src/components/MkCwButton.vue @@ -4,10 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> From 854ac95511ef017b891185b17921291bb27c1ef2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 10 Oct 2023 20:06:02 +0900 Subject: [PATCH 038/127] =?UTF-8?q?fix(backend):=20=E3=82=BB=E3=83=B3?= =?UTF-8?q?=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E8=A8=AD=E5=AE=9A=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=9F=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E6=8A=95=E7=A8=BF=E3=82=92users/notes=E3=81=A7?= =?UTF-8?q?=E8=BF=94=E3=81=95=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/users/notes.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index d2a65142a5..dfef35986e 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -77,6 +77,7 @@ export default class extends Endpoint { // eslint- const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const isRangeSpecified = untilId != null && sinceId != null; + const isSelf = me && (me.id === ps.userId); if (isRangeSpecified || sinceId == null) { const [ @@ -100,7 +101,7 @@ export default class extends Endpoint { // eslint- noteIds = noteIds.slice(0, ps.limit); if (noteIds.length > 0) { - const isFollowing = me ? me.id === ps.userId || Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId) : false; + const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId); const query = this.notesRepository.createQueryBuilder('note') .where('note.id IN (:...noteIds)', { noteIds: noteIds }) @@ -122,8 +123,9 @@ export default class extends Endpoint { // eslint- } } + if (note.channel?.isSensitive && !isSelf) return false; if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false; - if (note.visibility === 'followers' && !isFollowing) return false; + if (note.visibility === 'followers' && !isFollowing && !isSelf) return false; return true; }); From 26b7112b202e144a6fbbab38b9240d752f9cafff Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 08:02:29 +0900 Subject: [PATCH 039/127] add tests --- packages/backend/test/e2e/timelines.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 05209c9024..e794f806b4 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -1034,6 +1034,32 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + + test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => { + const [bob] = await Promise.all([signup()]); + + const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, bob); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); From 1f0c27edf29b7f01c15a39d83a82bb3089afee36 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 08:18:00 +0900 Subject: [PATCH 040/127] =?UTF-8?q?fix(backend):=20=20=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=8B=E3=82=89=E3=81=AE=E8=87=AA?= =?UTF-8?q?=E5=88=86=E3=81=AE=E6=8A=95=E7=A8=BF=E3=81=B8=E3=81=AE=E8=BF=94?= =?UTF-8?q?=E4=BF=A1=E3=81=8C=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=81=AB=E5=90=AB=E3=81=BE=E3=82=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++ .../backend/src/core/NoteCreateService.ts | 8 +-- packages/backend/test/e2e/timelines.ts | 66 +++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8714599f2..3d103c8f5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ --> +## 2023.10.1 +### Server +- Fix: フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正 + ## 2023.10.0 ### NOTE - 2023.9.2で導入されたノート編集機能はクオリティの高い実装が困難であることが判明したため撤回されました diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 2a73467122..5227ea7323 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -868,8 +868,8 @@ export class NoteCreateService implements OnApplicationShutdown { // 基本的にvisibleUserIdsには自身のidが含まれている前提であること if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue; - // 自分自身以外への返信 - if (note.replyId && note.replyUserId !== note.userId) { + // 「自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合 + if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) { if (!following.withReplies) continue; } @@ -886,8 +886,8 @@ export class NoteCreateService implements OnApplicationShutdown { !note.visibleUserIds.some(v => v === userListMembership.userListUserId) ) continue; - // 自分自身以外への返信 - if (note.replyId && note.replyUserId !== note.userId) { + // 「自分自身への返信 or そのリストの作成者への返信」のどちらでもない場合 + if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) { if (!userListMembership.withReplies) continue; } diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index e794f806b4..25143c2714 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -200,6 +200,22 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); }); + test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('自分の他人への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -589,6 +605,24 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); + /* 実装が面倒 + test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/local-timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + */ + test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -644,6 +678,22 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/hybrid-timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('リモートユーザーのノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); @@ -779,6 +829,22 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); }); + test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/user-list-timeline', { listId: list.id, withReplies: false }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); From 7a8d5e58400a51218d673211219111238a8b7ae8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 10:15:44 +0900 Subject: [PATCH 041/127] =?UTF-8?q?enhance:=20=E3=83=AD=E3=83=BC=E3=82=AB?= =?UTF-8?q?=E3=83=AB=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=80=81=E3=82=BD=E3=83=BC=E3=82=B7=E3=83=A3=E3=83=AB=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=A7=E8=BF=94?= =?UTF-8?q?=E4=BF=A1=E3=82=92=E5=90=AB=E3=82=80=E3=81=8B=E3=81=A9=E3=81=86?= =?UTF-8?q?=E3=81=8B=E8=A8=AD=E5=AE=9A=E5=8F=AF=E8=83=BD=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #12001 --- CHANGELOG.md | 3 + .../backend/src/core/NoteCreateService.ts | 4 ++ .../api/endpoints/notes/hybrid-timeline.ts | 28 ++++++++-- .../api/endpoints/notes/local-timeline.ts | 17 +++++- .../api/stream/channels/hybrid-timeline.ts | 4 +- .../api/stream/channels/local-timeline.ts | 4 +- packages/backend/test/e2e/timelines.ts | 56 +++++++++++++++++++ .../frontend/src/components/MkTimeline.vue | 6 ++ packages/frontend/src/pages/timeline.vue | 10 +++- packages/frontend/src/ui/deck/deck-store.ts | 1 + packages/frontend/src/ui/deck/tl-column.vue | 10 +++- 11 files changed, 131 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d103c8f5e..025ed45dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ --> ## 2023.10.1 +### General +- Enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に + ### Server - Fix: フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正 diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 5227ea7323..64d2880ba1 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -907,6 +907,10 @@ export class NoteCreateService implements OnApplicationShutdown { // 自分自身以外への返信 if (note.replyId && note.replyUserId !== note.userId) { this.redisTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); + + if (note.visibility === 'public' && note.userHost == null) { + this.redisTimelineService.push('localTimelineWithReplies', note.id, 300, r); + } } else { this.redisTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); if (note.fileIds.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 1b77285d47..8ac5f1b038 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -55,6 +55,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, }, required: [], } as const; @@ -94,12 +95,29 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([ - ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, - ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', - ], untilId, sinceId); + let noteIds: string[]; + + if (ps.withFiles) { + const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([ + `homeTimelineWithFiles:${me.id}`, + 'localTimelineWithFiles', + ], untilId, sinceId); + noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); + } else if (ps.withReplies) { + const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.redisTimelineService.getMulti([ + `homeTimeline:${me.id}`, + 'localTimeline', + 'localTimelineWithReplies', + ], untilId, sinceId); + noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds, ...ltlReplyNoteIds])); + } else { + const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([ + `homeTimeline:${me.id}`, + 'localTimeline', + ], untilId, sinceId); + noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); + } - let noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); noteIds.sort((a, b) => a > b ? -1 : 1); noteIds = noteIds.slice(0, ps.limit); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 2357f32d5e..55b5d47386 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -45,6 +45,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, excludeNsfw: { type: 'boolean', default: false }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, @@ -90,7 +91,21 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]) : [new Set(), new Set(), new Set()]; - let noteIds = await this.redisTimelineService.get(ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', untilId, sinceId); + let noteIds: string[]; + + if (ps.withFiles) { + noteIds = await this.redisTimelineService.get('localTimelineWithFiles', untilId, sinceId); + } else if (ps.withReplies) { + const [nonReplyNoteIds, replyNoteIds] = await this.redisTimelineService.getMulti([ + 'localTimeline', + 'localTimelineWithReplies', + ], untilId, sinceId); + noteIds = Array.from(new Set([...nonReplyNoteIds, ...replyNoteIds])); + noteIds.sort((a, b) => a > b ? -1 : 1); + } else { + noteIds = await this.redisTimelineService.get('localTimeline', untilId, sinceId); + } + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index d5f5d54e46..adedca5152 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel { public static shouldShare = false; public static requireCredential = true; private withRenotes: boolean; + private withReplies: boolean; private withFiles: boolean; constructor( @@ -39,6 +40,7 @@ class HybridTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; // Subscribe events @@ -87,7 +89,7 @@ class HybridTimelineChannel extends Channel { if (isInstanceMuted(note, new Set(this.userProfile!.mutedInstances ?? []))) return; // 関係ない返信は除外 - if (note.reply && !this.following[note.userId]?.withReplies) { + if (note.reply && !this.following[note.userId]?.withReplies && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 94c22f8915..69aa366f00 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel { public static shouldShare = false; public static requireCredential = false; private withRenotes: boolean; + private withReplies: boolean; private withFiles: boolean; constructor( @@ -38,6 +39,7 @@ class LocalTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withReplies = params.withReplies ?? false; this.withFiles = params.withFiles ?? false; // Subscribe events @@ -66,7 +68,7 @@ class LocalTimelineChannel extends Channel { } // 関係ない返信は除外 - if (note.reply && this.user && !this.following[note.userId]?.withReplies) { + if (note.reply && this.user && !this.following[note.userId]?.withReplies && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return; diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 25143c2714..a5d7a79fa5 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -512,6 +512,20 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); + test.concurrent('他人の他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/local-timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + }); + test.concurrent('チャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -623,6 +637,20 @@ describe('Timelines', () => { }); */ + test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/local-timeline', { withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + }); + test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -694,6 +722,20 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('他人の他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/hybrid-timeline', { }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + }); + test.concurrent('リモートユーザーのノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); @@ -734,6 +776,20 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/hybrid-timeline', { withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + }); + test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 45dedd5042..cdd72febd1 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -24,9 +24,11 @@ const props = withDefaults(defineProps<{ role?: string; sound?: boolean; withRenotes?: boolean; + withReplies?: boolean; onlyFiles?: boolean; }>(), { withRenotes: true, + withReplies: false, onlyFiles: false, }); @@ -90,10 +92,12 @@ if (props.src === 'antenna') { endpoint = 'notes/local-timeline'; query = { withRenotes: props.withRenotes, + withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }; connection = stream.useChannel('localTimeline', { withRenotes: props.withRenotes, + withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }); connection.on('note', prepend); @@ -101,10 +105,12 @@ if (props.src === 'antenna') { endpoint = 'notes/hybrid-timeline'; query = { withRenotes: props.withRenotes, + withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }; connection = stream.useChannel('hybridTimeline', { withRenotes: props.withRenotes, + withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }); connection.on('note', prepend); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index b8deb77952..c88be2c839 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -15,10 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); const withRenotes = $ref(true); +const withReplies = $ref(false); const onlyFiles = $ref(false); watch($$(src), () => queue = 0); @@ -142,7 +144,11 @@ const headerActions = $computed(() => [{ text: i18n.ts.showRenotes, icon: 'ti ti-repeat', ref: $$(withRenotes), - }, { + }, src === 'local' || src === 'social' ? { + type: 'switch', + text: i18n.ts.showRepliesToOthersInTimeline, + ref: $$(withReplies), + } : undefined, { type: 'switch', text: i18n.ts.fileAttachedOnly, icon: 'ti ti-photo', diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index b2a44ac96b..49fdf4d314 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -31,6 +31,7 @@ export type Column = { excludeTypes?: typeof notificationTypes[number][]; tl?: 'home' | 'local' | 'social' | 'global'; withRenotes?: boolean; + withReplies?: boolean; onlyFiles?: boolean; }; diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 847752247e..bab93622f0 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -23,9 +23,10 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -51,6 +52,7 @@ let disabled = $ref(false); const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable)); const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable)); const withRenotes = $ref(props.column.withRenotes ?? true); +const withReplies = $ref(props.column.withReplies ?? false); const onlyFiles = $ref(props.column.onlyFiles ?? false); watch($$(withRenotes), v => { @@ -107,7 +109,11 @@ const menu = [{ type: 'switch', text: i18n.ts.showRenotes, ref: $$(withRenotes), -}, { +}, props.column.tl === 'local' || props.column.tl === 'social' ? { + type: 'switch', + text: i18n.ts.showRepliesToOthersInTimeline, + ref: $$(withReplies), +} : undefined, { type: 'switch', text: i18n.ts.fileAttachedOnly, ref: $$(onlyFiles), From 8a302a9af4e632f6d41616176f8307da37f28e93 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 11:44:12 +0900 Subject: [PATCH 042/127] Update CHANGELOG.md Fix #12003 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 025ed45dd5..fb6ecb1fb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ ### NOTE - 2023.9.2で導入されたノート編集機能はクオリティの高い実装が困難であることが判明したため撤回されました - アップデートを行うと、タイムラインが一時的にリセットされます +- ソフトミュート設定はクライアントではなくサーバー側に保存されるようになったため、アップデートを行うとソフトミュートの設定がリセットされます ### Changes - API: users/notes, notes/local-timeline で fileType 指定はできなくなりました From cf3624a54fed61930355aeb58baaf75aea820e08 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 12:26:07 +0900 Subject: [PATCH 043/127] =?UTF-8?q?fix(backend):=20users/notes=E3=81=A7?= =?UTF-8?q?=E3=82=BB=E3=83=B3=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=83=81?= =?UTF-8?q?=E3=83=A3=E3=83=B3=E3=83=8D=E3=83=AB=E3=81=AE=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=81=8C=E5=90=AB=E3=81=BE=E3=82=8C=E3=82=8B=E5=A0=B4=E5=90=88?= =?UTF-8?q?=E3=81=8C=E3=81=82=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + packages/backend/src/server/api/endpoints/users/notes.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb6ecb1fb0..42fd7dadba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ ### Server - Fix: フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正 +- Fix: users/notesでセンシティブチャンネルの投稿が含まれる場合がある問題を修正 ## 2023.10.0 ### NOTE diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index dfef35986e..df0951ce74 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -150,7 +150,9 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('renote.user', 'renoteUser'); - if (!ps.withChannelNotes) { + if (ps.withChannelNotes) { + if (!isSelf) query.andWhere('channel.isSensitive = false'); + } else { query.andWhere('note.channelId IS NULL'); } From 566cb35370baac3ca0b7c47ea36afe0d2d4dc8ad Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 14:30:18 +0900 Subject: [PATCH 044/127] update test --- packages/backend/test/e2e/timelines.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index a5d7a79fa5..ce92a36cf9 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -648,7 +648,6 @@ describe('Timelines', () => { const res = await api('/notes/local-timeline', { withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); - assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); }); test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { From a26d9ea132f3a8a48109083f014abc377614d719 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 17:29:24 +0900 Subject: [PATCH 045/127] =?UTF-8?q?enhance(backend):=20LTL=E3=81=A7?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AD=E3=83=BC=E3=81=97=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=82=8B=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=81=AE=E8=87=AA=E5=88=86=E3=81=B8=E3=81=AE=E8=BF=94=E4=BF=A1?= =?UTF-8?q?=E3=81=8C=E5=90=AB=E3=81=BE=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/endpoints/notes/local-timeline.ts | 5 ++--- packages/backend/test/e2e/timelines.ts | 5 +---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 55b5d47386..d10c3bedbf 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -95,15 +95,13 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { noteIds = await this.redisTimelineService.get('localTimelineWithFiles', untilId, sinceId); - } else if (ps.withReplies) { + } else { const [nonReplyNoteIds, replyNoteIds] = await this.redisTimelineService.getMulti([ 'localTimeline', 'localTimelineWithReplies', ], untilId, sinceId); noteIds = Array.from(new Set([...nonReplyNoteIds, ...replyNoteIds])); noteIds.sort((a, b) => a > b ? -1 : 1); - } else { - noteIds = await this.redisTimelineService.get('localTimeline', untilId, sinceId); } noteIds = noteIds.slice(0, ps.limit); @@ -127,6 +125,7 @@ export default class extends Endpoint { // eslint- if (me && (note.userId === me.id)) { return true; } + if (!ps.withReplies && note.replyId && (me == null || note.replyUserId !== me.id)) return false; if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false; if (me && isUserRelated(note, userIdsWhoMeMuting)) return false; if (note.renoteId) { diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index ce92a36cf9..cd27b094ea 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -19,7 +19,7 @@ function genHost() { } function waitForPushToTl() { - return sleep(300); + return sleep(500); } let app: INestApplicationContext; @@ -619,7 +619,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); - /* 実装が面倒 test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -635,7 +634,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); - */ test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); @@ -786,7 +784,6 @@ describe('Timelines', () => { const res = await api('/notes/hybrid-timeline', { withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); - assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); }); test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { From 713295894847dde782fdfe2fa7b0c67a5d0bda90 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 17:58:40 +0900 Subject: [PATCH 046/127] =?UTF-8?q?fix(frontend):=20=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=81=A7=E6=A8=AA?= =?UTF-8?q?=E3=81=AB=E9=95=B7=E3=81=84=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=81=8C=E8=A6=8B=E5=88=87=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +++ packages/frontend/src/components/MkEmojiPicker.vue | 2 ++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42fd7dadba..4222966397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ ### General - Enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に +### Client +- Fix: 絵文字ピッカーで横に長いカスタム絵文字が見切れる問題を修正 + ### Server - Fix: フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正 - Fix: users/notesでセンシティブチャンネルの投稿が含まれる場合がある問題を修正 diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 32f440ae5d..7eff637482 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -615,6 +615,8 @@ defineExpose({ height: 1.25em; vertical-align: -.25em; pointer-events: none; + width: 100%; + object-fit: contain; } } } From 7b6b3ad821477cedb45c26bae66f35bfcb0d7244 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 11 Oct 2023 18:25:02 +0900 Subject: [PATCH 047/127] update test --- packages/backend/test/e2e/timelines.ts | 94 +++++++++++++------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index cd27b094ea..f753b54c6d 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -41,7 +41,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); @@ -57,7 +57,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -73,7 +73,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); @@ -90,7 +90,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -107,7 +107,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -124,7 +124,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -141,7 +141,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -159,7 +159,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); @@ -178,7 +178,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); @@ -194,7 +194,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -210,7 +210,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); @@ -224,7 +224,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); @@ -240,7 +240,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -291,7 +291,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -307,7 +307,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -325,7 +325,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -340,7 +340,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -354,7 +354,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -375,7 +375,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { withFiles: true }, alice); + const res = await api('/notes/timeline', { limit: 100, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -393,7 +393,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -405,7 +405,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); @@ -420,7 +420,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); @@ -433,7 +433,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -447,7 +447,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -460,7 +460,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); @@ -475,7 +475,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok'); @@ -491,7 +491,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', {}, alice); + const res = await api('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -506,7 +506,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -520,7 +520,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); @@ -534,7 +534,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -546,7 +546,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -562,7 +562,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -578,7 +578,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -595,7 +595,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -613,7 +613,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -629,7 +629,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); @@ -643,7 +643,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { withReplies: true }, alice); + const res = await api('/notes/local-timeline', { limit: 100, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -657,7 +657,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { withFiles: true }, alice); + const res = await api('/notes/local-timeline', { limit: 100, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -672,7 +672,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', {}, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -684,7 +684,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', {}, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -698,7 +698,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', {}, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -713,7 +713,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', {}, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); @@ -740,7 +740,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('/notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -754,7 +754,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', {}, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -768,7 +768,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', {}, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -781,7 +781,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { withReplies: true }, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -795,7 +795,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { withFiles: true }, alice); + const res = await api('/notes/hybrid-timeline', { limit: 100, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); From c2e177e37a616dcd36afdaf7a0bd09811f6c636d Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 12 Oct 2023 09:20:25 +0900 Subject: [PATCH 048/127] New Crowdin updates (#12013) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) --- locales/zh-TW.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index e715cae547..ccdb873790 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -2141,3 +2141,11 @@ _moderationLogTypes: createAd: "建立廣告" deleteAd: "刪除廣告" updateAd: "更新廣告" +_fileViewer: + title: "檔案詳細資訊" + type: "檔案類型 " + size: "檔案大小" + url: "URL" + uploadedAt: "加入日期" + attachedNotes: "含有附件的貼文" + thisPageCanBeSeenFromTheAuthor: "本頁面僅限上傳了這個檔案的使用者可以檢視。" From 34eeccf9087fe89c15bf0e1fe7d7db831aaca364 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 12 Oct 2023 09:20:33 +0900 Subject: [PATCH 049/127] 2023.10.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e94852616e..a9b2b13532 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0", + "version": "2023.10.1", "codename": "nasubi", "repository": { "type": "git", From 04908bc90c19e1c63f946a18a73d5c94f528585d Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 13 Oct 2023 16:42:57 +0900 Subject: [PATCH 050/127] .js --- packages/frontend/src/pages/timeline.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index c88be2c839..3449449a7a 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -43,7 +43,7 @@ import { instance } from '@/instance.js'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; -import { antennasCache, userListsCache } from '@/cache'; +import { antennasCache, userListsCache } from '@/cache.js'; provide('shouldOmitHeaderTitle', true); From 096fa16c4cbee60206e66209175501ac16eda3e0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 13 Oct 2023 16:49:56 +0900 Subject: [PATCH 051/127] =?UTF-8?q?enhance(frontend):=20TL=E3=81=AE?= =?UTF-8?q?=E8=BF=94=E4=BF=A1=E8=A1=A8=E7=A4=BA=E3=82=AA=E3=83=97=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E8=A8=98=E6=86=B6=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #12016 --- CHANGELOG.md | 11 +++++++++++ packages/frontend/src/pages/timeline.vue | 6 +++++- packages/frontend/src/store.ts | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4222966397..50334560c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,17 @@ --> +## 2023.x.x (unreleased) + +### General +- + +### Client +- Enhance: TLの返信表示オプションを記憶するように + +### Server +- + ## 2023.10.1 ### General - Enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 3449449a7a..8cc540779b 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -62,11 +62,15 @@ let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); const withRenotes = $ref(true); -const withReplies = $ref(false); +const withReplies = $ref($i ? defaultStore.state.tlWithReplies : false); const onlyFiles = $ref(false); watch($$(src), () => queue = 0); +watch($$(withReplies), (x) => { + if ($i) defaultStore.set('tlWithReplies', x); +}); + function queueUpdated(q: number): void { queue = q; } diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 58730c7cef..2829411ae5 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -357,6 +357,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + tlWithReplies: { + where: 'device', + default: false, + }, })); // TODO: 他のタブと永続化されたstateを同期 From 061e389340a7e5683be2ba0da4e1a1048597ccb6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 14 Oct 2023 10:04:13 +0900 Subject: [PATCH 052/127] =?UTF-8?q?perf(backend):=20nyaize=E3=82=92?= =?UTF-8?q?=E6=8A=95=E7=A8=BF=E6=99=82=E3=81=AB=E3=82=84=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #12030 --- .../backend/src/core/NoteCreateService.ts | 25 ++++++++++++++++++- .../src/core/entities/NoteEntityService.ts | 21 ---------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 64d2880ba1..4496be3e7d 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -55,6 +55,7 @@ import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; import { FeaturedService } from '@/core/FeaturedService.js'; import { RedisTimelineService } from '@/core/RedisTimelineService.js'; +import { nyaize } from '@/misc/nyaize.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -223,7 +224,10 @@ export class NoteCreateService implements OnApplicationShutdown { host: MiUser['host']; createdAt: MiUser['createdAt']; isBot: MiUser['isBot']; + isCat: MiUser['isCat']; }, data: Option, silent = false): Promise { + let patsedText: mfm.MfmNode[] | null = null; + // チャンネル外にリプライしたら対象のスコープに合わせる // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { @@ -302,6 +306,25 @@ export class NoteCreateService implements OnApplicationShutdown { data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); } data.text = data.text.trim(); + + if (user.isCat) { + patsedText = patsedText ?? mfm.parse(data.text); + function nyaizeNode(node: mfm.MfmNode) { + if (node.type === 'quote') return; + if (node.type === 'text') { + node.props.text = nyaize(node.props.text); + } + if (node.children) { + for (const child of node.children) { + nyaizeNode(child); + } + } + } + for (const node of patsedText) { + nyaizeNode(node); + } + data.text = mfm.toString(patsedText); + } } else { data.text = null; } @@ -312,7 +335,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Parse MFM if needed if (!tags || !emojis || !mentionedUsers) { - const tokens = data.text ? mfm.parse(data.text)! : []; + const tokens = patsedText ?? (data.text ? mfm.parse(data.text)! : []); const cwTokens = data.cw ? mfm.parse(data.cw)! : []; const choiceTokens = data.poll && data.poll.choices ? concat(data.poll.choices.map(choice => mfm.parse(choice)!)) diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index abe4aafd6e..25132977f2 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -5,11 +5,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import * as mfm from 'mfm-js'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { Packed } from '@/misc/json-schema.js'; -import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; @@ -362,25 +360,6 @@ export class NoteEntityService implements OnModuleInit { } : {}), }); - if (packed.user.isCat && packed.text) { - const tokens = packed.text ? mfm.parse(packed.text) : []; - function nyaizeNode(node: mfm.MfmNode) { - if (node.type === 'quote') return; - if (node.type === 'text') { - node.props.text = nyaize(node.props.text); - } - if (node.children) { - for (const child of node.children) { - nyaizeNode(child); - } - } - } - for (const node of tokens) { - nyaizeNode(node); - } - packed.text = mfm.toString(tokens); - } - if (!opts.skipHide) { await this.hideNote(packed, meId); } From 329830e2c3d1d46de712d1cbe4cf3bac51144dcb Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 14 Oct 2023 10:05:44 +0900 Subject: [PATCH 053/127] perf(backend): tweak populateMyReaction --- packages/backend/src/core/entities/NoteEntityService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 25132977f2..316367f23a 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -180,8 +180,8 @@ export class NoteEntityService implements OnModuleInit { // 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない } - // パフォーマンスのためノートが作成されてから1秒以上経っていない場合はリアクションを取得しない - if (note.createdAt.getTime() + 1000 > Date.now()) { + // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない + if (note.createdAt.getTime() + 2000 > Date.now()) { return undefined; } @@ -382,8 +382,8 @@ export class NoteEntityService implements OnModuleInit { const myReactionsMap = new Map(); if (meId) { const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); - // パフォーマンスのためノートが作成されてから1秒以上経っていない場合はリアクションを取得しない - const targets = [...notes.filter(n => n.createdAt.getTime() + 1000 < Date.now()).map(n => n.id), ...renoteIds]; + // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない + const targets = [...notes.filter(n => n.createdAt.getTime() + 2000 < Date.now()).map(n => n.id), ...renoteIds]; const myReactions = await this.noteReactionsRepository.findBy({ userId: meId, noteId: In(targets), From 3f4ee9840560d8c423111f1ab93b2e36218a1314 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 15 Oct 2023 10:36:22 +0900 Subject: [PATCH 054/127] perf(backend): improve streaming api performance (#12033) * wip * Update NoteEntityService.ts * wip * wip * wip * wip --- .../backend/src/core/NoteCreateService.ts | 2 +- .../src/core/entities/NoteEntityService.ts | 13 +++++--- .../src/server/api/stream/channels/channel.ts | 18 +++-------- .../api/stream/channels/global-timeline.ts | 18 +++-------- .../src/server/api/stream/channels/hashtag.ts | 12 +++---- .../api/stream/channels/home-timeline.ts | 30 ++++++----------- .../api/stream/channels/hybrid-timeline.ts | 30 ++++++----------- .../api/stream/channels/local-timeline.ts | 18 +++-------- .../server/api/stream/channels/user-list.ts | 32 +++++++------------ 9 files changed, 58 insertions(+), 115 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 4496be3e7d..e12945172f 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -577,7 +577,7 @@ export class NoteCreateService implements OnApplicationShutdown { } // Pack the note - const noteObj = await this.noteEntityService.pack(note); + const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true }); this.globalEventService.publishNotesStream(noteObj); diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 316367f23a..f871ba50a8 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -16,6 +16,7 @@ import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepos import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; import { DebounceLoader } from '@/misc/loader.js'; +import { IdService } from '@/core/IdService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { ReactionService } from '../ReactionService.js'; @@ -28,6 +29,7 @@ export class NoteEntityService implements OnModuleInit { private driveFileEntityService: DriveFileEntityService; private customEmojiService: CustomEmojiService; private reactionService: ReactionService; + private idService: IdService; private noteLoader = new DebounceLoader(this.findNoteOrFail); constructor( @@ -66,6 +68,7 @@ export class NoteEntityService implements OnModuleInit { this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.reactionService = this.moduleRef.get('ReactionService'); + this.idService = this.moduleRef.get('IdService'); } @bindThis @@ -167,11 +170,11 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - private async populateMyReaction(note: MiNote, meId: MiUser['id'], _hint_?: { + public async populateMyReaction(noteId: MiNote['id'], meId: MiUser['id'], _hint_?: { myReactions: Map; }) { if (_hint_?.myReactions) { - const reaction = _hint_.myReactions.get(note.id); + const reaction = _hint_.myReactions.get(noteId); if (reaction) { return this.reactionService.convertLegacyReaction(reaction.reaction); } else if (reaction === null) { @@ -181,13 +184,13 @@ export class NoteEntityService implements OnModuleInit { } // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない - if (note.createdAt.getTime() + 2000 > Date.now()) { + if (this.idService.parse(noteId).date.getTime() + 2000 > Date.now()) { return undefined; } const reaction = await this.noteReactionsRepository.findOneBy({ userId: meId, - noteId: note.id, + noteId: noteId, }); if (reaction) { @@ -355,7 +358,7 @@ export class NoteEntityService implements OnModuleInit { poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, ...(meId ? { - myReaction: this.populateMyReaction(note, meId, options?._hint_), + myReaction: this.populateMyReaction(note.id, meId, options?._hint_), } : {}), } : {}), }); diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index a01714e76d..e4c34e00ce 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -38,19 +38,6 @@ class ChannelChannel extends Channel { private async onNote(note: Packed<'Note'>) { if (note.channelId !== this.channelId) return; - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { - detail: true, - }); - } - // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する @@ -58,6 +45,11 @@ class ChannelChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + if (this.user && note.renoteId && !note.text) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } + this.connection.cacheNote(note); this.send('note', note); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 03f2dff62b..c499d1787e 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -52,19 +52,6 @@ class GlobalTimelineChannel extends Channel { if (note.visibility !== 'public') return; if (note.channelId != null) return; - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { - detail: true, - }); - } - // 関係ない返信は除外 if (note.reply && !this.following[note.userId]?.withReplies) { const reply = note.reply; @@ -84,6 +71,11 @@ class GlobalTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + if (this.user && note.renoteId && !note.text) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } + this.connection.cacheNote(note); this.send('note', note); diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 3945b1a1eb..2cfe9572d3 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -43,13 +43,6 @@ class HashtagChannel extends Channel { const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag)))); if (!matched) return; - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { - detail: true, - }); - } - // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する @@ -57,6 +50,11 @@ class HashtagChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + if (this.user && note.renoteId && !note.text) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } + this.connection.cacheNote(note); this.send('note', note); diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 24be590504..de755cccb9 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -51,27 +51,10 @@ class HomeTimelineChannel extends Channel { // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set(this.userProfile!.mutedInstances ?? []))) return; - if (['followers', 'specified'].includes(note.visibility)) { - note = await this.noteEntityService.pack(note.id, this.user!, { - detail: true, - }); - - if (note.isHidden) { - return; - } - } else { - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user!, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, { - detail: true, - }); - } + if (note.visibility === 'followers') { + if (!Object.hasOwn(this.following, note.userId)) return; + } else if (note.visibility === 'specified') { + if (!note.visibleUserIds!.includes(this.user!.id)) return; } // 関係ない返信は除外 @@ -90,6 +73,11 @@ class HomeTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + if (this.user && note.renoteId && !note.text) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } + this.connection.cacheNote(note); this.send('note', note); diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index adedca5152..83f0bccd90 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -62,27 +62,10 @@ class HybridTimelineChannel extends Channel { (note.channelId != null && this.followingChannels.has(note.channelId)) )) return; - if (['followers', 'specified'].includes(note.visibility)) { - note = await this.noteEntityService.pack(note.id, this.user!, { - detail: true, - }); - - if (note.isHidden) { - return; - } - } else { - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user!, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, { - detail: true, - }); - } + if (note.visibility === 'followers') { + if (!Object.hasOwn(this.following, note.userId)) return; + } else if (note.visibility === 'specified') { + if (!note.visibleUserIds!.includes(this.user!.id)) return; } // Ignore notes from instances the user has muted @@ -104,6 +87,11 @@ class HybridTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + if (this.user && note.renoteId && !note.text) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } + this.connection.cacheNote(note); this.send('note', note); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 69aa366f00..a211041134 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -54,19 +54,6 @@ class LocalTimelineChannel extends Channel { if (note.visibility !== 'public') return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { - detail: true, - }); - } - // 関係ない返信は除外 if (note.reply && this.user && !this.following[note.userId]?.withReplies && !this.withReplies) { const reply = note.reply; @@ -83,6 +70,11 @@ class LocalTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + if (this.user && note.renoteId && !note.text) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } + this.connection.cacheNote(note); this.send('note', note); diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 240822d9ab..b73cedaa8b 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -82,27 +82,10 @@ class UserListChannel extends Channel { if (!Object.hasOwn(this.membershipsMap, note.userId)) return; - if (['followers', 'specified'].includes(note.visibility)) { - note = await this.noteEntityService.pack(note.id, this.user, { - detail: true, - }); - - if (note.isHidden) { - return; - } - } else { - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { - detail: true, - }); - } + if (note.visibility === 'followers') { + if (!Object.hasOwn(this.following, note.userId)) return; + } else if (note.visibility === 'specified') { + if (!note.visibleUserIds!.includes(this.user!.id)) return; } // 関係ない返信は除外 @@ -119,6 +102,13 @@ class UserListChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + if (this.user && note.renoteId && !note.text) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } + + this.connection.cacheNote(note); + this.send('note', note); } From 5117818f6fcaa5ff6d033daaab980b956f2b20ad Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 15 Oct 2023 10:37:03 +0900 Subject: [PATCH 055/127] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50334560c9..9b51d2c036 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - Enhance: TLの返信表示オプションを記憶するように ### Server -- +- Enhance: ストリーミングAPIのパフォーマンスを向上 ## 2023.10.1 ### General From 15706c8c2bdb0a6edf2b34a7c1f25d80e4610a60 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 15 Oct 2023 16:25:42 +0900 Subject: [PATCH 056/127] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b51d2c036..85a998de10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ ### NOTE - 2023.9.2で導入されたノート編集機能はクオリティの高い実装が困難であることが判明したため撤回されました - アップデートを行うと、タイムラインが一時的にリセットされます + - アンテナ内のノートも含む - ソフトミュート設定はクライアントではなくサーバー側に保存されるようになったため、アップデートを行うとソフトミュートの設定がリセットされます ### Changes From 1fa1d316969de15d6aaef1dbf0a0b95aec30e377 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 10:45:22 +0900 Subject: [PATCH 057/127] =?UTF-8?q?perf(backend):=20createdAt=E3=82=92id?= =?UTF-8?q?=E3=81=8B=E3=82=89=E5=8F=96=E5=BE=97=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=20&=20=E7=84=A1=E9=A7=84=E3=81=AADate?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=B9=E3=82=BF=E3=83=B3=E3=82=B9=E3=81=AE?= =?UTF-8?q?=E7=94=9F=E6=88=90=E3=82=92=E9=81=BF=E3=81=91=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../1697420555911-deleteCreatedAt.js | 144 ++++++++++++++++++ .../backend/src/core/AccountMoveService.ts | 4 +- .../backend/src/core/AnnouncementService.ts | 10 +- packages/backend/src/core/AntennaService.ts | 2 - packages/backend/src/core/ClipService.ts | 5 +- .../src/core/CreateSystemUserService.ts | 3 +- .../backend/src/core/CustomEmojiService.ts | 2 +- packages/backend/src/core/DriveService.ts | 3 +- .../src/core/FederatedInstanceService.ts | 2 +- packages/backend/src/core/HashtagService.ts | 4 +- packages/backend/src/core/IdService.ts | 20 ++- .../backend/src/core/ModerationLogService.ts | 3 +- .../backend/src/core/NoteCreateService.ts | 5 +- .../backend/src/core/NotePiningService.ts | 3 +- packages/backend/src/core/NoteReadService.ts | 2 +- .../backend/src/core/NotificationService.ts | 2 +- packages/backend/src/core/PollService.ts | 4 +- packages/backend/src/core/QueryService.ts | 8 +- packages/backend/src/core/ReactionService.ts | 3 +- packages/backend/src/core/RelayService.ts | 2 +- packages/backend/src/core/RoleService.ts | 17 +-- packages/backend/src/core/SearchService.ts | 2 +- packages/backend/src/core/SignupService.ts | 3 +- .../backend/src/core/UserBlockingService.ts | 3 +- .../backend/src/core/UserFollowingService.ts | 6 +- packages/backend/src/core/UserListService.ts | 3 +- .../backend/src/core/UserMutingService.ts | 3 +- packages/backend/src/core/WebhookService.ts | 3 - .../src/core/activitypub/ApInboxService.ts | 3 +- .../src/core/activitypub/ApRendererService.ts | 8 +- .../core/activitypub/models/ApNoteService.ts | 2 +- .../activitypub/models/ApPersonService.ts | 6 +- .../src/core/chart/charts/active-users.ts | 19 ++- .../entities/AbuseUserReportEntityService.ts | 4 +- .../src/core/entities/AntennaEntityService.ts | 5 +- .../core/entities/BlockingEntityService.ts | 4 +- .../src/core/entities/ChannelEntityService.ts | 4 +- .../src/core/entities/ClipEntityService.ts | 4 +- .../core/entities/DriveFileEntityService.ts | 6 +- .../core/entities/DriveFolderEntityService.ts | 5 +- .../src/core/entities/FlashEntityService.ts | 4 +- .../core/entities/FollowingEntityService.ts | 4 +- .../core/entities/GalleryPostEntityService.ts | 4 +- .../core/entities/InviteCodeEntityService.ts | 4 +- .../entities/ModerationLogEntityService.ts | 4 +- .../src/core/entities/MutingEntityService.ts | 4 +- .../src/core/entities/NoteEntityService.ts | 5 +- .../entities/NoteFavoriteEntityService.ts | 4 +- .../entities/NoteReactionEntityService.ts | 6 +- .../src/core/entities/PageEntityService.ts | 4 +- .../entities/RenoteMutingEntityService.ts | 4 +- .../src/core/entities/RoleEntityService.ts | 5 +- .../src/core/entities/UserEntityService.ts | 12 +- .../core/entities/UserListEntityService.ts | 6 +- packages/backend/src/misc/id/aid.ts | 3 +- packages/backend/src/misc/id/aidx.ts | 3 +- packages/backend/src/misc/id/meid.ts | 4 +- packages/backend/src/misc/id/meidg.ts | 4 +- packages/backend/src/misc/id/object-id.ts | 4 +- .../backend/src/models/AbuseUserReport.ts | 6 - packages/backend/src/models/AccessToken.ts | 5 - packages/backend/src/models/Ad.ts | 6 - packages/backend/src/models/Announcement.ts | 6 - .../backend/src/models/AnnouncementRead.ts | 5 - packages/backend/src/models/Antenna.ts | 5 - packages/backend/src/models/App.ts | 6 - packages/backend/src/models/AuthSession.ts | 5 - packages/backend/src/models/Blocking.ts | 6 - packages/backend/src/models/Channel.ts | 6 - .../backend/src/models/ChannelFavorite.ts | 6 - .../backend/src/models/ChannelFollowing.ts | 6 - packages/backend/src/models/Clip.ts | 5 - packages/backend/src/models/ClipFavorite.ts | 3 - packages/backend/src/models/DriveFile.ts | 6 - packages/backend/src/models/DriveFolder.ts | 6 - packages/backend/src/models/Flash.ts | 6 - packages/backend/src/models/FlashLike.ts | 3 - packages/backend/src/models/FollowRequest.ts | 5 - packages/backend/src/models/Following.ts | 6 - packages/backend/src/models/GalleryLike.ts | 3 - packages/backend/src/models/GalleryPost.ts | 6 - packages/backend/src/models/ModerationLog.ts | 5 - packages/backend/src/models/Muting.ts | 6 - packages/backend/src/models/Note.ts | 5 - packages/backend/src/models/NoteFavorite.ts | 5 - packages/backend/src/models/NoteReaction.ts | 5 - .../backend/src/models/NoteThreadMuting.ts | 4 - packages/backend/src/models/Page.ts | 6 - packages/backend/src/models/PageLike.ts | 3 - .../src/models/PasswordResetRequest.ts | 3 - packages/backend/src/models/PollVote.ts | 6 - packages/backend/src/models/PromoRead.ts | 5 - .../backend/src/models/RegistrationTicket.ts | 3 - packages/backend/src/models/RegistryItem.ts | 5 - packages/backend/src/models/RenoteMuting.ts | 6 - packages/backend/src/models/Role.ts | 5 - packages/backend/src/models/RoleAssignment.ts | 5 - packages/backend/src/models/Signin.ts | 5 - packages/backend/src/models/SwSubscription.ts | 3 - packages/backend/src/models/User.ts | 6 - packages/backend/src/models/UserList.ts | 5 - .../backend/src/models/UserListFavorite.ts | 3 - .../backend/src/models/UserListMembership.ts | 5 - packages/backend/src/models/UserNotePining.ts | 5 - packages/backend/src/models/UserPending.ts | 3 - packages/backend/src/models/Webhook.ts | 5 - .../AggregateRetentionProcessorService.ts | 4 +- .../ExportFavoritesProcessorService.ts | 60 ++++---- .../processors/ExportNotesProcessorService.ts | 39 ++--- .../ImportAntennasProcessorService.ts | 3 +- .../ImportUserListsProcessorService.ts | 3 +- .../src/server/api/SigninApiService.ts | 3 +- .../backend/src/server/api/SigninService.ts | 3 +- .../src/server/api/SignupApiService.ts | 3 +- .../server/api/endpoints/admin/ad/create.ts | 3 +- .../endpoints/admin/announcements/create.ts | 1 - .../api/endpoints/admin/announcements/list.ts | 4 +- .../api/endpoints/admin/drive/show-file.ts | 4 +- .../server/api/endpoints/admin/emoji/copy.ts | 2 +- .../api/endpoints/admin/get-user-ips.ts | 7 +- .../api/endpoints/admin/invite/create.ts | 3 +- .../server/api/endpoints/admin/roles/users.ts | 4 +- .../server/api/endpoints/admin/show-user.ts | 4 +- .../server/api/endpoints/antennas/create.ts | 3 +- .../server/api/endpoints/antennas/notes.ts | 4 +- .../src/server/api/endpoints/app/create.ts | 3 +- .../src/server/api/endpoints/auth/accept.ts | 3 +- .../api/endpoints/auth/session/generate.ts | 3 +- .../server/api/endpoints/channels/create.ts | 3 +- .../server/api/endpoints/channels/favorite.ts | 3 +- .../server/api/endpoints/channels/follow.ts | 3 +- .../server/api/endpoints/channels/timeline.ts | 4 +- .../server/api/endpoints/clips/favorite.ts | 3 +- .../api/endpoints/drive/folders/create.ts | 3 +- .../src/server/api/endpoints/flash/create.ts | 3 +- .../src/server/api/endpoints/flash/like.ts | 3 +- .../api/endpoints/gallery/posts/create.ts | 3 +- .../api/endpoints/gallery/posts/like.ts | 3 +- .../src/server/api/endpoints/i/apps.ts | 9 +- .../server/api/endpoints/i/registry/set.ts | 3 +- .../server/api/endpoints/i/webhooks/create.ts | 3 +- .../src/server/api/endpoints/invite/create.ts | 5 +- .../src/server/api/endpoints/invite/limit.ts | 4 +- .../server/api/endpoints/miauth/gen-token.ts | 3 +- .../api/endpoints/notes/favorites/create.ts | 3 +- .../api/endpoints/notes/hybrid-timeline.ts | 4 +- .../api/endpoints/notes/local-timeline.ts | 4 +- .../endpoints/notes/polls/recommendation.ts | 2 +- .../server/api/endpoints/notes/polls/vote.ts | 3 +- .../endpoints/notes/thread-muting/create.ts | 3 +- .../server/api/endpoints/notes/timeline.ts | 4 +- .../api/endpoints/notes/user-list-timeline.ts | 4 +- .../src/server/api/endpoints/pages/create.ts | 3 +- .../src/server/api/endpoints/pages/like.ts | 3 +- .../src/server/api/endpoints/promo/read.ts | 3 +- .../api/endpoints/renote-mute/create.ts | 3 +- .../api/endpoints/request-reset-password.ts | 3 +- .../server/api/endpoints/reset-password.ts | 5 +- .../src/server/api/endpoints/roles/notes.ts | 4 +- .../src/server/api/endpoints/sw/register.ts | 3 +- .../users/lists/create-from-public.ts | 3 +- .../api/endpoints/users/lists/create.ts | 3 +- .../api/endpoints/users/lists/favorite.ts | 3 +- .../src/server/api/endpoints/users/notes.ts | 4 +- .../api/endpoints/users/report-abuse.ts | 3 +- .../server/api/endpoints/users/update-memo.ts | 2 +- .../src/server/oauth/OAuth2ProviderService.ts | 3 +- .../backend/src/server/web/FeedService.ts | 8 +- packages/backend/test/e2e/streaming.ts | 1 - .../backend/test/unit/AnnouncementService.ts | 6 +- packages/backend/test/unit/RoleService.ts | 2 - 171 files changed, 442 insertions(+), 537 deletions(-) create mode 100644 packages/backend/migration/1697420555911-deleteCreatedAt.js diff --git a/packages/backend/migration/1697420555911-deleteCreatedAt.js b/packages/backend/migration/1697420555911-deleteCreatedAt.js new file mode 100644 index 0000000000..958d61a348 --- /dev/null +++ b/packages/backend/migration/1697420555911-deleteCreatedAt.js @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class DeleteCreatedAt1697420555911 { + name = 'DeleteCreatedAt1697420555911' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_02878d441ceae15ce060b73daf"`); + await queryRunner.query(`DROP INDEX "public"."IDX_c8dfad3b72196dd1d6b5db168a"`); + await queryRunner.query(`DROP INDEX "public"."IDX_e11e649824a45d8ed01d597fd9"`); + await queryRunner.query(`DROP INDEX "public"."IDX_db2098070b2b5a523c58181f74"`); + await queryRunner.query(`DROP INDEX "public"."IDX_048a757923ed8b157e9895da53"`); + await queryRunner.query(`DROP INDEX "public"."IDX_1129c2ef687fc272df040bafaa"`); + await queryRunner.query(`DROP INDEX "public"."IDX_118ec703e596086fc4515acb39"`); + await queryRunner.query(`DROP INDEX "public"."IDX_b9a354f7941c1e779f3b33aea6"`); + await queryRunner.query(`DROP INDEX "public"."IDX_71cb7b435b7c0d4843317e7e16"`); + await queryRunner.query(`DROP INDEX "public"."IDX_11e71f2511589dcc8a4d3214f9"`); + await queryRunner.query(`DROP INDEX "public"."IDX_735a5544f9249d412255f47f95"`); + await queryRunner.query(`DROP INDEX "public"."IDX_582f8fab771a9040a12961f3e7"`); + await queryRunner.query(`DROP INDEX "public"."IDX_8f1a239bd077c8864a20c62c2c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_f86d57fbca33c7a4e6897490cc"`); + await queryRunner.query(`DROP INDEX "public"."IDX_d1259a2c2b7bb413ff449e8711"`); + await queryRunner.query(`DROP INDEX "public"."IDX_fbb4297c927a9b85e9cefa2eb1"`); + await queryRunner.query(`DROP INDEX "public"."IDX_0fb627e1c2f753262a74f0562d"`); + await queryRunner.query(`DROP INDEX "public"."IDX_149d2e44785707548c82999b01"`); + await queryRunner.query(`ALTER TABLE "drive_folder" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "app" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "announcement_read" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "user_list" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "auth_session" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "blocking" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "channel_following" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "channel_favorite" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "clip" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "clip_favorite" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "following" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "follow_request" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "gallery_post" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "gallery_like" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "moderation_log" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "muting" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "renote_muting" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "note_favorite" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "note_reaction" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "note_thread_muting" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "page_like" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "password_reset_request" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "poll_vote" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "promo_read" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "registration_ticket" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "signin" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "sw_subscription" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "user_list_favorite" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "user_list_membership" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "user_note_pining" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "user_pending" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "role_assignment" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "flash" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "flash_like" DROP COLUMN "createdAt"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "flash_like" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "flash" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "role_assignment" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "role" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "webhook" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "user_pending" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "user_note_pining" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "user_list_membership" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "user_list_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "sw_subscription" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "signin" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "registry_item" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "registration_ticket" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "promo_read" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "poll_vote" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "password_reset_request" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "page_like" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "page" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_thread_muting" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_reaction" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "renote_muting" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "muting" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "moderation_log" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "gallery_like" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "gallery_post" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "follow_request" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "following" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "clip_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "note" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "clip" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "channel_favorite" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "channel_following" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "channel" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "blocking" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "auth_session" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "antenna" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "user_list" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "announcement_read" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "announcement" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "ad" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "access_token" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "app" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "user" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "drive_file" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "drive_folder" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`CREATE INDEX "IDX_149d2e44785707548c82999b01" ON "flash" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_0fb627e1c2f753262a74f0562d" ON "poll_vote" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_fbb4297c927a9b85e9cefa2eb1" ON "page" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_d1259a2c2b7bb413ff449e8711" ON "renote_muting" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_f86d57fbca33c7a4e6897490cc" ON "muting" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_8f1a239bd077c8864a20c62c2c" ON "gallery_post" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_582f8fab771a9040a12961f3e7" ON "following" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_735a5544f9249d412255f47f95" ON "channel_favorite" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_11e71f2511589dcc8a4d3214f9" ON "channel_following" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_71cb7b435b7c0d4843317e7e16" ON "channel" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_b9a354f7941c1e779f3b33aea6" ON "blocking" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_118ec703e596086fc4515acb39" ON "announcement" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_1129c2ef687fc272df040bafaa" ON "ad" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_048a757923ed8b157e9895da53" ON "app" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_db2098070b2b5a523c58181f74" ON "abuse_user_report" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_e11e649824a45d8ed01d597fd9" ON "user" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_c8dfad3b72196dd1d6b5db168a" ON "drive_file" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_02878d441ceae15ce060b73daf" ON "drive_folder" ("createdAt") `); + } +} diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index db64f42754..ed2891113b 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -186,7 +186,7 @@ export class AccountMoveService { const genId = (): string => { let id: string; do { - id = this.idService.genId(); + id = this.idService.gen(); } while (newMutings.has(id)); return id; }; @@ -234,7 +234,7 @@ export class AccountMoveService { const genId = (): string => { let id: string; do { - id = this.idService.genId(); + id = this.idService.gen(); } while (newMemberships.has(id)); return id; }; diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts index a5330db53f..ec1a082d78 100644 --- a/packages/backend/src/core/AnnouncementService.ts +++ b/packages/backend/src/core/AnnouncementService.ts @@ -53,7 +53,7 @@ export class AnnouncementService { })) .andWhere(new Brackets(qb => { qb.orWhere('announcement.forExistingUsers = false'); - qb.orWhere('announcement.createdAt > :createdAt', { createdAt: user.createdAt }); + qb.orWhere('announcement.id > :userId', { userId: user.id }); })) .andWhere(`announcement.id NOT IN (${ readsQuery.getQuery() })`); @@ -65,8 +65,7 @@ export class AnnouncementService { @bindThis public async create(values: Partial, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> { const announcement = await this.announcementsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), updatedAt: null, title: values.title, text: values.text, @@ -179,8 +178,7 @@ export class AnnouncementService { public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise { try { await this.announcementReadsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), announcementId: announcementId, userId: user.id, }); @@ -204,7 +202,7 @@ export class AnnouncementService { const reads = me ? (options?.reads ?? await this.getReads(me.id)) : []; return announcements.map(announcement => ({ id: announcement.id, - createdAt: announcement.createdAt.toISOString(), + createdAt: this.idService.parse(announcement.id).date.toISOString(), updatedAt: announcement.updatedAt?.toISOString() ?? null, text: announcement.text, title: announcement.title, diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index ca7624b1d4..60569c44fc 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -57,14 +57,12 @@ export class AntennaService implements OnApplicationShutdown { case 'antennaCreated': this.antennas.push({ ...body, - createdAt: new Date(body.createdAt), lastUsedAt: new Date(body.lastUsedAt), }); break; case 'antennaUpdated': this.antennas[this.antennas.findIndex(a => a.id === body.id)] = { ...body, - createdAt: new Date(body.createdAt), lastUsedAt: new Date(body.lastUsedAt), }; break; diff --git a/packages/backend/src/core/ClipService.ts b/packages/backend/src/core/ClipService.ts index 3d9982e80f..e94f1eb531 100644 --- a/packages/backend/src/core/ClipService.ts +++ b/packages/backend/src/core/ClipService.ts @@ -46,8 +46,7 @@ export class ClipService { } const clip = await this.clipsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, name: name, isPublic: isPublic, @@ -109,7 +108,7 @@ export class ClipService { try { await this.clipNotesRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), noteId: noteId, clipId: clip.id, }); diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 3419d0b497..6b475316f0 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -52,8 +52,7 @@ export class CreateSystemUserService { if (exist) throw new Error('the user is already exists'); account = await transactionalEntityManager.insert(MiUser, { - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), username: username, usernameLower: username.toLowerCase(), host: null, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 145c224f67..505c8e4269 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -69,7 +69,7 @@ export class CustomEmojiService implements OnApplicationShutdown { roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; }, moderator?: MiUser): Promise { const emoji = await this.emojisRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), updatedAt: new Date(), name: data.name, category: data.category, diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index cecbec9638..484f4fc52e 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -564,8 +564,7 @@ export class DriveService { const folder = await fetchFolder(); let file = new MiDriveFile(); - file.id = this.idService.genId(); - file.createdAt = new Date(); + file.id = this.idService.gen(); file.userId = user ? user.id : null; file.userHost = user ? user.host : null; file.folderId = folder !== null ? folder.id : null; diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index 61806583c6..e41f010e48 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -56,7 +56,7 @@ export class FederatedInstanceService implements OnApplicationShutdown { if (index == null) { const i = await this.instancesRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), host, firstRetrievedAt: new Date(), }).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index ddff28359a..1a2f37be39 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -120,7 +120,7 @@ export class HashtagService { } else { if (isUserAttached) { this.hashtagsRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), name: tag, mentionedUserIds: [], mentionedUsersCount: 0, @@ -137,7 +137,7 @@ export class HashtagService { } as MiHashtag); } else { this.hashtagsRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), name: tag, mentionedUserIds: [user.id], mentionedUsersCount: 1, diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 06c58ad8a1..c98b8ea6fc 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -26,17 +26,21 @@ export class IdService { this.method = config.id.toLowerCase(); } + /** + * 時間を元にIDを生成します(省略時は現在日時) + * @param time 日時 + */ @bindThis - public genId(date?: Date): string { - if (!date || (date > new Date())) date = new Date(); + public gen(time?: number): string { + const t = (!time || (time > Date.now())) ? Date.now() : time; switch (this.method) { - case 'aid': return genAid(date); - case 'aidx': return genAidx(date); - case 'meid': return genMeid(date); - case 'meidg': return genMeidg(date); - case 'ulid': return ulid(date.getTime()); - case 'objectid': return genObjectId(date); + case 'aid': return genAid(t); + case 'aidx': return genAidx(t); + case 'meid': return genMeid(t); + case 'meidg': return genMeidg(t); + case 'ulid': return ulid(t); + case 'objectid': return genObjectId(t); default: throw new Error('unrecognized id generation method'); } } diff --git a/packages/backend/src/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts index f7f9063d92..8b78d02047 100644 --- a/packages/backend/src/core/ModerationLogService.ts +++ b/packages/backend/src/core/ModerationLogService.ts @@ -24,8 +24,7 @@ export class ModerationLogService { @bindThis public async log(moderator: { id: MiUser['id'] }, type: T, info?: ModerationLogPayloads[T]) { await this.moderationLogsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: moderator.id, type: type, info: (info as any) ?? {}, diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index e12945172f..400f1ec98c 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -222,7 +222,6 @@ export class NoteCreateService implements OnApplicationShutdown { id: MiUser['id']; username: MiUser['username']; host: MiUser['host']; - createdAt: MiUser['createdAt']; isBot: MiUser['isBot']; isCat: MiUser['isCat']; }, data: Option, silent = false): Promise { @@ -383,8 +382,7 @@ export class NoteCreateService implements OnApplicationShutdown { @bindThis private async insertNote(user: { id: MiUser['id']; host: MiUser['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { const insert = new MiNote({ - id: this.idService.genId(data.createdAt!), - createdAt: data.createdAt!, + id: this.idService.gen(data.createdAt?.getTime()), fileIds: data.files ? data.files.map(file => file.id) : [], replyId: data.reply ? data.reply.id : null, renoteId: data.renote ? data.renote.id : null, @@ -483,7 +481,6 @@ export class NoteCreateService implements OnApplicationShutdown { id: MiUser['id']; username: MiUser['username']; host: MiUser['host']; - createdAt: MiUser['createdAt']; isBot: MiUser['isBot']; }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { const meta = await this.metaService.fetch(); diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index 147554ee9a..52abb4c2a1 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -71,8 +71,7 @@ export class NotePiningService { } await this.userNotePiningsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: user.id, noteId: note.id, } as MiUserNotePining); diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index 422e0192cf..03c1735e04 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -57,7 +57,7 @@ export class NoteReadService implements OnApplicationShutdown { if (isThreadMuted) return; const unread = { - id: this.idService.genId(), + id: this.idService.gen(), noteId: note.id, userId: userId, isSpecified: params.isSpecified, diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 32d54d2576..c6d5023e65 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -125,7 +125,7 @@ export class NotificationService implements OnApplicationShutdown { } const notification = { - id: this.idService.genId(), + id: this.idService.gen(), createdAt: new Date(), type: type, notifierId: notifierId, diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 570f2350f1..9e1b5ca78a 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -72,10 +72,8 @@ export class PollService { throw new Error('already voted'); } - // Create vote await this.pollVotesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), noteId: note.id, userId: user.id, choice: choice, diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 50d1d2e65b..ae8f8a3f19 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -52,14 +52,14 @@ export class QueryService { q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); q.orderBy(`${q.alias}.id`, 'DESC'); } else if (sinceDate && untilDate) { - q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); - q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.gen(sinceDate) }); + q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.gen(untilDate) }); q.orderBy(`${q.alias}.id`, 'DESC'); } else if (sinceDate) { - q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.gen(sinceDate) }); q.orderBy(`${q.alias}.id`, 'ASC'); } else if (untilDate) { - q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); + q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.gen(untilDate) }); q.orderBy(`${q.alias}.id`, 'DESC'); } else { q.orderBy(`${q.alias}.id`, 'DESC'); diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index e7bbd44926..49b465a0f8 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -153,8 +153,7 @@ export class ReactionService { } const record: MiNoteReaction = { - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), noteId: note.id, userId: user.id, reaction, diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 7171bf84c5..d40cd080c7 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -54,7 +54,7 @@ export class RelayService { @bindThis public async addRelay(inbox: string): Promise { const relay = await this.relaysRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), inbox, status: 'requesting', }).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 2c3547e4ac..d18fb240f7 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -125,7 +125,6 @@ export class RoleService implements OnApplicationShutdown { if (cached) { cached.push({ ...body, - createdAt: new Date(body.createdAt), updatedAt: new Date(body.updatedAt), lastUsedAt: new Date(body.lastUsedAt), }); @@ -139,7 +138,6 @@ export class RoleService implements OnApplicationShutdown { if (i > -1) { cached[i] = { ...body, - createdAt: new Date(body.createdAt), updatedAt: new Date(body.updatedAt), lastUsedAt: new Date(body.lastUsedAt), }; @@ -159,7 +157,6 @@ export class RoleService implements OnApplicationShutdown { if (cached) { cached.push({ ...body, - createdAt: new Date(body.createdAt), expiresAt: body.expiresAt ? new Date(body.expiresAt) : null, }); } @@ -198,10 +195,10 @@ export class RoleService implements OnApplicationShutdown { return this.userEntityService.isRemoteUser(user); } case 'createdLessThan': { - return user.createdAt.getTime() > (Date.now() - (value.sec * 1000)); + return this.idService.parse(user.id).date.getTime() > (Date.now() - (value.sec * 1000)); } case 'createdMoreThan': { - return user.createdAt.getTime() < (Date.now() - (value.sec * 1000)); + return this.idService.parse(user.id).date.getTime() < (Date.now() - (value.sec * 1000)); } case 'followersLessThanOrEq': { return user.followersCount <= value.value; @@ -382,7 +379,7 @@ export class RoleService implements OnApplicationShutdown { @bindThis public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise { - const now = new Date(); + const now = Date.now(); const role = await this.rolesRepository.findOneByOrFail({ id: roleId }); @@ -392,7 +389,7 @@ export class RoleService implements OnApplicationShutdown { }); if (existing) { - if (existing.expiresAt && (existing.expiresAt.getTime() < now.getTime())) { + if (existing.expiresAt && (existing.expiresAt.getTime() < now)) { await this.roleAssignmentsRepository.delete({ roleId: roleId, userId: userId, @@ -403,8 +400,7 @@ export class RoleService implements OnApplicationShutdown { } const created = await this.roleAssignmentsRepository.insert({ - id: this.idService.genId(), - createdAt: now, + id: this.idService.gen(now), expiresAt: expiresAt, roleId: roleId, userId: userId, @@ -485,8 +481,7 @@ export class RoleService implements OnApplicationShutdown { public async create(values: Partial, moderator?: MiUser): Promise { const date = new Date(); const created = await this.rolesRepository.insert({ - id: this.idService.genId(), - createdAt: date, + id: this.idService.gen(date.getTime()), updatedAt: date, lastUsedAt: date, name: values.name, diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 3ef321dd32..b6d2bcabc8 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -131,7 +131,7 @@ export class SearchService { await this.meilisearchNoteIndex?.addDocuments([{ id: note.id, - createdAt: note.createdAt.getTime(), + createdAt: this.idService.parse(note.id).date.getTime(), userId: note.userId, userHost: note.userHost, channelId: note.channelId, diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index dfec0cfcfe..b9e3ded46f 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -120,8 +120,7 @@ export class SignupService { if (exist) throw new Error(' the username is already used'); account = await transactionalEntityManager.save(new MiUser({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), username: username, usernameLower: username.toLowerCase(), host: this.utilityService.toPunyNullable(host), diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 087dfd9214..39b19325c3 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -68,8 +68,7 @@ export class UserBlockingService implements OnModuleInit { ]); const blocking = { - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), blocker, blockerId: blocker.id, blockee, diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index beffcc2e9c..f6d0c3a6d5 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -196,8 +196,7 @@ export class UserFollowingService implements OnModuleInit { let alreadyFollowed = false as boolean; await this.followingsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), followerId: follower.id, followeeId: followee.id, @@ -465,8 +464,7 @@ export class UserFollowingService implements OnModuleInit { if (blocked) throw new Error('blocked'); const followRequest = await this.followRequestsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), followerId: follower.id, followeeId: followee.id, requestId, diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 5b4e7a711e..702c731fc3 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -93,8 +93,7 @@ export class UserListService implements OnApplicationShutdown { } await this.userListMembershipsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: target.id, userListId: list.id, userListUserId: list.userId, diff --git a/packages/backend/src/core/UserMutingService.ts b/packages/backend/src/core/UserMutingService.ts index 2387c9d648..397e6bdd5d 100644 --- a/packages/backend/src/core/UserMutingService.ts +++ b/packages/backend/src/core/UserMutingService.ts @@ -26,8 +26,7 @@ export class UserMutingService { @bindThis public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise { await this.mutingsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), expiresAt: expiresAt ?? null, muterId: user.id, muteeId: target.id, diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index ff70f7bc0c..930e6ef64a 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -51,7 +51,6 @@ export class WebhookService implements OnApplicationShutdown { if (body.active) { this.webhooks.push({ ...body, - createdAt: new Date(body.createdAt), latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, }); } @@ -62,13 +61,11 @@ export class WebhookService implements OnApplicationShutdown { if (i > -1) { this.webhooks[i] = { ...body, - createdAt: new Date(body.createdAt), latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, }; } else { this.webhooks.push({ ...body, - createdAt: new Date(body.createdAt), latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, }); } diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index b921ee7454..8d5d34d40b 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -514,8 +514,7 @@ export class ApInboxService { if (users.length < 1) return 'skip'; await this.abuseUserReportsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), targetUserId: users[0].id, targetUserHost: users[0].host, reporterId: actor.id, diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 7a9d2e21d8..e29bc1d096 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -27,6 +27,7 @@ import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFil import { bindThis } from '@/decorators.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { isNotNull } from '@/misc/is-not-null.js'; +import { IdService } from '@/core/IdService.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; @@ -59,6 +60,7 @@ export class ApRendererService { private userKeypairService: UserKeypairService, private apMfmService: ApMfmService, private mfmService: MfmService, + private idService: IdService, ) { } @@ -105,7 +107,7 @@ export class ApRendererService { id: `${this.config.url}/notes/${note.id}/activity`, actor: this.userEntityService.genLocalUserUri(note.userId), type: 'Announce', - published: note.createdAt.toISOString(), + published: this.idService.parse(note.id).date.toISOString(), to, cc, object, @@ -137,7 +139,7 @@ export class ApRendererService { id: `${this.config.url}/notes/${note.id}/activity`, actor: this.userEntityService.genLocalUserUri(note.userId), type: 'Create', - published: note.createdAt.toISOString(), + published: this.idService.parse(note.id).date.toISOString(), object, }; @@ -437,7 +439,7 @@ export class ApRendererService { }, _misskey_quote: quote, quoteUrl: quote, - published: note.createdAt.toISOString(), + published: this.idService.parse(note.id).date.toISOString(), to, cc, inReplyTo, diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 573dff5b91..1979cdda9c 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -386,7 +386,7 @@ export class ApNoteService { this.logger.info(`register emoji host=${host}, name=${name}`); return await this.emojisRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), host, name, uri: tag.id, diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index ea64883395..47f8d7313e 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -295,10 +295,9 @@ export class ApPersonService implements OnModuleInit { // Start transaction await this.db.transaction(async transactionalEntityManager => { user = await transactionalEntityManager.save(new MiUser({ - id: this.idService.genId(), + id: this.idService.gen(), avatarId: null, bannerId: null, - createdAt: new Date(), lastFetchedAt: new Date(), name: truncate(person.name, nameLength), isLocked: person.manuallyApprovesFollowers, @@ -607,8 +606,7 @@ export class ApPersonService implements OnModuleInit { for (const note of featuredNotes.filter((note): note is MiNote => note != null)) { td -= 1000; transactionalEntityManager.insert(MiUserNotePining, { - id: this.idService.genId(new Date(Date.now() + td)), - createdAt: new Date(), + id: this.idService.gen(Date.now() + td), userId: user.id, noteId: note.id, }); diff --git a/packages/backend/src/core/chart/charts/active-users.ts b/packages/backend/src/core/chart/charts/active-users.ts index 55da1469e5..f0918e059c 100644 --- a/packages/backend/src/core/chart/charts/active-users.ts +++ b/packages/backend/src/core/chart/charts/active-users.ts @@ -9,6 +9,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import type { MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/active-users.js'; @@ -29,6 +30,7 @@ export default class ActiveUsersChart extends Chart { // eslint-d private appLockService: AppLockService, private chartLoggerService: ChartLoggerService, + private idService: IdService, ) { super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema); } @@ -42,20 +44,21 @@ export default class ActiveUsersChart extends Chart { // eslint-d } @bindThis - public async read(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise { + public async read(user: { id: MiUser['id'], host: null }): Promise { + const createdAt = this.idService.parse(user.id).date; await this.commit({ 'read': [user.id], - 'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [], - 'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [], - 'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [], - 'registeredOutsideWeek': (Date.now() - user.createdAt.getTime() > week) ? [user.id] : [], - 'registeredOutsideMonth': (Date.now() - user.createdAt.getTime() > month) ? [user.id] : [], - 'registeredOutsideYear': (Date.now() - user.createdAt.getTime() > year) ? [user.id] : [], + 'registeredWithinWeek': (Date.now() - createdAt.getTime() < week) ? [user.id] : [], + 'registeredWithinMonth': (Date.now() - createdAt.getTime() < month) ? [user.id] : [], + 'registeredWithinYear': (Date.now() - createdAt.getTime() < year) ? [user.id] : [], + 'registeredOutsideWeek': (Date.now() - createdAt.getTime() > week) ? [user.id] : [], + 'registeredOutsideMonth': (Date.now() - createdAt.getTime() > month) ? [user.id] : [], + 'registeredOutsideYear': (Date.now() - createdAt.getTime() > year) ? [user.id] : [], }); } @bindThis - public async write(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise { + public async write(user: { id: MiUser['id'], host: null }): Promise { await this.commit({ 'write': [user.id], }); diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 0e65a10d26..97de891ece 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -9,6 +9,7 @@ import type { AbuseUserReportsRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -18,6 +19,7 @@ export class AbuseUserReportEntityService { private abuseUserReportsRepository: AbuseUserReportsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -29,7 +31,7 @@ export class AbuseUserReportEntityService { return await awaitAll({ id: report.id, - createdAt: report.createdAt.toISOString(), + createdAt: this.idService.parse(report.id).date.toISOString(), comment: report.comment, resolved: report.resolved, reporterId: report.reporterId, diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index ed108f2ce5..a9e504d374 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -9,12 +9,15 @@ import type { AntennasRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import type { MiAntenna } from '@/models/Antenna.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; @Injectable() export class AntennaEntityService { constructor( @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, + + private idService: IdService, ) { } @@ -26,7 +29,7 @@ export class AntennaEntityService { return { id: antenna.id, - createdAt: antenna.createdAt.toISOString(), + createdAt: this.idService.parse(antenna.id).date.toISOString(), name: antenna.name, keywords: antenna.keywords, excludeKeywords: antenna.excludeKeywords, diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts index 44466e24e8..b4760346b7 100644 --- a/packages/backend/src/core/entities/BlockingEntityService.ts +++ b/packages/backend/src/core/entities/BlockingEntityService.ts @@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js'; import type { MiBlocking } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -20,6 +21,7 @@ export class BlockingEntityService { private blockingsRepository: BlockingsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -32,7 +34,7 @@ export class BlockingEntityService { return await awaitAll({ id: blocking.id, - createdAt: blocking.createdAt.toISOString(), + createdAt: this.idService.parse(blocking.id).date.toISOString(), blockeeId: blocking.blockeeId, blockee: this.userEntityService.pack(blocking.blockeeId, me, { detail: true, diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index 094de4d2d5..dd72953c7d 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -11,6 +11,7 @@ import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import type { MiChannel } from '@/models/Channel.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; import { In } from 'typeorm'; @@ -38,6 +39,7 @@ export class ChannelEntityService { private noteEntityService: NoteEntityService, private driveFileEntityService: DriveFileEntityService, + private idService: IdService, ) { } @@ -81,7 +83,7 @@ export class ChannelEntityService { return { id: channel.id, - createdAt: channel.createdAt.toISOString(), + createdAt: this.idService.parse(channel.id).date.toISOString(), lastNotedAt: channel.lastNotedAt ? channel.lastNotedAt.toISOString() : null, name: channel.name, description: channel.description, diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index e141db03f1..96422894fd 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js'; import type { } from '@/models/Blocking.js'; import type { MiClip } from '@/models/Clip.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -23,6 +24,7 @@ export class ClipEntityService { private clipFavoritesRepository: ClipFavoritesRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -36,7 +38,7 @@ export class ClipEntityService { return await awaitAll({ id: clip.id, - createdAt: clip.createdAt.toISOString(), + createdAt: this.idService.parse(clip.id).date.toISOString(), lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null, userId: clip.userId, user: this.userEntityService.pack(clip.user ?? clip.userId), diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 23273b0413..5148b2ca9e 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -17,6 +17,7 @@ import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { isNotNull } from '@/misc/is-not-null.js'; +import { IdService } from '@/core/IdService.js'; import { UtilityService } from '../UtilityService.js'; import { VideoProcessingService } from '../VideoProcessingService.js'; import { UserEntityService } from './UserEntityService.js'; @@ -44,6 +45,7 @@ export class DriveFileEntityService { private utilityService: UtilityService, private driveFolderEntityService: DriveFolderEntityService, private videoProcessingService: VideoProcessingService, + private idService: IdService, ) { } @@ -196,7 +198,7 @@ export class DriveFileEntityService { return await awaitAll>({ id: file.id, - createdAt: file.createdAt.toISOString(), + createdAt: this.idService.parse(file.id).date.toISOString(), name: file.name, type: file.type, md5: file.md5, @@ -231,7 +233,7 @@ export class DriveFileEntityService { return await awaitAll>({ id: file.id, - createdAt: file.createdAt.toISOString(), + createdAt: this.idService.parse(file.id).date.toISOString(), name: file.name, type: file.type, md5: file.md5, diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index 55014284bd..8fa78154b9 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js'; import type { } from '@/models/Blocking.js'; import type { MiDriveFolder } from '@/models/DriveFolder.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; @Injectable() export class DriveFolderEntityService { @@ -20,6 +21,8 @@ export class DriveFolderEntityService { @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, + + private idService: IdService, ) { } @@ -38,7 +41,7 @@ export class DriveFolderEntityService { return await awaitAll({ id: folder.id, - createdAt: folder.createdAt.toISOString(), + createdAt: this.idService.parse(folder.id).date.toISOString(), name: folder.name, parentId: folder.parentId, diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts index 4701cddcba..dc335d9975 100644 --- a/packages/backend/src/core/entities/FlashEntityService.ts +++ b/packages/backend/src/core/entities/FlashEntityService.ts @@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import type { MiFlash } from '@/models/Flash.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -24,6 +25,7 @@ export class FlashEntityService { private flashLikesRepository: FlashLikesRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -37,7 +39,7 @@ export class FlashEntityService { return await awaitAll({ id: flash.id, - createdAt: flash.createdAt.toISOString(), + createdAt: this.idService.parse(flash.id).date.toISOString(), updatedAt: flash.updatedAt.toISOString(), userId: flash.userId, user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } すると無限ループするので注意 diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts index 9f6eb51e8c..52aa979677 100644 --- a/packages/backend/src/core/entities/FollowingEntityService.ts +++ b/packages/backend/src/core/entities/FollowingEntityService.ts @@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import type { MiFollowing } from '@/models/Following.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; type LocalFollowerFollowing = MiFollowing & { @@ -45,6 +46,7 @@ export class FollowingEntityService { private followingsRepository: FollowingsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -83,7 +85,7 @@ export class FollowingEntityService { return await awaitAll({ id: following.id, - createdAt: following.createdAt.toISOString(), + createdAt: this.idService.parse(following.id).date.toISOString(), followeeId: following.followeeId, followerId: following.followerId, followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, { diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index bbaf70f0fd..d7b960e0d9 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import type { MiGalleryPost } from '@/models/GalleryPost.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -26,6 +27,7 @@ export class GalleryPostEntityService { private userEntityService: UserEntityService, private driveFileEntityService: DriveFileEntityService, + private idService: IdService, ) { } @@ -39,7 +41,7 @@ export class GalleryPostEntityService { return await awaitAll({ id: post.id, - createdAt: post.createdAt.toISOString(), + createdAt: this.idService.parse(post.id).date.toISOString(), updatedAt: post.updatedAt.toISOString(), userId: post.userId, user: this.userEntityService.pack(post.user ?? post.userId, me), diff --git a/packages/backend/src/core/entities/InviteCodeEntityService.ts b/packages/backend/src/core/entities/InviteCodeEntityService.ts index 914eaafe68..0f15fb5ab2 100644 --- a/packages/backend/src/core/entities/InviteCodeEntityService.ts +++ b/packages/backend/src/core/entities/InviteCodeEntityService.ts @@ -11,6 +11,7 @@ import type { Packed } from '@/misc/json-schema.js'; import type { MiUser } from '@/models/User.js'; import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -20,6 +21,7 @@ export class InviteCodeEntityService { private registrationTicketsRepository: RegistrationTicketsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -39,7 +41,7 @@ export class InviteCodeEntityService { id: target.id, code: target.code, expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null, - createdAt: target.createdAt.toISOString(), + createdAt: this.idService.parse(target.id).date.toISOString(), createdBy: target.createdBy ? await this.userEntityService.pack(target.createdBy, me) : null, usedBy: target.usedBy ? await this.userEntityService.pack(target.usedBy, me) : null, usedAt: target.usedAt ? target.usedAt.toISOString() : null, diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index 83b024d83b..6729ca2671 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -10,6 +10,7 @@ import { awaitAll } from '@/misc/prelude/await-all.js'; import type { } from '@/models/Blocking.js'; import type { MiModerationLog } from '@/models/ModerationLog.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -19,6 +20,7 @@ export class ModerationLogEntityService { private moderationLogsRepository: ModerationLogsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -30,7 +32,7 @@ export class ModerationLogEntityService { return await awaitAll({ id: log.id, - createdAt: log.createdAt.toISOString(), + createdAt: this.idService.parse(log.id).date.toISOString(), type: log.type, info: log.info, userId: log.userId, diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts index e3d5d2e211..9d672169ba 100644 --- a/packages/backend/src/core/entities/MutingEntityService.ts +++ b/packages/backend/src/core/entities/MutingEntityService.ts @@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import type { MiMuting } from '@/models/Muting.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -21,6 +22,7 @@ export class MutingEntityService { private mutingsRepository: MutingsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -33,7 +35,7 @@ export class MutingEntityService { return await awaitAll({ id: muting.id, - createdAt: muting.createdAt.toISOString(), + createdAt: this.idService.parse(muting.id).date.toISOString(), expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null, muteeId: muting.muteeId, mutee: this.userEntityService.pack(muting.muteeId, me, { diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index f871ba50a8..674594296c 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -310,7 +310,7 @@ export class NoteEntityService implements OnModuleInit { const packed: Packed<'Note'> = await awaitAll({ id: note.id, - createdAt: note.createdAt.toISOString(), + createdAt: this.idService.parse(note.id).date.toISOString(), userId: note.userId, user: this.userEntityService.pack(note.user ?? note.userId, me, { detail: false, @@ -386,7 +386,8 @@ export class NoteEntityService implements OnModuleInit { if (meId) { const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない - const targets = [...notes.filter(n => n.createdAt.getTime() + 2000 < Date.now()).map(n => n.id), ...renoteIds]; + const oldId = this.idService.gen(Date.now() - 2000); + const targets = [...notes.filter(n => n.id < oldId).map(n => n.id), ...renoteIds]; const myReactions = await this.noteReactionsRepository.findBy({ userId: meId, noteId: In(targets), diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index 808c8c9f69..1c9aed413f 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -10,6 +10,7 @@ import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import type { MiNoteFavorite } from '@/models/NoteFavorite.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { NoteEntityService } from './NoteEntityService.js'; @Injectable() @@ -19,6 +20,7 @@ export class NoteFavoriteEntityService { private noteFavoritesRepository: NoteFavoritesRepository, private noteEntityService: NoteEntityService, + private idService: IdService, ) { } @@ -31,7 +33,7 @@ export class NoteFavoriteEntityService { return { id: favorite.id, - createdAt: favorite.createdAt.toISOString(), + createdAt: this.idService.parse(favorite.id).date.toISOString(), noteId: favorite.noteId, note: await this.noteEntityService.pack(favorite.note ?? favorite.noteId, me), }; diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index 9701f37fdb..f4aba3e543 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; import type { NoteReactionsRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; @@ -22,6 +23,7 @@ export class NoteReactionEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; private reactionService: ReactionService; + private idService: IdService; constructor( private moduleRef: ModuleRef, @@ -32,6 +34,7 @@ export class NoteReactionEntityService implements OnModuleInit { //private userEntityService: UserEntityService, //private noteEntityService: NoteEntityService, //private reactionService: ReactionService, + //private idService: IdService, ) { } @@ -39,6 +42,7 @@ export class NoteReactionEntityService implements OnModuleInit { this.userEntityService = this.moduleRef.get('UserEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); this.reactionService = this.moduleRef.get('ReactionService'); + this.idService = this.moduleRef.get('IdService'); } @bindThis @@ -57,7 +61,7 @@ export class NoteReactionEntityService implements OnModuleInit { return { id: reaction.id, - createdAt: reaction.createdAt.toISOString(), + createdAt: this.idService.parse(reaction.id).date.toISOString(), user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me), type: this.reactionService.convertLegacyReaction(reaction.reaction), ...(opts.withNote ? { diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index e3a1e19ddd..f39ef949db 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -13,6 +13,7 @@ import type { MiUser } from '@/models/User.js'; import type { MiPage } from '@/models/Page.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -30,6 +31,7 @@ export class PageEntityService { private userEntityService: UserEntityService, private driveFileEntityService: DriveFileEntityService, + private idService: IdService, ) { } @@ -85,7 +87,7 @@ export class PageEntityService { return await awaitAll({ id: page.id, - createdAt: page.createdAt.toISOString(), + createdAt: this.idService.parse(page.id).date.toISOString(), updatedAt: page.updatedAt.toISOString(), userId: page.userId, user: this.userEntityService.pack(page.user ?? page.userId, me), // { detail: true } すると無限ループするので注意 diff --git a/packages/backend/src/core/entities/RenoteMutingEntityService.ts b/packages/backend/src/core/entities/RenoteMutingEntityService.ts index 7111fab08a..3f9dc9180a 100644 --- a/packages/backend/src/core/entities/RenoteMutingEntityService.ts +++ b/packages/backend/src/core/entities/RenoteMutingEntityService.ts @@ -12,6 +12,7 @@ import type { } from '@/models/Blocking.js'; import type { MiUser } from '@/models/User.js'; import type { MiRenoteMuting } from '@/models/RenoteMuting.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -21,6 +22,7 @@ export class RenoteMutingEntityService { private renoteMutingsRepository: RenoteMutingsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -33,7 +35,7 @@ export class RenoteMutingEntityService { return await awaitAll({ id: muting.id, - createdAt: muting.createdAt.toISOString(), + createdAt: this.idService.parse(muting.id).date.toISOString(), muteeId: muting.muteeId, mutee: this.userEntityService.pack(muting.muteeId, me, { detail: true, diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 79375a7008..5563f9a1ac 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -12,6 +12,7 @@ import type { MiUser } from '@/models/User.js'; import type { MiRole } from '@/models/Role.js'; import { bindThis } from '@/decorators.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; @Injectable() export class RoleEntityService { @@ -21,6 +22,8 @@ export class RoleEntityService { @Inject(DI.roleAssignmentsRepository) private roleAssignmentsRepository: RoleAssignmentsRepository, + + private idService: IdService, ) { } @@ -51,7 +54,7 @@ export class RoleEntityService { return await awaitAll({ id: role.id, - createdAt: role.createdAt.toISOString(), + createdAt: this.idService.parse(role.id).date.toISOString(), updatedAt: role.updatedAt.toISOString(), name: role.name, description: role.description, diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index ee67634da5..212994feef 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -20,6 +20,7 @@ import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import { IdService } from '@/core/IdService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { AnnouncementService } from '../AnnouncementService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js'; @@ -60,6 +61,7 @@ export class UserEntityService implements OnModuleInit { private announcementService: AnnouncementService; private roleService: RoleService; private federatedInstanceService: FederatedInstanceService; + private idService: IdService; constructor( private moduleRef: ModuleRef, @@ -111,13 +113,6 @@ export class UserEntityService implements OnModuleInit { @Inject(DI.userMemosRepository) private userMemosRepository: UserMemoRepository, - - //private noteEntityService: NoteEntityService, - //private driveFileEntityService: DriveFileEntityService, - //private pageEntityService: PageEntityService, - //private customEmojiService: CustomEmojiService, - //private antennaService: AntennaService, - //private roleService: RoleService, ) { } @@ -130,6 +125,7 @@ export class UserEntityService implements OnModuleInit { this.announcementService = this.moduleRef.get('AnnouncementService'); this.roleService = this.moduleRef.get('RoleService'); this.federatedInstanceService = this.moduleRef.get('FederatedInstanceService'); + this.idService = this.moduleRef.get('IdService'); } //#region Validators @@ -364,7 +360,7 @@ export class UserEntityService implements OnModuleInit { ? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null))) .then(xs => xs.length === 0 ? null : xs.filter(x => x != null) as string[]) : null, - createdAt: user.createdAt.toISOString(), + createdAt: this.idService.parse(user.id).date.toISOString(), updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, bannerUrl: user.bannerUrl, diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index 06b6e852b1..31ab7293da 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -10,6 +10,7 @@ import type { Packed } from '@/misc/json-schema.js'; import type { } from '@/models/Blocking.js'; import type { MiUserList } from '@/models/UserList.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -22,6 +23,7 @@ export class UserListEntityService { private userListMembershipsRepository: UserListMembershipsRepository, private userEntityService: UserEntityService, + private idService: IdService, ) { } @@ -37,7 +39,7 @@ export class UserListEntityService { return { id: userList.id, - createdAt: userList.createdAt.toISOString(), + createdAt: this.idService.parse(userList.id).date.toISOString(), name: userList.name, userIds: users.map(x => x.userId), isPublic: userList.isPublic, @@ -50,7 +52,7 @@ export class UserListEntityService { ) { return Promise.all(memberships.map(async x => ({ id: x.id, - createdAt: x.createdAt.toISOString(), + createdAt: this.idService.parse(x.id).date.toISOString(), userId: x.userId, user: await this.userEntityService.pack(x.userId), withReplies: x.withReplies, diff --git a/packages/backend/src/misc/id/aid.ts b/packages/backend/src/misc/id/aid.ts index ec8aa849c9..e7b59f262b 100644 --- a/packages/backend/src/misc/id/aid.ts +++ b/packages/backend/src/misc/id/aid.ts @@ -24,8 +24,7 @@ function getNoise(): string { return counter.toString(36).padStart(2, '0').slice(-2); } -export function genAid(date: Date): string { - const t = date.getTime(); +export function genAid(t: number): string { if (isNaN(t)) throw new Error('Failed to create AID: Invalid Date'); counter++; return getTime(t) + getNoise(); diff --git a/packages/backend/src/misc/id/aidx.ts b/packages/backend/src/misc/id/aidx.ts index 5b031ea4c0..bed223225a 100644 --- a/packages/backend/src/misc/id/aidx.ts +++ b/packages/backend/src/misc/id/aidx.ts @@ -31,8 +31,7 @@ function getNoise(): string { return counter.toString(36).padStart(NOISE_LENGTH, '0').slice(-NOISE_LENGTH); } -export function genAidx(date: Date): string { - const t = date.getTime(); +export function genAidx(t: number): string { if (isNaN(t)) throw new Error('Failed to create AIDX: Invalid Date'); counter++; return getTime(t) + nodeId + getNoise(); diff --git a/packages/backend/src/misc/id/meid.ts b/packages/backend/src/misc/id/meid.ts index 82cda37237..366738de05 100644 --- a/packages/backend/src/misc/id/meid.ts +++ b/packages/backend/src/misc/id/meid.ts @@ -29,8 +29,8 @@ function getRandom() { return str; } -export function genMeid(date: Date): string { - return getTime(date.getTime()) + getRandom(); +export function genMeid(t: number): string { + return getTime(t) + getRandom(); } export function parseMeid(id: string): { date: Date; } { diff --git a/packages/backend/src/misc/id/meidg.ts b/packages/backend/src/misc/id/meidg.ts index fba7156718..426a46970b 100644 --- a/packages/backend/src/misc/id/meidg.ts +++ b/packages/backend/src/misc/id/meidg.ts @@ -29,8 +29,8 @@ function getRandom() { return str; } -export function genMeidg(date: Date): string { - return 'g' + getTime(date.getTime()) + getRandom(); +export function genMeidg(t: number): string { + return 'g' + getTime(t) + getRandom(); } export function parseMeidg(id: string): { date: Date; } { diff --git a/packages/backend/src/misc/id/object-id.ts b/packages/backend/src/misc/id/object-id.ts index e3b6e8e433..49bd9591c0 100644 --- a/packages/backend/src/misc/id/object-id.ts +++ b/packages/backend/src/misc/id/object-id.ts @@ -29,8 +29,8 @@ function getRandom() { return str; } -export function genObjectId(date: Date): string { - return getTime(date.getTime()) + getRandom(); +export function genObjectId(t: number): string { + return getTime(t) + getRandom(); } export function parseObjectId(id: string): { date: Date; } { diff --git a/packages/backend/src/models/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts index 2551af7cb6..593c44f66b 100644 --- a/packages/backend/src/models/AbuseUserReport.ts +++ b/packages/backend/src/models/AbuseUserReport.ts @@ -12,12 +12,6 @@ export class MiAbuseUserReport { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the AbuseUserReport.', - }) - public createdAt: Date; - @Index() @Column(id()) public targetUserId: MiUser['id']; diff --git a/packages/backend/src/models/AccessToken.ts b/packages/backend/src/models/AccessToken.ts index 5a6269a729..452711eb8c 100644 --- a/packages/backend/src/models/AccessToken.ts +++ b/packages/backend/src/models/AccessToken.ts @@ -13,11 +13,6 @@ export class MiAccessToken { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the AccessToken.', - }) - public createdAt: Date; - @Column('timestamp with time zone', { nullable: true, }) diff --git a/packages/backend/src/models/Ad.ts b/packages/backend/src/models/Ad.ts index 6dfc9cb30e..b1d7d7d79e 100644 --- a/packages/backend/src/models/Ad.ts +++ b/packages/backend/src/models/Ad.ts @@ -11,12 +11,6 @@ export class MiAd { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Ad.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { comment: 'The expired date of the Ad.', diff --git a/packages/backend/src/models/Announcement.ts b/packages/backend/src/models/Announcement.ts index 34b092a8d4..05d5a086f1 100644 --- a/packages/backend/src/models/Announcement.ts +++ b/packages/backend/src/models/Announcement.ts @@ -12,12 +12,6 @@ export class MiAnnouncement { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Announcement.', - }) - public createdAt: Date; - @Column('timestamp with time zone', { comment: 'The updated date of the Announcement.', nullable: true, diff --git a/packages/backend/src/models/AnnouncementRead.ts b/packages/backend/src/models/AnnouncementRead.ts index 3d6ec5652c..db09e65f50 100644 --- a/packages/backend/src/models/AnnouncementRead.ts +++ b/packages/backend/src/models/AnnouncementRead.ts @@ -14,11 +14,6 @@ export class MiAnnouncementRead { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the AnnouncementRead.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts index dc398b6dd2..0bc0084fc5 100644 --- a/packages/backend/src/models/Antenna.ts +++ b/packages/backend/src/models/Antenna.ts @@ -13,11 +13,6 @@ export class MiAntenna { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Antenna.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone') public lastUsedAt: Date; diff --git a/packages/backend/src/models/App.ts b/packages/backend/src/models/App.ts index c599ef8be0..5c56a224a2 100644 --- a/packages/backend/src/models/App.ts +++ b/packages/backend/src/models/App.ts @@ -12,12 +12,6 @@ export class MiApp { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the App.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/AuthSession.ts b/packages/backend/src/models/AuthSession.ts index d9de6b6979..81bed21211 100644 --- a/packages/backend/src/models/AuthSession.ts +++ b/packages/backend/src/models/AuthSession.ts @@ -13,11 +13,6 @@ export class MiAuthSession { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the AuthSession.', - }) - public createdAt: Date; - @Index() @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/Blocking.ts b/packages/backend/src/models/Blocking.ts index 1e3dd3a644..9bf7a63b6e 100644 --- a/packages/backend/src/models/Blocking.ts +++ b/packages/backend/src/models/Blocking.ts @@ -13,12 +13,6 @@ export class MiBlocking { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Blocking.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/Channel.ts b/packages/backend/src/models/Channel.ts index ae3886a657..f90f8c03d8 100644 --- a/packages/backend/src/models/Channel.ts +++ b/packages/backend/src/models/Channel.ts @@ -13,12 +13,6 @@ export class MiChannel { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Channel.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { nullable: true, diff --git a/packages/backend/src/models/ChannelFavorite.ts b/packages/backend/src/models/ChannelFavorite.ts index ab74aa5530..fc25ffe260 100644 --- a/packages/backend/src/models/ChannelFavorite.ts +++ b/packages/backend/src/models/ChannelFavorite.ts @@ -14,12 +14,6 @@ export class MiChannelFavorite { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the ChannelFavorite.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/ChannelFollowing.ts b/packages/backend/src/models/ChannelFollowing.ts index c62a95332a..4dd391a082 100644 --- a/packages/backend/src/models/ChannelFollowing.ts +++ b/packages/backend/src/models/ChannelFollowing.ts @@ -14,12 +14,6 @@ export class MiChannelFollowing { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the ChannelFollowing.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/Clip.ts b/packages/backend/src/models/Clip.ts index c60b2964e0..2483b0925a 100644 --- a/packages/backend/src/models/Clip.ts +++ b/packages/backend/src/models/Clip.ts @@ -12,11 +12,6 @@ export class MiClip { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Clip.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { nullable: true, diff --git a/packages/backend/src/models/ClipFavorite.ts b/packages/backend/src/models/ClipFavorite.ts index 054764389b..aa949b3ea8 100644 --- a/packages/backend/src/models/ClipFavorite.ts +++ b/packages/backend/src/models/ClipFavorite.ts @@ -14,9 +14,6 @@ export class MiClipFavorite { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/DriveFile.ts b/packages/backend/src/models/DriveFile.ts index c12f0e0f02..24e6be9c90 100644 --- a/packages/backend/src/models/DriveFile.ts +++ b/packages/backend/src/models/DriveFile.ts @@ -14,12 +14,6 @@ export class MiDriveFile { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the DriveFile.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/DriveFolder.ts b/packages/backend/src/models/DriveFolder.ts index 3e049136bd..18f6d17709 100644 --- a/packages/backend/src/models/DriveFolder.ts +++ b/packages/backend/src/models/DriveFolder.ts @@ -12,12 +12,6 @@ export class MiDriveFolder { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the DriveFolder.', - }) - public createdAt: Date; - @Column('varchar', { length: 128, comment: 'The name of the DriveFolder.', diff --git a/packages/backend/src/models/Flash.ts b/packages/backend/src/models/Flash.ts index 185063029d..ac880843b0 100644 --- a/packages/backend/src/models/Flash.ts +++ b/packages/backend/src/models/Flash.ts @@ -12,12 +12,6 @@ export class MiFlash { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Flash.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { comment: 'The updated date of the Flash.', diff --git a/packages/backend/src/models/FlashLike.ts b/packages/backend/src/models/FlashLike.ts index 7c66010ae6..ad7f4966b4 100644 --- a/packages/backend/src/models/FlashLike.ts +++ b/packages/backend/src/models/FlashLike.ts @@ -14,9 +14,6 @@ export class MiFlashLike { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/FollowRequest.ts b/packages/backend/src/models/FollowRequest.ts index 769b9a6cb5..1e907f3d68 100644 --- a/packages/backend/src/models/FollowRequest.ts +++ b/packages/backend/src/models/FollowRequest.ts @@ -13,11 +13,6 @@ export class MiFollowRequest { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the FollowRequest.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts index 607538b1e7..e320911a1d 100644 --- a/packages/backend/src/models/Following.ts +++ b/packages/backend/src/models/Following.ts @@ -14,12 +14,6 @@ export class MiFollowing { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Following.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/GalleryLike.ts b/packages/backend/src/models/GalleryLike.ts index b5f71764aa..84d4ce9c3e 100644 --- a/packages/backend/src/models/GalleryLike.ts +++ b/packages/backend/src/models/GalleryLike.ts @@ -14,9 +14,6 @@ export class MiGalleryLike { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/GalleryPost.ts b/packages/backend/src/models/GalleryPost.ts index 4c6063f32b..b72220caf9 100644 --- a/packages/backend/src/models/GalleryPost.ts +++ b/packages/backend/src/models/GalleryPost.ts @@ -13,12 +13,6 @@ export class MiGalleryPost { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the GalleryPost.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { comment: 'The updated date of the GalleryPost.', diff --git a/packages/backend/src/models/ModerationLog.ts b/packages/backend/src/models/ModerationLog.ts index a12b6ab614..71b33c3e47 100644 --- a/packages/backend/src/models/ModerationLog.ts +++ b/packages/backend/src/models/ModerationLog.ts @@ -12,11 +12,6 @@ export class MiModerationLog { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the ModerationLog.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/Muting.ts b/packages/backend/src/models/Muting.ts index 2f06ca8e5e..a528e1e7d7 100644 --- a/packages/backend/src/models/Muting.ts +++ b/packages/backend/src/models/Muting.ts @@ -13,12 +13,6 @@ export class MiMuting { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Muting.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { nullable: true, diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 3e2adf4d82..ac7f57d5d6 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -18,11 +18,6 @@ export class MiNote { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Note.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/NoteFavorite.ts b/packages/backend/src/models/NoteFavorite.ts index 1171684bcf..364eaabd98 100644 --- a/packages/backend/src/models/NoteFavorite.ts +++ b/packages/backend/src/models/NoteFavorite.ts @@ -14,11 +14,6 @@ export class MiNoteFavorite { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the NoteFavorite.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/NoteReaction.ts b/packages/backend/src/models/NoteReaction.ts index 43323f8a43..ee3a447464 100644 --- a/packages/backend/src/models/NoteReaction.ts +++ b/packages/backend/src/models/NoteReaction.ts @@ -14,11 +14,6 @@ export class MiNoteReaction { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the NoteReaction.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/NoteThreadMuting.ts b/packages/backend/src/models/NoteThreadMuting.ts index 2d120e4c25..00311aa570 100644 --- a/packages/backend/src/models/NoteThreadMuting.ts +++ b/packages/backend/src/models/NoteThreadMuting.ts @@ -13,10 +13,6 @@ export class MiNoteThreadMuting { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/Page.ts b/packages/backend/src/models/Page.ts index 3cb986f4ee..9cab875499 100644 --- a/packages/backend/src/models/Page.ts +++ b/packages/backend/src/models/Page.ts @@ -14,12 +14,6 @@ export class MiPage { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Page.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { comment: 'The updated date of the Page.', diff --git a/packages/backend/src/models/PageLike.ts b/packages/backend/src/models/PageLike.ts index 92adf9bcc2..b845f58b7d 100644 --- a/packages/backend/src/models/PageLike.ts +++ b/packages/backend/src/models/PageLike.ts @@ -14,9 +14,6 @@ export class MiPageLike { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/PasswordResetRequest.ts b/packages/backend/src/models/PasswordResetRequest.ts index 79f2e984b8..5be439511f 100644 --- a/packages/backend/src/models/PasswordResetRequest.ts +++ b/packages/backend/src/models/PasswordResetRequest.ts @@ -12,9 +12,6 @@ export class MiPasswordResetRequest { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index({ unique: true }) @Column('varchar', { length: 256, diff --git a/packages/backend/src/models/PollVote.ts b/packages/backend/src/models/PollVote.ts index 37cd55fc18..751be8a32b 100644 --- a/packages/backend/src/models/PollVote.ts +++ b/packages/backend/src/models/PollVote.ts @@ -14,12 +14,6 @@ export class MiPollVote { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the PollVote.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/PromoRead.ts b/packages/backend/src/models/PromoRead.ts index 09ebfc8346..d9f3075416 100644 --- a/packages/backend/src/models/PromoRead.ts +++ b/packages/backend/src/models/PromoRead.ts @@ -14,11 +14,6 @@ export class MiPromoRead { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the PromoRead.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/RegistrationTicket.ts b/packages/backend/src/models/RegistrationTicket.ts index d94f465916..730cedffba 100644 --- a/packages/backend/src/models/RegistrationTicket.ts +++ b/packages/backend/src/models/RegistrationTicket.ts @@ -23,9 +23,6 @@ export class MiRegistrationTicket { }) public expiresAt: Date | null; - @Column('timestamp with time zone') - public createdAt: Date; - @ManyToOne(type => MiUser, { onDelete: 'CASCADE', }) diff --git a/packages/backend/src/models/RegistryItem.ts b/packages/backend/src/models/RegistryItem.ts index fdce57c467..60bdced957 100644 --- a/packages/backend/src/models/RegistryItem.ts +++ b/packages/backend/src/models/RegistryItem.ts @@ -13,11 +13,6 @@ export class MiRegistryItem { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the RegistryItem.', - }) - public createdAt: Date; - @Column('timestamp with time zone', { comment: 'The updated date of the RegistryItem.', }) diff --git a/packages/backend/src/models/RenoteMuting.ts b/packages/backend/src/models/RenoteMuting.ts index d2a36249dc..17df43ea31 100644 --- a/packages/backend/src/models/RenoteMuting.ts +++ b/packages/backend/src/models/RenoteMuting.ts @@ -13,12 +13,6 @@ export class MiRenoteMuting { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Muting.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts index df7541db3d..6976956e13 100644 --- a/packages/backend/src/models/Role.ts +++ b/packages/backend/src/models/Role.ts @@ -89,11 +89,6 @@ export class MiRole { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Role.', - }) - public createdAt: Date; - @Column('timestamp with time zone', { comment: 'The updated date of the Role.', }) diff --git a/packages/backend/src/models/RoleAssignment.ts b/packages/backend/src/models/RoleAssignment.ts index 4e5322c60b..30c7e19f2a 100644 --- a/packages/backend/src/models/RoleAssignment.ts +++ b/packages/backend/src/models/RoleAssignment.ts @@ -14,11 +14,6 @@ export class MiRoleAssignment { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the RoleAssignment.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/Signin.ts b/packages/backend/src/models/Signin.ts index a8b1a45c53..656b44dfe0 100644 --- a/packages/backend/src/models/Signin.ts +++ b/packages/backend/src/models/Signin.ts @@ -12,11 +12,6 @@ export class MiSignin { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Signin.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/SwSubscription.ts b/packages/backend/src/models/SwSubscription.ts index be1e4e3687..f685a8ff3e 100644 --- a/packages/backend/src/models/SwSubscription.ts +++ b/packages/backend/src/models/SwSubscription.ts @@ -12,9 +12,6 @@ export class MiSwSubscription { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 4d961c4290..796d7c8356 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -13,12 +13,6 @@ export class MiUser { @PrimaryColumn(id()) public id: string; - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the User.', - }) - public createdAt: Date; - @Index() @Column('timestamp with time zone', { nullable: true, diff --git a/packages/backend/src/models/UserList.ts b/packages/backend/src/models/UserList.ts index 9af85af97e..7ad15419d7 100644 --- a/packages/backend/src/models/UserList.ts +++ b/packages/backend/src/models/UserList.ts @@ -12,11 +12,6 @@ export class MiUserList { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserList.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/UserListFavorite.ts b/packages/backend/src/models/UserListFavorite.ts index d0b054b932..a18ed9253a 100644 --- a/packages/backend/src/models/UserListFavorite.ts +++ b/packages/backend/src/models/UserListFavorite.ts @@ -14,9 +14,6 @@ export class MiUserListFavorite { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/UserListMembership.ts b/packages/backend/src/models/UserListMembership.ts index f57f9ac33d..fa8287f17a 100644 --- a/packages/backend/src/models/UserListMembership.ts +++ b/packages/backend/src/models/UserListMembership.ts @@ -14,11 +14,6 @@ export class MiUserListMembership { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserListMembership.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/UserNotePining.ts b/packages/backend/src/models/UserNotePining.ts index 1d50a5068e..ae5977aa56 100644 --- a/packages/backend/src/models/UserNotePining.ts +++ b/packages/backend/src/models/UserNotePining.ts @@ -14,11 +14,6 @@ export class MiUserNotePining { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserNotePinings.', - }) - public createdAt: Date; - @Index() @Column(id()) public userId: MiUser['id']; diff --git a/packages/backend/src/models/UserPending.ts b/packages/backend/src/models/UserPending.ts index b15ededa14..8b1f8f617f 100644 --- a/packages/backend/src/models/UserPending.ts +++ b/packages/backend/src/models/UserPending.ts @@ -11,9 +11,6 @@ export class MiUserPending { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') - public createdAt: Date; - @Index({ unique: true }) @Column('varchar', { length: 128, diff --git a/packages/backend/src/models/Webhook.ts b/packages/backend/src/models/Webhook.ts index 5b009c18a6..ec4e13cc76 100644 --- a/packages/backend/src/models/Webhook.ts +++ b/packages/backend/src/models/Webhook.ts @@ -14,11 +14,6 @@ export class MiWebhook { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Antenna.', - }) - public createdAt: Date; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts index 5aac3f19e8..9f49d85c7f 100644 --- a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts +++ b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts @@ -47,13 +47,13 @@ export class AggregateRetentionProcessorService { // 今日登録したユーザーを全て取得 const targetUsers = await this.usersRepository.findBy({ host: IsNull(), - createdAt: MoreThan(new Date(Date.now() - (1000 * 60 * 60 * 24))), + id: MoreThan(this.idService.gen(Date.now() - (1000 * 60 * 60 * 24))), }); const targetUserIds = targetUsers.map(u => u.id); try { await this.retentionAggregationsRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), createdAt: now, updatedAt: now, dateKey, diff --git a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts index 7248c7a649..af2a3434a9 100644 --- a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts @@ -15,6 +15,7 @@ import { createTemp } from '@/misc/create-temp.js'; import type { MiPoll } from '@/models/Poll.js'; import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; import type { DbJobDataWithUser } from '../types.js'; @@ -35,6 +36,7 @@ export class ExportFavoritesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, + private idService: IdService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('export-favorites'); } @@ -99,7 +101,7 @@ export class ExportFavoritesProcessorService { if (favorite.note.hasPoll) { poll = await this.pollsRepository.findOneByOrFail({ noteId: favorite.note.id }); } - const content = JSON.stringify(serialize(favorite, poll)); + const content = JSON.stringify(this.serialize(favorite, poll)); const isFirst = exportedFavoritesCount === 0; await write(isFirst ? content : ',\n' + content); exportedFavoritesCount++; @@ -125,34 +127,34 @@ export class ExportFavoritesProcessorService { cleanup(); } } -} -function serialize(favorite: MiNoteFavorite & { note: MiNote & { user: MiUser } }, poll: MiPoll | null = null): Record { - return { - id: favorite.id, - createdAt: favorite.createdAt, - note: { - id: favorite.note.id, - text: favorite.note.text, - createdAt: favorite.note.createdAt, - fileIds: favorite.note.fileIds, - replyId: favorite.note.replyId, - renoteId: favorite.note.renoteId, - poll: poll, - cw: favorite.note.cw, - visibility: favorite.note.visibility, - visibleUserIds: favorite.note.visibleUserIds, - localOnly: favorite.note.localOnly, - reactionAcceptance: favorite.note.reactionAcceptance, - uri: favorite.note.uri, - url: favorite.note.url, - user: { - id: favorite.note.user.id, - name: favorite.note.user.name, - username: favorite.note.user.username, - host: favorite.note.user.host, - uri: favorite.note.user.uri, + private serialize(favorite: MiNoteFavorite & { note: MiNote & { user: MiUser } }, poll: MiPoll | null = null): Record { + return { + id: favorite.id, + createdAt: this.idService.parse(favorite.id).date.toISOString(), + note: { + id: favorite.note.id, + text: favorite.note.text, + createdAt: this.idService.parse(favorite.note.id).date.toISOString(), + fileIds: favorite.note.fileIds, + replyId: favorite.note.replyId, + renoteId: favorite.note.renoteId, + poll: poll, + cw: favorite.note.cw, + visibility: favorite.note.visibility, + visibleUserIds: favorite.note.visibleUserIds, + localOnly: favorite.note.localOnly, + reactionAcceptance: favorite.note.reactionAcceptance, + uri: favorite.note.uri, + url: favorite.note.url, + user: { + id: favorite.note.user.id, + name: favorite.note.user.name, + username: favorite.note.user.username, + host: favorite.note.user.host, + uri: favorite.note.user.uri, + }, }, - }, - }; + }; + } } diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index e0bc80e190..cd4ccb0b07 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -17,6 +17,7 @@ import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { Packed } from '@/misc/json-schema.js'; +import { IdService } from '@/core/IdService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; import type { DbJobDataWithUser } from '../types.js'; @@ -37,8 +38,8 @@ export class ExportNotesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, - private driveFileEntityService: DriveFileEntityService, + private idService: IdService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('export-notes'); } @@ -103,7 +104,7 @@ export class ExportNotesProcessorService { poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); } const files = await this.driveFileEntityService.packManyByIds(note.fileIds); - const content = JSON.stringify(serialize(note, poll, files)); + const content = JSON.stringify(this.serialize(note, poll, files)); const isFirst = exportedNotesCount === 0; await write(isFirst ? content : ',\n' + content); exportedNotesCount++; @@ -129,22 +130,22 @@ export class ExportNotesProcessorService { cleanup(); } } -} -function serialize(note: MiNote, poll: MiPoll | null = null, files: Packed<'DriveFile'>[]): Record { - return { - id: note.id, - text: note.text, - createdAt: note.createdAt, - fileIds: note.fileIds, - files: files, - replyId: note.replyId, - renoteId: note.renoteId, - poll: poll, - cw: note.cw, - visibility: note.visibility, - visibleUserIds: note.visibleUserIds, - localOnly: note.localOnly, - reactionAcceptance: note.reactionAcceptance, - }; + private serialize(note: MiNote, poll: MiPoll | null = null, files: Packed<'DriveFile'>[]): Record { + return { + id: note.id, + text: note.text, + createdAt: this.idService.parse(note.id).date.toISOString(), + fileIds: note.fileIds, + files: files, + replyId: note.replyId, + renoteId: note.renoteId, + poll: poll, + cw: note.cw, + visibility: note.visibility, + visibleUserIds: note.visibleUserIds, + localOnly: note.localOnly, + reactionAcceptance: note.reactionAcceptance, + }; + } } diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts index 7c95bccaff..3e93b7b505 100644 --- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts @@ -76,8 +76,7 @@ export class ImportAntennasProcessorService { continue; } const result = await this.antennasRepository.insert({ - id: this.idService.genId(), - createdAt: now, + id: this.idService.gen(now.getTime()), lastUsedAt: now, userId: job.data.user.id, name: antenna.name, diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 9be36a9d0d..5dd3fbe887 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -80,8 +80,7 @@ export class ImportUserListsProcessorService { if (list == null) { list = await this.userListsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: user.id, name: listName, }).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 150f3f24d4..f3115690fe 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -128,8 +128,7 @@ export class SigninApiService { const fail = async (status?: number, failure?: { id: string }) => { // Append signin history await this.signinsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: user.id, ip: request.ip, headers: request.headers as any, diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index cebba8c8ee..98e9027006 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -30,8 +30,7 @@ export class SigninService { setImmediate(async () => { // Append signin history const record = await this.signinsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: user.id, ip: request.ip, headers: request.headers as any, diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 431df581b5..d2c4440116 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -164,8 +164,7 @@ export class SignupApiService { const hash = await bcrypt.hash(password, salt); const pendingUser = await this.userPendingsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), code, email: emailAddress!, username: username, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index e48dffecf4..17f792639b 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -44,8 +44,7 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const ad = await this.adsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), expiresAt: new Date(ps.expiresAt), startsAt: new Date(ps.startsAt), dayOfWeek: ps.dayOfWeek, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 262b36b9a4..253a29cf5a 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -71,7 +71,6 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const { raw, packed } = await this.announcementService.create({ - createdAt: new Date(), updatedAt: null, title: ps.title, text: ps.text, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index c82e702eef..fefc379c00 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -9,6 +9,7 @@ import type { MiAnnouncement } from '@/models/Announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['admin'], @@ -81,6 +82,7 @@ export default class extends Endpoint { // eslint- private announcementReadsRepository: AnnouncementReadsRepository, private queryService: QueryService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); @@ -102,7 +104,7 @@ export default class extends Endpoint { // eslint- return announcements.map(announcement => ({ id: announcement.id, - createdAt: announcement.createdAt.toISOString(), + createdAt: this.idService.parse(announcement.id).date.toISOString(), updatedAt: announcement.updatedAt?.toISOString() ?? null, title: announcement.title, text: announcement.text, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 7fb5342f8d..4e5320007e 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -8,6 +8,7 @@ import type { DriveFilesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -163,6 +164,7 @@ export default class extends Endpoint { // eslint- private usersRepository: UsersRepository, private roleService: RoleService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const file = ps.fileId ? await this.driveFilesRepository.findOneBy({ id: ps.fileId }) : await this.driveFilesRepository.findOne({ @@ -212,7 +214,7 @@ export default class extends Endpoint { // eslint- type: file.type, name: file.name, md5: file.md5, - createdAt: file.createdAt.toISOString(), + createdAt: this.idService.parse(file.id).date.toISOString(), requestIp: iAmModerator ? file.requestIp : null, requestHeaders: iAmModerator && !ownerIsModerator ? file.requestHeaders : null, }; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index c5f986ff02..a65e4e7624 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -79,7 +79,7 @@ export default class extends Endpoint { // eslint- } const copied = await this.emojisRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), updatedAt: new Date(), name: emoji.name, host: null, diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index cf94c998fa..7b807e848b 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserIpsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['admin'], @@ -28,17 +29,19 @@ export default class extends Endpoint { // eslint- constructor( @Inject(DI.userIpsRepository) private userIpsRepository: UserIpsRepository, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const ips = await this.userIpsRepository.find({ where: { userId: ps.userId }, - order: { createdAt: 'DESC' }, + order: { id: 'DESC' }, take: 30, }); return ips.map(x => ({ ip: x.ip, - createdAt: x.createdAt.toISOString(), + createdAt: this.idService.parse(x.id).date.toISOString(), })); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts index 2cc5ab6e35..4a22fd4824 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts @@ -72,8 +72,7 @@ export default class extends Endpoint { // eslint- for (let i = 0; i < ps.count; i++) { ticketsPromises.push(this.registrationTicketsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null, code: generateInviteCode(), }).then(x => this.registrationTicketsRepository.findOneByOrFail(x.identifiers[0]))); diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index ef5627bc9a..b7f9aa0495 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -10,6 +10,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -49,6 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private userEntityService: UserEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const role = await this.rolesRepository.findOneBy({ @@ -74,7 +76,7 @@ export default class extends Endpoint { // eslint- return await Promise.all(assigns.map(async assign => ({ id: assign.id, - createdAt: assign.createdAt, + createdAt: this.idService.parse(assign.id).date.toISOString(), user: await this.userEntityService.pack(assign.user!, me, { detail: true }), expiresAt: assign.expiresAt, }))); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 0731413d05..f550c4fd28 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -9,6 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['admin'], @@ -44,6 +45,7 @@ export default class extends Endpoint { // eslint- private roleService: RoleService, private roleEntityService: RoleEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ @@ -92,7 +94,7 @@ export default class extends Endpoint { // eslint- policies: await this.roleService.getUserPolicies(user.id), roles: await this.roleEntityService.packMany(roles, me), roleAssigns: roleAssigns.map(a => ({ - createdAt: a.createdAt.toISOString(), + createdAt: this.idService.parse(a.id).date.toISOString(), expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null, roleId: a.roleId, })), diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 15fca4904d..687893398e 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -112,8 +112,7 @@ export default class extends Endpoint { // eslint- const now = new Date(); const antenna = await this.antennasRepository.insert({ - id: this.idService.genId(), - createdAt: now, + id: this.idService.gen(now.getTime()), lastUsedAt: now, userId: me.id, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 6d69971e30..ff96411f3b 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -73,8 +73,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index cb00221506..f89d9823ba 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -55,8 +55,7 @@ export default class extends Endpoint { // eslint- // Create account const app = await this.appsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me ? me.id : null, name: ps.name, description: ps.description, diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index 1b1893fd94..e0baeb3565 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -80,8 +80,7 @@ export default class extends Endpoint { // eslint- const now = new Date(); await this.accessTokensRepository.insert({ - id: this.idService.genId(), - createdAt: now, + id: this.idService.gen(now.getTime()), lastUsedAt: now, appId: session.appId, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index 8b6a2c213d..6e474c59e0 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -79,8 +79,7 @@ export default class extends Endpoint { // eslint- // Create session token document const doc = await this.authSessionsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), appId: app.id, token: token, }).then(x => this.authSessionsRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index e72120e156..3ba411d28c 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -80,8 +80,7 @@ export default class extends Endpoint { // eslint- } const channel = await this.channelsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, name: ps.name, description: ps.description ?? null, diff --git a/packages/backend/src/server/api/endpoints/channels/favorite.ts b/packages/backend/src/server/api/endpoints/channels/favorite.ts index 1f78a86dd4..c175718919 100644 --- a/packages/backend/src/server/api/endpoints/channels/favorite.ts +++ b/packages/backend/src/server/api/endpoints/channels/favorite.ts @@ -57,8 +57,7 @@ export default class extends Endpoint { // eslint- } await this.channelFavoritesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, channelId: channel.id, }); diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index 5a43e8be1b..76ec6be805 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -57,8 +57,7 @@ export default class extends Endpoint { // eslint- } await this.channelFollowingsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), followerId: me.id, followeeId: channel.id, }); diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 2dfcf659d7..9c39d0ed86 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -74,8 +74,8 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const isRangeSpecified = untilId != null && sinceId != null; const channel = await this.channelsRepository.findOneBy({ diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts index 6cd34f0a54..015b2cfa85 100644 --- a/packages/backend/src/server/api/endpoints/clips/favorite.ts +++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts @@ -74,8 +74,7 @@ export default class extends Endpoint { // eslint- } await this.clipFavoritesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), clipId: clip.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index bc3a9bbe21..d18199f19b 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -76,8 +76,7 @@ export default class extends Endpoint { // eslint- // Create folder const folder = await this.driveFoldersRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), name: ps.name, parentId: parent !== null ? parent.id : null, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts index b46660d218..4fa65ac9aa 100644 --- a/packages/backend/src/server/api/endpoints/flash/create.ts +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -53,9 +53,8 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const flash = await this.flashsRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), userId: me.id, - createdAt: new Date(), updatedAt: new Date(), title: ps.title, summary: ps.summary, diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts index a90e5f653a..1003249c0c 100644 --- a/packages/backend/src/server/api/endpoints/flash/like.ts +++ b/packages/backend/src/server/api/endpoints/flash/like.ts @@ -83,8 +83,7 @@ export default class extends Endpoint { // eslint- // Create like await this.flashLikesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), flashId: flash.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index 94701712de..71e0ad4141 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -76,8 +76,7 @@ export default class extends Endpoint { // eslint- } const post = await this.galleryPostsRepository.insert(new MiGalleryPost({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), updatedAt: new Date(), title: ps.title, description: ps.description, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index c557054066..561b2492ab 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -83,8 +83,7 @@ export default class extends Endpoint { // eslint- // Create like await this.galleryLikesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), postId: post.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index daa3e536a4..09f6540a77 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokensRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { requireCredential: true, @@ -27,6 +28,8 @@ export default class extends Endpoint { // eslint- constructor( @Inject(DI.accessTokensRepository) private accessTokensRepository: AccessTokensRepository, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const query = this.accessTokensRepository.createQueryBuilder('token') @@ -34,8 +37,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('token.app', 'app'); switch (ps.sort) { - case '+createdAt': query.orderBy('token.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('token.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('token.id', 'DESC'); break; + case '-createdAt': query.orderBy('token.id', 'ASC'); break; case '+lastUsedAt': query.orderBy('token.lastUsedAt', 'DESC'); break; case '-lastUsedAt': query.orderBy('token.lastUsedAt', 'ASC'); break; default: query.orderBy('token.id', 'ASC'); break; @@ -46,7 +49,7 @@ export default class extends Endpoint { // eslint- return await Promise.all(tokens.map(token => ({ id: token.id, name: token.name ?? token.app?.name, - createdAt: token.createdAt, + createdAt: this.idService.parse(token.id).date.toISOString(), lastUsedAt: token.lastUsedAt, permission: token.permission, }))); diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index c074b152df..6203e7aa8b 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -53,8 +53,7 @@ export default class extends Endpoint { // eslint- }); } else { await this.registryItemsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), updatedAt: new Date(), userId: me.id, domain: null, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 48eaeff406..f00dba4a85 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -63,8 +63,7 @@ export default class extends Endpoint { // eslint- } const webhook = await this.webhooksRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, name: ps.name, url: ps.url, diff --git a/packages/backend/src/server/api/endpoints/invite/create.ts b/packages/backend/src/server/api/endpoints/invite/create.ts index 7361ab616c..94836283fa 100644 --- a/packages/backend/src/server/api/endpoints/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/invite/create.ts @@ -62,7 +62,7 @@ export default class extends Endpoint { // eslint- if (policies.inviteLimit) { const count = await this.registrationTicketsRepository.countBy({ - createdAt: MoreThan(new Date(Date.now() - (policies.inviteLimitCycle * 1000 * 60))), + id: MoreThan(this.idService.gen(Date.now() - (policies.inviteLimitCycle * 1000 * 60))), createdById: me.id, }); @@ -72,8 +72,7 @@ export default class extends Endpoint { // eslint- } const ticket = await this.registrationTicketsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), createdBy: me, createdById: me.id, expiresAt: policies.inviteExpirationTime ? new Date(Date.now() + (policies.inviteExpirationTime * 1000 * 60)) : null, diff --git a/packages/backend/src/server/api/endpoints/invite/limit.ts b/packages/backend/src/server/api/endpoints/invite/limit.ts index 43b94e4f06..1f4190c948 100644 --- a/packages/backend/src/server/api/endpoints/invite/limit.ts +++ b/packages/backend/src/server/api/endpoints/invite/limit.ts @@ -9,6 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistrationTicketsRepository } from '@/models/_.js'; import { RoleService } from '@/core/RoleService.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['meta'], @@ -41,12 +42,13 @@ export default class extends Endpoint { // eslint- private registrationTicketsRepository: RegistrationTicketsRepository, private roleService: RoleService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const policies = await this.roleService.getUserPolicies(me.id); const count = policies.inviteLimit ? await this.registrationTicketsRepository.countBy({ - createdAt: MoreThan(new Date(Date.now() - (policies.inviteExpirationTime * 60 * 1000))), + id: MoreThan(this.idService.gen(Date.now() - (policies.inviteExpirationTime * 60 * 1000))), createdById: me.id, }) : null; diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index e40656cb6d..cac8f41f8e 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -59,8 +59,7 @@ export default class extends Endpoint { // eslint- // Insert access token doc await this.accessTokensRepository.insert({ - id: this.idService.genId(), - createdAt: now, + id: this.idService.gen(now.getTime()), lastUsedAt: now, session: ps.session, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index cc648e22a8..ed3dce7f35 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -80,8 +80,7 @@ export default class extends Endpoint { // eslint- // Create favorite await this.noteFavoritesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), noteId: note.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 8ac5f1b038..378529e30d 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -77,8 +77,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const policies = await this.roleService.getUserPolicies(me.id); if (!policies.ltlAvailable) { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index d10c3bedbf..f69e60ab54 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -73,8 +73,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const policies = await this.roleService.getUserPolicies(me ? me.id : null); if (!policies.ltlAvailable) { diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 986201e950..af7ff8bdcd 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -98,7 +98,7 @@ export default class extends Endpoint { // eslint- id: In(polls.map(poll => poll.noteId)), }, order: { - createdAt: 'DESC', + id: 'DESC', }, }); diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index a58bf09b85..734c3f0e63 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -145,8 +145,7 @@ export default class extends Endpoint { // eslint- // Create vote const vote = await this.pollVotesRepository.insert({ - id: this.idService.genId(), - createdAt, + id: this.idService.gen(createdAt.getTime()), noteId: note.id, userId: me.id, choice: ps.choice, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index 449a838604..b2cdaa00ac 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -72,8 +72,7 @@ export default class extends Endpoint { // eslint- await this.noteReadService.read(me.id, mutedNotes); await this.noteThreadMutingsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), threadId: note.threadId ?? note.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 760d52c9db..8f13b3a4ba 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -66,8 +66,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const [ followings, diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index f7ee58264e..b8007e78fd 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -83,8 +83,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const list = await this.userListsRepository.findOneBy({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index c0e8fab16c..4c2ef516e5 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -103,8 +103,7 @@ export default class extends Endpoint { // eslint- }); const page = await this.pagesRepository.insert(new MiPage({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), updatedAt: new Date(), title: ps.title, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 6c69cad9d5..8c18982b50 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -83,8 +83,7 @@ export default class extends Endpoint { // eslint- // Create like await this.pageLikesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), pageId: page.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index b197756acc..7d07c92178 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -60,8 +60,7 @@ export default class extends Endpoint { // eslint- } await this.promoReadsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), noteId: note.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts index 3c9d266e21..7ff7b5de3a 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts @@ -90,8 +90,7 @@ export default class extends Endpoint { // eslint- // Create mute await this.renoteMutingsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), muterId: muter.id, muteeId: mutee.id, } as MiRenoteMuting); diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index adb160c58b..f13710e1dd 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -84,8 +84,7 @@ export default class extends Endpoint { // eslint- const token = secureRndstr(64, { chars: L_CHARS }); await this.passwordResetRequestsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: profile.userId, token, }); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 1858c922a0..3a6dc14bcd 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['reset password'], @@ -38,6 +39,8 @@ export default class extends Endpoint { // eslint- @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const req = await this.passwordResetRequestsRepository.findOneByOrFail({ @@ -45,7 +48,7 @@ export default class extends Endpoint { // eslint- }); // 発行してから30分以上経過していたら無効 - if (Date.now() - req.createdAt.getTime() > 1000 * 60 * 30) { + if (Date.now() - this.idService.parse(req.id).date.getTime() > 1000 * 60 * 30) { throw new Error(); // TODO } diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 0db51abc55..e6e1daaa51 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -69,8 +69,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const role = await this.rolesRepository.findOneBy({ id: ps.roleId, diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 5cfbeab73f..9ab062326d 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -88,8 +88,7 @@ export default class extends Endpoint { // eslint- } await this.swSubscriptionsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, endpoint: ps.endpoint, auth: ps.auth, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts index f2f6c4303a..4eb37c3e43 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts @@ -104,8 +104,7 @@ export default class extends Endpoint { // eslint- } const userList = await this.userListsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, name: ps.name, } as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 60b2b3f17e..e86e4c0ded 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -66,8 +66,7 @@ export default class extends Endpoint { // eslint- } const userList = await this.userListsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, name: ps.name, } as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts index 1707afee60..2ecf0a1256 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts @@ -69,8 +69,7 @@ export default class extends Endpoint { } await this.userListFavoritesRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), userId: me.id, userListId: ps.listId, }); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index df0951ce74..4f3d61ce07 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -74,8 +74,8 @@ export default class extends Endpoint { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); const isRangeSpecified = untilId != null && sinceId != null; const isSelf = me && (me.id === ps.userId); diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 50aa6fa09e..3bcf44cc42 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -82,8 +82,7 @@ export default class extends Endpoint { // eslint- } const report = await this.abuseUserReportsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), targetUserId: user.id, targetUserHost: user.host, reporterId: me.id, diff --git a/packages/backend/src/server/api/endpoints/users/update-memo.ts b/packages/backend/src/server/api/endpoints/users/update-memo.ts index 194d488052..b3f67815ef 100644 --- a/packages/backend/src/server/api/endpoints/users/update-memo.ts +++ b/packages/backend/src/server/api/endpoints/users/update-memo.ts @@ -72,7 +72,7 @@ export default class extends Endpoint { // eslint- if (!previousMemo) { await this.userMemosRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), userId: me.id, targetUserId: target.id, memo: ps.memo, diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index c3a78561c2..4fa7b800e8 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -325,8 +325,7 @@ export class OAuth2ProviderService { // NOTE: we don't have a setup for automatic token expiration await accessTokensRepository.insert({ - id: idService.genId(), - createdAt: now, + id: idService.gen(now.getTime()), lastUsedAt: now, userId: granted.userId, token: accessToken, diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 78551e800b..b547630298 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -13,6 +13,7 @@ import type { MiUser } from '@/models/User.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; @Injectable() export class FeedService { @@ -31,6 +32,7 @@ export class FeedService { private userEntityService: UserEntityService, private driveFileEntityService: DriveFileEntityService, + private idService: IdService, ) { } @@ -49,14 +51,14 @@ export class FeedService { renoteId: IsNull(), visibility: In(['public', 'home']), }, - order: { createdAt: -1 }, + order: { id: -1 }, take: 20, }); const feed = new Feed({ id: author.link, title: `${author.name} (@${user.username}@${this.config.host})`, - updated: notes[0].createdAt, + updated: this.idService.parse(notes[0].id).date, generator: 'Misskey', description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, link: author.link, @@ -78,7 +80,7 @@ export class FeedService { feed.addItem({ title: `New note by ${author.name}`, link: `${this.config.url}/notes/${note.id}`, - date: note.createdAt, + date: this.idService.parse(note.id).date, description: note.cw ?? undefined, content: note.text ?? undefined, image: file ? this.driveFileEntityService.getPublicUrl(file) ?? undefined : undefined, diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts index 77de144882..5a83bbb7da 100644 --- a/packages/backend/test/e2e/streaming.ts +++ b/packages/backend/test/e2e/streaming.ts @@ -18,7 +18,6 @@ describe('Streaming', () => { const follow = async (follower: any, followee: any) => { await Followings.save({ id: 'a', - createdAt: new Date(), followerId: follower.id, followeeId: followee.id, followerHost: follower.host, diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts index 8f61d91ba9..5fe41d1962 100644 --- a/packages/backend/test/unit/AnnouncementService.ts +++ b/packages/backend/test/unit/AnnouncementService.ts @@ -36,7 +36,6 @@ describe('AnnouncementService', () => { const un = secureRndstr(16); return usersRepository.insert({ id: genAidx(new Date()), - createdAt: new Date(), username: un, usernameLower: un, ...data, @@ -44,10 +43,9 @@ describe('AnnouncementService', () => { .then(x => usersRepository.findOneByOrFail(x.identifiers[0])); } - function createAnnouncement(data: Partial = {}) { + function createAnnouncement(data: Partial = {}) { return announcementsRepository.insert({ - id: genAidx(new Date()), - createdAt: new Date(), + id: genAidx(data.createdAt ?? new Date()), updatedAt: null, title: 'Title', text: 'Text', diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index c6a14702ae..41a2e49ed0 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -38,7 +38,6 @@ describe('RoleService', () => { const un = secureRndstr(16); return usersRepository.insert({ id: genAidx(new Date()), - createdAt: new Date(), username: un, usernameLower: un, ...data, @@ -49,7 +48,6 @@ describe('RoleService', () => { function createRole(data: Partial = {}) { return rolesRepository.insert({ id: genAidx(new Date()), - createdAt: new Date(), updatedAt: new Date(), lastUsedAt: new Date(), description: '', From 24437a04d49bb720ac632959270e072f072707c6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 12:00:56 +0900 Subject: [PATCH 058/127] fix of 1fa1d31696 --- packages/backend/src/core/AccountMoveService.ts | 6 ++---- .../backend/src/server/api/endpoints/admin/invite/list.ts | 4 ++-- .../backend/src/server/api/endpoints/admin/show-users.ts | 4 ++-- packages/backend/src/server/api/endpoints/drive/files.ts | 4 ++-- packages/backend/src/server/api/endpoints/hashtags/users.ts | 4 ++-- packages/backend/src/server/api/endpoints/users.ts | 4 ++-- 6 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index ed2891113b..350aa6ba24 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -180,7 +180,7 @@ export class AccountMoveService { { muteeId: dst.id, expiresAt: IsNull() }, ).then(mutings => mutings.map(muting => muting.muterId)); - const newMutings: Map = new Map(); + const newMutings: Map = new Map(); // 重複しないようにIDを生成 const genId = (): string => { @@ -194,7 +194,6 @@ export class AccountMoveService { if (existingMutingsMuterUserIds.includes(muting.muterId)) continue; // skip if already muted indefinitely newMutings.set(genId(), { ...muting, - createdAt: new Date(), muteeId: dst.id, }); } @@ -228,7 +227,7 @@ export class AccountMoveService { }, }).then(memberships => memberships.map(membership => membership.userListId)); - const newMemberships: Map = new Map(); + const newMemberships: Map = new Map(); // 重複しないようにIDを生成 const genId = (): string => { @@ -241,7 +240,6 @@ export class AccountMoveService { for (const membership of oldMemberships) { if (existingUserListIds.includes(membership.userListId)) continue; // skip if dst exists in this user's list newMemberships.set(genId(), { - createdAt: new Date(), userId: dst.id, userListId: membership.userListId, userListUserId: membership.userListUserId, diff --git a/packages/backend/src/server/api/endpoints/admin/invite/list.ts b/packages/backend/src/server/api/endpoints/admin/invite/list.ts index a20a51121a..f25d3fcb33 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/list.ts @@ -56,8 +56,8 @@ export default class extends Endpoint { // eslint- } switch (ps.sort) { - case '+createdAt': query.orderBy('ticket.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('ticket.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('ticket.id', 'DESC'); break; + case '-createdAt': query.orderBy('ticket.id', 'ASC'); break; case '+usedAt': query.orderBy('ticket.usedAt', 'DESC', 'NULLS LAST'); break; case '-usedAt': query.orderBy('ticket.usedAt', 'ASC', 'NULLS FIRST'); break; default: query.orderBy('ticket.id', 'DESC'); break; diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index e89e1a1490..fc810987d2 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -99,8 +99,8 @@ export default class extends Endpoint { // eslint- switch (ps.sort) { case '+follower': query.orderBy('user.followersCount', 'DESC'); break; case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('user.id', 'DESC'); break; + case '-createdAt': query.orderBy('user.id', 'ASC'); break; case '+updatedAt': query.orderBy('user.updatedAt', 'DESC', 'NULLS LAST'); break; case '-updatedAt': query.orderBy('user.updatedAt', 'ASC', 'NULLS FIRST'); break; case '+lastActiveDate': query.orderBy('user.lastActiveDate', 'DESC', 'NULLS LAST'); break; diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 6f3a62977f..b7e9d12e94 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -69,8 +69,8 @@ export default class extends Endpoint { // eslint- } switch (ps.sort) { - case '+createdAt': query.orderBy('file.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('file.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('file.id', 'DESC'); break; + case '-createdAt': query.orderBy('file.id', 'ASC'); break; case '+name': query.orderBy('file.name', 'DESC'); break; case '-name': query.orderBy('file.name', 'ASC'); break; case '+size': query.orderBy('file.size', 'DESC'); break; diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 1cef76d3d2..50aea79943 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -66,8 +66,8 @@ export default class extends Endpoint { // eslint- switch (ps.sort) { case '+follower': query.orderBy('user.followersCount', 'DESC'); break; case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('user.id', 'DESC'); break; + case '-createdAt': query.orderBy('user.id', 'ASC'); break; case '+updatedAt': query.orderBy('user.updatedAt', 'DESC'); break; case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break; } diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 21c585f1ad..8dc5841314 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -74,8 +74,8 @@ export default class extends Endpoint { // eslint- switch (ps.sort) { case '+follower': query.orderBy('user.followersCount', 'DESC'); break; case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('user.id', 'DESC'); break; + case '-createdAt': query.orderBy('user.id', 'ASC'); break; case '+updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'DESC'); break; case '-updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'ASC'); break; default: query.orderBy('user.id', 'ASC'); break; From 34d1b463a42ab82295b0254b7d97dfb9586600e9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 12:58:17 +0900 Subject: [PATCH 059/127] fix tests --- .../backend/test/unit/AnnouncementService.ts | 2 +- packages/backend/test/unit/RoleService.ts | 4 +-- packages/backend/test/unit/activitypub.ts | 3 ++- packages/backend/test/unit/misc/id.ts | 26 +++++++++---------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts index 5fe41d1962..f2aa5d35e4 100644 --- a/packages/backend/test/unit/AnnouncementService.ts +++ b/packages/backend/test/unit/AnnouncementService.ts @@ -35,7 +35,7 @@ describe('AnnouncementService', () => { function createUser(data: Partial = {}) { const un = secureRndstr(16); return usersRepository.insert({ - id: genAidx(new Date()), + id: genAidx(Date.now()), username: un, usernameLower: un, ...data, diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index 41a2e49ed0..8b5e3163c3 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -37,7 +37,7 @@ describe('RoleService', () => { function createUser(data: Partial = {}) { const un = secureRndstr(16); return usersRepository.insert({ - id: genAidx(new Date()), + id: genAidx(Date.now()), username: un, usernameLower: un, ...data, @@ -47,7 +47,7 @@ describe('RoleService', () => { function createRole(data: Partial = {}) { return rolesRepository.insert({ - id: genAidx(new Date()), + id: genAidx(Date.now()), updatedAt: new Date(), lastUsedAt: new Date(), description: '', diff --git a/packages/backend/test/unit/activitypub.ts b/packages/backend/test/unit/activitypub.ts index 2e9454927c..c418e8413d 100644 --- a/packages/backend/test/unit/activitypub.ts +++ b/packages/backend/test/unit/activitypub.ts @@ -23,6 +23,7 @@ import { secureRndstr } from '@/misc/secure-rndstr.js'; import { DownloadService } from '@/core/DownloadService.js'; import { MetaService } from '@/core/MetaService.js'; import type { MiRemoteUser } from '@/models/User.js'; +import { genAidx } from '@/misc/id/aidx.js'; import { MockResolver } from '../misc/mock-resolver.js'; const host = 'https://host1.test'; @@ -200,7 +201,7 @@ describe('ActivityPub', () => { describe('Renderer', () => { test('Render an announce with visibility: followers', () => { rendererService.renderAnnounce(null, { - createdAt: new Date(0), + id: genAidx(Date.now()), visibility: 'followers', } as MiNote); }); diff --git a/packages/backend/test/unit/misc/id.ts b/packages/backend/test/unit/misc/id.ts index 57b4ea9947..59783a9fa1 100644 --- a/packages/backend/test/unit/misc/id.ts +++ b/packages/backend/test/unit/misc/id.ts @@ -14,44 +14,44 @@ import { ulidRegExp, parseUlid } from '@/misc/id/ulid.js'; describe('misc:id', () => { test('aid', () => { - const date = new Date(); + const date = Date.now(); const gotAid = genAid(date); expect(gotAid).toMatch(aidRegExp); - expect(parseAid(gotAid).date.getTime()).toBe(date.getTime()); + expect(parseAid(gotAid).date.getTime()).toBe(date); }); test('aidx', () => { - const date = new Date(); + const date = Date.now(); const gotAidx = genAidx(date); expect(gotAidx).toMatch(aidxRegExp); - expect(parseAidx(gotAidx).date.getTime()).toBe(date.getTime()); + expect(parseAidx(gotAidx).date.getTime()).toBe(date); }); test('meid', () => { - const date = new Date(); + const date = Date.now(); const gotMeid = genMeid(date); expect(gotMeid).toMatch(meidRegExp); - expect(parseMeid(gotMeid).date.getTime()).toBe(date.getTime()); + expect(parseMeid(gotMeid).date.getTime()).toBe(date); }); test('meidg', () => { - const date = new Date(); + const date = Date.now(); const gotMeidg = genMeidg(date); expect(gotMeidg).toMatch(meidgRegExp); - expect(parseMeidg(gotMeidg).date.getTime()).toBe(date.getTime()); + expect(parseMeidg(gotMeidg).date.getTime()).toBe(date); }); test('objectid', () => { - const date = new Date(); + const date = Date.now(); const gotObjectId = genObjectId(date); expect(gotObjectId).toMatch(objectIdRegExp); - expect(Math.floor(parseObjectId(gotObjectId).date.getTime() / 1000)).toBe(Math.floor(date.getTime() / 1000)); + expect(Math.floor(parseObjectId(gotObjectId).date.getTime() / 1000)).toBe(Math.floor(date / 1000)); }); test('ulid', () => { - const date = new Date(); - const gotUlid = ulid(date.getTime()); + const date = Date.now(); + const gotUlid = ulid(date); expect(gotUlid).toMatch(ulidRegExp); - expect(parseUlid(gotUlid).date.getTime()).toBe(date.getTime()); + expect(parseUlid(gotUlid).date.getTime()).toBe(date); }); }); From 6a321ba340fbe24bf6c01bdeefda07bcaeb1769e Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 14:35:44 +0900 Subject: [PATCH 060/127] fix test --- packages/backend/test/unit/RoleService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index 8b5e3163c3..f644312bc9 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -196,10 +196,10 @@ describe('RoleService', () => { test('conditional role', async () => { const user1 = await createUser({ - createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), + id: genAidx(Date.now() - (1000 * 60 * 60 * 24 * 365)), }); const user2 = await createUser({ - createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), + id: genAidx(Date.now() - (1000 * 60 * 60 * 24 * 365)), followersCount: 10, }); await createRole({ From 3ebed5aa3e18cb66359c62f1f77443d120aa9f02 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 15:06:00 +0900 Subject: [PATCH 061/127] feat: local-only antenna Resolve #11869 --- CHANGELOG.md | 2 +- .../migration/1697436246389-antenna-localOnly.js | 16 ++++++++++++++++ packages/backend/src/core/AntennaService.ts | 2 ++ .../src/core/entities/AntennaEntityService.ts | 1 + packages/backend/src/models/Antenna.ts | 5 +++++ .../backend/src/models/json-schema/antenna.ts | 5 +++++ .../processors/ExportAntennasProcessorService.ts | 1 + .../processors/ImportAntennasProcessorService.ts | 2 ++ .../src/server/api/endpoints/antennas/create.ts | 2 ++ .../src/server/api/endpoints/antennas/update.ts | 2 ++ packages/backend/test/e2e/move.ts | 2 ++ .../frontend/src/pages/my-antennas/create.vue | 3 ++- .../frontend/src/pages/my-antennas/editor.vue | 3 +++ packages/misskey-js/etc/misskey-js.api.md | 3 ++- packages/misskey-js/src/entities.ts | 1 + 15 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 packages/backend/migration/1697436246389-antenna-localOnly.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 85a998de10..ee14d361f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ## 2023.x.x (unreleased) ### General -- +- Feat: アンテナでローカルの投稿のみ収集できるようになりました ### Client - Enhance: TLの返信表示オプションを記憶するように diff --git a/packages/backend/migration/1697436246389-antenna-localOnly.js b/packages/backend/migration/1697436246389-antenna-localOnly.js new file mode 100644 index 0000000000..0228673291 --- /dev/null +++ b/packages/backend/migration/1697436246389-antenna-localOnly.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class AntennaLocalOnly1697436246389 { + name = 'AntennaLocalOnly1697436246389' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "antenna" ADD "localOnly" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "localOnly"`); + } +} diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 60569c44fc..94c8ad0cf1 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -98,6 +98,8 @@ export class AntennaService implements OnApplicationShutdown { if (note.visibility === 'specified') return false; if (note.visibility === 'followers') return false; + if (antenna.localOnly && noteUser.host != null) return false; + if (!antenna.withReplies && note.replyId != null) return false; if (antenna.src === 'home') { diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index a9e504d374..265a61e8ad 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -37,6 +37,7 @@ export class AntennaEntityService { userListId: antenna.userListId, users: antenna.users, caseSensitive: antenna.caseSensitive, + localOnly: antenna.localOnly, notify: antenna.notify, withReplies: antenna.withReplies, withFile: antenna.withFile, diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts index 0bc0084fc5..b74c61b728 100644 --- a/packages/backend/src/models/Antenna.ts +++ b/packages/backend/src/models/Antenna.ts @@ -93,4 +93,9 @@ export class MiAntenna { default: true, }) public isActive: boolean; + + @Column('boolean', { + default: false, + }) + public localOnly: boolean; } diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts index 7b6475919c..4a9f0ed355 100644 --- a/packages/backend/src/models/json-schema/antenna.ts +++ b/packages/backend/src/models/json-schema/antenna.ts @@ -67,6 +67,11 @@ export const packedAntennaSchema = { optional: false, nullable: false, default: false, }, + localOnly: { + type: 'boolean', + optional: false, nullable: false, + default: false, + }, notify: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts index a0afbee3ba..d0968d2923 100644 --- a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts @@ -80,6 +80,7 @@ export class ExportAntennasProcessorService { return this.utilityService.getFullApAccount(u.username, u.host); // acct }) : null, caseSensitive: antenna.caseSensitive, + localOnly: antenna.localOnly, withReplies: antenna.withReplies, withFile: antenna.withFile, notify: antenna.notify, diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts index 3e93b7b505..291ea14b67 100644 --- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts @@ -43,6 +43,7 @@ const validate = new Ajv().compile({ type: 'string', } }, caseSensitive: { type: 'boolean' }, + localOnly: { type: 'boolean' }, withReplies: { type: 'boolean' }, withFile: { type: 'boolean' }, notify: { type: 'boolean' }, @@ -86,6 +87,7 @@ export class ImportAntennasProcessorService { excludeKeywords: antenna.excludeKeywords, users: (antenna.src === 'list' && antenna.userListAccts !== null ? antenna.userListAccts : antenna.users).filter(Boolean), caseSensitive: antenna.caseSensitive, + localOnly: antenna.localOnly, withReplies: antenna.withReplies, withFile: antenna.withFile, notify: antenna.notify, diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 687893398e..b029493d3a 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -63,6 +63,7 @@ export const paramDef = { type: 'string', } }, caseSensitive: { type: 'boolean' }, + localOnly: { type: 'boolean' }, withReplies: { type: 'boolean' }, withFile: { type: 'boolean' }, notify: { type: 'boolean' }, @@ -122,6 +123,7 @@ export default class extends Endpoint { // eslint- excludeKeywords: ps.excludeKeywords, users: ps.users, caseSensitive: ps.caseSensitive, + localOnly: ps.localOnly, withReplies: ps.withReplies, withFile: ps.withFile, notify: ps.notify, diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 0e98746881..3457bb6f66 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -62,6 +62,7 @@ export const paramDef = { type: 'string', } }, caseSensitive: { type: 'boolean' }, + localOnly: { type: 'boolean' }, withReplies: { type: 'boolean' }, withFile: { type: 'boolean' }, notify: { type: 'boolean' }, @@ -116,6 +117,7 @@ export default class extends Endpoint { // eslint- excludeKeywords: ps.excludeKeywords, users: ps.users, caseSensitive: ps.caseSensitive, + localOnly: ps.localOnly, withReplies: ps.withReplies, withFile: ps.withFile, notify: ps.notify, diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts index 3f158f9f13..b009ef124a 100644 --- a/packages/backend/test/e2e/move.ts +++ b/packages/backend/test/e2e/move.ts @@ -188,6 +188,7 @@ describe('Account Move', () => { excludeKeywords: [], users: [], caseSensitive: false, + localOnly: false, withReplies: false, withFile: false, notify: false, @@ -431,6 +432,7 @@ describe('Account Move', () => { excludeKeywords: [], users: [eve.id], caseSensitive: false, + localOnly: false, withReplies: false, withFile: false, notify: false, diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue index 2e08c13f1b..6c963cdb5d 100644 --- a/packages/frontend/src/pages/my-antennas/create.vue +++ b/packages/frontend/src/pages/my-antennas/create.vue @@ -14,7 +14,7 @@ import XAntenna from './editor.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { useRouter } from '@/router.js'; -import { antennasCache } from '@/cache'; +import { antennasCache } from '@/cache.js'; const router = useRouter(); @@ -27,6 +27,7 @@ let draft = $ref({ excludeKeywords: [], withReplies: false, caseSensitive: false, + localOnly: false, withFile: false, notify: false, }); diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue index 4add66c396..16b8b848fd 100644 --- a/packages/frontend/src/pages/my-antennas/editor.vue +++ b/packages/frontend/src/pages/my-antennas/editor.vue @@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only + {{ i18n.ts.localOnly }} {{ i18n.ts.caseSensitive }} {{ i18n.ts.withFileAntenna }} {{ i18n.ts.notifyAntenna }} @@ -75,6 +76,7 @@ let users: string = $ref(props.antenna.users.join('\n')); let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n')); let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n')); let caseSensitive: boolean = $ref(props.antenna.caseSensitive); +let localOnly: boolean = $ref(props.antenna.localOnly); let withReplies: boolean = $ref(props.antenna.withReplies); let withFile: boolean = $ref(props.antenna.withFile); let notify: boolean = $ref(props.antenna.notify); @@ -95,6 +97,7 @@ async function saveAntenna() { withFile, notify, caseSensitive, + localOnly, users: users.trim().split('\n').map(x => x.trim()), keywords: keywords.trim().split('\n').map(x => x.trim().split(' ')), excludeKeywords: excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')), diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 1a0bbeac78..b9777993fd 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -61,6 +61,7 @@ type Antenna = { userGroupId: ID | null; users: string[]; caseSensitive: boolean; + localOnly: boolean; notify: boolean; withReplies: boolean; withFile: boolean; @@ -2984,7 +2985,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:630:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:107:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:600:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:601:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index aed242d8aa..bcf5538532 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -470,6 +470,7 @@ export type Antenna = { userGroupId: ID | null; // TODO users: string[]; // TODO caseSensitive: boolean; + localOnly: boolean; notify: boolean; withReplies: boolean; withFile: boolean; From 1966876320722f8b9a5ef8b761c4dee63ac20e81 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 17:42:33 +0900 Subject: [PATCH 062/127] fix test --- packages/backend/test/e2e/antennas.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts index 7e2beab1ab..c0317f1435 100644 --- a/packages/backend/test/e2e/antennas.ts +++ b/packages/backend/test/e2e/antennas.ts @@ -174,6 +174,7 @@ describe('アンテナ', () => { users: [''], withFile: false, withReplies: false, + localOnly: false, } as Antenna; assert.deepStrictEqual(response, expected); }); From 5efd01ba70b37868f625e0acfc5c1c22d5df775c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC?= <56515516+mattyatea@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:11:27 +0900 Subject: [PATCH 063/127] =?UTF-8?q?feat:=20=E3=82=B5=E3=83=BC=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=82=B5=E3=82=A4=E3=83=AC=E3=83=B3=E3=82=B9=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=82=92=E8=BF=BD=E5=8A=A0=20(#12031)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat : サーバーサイレンスを追加 * Update CHANGELOG.md * Update CHANGELOG.md * Update locale * Update instance-info.vue * update misskey-js.api.md * lint fix * migration fix * 既存のものを使うように * fix * 色々直した * Update packages/frontend/src/pages/admin/instance-block.vue * Update packages/frontend/src/pages/admin/instance-block.vue * Update packages/frontend/src/components/MkInstanceCardMini.vue * Update packages/backend/src/core/entities/InstanceEntityService.ts * Update packages/backend/src/core/entities/InstanceEntityService.ts * Update packages/backend/src/core/entities/InstanceEntityService.ts * Update packages/backend/src/core/UserFollowingService.ts * Update packages/backend/src/core/UserFollowingService.ts * fix: サイレンスされてるサーバーからの投稿は全部ホームにする * fix: undefinedでfalseを返すようにした --------- Co-authored-by: syuilo --- CHANGELOG.md | 1 + locales/en-US.yml | 10 +++- locales/index.d.ts | 3 + locales/ja-JP.yml | 7 ++- .../1697247230117-InstanceSilence.js | 16 ++++++ .../backend/src/core/NoteCreateService.ts | 8 +++ .../backend/src/core/UserFollowingService.ts | 11 ++-- packages/backend/src/core/UtilityService.ts | 6 ++ .../core/entities/InstanceEntityService.ts | 4 +- packages/backend/src/models/Meta.ts | 5 ++ .../models/json-schema/federation-instance.ts | 5 ++ .../src/server/api/endpoints/admin/meta.ts | 11 ++++ .../server/api/endpoints/admin/update-meta.ts | 56 +++++++++++++------ .../api/endpoints/federation/instances.ts | 18 ++++++ .../src/components/MkInstanceCardMini.vue | 8 ++- .../frontend/src/pages/about.federation.vue | 3 + .../src/pages/admin/instance-block.vue | 25 +++++++-- packages/frontend/src/pages/instance-info.vue | 16 +++++- packages/misskey-js/etc/misskey-js.api.md | 4 +- packages/misskey-js/src/entities.ts | 2 + 20 files changed, 184 insertions(+), 35 deletions(-) create mode 100644 packages/backend/migration/1697247230117-InstanceSilence.js diff --git a/CHANGELOG.md b/CHANGELOG.md index ee14d361f9..22593d4862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ - Enhance: トレンドハッシュタグ取得時のパフォーマンスを大幅に向上 - Enhance: WebSocket接続が多い場合のパフォーマンスを向上 - Enhance: 不要なPostgreSQLのインデックスを削除しパフォーマンスを向上 +- Feat: サーバーサイレンス機能が追加されました - Fix: 連合なしアンケートに投票をするとUpdateがリモートに配信されてしまうのを修正 - Fix: nodeinfoにおいてCORS用のヘッダーが設定されていないのを修正 - Fix: 同じ種類のTLのストリーミングを複数接続できない問題を修正 diff --git a/locales/en-US.yml b/locales/en-US.yml index 66825eaa7f..a2873181fe 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -195,6 +195,7 @@ perHour: "Per Hour" perDay: "Per Day" stopActivityDelivery: "Stop sending activities" blockThisInstance: "Block this instance" +silenceThisInstance: "Silence this instance" operations: "Operations" software: "Software" version: "Version" @@ -213,6 +214,13 @@ clearQueueConfirmText: "Any undelivered notes remaining in the queue will not be clearCachedFiles: "Clear cache" clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?" blockedInstances: "Blocked Instances" +silencedInstances: "Silenced Instances" +silencedInstancesDescription: "List the hostnames of the instances that you want to\ + \ silence. Accounts in the listed instances are treated as \"Silenced\", can only make follow requests, and cannot mention local accounts if not followed. This will not affect the blocked instances." +hiddenTags: "Hidden Hashtags" +hiddenTagsDescription: "List the hashtags (without the #) of the hashtags you wish\ + \ to hide from trending and explore. Hidden hashtags are still discoverable via\ + \ other means. Blocked instances are not affected even if listed here." blockedInstancesDescription: "List the hostnames of the instances that you want to block separated by linebreaks. Listed instances will no longer be able to communicate with this instance." muteAndBlock: "Mutes and Blocks" mutedUsers: "Muted users" @@ -794,7 +802,7 @@ active: "Active" offline: "Offline" notRecommended: "Not recommended" botProtection: "Bot Protection" -instanceBlocking: "Blocked Instances" +instanceBlocking: "Blocked/Silenced Instances" selectAccount: "Select account" switchAccount: "Switch account" enabled: "Enabled" diff --git a/locales/index.d.ts b/locales/index.d.ts index 2494c1709b..483c470be8 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -198,6 +198,7 @@ export interface Locale { "perDay": string; "stopActivityDelivery": string; "blockThisInstance": string; + "silenceThisInstance": string; "operations": string; "software": string; "version": string; @@ -217,6 +218,8 @@ export interface Locale { "clearCachedFilesConfirm": string; "blockedInstances": string; "blockedInstancesDescription": string; + "silencedInstances": string; + "silencedInstancesDescription": string; "muteAndBlock": string; "mutedUsers": string; "blockedUsers": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9adc4381a7..725d1e7a87 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -195,6 +195,7 @@ perHour: "1時間ごと" perDay: "1日ごと" stopActivityDelivery: "アクティビティの配送を停止" blockThisInstance: "このサーバーをブロック" +silenceThisInstance: "サーバーをサイレンス" operations: "操作" software: "ソフトウェア" version: "バージョン" @@ -213,7 +214,9 @@ clearQueueConfirmText: "未配達の投稿は配送されなくなります。 clearCachedFiles: "キャッシュをクリア" clearCachedFilesConfirm: "キャッシュされたリモートファイルをすべて削除しますか?" blockedInstances: "ブロックしたサーバー" -blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このサーバーとやり取りできなくなります。サブドメインもブロックされます。" +blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。" +silencedInstances: "サイレンスしたサーバー" +silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたインスタンスには影響しません。" muteAndBlock: "ミュートとブロック" mutedUsers: "ミュートしたユーザー" blockedUsers: "ブロックしたユーザー" @@ -794,7 +797,7 @@ active: "アクティブ" offline: "オフライン" notRecommended: "非推奨" botProtection: "Botプロテクション" -instanceBlocking: "サーバーブロック" +instanceBlocking: "サーバーブロック・サイレンス" selectAccount: "アカウントを選択" switchAccount: "アカウントを切り替え" enabled: "有効" diff --git a/packages/backend/migration/1697247230117-InstanceSilence.js b/packages/backend/migration/1697247230117-InstanceSilence.js new file mode 100644 index 0000000000..5fdbca3b27 --- /dev/null +++ b/packages/backend/migration/1697247230117-InstanceSilence.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class InstanceSilence1697247230117 { + name = 'InstanceSilence1697247230117' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "silencedHosts" character varying(1024) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`); + } +} diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 400f1ec98c..a308e1aaa8 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -56,6 +56,7 @@ import { SearchService } from '@/core/SearchService.js'; import { FeaturedService } from '@/core/FeaturedService.js'; import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { nyaize } from '@/misc/nyaize.js'; +import { UtilityService } from '@/core/UtilityService.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -215,6 +216,7 @@ export class NoteCreateService implements OnApplicationShutdown { private perUserNotesChart: PerUserNotesChart, private activeUsersChart: ActiveUsersChart, private instanceChart: InstanceChart, + private utilityService: UtilityService, ) { } @bindThis @@ -259,6 +261,12 @@ export class NoteCreateService implements OnApplicationShutdown { } } + const inSilencedInstance = this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, user.host); + + if (data.visibility === 'public' && inSilencedInstance && user.host !== null) { + data.visibility = 'home'; + } + if (data.renote) { switch (data.renote.visibility) { case 'public': diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index f6d0c3a6d5..87484f0383 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common'; +import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { IsNull } from 'typeorm'; import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; @@ -28,6 +28,7 @@ import { MetaService } from '@/core/MetaService.js'; import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; +import { UtilityService } from '@/core/UtilityService.js'; import Logger from '../logger.js'; const logger = new Logger('following/create'); @@ -71,6 +72,7 @@ export class UserFollowingService implements OnModuleInit { private instancesRepository: InstancesRepository, private cacheService: CacheService, + private utilityService: UtilityService, private userEntityService: UserEntityService, private idService: IdService, private queueService: QueueService, @@ -118,15 +120,16 @@ export class UserFollowingService implements OnModuleInit { } const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id }); - // フォロー対象が鍵アカウントである or // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or - // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである + // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである or + // フォロワーがローカルユーザーであり、フォロー対象がサイレンスされているサーバーである // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく if ( followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || - (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') + (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') || + (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower) && this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, follower.host)) ) { let autoAccept = false; diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index d2d2776bd2..b95e41167b 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -35,6 +35,12 @@ export class UtilityService { return blockedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); } + @bindThis + public isSilencedHost(silencedHosts: string[] | undefined, host: string | null): boolean { + if (!silencedHosts || host == null) return false; + return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); + } + @bindThis public extractDbHost(uri: string): string { const url = new URL(uri); diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 0e27e9df7f..9afe87eab7 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -3,9 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/Blocking.js'; import type { MiInstance } from '@/models/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; @@ -43,6 +42,7 @@ export class InstanceEntityService { description: instance.description, maintainerName: instance.maintainerName, maintainerEmail: instance.maintainerEmail, + isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host), iconUrl: instance.iconUrl, faviconUrl: instance.faviconUrl, themeColor: instance.themeColor, diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index d2bd0c26e9..23ae513ede 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -76,6 +76,11 @@ export class MiMeta { }) public sensitiveWords: string[]; + @Column('varchar', { + length: 1024, array: true, default: '{}', + }) + public silencedHosts: string[]; + @Column('varchar', { length: 1024, nullable: true, diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index ac07519f16..4ad84d02ff 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -93,6 +93,11 @@ export const packedFederationInstanceSchema = { type: 'string', optional: false, nullable: true, }, + isSilenced: { + type: "boolean", + optional: false, + nullable: false, + }, infoUpdatedAt: { type: 'string', optional: false, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 5a74456ab0..f294934344 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -105,6 +105,16 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + silencedHosts: { + type: "array", + optional: true, + nullable: false, + items: { + type: "string", + optional: false, + nullable: false, + }, + }, pinnedUsers: { type: 'array', optional: false, nullable: false, @@ -367,6 +377,7 @@ export default class extends Endpoint { // eslint- pinnedUsers: instance.pinnedUsers, hiddenTags: instance.hiddenTags, blockedHosts: instance.blockedHosts, + silencedHosts: instance.silencedHosts, sensitiveWords: instance.sensitiveWords, preservedUsernames: instance.preservedUsernames, hcaptchaSecretKey: instance.hcaptchaSecretKey, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 7db25e659f..72c4936c13 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -20,18 +20,26 @@ export const paramDef = { type: 'object', properties: { disableRegistration: { type: 'boolean', nullable: true }, - pinnedUsers: { type: 'array', nullable: true, items: { - type: 'string', - } }, - hiddenTags: { type: 'array', nullable: true, items: { - type: 'string', - } }, - blockedHosts: { type: 'array', nullable: true, items: { - type: 'string', - } }, - sensitiveWords: { type: 'array', nullable: true, items: { - type: 'string', - } }, + pinnedUsers: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, + hiddenTags: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, + blockedHosts: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, + sensitiveWords: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' }, mascotImageUrl: { type: 'string', nullable: true }, bannerUrl: { type: 'string', nullable: true }, @@ -67,9 +75,11 @@ export const paramDef = { proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true }, maintainerName: { type: 'string', nullable: true }, maintainerEmail: { type: 'string', nullable: true }, - langs: { type: 'array', items: { - type: 'string', - } }, + langs: { + type: 'array', items: { + type: 'string', + }, + }, summalyProxy: { type: 'string', nullable: true }, deeplAuthKey: { type: 'string', nullable: true }, deeplIsPro: { type: 'boolean' }, @@ -115,6 +125,13 @@ export const paramDef = { perUserHomeTimelineCacheMax: { type: 'integer' }, perUserListTimelineCacheMax: { type: 'integer' }, notesPerOneAd: { type: 'integer' }, + silencedHosts: { + type: 'array', + nullable: true, + items: { + type: 'string', + }, + }, }, required: [], } as const; @@ -147,7 +164,14 @@ export default class extends Endpoint { // eslint- if (Array.isArray(ps.sensitiveWords)) { set.sensitiveWords = ps.sensitiveWords.filter(Boolean); } - + if (Array.isArray(ps.silencedHosts)) { + let lastValue = ''; + set.silencedHosts = ps.silencedHosts.sort().filter((h) => { + const lv = lastValue; + lastValue = h; + return h !== '' && h !== lv && !set.blockedHosts?.includes(h); + }); + } if (ps.themeColor !== undefined) { set.themeColor = ps.themeColor; } diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index be73e5dbb8..c8beefa9c7 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -36,6 +36,7 @@ export const paramDef = { blocked: { type: 'boolean', nullable: true }, notResponding: { type: 'boolean', nullable: true }, suspended: { type: 'boolean', nullable: true }, + silenced: { type: "boolean", nullable: true }, federating: { type: 'boolean', nullable: true }, subscribing: { type: 'boolean', nullable: true }, publishing: { type: 'boolean', nullable: true }, @@ -102,6 +103,23 @@ export default class extends Endpoint { // eslint- } } + if (typeof ps.silenced === "boolean") { + const meta = await this.metaService.fetch(true); + + if (ps.silenced) { + if (meta.silencedHosts.length === 0) { + return []; + } + query.andWhere("instance.host IN (:...silences)", { + silences: meta.silencedHosts, + }); + } else if (meta.silencedHosts.length > 0) { + query.andWhere("instance.host NOT IN (:...silences)", { + silences: meta.silencedHosts, + }); + } + } + if (typeof ps.federating === 'boolean') { if (ps.federating) { query.andWhere('((instance.followingCount > 0) OR (instance.followersCount > 0))'); diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index de726e3aa4..e384b7a0bc 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only -->