img shown succesfully

This commit is contained in:
echo 2026-02-03 18:47:42 +01:00
parent 7d3bc2a014
commit 28609a6492
4 changed files with 109 additions and 13 deletions

View File

@ -153,6 +153,10 @@ export class CreateLiveBlogDto {
@IsOptional()
@IsUUID()
categoryId?: string;
@IsOptional()
@IsString()
featuredImage?: string;
}
export class UpdateLiveBlogDto {
@ -187,6 +191,10 @@ export class UpdateLiveBlogDto {
@IsOptional()
@IsUUID()
categoryId?: string;
@IsOptional()
@IsString()
featuredImage?: string;
}
export class CreateLiveBlogUpdateDto {

View File

@ -186,6 +186,9 @@ export class LiveBlog {
@Column({ nullable: true })
categoryId: string;
@Column({ default: '' })
featuredImage: string;
@Column({ default: 0 })
viewCount: number;

View File

@ -17,6 +17,8 @@ interface StrapiArticle {
publishedAt: string | null;
createdAt: string;
updatedAt: string;
img?: any;
media?: any[];
}
interface StrapiLiveBlog {
@ -29,6 +31,8 @@ interface StrapiLiveBlog {
publishedAt: string | null;
createdAt: string;
updatedAt: string;
img?: any;
media?: any[];
}
interface StrapiResponse<T> {
@ -71,13 +75,65 @@ export class StrapiService {
return headers;
}
private extractImageUrl(strapiArticle: StrapiArticle): string | undefined {
// Try to get image from img field first (single image)
let imageUrl: string | undefined;
if (strapiArticle.img?.url) {
imageUrl = strapiArticle.img.url;
} else if (strapiArticle.media?.[0]?.url) {
// Try to get first image from media field (multiple images)
imageUrl = strapiArticle.media[0].url;
}
if (!imageUrl) {
return undefined;
}
// If URL is relative, prepend Strapi base URL
if (imageUrl.startsWith('/')) {
// Convert Docker service URL to localhost for frontend access
// Backend uses http://cms:1337 internally, but frontend needs http://localhost:1337
const frontendStrapiUrl = this.strapiUrl.replace('cms:', 'localhost:');
return `${frontendStrapiUrl}${imageUrl}`;
}
return imageUrl;
}
private extractLiveBlogImageUrl(strapiLiveBlog: StrapiLiveBlog): string | undefined {
// Try to get image from img field first (single image)
let imageUrl: string | undefined;
if (strapiLiveBlog.img?.url) {
imageUrl = strapiLiveBlog.img.url;
} else if (strapiLiveBlog.media?.[0]?.url) {
// Try to get first image from media field (multiple images)
imageUrl = strapiLiveBlog.media[0].url;
}
if (!imageUrl) {
return undefined;
}
// If URL is relative, prepend Strapi base URL
if (imageUrl.startsWith('/')) {
// Convert Docker service URL to localhost for frontend access
// Backend uses http://cms:1337 internally, but frontend needs http://localhost:1337
const frontendStrapiUrl = this.strapiUrl.replace('cms:', 'localhost:');
return `${frontendStrapiUrl}${imageUrl}`;
}
return imageUrl;
}
async syncArticles(): Promise<void> {
try {
this.logger.log('Starting articles sync from Strapi...');
const response = await lastValueFrom(
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiArticle[]>>(
`${this.strapiUrl}/api/articles`,
`${this.strapiUrl}/api/articles?populate=*`,
{
headers: this.getHeaders(),
},
@ -88,6 +144,8 @@ export class StrapiService {
let syncedCount = 0;
for (const strapiArticle of strapiArticles) {
const imageUrl = this.extractImageUrl(strapiArticle);
const articleData: Partial<CreateArticleDto> = {
title: strapiArticle.title,
excerpt: strapiArticle.description,
@ -97,6 +155,7 @@ export class StrapiService {
? ArticleStatus.PUBLISHED
: ArticleStatus.DRAFT,
tags: [],
featuredImage: imageUrl,
};
await this.articlesService.syncFromStrapi(
@ -122,9 +181,9 @@ export class StrapiService {
try {
this.logger.log(`Syncing single article from Strapi: ${strapiId}, event: ${event}`);
const response = await lastValueFrom(
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiArticle>>(
`${this.strapiUrl}/api/articles/${strapiId}`,
`${this.strapiUrl}/api/articles/${strapiId}?populate=*`,
{
headers: this.getHeaders(),
},
@ -146,6 +205,8 @@ export class StrapiService {
status = ArticleStatus.DRAFT;
}
const imageUrl = this.extractImageUrl(strapiArticle);
const articleData: Partial<CreateArticleDto> = {
title: strapiArticle.title,
excerpt: strapiArticle.description,
@ -153,6 +214,7 @@ export class StrapiService {
slug: strapiArticle.slug,
status,
tags: [],
featuredImage: imageUrl,
};
await this.articlesService.syncFromStrapi(
@ -194,9 +256,9 @@ export class StrapiService {
try {
this.logger.log('Starting live blogs sync from Strapi...');
const response = await lastValueFrom(
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiLiveBlog[]>>(
`${this.strapiUrl}/api/live-blogs`,
`${this.strapiUrl}/api/live-blogs?populate=*`,
{
headers: this.getHeaders(),
},
@ -207,11 +269,14 @@ export class StrapiService {
let syncedCount = 0;
for (const strapiLiveBlog of strapiLiveBlogs) {
const imageUrl = this.extractLiveBlogImageUrl(strapiLiveBlog);
const liveBlogData: Partial<CreateLiveBlogDto> = {
title: strapiLiveBlog.title,
description: strapiLiveBlog.description,
slug: strapiLiveBlog.slug,
status: this.mapStrapiStatusToLiveBlogStatus(strapiLiveBlog.status),
featuredImage: imageUrl,
};
await this.liveBlogService.syncFromStrapi(
@ -237,9 +302,9 @@ export class StrapiService {
try {
this.logger.log(`Syncing single live blog from Strapi: ${strapiId}, event: ${event}`);
const response = await lastValueFrom(
const response = await lastValueFrom(
this.httpService.get<StrapiResponse<StrapiLiveBlog>>(
`${this.strapiUrl}/api/live-blogs/${strapiId}`,
`${this.strapiUrl}/api/live-blogs/${strapiId}?populate=*`,
{
headers: this.getHeaders(),
},
@ -257,11 +322,14 @@ export class StrapiService {
status = LiveBlogStatus.DRAFT;
}
const imageUrl = this.extractLiveBlogImageUrl(strapiLiveBlog);
const liveBlogData: Partial<CreateLiveBlogDto> = {
title: strapiLiveBlog.title,
description: strapiLiveBlog.description,
slug: strapiLiveBlog.slug,
status,
featuredImage: imageUrl,
};
await this.liveBlogService.syncFromStrapi(

View File

@ -66,11 +66,28 @@ export function ArticleDetailComponent({ id }: { id: string }) {
</div>
{data.featuredImage && (
<img
src={data.featuredImage}
alt={data.title}
className="w-full h-64 md:h-96 object-cover rounded-xl mb-8"
/>
<div className="relative w-full h-64 md:h-96 mb-8">
<img
src={data.featuredImage}
alt={data.title}
className="w-full h-full object-cover rounded-xl"
onError={(e) => {
console.error('Failed to load image:', data.featuredImage, e);
e.currentTarget.style.display = 'none';
// Show fallback
const fallback = e.currentTarget.nextElementSibling as HTMLElement;
if (fallback) {
fallback.style.display = 'flex';
}
}}
/>
<div
className="absolute inset-0 bg-gray-100 rounded-xl flex items-center justify-center text-gray-400 hidden"
style={{ display: 'none' }}
>
<span>Image not available</span>
</div>
</div>
)}
<div className="prose prose-slate max-w-none">