diff --git a/apps/admin/src/components/MetadataPanel.tsx b/apps/admin/src/components/MetadataPanel.tsx index c5dc45c..951cb6e 100644 --- a/apps/admin/src/components/MetadataPanel.tsx +++ b/apps/admin/src/components/MetadataPanel.tsx @@ -3,7 +3,6 @@ import { Box, Stack, TextField, Typography, IconButton } from '@mui/material'; export type Metadata = { title: string; tagsText: string; // comma-separated - canonicalUrl: string; featureImage?: string; }; @@ -20,7 +19,6 @@ export default function MetadataPanel({ value, onChange }: { set({ title: e.target.value })} fullWidth /> set({ tagsText: e.target.value })} fullWidth /> - set({ canonicalUrl: e.target.value })} fullWidth /> set({ featureImage: e.target.value })} fullWidth /> {value.featureImage && ( diff --git a/apps/admin/src/components/steps/StepMetadata.tsx b/apps/admin/src/components/steps/StepMetadata.tsx index 83696e7..c969bf4 100644 --- a/apps/admin/src/components/steps/StepMetadata.tsx +++ b/apps/admin/src/components/steps/StepMetadata.tsx @@ -41,7 +41,6 @@ export default function StepMetadata({ const newMetadata: Metadata = { title: metadata.title, tagsText: metadata.tags, - canonicalUrl: metadata.canonicalUrl, featureImage: value.featureImage, // Keep existing feature image }; diff --git a/apps/admin/src/hooks/usePostEditor.ts b/apps/admin/src/hooks/usePostEditor.ts index 261820c..fe06e82 100644 --- a/apps/admin/src/hooks/usePostEditor.ts +++ b/apps/admin/src/hooks/usePostEditor.ts @@ -6,7 +6,7 @@ import type { Metadata } from '../components/MetadataPanel'; export function usePostEditor(initialPostId?: string | null) { const [postId, setPostId] = useState(null); const [draft, setDraft] = useState(''); - const [meta, setMeta] = useState({ title: '', tagsText: '', canonicalUrl: '', featureImage: '' }); + const [meta, setMeta] = useState({ title: '', tagsText: '', featureImage: '' }); const [postClips, setPostClips] = useState>([]); const [postStatus, setPostStatus] = useState<'inbox' | 'editing' | 'ready_for_publish' | 'published' | 'archived'>('editing'); const [promptText, setPromptText] = useState(''); @@ -46,7 +46,6 @@ export function usePostEditor(initialPostId?: string | null) { ...m, title: data.title || '', tagsText: data.tagsText || '', - canonicalUrl: data.canonicalUrl || '', featureImage: data.featureImage || '' })); if (data.status) setPostStatus(data.status); @@ -69,7 +68,6 @@ export function usePostEditor(initialPostId?: string | null) { contentHtml: draft, tags: meta.tagsText ? meta.tagsText.split(',').map(t => t.trim()).filter(Boolean) : undefined, featureImage: meta.featureImage || undefined, - canonicalUrl: meta.canonicalUrl || undefined, status: postStatus, prompt: promptText || undefined, selectedImageKeys: genImageKeys.length > 0 ? genImageKeys : undefined, @@ -102,7 +100,6 @@ export function usePostEditor(initialPostId?: string | null) { title: meta.title || 'Untitled', html: draft, feature_image: meta.featureImage || null, - canonical_url: meta.canonicalUrl || null, tags, status, authors: selectedAuthor ? [selectedAuthor] : undefined, diff --git a/apps/api/src/ghost.ts b/apps/api/src/ghost.ts index 5a94c8d..f1e9cc7 100644 --- a/apps/api/src/ghost.ts +++ b/apps/api/src/ghost.ts @@ -146,6 +146,19 @@ router.post('/post', async (req, res) => { } } catch {} + function isValidHttpUrl(u?: string | null): boolean { + if (!u || typeof u !== 'string') return false; + try { + const parsed = new URL(u); + return parsed.protocol === 'http:' || parsed.protocol === 'https:'; + } catch { + return false; + } + } + + const safeCanonical = isValidHttpUrl(canonical_url) ? canonical_url : undefined; + const safeFeatureImage = isValidHttpUrl(rewrittenFeatureImage) ? rewrittenFeatureImage : undefined; + const payload = { posts: [ { @@ -154,8 +167,8 @@ router.post('/post', async (req, res) => { html: rewrittenHtml, status: status || 'draft', tags: tags && Array.isArray(tags) ? tags : [], - ...(rewrittenFeatureImage ? { feature_image: rewrittenFeatureImage } : {}), - ...(canonical_url ? { canonical_url } : {}), + ...(safeFeatureImage ? { feature_image: safeFeatureImage } : {}), + ...(safeCanonical ? { canonical_url: safeCanonical } : {}), ...(authors && Array.isArray(authors) && authors.length > 0 ? { authors } : {}), }, ],