Compare commits

...

98 commits
miau ... nyaaa

Author SHA1 Message Date
Jeder 7c3e9fad6b Updated en-GB 2022-12-03 02:14:03 +01:00
Jeder d282ddc47b Merge remote-tracking branch 'upstream/main' into nyaaa 2022-12-03 02:12:47 +01:00
Jeder 3348a2ad96 Updated en-GB 2022-11-23 12:39:03 +01:00
Jeder 80d36c4ec4 Merge remote-tracking branch 'upstream/enable-push-notifs' into nyaaa 2022-11-21 22:12:25 +01:00
Jeder 788b19cd1e fuck 2022-11-21 15:54:31 +01:00
Jeder 76a9d165cb Merge remote-tracking branch 'upstream/main' into nyaaa 2022-11-21 15:51:30 +01:00
Jeder 6ce3e41eb6 Merge remote-tracking branch 'upstream/enable-push-notifs' into nyaaa 2022-11-18 13:46:37 +01:00
Jeder 691379c4f5 Merge remote-tracking branch 'upstream/native-notifications' into nyaaa 2022-11-18 13:32:34 +01:00
Jeder 0537312e95 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-11-18 13:31:23 +01:00
Jeder b99d2aea68 Revert "Replace toast (sorry!) notifications with browser ones"
This reverts commit 3232a9d0c7.
2022-11-18 13:28:44 +01:00
Jeder f9811eb62a Revert "Send notifications also when the tab is not visible"
This reverts commit 4bcf50356d.
2022-11-18 13:28:33 +01:00
Jeder 556f190ccb Revert "Some tweaks to how notifications show reactions and avatars"
This reverts commit 7dc64edfb0.
2022-11-18 13:26:53 +01:00
Johann150 7a82a3d262
server: always enable push notifications
The thing that previously presumably hindered this was that the VAPID
keys had to be set up. Previously admins had to do this, but this is a bad
idea for multiple reasons:
1) The meaning of "public key" and "private key" was not well documented
in the settings.
2) Giving out a private key over the API, even just for admins, sounds
like a bad idea.
2022-11-16 20:57:27 +01:00
Jeder 7518ede6e8 fuck 2022-11-13 16:31:09 +01:00
Jeder 54bfc890e3 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-11-13 16:17:44 +01:00
Johann150 45ee9951dc
client: remove unused notification-toast component 2022-11-12 21:22:36 +01:00
Johann150 cd87e262fe
client: pass along notifications if push notifs disabled 2022-11-12 20:53:37 +01:00
Johann150 23953b9ad1
service worker: refactor message event handler
It is now possible for the client to trigger notifications "manually"
if push notifications are not configured on the server.
2022-11-12 20:51:42 +01:00
Johann150 db0e6a241c
service worker: also show notifications if client is connected 2022-11-12 18:52:12 +01:00
Johann150 0c3c855d29
client: translate comments 2022-11-12 18:52:11 +01:00
Jeder 408382cb14 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-11-08 19:46:05 +01:00
Jeder bfdffa6c66 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-10-31 15:07:40 +01:00
Jeder c54a513879 Updated en-GB 2022-10-26 22:41:09 +02:00
Jeder 4c6cf88d38 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-10-26 22:40:38 +02:00
Jeder ba83032bfa Merge remote-tracking branch 'upstream/main' into nyaaa 2022-10-20 17:33:39 +02:00
Jeder 3eeee88973 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-10-16 17:15:50 +02:00
Jeder 6edf5928e9 Merge remote-tracking branch 'upstream/secure-mode' into nyaaa 2022-10-15 01:56:38 +02:00
Norm 8aee4bb4d8 Remove deprecated URLs 2022-10-14 23:36:11 +00:00
nullobsi 86355f948c Skip rendering private data in privateMode
Co-authored-by: Francis Dinh <normandy@biribiri.dev>
2022-10-14 23:36:11 +00:00
nullobsi a0525cb8ec Add secure mode settings to Security tab 2022-10-14 23:36:11 +00:00
nullobsi fdbc72130f In private mode, block access to many public APIs 2022-10-14 23:34:37 +00:00
nullobsi e465ea8103 Add Secure Mode and Private Mode
- Add instance actor
- Add private mode, which uses an allowlist
- Add Secure Mode, restricts access to blocked instances

Co-authored-by: Francis Dinh <normandy@biribiri.dev>
2022-10-14 21:47:47 +00:00
nullobsi 3f438bcdab Add migration for allowedHosts, secureMode, privateMode 2022-10-14 21:47:47 +00:00
Jeder 43253171f4 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-10-14 20:58:04 +02:00
Michcio 1dcced3668 Fix type in thread muting 2022-10-03 13:00:26 +02:00
Jeder 62c627a542 yarn 2022-10-03 13:00:21 +02:00
Jeder 6acc27e2c5 Updated en-GB 2022-10-03 12:45:03 +02:00
Chloe Kudryavtsev 3c1d0cc8dc client: remove blinking animation from notification indicator
It was annoying, and turns out, also a CPU hog!

Changelog: Removed
2022-10-03 12:44:33 +02:00
Michcio fd7121f5f0 Trash integrations lmao 2022-10-03 12:42:04 +02:00
Jeder dd8bcce32f Updated en-GB 2022-10-02 22:52:17 +02:00
Jeder 437c142502 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-10-02 22:50:49 +02:00
Jeder ddde9d5041 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-09-26 21:38:16 +02:00
Jeder 1a386d8bb6 Updated en-GB 2022-09-26 21:06:45 +02:00
Jeder 9ec76d450f Merge remote-tracking branch 'upstream/main' into nyaaa 2022-09-26 21:06:31 +02:00
Jeder 618a0c4ace i don't know what git wants from me 2022-09-26 20:56:24 +02:00
Jeder 78f4b90145 Updated en-GB 2022-09-24 20:59:29 +02:00
Puniko b6f3cbd807 increase image description limit to 2048 characters
Changelog: Changed
2022-09-24 20:58:47 +02:00
Jędrzej Tomaszewski 7dc64edfb0 Some tweaks to how notifications show reactions and avatars 2022-09-23 13:27:21 +02:00
Michcio 4bcf50356d Send notifications also when the tab is not visible 2022-09-23 12:15:38 +02:00
Michcio e0b686ece3 Produce sourcemaps always (debugging on prod is hard) 2022-09-23 12:15:30 +02:00
Michcio 3232a9d0c7 Replace toast (sorry!) notifications with browser ones
Icons don't work in Safari just because they don't lmao

