← back to Norma

app/api/orgs/signup/route.ts

73 lines

import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
import { auditLog } from '@/lib/audit';

/**
 * POST /api/orgs/signup
 * Nonprofit signup/intake form — creates or updates an organization with
 * political alignment, advocacy profile, and matching preferences.
 */
export async function POST(request: NextRequest) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;

  try {
    const data = await request.json();

    if (!data.name || typeof data.name !== 'string' || data.name.trim() === '') {
      return NextResponse.json({ error: 'Organization name is required' }, { status: 400 });
    }

    const result = await query(
      `INSERT INTO organizations (
        name, type, tax_status, key_issues, geo_focus, geo_focus_detail,
        state, city, political_leaning, advocacy_intensity,
        bipartisan_willingness, supporter_count, outreach_channels,
        response_speed, urgency_threshold, sentiment_preference,
        preferred_actions, primary_contact, email, phone, website,
        description, onboard_status, source
      ) VALUES (
        $1, $2, $3, $4, $5, $6,
        $7, $8, $9, $10,
        $11, $12, $13,
        $14, $15, $16,
        $17, $18, $19, $20, $21,
        $22, 'signup', 'self_signup'
      ) RETURNING id, name, onboard_status`,
      [
        data.name.trim(),
        data.org_type || null,
        data.tax_status || null,
        data.key_issues || [],
        data.geo_focus || null,
        data.geo_focus_detail || null,
        data.state || null,
        data.city || null,
        data.political_leaning || null,
        data.advocacy_intensity || null,
        data.bipartisan_willingness || null,
        data.supporter_count || null,
        data.outreach_channels || [],
        data.response_speed || null,
        data.urgency_threshold || null,
        data.sentiment_preference || null,
        data.preferred_actions || [],
        data.primary_contact || null,
        data.email || null,
        data.phone || null,
        data.website || null,
        data.description || null,
      ],
    );

    const ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || undefined;
    await auditLog('org.signup', 'organization', result.rows[0].id, { name: data.name }, ip);

    return NextResponse.json({ organization: result.rows[0] }, { status: 201 });
  } catch (err) {
    console.error('[api/orgs/signup] POST error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to register organization' }, { status: 500 });
  }
}