diff --git a/.gitlab/merge_request_templates/default.md b/.gitlab/merge_request_templates/default.md index 401688dc61..4c41f88a41 100644 --- a/.gitlab/merge_request_templates/default.md +++ b/.gitlab/merge_request_templates/default.md @@ -9,7 +9,7 @@ By submitting this merge request, you agree to follow our [Contribution Guidelin - [ ] I have made sure to run `pnpm run format` before submitting this pull request If this merge request makes changes to the Firefish API, please update `docs/api-change.md` -- [ ] I updated the documentation +- [ ] I updated the document / This merge request doesn't include API changes diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl index a5a1adc9b0..1b23250a84 100644 --- a/chart/templates/_helpers.tpl +++ b/chart/templates/_helpers.tpl @@ -122,7 +122,7 @@ db: port: 5432 {{- else }} host: {{ .Values.postgresql.postgresqlHostname }} - port: {{ .Values.postgresql.postgresqlPort | default "5432" | quote }} + port: {{ .Values.postgresql.postgresqlPort | default 5432 }} {{- end }} # Database name diff --git a/docs/api-change.md b/docs/api-change.md index da170cfa07..28087428dc 100644 --- a/docs/api-change.md +++ b/docs/api-change.md @@ -4,6 +4,24 @@ Breaking changes are indicated by the :warning: icon. ## v1.0.5 (unreleased) +### dev18 + +- :warning: response of `meta` no longer includes the following: + - `enableTwitterIntegration` + - `enableGithubIntegration` + - `enableDiscordIntegration` +- :warning: parameter of `admin/update-meta` and response of `admin/meta` no longer include the following: + - `enableTwitterIntegration` + - `enableGithubIntegration` + - `enableDiscordIntegration` + - `twitterConsumerKey` + - `twitterConsumerSecret` + - `githubClientId` + - `githubClientSecret` + - `discordClientId` + - `discordClientSecret` +- :warning: response of `admin/show-user` no longer includes `integrations`. + ### dev17 - Added `lang` parameter to `notes/create` and `notes/edit`. diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index e1701e9c99..e64bc95e7a 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -312,9 +312,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "الصفحات" -integration: "التكامل" -connectService: "اتصل" -disconnectService: "اقطع الاتصال" enableLocalTimeline: "تفعيل الخيط المحلي" enableGlobalTimeline: "تفعيل الخيط الزمني الشامل" disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من الوصول إلى كل الخيوط الزمنية حتى وإن لم تفعّل." diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index abafc4953e..10472516dd 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -316,9 +316,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "পৃষ্ঠা" -integration: "ইন্টিগ্রেশন" -connectService: "সংযুক্ত করুন" -disconnectService: "সংযোগ বিচ্ছিন্ন করুন" enableLocalTimeline: "স্থানীয় টাইমলাইন চালু করুন" enableGlobalTimeline: "গ্লোবাল টাইমলাইন চালু করুন" disablingTimelinesInfo: "আপনি এই টাইমলাইনগুলি বন্ধ করলেও প্রশাসক এবং মডারেটররা এই টাইমলাইনগুলি ব্যাবহার করতে পারবে" diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index e04c7a6670..341b66cb34 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -944,7 +944,6 @@ dayX: '{day}' tosUrl: URL de les Condicions d'ús thisYear: Any thisMonth: Mes -integration: Integracions driveCapacityPerRemoteAccount: Capacitat del Disc per usuari remot inMb: En megabytes iconUrl: Adreça URL de la icona @@ -1036,8 +1035,6 @@ accept: Accepta reject: Rebutja yearX: '{year}' pages: Pàgines -disconnectService: Desconnectar -connectService: Connectar enableLocalTimeline: Activa la línea de temps local enableRecommendedTimeline: Activa la línea de temps de recomanacions pinnedClipId: ID del clip que vols fixar diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index a92f414c40..4fa1133538 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -310,9 +310,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Stránky" -integration: "Integrace" -connectService: "Připojit" -disconnectService: "Odpojit" enableLocalTimeline: "Povolit lokální čas" enableGlobalTimeline: "Povolit globální čas" registration: "Registrace" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 08622ced1b..9521870fd8 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -350,9 +350,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Nutzer-Seiten" -integration: "Integration" -connectService: "Verbinden" -disconnectService: "Trennen" enableLocalTimeline: "Local-Timeline aktivieren" enableGlobalTimeline: "Global-Timeline aktivieren" disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle @@ -529,12 +526,12 @@ objectStorageBaseUrl: "Basis-URL" objectStorageBaseUrlDesc: "Die als Referenz verwendete URL. Verwendest du einen CDN oder Proxy, gib dessen URL an. \nFür S3 verwende 'https://.s3.amazonaws.com'. Für GCS o.ä. verwende 'https://storage.googleapis.com/'." -objectStorageBucket: "Eimer" +objectStorageBucket: "Bucket" objectStorageBucketDesc: "Bitte gib den Namen des Buckets an, der bei deinem Anbieter verwendet wird." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "Dateien werden in Ordnern unter diesem Prefix gespeichert." -objectStorageEndpoint: "Limit" +objectStorageEndpoint: "Endpunkt" objectStorageEndpointDesc: "Im Falle von S3 leerlassen, für andere Anbieter den relevanten Endpoint im Format „“ oder „:“ angeben." objectStorageRegion: "Region" @@ -1037,6 +1034,7 @@ _accountDelete: _ad: back: "Zurück" reduceFrequencyOfThisAd: "Diese Werbeanzeige weniger anzeigen" + adsBy: Community-Banner von {by} _forgotPassword: enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst." @@ -1243,6 +1241,7 @@ _wordMute: soft: "Leicht" hard: "Schwer" mutedNotes: "Stummgeschaltete Beiträge" + muteLangs: Stummgeschaltete Sprachen _instanceMute: instanceMuteDescription: "Schaltet alle Beiträge/Boosts stumm, die von den gelisteten Servern stammen, inklusive Antworten von Nutzern an einen Nutzer eines stummgeschalteten @@ -2215,3 +2214,5 @@ indexable: Indexierbar languageForTranslation: Übersetzungssprache veröffentlichen openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tickers in einem Beitrag +vibrate: Vibrationen abspielen +clickToShowPatterns: Klicken um Modul-Muster anzuzeigen diff --git a/locales/el-GR.yml b/locales/el-GR.yml index 68556cb384..5f9f370efd 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -215,8 +215,6 @@ thisMonth: "Μήνας" today: "Σήμερα" dayX: "{day}" pages: "Σελίδες" -connectService: "Σύνδεση" -disconnectService: "Αποσύνδεση" registration: "Εγγραφή" pinnedPages: "Καρφιτσωμένες Σελίδες" pinnedNotes: "Καρφιτσωμένες δημοσιεύσεις" @@ -730,7 +728,6 @@ lightThemes: Φωτεινά θέματα darkThemes: Σκοτεινά θέματα inputNewFolderName: Πληκτρολογήστε ένα νέο όνομα φακέλου hasChildFilesOrFolders: Εφόσον αυτός ο φάκελος δεν είναι άδειος, δεν μπορεί να διαγραφεί. -integration: Ενσωματώσεις enableRecommendedTimeline: Ενεργοποίηση χρονολογίου προτεινόμενων driveCapacityPerLocalAccount: Μέγεθος Αποθηκευτικού Χώρου ανά τοπικό μέλος driveCapacityPerRemoteAccount: Μέγεθος Αποθηκευτικού Χώρου ανά απομακρυσμένο μέλος diff --git a/locales/en-US.yml b/locales/en-US.yml index ae04dd25e7..10c84425b6 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -58,6 +58,7 @@ sendMessage: "Send a message" copyUsername: "Copy username" searchUser: "Search for a user" reply: "Reply" +replies: "Replies" jumpToPrevious: "Jump to previous" loadMore: "Load more" showMore: "Show more" @@ -112,18 +113,21 @@ unfollow: "Unfollow" followRequestPending: "Follow request pending" enterEmoji: "Enter an emoji" renote: "Boost" +renotes: "Boosts" unrenote: "Take back boost" renoted: "Boosted." cantRenote: "This post can't be boosted." cantReRenote: "A boost can't be boosted." quote: "Quote" +quotes: "Quotes" pinnedNote: "Pinned post" pinned: "Pin to profile" you: "You" clickToShow: "Click to show" sensitive: "NSFW" add: "Add" -reaction: "Reactions" +reaction: "Reaction" +reactions: "Reactions" removeReaction: "Remove your reaction" enableEmojiReactions: "Enable emoji reactions" showEmojisInReactionNotifications: "Show emojis in reaction notifications" @@ -371,9 +375,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Pages" -integration: "Integrations" -connectService: "Connect" -disconnectService: "Disconnect" enableLocalTimeline: "Enable local timeline" enableGlobalTimeline: "Enable global timeline" enableRecommendedTimeline: "Enable recommended timeline" @@ -739,6 +740,7 @@ system: "System" switchUi: "Layout" desktop: "Desktop" clip: "Clip" +clips: "Clips" createNew: "Create new" optional: "Optional" createNewClip: "Create new clip" @@ -781,7 +783,6 @@ pageLikesCount: "Number of liked Pages" pageLikedCount: "Number of received Page likes" contact: "Contact" useSystemFont: "Use the system's default font" -clips: "Clips" clipsDesc: "Clips are like share-able categorized bookmarks. You can create clips from the menu of individual posts." experimentalFeatures: "Experimental features" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 9c8c83a220..442fec1e32 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -340,9 +340,6 @@ dayX: "Día {day}" monthX: "Mes {month}" yearX: "Año {year}" pages: "Páginas" -integration: "Integraciones" -connectService: "Conectar" -disconnectService: "Desconectar" enableLocalTimeline: "Habilitar linea de tiempo local" enableGlobalTimeline: "Habilitar linea de tiempo global" disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia diff --git a/locales/fi.yml b/locales/fi.yml index c4bd16ed22..7ccfb32f46 100644 --- a/locales/fi.yml +++ b/locales/fi.yml @@ -339,8 +339,6 @@ instanceName: Instanssin nimi thisMonth: Kuukausi today: Tänään monthX: '{month}' -connectService: Yhdistä -disconnectService: Katkaise yhteys enableLocalTimeline: Ota käyttöön paikallinen aikajana enableGlobalTimeline: Ota käyttöön globaali aikajana enableRecommendedTimeline: Ota käyttöön suositellut -aikajana @@ -385,7 +383,6 @@ disablingTimelinesInfo: Järjestelmänvalvojilla ja moderaattoreilla on aina pä dayX: '{day}' yearX: '{year}' pages: Sivut -integration: Integraatiot instanceDescription: Instanssin kuvaus invite: Kutsu iconUrl: Ikoni URL-linkki diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index d7cdd0da75..9cd2d1e3c2 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -341,9 +341,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Pages" -integration: "Intégrations" -connectService: "Connexion" -disconnectService: "Déconnexion" enableLocalTimeline: "Activer le fil local" enableGlobalTimeline: "Activer le fil global" disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 2a995c231e..9bd99d40cc 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -342,9 +342,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Halaman" -integration: "Integrasi" -connectService: "Sambungkan" -disconnectService: "Putuskan" enableLocalTimeline: "Nyalakan linimasa lokal" enableGlobalTimeline: "Nyalakan linimasa global" disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 360f15560a..dc9da6dc7f 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -331,9 +331,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Pagine" -integration: "Integrazioni" -connectService: "Connetti" -disconnectService: "Disconnetti" enableLocalTimeline: "Abilita timeline locale" enableGlobalTimeline: "Abilita timeline federata" disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 964e5d0235..8f136ed24b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -52,6 +52,7 @@ sendMessage: "メッセージを送信" copyUsername: "ユーザー名をコピー" searchUser: "ユーザーを検索" reply: "返信" +replies: "返信" loadMore: "もっと読み込む" showMore: "もっと見る" showLess: "閉じる" @@ -97,11 +98,13 @@ unfollow: "フォロー解除" followRequestPending: "フォロー許可待ち" enterEmoji: "絵文字を入力" renote: "ブースト" +renotes: "ブースト" unrenote: "ブースト解除" renoted: "ブーストしました。" cantRenote: "この投稿はブーストできません。" cantReRenote: "ブーストをブーストすることはできません。" quote: "引用" +quotes: "引用" pinnedNote: "ピン留めされた投稿" pinned: "ピン留め" you: "あなた" @@ -109,6 +112,7 @@ clickToShow: "クリックして表示" sensitive: "閲覧注意" add: "追加" reaction: "リアクション" +reactions: "リアクション" enableEmojiReactions: "絵文字リアクションを有効にする" showEmojisInReactionNotifications: "自分の投稿に対するリアクションの通知で絵文字を表示する" reactionSetting: "ピッカーに表示するリアクション" @@ -334,9 +338,6 @@ dayX: "{day}日" monthX: "{month}月" yearX: "{year}年" pages: "ページ" -integration: "連携" -connectService: "接続する" -disconnectService: "切断する" enableLocalTimeline: "ローカルタイムラインを有効にする" enableGlobalTimeline: "グローバルタイムラインを有効にする" enableRecommendedTimeline: "おすすめタイムラインを有効にする" @@ -1504,7 +1505,7 @@ _profile: youCanIncludeHashtags: "ハッシュタグを含められます。" metadata: "追加情報" metadataEdit: "追加情報を編集" - metadataDescription: "プロフィールに追加情報を表示できます。プロフィールにリンクしたウェブサイトやSNSアカウントにタグ(または{a}タグ)を追加すると、そのリンクを本人認証できます。" + metadataDescription: "プロフィールに追加情報を表示できます。{rel}属性をつけた{a}タグや{l}タグを含むページをリンクすることで、リンクの本人確認もできます!" metadataLabel: "ラベル" metadataContent: "内容" changeAvatar: "アバター画像を変更" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 7db8a4f4a4..c768084362 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -317,9 +317,6 @@ dayX: "{day}日" monthX: "{month}月" yearX: "{year}年" pages: "ページ" -integration: "連携" -connectService: "つなげるで" -disconnectService: "切るで" enableLocalTimeline: "ローカルタイムラインを使えるようにする" enableGlobalTimeline: "グローバルタイムラインを使えるようにする" disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。" diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml index 29eca64c7a..410133fcab 100644 --- a/locales/kab-KAB.yml +++ b/locales/kab-KAB.yml @@ -36,7 +36,6 @@ selectList: "Fren tabdart" youHaveNoLists: "Ulac ɣur-k·m ula d yiwet n tabdart" security: "Taɣellist" remove: "Kkes" -connectService: "Qqen" userList: "Tibdarin" securityKey: "Tasarutt n tɣellist" securityKeyName: "Isem n tsarutt" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index f2e2d57b92..c27077b17f 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -323,9 +323,6 @@ dayX: "{day}일" monthX: "{month}월" yearX: "{year}년" pages: "페이지" -integration: "연동" -connectService: "계정 연동" -disconnectService: "계정 연동 해제" enableLocalTimeline: "로컬 타임라인 활성화" enableGlobalTimeline: "글로벌 타임라인 활성화" disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다." diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 8e195c73c9..a797a98c2d 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -483,8 +483,6 @@ accept: Accepteren reject: Afwijzen normal: Normaal pages: Pagina's -integration: Integraties -connectService: Koppelen monthX: '{month}' yearX: '{year}' instanceName: Servernaam @@ -492,7 +490,6 @@ instanceDescription: Server omschrijving maintainerName: Onderhouder maintainerEmail: Onderhouder email tosUrl: Algemene Voorwaarden URL -disconnectService: Ontkoppelen unread: Ongelezen manageGroups: Beheer groepen subscribePushNotification: Pushmeldingen inschakelen diff --git a/locales/no-NO.yml b/locales/no-NO.yml index fbe8904b9e..0fe80ad4da 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -225,8 +225,6 @@ instanceDescription: Tjenerbeskrivelse maintainerName: Administrator maintainerEmail: Administrator-epost monthX: '{month}' -connectService: Koble til -disconnectService: Koble fra enableLocalTimeline: Aktiver lokal tidslinje enableRegistration: Tillat registrering av nye brukere invite: Inviter @@ -449,7 +447,6 @@ watch: Følg med på thisMonth: Måned today: I dag dayX: '{day}' -integration: Integrasjoner yearX: '{year}' pages: Sider enableRecaptcha: Slå på reCAPTCHA diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index e0af7d72ec..5b83762cd5 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -331,9 +331,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Strony" -integration: "Integracje" -connectService: "Połącz" -disconnectService: "Rozłącz" enableLocalTimeline: "Włącz lokalną oś czasu" enableGlobalTimeline: "Włącz globalną oś czasu" disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 9df04022bb..cb5ebabdc0 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -339,9 +339,6 @@ dayX: " Dia {day}" monthX: "mês de {month}" yearX: "Ano {year}" pages: "Páginas" -integration: "Integração" -connectService: "Conectar" -disconnectService: "Desconectar" enableLocalTimeline: "Ativar linha do tempo local" enableGlobalTimeline: "Ativar linha do tempo global" disablingTimelinesInfo: "Se você desabilitar essas linhas do tempo, administradores diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index e9fa49f6c7..51c7b30810 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -316,9 +316,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Pagini" -integration: "Integrare" -connectService: "Conectează" -disconnectService: "Deconectează" enableLocalTimeline: "Activează cronologia locală" enableGlobalTimeline: "Activeaza cronologia globală" disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate cronologiile, chiar dacă nu sunt activate." diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index fada843e7a..7b9a24e2c8 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -335,9 +335,6 @@ dayX: "{day} день" monthX: "{month} месяц" yearX: "{year} год" pages: "Страницы" -integration: "Интеграции" -connectService: "Подключиться" -disconnectService: "Отключиться" enableLocalTimeline: "Включить локальную ленту" enableGlobalTimeline: "Включить глобальную ленту" disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам, diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index cdf0c807d8..70609220be 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -317,9 +317,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Stránky" -integration: "Integrácia" -connectService: "Pripojiť" -disconnectService: "Odpojiť" enableLocalTimeline: "Povoliť lokálnu časovú os" enableGlobalTimeline: "Povoliť globálnu časovú os" disablingTimelinesInfo: "Administrátori a moderátori majú vždy prístup ku všetkým časovým osiam, aj keď sú vypnuté." diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index 6c28196f11..7541467d2d 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -365,8 +365,6 @@ today: Idag dayX: '{day}' monthX: '{month}' yearX: '{year}' -connectService: Anslut -disconnectService: Bortkoppla enableLocalTimeline: Anslut till lokal tidslinje invite: Bjud in driveCapacityPerLocalAccount: Enhetens kapacitet per lokal användare @@ -779,7 +777,6 @@ serverLogs: Serverloggar deleteAll: Radera alla removeAllFollowing: Sluta följa alla följda användare medium: Mellan -integration: Integreringar xl: XL desktop: Skrivbord createNew: Skapa nya diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 4d34471cae..8f3e2595e3 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -327,9 +327,6 @@ dayX: "{วัน}" monthX: "{เดือน}" yearX: "{ปี}" pages: "หน้า" -integration: "รวบรวม" -connectService: "เชื่อมต่อ" -disconnectService: "ตัดการเชื่อมต่อ" enableLocalTimeline: "เปิดใช้งานไทม์ไลน์ในพื้นที่" enableGlobalTimeline: "เปิดใช้งานไทม์ไลน์ทั่วโลก" disablingTimelinesInfo: "ผู้ดูแลระบบและผู้ควบคุมจะสามารถเข้าถึงไทม์ไลน์ทั้งหมด ถึงแม้ว่าจะไม่ได้เปิดใช้งานก็ตาม" diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index 6b18bc35f7..5e71dea263 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -760,7 +760,6 @@ banner: Banner nsfw: NSFW doNothing: Görmezden Gel watch: İzle -connectService: Bağlan registration: Kayıt hcaptcha: hCaptcha pinnedNotes: Sabitlenmiş gönderiler @@ -885,7 +884,6 @@ deleteFolder: Bu klasörü sil addFile: Dosya ekle dayX: '{day}' enableLocalTimeline: Yerel zaman çizgisini aktif et -disconnectService: Bağlantıyı kes enableGlobalTimeline: Global zaman çizgisini aktif et enableRegistration: Yeni kullanıcı kaydını aktif et invite: Davet et @@ -1206,7 +1204,6 @@ location: Konum registeredDate: Katılım tarihi yearX: '{year}' pages: Sayfalar -integration: Entegrasyonlar antennasDesc: "Antenler, belirlediğiniz kriterlere uyan yeni gönderiler görüntüler!\n  Zaman çizelgeleri sayfasından erişilebilirler." notesAndReplies: Gönderiler ve yanıtlar diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index f1c9dab4b4..701078ce6d 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -336,9 +336,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Сторінки" -integration: "Інтеграції" -connectService: "Під’єднати" -disconnectService: "Відключитися" enableLocalTimeline: "Увімкнути локальну стрічку" enableGlobalTimeline: "Увімкнути глобальну стрічку" disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 297be51e1c..23af18e722 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -338,9 +338,6 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Trang" -integration: "Tương tác" -connectService: "Kết nối" -disconnectService: "Ngắt kết nối" enableLocalTimeline: "Bật bảng tin máy chủ" enableGlobalTimeline: "Bật bảng tin liên hợp" disablingTimelinesInfo: "Quản trị viên và Kiểm duyệt viên luôn có quyền truy cập mọi diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index b7a7ff1ddf..dba8d63682 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -49,6 +49,7 @@ sendMessage: "发送" copyUsername: "复制用户名" searchUser: "搜索用户" reply: "回复" +replies: "回复" loadMore: "加载更多" showMore: "查看更多" showLess: "关闭" @@ -94,11 +95,13 @@ unfollow: "取消关注" followRequestPending: "关注请求待批准" enterEmoji: "输入表情符号" renote: "转发" +renotes: "转发" unrenote: "取消转发" renoted: "已转发。" cantRenote: "此帖子无法被转发。" cantReRenote: "转发无法被再次转发。" quote: "引用" +quotes: "引用" pinnedNote: "已置顶的帖子" pinned: "置顶" you: "您" @@ -106,6 +109,7 @@ clickToShow: "点击以显示" sensitive: "敏感内容" add: "添加" reaction: "回应" +reactions: "回应" enableEmojiReaction: "启用表情符号回应" showEmojisInReactionNotifications: "在回应通知中显示表情符号" reactionSetting: "在回应选择器中显示的回应" @@ -321,9 +325,6 @@ dayX: "{day} 日" monthX: "{month} 月" yearX: "{year} 年" pages: "页面" -integration: "整合" -connectService: "连接" -disconnectService: "断开连接" enableLocalTimeline: "启用本地时间线功能" enableGlobalTimeline: "启用全局时间线" disablingTimelinesInfo: "管理员和监察员将始终拥有对所有时间线的访问权,即使它们没有被启用。" @@ -936,7 +937,8 @@ _accountDelete: inProgress: "正在删除" _ad: back: "返回" - reduceFrequencyOfThisAd: "减少此广告的频率" + reduceFrequencyOfThisAd: "减少此横幅的频率" + adsBy: 社区横幅(作者:{by}) _forgotPassword: enterEmail: "请输入您注册账号时用的电子邮箱地址,密码重置链接将发送至该邮箱上。" ifNoEmail: "如果您在注册时没有输入电子邮件地址,请联系服务器管理员。" @@ -1992,3 +1994,4 @@ indexable: 可索引的 languageForTranslation: 帖子翻译语言 vibrate: 播放振动 openServerInfo: 点击帖子上的服务器滚动条时显示服务器信息 +clickToShowPatterns: 点击显示模块模式 diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index f42dae4b30..34cf9acf12 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -21,7 +21,7 @@ basicSettings: "基本設定" otherSettings: "其他設定" openInWindow: "在新視窗開啟" profile: "個人檔案" -timeline: "時間線" +timeline: "時間軸" noAccountDescription: "此用戶還沒有自我介紹。" login: "登入" loggingIn: "登入中" @@ -49,6 +49,7 @@ sendMessage: "發送訊息" copyUsername: "複製使用者名稱" searchUser: "搜尋使用者" reply: "回覆" +replies: "回覆" loadMore: "載入更多" showMore: "載入更多" showLess: "關閉" @@ -94,11 +95,13 @@ unfollow: "取消追隨" followRequestPending: "追隨許可批准中" enterEmoji: "輸入表情符號" renote: "轉發" +renotes: "轉發" unrenote: "取消轉發" renoted: "已轉發。" cantRenote: "無法轉發此貼文。" cantReRenote: "無法轉發之前已經轉發過的內容。" quote: "引用" +quotes: "引用" pinnedNote: "已置頂的貼文" pinned: "置頂" you: "您" @@ -106,6 +109,7 @@ clickToShow: "按一下以顯示" sensitive: "敏感內容" add: "新增" reaction: "反應" +reactions: "反應" enableEmojiReaction: "啟用表情符號反應" showEmojisInReactionNotifications: "在反應通知中顯示表情符號" reactionSetting: "在選擇器中顯示反應" @@ -145,12 +149,12 @@ flagAsBot: "標記此帳號是機器人" flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Firefish內部系統將本帳戶識別為機器人。" flagAsCat: "你是喵咪嗎?w😺" flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示!" -flagShowTimelineReplies: "在時間線上顯示貼文的回覆" -flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示用戶的貼文以外,還會顯示用戶對其他貼文的回覆。" +flagShowTimelineReplies: "在時間軸上顯示貼文的回覆" +flagShowTimelineRepliesDescription: "啟用時,時間軸除了顯示用戶的貼文以外,還會顯示用戶對其他貼文的回覆。" autoAcceptFollowed: "自動准予追隨中使用者的追隨請求" addAccount: "添加帳戶" loginFailed: "登入失敗" -showOnRemote: "轉到所在伺服器顯示" +showOnRemote: "開啟來源頁面" general: "一般" wallpaper: "桌布" setWallpaper: "設定桌布" @@ -320,12 +324,9 @@ dayX: "{day}日" monthX: "{month}月" yearX: "{year}年" pages: "頁面" -integration: "整合" -connectService: "己連結" -disconnectService: "己斷開" -enableLocalTimeline: "開啟本地時間線" -enableGlobalTimeline: "啟用公開時間線" -disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和板主仍可訪問所有的時間線。" +enableLocalTimeline: "開啟本地時間軸" +enableGlobalTimeline: "啟用公開時間軸" +disablingTimelinesInfo: "即使您關閉了時間軸功能,管理員和板主仍可訪問所有的時間軸。" registration: "註冊" enableRegistration: "開啟新使用者註冊" invite: "邀請" @@ -385,7 +386,7 @@ administrator: "管理員" token: "權杖" twoStepAuthentication: "兩階段驗證" moderator: "板主" -moderation: "言論調節" +moderation: "管理" nUsersMentioned: "提到了{n}" securityKey: "安全金鑰" securityKeyName: "金鑰名稱" @@ -482,7 +483,7 @@ promotion: "推廣" promote: "推廣" numberOfDays: "有效天數" hideThisNote: "隱藏此貼文" -showFeaturedNotesInTimeline: "在時間線上顯示熱門推薦" +showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦" objectStorage: "Object Storage (物件儲存)" useObjectStorage: "使用Object Storage" objectStorageBaseUrl: "根URL" @@ -502,7 +503,7 @@ objectStorageUseProxyDesc: "如果不使用代理進行API連接,請關閉" objectStorageSetPublicRead: "上傳時設定為\"public-read\"" serverLogs: "伺服器日誌" deleteAll: "刪除所有記錄" -showFixedPostForm: "於時間線頁頂顯示「發送貼文」方框" +showFixedPostForm: "於時間軸頁頂顯示「發送貼文」方框" newNoteRecived: "發現新的貼文" sounds: "音效" listen: "聆聽" @@ -670,7 +671,7 @@ no: "取消" driveFilesCount: "雲端硬碟檔案數量" driveUsage: "雲端硬碟使用量" noCrawle: "拒絕搜尋引擎索引" -noCrawleDescription: "要求網路搜尋引擎不要索引你的個人資料頁、貼文及頁面等。" +noCrawleDescription: "要求外部搜尋引擎不要收錄(索引)你的內容(個人檔案、貼文、頁面等)。" lockedAccountInfo: "即使你通過了追隨者請求,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。" alwaysMarkSensitive: "默認將圖像/影像標記為敏感內容" loadRawImages: "以原始圖檔顯示附件圖檔的縮圖" @@ -688,7 +689,7 @@ experimentalFeatures: "實驗中的功能" developer: "開發者" makeExplorable: "使自己的帳戶能夠在“探索”頁面中顯示" makeExplorableDescription: "如果關閉,帳戶將不會被顯示在\"探索\"頁面中。" -showGapBetweenNotesInTimeline: "分開顯示時間線上的貼文" +showGapBetweenNotesInTimeline: "分開顯示時間軸上的貼文" duplicate: "複製" left: "左" center: "置中" @@ -734,7 +735,7 @@ inChannelSearch: "頻道内搜尋" useReactionPickerForContextMenu: "點擊右鍵開啟反應工具欄" typingUsers: "{users}輸入中" jumpToSpecifiedDate: "跳轉到特定日期" -showingPastTimeline: "顯示過往的時間線" +showingPastTimeline: "顯示過往的時間軸" clear: "清除" markAllAsRead: "全部標示為已讀" goBack: "返回" @@ -786,7 +787,7 @@ previewNoteText: "預覽文本" customCss: "自定義 CSS" customCssWarn: "這個設定必須由具備相關知識的人員操作,不當的設定可能导致客戶端無法正常使用。" global: "公開" -squareAvatars: "頭像以方形顯示" +squareAvatars: "大頭貼以方形顯示" sent: "發送" received: "收取" searchResult: "搜尋結果" @@ -822,7 +823,7 @@ unmuteThread: "將貼文串的靜音解除" ffVisibility: "連接的公開範圍" ffVisibilityDescription: "您可以設定您的關注/關注者資訊的公開範圍。" continueThread: "查看更多貼文" -deleteAccountConfirm: "將要刪除帳戶。是否確定?" +deleteAccountConfirm: "此帳戶將被刪除,是否繼續?" incorrectPassword: "密碼錯誤。" voteConfirm: "確定投給「{choice}」?" hide: "隱藏" @@ -933,6 +934,7 @@ _accountDelete: _ad: back: "返回" reduceFrequencyOfThisAd: "降低此橫幅的頻率" + adsBy: 社群橫幅(作者:{by}) _forgotPassword: enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。" ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。" @@ -1109,14 +1111,14 @@ _wordMute: muteWords: "加入靜音文字" muteWordsDescription: "用空格分隔指定AND,用換行分隔指定OR。" muteWordsDescription2: "將關鍵字用斜線括起來表示正規表達式。" - softDescription: "隱藏時間線中指定條件的貼文。" - hardDescription: "具有指定條件的貼文將不添加到時間線。 即使您更改條件,未被添加的貼文也會被排除在外。" + softDescription: "隱藏時間軸中指定條件的貼文。" + hardDescription: "符合指定條件的貼文將不添加到時間軸。 即使您更改條件,未被添加的貼文也會被排除在外。" soft: "軟性靜音" hard: "硬性靜音" mutedNotes: "已靜音的貼文" muteLangsDescription2: '使用語言代碼。例: en, fr, ja, zh.' lang: 語言 - langDescription: 將指定語言的貼文從時間線中隱藏。 + langDescription: 將指定語言的貼文從時間軸中隱藏。 muteLangs: 被靜音的語言 muteLangsDescription: OR條件以空格或換行進行分隔。 _instanceMute: @@ -1228,16 +1230,16 @@ _tutorial: step2_1: "首先,請完成你的個人資料。" step2_2: "通過提供一些關於你自己的資料,其他人會更容易了解他們是否想看到你的貼文或關注你。" step3_1: "現在是時候追隨一些人了!" - step3_2: "你的主頁和社交時間線是基於你所追蹤的人,所以試著先追蹤幾個帳戶。\n點擊個人資料右上角的加號圈就可以關注它。" + step3_2: "你的主頁和社交時間軸是基於你所追蹤的人,所以試著先追蹤幾個帳戶。\n點擊個人資料右上角的加號按鈕就可以關注它。" step4_1: "讓我們出去找你。" step4_2: "作為第一則貼文,有些人喜歡發 {introduction} 或單純發一個 \"hello world!\"" - step5_1: "時間線,到處都是時間線!" - step5_2: "您的伺服器已啟用了{timelines}個時間線。" - step5_3: "首頁 {icon} 時間線是顯示你追蹤的帳號的貼文。" - step5_4: "本地 {icon} 時間線是你可以看到伺服器中所有其他用戶的貼文的時間線。" - step5_5: "社交 {icon} 時間線是你的 首頁時間線 和 本地時間線 的結合體。" - step5_6: "推薦 {icon} 時間線是顯示你的伺服器管理員推薦的貼文。" - step5_7: "全球 {icon} 時間線是顯示來自所有其他連接的伺服器的貼文。" + step5_1: "時間軸,到處都是時間軸!" + step5_2: "您的伺服器已啟用了{timelines}個時間軸。" + step5_3: "首頁 {icon} 時間軸是顯示你追蹤的帳號的貼文。" + step5_4: "本地 {icon} 時間軸是你可以看到伺服器中所有其他用戶的貼文的時間軸。" + step5_5: "社交 {icon} 時間軸是你的 首頁時間軸 和 本地時間軸 的結合體。" + step5_6: "推薦 {icon} 時間軸是顯示你的伺服器管理員推薦的貼文。" + step5_7: "全球 {icon} 時間軸是顯示來自所有其他連接的伺服器的貼文。" step6_1: "那麼,這裡是什麼地方?" step6_2: "你不只是加入Firefish。你已經加入了Fediverse的一個門戶,這是一個由成千上萬台服務器組成的互聯網絡。" step6_3: "每個服務器也有不同,而並不是所有的服務器都運行Firefish。但這個服務器確實是運行Firefish的! 你可能會覺得有點複雜,但你很快就會明白的。" @@ -1326,7 +1328,7 @@ _weekday: _widgets: memo: "備忘錄" notifications: "通知" - timeline: "時間線" + timeline: "時間軸" calendar: "行事曆" trends: "發燒貼文" clock: "時鐘" @@ -1382,9 +1384,9 @@ _poll: remainingSeconds: "{s}秒後截止" _visibility: public: "公開" - publicDescription: "發佈至公開時間線" + publicDescription: "發佈至公開時間軸" home: "不在主頁顯示" - homeDescription: "僅發送至首頁的時間線" + homeDescription: "僅發送至首頁的時間軸" followers: "追隨者" followersDescription: "僅發佈至關注者" specified: "指定使用者" @@ -1783,6 +1785,7 @@ _notification: renote: "轉發" reacted: 對您的貼文做出了反應 renoted: 轉發了您的貼文 + voted: 投了票 _deck: alwaysShowMainColumn: "總是顯示主欄" columnAlign: "對齊欄位" @@ -1806,7 +1809,7 @@ _deck: main: "主列" widgets: "小工具" notifications: "通知" - tl: "時間線" + tl: "時間軸" antenna: "天線" list: "清單" mentions: "提及" @@ -1838,7 +1841,7 @@ accountMoved: '該使用者已遷移至新帳戶:' showAds: 顯示社群橫幅 noThankYou: 不用了,謝謝 selectInstance: 選擇伺服器 -enableRecommendedTimeline: 啟用推薦時間線 +enableRecommendedTimeline: 啟用推薦時間軸 antennaInstancesDescription: 分行列出一個伺服器 moveTo: 遷移此帳戶到新帳戶 moveToLabel: '請輸入你將會遷移到的帳戶:' @@ -1870,6 +1873,7 @@ silenced: 已靜音 _experiments: title: 試驗功能 enablePostImports: 啟用匯入貼文的功能 + postImportsCaption: 允許用戶從舊有的Firefish・Misskey・Mastodon・Akkoma・Pleroma帳號匯入貼文。在伺服器佇列堵塞時匯入貼文可能會導致載入速度變慢。 findOtherInstance: 找找另一個伺服器 noGraze: 瀏覽器擴充元件 "Graze for Mastodon" 會與Firefish發生衝突,請停用該擴充元件。 userSaysSomethingReasonRenote: '{name} 轉發了包含 {reason} 的貼文' @@ -1891,7 +1895,7 @@ pushNotification: 推送通知 subscribePushNotification: 啟用推送通知 unsubscribePushNotification: 停用推送通知 pushNotificationAlreadySubscribed: 推送通知已經啟用 -recommendedInstancesDescription: 以每行分隔的推薦伺服器出現在推薦的時間線中。 +recommendedInstancesDescription: 推薦的伺服器(將顯示在推薦時間軸中),一行一個。 searchPlaceholder: 在 Firefish 上搜尋 cw: 內容警告 selectChannel: 選擇一個頻道 @@ -1899,9 +1903,9 @@ newer: 較新 older: 較舊 jumpToPrevious: 跳到上一個 removeReaction: 移除你的反應 -listsDesc: 清單可以創建一個只有您指定用戶的時間線。 可以從時間線頁面訪問它們。 +listsDesc: 清單可以創建一個只有您指定用戶的時間軸。 可以從時間軸頁面訪問它們。 flagSpeakAsCatDescription: 在喵咪模式下你的貼文會被喵化ヾ(•ω•`)o -antennasDesc: "天線會顯示符合您設置條件的新貼文!\n 可以從時間線訪問它們。" +antennasDesc: "天線會顯示符合您設置條件的新貼文!\n 可以從時間軸訪問它們。" expandOnNoteClick: 點擊以打開貼文 expandOnNoteClickDesc: 即使停用,您仍然可以從右鍵選單或單擊發文時間來打開貼文。 hiddenTagsDescription: '列出您希望隱藏趨勢和探索的主題標籤(不帶 #)。 隱藏的主題標籤仍然可以通過其他方式發現。' @@ -1920,7 +1924,7 @@ seperateRenoteQuote: 分開轉發及引用的按鈕 clipsDesc: 摘錄就像一個可以分享的書籤。 你可以從每個貼文的菜單創建新摘錄或將貼文加入已有的摘錄。 noteId: 貼文 ID sendModMail: 發送審核通知 -enableIdenticonGeneration: 啟用碎片生成 +enableIdenticonGeneration: 啟用Identicon生成 enableServerMachineStats: 啟用伺服器硬體統計資訊 reactionPickerSkinTone: 首選表情符號膚色 indexFromDescription: 留空以索引每個貼文 @@ -1961,10 +1965,17 @@ _dialog: charactersExceeded: 超過字數限制! 當前 {current} / 限制 {max} _skinTones: yellow: 黃色 + medium: 中等 + dark: 深色 + mediumDark: 中等偏深 + light: 淺色 + mediumLight: 中等偏淺 exportZip: 匯出ZIP _feeds: atom: Atom rss: RSS + copyFeed: 複製訂閱URL + jsonFeed: JSON Feed emojiPackCreator: 表情包的作者 importZip: 匯入ZIP delete2fa: 停用二階段認證(2FA) @@ -1977,3 +1988,9 @@ addRe: 在回覆有內容警告的貼文時,在標題前面加上 "re:" vibrate: 播放振動 openServerInfo: 點擊貼文中的伺服器名稱以顯示伺服器資訊 languageForTranslation: 貼文翻譯語言 +objectStorageS3ForcePathStyleDesc: 以 "s3.amazonaws.com//" 而非 ".s3.amazonaws.com" + 的格式建構端點(Endpoint)URL。 +indexable: 登錄至貼文搜尋引擎 +origin: 來源 +objectStorageS3ForcePathStyle: 使用基於路徑的端點(Endpoint)URL +clickToShowPatterns: 點擊顯示模組模式(Module Pattern) diff --git a/packages/backend/native-utils/migration/src/lib.rs b/packages/backend/native-utils/migration/src/lib.rs index f8be136f46..a0f8fc85b4 100644 --- a/packages/backend/native-utils/migration/src/lib.rs +++ b/packages/backend/native-utils/migration/src/lib.rs @@ -7,6 +7,7 @@ mod m20230627_185451_index_note_url; mod m20230709_000510_move_antenna_to_cache; mod m20230806_170616_fix_antenna_stream_ids; mod m20230904_013244_is_indexable; +mod m20231002_143323_remove_integrations; pub struct Migrator; @@ -19,6 +20,7 @@ impl MigratorTrait for Migrator { Box::new(m20230709_000510_move_antenna_to_cache::Migration), Box::new(m20230806_170616_fix_antenna_stream_ids::Migration), Box::new(m20230904_013244_is_indexable::Migration), + Box::new(m20231002_143323_remove_integrations::Migration), ] } } diff --git a/packages/backend/native-utils/migration/src/m20231002_143323_remove_integrations.rs b/packages/backend/native-utils/migration/src/m20231002_143323_remove_integrations.rs new file mode 100644 index 0000000000..a8ef8fdbbd --- /dev/null +++ b/packages/backend/native-utils/migration/src/m20231002_143323_remove_integrations.rs @@ -0,0 +1,117 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(UserProfile::Table) + .drop_column(UserProfile::Integrations) + .to_owned(), + ) + .await?; + + manager + .alter_table( + Table::alter() + .table(Meta::Table) + .drop_column(Meta::EnableTwitterIntegration) + .drop_column(Meta::TwitterConsumerKey) + .drop_column(Meta::TwitterConsumerSecret) + .drop_column(Meta::EnableGithubIntegration) + .drop_column(Meta::GithubClientId) + .drop_column(Meta::GithubClientSecret) + .drop_column(Meta::EnableDiscordIntegration) + .drop_column(Meta::DiscordClientId) + .drop_column(Meta::DiscordClientSecret) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(Meta::Table) + .add_column(ColumnDef::new(Meta::DiscordClientSecret).string()) + .add_column(ColumnDef::new(Meta::DiscordClientId).string()) + .add_column( + ColumnDef::new(Meta::EnableDiscordIntegration) + .boolean() + .not_null() + .default(false), + ) + .add_column(ColumnDef::new(Meta::GithubClientSecret).string()) + .add_column(ColumnDef::new(Meta::GithubClientId).string()) + .add_column( + ColumnDef::new(Meta::EnableGithubIntegration) + .boolean() + .not_null() + .default(false), + ) + .add_column(ColumnDef::new(Meta::TwitterConsumerSecret).string()) + .add_column(ColumnDef::new(Meta::TwitterConsumerKey).string()) + .add_column( + ColumnDef::new(Meta::EnableTwitterIntegration) + .boolean() + .not_null() + .default(false), + ) + .to_owned(), + ) + .await?; + + manager + .alter_table( + Table::alter() + .table(UserProfile::Table) + .add_column( + ColumnDef::new(UserProfile::Integrations) + .json() + .default("{}"), + ) + .to_owned(), + ) + .await?; + + Ok(()) + } +} + +#[derive(Iden)] + +enum UserProfile { + Table, + #[iden = "integrations"] + Integrations, +} + +#[derive(Iden)] +enum Meta { + Table, + #[iden = "enableTwitterIntegration"] + EnableTwitterIntegration, + #[iden = "twitterConsumerKey"] + TwitterConsumerKey, + #[iden = "twitterConsumerSecret"] + TwitterConsumerSecret, + #[iden = "enableGithubIntegration"] + EnableGithubIntegration, + #[iden = "githubClientId"] + GithubClientId, + #[iden = "githubClientSecret"] + GithubClientSecret, + #[iden = "enableDiscordIntegration"] + EnableDiscordIntegration, + #[iden = "discordClientId"] + DiscordClientId, + #[iden = "discordClientSecret"] + DiscordClientSecret, +} diff --git a/packages/backend/native-utils/src/model/entity/meta.rs b/packages/backend/native-utils/src/model/entity/meta.rs index fcae7cd290..9cbed1544c 100644 --- a/packages/backend/native-utils/src/model/entity/meta.rs +++ b/packages/backend/native-utils/src/model/entity/meta.rs @@ -69,24 +69,6 @@ pub struct Model { pub sw_public_key: Option, #[sea_orm(column_name = "swPrivateKey")] pub sw_private_key: Option, - #[sea_orm(column_name = "enableTwitterIntegration")] - pub enable_twitter_integration: bool, - #[sea_orm(column_name = "twitterConsumerKey")] - pub twitter_consumer_key: Option, - #[sea_orm(column_name = "twitterConsumerSecret")] - pub twitter_consumer_secret: Option, - #[sea_orm(column_name = "enableGithubIntegration")] - pub enable_github_integration: bool, - #[sea_orm(column_name = "githubClientId")] - pub github_client_id: Option, - #[sea_orm(column_name = "githubClientSecret")] - pub github_client_secret: Option, - #[sea_orm(column_name = "enableDiscordIntegration")] - pub enable_discord_integration: bool, - #[sea_orm(column_name = "discordClientId")] - pub discord_client_id: Option, - #[sea_orm(column_name = "discordClientSecret")] - pub discord_client_secret: Option, #[sea_orm(column_name = "pinnedUsers")] pub pinned_users: Vec, #[sea_orm(column_name = "ToSUrl")] diff --git a/packages/backend/native-utils/src/model/entity/user_profile.rs b/packages/backend/native-utils/src/model/entity/user_profile.rs index f438e39966..dfec51d80a 100644 --- a/packages/backend/native-utils/src/model/entity/user_profile.rs +++ b/packages/backend/native-utils/src/model/entity/user_profile.rs @@ -45,8 +45,6 @@ pub struct Model { pub pinned_page_id: Option, #[sea_orm(column_type = "JsonBinary")] pub room: Json, - #[sea_orm(column_type = "JsonBinary")] - pub integrations: Json, #[sea_orm(column_name = "injectFeaturedNote")] pub inject_featured_note: bool, #[sea_orm(column_name = "enableWordMute")] diff --git a/packages/backend/package.json b/packages/backend/package.json index a7b180c428..689da8e59e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -100,7 +100,6 @@ "node-fetch": "3.3.2", "nodemailer": "6.9.4", "nsfwjs": "2.4.2", - "oauth": "^0.10.0", "opencc-js": "^1.0.5", "os-utils": "0.0.14", "otpauth": "^9.1.4", diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts index 1ff09d6299..199b2d9647 100644 --- a/packages/backend/src/misc/check-hit-antenna.ts +++ b/packages/backend/src/misc/check-hit-antenna.ts @@ -1,76 +1,29 @@ import type { Antenna } from "@/models/entities/antenna.js"; import type { Note } from "@/models/entities/note.js"; import type { User } from "@/models/entities/user.js"; -import { - UserListJoinings, - UserGroupJoinings, - Blockings, -} from "@/models/index.js"; +import { Blockings, UserProfiles } from "@/models/index.js"; import { getFullApAccount } from "./convert-host.js"; import * as Acct from "@/misc/acct.js"; import type { Packed } from "./schema.js"; import { Cache } from "./cache.js"; +import { getWordHardMute } from "./check-word-mute.js"; const blockingCache = new Cache("blocking", 60 * 5); +const mutedWordsCache = new Cache("mutedWords", 60 * 5); -// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている - -/** - * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい - */ export async function checkHitAntenna( antenna: Antenna, note: Note | Packed<"Note">, noteUser: { id: User["id"]; username: string; host: string | null }, - noteUserFollowers?: User["id"][], - antennaUserFollowing?: User["id"][], ): Promise { if (note.visibility === "specified") return false; if (note.visibility === "home") return false; - - // アンテナ作成者がノート作成者にブロックされていたらスキップ - const blockings = await blockingCache.fetch(noteUser.id, () => - Blockings.findBy({ blockerId: noteUser.id }).then((res) => - res.map((x) => x.blockeeId), - ), - ); - if (blockings.some((blocking) => blocking === antenna.userId)) return false; - - if (note.visibility === "followers") { - if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) - return false; - if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) - return false; + if (!antenna.withReplies && note.replyId != null) return false; + if (antenna.withFile) { + if (note.fileIds && note.fileIds.length === 0) return false; } - if (!antenna.withReplies && note.replyId != null) return false; - - if (antenna.src === "home") { - if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) - return false; - if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) - return false; - } else if (antenna.src === "list") { - const listUsers = ( - await UserListJoinings.findBy({ - userListId: antenna.userListId!, - }) - ).map((x) => x.userId); - - if (!listUsers.includes(note.userId)) return false; - } else if (antenna.src === "group") { - const joining = await UserGroupJoinings.findOneByOrFail({ - id: antenna.userGroupJoiningId!, - }); - - const groupUsers = ( - await UserGroupJoinings.findBy({ - userGroupId: joining.userGroupId, - }) - ).map((x) => x.userId); - - if (!groupUsers.includes(note.userId)) return false; - } else if (antenna.src === "users") { + if (antenna.src === "users") { const accts = antenna.users.map((x) => { const { username, host } = Acct.parse(x); return getFullApAccount(username, host).toLowerCase(); @@ -128,9 +81,20 @@ export async function checkHitAntenna( if (matched) return false; } - if (antenna.withFile) { - if (note.fileIds && note.fileIds.length === 0) return false; - } + // アンテナ作成者がノート作成者にブロックされていたらスキップ + const blockings = await blockingCache.fetch(noteUser.id, () => + Blockings.findBy({ blockerId: noteUser.id }).then((res) => + res.map((x) => x.blockeeId), + ), + ); + if (blockings.includes(antenna.userId)) return false; + + const mutedWords = await mutedWordsCache.fetch(antenna.userId, () => + UserProfiles.findOneBy({ userId: antenna.userId }).then( + (profile) => profile?.mutedWords, + ), + ); + if (await getWordHardMute(note, antenna.userId, mutedWords)) return false; // TODO: eval expression diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index 55573263a1..a38985a297 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -1,6 +1,5 @@ import RE2 from "re2"; import type { Note } from "@/models/entities/note.js"; -import type { User } from "@/models/entities/user.js"; import { DriveFile } from "@/models/entities/drive-file"; import { scyllaClient, type ScyllaNote } from "@/db/scylla.js"; @@ -11,10 +10,6 @@ type NoteLike = { cw?: Note["cw"]; }; -type UserLike = { - id: User["id"]; -}; - function checkWordMute( note: NoteLike | ScyllaNote, mutedWords: Array, @@ -77,15 +72,12 @@ function checkWordMute( return false; } -export function getWordHardMute( - note: NoteLike | ScyllaNote, - me: UserLike | null | undefined, - mutedWords: Array, -): boolean { - // 自分自身 - if (me && note.userId === me.id) { - return false; - } +export async function getWordHardMute( + note: NoteLike, + meId: string | null | undefined, + mutedWords?: Array, +): Promise { + if (note.userId === meId || mutedWords == null) return false; let ng = false; diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts index 6a19cdde75..90dff4066e 100644 --- a/packages/backend/src/models/entities/meta.ts +++ b/packages/backend/src/models/entities/meta.ts @@ -354,57 +354,6 @@ export class Meta { }) public swPrivateKey: string | null; - @Column("boolean", { - default: false, - }) - public enableTwitterIntegration: boolean; - - @Column("varchar", { - length: 128, - nullable: true, - }) - public twitterConsumerKey: string | null; - - @Column("varchar", { - length: 128, - nullable: true, - }) - public twitterConsumerSecret: string | null; - - @Column("boolean", { - default: false, - }) - public enableGithubIntegration: boolean; - - @Column("varchar", { - length: 128, - nullable: true, - }) - public githubClientId: string | null; - - @Column("varchar", { - length: 128, - nullable: true, - }) - public githubClientSecret: string | null; - - @Column("boolean", { - default: false, - }) - public enableDiscordIntegration: boolean; - - @Column("varchar", { - length: 128, - nullable: true, - }) - public discordClientId: string | null; - - @Column("varchar", { - length: 128, - nullable: true, - }) - public discordClientSecret: string | null; - @Column("varchar", { length: 128, nullable: true, diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts index 0b8863867c..a4c2e09754 100644 --- a/packages/backend/src/models/entities/user-profile.ts +++ b/packages/backend/src/models/entities/user-profile.ts @@ -215,11 +215,6 @@ export class UserProfile { @JoinColumn() public pinnedPage: Page | null; - @Column("jsonb", { - default: {}, - }) - public integrations: Record; - @Index() @Column("boolean", { default: false, diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 5cf55dff4a..e5e6c17d6e 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -599,7 +599,6 @@ export const UserRepository = db.getRepository(User).extend({ hasUnreadNotification: this.getHasUnreadNotification(user.id), hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), - integrations: profile!.integrations, mutedWords: profile!.mutedWords, mutedInstances: profile!.mutedInstances, mutingNotificationTypes: profile!.mutingNotificationTypes, diff --git a/packages/backend/src/models/schema/user.ts b/packages/backend/src/models/schema/user.ts index 61c20a898f..9550111014 100644 --- a/packages/backend/src/models/schema/user.ts +++ b/packages/backend/src/models/schema/user.ts @@ -459,11 +459,6 @@ export const packedMeDetailedOnlySchema = { nullable: false, optional: false, }, - integrations: { - type: "object", - nullable: true, - optional: false, - }, mutedWords: { type: "array", nullable: false, diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index 0e500b89ed..c8a0920316 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -95,11 +95,25 @@ export default async (job: Bull.Job): Promise => { } // HTTP-Signatureの検証 - const httpSignatureValidated = httpSignature.verifySignature( + let httpSignatureValidated = httpSignature.verifySignature( signature, authUser.key.keyPem, ); + // If signature validation failed, try refetching the actor + if (!httpSignatureValidated) { + authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user); + + if (authUser.key == null) { + return "skip: failed to re-resolve user publicKey"; + } + + httpSignatureValidated = httpSignature.verifySignature( + signature, + authUser.key.keyPem, + ); + } + // また、signatureのsignerは、activity.actorと一致する必要がある if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { // 一致しなくても、でもLD-Signatureがありそうならそっちも見る diff --git a/packages/backend/src/remote/activitypub/check-fetch.ts b/packages/backend/src/remote/activitypub/check-fetch.ts index c885b4a199..b7fa179ac8 100644 --- a/packages/backend/src/remote/activitypub/check-fetch.ts +++ b/packages/backend/src/remote/activitypub/check-fetch.ts @@ -86,11 +86,25 @@ export async function checkFetch(req: IncomingMessage): Promise { } // HTTP-Signatureの検証 - const httpSignatureValidated = httpSignature.verifySignature( + let httpSignatureValidated = httpSignature.verifySignature( signature, authUser.key.keyPem, ); + // If signature validation failed, try refetching the actor + if (!httpSignatureValidated) { + authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user); + + if (authUser.key == null) { + return 403; + } + + httpSignatureValidated = httpSignature.verifySignature( + signature, + authUser.key.keyPem, + ); + } + if (!httpSignatureValidated) { return 403; } diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/remote/activitypub/db-resolver.ts index 5ecbffa49d..a6b646c64d 100644 --- a/packages/backend/src/remote/activitypub/db-resolver.ts +++ b/packages/backend/src/remote/activitypub/db-resolver.ts @@ -17,9 +17,10 @@ import { Cache } from "@/misc/cache.js"; import { uriPersonCache, userByIdCache } from "@/services/user-cache.js"; import type { IObject } from "./type.js"; import { getApId } from "./type.js"; -import { resolvePerson } from "./models/person.js"; +import { resolvePerson, updatePerson } from "./models/person.js"; import { parseScyllaNote, prepared, scyllaClient } from "@/db/scylla.js"; + const publicKeyCache = new Cache("publicKey", 60 * 30); const publicKeyByUserIdCache = new Cache( "publicKeyByUserId", @@ -182,7 +183,7 @@ export default class DbResolver { */ public async getAuthUserFromKeyId(keyId: string): Promise<{ user: CacheableRemoteUser; - key: UserPublickey; + key: UserPublickey | null; } | null> { const key = await publicKeyCache.fetch( keyId, @@ -234,4 +235,13 @@ export default class DbResolver { key, }; } + + public async refetchPublicKeyForApId(user: CacheableRemoteUser): Promise { + await updatePerson(user.uri!, undefined, undefined, user); + let key = await UserPublickeys.findOneBy({ userId: user.id }); + if (key != null) { + await publicKeyByUserIdCache.set(user.id, key); + } + return key; + } } diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index d1e85890e6..6d71f96ac0 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -189,7 +189,7 @@ export async function createPerson( const host = toPuny(new URL(object.id).hostname); - const { fields } = analyzeAttachments(person.attachment || []); + const fields = analyzeAttachments(person.attachment || []); const tags = extractApHashtags(person.tag) .map((tag) => normalizeForSearch(tag)) @@ -661,39 +661,6 @@ export async function resolvePerson( return await createPerson(uri, resolver); } -const services: { - [x: string]: (id: string, username: string) => any; -} = { - "misskey:authentication:twitter": (userId, screenName) => ({ - userId, - screenName, - }), - "misskey:authentication:github": (id, login) => ({ id, login }), - "misskey:authentication:discord": (id, name) => $discord(id, name), -}; - -const $discord = (id: string, name: string) => { - if (typeof name !== "string") { - name = "unknown#0000"; - } - const [username, discriminator] = name.split("#"); - return { id, username, discriminator }; -}; - -function addService(target: { [x: string]: any }, source: IApPropertyValue) { - const service = services[source.name]; - - if (typeof source.value !== "string") { - source.value = "unknown"; - } - - const [id, username] = source.value.split("@"); - - if (service) { - target[source.name.split(":")[2]] = service(id, username); - } -} - export function analyzeAttachments( attachments: IObject | IObject[] | undefined, ) { @@ -701,22 +668,17 @@ export function analyzeAttachments( name: string; value: string; }[] = []; - const services: { [x: string]: any } = {}; if (Array.isArray(attachments)) { for (const attachment of attachments.filter(isPropertyValue)) { - if (isPropertyValue(attachment.identifier)) { - addService(services, attachment.identifier); - } else { - fields.push({ - name: attachment.name, - value: fromHtml(attachment.value), - }); - } + fields.push({ + name: attachment.name, + value: fromHtml(attachment.value), + }); } } - return { fields, services }; + return fields; } export async function updateFeatured(userId: User["id"], resolver?: Resolver) { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 15f2c96603..2a0a7cfbb8 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -170,21 +170,6 @@ export const meta = { optional: false, nullable: false, }, - enableTwitterIntegration: { - type: "boolean", - optional: false, - nullable: false, - }, - enableGithubIntegration: { - type: "boolean", - optional: false, - nullable: false, - }, - enableDiscordIntegration: { - type: "boolean", - optional: false, - nullable: false, - }, enableServiceWorker: { type: "boolean", optional: false, @@ -326,36 +311,6 @@ export const meta = { nullable: true, format: "id", }, - twitterConsumerKey: { - type: "string", - optional: true, - nullable: true, - }, - twitterConsumerSecret: { - type: "string", - optional: true, - nullable: true, - }, - githubClientId: { - type: "string", - optional: true, - nullable: true, - }, - githubClientSecret: { - type: "string", - optional: true, - nullable: true, - }, - discordClientId: { - type: "string", - optional: true, - nullable: true, - }, - discordClientSecret: { - type: "string", - optional: true, - nullable: true, - }, summaryProxy: { type: "string", optional: true, @@ -544,9 +499,6 @@ export default define(meta, paramDef, async (ps, me) => { defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, enableEmail: instance.enableEmail, - enableTwitterIntegration: instance.enableTwitterIntegration, - enableGithubIntegration: instance.enableGithubIntegration, - enableDiscordIntegration: instance.enableDiscordIntegration, enableServiceWorker: instance.enableServiceWorker, translatorAvailable: instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null, @@ -573,12 +525,6 @@ export default define(meta, paramDef, async (ps, me) => { enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, proxyAccountId: instance.proxyAccountId, - twitterConsumerKey: instance.twitterConsumerKey, - twitterConsumerSecret: instance.twitterConsumerSecret, - githubClientId: instance.githubClientId, - githubClientSecret: instance.githubClientSecret, - discordClientId: instance.discordClientId, - discordClientSecret: instance.discordClientSecret, summalyProxy: instance.summalyProxy, email: instance.email, smtpSecure: instance.smtpSecure, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 3c3a0913d0..cbc86a5c17 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -46,13 +46,6 @@ export default define(meta, paramDef, async (ps, me) => { }; } - const maskedKeys = ["accessToken", "accessTokenSecret", "refreshToken"]; - Object.keys(profile.integrations).forEach((integration) => { - maskedKeys.forEach( - (key) => (profile.integrations[integration][key] = ""), - ); - }); - const signins = await Signins.findBy({ userId: user.id }); return { @@ -67,7 +60,6 @@ export default define(meta, paramDef, async (ps, me) => { carefulBot: profile.carefulBot, injectFeaturedNote: profile.injectFeaturedNote, receiveAnnouncementEmail: profile.receiveAnnouncementEmail, - integrations: profile.integrations, mutedWords: profile.mutedWords, mutedInstances: profile.mutedInstances, mutingNotificationTypes: profile.mutingNotificationTypes, 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 f8bf52d215..d25d763875 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -134,15 +134,6 @@ export const paramDef = { deeplIsPro: { type: "boolean" }, libreTranslateApiUrl: { type: "string", nullable: true }, libreTranslateApiKey: { type: "string", nullable: true }, - enableTwitterIntegration: { type: "boolean" }, - twitterConsumerKey: { type: "string", nullable: true }, - twitterConsumerSecret: { type: "string", nullable: true }, - enableGithubIntegration: { type: "boolean" }, - githubClientId: { type: "string", nullable: true }, - githubClientSecret: { type: "string", nullable: true }, - enableDiscordIntegration: { type: "boolean" }, - discordClientId: { type: "string", nullable: true }, - discordClientSecret: { type: "string", nullable: true }, enableEmail: { type: "boolean" }, email: { type: "string", nullable: true }, smtpSecure: { type: "boolean" }, @@ -397,42 +388,6 @@ export default define(meta, paramDef, async (ps, me) => { set.summalyProxy = ps.summalyProxy; } - if (ps.enableTwitterIntegration !== undefined) { - set.enableTwitterIntegration = ps.enableTwitterIntegration; - } - - if (ps.twitterConsumerKey !== undefined) { - set.twitterConsumerKey = ps.twitterConsumerKey; - } - - if (ps.twitterConsumerSecret !== undefined) { - set.twitterConsumerSecret = ps.twitterConsumerSecret; - } - - if (ps.enableGithubIntegration !== undefined) { - set.enableGithubIntegration = ps.enableGithubIntegration; - } - - if (ps.githubClientId !== undefined) { - set.githubClientId = ps.githubClientId; - } - - if (ps.githubClientSecret !== undefined) { - set.githubClientSecret = ps.githubClientSecret; - } - - if (ps.enableDiscordIntegration !== undefined) { - set.enableDiscordIntegration = ps.enableDiscordIntegration; - } - - if (ps.discordClientId !== undefined) { - set.discordClientId = ps.discordClientId; - } - - if (ps.discordClientSecret !== undefined) { - set.discordClientSecret = ps.discordClientSecret; - } - if (ps.enableEmail !== undefined) { set.enableEmail = ps.enableEmail; } diff --git a/packages/backend/src/server/api/endpoints/i/known-as.ts b/packages/backend/src/server/api/endpoints/i/known-as.ts index 0d0c06180a..384cbd7c34 100644 --- a/packages/backend/src/server/api/endpoints/i/known-as.ts +++ b/packages/backend/src/server/api/endpoints/i/known-as.ts @@ -33,7 +33,7 @@ export const meta = { id: "4362f8dc-731f-4ad8-a694-be2a88922a24", }, uriNull: { - message: "User ActivityPup URI is null.", + message: "User ActivityPub URI is null.", code: "URI_NULL", id: "bf326f31-d430-4f97-9933-5d61e4d48a23", }, diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts index 0cafcbc902..6a419929ec 100644 --- a/packages/backend/src/server/api/endpoints/i/move.ts +++ b/packages/backend/src/server/api/endpoints/i/move.ts @@ -54,12 +54,12 @@ export const meta = { id: "fcd2eef9-a9b2-4c4f-8624-038099e90aa5", }, uriNull: { - message: "User ActivityPup URI is null.", + message: "User ActivityPub URI is null.", code: "URI_NULL", id: "bf326f31-d430-4f97-9933-5d61e4d48a23", }, localUriNull: { - message: "Local User ActivityPup URI is null.", + message: "Local User ActivityPub URI is null.", code: "URI_NULL", id: "95ba11b9-90e8-43a5-ba16-7acc1ab32e71", }, diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 29a0e7a1af..8c521fcff9 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -268,21 +268,6 @@ export const meta = { optional: false, nullable: false, }, - enableTwitterIntegration: { - type: "boolean", - optional: false, - nullable: false, - }, - enableGithubIntegration: { - type: "boolean", - optional: false, - nullable: false, - }, - enableDiscordIntegration: { - type: "boolean", - optional: false, - nullable: false, - }, enableServiceWorker: { type: "boolean", optional: false, @@ -343,21 +328,6 @@ export const meta = { optional: false, nullable: false, }, - twitter: { - type: "boolean", - optional: false, - nullable: false, - }, - github: { - type: "boolean", - optional: false, - nullable: false, - }, - discord: { - type: "boolean", - optional: false, - nullable: false, - }, serviceWorker: { type: "boolean", optional: false, @@ -493,10 +463,6 @@ export default define(meta, paramDef, async (ps, me) => { })), enableEmail: instance.enableEmail, - enableTwitterIntegration: instance.enableTwitterIntegration, - enableGithubIntegration: instance.enableGithubIntegration, - enableDiscordIntegration: instance.enableDiscordIntegration, - enableServiceWorker: instance.enableServiceWorker, translatorAvailable: @@ -539,9 +505,6 @@ export default define(meta, paramDef, async (ps, me) => { hcaptcha: instance.enableHcaptcha, recaptcha: instance.enableRecaptcha, objectStorage: instance.useObjectStorage, - twitter: instance.enableTwitterIntegration, - github: instance.enableGithubIntegration, - discord: instance.enableDiscordIntegration, serviceWorker: instance.enableServiceWorker, postEditing: true, postImports: instance.experimentalFeatures?.postImports || false, diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index 9e8c458868..bf617f06fc 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -21,9 +21,6 @@ import signup from "./private/signup.js"; import signin from "./private/signin.js"; import signupPending from "./private/signup-pending.js"; import verifyEmail from "./private/verify-email.js"; -import discord from "./service/discord.js"; -import github from "./service/github.js"; -import twitter from "./service/twitter.js"; import { koaBody } from "koa-body"; import { convertId, @@ -181,10 +178,6 @@ router.post("/signin", signin); router.post("/signup-pending", signupPending); router.post("/verify-email", verifyEmail); -router.use(discord.routes()); -router.use(github.routes()); -router.use(twitter.routes()); - router.post("/miauth/:session/check", async (ctx) => { const token = await AccessTokens.findOneBy({ session: ctx.params.session, diff --git a/packages/backend/src/server/api/service/discord.ts b/packages/backend/src/server/api/service/discord.ts deleted file mode 100644 index 848a70d4a5..0000000000 --- a/packages/backend/src/server/api/service/discord.ts +++ /dev/null @@ -1,333 +0,0 @@ -import type Koa from "koa"; -import Router from "@koa/router"; -import { OAuth2 } from "oauth"; -import { v4 as uuid } from "uuid"; -import { IsNull } from "typeorm"; -import { getJson } from "@/misc/fetch.js"; -import config from "@/config/index.js"; -import { publishMainStream } from "@/services/stream.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; -import { Users, UserProfiles } from "@/models/index.js"; -import type { ILocalUser } from "@/models/entities/user.js"; -import { redisClient } from "../../../db/redis.js"; -import signin from "../common/signin.js"; - -function getUserToken(ctx: Koa.BaseContext): string | null { - return ((ctx.headers["cookie"] || "").match(/igi=(\w+)/) || [null, null])[1]; -} - -function compareOrigin(ctx: Koa.BaseContext): boolean { - function normalizeUrl(url?: string): string { - return url ? (url.endsWith("/") ? url.slice(0, url.length - 1) : url) : ""; - } - - const referer = ctx.headers["referer"]; - - return normalizeUrl(referer) === normalizeUrl(config.url); -} - -// Init router -const router = new Router(); - -router.get("/disconnect/discord", async (ctx) => { - if (!compareOrigin(ctx)) { - ctx.throw(400, "invalid origin"); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, "signin required"); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - profile.integrations.discord = undefined; - - await UserProfiles.update(user.id, { - integrations: profile.integrations, - }); - - ctx.body = "Discordの連携を解除しました :v:"; - - // Publish i updated event - publishMainStream( - user.id, - "meUpdated", - await Users.pack(user, user, { - detail: true, - includeSecrets: true, - }), - ); -}); - -async function getOAuth2() { - const meta = await fetchMeta(true); - - if (meta.enableDiscordIntegration) { - return new OAuth2( - meta.discordClientId!, - meta.discordClientSecret!, - "https://discord.com/", - "api/oauth2/authorize", - "api/oauth2/token", - ); - } else { - return null; - } -} - -router.get("/connect/discord", async (ctx) => { - if (!compareOrigin(ctx)) { - ctx.throw(400, "invalid origin"); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, "signin required"); - return; - } - - const params = { - redirect_uri: `${config.url}/api/dc/cb`, - scope: ["identify"], - state: uuid(), - response_type: "code", - }; - - redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get("/signin/discord", async (ctx) => { - const sessid = uuid(); - - const params = { - redirect_uri: `${config.url}/api/dc/cb`, - scope: ["identify"], - state: uuid(), - response_type: "code", - }; - - ctx.cookies.set("signin_with_discord_sid", sessid, { - path: "/", - secure: config.url.startsWith("https"), - httpOnly: true, - }); - - redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get("/dc/cb", async (ctx) => { - const userToken = getUserToken(ctx); - - const oauth2 = await getOAuth2(); - - if (!userToken) { - const sessid = ctx.cookies.get("signin_with_discord_sid"); - - if (!sessid) { - ctx.throw(400, "invalid session"); - return; - } - - const code = ctx.query.code; - - if (!code || typeof code !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(sessid, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, "invalid session"); - return; - } - - const { accessToken, refreshToken, expiresDate } = await new Promise( - (res, rej) => - oauth2!.getOAuthAccessToken( - code, - { - grant_type: "authorization_code", - redirect_uri, - }, - (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - }, - ), - ); - - const { id, username, discriminator } = (await getJson( - "https://discord.com/api/users/@me", - "*/*", - 10 * 1000, - { - Authorization: `Bearer ${accessToken}`, - }, - )) as Record; - - if ( - typeof id !== "string" || - typeof username !== "string" || - typeof discriminator !== "string" - ) { - ctx.throw(400, "invalid session"); - return; - } - - const profile = await UserProfiles.createQueryBuilder() - .where("\"integrations\"->'discord'->>'id' = :id", { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (profile == null) { - ctx.throw( - 404, - `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`, - ); - return; - } - - await UserProfiles.update(profile.userId, { - integrations: { - ...profile.integrations, - discord: { - id: id, - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - username: username, - discriminator: discriminator, - }, - }, - }); - - signin( - ctx, - (await Users.findOneBy({ id: profile.userId })) as ILocalUser, - true, - ); - } else { - const code = ctx.query.code; - - if (!code || typeof code !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(userToken, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, "invalid session"); - return; - } - - const { accessToken, refreshToken, expiresDate } = await new Promise( - (res, rej) => - oauth2!.getOAuthAccessToken( - code, - { - grant_type: "authorization_code", - redirect_uri, - }, - (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - }, - ), - ); - - const { id, username, discriminator } = (await getJson( - "https://discord.com/api/users/@me", - "*/*", - 10 * 1000, - { - Authorization: `Bearer ${accessToken}`, - }, - )) as Record; - if ( - typeof id !== "string" || - typeof username !== "string" || - typeof discriminator !== "string" - ) { - ctx.throw(400, "invalid session"); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - await UserProfiles.update(user.id, { - integrations: { - ...profile.integrations, - discord: { - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - id: id, - username: username, - discriminator: discriminator, - }, - }, - }); - - ctx.body = `Discord: @${username}#${discriminator} を、Misskey: @${user.username} に接続しました!`; - - // Publish i updated event - publishMainStream( - user.id, - "meUpdated", - await Users.pack(user, user, { - detail: true, - includeSecrets: true, - }), - ); - } -}); - -export default router; diff --git a/packages/backend/src/server/api/service/github.ts b/packages/backend/src/server/api/service/github.ts deleted file mode 100644 index fd015fb8ad..0000000000 --- a/packages/backend/src/server/api/service/github.ts +++ /dev/null @@ -1,296 +0,0 @@ -import type Koa from "koa"; -import Router from "@koa/router"; -import { OAuth2 } from "oauth"; -import { v4 as uuid } from "uuid"; -import { IsNull } from "typeorm"; -import { getJson } from "@/misc/fetch.js"; -import config from "@/config/index.js"; -import { publishMainStream } from "@/services/stream.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; -import { Users, UserProfiles } from "@/models/index.js"; -import type { ILocalUser } from "@/models/entities/user.js"; -import { redisClient } from "../../../db/redis.js"; -import signin from "../common/signin.js"; - -function getUserToken(ctx: Koa.BaseContext): string | null { - return ((ctx.headers["cookie"] || "").match(/igi=(\w+)/) || [null, null])[1]; -} - -function compareOrigin(ctx: Koa.BaseContext): boolean { - function normalizeUrl(url?: string): string { - return url ? (url.endsWith("/") ? url.slice(0, url.length - 1) : url) : ""; - } - - const referer = ctx.headers["referer"]; - - return normalizeUrl(referer) === normalizeUrl(config.url); -} - -// Init router -const router = new Router(); - -router.get("/disconnect/github", async (ctx) => { - if (!compareOrigin(ctx)) { - ctx.throw(400, "invalid origin"); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, "signin required"); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - profile.integrations.github = undefined; - - await UserProfiles.update(user.id, { - integrations: profile.integrations, - }); - - ctx.body = "GitHubの連携を解除しました :v:"; - - // Publish i updated event - publishMainStream( - user.id, - "meUpdated", - await Users.pack(user, user, { - detail: true, - includeSecrets: true, - }), - ); -}); - -async function getOath2() { - const meta = await fetchMeta(true); - - if ( - meta.enableGithubIntegration && - meta.githubClientId && - meta.githubClientSecret - ) { - return new OAuth2( - meta.githubClientId, - meta.githubClientSecret, - "https://github.com/", - "login/oauth/authorize", - "login/oauth/access_token", - ); - } else { - return null; - } -} - -router.get("/connect/github", async (ctx) => { - if (!compareOrigin(ctx)) { - ctx.throw(400, "invalid origin"); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, "signin required"); - return; - } - - const params = { - redirect_uri: `${config.url}/api/gh/cb`, - scope: ["read:user"], - state: uuid(), - }; - - redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOath2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get("/signin/github", async (ctx) => { - const sessid = uuid(); - - const params = { - redirect_uri: `${config.url}/api/gh/cb`, - scope: ["read:user"], - state: uuid(), - }; - - ctx.cookies.set("signin_with_github_sid", sessid, { - path: "/", - secure: config.url.startsWith("https"), - httpOnly: true, - }); - - redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOath2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get("/gh/cb", async (ctx) => { - const userToken = getUserToken(ctx); - - const oauth2 = await getOath2(); - - if (!userToken) { - const sessid = ctx.cookies.get("signin_with_github_sid"); - - if (!sessid) { - ctx.throw(400, "invalid session"); - return; - } - - const code = ctx.query.code; - - if (!code || typeof code !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(sessid, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, "invalid session"); - return; - } - - const { accessToken } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken( - code, - { - redirect_uri, - }, - (err, accessToken, refresh, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ accessToken }); - } - }, - ), - ); - - const { login, id } = (await getJson( - "https://api.github.com/user", - "application/vnd.github.v3+json", - 10 * 1000, - { - Authorization: `bearer ${accessToken}`, - }, - )) as Record; - if (typeof login !== "string" || typeof id !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const link = await UserProfiles.createQueryBuilder() - .where("\"integrations\"->'github'->>'id' = :id", { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - ctx.throw( - 404, - `@${login}と連携しているMisskeyアカウントはありませんでした...`, - ); - return; - } - - signin( - ctx, - (await Users.findOneBy({ id: link.userId })) as ILocalUser, - true, - ); - } else { - const code = ctx.query.code; - - if (!code || typeof code !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(userToken, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, "invalid session"); - return; - } - - const { accessToken } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken( - code, - { redirect_uri }, - (err, accessToken, refresh, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ accessToken }); - } - }, - ), - ); - - const { login, id } = (await getJson( - "https://api.github.com/user", - "application/vnd.github.v3+json", - 10 * 1000, - { - Authorization: `bearer ${accessToken}`, - }, - )) as Record; - - if (typeof login !== "string" || typeof id !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - await UserProfiles.update(user.id, { - integrations: { - ...profile.integrations, - github: { - accessToken: accessToken, - id: id, - login: login, - }, - }, - }); - - ctx.body = `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`; - - // Publish i updated event - publishMainStream( - user.id, - "meUpdated", - await Users.pack(user, user, { - detail: true, - includeSecrets: true, - }), - ); - } -}); - -export default router; diff --git a/packages/backend/src/server/api/service/twitter.ts b/packages/backend/src/server/api/service/twitter.ts deleted file mode 100644 index 3695592410..0000000000 --- a/packages/backend/src/server/api/service/twitter.ts +++ /dev/null @@ -1,226 +0,0 @@ -import type Koa from "koa"; -import Router from "@koa/router"; -import { v4 as uuid } from "uuid"; -import autwh from "autwh"; -import { IsNull } from "typeorm"; -import { publishMainStream } from "@/services/stream.js"; -import config from "@/config/index.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; -import { Users, UserProfiles } from "@/models/index.js"; -import type { ILocalUser } from "@/models/entities/user.js"; -import signin from "../common/signin.js"; -import { redisClient } from "../../../db/redis.js"; - -function getUserToken(ctx: Koa.BaseContext): string | null { - return ((ctx.headers["cookie"] || "").match(/igi=(\w+)/) || [null, null])[1]; -} - -function compareOrigin(ctx: Koa.BaseContext): boolean { - function normalizeUrl(url?: string): string { - return url == null - ? "" - : url.endsWith("/") - ? url.substr(0, url.length - 1) - : url; - } - - const referer = ctx.headers["referer"]; - - return normalizeUrl(referer) === normalizeUrl(config.url); -} - -// Init router -const router = new Router(); - -router.get("/disconnect/twitter", async (ctx) => { - if (!compareOrigin(ctx)) { - ctx.throw(400, "invalid origin"); - return; - } - - const userToken = getUserToken(ctx); - if (userToken == null) { - ctx.throw(400, "signin required"); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - profile.integrations.twitter = undefined; - - await UserProfiles.update(user.id, { - integrations: profile.integrations, - }); - - ctx.body = "Twitterの連携を解除しました :v:"; - - // Publish i updated event - publishMainStream( - user.id, - "meUpdated", - await Users.pack(user, user, { - detail: true, - includeSecrets: true, - }), - ); -}); - -async function getTwAuth() { - const meta = await fetchMeta(true); - - if ( - meta.enableTwitterIntegration && - meta.twitterConsumerKey && - meta.twitterConsumerSecret - ) { - return autwh({ - consumerKey: meta.twitterConsumerKey, - consumerSecret: meta.twitterConsumerSecret, - callbackUrl: `${config.url}/api/tw/cb`, - }); - } else { - return null; - } -} - -router.get("/connect/twitter", async (ctx) => { - if (!compareOrigin(ctx)) { - ctx.throw(400, "invalid origin"); - return; - } - - const userToken = getUserToken(ctx); - if (userToken == null) { - ctx.throw(400, "signin required"); - return; - } - - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - redisClient.set(userToken, JSON.stringify(twCtx)); - ctx.redirect(twCtx.url); -}); - -router.get("/signin/twitter", async (ctx) => { - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - - const sessid = uuid(); - - redisClient.set(sessid, JSON.stringify(twCtx)); - - ctx.cookies.set("signin_with_twitter_sid", sessid, { - path: "/", - secure: config.url.startsWith("https"), - httpOnly: true, - }); - - ctx.redirect(twCtx.url); -}); - -router.get("/tw/cb", async (ctx) => { - const userToken = getUserToken(ctx); - - const twAuth = await getTwAuth(); - - if (userToken == null) { - const sessid = ctx.cookies.get("signin_with_twitter_sid"); - - if (sessid == null) { - ctx.throw(400, "invalid session"); - return; - } - - const get = new Promise((res, rej) => { - redisClient.get(sessid, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const verifier = ctx.query.oauth_verifier; - if (!verifier || typeof verifier !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const link = await UserProfiles.createQueryBuilder() - .where("\"integrations\"->'twitter'->>'userId' = :id", { - id: result.userId, - }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - ctx.throw( - 404, - `@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`, - ); - return; - } - - signin( - ctx, - (await Users.findOneBy({ id: link.userId })) as ILocalUser, - true, - ); - } else { - const verifier = ctx.query.oauth_verifier; - - if (!verifier || typeof verifier !== "string") { - ctx.throw(400, "invalid session"); - return; - } - - const get = new Promise((res, rej) => { - redisClient.get(userToken, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - await UserProfiles.update(user.id, { - integrations: { - ...profile.integrations, - twitter: { - accessToken: result.accessToken, - accessTokenSecret: result.accessTokenSecret, - userId: result.userId, - screenName: result.screenName, - }, - }, - }); - - ctx.body = `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`; - - // Publish i updated event - publishMainStream( - user.id, - "meUpdated", - await Users.pack(user, user, { - detail: true, - includeSecrets: true, - }), - ); - } -}); - -export default router; diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index f2aca7e3ca..e750ac92b1 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -68,7 +68,7 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 52e2dc2a97..5aafe865b3 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -67,7 +67,7 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 6e2da514f5..2ec53a3fa9 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -84,7 +84,7 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 2cc286b17f..40e38c24f0 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -60,7 +60,7 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts index 186b120320..bbbc4e7a9a 100644 --- a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts @@ -82,7 +82,7 @@ export default class extends Channel { // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user?.id, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index fb989f33ee..db51344b91 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -1,6 +1,6 @@ import * as mfm from "mfm-js"; -import es from "../../db/elasticsearch.js"; -import sonic from "../../db/sonic.js"; +import es from "@/db/elasticsearch.js"; +import sonic from "@/db/sonic.js"; import { publishMainStream, publishNotesStream, @@ -386,22 +386,18 @@ export default async ( // Word mute if (!scyllaClient) { - mutedWordsCache - .fetch(null, () => - UserProfiles.find({ - where: { - enableWordMute: true, - }, - select: ["userId", "mutedWords"], - }), - ) - .then((us) => { - for (const u of us) { - const shouldMute = getWordHardMute( - data, - { id: u.userId }, - u.mutedWords, - ); + mutedWordsCache + .fetch(null, () => + UserProfiles.find({ + where: { + enableWordMute: true, + }, + select: ["userId", "mutedWords"], + }), + ) + .then((us) => { + for (const u of us) { + getWordHardMute(data, u.userId, u.mutedWords).then((shouldMute) => { if (shouldMute) { MutedNotes.insert({ id: genId(), @@ -410,8 +406,8 @@ export default async ( reason: "word", }); } - } - }); + }); + }}); } // Antenna diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue index e84482131e..be5144dc59 100644 --- a/packages/client/src/components/MkNote.vue +++ b/packages/client/src/components/MkNote.vue @@ -273,7 +273,6 @@ diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue index d7630f8d05..2708cc07f1 100644 --- a/packages/client/src/components/MkSignin.vue +++ b/packages/client/src/components/MkSignin.vue @@ -124,38 +124,6 @@ - @@ -169,7 +137,6 @@ import { apiUrl, host as configHost } from "@/config"; import { byteify, hexify } from "@/scripts/2fa"; import * as os from "@/os"; import { login } from "@/account"; -import { instance } from "@/instance"; import { i18n } from "@/i18n"; const signing = ref(false); @@ -184,8 +151,6 @@ const queryingKey = ref(false); const hCaptchaResponse = ref(null); const reCaptchaResponse = ref(null); -const meta = computed(() => instance); - const emit = defineEmits<{ (ev: "login", v: any): void; }>(); diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue index ed9f9f612e..7deffdddd5 100644 --- a/packages/client/src/pages/admin/index.vue +++ b/packages/client/src/pages/admin/index.vue @@ -267,14 +267,6 @@ const menuDef = computed(() => [ to: "/admin/relays", active: currentPage.value?.route.name === "relays", }, - { - icon: "ph-plug ph-bold ph-lg", - text: i18n.ts.integration, - to: "/admin/integrations", - active: - currentPage.value?.route.name === - "integrations", - }, { icon: "ph-prohibit ph-bold ph-lg", text: i18n.ts.instanceBlocking, diff --git a/packages/client/src/pages/admin/integrations.discord.vue b/packages/client/src/pages/admin/integrations.discord.vue deleted file mode 100644 index 8be7582d38..0000000000 --- a/packages/client/src/pages/admin/integrations.discord.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/packages/client/src/pages/admin/integrations.github.vue b/packages/client/src/pages/admin/integrations.github.vue deleted file mode 100644 index 2f3416b086..0000000000 --- a/packages/client/src/pages/admin/integrations.github.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/packages/client/src/pages/admin/integrations.vue b/packages/client/src/pages/admin/integrations.vue deleted file mode 100644 index 505080d9e2..0000000000 --- a/packages/client/src/pages/admin/integrations.vue +++ /dev/null @@ -1,68 +0,0 @@ - - - diff --git a/packages/client/src/pages/settings/index.vue b/packages/client/src/pages/settings/index.vue index 6bb3f9809d..8d73547161 100644 --- a/packages/client/src/pages/settings/index.vue +++ b/packages/client/src/pages/settings/index.vue @@ -116,12 +116,6 @@ const menuDef = computed(() => [ to: "/settings/email", active: currentPage.value?.route.name === "email", }, - { - icon: "ph-share-network ph-bold ph-lg", - text: i18n.ts.integration, - to: "/settings/integration", - active: currentPage.value?.route.name === "integration", - }, { icon: "ph-lock ph-bold ph-lg", text: i18n.ts.security, diff --git a/packages/client/src/pages/settings/integration.vue b/packages/client/src/pages/settings/integration.vue deleted file mode 100644 index 2eb48a547e..0000000000 --- a/packages/client/src/pages/settings/integration.vue +++ /dev/null @@ -1,118 +0,0 @@ - - - diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 60e9d37c58..dd1621777e 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -103,11 +103,6 @@ export const routes = [ name: "email", component: page(() => import("./pages/settings/email.vue")), }, - { - path: "/integration", - name: "integration", - component: page(() => import("./pages/settings/integration.vue")), - }, { path: "/security", name: "security", @@ -525,11 +520,6 @@ export const routes = [ name: "relays", component: page(() => import("./pages/admin/relays.vue")), }, - { - path: "/integrations", - name: "integrations", - component: page(() => import("./pages/admin/integrations.vue")), - }, { path: "/instance-block", name: "instance-block", diff --git a/packages/client/src/scripts/check-word-mute.ts b/packages/client/src/scripts/check-word-mute.ts index b54ca73116..7bdf6a9685 100644 --- a/packages/client/src/scripts/check-word-mute.ts +++ b/packages/client/src/scripts/check-word-mute.ts @@ -1,3 +1,5 @@ +import type * as firefish from "firefish-js"; + export interface Muted { muted: boolean; matched: string[]; @@ -7,7 +9,7 @@ export interface Muted { const NotMuted = { muted: false, matched: [] }; function checkLangMute( - note: NoteLike, + note: firefish.entities.Note, mutedLangs: Array, ): Muted { const mutedLangList = new Set( @@ -20,7 +22,7 @@ function checkLangMute( } function checkWordMute( - note: NoteLike, + note: firefish.entities.Note, mutedWords: Array, ): Muted { let text = `${note.cw ?? ""} ${note.text ?? ""}`; @@ -72,15 +74,12 @@ function checkWordMute( } export function getWordSoftMute( - note: Record, - me: Record | null | undefined, + note: firefish.entities.Note, + meId: string | null | undefined, mutedWords: Array, mutedLangs: Array, ): Muted { - // 自分自身 - if (me && note.userId === me.id) { - return NotMuted; - } + if (note.userId === meId) return NotMuted; if (mutedWords.length > 0) { const noteMuted = checkWordMute(note, mutedWords); diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index 5eb25bc1e2..d8f73102ec 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -41,23 +41,23 @@ export const defaultStore = markRaw( default: 0, }, tlHomeHintClosed: { - where: "device", + where: "account", default: false, }, tlLocalHintClosed: { - where: "device", + where: "account", default: false, }, tlRecommendedHintClosed: { - where: "device", + where: "account", default: false, }, tlSocialHintClosed: { - where: "device", + where: "account", default: false, }, tlGlobalHintClosed: { - where: "device", + where: "account", default: false, }, keepCw: { diff --git a/packages/firefish-js/package.json b/packages/firefish-js/package.json index f921531537..a9e4e2ee9e 100644 --- a/packages/firefish-js/package.json +++ b/packages/firefish-js/package.json @@ -12,7 +12,7 @@ "api": "pnpm api-extractor run --local --verbose", "api-prod": "pnpm api-extractor run --verbose", "api-doc": "pnpm api-documenter markdown -i ./etc/", - "lint": "pnpm biome check --apply **/*.ts", + "lint": "pnpm biome check --apply *.ts", "format": "pnpm biome format --write **/*.ts", "jest": "jest --coverage --detectOpenHandles", "test": "pnpm jest && pnpm tsd" diff --git a/packages/sw/package.json b/packages/sw/package.json index d92cfd5e89..7c5bd14077 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -5,7 +5,7 @@ "build": "pnpm vite build --emptyOutDir", "build:debug": "pnpm run build", "watch": "pnpm swc src -d built -D -w", - "lint": "pnpm biome check **/*.ts --apply", + "lint": "pnpm biome check *.ts --apply", "format": "pnpm biome format * --write" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf1940c045..2b2f705a13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -283,7 +283,7 @@ importers: specifier: 7.0.2 version: 7.0.2(@types/koa@2.13.8)(ejs@3.1.9)(pug@3.0.2) megalodon: - specifier: ^8.1.1 + specifier: 8.1.1 version: 8.1.1 meilisearch: specifier: 0.34.1 @@ -315,9 +315,6 @@ importers: nsfwjs: specifier: 2.4.2 version: 2.4.2(@tensorflow/tfjs@4.2.0) - oauth: - specifier: ^0.10.0 - version: 0.10.0 opencc-js: specifier: ^1.0.5 version: 1.0.5