(
user: ContentUser,
contentId: string,
opts: { publishImmediately?: boolean } = {}
)
| 786 | * Approve pending content - direct function call (no HTTP required). |
| 787 | */ |
| 788 | export async function approveContentForUser( |
| 789 | user: ContentUser, |
| 790 | contentId: string, |
| 791 | opts: { publishImmediately?: boolean } = {} |
| 792 | ): Promise<ContentReviewResult> { |
| 793 | const publishImmediately = opts.publishImmediately ?? true; |
| 794 | const pool = getPool(); |
| 795 | |
| 796 | const contentResult = await pool.query( |
| 797 | `SELECT p.*, wg.slug as committee_slug, wg.name as committee_name, wg.slack_channel_id |
| 798 | FROM perspectives p |
| 799 | LEFT JOIN working_groups wg ON wg.id = p.working_group_id |
| 800 | WHERE p.id = $1`, |
| 801 | [contentId] |
| 802 | ); |
| 803 | |
| 804 | if (contentResult.rows.length === 0) { |
| 805 | return { success: false, error: 'not_found', error_message: `No content found with id: ${contentId}` }; |
| 806 | } |
| 807 | |
| 808 | const content = contentResult.rows[0]; |
| 809 | |
| 810 | if (content.status !== 'pending_review') { |
| 811 | return { |
| 812 | success: false, |
| 813 | error: 'invalid_status', |
| 814 | error_message: `Content is not pending review (current status: ${content.status})`, |
| 815 | }; |
| 816 | } |
| 817 | |
| 818 | const userIsAdmin = await isWebUserAAOAdmin(user.id); |
| 819 | const userIsLead = content.working_group_id |
| 820 | ? await isCommitteeLead(content.working_group_id, user.id) |
| 821 | : false; |
| 822 | |
| 823 | if (!userIsAdmin && !userIsLead) { |
| 824 | return { |
| 825 | success: false, |
| 826 | error: 'permission_denied', |
| 827 | error_message: 'You do not have permission to approve this content', |
| 828 | }; |
| 829 | } |
| 830 | |
| 831 | const newStatus: 'published' | 'draft' = publishImmediately ? 'published' : 'draft'; |
| 832 | const publishedAt = publishImmediately ? new Date().toISOString() : null; |
| 833 | |
| 834 | await pool.query( |
| 835 | `UPDATE perspectives |
| 836 | SET status = $1, published_at = $2, |
| 837 | reviewed_by_user_id = $3, reviewed_at = NOW() |
| 838 | WHERE id = $4`, |
| 839 | [newStatus, publishedAt, user.id, contentId] |
| 840 | ); |
| 841 | |
| 842 | logger.info({ |
| 843 | contentId, |
| 844 | reviewerId: user.id, |
| 845 | newStatus, |
no test coverage detected