// WordPress API utilities
import axios from 'axios';
import FormData from 'form-data';
import fs from 'fs';
import path from 'path';
import tinify from 'tinify';
let wpConfig = {
siteUrl: '',
username: '',
isAuthenticated: false
};
export function setWordPressConfig(config) {
wpConfig = { ...wpConfig, ...config };
}
export function getWordPressConfig() {
return { ...wpConfig };
}
export async function getAuthHeader() {
try {
if (wpConfig.appPassword) {
const credentials = Buffer.from(`${wpConfig.username}:${wpConfig.appPassword}`).toString('base64');
return `Basic ${credentials}`;
}
if (!wpConfig.password) {
throw new Error('No password or app password provided for WordPress authentication');
}
const credentials = Buffer.from(`${wpConfig.username}:${wpConfig.password}`).toString('base64');
return `Basic ${credentials}`;
}
catch (error) {
console.error('Authentication error:', error);
throw new Error('Failed to create authentication header: ' + (error.message || 'Unknown error'));
}
}
export function formatErrorResponse(error) {
if (isAxiosError(error)) {
return {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
message: error.message
};
}
return error.message || 'Unknown error';
}
export async function testAuthentication() {
try {
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/users/me`, { headers: { 'Authorization': authHeader } });
const roles = response.data.roles || ['contributor'];
wpConfig.isAuthenticated = true;
return {
success: true,
userInfo: {
id: response.data.id,
name: response.data.name || response.data.slug || 'Unknown',
roles: Array.isArray(roles) ? roles : [String(roles)]
}
};
}
catch (error) {
wpConfig.isAuthenticated = false;
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
export async function testSiteConnection(siteUrl) {
try {
const response = await axios.get(`${siteUrl}/wp-json/`, {
timeout: 5000
});
return response.data?.namespaces?.includes('wp/v2') ?? false;
}
catch {
return false;
}
}
export async function createPost(title, content, status = 'draft', excerpt = '', categories = [], tags = []) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/posts`, { title, content, status, excerpt, categories, tags }, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
post: {
id: response.data.id,
title: response.data.title.rendered,
link: response.data.link,
status: response.data.status
}
};
}
catch (error) {
console.error('Error creating post:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function getCategories() {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/categories?per_page=100`, { headers: { 'Authorization': authHeader } });
return {
success: true,
categories: response.data.map(category => ({
id: category.id,
name: category.name,
slug: category.slug,
count: category.count
}))
};
}
catch (error) {
console.error('Error fetching categories:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function getTags() {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/tags?per_page=100`, { headers: { 'Authorization': authHeader } });
return {
success: true,
tags: response.data
};
}
catch (error) {
console.error('Error fetching tags:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
function isAxiosError(error) {
return (typeof error === 'object' &&
error !== null &&
'isAxiosError' in error &&
error.isAxiosError === true);
}
export async function updatePost(postId, updates) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.put(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}`, updates, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
post: {
id: response.data.id,
title: response.data.title.rendered,
link: response.data.link,
status: response.data.status
}
};
}
catch (error) {
console.error('Error updating post:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function deletePost(postId, force = false) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const url = `${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}${force ? '?force=true' : ''}`;
const response = await axios.delete(url, {
headers: { 'Authorization': authHeader }
});
return {
success: true,
deleted: response.data.deleted || false,
previous: response.data.previous
};
}
catch (error) {
console.error('Error deleting post:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function getPost(postId, includeRevisions = false) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}`, { headers: { 'Authorization': authHeader } });
let revisions = [];
if (includeRevisions) {
try {
const revisionsResponse = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}/revisions`, { headers: { 'Authorization': authHeader } });
revisions = revisionsResponse.data;
}
catch (revError) {
console.warn('Could not fetch revisions:', revError);
}
}
return {
success: true,
post: {
id: response.data.id,
title: response.data.title.rendered,
content: response.data.content.rendered,
excerpt: response.data.excerpt.rendered,
status: response.data.status,
link: response.data.link,
date: response.data.date,
modified: response.data.modified,
categories: response.data.categories,
tags: response.data.tags,
author: response.data.author
},
revisions: includeRevisions ? revisions : undefined
};
}
catch (error) {
console.error('Error getting post:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function listPosts(options = {}) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const params = new URLSearchParams();
if (options.page)
params.append('page', options.page.toString());
if (options.perPage)
params.append('per_page', options.perPage.toString());
if (options.status)
params.append('status', options.status);
if (options.author)
params.append('author', options.author.toString());
if (options.categories?.length)
params.append('categories', options.categories.join(','));
if (options.tags?.length)
params.append('tags', options.tags.join(','));
if (options.search)
params.append('search', options.search);
if (options.orderBy)
params.append('orderby', options.orderBy);
if (options.order)
params.append('order', options.order);
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/posts?${params.toString()}`, { headers: { 'Authorization': authHeader } });
return {
success: true,
posts: response.data.map((post) => ({
id: post.id,
title: post.title.rendered,
excerpt: post.excerpt.rendered,
status: post.status,
link: post.link,
date: post.date,
modified: post.modified,
categories: post.categories,
tags: post.tags,
author: post.author
})),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '1'),
totalPosts: parseInt(response.headers['x-wp-total'] || '0')
};
}
catch (error) {
console.error('Error listing posts:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
// New taxonomy management methods
export async function createCategory(data) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/categories`, data, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
category: response.data
};
}
catch (error) {
console.error('Error creating category:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function createTag(data) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/tags`, data, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
tag: response.data
};
}
catch (error) {
console.error('Error creating tag:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function updateCategory(categoryId, updates) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.put(`${wpConfig.siteUrl}/wp-json/wp/v2/categories/${categoryId}`, updates, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
category: response.data
};
}
catch (error) {
console.error('Error updating category:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function updateTag(tagId, updates) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.put(`${wpConfig.siteUrl}/wp-json/wp/v2/tags/${tagId}`, updates, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
tag: response.data
};
}
catch (error) {
console.error('Error updating tag:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function deleteCategory(categoryId, force = false) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const url = `${wpConfig.siteUrl}/wp-json/wp/v2/categories/${categoryId}${force ? '?force=true' : ''}`;
const response = await axios.delete(url, {
headers: { 'Authorization': authHeader }
});
return {
success: true,
deleted: response.data.deleted || false,
previous: response.data.previous
};
}
catch (error) {
console.error('Error deleting category:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function deleteTag(tagId, force = false) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const url = `${wpConfig.siteUrl}/wp-json/wp/v2/tags/${tagId}${force ? '?force=true' : ''}`;
const response = await axios.delete(url, {
headers: { 'Authorization': authHeader }
});
return {
success: true,
deleted: response.data.deleted || false,
previous: response.data.previous
};
}
catch (error) {
console.error('Error deleting tag:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function mergeCategories(sourceCategoryId, targetCategoryId, deleteSource = true) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
// Get posts from source category
const postsResponse = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/posts?categories=${sourceCategoryId}&per_page=100`, { headers: { 'Authorization': authHeader } });
let mergedCount = 0;
// Update each post to use target category
for (const post of postsResponse.data) {
const currentCategories = post.categories.filter((id) => id !== sourceCategoryId);
if (!currentCategories.includes(targetCategoryId)) {
currentCategories.push(targetCategoryId);
}
await axios.put(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${post.id}`, { categories: currentCategories }, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
mergedCount++;
}
// Delete source category if requested
if (deleteSource) {
await deleteCategory(sourceCategoryId, true);
}
return {
success: true,
mergedPosts: mergedCount
};
}
catch (error) {
console.error('Error merging categories:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function assignCategoriesToPosts(postIds, categoryIds, replaceExisting = false) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
let updatedCount = 0;
for (const postId of postIds) {
let categories = categoryIds;
if (!replaceExisting) {
// Get current categories and merge
const postResponse = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}`, { headers: { 'Authorization': authHeader } });
const currentCategories = postResponse.data.categories || [];
categories = [...new Set([...currentCategories, ...categoryIds])];
}
await axios.put(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}`, { categories }, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
updatedCount++;
}
return {
success: true,
updatedPosts: updatedCount
};
}
catch (error) {
console.error('Error assigning categories to posts:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function assignTagsToPosts(postIds, tagIds, replaceExisting = false) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
let updatedCount = 0;
for (const postId of postIds) {
let tags = tagIds;
if (!replaceExisting) {
// Get current tags and merge
const postResponse = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}`, { headers: { 'Authorization': authHeader } });
const currentTags = postResponse.data.tags || [];
tags = [...new Set([...currentTags, ...tagIds])];
}
await axios.put(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}`, { tags }, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
updatedCount++;
}
return {
success: true,
updatedPosts: updatedCount
};
}
catch (error) {
console.error('Error assigning tags to posts:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function listTaxonomies(type = "all") {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/taxonomies`, { headers: { 'Authorization': authHeader } });
let taxonomies = Object.values(response.data);
if (type === "builtin") {
taxonomies = taxonomies.filter(tax => ['category', 'post_tag'].includes(tax.name));
}
else if (type === "custom") {
taxonomies = taxonomies.filter(tax => !['category', 'post_tag'].includes(tax.name));
}
return {
success: true,
taxonomies
};
}
catch (error) {
console.error('Error listing taxonomies:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
// User Tools
export async function listUsers(options = {}) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const params = new URLSearchParams();
if (options.page)
params.append('page', options.page.toString());
if (options.perPage)
params.append('per_page', options.perPage.toString());
if (options.role)
params.append('roles', options.role);
if (options.search)
params.append('search', options.search);
if (options.orderBy)
params.append('orderby', options.orderBy);
if (options.order)
params.append('order', options.order);
if (options.registeredAfter)
params.append('after', options.registeredAfter);
if (options.registeredBefore)
params.append('before', options.registeredBefore);
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/users?${params.toString()}`, { headers: { 'Authorization': authHeader } });
const totalPages = parseInt(response.headers['x-wp-totalpages'] || '1');
return {
success: true,
users: response.data,
totalPages
};
}
catch (error) {
console.error('Error listing users:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function createUser(userData) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const payload = {
username: userData.username,
email: userData.email,
password: userData.password,
first_name: userData.firstName || '',
last_name: userData.lastName || '',
name: userData.displayName || userData.username,
roles: userData.role ? [userData.role] : ['subscriber'],
description: userData.bio || '',
url: userData.website || ''
};
const authHeader = await getAuthHeader();
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/users`, payload, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
user: response.data
};
}
catch (error) {
console.error('Error creating user:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function updateUser(userId, userData) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const payload = {};
if (userData.email)
payload.email = userData.email;
if (userData.firstName)
payload.first_name = userData.firstName;
if (userData.lastName)
payload.last_name = userData.lastName;
if (userData.displayName)
payload.name = userData.displayName;
if (userData.bio)
payload.description = userData.bio;
if (userData.website)
payload.url = userData.website;
if (userData.password)
payload.password = userData.password;
const authHeader = await getAuthHeader();
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/users/${userId}`, payload, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
user: response.data
};
}
catch (error) {
console.error('Error updating user:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function disableUser(userId, reason) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.delete(`${wpConfig.siteUrl}/wp-json/wp/v2/users/${userId}?reassign=1`, { headers: { 'Authorization': authHeader } });
return {
success: true,
message: `User ${userId} has been disabled${reason ? ` (Reason: ${reason})` : ''}`
};
}
catch (error) {
console.error('Error disabling user:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function resetUserPassword(options) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
let userId = options.userId;
if (!userId && options.email) {
const usersResult = await listUsers({ search: options.email, perPage: 1 });
if (!usersResult.success || !usersResult.users?.length) {
throw new Error('User not found with provided email');
}
userId = usersResult.users[0].id;
}
if (!userId)
throw new Error('User ID could not be determined');
const newPassword = Math.random().toString(36).slice(-12);
const updateResult = await updateUser(userId, { password: newPassword });
if (!updateResult.success) {
throw new Error('Failed to reset password');
}
return {
success: true,
message: `Password reset for user ${userId}. ${options.sendEmail ? 'Reset notification sent via email.' : 'New password generated.'}`,
resetLink: options.sendEmail ? undefined : `Temporary password: ${newPassword}`
};
}
catch (error) {
console.error('Error resetting user password:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function setUserRole(userId, role, removeOtherRoles = true) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const payload = {
roles: removeOtherRoles ? [role] : undefined,
role: !removeOtherRoles ? role : undefined
};
const authHeader = await getAuthHeader();
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/users/${userId}`, payload, { headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' } });
return {
success: true,
user: response.data
};
}
catch (error) {
console.error('Error setting user role:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function listUserRoles(includeCapabilities = false) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/types/post`, { headers: { 'Authorization': authHeader } });
const defaultRoles = [
{
name: 'administrator',
display_name: 'Administrator',
capabilities: includeCapabilities ? {
'switch_themes': true,
'edit_themes': true,
'activate_plugins': true,
'edit_plugins': true,
'edit_users': true,
'edit_files': true,
'manage_options': true,
'moderate_comments': true,
'manage_categories': true,
'manage_links': true,
'upload_files': true,
'import': true,
'unfiltered_html': true,
'edit_posts': true,
'edit_others_posts': true,
'edit_published_posts': true,
'publish_posts': true,
'edit_pages': true,
'read': true,
'level_10': true,
'level_9': true,
'level_8': true,
'level_7': true,
'level_6': true,
'level_5': true,
'level_4': true,
'level_3': true,
'level_2': true,
'level_1': true,
'level_0': true
} : {}
},
{
name: 'editor',
display_name: 'Editor',
capabilities: includeCapabilities ? {
'moderate_comments': true,
'manage_categories': true,
'manage_links': true,
'upload_files': true,
'unfiltered_html': true,
'edit_posts': true,
'edit_others_posts': true,
'edit_published_posts': true,
'publish_posts': true,
'edit_pages': true,
'read': true,
'level_7': true,
'level_6': true,
'level_5': true,
'level_4': true,
'level_3': true,
'level_2': true,
'level_1': true,
'level_0': true
} : {}
},
{
name: 'author',
display_name: 'Author',
capabilities: includeCapabilities ? {
'upload_files': true,
'edit_posts': true,
'edit_published_posts': true,
'publish_posts': true,
'read': true,
'level_2': true,
'level_1': true,
'level_0': true
} : {}
},
{
name: 'contributor',
display_name: 'Contributor',
capabilities: includeCapabilities ? {
'edit_posts': true,
'read': true,
'level_1': true,
'level_0': true
} : {}
},
{
name: 'subscriber',
display_name: 'Subscriber',
capabilities: includeCapabilities ? {
'read': true,
'level_0': true
} : {}
}
];
return {
success: true,
roles: defaultRoles
};
}
catch (error) {
console.error('Error listing user roles:', error);
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
// Media Tools
export async function uploadMedia(source, options = {}) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const formData = new FormData();
if (source.startsWith('http')) {
const response = await axios.get(source, { responseType: 'stream' });
formData.append('file', response.data, path.basename(source));
}
else {
formData.append('file', fs.createReadStream(source), path.basename(source));
}
if (options.title)
formData.append('title', options.title);
if (options.caption)
formData.append('caption', options.caption);
if (options.altText)
formData.append('alt_text', options.altText);
if (options.description)
formData.append('description', options.description);
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/media`, formData, {
headers: {
'Authorization': authHeader,
...formData.getHeaders()
}
});
return {
success: true,
media: {
id: response.data.id,
title: response.data.title.rendered,
caption: response.data.caption.rendered,
altText: response.data.alt_text,
description: response.data.description.rendered,
mediaType: response.data.media_type,
mimeType: response.data.mime_type,
sourceUrl: response.data.source_url,
width: response.data.media_details?.width,
height: response.data.media_details?.height,
filesize: response.data.media_details?.filesize,
date: response.data.date,
link: response.data.link,
slug: response.data.slug
}
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function listMedia(options = {}) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const params = new URLSearchParams();
if (options.page)
params.append('page', options.page.toString());
if (options.perPage)
params.append('per_page', options.perPage.toString());
if (options.mediaType && options.mediaType !== 'any')
params.append('media_type', options.mediaType);
if (options.mimeType)
params.append('mime_type', options.mimeType);
if (options.orderBy)
params.append('orderby', options.orderBy);
if (options.order)
params.append('order', options.order);
if (options.parent)
params.append('parent', options.parent.toString());
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/media?${params.toString()}`, { headers: { 'Authorization': authHeader } });
const totalPages = parseInt(response.headers['x-wp-totalpages'] || '1');
return {
success: true,
media: response.data.map(item => ({
id: item.id,
title: item.title.rendered,
caption: item.caption.rendered,
altText: item.alt_text,
description: item.description.rendered,
mediaType: item.media_type,
mimeType: item.mime_type,
sourceUrl: item.source_url,
width: item.media_details?.width,
height: item.media_details?.height,
filesize: item.media_details?.filesize,
date: item.date,
link: item.link,
slug: item.slug
})),
totalPages
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function searchMedia(options) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const params = new URLSearchParams();
params.append('search', options.query);
if (options.mediaType && options.mediaType !== 'any')
params.append('media_type', options.mediaType);
if (options.dateAfter)
params.append('after', options.dateAfter);
if (options.dateBefore)
params.append('before', options.dateBefore);
if (options.limit)
params.append('per_page', options.limit.toString());
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/media?${params.toString()}`, { headers: { 'Authorization': authHeader } });
return {
success: true,
media: response.data.map(item => ({
id: item.id,
title: item.title.rendered,
caption: item.caption.rendered,
altText: item.alt_text,
description: item.description.rendered,
mediaType: item.media_type,
mimeType: item.mime_type,
sourceUrl: item.source_url,
width: item.media_details?.width,
height: item.media_details?.height,
filesize: item.media_details?.filesize,
date: item.date,
link: item.link,
slug: item.slug
}))
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function editMediaMetadata(mediaId, metadata) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const updateData = {};
if (metadata.title)
updateData.title = metadata.title;
if (metadata.caption)
updateData.caption = metadata.caption;
if (metadata.altText)
updateData.alt_text = metadata.altText;
if (metadata.description)
updateData.description = metadata.description;
const response = await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/media/${mediaId}`, updateData, {
headers: {
'Authorization': authHeader,
'Content-Type': 'application/json'
}
});
return {
success: true,
media: {
id: response.data.id,
title: response.data.title.rendered,
caption: response.data.caption.rendered,
altText: response.data.alt_text,
description: response.data.description.rendered,
mediaType: response.data.media_type,
mimeType: response.data.mime_type,
sourceUrl: response.data.source_url,
width: response.data.media_details?.width,
height: response.data.media_details?.height,
filesize: response.data.media_details?.filesize,
date: response.data.date,
link: response.data.link,
slug: response.data.slug
}
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function deleteMedia(mediaId, force = true) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const params = new URLSearchParams();
if (force)
params.append('force', 'true');
await axios.delete(`${wpConfig.siteUrl}/wp-json/wp/v2/media/${mediaId}?${params.toString()}`, { headers: { 'Authorization': authHeader } });
return { success: true };
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function setFeaturedImage(postId, mediaId) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/posts/${postId}`, { featured_media: mediaId }, {
headers: {
'Authorization': authHeader,
'Content-Type': 'application/json'
}
});
return { success: true };
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function getMediaDetails(mediaId) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const authHeader = await getAuthHeader();
const response = await axios.get(`${wpConfig.siteUrl}/wp-json/wp/v2/media/${mediaId}`, { headers: { 'Authorization': authHeader } });
return {
success: true,
media: {
id: response.data.id,
title: response.data.title.rendered,
caption: response.data.caption.rendered,
altText: response.data.alt_text,
description: response.data.description.rendered,
mediaType: response.data.media_type,
mimeType: response.data.mime_type,
sourceUrl: response.data.source_url,
width: response.data.media_details?.width,
height: response.data.media_details?.height,
filesize: response.data.media_details?.filesize,
date: response.data.date,
link: response.data.link,
slug: response.data.slug
}
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function bulkDeleteMedia(mediaIds, force = true) {
try {
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
const deleted = [];
const failed = [];
for (const mediaId of mediaIds) {
const result = await deleteMedia(mediaId, force);
if (result.success) {
deleted.push(mediaId);
}
else {
failed.push(mediaId);
}
}
return {
success: true,
deleted,
failed
};
}
catch (error) {
return {
success: false,
deleted: [],
failed: mediaIds,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}
export async function optimizeMedia(mediaId, options = {}) {
try {
if (!process.env.TINIFY_API_KEY)
throw new Error('TinyPNG API key not configured');
if (!wpConfig.siteUrl)
throw new Error('WordPress site URL not configured');
if (!wpConfig.isAuthenticated)
throw new Error('Not authenticated');
tinify.key = process.env.TINIFY_API_KEY;
const mediaDetails = await getMediaDetails(mediaId);
if (!mediaDetails.success || !mediaDetails.media)
throw new Error('Media not found');
const imageResponse = await axios.get(mediaDetails.media.sourceUrl, { responseType: 'arraybuffer' });
const source = tinify.fromBuffer(Buffer.from(imageResponse.data));
const resized = options.quality ? source.resize({ method: 'fit' }) : source;
const optimized = await resized.toBuffer();
if (options.replaceOriginal) {
const formData = new FormData();
formData.append('file', optimized, `optimized_${mediaDetails.media.slug}`);
const authHeader = await getAuthHeader();
await axios.post(`${wpConfig.siteUrl}/wp-json/wp/v2/media/${mediaId}`, formData, { headers: { 'Authorization': authHeader, ...formData.getHeaders() } });
}
return { success: true, media: mediaDetails.media };
}
catch (error) {
return {
success: false,
error: error instanceof Error ? formatErrorResponse(error) : 'Unknown error'
};
}
}