diff --git a/.github/min.node-version b/.github/min.node-version index d4b7699d36..b8ffd70759 100644 --- a/.github/min.node-version +++ b/.github/min.node-version @@ -1 +1 @@ -20.18.1 +22.15.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index af5c0da4a3..56efd7477c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,29 @@ -## Unreleased +## 2025.8.0 + +### Note +- サポートされるNode.jsの最小バージョンが**22.15.0**になりました ### General - ノートを削除した際、関連するノートが同時に削除されないようになりました - APIで、「replyIdが存在しているのにreplyがnull」や「renoteIdが存在しているのにrenoteがnull」であるという、今までにはなかったパターンが表れることになります +- 定期的に参照されていない古いリモートの投稿を削除する機能が実装されました(コントロールパネル→パフォーマンス→Remote Notes Cleaning) + - 既存のサーバーでは**デフォルトでオフ**、新規サーバーでは**デフォルトでオン**になります + - データベースの肥大化を防止することが可能です + - 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。 + - 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。 +- サーバーの初期設定が完了するまでは連合がオンにならないようになりました +- 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました ### Client +- Feat: セーフモード + - プラグイン・テーマ・カスタムCSSの使用でクライアントの起動に問題が発生した際に、これらを無効にして起動できます + - 以下の方法でセーフモードを起動できます + - `g` キーを連打する + - URLに`?safemode=true`を付ける + - PWAのショートカットで Safemode を選択して起動する - Fix: 一部の設定検索結果が存在しないパスになる問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171) +- Fix: テーマエディタが動作しない問題を修正 ### Server - Enhance: ノートの削除処理の効率化 diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 73f3870f18..15f8e5f16c 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1008,6 +1008,8 @@ lastNDays: "آخر {n} أيام" surrender: "ألغِ" postForm: "أنشئ ملاحظة" information: "عن" +inMinutes: "د" +inDays: "ي" _chat: invitations: "دعوة" noHistory: "السجل فارغ" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 26902eb07f..93ee2b1cb5 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -848,6 +848,8 @@ sourceCode: "সোর্স কোড" flip: "উল্টান" postForm: "নোট লিখুন" information: "আপনার সম্পর্কে" +inMinutes: "মিনিট" +inDays: "দিন" _chat: invitations: "আমন্ত্রণ" noHistory: "কোনো ইতিহাস নেই" diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 6e5882f087..1210ed4126 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -896,7 +896,7 @@ searchResult: "Resultats de la cerca" hashtags: "Etiquetes" troubleshooting: "Solucionar problemes" useBlurEffect: "Fes servir efectes de desenfocament a la interfície" -learnMore: "Saber més " +learnMore: "Saber-ne més " misskeyUpdated: "Misskey s'ha actualitzat " whatIsNew: "Mostra canvis" translate: "Traduir " @@ -1368,6 +1368,8 @@ redisplayAllTips: "Torna ha mostrat tots els trucs i consells" hideAllTips: "Amagar tots els trucs i consells" defaultImageCompressionLevel: "Nivell de comprensió de la imatge per defecte" defaultImageCompressionLevel_description: "Baixa, conserva la qualitat de la imatge però la mida de l'arxiu és més gran.
Alta, redueix la mida de l'arxiu però també la qualitat de la imatge." +inMinutes: "Minut(s)" +inDays: "Di(a)(es)" _order: newest: "Més recent" oldest: "Cronològic" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 5fc772a361..f55236c249 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -1107,6 +1107,8 @@ lastNDays: "Posledních {n} dnů" surrender: "Zrušit" postForm: "Formulář pro odeslání" information: "Informace" +inMinutes: "Minut" +inDays: "Dnů" _chat: invitations: "Pozvat" noHistory: "Žádná historie" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 202e512e63..e3897613a0 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1368,6 +1368,8 @@ redisplayAllTips: "Alle „Tipps und Tricks“ wieder anzeigen" hideAllTips: "Alle „Tipps und Tricks“ ausblenden" defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe" defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße.
Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität." +inMinutes: "Minute(n)" +inDays: "Tag(en)" _order: newest: "Neueste zuerst" oldest: "Älteste zuerst" diff --git a/locales/en-US.yml b/locales/en-US.yml index cc3b8f23ba..ce2b8ca8d8 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1302,7 +1302,7 @@ passkeyVerificationSucceededButPasswordlessLoginDisabled: "Passkey verification messageToFollower: "Message to followers" target: "Target" testCaptchaWarning: "This function is intended for CAPTCHA testing purposes.\nDo not use in a production environment." -prohibitedWordsForNameOfUser: "Prohibited words for user names" +prohibitedWordsForNameOfUser: "Prohibited words for usernames" prohibitedWordsForNameOfUserDescription: "If any of the strings in this list are included in the user's name, the name will be denied. Users with moderator privileges are not affected by this restriction." yourNameContainsProhibitedWords: "Your name contains prohibited words" yourNameContainsProhibitedWordsDescription: "If you wish to use this name, please contact your server administrator." @@ -1368,6 +1368,8 @@ redisplayAllTips: "Show all “Tips & Tricks” again" hideAllTips: "Hide all \"Tips & Tricks\"" defaultImageCompressionLevel: "Default image compression level" defaultImageCompressionLevel_description: "Lower level preserves image quality but increases file size.
Higher level reduce file size, but reduce image quality." +inMinutes: "Minute(s)" +inDays: "Day(s)" _order: newest: "Newest First" oldest: "Oldest First" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 9afebc6580..78b475daab 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -280,8 +280,8 @@ featured: "Destacados" usernameOrUserId: "Nombre o ID del usuario" noSuchUser: "No se encuentra el usuario" lookup: "Búsqueda" -announcements: "Anuncios" -imageUrl: "URL de la imágen" +announcements: "Avisos" +imageUrl: "URL de la imagen." remove: "Borrar" removed: "Borrado" removeAreYouSure: "¿Desea borrar \"{x}\"?" @@ -842,7 +842,7 @@ unlikeConfirm: "¿Quitar como favorito?" fullView: "Vista completa" quitFullView: "quitar vista completa" addDescription: "Agregar descripción" -userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando Pin en el menú de notas individuales" +userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando 'Fijar al perfil' en el menú de notas individuales" notSpecifiedMentionWarning: "Algunas menciones no están incluidas en el destino" info: "Información" userInfo: "Información del usuario" @@ -877,7 +877,7 @@ popularPosts: "Más vistos" shareWithNote: "Compartir con una nota" ads: "Anuncios" expiration: "Termina el" -startingperiod: "periodo de inicio" +startingperiod: "Comienzo" memo: "Notas" priority: "Prioridad" high: "Alta" @@ -1143,7 +1143,7 @@ channelArchiveConfirmTitle: "¿Seguro de archivar {name}?" channelArchiveConfirmDescription: "Un canal archivado no aparecerá en la lista de canales ni en los resultados. Las nuevas publicaciones tampoco serán añadidas." thisChannelArchived: "El canal ha sido archivado." displayOfNote: "Mostrar notas" -initialAccountSetting: "Configración inicial de su cuenta\nか\nConfigración de inicio" +initialAccountSetting: "Configración inicial de su cuenta" youFollowing: "Siguiendo" preventAiLearning: "Rechazar el uso en el Aprendizaje de Máquinas. (IA Generativa)" preventAiLearningDescription: "Pedirle a las arañas (crawlers) no usar los textos publicados o imágenes en el aprendizaje automático (IA Predictiva / Generativa). Ésto se logra añadiendo una marca respuesta HTML con la cadena \"noai\" al cantenido. Una prevención total no podría lograrse sólo usando ésta marca, ya que puede ser simplemente ignorada." @@ -1358,8 +1358,8 @@ advice: "Consejos" realtimeMode: "Modo en tiempo real" turnItOn: "Activar" turnItOff: "Desactivar" -emojiMute: "Silenciar emojis" -emojiUnmute: "No Silenciar emojis" +emojiMute: "Silenciar emoji" +emojiUnmute: "No silenciar emoji" muteX: "Silenciar {x}" unmuteX: "Dejar de silenciar {x}" abort: "Abortar" @@ -1368,6 +1368,8 @@ redisplayAllTips: "Volver a mostrar todos \"Trucos y consejos\"" hideAllTips: "Ocultar todos los \"Trucos y consejos\"" defaultImageCompressionLevel: "Nivel de compresión de la imagen por defecto" defaultImageCompressionLevel_description: "Baja, conserva la calidad de la imagen pero la medida del archivo es más grande.
Alta, reduce la medida del archivo pero también la calidad de la imagen." +inMinutes: "Minutos" +inDays: "Días" _order: newest: "Los más recientes primero" oldest: "Los más antiguos primero" @@ -1530,7 +1532,7 @@ _announcement: tooManyActiveAnnouncementDescription: "Tener demasiados anuncios activos empeora la experiencia de usuario. Por favor, considera archivar aquellos anuncios que hayan quedado obsoletos." readConfirmTitle: "¿Marcar como leído?" readConfirmText: "Esto marcará el contenido de \"{title}\" como leído." - shouldNotBeUsedToPresentPermanentInfo: "Dado que puede impactar en la experiencia de usuario de forma significativa, es recomendable usar notificaciones en el flujo de información en vez de información persistente." + shouldNotBeUsedToPresentPermanentInfo: "Se recomienda utilizar los avisos para publicar información que requiera inmediatez, en lugar de hacerlo constantemente, ya que esto perjudica especialmente la UX de los nuevos usuarios." dialogAnnouncementUxWarn: "Mostrar dos o más notificaciones en formato diálogo a la vez puede impactar en la experiencia de usuario de forma significativa, úsalos con cuidado." silence: "Silenciar notificaciones" silenceDescription: "Si lo activas, no enviarás notificación sobre este anuncio y el usuario no tendrá que leerlo." @@ -3121,7 +3123,7 @@ _uploader: tip: "El archivo aún no se ha cargado, por lo que este cuadro de diálogo te permite confirmar, renombrar, comprimir y recortar el archivo antes de cargarlo. Cuando esté listo, puedes iniciar la carga pulsando el botón \"Cargar\"." _clientPerformanceIssueTip: title: "Si crees que el consumo de batería es demasiado alto" - makeSureDisabledAdBlocker: "Por favor, desactive el bloqueador de publicidad." + makeSureDisabledAdBlocker: "Por favor, desactiva el bloqueador de publicidad." makeSureDisabledAdBlocker_description: "Los bloqueadores de anuncios pueden afectar al rendimiento. Asegúrate de que no están activados en tu sistema o en las funciones/extensiones de tu navegador." makeSureDisabledCustomCss: "Desactiva el CSS personalizado" makeSureDisabledCustomCss_description: "Anular estilos puede afectar al rendimiento. Asegúrate de que el CSS personalizado o las extensiones que sobrescriben estilos no están activados." diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index eba5a511de..d68e7dfde4 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -1272,6 +1272,8 @@ pleaseSelectAccount: "Sélectionner un compte" availableRoles: "Rôles disponibles" postForm: "Formulaire de publication" information: "Informations" +inMinutes: "min" +inDays: "j" _chat: invitations: "Inviter" noHistory: "Pas d'historique" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index fb8713d3b3..009142a4ad 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1263,6 +1263,8 @@ thereAreNChanges: "Ada {n} perubahan" prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna" postForm: "Buat catatan" information: "Informasi" +inMinutes: "menit" +inDays: "hari" _chat: invitations: "Undang" noHistory: "Tidak ada riwayat" diff --git a/locales/index.d.ts b/locales/index.d.ts index 088b89b79f..f77925b410 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -315,11 +315,11 @@ export interface Locale extends ILocale { */ "mention": string; /** - * あなた宛て + * メンション */ "mentions": string; /** - * ダイレクト投稿 + * 指名 */ "directNotes": string; /** @@ -5493,6 +5493,30 @@ export interface Locale extends ILocale { * 低くすると画質を保てますが、ファイルサイズは増加します。
高くするとファイルサイズを減らせますが、画質は低下します。 */ "defaultImageCompressionLevel_description": string; + /** + * 分 + */ + "inMinutes": string; + /** + * 日 + */ + "inDays": string; + /** + * セーフモードが有効です + */ + "safeModeEnabled": string; + /** + * セーフモードが有効なため、プラグインはすべて無効化されています。 + */ + "pluginsAreDisabledBecauseSafeMode": string; + /** + * セーフモードが有効なため、カスタムCSSは適用されていません。 + */ + "customCssIsDisabledBecauseSafeMode": string; + /** + * セーフモードが有効な間はデフォルトのテーマが使用されます。セーフモードをオフにすると元に戻ります。 + */ + "themeIsDefaultBecauseSafeMode": string; "_order": { /** * 新しい順 @@ -6329,7 +6353,7 @@ export interface Locale extends ILocale { */ "followers": string; /** - * 指定したユーザーにのみ公開され、また相手に通知が入ります。ダイレクトメッセージのかわりにお使いいただけます。 + * 指定したユーザーにのみ公開され、また相手に通知が入ります。 */ "direct": string; /** @@ -6337,7 +6361,7 @@ export interface Locale extends ILocale { */ "doNotSendConfidencialOnDirect1": string; /** - * 送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーにダイレクト投稿を送信する場合は、機密情報の扱いに注意が必要です。 + * 送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーが含まれる限定公開のノートを作成する際は、機密情報の扱いに注意が必要です。 */ "doNotSendConfidencialOnDirect2": string; /** @@ -6486,6 +6510,22 @@ export interface Locale extends ILocale { * 有効にすると、リアクション作成時のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。 */ "reactionsBufferingDescription": string; + /** + * リモート投稿の自動クリーニング + */ + "remoteNotesCleaning": string; + /** + * 有効にすると、参照されていない古いリモートの投稿を定期的にクリーンアップしてデータベースの肥大化を抑制します。 + */ + "remoteNotesCleaning_description": string; + /** + * 最大クリーニング処理継続時間 + */ + "remoteNotesCleaningMaxProcessingDuration": string; + /** + * 最低ノート保持日数 + */ + "remoteNotesCleaningExpiryDaysForEachNotes": string; /** * 問い合わせ先URL */ @@ -6558,6 +6598,14 @@ export interface Locale extends ILocale { * サーバーで受信したリモートのコンテンツを含め、サーバー内の全てのコンテンツを無条件でインターネットに公開することはリスクが伴います。特に、分散型の特性を知らない閲覧者にとっては、リモートのコンテンツであってもサーバー内で作成されたコンテンツであると誤って認識してしまう可能性があるため、注意が必要です。 */ "userGeneratedContentsVisibilityForVisitor_description2": string; + /** + * サーバーの初期設定ウィザードをやり直しますか? + */ + "restartServerSetupWizardConfirm_title": string; + /** + * 現在の一部の設定はリセットされます。 + */ + "restartServerSetupWizardConfirm_text": string; "_userGeneratedContentsVisibilityForVisitor": { /** * 全て公開 @@ -9593,7 +9641,7 @@ export interface Locale extends ILocale { */ "followersDescription": string; /** - * ダイレクト + * 指名 */ "specified": string; /** @@ -10482,11 +10530,11 @@ export interface Locale extends ILocale { */ "channel": string; /** - * あなた宛て + * メンション */ "mentions": string; /** - * ダイレクト + * 指名 */ "direct": string; /** @@ -11807,6 +11855,10 @@ export interface Locale extends ILocale { * 修復ツールを起動 */ "otherOption3": string; + /** + * Misskeyをセーフモードで起動 + */ + "otherOption4": string; }; "_search": { /** @@ -11943,6 +11995,14 @@ export interface Locale extends ILocale { * 連合可能なサーバーの指定など、高度な設定も後ほど可能です。 */ "youCanConfigureMoreFederationSettingsLater": string; + /** + * 受信コンテンツの自動クリーニング + */ + "remoteContentsCleaning": string; + /** + * 連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったコンテンツを自動でサーバーから削除し、ストレージを節約できます。 + */ + "remoteContentsCleaning_description": string; /** * 管理者情報 */ diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 23d46d6e22..53d52dbf7e 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1313,6 +1313,7 @@ availableRoles: "Ruoli disponibili" acknowledgeNotesAndEnable: "Attivare dopo averne compreso il comportamento." federationSpecified: "Questo server è federato solo con istanze specifiche del Fediverso. Puoi interagire solo con quelle scelte dall'amministrazione." federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server." +draft: "Bozza" confirmOnReact: "Confermare le reazioni" reactAreYouSure: "Vuoi davvero reagire con {emoji} ?" markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale come esplicito?" @@ -1367,6 +1368,11 @@ redisplayAllTips: "Mostra tutti i suggerimenti" hideAllTips: "Nascondi tutti i suggerimenti" defaultImageCompressionLevel: "Livello predefinito di compressione immagini" defaultImageCompressionLevel_description: "La compressione diminuisce la qualità dell'immagine, poca compressione mantiene alta qualità delle immagini. Aumentandola, si riducono le dimensioni del file, a discapito della qualità dell'immagine." +inMinutes: "min" +inDays: "giorni" +_order: + newest: "Prima i più recenti" + oldest: "Meno recenti prima" _chat: noMessagesYet: "Ancora nessun messaggio" newMessage: "Nuovo messaggio" @@ -1993,6 +1999,8 @@ _role: uploadableFileTypes: "Tipi di file caricabili" uploadableFileTypes_caption: "Specifica il tipo MIME. Puoi specificare più valori separandoli andando a capo, oppure indicare caratteri jolly con un asterisco (*). Ad esempio: image/*" uploadableFileTypes_caption2: "A seconda del file, il tipo potrebbe non essere determinato. Se si desidera consentire tali file, aggiungere {x} alla specifica." + noteDraftLimit: "Numero massimo di Note in bozza, lato server" + watermarkAvailable: "Disponibilità della funzione filigrana" _condition: roleAssignedTo: "Assegnato a ruoli manualmente" isLocal: "Profilo locale" @@ -2152,6 +2160,7 @@ _theme: install: "Installa un tema" manage: "Gestione dei temi" code: "Codice tema" + copyThemeCode: "Copia il codice del Tema" description: "Descrizione" installed: "{name} è installato" installedThemes: "Temi installati" @@ -2800,6 +2809,7 @@ _fileViewer: url: "URL" uploadedAt: "Caricato il" attachedNotes: "Note a cui è allegato" + usage: "In uso" thisPageCanBeSeenFromTheAuthor: "Questa pagina può essere vista solo da chi ha caricato il file." _externalResourceInstaller: title: "Installa da sito esterno" @@ -3103,6 +3113,7 @@ _serverSetupWizard: text2: "Se puoi, ti preghiamo di prendere in considerazione l'idea di fare una donazione, così potremo continuare a sviluppare." text3: "Sono previsti anche dei vantaggi speciali per i sostenitori!" _uploader: + editImage: "Modifica immagine" compressedToX: "Compresso in {x}" savedXPercent: "{x}% risparmiati" abortConfirm: "Alcuni file non sono stati caricati. Vuoi annullare l'operazione?" @@ -3169,5 +3180,20 @@ _imageEffector: stripe: "Strisce" polkadot: "A pallini" checker: "revisore" + blockNoise: "Attenua rumore" + tearing: "Strappa immagine" +drafts: "Bozza" _drafts: + select: "Selezionare bozza" + cannotCreateDraftAnymore: "Hai superato il numero massimo di bozze ammissibili." + cannotCreateDraft: "Impossibile creare una bozza di questo contenuto." + delete: "Elimina bozza" + deleteAreYouSure: "Vuoi davvero eliminare la bozza?" + noDrafts: "Non c'è nessuna bozza." + replyTo: "Rispondere a {user}" + quoteOf: "Citare la nota di {user}" + postTo: "Inserire in {channel}" + saveToDraft: "Salva come bozza" + restoreFromDraft: "Recuperare dalle bozze" restore: "Ripristina" + listDrafts: "Elenco bozze" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5bd2fc6e17..4d79b31b1b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -74,8 +74,8 @@ youGotNewFollower: "フォローされました" receiveFollowRequest: "フォローリクエストされました" followRequestAccepted: "フォローが承認されました" mention: "メンション" -mentions: "あなた宛て" -directNotes: "ダイレクト投稿" +mentions: "メンション" +directNotes: "指名" importAndExport: "インポートとエクスポート" import: "インポート" export: "エクスポート" @@ -1368,6 +1368,12 @@ redisplayAllTips: "全ての「ヒントとコツ」を再表示" hideAllTips: "全ての「ヒントとコツ」を非表示" defaultImageCompressionLevel: "デフォルトの画像圧縮度" defaultImageCompressionLevel_description: "低くすると画質を保てますが、ファイルサイズは増加します。
高くするとファイルサイズを減らせますが、画質は低下します。" +inMinutes: "分" +inDays: "日" +safeModeEnabled: "セーフモードが有効です" +pluginsAreDisabledBecauseSafeMode: "セーフモードが有効なため、プラグインはすべて無効化されています。" +customCssIsDisabledBecauseSafeMode: "セーフモードが有効なため、カスタムCSSは適用されていません。" +themeIsDefaultBecauseSafeMode: "セーフモードが有効な間はデフォルトのテーマが使用されます。セーフモードをオフにすると元に戻ります。" _order: newest: "新しい順" @@ -1603,9 +1609,9 @@ _initialTutorial: public: "すべてのユーザーに公開。" home: "ホームタイムラインのみに公開。フォロワー・プロフィールを見に来た人・リノートから、他のユーザーも見ることができます。" followers: "フォロワーにのみ公開。本人以外がリノートすることはできず、またフォロワー以外は閲覧できません。" - direct: "指定したユーザーにのみ公開され、また相手に通知が入ります。ダイレクトメッセージのかわりにお使いいただけます。" + direct: "指定したユーザーにのみ公開され、また相手に通知が入ります。" doNotSendConfidencialOnDirect1: "機密情報は送信する際は注意してください。" - doNotSendConfidencialOnDirect2: "送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーにダイレクト投稿を送信する場合は、機密情報の扱いに注意が必要です。" + doNotSendConfidencialOnDirect2: "送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーが含まれる限定公開のノートを作成する際は、機密情報の扱いに注意が必要です。" localOnly: "他のサーバーに投稿を連合しません。上記の公開範囲に関わらず、他のサーバーのユーザーは、この設定がついたノートを直接閲覧することができなくなります。" _cw: title: "内容を隠す(CW)" @@ -1649,6 +1655,10 @@ _serverSettings: fanoutTimelineDbFallback: "データベースへのフォールバック" fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。" reactionsBufferingDescription: "有効にすると、リアクション作成時のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。" + remoteNotesCleaning: "リモート投稿の自動クリーニング" + remoteNotesCleaning_description: "有効にすると、参照されていない古いリモートの投稿を定期的にクリーンアップしてデータベースの肥大化を抑制します。" + remoteNotesCleaningMaxProcessingDuration: "最大クリーニング処理継続時間" + remoteNotesCleaningExpiryDaysForEachNotes: "最低ノート保持日数" inquiryUrl: "問い合わせ先URL" inquiryUrlDescription: "サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。" openRegistration: "アカウントの作成をオープンにする" @@ -1667,6 +1677,8 @@ _serverSettings: userGeneratedContentsVisibilityForVisitor: "非利用者に対するユーザー作成コンテンツの公開範囲" userGeneratedContentsVisibilityForVisitor_description: "モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます。" userGeneratedContentsVisibilityForVisitor_description2: "サーバーで受信したリモートのコンテンツを含め、サーバー内の全てのコンテンツを無条件でインターネットに公開することはリスクが伴います。特に、分散型の特性を知らない閲覧者にとっては、リモートのコンテンツであってもサーバー内で作成されたコンテンツであると誤って認識してしまう可能性があるため、注意が必要です。" + restartServerSetupWizardConfirm_title: "サーバーの初期設定ウィザードをやり直しますか?" + restartServerSetupWizardConfirm_text: "現在の一部の設定はリセットされます。" _userGeneratedContentsVisibilityForVisitor: all: "全て公開" @@ -2524,7 +2536,7 @@ _visibility: homeDescription: "ホームタイムラインのみに公開" followers: "フォロワー" followersDescription: "自分のフォロワーのみに公開" - specified: "ダイレクト" + specified: "指名" specifiedDescription: "指定したユーザーのみに公開" disableFederation: "連合なし" disableFederationDescription: "他サーバーへの配信を行いません" @@ -2770,8 +2782,8 @@ _deck: antenna: "アンテナ" list: "リスト" channel: "チャンネル" - mentions: "あなた宛て" - direct: "ダイレクト" + mentions: "メンション" + direct: "指名" roleTimeline: "ロールタイムライン" chat: "チャット" @@ -3156,6 +3168,7 @@ _bootErrors: otherOption1: "クライアント設定とキャッシュを削除" otherOption2: "簡易クライアントを起動" otherOption3: "修復ツールを起動" + otherOption4: "Misskeyをセーフモードで起動" _search: searchScopeAll: "全て" @@ -3194,6 +3207,8 @@ _serverSetupWizard: doYouConnectToFediverse_description1: "分散型サーバーで構成されるネットワーク(Fediverse)に接続すると、他のサーバーと相互にコンテンツのやり取りが可能です。" doYouConnectToFediverse_description2: "Fediverseと接続することは「連合」とも呼ばれます。" youCanConfigureMoreFederationSettingsLater: "連合可能なサーバーの指定など、高度な設定も後ほど可能です。" + remoteContentsCleaning: "受信コンテンツの自動クリーニング" + remoteContentsCleaning_description: "連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったコンテンツを自動でサーバーから削除し、ストレージを節約できます。" adminInfo: "管理者情報" adminInfo_description: "問い合わせを受け付けるために使用される管理者情報を設定します。" adminInfo_mustBeFilled: "オープンサーバー、または連合がオンの場合は必ず入力が必要です。" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 92f849efbb..0cffbaa694 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -300,6 +300,7 @@ uploadFromUrlMayTakeTime: "アップロード終わるんにちょい時間か explore: "みつける" messageRead: "もう読んだ" noMoreHistory: "これより昔のんはあらへんで" +startChat: "チャットを始めよか" nUsersRead: "{n}人が読んでもうた" agreeTo: "{0}に同意したで" agree: "せやな" @@ -324,6 +325,7 @@ dark: "ダーク" lightThemes: "デイゲーム" darkThemes: "ナイトゲーム" syncDeviceDarkMode: "デバイスのダークモードと一緒にする" +switchDarkModeManuallyWhenSyncEnabledConfirm: "「{x}」がオンになってるで。同期をオフにして手動でモードを切り替えることにします?" drive: "ドライブ" fileName: "ファイル名" selectFile: "ファイル選んでや" @@ -422,6 +424,7 @@ antennaExcludeBots: "Botアカウントを除外" antennaKeywordsDescription: "スペースで区切ったるとAND指定で、改行で区切ったるとOR指定や" notifyAntenna: "新しいノートを通知すんで" withFileAntenna: "なんか添付されたノートだけ" +excludeNotesInSensitiveChannel: "センシティブなチャンネルのノートは入れんとくわ" enableServiceworker: "ブラウザにプッシュ通知が行くようにする" antennaUsersDescription: "ユーザー名を改行で区切ったってな" caseSensitive: "大文字と小文字は別もんや" @@ -693,6 +696,7 @@ userSaysSomethingAbout: "{name}が「{word}」についてなんか言うてた makeActive: "使うで" display: "表示" copy: "コピー" +copiedToClipboard: "クリップボードにコピーされたで" metrics: "メトリクス" overview: "概要" logs: "ログ" @@ -787,6 +791,7 @@ wide: "広い" narrow: "狭い" reloadToApplySetting: "設定はページリロード後に反映されるで。今リロードしとくか?" needReloadToApply: "反映には再起動せなあかんで" +needToRestartServerToApply: "反映にはサーバーを再起動せなあかんのよ。" showTitlebar: "タイトルバーを見せる" clearCache: "キャッシュをほかす" onlineUsersCount: "{n}人が起きとるで" @@ -974,6 +979,7 @@ document: "ドキュメント" numberOfPageCache: "ページ、どんだけキャッシュすんの?" numberOfPageCacheDescription: "増やすと使いやすくなるけど、負荷とメモリ使用量が増えてくで。一長一短やな。" logoutConfirm: "ログアウトしまっか?" +logoutWillClearClientData: "ログアウトするとクライアントの設定情報がブラウザから消されてまうで。再ログイン時に設定情報を復元できるようにするためには、設定の自動バックアップを有効にするとええで。" lastActiveDate: "最後に使った日時" statusbar: "ステータスバー" pleaseSelect: "選んだってやー" @@ -992,6 +998,7 @@ failedToUpload: "アップロードに失敗してもうたわ…" cannotUploadBecauseInappropriate: "きわどい内容を含むかもしれへんって言われたからアップロードできへんわ。" cannotUploadBecauseNoFreeSpace: "ドライブがもうパンパンやからアップロードできへんわ。" cannotUploadBecauseExceedsFileSizeLimit: "ファイルが思うたよりも大きいさかいアップロードできへんでこれ。" +cannotUploadBecauseUnallowedFileType: "許可されてへんファイル種別やからアップロードできへんっぽい。" beta: "ベータ" enableAutoSensitive: "自動できわどいか判断する" enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、サーバーによっては自動で設定されることがあるで。" @@ -1304,11 +1311,37 @@ federationSpecified: "このサーバーはホワイトリスト連合で運用 federationDisabled: "このサーバーは連合が無効化されてるで。他のサーバーのユーザーとやり取りすることはできひんで。" confirmOnReact: "ツッコむときに確認とる" reactAreYouSure: "\" {emoji} \" でツッコむ?" +markAsSensitiveConfirm: "このメディアをきわどい扱いしときますか?" +unmarkAsSensitiveConfirm: "このメディアはやっぱきわどくなかったってことでええんか?" +noName: "名前はあらへんで" +preferenceSyncConflictTitle: "サーバーに設定値があるみたいやわ" +preferenceSyncConflictText: "同期が有効にされた設定項目は設定値をサーバーに保存するねんけど、この設定項目はサーバーに保存されたやつがあるみたいやわ。どないするん?" +preferenceSyncConflictChoiceMerge: "ガッチャンコしよか" +preferenceSyncConflictChoiceCancel: "同期の有効化はやめとくわ" postForm: "投稿フォーム" information: "情報" +migrateOldSettings: "旧設定情報をお引っ越し" +migrateOldSettings_description: "通常これは自動で行われるはずなんやけど、なんかの理由で上手く移行できへんかったときは手動で移行処理をポチっとできるで。今の設定情報は上書きされるで。" +settingsMigrating: "設定を移行しとるで。ちょっと待っとってな... (後で、設定→その他→旧設定情報を移行 で手動で移行することもできるで)" +driveAboutTip: "ドライブでは、今までアップロードしたファイルがずらーっと表示されるで。
\nノートにファイルをもっかいのっけたり、あとで投稿するファイルをその辺に置いとくこともできるねん。
\nファイルをほかすと、前にそのファイルをのっけた全部の場所(ノート、ページ、アバター、バナー等)からも見えんくなるから気いつけてな。
\nフォルダを作って整理することもできるで。" +turnItOn: "オンにしとこ" +turnItOff: "オフでええわ" +emojiUnmute: "絵文字ミュートやめたる" +unmuteX: "{x}のミュートやめたる" +redisplayAllTips: "全部の「ヒントとコツ」をもっかい見して" +hideAllTips: "「ヒントとコツ」は全部表示せんでええ" +defaultImageCompressionLevel_description: "低くすると画質は保てるんやけど、ファイルサイズが増えるで。
高くするとファイルサイズは減らせるんやけど、画質が落ちるで。" +inMinutes: "分" +inDays: "日" _chat: + noMessagesYet: "まだメッセージはあらへんで" + individualChat_description: "特定のユーザーと一対一でチャットができるで。" + roomChat_description: "複数人でチャットできるで。\nあと、個人チャットを許可してへんユーザーとでも、相手がええって言うならチャットできるで。" + inviteUserToChat: "ユーザーを招待してチャットを始めてみ" invitations: "来てや" + noInvitations: "招待はあらへんで" noHistory: "履歴はないわ。" + noRooms: "ルームはあらへんで" members: "メンバーはん" home: "ホーム" send: "送信" @@ -2617,7 +2650,7 @@ _externalResourceInstaller: _errors: _invalidParams: title: "" - description: "" + description: "外部サイトからデータを持ってくるのに欲しい情報が足らへんみたいやわ。URLは合っとる?" _resourceTypeNotSupported: title: "" description: "" @@ -2648,7 +2681,7 @@ _dataSaver: title: "アイコンの絵" description: "アイコン画像のアニメが止まるで。普通の画像よりもデータ量がでかいから、もっと通信量を節約できるねん。" _code: - title: "コードハイライト" + title: "コードハイライトは表示せんでええ" description: "MFMとかでコードハイライト記法が使われてるとき、タップするまで読み込まれへんくなるで。コードハイライトではハイライトする言語ごとにその決めてるファイルを読む必要はあんねんな。けどな、それは自動で読み込まれなくなるから、通信量を少なくできることができるねん。" _hemisphere: N: "北半球" @@ -2858,3 +2891,8 @@ _watermarkEditor: image: "画像" advanced: "高度" angle: "角度" +_imageEffector: + discardChangesConfirm: "変更をせんで終わるか?" +_drafts: + deleteAreYouSure: "下書きをほかしてもええか?" + noDrafts: "下書きはあらへん" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 00a470a718..8dc227e553 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1368,6 +1368,8 @@ redisplayAllTips: "모든 '팁과 유용한 정보'를 재표시" hideAllTips: "모든 '팁과 유용한 정보'를 비표시" defaultImageCompressionLevel: "기본 이미지 압축 정도" defaultImageCompressionLevel_description: "낮추면 화질을 유지합니다만 파일 크기는 증가합니다.
높이면 파일 크기를 줄일 수 있습니다만 화질은 저하됩니다." +inMinutes: "분" +inDays: "일" _order: newest: "최신 순" oldest: "오래된 순" diff --git a/locales/no-NO.yml b/locales/no-NO.yml index b161ce19b6..a38237208a 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -461,6 +461,8 @@ replies: "Svar" renotes: "Renote" surrender: "Avbryt" information: "Informasjon" +inMinutes: "Minutter" +inDays: "Dager" _chat: invitations: "Inviter" members: "Medlemmer" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index a72b9f2c7d..1edd803972 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -1040,6 +1040,8 @@ surrender: "Odrzuć" gameRetry: "Spróbuj ponownie" postForm: "Formularz tworzenia wpisu" information: "Informacje" +inMinutes: "minuta" +inDays: "dzień" _chat: invitations: "Zaproś" noHistory: "Brak historii" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 978f81b626..5fdf4f8258 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -1368,6 +1368,8 @@ redisplayAllTips: "Mostrar todas as \"Dicas e Truques\" novamente" hideAllTips: "Ocultas todas as \"Dicas e Truques\"" defaultImageCompressionLevel: "Nível de compressão de imagem padrão" defaultImageCompressionLevel_description: "Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem.
Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem." +inMinutes: "Minuto(s)" +inDays: "Dia(s)" _order: newest: "Priorizar Mais Novos" oldest: "Priorizar Mais Antigos" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 1e34af9351..375b46c3e9 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -2,7 +2,7 @@ _lang_: "Русский" headlineMisskey: "Сеть, сплетённая из заметок" introMisskey: "Добро пожаловать! Misskey — это децентрализованный сервис микроблогов с открытым исходным кодом.\nПишите «заметки» — делитесь со всеми происходящим вокруг или рассказывайте о себе 📡\nСтавьте «реакции» — выражайте свои чувства и эмоции от заметок других 👍\nОткройте для себя новый мир 🚀" -poweredByMisskeyDescription: "{name} – сервис на платформе с открытым исходным кодом Misskey, называемый экземпляром Misskey." +poweredByMisskeyDescription: "{name} – один из инстансов (также называемый экземпляром Misskey), использующий платформу с открытым исходным кодом Misskey." monthAndDay: "{day}.{month}" search: "Поиск" reset: "Сброс" @@ -82,7 +82,7 @@ export: "Экспорт" files: "Файлы" download: "Скачать" driveFileDeleteConfirm: "Удалить файл «{name}»? Заметки с ним также будут удалены." -unfollowConfirm: "Удалить из подписок пользователя {name}?" +unfollowConfirm: "Отписаться от {name} ?" exportRequested: "Вы запросили экспорт. Это может занять некоторое время. Результат будет добавлен на «Диск»." importRequested: "Вы запросили импорт. Это может занять некоторое время." lists: "Списки" @@ -298,6 +298,7 @@ uploadFromUrl: "Загрузить по ссылке" uploadFromUrlDescription: "Ссылка на файл, который хотите загрузить" uploadFromUrlRequested: "Загрузка выбранного" uploadFromUrlMayTakeTime: "Загрузка может занять некоторое время." +uploadNFiles: "Загрузить {n} файл" explore: "Обзор" messageRead: "Прочитали" noMoreHistory: "История закончилась" @@ -575,8 +576,10 @@ showFixedPostForm: "Показывать поле для ввода новой showFixedPostFormInChannel: "Показывать поле для ввода новой заметки наверху ленты (каналы)" withRepliesByDefaultForNewlyFollowed: "По умолчанию включайте ответы новых пользователей, на которых вы подписались, во временную шкалу" newNoteRecived: "Появилась новая заметка" +newNote: "Новая заметка" sounds: "Звуки" sound: "Звуки" +notificationSoundSettings: "Настройки звука уведомлений" listen: "Слушать" none: "Ничего" showInPage: "Показать страницу" @@ -791,6 +794,7 @@ wide: "Толстый" narrow: "Тонкий" reloadToApplySetting: "Это настройка вступает в силу при загрузке страницы. Перезагрузить сейчас?" needReloadToApply: "Изменения вступят в силу после перезагрузки страницы." +needToRestartServerToApply: "Для вступления изменений в силу необходимо перезапустить сервер." showTitlebar: "Показать заголовок" clearCache: "Очистить кэш" onlineUsersCount: "Пользователей сейчас в сети: {n}" @@ -1176,13 +1180,25 @@ unused: "Неиспользованное" used: "Использован" expired: "Срок действия приглашения истёк" doYouAgree: "Согласны?" +beSureToReadThisAsItIsImportant: "Это важно, поэтому, пожалуйста, прочтите это." +iHaveReadXCarefullyAndAgree: "Я прочитал(а) и согласен(сна) с условиями \"{x}" +dialog: "Диалог" icon: "Аватар" +currentAnnouncements: "Текущие новости" +pastAnnouncements: "Предыдущие новости" +youHaveUnreadAnnouncements: "У вас есть непрочитанные уведомления" replies: "Ответы" renotes: "Репост" loadReplies: "Показать ответы" +loadConversation: "Загрузить беседу" pinnedList: "Закреплённый список" keepScreenOn: "Держать экран включённым" +unnotifyNotes: "Отписаться от сообщений" +authentication: "Аутентификация" +authenticationRequiredToContinue: "Пожалуйста, пройдите аутентификацию, чтобы продолжить" +dateAndTime: "Дата и время" showRenotes: "Показывать репосты" +edited: "Изменено" mutualFollow: "Взаимные подписки" followingOrFollower: "Подписки или подписчики" fileAttachedOnly: "Только заметки с файлами" @@ -1193,30 +1209,71 @@ sourceCode: "Исходный код" sourceCodeIsNotYetProvided: "Исходный код пока не доступен. Свяжитесь с администратором, чтобы исправить эту проблему." repositoryUrl: "Ссылка на репозиторий" repositoryUrlDescription: "Если вы используете Misskey как есть (без изменений в исходном коде), введите https://github.com/misskey-dev/misskey" +feedback: "Обратная связь" privacyPolicy: "Политика Конфиденциальности" privacyPolicyUrl: "Ссылка на Политику Конфиденциальности" +tosAndPrivacyPolicy: "Условия использования и политика конфиденциальности" +avatarDecorations: "Украшения для аватара" attach: "Прикрепить" angle: "Угол" flip: "Переворот" +showAvatarDecorations: "Показать украшения для аватара" +pullDownToRefresh: "Опустите что бы обновить" useGroupedNotifications: "Отображать уведомления сгруппировано" +signupPendingError: "Возникла проблема с подтверждением вашего адреса электронной почты. Возможно, срок действия ссылки истёк." +cwNotationRequired: "Если включена опция «Скрыть содержимое», необходимо написать аннотацию." doReaction: "Добавить реакцию" code: "Код" +reloadRequiredToApplySettings: "Для применения настроек необходима обновить страницу." remainingN: "Остаётся: {n}" +overwriteContentConfirm: "Текущее содержимое будет перезаписано. Вы уверены?" seasonalScreenEffect: "Эффект времени года на экране" decorate: "Украсить" addMfmFunction: "Добавить MFM" +bubbleGame: "BubbleGame" +sfx: "Звуковые эффекты" +soundWillBePlayed: "Будет воспроизведен звук" +showReplay: "Показать повтор" +endReplay: "Конец повтора" lastNDays: "Последние {n} сут" hemisphere: "Место проживания" +userSaysSomethingSensitive: "Сообщение, содержит конфиденциальные файлы от {name}" enableHorizontalSwipe: "Смахните в сторону, чтобы сменить вкладки" surrender: "Этот пост не может быть отменен." +gameRetry: "Повторить попытку" +notUsePleaseLeaveBlank: "Если не используется, оставьте пустым" useNativeUIForVideoAudioPlayer: "Использовать интерфейс браузера при проигрывании видео и звука" keepOriginalFilename: "Сохранять исходное имя файла" keepOriginalFilenameDescription: "Если вы выключите данную настройку, имена файлов будут автоматически заменены случайной строкой при загрузке." alwaysConfirmFollow: "Всегда подтверждать подписку" inquiry: "Связаться" +fromX: "Из {x}" +genEmbedCode: "Сгенерировать код для " +noteOfThisUser: "Список заметок этого пользователя" +clipNoteLimitExceeded: "К этому клипу больше нельзя добавить заметки" +performance: "Производительность" +modified: "Изменено" +signinWithPasskey: "Войдите в систему, используя свой пароль" +unknownWebAuthnKey: "Не известный ключ " +passkeyVerificationFailed: "Ошибка проверка ключа доступа " messageToFollower: "Сообщение подписчикам" +testCaptchaWarning: "Эта функция предназначена для тестирования CAPTCHA. Не использовать это в рабочей среде" +prohibitedWordsForNameOfUser: "Запрещенные слова (имя пользователя)" +prohibitedWordsForNameOfUserDescription: "Если имя пользователя содержит строку из этого списка, изменение имени пользователя будет запрещено. На пользователей с правами модератора это ограничение не распространяется. Имена пользователей также проверяются путём замены всех букв в нижнем регистре" +yourNameContainsProhibitedWords: "Имя, которое вы пытаетесь изменить, содержит запрещенную строку символов" +yourNameContainsProhibitedWordsDescription: "Имя содержит запрещённую строку символов. Если вы хотите использовать это имя, обратитесь к администратору сервера" +thisContentsAreMarkedAsSigninRequiredByAuthor: "Автор сообщения установил требование в виде авторизации для просмотра" +lockdown: "Доступ ограничен" +pleaseSelectAccount: "Выберите свой аккаунт" +availableRoles: "Доступные роли" +federationDisabled: "Федерация отключена для этого сервера. Вы не можете взаимодействовать с пользователями на других серверах." +draft: "Черновик" +markAsSensitiveConfirm: "Отметить контент как чувствительный?" +resetToDefaultValue: "Сбросить настройки до стандартных" postForm: "Форма отправки" information: "Описание" +inMinutes: "мин" +inDays: "сут" _chat: invitations: "Пригласить" noHistory: "История пока пуста" @@ -2200,3 +2257,4 @@ _watermarkEditor: image: "Изображения" advanced: "Для продвинутых" angle: "Угол" +drafts: "Черновик" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 651e91fa0a..80a1f2f0a9 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -913,6 +913,8 @@ flip: "Preklopiť" lastNDays: "Posledných {n} dní" postForm: "Napísať poznámku" information: "Informácie" +inMinutes: "min" +inDays: "dní" _chat: invitations: "Pozvať" noHistory: "Žiadna história" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index d6b0ef4de4..86b24ddc37 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1368,6 +1368,8 @@ redisplayAllTips: "แสดงคำแนะนำและเคล็ดล hideAllTips: "ซ่อนคำแนะนำและเคล็ดลับทั้งหมด" defaultImageCompressionLevel: "ความละเอียดเริ่มต้นสำหรับการบีบอัดภาพ" defaultImageCompressionLevel_description: "หากตั้งค่าต่ำ จะรักษาคุณภาพภาพได้ดีขึ้นแต่ขนาดไฟล์จะเพิ่มขึ้น
หากตั้งค่าสูง จะลดขนาดไฟล์ได้ แต่คุณภาพภาพจะลดลง" +inMinutes: "นาที" +inDays: "วัน" _order: newest: "เรียงจากใหม่ไปเก่า" oldest: "เรียงจากเก่าไปใหม่" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index c6708ec0e2..26843c6917 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -919,6 +919,8 @@ flip: "Перевернути" lastNDays: "Останні {n} днів" postForm: "Створення нотатки" information: "Інформація" +inMinutes: "х" +inDays: "д" _chat: invitations: "Запросити" noHistory: "Історія порожня" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 47ea715058..049e96b044 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -1221,6 +1221,8 @@ information: "Giới thiệu" chat: "Trò chuyện" migrateOldSettings: "Di chuyển cài đặt cũ" migrateOldSettings_description: "Thông thường, quá trình này diễn ra tự động, nhưng nếu vì lý do nào đó mà quá trình di chuyển không thành công, bạn có thể kích hoạt thủ công quy trình di chuyển, quá trình này sẽ ghi đè lên thông tin cấu hình hiện tại của bạn." +inMinutes: "phút" +inDays: "ngày" _chat: invitations: "Mời" noHistory: "Không có dữ liệu" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 0958158e5e..4829ac68d7 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1318,7 +1318,7 @@ confirmOnReact: "发送回应前需要确认" reactAreYouSure: "要用「{emoji}」进行回应吗?" markAsSensitiveConfirm: "要将此媒体标记为敏感吗?" unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?" -preferences: "设置" +preferences: "偏好设置" accessibility: "辅助功能" preferencesProfile: "设置的配置" copyPreferenceId: "复制设置 ID" @@ -1368,6 +1368,8 @@ redisplayAllTips: "重新显示所有的提示和技巧" hideAllTips: "隐藏所有的提示和技巧" defaultImageCompressionLevel: "默认图像压缩等级" defaultImageCompressionLevel_description: "较低的等级可以保持画质,但会增加文件大小。
较高的等级可以减少文件大小,但相对应的画质将会降低。" +inMinutes: "分" +inDays: "日" _order: newest: "从新到旧" oldest: "从旧到新" @@ -1927,7 +1929,7 @@ _role: name: "角色名称" description: "角色描述" permission: "角色权限" - descriptionOfPermission: "监察员可以执行基本地审核操作。\n管理员可以更改服务器的所有设置。" + descriptionOfPermission: "监察员可以执行基本的审核操作。\n管理员可以更改实例的所有设置。" assignTarget: "授权对象" descriptionOfAssignTarget: "手动指手动选择谁被包括在这个角色中。\n符合条件指设置条件以自动包括符合条件的用户。" manual: "手动" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index d0dbecfcba..e9f477c0eb 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -638,7 +638,7 @@ inboxUrl: "收件夾 URL" addedRelays: "已加入的中繼器" serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。" deletedNote: "已刪除的貼文" -invisibleNote: "私人貼文" +invisibleNote: "私密的貼文" enableInfiniteScroll: "啟用自動滾動頁面模式" visibility: "可見性" poll: "票選活動" @@ -1368,6 +1368,8 @@ redisplayAllTips: "重新顯示所有「提示與技巧」" hideAllTips: "隱藏所有「提示與技巧」" defaultImageCompressionLevel: "預設的影像壓縮程度" defaultImageCompressionLevel_description: "低的話可以保留畫質,但是會增加檔案的大小。
高的話可以減少檔案大小,但是會降低畫質。" +inMinutes: "分鐘" +inDays: "日" _order: newest: "最新的在前" oldest: "最舊的在前" diff --git a/package.json b/package.json index 7bfca56065..156bc69225 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.7.0", + "version": "2025.8.0-alpha.2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/migration/1753863104203-remoteNotesCleaning.js b/packages/backend/migration/1753863104203-remoteNotesCleaning.js new file mode 100644 index 0000000000..37d42a571d --- /dev/null +++ b/packages/backend/migration/1753863104203-remoteNotesCleaning.js @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class RemoteNotesCleaning1753863104203 { + name = 'RemoteNotesCleaning1753863104203' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "enableRemoteNotesCleaning" boolean NOT NULL DEFAULT true`); + await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningMaxProcessingDurationInMinutes" integer NOT NULL DEFAULT \'60\''); + await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningExpiryDaysForEachNotes" integer NOT NULL DEFAULT \'90\''); + } + + async down(queryRunner) { + await queryRunner.query('ALTER TABLE "meta" DROP COLUMN "remoteNotesCleaningExpiryDaysForEachNotes"'); + await queryRunner.query('ALTER TABLE "meta" DROP COLUMN "remoteNotesCleaningMaxProcessingDurationInMinutes"'); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableRemoteNotesCleaning"`); + } +} diff --git a/packages/backend/migration/1754019326356-tweakDefaultFederationSettings.js b/packages/backend/migration/1754019326356-tweakDefaultFederationSettings.js new file mode 100644 index 0000000000..12c723f80d --- /dev/null +++ b/packages/backend/migration/1754019326356-tweakDefaultFederationSettings.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class TweakDefaultFederationSettings1754019326356 { + name = 'TweakDefaultFederationSettings1754019326356' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'none'`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "enableRemoteNotesCleaning" SET DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "enableRemoteNotesCleaning" SET DEFAULT true`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'all'`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 1a25f63b83..dbf4a5566c 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "engines": { - "node": "^20.18.1 || ^22.0.0" + "node": "^22.15.0" }, "scripts": { "start": "node ./built/boot/entry.js", diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index fce8cc2067..4be568b334 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -53,6 +53,37 @@ export const QUEUE_TYPES = [ 'systemWebhookDeliver', ] as const; +const REPEATABLE_SYSTEM_JOB_DEF = [{ + name: 'tickCharts', + pattern: '55 * * * *', +}, { + name: 'resyncCharts', + pattern: '0 0 * * *', +}, { + name: 'cleanCharts', + pattern: '0 0 * * *', +}, { + name: 'aggregateRetention', + pattern: '0 0 * * *', +}, { + name: 'clean', + pattern: '0 0 * * *', +}, { + name: 'checkExpiredMutings', + pattern: '*/5 * * * *', +}, { + name: 'bakeBufferedReactions', + pattern: '0 0 * * *', +}, { + name: 'checkModeratorsActivity', + // 毎時30分に起動 + pattern: '30 * * * *', +}, { + name: 'cleanRemoteNotes', + // 毎日午前4時に起動(最も人の少ない時間帯) + pattern: '0 4 * * *', +}]; + @Injectable() export class QueueService { constructor( @@ -69,85 +100,30 @@ export class QueueService { @Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue, @Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue, ) { - this.systemQueue.upsertJobScheduler('tickCharts', { - pattern: '55 * * * *', - }, { - name: 'tickCharts', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, - }); + for (const def of REPEATABLE_SYSTEM_JOB_DEF) { + this.systemQueue.upsertJobScheduler(def.name, { + pattern: def.pattern, + }, { + name: def.name, + opts: { + // 期限ではなくcountで設定したいが、ジョブごとではなくキュー全体でカウントされるため、高頻度で実行されるジョブによって低頻度で実行されるジョブのログが消えることになる + removeOnComplete: { + age: 3600 * 24 * 7, // keep up to 7 days + }, + removeOnFail: { + age: 3600 * 24 * 7, // keep up to 7 days + }, + }, + }); + } - this.systemQueue.upsertJobScheduler('resyncCharts', { - pattern: '0 0 * * *', - }, { - name: 'resyncCharts', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, - }); - - this.systemQueue.upsertJobScheduler('cleanCharts', { - pattern: '0 0 * * *', - }, { - name: 'cleanCharts', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, - }); - - this.systemQueue.upsertJobScheduler('aggregateRetention', { - pattern: '0 0 * * *', - }, { - name: 'aggregateRetention', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, - }); - - this.systemQueue.upsertJobScheduler('clean', { - pattern: '0 0 * * *', - }, { - name: 'clean', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, - }); - - this.systemQueue.upsertJobScheduler('checkExpiredMutings', { - pattern: '*/5 * * * *', - }, { - name: 'checkExpiredMutings', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, - }); - - this.systemQueue.upsertJobScheduler('bakeBufferedReactions', { - pattern: '0 0 * * *', - }, { - name: 'bakeBufferedReactions', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, - }); - - this.systemQueue.upsertJobScheduler('checkModeratorsActivity', { - // 毎時30分に起動 - pattern: '30 * * * *', - }, { - name: 'checkModeratorsActivity', - opts: { - removeOnComplete: 10, - removeOnFail: 30, - }, + // 古いバージョンで作成され現在使われなくなったrepeatableジョブをクリーンアップ + this.systemQueue.getJobSchedulers().then(schedulers => { + for (const scheduler of schedulers) { + if (!REPEATABLE_SYSTEM_JOB_DEF.some(def => def.name === scheduler.key)) { + this.systemQueue.removeJobScheduler(scheduler.key); + } + } }); } @@ -834,6 +810,13 @@ export class QueueService { } } + @bindThis + public async queueGetJobLogs(queueType: typeof QUEUE_TYPES[number], jobId: string) { + const queue = this.getQueue(queueType); + const result = await queue.getJobLogs(jobId); + return result.logs; + } + @bindThis public async queueGetJobs(queueType: typeof QUEUE_TYPES[number], jobTypes: JobType[], search?: string) { const RETURN_LIMIT = 100; diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index 643a5f525d..71dc718916 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -227,9 +227,9 @@ export class SearchService { if (opts.host) { if (opts.host === '.') { - query.andWhere('user.host IS NULL'); + query.andWhere('note.userHost IS NULL'); } else { - query.andWhere('user.host = :host', { host: opts.host }); + query.andWhere('note.userHost = :host', { host: opts.host }); } } diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 85c10ab666..1fc50cbd07 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -654,7 +654,7 @@ export class MiMeta { @Column('varchar', { length: 128, - default: 'all', + default: 'none', }) public federation: 'all' | 'specified' | 'none'; @@ -701,6 +701,21 @@ export class MiMeta { default: true, }) public allowExternalApRedirect: boolean; + + @Column('boolean', { + default: false, + }) + public enableRemoteNotesCleaning: boolean; + + @Column('integer', { + default: 60, // minutes + }) + public remoteNotesCleaningMaxProcessingDurationInMinutes: number; + + @Column('integer', { + default: 90, // days + }) + public remoteNotesCleaningExpiryDaysForEachNotes: number; } export type SoftwareSuspension = { diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index 9044285bf6..e01414cd53 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -6,7 +6,6 @@ import { Module } from '@nestjs/common'; import { CoreModule } from '@/core/CoreModule.js'; import { GlobalModule } from '@/GlobalModule.js'; -import { CheckModeratorsActivityProcessorService } from '@/queue/processors/CheckModeratorsActivityProcessorService.js'; import { QueueLoggerService } from './QueueLoggerService.js'; import { QueueProcessorService } from './QueueProcessorService.js'; import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; @@ -18,6 +17,8 @@ import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMu import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js'; import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; import { CleanProcessorService } from './processors/CleanProcessorService.js'; +import { CheckModeratorsActivityProcessorService } from './processors/CheckModeratorsActivityProcessorService.js'; +import { CleanRemoteNotesProcessorService } from './processors/CleanRemoteNotesProcessorService.js'; import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; @@ -83,6 +84,7 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor AggregateRetentionProcessorService, CheckExpiredMutingsProcessorService, CheckModeratorsActivityProcessorService, + CleanRemoteNotesProcessorService, QueueProcessorService, ], exports: [ diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index c98ebcdcd9..7b64182754 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -43,6 +43,7 @@ import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMu import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js'; import { CleanProcessorService } from './processors/CleanProcessorService.js'; import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js'; +import { CleanRemoteNotesProcessorService } from './processors/CleanRemoteNotesProcessorService.js'; import { QueueLoggerService } from './QueueLoggerService.js'; import { QUEUE, baseWorkerOptions } from './const.js'; @@ -123,6 +124,7 @@ export class QueueProcessorService implements OnApplicationShutdown { private bakeBufferedReactionsProcessorService: BakeBufferedReactionsProcessorService, private checkModeratorsActivityProcessorService: CheckModeratorsActivityProcessorService, private cleanProcessorService: CleanProcessorService, + private cleanRemoteNotesProcessorService: CleanRemoteNotesProcessorService, ) { this.logger = this.queueLoggerService.logger; @@ -164,6 +166,7 @@ export class QueueProcessorService implements OnApplicationShutdown { case 'bakeBufferedReactions': return this.bakeBufferedReactionsProcessorService.process(); case 'checkModeratorsActivity': return this.checkModeratorsActivityProcessorService.process(); case 'clean': return this.cleanProcessorService.process(); + case 'cleanRemoteNotes': return this.cleanRemoteNotesProcessorService.process(job); default: throw new Error(`unrecognized job type ${job.name} for system`); } }; diff --git a/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts new file mode 100644 index 0000000000..5b682e20b8 --- /dev/null +++ b/packages/backend/src/queue/processors/CleanRemoteNotesProcessorService.ts @@ -0,0 +1,174 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { setTimeout } from 'node:timers/promises'; +import { Inject, Injectable } from '@nestjs/common'; +import { And, In, IsNull, LessThan, MoreThan, Not } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { MiMeta, MiNote, NoteFavoritesRepository, NotesRepository, UserNotePiningsRepository } from '@/models/_.js'; +import type Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; +import type * as Bull from 'bullmq'; + +@Injectable() +export class CleanRemoteNotesProcessorService { + private logger: Logger; + + constructor( + @Inject(DI.meta) + private meta: MiMeta, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.noteFavoritesRepository) + private noteFavoritesRepository: NoteFavoritesRepository, + + @Inject(DI.userNotePiningsRepository) + private userNotePiningsRepository: UserNotePiningsRepository, + + private idService: IdService, + private queueLoggerService: QueueLoggerService, + ) { + this.logger = this.queueLoggerService.logger.createSubLogger('clean-remote-notes'); + } + + @bindThis + public async process(job: Bull.Job>): Promise<{ + deletedCount: number; + oldest: number | null; + newest: number | null; + skipped?: boolean; + }> { + if (!this.meta.enableRemoteNotesCleaning) { + this.logger.info('Remote notes cleaning is disabled, skipping...'); + return { + deletedCount: 0, + oldest: null, + newest: null, + skipped: true, + }; + } + + this.logger.info('cleaning remote notes...'); + + const maxDuration = this.meta.remoteNotesCleaningMaxProcessingDurationInMinutes * 60 * 1000; // Convert minutes to milliseconds + const startAt = Date.now(); + + const MAX_NOTE_COUNT_PER_QUERY = 50; + + const stats = { + deletedCount: 0, + oldest: null as number | null, + newest: null as number | null, + }; + + let cursor: MiNote['id'] = this.idService.gen(Date.now() - (1000 * 60 * 60 * 24 * this.meta.remoteNotesCleaningExpiryDaysForEachNotes)); + + while (true) { + const batchBeginAt = Date.now(); + + let notes: Pick[] = await this.notesRepository.find({ + where: { + id: LessThan(cursor), + userHost: Not(IsNull()), + clippedCount: 0, + renoteCount: 0, + }, + take: MAX_NOTE_COUNT_PER_QUERY, + order: { + // 新しい順 + // https://github.com/misskey-dev/misskey/pull/16292#issuecomment-3139376314 + id: -1, + }, + select: ['id'], + }); + + const fetchedCount = notes.length; + + for (const note of notes) { + if (note.id < cursor) { + cursor = note.id; + } + } + + const pinings = notes.length === 0 ? [] : await this.userNotePiningsRepository.find({ + where: { + noteId: In(notes.map(note => note.id)), + }, + select: ['noteId'], + }); + + notes = notes.filter(note => { + return !pinings.some(pining => pining.noteId === note.id); + }); + + const favorites = notes.length === 0 ? [] : await this.noteFavoritesRepository.find({ + where: { + noteId: In(notes.map(note => note.id)), + }, + select: ['noteId'], + }); + + notes = notes.filter(note => { + return !favorites.some(favorite => favorite.noteId === note.id); + }); + + const replies = notes.length === 0 ? [] : await this.notesRepository.find({ + where: { + replyId: In(notes.map(note => note.id)), + userHost: IsNull(), + }, + select: ['replyId'], + }); + + notes = notes.filter(note => { + return !replies.some(reply => reply.replyId === note.id); + }); + + if (notes.length > 0) { + await this.notesRepository.delete(notes.map(note => note.id)); + + for (const note of notes) { + const t = this.idService.parse(note.id).date.getTime(); + if (stats.oldest === null || t < stats.oldest) { + stats.oldest = t; + } + if (stats.newest === null || t > stats.newest) { + stats.newest = t; + } + } + + stats.deletedCount += notes.length; + } + + job.log(`Deleted ${notes.length} of ${fetchedCount}; ${Date.now() - batchBeginAt}ms`); + + const elapsed = Date.now() - startAt; + + if (elapsed >= maxDuration) { + this.logger.info(`Reached maximum duration of ${maxDuration}ms, stopping...`); + job.log('Reached maximum duration, stopping cleaning.'); + job.updateProgress(100); + break; + } + + job.updateProgress((elapsed / maxDuration) * 100); + + await setTimeout(1000 * 5); // Wait a moment to avoid overwhelming the db + } + + this.logger.succ('cleaning of remote notes completed.'); + + return { + deletedCount: stats.deletedCount, + oldest: stats.oldest, + newest: stats.newest, + skipped: false, + }; + } +} diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts index 5c4a58a6fc..c0c43dd5c9 100644 --- a/packages/backend/src/server/api/endpoint-list.ts +++ b/packages/backend/src/server/api/endpoint-list.ts @@ -70,6 +70,7 @@ export * as 'admin/queue/inbox-delayed' from './endpoints/admin/queue/inbox-dela export * as 'admin/queue/retry-job' from './endpoints/admin/queue/retry-job.js'; export * as 'admin/queue/remove-job' from './endpoints/admin/queue/remove-job.js'; export * as 'admin/queue/show-job' from './endpoints/admin/queue/show-job.js'; +export * as 'admin/queue/show-job-logs' from './endpoints/admin/queue/show-job-logs.js'; export * as 'admin/queue/promote-jobs' from './endpoints/admin/queue/promote-jobs.js'; export * as 'admin/queue/jobs' from './endpoints/admin/queue/jobs.js'; export * as 'admin/queue/stats' from './endpoints/admin/queue/stats.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 924163afbb..4d3f6d6cd8 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -571,6 +571,18 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + enableRemoteNotesCleaning: { + type: 'boolean', + optional: false, nullable: false, + }, + remoteNotesCleaningExpiryDaysForEachNotes: { + type: 'number', + optional: false, nullable: false, + }, + remoteNotesCleaningMaxProcessingDurationInMinutes: { + type: 'number', + optional: false, nullable: false, + }, }, }, } as const; @@ -722,6 +734,9 @@ export default class extends Endpoint { // eslint- proxyRemoteFiles: instance.proxyRemoteFiles, signToActivityPubGet: instance.signToActivityPubGet, allowExternalApRedirect: instance.allowExternalApRedirect, + enableRemoteNotesCleaning: instance.enableRemoteNotesCleaning, + remoteNotesCleaningExpiryDaysForEachNotes: instance.remoteNotesCleaningExpiryDaysForEachNotes, + remoteNotesCleaningMaxProcessingDurationInMinutes: instance.remoteNotesCleaningMaxProcessingDurationInMinutes, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/show-job-logs.ts b/packages/backend/src/server/api/endpoints/admin/queue/show-job-logs.ts new file mode 100644 index 0000000000..b9292ed12a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/queue/show-job-logs.ts @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, + kind: 'read:admin:queue', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + optional: false, nullable: false, + type: 'string', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + queue: { type: 'string', enum: QUEUE_TYPES }, + jobId: { type: 'string' }, + }, + required: ['queue', 'jobId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private queueService: QueueService, + ) { + super(meta, paramDef, async (ps, me) => { + return this.queueService.queueGetJobLogs(ps.queue, ps.jobId); + }); + } +} 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 578aa2b662..08cea23119 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -205,6 +205,9 @@ export const paramDef = { proxyRemoteFiles: { type: 'boolean' }, signToActivityPubGet: { type: 'boolean' }, allowExternalApRedirect: { type: 'boolean' }, + enableRemoteNotesCleaning: { type: 'boolean' }, + remoteNotesCleaningExpiryDaysForEachNotes: { type: 'number' }, + remoteNotesCleaningMaxProcessingDurationInMinutes: { type: 'number' }, }, required: [], } as const; @@ -723,6 +726,18 @@ export default class extends Endpoint { // eslint- set.allowExternalApRedirect = ps.allowExternalApRedirect; } + if (ps.enableRemoteNotesCleaning !== undefined) { + set.enableRemoteNotesCleaning = ps.enableRemoteNotesCleaning; + } + + if (ps.remoteNotesCleaningExpiryDaysForEachNotes !== undefined) { + set.remoteNotesCleaningExpiryDaysForEachNotes = ps.remoteNotesCleaningExpiryDaysForEachNotes; + } + + if (ps.remoteNotesCleaningMaxProcessingDurationInMinutes !== undefined) { + set.remoteNotesCleaningMaxProcessingDurationInMinutes = ps.remoteNotesCleaningMaxProcessingDurationInMinutes; + } + const before = await this.metaService.fetch(true); await this.metaService.update(set); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 40ca57f174..ca20a94053 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -194,6 +194,10 @@ export class ClientServerService { ], }, }, + 'shortcuts': [{ + 'name': 'Safemode', + 'url': '/?safemode=true', + }], }; manifest = { diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 24794cbf2a..1a30e9ed2b 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -94,23 +94,37 @@ } //#endregion - //#region Theme - const theme = localStorage.getItem('theme'); - if (theme) { - for (const [k, v] of Object.entries(JSON.parse(theme))) { - document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString()); + let isSafeMode = (localStorage.getItem('isSafeMode') === 'true'); - // HTMLの theme-color 適用 - if (k === 'htmlThemeColor') { - for (const tag of document.head.children) { - if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { - tag.setAttribute('content', v); - break; + if (!isSafeMode) { + const urlParams = new URLSearchParams(window.location.search); + + if (urlParams.has('safemode') && urlParams.get('safemode') === 'true') { + localStorage.setItem('isSafeMode', 'true'); + isSafeMode = true; + } + } + + //#region Theme + if (!isSafeMode) { + const theme = localStorage.getItem('theme'); + if (theme) { + for (const [k, v] of Object.entries(JSON.parse(theme))) { + document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString()); + + // HTMLの theme-color 適用 + if (k === 'htmlThemeColor') { + for (const tag of document.head.children) { + if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { + tag.setAttribute('content', v); + break; + } } } } } } + const colorScheme = localStorage.getItem('colorScheme'); if (colorScheme) { document.documentElement.style.setProperty('color-scheme', colorScheme); @@ -127,11 +141,13 @@ document.documentElement.classList.add('useSystemFont'); } - const customCss = localStorage.getItem('customCss'); - if (customCss && customCss.length > 0) { - const style = document.createElement('style'); - style.innerHTML = customCss; - document.head.appendChild(style); + if (!isSafeMode) { + const customCss = localStorage.getItem('customCss'); + if (customCss && customCss.length > 0) { + const style = document.createElement('style'); + style.innerHTML = customCss; + document.head.appendChild(style); + } } async function addStyle(styleText) { @@ -159,9 +175,13 @@ otherOption1: 'Clear preferences and cache', otherOption2: 'Start the simple client', otherOption3: 'Start the repair tool', + otherOption4: 'Start Misskey in safe mode', }, locale?._bootErrors || {}); const reload = locale?.reload || 'Reload'; + const safeModeUrl = new URL(window.location.href); + safeModeUrl.searchParams.set('safemode', 'true'); + let errorsElement = document.getElementById('errors'); if (!errorsElement) { @@ -182,6 +202,12 @@

${messages.solution4}

${messages.otherOption} + + + +