diff --git a/apps/webapp/app/components/run/RunTimeline.tsx b/apps/webapp/app/components/run/RunTimeline.tsx index 3933e11b56..37186e0f31 100644 --- a/apps/webapp/app/components/run/RunTimeline.tsx +++ b/apps/webapp/app/components/run/RunTimeline.tsx @@ -248,7 +248,7 @@ function buildTimelineItems(run: TimelineSpanRun): TimelineItem[] { items.push({ type: "line", id: "executing", - title: formatDuration(run.executedAt, run.updatedAt), + title: formatDuration(run.executedAt, run.completedAt ?? run.updatedAt), state, shouldRender: true, variant: "normal", @@ -271,7 +271,7 @@ function buildTimelineItems(run: TimelineSpanRun): TimelineItem[] { items.push({ type: "line", id: "legacy-executing", - title: formatDuration(run.startedAt, run.updatedAt), + title: formatDuration(run.startedAt, run.completedAt ?? run.updatedAt), state, shouldRender: true, variant: "normal", @@ -296,7 +296,7 @@ function buildTimelineItems(run: TimelineSpanRun): TimelineItem[] { type: "event", id: "finished", title: "Finished", - date: run.updatedAt, + date: run.completedAt ?? run.updatedAt, previousDate: run.executedAt ?? run.startedAt ?? undefined, state, shouldRender: true, diff --git a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx index de7a421928..c90eecb423 100644 --- a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx +++ b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx @@ -73,7 +73,6 @@ export function TaskRunsTable({ allowSelection = false, variant = "dimmed", }: RunsTableProps) { - const user = useUser(); const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); @@ -81,7 +80,7 @@ export function TaskRunsTable({ const { selectedItems, has, hasAll, select, deselect, toggle } = useSelectedItems(allowSelection); const { isManagedCloud } = useFeatures(); - const showCompute = user.admin && isManagedCloud; + const showCompute = isManagedCloud; const navigateCheckboxes = useCallback( (event: React.KeyboardEvent, index: number) => { @@ -371,7 +370,9 @@ export function TaskRunsTable({ {showCompute && ( - {run.costInCents > 0 ? formatCurrencyAccurate(run.costInCents / 100) : "–"} + {run.costInCents > 0 + ? formatCurrencyAccurate((run.costInCents + run.baseCostInCents) / 100) + : "–"} )} diff --git a/apps/webapp/app/presenters/v3/RunListPresenter.server.ts b/apps/webapp/app/presenters/v3/RunListPresenter.server.ts index a1f580fe8f..73366f95fc 100644 --- a/apps/webapp/app/presenters/v3/RunListPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/RunListPresenter.server.ts @@ -209,6 +209,7 @@ export class RunListPresenter extends BasePresenter { lockedAt: Date | null; delayUntil: Date | null; updatedAt: Date; + completedAt: Date | null; isTest: boolean; spanId: string; idempotencyKey: string | null; @@ -238,6 +239,7 @@ export class RunListPresenter extends BasePresenter { tr."delayUntil" AS "delayUntil", tr."lockedAt" AS "lockedAt", tr."updatedAt" AS "updatedAt", + tr."completedAt" AS "completedAt", tr."isTest" AS "isTest", tr."spanId" AS "spanId", tr."idempotencyKey" AS "idempotencyKey", @@ -383,7 +385,9 @@ WHERE startedAt: startedAt ? startedAt.toISOString() : undefined, delayUntil: run.delayUntil ? run.delayUntil.toISOString() : undefined, hasFinished, - finishedAt: hasFinished ? run.updatedAt.toISOString() : undefined, + finishedAt: hasFinished + ? run.completedAt?.toISOString() ?? run.updatedAt.toISOString() + : undefined, isTest: run.isTest, status: run.status, version: run.version, diff --git a/apps/webapp/app/routes/engine.v1.dev.presence.ts b/apps/webapp/app/routes/engine.v1.dev.presence.ts index bb38a88f25..58a5e8c7a4 100644 --- a/apps/webapp/app/routes/engine.v1.dev.presence.ts +++ b/apps/webapp/app/routes/engine.v1.dev.presence.ts @@ -8,7 +8,7 @@ import { createSSELoader } from "~/utils/sse"; export const loader = createSSELoader({ timeout: env.DEV_PRESENCE_SSE_TIMEOUT, interval: env.DEV_PRESENCE_TTL_MS * 0.8, - debug: true, + debug: false, handler: async ({ id, controller, debug, request }) => { const authentication = await authenticateApiRequestWithFailure(request); diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.dev.presence.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.dev.presence.tsx index 12ee82b8e9..8ce2f5b916 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.dev.presence.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.dev.presence.tsx @@ -9,7 +9,7 @@ import { createSSELoader, type SendFunction } from "~/utils/sse"; export const loader = createSSELoader({ timeout: env.DEV_PRESENCE_SSE_TIMEOUT, interval: env.DEV_PRESENCE_POLL_MS, - debug: true, + debug: false, handler: async ({ id, controller, debug, request, params }) => { const userId = await requireUserId(request); const { organizationSlug, projectParam } = ProjectParamSchema.parse(params); diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues.stream.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues.stream.tsx index 007e2c4f7e..07e7b15c72 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues.stream.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues.stream.tsx @@ -8,7 +8,7 @@ import { createSSELoader } from "~/utils/sse"; export const loader = createSSELoader({ timeout: env.QUEUE_SSE_AUTORELOAD_TIMEOUT_MS, interval: env.QUEUE_SSE_AUTORELOAD_INTERVAL_MS, - debug: true, + debug: false, handler: async ({ request, params }) => { const userId = await requireUserId(request); const { projectParam, envParam } = EnvironmentParamSchema.parse(params); diff --git a/apps/webapp/app/services/engineRateLimit.server.ts b/apps/webapp/app/services/engineRateLimit.server.ts index f34ed0ef44..9eb20342f3 100644 --- a/apps/webapp/app/services/engineRateLimit.server.ts +++ b/apps/webapp/app/services/engineRateLimit.server.ts @@ -24,8 +24,8 @@ export const engineRateLimiter = authorizationRateLimitMiddleware({ stale: 60_000 * 20, // Date is stale after 20 minutes }, pathMatchers: [/^\/engine/], - // Allow /api/v1/tasks/:id/callback/:secret - pathWhiteList: [], + // Regex allow any path starting with /engine/v1/worker-actions/ + pathWhiteList: [/^\/engine\/v1\/worker-actions\/.*/], log: { rejections: env.RUN_ENGINE_RATE_LIMIT_REJECTION_LOGS_ENABLED === "1", requests: env.RUN_ENGINE_RATE_LIMIT_REQUEST_LOGS_ENABLED === "1",