import * as plugins from './ghost.plugins.js'; import { Post, type IPost, type ITag, type IAuthor } from './classes.post.js'; import { Author } from './classes.author.js'; import { Tag } from './classes.tag.js'; import { Page, type IPage } from './classes.page.js'; export interface IGhostConstructorOptions { baseUrl: string; contentApiKey: string; adminApiKey: string; } export class Ghost { public options: IGhostConstructorOptions; public adminApi: any; public contentApi: any; constructor(optionsArg: IGhostConstructorOptions) { this.options = optionsArg; this.adminApi = new plugins.GhostAdminAPI({ url: this.options.baseUrl, key: this.options.adminApiKey, version: 'v3', }); this.contentApi = new plugins.GhostContentAPI({ url: this.options.baseUrl, key: this.options.contentApiKey, version: 'v3', }); } public async getPosts(optionsArg?: { limit?: number; tag?: string; author?: string; featured?: boolean; filter?: string; }): Promise { try { const limit = optionsArg?.limit || 1000; const filters: string[] = []; if (optionsArg?.tag) { filters.push(`tag:${optionsArg.tag}`); } if (optionsArg?.author) { filters.push(`author:${optionsArg.author}`); } if (optionsArg?.featured !== undefined) { filters.push(`featured:${optionsArg.featured}`); } if (optionsArg?.filter) { filters.push(optionsArg.filter); } const filterString = filters.length > 0 ? filters.join('+') : undefined; const postsData = await this.contentApi.posts.browse({ limit, include: 'tags,authors', ...(filterString && { filter: filterString }) }); return postsData.map((postData: IPost) => new Post(this, postData)); } catch (error) { console.error('Error fetching posts:', error); throw error; } } public async getPostById(id: string): Promise { try { const postData = await this.contentApi.posts.read({ id }); return new Post(postData, this.adminApi); } catch (error) { console.error(`Error fetching post with id ${id}:`, error); throw error; } } public async createPost(postData: IPost): Promise { try { const createdPostData = await this.adminApi.posts.add(postData); return new Post(createdPostData, this.adminApi); } catch (error) { console.error('Error creating post:', error); throw error; } } public async createPostFromHtml(optionsArg: { title: string; html: string }) { const postData = await this.adminApi.posts.add( { title: optionsArg.title, html: optionsArg.html }, { source: 'html' } // Tell the API to use HTML as the content source, instead of Lexical ); return new Post(this, postData); } public async getTags(optionsArg?: { filter?: string; limit?: number }): Promise { try { const limit = optionsArg?.limit || 1000; const tagsData = await this.contentApi.tags.browse({ limit }); if (optionsArg?.filter) { const matcher = new plugins.smartmatch.SmartMatch(optionsArg.filter); return tagsData.filter((tag: ITag) => matcher.match(tag.slug)); } return tagsData; } catch (error) { console.error('Error fetching tags:', error); throw error; } } public async getTagById(id: string): Promise { try { const tagData = await this.contentApi.tags.read({ id }); return new Tag(this, tagData); } catch (error) { console.error(`Error fetching tag with id ${id}:`, error); throw error; } } public async getTagBySlug(slug: string): Promise { try { const tagData = await this.contentApi.tags.read({ slug }); return new Tag(this, tagData); } catch (error) { console.error(`Error fetching tag with slug ${slug}:`, error); throw error; } } public async createTag(tagData: Partial): Promise { try { const createdTagData = await this.adminApi.tags.add(tagData); return new Tag(this, createdTagData); } catch (error) { console.error('Error creating tag:', error); throw error; } } public async getAuthors(optionsArg?: { filter?: string; limit?: number }): Promise { try { const limit = optionsArg?.limit || 1000; const authorsData = await this.contentApi.authors.browse({ limit }); if (optionsArg?.filter) { const matcher = new plugins.smartmatch.SmartMatch(optionsArg.filter); return authorsData .filter((author: IAuthor) => matcher.match(author.slug)) .map((author: IAuthor) => new Author(this, author)); } return authorsData.map((author: IAuthor) => new Author(this, author)); } catch (error) { console.error('Error fetching authors:', error); throw error; } } public async getAuthorById(id: string): Promise { try { const authorData = await this.contentApi.authors.read({ id }); return new Author(this, authorData); } catch (error) { console.error(`Error fetching author with id ${id}:`, error); throw error; } } public async getAuthorBySlug(slug: string): Promise { try { const authorData = await this.contentApi.authors.read({ slug }); return new Author(this, authorData); } catch (error) { console.error(`Error fetching author with slug ${slug}:`, error); throw error; } } public async getPages(optionsArg?: { limit?: number; filter?: string }): Promise { try { const limit = optionsArg?.limit || 1000; const pagesData = await this.contentApi.pages.browse({ limit, include: 'tags,authors' }); if (optionsArg?.filter) { const matcher = new plugins.smartmatch.SmartMatch(optionsArg.filter); return pagesData .filter((page: IPage) => matcher.match(page.slug)) .map((page: IPage) => new Page(this, page)); } return pagesData.map((pageData: IPage) => new Page(this, pageData)); } catch (error) { console.error('Error fetching pages:', error); throw error; } } public async getPageById(id: string): Promise { try { const pageData = await this.contentApi.pages.read({ id }); return new Page(this, pageData); } catch (error) { console.error(`Error fetching page with id ${id}:`, error); throw error; } } public async getPageBySlug(slug: string): Promise { try { const pageData = await this.contentApi.pages.read({ slug }); return new Page(this, pageData); } catch (error) { console.error(`Error fetching page with slug ${slug}:`, error); throw error; } } public async createPage(pageData: Partial): Promise { try { const createdPageData = await this.adminApi.pages.add(pageData); return new Page(this, createdPageData); } catch (error) { console.error('Error creating page:', error); throw error; } } public async searchPosts(query: string, optionsArg?: { limit?: number }): Promise { try { const limit = optionsArg?.limit || 100; const postsData = await this.contentApi.posts.browse({ limit, filter: `title:~'${query}'`, include: 'tags,authors' }); return postsData.map((postData: IPost) => new Post(this, postData)); } catch (error) { console.error('Error searching posts:', error); throw error; } } public async uploadImage(filePath: string): Promise { try { const result = await this.adminApi.images.upload({ file: filePath }); return result.url; } catch (error) { console.error('Error uploading image:', error); throw error; } } public async bulkUpdatePosts(postIds: string[], updates: Partial): Promise { try { const updatePromises = postIds.map(async (id) => { const post = await this.getPostById(id); return post.update({ ...post.postData, ...updates, id }); }); return await Promise.all(updatePromises); } catch (error) { console.error('Error bulk updating posts:', error); throw error; } } public async bulkDeletePosts(postIds: string[]): Promise { try { const deletePromises = postIds.map(async (id) => { const post = await this.getPostById(id); return post.delete(); }); await Promise.all(deletePromises); } catch (error) { console.error('Error bulk deleting posts:', error); throw error; } } public async getRelatedPosts(postId: string, limit: number = 5): Promise { try { const post = await this.getPostById(postId); const tags = post.postData.tags; if (!tags || !Array.isArray(tags) || tags.length === 0) { return []; } const tagSlugs = tags.map(tag => tag.slug).join(','); const postsData = await this.contentApi.posts.browse({ limit, filter: `tag:[${tagSlugs}]+id:-${postId}`, include: 'tags,authors' }); return postsData.map((postData: IPost) => new Post(this, postData)); } catch (error) { console.error('Error fetching related posts:', error); throw error; } } }