category field added
implemented ctegory logic, ditional pages added
This commit is contained in:
1 parent
add12b2fbf
commit
46eb41aaa5
16 files changed
+598
-21
No files matched your search
@@ -23,7 +23,8 @@
|
||||
"dev:docker": "nest start --watch",
|
||||
"dev:local": "cp -f .env.local .env && nest start --watch",
|
||||
"dev:reset-env": "cp -f .env.docker .env",
|
||||
"seed:admin": "ts-node scripts/seed-admin.ts"
|
||||
"seed:admin": "ts-node scripts/seed-admin.ts",
|
||||
"seed:categories": "ts-node scripts/seed-categories.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/axios": "^4.0.1",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { Category } from '../src/modules/entities';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.createApplicationContext(AppModule);
|
||||
|
||||
try {
|
||||
const categoryRepository = app.get(getRepositoryToken(Category));
|
||||
|
||||
// Define categories with Macedonian names and English slugs
|
||||
const categories = [
|
||||
{
|
||||
name: 'Спорт',
|
||||
slug: 'sport',
|
||||
description: 'Спортски вести и анализи',
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
name: 'Уметност',
|
||||
slug: 'art',
|
||||
description: 'Уметност, култура и забава',
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
name: 'Наука',
|
||||
slug: 'science',
|
||||
description: 'Научни откритија и технологија',
|
||||
order: 3,
|
||||
},
|
||||
];
|
||||
|
||||
console.log('Seeding categories...');
|
||||
|
||||
for (const categoryData of categories) {
|
||||
// Check if category already exists
|
||||
const existingCategory = await categoryRepository.findOne({
|
||||
where: { slug: categoryData.slug },
|
||||
});
|
||||
|
||||
if (existingCategory) {
|
||||
console.log(`Category "${categoryData.name}" (${categoryData.slug}) already exists`);
|
||||
} else {
|
||||
const category = categoryRepository.create(categoryData);
|
||||
await categoryRepository.save(category);
|
||||
console.log(`Created category: "${categoryData.name}" (${categoryData.slug})`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Category seeding completed!');
|
||||
console.log('Categories available:');
|
||||
const allCategories = await categoryRepository.find({ order: { order: 'ASC' } });
|
||||
allCategories.forEach((cat: Category) => {
|
||||
console.log(` • ${cat.name} (${cat.slug}) - ${cat.description || 'No description'}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error seeding categories:', error);
|
||||
} finally {
|
||||
await app.close();
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Module, forwardRef } from '@nestjs/common';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { StrapiService } from './strapi.service';
|
||||
import { StrapiController } from './strapi.controller';
|
||||
import { ArticlesModule } from './articles.module';
|
||||
import { LiveBlogModule } from './live-blog.module';
|
||||
import { Category } from './entities';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
HttpModule,
|
||||
TypeOrmModule.forFeature([Category]),
|
||||
forwardRef(() => ArticlesModule),
|
||||
forwardRef(() => LiveBlogModule),
|
||||
],
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Injectable, Logger, Inject, forwardRef } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { ArticlesService } from './articles.service';
|
||||
import { LiveBlogService } from './live-blog.service';
|
||||
@@ -11,6 +13,7 @@ import {
|
||||
ImagePosition,
|
||||
ImageSize,
|
||||
VideoPosition,
|
||||
Category,
|
||||
} from './entities';
|
||||
|
||||
interface StrapiImage {
|
||||
@@ -45,6 +48,7 @@ interface StrapiArticle {
|
||||
videoUrl?: string;
|
||||
videoPosition?: string;
|
||||
videoCaption?: string;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
interface StrapiLiveBlog {
|
||||
@@ -64,6 +68,7 @@ interface StrapiLiveBlog {
|
||||
videoUrl?: string;
|
||||
videoPosition?: string;
|
||||
videoCaption?: string;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
interface StrapiResponse<T> {
|
||||
@@ -91,6 +96,8 @@ export class StrapiService {
|
||||
private readonly articlesService: ArticlesService,
|
||||
@Inject(forwardRef(() => LiveBlogService))
|
||||
private readonly liveBlogService: LiveBlogService,
|
||||
@InjectRepository(Category)
|
||||
private readonly categoryRepository: Repository<Category>,
|
||||
) {
|
||||
this.strapiUrl =
|
||||
this.configService.get<string>('STRAPI_URL') || 'http://localhost:1337';
|
||||
@@ -99,13 +106,51 @@ export class StrapiService {
|
||||
}
|
||||
|
||||
private getHeaders() {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
return {
|
||||
Authorization: `Bearer ${this.strapiApiToken}`,
|
||||
};
|
||||
if (this.strapiApiToken) {
|
||||
headers['Authorization'] = `Bearer ${this.strapiApiToken}`;
|
||||
}
|
||||
|
||||
private async findOrCreateCategory(
|
||||
categorySlug: string,
|
||||
): Promise<Category | null> {
|
||||
if (!categorySlug) {
|
||||
return null;
|
||||
}
|
||||
return headers;
|
||||
|
||||
// Map CMS category slugs to Macedonian display names
|
||||
const categoryMap: Record<string, { name: string; description: string }> = {
|
||||
sport: { name: 'Спорт', description: 'Спортски вести и анализи' },
|
||||
art: { name: 'Уметност', description: 'Уметност, култура и забава' },
|
||||
science: { name: 'Наука', description: 'Научни откритија и технологија' },
|
||||
};
|
||||
|
||||
const categoryInfo = categoryMap[categorySlug];
|
||||
if (!categoryInfo) {
|
||||
this.logger.warn(`Unknown category slug: ${categorySlug}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to find existing category by slug
|
||||
let category = await this.categoryRepository.findOne({
|
||||
where: { slug: categorySlug },
|
||||
});
|
||||
|
||||
if (!category) {
|
||||
// Create new category
|
||||
category = this.categoryRepository.create({
|
||||
name: categoryInfo.name,
|
||||
slug: categorySlug,
|
||||
description: categoryInfo.description,
|
||||
order: 0,
|
||||
});
|
||||
category = await this.categoryRepository.save(category);
|
||||
this.logger.log(
|
||||
`Created new category: ${categorySlug} (${categoryInfo.name})`,
|
||||
);
|
||||
}
|
||||
|
||||
return category;
|
||||
}
|
||||
|
||||
private extractImageUrl(strapiArticle: StrapiArticle): string | undefined {
|
||||
@@ -181,6 +226,17 @@ export class StrapiService {
|
||||
for (const strapiArticle of strapiArticles) {
|
||||
const imageUrl = this.extractImageUrl(strapiArticle);
|
||||
|
||||
// Find or create category
|
||||
let categoryId: string | undefined;
|
||||
if (strapiArticle.category) {
|
||||
const category = await this.findOrCreateCategory(
|
||||
strapiArticle.category,
|
||||
);
|
||||
if (category) {
|
||||
categoryId = category.id;
|
||||
}
|
||||
}
|
||||
|
||||
const articleData: Partial<CreateArticleDto> = {
|
||||
title: strapiArticle.title,
|
||||
excerpt: strapiArticle.description,
|
||||
@@ -198,6 +254,7 @@ export class StrapiService {
|
||||
videoPosition: (strapiArticle.videoPosition ||
|
||||
'inline') as VideoPosition,
|
||||
videoCaption: strapiArticle.videoCaption || '',
|
||||
categoryId,
|
||||
};
|
||||
|
||||
await this.articlesService.syncFromStrapi(
|
||||
@@ -256,6 +313,17 @@ export class StrapiService {
|
||||
|
||||
const imageUrl = this.extractImageUrl(strapiArticle);
|
||||
|
||||
// Find or create category
|
||||
let categoryId: string | undefined;
|
||||
if (strapiArticle.category) {
|
||||
const category = await this.findOrCreateCategory(
|
||||
strapiArticle.category,
|
||||
);
|
||||
if (category) {
|
||||
categoryId = category.id;
|
||||
}
|
||||
}
|
||||
|
||||
const articleData: Partial<CreateArticleDto> = {
|
||||
title: strapiArticle.title,
|
||||
excerpt: strapiArticle.description,
|
||||
@@ -270,6 +338,7 @@ export class StrapiService {
|
||||
videoPosition: (strapiArticle.videoPosition ||
|
||||
'inline') as VideoPosition,
|
||||
videoCaption: strapiArticle.videoCaption || '',
|
||||
categoryId,
|
||||
};
|
||||
|
||||
await this.articlesService.syncFromStrapi(
|
||||
@@ -333,6 +402,17 @@ export class StrapiService {
|
||||
for (const strapiLiveBlog of strapiLiveBlogs) {
|
||||
const imageUrl = this.extractLiveBlogImageUrl(strapiLiveBlog);
|
||||
|
||||
// Find or create category
|
||||
let categoryId: string | undefined;
|
||||
if (strapiLiveBlog.category) {
|
||||
const category = await this.findOrCreateCategory(
|
||||
strapiLiveBlog.category,
|
||||
);
|
||||
if (category) {
|
||||
categoryId = category.id;
|
||||
}
|
||||
}
|
||||
|
||||
const liveBlogData: Partial<CreateLiveBlogDto> = {
|
||||
title: strapiLiveBlog.title,
|
||||
description: strapiLiveBlog.description,
|
||||
@@ -346,6 +426,7 @@ export class StrapiService {
|
||||
videoPosition: (strapiLiveBlog.videoPosition ||
|
||||
'inline') as VideoPosition,
|
||||
videoCaption: strapiLiveBlog.videoCaption || '',
|
||||
categoryId,
|
||||
};
|
||||
|
||||
await this.liveBlogService.syncFromStrapi(
|
||||
@@ -400,6 +481,17 @@ export class StrapiService {
|
||||
|
||||
const imageUrl = this.extractLiveBlogImageUrl(strapiLiveBlog);
|
||||
|
||||
// Find or create category
|
||||
let categoryId: string | undefined;
|
||||
if (strapiLiveBlog.category) {
|
||||
const category = await this.findOrCreateCategory(
|
||||
strapiLiveBlog.category,
|
||||
);
|
||||
if (category) {
|
||||
categoryId = category.id;
|
||||
}
|
||||
}
|
||||
|
||||
const liveBlogData: Partial<CreateLiveBlogDto> = {
|
||||
title: strapiLiveBlog.title,
|
||||
description: strapiLiveBlog.description,
|
||||
@@ -412,6 +504,7 @@ export class StrapiService {
|
||||
videoPosition: (strapiLiveBlog.videoPosition ||
|
||||
'inline') as VideoPosition,
|
||||
videoCaption: strapiLiveBlog.videoCaption || '',
|
||||
categoryId,
|
||||
};
|
||||
|
||||
await this.liveBlogService.syncFromStrapi(
|
||||
|
||||
Reference in new issue
Block a user