diff --git a/CHANGELOG.md b/CHANGELOG.md index e6c66f0859..62810ebf44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,8 +28,10 @@ - Feat: 二要素認証でパスキーをサポートするようになりました - Feat: 指定したユーザーが投稿したときに通知できるようになりました - Feat: プロフィールでのリンク検証 +- Feat: モデレーションログ機能 - Feat: 通知をテストできるようになりました - Feat: PWAのアイコンが設定できるようになりました +- Enhance: サーバー名の略称が設定できるようになりました - Enhance: アンテナの受信ソースに指定したユーザを除外するものを追加 - Enhance: 二要素認証設定時のセキュリティを強化 - パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要になりました @@ -67,6 +69,7 @@ - Enhance: ノート詳細ページ読み込み時のパフォーマンスが向上しました - Enhance: タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 - Enhance: 「Moderation note」、「Add moderation note」をローカライズできるように +- Enhance: プラグインのソースコードを確認・コピーできるように - Enhance: 細かなデザインの調整 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正 @@ -76,6 +79,7 @@ - Fix: Misskeyプラグインをインストールする際のAiScriptバージョンのチェックが0.14.0以降に対応していない問題を修正 - Fix: 他のサーバーのユーザーへ「メッセージを送信」した時の初期テキストのメンションが間違っている問題を修正 - Fix: 環境によってはMisskey Webが開けない問題を修正 +- Fix: プラグインの権限リストが見れない問題を修正 ### Server - Change: cacheRemoteFilesの初期値はfalseになりました @@ -87,6 +91,7 @@ - Enhance: 自分へのメンション一覧を取得する際のパフォーマンスを向上 - Enhance: Docker環境でjemallocを使用することでメモリ使用量を削減 - Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正 +- Fix: notes/reactionsのページネーションが機能しない問題を修正 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように - Fix: 一部のfeatured noteを照会できない問題を修正 - Fix: muteがapiからのuser list timeline取得で機能しない問題を修正 diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 4b769ecfe1..942fd9fdd6 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1154,6 +1154,7 @@ _plugin: install: "ثبّت إضافات" installWarn: "رجاءً لا تثبت إضافات غير موثوقة." manage: "إدارة الإضافات" + viewSource: "اظهر المصدر" _preferencesBackups: createdAt: "تم إنشاؤه: {date} {time}" updatedAt: "آخر تحديث: {date} {time}" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index f61ac0c8b0..0247d9f97d 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -902,6 +902,7 @@ _plugin: install: "প্লাগইন ইন্সটল করুন" installWarn: "অবিশ্বস্ত প্লাগইন ইনস্টল করবেন না।" manage: "প্লাগইন ম্যানেজ করুন" + viewSource: "উৎস দেখুন" _registry: scope: "স্কোপ" key: "কী" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 525b393372..ede88801b8 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -1492,6 +1492,7 @@ _plugin: install: "Instalovat plugin" installWarn: "Neinstalujte nedůvěryhodné pluginy." manage: "Správce pluginů" + viewSource: "Zobrazit zdroj" _preferencesBackups: list: "Vytvořit backup" saveNew: "Uložit novou zálohu" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 85d395c9f3..b9ba4313ae 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -722,6 +722,7 @@ lockedAccountInfo: "Auch wenn du Follow-Anfragen auf manuelle Bestätigung setzt alwaysMarkSensitive: "Medien standardmäßig als sensibel markieren" loadRawImages: "Anstatt Vorschaubilder immer Originalbilder anzeigen" disableShowingAnimatedImages: "Animierte Bilder nicht abspielen" +highlightSensitiveMedia: "Sensitive Medien markieren" verificationEmailSent: "Eine Bestätigungsmail wurde an deine Email-Adresse versendet. Besuche den dort enthaltenen Link, um die Verifizierung abzuschließen." notSet: "Nicht konfiguriert" emailVerified: "Email-Adresse bestätigt" @@ -927,7 +928,7 @@ typeToConfirm: "Bitte gib zur Bestätigung {x} ein" deleteAccount: "Benutzerkonto löschen" document: "Dokumentation" numberOfPageCache: "Seitencachegröße" -numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, erhöht aber Serverlast und Arbeitsspeicherauslastung." +numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, aber erhöht Last und Arbeitsspeicherauslastung auf dem Nutzergerät." logoutConfirm: "Wirklich abmelden?" lastActiveDate: "Zuletzt verwendet am" statusbar: "Statusleiste" @@ -1130,6 +1131,8 @@ keepScreenOn: "Bildschirm angeschaltet lassen" verifiedLink: "Link-Besitz wurde verifiziert" notifyNotes: "Über neue Notizen benachrichtigen" unnotifyNotes: "Nicht über neue Notizen benachrichtigen" +authentication: "Authentifikation" +authenticationRequiredToContinue: "Bitte authentifiziere dich, um fortzufahren" _announcement: forExistingUsers: "Nur für existierende Nutzer" forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt." @@ -1163,6 +1166,8 @@ _serverSettings: appIconStyleRecommendation: "Da das Icon zu einem Kreis oder Quadrat zugeschnitten wird, wird ein Icon mit gefülltem Margin um den Inhalt herum empfohlen." appIconResolutionMustBe: "Die Mindestauflösung ist {resolution}." manifestJsonOverride: "Überschreiben von manifest.json" + shortName: "Abkürzung" + shortNameDescription: "Ein Kürzel für den Namen der Instanz, der angezeigt werden kann, falls der volle Instanzname lang ist." _accountMigration: moveFrom: "Von einem anderen Konto zu diesem migrieren" moveFromSub: "Alias für ein anderes Konto erstellen" @@ -1543,6 +1548,7 @@ _plugin: install: "Plugins installieren" installWarn: "Installiere bitte nur vertrauenswürdige Plugins." manage: "Plugins verwalten" + viewSource: "Quelltext anzeigen" _preferencesBackups: list: "Erstellte Backups" saveNew: "Neu erstellen" @@ -1875,6 +1881,7 @@ _antennaSources: users: "Notizen von einem oder mehreren angegebenen Benutzern" userList: "Notizen von allen Benutzern einer Liste" userGroup: "Notizen von allen Benutzern einer Gruppe" + userBlacklist: "Alle Notizen abgesehen derer angegebener Benutzer" _weekday: sunday: "Sonntag" monday: "Montag" @@ -2107,6 +2114,7 @@ _notification: notificationWillBeDisplayedLikeThis: "Benachrichtigungen sehen so aus" _types: all: "Alle" + note: "Neue Notizen" follow: "Neue Follower" mention: "Erwähnungen" reply: "Antworten" diff --git a/locales/en-US.yml b/locales/en-US.yml index a817a41259..2d829f2129 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -767,7 +767,7 @@ alwaysMarkSensitive: "Mark as sensitive by default" loadRawImages: "Load original images instead of showing thumbnails" disableShowingAnimatedImages: "Don't play animated images" disableShowingAnimatedImagesDescription: "When disabled, the animated images will play. Be careful as it may cause photosensitive seizures." -highlightSensitiveMedia: "Making Sensitive Media Stand Out" +highlightSensitiveMedia: "Highlight sensitive media" verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification.\nIf you do not receive an email, please check your spam folder." notSet: "Not set" emailVerified: "Email has been verified" @@ -975,7 +975,7 @@ typeToConfirm: "Please enter {x} to confirm" deleteAccount: "Delete account" document: "Documentation" numberOfPageCache: "Number of cached pages" -numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used." +numberOfPageCacheDescription: "Increasing this number will improve convenience for but cause more load as more memory usage on the user's device." logoutConfirm: "Really log out?" lastActiveDate: "Last used at" statusbar: "Status bar" @@ -1185,6 +1185,8 @@ keepScreenOn: "Keep screen on" verifiedLink: "Link ownership has been verified" notifyNotes: "Notify about new notes" unnotifyNotes: "Stop notifying about new notes" +authentication: "Authentication" +authenticationRequiredToContinue: "Please authenticate to continue" additionalPermissionsForFlash: "Allow to add permission to Play" thisFlashRequiresTheFollowingPermissions: "This Play requires the following permissions" doYouWantToAllowThisPlayToAccessYourAccount: "Do you want to allow this Play to access your account?" @@ -1301,6 +1303,8 @@ _serverSettings: appIconStyleRecommendation: "As the icon may be cropped to a square or circle, an icon with colored margin around the content is recommended." appIconResolutionMustBe: "The minimum resolution is {resolution}." manifestJsonOverride: "manifest.json Override" + shortName: "Short name" + shortNameDescription: "A shorthand for the instance's name that can be displayed if the full official name is long." _accountMigration: moveFrom: "Migrate another account to this one" moveFromSub: "Create alias to another account" @@ -1687,6 +1691,7 @@ _plugin: install: "Install plugins" installWarn: "Please do not install untrustworthy plugins." manage: "Manage plugins" + viewSource: "View source" _preferencesBackups: list: "Created backups" saveNew: "Save new backup" @@ -2041,6 +2046,7 @@ _antennaSources: users: "Notes from specific users" userList: "Notes from a specified list of users" userGroup: "Notes from users in a specified group" + userBlacklist: "All notes except for those of one or more specified users" _weekday: sunday: "Sunday" monday: "Monday" @@ -2274,7 +2280,7 @@ _notification: notificationWillBeDisplayedLikeThis: "Notifications look like this" _types: all: "All" - note: "Notes" + note: "New notes" follow: "New followers" mention: "Mentions" reply: "Replies" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 089860e6fa..a82c93a752 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1532,6 +1532,7 @@ _plugin: install: "Instalar plugins" installWarn: "Por favor no instale plugins que no son de confianza" manage: "Gestionar plugins" + viewSource: "Ver la fuente" _preferencesBackups: list: "Respaldos creados" saveNew: "Guardar nuevo respaldo" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 4a03731997..02d4c4c080 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -272,6 +272,7 @@ startMessaging: "Commencer à discuter" nUsersRead: "Lu par {n} personnes" agreeTo: "J’accepte {0}" agree: "Accepter" +agreeBelow: "J’accepte ce qui suit" basicNotesBeforeCreateAccount: "Notes importantes" termsOfService: "Conditions d'utilisation" start: "Commencer" @@ -406,6 +407,7 @@ aboutMisskey: "À propos de CherryPick" administrator: "Administrateur" token: "Jeton" 2fa: "Authentification à deux facteurs" +setupOf2fa: "Configuration de l’authentification à deux facteurs" totp: "Application d'authentification" totpDescription: "Entrez un mot de passe à usage unique à l'aide d'une application d'authentification" moderator: "Modérateur·rice·s" @@ -413,6 +415,7 @@ moderation: "Modérations" moderationNote: "Note de modération" addModerationNote: "Ajouter une note 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é" lastUsed: "Dernier utilisé" lastUsedAt: "Dernière utilisation : {t}" @@ -809,6 +812,7 @@ popularPosts: "Les plus consultées" shareWithNote: "Partager dans une note" ads: "Publicité" expiration: "Échéance" +startingperiod: "Commencer" memo: "Pense-bête" priority: "Priorité" high: "Haute" @@ -972,6 +976,7 @@ internalServerError: "Erreur interne du serveur" copyErrorInfo: "Copier les détails de l’erreur" exploreOtherServers: "Trouver une autre instance" disableFederationOk: "Désactiver" +likeOnly: "Les favoris uniquement" license: "Licence" video: "Vidéo" videos: "Vidéos" @@ -992,6 +997,7 @@ horizontal: "Latéral" serverRules: "Règles du serveur" archive: "Archive" youFollowing: "Abonné·e" +options: "Options" later: "Plus tard" goToMisskey: "Retour vers CherryPick" expirationDate: "Date d’expiration" @@ -1004,12 +1010,24 @@ icon: "Avatar" forYou: "Pour vous" replies: "Répondre" renotes: "Renoter" +loadReplies: "Inclure les réponses" +pinnedList: "Liste épinglée" +notifyNotes: "Notifier à propos des nouvelles notes" +authentication: "Authentification" +authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer" _announcement: readConfirmTitle: "Marquer comme lu ?" _initialAccountSetting: profileSetting: "Paramètres du profil" privacySetting: "Paramètres de confidentialité" + initialAccountSettingCompleted: "Configuration du profil terminée avec succès !" + ifYouNeedLearnMore: "Si vous voulez en savoir plus comment utiliser {name}(Misskey), veuillez visiter {link}." + skipAreYouSure: "Désirez-vous ignorer la configuration du profile ?" +_serverSettings: + iconUrl: "URL de l’icône" _accountMigration: + moveFrom: "Migrer un autre compte vers le présent compte" + moveFromSub: "Créer un alias vers un autre compte" moveToLabel: "Compte vers lequel vous migrez :" startMigration: "Migrer" movedTo: "Compte vers lequel vous migrez :" @@ -1066,20 +1084,33 @@ _achievements: _login1000: flavor: "Merci d'utiliser CherryPick !" _profileFilled: + title: "Bien préparé" description: "Configuration de votre profil" _markedAsCat: title: "Je suis un chat" + description: "Rendre votre compte comme un chat" flavor: "Je n'ai pas encore de nom" + _following1: + title: "Vous suivez votre premier utilisateur·rice" _following50: title: "Beaucoup d'amis" _followers10: title: "Abonnez-moi !" + _followers100: + title: "Populaire" + _followers500: + title: "Tour radio" + _followers1000: + title: "Influenceur·euse" _iLoveMisskey: title: "J’adore CherryPick" description: "Publication « J’❤ #CherryPick »" + flavor: "L'équipe de développement de CherryPick apprécie vraiment votre aide !" _foundTreasure: title: "Chasse au trésor" description: "Vous avez trouvé le trésor caché" + _client30min: + title: "Pause bien méritée" _postedAtLateNight: flavor: "C’est l’heure d’aller au lit." _postedAt0min0sec: @@ -1088,18 +1119,45 @@ _achievements: flavor: "Tic tac, tic tac, tic tac, ding !" _viewInstanceChart: title: "Analyste" + _outputHelloWorldOnScratchpad: + title: "Bonjour tout le monde !" + _open3windows: + title: "Multi-fenêtres" + _driveFolderCircularReference: + title: "Référence circulaire" + _setNameToSyuilo: + description: "Vous avez spécifié « syuilo » comme nom" + _passedSinceAccountCreated1: + title: "Premier anniversaire" + _passedSinceAccountCreated2: + title: "Second anniversaire" + _passedSinceAccountCreated3: + title: "3ème anniversaire" _loggedInOnBirthday: title: "Joyeux Anniversaire !" + description: "Vous vous êtes connecté à la date de votre anniversaire" _loggedInOnNewYearsDay: title: "Bonne année !" _cookieClicked: flavor: "Attendez une minute, vous êtes sur le mauvais site web ?" + _brainDiver: + flavor: "Misskey-Misskey La-Tu-Ma" _role: + new: "Nouveau rôle" + edit: "Modifier le rôle" name: "Nom du rôle" description: "Description du rôle" permission: "Rôle et autorisations" assignTarget: "Attribuer" condition: "Condition" + isPublic: "Rôle public" + options: "Options" + policies: "Stratégies" + baseRole: "Modèle de rôle" + useBaseValue: "Utiliser la valeur du modèle de rôle" + chooseRoleToAssign: "Sélectionner le rôle à assigner" + iconUrl: "URL de l’icône" + displayOrder: "Classement" priority: "Priorité" _priority: low: "Basse" @@ -1158,6 +1216,7 @@ _plugin: install: "Installation de plugin" installWarn: "N’installez que des extensions provenant de sources de confiance." manage: "Gestion des plugins" + viewSource: "Afficher la source" _preferencesBackups: list: "Sauvegardes créées" saveNew: "Nouvelle sauvegarde" @@ -1410,6 +1469,7 @@ _2fa: securityKeyNotSupported: "Votre navigateur ne prend pas en charge les clés de sécurité." securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion grâce à une clé de sécurité matérielle qui prend en charge FIDO2, ou bien en configurant l'authentification par empreinte digitale ou par code PIN sur votre appareil." securityKeyName: "Nom de la clé" + removeKey: "Supprimer la clé de sécurité" removeKeyConfirm: "Voulez-vous supprimer {name} ?" renewTOTPOk: "Reconfigurer" renewTOTPCancel: "Pas maintenant" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 4551a693ba..8e912743a3 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1510,6 +1510,7 @@ _plugin: install: "Memasang plugin" installWarn: "Mohon jangan memasang plugin yang tidak dapat dipercayai." manage: "Manajemen plugin" + viewSource: "Lihat sumber" _preferencesBackups: list: "Cadangan yang dibuat" saveNew: "Simpan cadangan baru" diff --git a/locales/index.d.ts b/locales/index.d.ts index 978442639d..8ed240c609 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -463,6 +463,7 @@ export interface Locale { "moderation": string; "moderationNote": string; "addModerationNote": string; + "moderationLogs": string; "nUsersMentioned": string; "securityKeyAndPasskey": string; "securityKey": string; @@ -1317,6 +1318,8 @@ export interface Locale { "appIconStyleRecommendation": string; "appIconResolutionMustBe": string; "manifestJsonOverride": string; + "shortName": string; + "shortNameDescription": string; }; "_accountMigration": { "moveFrom": string; @@ -1798,6 +1801,7 @@ export interface Locale { "install": string; "installWarn": string; "manage": string; + "viewSource": string; }; "_preferencesBackups": { "list": string; @@ -2509,6 +2513,20 @@ export interface Locale { "mention": string; }; }; + "_moderationLogTypes": { + "assignRole": string; + "unassignRole": string; + "updateRole": string; + "suspend": string; + "unsuspend": string; + "addCustomEmoji": string; + "updateServerSettings": string; + "updateUserNote": string; + "deleteDriveFile": string; + "deleteNote": string; + "createGlobalAnnouncement": string; + "createUserAnnouncement": string; + }; "_abuse": { "_resolver": { "1hour": string; diff --git a/locales/it-IT.yml b/locales/it-IT.yml index e609053fcb..f3fb494821 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1543,6 +1543,7 @@ _plugin: install: "Installa estensioni" installWarn: "Si prega di installare soltanto estensioni che provengono da fonti affidabili." manage: "Gestisci estensioni" + viewSource: "Visualizza sorgente" _preferencesBackups: list: "Elenco di impostazioni salvate in precedenza" saveNew: "Nuovo salvataggio" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 602df610cf..2f7298769a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -460,6 +460,7 @@ moderator: "モデレーター" moderation: "モデレーション" moderationNote: "モデレーションノート" addModerationNote: "モデレーションノートを追加する" +moderationLogs: "モデログ" nUsersMentioned: "{n}人が投稿" securityKeyAndPasskey: "セキュリティキー・パスキー" securityKey: "セキュリティキー" @@ -1314,6 +1315,8 @@ _serverSettings: appIconStyleRecommendation: "円形もしくは角丸にクロップされる場合があるため、塗り潰された余白のある背景を持つことが推奨されます。" appIconResolutionMustBe: "解像度は必ず{resolution}である必要があります。" manifestJsonOverride: "manifest.jsonのオーバーライド" + shortName: "略称" + shortNameDescription: "サーバーの正式名称が長い場合に、代わりに表示することのできる略称や通称。" _accountMigration: moveFrom: "別のアカウントからこのアカウントに移行" @@ -1714,6 +1717,7 @@ _plugin: install: "プラグインのインストール" installWarn: "信頼できないプラグインはインストールしないでください。" manage: "プラグインの管理" + viewSource: "ソースを表示" _preferencesBackups: list: "作成したバックアップ" @@ -2340,7 +2344,7 @@ _notification: _types: all: "すべて" - note: "ノート" + note: "ユーザーの新規投稿" follow: "フォロー" mention: "メンション" reply: "リプライ" @@ -2419,6 +2423,20 @@ _webhookSettings: reaction: "リアクションがあったとき" mention: "メンションされたとき" +_moderationLogTypes: + assignRole: "ロールへアサイン" + unassignRole: "ロールのアサイン解除" + updateRole: "ロール設定更新" + suspend: "凍結" + unsuspend: "凍結解除" + addCustomEmoji: "カスタム絵文字追加" + updateServerSettings: "サーバー設定更新" + updateUserNote: "モデレーションノート更新" + deleteDriveFile: "ファイルを削除" + deleteNote: "ノートを削除" + createGlobalAnnouncement: "全体のお知らせを作成" + createUserAnnouncement: "ユーザーへお知らせを作成" + _abuse: _resolver: 1hour: "一時間" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index aa7870cde9..5203c5a071 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -1524,6 +1524,7 @@ _plugin: install: "プラグインのインストール" installWarn: "信頼できへんプラグインはインストールせんとってな" manage: "プラグインの管理" + viewSource: "ソースを表示" _preferencesBackups: list: "作ったバックアップ" saveNew: "新しく保存" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index cb8433ab66..2a7765e4ba 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1671,6 +1671,7 @@ _plugin: install: "플러그인 설치" installWarn: "신뢰할 수 없는 플러그인은 설치하지 않는 것이 좋아요." manage: "플러그인 관리" + viewSource: "소스 보기" _preferencesBackups: list: "생성한 백업" saveNew: "새 백업 만들기" @@ -2256,7 +2257,7 @@ _notification: notificationWillBeDisplayedLikeThis: "알림이 이렇게 표시돼요!" _types: all: "전부" - note: "노트" + note: "새 노트" follow: "팔로잉" mention: "멘션" reply: "답글" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index fcc9919b5d..6a8ca08a2c 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -939,6 +939,7 @@ _plugin: install: "Zainstaluj wtyczki" installWarn: "Nie instaluj niezaufanych wtyczek." manage: "Zarządzanie wtyczkami" + viewSource: "Zobacz źródło" _preferencesBackups: list: "Utworzone kopie zapasowe" saveNew: "Zapisz nową kopię zapasową" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index b6b25a30b4..3308113ffe 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -1441,6 +1441,7 @@ _plugin: install: "Установка расширений" installWarn: "Пожалуйста, не устанавливайте расширения, которым не доверяете." manage: "Управление расширениями" + viewSource: "Просмотр исходника" _preferencesBackups: list: "Существующие резервные копии" saveNew: "Создать резервную копию" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 173a02240f..e7a684a42e 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -992,6 +992,7 @@ _plugin: install: "Inštalova pluginy" installWarn: "Prosím neinštalujte nedôveryhodné pluginy." manage: "Spravovanie pluginov" + viewSource: "Ukázať zdroj" _preferencesBackups: list: "Vytvorené zálohy" saveNew: "Uložiť novú" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index cf4e17ad7c..ffd986b76e 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1523,6 +1523,7 @@ _plugin: install: "ติดตั้งปลั๊กอิน" installWarn: "กรุณาอย่าติดตั้งปลั๊กอินที่ไม่น่าเชื่อถือนะคะ" manage: "จัดการปลั๊กอิน" + viewSource: "ดูต้นฉบับ" _preferencesBackups: list: "สร้างการสำรองข้อมูล" saveNew: "บันทึกใหม่" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 08dd63c565..4b871d198f 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -1194,6 +1194,7 @@ _plugin: install: "Встановити плагін" installWarn: "Будь ласка, не встановлюйте плагінів, яким ви не довіряєте." manage: "Керування плагінами" + viewSource: "Переглянути вихідний код" _preferencesBackups: list: "Створені бекапи" saveNew: "Зберегти як новий" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index ba5d98ec40..c617f31dd2 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -1357,6 +1357,7 @@ _plugin: install: "Cài đặt tiện ích" installWarn: "Vui lòng không cài đặt những tiện ích đáng ngờ." manage: "Quản lý plugin" + viewSource: "Xem mã nguồn" _preferencesBackups: list: "Tạo sao lưu" saveNew: "Lưu bản sao lưu" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index d1128cd2a8..548fa0dd93 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -722,6 +722,7 @@ lockedAccountInfo: "即使启用该功能,只要您不将帖子可见范围设 alwaysMarkSensitive: "默认将媒体文件标记为敏感内容" loadRawImages: "添加附件图像的缩略图时使用原始图像质量" disableShowingAnimatedImages: "不播放动画" +highlightSensitiveMedia: "高亮显示敏感媒体" verificationEmailSent: "已发送确认电子邮件。请访问电子邮件中的链接以完成设置。" notSet: "未设置" emailVerified: "电子邮件地址已验证" @@ -1130,6 +1131,8 @@ keepScreenOn: "保持设备屏幕开启" verifiedLink: "已验证的链接" notifyNotes: "打开发帖通知" unnotifyNotes: "关闭发帖通知" +authentication: "验证" +authenticationRequiredToContinue: "要继续,请先进行验证" _announcement: forExistingUsers: "仅限现有用户" forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。" @@ -1543,6 +1546,7 @@ _plugin: install: "安装插件" installWarn: "请不要安装不可信的插件。" manage: "管理插件..." + viewSource: "查看源代码" _preferencesBackups: list: "已创建的备份" saveNew: "另存为" @@ -1875,6 +1879,7 @@ _antennaSources: users: "来自指定用户的帖子" userList: "来自指定列表中的帖子" userGroup: "来自指定群组中用户的帖子" + userBlacklist: "除掉已选择用户后所有的帖子" _weekday: sunday: "星期日" monday: "星期一" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index baab998379..aeefc1b8dc 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -321,7 +321,7 @@ copyUrl: "複製URL" rename: "重新命名" avatar: "大頭貼" banner: "橫幅" -displayOfSensitiveMedia: "顯示敏感媒體" +displayOfSensitiveMedia: "敏感檔案的顯示" whenServerDisconnected: "與伺服器的連接中斷時" disconnectedFromServer: "與伺服器中斷連線" reload: "重新整理" @@ -502,7 +502,7 @@ createAccount: "建立帳戶" existingAccount: "現有帳戶" regenerate: "再次生成" fontSize: "字體大小" -mediaListWithOneImageAppearance: "只有一張圖片時的媒體列表高度" +mediaListWithOneImageAppearance: "只有一張圖片時的檔案列表高度" limitTo: "上限為 {x}" noFollowRequests: "沒有追隨您的請求" openImageInNewTab: "於新分頁中開啟圖片" @@ -719,9 +719,10 @@ driveUsage: "雲端硬碟使用量" noCrawle: "拒絕搜尋引擎索引" noCrawleDescription: "要求網路搜尋引擎不要索引你的個人資料頁、貼文及頁面等。" lockedAccountInfo: "即使你通過了追隨者請求,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。" -alwaysMarkSensitive: "預設將多媒體標記為敏感內容" +alwaysMarkSensitive: "預設標記檔案為敏感內容" loadRawImages: "以原始圖檔顯示附件圖檔的縮圖" disableShowingAnimatedImages: "不播放動態圖檔" +highlightSensitiveMedia: "強調敏感標記" verificationEmailSent: "已發送驗證電子郵件。請點擊進入電子郵件中的鏈接完成驗證。" notSet: "未設定" emailVerified: "已成功驗證您的電郵" @@ -940,7 +941,7 @@ type: "類型" speed: "速度" slow: "慢" fast: "快" -sensitiveMediaDetection: "敏感性媒體的檢測" +sensitiveMediaDetection: "敏感檔案的檢測" localOnly: "僅限本地" remoteOnly: "僅限遠端" failedToUpload: "上傳失敗" @@ -949,7 +950,7 @@ cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有可用空間,因此 cannotUploadBecauseExceedsFileSizeLimit: "由於超過了檔案大小的限制,無法上傳。" beta: "測試版" enableAutoSensitive: "自動 NSFW 判定" -enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷多媒體內容是否需要標記 NSFW。即使關閉此功能,也可能會依實例規則而自動啟用。" +enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷檔案是否需要標記為敏感。即使關閉此功能,也可能會依實例規則而自動啟用。" activeEmailValidationDescription: "積極驗證使用者的電郵地址,以判斷它是否可以通訊。關閉此選項代表只會檢查地址是否符合格式。" navbar: "導覽列" shuffle: "隨機" @@ -1130,6 +1131,8 @@ keepScreenOn: "保持設備螢幕開啟" verifiedLink: "已驗證連結" notifyNotes: "開啟貼文通知" unnotifyNotes: "關閉貼文通知" +authentication: "驗證" +authenticationRequiredToContinue: "請於繼續前完成驗證" _announcement: forExistingUsers: "僅限既有的使用者" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" @@ -1163,6 +1166,8 @@ _serverSettings: appIconStyleRecommendation: "因為可能會裁剪成圓形或圓角,所以建議用單色填滿邊框及背景。" appIconResolutionMustBe: "解析度必須為 {resolution}。" manifestJsonOverride: "覆寫 manifest.json" + shortName: "簡稱" + shortNameDescription: "如果伺服器的正式名稱很長,可用簡稱或通稱代替。" _accountMigration: moveFrom: "從其他帳戶遷移到這個帳戶" moveFromSub: "為另一個帳戶建立別名" @@ -1492,7 +1497,7 @@ _role: or: "~或~" not: "~否" _sensitiveMediaDetection: - description: "您可以使用機器學習自動檢測敏感媒體並將其用於審查。 伺服器的負荷會稍微增加。" + description: "您可以使用機器學習自動檢測敏感檔案以便審查。這會稍微增加伺服器負荷。" sensitivity: "檢測敏感度" sensitivityDescription: "敏感度低時,誤檢測(偽陽性)會減少。敏感度高時,漏檢(偽陰性)會減少。" setSensitiveFlagAutomatically: "設定 NSFW 標籤" @@ -1543,6 +1548,7 @@ _plugin: install: "安裝外掛組件" installWarn: "請不要安裝來源不明的外掛。" manage: "管理外掛" + viewSource: "檢視原始碼" _preferencesBackups: list: "已備份的設定檔" saveNew: "另存新檔" @@ -1577,9 +1583,9 @@ _aboutMisskey: morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰" patrons: "贊助者" _displayOfSensitiveMedia: - respect: "隱藏被標記為敏感的多媒體內容" - ignore: "不隱藏被標記為敏感的多媒體內容" - force: "隱藏所有多媒體內容" + respect: "隱藏敏感檔案" + ignore: "顯示敏感檔案" + force: "隱藏所有檔案" _mfm: cheatSheet: "MFM代碼小抄" intro: "MFM是Misskey專用的標記語言,可以在Misskey中的各個位置使用。 您可以這裏看到MFM可用語法列表。" @@ -1875,6 +1881,7 @@ _antennaSources: users: "來自特定使用者的貼文" userList: "來自特定清單中的貼文" userGroup: "來自特定群組的貼文" + userBlacklist: "除指定使用者外的所有貼文" _weekday: sunday: "週日" monday: "週一" @@ -2107,6 +2114,7 @@ _notification: notificationWillBeDisplayedLikeThis: "通知會以這樣的方式顯示" _types: all: "全部 " + note: "使用者的最新貼文" follow: "追隨中" mention: "提及" reply: "回覆" diff --git a/package.json b/package.json index 01bed78a2e..0ffac749fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cherrypick", - "version": "2023.9.0-beta.11-cp-4.3.0-beta.7", + "version": "2023.9.0-rc.2-cp-4.3.0-beta.7", "codename": "nasubi", "repository": { "type": "git", @@ -57,7 +57,7 @@ "@typescript-eslint/parser": "6.7.2", "cross-env": "7.0.3", "cypress": "13.2.0", - "eslint": "8.49.0", + "eslint": "8.50.0", "start-server-and-test": "2.0.1" }, "optionalDependencies": { diff --git a/packages/backend/migration/1695440131671-short-name.js b/packages/backend/migration/1695440131671-short-name.js new file mode 100644 index 0000000000..2c37297fc1 --- /dev/null +++ b/packages/backend/migration/1695440131671-short-name.js @@ -0,0 +1,11 @@ +export class ShortName1695440131671 { + name = 'ShortName1695440131671' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "shortName" character varying(64)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "shortName"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 4c3931ef37..531ea1590f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -73,9 +73,9 @@ "@fastify/view": "8.2.0", "@google-cloud/logging": "^10.5.0", "@google-cloud/translate": "^7.2.1", - "@nestjs/common": "10.2.5", - "@nestjs/core": "10.2.5", - "@nestjs/testing": "10.2.5", + "@nestjs/common": "10.2.6", + "@nestjs/core": "10.2.6", + "@nestjs/testing": "10.2.6", "@peertube/http-signature": "1.7.0", "@simplewebauthn/server": "8.1.1", "@sinonjs/fake-timers": "11.1.0", @@ -89,7 +89,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.2", - "bullmq": "4.11.3", + "bullmq": "4.11.4", "cacheable-lookup": "7.0.0", "cbor": "9.0.1", "chalk": "5.3.0", @@ -178,7 +178,7 @@ "@simplewebauthn/typescript-types": "8.0.0", "@swc/jest": "0.2.29", "@types/accepts": "1.3.5", - "@types/archiver": "5.3.2", + "@types/archiver": "5.3.3", "@types/bcryptjs": "2.4.4", "@types/body-parser": "1.19.3", "@types/cbor": "6.0.0", @@ -220,7 +220,7 @@ "@typescript-eslint/parser": "6.7.2", "aws-sdk-client-mock": "3.0.0", "cross-env": "7.0.3", - "eslint": "8.49.0", + "eslint": "8.50.0", "eslint-plugin-import": "2.28.1", "execa": "8.0.1", "jest": "29.7.0", diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts index b6a5ff989f..d54ddecada 100644 --- a/packages/backend/src/core/AnnouncementService.ts +++ b/packages/backend/src/core/AnnouncementService.ts @@ -12,6 +12,7 @@ import { bindThis } from '@/decorators.js'; import { Packed } from '@/misc/json-schema.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; @Injectable() export class AnnouncementService { @@ -24,6 +25,7 @@ export class AnnouncementService { private idService: IdService, private globalEventService: GlobalEventService, + private moderationLogService: ModerationLogService, ) { } @@ -58,7 +60,7 @@ export class AnnouncementService { } @bindThis - public async create(values: Partial): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> { + public async create(values: Partial, moderator: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> { const announcement = await this.announcementsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), @@ -80,10 +82,21 @@ export class AnnouncementService { this.globalEventService.publishMainStream(announcement.userId, 'announcementCreated', { announcement: packed, }); + + this.moderationLogService.log(moderator, 'createUserAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + userId: values.userId, + }); } else { this.globalEventService.publishBroadcastStream('announcementCreated', { announcement: packed, }); + + this.moderationLogService.log(moderator, 'createGlobalAnnouncement', { + announcementId: announcement.id, + announcement: announcement, + }); } } diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index d509d7a98b..dff09afb47 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -42,6 +42,7 @@ import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { correctFilename } from '@/misc/correct-filename.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; type AddFileArgs = { /** User who wish to add file */ @@ -119,6 +120,7 @@ export class DriveService { private globalEventService: GlobalEventService, private queueService: QueueService, private roleService: RoleService, + private moderationLogService: ModerationLogService, private driveChart: DriveChart, private perUserDriveChart: PerUserDriveChart, private instanceChart: InstanceChart, @@ -662,7 +664,7 @@ export class DriveService { } @bindThis - public async deleteFile(file: MiDriveFile, isExpired = false) { + public async deleteFile(file: MiDriveFile, isExpired = false, deleter?: MiUser) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -685,11 +687,11 @@ export class DriveService { } } - this.deletePostProcess(file, isExpired); + this.deletePostProcess(file, isExpired, deleter); } @bindThis - public async deleteFileSync(file: MiDriveFile, isExpired = false, isRemote: boolean) { + public async deleteFileSync(file: MiDriveFile, isExpired = false, isRemote: boolean, deleter?: MiUser) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -716,11 +718,11 @@ export class DriveService { await Promise.all(promises); } - this.deletePostProcess(file, isExpired); + this.deletePostProcess(file, isExpired, deleter); } @bindThis - private async deletePostProcess(file: MiDriveFile, isExpired = false) { + private async deletePostProcess(file: MiDriveFile, isExpired = false, deleter?: MiUser) { // リモートファイル期限切れ削除後は直リンクにする if (isExpired && file.userHost !== null && file.uri != null) { this.driveFilesRepository.update(file.id, { @@ -747,6 +749,17 @@ export class DriveService { this.instanceChart.updateDrive(file, false); } } + + if (file.userId) { + this.globalEventService.publishDriveStream(file.userId, 'fileDeleted', file.id); + } + + if (deleter && await this.roleService.isModerator(deleter) && (file.userId !== deleter.id)) { + this.moderationLogService.log(deleter, 'deleteDriveFile', { + fileId: file.id, + fileUserId: file.userId, + }); + } } @bindThis diff --git a/packages/backend/src/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts index 66860dc619..25663d2a95 100644 --- a/packages/backend/src/core/ModerationLogService.ts +++ b/packages/backend/src/core/ModerationLogService.ts @@ -9,6 +9,7 @@ import type { ModerationLogsRepository } from '@/models/_.js'; import type { MiUser } from '@/models/User.js'; import { IdService } from '@/core/IdService.js'; import { bindThis } from '@/decorators.js'; +import { ModerationLogPayloads, moderationLogTypes } from '@/types.js'; @Injectable() export class ModerationLogService { @@ -21,13 +22,13 @@ export class ModerationLogService { } @bindThis - public async insertModerationLog(moderator: { id: MiUser['id'] }, type: string, info?: Record) { + public async log(moderator: { id: MiUser['id'] }, type: T, info?: ModerationLogPayloads[T]) { await this.moderationLogsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: moderator.id, type: type, - info: info ?? {}, + info: (info as any) ?? {}, }); } } diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 662951dd66..43ae4b38e6 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -23,6 +23,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; @Injectable() export class NoteDeleteService { @@ -48,6 +49,7 @@ export class NoteDeleteService { private apDeliverManagerService: ApDeliverManagerService, private metaService: MetaService, private searchService: SearchService, + private moderationLogService: ModerationLogService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, private instanceChart: InstanceChart, @@ -58,7 +60,7 @@ export class NoteDeleteService { * @param user 投稿者 * @param note 投稿 */ - async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false) { + async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) { const deletedAt = new Date(); const cascadingNotes = await this.findCascadingNotes(note); @@ -131,6 +133,14 @@ export class NoteDeleteService { id: note.id, userId: user.id, }); + + if (deleter && (note.userId !== deleter.id)) { + this.moderationLogService.log(deleter, 'deleteNote', { + noteId: note.id, + noteUserId: note.userId, + note: note, + }); + } } @bindThis diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 60c05de653..579519c366 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -18,6 +18,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { StreamMessages } from '@/server/api/stream/types.js'; 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 type { OnApplicationShutdown } from '@nestjs/common'; @@ -102,6 +103,7 @@ export class RoleService implements OnApplicationShutdown { private userEntityService: UserEntityService, private globalEventService: GlobalEventService, private idService: IdService, + private moderationLogService: ModerationLogService, ) { //this.onMessage = this.onMessage.bind(this); @@ -380,9 +382,11 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null): Promise { + public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise { const now = new Date(); + const role = await this.rolesRepository.findOneByOrFail({ id: roleId }); + const existing = await this.roleAssignmentsRepository.findOneBy({ roleId: roleId, userId: userId, @@ -412,10 +416,19 @@ export class RoleService implements OnApplicationShutdown { }); this.globalEventService.publishInternalEvent('userRoleAssigned', created); + + if (moderator) { + this.moderationLogService.log(moderator, 'assignRole', { + roleId: roleId, + roleName: role.name, + userId: userId, + expiresAt: expiresAt ? expiresAt.toISOString() : null, + }); + } } @bindThis - public async unassign(userId: MiUser['id'], roleId: MiRole['id']): Promise { + public async unassign(userId: MiUser['id'], roleId: MiRole['id'], moderator?: MiUser): Promise { const now = new Date(); const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId }); @@ -436,6 +449,15 @@ export class RoleService implements OnApplicationShutdown { }); this.globalEventService.publishInternalEvent('userRoleUnassigned', existing); + + if (moderator) { + const role = await this.rolesRepository.findOneByOrFail({ id: roleId }); + this.moderationLogService.log(moderator, 'unassignRole', { + roleId: roleId, + roleName: role.name, + userId: userId, + }); + } } @bindThis @@ -457,6 +479,26 @@ export class RoleService implements OnApplicationShutdown { redisPipeline.exec(); } + @bindThis + public async update(role: MiRole, params: Partial, moderator?: MiUser): Promise { + const date = new Date(); + await this.rolesRepository.update(role.id, { + updatedAt: date, + ...params, + }); + + const updated = await this.rolesRepository.findOneByOrFail({ id: role.id }); + this.globalEventService.publishInternalEvent('roleUpdated', updated); + + if (moderator) { + this.moderationLogService.log(moderator, 'updateRole', { + roleId: role.id, + before: role, + after: updated, + }); + } + } + @bindThis public dispose(): void { this.redisForSub.off('message', this.onMessage); diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 38b853b1de..04302ba58e 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -20,6 +20,11 @@ export class MiMeta { }) public name: string | null; + @Column('varchar', { + length: 64, nullable: true, + }) + public shortName: string | null; + @Column('varchar', { length: 1024, nullable: true, }) diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 031d0c07a8..82a543259f 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -81,7 +81,7 @@ export default class extends Endpoint { // eslint- forExistingUsers: ps.forExistingUsers, needConfirmationToRead: ps.needConfirmationToRead, userId: ps.userId, - }); + }, me); return packed; }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 7bac576fd7..fc34add1c2 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -79,7 +79,7 @@ export default class extends Endpoint { // eslint- roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }); - this.moderationLogService.insertModerationLog(me, 'addEmoji', { + this.moderationLogService.log(me, 'addCustomEmoji', { emojiId: emoji.id, }); diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index ceae9f7559..412c0fdf03 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -381,6 +381,7 @@ export default class extends Endpoint { // eslint- maintainerEmail: instance.maintainerEmail, version: this.config.version, name: instance.name, + shortName: instance.shortName, uri: this.config.url, description: instance.description, langs: instance.langs, diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 6c6f149a83..31c35f2564 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -30,7 +30,7 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { this.queueService.destroy(); - this.moderationLogService.insertModerationLog(me, 'clearQueue'); + this.moderationLogService.log(me, 'clearQueue'); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts index ed2f2ad0d8..5a5702a758 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts @@ -70,7 +70,7 @@ export default class extends Endpoint { // eslint- break; } - this.moderationLogService.insertModerationLog(me, 'promoteQueue'); + this.moderationLogService.log(me, 'promoteQueue'); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts index 3ac140e34d..b7f8a22673 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts @@ -83,7 +83,7 @@ export default class extends Endpoint { // eslint- return; } - await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null); + await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts index 0fe282fadc..df2dd70b11 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts @@ -81,7 +81,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchUser); } - await this.roleService.unassign(user.id, role.id); + await this.roleService.unassign(user.id, role.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index bc0a6f2bf1..d1c81dc861 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -9,6 +9,7 @@ import type { RolesRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; +import { RoleService } from '@/core/RoleService.js'; export const meta = { tags: ['admin', 'role'], @@ -70,16 +71,16 @@ export default class extends Endpoint { // eslint- @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, - private globalEventService: GlobalEventService, + private roleService: RoleService, ) { - super(meta, paramDef, async (ps) => { - const roleExist = await this.rolesRepository.exist({ where: { id: ps.roleId } }); - if (!roleExist) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); + if (role == null) { throw new ApiError(meta.errors.noSuchRole); } const date = new Date(); - await this.rolesRepository.update(ps.roleId, { + await this.roleService.update(role, { updatedAt: date, name: ps.name, description: ps.description, @@ -95,9 +96,7 @@ export default class extends Endpoint { // eslint- canEditMembersByModerator: ps.canEditMembersByModerator, displayOrder: ps.displayOrder, policies: ps.policies, - }); - const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId }); - this.globalEventService.publishInternalEvent('roleUpdated', updated); + }, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 8c1126a30c..e158a6051a 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -62,6 +62,8 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + type: { type: 'string', nullable: true }, + userId: { type: 'string', format: 'misskey:id', nullable: true }, }, required: [], } as const; @@ -78,6 +80,14 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.moderationLogsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId); + if (ps.type != null) { + query.andWhere('report.type = :type', { type: ps.type }); + } + + if (ps.userId != null) { + query.andWhere('report.userId = :userId', { userId: ps.userId }); + } + const reports = await query.limit(ps.limit).getMany(); return await this.moderationLogEntityService.packMany(reports); diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index eb4059824b..0876880c2f 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -60,7 +60,7 @@ export default class extends Endpoint { // eslint- isSuspended: true, }); - this.moderationLogService.insertModerationLog(me, 'suspend', { + this.moderationLogService.log(me, 'suspend', { targetId: user.id, }); diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index b7a828c1ef..b1cae960b3 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -45,7 +45,7 @@ export default class extends Endpoint { // eslint- isSuspended: false, }); - this.moderationLogService.insertModerationLog(me, 'unsuspend', { + this.moderationLogService.log(me, 'unsuspend', { targetId: user.id, }); diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index e22453e201..bc97224f88 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -46,6 +46,7 @@ export const paramDef = { backgroundImageUrl: { type: 'string', nullable: true }, logoImageUrl: { type: 'string', nullable: true }, name: { type: 'string', nullable: true }, + shortName: { type: 'string', nullable: true }, description: { type: 'string', nullable: true }, defaultLightTheme: { type: 'string', nullable: true }, defaultDarkTheme: { type: 'string', nullable: true }, @@ -212,6 +213,10 @@ export default class extends Endpoint { // eslint- set.name = ps.name; } + if (ps.shortName !== undefined) { + set.shortName = ps.shortName; + } + if (ps.description !== undefined) { set.description = ps.description; } @@ -548,8 +553,16 @@ export default class extends Endpoint { // eslint- set.manifestJsonOverride = ps.manifestJsonOverride; } + const before = await this.metaService.fetch(true); + await this.metaService.update(set); - this.moderationLogService.insertModerationLog(me, 'updateMeta'); + + const after = await this.metaService.fetch(true); + + this.moderationLogService.log(me, 'updateServerSettings', { + before, + after, + }); if (set.enableServerMachineStats === true) { const serverStatsService: ServerStatsService = await this.moduleRef.resolve(ServerStatsService); diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index 23d6a44604..91befa24ca 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -32,6 +33,8 @@ export default class extends Endpoint { // eslint- @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -40,9 +43,17 @@ export default class extends Endpoint { // eslint- throw new Error('user not found'); } + const currentProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + await this.userProfilesRepository.update({ userId: user.id }, { moderationNote: ps.text, }); + + this.moderationLogService.log(me, 'updateUserNote', { + userId: user.id, + before: currentProfile.moderationNote, + after: ps.text, + }); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index df80073c51..c4cdd6d6a4 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -65,11 +65,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.accessDenied); } - // Delete - await this.driveService.deleteFile(file); - - // Publish fileDeleted event - this.globalEventService.publishDriveStream(me.id, 'fileDeleted', file.id); + await this.driveService.deleteFile(file, false, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 6c2213e5b8..476c9964a7 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -40,6 +40,10 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + shortName: { + type: 'string', + optional: false, nullable: true, + }, uri: { type: 'string', optional: false, nullable: false, @@ -288,6 +292,7 @@ export default class extends Endpoint { // eslint- version: this.config.version, name: instance.name, + shortName: instance.shortName, uri: this.config.url, description: instance.description, langs: instance.langs, diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index d24af64fcc..37038bb3ce 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -70,7 +70,7 @@ export default class extends Endpoint { // eslint- } // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note); + await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note, false, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 5d4f9dbf0b..3d1af099f1 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -4,12 +4,13 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import { Brackets, type FindOptionsWhere } from 'typeorm'; import type { NoteReactionsRepository } from '@/models/_.js'; import type { MiNoteReaction } from '@/models/NoteReaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; import { DI } from '@/di-symbols.js'; -import type { FindOptionsWhere } from 'typeorm'; +import { QueryService } from '@/core/QueryService.js'; export const meta = { tags: ['notes', 'reactions'], @@ -44,7 +45,6 @@ export const paramDef = { noteId: { type: 'string', format: 'misskey:id' }, type: { type: 'string', nullable: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - offset: { type: 'integer', default: 0 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, }, @@ -58,29 +58,23 @@ export default class extends Endpoint { // eslint- private noteReactionsRepository: NoteReactionsRepository, private noteReactionEntityService: NoteReactionEntityService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = { - noteId: ps.noteId, - } as FindOptionsWhere; + const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId) + .andWhere('reaction.noteId = :noteId', { noteId: ps.noteId }) + .leftJoinAndSelect('reaction.user', 'user') + .leftJoinAndSelect('reaction.note', 'note'); if (ps.type) { // ローカルリアクションはホスト名が . とされているが // DB 上ではそうではないので、必要に応じて変換 const suffix = '@.:'; const type = ps.type.endsWith(suffix) ? ps.type.slice(0, ps.type.length - suffix.length) + ':' : ps.type; - query.reaction = type; + query.andWhere('reaction.reaction = :type', { type }); } - const reactions = await this.noteReactionsRepository.find({ - where: query, - take: ps.limit, - skip: ps.offset, - order: { - id: -1, - }, - relations: ['user', 'note'], - }); + const reactions = await query.limit(ps.limit).getMany(); return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me))); }); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 1757df4a8d..9bcfb9d824 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -114,10 +114,10 @@ export class ClientServerService { let manifest = { // 空文字列の場合右辺を使いたいため // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - 'short_name': instance.name || 'CherryPick', + 'short_name': instance.shortName || instance.name || this.config.host, // 空文字列の場合右辺を使いたいため // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - 'name': instance.name || 'CherryPick', + 'name': instance.name || this.config.host, 'start_url': '/', 'display': 'standalone', 'background_color': '#95e3e8', diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 8170ce6790..22d7fd6f24 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -35,7 +35,7 @@ html link(rel='prefetch' href=infoImageUrl) link(rel='prefetch' href=notFoundImageUrl) //- https://github.com/misskey-dev/misskey/issues/9842 - link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.32.0') + link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.35.0') link(rel='modulepreload' href=`/vite/${clientEntry.file}`) if !config.clientManifestExists diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index e6eb6cfb94..7cadfd92a1 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -27,3 +27,82 @@ export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const; export const ffVisibility = ['public', 'followers', 'private'] as const; + +export const moderationLogTypes = [ + 'updateServerSettings', + 'suspend', + 'unsuspend', + 'updateUserNote', + 'addCustomEmoji', + 'assignRole', + 'unassignRole', + 'updateRole', + 'deleteRole', + 'clearQueue', + 'promoteQueue', + 'deleteDriveFile', + 'deleteNote', + 'createGlobalAnnouncement', + 'createUserAnnouncement', +] as const; + +export type ModerationLogPayloads = { + updateServerSettings: { + before: any | null; + after: any | null; + }; + suspend: { + targetId: string; + }; + unsuspend: { + targetId: string; + }; + updateUserNote: { + userId: string; + before: string | null; + after: string | null; + }; + addCustomEmoji: { + emojiId: string; + }; + assignRole: { + userId: string; + roleId: string; + roleName: string; + expiresAt: string | null; + }; + unassignRole: { + userId: string; + roleId: string; + roleName: string; + }; + updateRole: { + roleId: string; + before: any; + after: any; + }; + deleteRole: { + roleId: string; + roleName: string; + }; + clearQueue: Record; + promoteQueue: Record; + deleteDriveFile: { + fileId: string; + fileUserId: string | null; + }; + deleteNote: { + noteId: string; + noteUserId: string; + note: any; + }; + createGlobalAnnouncement: { + announcementId: string; + announcement: any; + }; + createUserAnnouncement: { + announcementId: string; + announcement: any; + userId: string; + }; +}; diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts index 174f8d3c9f..f09d1c9111 100644 --- a/packages/backend/test/unit/AnnouncementService.ts +++ b/packages/backend/test/unit/AnnouncementService.ts @@ -16,6 +16,7 @@ import { genAidx } from '@/misc/id/aidx.js'; import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import type { TestingModule } from '@nestjs/testing'; import type { MockFunctionMetadata } from 'jest-mock'; @@ -29,6 +30,7 @@ describe('AnnouncementService', () => { let announcementsRepository: AnnouncementsRepository; let announcementReadsRepository: AnnouncementReadsRepository; let globalEventService: jest.Mocked; + let moderationLogService: jest.Mocked; function createUser(data: Partial = {}) { const un = secureRndstr(16); @@ -71,8 +73,11 @@ describe('AnnouncementService', () => { publishMainStream: jest.fn(), publishBroadcastStream: jest.fn(), }; - } - if (typeof token === 'function') { + } else if (token === ModerationLogService) { + return { + log: jest.fn(), + }; + } else if (typeof token === 'function') { const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata; const Mock = moduleMocker.generateFromMetadata(mockMetadata); return new Mock(); @@ -87,6 +92,7 @@ describe('AnnouncementService', () => { announcementsRepository = app.get(DI.announcementsRepository); announcementReadsRepository = app.get(DI.announcementReadsRepository); globalEventService = app.get(GlobalEventService) as jest.Mocked; + moderationLogService = app.get(ModerationLogService) as jest.Mocked; }); afterEach(async () => { @@ -155,10 +161,11 @@ describe('AnnouncementService', () => { describe('create', () => { test('通常', async () => { + const me = await createUser(); const result = await announcementService.create({ title: 'Title', text: 'Text', - }); + }, me); expect(result.raw.title).toBe('Title'); expect(result.packed.title).toBe('Title'); @@ -166,15 +173,17 @@ describe('AnnouncementService', () => { expect(globalEventService.publishBroadcastStream).toHaveBeenCalled(); expect(globalEventService.publishBroadcastStream.mock.lastCall![0]).toBe('announcementCreated'); expect((globalEventService.publishBroadcastStream.mock.lastCall![1] as any).announcement).toBe(result.packed); + expect(moderationLogService.log).toHaveBeenCalled(); }); test('ユーザー指定', async () => { + const me = await createUser(); const user = await createUser(); const result = await announcementService.create({ title: 'Title', text: 'Text', userId: user.id, - }); + }, me); expect(result.raw.title).toBe('Title'); expect(result.packed.title).toBe('Title'); @@ -184,6 +193,7 @@ describe('AnnouncementService', () => { expect(globalEventService.publishMainStream.mock.lastCall![0]).toBe(user.id); expect(globalEventService.publishMainStream.mock.lastCall![1]).toBe('announcementCreated'); expect((globalEventService.publishMainStream.mock.lastCall![2] as any).announcement).toBe(result.packed); + expect(moderationLogService.log).toHaveBeenCalled(); }); }); diff --git a/packages/cherrypick-js/etc/cherrypick-js.api.md b/packages/cherrypick-js/etc/cherrypick-js.api.md index 92525bf90a..97f00e5532 100644 --- a/packages/cherrypick-js/etc/cherrypick-js.api.md +++ b/packages/cherrypick-js/etc/cherrypick-js.api.md @@ -2348,7 +2348,8 @@ declare namespace entities { Invite, InviteLimit, UserSorting, - OriginType + OriginType, + ModerationLog } } export { entities } @@ -2474,6 +2475,7 @@ type LiteInstanceMetadata = { maintainerEmail: string | null; version: string; name: string | null; + shortName: string | null; uri: string; description: string | null; langs: string[]; @@ -2587,6 +2589,50 @@ type MessagingMessage = { groupId: UserGroup['id'] | null; }; +// @public (undocumented) +type ModerationLog = { + id: ID; + createdAt: DateString; + userId: User['id']; + user: UserDetailed | null; +} & ({ + type: 'updateServerSettings'; + info: ModerationLogPayloads['updateServerSettings']; +} | { + type: 'suspend'; + info: ModerationLogPayloads['suspend']; +} | { + type: 'unsuspend'; + info: ModerationLogPayloads['unsuspend']; +} | { + type: 'updateUserNote'; + info: ModerationLogPayloads['updateUserNote']; +} | { + type: 'addCustomEmoji'; + info: ModerationLogPayloads['addCustomEmoji']; +} | { + type: 'assignRole'; + info: ModerationLogPayloads['assignRole']; +} | { + type: 'unassignRole'; + info: ModerationLogPayloads['unassignRole']; +} | { + type: 'updateRole'; + info: ModerationLogPayloads['updateRole']; +} | { + type: 'deleteRole'; + info: ModerationLogPayloads['deleteRole']; +} | { + type: 'clearQueue'; + info: ModerationLogPayloads['clearQueue']; +} | { + type: 'promoteQueue'; + info: ModerationLogPayloads['promoteQueue']; +}); + +// @public (undocumented) +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement"]; + // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; @@ -2938,6 +2984,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:659:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/entities.ts:579:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/cherrypick-js/package.json b/packages/cherrypick-js/package.json index 717c28bd21..8bf8a18b85 100644 --- a/packages/cherrypick-js/package.json +++ b/packages/cherrypick-js/package.json @@ -26,7 +26,7 @@ "@types/node": "20.6.3", "@typescript-eslint/eslint-plugin": "6.7.2", "@typescript-eslint/parser": "6.7.2", - "eslint": "8.49.0", + "eslint": "8.50.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", diff --git a/packages/cherrypick-js/src/consts.ts b/packages/cherrypick-js/src/consts.ts index 6cf6dc07e7..346affc6a5 100644 --- a/packages/cherrypick-js/src/consts.ts +++ b/packages/cherrypick-js/src/consts.ts @@ -44,3 +44,82 @@ export const permissions = [ 'read:flash-likes', 'write:flash-likes', ]; + +export const moderationLogTypes = [ + 'updateServerSettings', + 'suspend', + 'unsuspend', + 'updateUserNote', + 'addCustomEmoji', + 'assignRole', + 'unassignRole', + 'updateRole', + 'deleteRole', + 'clearQueue', + 'promoteQueue', + 'deleteDriveFile', + 'deleteNote', + 'createGlobalAnnouncement', + 'createUserAnnouncement', +] as const; + +export type ModerationLogPayloads = { + updateServerSettings: { + before: any | null; + after: any | null; + }; + suspend: { + targetId: string; + }; + unsuspend: { + targetId: string; + }; + updateUserNote: { + userId: string; + before: string | null; + after: string | null; + }; + addCustomEmoji: { + emojiId: string; + }; + assignRole: { + userId: string; + roleId: string; + roleName: string; + expiresAt: string | null; + }; + unassignRole: { + userId: string; + roleId: string; + roleName: string; + }; + updateRole: { + roleId: string; + before: any; + after: any; + }; + deleteRole: { + roleId: string; + roleName: string; + }; + clearQueue: Record; + promoteQueue: Record; + deleteDriveFile: { + fileId: string; + fileUserId: string | null; + }; + deleteNote: { + noteId: string; + noteUserId: string; + note: any; + }; + createGlobalAnnouncement: { + announcementId: string; + announcement: any; + }; + createUserAnnouncement: { + announcementId: string; + announcement: any; + userId: string; + }; +}; diff --git a/packages/cherrypick-js/src/entities.ts b/packages/cherrypick-js/src/entities.ts index af3dd76641..65cdc19caf 100644 --- a/packages/cherrypick-js/src/entities.ts +++ b/packages/cherrypick-js/src/entities.ts @@ -1,3 +1,5 @@ +import { ModerationLogPayloads } from './consts.js'; + export type ID = string; export type DateString = string; @@ -304,6 +306,7 @@ export type LiteInstanceMetadata = { maintainerEmail: string | null; version: string; name: string | null; + shortName: string | null; uri: string; description: string | null; langs: string[]; @@ -573,3 +576,43 @@ export type UserSorting = | '+updatedAt' | '-updatedAt'; export type OriginType = 'combined' | 'local' | 'remote'; + +export type ModerationLog = { + id: ID; + createdAt: DateString; + userId: User['id']; + user: UserDetailed | null; +} & ({ + type: 'updateServerSettings'; + info: ModerationLogPayloads['updateServerSettings']; +} | { + type: 'suspend'; + info: ModerationLogPayloads['suspend']; +} | { + type: 'unsuspend'; + info: ModerationLogPayloads['unsuspend']; +} | { + type: 'updateUserNote'; + info: ModerationLogPayloads['updateUserNote']; +} | { + type: 'addCustomEmoji'; + info: ModerationLogPayloads['addCustomEmoji']; +} | { + type: 'assignRole'; + info: ModerationLogPayloads['assignRole']; +} | { + type: 'unassignRole'; + info: ModerationLogPayloads['unassignRole']; +} | { + type: 'updateRole'; + info: ModerationLogPayloads['updateRole']; +} | { + type: 'deleteRole'; + info: ModerationLogPayloads['deleteRole']; +} | { + type: 'clearQueue'; + info: ModerationLogPayloads['clearQueue']; +} | { + type: 'promoteQueue'; + info: ModerationLogPayloads['promoteQueue']; +}); diff --git a/packages/cherrypick-js/src/index.ts b/packages/cherrypick-js/src/index.ts index ae4dd31fe0..e78501fdfd 100644 --- a/packages/cherrypick-js/src/index.ts +++ b/packages/cherrypick-js/src/index.ts @@ -17,6 +17,7 @@ export const notificationTypes = consts.notificationTypes; export const noteVisibilities = consts.noteVisibilities; export const mutedNoteReasons = consts.mutedNoteReasons; export const ffVisibility = consts.ffVisibility; +export const moderationLogTypes = consts.moderationLogTypes; // api extractor not supported yet //export * as api from './api.js'; diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 9325ddc51c..9fe6b7fda3 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -24,7 +24,7 @@ "@rollup/plugin-replace": "5.0.2", "@rollup/pluginutils": "5.0.4", "@syuilo/aiscript": "0.16.0", - "@tabler/icons-webfont": "2.32.0", + "@tabler/icons-webfont": "2.35.0", "@vitejs/plugin-vue": "4.3.4", "@vue-macros/reactivity-transform": "0.3.23", "@vue/compiler-sfc": "3.3.4", @@ -81,24 +81,24 @@ "vuedraggable": "next" }, "devDependencies": { - "@storybook/addon-actions": "7.4.3", - "@storybook/addon-essentials": "7.4.3", - "@storybook/addon-interactions": "7.4.3", - "@storybook/addon-links": "7.4.3", - "@storybook/addon-storysource": "7.4.3", - "@storybook/addons": "7.4.3", - "@storybook/blocks": "7.4.3", - "@storybook/core-events": "7.4.3", + "@storybook/addon-actions": "7.4.4", + "@storybook/addon-essentials": "7.4.4", + "@storybook/addon-interactions": "7.4.4", + "@storybook/addon-links": "7.4.4", + "@storybook/addon-storysource": "7.4.4", + "@storybook/addons": "7.4.4", + "@storybook/blocks": "7.4.4", + "@storybook/core-events": "7.4.4", "@storybook/jest": "0.2.2", - "@storybook/manager-api": "7.4.3", - "@storybook/preview-api": "7.4.3", - "@storybook/react": "7.4.3", - "@storybook/react-vite": "7.4.3", + "@storybook/manager-api": "7.4.4", + "@storybook/preview-api": "7.4.4", + "@storybook/react": "7.4.4", + "@storybook/react-vite": "7.4.4", "@storybook/testing-library": "0.2.1", - "@storybook/theming": "7.4.3", - "@storybook/types": "7.4.3", - "@storybook/vue3": "7.4.3", - "@storybook/vue3-vite": "7.4.3", + "@storybook/theming": "7.4.4", + "@storybook/types": "7.4.4", + "@storybook/vue3": "7.4.4", + "@storybook/vue3-vite": "7.4.4", "@testing-library/vue": "7.0.0", "@types/autosize": "^4.0.1", "@types/escape-regexp": "0.0.1", @@ -121,7 +121,7 @@ "acorn": "8.10.0", "cross-env": "7.0.3", "cypress": "13.2.0", - "eslint": "8.49.0", + "eslint": "8.50.0", "eslint-plugin-import": "2.28.1", "eslint-plugin-storybook": "^0.6.13", "eslint-plugin-vue": "9.17.0", @@ -135,7 +135,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "start-server-and-test": "2.0.1", - "storybook": "7.4.3", + "storybook": "7.4.4", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "summaly": "github:misskey-dev/summaly", "vite-plugin-turbosnap": "1.0.3", diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 7f204348b9..2b6e022b23 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -36,14 +36,14 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
+