477 lines
14 KiB
TypeScript
477 lines
14 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
Param,
|
|
Post,
|
|
Put,
|
|
Query,
|
|
} from '@nestjs/common';
|
|
import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service';
|
|
import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager';
|
|
import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service';
|
|
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
|
|
import { Organization, User } from '@prisma/client';
|
|
import { IntegrationFunctionDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.function.dto';
|
|
import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability';
|
|
import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing';
|
|
import { ApiTags } from '@nestjs/swagger';
|
|
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
|
|
import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service';
|
|
import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto';
|
|
import { PlugDto } from '@gitroom/nestjs-libraries/dtos/plugs/plug.dto';
|
|
import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract';
|
|
|
|
import { timer } from '@gitroom/helpers/utils/timer';
|
|
import { TelegramProvider } from '@gitroom/nestjs-libraries/integrations/social/telegram.provider';
|
|
import { MoltbookProvider } from '@gitroom/nestjs-libraries/integrations/social/moltbook.provider';
|
|
import {
|
|
AuthorizationActions,
|
|
Sections,
|
|
} from '@gitroom/backend/services/auth/permissions/permission.exception.class';
|
|
import { uniqBy } from 'lodash';
|
|
import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service';
|
|
|
|
@ApiTags('Integrations')
|
|
@Controller('/integrations')
|
|
export class IntegrationsController {
|
|
constructor(
|
|
private _integrationManager: IntegrationManager,
|
|
private _integrationService: IntegrationService,
|
|
private _postService: PostsService,
|
|
private _refreshIntegrationService: RefreshIntegrationService
|
|
) {}
|
|
|
|
@Post('/provider/:id/connect')
|
|
@CheckPolicies([AuthorizationActions.Create, Sections.CHANNEL])
|
|
async saveProviderPage(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Param('id') id: string,
|
|
@Body() body: any
|
|
) {
|
|
return this._integrationService.saveProviderPage(org.id, id, body);
|
|
}
|
|
|
|
@Get('/:identifier/internal-plugs')
|
|
getInternalPlugs(@Param('identifier') identifier: string) {
|
|
return this._integrationManager.getInternalPlugs(identifier);
|
|
}
|
|
|
|
@Get('/customers')
|
|
getCustomers(@GetOrgFromRequest() org: Organization) {
|
|
return this._integrationService.customers(org.id);
|
|
}
|
|
|
|
@Put('/:id/group')
|
|
async updateIntegrationGroup(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Param('id') id: string,
|
|
@Body() body: { group: string }
|
|
) {
|
|
return this._integrationService.updateIntegrationGroup(
|
|
org.id,
|
|
id,
|
|
body.group
|
|
);
|
|
}
|
|
|
|
@Put('/:id/customer-name')
|
|
async updateOnCustomerName(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Param('id') id: string,
|
|
@Body() body: { name: string }
|
|
) {
|
|
return this._integrationService.updateOnCustomerName(org.id, id, body.name);
|
|
}
|
|
|
|
@Get('/list')
|
|
async getIntegrationList(@GetOrgFromRequest() org: Organization) {
|
|
return {
|
|
integrations: await Promise.all(
|
|
(
|
|
await this._integrationService.getIntegrationsList(org.id)
|
|
).map(async (p) => {
|
|
const findIntegration = this._integrationManager.getSocialIntegration(
|
|
p.providerIdentifier
|
|
);
|
|
return {
|
|
name: p.name,
|
|
id: p.id,
|
|
internalId: p.internalId,
|
|
disabled: p.disabled,
|
|
editor: findIntegration.editor,
|
|
picture: p.picture || '/no-picture.jpg',
|
|
identifier: p.providerIdentifier,
|
|
inBetweenSteps: p.inBetweenSteps,
|
|
refreshNeeded: p.refreshNeeded,
|
|
isCustomFields: !!findIntegration.customFields,
|
|
...(findIntegration.customFields
|
|
? { customFields: await findIntegration.customFields() }
|
|
: {}),
|
|
display: p.profile,
|
|
type: p.type,
|
|
time: JSON.parse(p.postingTimes),
|
|
changeProfilePicture: !!findIntegration?.changeProfilePicture,
|
|
changeNickName: !!findIntegration?.changeNickname,
|
|
customer: p.customer,
|
|
additionalSettings: p.additionalSettings || '[]',
|
|
};
|
|
})
|
|
),
|
|
};
|
|
}
|
|
|
|
@Post('/:id/settings')
|
|
async updateProviderSettings(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Param('id') id: string,
|
|
@Body('additionalSettings') body: string
|
|
) {
|
|
if (typeof body !== 'string') {
|
|
throw new Error('Invalid body');
|
|
}
|
|
|
|
await this._integrationService.updateProviderSettings(org.id, id, body);
|
|
}
|
|
@Post('/:id/nickname')
|
|
async setNickname(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Param('id') id: string,
|
|
@Body() body: { name: string; picture: string }
|
|
) {
|
|
const integration = await this._integrationService.getIntegrationById(
|
|
org.id,
|
|
id
|
|
);
|
|
if (!integration) {
|
|
throw new Error('Invalid integration');
|
|
}
|
|
|
|
const manager = this._integrationManager.getSocialIntegration(
|
|
integration.providerIdentifier
|
|
);
|
|
if (!manager.changeProfilePicture && !manager.changeNickname) {
|
|
throw new Error('Invalid integration');
|
|
}
|
|
|
|
const { url } = manager.changeProfilePicture
|
|
? await manager.changeProfilePicture(
|
|
integration.internalId,
|
|
integration.token,
|
|
body.picture
|
|
)
|
|
: { url: '' };
|
|
|
|
const { name } = manager.changeNickname
|
|
? await manager.changeNickname(
|
|
integration.internalId,
|
|
integration.token,
|
|
body.name
|
|
)
|
|
: { name: '' };
|
|
|
|
return this._integrationService.updateNameAndUrl(id, name, url);
|
|
}
|
|
|
|
@Get('/:id')
|
|
getSingleIntegration(
|
|
@Param('id') id: string,
|
|
@Query('order') order: string,
|
|
@GetUserFromRequest() user: User,
|
|
@GetOrgFromRequest() org: Organization
|
|
) {
|
|
return this._integrationService.getIntegrationForOrder(
|
|
id,
|
|
order,
|
|
user.id,
|
|
org.id
|
|
);
|
|
}
|
|
|
|
@Get('/social/:integration')
|
|
@CheckPolicies([AuthorizationActions.Create, Sections.CHANNEL])
|
|
async getIntegrationUrl(
|
|
@Param('integration') integration: string,
|
|
@Query('refresh') refresh: string,
|
|
@Query('externalUrl') externalUrl: string,
|
|
@Query('onboarding') onboarding: string,
|
|
@GetOrgFromRequest() org: Organization
|
|
) {
|
|
if (
|
|
!this._integrationManager
|
|
.getAllowedSocialsIntegrations()
|
|
.includes(integration)
|
|
) {
|
|
throw new Error('Integration not allowed');
|
|
}
|
|
|
|
const integrationProvider =
|
|
this._integrationManager.getSocialIntegration(integration);
|
|
|
|
if (integrationProvider.externalUrl && !externalUrl) {
|
|
throw new Error('Missing external url');
|
|
}
|
|
|
|
try {
|
|
const getExternalUrl = integrationProvider.externalUrl
|
|
? {
|
|
...(await integrationProvider.externalUrl(externalUrl)),
|
|
instanceUrl: externalUrl,
|
|
}
|
|
: undefined;
|
|
|
|
const { codeVerifier, state, url } =
|
|
await integrationProvider.generateAuthUrl(getExternalUrl);
|
|
|
|
if (refresh) {
|
|
await ioRedis.set(`refresh:${state}`, refresh, 'EX', 3600);
|
|
}
|
|
|
|
if (onboarding === 'true') {
|
|
await ioRedis.set(`onboarding:${state}`, 'true', 'EX', 3600);
|
|
}
|
|
|
|
await ioRedis.set(`organization:${state}`, org.id, 'EX', 3600);
|
|
await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 3600);
|
|
await ioRedis.set(
|
|
`external:${state}`,
|
|
JSON.stringify(getExternalUrl),
|
|
'EX',
|
|
3600
|
|
);
|
|
|
|
return { url };
|
|
} catch (err) {
|
|
return { err: true };
|
|
}
|
|
}
|
|
|
|
@Post('/:id/time')
|
|
async setTime(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Param('id') id: string,
|
|
@Body() body: IntegrationTimeDto
|
|
) {
|
|
return this._integrationService.setTimes(org.id, id, body);
|
|
}
|
|
|
|
@Post('/mentions')
|
|
async mentions(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Body() body: IntegrationFunctionDto
|
|
) {
|
|
const getIntegration = await this._integrationService.getIntegrationById(
|
|
org.id,
|
|
body.id
|
|
);
|
|
if (!getIntegration) {
|
|
throw new Error('Invalid integration');
|
|
}
|
|
|
|
let newList: any[] | { none: true } = [];
|
|
try {
|
|
newList = (await this.functionIntegration(org, body)) || [];
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
|
|
if (!Array.isArray(newList) && newList?.none) {
|
|
return newList;
|
|
}
|
|
|
|
const list = await this._integrationService.getMentions(
|
|
getIntegration.providerIdentifier,
|
|
body?.data?.query
|
|
);
|
|
|
|
if (Array.isArray(newList) && newList.length) {
|
|
await this._integrationService.insertMentions(
|
|
getIntegration.providerIdentifier,
|
|
newList
|
|
.map((p: any) => ({
|
|
name: p.label || '',
|
|
username: p.id || '',
|
|
image: p.image || '',
|
|
doNotCache: p.doNotCache || false,
|
|
}))
|
|
.filter((f: any) => f.name && !f.doNotCache)
|
|
);
|
|
}
|
|
|
|
return uniqBy(
|
|
[
|
|
...list.map((p) => ({
|
|
id: p.username,
|
|
image: p.image,
|
|
label: p.name,
|
|
})),
|
|
...(newList as any[]),
|
|
],
|
|
(p) => p.id
|
|
).filter((f) => f.label && f.id);
|
|
}
|
|
|
|
@Post('/function')
|
|
async functionIntegration(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Body() body: IntegrationFunctionDto
|
|
): Promise<any> {
|
|
const getIntegration = await this._integrationService.getIntegrationById(
|
|
org.id,
|
|
body.id
|
|
);
|
|
if (!getIntegration) {
|
|
throw new Error('Invalid integration');
|
|
}
|
|
|
|
const integrationProvider = this._integrationManager.getSocialIntegration(
|
|
getIntegration.providerIdentifier
|
|
);
|
|
if (!integrationProvider) {
|
|
throw new Error('Invalid provider');
|
|
}
|
|
|
|
// @ts-ignore
|
|
if (integrationProvider[body.name]) {
|
|
try {
|
|
// @ts-ignore
|
|
const load = await integrationProvider[body.name](
|
|
getIntegration.token,
|
|
body.data,
|
|
getIntegration.internalId,
|
|
getIntegration
|
|
);
|
|
|
|
return load;
|
|
} catch (err) {
|
|
if (err instanceof RefreshToken) {
|
|
const data = await this._refreshIntegrationService.refresh(
|
|
getIntegration
|
|
);
|
|
|
|
if (!data) {
|
|
return;
|
|
}
|
|
|
|
const { accessToken } = data;
|
|
|
|
if (accessToken) {
|
|
if (integrationProvider.refreshWait) {
|
|
await timer(10000);
|
|
}
|
|
return this.functionIntegration(org, body);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
throw new Error('Function not found');
|
|
}
|
|
|
|
@Post('/disable')
|
|
disableChannel(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Body('id') id: string
|
|
) {
|
|
return this._integrationService.disableChannel(org.id, id);
|
|
}
|
|
|
|
@Post('/enable')
|
|
enableChannel(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Body('id') id: string
|
|
) {
|
|
return this._integrationService.enableChannel(
|
|
org.id,
|
|
// @ts-ignore
|
|
org?.subscription?.totalChannels || pricing.FREE.channel,
|
|
id
|
|
);
|
|
}
|
|
|
|
@Delete('/')
|
|
async deleteChannel(
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Body('id') id: string
|
|
) {
|
|
const isTherePosts = await this._integrationService.getPostsForChannel(
|
|
org.id,
|
|
id
|
|
);
|
|
if (isTherePosts.length) {
|
|
for (const post of isTherePosts) {
|
|
this._postService.deletePost(org.id, post.group).catch((err) => {});
|
|
}
|
|
}
|
|
|
|
return this._integrationService.deleteChannel(org.id, id);
|
|
}
|
|
|
|
@Get('/plug/list')
|
|
async getPlugList() {
|
|
return { plugs: this._integrationManager.getAllPlugs() };
|
|
}
|
|
|
|
@Get('/:id/plugs')
|
|
async getPlugsByIntegrationId(
|
|
@Param('id') id: string,
|
|
@GetOrgFromRequest() org: Organization
|
|
) {
|
|
return this._integrationService.getPlugsByIntegrationId(org.id, id);
|
|
}
|
|
|
|
@Post('/:id/plugs')
|
|
async postPlugsByIntegrationId(
|
|
@Param('id') id: string,
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Body() body: PlugDto
|
|
) {
|
|
return this._integrationService.createOrUpdatePlug(org.id, id, body);
|
|
}
|
|
|
|
@Put('/plugs/:id/activate')
|
|
async changePlugActivation(
|
|
@Param('id') id: string,
|
|
@GetOrgFromRequest() org: Organization,
|
|
@Body('status') status: boolean
|
|
) {
|
|
return this._integrationService.changePlugActivation(org.id, id, status);
|
|
}
|
|
|
|
@Get('/telegram/updates')
|
|
async getUpdates(@Query() query: { word: string; id?: number }) {
|
|
return new TelegramProvider().getBotId(query);
|
|
}
|
|
|
|
@Post('/moltbook/register')
|
|
async moltbookRegister(
|
|
@Body() body: { name: string; description: string }
|
|
) {
|
|
try {
|
|
const provider = new MoltbookProvider();
|
|
const result = await provider.registerAgent(body.name, body.description);
|
|
return {
|
|
apiKey: result.api_key,
|
|
claimUrl: result.claim_url,
|
|
verificationCode: result.verification_code,
|
|
};
|
|
} catch (err: any) {
|
|
return { error: err.message || 'Registration failed' };
|
|
}
|
|
}
|
|
|
|
@Get('/moltbook/status')
|
|
async moltbookStatus(@Query('apiKey') apiKey: string) {
|
|
try {
|
|
const provider = new MoltbookProvider();
|
|
const result = await provider.checkAgentStatus(apiKey);
|
|
return { claimed: result?.status === 'claimed' };
|
|
} catch (err) {
|
|
return { claimed: false };
|
|
}
|
|
}
|
|
}
|