import https from 'https';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import type {
ApiConfig,
ApiResponse,
DockerImage,
DockerTag,
GitLabReference,
GitLabVersion,
GitLabPipeline,
} from '../types/index.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/**
* DevOps API Client with PEM certificate authentication
*/
export class DevOpsApiClient {
private baseUrl: string;
private httpsAgent: https.Agent;
constructor(config?: Partial<ApiConfig>) {
this.baseUrl = config?.baseUrl || 'https://labs.pointware.com';
// Load PEM certificate
const certPath = config?.certPath || path.join(__dirname, '../../certs/client-cert.pem');
let cert: Buffer;
try {
cert = fs.readFileSync(certPath);
} catch (error) {
throw new Error(`Failed to load PEM certificate from ${certPath}: ${error}`);
}
// Create HTTPS agent with PEM certificate
this.httpsAgent = new https.Agent({
cert,
key: cert, // Using same file for both cert and key (combined PEM)
rejectUnauthorized: true,
});
}
/**
* Make HTTP request with PEM authentication
*/
private async request<T>(endpoint: string, method: string = 'GET'): Promise<ApiResponse<T>> {
return new Promise((resolve, reject) => {
const url = new URL(endpoint, this.baseUrl);
const options: https.RequestOptions = {
method,
agent: this.httpsAgent,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
};
const req = https.request(url, options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
const jsonData = data ? JSON.parse(data) : {};
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
resolve({
success: true,
data: jsonData as T,
});
} else {
resolve({
success: false,
error: jsonData.error || `HTTP ${res.statusCode}`,
message: jsonData.message,
});
}
} catch (error) {
reject(new Error(`Failed to parse response: ${error}`));
}
});
});
req.on('error', (error) => {
reject(new Error(`Request failed: ${error.message}`));
});
req.end();
});
}
// ============ Docker Image APIs ============
/**
* Search Docker images by keyword
*/
async searchDockerImages(keyword: string): Promise<ApiResponse<DockerImage[]>> {
return this.request<DockerImage[]>(`/api/v1/docker/images?keyword=${encodeURIComponent(keyword)}`);
}
/**
* Get tags for a Docker image
*/
async getDockerImageTags(imageName: string): Promise<ApiResponse<DockerTag[]>> {
return this.request<DockerTag[]>(`/api/v1/docker/images/${encodeURIComponent(imageName)}/tags`);
}
// ============ GitLab CI APIs ============
/**
* Search GitLab CI reference YAML by keyword
*/
async searchGitLabReferences(keyword: string): Promise<ApiResponse<GitLabReference[]>> {
return this.request<GitLabReference[]>(`/api/v1/gitlab/references?keyword=${encodeURIComponent(keyword)}`);
}
/**
* Get versions for a GitLab CI reference
*/
async getGitLabReferenceVersions(referenceName: string): Promise<ApiResponse<GitLabVersion[]>> {
return this.request<GitLabVersion[]>(`/api/v1/gitlab/references/${encodeURIComponent(referenceName)}/versions`);
}
/**
* Get GitLab CI custom pipeline information
*/
async getGitLabPipeline(pipelineName: string): Promise<ApiResponse<GitLabPipeline>> {
return this.request<GitLabPipeline>(`/api/v1/gitlab/pipelines/${encodeURIComponent(pipelineName)}`);
}
}