422 lines
11 KiB
TypeScript
422 lines
11 KiB
TypeScript
export const config = {
|
|
runtime: "nodejs",
|
|
};
|
|
|
|
import { createClient } from "@supabase/supabase-js";
|
|
|
|
const supabaseUrl = process.env.VITE_SUPABASE_URL;
|
|
const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE;
|
|
|
|
if (!supabaseUrl || !supabaseServiceRole) {
|
|
throw new Error("Missing Supabase configuration");
|
|
}
|
|
|
|
const supabase = createClient(supabaseUrl, supabaseServiceRole);
|
|
|
|
const VALID_ARMS = [
|
|
"labs",
|
|
"gameforge",
|
|
"corp",
|
|
"foundation",
|
|
"devlink",
|
|
"nexus",
|
|
"staff",
|
|
];
|
|
|
|
export default async function handler(req: any, res: any) {
|
|
if (req.method === "POST") {
|
|
try {
|
|
const {
|
|
title,
|
|
content,
|
|
arm_affiliation,
|
|
creator_id,
|
|
collaborator_ids,
|
|
tags,
|
|
category,
|
|
} = req.body;
|
|
|
|
// Validate required fields
|
|
if (!title || !content || !arm_affiliation || !creator_id) {
|
|
return res.status(400).json({
|
|
error:
|
|
"Missing required fields: title, content, arm_affiliation, creator_id",
|
|
});
|
|
}
|
|
|
|
// Validate arm_affiliation
|
|
if (!VALID_ARMS.includes(arm_affiliation)) {
|
|
return res.status(400).json({
|
|
error: `Invalid arm_affiliation. Must be one of: ${VALID_ARMS.join(", ")}`,
|
|
});
|
|
}
|
|
|
|
// Validate content length
|
|
if (title.trim().length === 0 || title.length > 500) {
|
|
return res
|
|
.status(400)
|
|
.json({ error: "Title must be between 1 and 500 characters" });
|
|
}
|
|
|
|
if (content.trim().length === 0 || content.length > 5000) {
|
|
return res
|
|
.status(400)
|
|
.json({ error: "Content must be between 1 and 5000 characters" });
|
|
}
|
|
|
|
// Validate creator exists
|
|
const { data: creator, error: creatorError } = await supabase
|
|
.from("user_profiles")
|
|
.select("id")
|
|
.eq("id", creator_id)
|
|
.single();
|
|
|
|
if (creatorError || !creator) {
|
|
return res.status(401).json({ error: "Creator not found" });
|
|
}
|
|
|
|
// Insert the collaboration post
|
|
const { data: postData, error: postError } = await supabase
|
|
.from("collaboration_posts")
|
|
.insert({
|
|
title: title.trim(),
|
|
content: content.trim(),
|
|
arm_affiliation,
|
|
created_by: creator_id,
|
|
tags: tags || [],
|
|
category: category || null,
|
|
is_published: true,
|
|
likes_count: 0,
|
|
comments_count: 0,
|
|
})
|
|
.select("id");
|
|
|
|
if (postError) {
|
|
console.error("[Collaboration Posts API] Insert error:", postError);
|
|
return res.status(500).json({ error: postError.message });
|
|
}
|
|
|
|
const postId = postData?.[0]?.id;
|
|
if (!postId) {
|
|
return res.status(500).json({ error: "Failed to create post" });
|
|
}
|
|
|
|
// Add creator as author
|
|
await supabase.from("collaboration_posts_authors").insert({
|
|
collaboration_post_id: postId,
|
|
user_id: creator_id,
|
|
role: "creator",
|
|
});
|
|
|
|
// Add collaborators if provided
|
|
if (
|
|
collaborator_ids &&
|
|
Array.isArray(collaborator_ids) &&
|
|
collaborator_ids.length > 0
|
|
) {
|
|
const collaboratorInserts = collaborator_ids
|
|
.filter((id: string) => id !== creator_id) // Exclude creator from collaborators
|
|
.map((id: string) => ({
|
|
collaboration_post_id: postId,
|
|
user_id: id,
|
|
role: "contributor",
|
|
}));
|
|
|
|
if (collaboratorInserts.length > 0) {
|
|
await supabase
|
|
.from("collaboration_posts_authors")
|
|
.insert(collaboratorInserts);
|
|
}
|
|
}
|
|
|
|
// Fetch the complete post with authors
|
|
const { data, error: fetchError } = await supabase
|
|
.from("collaboration_posts")
|
|
.select(
|
|
`
|
|
id,
|
|
title,
|
|
content,
|
|
arm_affiliation,
|
|
created_by,
|
|
created_at,
|
|
updated_at,
|
|
is_published,
|
|
likes_count,
|
|
comments_count,
|
|
tags,
|
|
category,
|
|
user_profiles!collaboration_posts_created_by_fkey (
|
|
id,
|
|
username,
|
|
full_name,
|
|
avatar_url
|
|
),
|
|
collaboration_posts_authors (
|
|
user_id,
|
|
role,
|
|
user_profiles!collaboration_posts_authors_user_id_fkey (
|
|
id,
|
|
username,
|
|
full_name,
|
|
avatar_url
|
|
)
|
|
)
|
|
`,
|
|
)
|
|
.eq("id", postId)
|
|
.single();
|
|
|
|
if (fetchError) {
|
|
console.error("[Collaboration Posts API] Fetch error:", fetchError);
|
|
return res.status(500).json({ error: fetchError.message });
|
|
}
|
|
|
|
return res.status(201).json({
|
|
post: data,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[Collaboration Posts API POST] Unexpected error:", error);
|
|
return res
|
|
.status(500)
|
|
.json({ error: error.message || "Internal server error" });
|
|
}
|
|
}
|
|
|
|
if (req.method === "GET") {
|
|
try {
|
|
const { arm_filter, limit = 20, offset = 0 } = req.query;
|
|
|
|
// Build query
|
|
let query = supabase
|
|
.from("collaboration_posts")
|
|
.select(
|
|
`
|
|
id,
|
|
title,
|
|
content,
|
|
arm_affiliation,
|
|
created_by,
|
|
created_at,
|
|
updated_at,
|
|
is_published,
|
|
likes_count,
|
|
comments_count,
|
|
tags,
|
|
category,
|
|
user_profiles!collaboration_posts_created_by_fkey (
|
|
id,
|
|
username,
|
|
full_name,
|
|
avatar_url
|
|
),
|
|
collaboration_posts_authors (
|
|
user_id,
|
|
role,
|
|
user_profiles!collaboration_posts_authors_user_id_fkey (
|
|
id,
|
|
username,
|
|
full_name,
|
|
avatar_url
|
|
)
|
|
)
|
|
`,
|
|
{ count: "exact" },
|
|
)
|
|
.eq("is_published", true)
|
|
.order("created_at", { ascending: false });
|
|
|
|
// Apply arm filter if specified
|
|
if (arm_filter) {
|
|
const armIds = Array.isArray(arm_filter) ? arm_filter : [arm_filter];
|
|
query = query.in("arm_affiliation", armIds);
|
|
}
|
|
|
|
// Apply pagination
|
|
query = query.range(
|
|
parseInt(offset),
|
|
parseInt(offset) + parseInt(limit) - 1,
|
|
);
|
|
|
|
const { data, error, count } = await query;
|
|
|
|
if (error) {
|
|
console.error("[Collaboration Posts API] Query error:", error);
|
|
return res.status(500).json({ error: error.message });
|
|
}
|
|
|
|
return res.status(200).json({
|
|
posts: data || [],
|
|
total: count || 0,
|
|
limit: parseInt(limit),
|
|
offset: parseInt(offset),
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[Collaboration Posts API GET] Unexpected error:", error);
|
|
return res
|
|
.status(500)
|
|
.json({ error: error.message || "Internal server error" });
|
|
}
|
|
}
|
|
|
|
if (req.method === "PUT") {
|
|
try {
|
|
const { id, title, content, arm_affiliation, category, tags, user_id } =
|
|
req.body;
|
|
|
|
if (!id || !user_id) {
|
|
return res.status(400).json({ error: "Missing id or user_id" });
|
|
}
|
|
|
|
// Get the post to verify ownership
|
|
const { data: post, error: fetchError } = await supabase
|
|
.from("collaboration_posts")
|
|
.select("created_by")
|
|
.eq("id", id)
|
|
.single();
|
|
|
|
if (fetchError) {
|
|
console.error("[Collaboration Posts API] Fetch error:", fetchError);
|
|
return res.status(404).json({ error: "Post not found" });
|
|
}
|
|
|
|
if (post.created_by !== user_id) {
|
|
return res
|
|
.status(403)
|
|
.json({ error: "You can only edit posts you created" });
|
|
}
|
|
|
|
// Validate updates
|
|
if (title && (title.trim().length === 0 || title.length > 500)) {
|
|
return res
|
|
.status(400)
|
|
.json({ error: "Title must be between 1 and 500 characters" });
|
|
}
|
|
|
|
if (content && (content.trim().length === 0 || content.length > 5000)) {
|
|
return res
|
|
.status(400)
|
|
.json({ error: "Content must be between 1 and 5000 characters" });
|
|
}
|
|
|
|
if (arm_affiliation && !VALID_ARMS.includes(arm_affiliation)) {
|
|
return res.status(400).json({
|
|
error: `Invalid arm_affiliation. Must be one of: ${VALID_ARMS.join(", ")}`,
|
|
});
|
|
}
|
|
|
|
// Build update object
|
|
const updateData: any = {};
|
|
if (title) updateData.title = title.trim();
|
|
if (content) updateData.content = content.trim();
|
|
if (arm_affiliation) updateData.arm_affiliation = arm_affiliation;
|
|
if (category !== undefined) updateData.category = category;
|
|
if (tags) updateData.tags = tags;
|
|
|
|
const { data, error } = await supabase
|
|
.from("collaboration_posts")
|
|
.update(updateData)
|
|
.eq("id", id)
|
|
.select(
|
|
`
|
|
id,
|
|
title,
|
|
content,
|
|
arm_affiliation,
|
|
created_by,
|
|
created_at,
|
|
updated_at,
|
|
is_published,
|
|
likes_count,
|
|
comments_count,
|
|
tags,
|
|
category,
|
|
user_profiles!collaboration_posts_created_by_fkey (
|
|
id,
|
|
username,
|
|
full_name,
|
|
avatar_url
|
|
),
|
|
collaboration_posts_authors (
|
|
user_id,
|
|
role,
|
|
user_profiles!collaboration_posts_authors_user_id_fkey (
|
|
id,
|
|
username,
|
|
full_name,
|
|
avatar_url
|
|
)
|
|
)
|
|
`,
|
|
);
|
|
|
|
if (error) {
|
|
console.error("[Collaboration Posts API] Update error:", error);
|
|
return res.status(500).json({ error: error.message });
|
|
}
|
|
|
|
return res.status(200).json({
|
|
post: data?.[0],
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[Collaboration Posts API PUT] Unexpected error:", error);
|
|
return res
|
|
.status(500)
|
|
.json({ error: error.message || "Internal server error" });
|
|
}
|
|
}
|
|
|
|
if (req.method === "DELETE") {
|
|
try {
|
|
const { id, user_id } = req.body;
|
|
|
|
if (!id || !user_id) {
|
|
return res.status(400).json({ error: "Missing id or user_id" });
|
|
}
|
|
|
|
// Get the post to verify ownership
|
|
const { data: post, error: fetchError } = await supabase
|
|
.from("collaboration_posts")
|
|
.select("created_by")
|
|
.eq("id", id)
|
|
.single();
|
|
|
|
if (fetchError) {
|
|
console.error("[Collaboration Posts API] Fetch error:", fetchError);
|
|
return res.status(404).json({ error: "Post not found" });
|
|
}
|
|
|
|
if (post.created_by !== user_id) {
|
|
return res
|
|
.status(403)
|
|
.json({ error: "You can only delete posts you created" });
|
|
}
|
|
|
|
// Delete the post (cascade will delete likes, comments, and authors)
|
|
const { error } = await supabase
|
|
.from("collaboration_posts")
|
|
.delete()
|
|
.eq("id", id);
|
|
|
|
if (error) {
|
|
console.error("[Collaboration Posts API] Delete error:", error);
|
|
return res.status(500).json({ error: error.message });
|
|
}
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: "Post deleted successfully",
|
|
});
|
|
} catch (error: any) {
|
|
console.error(
|
|
"[Collaboration Posts API DELETE] Unexpected error:",
|
|
error,
|
|
);
|
|
return res
|
|
.status(500)
|
|
.json({ error: error.message || "Internal server error" });
|
|
}
|
|
}
|
|
|
|
return res.status(405).json({ error: "Method not allowed" });
|
|
}
|