← back to Norma

app/api/settings/tier-credentials/route.ts

105 lines

import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { hashPassword, ALL_ROLES, type UserRole } from '@/lib/auth';
import { requireRole } from '@/lib/require-role';

/**
 * GET /api/settings/tier-credentials
 * List all tier_credentials with org name join.
 */
export async function GET(request: NextRequest) {
  const result = requireRole(request, 'admin');
  if (result instanceof NextResponse) return result;

  try {
    const { rows } = await query(
      `SELECT tc.id, tc.username, tc.role, tc.org_id, tc.display_name,
              tc.created_at, tc.updated_at,
              na.org_name
       FROM tier_credentials tc
       LEFT JOIN nonprofit_accounts na ON tc.org_id = na.id
       ORDER BY tc.created_at ASC`
    );
    return NextResponse.json({ credentials: rows });
  } catch (err) {
    console.error('[tier-credentials] GET error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to fetch credentials' }, { status: 500 });
  }
}

/**
 * POST /api/settings/tier-credentials
 * Create a new tier credential.
 * Body: { username, password, role, org_id?, display_name? }
 */
export async function POST(request: NextRequest) {
  const session = requireRole(request, 'admin');
  if (session instanceof NextResponse) return session;

  try {
    const body = await request.json();
    const {
      username,
      password,
      role,
      org_id,
      display_name,
    } = body;

    if (!username || !password || !role) {
      return NextResponse.json(
        { error: 'username, password, and role are required' },
        { status: 400 }
      );
    }

    if (!ALL_ROLES.includes(role as UserRole)) {
      return NextResponse.json(
        { error: `role must be one of ${ALL_ROLES.join(', ')}` },
        { status: 400 }
      );
    }

    if (username.includes(':')) {
      return NextResponse.json(
        { error: 'Username cannot contain colons' },
        { status: 400 }
      );
    }

    // Check uniqueness
    const existing = await query(
      'SELECT id FROM tier_credentials WHERE username = $1',
      [username]
    );
    if (existing.rows.length > 0) {
      return NextResponse.json(
        { error: 'Username already exists' },
        { status: 409 }
      );
    }

    const hashed = await hashPassword(password);

    const { rows } = await query(
      `INSERT INTO tier_credentials
         (username, password_hash, role, org_id, display_name)
       VALUES ($1, $2, $3, $4, $5)
       RETURNING id, username, role, org_id, display_name,
                 created_at, updated_at`,
      [
        username,
        hashed,
        role,
        org_id || null,
        display_name || null,
      ]
    );

    return NextResponse.json({ credential: rows[0] }, { status: 201 });
  } catch (err) {
    console.error('[tier-credentials] POST error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to create credential' }, { status: 500 });
  }
}