Skip to main content
Glama
canny.js6.67 kB
import axios from 'axios'; import { CONFIG } from '../config/config.js'; /** * Canny API Client * Implements Customer-Centric approach by providing reliable API access * Following Efficiency principle by implementing proper error handling and retries */ export class CannyClient { apiKey; baseUrl; client; rateLimitTracker = new Map(); constructor(apiKey, baseUrl = CONFIG.baseUrl) { this.apiKey = apiKey; this.baseUrl = baseUrl; this.client = axios.create({ baseURL: this.baseUrl, timeout: CONFIG.timeout, headers: { 'Content-Type': 'application/json', }, }); // Add request interceptor for API key and rate limiting this.client.interceptors.request.use((config) => { // Add API key to request if (config.method === 'get') { config.params = { ...config.params, apiKey: this.apiKey }; } else { config.data = { ...config.data, apiKey: this.apiKey }; } // Check rate limiting this.checkRateLimit(config.url || ''); return config; }, (error) => Promise.reject(error)); } checkRateLimit(endpoint) { const now = Date.now(); const windowMs = 60 * 1000; // 1 minute window if (!this.rateLimitTracker.has(endpoint)) { this.rateLimitTracker.set(endpoint, []); } const requests = this.rateLimitTracker.get(endpoint); // Remove old requests outside the window const validRequests = requests.filter(time => now - time < windowMs); if (validRequests.length >= CONFIG.rateLimit.requestsPerMinute) { throw new Error(`Rate limit exceeded for endpoint: ${endpoint}`); } validRequests.push(now); this.rateLimitTracker.set(endpoint, validRequests); } async makeRequest(config) { try { const response = await this.client.request(config); return { data: response.data, status: response.status, }; } catch (error) { if (axios.isAxiosError(error)) { return { error: error.response?.data?.error || error.message, status: error.response?.status || 500, }; } return { error: error instanceof Error ? error.message : 'Unknown error', status: 500, }; } } /** * Get all boards accessible to the API key */ async getBoards() { const response = await this.makeRequest({ method: 'GET', url: '/boards/list', }); // Handle the nested response structure if (response.data && response.data.boards) { return { data: response.data.boards, status: response.status, }; } return { data: [], status: response.status, error: response.error, }; } /** * Get posts from a specific board */ async getPosts(boardId, options) { return this.makeRequest({ method: 'GET', url: '/posts/list', params: { boardID: boardId, limit: options?.limit || 10, skip: options?.skip || 0, ...(options?.status && { status: options.status }), ...(options?.search && { search: options.search }), ...(options?.sort && { sort: options.sort }), }, }); } /** * Get a specific post by ID */ async getPost(postId) { return this.makeRequest({ method: 'GET', url: '/posts/retrieve', params: { id: postId }, }); } /** * Create a new post */ async createPost(data) { return this.makeRequest({ method: 'POST', url: '/posts/create', data, }); } /** * Update an existing post */ async updatePost(postId, data) { return this.makeRequest({ method: 'POST', url: '/posts/change_status', data: { postID: postId, ...data }, }); } /** * Search posts across all accessible boards */ async searchPosts(query, options) { return this.makeRequest({ method: 'GET', url: '/posts/list', params: { search: query, limit: options?.limit || 20, ...(options?.boardIDs && { boardIDs: options.boardIDs.join(',') }), ...(options?.status && { status: options.status }), }, }); } /** * Get categories from a specific board */ async getCategories(boardId) { const response = await this.makeRequest({ method: 'GET', url: '/categories/list', params: { boardID: boardId, }, }); // Handle the nested response structure if (response.data && response.data.categories) { return { data: response.data.categories, status: response.status, }; } return { data: [], status: response.status, error: response.error, }; } /** * Get comments from a specific post */ async getComments(postId, options) { return this.makeRequest({ method: 'GET', url: '/comments/list', params: { postID: postId, limit: options?.limit || 10, skip: options?.skip || 0, }, }); } /** * Get users from your Canny instance */ async getUsers(options) { return this.makeRequest({ method: 'GET', url: '/users/list', params: { limit: options?.limit || 10, skip: options?.skip || 0, ...(options?.search && { search: options.search }), }, }); } /** * Get tags from boards */ async getTags(options) { return this.makeRequest({ method: 'GET', url: '/tags/list', params: { limit: options?.limit || 20, ...(options?.boardId && { boardID: options.boardId }), }, }); } } //# sourceMappingURL=canny.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/itsocialist/canny-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server