Public Access
bd5f676e0a
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
# Conflicts: # cli/manifest_licensing.php
123 lines
3.4 KiB
TypeScript
123 lines
3.4 KiB
TypeScript
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
*
|
|
* This file is part of a Moko Consulting project.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* FILE INFORMATION
|
|
* DEFGROUP: dolibarr-api-mcp.Client
|
|
* INGROUP: dolibarr-api-mcp
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/dolibarr-api-mcp
|
|
* PATH: /src/client.ts
|
|
* VERSION: 01.00.00
|
|
* BRIEF: HTTP client for Dolibarr REST API
|
|
*/
|
|
|
|
import * as https from 'node:https';
|
|
import * as http from 'node:http';
|
|
import type { DolibarrConnection, ApiResponse } from './types.js';
|
|
|
|
const API_PREFIX = '/api/index.php';
|
|
const TIMEOUT_MS = 30_000;
|
|
|
|
export class DolibarrClient {
|
|
private readonly base_url: string;
|
|
private readonly headers: Record<string, string>;
|
|
private readonly insecure: boolean;
|
|
|
|
constructor(conn: DolibarrConnection) {
|
|
this.base_url = conn.baseUrl.replace(/\/+$/, '') + API_PREFIX;
|
|
this.headers = {
|
|
'DOLAPIKEY': conn.apiKey,
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
};
|
|
this.insecure = conn.insecure ?? false;
|
|
}
|
|
|
|
async get(endpoint: string, params?: Record<string, string>): Promise<ApiResponse> {
|
|
const url = this.buildUrl(endpoint, params);
|
|
return this.request(url, 'GET');
|
|
}
|
|
|
|
async post(endpoint: string, body?: unknown): Promise<ApiResponse> {
|
|
const url = this.buildUrl(endpoint);
|
|
return this.request(url, 'POST', body);
|
|
}
|
|
|
|
async put(endpoint: string, body: unknown): Promise<ApiResponse> {
|
|
const url = this.buildUrl(endpoint);
|
|
return this.request(url, 'PUT', body);
|
|
}
|
|
|
|
async delete(endpoint: string): Promise<ApiResponse> {
|
|
const url = this.buildUrl(endpoint);
|
|
return this.request(url, 'DELETE');
|
|
}
|
|
|
|
private buildUrl(endpoint: string, params?: Record<string, string>): string {
|
|
const path = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
const url = new URL(`${this.base_url}${path}`);
|
|
if (params) {
|
|
for (const [key, value] of Object.entries(params)) {
|
|
url.searchParams.set(key, value);
|
|
}
|
|
}
|
|
return url.toString();
|
|
}
|
|
|
|
private request(url: string, method: string, body?: unknown): Promise<ApiResponse> {
|
|
return new Promise((resolve, reject) => {
|
|
const parsed = new URL(url);
|
|
const is_https = parsed.protocol === 'https:';
|
|
const transport = is_https ? https : http;
|
|
|
|
const options: https.RequestOptions = {
|
|
hostname: parsed.hostname,
|
|
port: parsed.port || (is_https ? 443 : 80),
|
|
path: parsed.pathname + parsed.search,
|
|
method,
|
|
headers: { ...this.headers },
|
|
timeout: TIMEOUT_MS,
|
|
};
|
|
|
|
if (this.insecure && is_https) {
|
|
options.rejectUnauthorized = false;
|
|
}
|
|
|
|
const payload = body !== undefined ? JSON.stringify(body) : undefined;
|
|
if (payload) {
|
|
(options.headers as Record<string, string>)['Content-Length'] = Buffer.byteLength(payload).toString();
|
|
}
|
|
|
|
const req = transport.request(options, (res) => {
|
|
const chunks: Buffer[] = [];
|
|
res.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
res.on('end', () => {
|
|
const raw = Buffer.concat(chunks).toString('utf-8');
|
|
let data: unknown;
|
|
|
|
try {
|
|
data = JSON.parse(raw);
|
|
} catch {
|
|
data = raw;
|
|
}
|
|
|
|
resolve({ status: res.statusCode ?? 0, data });
|
|
});
|
|
});
|
|
|
|
req.on('error', (err) => reject(err));
|
|
req.on('timeout', () => {
|
|
req.destroy();
|
|
reject(new Error('Request timed out'));
|
|
});
|
|
|
|
if (payload) {
|
|
req.write(payload);
|
|
}
|
|
req.end();
|
|
});
|
|
}
|
|
}
|