All files / web/src/app/api/euclid/creations route.ts

0% Statements 0/121
0% Branches 0/1
0% Functions 0/1
0% Lines 0/121

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122                                                                                                                                                                                                                                                   
import { NextResponse, type NextRequest } from 'next/server'
import { withAuth } from '@/lib/auth/withAuth'
import { getUserId } from '@/lib/viewer'
import { db } from '@/db'
import * as schema from '@/db/schema'
import { eq, desc, and, inArray, isNull } from 'drizzle-orm'
import type { CreationData } from '@/db/schema/euclid-creations'

/** POST /api/euclid/creations — save a new playground creation */
export const POST = withAuth(async (request: NextRequest) => {
  try {
    const userId = await getUserId()
    const body = await request.json()

    const { data, thumbnail, isPublic, playerId, title } = body as {
      data: CreationData
      thumbnail?: string
      isPublic?: boolean
      playerId?: string | null
      title?: string | null
    }

    if (!data || !data.actions) {
      return NextResponse.json({ error: 'Missing creation data' }, { status: 400 })
    }

    const [creation] = await db
      .insert(schema.euclidCreations)
      .values({
        userId,
        playerId: playerId ?? null,
        data,
        thumbnail: thumbnail ?? null,
        isPublic: isPublic ?? false,
        title: title ?? null,
      })
      .returning()

    return NextResponse.json({ id: creation.id }, { status: 201 })
  } catch (err) {
    console.error('[euclid/creations] POST failed:', err)
    return NextResponse.json({ error: 'Failed to save creation' }, { status: 500 })
  }
})

/**
 * GET /api/euclid/creations
 *
 * Query params:
 *   ?mine=true            — scoped to current user/player (requires auth)
 *   ?playerId=<id>        — filter to a specific player (combine with mine=true)
 *                           if mine=true and no playerId, returns user-level creations (null player)
 *   ?isPublic=true        — filter to public only (combine with mine=true for "my published")
 *   ?ids=id1,id2,...      — fetch specific IDs (for "seen" tab)
 *   ?limit=N              — max results (default 50, max 100)
 */
export const GET = withAuth(async (request: NextRequest) => {
  try {
    const url = new URL(request.url)
    const mine = url.searchParams.get('mine') === 'true'
    const playerIdParam = url.searchParams.get('playerId')
    const isPublicFilter = url.searchParams.get('isPublic')
    const idsParam = url.searchParams.get('ids')
    const limit = Math.min(Number(url.searchParams.get('limit') ?? '50'), 100)

    const cols = {
      id: schema.euclidCreations.id,
      thumbnail: schema.euclidCreations.thumbnail,
      isPublic: schema.euclidCreations.isPublic,
      createdAt: schema.euclidCreations.createdAt,
      title: schema.euclidCreations.title,
      updatedAt: schema.euclidCreations.updatedAt,
    }

    // Fetch specific IDs (for "seen" tab)
    if (idsParam) {
      const ids = idsParam.split(',').filter(Boolean).slice(0, 100)
      if (ids.length === 0) return NextResponse.json({ creations: [] })
      const creations = await db
        .select(cols)
        .from(schema.euclidCreations)
        .where(inArray(schema.euclidCreations.id, ids))
        .all()
      return NextResponse.json({ creations })
    }

    const conditions = []

    if (mine) {
      if (playerIdParam) {
        // Player-scoped: show this player's creations
        conditions.push(eq(schema.euclidCreations.playerId, playerIdParam))
      } else {
        // User-scoped (no player selected): show user's creations with no player
        const userId = await getUserId()
        conditions.push(eq(schema.euclidCreations.userId, userId))
        conditions.push(isNull(schema.euclidCreations.playerId))
      }
    }

    if (isPublicFilter !== null) {
      conditions.push(eq(schema.euclidCreations.isPublic, isPublicFilter === 'true'))
    } else if (!mine) {
      // Default public gallery: only public
      conditions.push(eq(schema.euclidCreations.isPublic, true))
    }

    const creations = await db
      .select(cols)
      .from(schema.euclidCreations)
      .where(conditions.length > 0 ? and(...conditions) : undefined)
      .orderBy(desc(schema.euclidCreations.createdAt))
      .limit(limit)
      .all()

    return NextResponse.json({ creations })
  } catch (err) {
    console.error('[euclid/creations] GET failed:', err)
    return NextResponse.json({ error: 'Failed to fetch creations' }, { status: 500 })
  }
})