Skip to content

Commit 07be5c6

Browse files
committed
Merge branch 'codex/file-ownership-foundation' into HEAD
2 parents ce0ca7e + 308f670 commit 07be5c6

3 files changed

Lines changed: 6 additions & 93 deletions

File tree

‎apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
} from '@/lib/api/contracts/v1/admin'
3434
import { parseRequest } from '@/lib/api/server'
3535
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
36-
import { ProjectConflictError } from '@/lib/projects/membership'
36+
import { ProjectConflictError } from '@/lib/projects/errors'
3737
import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access'
3838
import { getWorkspaceById } from '@/lib/workspaces/permissions/utils'
3939
import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils'
@@ -286,7 +286,7 @@ export const DELETE = withRouteHandler(
286286
}
287287
return true
288288
})
289-
if (!removed) return notFoundResponse('Workspace member')
289+
if (!removed) return adminNotFoundResponse('Workspace member')
290290

291291
logger.info(`Admin API: Removed member ${memberId} from workspace ${workspaceId}`, {
292292
userId: existingMember.userId,
@@ -310,7 +310,7 @@ export const DELETE = withRouteHandler(
310310
workspaceId,
311311
})
312312
} catch (error) {
313-
if (error instanceof ProjectConflictError) return conflictResponse(error.message)
313+
if (error instanceof ProjectConflictError) return adminConflictResponse(error.message)
314314
if (error instanceof WorkspaceBillingAccountRemovalError) {
315315
return adminBadRequestResponse(error.message)
316316
}

‎apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import {
5050
import { parseRequest } from '@/lib/api/server'
5151
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
5252
import { syncWorkspaceEnvCredentials } from '@/lib/credentials/environment'
53-
import { ProjectConflictError } from '@/lib/projects/membership'
53+
import { ProjectConflictError } from '@/lib/projects/errors'
5454
import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access'
5555
import { getWorkspaceById } from '@/lib/workspaces/permissions/utils'
5656
import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils'
@@ -402,7 +402,7 @@ export const DELETE = withRouteHandler(
402402
}
403403
return true
404404
})
405-
if (!removed) return notFoundResponse('Workspace member')
405+
if (!removed) return adminNotFoundResponse('Workspace member')
406406

407407
logger.info(`Admin API: Removed user ${userId} from workspace ${workspaceId}`)
408408

@@ -419,7 +419,7 @@ export const DELETE = withRouteHandler(
419419

420420
return singleResponse({ removed: true, userId, workspaceId })
421421
} catch (error) {
422-
if (error instanceof ProjectConflictError) return conflictResponse(error.message)
422+
if (error instanceof ProjectConflictError) return adminConflictResponse(error.message)
423423
if (error instanceof WorkspaceBillingAccountRemovalError) {
424424
return adminBadRequestResponse(error.message)
425425
}

‎packages/db/file-folder-version-ownership.integration.ts‎

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -505,90 +505,3 @@ describe('file folder and version ownership in PostgreSQL', () => {
505505
])
506506
})
507507
})
508-
509-
describe('file ownership composed with enforced Project membership', () => {
510-
it('preserves Project lifecycle checks and public metadata through folder, file, and version writes', async () => {
511-
const admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined })
512-
const name = `file_owner_chain_test_${generateId().replaceAll('-', '')}`
513-
const url = new URL(databaseUrl)
514-
url.pathname = `/${name}`
515-
await admin.unsafe(`CREATE DATABASE "${name}"`)
516-
const chain = postgres(url.toString(), { max: 1, onnotice: () => undefined })
517-
try {
518-
await chain.unsafe(`
519-
CREATE TABLE "user" (id text PRIMARY KEY);
520-
CREATE TABLE organization (id text PRIMARY KEY);
521-
CREATE TABLE workspace (
522-
id text PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL,
523-
organization_id text, archived_at timestamp,
524-
forked_from_workspace_id text REFERENCES workspace(id) ON DELETE SET NULL
525-
);
526-
CREATE TABLE workflow (
527-
id text PRIMARY KEY, workspace_id text REFERENCES workspace(id) ON DELETE CASCADE,
528-
archived_at timestamp
529-
);
530-
CREATE TABLE folder (
531-
id text PRIMARY KEY, name text NOT NULL,
532-
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
533-
workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
534-
resource_type text NOT NULL, parent_id text REFERENCES folder(id) ON DELETE SET NULL,
535-
deleted_at timestamp
536-
);
537-
CREATE TABLE workspace_files (
538-
id text PRIMARY KEY, context text NOT NULL,
539-
user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
540-
workspace_id text REFERENCES workspace(id) ON DELETE CASCADE,
541-
organization_id text REFERENCES organization(id) ON DELETE CASCADE,
542-
folder_id text REFERENCES folder(id) ON DELETE SET NULL,
543-
chat_id text, original_name text NOT NULL DEFAULT 'document.md', deleted_at timestamp
544-
);
545-
CREATE TABLE workspace_file_version (
546-
id text PRIMARY KEY, file_id text NOT NULL REFERENCES workspace_files(id) ON DELETE CASCADE,
547-
workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE
548-
);
549-
INSERT INTO "user" VALUES ('owner'), ('creator');
550-
INSERT INTO workspace (id, name, owner_id) VALUES ('environment', 'Production', 'owner');
551-
`)
552-
for (const source of [
553-
readFileSync(new URL('./migrations/0394_project_foundation.sql', import.meta.url), 'utf8'),
554-
readFileSync(
555-
new URL('./migrations/0403_project_membership_enforcement.sql', import.meta.url),
556-
'utf8'
557-
),
558-
...migrations,
559-
]) {
560-
for (const statement of source.split('--> statement-breakpoint')) {
561-
if (statement.trim()) await chain.unsafe(statement)
562-
}
563-
}
564-
const [project] = await chain<{ id: string }[]>`SELECT id FROM project`
565-
const [before] = await chain`SELECT * FROM project WHERE id = ${project.id}`
566-
await chain`INSERT INTO folder (id, name, user_id, resource_type, project_id)
567-
VALUES ('folder', 'Architecture', 'creator', 'file', ${project.id})`
568-
await chain`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id)
569-
VALUES ('file', 'project', 'creator', ${project.id}, 'folder')`
570-
await chain`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')`
571-
const [after] = await chain`SELECT * FROM project WHERE id = ${project.id}`
572-
expect(after).toEqual(before)
573-
await expect(chain`DELETE FROM "user" WHERE id = 'creator'`).rejects.toMatchObject({
574-
code: '23503',
575-
})
576-
await expect(chain`DELETE FROM workspace WHERE id = 'environment'`).rejects.toMatchObject({
577-
code: '23514',
578-
})
579-
await chain.begin(async (tx) => {
580-
await tx`UPDATE workspace SET archived_at = now() WHERE id = 'environment'`
581-
await tx`UPDATE project SET archived_at = now() WHERE id = ${project.id}`
582-
})
583-
expect(await chain`SELECT id, workspace_id FROM workspace_file_version`).toEqual([
584-
{ id: 'version', workspace_id: null },
585-
])
586-
expect(await chain`SELECT id FROM folder`).toEqual([{ id: 'folder' }])
587-
expect(await chain`SELECT id FROM workspace_files`).toEqual([{ id: 'file' }])
588-
} finally {
589-
await chain.end({ timeout: 2 })
590-
await admin.unsafe(`DROP DATABASE "${name}" WITH (FORCE)`)
591-
await admin.end()
592-
}
593-
})
594-
})

0 commit comments

Comments
 (0)