Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions integrations/cloudflare/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@core/cloudflare",
"version": "0.1.0",
"description": "Cloudflare integration for CORE - manage zones, DNS records, and cache",
"main": "./bin/index.cjs",
"type": "module",
"files": [
"cloudflare",
"bin"
],
"bin": {
"cloudflare": "./bin/index.cjs"
},
"scripts": {
"build": "rimraf bin && bun build src/index.ts --outfile dist/index.js --target node",
"lint": "eslint --ext js,ts,tsx src/ --fix",
"prettier": "prettier --config .prettierrc --write ."
},
"devDependencies": {
"@babel/preset-typescript": "^7.26.0",
"@types/node": "^18.0.20",
"eslint": "^9.24.0",
"eslint-config-prettier": "^10.1.2",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-unused-imports": "^2.0.0",
"prettier": "^3.4.2",
"rimraf": "^3.0.2",
"tslib": "^2.8.1",
"typescript": "^4.7.2",
"tsup": "^8.0.1"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"axios": "^1.7.9",
"commander": "^12.0.0",
"@redplanethq/sdk": "0.1.14",
"zod": "^3.22.4",
"zod-to-json-schema": "^3.22.4"
}
}
66 changes: 66 additions & 0 deletions integrations/cloudflare/scripts/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import pg from 'pg';
const { Client } = pg;

async function main() {
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
console.error('DATABASE_URL environment variable is required');
process.exit(1);
}

const client = new Client({ connectionString });

const spec = {
name: 'Cloudflare',
key: 'cloudflare',
description:
'Connect your Cloudflare account to CORE. Manage DNS records, inspect zone configurations, and purge cache — all from your workspace.',
icon: 'cloudflare',
mcp: {
type: 'cli',
},
auth: {
api_key: {
fields: [
{
name: 'api_token',
label: 'API Token',
placeholder: 'your-cloudflare-api-token',
description:
'Create an API Token in Cloudflare → My Profile → API Tokens. Grant the token Zone:Read and DNS:Edit permissions for the zones you want to manage.',
},
],
},
},
};

try {
await client.connect();

await client.query(
`
INSERT INTO core."IntegrationDefinitionV2" ("id", "name", "slug", "description", "icon", "spec", "config", "version", "url", "updatedAt", "createdAt")
VALUES (gen_random_uuid(), 'Cloudflare', 'cloudflare', 'Connect your Cloudflare account to CORE. Manage DNS records, inspect zone configurations, and purge cache — all from your workspace.', 'cloudflare', $1, $2, '0.1.0', $3, NOW(), NOW())
ON CONFLICT (name) DO UPDATE SET
"slug" = EXCLUDED."slug",
"description" = EXCLUDED."description",
"icon" = EXCLUDED."icon",
"spec" = EXCLUDED."spec",
"config" = EXCLUDED."config",
"version" = EXCLUDED."version",
"url" = EXCLUDED."url",
"updatedAt" = NOW()
RETURNING *;
`,
[JSON.stringify(spec), JSON.stringify({}), '../../integrations/cloudflare/bin/index.cjs']
);

console.log('Cloudflare integration registered successfully in the database.');
} catch (error) {
console.error('Error registering Cloudflare integration:', error);
} finally {
await client.end();
}
}

main().catch(console.error);
37 changes: 37 additions & 0 deletions integrations/cloudflare/src/account-create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { getCloudflareClient } from './utils';

export async function integrationCreate(data: Record<string, string>) {
const { api_token } = data;

const client = getCloudflareClient(api_token);

// Verify the token and retrieve the associated user/account info
const verifyResponse = await client.get('/user/tokens/verify');
const tokenInfo = verifyResponse.data?.result;

if (!tokenInfo || tokenInfo.status !== 'active') {
throw new Error('Cloudflare API token is invalid or inactive');
}

// Fetch the user account to use as account identifier
const userResponse = await client.get('/user');
const user = userResponse.data?.result;

return [
{
type: 'account',
data: {
settings: {
email: user?.email ?? '',
name: user?.first_name ? `${user.first_name} ${user.last_name ?? ''}`.trim() : '',
token_id: tokenInfo.id,
token_name: tokenInfo.name,
},
accountId: `cloudflare-${user?.id ?? tokenInfo.id}`,
config: {
api_token,
},
},
},
];
}
87 changes: 87 additions & 0 deletions integrations/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
IntegrationCLI,
IntegrationEventPayload,
IntegrationEventType,
Spec,
} from '@redplanethq/sdk';

import { integrationCreate } from './account-create';
import { getTools, callTool } from './mcp';
import { CloudflareConfig } from './utils';
import { fileURLToPath } from 'url';

export async function run(eventPayload: IntegrationEventPayload) {
switch (eventPayload.event) {
case IntegrationEventType.SETUP:
return await integrationCreate(eventPayload.eventBody);

case IntegrationEventType.GET_TOOLS: {
return getTools();
}

case IntegrationEventType.CALL_TOOL: {
const config = eventPayload.config as unknown as CloudflareConfig;

if (!config?.api_token) {
return {
content: [{ type: 'text', text: 'Error: No API token provided in config' }],
isError: true,
};
}

const { name, arguments: args } = eventPayload.eventBody;
return await callTool(name, args, config);
}

default:
return [
{
type: 'message',
data: { message: `The event payload type is ${eventPayload.event}` },
},
];
}
}

class CloudflareCLI extends IntegrationCLI {
constructor() {
super('cloudflare', '1.0.0');
}

protected async handleEvent(eventPayload: IntegrationEventPayload): Promise<any> {
return await run(eventPayload);
}

protected async getSpec(): Promise<Spec> {
return {
name: 'Cloudflare',
key: 'cloudflare',
description:
'Connect your Cloudflare account to CORE. Manage DNS records, inspect zone configurations, and purge cache — all from your workspace.',
icon: 'cloudflare',
// Cast to allow v2 fields array auth config
auth: {
api_key: {
fields: [
{
name: 'api_token',
label: 'API Token',
placeholder: 'your-cloudflare-api-token',
description:
'Create an API Token in Cloudflare → My Profile → API Tokens. Grant the token Zone:Read and DNS:Edit permissions for the zones you want to manage.',
},
],
},
} as any,
};
}
}

function main() {
const cloudflareCLI = new CloudflareCLI();
cloudflareCLI.parse();
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
main();
}
Loading