From 562b02310f9f994f98943c9d93d2f9d948858c32 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 3 Feb 2023 15:01:31 +0900 Subject: [PATCH] drop twitter/github/discord integrations Close #9775 --- .../activitypub/models/ApPersonService.ts | 48 +-- .../src/core/entities/UserEntityService.ts | 1 - packages/backend/src/models/entities/Meta.ts | 51 --- .../src/models/entities/UserProfile.ts | 5 - packages/backend/src/models/schema/user.ts | 4 - .../src/server/NodeinfoServerService.ts | 3 - packages/backend/src/server/ServerModule.ts | 6 - .../src/server/api/ApiServerService.ts | 10 - .../src/server/api/endpoints/admin/meta.ts | 45 --- .../server/api/endpoints/admin/show-user.ts | 6 - .../server/api/endpoints/admin/update-meta.ts | 45 --- .../backend/src/server/api/endpoints/meta.ts | 32 -- .../api/integration/DiscordServerService.ts | 308 ------------------ .../api/integration/GithubServerService.ts | 280 ---------------- .../api/integration/TwitterServerService.ts | 225 ------------- packages/frontend/src/components/MkSignin.vue | 5 - packages/frontend/src/pages/admin/index.vue | 5 - .../src/pages/admin/integrations.discord.vue | 60 ---- .../src/pages/admin/integrations.github.vue | 60 ---- .../src/pages/admin/integrations.twitter.vue | 60 ---- .../frontend/src/pages/admin/integrations.vue | 61 ---- .../frontend/src/pages/settings/index.vue | 5 - .../src/pages/settings/integration.vue | 99 ------ packages/frontend/src/router.ts | 8 - 24 files changed, 6 insertions(+), 1426 deletions(-) delete mode 100644 packages/backend/src/server/api/integration/DiscordServerService.ts delete mode 100644 packages/backend/src/server/api/integration/GithubServerService.ts delete mode 100644 packages/backend/src/server/api/integration/TwitterServerService.ts delete mode 100644 packages/frontend/src/pages/admin/integrations.discord.vue delete mode 100644 packages/frontend/src/pages/admin/integrations.github.vue delete mode 100644 packages/frontend/src/pages/admin/integrations.twitter.vue delete mode 100644 packages/frontend/src/pages/admin/integrations.vue delete mode 100644 packages/frontend/src/pages/settings/integration.vue diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index f86b5e6f96..2325bbe093 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -29,6 +29,7 @@ import { UserNotePining } from '@/models/entities/UserNotePining.js'; import { StatusError } from '@/misc/status-error.js'; import type { UtilityService } from '@/core/UtilityService.js'; import type { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -43,37 +44,6 @@ import type { IActor, IObject, IApPropertyValue } from '../type.js'; const nameLength = 128; const summaryLength = 2048; -const services: { - [x: string]: (id: string, username: string) => any -} = { - 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }), - 'misskey:authentication:github': (id, login) => ({ id, login }), - 'misskey:authentication:discord': (id, name) => $discord(id, name), -}; - -const $discord = (id: string, name: string) => { - if (typeof name !== 'string') { - name = 'unknown#0000'; - } - const [username, discriminator] = name.split('#'); - return { id, username, discriminator }; -}; - -function addService(target: { [x: string]: any }, source: IApPropertyValue) { - const service = services[source.name]; - - if (typeof source.value !== 'string') { - source.value = 'unknown'; - } - - const [id, username] = source.value.split('@'); - - if (service) { - target[source.name.split(':')[2]] = service(id, username); - } -} -import { bindThis } from '@/decorators.js'; - @Injectable() export class ApPersonService implements OnModuleInit { private utilityService: UtilityService; @@ -540,22 +510,16 @@ export class ApPersonService implements OnModuleInit { name: string, value: string }[] = []; - const services: { [x: string]: any } = {}; - if (Array.isArray(attachments)) { for (const attachment of attachments.filter(isPropertyValue)) { - if (isPropertyValue(attachment.identifier)) { - addService(services, attachment.identifier); - } else { - fields.push({ - name: attachment.name, - value: this.mfmService.fromHtml(attachment.value), - }); - } + fields.push({ + name: attachment.name, + value: this.mfmService.fromHtml(attachment.value), + }); } } - return { fields, services }; + return { fields }; } @bindThis diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 546e61a26e..aaa80033b3 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -489,7 +489,6 @@ export class UserEntityService implements OnModuleInit { hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(user.id), hasUnreadNotification: this.getHasUnreadNotification(user.id), hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), - integrations: profile!.integrations, mutedWords: profile!.mutedWords, mutedInstances: profile!.mutedInstances, mutingNotificationTypes: profile!.mutingNotificationTypes, diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts index 5d222a6da1..9d777c6236 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/entities/Meta.ts @@ -279,57 +279,6 @@ export class Meta { }) public swPrivateKey: string | null; - @Column('boolean', { - default: false, - }) - public enableTwitterIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public twitterConsumerKey: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public twitterConsumerSecret: string | null; - - @Column('boolean', { - default: false, - }) - public enableGithubIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public githubClientId: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public githubClientSecret: string | null; - - @Column('boolean', { - default: false, - }) - public enableDiscordIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public discordClientId: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public discordClientSecret: string | null; - @Column('varchar', { length: 128, nullable: true, diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts index 86df8d5d98..1ff261cda3 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -184,11 +184,6 @@ export class UserProfile { @JoinColumn() public pinnedPage: Page | null; - @Column('jsonb', { - default: {}, - }) - public integrations: Record; - @Index() @Column('boolean', { default: false, select: false, diff --git a/packages/backend/src/models/schema/user.ts b/packages/backend/src/models/schema/user.ts index aac5e9332c..1fc9352539 100644 --- a/packages/backend/src/models/schema/user.ts +++ b/packages/backend/src/models/schema/user.ts @@ -323,10 +323,6 @@ export const packedMeDetailedOnlySchema = { type: 'boolean', nullable: false, optional: false, }, - integrations: { - type: 'object', - nullable: true, optional: false, - }, mutedWords: { type: 'array', nullable: false, optional: false, diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 024ddfe632..a43630c041 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -111,9 +111,6 @@ export class NodeinfoServerService { 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, diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 9dc1527698..b605f3c8ab 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -7,9 +7,6 @@ import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ServerService } from './ServerService.js'; import { WellKnownServerService } from './WellKnownServerService.js'; import { GetterService } from './api/GetterService.js'; -import { DiscordServerService } from './api/integration/DiscordServerService.js'; -import { GithubServerService } from './api/integration/GithubServerService.js'; -import { TwitterServerService } from './api/integration/TwitterServerService.js'; import { ChannelsService } from './api/stream/ChannelsService.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { ApiLoggerService } from './api/ApiLoggerService.js'; @@ -54,9 +51,6 @@ import { UserListChannelService } from './api/stream/channels/user-list.js'; ServerService, WellKnownServerService, GetterService, - DiscordServerService, - GithubServerService, - TwitterServerService, ChannelsService, ApiCallService, ApiLoggerService, diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index b29c9616cc..e406949cd4 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -12,9 +12,6 @@ import endpoints, { IEndpoint } from './endpoints.js'; import { ApiCallService } from './ApiCallService.js'; import { SignupApiService } from './SignupApiService.js'; import { SigninApiService } from './SigninApiService.js'; -import { GithubServerService } from './integration/GithubServerService.js'; -import { DiscordServerService } from './integration/DiscordServerService.js'; -import { TwitterServerService } from './integration/TwitterServerService.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @Injectable() @@ -38,9 +35,6 @@ export class ApiServerService { private apiCallService: ApiCallService, private signupApiService: SignupApiService, private signinApiService: SigninApiService, - private githubServerService: GithubServerService, - private discordServerService: DiscordServerService, - private twitterServerService: TwitterServerService, ) { //this.createServer = this.createServer.bind(this); } @@ -133,10 +127,6 @@ export class ApiServerService { fastify.post<{ Body: { code: string; } }>('/signup-pending', (request, reply) => this.signupApiService.signupPending(request, reply)); - fastify.register(this.discordServerService.create); - fastify.register(this.githubServerService.create); - fastify.register(this.twitterServerService.create); - fastify.get('/v1/instance/peers', async (request, reply) => { const instances = await this.instancesRepository.find({ select: ['host'], diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index b393827054..2b19104ea7 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -138,18 +138,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, @@ -223,30 +211,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, @@ -389,9 +353,6 @@ export default class extends Endpoint { 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, @@ -409,12 +370,6 @@ export default class extends Endpoint { setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically, enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, proxyAccountId: instance.proxyAccountId, - twitterConsumerKey: instance.twitterConsumerKey, - twitterConsumerSecret: instance.twitterConsumerSecret, - githubClientId: instance.githubClientId, - githubClientSecret: instance.githubClientSecret, - discordClientId: instance.discordClientId, - discordClientSecret: instance.discordClientSecret, summalyProxy: instance.summalyProxy, email: instance.email, smtpSecure: instance.smtpSecure, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 94603cc91a..823af6d8be 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -65,11 +65,6 @@ export default class extends Endpoint { }; } - const maskedKeys = ['accessToken', 'accessTokenSecret', 'refreshToken']; - Object.keys(profile.integrations).forEach(integration => { - maskedKeys.forEach(key => profile.integrations[integration][key] = ''); - }); - const signins = await this.signinsRepository.findBy({ userId: user.id }); const roles = await this.roleService.getUserRoles(user.id); @@ -84,7 +79,6 @@ export default class extends Endpoint { carefulBot: profile.carefulBot, injectFeaturedNote: profile.injectFeaturedNote, receiveAnnouncementEmail: profile.receiveAnnouncementEmail, - integrations: profile.integrations, mutedWords: profile.mutedWords, mutedInstances: profile.mutedInstances, mutingNotificationTypes: profile.mutingNotificationTypes, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index aacd634ed8..354ef22aa7 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -68,15 +68,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' }, @@ -270,42 +261,6 @@ export default class extends Endpoint { set.summalyProxy = ps.summalyProxy; } - if (ps.enableTwitterIntegration !== undefined) { - set.enableTwitterIntegration = ps.enableTwitterIntegration; - } - - if (ps.twitterConsumerKey !== undefined) { - set.twitterConsumerKey = ps.twitterConsumerKey; - } - - if (ps.twitterConsumerSecret !== undefined) { - set.twitterConsumerSecret = ps.twitterConsumerSecret; - } - - if (ps.enableGithubIntegration !== undefined) { - set.enableGithubIntegration = ps.enableGithubIntegration; - } - - if (ps.githubClientId !== undefined) { - set.githubClientId = ps.githubClientId; - } - - if (ps.githubClientSecret !== undefined) { - set.githubClientSecret = ps.githubClientSecret; - } - - if (ps.enableDiscordIntegration !== undefined) { - set.enableDiscordIntegration = ps.enableDiscordIntegration; - } - - if (ps.discordClientId !== undefined) { - set.discordClientId = ps.discordClientId; - } - - if (ps.discordClientSecret !== undefined) { - set.discordClientSecret = ps.discordClientSecret; - } - if (ps.enableEmail !== undefined) { set.enableEmail = ps.enableEmail; } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 89fa503173..3baf945323 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -169,18 +169,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, @@ -225,18 +213,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, @@ -325,11 +301,6 @@ export default class extends Endpoint { imageUrl: ad.imageUrl, })), enableEmail: instance.enableEmail, - - enableTwitterIntegration: instance.enableTwitterIntegration, - enableGithubIntegration: instance.enableGithubIntegration, - enableDiscordIntegration: instance.enableDiscordIntegration, - enableServiceWorker: instance.enableServiceWorker, translatorAvailable: instance.deeplAuthKey != null, @@ -358,9 +329,6 @@ export default class extends Endpoint { recaptcha: instance.enableRecaptcha, turnstile: instance.enableTurnstile, objectStorage: instance.useObjectStorage, - twitter: instance.enableTwitterIntegration, - github: instance.enableGithubIntegration, - discord: instance.enableDiscordIntegration, serviceWorker: instance.enableServiceWorker, miauth: true, }; diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts deleted file mode 100644 index cbced901e4..0000000000 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import Redis from 'ioredis'; -import { OAuth2 } from 'oauth'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import type { Config } from '@/config.js'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import type { ILocalUser } from '@/models/entities/User.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; -import { bindThis } from '@/decorators.js'; -import { SigninService } from '../SigninService.js'; -import type { FastifyInstance, FastifyRequest, FastifyPluginOptions } from 'fastify'; - -@Injectable() -export class DiscordServerService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.redis) - private redisClient: Redis.Redis, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - private userEntityService: UserEntityService, - private httpRequestService: HttpRequestService, - private globalEventService: GlobalEventService, - private metaService: MetaService, - private signinService: SigninService, - ) { - //this.create = this.create.bind(this); - } - - @bindThis - public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.get('/disconnect/discord', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.discord; - - await this.userProfilesRepository.update(user.id, { - integrations: profile.integrations, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return 'Discordの連携を解除しました :v:'; - }); - - const getOAuth2 = async () => { - const meta = await this.metaService.fetch(true); - - if (meta.enableDiscordIntegration) { - return new OAuth2( - meta.discordClientId!, - meta.discordClientSecret!, - 'https://discord.com/', - 'api/oauth2/authorize', - 'api/oauth2/token'); - } else { - return null; - } - }; - - fastify.get('/connect/discord', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const params = { - redirect_uri: `${this.config.url}/api/dc/cb`, - scope: ['identify'], - state: uuid(), - response_type: 'code', - }; - - this.redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get('/signin/discord', async (request, reply) => { - const sessid = uuid(); - - const params = { - redirect_uri: `${this.config.url}/api/dc/cb`, - scope: ['identify'], - state: uuid(), - response_type: 'code', - }; - - reply.setCookie('signin_with_discord_sid', sessid, { - path: '/', - secure: this.config.url.startsWith('https'), - httpOnly: true, - }); - - this.redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get<{ Querystring: { code: string; state: string; } }>('/dc/cb', async (request, reply) => { - const userToken = this.getUserToken(request); - - const oauth2 = await getOAuth2(); - - if (!userToken) { - const sessid = request.cookies['signin_with_discord_sid']; - - if (!sessid) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(sessid, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken(code, { - grant_type: 'authorization_code', - redirect_uri, - }, (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - })); - - const { id, username, discriminator } = (await this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', { - 'Authorization': `Bearer ${accessToken}`, - })) as Record; - - if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const profile = await this.userProfilesRepository.createQueryBuilder() - .where('"integrations"->\'discord\'->>\'id\' = :id', { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (profile == null) { - throw new FastifyReplyError(404, `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`); - } - - await this.userProfilesRepository.update(profile.userId, { - integrations: { - ...profile.integrations, - discord: { - id: id, - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - username: username, - discriminator: discriminator, - }, - }, - }); - - return this.signinService.signin(request, reply, await this.usersRepository.findOneBy({ id: profile.userId }) as ILocalUser, true); - } else { - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(userToken, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken(code, { - grant_type: 'authorization_code', - redirect_uri, - }, (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - })); - - const { id, username, discriminator } = (await this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', { - 'Authorization': `Bearer ${accessToken}`, - })) as Record; - if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - await this.userProfilesRepository.update(user.id, { - integrations: { - ...profile.integrations, - discord: { - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - id: id, - username: username, - discriminator: discriminator, - }, - }, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return `Discord: @${username}#${discriminator} を、Misskey: @${user.username} に接続しました!`; - } - }); - - done(); - } - - @bindThis - private getUserToken(request: FastifyRequest): string | null { - return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; - } - - @bindThis - private compareOrigin(request: FastifyRequest): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = request.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(this.config.url)); - } -} diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts deleted file mode 100644 index 76089c9359..0000000000 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import Redis from 'ioredis'; -import { OAuth2 } from 'oauth'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import type { Config } from '@/config.js'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import type { ILocalUser } from '@/models/entities/User.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; -import { bindThis } from '@/decorators.js'; -import { SigninService } from '../SigninService.js'; -import type { FastifyInstance, FastifyRequest, FastifyPluginOptions } from 'fastify'; - -@Injectable() -export class GithubServerService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.redis) - private redisClient: Redis.Redis, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - private userEntityService: UserEntityService, - private httpRequestService: HttpRequestService, - private globalEventService: GlobalEventService, - private metaService: MetaService, - private signinService: SigninService, - ) { - //this.create = this.create.bind(this); - } - - @bindThis - public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.get('/disconnect/github', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.github; - - await this.userProfilesRepository.update(user.id, { - integrations: profile.integrations, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return 'GitHubの連携を解除しました :v:'; - }); - - const getOath2 = async () => { - const meta = await this.metaService.fetch(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; - } - }; - - fastify.get('/connect/github', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const params = { - redirect_uri: `${this.config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid(), - }; - - this.redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOath2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get('/signin/github', async (request, reply) => { - const sessid = uuid(); - - const params = { - redirect_uri: `${this.config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid(), - }; - - reply.setCookie('signin_with_github_sid', sessid, { - path: '/', - secure: this.config.url.startsWith('https'), - httpOnly: true, - }); - - this.redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOath2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get<{ Querystring: { code: string; state: string; } }>('/gh/cb', async (request, reply) => { - const userToken = this.getUserToken(request); - - const oauth2 = await getOath2(); - - if (!userToken) { - const sessid = request.cookies['signin_with_github_sid']; - - if (!sessid) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(sessid, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken } = await new Promise<{ accessToken: string }>((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 this.httpRequestService.getJson('https://api.github.com/user', 'application/vnd.github.v3+json', { - 'Authorization': `bearer ${accessToken}`, - })) as Record; - if (typeof login !== 'string' || typeof id !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const link = await this.userProfilesRepository.createQueryBuilder() - .where('"integrations"->\'github\'->>\'id\' = :id', { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - throw new FastifyReplyError(404, `@${login}と連携しているMisskeyアカウントはありませんでした...`); - } - - return this.signinService.signin(request, reply, await this.usersRepository.findOneBy({ id: link.userId }) as ILocalUser, true); - } else { - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(userToken, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken } = await new Promise<{ accessToken: string }>((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 this.httpRequestService.getJson('https://api.github.com/user', 'application/vnd.github.v3+json', { - 'Authorization': `bearer ${accessToken}`, - })) as Record; - - if (typeof login !== 'string' || typeof id !== 'number') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - await this.userProfilesRepository.update(user.id, { - integrations: { - ...profile.integrations, - github: { - accessToken: accessToken, - id: id, - login: login, - }, - }, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`; - } - }); - - done(); - } - - @bindThis - private getUserToken(request: FastifyRequest): string | null { - return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; - } - - @bindThis - private compareOrigin(request: FastifyRequest): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = request.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(this.config.url)); - } -} diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts deleted file mode 100644 index f31a788d31..0000000000 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import Redis from 'ioredis'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import * as autwh from 'autwh'; -import type { Config } from '@/config.js'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import type { ILocalUser } from '@/models/entities/User.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; -import { bindThis } from '@/decorators.js'; -import { SigninService } from '../SigninService.js'; -import type { FastifyInstance, FastifyRequest, FastifyPluginOptions } from 'fastify'; - -@Injectable() -export class TwitterServerService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.redis) - private redisClient: Redis.Redis, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - private userEntityService: UserEntityService, - private httpRequestService: HttpRequestService, - private globalEventService: GlobalEventService, - private metaService: MetaService, - private signinService: SigninService, - ) { - //this.create = this.create.bind(this); - } - - @bindThis - public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.get('/disconnect/twitter', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (userToken == null) { - throw new FastifyReplyError(400, 'signin required'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.twitter; - - await this.userProfilesRepository.update(user.id, { - integrations: profile.integrations, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return 'Twitterの連携を解除しました :v:'; - }); - - const getTwAuth = async () => { - const meta = await this.metaService.fetch(true); - - if (meta.enableTwitterIntegration && meta.twitterConsumerKey && meta.twitterConsumerSecret) { - return autwh({ - consumerKey: meta.twitterConsumerKey, - consumerSecret: meta.twitterConsumerSecret, - callbackUrl: `${this.config.url}/api/tw/cb`, - }); - } else { - return null; - } - }; - - fastify.get('/connect/twitter', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (userToken == null) { - throw new FastifyReplyError(400, 'signin required'); - } - - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - this.redisClient.set(userToken, JSON.stringify(twCtx)); - reply.redirect(twCtx.url); - }); - - fastify.get('/signin/twitter', async (request, reply) => { - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - - const sessid = uuid(); - - this.redisClient.set(sessid, JSON.stringify(twCtx)); - - reply.setCookie('signin_with_twitter_sid', sessid, { - path: '/', - secure: this.config.url.startsWith('https'), - httpOnly: true, - }); - - reply.redirect(twCtx.url); - }); - - fastify.get('/tw/cb', async (request, reply) => { - const userToken = this.getUserToken(request); - - const twAuth = await getTwAuth(); - - if (userToken == null) { - const sessid = request.cookies['signin_with_twitter_sid']; - - if (sessid == null) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const get = new Promise((res, rej) => { - this.redisClient.get(sessid, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const verifier = request.query.oauth_verifier; - if (!verifier || typeof verifier !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const link = await this.userProfilesRepository.createQueryBuilder() - .where('"integrations"->\'twitter\'->>\'userId\' = :id', { id: result.userId }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - throw new FastifyReplyError(404, `@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`); - } - - return this.signinService.signin(request, reply, await this.usersRepository.findOneBy({ id: link.userId }) as ILocalUser, true); - } else { - const verifier = request.query.oauth_verifier; - - if (!verifier || typeof verifier !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const get = new Promise((res, rej) => { - this.redisClient.get(userToken, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - await this.userProfilesRepository.update(user.id, { - integrations: { - ...profile.integrations, - twitter: { - accessToken: result.accessToken, - accessTokenSecret: result.accessTokenSecret, - userId: result.userId, - screenName: result.screenName, - }, - }, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`; - } - }); - - done(); - } - - @bindThis - private getUserToken(request: FastifyRequest): string | null { - return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; - } - - @bindThis - private compareOrigin(request: FastifyRequest): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = request.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(this.config.url)); - } -} diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index d50d6c48ad..cc1a7c4af5 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -40,11 +40,6 @@ - diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 0166960ff6..9a07d3c959 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -164,11 +164,6 @@ const menuDef = $computed(() => [{ text: i18n.ts.relays, to: '/admin/relays', active: currentPage?.route.name === 'relays', - }, { - icon: 'ti ti-share', - text: i18n.ts.integration, - to: '/admin/integrations', - active: currentPage?.route.name === 'integrations', }, { icon: 'ti ti-ban', text: i18n.ts.instanceBlocking, diff --git a/packages/frontend/src/pages/admin/integrations.discord.vue b/packages/frontend/src/pages/admin/integrations.discord.vue deleted file mode 100644 index 68ea040d7e..0000000000 --- a/packages/frontend/src/pages/admin/integrations.discord.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - diff --git a/packages/frontend/src/pages/admin/integrations.github.vue b/packages/frontend/src/pages/admin/integrations.github.vue deleted file mode 100644 index 2bd7852773..0000000000 --- a/packages/frontend/src/pages/admin/integrations.github.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - diff --git a/packages/frontend/src/pages/admin/integrations.twitter.vue b/packages/frontend/src/pages/admin/integrations.twitter.vue deleted file mode 100644 index aa65833663..0000000000 --- a/packages/frontend/src/pages/admin/integrations.twitter.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - diff --git a/packages/frontend/src/pages/admin/integrations.vue b/packages/frontend/src/pages/admin/integrations.vue deleted file mode 100644 index 6888a492f6..0000000000 --- a/packages/frontend/src/pages/admin/integrations.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 4dbc6ec74c..8631f3e341 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -89,11 +89,6 @@ const menuDef = computed(() => [{ text: i18n.ts.email, to: '/settings/email', active: currentPage?.route.name === 'email', - }, { - icon: 'ti ti-share', - text: i18n.ts.integration, - to: '/settings/integration', - active: currentPage?.route.name === 'integration', }, { icon: 'ti ti-lock', text: i18n.ts.security, diff --git a/packages/frontend/src/pages/settings/integration.vue b/packages/frontend/src/pages/settings/integration.vue deleted file mode 100644 index 1e5a785465..0000000000 --- a/packages/frontend/src/pages/settings/integration.vue +++ /dev/null @@ -1,99 +0,0 @@ - - - diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 22106e1595..595b1f622a 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -70,10 +70,6 @@ export const routes = [{ path: '/email', name: 'email', component: page(() => import('./pages/settings/email.vue')), - }, { - path: '/integration', - name: 'integration', - component: page(() => import('./pages/settings/integration.vue')), }, { path: '/security', name: 'security', @@ -399,10 +395,6 @@ export const routes = [{ path: '/relays', name: 'relays', component: page(() => import('./pages/admin/relays.vue')), - }, { - path: '/integrations', - name: 'integrations', - component: page(() => import('./pages/admin/integrations.vue')), }, { path: '/instance-block', name: 'instance-block',