Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
719bfafb93 | |||
d493d9fd01 |
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-10-08 - 2.1.0 - feat(syncedinstance)
|
||||
Add SyncedInstance for multi-instance content synchronization, export it, add tests, and expand README
|
||||
|
||||
- Introduce SyncedInstance class (ts/classes.syncedinstance.ts) to synchronize tags, posts and pages across Ghost instances with mapping and history support. New APIs: syncTags, syncPosts, syncPages, syncAll, getSyncStatus, clearSyncHistory, clearMappings.
|
||||
- Export SyncedInstance from the package entry point (ts/index.ts).
|
||||
- Add integration tests for SyncedInstance (test/test.syncedinstance.node.ts).
|
||||
- Expand README (readme.md) with Quick Start, detailed Multi-Instance Synchronization docs, examples and updated API reference.
|
||||
|
||||
## 2025-10-08 - 2.0.0 - BREAKING CHANGE(classes.ghost)
|
||||
Remove Settings and Webhooks browse/read APIs, remove noisy console.error logs, and update tests/docs
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@apiclient.xyz/ghost",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"private": false,
|
||||
"description": "An unofficial Ghost CMS API package enabling content and admin functionality for managing posts.",
|
||||
"main": "dist_ts/index.js",
|
||||
|
185
test/test.syncedinstance.node.ts
Normal file
185
test/test.syncedinstance.node.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as qenv from '@push.rocks/qenv';
|
||||
const testQenv = new qenv.Qenv('./', './.nogit/');
|
||||
|
||||
import * as ghost from '../ts/index.js';
|
||||
|
||||
let sourceGhost: ghost.Ghost;
|
||||
let targetGhost: ghost.Ghost;
|
||||
let syncedInstance: ghost.SyncedInstance;
|
||||
let testTag: ghost.Tag;
|
||||
let testPost: ghost.Post;
|
||||
let testPage: ghost.Page;
|
||||
|
||||
tap.test('initialize source and target Ghost instances', async () => {
|
||||
sourceGhost = new ghost.Ghost({
|
||||
baseUrl: 'http://localhost:2368',
|
||||
adminApiKey: await testQenv.getEnvVarOnDemand('ADMIN_APIKEY'),
|
||||
contentApiKey: await testQenv.getEnvVarOnDemand('CONTENT_APIKEY'),
|
||||
});
|
||||
|
||||
targetGhost = new ghost.Ghost({
|
||||
baseUrl: 'http://localhost:2368',
|
||||
adminApiKey: await testQenv.getEnvVarOnDemand('ADMIN_APIKEY'),
|
||||
contentApiKey: await testQenv.getEnvVarOnDemand('CONTENT_APIKEY'),
|
||||
});
|
||||
|
||||
expect(sourceGhost).toBeInstanceOf(ghost.Ghost);
|
||||
expect(targetGhost).toBeInstanceOf(ghost.Ghost);
|
||||
});
|
||||
|
||||
tap.test('create SyncedInstance', async () => {
|
||||
syncedInstance = new ghost.SyncedInstance(sourceGhost, [targetGhost]);
|
||||
expect(syncedInstance).toBeInstanceOf(ghost.SyncedInstance);
|
||||
expect(syncedInstance.sourceGhost).toEqual(sourceGhost);
|
||||
expect(syncedInstance.targetGhosts).toBeArray();
|
||||
expect(syncedInstance.targetGhosts.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('create test tag on source', async () => {
|
||||
const timestamp = Date.now();
|
||||
testTag = await sourceGhost.createTag({
|
||||
name: `Sync Test Tag ${timestamp}`,
|
||||
slug: `sync-test-tag-${timestamp}`,
|
||||
description: 'This is a test tag for syncing'
|
||||
});
|
||||
expect(testTag).toBeInstanceOf(ghost.Tag);
|
||||
});
|
||||
|
||||
tap.test('sync tags from source to target', async () => {
|
||||
const report = await syncedInstance.syncTags();
|
||||
|
||||
expect(report).toBeTruthy();
|
||||
expect(report.contentType).toEqual('tags');
|
||||
expect(report.totalItems).toBeGreaterThan(0);
|
||||
expect(report.targetReports).toBeArray();
|
||||
expect(report.targetReports.length).toEqual(1);
|
||||
|
||||
const targetReport = report.targetReports[0];
|
||||
expect(targetReport.results).toBeArray();
|
||||
});
|
||||
|
||||
tap.test('verify sync status was tracked', async () => {
|
||||
const status = syncedInstance.getSyncStatus();
|
||||
expect(status.totalMappings).toBeGreaterThan(0);
|
||||
expect(status.recentSyncs).toBeArray();
|
||||
expect(status.recentSyncs.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('create test post on source', async () => {
|
||||
const timestamp = Date.now();
|
||||
testPost = await sourceGhost.createPost({
|
||||
title: `Sync Test Post ${timestamp}`,
|
||||
slug: `sync-test-post-${timestamp}`,
|
||||
html: '<p>This is a test post for syncing</p>',
|
||||
status: 'published',
|
||||
tags: [{ id: testTag.tagData.id }]
|
||||
});
|
||||
expect(testPost).toBeInstanceOf(ghost.Post);
|
||||
});
|
||||
|
||||
tap.test('sync posts from source to target', async () => {
|
||||
const report = await syncedInstance.syncPosts();
|
||||
|
||||
expect(report).toBeTruthy();
|
||||
expect(report.contentType).toEqual('posts');
|
||||
expect(report.totalItems).toBeGreaterThan(0);
|
||||
expect(report.targetReports).toBeArray();
|
||||
expect(report.targetReports.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('verify post sync in status', async () => {
|
||||
const status = syncedInstance.getSyncStatus();
|
||||
expect(status.recentSyncs).toBeArray();
|
||||
const postSync = status.recentSyncs.find(s => s.contentType === 'posts');
|
||||
expect(postSync).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('create test page on source', async () => {
|
||||
const timestamp = Date.now();
|
||||
testPage = await sourceGhost.createPage({
|
||||
title: `Sync Test Page ${timestamp}`,
|
||||
slug: `sync-test-page-${timestamp}`,
|
||||
html: '<p>This is a test page for syncing</p>',
|
||||
status: 'published'
|
||||
});
|
||||
expect(testPage).toBeInstanceOf(ghost.Page);
|
||||
});
|
||||
|
||||
tap.test('sync pages from source to target', async () => {
|
||||
const report = await syncedInstance.syncPages();
|
||||
|
||||
expect(report).toBeTruthy();
|
||||
expect(report.contentType).toEqual('pages');
|
||||
expect(report.totalItems).toBeGreaterThan(0);
|
||||
expect(report.targetReports).toBeArray();
|
||||
expect(report.targetReports.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('verify page sync in status', async () => {
|
||||
const status = syncedInstance.getSyncStatus();
|
||||
const pageSync = status.recentSyncs.find(s => s.contentType === 'pages');
|
||||
expect(pageSync).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('test syncAll method', async () => {
|
||||
const reports = await syncedInstance.syncAll({
|
||||
types: ['tags', 'posts', 'pages'],
|
||||
syncOptions: { dryRun: true }
|
||||
});
|
||||
|
||||
expect(reports).toBeArray();
|
||||
expect(reports.length).toEqual(3);
|
||||
expect(reports[0].contentType).toEqual('tags');
|
||||
expect(reports[1].contentType).toEqual('posts');
|
||||
expect(reports[2].contentType).toEqual('pages');
|
||||
});
|
||||
|
||||
tap.test('test clear methods', async () => {
|
||||
syncedInstance.clearMappings();
|
||||
syncedInstance.clearSyncHistory();
|
||||
|
||||
const status = syncedInstance.getSyncStatus();
|
||||
expect(status.totalMappings).toEqual(0);
|
||||
expect(status.recentSyncs.length).toEqual(0);
|
||||
});
|
||||
|
||||
tap.test('update tag and re-sync', async () => {
|
||||
await testTag.update({
|
||||
description: 'Updated description for sync test'
|
||||
});
|
||||
|
||||
const report = await syncedInstance.syncTags();
|
||||
expect(report).toBeTruthy();
|
||||
expect(report.totalItems).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('cleanup - delete synced content', async () => {
|
||||
if (testPost) {
|
||||
await testPost.delete();
|
||||
const targetPosts = await targetGhost.getPosts({ filter: `slug:${testPost.postData.slug}` });
|
||||
if (targetPosts.length > 0) {
|
||||
await targetPosts[0].delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (testPage) {
|
||||
await testPage.delete();
|
||||
try {
|
||||
const targetPage = await targetGhost.getPageBySlug(testPage.pageData.slug);
|
||||
await targetPage.delete();
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
if (testTag) {
|
||||
await testTag.delete();
|
||||
try {
|
||||
const targetTag = await targetGhost.getTagBySlug(testTag.tagData.slug);
|
||||
await targetTag.delete();
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default tap.start();
|
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@apiclient.xyz/ghost',
|
||||
version: '2.0.0',
|
||||
version: '2.1.0',
|
||||
description: 'An unofficial Ghost CMS API package enabling content and admin functionality for managing posts.'
|
||||
}
|
||||
|
406
ts/classes.syncedinstance.ts
Normal file
406
ts/classes.syncedinstance.ts
Normal file
@@ -0,0 +1,406 @@
|
||||
import * as plugins from './ghost.plugins.js';
|
||||
import { Ghost } from './classes.ghost.js';
|
||||
import { type IPost } from './classes.post.js';
|
||||
import { type IPage } from './classes.page.js';
|
||||
import { type ITag } from './classes.post.js';
|
||||
|
||||
export interface ISyncOptions {
|
||||
incremental?: boolean;
|
||||
filter?: string;
|
||||
dryRun?: boolean;
|
||||
}
|
||||
|
||||
export interface ISyncItemResult {
|
||||
sourceId: string;
|
||||
sourceSlug: string;
|
||||
targetId?: string;
|
||||
status: 'created' | 'updated' | 'skipped' | 'failed';
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface ISyncTargetReport {
|
||||
targetUrl: string;
|
||||
results: ISyncItemResult[];
|
||||
successCount: number;
|
||||
failureCount: number;
|
||||
}
|
||||
|
||||
export interface ISyncReport {
|
||||
contentType: 'posts' | 'pages' | 'tags';
|
||||
totalItems: number;
|
||||
targetReports: ISyncTargetReport[];
|
||||
duration: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface ISyncMapping {
|
||||
sourceId: string;
|
||||
sourceSlug: string;
|
||||
targetMappings: Array<{
|
||||
targetUrl: string;
|
||||
targetId: string;
|
||||
lastSynced: Date;
|
||||
}>;
|
||||
}
|
||||
|
||||
export class SyncedInstance {
|
||||
public sourceGhost: Ghost;
|
||||
public targetGhosts: Ghost[];
|
||||
private syncMappings: Map<string, ISyncMapping>;
|
||||
private syncHistory: ISyncReport[];
|
||||
|
||||
constructor(sourceGhost: Ghost, targetGhosts: Ghost[]) {
|
||||
this.sourceGhost = sourceGhost;
|
||||
this.targetGhosts = targetGhosts;
|
||||
this.syncMappings = new Map();
|
||||
this.syncHistory = [];
|
||||
}
|
||||
|
||||
private addMapping(contentType: string, sourceId: string, sourceSlug: string, targetUrl: string, targetId: string) {
|
||||
const key = `${contentType}:${sourceId}`;
|
||||
const existing = this.syncMappings.get(key);
|
||||
|
||||
if (existing) {
|
||||
const targetMapping = existing.targetMappings.find(tm => tm.targetUrl === targetUrl);
|
||||
if (targetMapping) {
|
||||
targetMapping.targetId = targetId;
|
||||
targetMapping.lastSynced = new Date();
|
||||
} else {
|
||||
existing.targetMappings.push({
|
||||
targetUrl,
|
||||
targetId,
|
||||
lastSynced: new Date()
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.syncMappings.set(key, {
|
||||
sourceId,
|
||||
sourceSlug,
|
||||
targetMappings: [{
|
||||
targetUrl,
|
||||
targetId,
|
||||
lastSynced: new Date()
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getMapping(contentType: string, sourceId: string, targetUrl: string): string | undefined {
|
||||
const key = `${contentType}:${sourceId}`;
|
||||
const mapping = this.syncMappings.get(key);
|
||||
if (!mapping) return undefined;
|
||||
|
||||
const targetMapping = mapping.targetMappings.find(tm => tm.targetUrl === targetUrl);
|
||||
return targetMapping?.targetId;
|
||||
}
|
||||
|
||||
public async syncTags(optionsArg?: ISyncOptions): Promise<ISyncReport> {
|
||||
const startTime = Date.now();
|
||||
const report: ISyncReport = {
|
||||
contentType: 'tags',
|
||||
totalItems: 0,
|
||||
targetReports: [],
|
||||
duration: 0,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
const sourceTags = await this.sourceGhost.getTags(optionsArg?.filter ? { filter: optionsArg.filter } : {});
|
||||
report.totalItems = sourceTags.length;
|
||||
|
||||
for (const targetGhost of this.targetGhosts) {
|
||||
const targetReport: ISyncTargetReport = {
|
||||
targetUrl: targetGhost.options.baseUrl,
|
||||
results: [],
|
||||
successCount: 0,
|
||||
failureCount: 0
|
||||
};
|
||||
|
||||
for (const sourceTag of sourceTags) {
|
||||
try {
|
||||
let targetTag: any;
|
||||
let status: 'created' | 'updated' | 'skipped' = 'created';
|
||||
|
||||
try {
|
||||
targetTag = await targetGhost.getTagBySlug(sourceTag.slug);
|
||||
if (!optionsArg?.dryRun) {
|
||||
await targetTag.update({
|
||||
name: sourceTag.name,
|
||||
description: sourceTag.description,
|
||||
feature_image: sourceTag.feature_image,
|
||||
visibility: sourceTag.visibility,
|
||||
meta_title: sourceTag.meta_title,
|
||||
meta_description: sourceTag.meta_description
|
||||
});
|
||||
}
|
||||
status = 'updated';
|
||||
} catch (error) {
|
||||
if (!optionsArg?.dryRun) {
|
||||
targetTag = await targetGhost.createTag({
|
||||
name: sourceTag.name,
|
||||
slug: sourceTag.slug,
|
||||
description: sourceTag.description,
|
||||
feature_image: sourceTag.feature_image,
|
||||
visibility: sourceTag.visibility,
|
||||
meta_title: sourceTag.meta_title,
|
||||
meta_description: sourceTag.meta_description
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!optionsArg?.dryRun && targetTag) {
|
||||
this.addMapping('tags', sourceTag.id, sourceTag.slug, targetGhost.options.baseUrl, targetTag.tagData.id);
|
||||
}
|
||||
|
||||
targetReport.results.push({
|
||||
sourceId: sourceTag.id,
|
||||
sourceSlug: sourceTag.slug,
|
||||
targetId: targetTag?.tagData?.id,
|
||||
status
|
||||
});
|
||||
targetReport.successCount++;
|
||||
} catch (error) {
|
||||
targetReport.results.push({
|
||||
sourceId: sourceTag.id,
|
||||
sourceSlug: sourceTag.slug,
|
||||
status: 'failed',
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
targetReport.failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
report.targetReports.push(targetReport);
|
||||
}
|
||||
|
||||
report.duration = Date.now() - startTime;
|
||||
this.syncHistory.push(report);
|
||||
return report;
|
||||
}
|
||||
|
||||
public async syncPosts(optionsArg?: ISyncOptions): Promise<ISyncReport> {
|
||||
const startTime = Date.now();
|
||||
const report: ISyncReport = {
|
||||
contentType: 'posts',
|
||||
totalItems: 0,
|
||||
targetReports: [],
|
||||
duration: 0,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
const sourcePosts = await this.sourceGhost.getPosts(optionsArg?.filter ? { filter: optionsArg.filter } : {});
|
||||
report.totalItems = sourcePosts.length;
|
||||
|
||||
for (const targetGhost of this.targetGhosts) {
|
||||
const targetReport: ISyncTargetReport = {
|
||||
targetUrl: targetGhost.options.baseUrl,
|
||||
results: [],
|
||||
successCount: 0,
|
||||
failureCount: 0
|
||||
};
|
||||
|
||||
for (const sourcePost of sourcePosts) {
|
||||
try {
|
||||
const postData = sourcePost.postData;
|
||||
|
||||
const tagSlugs = postData.tags?.map(t => t.slug) || [];
|
||||
const targetTagIds: string[] = [];
|
||||
|
||||
for (const tagSlug of tagSlugs) {
|
||||
try {
|
||||
const targetTag = await targetGhost.getTagBySlug(tagSlug);
|
||||
targetTagIds.push(targetTag.tagData.id);
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
const syncData: any = {
|
||||
title: postData.title,
|
||||
slug: postData.slug,
|
||||
html: postData.html,
|
||||
feature_image: postData.feature_image,
|
||||
featured: postData.featured,
|
||||
status: postData.status,
|
||||
visibility: postData.visibility,
|
||||
meta_title: postData.meta_title,
|
||||
meta_description: postData.meta_description,
|
||||
published_at: postData.published_at,
|
||||
custom_excerpt: postData.custom_excerpt,
|
||||
tags: targetTagIds.length > 0 ? targetTagIds.map(id => ({ id })) : undefined
|
||||
};
|
||||
|
||||
let targetPost: any;
|
||||
let status: 'created' | 'updated' = 'created';
|
||||
|
||||
try {
|
||||
targetPost = await targetGhost.contentApi.posts.read({ slug: postData.slug }, { formats: ['html'] });
|
||||
if (!optionsArg?.dryRun) {
|
||||
const updated = await targetGhost.adminApi.posts.edit({
|
||||
...syncData,
|
||||
id: targetPost.id,
|
||||
updated_at: targetPost.updated_at
|
||||
});
|
||||
targetPost = updated;
|
||||
}
|
||||
status = 'updated';
|
||||
} catch (error) {
|
||||
if (!optionsArg?.dryRun) {
|
||||
targetPost = await targetGhost.adminApi.posts.add(syncData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!optionsArg?.dryRun && targetPost) {
|
||||
this.addMapping('posts', postData.id, postData.slug, targetGhost.options.baseUrl, targetPost.id);
|
||||
}
|
||||
|
||||
targetReport.results.push({
|
||||
sourceId: postData.id,
|
||||
sourceSlug: postData.slug,
|
||||
targetId: targetPost?.id,
|
||||
status
|
||||
});
|
||||
targetReport.successCount++;
|
||||
} catch (error) {
|
||||
targetReport.results.push({
|
||||
sourceId: sourcePost.postData.id,
|
||||
sourceSlug: sourcePost.postData.slug,
|
||||
status: 'failed',
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
targetReport.failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
report.targetReports.push(targetReport);
|
||||
}
|
||||
|
||||
report.duration = Date.now() - startTime;
|
||||
this.syncHistory.push(report);
|
||||
return report;
|
||||
}
|
||||
|
||||
public async syncPages(optionsArg?: ISyncOptions): Promise<ISyncReport> {
|
||||
const startTime = Date.now();
|
||||
const report: ISyncReport = {
|
||||
contentType: 'pages',
|
||||
totalItems: 0,
|
||||
targetReports: [],
|
||||
duration: 0,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
const sourcePages = await this.sourceGhost.getPages(optionsArg?.filter ? { filter: optionsArg.filter } : {});
|
||||
report.totalItems = sourcePages.length;
|
||||
|
||||
for (const targetGhost of this.targetGhosts) {
|
||||
const targetReport: ISyncTargetReport = {
|
||||
targetUrl: targetGhost.options.baseUrl,
|
||||
results: [],
|
||||
successCount: 0,
|
||||
failureCount: 0
|
||||
};
|
||||
|
||||
for (const sourcePage of sourcePages) {
|
||||
try {
|
||||
const pageData = sourcePage.pageData;
|
||||
|
||||
const syncData: Partial<IPage> = {
|
||||
title: pageData.title,
|
||||
slug: pageData.slug,
|
||||
html: pageData.html,
|
||||
feature_image: pageData.feature_image,
|
||||
featured: pageData.featured,
|
||||
status: pageData.status,
|
||||
visibility: pageData.visibility,
|
||||
meta_title: pageData.meta_title,
|
||||
meta_description: pageData.meta_description,
|
||||
published_at: pageData.published_at,
|
||||
custom_excerpt: pageData.custom_excerpt
|
||||
};
|
||||
|
||||
let targetPage: any;
|
||||
let status: 'created' | 'updated' = 'created';
|
||||
|
||||
try {
|
||||
targetPage = await targetGhost.contentApi.pages.read({ slug: pageData.slug }, { formats: ['html'] });
|
||||
if (!optionsArg?.dryRun) {
|
||||
const updated = await targetGhost.adminApi.pages.edit({
|
||||
...syncData,
|
||||
id: targetPage.id,
|
||||
updated_at: targetPage.updated_at
|
||||
});
|
||||
targetPage = updated;
|
||||
}
|
||||
status = 'updated';
|
||||
} catch (error) {
|
||||
if (!optionsArg?.dryRun) {
|
||||
targetPage = await targetGhost.adminApi.pages.add(syncData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!optionsArg?.dryRun && targetPage) {
|
||||
this.addMapping('pages', pageData.id, pageData.slug, targetGhost.options.baseUrl, targetPage.id);
|
||||
}
|
||||
|
||||
targetReport.results.push({
|
||||
sourceId: pageData.id,
|
||||
sourceSlug: pageData.slug,
|
||||
targetId: targetPage?.id,
|
||||
status
|
||||
});
|
||||
targetReport.successCount++;
|
||||
} catch (error) {
|
||||
targetReport.results.push({
|
||||
sourceId: sourcePage.pageData.id,
|
||||
sourceSlug: sourcePage.pageData.slug,
|
||||
status: 'failed',
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
targetReport.failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
report.targetReports.push(targetReport);
|
||||
}
|
||||
|
||||
report.duration = Date.now() - startTime;
|
||||
this.syncHistory.push(report);
|
||||
return report;
|
||||
}
|
||||
|
||||
public async syncAll(optionsArg?: { types?: Array<'posts' | 'pages' | 'tags'>; syncOptions?: ISyncOptions }): Promise<ISyncReport[]> {
|
||||
const types = optionsArg?.types || ['tags', 'posts', 'pages'];
|
||||
const reports: ISyncReport[] = [];
|
||||
|
||||
for (const type of types) {
|
||||
if (type === 'tags') {
|
||||
reports.push(await this.syncTags(optionsArg?.syncOptions));
|
||||
} else if (type === 'posts') {
|
||||
reports.push(await this.syncPosts(optionsArg?.syncOptions));
|
||||
} else if (type === 'pages') {
|
||||
reports.push(await this.syncPages(optionsArg?.syncOptions));
|
||||
}
|
||||
}
|
||||
|
||||
return reports;
|
||||
}
|
||||
|
||||
public getSyncStatus(): {
|
||||
totalMappings: number;
|
||||
mappings: ISyncMapping[];
|
||||
recentSyncs: ISyncReport[];
|
||||
} {
|
||||
return {
|
||||
totalMappings: this.syncMappings.size,
|
||||
mappings: Array.from(this.syncMappings.values()),
|
||||
recentSyncs: this.syncHistory.slice(-10)
|
||||
};
|
||||
}
|
||||
|
||||
public clearSyncHistory(): void {
|
||||
this.syncHistory = [];
|
||||
}
|
||||
|
||||
public clearMappings(): void {
|
||||
this.syncMappings.clear();
|
||||
}
|
||||
}
|
@@ -3,4 +3,5 @@ export * from './classes.post.js';
|
||||
export * from './classes.author.js';
|
||||
export * from './classes.tag.js';
|
||||
export * from './classes.page.js';
|
||||
export * from './classes.member.js';
|
||||
export * from './classes.member.js';
|
||||
export * from './classes.syncedinstance.js';
|
Reference in New Issue
Block a user