mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
fix(account-sharing): stamp account_id on flow and pipeline inserts (#210)
`flows.account_id` and `pipelines.account_id` are NOT NULL post-017 with no DB default, so inserts omitting account_id fail with a not-null violation (same class as #206 / #205). - POST /api/flows: resolve the caller's profiles.account_id and stamp it on both insert paths (template clone + blank/manual). Closes #210. - Pipelines page: stamp account_id (from useAuth) on the auto-seed and the manual "create pipeline" inserts — same bug, found while auditing other account-scoped insert flows. Audited every insert into the 15 NOT-NULL-account_id tables; all other sites already stamp account_id. typecheck + 370 tests pass; lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
75ff74730f
commit
48fc10d084
@ -27,6 +27,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { GitBranch, Plus, ChevronDown, Settings } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { useCan } from "@/hooks/use-can";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { GatedButton } from "@/components/ui/gated-button";
|
||||
|
||||
// Pipeline creation is admin-class (settings-tier write under
|
||||
@ -47,6 +48,7 @@ export default function PipelinesPage() {
|
||||
const supabase = createClient();
|
||||
const canEditSettings = useCan("edit-settings");
|
||||
const canCreateDeals = useCan("send-messages");
|
||||
const { accountId } = useAuth();
|
||||
|
||||
const [pipelines, setPipelines] = useState<Pipeline[]>([]);
|
||||
const [selectedPipelineId, setSelectedPipelineId] = useState<string>("");
|
||||
@ -111,10 +113,12 @@ export default function PipelinesPage() {
|
||||
} = await supabase.auth.getSession();
|
||||
const user = session?.user;
|
||||
if (!user) return null;
|
||||
// pipelines.account_id is NOT NULL post-017 with no DB default.
|
||||
if (!accountId) return null;
|
||||
|
||||
const { data: pipeline, error } = await supabase
|
||||
.from("pipelines")
|
||||
.insert({ user_id: user.id, name: "Sales Pipeline" })
|
||||
.insert({ user_id: user.id, account_id: accountId, name: "Sales Pipeline" })
|
||||
.select()
|
||||
.single();
|
||||
|
||||
@ -132,7 +136,7 @@ export default function PipelinesPage() {
|
||||
await supabase.from("pipeline_stages").insert(stagesPayload);
|
||||
|
||||
return pipeline as Pipeline;
|
||||
}, [supabase]);
|
||||
}, [supabase, accountId]);
|
||||
|
||||
// Initial load + seed-if-empty
|
||||
useEffect(() => {
|
||||
@ -254,10 +258,16 @@ export default function PipelinesPage() {
|
||||
setCreating(false);
|
||||
return;
|
||||
}
|
||||
// pipelines.account_id is NOT NULL post-017 with no DB default.
|
||||
if (!accountId) {
|
||||
toast.error("Your profile is not linked to an account.");
|
||||
setCreating(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: pipeline, error } = await supabase
|
||||
.from("pipelines")
|
||||
.insert({ user_id: user.id, name })
|
||||
.insert({ user_id: user.id, account_id: accountId, name })
|
||||
.select()
|
||||
.single();
|
||||
|
||||
|
||||
@ -49,7 +49,23 @@ export async function POST(request: Request) {
|
||||
if (!guard.ok) {
|
||||
return NextResponse.json(guard.body, { status: guard.status })
|
||||
}
|
||||
const { userId } = guard
|
||||
const { userId, supabase } = guard
|
||||
|
||||
// Resolve the caller's account_id — `flows.account_id` is NOT NULL
|
||||
// post-017, so an INSERT without it trips the not-null constraint
|
||||
// even though the admin client below bypasses RLS.
|
||||
const { data: profile } = await supabase
|
||||
.from('profiles')
|
||||
.select('account_id')
|
||||
.eq('user_id', userId)
|
||||
.single()
|
||||
const accountId = profile?.account_id as string | undefined
|
||||
if (!accountId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Your profile is not linked to an account.' },
|
||||
{ status: 403 },
|
||||
)
|
||||
}
|
||||
|
||||
const body = (await request.json().catch(() => null)) as
|
||||
| {
|
||||
@ -85,6 +101,7 @@ export async function POST(request: Request) {
|
||||
.from('flows')
|
||||
.insert({
|
||||
user_id: userId,
|
||||
account_id: accountId,
|
||||
name: body.name?.trim() || template.name,
|
||||
description: template.description,
|
||||
status: 'draft',
|
||||
@ -133,6 +150,7 @@ export async function POST(request: Request) {
|
||||
.from('flows')
|
||||
.insert({
|
||||
user_id: userId,
|
||||
account_id: accountId,
|
||||
name: body.name.trim(),
|
||||
description: body.description ?? null,
|
||||
status: 'draft',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user