This is just barely tested, bear with me
2022-09-23 12:15:19 +02:00
Jędrzej Tomaszewski c19532e193 bruh 2022-09-12 22:10:14 +02:00
Jędrzej Tomaszewski 27c24dfd93 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-09-12 21:59:35 +02:00
Jędrzej Tomaszewski d19b7129e9 applied tosti's emoji search patch 2022-09-01 11:04:31 +02:00
Jędrzej Tomaszewski 682a338b06 some modifications to the look of instance ticker 2022-08-30 23:36:17 +02:00
Jędrzej Tomaszewski db02477874 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-08-30 23:35:35 +02:00
Jędrzej Tomaszewski 6d42caef44 i have no fucking clue why this file made all of this break 2022-08-30 10:54:43 +02:00
Jędrzej Tomaszewski 320433a579 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-08-30 10:23:48 +02:00
Jędrzej Tomaszewski 3b5a6608e0 Change followers only icon to closed lock 2022-08-30 10:19:22 +02:00
Jędrzej Tomaszewski 13cc027c95 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-08-26 09:43:46 +02:00
Jędrzej Tomaszewski 0c01443d42 Updated en-GB 2022-08-18 12:57:05 +02:00
Michcio e2a019a197 Remove all right click context menu functionality
The context menus provided by Misskey, overriding the browser context menus
on right click, were driving me very angry. This makes it much easier to copy
image URLs or even just do a quick "Inspect element".

Side victims: the reaction picker context menu feature. I never used it, so
I am only guessing what it was doing, but since I removed the whole underlying
mechanic, it only felt right to yeet the feature too.
2022-08-18 12:56:46 +02:00
Michcio 97701c9432 Lazify loading of reactions to users mapping
Borrowed some ideas from code at https://medium.com/js-dojo/lazy-rendering-in-vue-to-improve-performance-dcccd445d5f

Generally the idea is that reaction avatars are now fetched only
when the reaction bar slides into view. This should lower the load
a bit on the server.

TODO: check there might be a glitch when adding a reaction
2022-08-18 12:49:24 +02:00
Michał Sidor f06d351576 Show reacting people next to reaction buttons
This change replaces the reaction count on the reaction buttons under
the post with micro avatars of the people reacting. This makes the
whole thing feel more personal IMHO.

Performance concerns: because the posts by themselves only contain
reaction counts, this means executing an extra API call is done to
fetch the list of users who reacted. This was already being done when
hovering a reaction button, and my Raspberry Pi is doing pretty fine
despite this patch, but it should probably be addressed.
2022-08-18 12:47:47 +02:00
Jędrzej Tomaszewski 1d8fca7c1d Merge remote-tracking branch 'upstream/main' into nyaaa 2022-08-18 12:45:14 +02:00
Jędrzej Tomaszewski eea3652871 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-08-15 14:29:19 +02:00
Jędrzej Tomaszewski 8b3bfdf927 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-08-09 13:14:36 +02:00
Jędrzej Tomaszewski 768164a753 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-08-08 10:23:44 +02:00
Jędrzej Tomaszewski 89839a989e Merge remote-tracking branch 'upstream/main' into nyaaa 2022-07-27 18:19:17 +02:00
nullobsi 9ed1a09e8c Hide private data in pug when private mode is enabled 2022-07-25 23:32:28 +02:00
nullobsi f6d184ca34 Add secure mode settings to Security tab 2022-07-25 23:32:23 +02:00
nullobsi 6ec798cb80 In private mode, block access to many public APIs 2022-07-25 23:20:37 +02:00
nullobsi 1be82f6992 Add Secure Mode and Private Mode
- Add instance actor
- Add private mode, which uses an allowlist
- Add Secure Mode, restricts access to blocked instances
2022-07-25 23:18:02 +02:00
nullobsi 6e45571807 Add migration for allowedHosts, secureMode, privateMode 2022-07-25 23:13:44 +02:00
Jędrzej Tomaszewski 44292e7a91 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-07-15 16:35:53 +02:00
Jędrzej Tomaszewski 5e0ebd79de Merge remote-tracking branch 'upstream/main' into nyaaa 2022-07-15 10:33:57 +02:00
Jędrzej Tomaszewski cc74dce2d0 Merge remote-tracking branch 'upstream/main' into nyaaa 2022-07-13 19:13:04 +02:00
Jędrzej Tomaszewski bd0325b40d Updated en-GB 2022-07-13 18:20:34 +02:00
Jędrzej Tomaszewski 1e4881d47f merge conflicts baa 2022-07-13 18:06:02 +02:00
Jędrzej Tomaszewski 280421c105 Merge branch 'nyaaa' of github.com:Jeder321/misskey into nyaaa 2022-07-06 19:45:35 +02:00
Jeder321 d87207eaa3
Delete dependabot.yml
fuck dependabot
2022-07-06 19:44:50 +02:00
Jędrzej Tomaszewski f769f58231 stuff i forgot to commit lol 2022-07-06 19:31:28 +02:00
Jędrzej Tomaszewski 47119c4309 forgot about rounded corners 2022-06-14 20:09:21 +02:00
Jędrzej Tomaszewski 6abf536801 software name dings but looking better 2022-06-14 19:25:27 +02:00
Johann150 b874ca55e3 enhance: show software in instance ticker 2022-06-13 23:05:13 +02:00
Jędrzej Tomaszewski 58c27a5049 Revert "software name dings"
This reverts commit d7fe6b8af2.
2022-06-13 23:04:14 +02:00
Jędrzej Tomaszewski d7fe6b8af2 software name dings 2022-06-13 22:31:55 +02:00
Jędrzej Tomaszewski 311f8af433 Updated en-GB 2022-06-12 21:37:40 +02:00
Jędrzej Tomaszewski 27680ba04c fix merge conflicts 2022-06-12 21:37:31 +02:00
Jędrzej Tomaszewski b41315e87f fix merge conflicts 2022-04-26 17:14:24 +02:00
Jędrzej Tomaszewski 9bd9f1f2b0 star is like patch 2022-04-22 15:35:28 +02:00
Jędrzej Tomaszewski c0563c7931 Merge remote-tracking branch 'upstream/master' into nyaaa 2022-04-12 19:25:20 +02:00
Jędrzej Tomaszewski bdaae6b096 Merge remote-tracking branch 'upstream/master' into nyaaa 2022-04-06 21:38:38 +02:00
Jędrzej Tomaszewski 2a4febaa5f forgot about adding en-gb in translate.ts ig 2022-04-02 13:37:07 +02:00
Jędrzej Tomaszewski faea9a939a 8192 char note character limit 2022-04-02 13:31:49 +02:00
Jędrzej Tomaszewski 5de0db8910 larger alt-text 2022-04-02 13:30:48 +02:00
Jędrzej Tomaszewski a1cb1d27ab change en-uk locale to en-gb 2022-04-02 13:26:21 +02:00
Jędrzej Tomaszewski 0fdc71f224 more emojis in the search 2022-04-02 13:20:30 +02:00
165 changed files with 2183 additions and 1880 deletions

