twenty-mcp-server
Version:
Easy-to-install Model Context Protocol server for Twenty CRM. Try instantly with 'npx twenty-mcp-server setup' or install globally for permanent use.
131 lines • 4.94 kB
JavaScript
import { getKeyStorageService } from '../auth/key-storage.js';
/**
* API routes for managing user API keys
*/
export class ApiKeyRoutes {
keyStorage = getKeyStorageService();
/**
* Handle API key management requests
*/
async handle(req, res) {
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type');
// Must be authenticated
if (!req.auth) {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'unauthorized',
error_description: 'Authentication required'
}));
return;
}
const url = new URL(req.url, `http://localhost`);
const path = url.pathname;
// Route to appropriate handler
if (path === '/api/keys' && req.method === 'GET') {
await this.getKeyMetadata(req, res);
}
else if (path === '/api/keys' && req.method === 'POST') {
await this.storeKey(req, res);
}
else if (path === '/api/keys' && req.method === 'DELETE') {
await this.deleteKey(req, res);
}
else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
}
}
/**
* Get API key metadata (without revealing the key)
*/
async getKeyMetadata(req, res) {
try {
const metadata = await this.keyStorage.getApiKeyMetadata(req.auth.userId);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
hasKey: metadata.hasKey,
updatedAt: metadata.updatedAt,
baseUrl: metadata.baseUrl,
}));
}
catch (error) {
console.error('Error getting key metadata:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'internal_error',
error_description: 'Failed to retrieve key metadata'
}));
}
}
/**
* Store a new API key
*/
async storeKey(req, res) {
try {
// Parse request body
const chunks = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', async () => {
try {
const body = JSON.parse(Buffer.concat(chunks).toString());
if (!body.apiKey) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'invalid_request',
error_description: 'API key is required'
}));
return;
}
// Store the key
await this.keyStorage.storeApiKey(req.auth.userId, body.apiKey, body.baseUrl);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
message: 'API key stored successfully'
}));
}
catch (error) {
console.error('Error parsing request:', error);
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'invalid_request',
error_description: 'Invalid JSON body'
}));
}
});
}
catch (error) {
console.error('Error storing key:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'internal_error',
error_description: 'Failed to store API key'
}));
}
}
/**
* Delete the stored API key
*/
async deleteKey(req, res) {
try {
await this.keyStorage.deleteApiKey(req.auth.userId);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
message: 'API key deleted successfully'
}));
}
catch (error) {
console.error('Error deleting key:', error);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'internal_error',
error_description: 'Failed to delete API key'
}));
}
}
}
//# sourceMappingURL=api-keys.js.map