forked from FoundKeyGang/FoundKey
Trash integrations lmao
This commit is contained in:
parent
6aa52ecbfa
commit
58002cac58
39 changed files with 0 additions and 1363 deletions
|
@ -324,9 +324,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "الصفحات"
|
pages: "الصفحات"
|
||||||
integration: "التكامل"
|
|
||||||
connectService: "اتصل"
|
|
||||||
disconnectService: "اقطع الاتصال"
|
|
||||||
enableLocalTimeline: "تفعيل الخيط المحلي"
|
enableLocalTimeline: "تفعيل الخيط المحلي"
|
||||||
enableGlobalTimeline: "تفعيل الخيط الزمني الشامل"
|
enableGlobalTimeline: "تفعيل الخيط الزمني الشامل"
|
||||||
disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من الوصول إلى كل الخيوط الزمنية\
|
disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من الوصول إلى كل الخيوط الزمنية\
|
||||||
|
|
|
@ -339,9 +339,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "পৃষ্ঠা"
|
pages: "পৃষ্ঠা"
|
||||||
integration: "ইন্টিগ্রেশন"
|
|
||||||
connectService: "সংযুক্ত করুন"
|
|
||||||
disconnectService: "সংযোগ বিচ্ছিন্ন করুন"
|
|
||||||
enableLocalTimeline: "স্থানীয় টাইমলাইন চালু করুন"
|
enableLocalTimeline: "স্থানীয় টাইমলাইন চালু করুন"
|
||||||
enableGlobalTimeline: "গ্লোবাল টাইমলাইন চালু করুন"
|
enableGlobalTimeline: "গ্লোবাল টাইমলাইন চালু করুন"
|
||||||
disablingTimelinesInfo: "আপনি এই টাইমলাইনগুলি বন্ধ করলেও প্রশাসক এবং মডারেটররা এই\
|
disablingTimelinesInfo: "আপনি এই টাইমলাইনগুলি বন্ধ করলেও প্রশাসক এবং মডারেটররা এই\
|
||||||
|
|
|
@ -307,9 +307,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Stránky"
|
pages: "Stránky"
|
||||||
integration: "Integrace"
|
|
||||||
connectService: "Připojit"
|
|
||||||
disconnectService: "Odpojit"
|
|
||||||
enableLocalTimeline: "Povolit lokální čas"
|
enableLocalTimeline: "Povolit lokální čas"
|
||||||
enableGlobalTimeline: "Povolit globální čas"
|
enableGlobalTimeline: "Povolit globální čas"
|
||||||
registration: "Registrace"
|
registration: "Registrace"
|
||||||
|
|
|
@ -350,9 +350,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Seiten"
|
pages: "Seiten"
|
||||||
integration: "Integration"
|
|
||||||
connectService: "Verbinden"
|
|
||||||
disconnectService: "Trennen"
|
|
||||||
enableLocalTimeline: "Lokale Chronik aktivieren"
|
enableLocalTimeline: "Lokale Chronik aktivieren"
|
||||||
enableGlobalTimeline: "Globale Chronik aktivieren"
|
enableGlobalTimeline: "Globale Chronik aktivieren"
|
||||||
disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle\
|
disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle\
|
||||||
|
|
|
@ -340,9 +340,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Pages"
|
pages: "Pages"
|
||||||
integration: "Integration"
|
|
||||||
connectService: "Connect"
|
|
||||||
disconnectService: "Disconnect"
|
|
||||||
enableLocalTimeline: "Enable local timeline"
|
enableLocalTimeline: "Enable local timeline"
|
||||||
enableGlobalTimeline: "Enable global timeline"
|
enableGlobalTimeline: "Enable global timeline"
|
||||||
disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all\
|
disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all\
|
||||||
|
@ -1511,13 +1508,3 @@ _deck:
|
||||||
list: "List"
|
list: "List"
|
||||||
mentions: "Mentions"
|
mentions: "Mentions"
|
||||||
direct: "Direct notes"
|
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."
|
|
||||||
|
|
|
@ -342,9 +342,6 @@ dayX: "Día {day}"
|
||||||
monthX: "Mes {month}"
|
monthX: "Mes {month}"
|
||||||
yearX: "Año {year}"
|
yearX: "Año {year}"
|
||||||
pages: "Páginas"
|
pages: "Páginas"
|
||||||
integration: "Integración"
|
|
||||||
connectService: "Conectar"
|
|
||||||
disconnectService: "Desconectar"
|
|
||||||
enableLocalTimeline: "Habilitar linea de tiempo local"
|
enableLocalTimeline: "Habilitar linea de tiempo local"
|
||||||
enableGlobalTimeline: "Habilitar linea de tiempo global"
|
enableGlobalTimeline: "Habilitar linea de tiempo global"
|
||||||
disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia\
|
disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia\
|
||||||
|
|
|
@ -342,9 +342,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Pages"
|
pages: "Pages"
|
||||||
integration: "Intégrations"
|
|
||||||
connectService: "Connexion"
|
|
||||||
disconnectService: "Déconnexion"
|
|
||||||
enableLocalTimeline: "Activer le fil local"
|
enableLocalTimeline: "Activer le fil local"
|
||||||
enableGlobalTimeline: "Activer le fil global"
|
enableGlobalTimeline: "Activer le fil global"
|
||||||
disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s\
|
disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s\
|
||||||
|
|
|
@ -341,9 +341,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Halaman"
|
pages: "Halaman"
|
||||||
integration: "Integrasi"
|
|
||||||
connectService: "Sambungkan"
|
|
||||||
disconnectService: "Putuskan"
|
|
||||||
enableLocalTimeline: "Nyalakan linimasa lokal"
|
enableLocalTimeline: "Nyalakan linimasa lokal"
|
||||||
enableGlobalTimeline: "Nyalakan linimasa global"
|
enableGlobalTimeline: "Nyalakan linimasa global"
|
||||||
disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa\
|
disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa\
|
||||||
|
|
|
@ -335,9 +335,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Pagine"
|
pages: "Pagine"
|
||||||
integration: "App collegate"
|
|
||||||
connectService: "Connessione"
|
|
||||||
disconnectService: "Disconnessione "
|
|
||||||
enableLocalTimeline: "Abilita Timeline locale"
|
enableLocalTimeline: "Abilita Timeline locale"
|
||||||
enableGlobalTimeline: "Abilita Timeline federata"
|
enableGlobalTimeline: "Abilita Timeline federata"
|
||||||
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e\
|
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e\
|
||||||
|
|
|
@ -317,9 +317,6 @@ dayX: "{day}日"
|
||||||
monthX: "{month}月"
|
monthX: "{month}月"
|
||||||
yearX: "{year}年"
|
yearX: "{year}年"
|
||||||
pages: "ページ"
|
pages: "ページ"
|
||||||
integration: "連携"
|
|
||||||
connectService: "接続する"
|
|
||||||
disconnectService: "切断する"
|
|
||||||
enableLocalTimeline: "ローカルタイムラインを有効にする"
|
enableLocalTimeline: "ローカルタイムラインを有効にする"
|
||||||
enableGlobalTimeline: "グローバルタイムラインを有効にする"
|
enableGlobalTimeline: "グローバルタイムラインを有効にする"
|
||||||
disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。"
|
disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。"
|
||||||
|
@ -1449,13 +1446,3 @@ _deck:
|
||||||
list: "リスト"
|
list: "リスト"
|
||||||
mentions: "あなた宛て"
|
mentions: "あなた宛て"
|
||||||
direct: "ダイレクト"
|
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:"
|
|
||||||
|
|
|
@ -319,7 +319,6 @@ dayX: "{day}日"
|
||||||
monthX: "{month}月"
|
monthX: "{month}月"
|
||||||
yearX: "{year}年"
|
yearX: "{year}年"
|
||||||
pages: "ページ"
|
pages: "ページ"
|
||||||
integration: "連携"
|
|
||||||
enableLocalTimeline: "ローカルタイムラインを使えるようにする"
|
enableLocalTimeline: "ローカルタイムラインを使えるようにする"
|
||||||
enableGlobalTimeline: "グローバルタイムラインを使えるようにする"
|
enableGlobalTimeline: "グローバルタイムラインを使えるようにする"
|
||||||
disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。"
|
disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。"
|
||||||
|
|
|
@ -315,9 +315,6 @@ dayX: "{day}일"
|
||||||
monthX: "{month}월"
|
monthX: "{month}월"
|
||||||
yearX: "{year}년"
|
yearX: "{year}년"
|
||||||
pages: "페이지"
|
pages: "페이지"
|
||||||
integration: "연동"
|
|
||||||
connectService: "계정 연동"
|
|
||||||
disconnectService: "계정 연동 해제"
|
|
||||||
enableLocalTimeline: "로컬 타임라인 활성화"
|
enableLocalTimeline: "로컬 타임라인 활성화"
|
||||||
enableGlobalTimeline: "글로벌 타임라인 활성화"
|
enableGlobalTimeline: "글로벌 타임라인 활성화"
|
||||||
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
|
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
|
||||||
|
|
|
@ -329,9 +329,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Strony"
|
pages: "Strony"
|
||||||
integration: "Integracja"
|
|
||||||
connectService: "Połącz"
|
|
||||||
disconnectService: "Rozłącz"
|
|
||||||
enableLocalTimeline: "Włącz lokalną oś czasu"
|
enableLocalTimeline: "Włącz lokalną oś czasu"
|
||||||
enableGlobalTimeline: "Włącz globalną oś czasu"
|
enableGlobalTimeline: "Włącz globalną oś czasu"
|
||||||
disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do\
|
disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do\
|
||||||
|
|
|
@ -342,9 +342,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Pagini"
|
pages: "Pagini"
|
||||||
integration: "Integrare"
|
|
||||||
connectService: "Conectează"
|
|
||||||
disconnectService: "Deconectează"
|
|
||||||
enableLocalTimeline: "Activează cronologia locală"
|
enableLocalTimeline: "Activează cronologia locală"
|
||||||
enableGlobalTimeline: "Activeaza cronologia globală"
|
enableGlobalTimeline: "Activeaza cronologia globală"
|
||||||
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate\
|
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate\
|
||||||
|
|
|
@ -335,9 +335,6 @@ dayX: "{day} день"
|
||||||
monthX: "{month} месяц"
|
monthX: "{month} месяц"
|
||||||
yearX: "{year} год"
|
yearX: "{year} год"
|
||||||
pages: "Страницы"
|
pages: "Страницы"
|
||||||
integration: "Интеграция"
|
|
||||||
connectService: "Подключиться"
|
|
||||||
disconnectService: "Отключиться"
|
|
||||||
enableLocalTimeline: "Включить локальную ленту"
|
enableLocalTimeline: "Включить локальную ленту"
|
||||||
enableGlobalTimeline: "Включить глобальную ленту"
|
enableGlobalTimeline: "Включить глобальную ленту"
|
||||||
disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам,\
|
disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам,\
|
||||||
|
|
|
@ -336,9 +336,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Stránky"
|
pages: "Stránky"
|
||||||
integration: "Integrácia"
|
|
||||||
connectService: "Pripojiť"
|
|
||||||
disconnectService: "Odpojiť"
|
|
||||||
enableLocalTimeline: "Povoliť lokálnu časovú os"
|
enableLocalTimeline: "Povoliť lokálnu časovú os"
|
||||||
enableGlobalTimeline: "Povoliť globálnu časovú os"
|
enableGlobalTimeline: "Povoliť globálnu časovú os"
|
||||||
disablingTimelinesInfo: "Administrátori a moderátori majú vždy prístup ku všetkým\
|
disablingTimelinesInfo: "Administrátori a moderátori majú vždy prístup ku všetkým\
|
||||||
|
|
|
@ -336,9 +336,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Сторінки"
|
pages: "Сторінки"
|
||||||
integration: "Інтеграція"
|
|
||||||
connectService: "Під’єднати"
|
|
||||||
disconnectService: "Відключитися"
|
|
||||||
enableLocalTimeline: "Увімкнути локальну стрічку"
|
enableLocalTimeline: "Увімкнути локальну стрічку"
|
||||||
enableGlobalTimeline: "Увімкнути глобальну стрічку"
|
enableGlobalTimeline: "Увімкнути глобальну стрічку"
|
||||||
disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх\
|
disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх\
|
||||||
|
|
|
@ -336,9 +336,6 @@ dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
yearX: "{year}"
|
yearX: "{year}"
|
||||||
pages: "Trang"
|
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ủ"
|
enableLocalTimeline: "Bật bảng tin máy chủ"
|
||||||
enableGlobalTimeline: "Bật bảng tin liên hợp"
|
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\
|
disablingTimelinesInfo: "Quản trị viên và Kiểm duyệt viên luôn có quyền truy cập mọi\
|
||||||
|
|
|
@ -315,9 +315,6 @@ dayX: "{day}日"
|
||||||
monthX: "{month}月"
|
monthX: "{month}月"
|
||||||
yearX: "{year}年"
|
yearX: "{year}年"
|
||||||
pages: "页面"
|
pages: "页面"
|
||||||
integration: "关联"
|
|
||||||
connectService: "连接"
|
|
||||||
disconnectService: "断开连接"
|
|
||||||
enableLocalTimeline: "启用本地时间线功能"
|
enableLocalTimeline: "启用本地时间线功能"
|
||||||
enableGlobalTimeline: "启用全局时间线"
|
enableGlobalTimeline: "启用全局时间线"
|
||||||
disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。"
|
disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。"
|
||||||
|
|
|
@ -315,9 +315,6 @@ dayX: "{day}日"
|
||||||
monthX: "{month}月"
|
monthX: "{month}月"
|
||||||
yearX: "{year}年"
|
yearX: "{year}年"
|
||||||
pages: "頁面"
|
pages: "頁面"
|
||||||
integration: "整合"
|
|
||||||
connectService: "己連結"
|
|
||||||
disconnectService: "己斷開 "
|
|
||||||
enableLocalTimeline: "開啟本地時間軸"
|
enableLocalTimeline: "開啟本地時間軸"
|
||||||
enableGlobalTimeline: "啟用公開時間軸"
|
enableGlobalTimeline: "啟用公開時間軸"
|
||||||
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
|
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
"ajv": "8.11.0",
|
"ajv": "8.11.0",
|
||||||
"archiver": "5.3.1",
|
"archiver": "5.3.1",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autwh": "0.1.0",
|
|
||||||
"aws-sdk": "2.1165.0",
|
"aws-sdk": "2.1165.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "1.1.5",
|
"blurhash": "1.1.5",
|
||||||
|
@ -150,7 +149,6 @@
|
||||||
"@types/node": "18.7.16",
|
"@types/node": "18.7.16",
|
||||||
"@types/node-fetch": "3.0.3",
|
"@types/node-fetch": "3.0.3",
|
||||||
"@types/nodemailer": "6.4.5",
|
"@types/nodemailer": "6.4.5",
|
||||||
"@types/oauth": "^0.9.1",
|
|
||||||
"@types/opentype.js": "^1.3.4",
|
"@types/opentype.js": "^1.3.4",
|
||||||
"@types/pg": "^8.6.5",
|
"@types/pg": "^8.6.5",
|
||||||
"@types/pug": "2.0.6",
|
"@types/pug": "2.0.6",
|
||||||
|
|
|
@ -101,18 +101,6 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
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: {
|
enableServiceWorker: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -166,30 +154,6 @@ export const meta = {
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
format: 'id',
|
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: {
|
summaryProxy: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
|
@ -314,9 +278,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
defaultLightTheme: instance.defaultLightTheme,
|
defaultLightTheme: instance.defaultLightTheme,
|
||||||
defaultDarkTheme: instance.defaultDarkTheme,
|
defaultDarkTheme: instance.defaultDarkTheme,
|
||||||
enableEmail: instance.enableEmail,
|
enableEmail: instance.enableEmail,
|
||||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
|
||||||
enableGithubIntegration: instance.enableGithubIntegration,
|
|
||||||
enableDiscordIntegration: instance.enableDiscordIntegration,
|
|
||||||
enableServiceWorker: instance.enableServiceWorker,
|
enableServiceWorker: instance.enableServiceWorker,
|
||||||
translatorAvailable: instance.deeplAuthKey != null,
|
translatorAvailable: instance.deeplAuthKey != null,
|
||||||
pinnedPages: instance.pinnedPages,
|
pinnedPages: instance.pinnedPages,
|
||||||
|
@ -330,12 +291,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
hcaptchaSecretKey: instance.hcaptchaSecretKey,
|
||||||
recaptchaSecretKey: instance.recaptchaSecretKey,
|
recaptchaSecretKey: instance.recaptchaSecretKey,
|
||||||
proxyAccountId: instance.proxyAccountId,
|
proxyAccountId: instance.proxyAccountId,
|
||||||
twitterConsumerKey: instance.twitterConsumerKey,
|
|
||||||
twitterConsumerSecret: instance.twitterConsumerSecret,
|
|
||||||
githubClientId: instance.githubClientId,
|
|
||||||
githubClientSecret: instance.githubClientSecret,
|
|
||||||
discordClientId: instance.discordClientId,
|
|
||||||
discordClientSecret: instance.discordClientSecret,
|
|
||||||
summalyProxy: instance.summalyProxy,
|
summalyProxy: instance.summalyProxy,
|
||||||
email: instance.email,
|
email: instance.email,
|
||||||
smtpSecure: instance.smtpSecure,
|
smtpSecure: instance.smtpSecure,
|
||||||
|
|
|
@ -58,15 +58,6 @@ export const paramDef = {
|
||||||
summalyProxy: { type: 'string', nullable: true },
|
summalyProxy: { type: 'string', nullable: true },
|
||||||
deeplAuthKey: { type: 'string', nullable: true },
|
deeplAuthKey: { type: 'string', nullable: true },
|
||||||
deeplIsPro: { type: 'boolean' },
|
deeplIsPro: { type: 'boolean' },
|
||||||
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' },
|
enableEmail: { type: 'boolean' },
|
||||||
email: { type: 'string', nullable: true },
|
email: { type: 'string', nullable: true },
|
||||||
smtpSecure: { type: 'boolean' },
|
smtpSecure: { type: 'boolean' },
|
||||||
|
@ -231,42 +222,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
set.summalyProxy = ps.summalyProxy;
|
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) {
|
if (ps.enableEmail !== undefined) {
|
||||||
set.enableEmail = ps.enableEmail;
|
set.enableEmail = ps.enableEmail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,18 +170,6 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
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: {
|
enableServiceWorker: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -226,18 +214,6 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
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: {
|
serviceWorker: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -317,10 +293,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
defaultDarkTheme: instance.defaultDarkTheme,
|
defaultDarkTheme: instance.defaultDarkTheme,
|
||||||
enableEmail: instance.enableEmail,
|
enableEmail: instance.enableEmail,
|
||||||
|
|
||||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
|
||||||
enableGithubIntegration: instance.enableGithubIntegration,
|
|
||||||
enableDiscordIntegration: instance.enableDiscordIntegration,
|
|
||||||
|
|
||||||
enableServiceWorker: instance.enableServiceWorker,
|
enableServiceWorker: instance.enableServiceWorker,
|
||||||
|
|
||||||
translatorAvailable: instance.deeplAuthKey != null,
|
translatorAvailable: instance.deeplAuthKey != null,
|
||||||
|
@ -343,9 +315,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
hcaptcha: instance.enableHcaptcha,
|
hcaptcha: instance.enableHcaptcha,
|
||||||
recaptcha: instance.enableRecaptcha,
|
recaptcha: instance.enableRecaptcha,
|
||||||
objectStorage: instance.useObjectStorage,
|
objectStorage: instance.useObjectStorage,
|
||||||
twitter: instance.enableTwitterIntegration,
|
|
||||||
github: instance.enableGithubIntegration,
|
|
||||||
discord: instance.enableDiscordIntegration,
|
|
||||||
serviceWorker: instance.enableServiceWorker,
|
serviceWorker: instance.enableServiceWorker,
|
||||||
miauth: true,
|
miauth: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,9 +15,6 @@ import handler from './api-handler.js';
|
||||||
import signup from './private/signup.js';
|
import signup from './private/signup.js';
|
||||||
import signin from './private/signin.js';
|
import signin from './private/signin.js';
|
||||||
import signupPending from './private/signup-pending.js';
|
import signupPending from './private/signup-pending.js';
|
||||||
import discord from './service/discord.js';
|
|
||||||
import github from './service/github.js';
|
|
||||||
import twitter from './service/twitter.js';
|
|
||||||
|
|
||||||
// Init app
|
// Init app
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
|
@ -81,10 +78,6 @@ router.post('/signup', signup);
|
||||||
router.post('/signin', signin);
|
router.post('/signin', signin);
|
||||||
router.post('/signup-pending', signupPending);
|
router.post('/signup-pending', signupPending);
|
||||||
|
|
||||||
router.use(discord.routes());
|
|
||||||
router.use(github.routes());
|
|
||||||
router.use(twitter.routes());
|
|
||||||
|
|
||||||
router.get('/v1/instance/peers', async ctx => {
|
router.get('/v1/instance/peers', async ctx => {
|
||||||
const instances = await Instances.find({
|
const instances = await Instances.find({
|
||||||
select: ['host'],
|
select: ['host'],
|
||||||
|
|
|
@ -1,298 +0,0 @@
|
||||||
import 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 { ILocalUser } from '@/models/entities/user.js';
|
|
||||||
import { redisClient } from '@/db/redis.js';
|
|
||||||
import { I18n } from '@/misc/i18n.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.substr(0, url.length - 1) : url : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const referer = ctx.headers['referer'];
|
|
||||||
|
|
||||||
return (normalizeUrl(referer) === normalizeUrl(config.url));
|
|
||||||
}
|
|
||||||
|
|
||||||
const locales = await import('../../../../../../locales/index.js').then(mod => mod.default);
|
|
||||||
|
|
||||||
// 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 });
|
|
||||||
const locale = locales[profile.lang || 'en-US'];
|
|
||||||
const i18n = new I18n(locale);
|
|
||||||
|
|
||||||
delete profile.integrations.discord;
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: profile.integrations,
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = i18n.t('_services._discord.disconnected');
|
|
||||||
|
|
||||||
// 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<any>((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<any>((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<string, unknown>;
|
|
||||||
|
|
||||||
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 })
|
|
||||||
.andWhere('"userHost" IS NULL')
|
|
||||||
.getOne();
|
|
||||||
|
|
||||||
if (profile == null) {
|
|
||||||
ctx.throw(404, `There were no FoundKey accounts linked to @${username}#${discriminator}...`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await UserProfiles.update(profile.userId, {
|
|
||||||
integrations: {
|
|
||||||
...profile.integrations,
|
|
||||||
discord: {
|
|
||||||
id,
|
|
||||||
accessToken,
|
|
||||||
refreshToken,
|
|
||||||
expiresDate,
|
|
||||||
username,
|
|
||||||
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<any>((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<any>((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<string, unknown>;
|
|
||||||
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 });
|
|
||||||
const locale = locales[profile.lang || 'en-US'];
|
|
||||||
const i18n = new I18n(locale);
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: {
|
|
||||||
...profile.integrations,
|
|
||||||
discord: {
|
|
||||||
accessToken,
|
|
||||||
refreshToken,
|
|
||||||
expiresDate,
|
|
||||||
id,
|
|
||||||
username,
|
|
||||||
discriminator,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = i18n.t('_services._discord.connected', {
|
|
||||||
username,
|
|
||||||
discriminator,
|
|
||||||
mkUsername: user.username,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Publish i updated event
|
|
||||||
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
|
|
||||||
detail: true,
|
|
||||||
includeSecrets: true,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -1,269 +0,0 @@
|
||||||
import 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 { ILocalUser } from '@/models/entities/user.js';
|
|
||||||
import { redisClient } from '@/db/redis.js';
|
|
||||||
import signin from '../common/signin.js';
|
|
||||||
import { I18n } from '@/misc/i18n.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.substr(0, url.length - 1) : url : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const referer = ctx.headers['referer'];
|
|
||||||
|
|
||||||
return (normalizeUrl(referer) === normalizeUrl(config.url));
|
|
||||||
}
|
|
||||||
|
|
||||||
const locales = await import('../../../../../../locales/index.js').then(mod => mod.default);
|
|
||||||
|
|
||||||
// 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 });
|
|
||||||
const locale = locales[profile.lang || 'en-US'];
|
|
||||||
const i18n = new I18n(locale);
|
|
||||||
|
|
||||||
delete profile.integrations.github;
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: profile.integrations,
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = i18n.t('_services._github.disconnected');
|
|
||||||
|
|
||||||
// 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<any>((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<any>((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<string, unknown>;
|
|
||||||
if (typeof login !== 'string' || typeof id !== 'string') {
|
|
||||||
ctx.throw(400, 'invalid session');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const link = await UserProfiles.createQueryBuilder()
|
|
||||||
.where('"integrations"->\'github\'->>\'id\' = :id', { id })
|
|
||||||
.andWhere('"userHost" IS NULL')
|
|
||||||
.getOne();
|
|
||||||
|
|
||||||
if (link == null) {
|
|
||||||
ctx.throw(404, `There were no FoundKey accounts linked to @${login}...`);
|
|
||||||
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<any>((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<any>((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<string, unknown>;
|
|
||||||
|
|
||||||
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 });
|
|
||||||
const locale = locales[profile.lang || 'en-US'];
|
|
||||||
const i18n = new I18n(locale);
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: {
|
|
||||||
...profile.integrations,
|
|
||||||
github: {
|
|
||||||
accessToken,
|
|
||||||
id,
|
|
||||||
login,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = i18n.t('_services._github.connected', {
|
|
||||||
login,
|
|
||||||
userName: user.username,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Publish i updated event
|
|
||||||
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
|
|
||||||
detail: true,
|
|
||||||
includeSecrets: true,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -1,211 +0,0 @@
|
||||||
import 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 { ILocalUser } from '@/models/entities/user.js';
|
|
||||||
import { redisClient } from '@/db/redis.js';
|
|
||||||
import signin from '../common/signin.js';
|
|
||||||
import { I18n } from '@/misc/i18n.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));
|
|
||||||
}
|
|
||||||
|
|
||||||
const locales = await import('../../../../../../locales/index.js').then(mod => mod.default);
|
|
||||||
|
|
||||||
// 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 });
|
|
||||||
const locale = locales[profile.lang || 'en-US'];
|
|
||||||
const i18n = new I18n(locale);
|
|
||||||
|
|
||||||
delete profile.integrations.twitter;
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: profile.integrations,
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = i18n.t('_services._twitter.disconnected');
|
|
||||||
|
|
||||||
// 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<any>((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, `There were no FoundKey accounts linked to @${result.screenName}...`);
|
|
||||||
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<any>((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 });
|
|
||||||
const locale = locales[profile.lang || 'en-US'];
|
|
||||||
const i18n = new I18n(locale);
|
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
|
||||||
integrations: {
|
|
||||||
...profile.integrations,
|
|
||||||
twitter: {
|
|
||||||
accessToken: result.accessToken,
|
|
||||||
accessTokenSecret: result.accessTokenSecret,
|
|
||||||
userId: result.userId,
|
|
||||||
screenName: result.screenName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.body = i18n.t('_services._twitter.connected', {
|
|
||||||
twitterUserName: result.screenName,
|
|
||||||
userName: user.username,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Publish i updated event
|
|
||||||
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
|
|
||||||
detail: true,
|
|
||||||
includeSecrets: true,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -72,9 +72,6 @@ const nodeinfo2 = async () => {
|
||||||
enableHcaptcha: meta.enableHcaptcha,
|
enableHcaptcha: meta.enableHcaptcha,
|
||||||
enableRecaptcha: meta.enableRecaptcha,
|
enableRecaptcha: meta.enableRecaptcha,
|
||||||
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
||||||
enableTwitterIntegration: meta.enableTwitterIntegration,
|
|
||||||
enableGithubIntegration: meta.enableGithubIntegration,
|
|
||||||
enableDiscordIntegration: meta.enableDiscordIntegration,
|
|
||||||
enableEmail: meta.enableEmail,
|
enableEmail: meta.enableEmail,
|
||||||
enableServiceWorker: meta.enableServiceWorker,
|
enableServiceWorker: meta.enableServiceWorker,
|
||||||
proxyAccountName: proxyAccount ? proxyAccount.username : null,
|
proxyAccountName: proxyAccount ? proxyAccount.username : null,
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
"abort-controller": "3.0.0",
|
"abort-controller": "3.0.0",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autosize": "5.0.1",
|
"autosize": "5.0.1",
|
||||||
"autwh": "0.1.0",
|
|
||||||
"blurhash": "1.1.5",
|
"blurhash": "1.1.5",
|
||||||
"broadcast-channel": "4.13.0",
|
"broadcast-channel": "4.13.0",
|
||||||
"browser-image-resizer": "2.4.1",
|
"browser-image-resizer": "2.4.1",
|
||||||
|
@ -86,7 +85,6 @@
|
||||||
"@types/katex": "0.14.0",
|
"@types/katex": "0.14.0",
|
||||||
"@types/matter-js": "0.17.7",
|
"@types/matter-js": "0.17.7",
|
||||||
"@types/mocha": "9.1.1",
|
"@types/mocha": "9.1.1",
|
||||||
"@types/oauth": "0.9.1",
|
|
||||||
"@types/punycode": "2.1.0",
|
"@types/punycode": "2.1.0",
|
||||||
"@types/qrcode": "1.5.0",
|
"@types/qrcode": "1.5.0",
|
||||||
"@types/seedrandom": "3.0.2",
|
"@types/seedrandom": "3.0.2",
|
||||||
|
|
|
@ -40,11 +40,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="social _section">
|
|
||||||
<a v-if="meta && meta.enableTwitterIntegration" class="_borderButton _gap" :href="`${apiUrl}/signin/twitter`"><i class="fab fa-twitter" style="margin-right: 4px;"></i>{{ i18n.t('signinWith', { x: 'Twitter' }) }}</a>
|
|
||||||
<a v-if="meta && meta.enableGithubIntegration" class="_borderButton _gap" :href="`${apiUrl}/signin/github`"><i class="fab fa-github" style="margin-right: 4px;"></i>{{ i18n.t('signinWith', { x: 'GitHub' }) }}</a>
|
|
||||||
<a v-if="meta && meta.enableDiscordIntegration" class="_borderButton _gap" :href="`${apiUrl}/signin/discord`"><i class="fab fa-discord" style="margin-right: 4px;"></i>{{ i18n.t('signinWith', { x: 'Discord' }) }}</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -157,11 +157,6 @@ const menuDef = $computed(() => [{
|
||||||
text: i18n.ts.relays,
|
text: i18n.ts.relays,
|
||||||
to: '/admin/relays',
|
to: '/admin/relays',
|
||||||
active: props.initialPage === 'relays',
|
active: props.initialPage === 'relays',
|
||||||
}, {
|
|
||||||
icon: 'fas fa-share-alt',
|
|
||||||
text: i18n.ts.integration,
|
|
||||||
to: '/admin/integrations',
|
|
||||||
active: props.initialPage === 'integrations',
|
|
||||||
}, {
|
}, {
|
||||||
icon: 'fas fa-ban',
|
icon: 'fas fa-ban',
|
||||||
text: i18n.ts.instanceBlocking,
|
text: i18n.ts.instanceBlocking,
|
||||||
|
@ -199,7 +194,6 @@ const component = $computed(() => {
|
||||||
case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
|
case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
|
||||||
case 'security': return defineAsyncComponent(() => import('./security.vue'));
|
case 'security': return defineAsyncComponent(() => import('./security.vue'));
|
||||||
case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
|
case 'relays': return defineAsyncComponent(() => import('./relays.vue'));
|
||||||
case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
|
|
||||||
case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
|
case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
|
||||||
case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
|
case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
|
||||||
default: return null;
|
default: return null;
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
<template>
|
|
||||||
<FormSuspense :p="init">
|
|
||||||
<div class="_formRoot">
|
|
||||||
<FormSwitch v-model="enableDiscordIntegration" class="_formBlock">
|
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
|
||||||
</FormSwitch>
|
|
||||||
|
|
||||||
<template v-if="enableDiscordIntegration">
|
|
||||||
<FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo>
|
|
||||||
|
|
||||||
<FormInput v-model="discordClientId" class="_formBlock">
|
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
|
||||||
<template #label>Client ID</template>
|
|
||||||
</FormInput>
|
|
||||||
|
|
||||||
<FormInput v-model="discordClientSecret" class="_formBlock">
|
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
|
||||||
<template #label>Client Secret</template>
|
|
||||||
</FormInput>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
|
|
||||||
</div>
|
|
||||||
</FormSuspense>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import FormSwitch from '@/components/form/switch.vue';
|
|
||||||
import FormInput from '@/components/form/input.vue';
|
|
||||||
import FormButton from '@/components/ui/button.vue';
|
|
||||||
import FormInfo from '@/components/ui/info.vue';
|
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { fetchInstance } from '@/instance';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
|
|
||||||
let uri: string = $ref('');
|
|
||||||
let enableDiscordIntegration: boolean = $ref(false);
|
|
||||||
let discordClientId: string | null = $ref(null);
|
|
||||||
let discordClientSecret: string | null = $ref(null);
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const meta = await os.api('admin/meta');
|
|
||||||
uri = meta.uri;
|
|
||||||
enableDiscordIntegration = meta.enableDiscordIntegration;
|
|
||||||
discordClientId = meta.discordClientId;
|
|
||||||
discordClientSecret = meta.discordClientSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function save() {
|
|
||||||
os.apiWithDialog('admin/update-meta', {
|
|
||||||
enableDiscordIntegration,
|
|
||||||
discordClientId,
|
|
||||||
discordClientSecret,
|
|
||||||
}).then(() => {
|
|
||||||
fetchInstance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,59 +0,0 @@
|
||||||
<template>
|
|
||||||
<FormSuspense :p="init">
|
|
||||||
<div class="_formRoot">
|
|
||||||
<FormSwitch v-model="enableGithubIntegration" class="_formBlock">
|
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
|
||||||
</FormSwitch>
|
|
||||||
|
|
||||||
<template v-if="enableGithubIntegration">
|
|
||||||
<FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo>
|
|
||||||
|
|
||||||
<FormInput v-model="githubClientId" class="_formBlock">
|
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
|
||||||
<template #label>Client ID</template>
|
|
||||||
</FormInput>
|
|
||||||
|
|
||||||
<FormInput v-model="githubClientSecret" class="_formBlock">
|
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
|
||||||
<template #label>Client Secret</template>
|
|
||||||
</FormInput>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
|
|
||||||
</div>
|
|
||||||
</FormSuspense>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import FormSwitch from '@/components/form/switch.vue';
|
|
||||||
import FormInput from '@/components/form/input.vue';
|
|
||||||
import FormButton from '@/components/ui/button.vue';
|
|
||||||
import FormInfo from '@/components/ui/info.vue';
|
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { fetchInstance } from '@/instance';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
|
|
||||||
let uri: string = $ref('');
|
|
||||||
let enableGithubIntegration: boolean = $ref(false);
|
|
||||||
let githubClientId: string | null = $ref(null);
|
|
||||||
let githubClientSecret: string | null = $ref(null);
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const meta = await os.api('admin/meta');
|
|
||||||
uri = meta.uri;
|
|
||||||
enableGithubIntegration = meta.enableGithubIntegration;
|
|
||||||
githubClientId = meta.githubClientId;
|
|
||||||
githubClientSecret = meta.githubClientSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function save() {
|
|
||||||
os.apiWithDialog('admin/update-meta', {
|
|
||||||
enableGithubIntegration,
|
|
||||||
githubClientId,
|
|
||||||
githubClientSecret,
|
|
||||||
}).then(() => {
|
|
||||||
fetchInstance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,59 +0,0 @@
|
||||||
<template>
|
|
||||||
<FormSuspense :p="init">
|
|
||||||
<div class="_formRoot">
|
|
||||||
<FormSwitch v-model="enableTwitterIntegration" class="_formBlock">
|
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
|
||||||
</FormSwitch>
|
|
||||||
|
|
||||||
<template v-if="enableTwitterIntegration">
|
|
||||||
<FormInfo class="_formBlock">Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo>
|
|
||||||
|
|
||||||
<FormInput v-model="twitterConsumerKey" class="_formBlock">
|
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
|
||||||
<template #label>Consumer Key</template>
|
|
||||||
</FormInput>
|
|
||||||
|
|
||||||
<FormInput v-model="twitterConsumerSecret" class="_formBlock">
|
|
||||||
<template #prefix><i class="fas fa-key"></i></template>
|
|
||||||
<template #label>Consumer Secret</template>
|
|
||||||
</FormInput>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
|
|
||||||
</div>
|
|
||||||
</FormSuspense>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import FormSwitch from '@/components/form/switch.vue';
|
|
||||||
import FormInput from '@/components/form/input.vue';
|
|
||||||
import FormButton from '@/components/ui/button.vue';
|
|
||||||
import FormInfo from '@/components/ui/info.vue';
|
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { fetchInstance } from '@/instance';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
|
|
||||||
let uri: string = $ref('');
|
|
||||||
let enableTwitterIntegration: boolean = $ref(false);
|
|
||||||
let twitterConsumerKey: string | null = $ref(null);
|
|
||||||
let twitterConsumerSecret: string | null = $ref(null);
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const meta = await os.api('admin/meta');
|
|
||||||
uri = meta.uri;
|
|
||||||
enableTwitterIntegration = meta.enableTwitterIntegration;
|
|
||||||
twitterConsumerKey = meta.twitterConsumerKey;
|
|
||||||
twitterConsumerSecret = meta.twitterConsumerSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function save() {
|
|
||||||
os.apiWithDialog('admin/update-meta', {
|
|
||||||
enableTwitterIntegration,
|
|
||||||
twitterConsumerKey,
|
|
||||||
twitterConsumerSecret,
|
|
||||||
}).then(() => {
|
|
||||||
fetchInstance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,54 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkStickyContainer>
|
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
|
||||||
<FormSuspense :p="init">
|
|
||||||
<FormFolder class="_formBlock">
|
|
||||||
<template #icon><i class="fab fa-twitter"></i></template>
|
|
||||||
<template #label>Twitter</template>
|
|
||||||
<template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
|
|
||||||
<XTwitter/>
|
|
||||||
</FormFolder>
|
|
||||||
<FormFolder class="_formBlock">
|
|
||||||
<template #icon><i class="fab fa-github"></i></template>
|
|
||||||
<template #label>GitHub</template>
|
|
||||||
<template #suffix>{{ enableGithubIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
|
|
||||||
<XGithub/>
|
|
||||||
</FormFolder>
|
|
||||||
<FormFolder class="_formBlock">
|
|
||||||
<template #icon><i class="fab fa-discord"></i></template>
|
|
||||||
<template #label>Discord</template>
|
|
||||||
<template #suffix>{{ enableDiscordIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template>
|
|
||||||
<XDiscord/>
|
|
||||||
</FormFolder>
|
|
||||||
</FormSuspense>
|
|
||||||
</MkSpacer>
|
|
||||||
</MkStickyContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import XTwitter from './integrations.twitter.vue';
|
|
||||||
import XGithub from './integrations.github.vue';
|
|
||||||
import XDiscord from './integrations.discord.vue';
|
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
|
||||||
import FormFolder from '@/components/form/folder.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
let enableTwitterIntegration: boolean = $ref(false);
|
|
||||||
let enableGithubIntegration: boolean = $ref(false);
|
|
||||||
let enableDiscordIntegration: boolean = $ref(false);
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const meta = await os.api('admin/meta');
|
|
||||||
enableTwitterIntegration = meta.enableTwitterIntegration;
|
|
||||||
enableGithubIntegration = meta.enableGithubIntegration;
|
|
||||||
enableDiscordIntegration = meta.enableDiscordIntegration;
|
|
||||||
}
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.integration,
|
|
||||||
icon: 'fas fa-share-alt',
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -89,11 +89,6 @@ const menuDef = computed(() => [{
|
||||||
text: i18n.ts.email,
|
text: i18n.ts.email,
|
||||||
to: '/settings/email',
|
to: '/settings/email',
|
||||||
active: props.initialPage === 'email',
|
active: props.initialPage === 'email',
|
||||||
}, {
|
|
||||||
icon: 'fas fa-share-alt',
|
|
||||||
text: i18n.ts.integration,
|
|
||||||
to: '/settings/integration',
|
|
||||||
active: props.initialPage === 'integration',
|
|
||||||
}, {
|
}, {
|
||||||
icon: 'fas fa-lock',
|
icon: 'fas fa-lock',
|
||||||
text: i18n.ts.security,
|
text: i18n.ts.security,
|
||||||
|
@ -200,7 +195,6 @@ const component = computed(() => {
|
||||||
case 'mute-block': return defineAsyncComponent(() => import('./mute-block.vue'));
|
case 'mute-block': return defineAsyncComponent(() => import('./mute-block.vue'));
|
||||||
case 'word-mute': return defineAsyncComponent(() => import('./word-mute.vue'));
|
case 'word-mute': return defineAsyncComponent(() => import('./word-mute.vue'));
|
||||||
case 'instance-mute': return defineAsyncComponent(() => import('./instance-mute.vue'));
|
case 'instance-mute': return defineAsyncComponent(() => import('./instance-mute.vue'));
|
||||||
case 'integration': return defineAsyncComponent(() => import('./integration.vue'));
|
|
||||||
case 'security': return defineAsyncComponent(() => import('./security.vue'));
|
case 'security': return defineAsyncComponent(() => import('./security.vue'));
|
||||||
case '2fa': return defineAsyncComponent(() => import('./2fa.vue'));
|
case '2fa': return defineAsyncComponent(() => import('./2fa.vue'));
|
||||||
case 'api': return defineAsyncComponent(() => import('./api.vue'));
|
case 'api': return defineAsyncComponent(() => import('./api.vue'));
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="_formRoot">
|
|
||||||
<FormSection v-if="instance.enableTwitterIntegration">
|
|
||||||
<template #label><i class="fab fa-twitter"></i> Twitter</template>
|
|
||||||
<p v-if="integrations.twitter">{{ i18n.ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
|
|
||||||
<MkButton v-if="integrations.twitter" danger @click="disconnectTwitter">{{ i18n.ts.disconnectService }}</MkButton>
|
|
||||||
<MkButton v-else primary @click="connectTwitter">{{ i18n.ts.connectService }}</MkButton>
|
|
||||||
</FormSection>
|
|
||||||
|
|
||||||
<FormSection v-if="instance.enableDiscordIntegration">
|
|
||||||
<template #label><i class="fab fa-discord"></i> Discord</template>
|
|
||||||
<p v-if="integrations.discord">{{ i18n.ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
|
|
||||||
<MkButton v-if="integrations.discord" danger @click="disconnectDiscord">{{ i18n.ts.disconnectService }}</MkButton>
|
|
||||||
<MkButton v-else primary @click="connectDiscord">{{ i18n.ts.connectService }}</MkButton>
|
|
||||||
</FormSection>
|
|
||||||
|
|
||||||
<FormSection v-if="instance.enableGithubIntegration">
|
|
||||||
<template #label><i class="fab fa-github"></i> GitHub</template>
|
|
||||||
<p v-if="integrations.github">{{ i18n.ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
|
|
||||||
<MkButton v-if="integrations.github" danger @click="disconnectGithub">{{ i18n.ts.disconnectService }}</MkButton>
|
|
||||||
<MkButton v-else primary @click="connectGithub">{{ i18n.ts.connectService }}</MkButton>
|
|
||||||
</FormSection>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, onMounted, ref, watch } from 'vue';
|
|
||||||
import { apiUrl } from '@/config';
|
|
||||||
import FormSection from '@/components/form/section.vue';
|
|
||||||
import MkButton from '@/components/ui/button.vue';
|
|
||||||
import { $i } from '@/account';
|
|
||||||
import { instance } from '@/instance';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
const twitterForm = ref<Window | null>(null);
|
|
||||||
const discordForm = ref<Window | null>(null);
|
|
||||||
const githubForm = ref<Window | null>(null);
|
|
||||||
|
|
||||||
const integrations = computed(() => $i!.integrations);
|
|
||||||
|
|
||||||
function openWindow(service: string, type: string) {
|
|
||||||
return window.open(`${apiUrl}/${type}/${service}`,
|
|
||||||
`${service}_${type}_window`,
|
|
||||||
'height=570, width=520',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectTwitter() {
|
|
||||||
twitterForm.value = openWindow('twitter', 'connect');
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnectTwitter() {
|
|
||||||
openWindow('twitter', 'disconnect');
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectDiscord() {
|
|
||||||
discordForm.value = openWindow('discord', 'connect');
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnectDiscord() {
|
|
||||||
openWindow('discord', 'disconnect');
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectGithub() {
|
|
||||||
githubForm.value = openWindow('github', 'connect');
|
|
||||||
}
|
|
||||||
|
|
||||||
function disconnectGithub() {
|
|
||||||
openWindow('github', 'disconnect');
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
document.cookie = `igi=${$i!.token}; path=/;` +
|
|
||||||
' max-age=31536000;' +
|
|
||||||
(document.location.protocol.startsWith('https') ? ' secure' : '');
|
|
||||||
|
|
||||||
watch(integrations, () => {
|
|
||||||
if (integrations.value.twitter) {
|
|
||||||
if (twitterForm.value) twitterForm.value.close();
|
|
||||||
}
|
|
||||||
if (integrations.value.discord) {
|
|
||||||
if (discordForm.value) discordForm.value.close();
|
|
||||||
}
|
|
||||||
if (integrations.value.github) {
|
|
||||||
if (githubForm.value) githubForm.value.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.integration,
|
|
||||||
icon: 'fas fa-share-alt',
|
|
||||||
});
|
|
||||||
</script>
|
|
29
yarn.lock
29
yarn.lock
|
@ -2172,15 +2172,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/oauth@npm:0.9.1, @types/oauth@npm:^0.9.1":
|
|
||||||
version: 0.9.1
|
|
||||||
resolution: "@types/oauth@npm:0.9.1"
|
|
||||||
dependencies:
|
|
||||||
"@types/node": "*"
|
|
||||||
checksum: 5c079611b455eff58fba6358e028b191a1e65475600f8ed8d98c1696fedcfb0290aa6c6a19cf50f21a9e2d816ecb43a19f910900d91f8ba3727e33c48f97d7f3
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@types/opentype.js@npm:^1.3.4":
|
"@types/opentype.js@npm:^1.3.4":
|
||||||
version: 1.3.4
|
version: 1.3.4
|
||||||
resolution: "@types/opentype.js@npm:1.3.4"
|
resolution: "@types/opentype.js@npm:1.3.4"
|
||||||
|
@ -3400,15 +3391,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"autwh@npm:0.1.0":
|
|
||||||
version: 0.1.0
|
|
||||||
resolution: "autwh@npm:0.1.0"
|
|
||||||
dependencies:
|
|
||||||
oauth: 0.9.15
|
|
||||||
checksum: 5ca904d43421e7475de29adfda65ca769105cbf597c9c43fef934f20b0daa331621157cdf34513eae5f492e1b4fc6443f37438da08bf2ba1c4e3e7d6d3f3f738
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"aws-sdk@npm:2.1165.0":
|
"aws-sdk@npm:2.1165.0":
|
||||||
version: 2.1165.0
|
version: 2.1165.0
|
||||||
resolution: "aws-sdk@npm:2.1165.0"
|
resolution: "aws-sdk@npm:2.1165.0"
|
||||||
|
@ -3591,7 +3573,6 @@ __metadata:
|
||||||
"@types/node": 18.7.16
|
"@types/node": 18.7.16
|
||||||
"@types/node-fetch": 3.0.3
|
"@types/node-fetch": 3.0.3
|
||||||
"@types/nodemailer": 6.4.5
|
"@types/nodemailer": 6.4.5
|
||||||
"@types/oauth": ^0.9.1
|
|
||||||
"@types/opentype.js": ^1.3.4
|
"@types/opentype.js": ^1.3.4
|
||||||
"@types/pg": ^8.6.5
|
"@types/pg": ^8.6.5
|
||||||
"@types/pug": 2.0.6
|
"@types/pug": 2.0.6
|
||||||
|
@ -3622,7 +3603,6 @@ __metadata:
|
||||||
ajv: 8.11.0
|
ajv: 8.11.0
|
||||||
archiver: 5.3.1
|
archiver: 5.3.1
|
||||||
autobind-decorator: 2.4.0
|
autobind-decorator: 2.4.0
|
||||||
autwh: 0.1.0
|
|
||||||
aws-sdk: 2.1165.0
|
aws-sdk: 2.1165.0
|
||||||
bcryptjs: 2.4.3
|
bcryptjs: 2.4.3
|
||||||
blurhash: 1.1.5
|
blurhash: 1.1.5
|
||||||
|
@ -4563,7 +4543,6 @@ __metadata:
|
||||||
"@types/katex": 0.14.0
|
"@types/katex": 0.14.0
|
||||||
"@types/matter-js": 0.17.7
|
"@types/matter-js": 0.17.7
|
||||||
"@types/mocha": 9.1.1
|
"@types/mocha": 9.1.1
|
||||||
"@types/oauth": 0.9.1
|
|
||||||
"@types/punycode": 2.1.0
|
"@types/punycode": 2.1.0
|
||||||
"@types/qrcode": 1.5.0
|
"@types/qrcode": 1.5.0
|
||||||
"@types/seedrandom": 3.0.2
|
"@types/seedrandom": 3.0.2
|
||||||
|
@ -4578,7 +4557,6 @@ __metadata:
|
||||||
abort-controller: 3.0.0
|
abort-controller: 3.0.0
|
||||||
autobind-decorator: 2.4.0
|
autobind-decorator: 2.4.0
|
||||||
autosize: 5.0.1
|
autosize: 5.0.1
|
||||||
autwh: 0.1.0
|
|
||||||
blurhash: 1.1.5
|
blurhash: 1.1.5
|
||||||
broadcast-channel: 4.13.0
|
broadcast-channel: 4.13.0
|
||||||
browser-image-resizer: 2.4.1
|
browser-image-resizer: 2.4.1
|
||||||
|
@ -12135,13 +12113,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"oauth@npm:0.9.15":
|
|
||||||
version: 0.9.15
|
|
||||||
resolution: "oauth@npm:0.9.15"
|
|
||||||
checksum: 957c0d8d85300398dcb0e293953650c0fc3facc795bee8228238414f19f59cef5fd4ee8d17a972c142924c10c5f6ec50ef80f77f4a6cc6e3c98f9d22c027801c
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1":
|
"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1":
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
resolution: "object-assign@npm:4.1.1"
|
resolution: "object-assign@npm:4.1.1"
|
||||||
|
|
Loading…
Reference in a new issue