← back to Norma
app/api/social/posts/route.ts
141 lines
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
/**
* GET /api/social/posts?limit=20&status=draft&platform=twitter
* Returns social posts, newest first.
*/
export async function GET(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const { searchParams } = new URL(request.url);
const limit = Math.min(parseInt(searchParams.get('limit') || '50', 10), 200);
const status = searchParams.get('status');
const platform = searchParams.get('platform');
const conditions: string[] = [];
const values: unknown[] = [];
let idx = 1;
if (status) {
conditions.push(`p.status = $${idx++}`);
values.push(status);
}
if (platform) {
conditions.push(`p.platform = $${idx++}`);
values.push(platform);
}
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
const result = await query(
`SELECT p.*, a.account_name, a.display_name
FROM social_posts p
LEFT JOIN social_accounts a ON p.account_id = a.id
${where}
ORDER BY p.created_at DESC
LIMIT $${idx}`,
[...values, limit]
);
return NextResponse.json({ posts: result.rows, count: result.rows.length });
}
/**
* POST /api/social/posts
* Create a new social post (draft by default).
*/
export async function POST(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const body = await request.json();
const {
platforms = [],
post_type = 'post',
body: postBody,
link_url,
hashtags = [],
mentions = [],
media_urls = [],
cross_post_group,
} = body;
if (!postBody || platforms.length === 0) {
return NextResponse.json({ error: 'body and platforms[] required' }, { status: 400 });
}
// Get account IDs for each platform
const accountResult = await query(
`SELECT id, platform FROM social_accounts WHERE platform = ANY($1) AND status = 'connected'`,
[platforms]
);
const accountMap = new Map(accountResult.rows.map((r: { id: string; platform: string }) => [r.platform, r.id]));
const groupId = cross_post_group || (platforms.length > 1 ? crypto.randomUUID() : null);
const created: unknown[] = [];
for (const platform of platforms) {
const accountId = accountMap.get(platform);
if (!accountId) continue;
const result = await query(
`INSERT INTO social_posts
(account_id, platform, post_type, body, link_url, hashtags, mentions, media_urls, cross_post_group, status)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, 'draft')
RETURNING *`,
[accountId, platform, post_type, postBody, link_url || null, hashtags, mentions, JSON.stringify(media_urls), groupId]
);
created.push(result.rows[0]);
}
return NextResponse.json({ posts: created, count: created.length });
}
/**
* PATCH /api/social/posts
* Update a post (edit body, status, etc.)
*/
export async function PATCH(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const { id, body: postBody, status, hashtags, link_url } = await request.json();
if (!id) return NextResponse.json({ error: 'id required' }, { status: 400 });
const sets: string[] = ['updated_at = NOW()'];
const values: unknown[] = [];
let idx = 1;
if (postBody !== undefined) { sets.push(`body = $${idx++}`); values.push(postBody); }
if (status !== undefined) { sets.push(`status = $${idx++}`); values.push(status); }
if (hashtags !== undefined) { sets.push(`hashtags = $${idx++}`); values.push(hashtags); }
if (link_url !== undefined) { sets.push(`link_url = $${idx++}`); values.push(link_url); }
values.push(id);
const result = await query(
`UPDATE social_posts SET ${sets.join(', ')} WHERE id = $${idx} RETURNING *`,
values
);
return NextResponse.json({ post: result.rows[0] });
}
/**
* DELETE /api/social/posts
* Delete a draft post.
*/
export async function DELETE(request: NextRequest) {
const auth = requireRole(request, 'admin');
if (auth instanceof NextResponse) return auth;
const { id } = await request.json();
if (!id) return NextResponse.json({ error: 'id required' }, { status: 400 });
await query(`DELETE FROM social_posts WHERE id = $1 AND status = 'draft'`, [id]);
return NextResponse.json({ deleted: true });
}