Skip to content

Conversation

@HarshMN2345
Copy link
Member

@HarshMN2345 HarshMN2345 commented Nov 3, 2025

What does this PR do?

(Provide a description of what this PR does.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Summary by CodeRabbit

  • New Features

    • Added "Screenshots generated" metric to usage and billing dashboards, including per-project breakdown and charts for tracking screenshot generation usage
  • Improvements

    • Build deployment status now accurately reflects timeout conditions, improving visibility into failed builds caused by timeout

@appwrite
Copy link

appwrite bot commented Nov 3, 2025

Console

Project ID: 688b7bf400350cbd60e9

Sites (1)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Ready Ready View Logs Preview URL QR Code

Tip

You can use Avatars API to generate QR code for any text or URLs.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 3, 2025

Walkthrough

This pull request introduces two major feature areas: (1) a new billing metric category for screenshots generated, extending usage tracking and billing plan structures across multiple data models, UI components, and pages; and (2) a build timeout detection system that calculates whether deployments have exceeded configured timeout thresholds, with effective status computation applied across deployment and site-related components to reflect timeout-induced failures.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–75 minutes

Key areas requiring extra attention:

  • src/lib/helpers/buildTimeout.ts: New timeout calculation logic with date/time arithmetic and status mapping; verify boundary conditions, timezone handling, and default behavior when timeout values are missing or invalid
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte: Significant refactoring including prop declaration changes (from export let to $props()), event source replacement (realtime.forProject to realtime.forConsole), and state management migration (derived state and booleans); ensure invalidation logic and realtime payload handling remain correct
  • src/routes/(console)/project-[region]-[project]/sites/(components)/logsTimer.svelte: Public prop signature altered from exported declarations to destructured $props(); verify this breaking change does not affect parent component call sites
  • Repetitive application of getEffectiveBuildStatus across deployment/site components: while following a consistent pattern, confirm that each integration correctly passes the three required parameters and uses the result appropriately in conditional rendering and action visibility logic
  • Billing model extensions (screenshotsGenerated fields in multiple interfaces): verify consistency across OrganizationUsage, Plan, and related structures, and check that all downstream consumers properly initialize and handle these fields

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: adding a new 'screenshotsGenerated' metric across the console. It accurately reflects the primary focus of the changeset, which introduces this metric to billing, usage, UI components, and related systems.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-screenshots-api-billing-updates-console

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@HarshMN2345 HarshMN2345 removed the request for review from ItzNotABug November 3, 2025 19:59
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/routes/(console)/project-[region]-[project]/sites/(components)/deploymentActionMenu.svelte (1)

134-145: Inconsistent status check for delete button.

Line 134 checks deployment.status directly instead of using effectiveStatus like all other status-based conditionals in this file (lines 80, 92, 111, 122). This inconsistency could lead to incorrect behavior when a deployment has timed out.

Apply this diff to use effectiveStatus consistently:

-            {#if ['ready', 'failed'].includes(deployment.status)}
+            {#if ['ready', 'failed'].includes(effectiveStatus)}
                 <ActionMenu.Item.Button
                     status="danger"
                     leadingIcon={IconTrash}
src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte (1)

160-170: Inconsistent status check for activate button.

Line 160 checks deployment.status === 'ready' instead of using effectiveStatus like the rest of the file (lines 109-110, 117, 119, 174, 187). This could prevent activation of deployments that have actually completed but whose status hasn't been updated.

Apply this diff to use effectiveStatus consistently:

-                                {#if deployment.status === 'ready' && deployment.$id !== $func.deploymentId}
+                                {#if effectiveStatus === 'ready' && deployment.$id !== $func.deploymentId}
                                     <ActionMenu.Item.Button
                                         leadingIcon={IconLightningBolt}
src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte (1)

127-138: Delete gating ignores timeout status

The delete action still checks data.deployment.status. When a deployment times out, effectiveStatus becomes 'failed', but the original status remains 'building', so the delete option never appears. Switch this condition to reference effectiveStatus to keep the UI consistent with the new timeout logic.

-                            {#if $canWriteFunctions && ['ready', 'failed'].includes(data.deployment.status)}
+                            {#if $canWriteFunctions && ['ready', 'failed'].includes(effectiveStatus)}
🧹 Nitpick comments (1)
src/routes/(console)/project-[region]-[project]/functions/function-[function]/(components)/deploymentCard.svelte (1)

134-134: Consider capitalizing the label for consistency.

The Status component now correctly uses effectiveStatus, but unlike siteCard.svelte (line 129), the label isn't capitalized. Consider using label={capitalize(effectiveStatus)} for consistency across components.

Apply this diff for consistency:

-                                <Status status={effectiveStatus} label={effectiveStatus} />
+                                <Status status={effectiveStatus} label={capitalize(effectiveStatus)} />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5fd02a and b9b49f3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • package.json (1 hunks)
  • src/lib/helpers/buildTimeout.ts (1 hunks)
  • src/lib/sdk/billing.ts (4 hunks)
  • src/lib/sdk/usage.ts (1 hunks)
  • src/lib/stores/billing.ts (1 hunks)
  • src/routes/(console)/organization-[organization]/billing/planSummary.svelte (2 hunks)
  • src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.svelte (1 hunks)
  • src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts (1 hunks)
  • src/routes/(console)/organization-[organization]/usage/[[invoice]]/ProjectBreakdown.svelte (2 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/(components)/deploymentCard.svelte (3 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte (5 hunks)
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte (5 hunks)
  • src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte (2 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/(components)/deploymentActionMenu.svelte (6 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/(components)/logs.svelte (3 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/(components)/logsTimer.svelte (1 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/(components)/siteCard.svelte (3 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/deployment-[deployment]/+page.svelte (4 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte (3 hunks)
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deploymentsOverview.svelte (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-26T06:48:57.938Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2373
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte:629-631
Timestamp: 2025-09-26T06:48:57.938Z
Learning: In the Appwrite console codebase using appwrite.io/pink-svelte, the Icon component automatically handles CSS variable names passed to its color prop by internally wrapping them with var(). Therefore, passing '--some-css-variable' as a string to the Icon color prop works correctly without needing to manually wrap it with var().

Applied to files:

  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte
  • package.json
📚 Learning: 2025-10-13T05:16:07.656Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/header.svelte:54-58
Timestamp: 2025-10-13T05:16:07.656Z
Learning: In SvelteKit apps, shared layout components (like headers) that use `$derived(page.data.*)` should use optional chaining when accessing properties that may not be present on all routes. During page transitions, reactive statements can briefly evaluate with different page.data structures, so optional chaining prevents runtime errors when navigating between routes with different data shapes (e.g., between `/databases` and `/databases/database-[database]`).

Applied to files:

  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte
📚 Learning: 2025-10-05T09:41:40.439Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2398
File: src/routes/(console)/verify-email/+page.svelte:48-51
Timestamp: 2025-10-05T09:41:40.439Z
Learning: In SvelteKit 5, `page` imported from `$app/state` is a reactive state object (using runes), not a store. It should be accessed as `page.data` without the `$` prefix, unlike the store-based `$page` from `$app/stores` in earlier versions.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte
🧬 Code graph analysis (2)
src/lib/helpers/buildTimeout.ts (1)
src/routes/(console)/store.ts (1)
  • consoleVariables (9-12)
src/lib/sdk/billing.ts (1)
src/lib/sdk/usage.ts (1)
  • Metric (21-30)
🪛 ESLint
src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte

[error] 33-33: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)


[error] 34-34: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e
🔇 Additional comments (17)
package.json (1)

25-25: LGTM!

The dependency update to @appwrite.io/console brings in support for the new screenshotsGenerated metric and build timeout features introduced in this PR.

src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts (1)

37-39: LGTM!

The addition of screenshotsGenerated and screenshotsGeneratedTotal fields follows the established pattern for the migration fallback case. The fields are correctly set to null and placed logically after the related imageTransformations fields.

src/routes/(console)/organization-[organization]/usage/[[invoice]]/ProjectBreakdown.svelte (2)

19-20: LGTM!

The addition of 'screenshotsGenerated' to the Metric type union is correct and follows the established pattern. The placement after 'imageTransformations' maintains logical grouping of related metrics.


92-95: LGTM!

The formatting logic for screenshotsGenerated correctly reuses the formatNumberWithCommas formatter, which is appropriate for count-based metrics and consistent with the handling of imageTransformations.

src/routes/(console)/project-[region]-[project]/sites/(components)/siteCard.svelte (4)

26-27: LGTM!

The imports for getEffectiveBuildStatus and regionalConsoleVariables are correctly placed and necessary for the build timeout detection feature.


45-47: LGTM!

The effectiveStatus derived value correctly computes the effective build status using the deployment's status, creation timestamp, and regional console variables. This enables timeout-based status detection.


121-121: LGTM!

The condition now correctly uses effectiveStatus instead of the raw deployment.status, which ensures that timeout-induced failures are properly displayed in the UI.


128-129: LGTM!

The Status component now correctly displays the effectiveStatus instead of the raw deployment status, ensuring that timeout-induced failures are properly reflected in the UI.

src/routes/(console)/project-[region]-[project]/functions/function-[function]/(components)/deploymentCard.svelte (3)

18-19: LGTM!

The imports for getEffectiveBuildStatus and regionalConsoleVariables are correctly placed and follow the same pattern as other deployment-related components.


41-43: LGTM!

The effectiveStatus derived value is correctly implemented using the same pattern as other deployment components, ensuring consistent timeout detection across the console.


130-130: LGTM!

The condition correctly uses effectiveStatus to detect both explicit failures and timeout-induced failures in function deployments.

src/lib/sdk/usage.ts (1)

336-344: LGTM!

The addition of screenshotsGenerated and screenshotsGeneratedTotal fields to the UsageProject type is well-documented and follows the established pattern used for imageTransformations. The JSDoc comments clearly describe the purpose of each field.

src/routes/(console)/organization-[organization]/billing/planSummary.svelte (2)

130-132: LGTM!

The extraction of screenshotsGenerated resource data follows the established pattern and is correctly placed in the project data mapping.


286-298: LGTM!

The Screenshots generated billing line item is correctly implemented following the same pattern as other metrics. The usage display, progress bar, and price formatting are all properly configured.

src/lib/stores/billing.ts (1)

153-154: LGTM!

The addition of 'screenshotsGenerated' to the PlanServices union type is correct and enables the billing logic to handle screenshot generation limits through the getServiceLimit function.

src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte (1)

246-283: LGTM! Consistent implementation of the new metric.

The screenshots generated card follows the same pattern as other metrics (e.g., image transformations at lines 208-245), with proper formatting, chart configuration, and empty state handling.

src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.svelte (1)

325-370: LGTM! Properly integrated screenshots metric.

The implementation mirrors the structure of other metrics (bandwidth, users, executions) with correct chart configuration, progress bar display, and project breakdown integration.

Comment on lines +43 to +49
let effectiveStatus = $derived(
getEffectiveBuildStatus(
data.deployment.status,
data.deployment.$createdAt,
$regionalConsoleVariables
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Timeout logic never re-runs

Same as in logsTimer.svelte: the derived effectiveStatus has no time-based dependency, so once the component mounts it never re-checks the timeout and deployments stuck in building/processing never graduate to 'failed'. Please introduce a ticking dependency (e.g. a $state updated on an interval or a SvelteDate) so the timeout calculation re-executes after the configured threshold.

Comment on lines +33 to +34
$: screenshotsGenerated = (data.usage as any).screenshotsGenerated ?? null;
$: screenshotsGeneratedTotal = (data.usage as any).screenshotsGeneratedTotal ?? 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Replace explicit any type cast with proper typing.

The explicit any type cast bypasses TypeScript's type checking. The data.usage type definition should be updated to include the new screenshotsGenerated and screenshotsGeneratedTotal fields rather than using a type assertion.

Consider updating the type definition for data.usage to include:

{
  // ... existing fields
  screenshotsGenerated?: Array<Models.Metric>;
  screenshotsGeneratedTotal?: number;
}

Then remove the any cast:

-    $: screenshotsGenerated = (data.usage as any).screenshotsGenerated ?? null;
-    $: screenshotsGeneratedTotal = (data.usage as any).screenshotsGeneratedTotal ?? 0;
+    $: screenshotsGenerated = data.usage.screenshotsGenerated ?? null;
+    $: screenshotsGeneratedTotal = data.usage.screenshotsGeneratedTotal ?? 0;
🧰 Tools
🪛 ESLint

[error] 33-33: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)


[error] 34-34: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte
around lines 33-34, remove the explicit any cast on data.usage and instead
update the TypeScript type for data.usage to include screenshotsGenerated?:
Models.Metric[] and screenshotsGeneratedTotal?: number; then update any shared
usage/interface type (or the load function return type) so Svelte knows these
fields exist and you can directly reference data.usage.screenshotsGenerated and
data.usage.screenshotsGeneratedTotal without using "as any".

Comment on lines +12 to +14
let effectiveStatus = $derived(
getEffectiveBuildStatus(status, deployment.$createdAt, $regionalConsoleVariables)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Timeout logic never re-runs

$derived only re-evaluates when its dependencies change. Here, those dependencies (status, deployment.$createdAt, $regionalConsoleVariables) stay constant while a build is stuck. As a result, the timeout check (which reads Date.now()) fires once and effectiveStatus never flips to 'failed', so timed-out builds remain shown as “building” forever. Please add a time-based dependency (for example, drive a $state updated via setInterval, or use a reactive SvelteDate) so the derived value recomputes as time passes.

@HarshMN2345 HarshMN2345 closed this Nov 3, 2025
@HarshMN2345 HarshMN2345 deleted the feat-screenshots-api-billing-updates-console branch November 3, 2025 20:15
@HarshMN2345
Copy link
Member Author

closed in favour of #2543

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants