// Elementor Data Service - Core data operations
import { AxiosInstance } from 'axios';
import { logger } from '../../utils/logger.js';
import { ErrorHandler, MCPError, ErrorCategory } from '../../utils/error-handler.js';
import { ElementorData, ElementorElement } from '../../types/elementor.js';
export class ElementorDataService {
constructor(private client: AxiosInstance) {}
/**
* Get Elementor data for a post/page
*/
async getElementorData(postId: number): Promise<ElementorData> {
return ErrorHandler.wrapAsync(async () => {
logger.debug(`Fetching Elementor data for post ${postId}`);
// Get page with context=edit to access meta fields
const response = await this.client.get(`/pages/${postId}`, {
params: { context: 'edit' }
});
const meta = response.data.meta;
const elementorDataString = meta._elementor_data;
if (!elementorDataString || elementorDataString === '[]') {
logger.warn(`No Elementor data found for post ${postId}`);
return { elements: [] };
}
try {
const elementorData = JSON.parse(elementorDataString);
logger.debug(`Successfully parsed Elementor data for post ${postId}`);
return { elements: elementorData };
} catch (error) {
throw new MCPError(
`Failed to parse Elementor data: ${error}`,
ErrorCategory.ELEMENTOR_DATA,
'PARSE_ERROR',
{ postId, error }
);
}
}, 'ElementorDataService.getElementorData');
}
/**
* Update Elementor data for a post/page
*/
async updateElementorData(postId: number, elementorData: ElementorData): Promise<void> {
return ErrorHandler.wrapAsync(async () => {
logger.info(`Updating Elementor data for post ${postId}`);
// Convert to JSON string
const elementorDataString = JSON.stringify(elementorData.elements);
// Update via WordPress REST API
await this.client.post(`/pages/${postId}`, {
meta: {
_elementor_data: elementorDataString,
_elementor_edit_mode: 'builder'
}
});
logger.info(`Elementor data updated successfully for post ${postId}`);
}, 'ElementorDataService.updateElementorData');
}
/**
* Get chunked Elementor data for large pages
*/
async getElementorDataChunked(postId: number, chunkSize: number = 50): Promise<any> {
return ErrorHandler.wrapAsync(async () => {
const elementorData = await this.getElementorData(postId);
const chunks: any[] = [];
let currentChunk: any[] = [];
const flattenElements = (elements: ElementorElement[]): ElementorElement[] => {
const flat: ElementorElement[] = [];
for (const element of elements) {
flat.push(element);
if (element.elements && element.elements.length > 0) {
flat.push(...flattenElements(element.elements));
}
}
return flat;
};
const allElements = flattenElements(elementorData.elements);
for (let i = 0; i < allElements.length; i += chunkSize) {
currentChunk = allElements.slice(i, i + chunkSize);
chunks.push({
chunkIndex: Math.floor(i / chunkSize),
elements: currentChunk,
totalElements: allElements.length
});
}
return {
postId,
totalChunks: chunks.length,
totalElements: allElements.length,
chunkSize,
chunks
};
}, 'ElementorDataService.getElementorDataChunked');
}
/**
* Backup Elementor data
*/
async backupElementorData(postId: number): Promise<any> {
return ErrorHandler.wrapAsync(async () => {
logger.info(`Creating backup of Elementor data for post ${postId}`);
const elementorData = await this.getElementorData(postId);
const timestamp = new Date().toISOString();
const backup = {
postId,
timestamp,
data: elementorData,
checksum: this.generateChecksum(JSON.stringify(elementorData))
};
logger.info(`Backup created for post ${postId}`, { timestamp });
return backup;
}, 'ElementorDataService.backupElementorData');
}
/**
* Clear Elementor cache
*/
async clearElementorCache(): Promise<void> {
return ErrorHandler.wrapAsync(async () => {
logger.info('Clearing Elementor cache');
// This would typically call Elementor's cache clearing endpoint
// For now, we'll just log it as most cache clearing happens server-side
logger.info('Elementor cache clear request completed');
}, 'ElementorDataService.clearElementorCache');
}
/**
* Generate simple checksum for data verification
*/
private generateChecksum(data: string): string {
let hash = 0;
for (let i = 0; i < data.length; i++) {
const char = data.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash).toString(36);
}
}