From ee44f35feab7151630d7db5cd61286d3836506f3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 25 Sep 2023 13:57:09 +0900 Subject: [PATCH 001/411] [skip ci] New Crowdin updates (#11891) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Italian) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Chinese Traditional) --- locales/de-DE.yml | 2 +- locales/en-US.yml | 2 +- locales/it-IT.yml | 38 +++++++++++++++++++++++--------------- locales/zh-CN.yml | 2 +- locales/zh-TW.yml | 31 ++++++++++++++++++------------- 5 files changed, 44 insertions(+), 31 deletions(-) diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 98afd4db4c..aae5f45046 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -2101,9 +2101,9 @@ _webhookSettings: reaction: "Wenn du eine Reaktion erhältst" mention: "Wenn du erwähnt wirst" _moderationLogTypes: + updateRole: "Rolle aktualisiert" assignRole: "Zu Rolle zugewiesen" unassignRole: "Aus Rolle entfernt" - updateRole: "Rolle aktualisiert" suspend: "Gesperrt" unsuspend: "Entsperrt" addCustomEmoji: "Benutzerdefiniertes Emoji hinzugefügt" diff --git a/locales/en-US.yml b/locales/en-US.yml index 75acf424be..920995a954 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2101,9 +2101,9 @@ _webhookSettings: reaction: "When receiving a reaction" mention: "When being mentioned" _moderationLogTypes: + updateRole: "Role updated" assignRole: "Assigned to role" unassignRole: "Removed from role" - updateRole: "Role updated" suspend: "Suspended" unsuspend: "Unsuspended" addCustomEmoji: "Custom emoji added" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index b700fb8777..e99a41b75c 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -2031,7 +2031,7 @@ _notification: _types: all: "Tutto" note: "Nuove Note" - follow: "Novità follower" + follow: "Nuovi profili follower" mention: "Menzioni" reply: "Risposte" renote: "Rinota" @@ -2101,18 +2101,26 @@ _webhookSettings: reaction: "Quando ricevo una reazione" mention: "Quando mi menzionano" _moderationLogTypes: - assignRole: "Assegna un ruolo" - unassignRole: "Disassegna un ruolo" - updateRole: "Aggiorna un ruolo" + updateRole: "Ruolo aggiornato" + assignRole: "Ruolo assegnato" + unassignRole: "Ruolo disassegnato" suspend: "Sospensione" - unsuspend: "Toglie la sospensione" - addCustomEmoji: "Aggiunge una emoji personalizzata" - updateServerSettings: "Aggiorna le impostazioni del server" - updateUserNote: "Aggiorna il promemoria di moderazione" - deleteDriveFile: "Elimina file da Drive" - deleteNote: "Elimina la Nota" - createGlobalAnnouncement: "Crea un annuncio globale" - createUserAnnouncement: "Crea un annuncio ai profili iscritti" - resetPassword: "Ripristina la password" - suspendRemoteInstance: "Sospendi istanza remota" - unsuspendRemoteInstance: "Riattiva istanza remota" + unsuspend: "Sospensione rimossa" + addCustomEmoji: "Emoji personalizzata aggiunta" + updateCustomEmoji: "Emoji personalizzata aggiornata" + deleteCustomEmoji: "Emoji personalizzata eliminata" + updateServerSettings: "Impostazioni del server aggiornate" + updateUserNote: "Promemoria di moderazione aggiornato" + deleteDriveFile: "File da Drive eliminato" + deleteNote: "Nota eliminata" + createGlobalAnnouncement: "Annuncio globale creato" + createUserAnnouncement: "Annuncio ai profili iscritti creato" + updateGlobalAnnouncement: "Annuncio globale aggiornato" + updateUserAnnouncement: "Annuncio ai profili iscritti aggiornato" + deleteGlobalAnnouncement: "Annuncio globale eliminato" + deleteUserAnnouncement: "Annuncio ai profili iscritti eliminato" + resetPassword: "Password azzerata" + suspendRemoteInstance: "Istanza remota sospesa" + unsuspendRemoteInstance: "Istanza remota riattivata" + markSensitiveDriveFile: "File nel Drive segnato come esplicito" + unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index bd03dba185..27f95eb377 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -2101,9 +2101,9 @@ _webhookSettings: reaction: "被回应时" mention: "被提及时" _moderationLogTypes: + updateRole: "更新角色" assignRole: "分配角色" unassignRole: "取消分配角色" - updateRole: "更新角色" suspend: "冻结" unsuspend: "解除冻结" addCustomEmoji: "添加自定义表情符号" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index cd6473aebd..7cea244098 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1,6 +1,6 @@ --- -_lang_: "繁體中文" -headlineMisskey: "貼文連繫網絡" +_lang_: "繁體中文(台灣)" +headlineMisskey: "貼文連繫網路" introMisskey: "歡迎!Misskey 是一個開放原始碼且去中心化的社群網路服務。\n發布「貼文」向身邊的人分享您的想法!📡\n利用「反應」表達您對貼文的感覺!👍\n讓我們一起探索新的世界吧!🚀" poweredByMisskeyDescription: "{name}是開放原始碼平臺 Misskey 的伺服器之一。" monthAndDay: "{month} 月 {day} 日" @@ -45,7 +45,7 @@ pin: "置頂" unpin: "取消置頂" copyContent: "複製內容" copyLink: "複製連結" -copyLinkRenote: "複製轉貼連結" +copyLinkRenote: "複製轉發的連結" delete: "刪除" deleteAndEdit: "刪除並編輯" deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有反應、轉發和回覆也將會消失。" @@ -107,7 +107,7 @@ followRequestPending: "追隨許可待批准" enterEmoji: "輸入表情符號" renote: "轉發" unrenote: "取消轉發" -renoted: "轉發成功" +renoted: "轉發成功。" cantRenote: "無法轉發此貼文。" cantReRenote: "無法轉發之前已經轉發過的內容。" quote: "引用" @@ -138,8 +138,8 @@ suspend: "凍結" unsuspend: "解除凍結" blockConfirm: "確定要封鎖此使用者嗎?" unblockConfirm: "確定要解除封鎖此使用者嗎?" -suspendConfirm: "確定凍結此帳戶?" -unsuspendConfirm: "確定解凍此帳戶?" +suspendConfirm: "確定凍結此使用者?" +unsuspendConfirm: "確定解凍此使用者?" selectList: "選擇清單" editList: "編輯清單" selectChannel: "選擇頻道" @@ -152,12 +152,12 @@ customEmojis: "自訂表情符號" emoji: "表情符號" emojis: "表情符號" emojiName: "表情符號名稱" -emojiUrl: "表情符號URL" +emojiUrl: "表情符號 URL" addEmoji: "新增表情符號" settingGuide: "推薦設定" cacheRemoteFiles: "快取遠端檔案" -cacheRemoteFilesDescription: "禁用此設定會停止建立遠端檔案快取,從而節省伺服器儲存空間,但會因從遠端讀取資料而增加網路數據用量。" -youCanCleanRemoteFilesCache: "按檔案管理的🗑️按鈕,將快取全部刪除。" +cacheRemoteFilesDescription: "啟用此設定後,遠端檔案會被快取在本伺服器的儲存空間中。雖然顯示圖片會變快,但會消耗較多伺服器的儲存空間。至於要快取遠端使用者到什麼程度,是依照角色的雲端硬碟容量而定。當超過這個限制時,從較舊的檔案開始自快取中刪除並改為連結。關閉這個設定時,遠端檔案從一開始就維持連結的方式,但為了產生圖片的縮圖並保護使用者的隱私,建議將 default.yml 的 proxyRemoteFiles 設為 true。" +youCanCleanRemoteFilesCache: "按檔案管理的🗑️按鈕,可將快取全部刪除。" cacheRemoteSensitiveFiles: "快取遠端的敏感檔案" cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取遠端的敏感檔案,而是直接連結。" flagAsBot: "此使用者是機器人" @@ -424,7 +424,7 @@ securityKeyAndPasskey: "安全金鑰、Passkey" securityKey: "安全金鑰" lastUsed: "上次使用" lastUsedAt: "上次使用:{t}" -unregister: "註銷帳戶" +unregister: "註銷" passwordLessLogin: "設置無密碼登入" passwordLessLoginDescription: "不使用密碼,以安全金鑰或 Passkey 登入" resetPassword: "重設密碼" @@ -509,8 +509,8 @@ promote: "推廣" numberOfDays: "有效天數" hideThisNote: "隱藏此貼文" showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦" -objectStorage: "對象存儲" -useObjectStorage: "使用對象存儲" +objectStorage: "物件儲存" +useObjectStorage: "使用物件儲存" objectStorageBaseUrl: "Base URL" objectStorageBaseUrlDesc: "用於引用的 URL。如果您使用的是 CDN 或反向代理,請指定其 URL,例如 S3(https://.s3.amazonaws.com)、GCS(https://storage.googleapis.com/)。" objectStorageBucket: "儲存空間(Bucket)" @@ -1120,6 +1120,7 @@ notifyNotes: "開啟貼文通知" unnotifyNotes: "關閉貼文通知" authentication: "驗證" authenticationRequiredToContinue: "請於繼續前完成驗證" +dateAndTime: "日期與時間" _announcement: forExistingUsers: "僅限既有的使用者" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" @@ -2101,9 +2102,11 @@ _webhookSettings: reaction: "當獲得反應時" mention: "當被提到時" _moderationLogTypes: + createRole: "新增角色" + deleteRole: "刪除角色 " + updateRole: "更新角色設定" assignRole: "指派角色" unassignRole: "撤銷角色" - updateRole: "更新角色設定" suspend: "凍結" unsuspend: "解除凍結" addCustomEmoji: "新增自訂表情符號" @@ -2122,3 +2125,5 @@ _moderationLogTypes: resetPassword: "重設密碼" suspendRemoteInstance: "封鎖遠端伺服器" unsuspendRemoteInstance: "解除封鎖遠端伺服器" + markSensitiveDriveFile: "標記為敏感檔案" + unmarkSensitiveDriveFile: "撤銷標記為敏感檔案" From bd19d75c9c49d601cbf29e2fdcd67f3864b74175 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 25 Sep 2023 16:03:43 +0900 Subject: [PATCH 002/411] enhance: improve moderation log --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../api/endpoints/admin/resolve-abuse-user-report.ts | 8 ++++++++ packages/backend/src/types.ts | 6 ++++++ packages/frontend/src/pages/admin/modlog.ModLog.vue | 5 +++++ packages/misskey-js/etc/misskey-js.api.md | 2 +- packages/misskey-js/src/consts.ts | 6 ++++++ 7 files changed, 28 insertions(+), 1 deletion(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 5e24ecffa5..4d8123eb5d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2276,6 +2276,7 @@ export interface Locale { "unsuspendRemoteInstance": string; "markSensitiveDriveFile": string; "unmarkSensitiveDriveFile": string; + "resolveAbuseReport": string; }; } declare const locales: { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1af73c6201..647f5fb5f0 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2189,3 +2189,4 @@ _moderationLogTypes: unsuspendRemoteInstance: "リモートサーバーを再開" markSensitiveDriveFile: "ファイルをセンシティブ付与" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" + resolveAbuseReport: "通報を解決" diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 8667640a67..fb5ac7a335 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -10,6 +10,7 @@ import { InstanceActorService } from '@/core/InstanceActorService.js'; import { QueueService } from '@/core/QueueService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -41,6 +42,7 @@ export default class extends Endpoint { // eslint- private queueService: QueueService, private instanceActorService: InstanceActorService, private apRendererService: ApRendererService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId }); @@ -61,6 +63,12 @@ export default class extends Endpoint { // eslint- assigneeId: me.id, forwarded: ps.forward && report.targetUserHost != null, }); + + this.moderationLogService.log(me, 'resolveAbuseReport', { + reportId: report.id, + report: report, + forwarded: ps.forward && report.targetUserHost != null, + }); }); } } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index b458c0fbcb..35ea710f9e 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -55,6 +55,7 @@ export const moderationLogTypes = [ 'unsuspendRemoteInstance', 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', + 'resolveAbuseReport', ] as const; export type ModerationLogPayloads = { @@ -192,4 +193,9 @@ export type ModerationLogPayloads = { fileUserUsername: string | null; fileUserHost: string | null; }; + resolveAbuseReport: { + reportId: string; + report: any; + forwarded: boolean; + }; }; diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index f0de026ad8..14f94479f1 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -75,6 +75,11 @@ SPDX-License-Identifier: AGPL-3.0-only + +
+ raw +
{{ JSON5.stringify(log, null, '\t') }}
+
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index f72532f297..0686354ff4 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2607,7 +2607,7 @@ type ModerationLog = { }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index d2751c447e..aedfb5570e 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -73,6 +73,7 @@ export const moderationLogTypes = [ 'unsuspendRemoteInstance', 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', + 'resolveAbuseReport', ] as const; export type ModerationLogPayloads = { @@ -210,4 +211,9 @@ export type ModerationLogPayloads = { fileUserUsername: string | null; fileUserHost: string | null; }; + resolveAbuseReport: { + reportId: string; + report: any; + forwarded: boolean; + }; }; From 2039e244c5c1ef4e68e16292e9d19e3d94629d5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:05:02 +0900 Subject: [PATCH 003/411] build(deps): bump actions/checkout from 4.0.0 to 4.1.0 (#11900) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.0.0...v4.1.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/api-misskey-js.yml | 2 +- .github/workflows/check_copyright_year.yml | 2 +- .github/workflows/docker-develop.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/dockle.yml | 2 +- .github/workflows/lint.yml | 6 +++--- .github/workflows/pr-preview-deploy.yml | 2 +- .github/workflows/test-backend.yml | 2 +- .github/workflows/test-frontend.yml | 4 ++-- .github/workflows/test-misskey-js.yml | 2 +- .github/workflows/test-production.yml | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml index 4cf523a6b9..39f29bf773 100644 --- a/.github/workflows/api-misskey-js.yml +++ b/.github/workflows/api-misskey-js.yml @@ -9,7 +9,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - run: corepack enable diff --git a/.github/workflows/check_copyright_year.yml b/.github/workflows/check_copyright_year.yml index 313265f671..fb04cf1b00 100644 --- a/.github/workflows/check_copyright_year.yml +++ b/.github/workflows/check_copyright_year.yml @@ -10,7 +10,7 @@ jobs: check_copyright_year: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 - run: | if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then echo "Please change copyright year!" diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml index 05bb7f77f9..3e5bb17902 100644 --- a/.github/workflows/docker-develop.yml +++ b/.github/workflows/docker-develop.yml @@ -13,7 +13,7 @@ jobs: if: github.repository == 'misskey-dev/misskey' steps: - name: Check out the repo - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v3.0.0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 32a98a416d..33c85cbaf4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Check out the repo - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v3.0.0 diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml index d811944d61..2a1ac3a16c 100644 --- a/.github/workflows/dockle.yml +++ b/.github/workflows/dockle.yml @@ -14,7 +14,7 @@ jobs: env: DOCKER_CONTENT_TRUST: 1 steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 - run: | curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb" sudo dpkg -i dockle.deb diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7c10c23e77..798e6f49a3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: pnpm_install: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 with: fetch-depth: 0 submodules: true @@ -38,7 +38,7 @@ jobs: - sw - misskey-js steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 with: fetch-depth: 0 submodules: true @@ -64,7 +64,7 @@ jobs: - backend - misskey-js steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 with: fetch-depth: 0 submodules: true diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml index 702d8917e3..44f97645d0 100644 --- a/.github/workflows/pr-preview-deploy.yml +++ b/.github/workflows/pr-preview-deploy.yml @@ -53,7 +53,7 @@ jobs: # Check out merge commit - name: Fork based /deploy checkout - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 with: ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge' diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 19496c8959..ac7d1afda1 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -29,7 +29,7 @@ jobs: - 56312:6379 steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 with: submodules: true - name: Install pnpm diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml index 0618a0ef0a..e67b516546 100644 --- a/.github/workflows/test-frontend.yml +++ b/.github/workflows/test-frontend.yml @@ -16,7 +16,7 @@ jobs: node-version: [20.5.1] steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 with: submodules: true - name: Install pnpm @@ -68,7 +68,7 @@ jobs: - 56312:6379 steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 with: submodules: true # https://github.com/cypress-io/cypress-docker-images/issues/150 diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml index 7999c183b1..1846b628d3 100644 --- a/.github/workflows/test-misskey-js.yml +++ b/.github/workflows/test-misskey-js.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.0.0 + uses: actions/checkout@v4.1.0 - run: corepack enable diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml index 0504f42d16..c570018962 100644 --- a/.github/workflows/test-production.yml +++ b/.github/workflows/test-production.yml @@ -19,7 +19,7 @@ jobs: node-version: [20.5.1] steps: - - uses: actions/checkout@v4.0.0 + - uses: actions/checkout@v4.1.0 with: submodules: true - name: Install pnpm From 5ad0906c89af3adfcbac3c1b1208aa1fb4b6130f Mon Sep 17 00:00:00 2001 From: CyberRex Date: Wed, 27 Sep 2023 09:32:36 +0900 Subject: [PATCH 004/411] =?UTF-8?q?feat(backend):=20Master=E3=83=97?= =?UTF-8?q?=E3=83=AD=E3=82=BB=E3=82=B9=E3=81=AEPID=E3=82=92=E6=9B=B8?= =?UTF-8?q?=E3=81=8D=E5=87=BA=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=20(#11909)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .config/example.yml | 25 ++++++++++++++----------- packages/backend/src/boot/master.ts | 1 + packages/backend/src/config.ts | 3 +++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index 086a6ca8fc..03864a3299 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -30,7 +30,7 @@ url: https://example.tld/ # The port that your Misskey server should listen on. port: 3000 -# You can also use UNIX domain socket. +# You can also use UNIX domain socket. # socket: /path/to/misskey.sock # chmodSocket: '777' @@ -60,17 +60,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -206,3 +206,6 @@ signToActivityPubGet: true # Upload or download file size limits (bytes) #maxFileSize: 262144000 + +# PID File of master process +#pidFile: /tmp/misskey.pid diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index a45ea2bb8f..623cc964ac 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -63,6 +63,7 @@ export async function masterMain() { showNodejsVersion(); config = loadConfigBoot(); //await connectDb(); + if (config.pidFile) fs.writeFileSync(config.pidFile, process.pid.toString()); } catch (e) { bootLogger.error('Fatal error occurred during initialization', null, true); process.exit(1); diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index abbfdfed8f..f89879d535 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -89,6 +89,7 @@ type Source = { perChannelMaxNoteCacheCount?: number; perUserNotificationsMaxCount?: number; deactivateAntennaThreshold?: number; + pidFile: string; }; export type Config = { @@ -163,6 +164,7 @@ export type Config = { perChannelMaxNoteCacheCount: number; perUserNotificationsMaxCount: number; deactivateAntennaThreshold: number; + pidFile: string; }; const _filename = fileURLToPath(import.meta.url); @@ -255,6 +257,7 @@ export function loadConfig(): Config { perChannelMaxNoteCacheCount: config.perChannelMaxNoteCacheCount ?? 1000, perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 300, deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7), + pidFile: config.pidFile, }; } From 440f3144ae46d7f22e4bd861b821d55a8709c860 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 27 Sep 2023 10:00:26 +0900 Subject: [PATCH 005/411] enhance(frontend): improve moderation log --- CHANGELOG.md | 10 ++++++++++ packages/frontend/src/pages/admin/modlog.ModLog.vue | 1 + 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26013c14df..b9b03bc266 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,16 @@ --> +## next + +### General + +### Client +- Enhance: モデレーションログ機能の強化 + +### Server +- Enhance: MasterプロセスのPIDを書き出せるように + ## 2023.9.1 ### General diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 14f94479f1..8d83b32fa1 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only : {{ log.info.role.name }} : {{ log.info.before.name }} : {{ log.info.role.name }} + : {{ log.info.emoji.name }} : {{ log.info.before.name }} : @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }} : @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }} From 9d0c0773114e851fc5d1206bd9202e5a3ab7097a Mon Sep 17 00:00:00 2001 From: Tassoman Date: Wed, 27 Sep 2023 06:48:21 +0200 Subject: [PATCH 006/411] fix: leverage join misskey multilingual behaviour (#11908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --- packages/frontend/src/components/MkVisitorDashboard.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 7a8d7e6109..e4520bbb2d 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -114,8 +114,7 @@ function showMenu(ev) { } function exploreOtherServers() { - // TODO: 言語をよしなに - window.open('https://join.misskey.page/ja-JP/instances', '_blank'); + window.open('https://join.misskey.page/instances', '_blank'); } From 055464a6241e10c8516ebee4ac9a01a2f753f8fb Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 10:02:05 +0900 Subject: [PATCH 007/411] enhance: improve moderation log --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../src/server/api/endpoints/admin/invite/create.ts | 7 +++++++ packages/backend/src/types.ts | 4 ++++ packages/misskey-js/etc/misskey-js.api.md | 5 ++++- packages/misskey-js/src/consts.ts | 4 ++++ packages/misskey-js/src/entities.ts | 3 +++ 7 files changed, 24 insertions(+), 1 deletion(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 4d8123eb5d..5473a26fca 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2277,6 +2277,7 @@ export interface Locale { "markSensitiveDriveFile": string; "unmarkSensitiveDriveFile": string; "resolveAbuseReport": string; + "createInvitation": string; }; } declare const locales: { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 647f5fb5f0..ffe1d20e10 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2190,3 +2190,4 @@ _moderationLogTypes: markSensitiveDriveFile: "ファイルをセンシティブ付与" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" resolveAbuseReport: "通報を解決" + createInvitation: "招待コードを作成" diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts index 7112e06bdc..2cc5ab6e35 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts @@ -10,6 +10,7 @@ import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { generateInviteCode } from '@/misc/generate-invite-code.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -60,6 +61,7 @@ export default class extends Endpoint { // eslint- private inviteCodeEntityService: InviteCodeEntityService, private idService: IdService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { if (ps.expiresAt && isNaN(Date.parse(ps.expiresAt))) { @@ -78,6 +80,11 @@ export default class extends Endpoint { // eslint- } const tickets = await Promise.all(ticketsPromises); + + this.moderationLogService.log(me, 'createInvitation', { + invitations: tickets, + }); + return await this.inviteCodeEntityService.packMany(tickets, me); }); } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 35ea710f9e..7b928263ab 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -56,6 +56,7 @@ export const moderationLogTypes = [ 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', + 'createInvitation', ] as const; export type ModerationLogPayloads = { @@ -198,4 +199,7 @@ export type ModerationLogPayloads = { report: any; forwarded: boolean; }; + createInvitation: { + invitations: any[]; + }; }; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0686354ff4..7d4d4cc8b8 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2604,10 +2604,13 @@ type ModerationLog = { } | { type: 'unmarkSensitiveDriveFile'; info: ModerationLogPayloads['unmarkSensitiveDriveFile']; +} | { + type: 'createInvitation'; + info: ModerationLogPayloads['createInvitation']; }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index aedfb5570e..14a5b5643c 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -74,6 +74,7 @@ export const moderationLogTypes = [ 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', + 'createInvitation', ] as const; export type ModerationLogPayloads = { @@ -216,4 +217,7 @@ export type ModerationLogPayloads = { report: any; forwarded: boolean; }; + createInvitation: { + invitations: any[]; + }; }; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 41c9bdef6e..a089ef5a68 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -655,4 +655,7 @@ export type ModerationLog = { } | { type: 'unmarkSensitiveDriveFile'; info: ModerationLogPayloads['unmarkSensitiveDriveFile']; +} | { + type: 'createInvitation'; + info: ModerationLogPayloads['createInvitation']; }); From ce1218a2b27cb8208019ad3697f54b459fee63bd Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 11:02:01 +0900 Subject: [PATCH 008/411] =?UTF-8?q?enhance:=20=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E4=B8=80=E8=A6=A7=E3=81=A7Renote=E3=82=92=E9=99=A4?= =?UTF-8?q?=E5=A4=96=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../backend/src/server/api/endpoints/users/notes.ts | 11 +++++++++++ packages/frontend/src/pages/user/index.timeline.vue | 9 +++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9b03bc266..3e943714cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ## next ### General +- Enhance: ユーザーページのノート一覧でRenoteを除外できるように ### Client - Enhance: モデレーションログ機能の強化 diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 5934baef47..982fcc858a 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -42,6 +42,7 @@ export const paramDef = { properties: { userId: { type: 'string', format: 'misskey:id' }, includeReplies: { type: 'boolean', default: true }, + includeRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -118,6 +119,16 @@ export default class extends Endpoint { // eslint- query.andWhere('note.replyId IS NULL'); } + if (ps.includeRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { qb.orWhere('note.userId != :userId', { userId: user.id }); diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue index 3a2a2ade81..fcb2b41651 100644 --- a/packages/frontend/src/pages/user/index.timeline.vue +++ b/packages/frontend/src/pages/user/index.timeline.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -36,7 +36,8 @@ const pagination = { limit: 10, params: computed(() => ({ userId: props.user.id, - includeReplies: include.value === 'replies' || include.value === 'files', + includeRenotes: include.value === 'all', + includeReplies: include.value === 'all' || include.value === 'files', withFiles: include.value === 'files', })), }; @@ -51,7 +52,7 @@ const pagination = { .tl { background: var(--bg); - border-radius: var(--radius); - overflow: clip; + border-radius: var(--radius); + overflow: clip; } From d854942a1f4d28613f08daa63b086ca58522854d Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 11:04:14 +0900 Subject: [PATCH 009/411] .js --- packages/frontend/src/components/global/MkPageHeader.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index ef8bfbbbfc..580816abaa 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -45,7 +45,7 @@ import { onMounted, onUnmounted, ref, inject } from 'vue'; import tinycolor from 'tinycolor2'; import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import { scrollToTop } from '@/scripts/scroll.js'; -import { globalEvents } from '@/events'; +import { globalEvents } from '@/events.js'; import { injectPageMetadata } from '@/scripts/page-metadata.js'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js'; From eb740e2c72ae6854b244ad099c927c069008720e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 11:41:41 +0900 Subject: [PATCH 010/411] =?UTF-8?q?enhance:=20=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=8B=E3=82=89Renote=E3=82=92?= =?UTF-8?q?=E9=99=A4=E5=A4=96=E3=81=99=E3=82=8B=E3=82=AA=E3=83=97=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../api/endpoints/notes/user-list-timeline.ts | 16 ++++++++ .../src/server/api/endpoints/users/notes.ts | 8 ++-- .../api/stream/channels/global-timeline.ts | 6 ++- .../api/stream/channels/home-timeline.ts | 6 ++- .../api/stream/channels/hybrid-timeline.ts | 6 ++- .../api/stream/channels/local-timeline.ts | 6 ++- .../frontend/src/components/MkTimeline.vue | 37 ++++++++++++++----- .../frontend/src/pages/settings/general.vue | 2 - packages/frontend/src/pages/timeline.vue | 24 +++++++++++- .../src/pages/user/index.timeline.vue | 4 +- packages/frontend/src/store.ts | 4 -- 14 files changed, 94 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e943714cc..33dfa28d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ## next ### General +- Enhance: タイムラインからRenoteを除外するオプションを追加 - Enhance: ユーザーページのノート一覧でRenoteを除外できるように ### Client diff --git a/locales/index.d.ts b/locales/index.d.ts index 5473a26fca..eb2793c710 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1124,6 +1124,7 @@ export interface Locale { "authentication": string; "authenticationRequiredToContinue": string; "dateAndTime": string; + "showRenotes": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ffe1d20e10..637d580d6a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1121,6 +1121,7 @@ unnotifyNotes: "投稿の通知を解除" authentication: "認証" authenticationRequiredToContinue: "続けるには認証を行ってください" dateAndTime: "日時" +showRenotes: "リノートを表示" _announcement: forExistingUsers: "既存ユーザーのみ" diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 6932073791..c20274b2ba 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -49,6 +49,8 @@ export const paramDef = { includeMyRenotes: { type: 'boolean', default: true }, includeRenotedMyNotes: { type: 'boolean', default: true }, includeLocalRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false, @@ -130,6 +132,20 @@ export default class extends Endpoint { // eslint- })); } + if (!ps.withReplies) { + query.andWhere('note.replyId IS NULL'); + } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 982fcc858a..e660a0bb25 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -41,8 +41,8 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, - includeReplies: { type: 'boolean', default: true }, - includeRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -115,11 +115,11 @@ export default class extends Endpoint { // eslint- } } - if (!ps.includeReplies) { + if (!ps.withReplies) { query.andWhere('note.replyId IS NULL'); } - if (ps.includeRenotes === false) { + if (ps.withRenotes === false) { query.andWhere(new Brackets(qb => { qb.orWhere('note.renoteId IS NULL'); qb.orWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index a33f1a956a..fef52b6856 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -19,6 +19,7 @@ class GlobalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -37,7 +38,8 @@ class GlobalTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.gtlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -68,6 +70,8 @@ class GlobalTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index bd8888f679..198c68e1c2 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -17,6 +17,7 @@ class HomeTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withRenotes: boolean; constructor( private noteEntityService: NoteEntityService, @@ -30,7 +31,8 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; this.subscriber.on('notesStream', this.onNote); } @@ -77,6 +79,8 @@ class HomeTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 760fb8d19f..cde4297478 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -37,7 +38,8 @@ class HybridTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -89,6 +91,8 @@ class HybridTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index f32f8c5cec..ef708c4fee 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -36,7 +37,8 @@ class LocalTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -68,6 +70,8 @@ class LocalTimelineChannel extends Channel { if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index d6712e7606..3e7c537512 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -15,14 +15,19 @@ import * as sound from '@/scripts/sound.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ src: string; list?: string; antenna?: string; channel?: string; role?: string; sound?: boolean; -}>(); + withRenotes?: boolean; + withReplies?: boolean; +}>(), { + withRenotes: true, + withReplies: false, +}); const emit = defineEmits<{ (ev: 'note'): void; @@ -62,10 +67,12 @@ if (props.src === 'antenna') { } else if (props.src === 'home') { endpoint = 'notes/timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('homeTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); @@ -73,28 +80,34 @@ if (props.src === 'antenna') { } else if (props.src === 'local') { endpoint = 'notes/local-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('localTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'social') { endpoint = 'notes/hybrid-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('hybridTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'global') { endpoint = 'notes/global-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('globalTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'mentions') { @@ -116,9 +129,13 @@ if (props.src === 'antenna') { } else if (props.src === 'list') { endpoint = 'notes/user-list-timeline'; query = { + withRenotes: props.withRenotes, + withReplies: props.withReplies, listId: props.list, }; connection = stream.useChannel('userList', { + withRenotes: props.withRenotes, + withReplies: props.withReplies, listId: props.list, }); connection.on('note', prepend); diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index a536bd1baa..55de53fb07 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.showFixedPostForm }} {{ i18n.ts.showFixedPostFormInChannel }} - {{ i18n.ts.flagShowTimelineReplies }} @@ -249,7 +248,6 @@ const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance')); const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition')); const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); -const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies')); const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); watch(lang, () => { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index cce7360b9b..5fc43dc650 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -15,9 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -58,6 +60,8 @@ const rootEl = $shallowRef(); let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); +const withRenotes = $ref(true); +const withReplies = $ref(false); watch($$(src), () => queue = 0); @@ -129,7 +133,23 @@ function focus(): void { tlComponent.focus(); } -const headerActions = $computed(() => []); +const headerActions = $computed(() => [{ + icon: 'ti ti-dots', + text: i18n.ts.options, + handler: (ev) => { + os.popupMenu([{ + type: 'switch', + text: i18n.ts.showRenotes, + icon: 'ti ti-repeat', + ref: $$(withRenotes), + }, { + type: 'switch', + text: i18n.ts.withReplies, + icon: 'ti ti-arrow-back-up', + ref: $$(withReplies), + }], ev.currentTarget ?? ev.target); + }, +}]); const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({ key: 'list:' + l.id, diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue index fcb2b41651..42040f5304 100644 --- a/packages/frontend/src/pages/user/index.timeline.vue +++ b/packages/frontend/src/pages/user/index.timeline.vue @@ -36,8 +36,8 @@ const pagination = { limit: 10, params: computed(() => ({ userId: props.user.id, - includeRenotes: include.value === 'all', - includeReplies: include.value === 'all' || include.value === 'files', + withRenotes: include.value === 'all', + withReplies: include.value === 'all' || include.value === 'files', withFiles: include.value === 'files', })), }; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 8a7ee62eff..e715088d03 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -109,10 +109,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: [] as string[], }, - showTimelineReplies: { - where: 'account', - default: false, - }, menu: { where: 'deviceAccount', From 772d2432b6e84a7a7c0fa8ad1852701cdc600f88 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 15:32:47 +0900 Subject: [PATCH 011/411] =?UTF-8?q?enhance:=20=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=8B=E3=82=89Renote=E3=82=92?= =?UTF-8?q?=E9=99=A4=E5=A4=96=E3=81=99=E3=82=8B=E3=82=AA=E3=83=97=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/endpoints/notes/global-timeline.ts | 12 +++++++ .../api/endpoints/notes/hybrid-timeline.ts | 11 +++++++ .../api/endpoints/notes/local-timeline.ts | 11 +++++++ .../server/api/endpoints/notes/timeline.ts | 11 +++++++ packages/frontend/src/ui/deck/deck-store.ts | 2 ++ packages/frontend/src/ui/deck/tl-column.vue | 33 +++++++++++++++++-- 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 0b3b5c902e..8784e86153 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; @@ -40,6 +41,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -88,6 +90,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index e9ae5dc755..9bde5dee21 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -52,6 +52,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, }, required: [], } as const; @@ -137,6 +138,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index af1e0398dc..0fefddc51b 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -42,6 +42,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, fileType: { type: 'array', items: { type: 'string', } }, @@ -110,6 +111,16 @@ export default class extends Endpoint { // eslint- query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); } } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 042115ab84..0d47cc1702 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -42,6 +42,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, }, required: [], } as const; @@ -126,6 +127,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index f910c5181d..034d675b0e 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -30,6 +30,8 @@ export type Column = { roleId?: string; includingTypes?: typeof notificationTypes[number][]; tl?: 'home' | 'local' | 'social' | 'global'; + withRenotes?: boolean; + withReplies?: boolean; }; export const deckStore = markRaw(new Storage('deck', { diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 813b801d21..073898409c 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -20,12 +20,19 @@ SPDX-License-Identifier: AGPL-3.0-only

{{ i18n.ts._disabledTimeline.description }}

- + From 63c6a9bb80f8e8eb9f783adc15e03ed498b52d1a Mon Sep 17 00:00:00 2001 From: taichan <40626578+taichanNE30@users.noreply.github.com> Date: Thu, 28 Sep 2023 15:35:00 +0900 Subject: [PATCH 012/411] =?UTF-8?q?Feat:=20register=5Fpost=5Fform=5Faction?= =?UTF-8?q?=E3=81=A7cw=E3=82=92=E5=A4=89=E6=9B=B4=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=20(#11911)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 投稿フォームのアクション追加するプラグインでCWを変更可能にする * Update CHANGELOG --- CHANGELOG.md | 1 + packages/frontend/src/components/MkPostForm.vue | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33dfa28d02..9ace360474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ### Client - Enhance: モデレーションログ機能の強化 +- Plugin:register_post_form_actionを用いてCWを取得・変更できるように ### Server - Enhance: MasterプロセスのPIDを書き出せるように diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 2b4dcc8ed4..1f4f75d5ed 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -819,8 +819,10 @@ function showActions(ev) { action: () => { action.handler({ text: text, + cw: cw, }, (key, value) => { if (key === 'text') { text = value; } + if (key === 'cw') { useCw = value !== null; cw = value; } }); }, })), ev.currentTarget ?? ev.target); From a388e25f3eb1867fa476edd8566f35d968d9cc80 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 15:35:21 +0900 Subject: [PATCH 013/411] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ace360474..c711572655 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ ### Client - Enhance: モデレーションログ機能の強化 -- Plugin:register_post_form_actionを用いてCWを取得・変更できるように +- Enhance: Plugin:register_post_form_actionを用いてCWを取得・変更できるように ### Server - Enhance: MasterプロセスのPIDを書き出せるように From c106db89e1d54c20c6466e42dde540e0d5c5c4eb Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 17:21:16 +0900 Subject: [PATCH 014/411] feat: note edit --- CHANGELOG.md | 2 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/backend/src/core/RoleService.ts | 3 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/notes/update.ts | 88 +++++++++++++++++++ .../backend/src/server/api/stream/types.ts | 4 + .../frontend/src/components/MkPostForm.vue | 8 +- .../src/components/MkPostFormDialog.vue | 1 + packages/frontend/src/const.ts | 1 + .../frontend/src/pages/admin/roles.editor.vue | 20 +++++ packages/frontend/src/pages/admin/roles.vue | 8 ++ .../frontend/src/scripts/get-note-menu.ts | 9 ++ .../frontend/src/scripts/use-note-capture.ts | 6 ++ packages/misskey-js/src/streaming.types.ts | 7 ++ 16 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/notes/update.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c711572655..d24364c57c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ ## next ### General +- Feat: ノートの編集をできるように + - ロールで編集可否を設定可能 - Enhance: タイムラインからRenoteを除外するオプションを追加 - Enhance: ユーザーページのノート一覧でRenoteを除外できるように diff --git a/locales/index.d.ts b/locales/index.d.ts index eb2793c710..8c6b724623 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1538,6 +1538,7 @@ export interface Locale { "gtlAvailable": string; "ltlAvailable": string; "canPublicNote": string; + "canEditNote": string; "canInvite": string; "inviteLimit": string; "inviteLimitCycle": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 637d580d6a..c31b4a5c27 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1459,6 +1459,7 @@ _role: gtlAvailable: "グローバルタイムラインの閲覧" ltlAvailable: "ローカルタイムラインの閲覧" canPublicNote: "パブリック投稿の許可" + canEditNote: "ノートの編集" canInvite: "サーバー招待コードの発行" inviteLimit: "招待コードの作成可能数" inviteLimitCycle: "招待コードの発行間隔" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 934b7d676b..ec4d804219 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -26,6 +26,7 @@ export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; + canEditNote: boolean; canInvite: boolean; inviteLimit: number; inviteLimitCycle: number; @@ -50,6 +51,7 @@ export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, + canEditNote: true, canInvite: false, inviteLimit: 0, inviteLimitCycle: 60 * 24 * 7, @@ -294,6 +296,7 @@ export class RoleService implements OnApplicationShutdown { gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)), ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)), canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), + canEditNote: calc('canEditNote', vs => vs.some(v => v === true)), canInvite: calc('canInvite', vs => vs.some(v => v === true)), inviteLimit: calc('inviteLimit', vs => Math.max(...vs)), inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 41a11bfb19..c883c96ba2 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -258,6 +258,7 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_update from './endpoints/notes/update.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -606,6 +607,7 @@ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; +const $notes_update: Provider = { provide: 'ep:notes/update', useClass: ep___notes_update.default }; const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; @@ -958,6 +960,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_delete, + $notes_update, $notes_favorites_create, $notes_favorites_delete, $notes_featured, @@ -1304,6 +1307,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_delete, + $notes_update, $notes_favorites_create, $notes_favorites_delete, $notes_featured, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index ab20a708ef..b40d654f9c 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -258,6 +258,7 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_update from './endpoints/notes/update.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -604,6 +605,7 @@ const eps = [ ['notes/conversation', ep___notes_conversation], ['notes/create', ep___notes_create], ['notes/delete', ep___notes_delete], + ['notes/update', ep___notes_update], ['notes/favorites/create', ep___notes_favorites_create], ['notes/favorites/delete', ep___notes_favorites_delete], ['notes/featured', ep___notes_featured], diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts new file mode 100644 index 0000000000..ccd2878d3c --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import type { UsersRepository, NotesRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteDeleteService } from '@/core/NoteDeleteService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: true, + requireRolePolicy: 'canEditNote', + + kind: 'write:notes', + + limit: { + duration: ms('1hour'), + max: 10, + minInterval: ms('1sec'), + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + text: { + type: 'string', + minLength: 1, + maxLength: MAX_NOTE_TEXT_LENGTH, + nullable: false, + }, + cw: { type: 'string', nullable: true, maxLength: 100 }, + }, + required: ['noteId', 'text', 'cw'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private getterService: GetterService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps, me) => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; + }); + + if (note.userId !== me.id) { + throw new ApiError(meta.errors.noSuchNote); + } + + await this.notesRepository.update({ id: note.id }, { + cw: ps.cw, + text: ps.text, + }); + + this.globalEventService.publishNoteStream(note.id, 'updated', { + cw: ps.cw, + text: ps.text, + }); + }); + } +} diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 90e0a61f26..2436750cd6 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -130,6 +130,10 @@ export interface NoteStreamTypes { deleted: { deletedAt: Date; }; + updated: { + cw: string | null; + text: string; + }; reacted: { reaction: string; emoji?: { diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 1f4f75d5ed..b82ca3ef19 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -143,6 +143,7 @@ const props = withDefaults(defineProps<{ fixed?: boolean; autofocus?: boolean; freezeAfterPosted?: boolean; + updateMode?: boolean; }>(), { initialVisibleUsers: () => [], autofocus: true, @@ -698,17 +699,18 @@ async function post(ev?: MouseEvent) { } let postData = { - text: text === '' ? undefined : text, + text: text === '' ? null : text, fileIds: files.length > 0 ? files.map(f => f.id) : undefined, replyId: props.reply ? props.reply.id : undefined, renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined, channelId: props.channel ? props.channel.id : undefined, poll: poll, - cw: useCw ? cw ?? '' : undefined, + cw: useCw ? cw ?? '' : null, localOnly: localOnly, visibility: visibility, visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined, reactionAcceptance, + noteId: props.updateMode ? props.initialNote?.id : undefined, }; if (withHashtags && hashtags && hashtags.trim() !== '') { @@ -731,7 +733,7 @@ async function post(ev?: MouseEvent) { } posting = true; - os.api('notes/create', postData, token).then(() => { + os.api(props.updateMode ? 'notes/update' : 'notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { posted = true; } else { diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue index c07a166a83..f33d498f93 100644 --- a/packages/frontend/src/components/MkPostFormDialog.vue +++ b/packages/frontend/src/components/MkPostFormDialog.vue @@ -30,6 +30,7 @@ const props = defineProps<{ instant?: boolean; fixed?: boolean; autofocus?: boolean; + updateMode?: boolean; }>(); const emit = defineEmits<{ diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 15038b1063..9fd6d40d72 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -61,6 +61,7 @@ export const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', + 'canEditNote', 'canInvite', 'inviteLimit', 'inviteLimitCycle', diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 2ef3e254cd..1b72e1d332 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only
+ + + +
+ + + + + + + + + +
+
+