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}"
|
||||
yearX: "{year}"
|
||||
pages: "الصفحات"
|
||||
integration: "التكامل"
|
||||
connectService: "اتصل"
|
||||
disconnectService: "اقطع الاتصال"
|
||||
enableLocalTimeline: "تفعيل الخيط المحلي"
|
||||
enableGlobalTimeline: "تفعيل الخيط الزمني الشامل"
|
||||
disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من الوصول إلى كل الخيوط الزمنية\
|
||||
|
|
|
@ -339,9 +339,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "পৃষ্ঠা"
|
||||
integration: "ইন্টিগ্রেশন"
|
||||
connectService: "সংযুক্ত করুন"
|
||||
disconnectService: "সংযোগ বিচ্ছিন্ন করুন"
|
||||
enableLocalTimeline: "স্থানীয় টাইমলাইন চালু করুন"
|
||||
enableGlobalTimeline: "গ্লোবাল টাইমলাইন চালু করুন"
|
||||
disablingTimelinesInfo: "আপনি এই টাইমলাইনগুলি বন্ধ করলেও প্রশাসক এবং মডারেটররা এই\
|
||||
|
|
|
@ -307,9 +307,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Stránky"
|
||||
integration: "Integrace"
|
||||
connectService: "Připojit"
|
||||
disconnectService: "Odpojit"
|
||||
enableLocalTimeline: "Povolit lokální čas"
|
||||
enableGlobalTimeline: "Povolit globální čas"
|
||||
registration: "Registrace"
|
||||
|
|
|
@ -350,9 +350,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\
|
||||
|
|
|
@ -340,9 +340,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\
|
||||
|
@ -1511,13 +1508,3 @@ _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."
|
||||
|
|
|
@ -342,9 +342,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\
|
||||
|
|
|
@ -342,9 +342,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\
|
||||
|
|
|
@ -341,9 +341,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\
|
||||
|
|
|
@ -335,9 +335,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\
|
||||
|
|
|
@ -317,9 +317,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "ページ"
|
||||
integration: "連携"
|
||||
connectService: "接続する"
|
||||
disconnectService: "切断する"
|
||||
enableLocalTimeline: "ローカルタイムラインを有効にする"
|
||||
enableGlobalTimeline: "グローバルタイムラインを有効にする"
|
||||
disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。"
|
||||
|
@ -1449,13 +1446,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:"
|
||||
|
|
|
@ -319,7 +319,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "ページ"
|
||||
integration: "連携"
|
||||
enableLocalTimeline: "ローカルタイムラインを使えるようにする"
|
||||
enableGlobalTimeline: "グローバルタイムラインを使えるようにする"
|
||||
disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。"
|
||||
|
|
|
@ -315,9 +315,6 @@ dayX: "{day}일"
|
|||
monthX: "{month}월"
|
||||
yearX: "{year}년"
|
||||
pages: "페이지"
|
||||
integration: "연동"
|
||||
connectService: "계정 연동"
|
||||
disconnectService: "계정 연동 해제"
|
||||
enableLocalTimeline: "로컬 타임라인 활성화"
|
||||
enableGlobalTimeline: "글로벌 타임라인 활성화"
|
||||
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
|
||||
|
|
|
@ -329,9 +329,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\
|
||||
|
|
|
@ -342,9 +342,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\
|
||||
|
|
|
@ -335,9 +335,6 @@ dayX: "{day} день"
|
|||
monthX: "{month} месяц"
|
||||
yearX: "{year} год"
|
||||
pages: "Страницы"
|
||||
integration: "Интеграция"
|
||||
connectService: "Подключиться"
|
||||
disconnectService: "Отключиться"
|
||||
enableLocalTimeline: "Включить локальную ленту"
|
||||
enableGlobalTimeline: "Включить глобальную ленту"
|
||||
disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам,\
|
||||
|
|
|
@ -336,9 +336,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\
|
||||
|
|
|
@ -336,9 +336,6 @@ dayX: "{day}"
|
|||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Сторінки"
|
||||
integration: "Інтеграція"
|
||||
connectService: "Під’єднати"
|
||||
disconnectService: "Відключитися"
|
||||
enableLocalTimeline: "Увімкнути локальну стрічку"
|
||||
enableGlobalTimeline: "Увімкнути глобальну стрічку"
|
||||
disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх\
|
||||
|
|
|
@ -336,9 +336,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\
|
||||
|
|
|
@ -315,9 +315,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "页面"
|
||||
integration: "关联"
|
||||
connectService: "连接"
|
||||
disconnectService: "断开连接"
|
||||
enableLocalTimeline: "启用本地时间线功能"
|
||||
enableGlobalTimeline: "启用全局时间线"
|
||||
disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。"
|
||||
|
|
|
@ -315,9 +315,6 @@ dayX: "{day}日"
|
|||
monthX: "{month}月"
|
||||
yearX: "{year}年"
|
||||
pages: "頁面"
|
||||
integration: "整合"
|
||||
connectService: "己連結"
|
||||
disconnectService: "己斷開 "
|
||||
enableLocalTimeline: "開啟本地時間軸"
|
||||
enableGlobalTimeline: "啟用公開時間軸"
|
||||
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
|
||||
|
|
|
@ -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",
|
||||
|
@ -150,7 +149,6 @@
|
|||
"@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",
|
||||
|
|
|
@ -101,18 +101,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,
|
||||
},
|
||||
enableServiceWorker: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
|
@ -166,30 +154,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,
|
||||
|
@ -314,9 +278,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
defaultLightTheme: instance.defaultLightTheme,
|
||||
defaultDarkTheme: instance.defaultDarkTheme,
|
||||
enableEmail: instance.enableEmail,
|
||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
||||
enableGithubIntegration: instance.enableGithubIntegration,
|
||||
enableDiscordIntegration: instance.enableDiscordIntegration,
|
||||
enableServiceWorker: instance.enableServiceWorker,
|
||||
translatorAvailable: instance.deeplAuthKey != null,
|
||||
pinnedPages: instance.pinnedPages,
|
||||
|
@ -330,12 +291,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
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,
|
||||
|
|
|
@ -58,15 +58,6 @@ export const paramDef = {
|
|||
summalyProxy: { type: 'string', nullable: true },
|
||||
deeplAuthKey: { type: 'string', nullable: true },
|
||||
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' },
|
||||
email: { type: 'string', nullable: true },
|
||||
smtpSecure: { type: 'boolean' },
|
||||
|
@ -231,42 +222,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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
enableServiceWorker: {
|
||||
type: 'boolean',
|
||||
optional: false, nullable: false,
|
||||
|
@ -226,18 +214,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: false, nullable: false,
|
||||
|
@ -317,10 +293,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
defaultDarkTheme: instance.defaultDarkTheme,
|
||||
enableEmail: instance.enableEmail,
|
||||
|
||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
||||
enableGithubIntegration: instance.enableGithubIntegration,
|
||||
enableDiscordIntegration: instance.enableDiscordIntegration,
|
||||
|
||||
enableServiceWorker: instance.enableServiceWorker,
|
||||
|
||||
translatorAvailable: instance.deeplAuthKey != null,
|
||||
|
@ -343,9 +315,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
hcaptcha: instance.enableHcaptcha,
|
||||
recaptcha: instance.enableRecaptcha,
|
||||
objectStorage: instance.useObjectStorage,
|
||||
twitter: instance.enableTwitterIntegration,
|
||||
github: instance.enableGithubIntegration,
|
||||
discord: instance.enableDiscordIntegration,
|
||||
serviceWorker: instance.enableServiceWorker,
|
||||
miauth: true,
|
||||
},
|
||||
|
|
|
@ -15,9 +15,6 @@ import handler from './api-handler.js';
|
|||
import signup from './private/signup.js';
|
||||
import signin from './private/signin.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
|
||||
const app = new Koa();
|
||||
|
@ -81,10 +78,6 @@ router.post('/signup', signup);
|
|||
router.post('/signin', signin);
|
||||
router.post('/signup-pending', signupPending);
|
||||
|
||||
router.use(discord.routes());
|
||||
router.use(github.routes());
|
||||
router.use(twitter.routes());
|
||||
|
||||
router.get('/v1/instance/peers', async ctx => {
|
||||
const instances = await Instances.find({
|
||||
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,
|
||||
enableRecaptcha: meta.enableRecaptcha,
|
||||
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
|
||||
enableTwitterIntegration: meta.enableTwitterIntegration,
|
||||
enableGithubIntegration: meta.enableGithubIntegration,
|
||||
enableDiscordIntegration: meta.enableDiscordIntegration,
|
||||
enableEmail: meta.enableEmail,
|
||||
enableServiceWorker: meta.enableServiceWorker,
|
||||
proxyAccountName: proxyAccount ? proxyAccount.username : null,
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
"abort-controller": "3.0.0",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "5.0.1",
|
||||
"autwh": "0.1.0",
|
||||
"blurhash": "1.1.5",
|
||||
"broadcast-channel": "4.13.0",
|
||||
"browser-image-resizer": "2.4.1",
|
||||
|
@ -86,7 +85,6 @@
|
|||
"@types/katex": "0.14.0",
|
||||
"@types/matter-js": "0.17.7",
|
||||
"@types/mocha": "9.1.1",
|
||||
"@types/oauth": "0.9.1",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/qrcode": "1.5.0",
|
||||
"@types/seedrandom": "3.0.2",
|
||||
|
|
|
@ -40,11 +40,6 @@
|
|||
</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>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -157,11 +157,6 @@ const menuDef = $computed(() => [{
|
|||
text: i18n.ts.relays,
|
||||
to: '/admin/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',
|
||||
text: i18n.ts.instanceBlocking,
|
||||
|
@ -199,7 +194,6 @@ const component = $computed(() => {
|
|||
case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue'));
|
||||
case 'security': return defineAsyncComponent(() => import('./security.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 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
|
||||
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,
|
||||
to: '/settings/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',
|
||||
text: i18n.ts.security,
|
||||
|
@ -200,7 +195,6 @@ const component = computed(() => {
|
|||
case 'mute-block': return defineAsyncComponent(() => import('./mute-block.vue'));
|
||||
case 'word-mute': return defineAsyncComponent(() => import('./word-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 '2fa': return defineAsyncComponent(() => import('./2fa.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
|
||||
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":
|
||||
version: 1.3.4
|
||||
resolution: "@types/opentype.js@npm:1.3.4"
|
||||
|
@ -3400,15 +3391,6 @@ __metadata:
|
|||
languageName: node
|
||||
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":
|
||||
version: 2.1165.0
|
||||
resolution: "aws-sdk@npm:2.1165.0"
|
||||
|
@ -3591,7 +3573,6 @@ __metadata:
|
|||
"@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
|
||||
|
@ -3622,7 +3603,6 @@ __metadata:
|
|||
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
|
||||
|
@ -4563,7 +4543,6 @@ __metadata:
|
|||
"@types/katex": 0.14.0
|
||||
"@types/matter-js": 0.17.7
|
||||
"@types/mocha": 9.1.1
|
||||
"@types/oauth": 0.9.1
|
||||
"@types/punycode": 2.1.0
|
||||
"@types/qrcode": 1.5.0
|
||||
"@types/seedrandom": 3.0.2
|
||||
|
@ -4578,7 +4557,6 @@ __metadata:
|
|||
abort-controller: 3.0.0
|
||||
autobind-decorator: 2.4.0
|
||||
autosize: 5.0.1
|
||||
autwh: 0.1.0
|
||||
blurhash: 1.1.5
|
||||
broadcast-channel: 4.13.0
|
||||
browser-image-resizer: 2.4.1
|
||||
|
@ -12135,13 +12113,6 @@ __metadata:
|
|||
languageName: node
|
||||
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":
|
||||
version: 4.1.1
|
||||
resolution: "object-assign@npm:4.1.1"
|
||||
|
|
Loading…
Reference in a new issue