Compare commits

...

74 Commits

Author SHA1 Message Date
syuilo
524b669f5a 2023.10.2-beta.1 2023-10-18 17:22:13 +09:00
syuilo
5b244cc6eb update test 2023-10-18 17:20:43 +09:00
syuilo
ae1e276085 fix of 6cc02fee99 2023-10-18 17:17:56 +09:00
syuilo
6b5ee43800 enhance(backend): improve fanout tl for stl 2023-10-18 15:51:50 +09:00
syuilo
6b7efb6f1d fix(backend): fix admin/get-user-ips error 2023-10-18 15:32:35 +09:00
syuilo
3c5bcdd7b3 Update CHANGELOG.md 2023-10-18 12:27:15 +09:00
syuilo
6cc02fee99 enhance(backend): improve fanout tl
Resolve #11958
Resolve #12061
2023-10-18 12:26:16 +09:00
syuilo
2a88d8ee2d refactor(backend): rename service 2023-10-18 12:07:18 +09:00
syuilo
52a82ac193 Update about-misskey.vue 2023-10-18 10:07:21 +09:00
nryeouo
d82b3a08e2 Update ja-JP.yml (#12053)
ポスト
2023-10-18 10:01:50 +09:00
taichan
f6e1ee1d40 users/notesでDBから参照した際にチャンネル投稿のみ取得される問題を修正 (#12056)
* fix(backend): ユーザーのノート取得でDBにfallbackした際にチャンネルノートのみになる

* Update CHANGELOG

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-10-18 09:55:15 +09:00
woxtu
0bddd0ceae Remove unnecessary nullish coalescing (#12058) 2023-10-18 09:54:18 +09:00
かっこかり
53099cad5a fix: admin/update-meta が保存できない (#12057)
* Fix: invalid param impressumUrl

* Update CHANGELOG.md
2023-10-18 09:48:49 +09:00
syuilo
145c5c8b2b update deps 2023-10-18 09:43:59 +09:00
anatawa12
5a3c6575dd 新規にフォローした人のwithRepliesをtrueにする機能を追加 (#12048)
* feat: add defaultWithReplies to MiUser

* feat: use defaultWithReplies when creating MiFollowing

* feat: update defaultWithReplies from API

* feat: return defaultWithReplies as a part of $i

* feat(frontend): configure defaultWithReplies

* docs(changelog): 新規にフォローした人のをデフォルトでTL二追加できるように

* fix: typo

* style: fix lint failure

* chore: improve UI text

* chore: make optional params of  UserFollowingService.follow() object

* chore: UserFollowingService.follow() accept withReplies

* chore: add withReplies to MiFollowRequest

* chore: process withReplies for follow request

* feat: accept withReplies on 'following/create' endpoint

* feat: store defaultWithReplies in client store

* Revert "feat: return defaultWithReplies as a part of $i"

This reverts commit f2cc4fe6

* Revert "feat: update defaultWithReplies from API"

This reverts commit 95e3cee6

* Revert "feat: add defaultWithReplies to MiUser"

This reverts commit 9f5ab14d70.

* feat: configuring withReplies in import-following

* feat(frontend): configure withReplies

* fix(frontend): incorrectly showRepliesToOthersInTimeline can be shown

* fix(backend): withReplies of following/create not working

* fix(frontend): importFollowing error

* fix: withReplies is not working with follow import

* fix(frontend): use v-model

* style: fix lint

---------

Co-authored-by: Sayamame-beans <61457993+sayamame-beans@users.noreply.github.com>
Co-authored-by: syuilo <syuilotan@yahoo.co.jp>
2023-10-17 20:56:17 +09:00
syuilo
e9db0680c4 update deps 2023-10-17 09:04:17 +09:00
syuilo
5686e1d2d0 refactor 2023-10-17 09:00:42 +09:00
syuilo
9338ab461e update deps 2023-10-16 20:13:05 +09:00
syuilo
7e4562d50a Update CHANGELOG.md 2023-10-16 20:12:06 +09:00
まっちゃてぃー
5efd01ba70 feat: サーバーサイレンス機能を追加 (#12031)
* 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 <Syuilotan@yahoo.co.jp>
2023-10-16 20:11:27 +09:00
syuilo
1966876320 fix test 2023-10-16 17:42:33 +09:00
syuilo
3ebed5aa3e feat: local-only antenna
Resolve #11869
2023-10-16 15:06:00 +09:00
syuilo
6a321ba340 fix test 2023-10-16 14:35:44 +09:00
syuilo
34d1b463a4 fix tests 2023-10-16 12:58:17 +09:00
syuilo
24437a04d4 fix of 1fa1d31696 2023-10-16 12:00:56 +09:00
syuilo
1fa1d31696 perf(backend): createdAtをidから取得するように & 無駄なDateインスタンスの生成を避けるように 2023-10-16 10:45:22 +09:00
syuilo
15706c8c2b Update CHANGELOG.md 2023-10-15 16:25:42 +09:00
syuilo
5117818f6f Update CHANGELOG.md 2023-10-15 10:37:03 +09:00
syuilo
3f4ee98405 perf(backend): improve streaming api performance (#12033)
* wip

* Update NoteEntityService.ts

* wip

* wip

* wip

* wip
2023-10-15 10:36:22 +09:00
syuilo
329830e2c3 perf(backend): tweak populateMyReaction 2023-10-14 10:05:44 +09:00
syuilo
061e389340 perf(backend): nyaizeを投稿時にやる
Resolve #12030
2023-10-14 10:04:13 +09:00
syuilo
096fa16c4c enhance(frontend): TLの返信表示オプションを記憶するように
Resolve #12016
2023-10-13 16:49:56 +09:00
syuilo
04908bc90c .js 2023-10-13 16:42:57 +09:00
syuilo
87c9870447 Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-10-12 09:20:41 +09:00
syuilo
34eeccf908 2023.10.1 2023-10-12 09:20:33 +09:00
syuilo
c2e177e37a New Crowdin updates (#12013)
* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)
2023-10-12 09:20:25 +09:00
syuilo
7b6b3ad821 update test 2023-10-11 18:25:02 +09:00
syuilo
7132958948 fix(frontend): 絵文字ピッカーで横に長いカスタム絵文字が見切れる問題を修正 2023-10-11 17:58:40 +09:00
syuilo
a26d9ea132 enhance(backend): LTLでフォローしているユーザーからの自分への返信が含まれるように 2023-10-11 17:29:24 +09:00
syuilo
566cb35370 update test 2023-10-11 14:30:18 +09:00
syuilo
cf3624a54f fix(backend): users/notesでセンシティブチャンネルの投稿が含まれる場合がある問題を修正 2023-10-11 12:26:07 +09:00
syuilo
8a302a9af4 Update CHANGELOG.md
Fix #12003
2023-10-11 11:44:12 +09:00
syuilo
7a8d5e5840 enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に
Resolve #12001
2023-10-11 10:15:44 +09:00
syuilo
1f0c27edf2 fix(backend): フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正 2023-10-11 08:18:00 +09:00
syuilo
26b7112b20 add tests 2023-10-11 08:02:29 +09:00
syuilo
854ac95511 fix(backend): センシティブ設定されたチャンネルの投稿をusers/notesで返さないように 2023-10-10 20:06:02 +09:00
syuilo
51b6a012a5 fix(frontend): ユーザープロフィールページでセンシティブなメディアが隠されない問題を修正 2023-10-10 19:49:25 +09:00
syuilo
085bcf24da Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2023-10-10 19:08:02 +09:00
syuilo
66940d6cf1 fix(backend): channels/timelineでミュートが効かない問題を修正 2023-10-10 19:07:59 +09:00
syuilo
61ff98c8dd New Crowdin updates (#12000)
* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)
2023-10-10 19:03:21 +09:00
syuilo
43fe0cfda8 Update CHANGELOG.md 2023-10-10 19:03:01 +09:00
syuilo
57b794edfb New Crowdin updates (#11999)
* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)
2023-10-10 18:14:09 +09:00
syuilo
47de264478 2023.10.0 2023-10-10 18:13:57 +09:00
syuilo
373c2af46a clean up 2023-10-10 18:11:58 +09:00
syuilo
f5e72f7d3e 🎨 CWボタンを大きく 2023-10-10 18:08:54 +09:00
syuilo
d81c833775 Update CHANGELOG.md 2023-10-10 16:34:22 +09:00
syuilo
cf6e53b2ac update deps 2023-10-10 16:26:48 +09:00
syuilo
9dd0f8c39b clean up 2023-10-10 16:25:06 +09:00
syuilo
d94380780f Update CHANGELOG.md 2023-10-10 10:47:11 +09:00
かっこかり
af1087aed4 Feat:「ファイルの詳細」ページを追加 (#11995)
* (add) ファイルビューア

* Update Changelog

* 既存のAPIを利用

* run api extratctor

* Change i18n

* (add) ページに関する説明を追加

* Update CHANGELOG

* (fix) design, classes
2023-10-10 10:43:43 +09:00
syuilo
9f33ce1cd0 fix of 0bb0c32908 2023-10-10 09:45:40 +09:00
syuilo
4eb9e50a36 2023.10.0-beta.15 2023-10-09 21:52:43 +09:00
syuilo
8ab3640291 fix of 0bb0c32908 2023-10-09 21:52:31 +09:00
syuilo
fc777be7bc 2023.10.0-beta.14 2023-10-09 21:23:18 +09:00
syuilo
edf847d966 fix of 0bb0c32908 2023-10-09 21:23:07 +09:00
syuilo
457b880eba 2023.10.0-beta.13 2023-10-09 20:55:53 +09:00
syuilo
13dbfef9f8 update deps 2023-10-09 20:55:40 +09:00
syuilo
11c9e193a4 fix(backend): Misskeyのバックエンドプロセスが終了しない
Resolve #10995
2023-10-09 20:47:49 +09:00
syuilo
0bb0c32908 enhance(backend): RedisへのTLの構築をListで行うように
#11404
2023-10-09 20:31:39 +09:00
syuilo
aafe80c121 2023.10.0-beta.12 2023-10-09 18:48:43 +09:00
syuilo
7473b2854f fix(backend): users/notesでsinceId指定時にデータベースにフォールバックするように修正 2023-10-09 18:14:38 +09:00
syuilo
04971ca565 perf(backend): untilDate/sinceDate指定時のクエリパフォーマンスを向上 2023-10-09 18:13:53 +09:00
syuilo
6ff98846e6 fix(backend): 「ファイル付きのみ」のTLでファイル無しの新着ノートが表示される
Fix #11939
2023-10-09 17:48:09 +09:00
syuilo
7066d61730 fix 2023-10-09 17:41:54 +09:00
281 changed files with 3381 additions and 2374 deletions

View File

@@ -5,7 +5,7 @@
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers-contrib/features/pnpm:2": {
"version": "8.8.0"
"version": "8.9.2"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "20.5.1"

View File

@@ -12,11 +12,42 @@
-->
## 2023.x.x (unreleased)
### General
- Feat: アンテナでローカルの投稿のみ収集できるようになりました
- Feat: サーバーサイレンス機能が追加されました
- Enhance: 依存関係の更新
- Enhance: 新規にフォローした人のをデフォルトでTLに追加できるように
- Enhance: HTLとLTLを2023.10.0アップデート以前まで遡れるように
- Enhance: フォロー/フォロー解除したときに過去分のHTLにも含まれる投稿が反映されるように
- Change: nyaizeはAPIレスポンス時ではなく投稿時に一度だけ非可逆的に行われるようになりました
### Client
- Enhance: TLの返信表示オプションを記憶するように
### Server
- Enhance: ストリーミングAPIのパフォーマンスを向上
- Fix: users/notesでDBから参照した際にチャンネル投稿のみ取得される問題を修正
- Fix: コントロールパネルの設定項目が正しく保存できない問題を修正
## 2023.10.1
### General
- Enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に
### Client
- Fix: 絵文字ピッカーで横に長いカスタム絵文字が見切れる問題を修正
### Server
- Fix: フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正
- Fix: users/notesでセンシティブチャンネルの投稿が含まれる場合がある問題を修正
## 2023.10.0
### NOTE
- 2023.9.2で導入されたノート編集機能はクオリティの高い実装が困難であることが判明したため撤回されました
- アップデート後、アップデートより前の時点にTLを遡ることはできません
-ップデート後であっても、今後のアップデートで2023.10.0以前のTLに遡れるようになる可能性はあります
- アップデートを行うと、タイムラインが一時的にリセットされます
-ンテナ内のノートも含む
- ソフトミュート設定はクライアントではなくサーバー側に保存されるようになったため、アップデートを行うとソフトミュートの設定がリセットされます
### Changes
- API: users/notes, notes/local-timeline で fileType 指定はできなくなりました
@@ -39,11 +70,15 @@
- Fix: ユーザーリストTLにチャンネル投稿が含まれる問題を修正
### Client
- Feat: 「ファイルの詳細」ページを追加
- ドライブのファイルの拡大プレビューができるように
- ファイルが添付されたノートの一覧が表示できるように
- Enhance: 二要素認証のバックアップコード一覧をテキストファイルでダウンロード可能に
- Enhance: 動画再生時のデフォルトボリュームを30%に
- Fix: リアクションしたユーザ一覧のUIが稀に左上に残ってしまう不具合を修正
### Server
- Enhance: drive/files/attached-notes がページネーションに対応しました
- Enhance: タイムライン取得時のパフォーマンスを大幅に向上
- Enhance: ハイライト取得時のパフォーマンスを大幅に向上
- Enhance: トレンドハッシュタグ取得時のパフォーマンスを大幅に向上
@@ -53,6 +88,8 @@
- Fix: nodeinfoにおいてCORS用のヘッダーが設定されていないのを修正
- Fix: 同じ種類のTLのストリーミングを複数接続できない問題を修正
- Fix: アンテナTLを途中までしかページネーションできなくなることがある問題を修正
- Fix: 「ファイル付きのみ」のTLでファイル無しの新着ートが流れる問題を修正
- Fix: プロセスが終了しない、あるいは非常に時間がかかる問題を修正
## 2023.9.3
### General

View File

@@ -995,9 +995,6 @@ _theme:
infoFg: "তথ্যের পাঠ্য"
infoWarnBg: "ওয়ার্নিং এর পটভূমি"
infoWarnFg: "ওয়ার্নিং এর পাঠ্য"
cwBg: "CW বাটনের পটভূমি"
cwFg: "CW বাটনের পাঠ্য"
cwHoverBg: "CW বাটনের পটভূমি (হভার)"
toastBg: "বিজ্ঞপ্তির পটভূমি"
toastFg: "বিজ্ঞপ্তির পাঠ্য"
buttonBg: "বাটনের পটভূমি"

View File

@@ -1622,9 +1622,6 @@ _theme:
infoFg: "Text informací"
infoWarnBg: "Pozadí varování"
infoWarnFg: "Text varování"
cwBg: "Pozadí CW tlačítka"
cwFg: "Text CW tlačítka"
cwHoverBg: "Pozadí CW tlačítka (Hover)"
toastBg: "Pozadí oznámení"
toastFg: "Text oznámení"
buttonBg: "Pozadí tlačítka"

View File

@@ -1685,9 +1685,6 @@ _theme:
infoFg: "Text von Informationen"
infoWarnBg: "Hintergrund von Warnungen"
infoWarnFg: "Text von Warnungen"
cwBg: "Hintergrund des Inhaltswarnungsknopfs"
cwFg: "Text des Inhaltswarnungsknopfs"
cwHoverBg: "Hintergrund des Inhaltswarnungsknopfs (Mouseover)"
toastBg: "Hintergrund von Benachrichtigungen"
toastFg: "Text von Benachrichtigungen"
buttonBg: "Hintergrund von Schaltflächen"
@@ -2144,3 +2141,11 @@ _moderationLogTypes:
createAd: "Werbung erstellt"
deleteAd: "Werbung gelöscht"
updateAd: "Werbung aktualisiert"
_fileViewer:
title: "Dateiinformationen"
type: "Dateityp"
size: "Dateigröße"
url: "URL"
uploadedAt: "Hochgeladen am"
attachedNotes: "Zugehörige Notizen"
thisPageCanBeSeenFromTheAuthor: "Nur der Benutzer, der diese Datei hochgeladen hat, kann diese Seite sehen."

View File

@@ -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"
@@ -1685,9 +1693,6 @@ _theme:
infoFg: "Information text"
infoWarnBg: "Warning background"
infoWarnFg: "Warning text"
cwBg: "CW button background"
cwFg: "CW button text"
cwHoverBg: "CW button background (Hover)"
toastBg: "Notification background"
toastFg: "Notification text"
buttonBg: "Button background"
@@ -2144,3 +2149,11 @@ _moderationLogTypes:
createAd: "Ad created"
deleteAd: "Ad deleted"
updateAd: "Ad updated"
_fileViewer:
title: "File details"
type: "File type"
size: "Filesize"
url: "URL"
uploadedAt: "Uploaded at"
attachedNotes: "Attached notes"
thisPageCanBeSeenFromTheAuthor: "This page can only be seen by the user who uploaded this file."

View File

@@ -1666,9 +1666,6 @@ _theme:
infoFg: "Texto de información"
infoWarnBg: "Fondo de advertencias"
infoWarnFg: "Texto de advertencias"
cwBg: "Fondo del botón CW"
cwFg: "Texto del botón CW"
cwHoverBg: "Fondo del botón CW (hover)"
toastBg: "Fondo de notificaciones"
toastFg: "Texto de notificaciones"
buttonBg: "Fondo de botón"

View File

@@ -45,6 +45,7 @@ pin: "Épingler sur le profil"
unpin: "Désépingler"
copyContent: "Copier le contenu"
copyLink: "Copier le lien"
copyLinkRenote: "Copier le lien de la renote"
delete: "Supprimer"
deleteAndEdit: "Supprimer et réécrire"
deleteAndEditConfirm: "Êtes-vous sûr de vouloir effacer cette note et la modifier ? Vous perdrez toutes les réactions, renotes et réponses."
@@ -129,6 +130,8 @@ unmarkAsSensitive: "Supprimer le marquage comme sensible"
enterFileName: "Entrer le nom du fichier"
mute: "Masquer"
unmute: "Ne plus masquer"
renoteMute: "Masquer les renotes"
renoteUnmute: "Ne plus masquer les renotes"
block: "Bloquer"
unblock: "Débloquer"
suspend: "Suspendre"
@@ -414,6 +417,7 @@ moderator: "Modérateur·rice·s"
moderation: "Modérations"
moderationNote: "Note de modération"
addModerationNote: "Ajouter une note de modération"
moderationLogs: "Journal de modération"
nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s"
securityKeyAndPasskey: "Sécurité et clés de sécurité"
securityKey: "Clé de sécurité"
@@ -472,6 +476,7 @@ aboutX: "À propos de {x}"
emojiStyle: "Style des émojis"
native: "Natif"
disableDrawer: "Les menus ne s'affichent pas dans le tiroir"
showNoteActionsOnlyHover: "Afficher les actions de note uniquement au survol"
noHistory: "Pas d'historique"
signinHistory: "Historique de connexion"
enableAdvancedMfm: "Activer la MFM avancée"
@@ -647,6 +652,7 @@ behavior: "Comportement"
sample: "Exemple"
abuseReports: "Signalements"
reportAbuse: "Signaler"
reportAbuseRenote: "Signaler la renote"
reportAbuseOf: "Signaler {name}"
fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien."
abuseReported: "Le rapport est envoyé. Merci."
@@ -671,6 +677,8 @@ clip: "Clip"
createNew: "Créer nouveau"
optional: "Facultatif"
createNewClip: "Créer un nouveau clip"
unclip: "Supprimer le clip"
confirmToUnclipAlreadyClippedNote: "Cette note fait déjà partie du clip « {name} ». Souhaitez-vous la supprimer de ce clip ?"
public: "Public"
private: "Privé"
i18nInfo: "Misskey est traduit dans différentes langues par des bénévoles. Vous pouvez contribuer à {link}."
@@ -933,12 +941,15 @@ unsubscribePushNotification: "Désactiver les notifications push"
pushNotificationAlreadySubscribed: "Les notifications push sont déjà activées"
pushNotificationNotSupported: "Votre navigateur ou votre instance ne prend pas en charge les notifications push"
sendPushNotificationReadMessage: "Supprimer les notifications push une fois que les notifications ou messages pertinents ont été lus."
windowMaximize: "Maximiser"
windowMinimize: "Minimaliser"
windowRestore: "Restaurer"
caption: "Libellé"
loggedInAsBot: "Connecté actuellement en tant que bot"
tools: "Outils"
cannotLoad: "Chargement impossible"
like: "J'aime"
unlike: "Ne plus aimer"
numberOfLikes: "Favoris"
show: "Affichage"
neverShow: "Ne plus afficher"
@@ -949,6 +960,7 @@ noRole: "Aucun rôle"
normalUser: "Simple utilisateur·rice"
undefined: "Non défini"
assign: "Attribuer"
unassign: "Retirer"
color: "Couleur"
manageCustomEmojis: "Gestion des émojis personnalisés"
preset: "Préréglage"
@@ -958,12 +970,16 @@ thisPostMayBeAnnoying: "Cette note peut gêner d'autres personnes."
thisPostMayBeAnnoyingHome: "Publier vers le fil principal"
thisPostMayBeAnnoyingCancel: "Annuler"
thisPostMayBeAnnoyingIgnore: "Publier quand-même"
collapseRenotes: "Réduire les renotes déjà vues"
internalServerError: "Erreur interne du serveur"
copyErrorInfo: "Copier les détails de lerreur"
exploreOtherServers: "Trouver une autre instance"
disableFederationOk: "Désactiver"
likeOnly: "Les favoris uniquement"
sensitiveWords: "Mots sensibles"
notesSearchNotAvailable: "La recherche de notes n'est pas disponible."
license: "Licence"
myClips: "Mes clips"
video: "Vidéo"
videos: "Vidéos"
dataSaver: "Économiseur de données"
@@ -973,6 +989,7 @@ accountMovedShort: "Ce compte a migré"
operationForbidden: "Opération non autorisée"
addMemo: "Ajouter un mémo"
reactionsList: "Réactions"
renotesList: "Liste de renotes"
notificationDisplay: "Style des notifications"
leftTop: "En haut à gauche"
rightTop: "En haut à droite"
@@ -982,6 +999,7 @@ vertical: "Vertical"
horizontal: "Latéral"
serverRules: "Règles du serveur"
archive: "Archive"
displayOfNote: "Affichage de la note"
youFollowing: "Abonné·e"
options: "Options"
later: "Plus tard"
@@ -1001,6 +1019,7 @@ pinnedList: "Liste épinglée"
notifyNotes: "Notifier à propos des nouvelles notes"
authentication: "Authentification"
authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer"
showRenotes: "Afficher les renotes"
_announcement:
readConfirmTitle: "Marquer comme lu ?"
_initialAccountSetting:
@@ -1082,12 +1101,20 @@ _achievements:
title: "Beaucoup d'amis"
_followers10:
title: "Abonnez-moi !"
description: "Obtenir plus de 10 abonné·e·s"
_followers50:
description: "Obtenir plus de 50 abonné·e·s"
_followers100:
title: "Populaire"
description: "Obtenir plus de 100 abonné·e·s"
_followers300:
description: "Obtenir plus de 300 abonné·e·s"
_followers500:
title: "Tour radio"
description: "Obtenir plus de 500 abonné·e·s"
_followers1000:
title: "Influenceur·euse"
description: "Obtenir plus de 1000 abonné·e·s"
_iLoveMisskey:
title: "Jadore Misskey"
description: "Publication « J❤ #Misskey »"
@@ -1151,6 +1178,7 @@ _role:
high: "Haute"
_options:
canManageCustomEmojis: "Gestion des émojis personnalisés"
wordMuteMax: "Nombre maximal de caractères dans le filtre de mots"
_sensitiveMediaDetection:
description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement."
sensitivity: "Sensibilité de la détection"
@@ -1330,9 +1358,6 @@ _theme:
infoFg: "Texte d'information"
infoWarnBg: "Arrière-plan des avertissements"
infoWarnFg: "Texte davertissement"
cwBg: "Arrière-plan du CW"
cwFg: "Texte du bouton CW"
cwHoverBg: "Arrière-plan du bouton CW (survolé)"
toastBg: "Arrière-plan de la bulle de notification"
toastFg: "Texte de la bulle de notification"
buttonBg: "Arrière-plan du bouton"

View File

@@ -1627,9 +1627,6 @@ _theme:
infoFg: "Teks informasi"
infoWarnBg: "Latar belakang peringatan"
infoWarnFg: "Teks peringatan"
cwBg: "Latar belakang tombol Sembunyikan Konten"
cwFg: "Teks tombol Sembunyikan Konten"
cwHoverBg: "Latar belakang tombol Sembunyikan Konten (Mengambang)"
toastBg: "Latar belakang notifikasi"
toastFg: "Teks notifikasi"
buttonBg: "Latar belakang tombol"

17
locales/index.d.ts vendored
View File

@@ -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;
@@ -534,6 +537,7 @@ export interface Locale {
"deleteAll": string;
"showFixedPostForm": string;
"showFixedPostFormInChannel": string;
"withRepliesByDefaultForNewlyFollowed": string;
"newNoteRecived": string;
"sounds": string;
"sound": string;
@@ -1796,9 +1800,6 @@ export interface Locale {
"infoFg": string;
"infoWarnBg": string;
"infoWarnFg": string;
"cwBg": string;
"cwFg": string;
"cwHoverBg": string;
"toastBg": string;
"toastFg": string;
"buttonBg": string;
@@ -2054,6 +2055,7 @@ export interface Locale {
"userLists": string;
"excludeMutingUsers": string;
"excludeInactiveUsers": string;
"withReplies": string;
};
"_charts": {
"federation": string;
@@ -2294,6 +2296,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;

View File

@@ -113,7 +113,7 @@ cantReRenote: "È impossibile rinotare una Rinota."
quote: "Cita"
inChannelRenote: "Rinota nel canale"
inChannelQuote: "Cita nel canale"
pinnedNote: "Nota fissata"
pinnedNote: "Nota in primo piano"
pinned: "Fissa sul profilo"
you: "Tu"
clickToShow: "Clicca per visualizzare"
@@ -364,7 +364,7 @@ pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagi
pinnedPages: "Pagine in evidenza"
pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga."
pinnedClipId: "ID della Clip in evidenza"
pinnedNotes: "Nota fissata"
pinnedNotes: "Note in primo piano"
hcaptcha: "hCaptcha"
enableHcaptcha: "Abilita hCaptcha"
hcaptchaSiteKey: "Chiave del sito"
@@ -384,7 +384,7 @@ name: "Nome"
antennaSource: "Fonte dell'antenna"
antennaKeywords: "Parole chiavi da ricevere"
antennaExcludeKeywords: "Parole chiavi da escludere"
antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"."
antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)."
notifyAntenna: "Invia notifiche delle nuove note"
withFileAntenna: "Solo note con file in allegato"
enableServiceworker: "Abilita ServiceWorker"
@@ -393,7 +393,7 @@ caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole"
withReplies: "Includere le risposte"
connectedTo: "Connessione ai seguenti profili:"
notesAndReplies: "Note e risposte"
withFiles: "Con file in allegato"
withFiles: "Con allegati"
silence: "Silenzia"
silenceConfirm: "Vuoi davvero silenziare questo profilo?"
unsilence: "Riattiva"
@@ -1121,11 +1121,11 @@ unnotifyNotes: "Interrompi le notifiche di nuove Note"
authentication: "Autenticazione"
authenticationRequiredToContinue: "Per procedere, è richiesta l'autenticazione"
dateAndTime: "Data e Ora"
showRenotes: "Leggi le Rinota"
showRenotes: "Includi le Rinota"
edited: "Modificato"
notificationRecieveConfig: "Preferenze di notifica"
mutualFollow: "Follow reciproco"
fileAttachedOnly: "Con file in allegato"
fileAttachedOnly: "Solo con allegati"
showRepliesToOthersInTimeline: "Risposte altrui nella TL"
hideRepliesToOthersInTimeline: "Nascondi Riposte altrui nella TL"
externalServices: "Servizi esterni"
@@ -1533,6 +1533,10 @@ _ad:
reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso"
hide: "Nascondi"
timezoneinfo: "Il giorno della settimana è determinato in base al fuso orario del server."
adsSettings: "Impostazioni banner"
notesPerOneAd: "Quantità di Note tra i banner"
setZeroToDisable: "Imposta 0 (zero) per disattivare la distribuzione dei banner durante gli aggiornamenti in tempo reale"
adsTooClose: "Attenzione, l'intervallo di pubblicazione dei banner è molto breve, potrebbe infastidire significativamente la fruizione"
_forgotPassword:
enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo."
ifNoEmail: "Se il tuo indirizzo email non risulta registrato, contatta l'amministrazione dell'istanza."
@@ -1616,7 +1620,7 @@ _menuDisplay:
hide: "Nascondere"
_wordMute:
muteWords: "Parole da filtrare"
muteWordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con una interruzione di riga, indica la condizione \"O\""
muteWordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)."
muteWordsDescription2: "Se vuoi indicare delle Espressioni Regolari (regexp), metti la condizione all'interno di due slash (/)"
_instanceMute:
instanceMuteDescription: "Disattiva tutte le note, le note di rinvio (condivisione) dell'istanza configurata, comprese le risposte agli utenti dell'istanza."
@@ -1626,7 +1630,7 @@ _instanceMute:
_theme:
explore: "Esplora temi"
install: "Installa un tema"
manage: "Gerisci temi"
manage: "Gestione temi"
code: "Codice tema"
description: "Descrizione"
installed: "{name} è installato"
@@ -1681,9 +1685,6 @@ _theme:
infoFg: "Testo di informazioni"
infoWarnBg: "Sfondo degli avvisi"
infoWarnFg: "Testo di avviso"
cwBg: "Sfondo del CW"
cwFg: "Testo del pulsante CW"
cwHoverBg: "Sfondo del pulsante CW (sorvolato)"
toastBg: "Sfondo di notifica a comparsa"
toastFg: "Testo di notifica a comparsa"
buttonBg: "Sfondo del pulsante"
@@ -1885,7 +1886,7 @@ _visibility:
followersDescription: "Visibile solo ai tuoi follower"
specified: "Nota diretta"
specifiedDescription: "Visibile solo ai profili menzionati"
disableFederation: "Non federare"
disableFederation: "Senza federazione"
disableFederationDescription: "Non spedire attività alle altre istanze remote"
_postForm:
replyPlaceholder: "Rispondi a questa nota..."

View File

@@ -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: "ブロックしたユーザー"
@@ -531,6 +534,7 @@ serverLogs: "サーバーログ"
deleteAll: "全て削除"
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)"
withRepliesByDefaultForNewlyFollowed: "フォローする際、デフォルトで返信をTLに含むようにする"
newNoteRecived: "新しいノートがあります"
sounds: "サウンド"
sound: "サウンド"
@@ -589,7 +593,7 @@ poll: "アンケート"
useCw: "内容を隠す"
enablePlayer: "プレイヤーを開く"
disablePlayer: "プレイヤーを閉じる"
expandTweet: "ツイートを展開する"
expandTweet: "ポストを展開する"
themeEditor: "テーマエディター"
description: "説明"
describeFile: "キャプションを付ける"
@@ -794,7 +798,7 @@ active: "アクティブ"
offline: "オフライン"
notRecommended: "非推奨"
botProtection: "Botプロテクション"
instanceBlocking: "サーバーブロック"
instanceBlocking: "サーバーブロック・サイレンス"
selectAccount: "アカウントを選択"
switchAccount: "アカウントを切り替え"
enabled: "有効"
@@ -1714,9 +1718,6 @@ _theme:
infoFg: "情報の文字"
infoWarnBg: "警告の背景"
infoWarnFg: "警告の文字"
cwBg: "CW ボタンの背景"
cwFg: "CW ボタンの文字"
cwHoverBg: "CW ボタンの背景 (ホバー)"
toastBg: "通知トーストの背景"
toastFg: "通知トーストの文字"
buttonBg: "ボタンの背景"
@@ -1969,6 +1970,7 @@ _exportOrImport:
userLists: "リスト"
excludeMutingUsers: "ミュートしているユーザーを除外"
excludeInactiveUsers: "使われていないアカウントを除外"
withReplies: "インポートした人による返信をTLに含むようにする"
_charts:
federation: "連合"
@@ -2206,3 +2208,12 @@ _moderationLogTypes:
createAd: "広告を作成"
deleteAd: "広告を削除"
updateAd: "広告を更新"
_fileViewer:
title: "ファイルの詳細"
type: "ファイルタイプ"
size: "ファイルサイズ"
url: "URL"
uploadedAt: "追加日"
attachedNotes: "添付されているノート"
thisPageCanBeSeenFromTheAuthor: "このページは、このファイルをアップロードしたユーザーしか閲覧できません。"

View File

@@ -1649,9 +1649,6 @@ _theme:
infoFg: "情報の文字"
infoWarnBg: "警告の背景"
infoWarnFg: "警告の文字"
cwBg: "CW ボタンの背景"
cwFg: "CW ボタンの文字"
cwHoverBg: "CW ボタンの背景 (ホバー)"
toastBg: "通知トーストの背景"
toastFg: "通知トーストの文字"
buttonBg: "ボタンの背景"

View File

@@ -1663,9 +1663,6 @@ _theme:
infoFg: "정보창 텍스트"
infoWarnBg: "경고창 배경"
infoWarnFg: "경고창 텍스트"
cwBg: "CW 버튼 배경"
cwFg: "CW 버튼 텍스트"
cwHoverBg: "CW 버튼 배경 (호버)"
toastBg: "알림창 배경"
toastFg: "알림창 텍스트"
buttonBg: "버튼 배경"

View File

@@ -1043,9 +1043,6 @@ _theme:
infoFg: "Tekst informacji"
infoWarnBg: "Tło ostrzeżenia"
infoWarnFg: "Tekst ostrzeżenia"
cwBg: "Tło CW"
cwFg: "Tekst CW"
cwHoverBg: "Tło CW (po najechaniu)"
toastBg: "Tło powiadomień"
toastFg: "Tekst powiadomień"
buttonBg: "Tło przycisku"

View File

@@ -1551,9 +1551,6 @@ _theme:
infoFg: "Текст сообщения"
infoWarnBg: "Фон предупреждения"
infoWarnFg: "Текст предупреждения"
cwBg: "Фон предупреждения о содержимом"
cwFg: "Текст предупреждения о содержимом"
cwHoverBg: "Фон предупреждения о содержимом (под указателем)"
toastBg: "Фон оповещения"
toastFg: "Текст оповещения"
buttonBg: "Фон кнопки"

View File

@@ -1102,9 +1102,6 @@ _theme:
infoFg: "Informačný text"
infoWarnBg: "Pozadie varovania"
infoWarnFg: "Text varovania"
cwBg: "CW pozadie tlačidla"
cwFg: "CW text tlačidla"
cwHoverBg: "CW pozadie tlačidla (pod kurzorom)"
toastBg: "Pozadie upozornenia"
toastFg: "Text upozornenia"
buttonBg: "Pozadie tlačidla"

View File

@@ -1663,9 +1663,6 @@ _theme:
infoFg: "ข้อความข้อมูล"
infoWarnBg: "คำเตือนพื้นหลัง"
infoWarnFg: "คำเตือนข้อความ"
cwBg: "ปุ่ม CW พื้นหลัง"
cwFg: "ปุ่ม CW ข้อความ"
cwHoverBg: "ปุ่ม CW พื้นหลัง (โฮเวอร์)"
toastBg: "ประวัติการแจ้งเตือน"
toastFg: "ข้อความแจ้งเตือน"
buttonBg: "ปุ่มพื้นหลัง"

View File

@@ -1290,9 +1290,6 @@ _theme:
infoFg: "Текст інформації"
infoWarnBg: "Фон попередження"
infoWarnFg: "Текст попередження"
cwBg: "Фон чутливого змісту"
cwFg: "Текст чутливого змісту"
cwHoverBg: "Фон чутливого змісту (при наведенні)"
toastBg: "Фон повідомлення"
toastFg: "Текст повідомлення"
buttonBg: "Фон кнопки"

View File

@@ -1467,9 +1467,6 @@ _theme:
infoFg: "Chữ thông tin"
infoWarnBg: "Nền cảnh báo"
infoWarnFg: "Chữ cảnh báo"
cwBg: "Nền nút nội dung ẩn"
cwFg: "Chữ nút nội dung ẩn"
cwHoverBg: "Nền nút nội dung ẩn (Chạm)"
toastBg: "Nền thông báo"
toastFg: "Chữ thông báo"
buttonBg: "Nền nút"

View File

@@ -1673,9 +1673,6 @@ _theme:
infoFg: "信息文本"
infoWarnBg: "警告背景"
infoWarnFg: "警告文本"
cwBg: "隐藏内容按钮背景"
cwFg: "隐藏内容按钮文本"
cwHoverBg: "隐藏内容按钮背景(悬停)"
toastBg: "Toast 通知背景"
toastFg: "Toast 通知文本"
buttonBg: "按钮背景"

View File

@@ -1685,9 +1685,6 @@ _theme:
infoFg: "資訊內容"
infoWarnBg: "警告背景"
infoWarnFg: "警告文字"
cwBg: "隱藏內容按鈕背景"
cwFg: "隱藏內容按鈕文字"
cwHoverBg: "隱藏內容按鈕背景(懸浮)"
toastBg: "通知背景"
toastFg: "通知文本"
buttonBg: "按鈕背景"
@@ -2144,3 +2141,11 @@ _moderationLogTypes:
createAd: "建立廣告"
deleteAd: "刪除廣告"
updateAd: "更新廣告"
_fileViewer:
title: "檔案詳細資訊"
type: "檔案類型 "
size: "檔案大小"
url: "URL"
uploadedAt: "加入日期"
attachedNotes: "含有附件的貼文"
thisPageCanBeSeenFromTheAuthor: "本頁面僅限上傳了這個檔案的使用者可以檢視。"

View File

@@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "2023.10.0-beta.11",
"version": "2023.10.2-beta.1",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "pnpm@8.8.0",
"packageManager": "pnpm@8.9.2",
"workspaces": [
"packages/frontend",
"packages/backend",
@@ -51,11 +51,11 @@
"typescript": "5.2.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "6.7.4",
"@typescript-eslint/parser": "6.7.4",
"@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.8.0",
"cross-env": "7.0.3",
"cypress": "13.3.0",
"eslint": "8.50.0",
"cypress": "13.3.1",
"eslint": "8.51.0",
"start-server-and-test": "2.0.1"
},
"optionalDependencies": {

View File

@@ -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"`);
}
}

View File

@@ -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") `);
}
}

View File

@@ -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"`);
}
}

View File

@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class FollowRequestWithReplies1697441463087 {
name = 'FollowRequestWithReplies1697441463087'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "follow_request" ADD "withReplies" boolean NOT NULL DEFAULT false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "follow_request" DROP COLUMN "withReplies"`);
}
}

View File

@@ -59,9 +59,9 @@
"@aws-sdk/client-s3": "3.412.0",
"@aws-sdk/lib-storage": "3.412.0",
"@smithy/node-http-handler": "2.1.5",
"@bull-board/api": "5.8.4",
"@bull-board/fastify": "5.8.4",
"@bull-board/ui": "5.8.4",
"@bull-board/api": "5.9.1",
"@bull-board/fastify": "5.9.1",
"@bull-board/ui": "5.9.1",
"@discordapp/twemoji": "14.1.2",
"@fastify/accepts": "4.2.0",
"@fastify/cookie": "9.1.0",
@@ -75,10 +75,10 @@
"@nestjs/core": "10.2.7",
"@nestjs/testing": "10.2.7",
"@peertube/http-signature": "1.7.0",
"@simplewebauthn/server": "8.2.0",
"@simplewebauthn/server": "8.3.2",
"@sinonjs/fake-timers": "11.1.0",
"@swc/cli": "0.1.62",
"@swc/core": "1.3.92",
"@swc/core": "1.3.93",
"accepts": "1.3.8",
"ajv": "8.12.0",
"archiver": "6.0.1",
@@ -86,7 +86,7 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.2",
"bullmq": "4.12.2",
"bullmq": "4.12.4",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.1",
"chalk": "5.3.0",
@@ -97,7 +97,7 @@
"content-disposition": "0.5.4",
"date-fns": "2.30.0",
"deep-email-validator": "0.1.21",
"fastify": "4.23.2",
"fastify": "4.24.2",
"feed": "4.2.2",
"file-type": "18.5.0",
"fluent-ffmpeg": "2.1.2",
@@ -121,16 +121,16 @@
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
"ms": "3.0.0-canary.1",
"nanoid": "5.0.1",
"nanoid": "5.0.2",
"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": "1.12.0",
"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",
@@ -155,7 +155,7 @@
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"summaly": "github:misskey-dev/summaly",
"systeminformation": "5.21.11",
"systeminformation": "5.21.12",
"tinycolor2": "1.6.0",
"tmp": "0.2.1",
"tsc-alias": "1.8.8",
@@ -173,36 +173,36 @@
"@jest/globals": "29.7.0",
"@simplewebauthn/typescript-types": "8.0.0",
"@swc/jest": "0.2.29",
"@types/accepts": "1.3.5",
"@types/archiver": "5.3.3",
"@types/bcryptjs": "2.4.4",
"@types/body-parser": "1.19.3",
"@types/accepts": "1.3.6",
"@types/archiver": "5.3.4",
"@types/bcryptjs": "2.4.5",
"@types/body-parser": "1.19.4",
"@types/cbor": "6.0.0",
"@types/color-convert": "2.0.1",
"@types/content-disposition": "0.5.6",
"@types/color-convert": "2.0.2",
"@types/content-disposition": "0.5.7",
"@types/fluent-ffmpeg": "2.1.22",
"@types/http-link-header": "1.0.3",
"@types/jest": "29.5.5",
"@types/js-yaml": "4.0.6",
"@types/js-yaml": "4.0.7",
"@types/jsdom": "21.1.3",
"@types/jsonld": "1.5.10",
"@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.6",
"@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.5",
"@types/pug": "2.0.7",
"@types/punycode": "2.1.0",
"@types/qrcode": "1.5.2",
"@types/random-seed": "0.3.3",
"@types/ratelimiter": "3.4.4",
"@types/rename": "1.0.5",
"@types/sanitize-html": "2.9.1",
"@types/sanitize-html": "2.9.2",
"@types/semver": "7.5.3",
"@types/sharp": "0.32.0",
"@types/simple-oauth2": "5.0.5",
@@ -211,12 +211,12 @@
"@types/tmp": "0.2.4",
"@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",
"@types/ws": "8.5.7",
"@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.8.0",
"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",

View File

@@ -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();

View File

@@ -180,13 +180,13 @@ export class AccountMoveService {
{ muteeId: dst.id, expiresAt: IsNull() },
).then(mutings => mutings.map(muting => muting.muterId));
const newMutings: Map<string, { muterId: string; muteeId: string; createdAt: Date; expiresAt: Date | null; }> = new Map();
const newMutings: Map<string, { muterId: string; muteeId: string; expiresAt: Date | null; }> = new Map();
// 重複しないようにIDを生成
const genId = (): string => {
let id: string;
do {
id = this.idService.genId();
id = this.idService.gen();
} while (newMutings.has(id));
return id;
};
@@ -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,20 +227,19 @@ export class AccountMoveService {
},
}).then(memberships => memberships.map(membership => membership.userListId));
const newMemberships: Map<string, { createdAt: Date; userId: string; userListId: string; userListUserId: string; }> = new Map();
const newMemberships: Map<string, { userId: string; userListId: string; userListUserId: string; }> = new Map();
// 重複しないようにIDを生成
const genId = (): string => {
let id: string;
do {
id = this.idService.genId();
id = this.idService.gen();
} while (newMemberships.has(id));
return id;
};
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,

View File

@@ -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<MiAnnouncement>, 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<void> {
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,

View File

@@ -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 { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable()
@@ -38,6 +39,7 @@ export class AntennaService implements OnApplicationShutdown {
private utilityService: UtilityService,
private globalEventService: GlobalEventService,
private funoutTimelineService: FunoutTimelineService,
) {
this.antennasFetched = false;
this.antennas = [];
@@ -55,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;
@@ -77,9 +77,6 @@ export class AntennaService implements OnApplicationShutdown {
@bindThis
public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<void> {
// リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、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 +84,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.funoutTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline);
this.globalEventService.publishAntennaStream(antenna.id, 'note', note);
}
@@ -106,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') {

View File

@@ -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,
});

View File

@@ -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 { FunoutTimelineService } from './FunoutTimelineService.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 $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService };
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,
FunoutTimelineService,
ChartLoggerService,
FederationChart,
NotesChart,
@@ -446,6 +449,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SearchService,
$ClipService,
$FeaturedService,
$FunoutTimelineService,
$ChartLoggerService,
$FederationChart,
$NotesChart,
@@ -572,6 +576,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
SearchService,
ClipService,
FeaturedService,
FunoutTimelineService,
FederationChart,
NotesChart,
UsersChart,
@@ -696,6 +701,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SearchService,
$ClipService,
$FeaturedService,
$FunoutTimelineService,
$FederationChart,
$NotesChart,
$UsersChart,

View File

@@ -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,

View File

@@ -69,7 +69,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
}, moderator?: MiUser): Promise<MiEmoji> {
const emoji = await this.emojisRepository.insert({
id: this.idService.genId(),
id: this.idService.gen(),
updatedAt: new Date(),
name: data.name,
category: data.category,
@@ -331,7 +331,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
const queryOrNull = async () => (await this.emojisRepository.findOneBy({
name,
host: host ?? IsNull(),
host,
})) ?? null;
const emoji = await this.cache.fetch(`${name} ${host}`, queryOrNull);

View File

@@ -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;

View File

@@ -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]));

View File

@@ -0,0 +1,85 @@
/*
* 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 FunoutTimelineService {
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<string[][]> {
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),
);
});
}
@bindThis
public purge(name: string) {
return this.redisForTimelines.del('list:' + name);
}
}

View File

@@ -45,7 +45,7 @@ export class HashtagService {
await this.updateHashtag(user, tag, true, true);
}
for (const tag of (user.tags ?? []).filter(x => !tags.includes(x))) {
for (const tag of user.tags.filter(x => !tags.includes(x))) {
await this.updateHashtag(user, tag, true, false);
}
}
@@ -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,

View File

@@ -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');
}
}

View File

@@ -24,8 +24,7 @@ export class ModerationLogService {
@bindThis
public async log<T extends typeof moderationLogTypes[number]>(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) ?? {},

View File

@@ -54,6 +54,9 @@ 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 { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
import { nyaize } from '@/misc/nyaize.js';
import { UtilityService } from '@/core/UtilityService.js';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@@ -194,6 +197,7 @@ export class NoteCreateService implements OnApplicationShutdown {
private idService: IdService,
private globalEventService: GlobalEventService,
private queueService: QueueService,
private funoutTimelineService: FunoutTimelineService,
private noteReadService: NoteReadService,
private notificationService: NotificationService,
private relayService: RelayService,
@@ -212,6 +216,7 @@ export class NoteCreateService implements OnApplicationShutdown {
private perUserNotesChart: PerUserNotesChart,
private activeUsersChart: ActiveUsersChart,
private instanceChart: InstanceChart,
private utilityService: UtilityService,
) { }
@bindThis
@@ -219,9 +224,11 @@ 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<MiNote> {
let patsedText: mfm.MfmNode[] | null = null;
// チャンネル外にリプライしたら対象のスコープに合わせる
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
@@ -245,8 +252,10 @@ export class NoteCreateService implements OnApplicationShutdown {
if (data.channel != null) data.visibleUsers = [];
if (data.channel != null) data.localOnly = true;
const meta = await this.metaService.fetch();
if (data.visibility === 'public' && data.channel == null) {
const sensitiveWords = (await this.metaService.fetch()).sensitiveWords;
const sensitiveWords = meta.sensitiveWords;
if (this.isSensitive(data, sensitiveWords)) {
data.visibility = 'home';
} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
@@ -254,6 +263,12 @@ export class NoteCreateService implements OnApplicationShutdown {
}
}
const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
if (data.visibility === 'public' && inSilencedInstance && user.host !== null) {
data.visibility = 'home';
}
if (data.renote) {
switch (data.renote.visibility) {
case 'public':
@@ -300,6 +315,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 = 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;
}
@@ -310,7 +344,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)!))
@@ -325,7 +359,7 @@ export class NoteCreateService implements OnApplicationShutdown {
mentionedUsers = data.apMentions ?? await this.extractMentionedUsers(user, combinedTokens);
}
tags = tags.filter(tag => Array.from(tag ?? '').length <= 128).splice(0, 32);
tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32);
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
mentionedUsers.push(await this.usersRepository.findOneByOrFail({ id: data.reply!.userId }));
@@ -347,14 +381,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 */ },
@@ -366,8 +392,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,
@@ -466,7 +491,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();
@@ -560,7 +584,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);
@@ -822,20 +846,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.funoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r);
this.funoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
const channelFollowings = await this.channelFollowingsRepository.find({
where: {
@@ -845,18 +863,9 @@ export class NoteCreateService implements OnApplicationShutdown {
});
for (const channelFollowing of channelFollowings) {
redisPipeline.xadd(
`homeTimeline:${channelFollowing.followerId}`,
'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(),
'*',
'note', note.id);
this.funoutTimelineService.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.funoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
}
}
} else {
@@ -889,23 +898,14 @@ 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;
}
redisPipeline.xadd(
`homeTimeline:${following.followerId}`,
'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(),
'*',
'note', note.id);
this.funoutTimelineService.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.funoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
}
}
@@ -916,77 +916,41 @@ 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;
}
redisPipeline.xadd(
`userListTimeline:${userListMembership.userListId}`,
'MAXLEN', '~', meta.perUserListTimelineCacheMax.toString(),
'*',
'note', note.id);
this.funoutTimelineService.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.funoutTimelineService.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.funoutTimelineService.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.funoutTimelineService.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);
} else {
redisPipeline.xadd(
`userTimeline:${user.id}`,
'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(),
'*',
'note', note.id);
this.funoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
if (note.visibility === 'public' && note.userHost == null) {
this.funoutTimelineService.push('localTimelineWithReplies', note.id, 300, r);
}
} else {
this.funoutTimelineService.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.funoutTimelineService.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.funoutTimelineService.push('localTimeline', note.id, 1000, r);
if (note.fileIds.length > 0) {
redisPipeline.xadd(
'localTimelineWithFiles',
'MAXLEN', '~', '500',
'*',
'note', note.id);
this.funoutTimelineService.push('localTimelineWithFiles', note.id, 500, r);
}
}
}
@@ -998,7 +962,7 @@ export class NoteCreateService implements OnApplicationShutdown {
}
}
redisPipeline.exec();
r.exec();
}
@bindThis

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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.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}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) });
q.orderBy(`${q.alias}.createdAt`, 'ASC');
q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.gen(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.gen(untilDate) });
q.orderBy(`${q.alias}.id`, 'DESC');
} else {
q.orderBy(`${q.alias}.id`, 'DESC');
}
@@ -124,7 +127,7 @@ export class QueryService {
}
@bindThis
public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: MiUser): void {
public generateMutedUserQuery(q: SelectQueryBuilder<any>, 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 });

View File

@@ -237,10 +237,11 @@ export class QueueService {
}
@bindThis
public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id']) {
public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id'], withReplies?: boolean) {
return this.dbQueue.add('importFollowing', {
user: { id: user.id },
fileId: fileId,
withReplies,
}, {
removeOnComplete: true,
removeOnFail: true,
@@ -248,8 +249,8 @@ export class QueueService {
}
@bindThis
public createImportFollowingToDbJob(user: ThinUser, targets: string[]) {
const jobs = targets.map(rel => this.generateToDbJobData('importFollowingToDb', { user, target: rel }));
public createImportFollowingToDbJob(user: ThinUser, targets: string[], withReplies?: boolean) {
const jobs = targets.map(rel => this.generateToDbJobData('importFollowingToDb', { user, target: rel, withReplies }));
return this.dbQueue.addBulk(jobs);
}
@@ -342,7 +343,7 @@ export class QueueService {
}
@bindThis
public createFollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string, silent?: boolean }[]) {
public createFollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string, silent?: boolean, withReplies?: boolean }[]) {
const jobs = followings.map(rel => this.generateRelationshipJobData('follow', rel));
return this.relationshipQueue.addBulk(jobs);
}
@@ -384,6 +385,7 @@ export class QueueService {
to: { id: data.to.id },
silent: data.silent,
requestId: data.requestId,
withReplies: data.withReplies,
},
opts: {
removeOnComplete: true,

View File

@@ -148,13 +148,12 @@ export class ReactionService {
reaction = FALLBACK;
}
} else {
reaction = this.normalize(reaction ?? null);
reaction = this.normalize(reaction);
}
}
const record: MiNoteReaction = {
id: this.idService.genId(),
createdAt: new Date(),
id: this.idService.gen(),
noteId: note.id,
userId: user.id,
reaction,

View File

@@ -54,7 +54,7 @@ export class RelayService {
@bindThis
public async addRelay(inbox: string): Promise<MiRelay> {
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]));

View File

@@ -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 { FunoutTimelineService } from '@/core/FunoutTimelineService.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 funoutTimelineService: FunoutTimelineService,
) {
//this.onMessage = this.onMessage.bind(this);
@@ -123,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),
});
@@ -137,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),
};
@@ -157,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,
});
}
@@ -196,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;
@@ -380,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<void> {
const now = new Date();
const now = Date.now();
const role = await this.rolesRepository.findOneByOrFail({ id: roleId });
@@ -390,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,
@@ -401,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,
@@ -472,12 +470,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.funoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline);
this.globalEventService.publishRoleTimelineStream(role.id, 'note', note);
}
@@ -488,8 +481,7 @@ export class RoleService implements OnApplicationShutdown {
public async create(values: Partial<MiRole>, moderator?: MiUser): Promise<MiRole> {
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,

View File

@@ -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,

View File

@@ -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),

View File

@@ -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,

View File

@@ -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,8 @@ 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 { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
import Logger from '../logger.js';
const logger = new Logger('following/create');
@@ -71,6 +73,7 @@ export class UserFollowingService implements OnModuleInit {
private instancesRepository: InstancesRepository,
private cacheService: CacheService,
private utilityService: UtilityService,
private userEntityService: UserEntityService,
private idService: IdService,
private queueService: QueueService,
@@ -81,6 +84,7 @@ export class UserFollowingService implements OnModuleInit {
private webhookService: WebhookService,
private apRendererService: ApRendererService,
private accountMoveService: AccountMoveService,
private funoutTimelineService: FunoutTimelineService,
private perUserFollowingChart: PerUserFollowingChart,
private instanceChart: InstanceChart,
) {
@@ -91,7 +95,15 @@ export class UserFollowingService implements OnModuleInit {
}
@bindThis
public async follow(_follower: { id: MiUser['id'] }, _followee: { id: MiUser['id'] }, requestId?: string, silent = false): Promise<void> {
public async follow(
_follower: { id: MiUser['id'] },
_followee: { id: MiUser['id'] },
{ requestId, silent = false, withReplies }: {
requestId?: string,
silent?: boolean,
withReplies?: boolean,
} = {},
): Promise<void> {
const [follower, followee] = await Promise.all([
this.usersRepository.findOneByOrFail({ id: _follower.id }),
this.usersRepository.findOneByOrFail({ id: _followee.id }),
@@ -118,15 +130,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;
@@ -168,12 +181,12 @@ export class UserFollowingService implements OnModuleInit {
}
if (!autoAccept) {
await this.createFollowRequest(follower, followee, requestId);
await this.createFollowRequest(follower, followee, requestId, withReplies);
return;
}
}
await this.insertFollowingDoc(followee, follower, silent);
await this.insertFollowingDoc(followee, follower, silent, withReplies);
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
@@ -190,16 +203,17 @@ export class UserFollowingService implements OnModuleInit {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']
},
silent = false,
withReplies?: boolean,
): Promise<void> {
if (follower.id === followee.id) return;
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,
withReplies: withReplies,
// 非正規化
followerHost: follower.host,
@@ -276,8 +290,8 @@ export class UserFollowingService implements OnModuleInit {
this.perUserFollowingChart.update(follower, followee, true);
}
// Publish follow event
if (this.userEntityService.isLocalUser(follower) && !silent) {
// Publish follow event
this.userEntityService.pack(followee.id, follower, {
detail: true,
}).then(async packed => {
@@ -290,6 +304,8 @@ export class UserFollowingService implements OnModuleInit {
});
}
});
this.funoutTimelineService.purge(`homeTimeline:${follower.id}`);
}
// Publish followed event
@@ -343,8 +359,8 @@ export class UserFollowingService implements OnModuleInit {
this.decrementFollowing(following.follower, following.followee);
// Publish unfollow event
if (!silent && this.userEntityService.isLocalUser(follower)) {
// Publish unfollow event
this.userEntityService.pack(followee.id, follower, {
detail: true,
}).then(async packed => {
@@ -357,6 +373,8 @@ export class UserFollowingService implements OnModuleInit {
});
}
});
this.funoutTimelineService.purge(`homeTimeline:${follower.id}`);
}
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
@@ -452,6 +470,7 @@ export class UserFollowingService implements OnModuleInit {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
},
requestId?: string,
withReplies?: boolean,
): Promise<void> {
if (follower.id === followee.id) return;
@@ -465,11 +484,11 @@ 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,
withReplies,
// 非正規化
followerHost: follower.host,
@@ -554,7 +573,7 @@ export class UserFollowingService implements OnModuleInit {
throw new IdentifiableError('8884c2dd-5795-4ac9-b27e-6a01d38190f9', 'No follow request.');
}
await this.insertFollowingDoc(followee, follower);
await this.insertFollowingDoc(followee, follower, false, request.withReplies);
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee));
@@ -694,4 +713,12 @@ export class UserFollowingService implements OnModuleInit {
});
}
}
@bindThis
public getFollowees(userId: MiUser['id']) {
return this.followingsRepository.createQueryBuilder('following')
.select('following.followeeId')
.where('following.followerId = :followerId', { followerId: userId })
.getMany();
}
}

View File

@@ -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,

View File

@@ -26,8 +26,7 @@ export class UserMutingService {
@bindThis
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
await this.mutingsRepository.insert({
id: this.idService.genId(),
createdAt: new Date(),
id: this.idService.gen(),
expiresAt: expiresAt ?? null,
muterId: user.id,
muteeId: target.id,

View File

@@ -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);

View File

@@ -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,
});
}

View File

@@ -164,7 +164,7 @@ export class ApInboxService {
}
// don't queue because the sender may attempt again when timeout
await this.userFollowingService.follow(actor, followee, activity.id);
await this.userFollowingService.follow(actor, followee, { requestId: activity.id });
return 'ok';
}
@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
});

View File

@@ -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<typeof schema> { // 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<typeof schema> { // eslint-d
}
@bindThis
public async read(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> {
public async read(user: { id: MiUser['id'], host: null }): Promise<void> {
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<void> {
public async write(user: { id: MiUser['id'], host: null }): Promise<void> {
await this.commit({
'write': [user.id],
});

View File

@@ -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,

View File

@@ -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,
@@ -34,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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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),

View File

@@ -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,
) {
}
@@ -88,7 +90,7 @@ export class DriveFileEntityService {
if (file.type.startsWith('video')) {
if (file.thumbnailUrl) return file.thumbnailUrl;
return this.videoProcessingService.getExternalVideoThumbnailUrl(file.webpublicUrl ?? file.url ?? file.uri);
return this.videoProcessingService.getExternalVideoThumbnailUrl(file.webpublicUrl ?? file.url);
} else if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) {
// 動画ではなくリモートかつメディアプロキシ
return this.getProxiedUrl(file.uri, 'static');
@@ -143,7 +145,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) ?? 0;
return parseInt(sum, 10) || 0;
}
@bindThis
@@ -155,7 +157,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) ?? 0;
return parseInt(sum, 10) || 0;
}
@bindThis
@@ -167,7 +169,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) ?? 0;
return parseInt(sum, 10) || 0;
}
@bindThis
@@ -179,7 +181,7 @@ export class DriveFileEntityService {
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) ?? 0;
return parseInt(sum, 10) || 0;
}
@bindThis
@@ -196,7 +198,7 @@ export class DriveFileEntityService {
return await awaitAll<Packed<'DriveFile'>>({
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<Packed<'DriveFile'>>({
id: file.id,
createdAt: file.createdAt.toISOString(),
createdAt: this.idService.parse(file.id).date.toISOString(),
name: file.name,
type: file.type,
md5: file.md5,

View File

@@ -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,

View File

@@ -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 } すると無限ループするので注意

View File

@@ -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, {

View File

@@ -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),

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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, {

View File

@@ -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';
@@ -18,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';
@@ -30,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(
@@ -68,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
@@ -169,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<MiNote['id'], MiNoteReaction | null>;
}) {
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) {
@@ -182,14 +183,14 @@ export class NoteEntityService implements OnModuleInit {
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
}
// パフォーマンスのためノートが作成されてから1秒以上経っていない場合はリアクションを取得しない
if (note.createdAt.getTime() + 1000 > Date.now()) {
// パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない
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) {
@@ -309,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,
@@ -317,7 +318,7 @@ export class NoteEntityService implements OnModuleInit {
text: text,
cw: note.cw,
visibility: note.visibility,
localOnly: note.localOnly ?? undefined,
localOnly: note.localOnly,
reactionAcceptance: note.reactionAcceptance,
visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined,
renoteCount: note.renoteCount,
@@ -357,30 +358,11 @@ 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_),
} : {}),
} : {}),
});
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);
}
@@ -403,8 +385,9 @@ export class NoteEntityService implements OnModuleInit {
const myReactionsMap = new Map<MiNote['id'], MiNoteReaction | null>();
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 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),

View File

@@ -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),
};

View File

@@ -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 ? {

View File

@@ -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 } すると無限ループするので注意

View File

@@ -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,

View File

@@ -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,

View File

@@ -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
@@ -337,8 +333,8 @@ export class UserEntityService implements OnModuleInit {
host: user.host,
avatarUrl: user.avatarUrl ?? this.getIdenticonUrl(user),
avatarBlurhash: user.avatarBlurhash,
isBot: user.isBot ?? falsy,
isCat: user.isCat ?? falsy,
isBot: user.isBot,
isCat: user.isCat,
instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? {
name: instance.name,
softwareName: instance.softwareName,
@@ -364,14 +360,14 @@ 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,
bannerBlurhash: user.bannerBlurhash,
isLocked: user.isLocked,
isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote),
isSuspended: user.isSuspended ?? falsy,
isSuspended: user.isSuspended,
description: profile!.description,
location: profile!.location,
birthday: profile!.birthday,

View File

@@ -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,

View File

@@ -108,6 +108,5 @@ async function net() {
// FS STAT
async function fs() {
const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 }));
return data ?? { rIO_sec: 0, wIO_sec: 0 };
return await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 }));
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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; } {

View File

@@ -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; } {

View File

@@ -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; } {

View File

@@ -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'];

View File

@@ -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,
})

View File

@@ -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.',

View File

@@ -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,

View File

@@ -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'];

View File

@@ -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;
@@ -98,4 +93,9 @@ export class MiAntenna {
default: true,
})
public isActive: boolean;
@Column('boolean', {
default: false,
})
public localOnly: boolean;
}

View File

@@ -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(),

Some files were not shown because too many files have changed in this diff Show More