View file

@ -293,9 +293,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "الصفحات"
integration: "التكامل"
connectService: "اتصل"
disconnectService: "اقطع الاتصال"
enableLocalTimeline: "تفعيل الخيط المحلي"
enableGlobalTimeline: "تفعيل الخيط الزمني الشامل"
disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من الوصول إلى كل الخيوط الزمنية\

View file

@ -308,9 +308,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "পৃষ্ঠা"
integration: "ইন্টিগ্রেশন"
connectService: "সংযুক্ত করুন"
disconnectService: "সংযোগ বিচ্ছিন্ন করুন"
enableLocalTimeline: "স্থানীয় টাইমলাইন চালু করুন"
enableGlobalTimeline: "গ্লোবাল টাইমলাইন চালু করুন"
disablingTimelinesInfo: "আপনি এই টাইমলাইনগুলি বন্ধ করলেও প্রশাসক এবং মডারেটররা এই\

View file

@ -277,9 +277,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"
enableRegistration: "Povolit registraci novým uživatelům"

View file

@ -319,9 +319,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Seiten"
integration: "Integration"
connectService: "Verbinden"
disconnectService: "Trennen"
enableLocalTimeline: "Lokale Chronik aktivieren"
enableGlobalTimeline: "Globale Chronik aktivieren"
disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle\

1353
locales/en-GB.yml Normal file

File diff suppressed because it is too large Load diff

View file

@ -311,9 +311,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pages"
integration: "Integration"
connectService: "Connect"
disconnectService: "Disconnect"
enableLocalTimeline: "Enable local timeline"
enableGlobalTimeline: "Enable global timeline"
disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all\
@ -729,6 +726,13 @@ popularPosts: "Popular posts"
shareWithNote: "Share with note"
emailNotConfiguredWarning: "Email address not set."
ratio: "Ratio"
secureMode: "Secure Mode (Authorized Fetch)"
instanceSecurity: "Instance Security"
secureModeInfo: "Requests from other instances must be signed, otherwise notes won't be returned. signToActivityPubGet must be set to true in the other instance's configuration file."
privateMode: "Private Mode"
privateModeInfo: "When enabled, only authorized instances may fetch notes. Hides all notes from public."
allowedInstances: "Allowed Instances"
allowedInstancesDescription: "Set the hosts of the instances you want to allow, separated by line. Valid in private mode only."
previewNoteText: "Show preview"
customCss: "Custom CSS"
customCssWarn: "This setting should only be used if you know what it does. Entering\
@ -1341,16 +1345,6 @@ _deck:
list: "List"
mentions: "Mentions"
direct: "Direct notes"
_services:
_discord:
connected: "Discord: @{username}#{discriminator} connected to FoundKey: @{mkUsername}!"
disconnected: "Discord linkage has been removed."
_twitter:
connected: "Twitter: @{twitterUserName} connected to FoundKey: @{userName}!"
disconnected: "Twitter linkage has been removed."
_github:
connected: "GitHub: @{login} connected to FoundKey: @{userName}!"
disconnected: "GitHub linkage has been removed."
_translationService:
_deepl:
authKey: "DeepL Auth Key"

View file

@ -311,9 +311,6 @@ dayX: "Día {day}"
monthX: "Mes {month}"
yearX: "Año {year}"
pages: "Páginas"
integration: "Integración"
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\

View file

@ -311,9 +311,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\

View file

@ -310,9 +310,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\

View file

