mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Activating an automation with obviously-broken config (add_tag step
with empty tag_id, send_message with empty text, keyword_match
trigger with no keywords, etc.) used to succeed — and then every
dispatch produced a cryptic failed log row that users often missed.
- New validate.ts centralises the checks (mirrors engine.ts's
runtime guards one layer earlier so failures surface at save time).
Returns { path, message } issues so the UI can point users at the
exact problem.
- POST /api/automations rejects with 400 when is_active === true and
the merged config fails validation.
- PATCH /api/automations/[id] computes the post-patch "effective"
state (merging body fields over the existing row, loading current
steps if body.steps isn't provided) and applies the same gate.
Drafts (is_active=false) remain free to be incomplete so users can
still save progress mid-build.
- Builder surfaces the first issue in a toast with path as the
description, so users don't need DevTools to find what to fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { createClient } from '@/lib/supabase/server'
|
|
import { supabaseAdmin } from '@/lib/automations/admin-client'
|
|
import { getTemplate } from '@/lib/automations/templates'
|
|
import { insertSteps, type BuilderStepInput } from '@/lib/automations/steps-tree'
|
|
import {
|
|
validateStepsForActivation,
|
|
validateTriggerForActivation,
|
|
} from '@/lib/automations/validate'
|
|
|
|
export async function GET() {
|
|
const supabase = await createClient()
|
|
const {
|
|
data: { user },
|
|
} = await supabase.auth.getUser()
|
|
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
|
|
const { data, error } = await supabase
|
|
.from('automations')
|
|
.select('*')
|
|
.order('created_at', { ascending: false })
|
|
if (error) return NextResponse.json({ error: error.message }, { status: 500 })
|
|
return NextResponse.json({ automations: data ?? [] })
|
|
}
|
|
|
|
export async function POST(request: Request) {
|
|
const supabase = await createClient()
|
|
const {
|
|
data: { user },
|
|
} = await supabase.auth.getUser()
|
|
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
|
|
const body = await request.json().catch(() => null)
|
|
if (!body) return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 })
|
|
|
|
const { name, description, trigger_type, trigger_config, is_active, steps, template } = body
|
|
|
|
let effectiveSteps: BuilderStepInput[] | undefined = steps
|
|
let effectiveName = name
|
|
let effectiveDescription = description
|
|
let effectiveTriggerType = trigger_type
|
|
let effectiveTriggerConfig = trigger_config
|
|
|
|
if (template && (!steps || steps.length === 0)) {
|
|
const t = getTemplate(template)
|
|
if (t) {
|
|
effectiveName = effectiveName ?? t.name
|
|
effectiveDescription = effectiveDescription ?? t.description
|
|
effectiveTriggerType = effectiveTriggerType ?? t.trigger_type
|
|
effectiveTriggerConfig = effectiveTriggerConfig ?? t.trigger_config
|
|
effectiveSteps = t.steps as unknown as BuilderStepInput[]
|
|
}
|
|
}
|
|
|
|
if (!effectiveName || !effectiveTriggerType) {
|
|
return NextResponse.json(
|
|
{ error: 'name and trigger_type are required' },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
|
|
// Block activation of a clearly broken automation up-front instead of
|
|
// letting every trigger silently produce a failed log row. Drafts
|
|
// (is_active=false) are allowed to be incomplete so users can save
|
|
// progress mid-build.
|
|
if (is_active) {
|
|
const issues = [
|
|
...validateTriggerForActivation(effectiveTriggerType, effectiveTriggerConfig ?? {}),
|
|
...validateStepsForActivation(
|
|
(effectiveSteps ?? []) as unknown as { step_type: string; step_config: Record<string, unknown> }[],
|
|
),
|
|
]
|
|
if (issues.length > 0) {
|
|
return NextResponse.json(
|
|
{ error: 'Cannot activate automation with invalid configuration', issues },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
}
|
|
|
|
const admin = supabaseAdmin()
|
|
const { data: automation, error: insertErr } = await admin
|
|
.from('automations')
|
|
.insert({
|
|
user_id: user.id,
|
|
name: effectiveName,
|
|
description: effectiveDescription ?? null,
|
|
trigger_type: effectiveTriggerType,
|
|
trigger_config: effectiveTriggerConfig ?? {},
|
|
is_active: !!is_active,
|
|
})
|
|
.select()
|
|
.single()
|
|
|
|
if (insertErr || !automation) {
|
|
return NextResponse.json(
|
|
{ error: insertErr?.message ?? 'insert failed' },
|
|
{ status: 500 },
|
|
)
|
|
}
|
|
|
|
if (effectiveSteps && effectiveSteps.length > 0) {
|
|
const err = await insertSteps(automation.id, effectiveSteps)
|
|
if (err) return NextResponse.json({ error: err }, { status: 500 })
|
|
}
|
|
|
|
return NextResponse.json({ automation }, { status: 201 })
|
|
}
|