mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
195 lines
6.5 KiB
TypeScript
195 lines
6.5 KiB
TypeScript
import { db } from '@sim/db'
|
|
import { member, templateCreators } from '@sim/db/schema'
|
|
import { createLogger } from '@sim/logger'
|
|
import { and, eq, or } from 'drizzle-orm'
|
|
import { type NextRequest, NextResponse } from 'next/server'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { z } from 'zod'
|
|
import { getSession } from '@/lib/auth'
|
|
import { generateRequestId } from '@/lib/core/utils/request'
|
|
import type { CreatorProfileDetails } from '@/app/_types/creator-profile'
|
|
|
|
const logger = createLogger('CreatorProfilesAPI')
|
|
|
|
const CreatorProfileDetailsSchema = z.object({
|
|
about: z.string().max(2000, 'Max 2000 characters').optional(),
|
|
xUrl: z.string().url().optional().or(z.literal('')),
|
|
linkedinUrl: z.string().url().optional().or(z.literal('')),
|
|
websiteUrl: z.string().url().optional().or(z.literal('')),
|
|
contactEmail: z.string().email().optional().or(z.literal('')),
|
|
})
|
|
|
|
const CreateCreatorProfileSchema = z.object({
|
|
referenceType: z.enum(['user', 'organization']),
|
|
referenceId: z.string().min(1, 'Reference ID is required'),
|
|
name: z.string().min(1, 'Name is required').max(100, 'Max 100 characters'),
|
|
profileImageUrl: z.string().min(1, 'Profile image is required'),
|
|
details: CreatorProfileDetailsSchema.optional(),
|
|
})
|
|
|
|
// GET /api/creators - Get creator profiles for current user
|
|
export async function GET(request: NextRequest) {
|
|
const requestId = generateRequestId()
|
|
const { searchParams } = new URL(request.url)
|
|
const userId = searchParams.get('userId')
|
|
|
|
try {
|
|
const session = await getSession()
|
|
if (!session?.user?.id) {
|
|
logger.warn(`[${requestId}] Unauthorized access attempt`)
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
// Get user's organizations where they're admin or owner
|
|
const userOrgs = await db
|
|
.select({ organizationId: member.organizationId })
|
|
.from(member)
|
|
.where(
|
|
and(
|
|
eq(member.userId, session.user.id),
|
|
or(eq(member.role, 'owner'), eq(member.role, 'admin'))
|
|
)
|
|
)
|
|
|
|
const orgIds = userOrgs.map((m) => m.organizationId)
|
|
|
|
// Get creator profiles for user and their organizations
|
|
const profiles = await db
|
|
.select()
|
|
.from(templateCreators)
|
|
.where(
|
|
or(
|
|
and(
|
|
eq(templateCreators.referenceType, 'user'),
|
|
eq(templateCreators.referenceId, session.user.id)
|
|
),
|
|
...orgIds.map((orgId) =>
|
|
and(
|
|
eq(templateCreators.referenceType, 'organization'),
|
|
eq(templateCreators.referenceId, orgId)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
logger.info(`[${requestId}] Retrieved ${profiles.length} creator profiles`)
|
|
|
|
return NextResponse.json({ profiles })
|
|
} catch (error: any) {
|
|
logger.error(`[${requestId}] Error fetching creator profiles`, error)
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
}
|
|
}
|
|
|
|
// POST /api/creators - Create a new creator profile
|
|
export async function POST(request: NextRequest) {
|
|
const requestId = generateRequestId()
|
|
|
|
try {
|
|
const session = await getSession()
|
|
if (!session?.user?.id) {
|
|
logger.warn(`[${requestId}] Unauthorized creation attempt`)
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
const body = await request.json()
|
|
const data = CreateCreatorProfileSchema.parse(body)
|
|
|
|
logger.debug(`[${requestId}] Creating creator profile:`, {
|
|
referenceType: data.referenceType,
|
|
referenceId: data.referenceId,
|
|
})
|
|
|
|
// Validate permissions
|
|
if (data.referenceType === 'user') {
|
|
if (data.referenceId !== session.user.id) {
|
|
logger.warn(`[${requestId}] User tried to create profile for another user`)
|
|
return NextResponse.json(
|
|
{ error: 'Cannot create profile for another user' },
|
|
{ status: 403 }
|
|
)
|
|
}
|
|
} else if (data.referenceType === 'organization') {
|
|
// Check if user is admin/owner of the organization
|
|
const membership = await db
|
|
.select()
|
|
.from(member)
|
|
.where(
|
|
and(
|
|
eq(member.userId, session.user.id),
|
|
eq(member.organizationId, data.referenceId),
|
|
or(eq(member.role, 'owner'), eq(member.role, 'admin'))
|
|
)
|
|
)
|
|
.limit(1)
|
|
|
|
if (membership.length === 0) {
|
|
logger.warn(`[${requestId}] User not authorized for organization: ${data.referenceId}`)
|
|
return NextResponse.json(
|
|
{ error: 'You must be an admin or owner to create an organization profile' },
|
|
{ status: 403 }
|
|
)
|
|
}
|
|
}
|
|
|
|
// Check if profile already exists
|
|
const existing = await db
|
|
.select()
|
|
.from(templateCreators)
|
|
.where(
|
|
and(
|
|
eq(templateCreators.referenceType, data.referenceType),
|
|
eq(templateCreators.referenceId, data.referenceId)
|
|
)
|
|
)
|
|
.limit(1)
|
|
|
|
if (existing.length > 0) {
|
|
logger.warn(
|
|
`[${requestId}] Profile already exists for ${data.referenceType}:${data.referenceId}`
|
|
)
|
|
return NextResponse.json({ error: 'Creator profile already exists' }, { status: 409 })
|
|
}
|
|
|
|
// Create the profile
|
|
const profileId = uuidv4()
|
|
const now = new Date()
|
|
|
|
const details: CreatorProfileDetails = {}
|
|
if (data.details?.about) details.about = data.details.about
|
|
if (data.details?.xUrl) details.xUrl = data.details.xUrl
|
|
if (data.details?.linkedinUrl) details.linkedinUrl = data.details.linkedinUrl
|
|
if (data.details?.websiteUrl) details.websiteUrl = data.details.websiteUrl
|
|
if (data.details?.contactEmail) details.contactEmail = data.details.contactEmail
|
|
|
|
const newProfile = {
|
|
id: profileId,
|
|
referenceType: data.referenceType,
|
|
referenceId: data.referenceId,
|
|
name: data.name,
|
|
profileImageUrl: data.profileImageUrl || null,
|
|
details: Object.keys(details).length > 0 ? details : null,
|
|
createdBy: session.user.id,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
}
|
|
|
|
await db.insert(templateCreators).values(newProfile)
|
|
|
|
logger.info(`[${requestId}] Successfully created creator profile: ${profileId}`)
|
|
|
|
return NextResponse.json({ data: newProfile }, { status: 201 })
|
|
} catch (error: any) {
|
|
if (error instanceof z.ZodError) {
|
|
logger.warn(`[${requestId}] Invalid profile data`, { errors: error.errors })
|
|
return NextResponse.json(
|
|
{ error: 'Invalid profile data', details: error.errors },
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
logger.error(`[${requestId}] Error creating creator profile`, error)
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
}
|
|
}
|