From 562b02310f9f994f98943c9d93d2f9d948858c32 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> 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<string, any>; - @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<typeof meta, typeof paramDef> { 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<typeof meta, typeof paramDef> { 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<typeof meta, typeof paramDef> { }; } - const maskedKeys = ['accessToken', 'accessTokenSecret', 'refreshToken']; - Object.keys(profile.integrations).forEach(integration => { - maskedKeys.forEach(key => profile.integrations[integration][key] = '<MASKED>'); - }); - 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<typeof meta, typeof paramDef> { 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<typeof meta, typeof paramDef> { 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<typeof meta, typeof paramDef> { 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<typeof meta, typeof paramDef> { 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<any>((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<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 this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', { - 'Authorization': `Bearer ${accessToken}`, - })) as Record<string, unknown>; - - 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<any>((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<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 this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', { - 'Authorization': `Bearer ${accessToken}`, - })) as Record<string, unknown>; - 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<any>((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<string, unknown>; - 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<any>((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<string, unknown>; - - 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<any>((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<any>((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 @@ </div> </div> </div> - <div class="social"> - <a v-if="meta && meta.enableTwitterIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/twitter`"><i class="ti ti-brand-twitter" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Twitter' }) }}</a> - <a v-if="meta && meta.enableGithubIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/github`"><i class="ti ti-brand-github" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'GitHub' }) }}</a> - <a v-if="meta && meta.enableDiscordIntegration" class="_borderButton _margin" :href="`${apiUrl}/signin/discord`"><i class="ti ti-brand-discord" style="margin-right: 4px;"></i>{{ $t('signinWith', { x: 'Discord' }) }}</a> - </div> </form> </template> 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 @@ -<template> -<FormSuspense :p="init"> - <div class="_gaps_m"> - <MkSwitch v-model="enableDiscordIntegration"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - - <template v-if="enableDiscordIntegration"> - <FormInfo>Callback URL: {{ `${uri}/api/dc/cb` }}</FormInfo> - - <MkInput v-model="discordClientId"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Client ID</template> - </MkInput> - - <MkInput v-model="discordClientSecret"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Client Secret</template> - </MkInput> - </template> - - <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - </div> -</FormSuspense> -</template> - -<script lang="ts" setup> -import { } from 'vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkButton from '@/components/MkButton.vue'; -import FormInfo from '@/components/MkInfo.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> 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 @@ -<template> -<FormSuspense :p="init"> - <div class="_gaps_m"> - <MkSwitch v-model="enableGithubIntegration"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - - <template v-if="enableGithubIntegration"> - <FormInfo>Callback URL: {{ `${uri}/api/gh/cb` }}</FormInfo> - - <MkInput v-model="githubClientId"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Client ID</template> - </MkInput> - - <MkInput v-model="githubClientSecret"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Client Secret</template> - </MkInput> - </template> - - <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - </div> -</FormSuspense> -</template> - -<script lang="ts" setup> -import { } from 'vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkButton from '@/components/MkButton.vue'; -import FormInfo from '@/components/MkInfo.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> 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 @@ -<template> -<FormSuspense :p="init"> - <div class="_gaps_m"> - <MkSwitch v-model="enableTwitterIntegration"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - - <template v-if="enableTwitterIntegration"> - <FormInfo>Callback URL: {{ `${uri}/api/tw/cb` }}</FormInfo> - - <MkInput v-model="twitterConsumerKey"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Consumer Key</template> - </MkInput> - - <MkInput v-model="twitterConsumerSecret"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Consumer Secret</template> - </MkInput> - </template> - - <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - </div> -</FormSuspense> -</template> - -<script lang="ts" setup> -import { defineComponent } from 'vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkButton from '@/components/MkButton.vue'; -import FormInfo from '@/components/MkInfo.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> 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 @@ -<template> -<MkStickyContainer> - <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :content-max="700" :margin-min="16" :margin-max="32"> - <FormSuspense :p="init"> - <div class="_gaps_m"> - <MkFolder> - <template #icon><i class="ti ti-brand-twitter"></i></template> - <template #label>Twitter</template> - <template #suffix>{{ enableTwitterIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> - <XTwitter/> - </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-brand-github"></i></template> - <template #label>GitHub</template> - <template #suffix>{{ enableGithubIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> - <XGithub/> - </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-brand-discord"></i></template> - <template #label>Discord</template> - <template #suffix>{{ enableDiscordIntegration ? i18n.ts.enabled : i18n.ts.disabled }}</template> - <XDiscord/> - </MkFolder> - </div> - </FormSuspense> - </MkSpacer> -</MkStickyContainer> -</template> - -<script lang="ts" setup> -import { } from 'vue'; -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 MkFolder from '@/components/MkFolder.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; -} - -const headerActions = $computed(() => []); - -const headerTabs = $computed(() => []); - -definePageMetadata({ - title: i18n.ts.integration, - icon: 'ti ti-share', -}); -</script> 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 @@ -<template> -<div class="_gaps_m"> - <FormSection v-if="instance.enableTwitterIntegration"> - <template #label><i class="ti ti-brand-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="ti ti-brand-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="ti ti-brand-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/MkButton.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(); - } - }); -}); - -const headerActions = $computed(() => []); - -const headerTabs = $computed(() => []); - -definePageMetadata({ - title: i18n.ts.integration, - icon: 'ti ti-share', -}); -</script> 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',