Commit 08d986d
authored
[Backend][fix] - FK constraint on docker image starts (#1093)
# Foreign Key Constraint
When deploying Stack Auth with Docker and changing
`STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS` between container
restarts, the seed script fails with:
```ts
PrismaClientKnownRequestError: Invalid prisma.teamMemberDirectPermission.upsert() invocation:
Foreign key constraint violated on the constraint: TeamMemberDirectPermission_tenancyId_projectUserId_teamId_fkey
```
This is a bug in the seed script's idempotency logic. The issue occurs
in `apps/backend/prisma/seed.ts` (lines 296–388):
- When admin credentials are provided, the script checks if the admin
user already exists (line 297–303)
- If the user exists, it skips the user creation block (line 305–306),
which also skips creating the TeamMember record
- However, the `grantTeamPermission()` call at line 382 is outside the
if/else block and always runs
- This tries to create a TeamMemberDirectPermission record, which has a
foreign key constraint to TeamMember
- If the TeamMember doesn't exist (e.g., user was created with
`STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS=false` previously, or
the TeamMember was never created), the foreign key constraint fails.
## How could this happen?
1. Changed `INTERNAL_ACCESS` setting: First run with
`STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS=false` (user created,
no TeamMember), then restarted with `=true`
2. Partial seed failure/interruption: A previous seed run created the
user but failed before creating the TeamMember
3. Manual database modification: TeamMember was deleted but user still
exists.
The most likely scenario would be 1 here:
### Scenario 1:
1. First deployment:
`STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS=false`
- User created ✓
- TeamMember **_NOT_** created(because `adminInternalAccess=false`)
2. Second deployment:
`STACK_SEED_INTERNAL_PROJECT_USER_INTERNAL_ACCESS=true`
- User already exists → Skip creation
- `grantTeamPermission()` called → tries to create
TeamMemberDirectPermission
- **_FAILS_** because TeamMember doesn't exist.
## Solution
Add a `TeamMember` upsert before granting permissions when
`adminInternalAccess` is true:
```ts
if (adminInternalAccess) {
await internalPrisma.teamMember.upsert({
where: {
tenancyId_projectUserId_teamId: {
tenancyId: internalTenancy.id,
projectUserId: defaultUserId,
teamId: internalTeamId,
},
},
create: {
tenancyId: internalTenancy.id,
teamId: internalTeamId,
projectUserId: defaultUserId,
},
update: {},
});
}
```
This ensures the `TeamMember` record exists before
`grantTeamPermission() is called, regardless of whether the user was
just created or already existed.
## Impact
- Existing deployments: No impact. If `TeamMember` already exists, the
upsert does nothing.
- New deployment: Works correctly.
- Broken deployments: This fix will repair them on the next container
restart.
## Testing
Tested by building a local Docker image and running the reproduction
script that:
- Starts with `INTERNAL_ACCESS=false`
- Restarts with `INTERNAL_ACCESS=true`
- verifies no foreign key constraint error occurs.
---
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Made permission grants resilient to repeated seed runs by ensuring
grants only apply when appropriate admin access is present.
* Prevented duplicate team member entries during setup by making member
creation idempotent, so repeated runs no longer create or alter existing
records.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->1 parent 3883b3b commit 08d986d
1 file changed
Lines changed: 29 additions & 15 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
315 | 315 | | |
316 | 316 | | |
317 | 317 | | |
318 | | - | |
319 | | - | |
320 | | - | |
321 | | - | |
322 | | - | |
323 | | - | |
324 | | - | |
325 | | - | |
326 | | - | |
| 318 | + | |
| 319 | + | |
327 | 320 | | |
328 | 321 | | |
329 | 322 | | |
| |||
379 | 372 | | |
380 | 373 | | |
381 | 374 | | |
382 | | - | |
383 | | - | |
384 | | - | |
385 | | - | |
386 | | - | |
387 | | - | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
388 | 402 | | |
389 | 403 | | |
390 | 404 | | |
| |||
0 commit comments