import axios, { AxiosInstance, AxiosError } from 'axios';
export class NocoBaseClient {
private client: AxiosInstance;
private token: string | null = null;
constructor(
private baseURL: string,
private config: {
apiToken?: string,
email?: string,
password?: string
}
) {
this.client = axios.create({
baseURL: `${baseURL}/api`,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
validateStatus: (status) => status < 500, // Handle 4xx manually
});
// Initialize with token if provided
if (config.apiToken) {
this.setToken(config.apiToken);
}
}
private setToken(token: string) {
this.token = token;
this.client.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}
async authenticate() {
if (this.token) return;
if (this.config.apiToken) {
this.setToken(this.config.apiToken);
return;
}
if (this.config.email && this.config.password) {
try {
const response = await this.client.post('/auth:signIn', {
email: this.config.email,
password: this.config.password,
});
if (response.data?.data?.token) {
this.setToken(response.data.data.token);
} else {
throw new Error('Login failed: No token received');
}
} catch (error: any) {
console.error('[Client] Authentication failed:', error.message);
throw error;
}
} else {
throw new Error('No configuration provided for authentication');
}
}
// --- Raw Request Methods ---
async get(url: string, config?: any) {
await this.authenticate();
return this.client.get(url, config);
}
async post(url: string, data?: any, config?: any) {
await this.authenticate();
return this.client.post(url, data, config);
}
async patch(url: string, data?: any, config?: any) {
await this.authenticate();
return this.client.patch(url, data, config);
}
async delete(url: string, config?: any) {
await this.authenticate();
return this.client.delete(url, config);
}
// --- Route Specific Methods ---
async createRoute(type: 'desktop' | 'mobile', data: any) {
const collection = type === 'mobile' ? 'mobileRoutes' : 'desktopRoutes';
return this.post(`/${collection}:create`, data);
}
async listRoutes(type: 'desktop' | 'mobile', params: any) {
const collection = type === 'mobile' ? 'mobileRoutes' : 'desktopRoutes';
return this.get(`/${collection}:list`, { params });
}
async getRoute(type: 'desktop' | 'mobile', routeUid: string) {
const collection = type === 'mobile' ? 'mobileRoutes' : 'desktopRoutes';
return this.get(`/${collection}:get`, {
params: { filterByTk: routeUid }
});
}
async updateRoute(type: 'desktop' | 'mobile', routeUid: string, updates: any) {
const collection = type === 'mobile' ? 'mobileRoutes' : 'desktopRoutes';
return this.patch(`/${collection}:update`, updates, {
params: { filterByTk: routeUid }
});
}
async deleteRoute(type: 'desktop' | 'mobile', routeUid: string, cascade: boolean = true) {
const collection = type === 'mobile' ? 'mobileRoutes' : 'desktopRoutes';
return this.post(`/${collection}:destroy`, null, {
params: { filterByTk: routeUid, cascade }
});
}
// --- Schema Specific Methods (Standardized based on OpenAPI Spec) ---
/**
* Get JSON schema (full tree)
*/
async getSchema(schemaUid: string, includeChildren: boolean = true) {
const endpoint = includeChildren
? `/uiSchemas:getJsonSchema/${schemaUid}`
: `/uiSchemas:get/${schemaUid}`;
return this.get(endpoint);
}
/**
* Get only properties of a schema node
*/
async getSchemaProperties(schemaUid: string) {
return this.get(`/uiSchemas:getProperties/${schemaUid}`);
}
/**
* Insert a new UI schema as a root node
*/
async insertSchema(schema: any) {
return this.post('/uiSchemas:insert', schema);
}
/**
* Insert a node adjacent to another node (Using path parameter {uid})
*/
async insertSchemaAdjacent(targetUid: string, position: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd', schema: any, wrap: any = null) {
return this.post(`/uiSchemas:insertAdjacent/${targetUid}?position=${position}`, {
schema,
wrap
});
}
/**
* Update an UI schema node
*/
async patchSchema(data: { 'x-uid': string;[key: string]: any }) {
return this.post('/uiSchemas:patch', data);
}
/**
* Remove an UI schema node (Using Spec-compliant :remove/{uid})
*/
async removeSchema(schemaUid: string) {
return this.post(`/uiSchemas:remove/${schemaUid}`);
}
/**
* Save UI schema as template
*/
async saveAsTemplate(schemaUid: string, data: {
name: string;
collectionName?: string;
componentName?: string;
resourceName?: string;
key?: string;
}) {
return this.post(`/uiSchemas:saveAsTemplate?filterByTk=${schemaUid}`, {
uid: schemaUid,
...data
});
}
// --- Generic Resource Methods ---
async resourceList(resource: string, params: any = {}) {
return this.get(`/${resource}:list`, { params });
}
async resourceGet(resource: string, filterByTk: string | number, params: any = {}) {
return this.get(`/${resource}:get`, {
params: { ...params, filterByTk }
});
}
async resourceCreate(resource: string, data: any) {
return this.post(`/${resource}:create`, data);
}
async resourceUpdate(resource: string, filterByTk: string | number, values: any) {
return this.post(`/${resource}:update`, values, {
params: { filterByTk }
});
}
async resourceDelete(resource: string, filterByTk: string | number) {
return this.post(`/${resource}:destroy`, null, {
params: { filterByTk }
});
}
// --- Workflow Specific Methods ---
async triggerWorkflow(workflowId: string | number, context: any = {}) {
// According to spec: /workflows:trigger?triggerWorkflows=1
return this.post(`/workflows:trigger?triggerWorkflows=${workflowId}`, context);
}
// --- System Specific Methods ---
async getSystemSettings() {
return this.get('/systemSettings:get');
}
async updateSystemSettings(values: any) {
return this.post('/systemSettings:update', values);
}
async appRestart() {
return this.post('/app:restart');
}
async appGetInfo() {
return this.get('/app:getInfo');
}
// --- FlowModels Methods (V2/Modern Pages) ---
/**
* Save a flow model (for V2 pages: columns, actions, blocks)
*/
async flowModelsSave(model: any) {
return this.post('/flowModels:save', model);
}
/**
* Find one flow model
*/
async flowModelsFindOne(parentId: string, subKey: string) {
return this.get('/flowModels:findOne', {
params: { parentId, subKey }
});
}
/**
* List flow models
*/
async flowModelsList(parentId: string) {
return this.get('/flowModels:list', {
params: { filter: { parentId } }
});
}
/**
* Delete flow model
*/
async flowModelsDestroy(uid: string) {
return this.post('/flowModels:destroy', null, {
params: { filterByTk: uid }
});
}
}