2026-02-11 10:48:24 +09:00

241 lines
5.8 KiB
TypeScript

/**
* Property model actions - Server actions for scene loading/saving
* Manages 3D models (scene graphs) stored in properties_models table
*/
'use server'
import type { AnyNode, AnyNodeId } from '@pascal-app/core'
import { createServerSupabaseClient } from '../database/server'
import { getSession } from '../auth/server'
import { createId } from '../utils/id-generator'
import type { ActionResult } from '../properties/actions'
export interface SceneGraph {
nodes: Record<AnyNodeId, AnyNode>
rootNodeIds: AnyNodeId[]
}
export interface PropertyModel {
id: string
name: string
version: number
draft: boolean
property_id: string
scene_graph: SceneGraph | null
created_at: string
updated_at: string
}
/**
* Get the latest model for a property (highest version)
*/
export async function getPropertyModel(propertyId: string): Promise<ActionResult<PropertyModel | null>> {
try {
const session = await getSession()
if (!session?.user) {
return {
success: false,
error: 'Not authenticated',
data: null,
}
}
const supabase = await createServerSupabaseClient()
// Get the property to verify ownership
const { data: property, error: propertyError } = await supabase
.from('properties')
.select('id, owner_id')
.eq('id', propertyId)
.single()
if (propertyError || !property) {
return {
success: false,
error: 'Property not found',
data: null,
}
}
// Verify ownership
if (property.owner_id !== session.user.id) {
return {
success: false,
error: 'Unauthorized',
data: null,
}
}
// Get the latest model (highest version, then most recent)
const { data: model, error: modelError } = await supabase
.from('properties_models')
.select('*')
.eq('property_id', propertyId)
.is('deleted_at', null)
.order('version', { ascending: false })
.order('created_at', { ascending: false })
.limit(1)
.single()
console.log('[getPropertyModel] Query result:', {
propertyId,
hasModel: !!model,
modelError: modelError?.message,
errorCode: modelError?.code,
modelKeys: model ? Object.keys(model) : [],
})
if (modelError) {
// No model found is not an error - just return null
if (modelError.code === 'PGRST116') {
console.log('[getPropertyModel] No model found (PGRST116), returning null')
return {
success: true,
data: null,
}
}
console.log('[getPropertyModel] Database error:', modelError)
return {
success: false,
error: modelError.message,
data: null,
}
}
console.log('[getPropertyModel] Model found:', {
id: model.id,
version: model.version,
hasSceneGraph: !!model.scene_graph,
})
return {
success: true,
data: model as PropertyModel,
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to fetch property model',
data: null,
}
}
}
/**
* Save or update a property model's scene graph
* If a model exists, updates it. Otherwise creates a new one.
*/
export async function savePropertyModel(
propertyId: string,
sceneGraph: SceneGraph,
): Promise<ActionResult<PropertyModel>> {
try {
const session = await getSession()
if (!session?.user) {
return {
success: false,
error: 'Not authenticated',
}
}
const supabase = await createServerSupabaseClient()
// Get the property to verify ownership
const { data: property, error: propertyError } = await supabase
.from('properties')
.select('id, owner_id, name')
.eq('id', propertyId)
.single()
if (propertyError || !property) {
return {
success: false,
error: 'Property not found',
}
}
// Verify ownership
if (property.owner_id !== session.user.id) {
return {
success: false,
error: 'Unauthorized',
}
}
// Check if a model already exists
const { data: existingModel } = await supabase
.from('properties_models')
.select('id, version')
.eq('property_id', propertyId)
.is('deleted_at', null)
.order('version', { ascending: false })
.order('created_at', { ascending: false })
.limit(1)
.single()
if (existingModel) {
// Update existing model
const { data: updatedModel, error: updateError } = await supabase
.from('properties_models')
.update({
scene_graph: sceneGraph as any,
updated_at: new Date().toISOString(),
})
.eq('id', existingModel.id)
.select()
.single()
if (updateError) {
return {
success: false,
error: updateError.message,
}
}
return {
success: true,
data: updatedModel as PropertyModel,
message: 'Model updated successfully',
}
} else {
// Create new model
const modelId = createId('model')
const { data: newModel, error: createError } = await supabase
.from('properties_models')
.insert({
id: modelId,
property_id: propertyId,
name: `${property.name} - Editor`,
version: 1,
draft: true,
scene_graph: sceneGraph as any,
})
.select()
.single()
if (createError) {
return {
success: false,
error: createError.message,
}
}
return {
success: true,
data: newModel as PropertyModel,
message: 'Model created successfully',
}
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to save property model',
}
}
}