initial commit: rocket.new export of broadcastbeat

This commit is contained in:
Ryan Salazar
2026-05-07 16:39:17 +00:00
commit 70aa1ad46e
302 changed files with 60710 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
import { NextRequest, NextResponse } from 'next/server';
import { createClient } from '@/lib/supabase/client';
import {
sendProfileApprovedEmail,
sendProfileRejectedEmail,
} from '@/lib/marketplace/emailService';
export async function POST(req: NextRequest) {
try {
const { profileId, action, reason } = await req.json();
if (!profileId || !action) {
return NextResponse.json({ error: 'profileId and action required' }, { status: 400 });
}
const supabase = createClient();
// Fetch profile
const { data: profile } = await supabase
.from('marketplace_profiles')
.select('*')
.eq('id', profileId)
.maybeSingle();
if (!profile) {
return NextResponse.json({ error: 'Profile not found' }, { status: 404 });
}
let email: string | null = null;
let name = 'there';
if (profile.profile_type === 'crew') {
const { data: details } = await supabase
.from('marketplace_crew_details')
.select('name')
.eq('profile_id', profileId)
.maybeSingle();
if (details?.name) name = details.name;
} else {
const { data: details } = await supabase
.from('marketplace_vendor_details')
.select('company_name, contact_email')
.eq('profile_id', profileId)
.maybeSingle();
if (details?.company_name) name = details.company_name;
if (details?.contact_email) email = details.contact_email;
}
if (email) {
if (action === 'approve') {
await sendProfileApprovedEmail(email, name);
} else if (action === 'reject' && reason) {
await sendProfileRejectedEmail(email, name, reason);
}
}
return NextResponse.json({ success: true });
} catch (err: any) {
console.error('Admin notify error:', err);
return NextResponse.json({ error: err.message }, { status: 500 });
}
}

View File

@@ -0,0 +1,34 @@
import { NextRequest, NextResponse } from 'next/server';
import nodemailer from 'nodemailer';
export async function POST(req: NextRequest) {
try {
const { to, subject, html } = await req.json();
if (!to || !subject || !html) {
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
}
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT || '587'),
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
await transporter.sendMail({
from: `"${process.env.SMTP_FROM_NAME || 'BroadcastBeat'}" <${process.env.SMTP_FROM_EMAIL}>`,
to,
subject,
html,
});
return NextResponse.json({ success: true });
} catch (err: any) {
console.error('Marketplace email error:', err);
return NextResponse.json({ error: err.message }, { status: 500 });
}
}

View File

@@ -0,0 +1,153 @@
import { NextRequest, NextResponse } from 'next/server';
import { createClient as createBrowserClient } from '@/lib/supabase/client';
// Index a profile in Typesense after approval
export async function POST(req: NextRequest) {
try {
const { profileId } = await req.json();
if (!profileId) {
return NextResponse.json({ error: 'profileId required' }, { status: 400 });
}
const supabase = createBrowserClient();
// Fetch profile
const { data: profile } = await supabase
.from('marketplace_profiles')
.select('*')
.eq('id', profileId)
.maybeSingle();
if (!profile) {
return NextResponse.json({ error: 'Profile not found' }, { status: 404 });
}
const TYPESENSE_HOST = process.env.TYPESENSE_HOST;
const TYPESENSE_PORT = process.env.TYPESENSE_PORT || '8108';
const TYPESENSE_PROTOCOL = process.env.TYPESENSE_PROTOCOL || 'http';
const TYPESENSE_API_KEY = process.env.TYPESENSE_API_KEY;
if (!TYPESENSE_HOST || !TYPESENSE_API_KEY) {
return NextResponse.json({ warning: 'Typesense not configured' }, { status: 200 });
}
const baseUrl = `${TYPESENSE_PROTOCOL}://${TYPESENSE_HOST}:${TYPESENSE_PORT}`;
const headers = {
'X-TYPESENSE-API-KEY': TYPESENSE_API_KEY,
'Content-Type': 'application/json',
};
if (profile.profile_type === 'crew') {
const { data: details } = await supabase
.from('marketplace_crew_details')
.select('*')
.eq('profile_id', profileId)
.maybeSingle();
if (!details) return NextResponse.json({ error: 'Crew details not found' }, { status: 404 });
await ensureCrewCollection(baseUrl, headers);
const doc = {
id: profile.id,
name: details.name,
location: details.location,
skills: details.skills || [],
union_status: details.union_status || 'non_union',
day_rate: details.day_rate || 0,
bio: details.bio || '',
slug: profile.slug,
featured: profile.subscription_tier === 'featured',
headshot_url: details.headshot_url || '',
};
await fetch(`${baseUrl}/collections/crew_profiles/documents`, {
method: 'POST',
headers,
body: JSON.stringify(doc),
});
} else {
const { data: details } = await supabase
.from('marketplace_vendor_details')
.select('*')
.eq('profile_id', profileId)
.maybeSingle();
if (!details) return NextResponse.json({ error: 'Vendor details not found' }, { status: 404 });
await ensureVendorCollection(baseUrl, headers);
const doc = {
id: profile.id,
company_name: details.company_name,
location: details.location,
category: details.category || '',
description: details.description || '',
slug: profile.slug,
featured: profile.subscription_tier === 'featured',
logo_url: details.logo_url || '',
};
await fetch(`${baseUrl}/collections/vendor_profiles/documents`, {
method: 'POST',
headers,
body: JSON.stringify(doc),
});
}
return NextResponse.json({ success: true });
} catch (err: any) {
console.error('Typesense index error:', err);
return NextResponse.json({ error: err.message }, { status: 500 });
}
}
async function ensureCrewCollection(baseUrl: string, headers: Record<string, string>) {
const check = await fetch(`${baseUrl}/collections/crew_profiles`, { headers });
if (check.ok) return;
await fetch(`${baseUrl}/collections`, {
method: 'POST',
headers,
body: JSON.stringify({
name: 'crew_profiles',
fields: [
{ name: 'id', type: 'string' },
{ name: 'name', type: 'string' },
{ name: 'location', type: 'string' },
{ name: 'skills', type: 'string[]', facet: true },
{ name: 'union_status', type: 'string', facet: true },
{ name: 'day_rate', type: 'int32', optional: true },
{ name: 'bio', type: 'string', optional: true },
{ name: 'slug', type: 'string' },
{ name: 'featured', type: 'bool', facet: true },
{ name: 'headshot_url', type: 'string', optional: true },
],
default_sorting_field: 'day_rate',
}),
});
}
async function ensureVendorCollection(baseUrl: string, headers: Record<string, string>) {
const check = await fetch(`${baseUrl}/collections/vendor_profiles`, { headers });
if (check.ok) return;
await fetch(`${baseUrl}/collections`, {
method: 'POST',
headers,
body: JSON.stringify({
name: 'vendor_profiles',
fields: [
{ name: 'id', type: 'string' },
{ name: 'company_name', type: 'string' },
{ name: 'location', type: 'string' },
{ name: 'category', type: 'string', facet: true },
{ name: 'description', type: 'string', optional: true },
{ name: 'slug', type: 'string' },
{ name: 'featured', type: 'bool', facet: true },
{ name: 'logo_url', type: 'string', optional: true },
],
default_sorting_field: '',
}),
});
}