Create blog publish API endpoint
cgen-a4beb225c4d14bddab119d2b5b7eed91
This commit is contained in:
parent
0df5193de7
commit
c779ebc44c
1 changed files with 108 additions and 0 deletions
108
api/blog/publish.ts
Normal file
108
api/blog/publish.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { createClient } from "@supabase/supabase-js";
|
||||||
|
import { publishPostToGhost, updatePostInGhost } from "@/server/ghost-admin-api";
|
||||||
|
|
||||||
|
const supabaseUrl = process.env.SUPABASE_URL || "";
|
||||||
|
const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE || "";
|
||||||
|
|
||||||
|
const supabase =
|
||||||
|
supabaseUrl && supabaseServiceRole
|
||||||
|
? createClient(supabaseUrl, supabaseServiceRole)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
async function isUserAdminOrStaff(userId: string): Promise<boolean> {
|
||||||
|
if (!supabase) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from("user_roles")
|
||||||
|
.select("role")
|
||||||
|
.eq("user_id", userId)
|
||||||
|
.in("role", ["admin", "staff"])
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return !error && data;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function handler(req: any, res: any) {
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
return res.status(405).json({ error: "Method not allowed" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get user from session or auth header
|
||||||
|
const userId = req.user?.id || req.query.user_id;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return res.status(401).json({ error: "Unauthorized" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is admin or staff
|
||||||
|
const isAuthorized = await isUserAdminOrStaff(userId);
|
||||||
|
if (!isAuthorized) {
|
||||||
|
return res.status(403).json({ error: "Forbidden: Admin/Staff access required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
excerpt,
|
||||||
|
html,
|
||||||
|
slug,
|
||||||
|
feature_image,
|
||||||
|
published_at,
|
||||||
|
status = "published",
|
||||||
|
tags,
|
||||||
|
meta_description,
|
||||||
|
meta_title,
|
||||||
|
post_id, // for updates
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (!title || !html) {
|
||||||
|
return res.status(400).json({ error: "Title and body are required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish or update post
|
||||||
|
let result;
|
||||||
|
if (post_id) {
|
||||||
|
result = await updatePostInGhost(post_id, {
|
||||||
|
title,
|
||||||
|
excerpt,
|
||||||
|
html,
|
||||||
|
feature_image,
|
||||||
|
published_at,
|
||||||
|
status,
|
||||||
|
tags: tags?.map((tag: string) => ({ name: tag })) || [],
|
||||||
|
meta_description,
|
||||||
|
meta_title,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result = await publishPostToGhost({
|
||||||
|
title,
|
||||||
|
excerpt,
|
||||||
|
html,
|
||||||
|
slug,
|
||||||
|
feature_image,
|
||||||
|
published_at,
|
||||||
|
status,
|
||||||
|
tags: tags?.map((tag: string) => ({ name: tag })) || [],
|
||||||
|
meta_description,
|
||||||
|
meta_title,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
postId: result.id,
|
||||||
|
url: result.url,
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("Blog publish API error:", error);
|
||||||
|
return res.status(500).json({
|
||||||
|
error: "Failed to publish post",
|
||||||
|
message: error?.message || "Unknown error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue