placebo.mk/backend/src/modules/strapi.service.ts
2026-01-29 04:01:55 +01:00

270 lines
7.4 KiB
TypeScript

import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpService } from '@nestjs/axios';
import { lastValueFrom } from 'rxjs';
import { ArticlesService } from './articles.service';
import { LiveBlogService } from './live-blog.service';
import { CreateArticleDto, CreateLiveBlogDto } from './articles.dto';
import { ArticleStatus, LiveBlogStatus } from './entities';
interface StrapiArticle {
id: number;
documentId: string;
title: string;
description: string;
content: string;
slug: string;
publishedAt: string | null;
createdAt: string;
updatedAt: string;
}
interface StrapiLiveBlog {
id: number;
documentId: string;
title: string;
description: string;
slug: string;
status: 'draft' | 'live' | 'ended' | 'archived';
publishedAt: string | null;
createdAt: string;
updatedAt: string;
}
interface StrapiResponse<T> {
data: T;
meta: {
pagination: {
page: number;
pageSize: number;
pageCount: number;
total: number;
};
};
}
@Injectable()
export class StrapiService {
private readonly logger = new Logger(StrapiService.name);
private readonly strapiUrl: string;
private readonly strapiApiToken: string;
constructor(
private readonly configService: ConfigService,
private readonly httpService: HttpService,
private readonly articlesService: ArticlesService,
private readonly liveBlogService: LiveBlogService,
) {
this.strapiUrl =
this.configService.get<string>('STRAPI_URL') || 'http://localhost:1337';
this.strapiApiToken =
this.configService.get<string>('STRAPI_API_TOKEN') || '';
}
private getHeaders() {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (this.strapiApiToken) {
headers['Authorization'] = `Bearer ${this.strapiApiToken}`;
}
return headers;
}
async syncArticles(): Promise<void> {
try {
this.logger.log('Starting articles sync from Strapi...');
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiArticle[]>>(
`${this.strapiUrl}/api/articles`,
{
headers: this.getHeaders(),
},
),
);
const strapiArticles = response.data.data;
let syncedCount = 0;
for (const strapiArticle of strapiArticles) {
const articleData: Partial<CreateArticleDto> = {
title: strapiArticle.title,
excerpt: strapiArticle.description,
content: strapiArticle.content,
slug: strapiArticle.slug,
status: strapiArticle.publishedAt
? ArticleStatus.PUBLISHED
: ArticleStatus.DRAFT,
tags: [],
};
await this.articlesService.syncFromStrapi(
strapiArticle.documentId,
articleData,
);
syncedCount++;
}
this.logger.log(
`Successfully synced ${syncedCount} articles from Strapi`,
);
} catch (error) {
this.logger.error('Failed to sync articles from Strapi', error);
throw error;
}
}
async syncSingleArticle(strapiId: string): Promise<void> {
try {
this.logger.log(`Syncing single article from Strapi: ${strapiId}`);
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiArticle>>(
`${this.strapiUrl}/api/articles/${strapiId}`,
{
headers: this.getHeaders(),
},
),
);
const strapiArticle = response.data.data;
const articleData: Partial<CreateArticleDto> = {
title: strapiArticle.title,
excerpt: strapiArticle.description,
content: strapiArticle.content,
slug: strapiArticle.slug,
status: strapiArticle.publishedAt
? ArticleStatus.PUBLISHED
: ArticleStatus.DRAFT,
tags: [],
};
await this.articlesService.syncFromStrapi(
strapiArticle.documentId,
articleData,
);
this.logger.log(`Successfully synced article: ${strapiArticle.title}`);
} catch (error) {
this.logger.error(
`Failed to sync article ${strapiId} from Strapi`,
error,
);
throw error;
}
}
async syncLiveBlogs(): Promise<void> {
try {
this.logger.log('Starting live blogs sync from Strapi...');
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiLiveBlog[]>>(
`${this.strapiUrl}/api/live-blogs`,
{
headers: this.getHeaders(),
},
),
);
const strapiLiveBlogs = response.data.data;
let syncedCount = 0;
for (const strapiLiveBlog of strapiLiveBlogs) {
const liveBlogData: Partial<CreateLiveBlogDto> = {
title: strapiLiveBlog.title,
description: strapiLiveBlog.description,
slug: strapiLiveBlog.slug,
status: this.mapStrapiStatusToLiveBlogStatus(strapiLiveBlog.status),
};
await this.liveBlogService.syncFromStrapi(
strapiLiveBlog.documentId,
liveBlogData,
);
syncedCount++;
}
this.logger.log(
`Successfully synced ${syncedCount} live blogs from Strapi`,
);
} catch (error) {
this.logger.error('Failed to sync live blogs from Strapi', error);
throw error;
}
}
async syncSingleLiveBlog(strapiId: string): Promise<void> {
try {
this.logger.log(`Syncing single live blog from Strapi: ${strapiId}`);
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiLiveBlog>>(
`${this.strapiUrl}/api/live-blogs/${strapiId}`,
{
headers: this.getHeaders(),
},
),
);
const strapiLiveBlog = response.data.data;
const liveBlogData: Partial<CreateLiveBlogDto> = {
title: strapiLiveBlog.title,
description: strapiLiveBlog.description,
slug: strapiLiveBlog.slug,
status: this.mapStrapiStatusToLiveBlogStatus(strapiLiveBlog.status),
};
await this.liveBlogService.syncFromStrapi(
strapiLiveBlog.documentId,
liveBlogData,
);
this.logger.log(`Successfully synced live blog: ${strapiLiveBlog.title}`);
} catch (error) {
this.logger.error(
`Failed to sync live blog ${strapiId} from Strapi`,
error,
);
throw error;
}
}
private mapStrapiStatusToLiveBlogStatus(status: string): LiveBlogStatus {
switch (status) {
case 'live':
return LiveBlogStatus.LIVE;
case 'draft':
return LiveBlogStatus.DRAFT;
case 'ended':
return LiveBlogStatus.ENDED;
case 'archived':
return LiveBlogStatus.ARCHIVED;
default:
return LiveBlogStatus.DRAFT;
}
}
async handleWebhook(
event: 'entry.create' | 'entry.update' | 'entry.delete',
data: { documentId: string; model?: string },
): Promise<void> {
this.logger.log(
`Received webhook event: ${event} for model: ${data.model}`,
);
if (event === 'entry.delete') {
this.logger.log(`Handling delete for document: ${data.documentId}`);
return;
}
// Route to appropriate sync method based on model
if (data.model === 'article') {
await this.syncSingleArticle(data.documentId);
} else if (data.model === 'live-blog') {
await this.syncSingleLiveBlog(data.documentId);
} else {
this.logger.warn(`Unknown model type in webhook: ${data.model}`);
}
}
}