Skip to content

Commit 32734a0

Browse files
committed
fix(projects): keep the disabled-flag 503 that realtime treats as transient
1 parent 6cb383b commit 32734a0

6 files changed

Lines changed: 20 additions & 36 deletions

File tree

‎apps/sim/app/api/projects/route.test.ts‎

Lines changed: 0 additions & 28 deletions
This file was deleted.

‎apps/sim/ee/access-control/components/project-issue-restrictions.tsx‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

33
import { Checkbox, Chip, Info, OverflowText } from '@sim/emcn'
4+
import { isApiClientError } from '@/lib/api/client/errors'
45
import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section'
56
import { useProjects } from '@/hooks/queries/projects'
67

@@ -20,7 +21,13 @@ export function ProjectIssueRestrictions({
2021
onChange,
2122
}: ProjectIssueRestrictionsProps) {
2223
const projects = useProjects(organizationId)
23-
if (!projects.isEnabled || projects.isPending) return null
24+
if (
25+
!projects.isEnabled ||
26+
projects.isPending ||
27+
(isApiClientError(projects.error) && projects.error.status === 503)
28+
) {
29+
return null
30+
}
2431
const choices = projects.data?.pages.flatMap((page) => page.projects) ?? []
2532
if (!projects.error && choices.length === 0) return null
2633
const selected = new Set(value)

‎apps/sim/hooks/queries/projects.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function useProjects(organizationId: string) {
2929
(!isApiClientError(error) ||
3030
error.status === 408 ||
3131
error.status === 429 ||
32-
error.status >= 500),
32+
(error.status >= 500 && error.status !== 503)),
3333
enabled: projectsEnabled && Boolean(organizationId),
3434
})
3535
}

‎apps/sim/lib/projects/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
A Project groups environments. An environment is an existing `workspace` record; there is no separate environment table. Every newly created Project starts with an environment.
44

5-
Project APIs return HTTP 403 until the `projects` feature flag is on (AppConfig on hosted deployments; the `PROJECTS` secret elsewhere). The flag defaults off and gates Project and Project-file APIs through `requireProjectApiEnabled()`: workspace creation always assigns a Project atomically, and assigned Projects keep their lifecycle protections (fork inheritance, disconnect) either way. Disabling the flag does not disable retention or cleanup of existing Project data.
5+
Project APIs return HTTP 503 until the `projects` feature flag is on (AppConfig on hosted deployments; the `PROJECTS` secret elsewhere). The flag defaults off and gates Project and Project-file APIs through `requireProjectApiEnabled()`: workspace creation always assigns a Project atomically, and assigned Projects keep their lifecycle protections (fork inheritance, disconnect) either way. Disabling the flag does not disable retention or cleanup of existing Project data.
66

77
## Storage and consistency
88

‎apps/sim/lib/projects/__integration__/foundation.integration.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ describe('Project foundation at the database and application boundary', () => {
459459
() => archiveProject.execute({ principal: f.owner, input, request }),
460460
() => getProjectIssueAccess.execute({ principal: f.owner, input, request }),
461461
]
462-
for (const call of calls) await expect(call()).rejects.toMatchObject({ code: 'forbidden' })
462+
for (const call of calls) await expect(call()).rejects.toMatchObject({ statusCode: 503 })
463463
expect(
464464
await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId))
465465
).toHaveLength(1)
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { isFeatureEnabled } from '@/lib/core/config/feature-flags'
2-
import { OrchestrationError } from '@/lib/core/orchestration/types'
2+
import { HttpError } from '@/lib/core/utils/http-error'
3+
4+
class ProjectUnavailableError extends HttpError {
5+
readonly statusCode = 503
6+
constructor() {
7+
super('Projects are not enabled on this deployment')
8+
}
9+
}
310

411
/** Project rollout is a global switch; it never varies by organization or user. */
512
export function isProjectsEnabled(): Promise<boolean> {
613
return isFeatureEnabled('projects')
714
}
815

916
export async function requireProjectApiEnabled(): Promise<void> {
10-
if (!(await isProjectsEnabled())) {
11-
throw new OrchestrationError('forbidden', 'Projects are not enabled')
12-
}
17+
if (!(await isProjectsEnabled())) throw new ProjectUnavailableError()
1318
}

0 commit comments

Comments
 (0)