@ -22,6 +22,7 @@ const languages = [
'cs-CZ',
'de-DE',
'en-US',
'en-GB',
'es-ES',
'fr-FR',
'id-ID',

View file

@ -304,9 +304,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pagine"
integration: "App collegate"
connectService: "Connessione"
disconnectService: "Disconnessione "
enableLocalTimeline: "Abilita Timeline locale"
enableGlobalTimeline: "Abilita Timeline federata"
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e\

View file

@ -286,9 +286,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "ページ"
integration: "連携"
connectService: "接続する"
disconnectService: "切断する"
enableLocalTimeline: "ローカルタイムラインを有効にする"
enableGlobalTimeline: "グローバルタイムラインを有効にする"
disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。"
@ -672,6 +669,13 @@ popularPosts: "人気の投稿"
shareWithNote: "ノートで共有"
emailNotConfiguredWarning: "メールアドレスの設定がされていません。"
ratio: "比率"
secureMode: "セキュアモード (Authorized Fetch)"
instanceSecurity: "インスタンスのセキュリティー"
secureModeInfo: "他のインスタンスからリクエストするときに、証明を付けなければ返送しません。他のインスタンスの設定ファイルでsignToActivityPubGetはtrueにしてください。"
privateMode: "非公開モード"
privateModeInfo: "有効にして、許可されているインスタンスのみがリクエストできます。すべてのノートが公開に非表示にします。"
allowedInstances: "許可されたインスタンス"
allowedInstancesDescription: "許可したいインスタンスのホストを改行で区切って設定します。非公開モードだけで有効です。"
previewNoteText: "本文をプレビュー"
customCss: "カスタムCSS"
customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。"
@ -1273,13 +1277,3 @@ _deck:
list: "リスト"
mentions: "あなた宛て"
direct: "ダイレクト"
_services:
_discord:
connected: "Discord: @{username}#{discriminator} を、FoundKey: @{mkUsername} に接続しました!"
disconnected: "Discordの連携を解除しました :v:"
_twitter:
connected: "Twitter: @{twitterUserName} を、FoundKey: @{userName} に接続しました!"
disconnected: "Twitterの連携を解除しました :v:"
_github:
connected: "GitHub: @{login} を、FoundKey: @{userName} に接続しました!"
disconnected: "GitHubの連携を解除しました :v:"

View file

@ -288,7 +288,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "ページ"
integration: "連携"
enableLocalTimeline: "ローカルタイムラインを使えるようにする"
enableGlobalTimeline: "グローバルタイムラインを使えるようにする"
disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。"

View file

@ -315,9 +315,6 @@ dayX: "{day}일"
monthX: "{month}월"
yearX: "{year}년"
pages: "페이지"
integration: "연동"
connectService: "계정 연동"
disconnectService: "계정 연동 해제"
enableLocalTimeline: "로컬 타임라인 활성화"
enableGlobalTimeline: "글로벌 타임라인 활성화"
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
@ -716,7 +713,6 @@ receiveAnnouncementFromInstance: "이 인스턴스의 알림을 이메일로 수
emailNotification: "메일 알림"
publish: "게시"
inChannelSearch: "채널에서 검색"
useReactionPickerForContextMenu: "우클릭하여 리액션 선택기 열기"
typingUsers: "{users} 님이 입력하고 있어요.."
jumpToSpecifiedDate: "특정 날짜로 이동"
showingPastTimeline: "과거의 타임라인을 표시하고 있어요"

View file

@ -298,9 +298,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Strony"
integration: "Integracja"
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\

View file

@ -311,9 +311,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\

View file

@ -304,9 +304,6 @@ dayX: "{day} день"
monthX: "{month} месяц"
yearX: "{year} год"
pages: "Страницы"
integration: "Интеграция"
connectService: "Подключиться"
disconnectService: "Отключиться"
enableLocalTimeline: "Включить локальную ленту"
enableGlobalTimeline: "Включить глобальную ленту"
disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам,\

View file

@ -305,9 +305,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\

View file

@ -305,9 +305,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Сторінки"
integration: "Інтеграція"
connectService: "Під’єднати"
disconnectService: "Відключитися"
enableLocalTimeline: "Увімкнути локальну стрічку"
enableGlobalTimeline: "Увімкнути глобальну стрічку"
disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх\

View file

@ -305,9 +305,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\

View file

@ -284,9 +284,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "页面"
integration: "关联"
connectService: "连接"
disconnectService: "断开连接"
enableLocalTimeline: "启用本地时间线功能"
enableGlobalTimeline: "启用全局时间线"
disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。"

View file

@ -284,9 +284,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "頁面"
integration: "整合"
connectService: "己連結"
disconnectService: "己斷開 "
enableLocalTimeline: "開啟本地時間軸"
enableGlobalTimeline: "啟用公開時間軸"
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"

View file

@ -0,0 +1,17 @@
export class allowlistSecureMode1626733991004 {
name = 'allowlistSecureMode1626733991004';
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "allowedHosts" character varying(256) [] default '{}'`);
await queryRunner.query(`ALTER TABLE "meta" ADD "secureMode" bool default false`);
await queryRunner.query(`ALTER TABLE "meta" ADD "privateMode" bool default false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "allowedHosts"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "secureMode"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "privateMode"`);
}
}

View file

@ -29,7 +29,6 @@
"ajv": "8.11.0",
"archiver": "5.3.1",
"autobind-decorator": "2.4.0",
"autwh": "0.1.0",
"aws-sdk": "2.1165.0",
"bcryptjs": "2.4.3",
"blurhash": "1.1.5",
@ -146,7 +145,7 @@
"@types/node": "18.7.16",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.5",
"@types/oauth": "^0.9.1",
"@types/opentype.js": "^1.3.4",
"@types/pg": "^8.6.5",
"@types/pug": "2.0.6",
"@types/punycode": "2.1.0",

View file

@ -12,3 +12,4 @@ export const DB_MAX_NOTE_TEXT_LENGTH = 8192;
* Surrogate pairs count as one
*/
export const DB_MAX_IMAGE_COMMENT_LENGTH = 2048;

View file

@ -82,6 +82,21 @@ export class Meta {
})
public blockedHosts: string[];
@Column('boolean', {
default: false
})
public secureMode: boolean;
@Column('boolean', {
default: false
})
public privateMode: boolean;
@Column('varchar', {
length: 256, array: true, default: '{}'
})
public allowedHosts: string[];
@Column('varchar', {
length: 512, array: true, default: '{/featured,/channels,/explore,/pages,/about-foundkey}',
})

View file

@ -38,6 +38,11 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
return `Blocked request: ${host}`;
}
// Only permitted instances if in private mode.
if (meta.privateMode && !meta.allowedHosts.includes(host)) {
return `Blocked request: ${host}`;
}
const keyIdLower = signature.keyId.toLowerCase();
if (keyIdLower.startsWith('acct:')) {
return `Old keyId is no longer supported. ${keyIdLower}`;

View file

@ -0,0 +1,69 @@
import config from '@/config/index.js';
import { IncomingMessage } from 'http';
import { fetchMeta } from '@/misc/fetch-meta.js';
import httpSignature from '@peertube/http-signature';
import { URL } from 'url';
import { toPuny } from '@/misc/convert-host.js';
import DbResolver from '@/remote/activitypub/db-resolver.js';
import { getApId } from '@/remote/activitypub/type.js';
export default async function checkFetch(req: IncomingMessage): Promise<number> {
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
let signature;
try {
signature = httpSignature.parseRequest(req, { 'headers': [] });
} catch (e) {
return 401;
}
const keyId = new URL(signature.keyId);
const host = toPuny(keyId.hostname);
if (meta.blockedHosts.includes(host)) {
return 403;
}
if (meta.privateMode && host !== config.host && !meta.allowedHosts.includes(host)) {
return 403;
}
const keyIdLower = signature.keyId.toLowerCase();
if (keyIdLower.startsWith('acct:')) {
// Old keyId is no longer supported.
return 401;
}
const dbResolver = new DbResolver();
// Get user from database based on HTTP-Signature keyId
let authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId);
// If keyid is unknown, try resolving it
if (authUser == null) {
try {
keyId.hash = '';
authUser = await dbResolver.getAuthUserFromApId(getApId(keyId.toString()));
} catch (e) {
return 403;
}
}
if (authUser?.key == null) {
return 403;
}
if (authUser.user.host !== host) {
return 403;
}
// HTTP-Signature validation
const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
if (!httpSignatureValidated) {
return 403;
}
}
return 200;
}

View file

@ -13,8 +13,10 @@ export const renderLike = async (noteReaction: NoteReaction, note: Note) => {
id: `${config.url}/likes/${noteReaction.id}`,
actor: `${config.url}/users/${noteReaction.userId}`,
object: note.uri ? note.uri : `${config.url}/notes/${noteReaction.noteId}`,
content: reaction,
_misskey_reaction: reaction,
... (reaction !== '\u2b50' ? {
content: reaction,
_misskey_reaction: reaction,
} : {}),
} as any;
if (reaction.startsWith(':')) {

View file

@ -80,7 +80,11 @@ export default class Resolver {
throw new Error('Instance is blocked');
}
if (!this.user) {
if (meta.privateMode && config.host !== host && !meta.allowedHosts.includes(host)) {
throw new Error('Instance is not allowed');
}
if (config.signToActivityPubGet && !this.user) {
this.user = await getInstanceActor();
}

View file

@ -9,11 +9,14 @@ import renderKey from '@/remote/activitypub/renderer/key.js';
import { renderPerson } from '@/remote/activitypub/renderer/person.js';
import renderEmoji from '@/remote/activitypub/renderer/emoji.js';
import { inbox as processInbox } from '@/queue/index.js';
import { isSelfHost } from '@/misc/convert-host.js';
import { isSelfHost, toPuny } from '@/misc/convert-host.js';
import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js';
import { ILocalUser, User } from '@/models/entities/user.js';
import { renderLike } from '@/remote/activitypub/renderer/like.js';
import { getUserKeypair } from '@/misc/keypair-store.js';
import checkFetch from '@/remote/activitypub/check-fetch.js';
import { getInstanceActor } from '@/services/instance-actor.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import renderFollow from '@/remote/activitypub/renderer/follow.js';
import Outbox, { packActivity } from './activitypub/outbox.js';
import Followers from './activitypub/followers.js';
@ -66,6 +69,12 @@ router.post('/users/:user/inbox', json(), inbox);
router.get('/notes/:note', async (ctx, next) => {
if (!isActivityPubReq(ctx)) return await next();
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const note = await Notes.findOneBy({
id: ctx.params.note,
visibility: In(['public' as const, 'home' as const]),
@ -78,7 +87,7 @@ router.get('/notes/:note', async (ctx, next) => {
}
// redirect if remote
if (note.userHost != null) {
if (note.userHost !== null) {
if (note.uri == null || isSelfHost(note.userHost)) {
ctx.status = 500;
return;
@ -88,7 +97,13 @@ router.get('/notes/:note', async (ctx, next) => {
}
ctx.body = renderActivity(await renderNote(note, false));
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
});
@ -103,6 +118,12 @@ router.get('/notes/:note/activity', async ctx => {
return;
}
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const note = await Notes.findOneBy({
id: ctx.params.note,
userHost: IsNull(),
@ -116,7 +137,12 @@ router.get('/notes/:note/activity', async ctx => {
}
ctx.body = renderActivity(await packActivity(note));
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
});
@ -134,6 +160,20 @@ router.get('/users/:user/collections/featured', Featured);
// publickey
router.get('/users/:user/publickey', async ctx => {
const instanceActor = await getInstanceActor();
if (ctx.params.user === instanceActor.id) {
ctx.body = renderActivity(renderKey(instanceActor, await getUserKeypair(instanceActor.id)));
ctx.set('Cache-Control', 'public, max-age=180');
setResponseType(ctx);
return;
}
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const userId = ctx.params.user;
const user = await Users.findOneBy({
@ -150,7 +190,12 @@ router.get('/users/:user/publickey', async ctx => {
if (Users.isLocalUser(user)) {
ctx.body = renderActivity(renderKey(user, keypair));
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
} else {
ctx.status = 400;
@ -165,13 +210,30 @@ async function userInfo(ctx: Router.RouterContext, user: User | null): Promise<v
}
ctx.body = renderActivity(await renderPerson(user as ILocalUser));
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
}
router.get('/users/:user', async (ctx, next) => {
if (!isActivityPubReq(ctx)) return await next();
const instanceActor = await getInstanceActor();
if (ctx.params.user === instanceActor.id) {
await userInfo(ctx, instanceActor);
return;
}
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const userId = ctx.params.user;
const user = await Users.findOneBy({
@ -186,6 +248,18 @@ router.get('/users/:user', async (ctx, next) => {
router.get('/@:user', async (ctx, next) => {
if (!isActivityPubReq(ctx)) return await next();
if (ctx.params.user === 'instance.actor') {
const instanceActor = await getInstanceActor();
await userInfo(ctx, instanceActor);
return;
}
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const user = await Users.findOneBy({
usernameLower: ctx.params.user.toLowerCase(),
host: IsNull(),
@ -195,8 +269,19 @@ router.get('/@:user', async (ctx, next) => {
await userInfo(ctx, user);
});
router.get('/actor', async (ctx, next) => {
const instanceActor = await getInstanceActor();
await userInfo(ctx, instanceActor);
});
// emoji
router.get('/emojis/:emoji', async ctx => {
const verify = await checkFetch(ctx.req);
if (verify != 200) {
ctx.status = verify;
return;
}
const emoji = await Emojis.findOneBy({
host: IsNull(),
name: ctx.params.emoji,
@ -208,12 +293,23 @@ router.get('/emojis/:emoji', async ctx => {
}
ctx.body = renderActivity(await renderEmoji(emoji));
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
});
// like
router.get('/likes/:like', async ctx => {
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const reaction = await NoteReactions.findOneBy({ id: ctx.params.like });
if (reaction == null) {
@ -232,12 +328,22 @@ router.get('/likes/:like', async ctx => {
}
ctx.body = renderActivity(await renderLike(reaction, note));
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
});
// follow
router.get('/follows/:follower/:followee', async ctx => {
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
// This may be used before the follow is completed, so we do not
// check if the following exists.
@ -258,7 +364,12 @@ router.get('/follows/:follower/:followee', async ctx => {
}
ctx.body = renderActivity(renderFollow(follower, followee));
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
});

View file

@ -5,9 +5,20 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js';
import renderNote from '@/remote/activitypub/renderer/note.js';
import { Users, Notes, UserNotePinings } from '@/models/index.js';
import checkFetch from '@/remote/activitypub/check-fetch.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { setResponseType } from '../activitypub.js';
import { IsNull } from 'typeorm';
import checkFetch from '@/remote/activitypub/check-fetch.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
export default async (ctx: Router.RouterContext) => {
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const userId = ctx.params.user;
const user = await Users.findOneBy({
@ -36,6 +47,12 @@ export default async (ctx: Router.RouterContext) => {
);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'public, max-age=180');
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
setResponseType(ctx);
};

View file

@ -9,12 +9,20 @@ import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js';
import { Users, Followings, UserProfiles } from '@/models/index.js';
import { Following } from '@/models/entities/following.js';
import { setResponseType } from '../activitypub.js';
import checkFetch from '@/remote/activitypub/check-fetch.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
export default async (ctx: Router.RouterContext) => {
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const userId = ctx.params.user;
const cursor = ctx.request.query.cursor;
if (cursor != null && typeof cursor !== 'string') {
if (cursor !== null && typeof cursor !== 'string') {
ctx.status = 400;
return;
}
@ -89,7 +97,12 @@ export default async (ctx: Router.RouterContext) => {
// index page
const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'public, max-age=180');
setResponseType(ctx);
}
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
};

View file

@ -9,12 +9,20 @@ import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js';
import { Users, Followings, UserProfiles } from '@/models/index.js';
import { Following } from '@/models/entities/following.js';
import { setResponseType } from '../activitypub.js';
import checkFetch from '@/remote/activitypub/check-fetch.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
export default async (ctx: Router.RouterContext) => {
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const userId = ctx.params.user;
const cursor = ctx.request.query.cursor;
if (cursor != null && typeof cursor !== 'string') {
if (cursor !== null && typeof cursor !== 'string') {
ctx.status = 400;
return;
}
@ -89,7 +97,12 @@ export default async (ctx: Router.RouterContext) => {
// index page
const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'public, max-age=180');
setResponseType(ctx);
}
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
};

View file

@ -14,25 +14,33 @@ import { Note } from '@/models/entities/note.js';
import { isPureRenote } from '@/misc/renote.js';
import { makePaginationQuery } from '../api/common/make-pagination-query.js';
import { setResponseType } from '../activitypub.js';
import checkFetch from '@/remote/activitypub/check-fetch.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
export default async (ctx: Router.RouterContext) => {
const verify = await checkFetch(ctx.req);
if (verify !== 200) {
ctx.status = verify;
return;
}
const userId = ctx.params.user;
const sinceId = ctx.request.query.since_id;
if (sinceId != null && typeof sinceId !== 'string') {
if (sinceId !== null && typeof sinceId !== 'string') {
ctx.status = 400;
return;
}
const untilId = ctx.request.query.until_id;
if (untilId != null && typeof untilId !== 'string') {
if (untilId !== null && typeof untilId !== 'string') {
ctx.status = 400;
return;
}
const page = ctx.request.query.page === 'true';
if (countIf(x => x != null, [sinceId, untilId]) > 1) {
if (countIf(x => x !== null, [sinceId, untilId]) > 1) {
ctx.status = 400;
return;
}
@ -90,9 +98,15 @@ export default async (ctx: Router.RouterContext) => {
`${partOf}?page=true&since_id=000000000000000000000000`,
);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'public, max-age=180');
setResponseType(ctx);
}
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
ctx.set('Cache-Control', 'no-store');
} else {
ctx.set('Cache-Control', 'public, max-age=180');
}
};
/**

View file

@ -7,6 +7,8 @@ import { limiter } from './limiter.js';
import endpoints, { IEndpointMeta } from './endpoints.js';
import { ApiError } from './error.js';
import { apiLogger } from './logger.js';
import { AccessToken } from '@/models/entities/access-token.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
export default async (endpoint: string, user: CacheableLocalUser | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => {
const isSecure = user != null && token == null;
@ -61,6 +63,17 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi
throw new ApiError('ACCESS_DENIED', 'This operation requires privileges which this token does not grant.');
}
// private mode
const meta = await fetchMeta();
if (meta.privateMode && ep.meta.requireCredentialPrivateMode && user == null) {
throw new ApiError({
message: 'Credential required.',
code: 'CREDENTIAL_REQUIRED',
id: '1384574d-a912-4b81-8601-c7b1c4085df1',
httpStatusCode: 401
});
}
// Cast non JSON input
if ((ep.meta.requireFile || ctx?.method === 'GET') && ep.params.properties) {
for (const k of Object.keys(ep.params.properties)) {

View file

@ -683,6 +683,12 @@ export interface IEndpointMeta {
*/
readonly secure?: boolean;
/**
* If in private mode, whether credentials are required when making a request to this endpoint.
* If omitted, this is interpreted as false.
*/
readonly requireCredentialPrivateMode?: boolean;
/**
*
*

View file

@ -102,18 +102,6 @@ export const meta = {
type: 'boolean',
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,
},
translatorAvailable: {
type: 'boolean',
optional: false, nullable: false,
@ -150,6 +138,22 @@ export const meta = {
optional: false, nullable: false,
},
},
allowedHosts: {
type: 'array',
optional: true, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
privateMode: {
type: 'boolean',
optional: false, nullable: false,
},
secureMode: {
type: 'boolean',
optional: false, nullable: false,
},
hcaptchaSecretKey: {
type: 'string',
optional: true, nullable: true,
@ -163,30 +167,6 @@ export const meta = {
optional: true, 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, nullable: true,
@ -335,15 +315,12 @@ export default define(meta, paramDef, async (ps, me) => {
pinnedUsers: instance.pinnedUsers,
hiddenTags: instance.hiddenTags,
blockedHosts: instance.blockedHosts,
allowedHosts: instance.allowedHosts,
privateMode: instance.privateMode,
secureMode: instance.secureMode,
hcaptchaSecretKey: instance.hcaptchaSecretKey,
recaptchaSecretKey: instance.recaptchaSecretKey,
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,

View file

@ -26,6 +26,11 @@ export const paramDef = {
blockedHosts: { type: 'array', nullable: true, items: {
type: 'string',
} },
allowedHosts: { type: 'array', nullable: true, items: {
type: 'string',
} },
secureMode: { type: 'boolean', nullable: true },
privateMode: { type: 'boolean', nullable: true },
themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
bannerUrl: { type: 'string', nullable: true },
iconUrl: { type: 'string', nullable: true },
@ -60,15 +65,6 @@ export const paramDef = {
deeplAuthKey: { type: 'string', nullable: true },
libreTranslateAuthKey: { type: 'string', nullable: true },
libreTranslateEndpoint: { 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' },
@ -130,6 +126,18 @@ export default define(meta, paramDef, async (ps, me) => {
set.themeColor = ps.themeColor;
}
if (Array.isArray(ps.allowedHosts)) {
set.allowedHosts = ps.allowedHosts.filter(Boolean);
}
if (typeof ps.privateMode === 'boolean') {
set.privateMode = ps.privateMode;
}
if (typeof ps.secureMode === 'boolean') {
set.secureMode = ps.secureMode;
}
if (ps.bannerUrl !== undefined) {
set.bannerUrl = ps.bannerUrl;
}
@ -230,42 +238,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;
}

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['meta'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -5,6 +5,7 @@ export const meta = {
tags: ['channels'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['channels'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'object',

View file

@ -8,6 +8,7 @@ export const meta = {
tags: ['notes', 'channels'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts', 'users'],
requireCredentialPrivateMode: true,
res: getJsonSchema(activeUsersChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts'],
requireCredentialPrivateMode: true,
res: getJsonSchema(apRequestChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts', 'drive'],
requireCredentialPrivateMode: true,
res: getJsonSchema(driveChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts'],
requireCredentialPrivateMode: true,
res: getJsonSchema(federationChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts', 'hashtags'],
requireCredentialPrivateMode: true,
res: getJsonSchema(hashtagChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts'],
requireCredentialPrivateMode: true,
res: getJsonSchema(instanceChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts', 'notes'],
requireCredentialPrivateMode: true,
res: getJsonSchema(notesChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../../define.js';
export const meta = {
tags: ['charts', 'drive', 'users'],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserDriveChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../../define.js';
export const meta = {
tags: ['charts', 'users', 'following'],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserFollowingChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../../define.js';
export const meta = {
tags: ['charts', 'users', 'notes'],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserNotesChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../../define.js';
export const meta = {
tags: ['charts', 'users', 'reactions'],
requireCredentialPrivateMode: true,
res: getJsonSchema(perUserReactionsChart.schema),

View file

@ -4,6 +4,7 @@ import define from '../../define.js';
export const meta = {
tags: ['charts', 'users'],
requireCredentialPrivateMode: true,
res: getJsonSchema(usersChart.schema),

View file

@ -10,6 +10,7 @@ export const meta = {
tags: ['account', 'notes', 'clips'],
requireCredential: false,
requireCredentialPrivateMode: true,
kind: 'read:account',

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['clips', 'account'],
requireCredential: false,
requireCredentialPrivateMode: true,
kind: 'read:account',

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['federation'],
requireCredential: true,
requireCredentialPrivateMode: true,
requireAdmin: true,
res: {

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['federation'],
requireCredential: true,
requireCredentialPrivateMode: true,
requireAdmin: true,
res: {

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['federation'],
requireCredential: true,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['federation'],
requireCredential: true,
requireCredentialPrivateMode: true,
res: {
oneOf: [{

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['federation'],
requireCredential: true,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['gallery'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -5,6 +5,7 @@ export const meta = {
tags: ['gallery'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
export const meta = {
tags: ['gallery'],
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -6,6 +6,7 @@ export const meta = {
tags: ['gallery'],
requireCredential: false,
requireCredentialPrivateMode: true,
errors: ['NO_SUCH_POST'],

View file

@ -7,6 +7,7 @@ export const meta = {
tags: ['meta'],
requireCredential: false,
requireCredentialPrivateMode: true,
} as const;
export const paramDef = {

View file

@ -5,6 +5,7 @@ export const meta = {
tags: ['hashtags'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -5,6 +5,7 @@ export const meta = {
tags: ['hashtags'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -7,6 +7,7 @@ export const meta = {
tags: ['hashtags'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'object',

View file

@ -25,6 +25,7 @@ export const meta = {
tags: ['hashtags'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -5,6 +5,7 @@ import define from '../../define.js';
export const meta = {
requireCredential: false,
requireCredentialPrivateMode: true,
tags: ['hashtags', 'users'],

View file

@ -170,18 +170,6 @@ export const meta = {
type: 'boolean',
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,
},
translatorAvailable: {
type: 'boolean',
optional: false, nullable: false,
@ -222,18 +210,6 @@ export const meta = {
type: 'boolean',
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: true, nullable: false,
@ -246,6 +222,16 @@ export const meta = {
},
},
},
secureMode: {
type: 'boolean',
optional: true, nullable: false,
default: false,
},
privateMode: {
type: 'boolean',
optional: true, nullable: false,
default: false,
},
},
},
} as const;
@ -264,7 +250,7 @@ export const paramDef = {
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, me) => {
export default define(meta, paramDef, async (ps, me): Promise<Record<string, any>> => {
const instance = await fetchMeta(true);
const emojis = await Emojis.find({
@ -281,7 +267,7 @@ export default define(meta, paramDef, async (ps, me) => {
},
});
return {
const response: Record<string, any> = {
maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail,
@ -292,6 +278,10 @@ export default define(meta, paramDef, async (ps, me) => {
description: instance.description,
langs: instance.langs,
tosUrl: instance.ToSUrl,
secureMode: instance.secureMode,
privateMode: instance.privateMode,
disableRegistration: instance.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline,
@ -309,27 +299,30 @@ export default define(meta, paramDef, async (ps, me) => {
backgroundImageUrl: instance.backgroundImageUrl,
logoImageUrl: instance.logoImageUrl,
maxNoteTextLength: config.maxNoteTextLength,
emojis: await Emojis.packMany(emojis),
emojis: instance.privateMode && !me ? [] : await Emojis.packMany(emojis),
defaultLightTheme: instance.defaultLightTheme,
defaultDarkTheme: instance.defaultDarkTheme,
enableEmail: instance.enableEmail,
enableTwitterIntegration: instance.enableTwitterIntegration,
enableGithubIntegration: instance.enableGithubIntegration,
enableDiscordIntegration: instance.enableDiscordIntegration,
translatorAvailable: translatorAvailable(instance),
pinnedPages: instance.pinnedPages,
pinnedClipId: instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles,
requireSetup: (await Users.countBy({
host: IsNull(),
})) === 0,
...(ps.detail ? {
pinnedPages: instance.privateMode && !me ? [] : instance.pinnedPages,
pinnedClipId: instance.privateMode && !me ? [] : instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles,
requireSetup: (await Users.countBy({
host: IsNull(),
})) === 0,
} : {}),
};
proxyAccountName: instance.proxyAccountId ? (await Users.pack(instance.proxyAccountId).catch(() => null))?.username : null,
features: {
if (ps.detail) {
if (!instance.privateMode || me) {
const proxyAccount = instance.proxyAccountId ? await Users.pack(instance.proxyAccountId).catch(() => null) : null;
response.proxyAccountName = proxyAccount ? proxyAccount.username : null;
}
response.features = {
registration: !instance.disableRegistration,
localTimeLine: !instance.disableLocalTimeline,
globalTimeLine: !instance.disableGlobalTimeline,
@ -338,11 +331,10 @@ 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: true,
miauth: true,
},
};
};
}
return response;
});

View file

@ -5,6 +5,7 @@ import { makePaginationQuery } from '../common/make-pagination-query.js';
export const meta = {
tags: ['notes'],
requireCredentialPrivateMode: true,
res: {
type: 'array',
optional: false, nullable: false,

View file

@ -9,6 +9,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
description: 'Get a list of children of a notes. Children includes replies as well as quote renotes that quote the respective post. A post will not be duplicated if it is a reply and a quote of a note in this thread. For depths larger than 1 the threading has to be computed by the client.',
@ -21,7 +22,7 @@ export const meta = {
ref: 'Note',
},
},
} as const;
};
export const paramDef = {
type: 'object',

View file

@ -8,6 +8,7 @@ export const meta = {
tags: ['clips', 'notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -8,6 +8,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -8,6 +8,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -13,6 +13,7 @@ import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-q
export const meta = {
tags: ['notes'],
requireCredentialPrivateMode: true,
res: {
type: 'array',
optional: false, nullable: false,

View file

@ -15,6 +15,7 @@ import { generateMutedRenotesQuery } from '../../common/generated-muted-renote-q
export const meta = {
tags: ['notes'],
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -9,6 +9,7 @@ export const meta = {
tags: ['notes', 'reactions'],
requireCredential: false,
requireCredentialPrivateMode: true,
allowGet: true,
cacheSec: 60,

View file

@ -11,6 +11,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -9,6 +9,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -10,6 +10,7 @@ import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
export const meta = {
tags: ['notes', 'hashtags'],
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -12,6 +12,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -7,6 +7,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'object',

View file

@ -43,6 +43,7 @@ export const meta = {
tags: ['notes'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'object',

View file

@ -5,6 +5,7 @@ export const meta = {
tags: ['pages'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -8,6 +8,7 @@ export const meta = {
tags: ['pages'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'object',

View file

@ -9,6 +9,7 @@ export const meta = {
tags: ['users'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -4,6 +4,7 @@ import define from '../define.js';
export const meta = {
requireCredential: false,
requireCredentialPrivateMode: true,
tags: ['meta'],
} as const;

View file

@ -4,6 +4,7 @@ import define from '../define.js';
export const meta = {
requireCredential: false,
requireCredentialPrivateMode: true,
tags: ['meta'],

View file

@ -8,6 +8,7 @@ export const meta = {
tags: ['users'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',

View file

@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
export const meta = {
tags: ['users', 'clips'],
requireCredentialPrivateMode: true,
description: 'Show all clips this user owns.',

View file

@ -9,6 +9,7 @@ export const meta = {
tags: ['users'],
requireCredential: false,
requireCredentialPrivateMode: true,
description: 'Show everyone that follows this user.',

View file

@ -9,6 +9,7 @@ export const meta = {
tags: ['users'],
requireCredential: false,
requireCredentialPrivateMode: true,
description: 'Show everyone that this user is following.',

View file

@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../../common/make-pagination-query.js';
export const meta = {
tags: ['users', 'gallery'],
requireCredentialPrivateMode: true,
description: 'Show all gallery posts by the given user.',

View file

@ -11,6 +11,7 @@ import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
export const meta = {
tags: ['users', 'notes'],
requireCredentialPrivateMode: true,
description: 'Show all notes that this user created.',
res: {

View file

@ -4,6 +4,7 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
export const meta = {
tags: ['users', 'pages'],
requireCredentialPrivateMode: true,
description: 'Show all pages this user created.',

View file

@ -8,6 +8,7 @@ export const meta = {
tags: ['users', 'reactions'],
requireCredential: false,
requireCredentialPrivateMode: true,
description: 'Show all reactions this user made.',

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