refactor: 移除项目详情页的图片/视频显示逻辑
由于 source 字段语义不清且容易产生 CSP 错误,暂时移除 图片和视频的显示功能,待后续设计更完善的图片管理方案
This commit is contained in:
@@ -48,48 +48,6 @@ function formatDate(date: Date | string, locale: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to check if URL is an image
|
|
||||||
function isImageUrl(url: string): boolean {
|
|
||||||
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp']
|
|
||||||
const lowerUrl = url.toLowerCase()
|
|
||||||
return imageExtensions.some(ext => lowerUrl.includes(ext))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to check if URL can be embedded in iframe
|
|
||||||
// Many sites like GitHub, Google, etc. block iframe embedding via CSP
|
|
||||||
function isEmbeddableUrl(url: string): boolean {
|
|
||||||
const hostname = new URL(url).hostname.toLowerCase()
|
|
||||||
|
|
||||||
// Whitelist of domains that allow iframe embedding
|
|
||||||
const embeddableDomains = [
|
|
||||||
'youtube.com',
|
|
||||||
'youtu.be',
|
|
||||||
'vimeo.com',
|
|
||||||
'player.vimeo.com',
|
|
||||||
'drive.google.com',
|
|
||||||
'docs.google.com',
|
|
||||||
'www.figma.com',
|
|
||||||
'codepen.io',
|
|
||||||
'jsfiddle.net',
|
|
||||||
'codesandbox.io',
|
|
||||||
'stackblitz.com',
|
|
||||||
'replit.com',
|
|
||||||
'loom.com',
|
|
||||||
'wistia.com',
|
|
||||||
'brightcove.com',
|
|
||||||
'dailymotion.com',
|
|
||||||
'twitch.tv',
|
|
||||||
'soundcloud.com',
|
|
||||||
'spotify.com',
|
|
||||||
'canva.com',
|
|
||||||
'notion.so',
|
|
||||||
'typeform.com',
|
|
||||||
]
|
|
||||||
|
|
||||||
// Check if hostname matches any embeddable domain
|
|
||||||
return embeddableDomains.some((domain) => hostname === domain || hostname.endsWith('.' + domain))
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function ProjectDetail({ project, locale }: ProjectDetailProps) {
|
export async function ProjectDetail({ project, locale }: ProjectDetailProps) {
|
||||||
const t = await getTranslations('project')
|
const t = await getTranslations('project')
|
||||||
|
|
||||||
@@ -149,26 +107,6 @@ export async function ProjectDetail({ project, locale }: ProjectDetailProps) {
|
|||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Featured Image/Video Placeholder - only show if source exists */}
|
|
||||||
{project.source && (
|
|
||||||
<div className="relative w-full border-2 border-black dark:border-gray-600 bg-gray-100 dark:bg-gray-800 mb-10 overflow-hidden shadow-brutal dark:shadow-brutal-dark">
|
|
||||||
{isImageUrl(project.source) ? (
|
|
||||||
<img
|
|
||||||
src={project.source}
|
|
||||||
alt={`${displayName} demo`}
|
|
||||||
className="w-full h-auto"
|
|
||||||
/>
|
|
||||||
) : isEmbeddableUrl(project.source) ? (
|
|
||||||
<iframe
|
|
||||||
src={project.source}
|
|
||||||
className="w-full aspect-video"
|
|
||||||
title={`${displayName} demo`}
|
|
||||||
allowFullScreen
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Article Content */}
|
{/* Article Content */}
|
||||||
|
|||||||
Reference in New Issue
Block a user