const BASE_URL = "https://ploi.io/api";
export interface Server {
id: number;
name: string;
ip_address: string;
internal_ip: string | null;
type: string;
php_version: string;
mysql_version: string | null;
sites_count: number;
status: string;
created_at: string;
php_cli_version: string;
monitoring: boolean;
reboot_required: boolean;
}
export interface Site {
id: number;
server_id: number;
domain: string;
deploy_script: string | null;
web_directory: string;
project_type: string;
php_version: string;
status: string;
has_repository: boolean;
zero_downtime_deployment: boolean;
created_at: string;
}
export interface Database {
id: number;
server_id: number;
name: string;
type: string;
status: string;
created_at: string;
}
export interface User {
id: number;
name: string;
email: string;
}
interface ApiResponse<T> {
data: T;
}
interface PaginatedResponse<T> {
data: T[];
links: {
first: string | null;
last: string | null;
prev: string | null;
next: string | null;
};
meta: {
current_page: number;
last_page: number;
per_page: number;
total: number;
};
}
export class PloiClient {
private token: string;
constructor(token: string) {
this.token = token;
}
private async request<T>(
method: string,
path: string,
body?: unknown
): Promise<T> {
const url = `${BASE_URL}${path}`;
const response = await fetch(url, {
method,
headers: {
Authorization: `Bearer ${this.token}`,
Accept: "application/json",
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(
`Ploi API error (${response.status}): ${errorText || response.statusText}`
);
}
if (response.status === 204) {
return undefined as T;
}
return response.json() as Promise<T>;
}
// Servers
async listServers(): Promise<Server[]> {
const response = await this.request<PaginatedResponse<Server>>(
"GET",
"/servers"
);
return response.data;
}
async getServer(id: number): Promise<Server> {
const response = await this.request<ApiResponse<Server>>(
"GET",
`/servers/${id}`
);
return response.data;
}
async restartServer(id: number): Promise<void> {
await this.request<void>("POST", `/servers/${id}/restart`);
}
async getServerLogs(id: number, type: string = "auth"): Promise<string> {
const response = await this.request<ApiResponse<string | null>>(
"GET",
`/servers/${id}/logs/${type}`
);
return response.data ?? "No logs available";
}
// Sites
async listSites(serverId: number): Promise<Site[]> {
const response = await this.request<PaginatedResponse<Site>>(
"GET",
`/servers/${serverId}/sites`
);
return response.data;
}
async getSite(serverId: number, siteId: number): Promise<Site> {
const response = await this.request<ApiResponse<Site>>(
"GET",
`/servers/${serverId}/sites/${siteId}`
);
return response.data;
}
async deploySite(serverId: number, siteId: number): Promise<void> {
await this.request<void>(
"POST",
`/servers/${serverId}/sites/${siteId}/deploy`
);
}
async getSiteLogs(serverId: number, siteId: number): Promise<string> {
const response = await this.request<ApiResponse<string | null>>(
"GET",
`/servers/${serverId}/sites/${siteId}/log`
);
return response.data ?? "No logs available";
}
async suspendSite(serverId: number, siteId: number): Promise<void> {
await this.request<void>(
"POST",
`/servers/${serverId}/sites/${siteId}/suspend`
);
}
async resumeSite(serverId: number, siteId: number): Promise<void> {
await this.request<void>(
"POST",
`/servers/${serverId}/sites/${siteId}/resume`
);
}
// Databases
async listDatabases(serverId: number): Promise<Database[]> {
const response = await this.request<PaginatedResponse<Database>>(
"GET",
`/servers/${serverId}/databases`
);
return response.data;
}
async createDatabaseBackup(
serverId: number,
databaseId: number
): Promise<void> {
await this.request<void>(
"POST",
`/servers/${serverId}/databases/${databaseId}/backup`
);
}
// User
async getUser(): Promise<User> {
const response = await this.request<ApiResponse<User>>("GET", "/user");
return response.data;
}
}