Skip to content

POC - website screenshots#10675

Merged
eldadfux merged 21 commits into1.8.xfrom
feat-screenshots-endpoint
Nov 1, 2025
Merged

POC - website screenshots#10675
eldadfux merged 21 commits into1.8.xfrom
feat-screenshots-endpoint

Conversation

@eldadfux
Copy link
Copy Markdown
Member

@eldadfux eldadfux commented Oct 21, 2025

What does this PR do?

This pull request adds a new API endpoint to the avatars service that allows users to generate and retrieve webpage screenshots with customizable options. It also introduces new validators to support the new endpoint's parameters.

New Feature: Webpage Screenshot API

  • Added a new GET /v1/avatars/screenshot endpoint in app/controllers/api/avatars.php to capture screenshots of webpages, with support for parameters such as url, headers, viewport, scale, fullPage, sleep, width, height, quality, and output format. The endpoint validates inputs, interacts with a browser service, processes the image, and returns the screenshot in the requested format.

Validator Enhancements

  • Imported new validators (AnyOf and Assoc) to validate the new endpoint's parameters, ensuring flexible and robust input handling for HTTP headers and other options.<!--
    Thank you for sending the PR! We appreciate you spending the time to work on these changes.

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. Screenshots may also be helpful.)

Related PRs and Issues

  • (Related PR or issue)

Checklist

  • Have you read the Contributing Guidelines on issues?
  • If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 21, 2025

📝 Walkthrough

Walkthrough

Adds an avatars screenshot feature: new REST endpoint GET /v1/avatars/screenshots with extensive parameter validation, payload construction and remote browser service invocation, Imagick-based image processing and response handling; corresponding GraphQL query support; new config variable _APP_BROWSER_HOST; new metric constant METRIC_AVATARS_SCREENSHOTS_GENERATED; appwrite-browser image bumped to appwrite/browser:0.3.1; Functions Builds now reads browser endpoint from an environment variable; GraphQL Mapper treats Range FLOAT as Float; and comprehensive e2e tests for REST and GraphQL screenshot flows.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing focused review:
    • app/controllers/api/avatars.php — validation, browser service payload, remote call/error handling, Imagick usage, cropping/resizing/format/quality logic, response headers and caching.
    • tests/e2e/** — large, heterogeneous test additions for REST and GraphQL covering many parameter permutations and failure cases.
    • app/config/variables.php and src/Appwrite/Platform/Modules/Functions/Workers/Builds.php — new config var and env-backed browser endpoint retrieval.
    • app/init/constants.php and app/controllers/shared/api.php — new metric constant and metric-disable-on-cache-hit logic.
    • docker-compose.yml and app/views/install/compose.phtml — container image update.
    • src/Appwrite/GraphQL/Types/Mapper.php — Range FLOAT mapping change.

Possibly related PRs

  • Feat: Add screenshots to site #9532 — modifies Functions Builds/browser endpoint handling and deployment screenshot attributes; related at the Builds worker/browser endpoint integration layer.

Suggested reviewers

  • vermakhushboo

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "POC - website screenshots" is directly related to the main change in the changeset, which adds a new GET /v1/avatars/screenshots endpoint for capturing webpage screenshots. The title clearly refers to the core feature being introduced (website screenshots), though the "POC" prefix adds a note about the implementation status. The title is concise and readable, allowing a teammate reviewing history to quickly understand that this introduces screenshot functionality to the avatars service.
Description Check ✅ Passed The PR description is clearly related to the changeset and accurately describes the main changes being introduced. It explains that a new API endpoint is being added to the avatars service for generating webpage screenshots with customizable options, mentions the introduction of new validators (Assoc and ArrayList), and refers to supporting multiple parameters including url, headers, viewport, scale, and output format. The description meaningfully conveys what is being added, matching the actual changes reflected in the raw summary.
✨ 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-endpoint

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a39970c and d56bd44.

📒 Files selected for processing (1)
  • app/controllers/shared/api.php (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/controllers/shared/api.php
⏰ 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). (20)
  • GitHub Check: E2E Service Test (Messaging)
  • GitHub Check: E2E Service Test (Locale)
  • GitHub Check: E2E Service Test (Sites)
  • GitHub Check: E2E Service Test (Proxy)
  • GitHub Check: E2E Service Test (Projects)
  • GitHub Check: E2E Service Test (Health)
  • GitHub Check: E2E Service Test (Realtime)
  • GitHub Check: E2E Service Test (Functions)
  • GitHub Check: E2E Service Test (FunctionsSchedule)
  • GitHub Check: E2E Service Test (Databases/TablesDB)
  • GitHub Check: E2E Service Test (Account)
  • GitHub Check: E2E Service Test (Console)
  • GitHub Check: E2E Service Test (Databases/Legacy)
  • GitHub Check: E2E Service Test (Avatars)
  • GitHub Check: E2E Service Test (Site Screenshots)
  • GitHub Check: E2E Service Test (Dev Keys)
  • GitHub Check: Unit Test
  • GitHub Check: E2E General Test
  • GitHub Check: Benchmark
  • GitHub Check: scan

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.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 21, 2025

Security Scan Results for PR

Docker Image Scan Results

Package Version Vulnerability Severity
binutils 2.44-r2 CVE-2025-5244 HIGH
binutils 2.44-r2 CVE-2025-5245 HIGH
libxml2 2.13.8-r0 CVE-2025-49794 CRITICAL
libxml2 2.13.8-r0 CVE-2025-49796 CRITICAL
libxml2 2.13.8-r0 CVE-2025-49795 HIGH
libxml2 2.13.8-r0 CVE-2025-6021 HIGH
pcre2 10.43-r1 CVE-2025-58050 CRITICAL
golang.org/x/crypto v0.31.0 CVE-2025-22869 HIGH
golang.org/x/oauth2 v0.24.0 CVE-2025-22868 HIGH
stdlib 1.22.10 CVE-2025-47907 HIGH
stdlib 1.22.10 CVE-2025-47912 HIGH
stdlib 1.22.10 CVE-2025-58183 HIGH
stdlib 1.22.10 CVE-2025-58185 HIGH
stdlib 1.22.10 CVE-2025-58186 HIGH
stdlib 1.22.10 CVE-2025-58187 HIGH
stdlib 1.22.10 CVE-2025-58188 HIGH
stdlib 1.22.10 CVE-2025-58189 HIGH
stdlib 1.22.10 CVE-2025-61723 HIGH
stdlib 1.22.10 CVE-2025-61724 HIGH
stdlib 1.22.10 CVE-2025-61725 HIGH

Source Code Scan Results

🎉 No vulnerabilities found!

@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 21, 2025

✨ Benchmark results

  • Requests per second: 1,213
  • Requests with 200 status code: 218,358
  • P99 latency: 0.159784675

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 1,213 1,183
200 218,358 213,001
P99 0.159784675 0.170397763

…d GraphQL support and extensive tests for various scenarios
…v1/avatars/screenshots' for consistency; update related tests accordingly.
… scaling; update related tests to validate new functionality and edge cases.
eldadfux and others added 4 commits October 24, 2025 21:42
…shot API by adding 'waitUntil' parameter and refactoring image processing logic for improved efficiency.
…eparate 'viewportWidth' and 'viewportHeight' parameters; update related tests for validation of new dimensions and edge cases.
…e communication; update avatar screenshot API to use System::getEnv for environment variable retrieval
@lohanidamodar lohanidamodar self-assigned this Oct 29, 2025
@lohanidamodar lohanidamodar marked this pull request as ready for review October 29, 2025 03:50
Copy link
Copy Markdown
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: 5

♻️ Duplicate comments (1)
tests/e2e/Services/Avatars/AvatarsBase.php (1)

575-579: Nice: body size assertion added

This addresses the earlier feedback to assert a minimum payload size.

🧹 Nitpick comments (8)
app/config/variables.php (1)

955-964: Add basic URL validation filter for _APP_BROWSER_HOST

Recommend setting filter to 'url' to catch obvious misconfigurations early. Keeps behavior but adds guardrails.

Apply:

-                'filter' => ''
+                'filter' => 'url'
tests/e2e/Services/Avatars/AvatarsBase.php (3)

600-699: Avoid testing with sensitive headers (Authorization) or assert they’re blocked

Passing Authorization to arbitrary sites can normalize risky patterns. Prefer benign headers (e.g., X-Test-Header) or add a negative test asserting security-sensitive headers (Host, Cookie, Authorization, X-Forwarded-*) are rejected/stripped by the service.

If the service blocks these, add:

// Expect 400 when sending disallowed headers
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
  'x-appwrite-project' => $this->getProject()['$id'],
], [
  'url' => 'https://appwrite.io',
  'headers' => ['Host' => 'evil.test', 'Cookie' => 'a=b', 'Authorization' => 'Bearer X'],
]);
$this->assertEquals(400, $response['headers']['status-code']);

728-741: Duplicate success case for max viewport

This repeats the earlier 1920x1080 success path (Lines 700–709). Consider removing one to shave test time.


877-889: Add positive cases for output and quality

You test invalid output/quality but not valid combinations. Add a couple of happy paths to assert correct content-type/encoding.

Apply after this block:

+        // SUCCESS: output=jpg with quality
+        $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
+            'x-appwrite-project' => $this->getProject()['$id'],
+        ], [
+            'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
+            'width' => 800,
+            'height' => 600,
+            'output' => 'jpg',
+            'quality' => 80,
+        ]);
+        $this->assertEquals(200, $response['headers']['status-code']);
+        $this->assertStringContainsString('image/jpeg', $response['headers']['content-type']);
+
+        // SUCCESS: output=webp
+        $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
+            'x-appwrite-project' => $this->getProject()['$id'],
+        ], [
+            'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
+            'width' => 800,
+            'height' => 600,
+            'output' => 'webp',
+        ]);
+        $this->assertEquals(200, $response['headers']['status-code']);
+        $this->assertStringContainsString('image/webp', $response['headers']['content-type']);
tests/e2e/Services/GraphQL/AvatarsTest.php (2)

198-203: Remove noisy debug echos from passing path.

Echoing headers/body on non-image responses adds noise to CI logs and can leak data. Prefer asserting and letting the failure output surface, or only print on assertion failure via PHPUnit messages.

Apply this diff:

-        // Debug: Print the actual response if it's not an image
-        if (!str_contains($screenshot['headers']['content-type'], 'image/')) {
-            echo "Response content-type: " . $screenshot['headers']['content-type'] . "\n";
-            echo "Response body: " . print_r($screenshot['body'], true) . "\n";
-        }

Also applies to: 229-234, 281-286, 345-350


240-270: Exercise output/quality variants as well.

Consider adding cases for output ("webp", "jpeg") and quality boundaries to cover format negotiation through GraphQL too.

app/controllers/api/avatars.php (2)

699-713: Over-complicated headers serialization; avoid stdClass and duplicate config building.

json_encode will serialize assoc arrays as JSON objects; forcing stdClass and rebuilding $finalConfig is unnecessary and error-prone.

Apply this diff:

-        // Create a new object to ensure proper JSON serialization
-        $headersObject = new \stdClass();
-        foreach ($headers as $key => $value) {
-            $headersObject->$key = $value;
-        }
-
-        // Create the config with headers as an object
+        // Build request config; assoc arrays serialize to JSON objects
         $config = [
             'url' => $url,
             'theme' => $theme,
-            'headers' => $headersObject,
+            'headers' => $headers,
             'sleep' => $sleep * 1000, // Convert seconds to milliseconds
             'waitUntil' => 'load',
             'viewport' => [
                 'width' => $viewportWidth,
                 'height' => $viewportHeight
             ]
         ];
@@
-        // Manually handle the config to ensure headers is an object but arrays remain arrays
-        $finalConfig = [
-            'url' => $config['url'],
-            'theme' => $config['theme'],
-            'headers' => $config['headers'], // Keep as object
-            'sleep' => $config['sleep'],
-            'viewport' => $config['viewport'] // Keep as object
-        ];
-        // Add scale if not default
-        if ($scale != 1) {
-            $finalConfig['deviceScaleFactor'] = $scale;
-        }
-        // Add optional parameters that were set, preserving arrays as arrays
-        if (!empty($userAgent)) {
-            $finalConfig['userAgent'] = $userAgent;
-        }
-        if ($fullpage) {
-            $finalConfig['fullPage'] = true;
-        }
-        if (!empty($locale)) {
-            $finalConfig['locale'] = $locale;
-        }
-        if (!empty($timezone)) {
-            $finalConfig['timezoneId'] = $timezone;
-        }
-        // Add geolocation if any coordinates are provided
-        if ($latitude != 0 || $longitude != 0) {
-            $finalConfig['geolocation'] = [
-                'latitude' => $latitude,
-                'longitude' => $longitude,
-                'accuracy' => $accuracy
-            ];
-        }
-        if ($touch) {
-            $finalConfig['hasTouch'] = true;
-        }
-        // Add permissions if provided (preserve as array)
-        if (!empty($permissions)) {
-            $finalConfig['permissions'] = $permissions; // Keep as array
-        }
-        $config = $finalConfig;
+        // Optional params
+        if ($scale != 1) $config['deviceScaleFactor'] = $scale;
+        if (!empty($userAgent)) $config['userAgent'] = $userAgent;
+        if ($fullpage) $config['fullPage'] = true;
+        if (!empty($locale)) $config['locale'] = $locale;
+        if (!empty($timezone)) $config['timezoneId'] = $timezone;
+        if ($latitude != 0 || $longitude != 0) {
+            $config['geolocation'] = ['latitude' => $latitude, 'longitude' => $longitude, 'accuracy' => $accuracy];
+        }
+        if ($touch) $config['hasTouch'] = true;
+        if (!empty($permissions)) $config['permissions'] = $permissions;

Also applies to: 773-823


843-860: Processing path and content-type are sound; minor polish.

Logic correctly skips processing for original images and maps output to storage-outputs. Consider defaulting output based on actual screenshot mime if the service returns non-PNG (future-proofing).

Also applies to: 861-871

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a260c3 and 8f87b9c.

⛔ Files ignored due to path filters (1)
  • docs/references/avatars/get-screenshot.md is excluded by !docs/references/**
📒 Files selected for processing (11)
  • app/config/variables.php (1 hunks)
  • app/controllers/api/avatars.php (3 hunks)
  • app/controllers/shared/api.php (3 hunks)
  • app/init/constants.php (1 hunks)
  • app/views/install/compose.phtml (1 hunks)
  • docker-compose.yml (1 hunks)
  • src/Appwrite/GraphQL/Types/Mapper.php (1 hunks)
  • src/Appwrite/Platform/Modules/Functions/Workers/Builds.php (1 hunks)
  • tests/e2e/Services/Avatars/AvatarsBase.php (1 hunks)
  • tests/e2e/Services/GraphQL/AvatarsTest.php (1 hunks)
  • tests/e2e/Services/GraphQL/Base.php (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
app/controllers/shared/api.php (1)
src/Appwrite/Event/StatsUsage.php (1)
  • disableMetric (61-66)
tests/e2e/Services/Avatars/AvatarsBase.php (3)
tests/e2e/Services/GraphQL/AvatarsTest.php (1)
  • testGetScreenshot (177-207)
tests/e2e/Client.php (1)
  • Client (8-342)
tests/e2e/Scopes/Scope.php (1)
  • getProject (150-150)
tests/e2e/Services/GraphQL/AvatarsTest.php (4)
tests/e2e/Services/Avatars/AvatarsBase.php (1)
  • testGetScreenshot (562-1295)
tests/e2e/Services/GraphQL/Base.php (1)
  • getQuery (519-3448)
tests/e2e/Client.php (1)
  • Client (8-342)
tests/e2e/Scopes/Scope.php (1)
  • getHeaders (145-145)
app/controllers/api/avatars.php (3)
src/Appwrite/Utopia/Response.php (2)
  • Response (158-980)
  • file (838-845)
src/Appwrite/URL/URL.php (1)
  • URL (5-121)
src/Appwrite/Event/StatsUsage.php (1)
  • addMetric (45-53)
⏰ 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 Service Test (Databases/TablesDB)
🔇 Additional comments (13)
app/views/install/compose.phtml (1)

862-862: LGTM! Browser service version updated.

The appwrite-browser image version bump from 0.2.4 to 0.3.1 aligns with the new screenshot functionality introduced in this PR.

docker-compose.yml (2)

464-464: Good addition of browser host environment variable.

Adding _APP_BROWSER_HOST to the worker-builds service environment enables runtime configuration of the browser service endpoint, supporting the refactor in src/Appwrite/Platform/Modules/Functions/Workers/Builds.php.


961-961: LGTM! Browser service version updated.

Consistent with the production compose template change.

app/init/constants.php (1)

273-273: LGTM! New metric constant added.

The constant METRIC_AVATARS_SCREENSHOTS_GENERATED follows the established naming convention and is appropriately placed in the metrics section.

app/controllers/shared/api.php (2)

57-62: Good defensive type handling for label substitution.

Converting non-string replacements to strings (JSON-encoding arrays) ensures safe string substitution and prevents type errors during label parsing.


588-591: Correct metric handling for cached screenshots.

Disabling METRIC_AVATARS_SCREENSHOTS_GENERATED on cache hits prevents double-counting and accurately reflects that screenshots are served from cache rather than newly generated.

src/Appwrite/Platform/Modules/Functions/Workers/Builds.php (1)

991-991: Good refactor to environment-based configuration.

Moving the browser service endpoint from configuration to an environment variable with a sensible default enables runtime configuration while maintaining backward compatibility.

src/Appwrite/GraphQL/Types/Mapper.php (1)

336-343: Excellent enhancement for float range support.

The refined Range validator mapping correctly distinguishes between float and integer types, enabling accurate GraphQL type representation for numeric parameters like dimensions and viewport values in the screenshot endpoint.

tests/e2e/Services/GraphQL/Base.php (2)

291-292: Constant addition looks good

Naming aligns with existing Avatars constants.


1785-1790: Parity check: GraphQL args vs REST; confirm intended omissions

REST supports headers, output, quality, sleep; this GraphQL query omits them. If intentional, fine; otherwise consider adding for parity. Also verify resolver signature matches these arg names/types.

Run to confirm schema/resolver args:

tests/e2e/Services/GraphQL/AvatarsTest.php (2)

356-385: Error contract check looks good.

Asserting HTTP 200 with GraphQL errors array is consistent with GraphQL semantics. Keep this pattern for other invalid-params cases if added later.

If the server changes to use non-200 on GraphQL validation errors, update expected status accordingly.


195-205: GraphQL response type vs. assertions mismatch — please verify.

These tests assert an image Content-Type from the GraphQL endpoint. However, the GET_SCREENSHOT GraphQL query (per tests/e2e/Services/GraphQL/Base.php excerpt) returns a field named status, which implies a JSON response. If the GraphQL layer streams binary for LOCATION methods, fine; otherwise these assertions will fail. Please confirm the actual GraphQL behavior for avatarsGetScreenshot and align assertions or query accordingly.

Run this to inspect the GET_SCREENSHOT query and confirm expected response type:

Also applies to: 235-236, 287-288, 351-352

app/controllers/api/avatars.php (1)

665-685: Validator set looks good.

Assoc for headers and ArrayList+WhiteList for permissions are appropriate; good coverage on ranges and text lengths.

Comment on lines +693 to +701
$domain = new Domain(\parse_url($url, PHP_URL_HOST));

if (!$domain->isKnown()) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}

$client = new Client();
$client->setTimeout(30);
$client->addHeader('content-type', Client::CONTENT_TYPE_APPLICATION_JSON);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

SSRF hardening and timeout tuning.

  • Domain::isKnown() is a good first gate but won’t block DNS rebinding or public-looking domains resolving to private IPs. Ensure the browser service itself enforces private-network blocking, IP allow/deny lists, and redirect validation.
  • setTimeout(30) might be too low when sleep>0 and fullpage is true. Consider 60s or make it proportional.

Would you like a hardening checklist and example IP resolution guard for the browser service?

Also applies to: 824-836

🤖 Prompt for AI Agents
In app/controllers/api/avatars.php around lines 693-701 (and similarly at
824-836), the current code only uses Domain::isKnown() and sets a 30s client
timeout, which leaves SSRF and timeout issues; update to enforce SSRF hardening
by resolving the hostname before making requests and validating all resolved IPs
against allow/deny rules and private-network ranges (block RFC1918, link-local,
loopback, unique local, multicast, etc.) to prevent DNS rebinding and private IP
access, ensure redirects are validated (reject redirects to disallowed
IPs/ranges), and move IP validation logic to the browser service so it applies
consistently; increase the HTTP client timeout to 60s or make it proportional to
parameters like sleep/fullpage (e.g., baseTimeout * (sleep? factor:1)) and
surface errors with clear messages when validation fails.

Copy link
Copy Markdown
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

🧹 Nitpick comments (2)
app/controllers/api/avatars.php (2)

700-700: Timeout may be insufficient for fullpage + sleep scenarios.

The 30-second timeout can be exceeded when:

  • fullpage=true on long pages requiring scroll and multiple renders
  • sleep is set to 10 seconds (the maximum)
  • Page has slow-loading resources

Consider increasing to 60 seconds or making it proportional: setTimeout(30 + $sleep + ($fullpage ? 20 : 0))


817-819: Generic exception handling loses error context.

The catch block swallows all Throwable types and converts them to a generic AVATAR_REMOTE_URL_FAILED exception. This loses important context:

  • Network timeouts vs. browser service errors vs. image processing failures
  • HTTP status codes from the browser service
  • Specific Imagick errors

Consider preserving more context:

 } catch (\Throwable $th) {
-    throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED, 'Screenshot generation failed: ' . $th->getMessage());
+    $message = 'Screenshot generation failed: ' . $th->getMessage();
+    if ($th instanceof \ImagickException) {
+        throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Image processing failed: ' . $th->getMessage());
+    }
+    throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED, $message, $th->getCode(), $th);
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f87b9c and a39970c.

📒 Files selected for processing (2)
  • app/controllers/api/avatars.php (3 hunks)
  • app/controllers/shared/api.php (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
app/controllers/shared/api.php (1)
src/Appwrite/Event/StatsUsage.php (1)
  • disableMetric (61-66)
app/controllers/api/avatars.php (2)
src/Appwrite/Utopia/Response.php (2)
  • Response (158-980)
  • file (838-845)
src/Appwrite/Event/StatsUsage.php (1)
  • addMetric (45-53)
⏰ 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). (3)
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: scan
🔇 Additional comments (6)
app/controllers/shared/api.php (1)

588-591: LGTM!

The logic correctly disables the screenshot generation metric when serving from cache, preventing double-counting. This is the appropriate behavior for cache hits.

app/controllers/api/avatars.php (5)

665-684: LGTM!

Parameter definitions are comprehensive and use appropriate validators. The viewport is correctly split into separate width/height parameters, and the permissions array validator properly constrains allowed values.


704-712: LGTM – defensive headers conversion.

The indexed array check (lines 704-706) provides defense-in-depth even though the Assoc validator should prevent it. Converting to stdClass (lines 708-712) ensures headers serialize as a JSON object {} rather than an array [], which is correct for HTTP headers.


714-767: LGTM – config construction is well-structured.

The configuration object is built correctly:

  • Sleep time properly converted to milliseconds (line 720)
  • Optional parameters conditionally added only when non-default
  • fullPage set at top level (line 739) without duplication in viewport
  • Arrays like permissions preserved as arrays (line 765)

768-786: LGTM – screenshot generation logic is sound.

The browser service integration:

  • Correctly uses System::getEnv() for configuration (line 769)
  • Properly handles HTTP errors with status codes >= 400 (lines 777-779)
  • Validates screenshot data before processing (lines 783-785)
  • Provides helpful error messages including service responses

787-814: LGTM – image processing logic is efficient.

The conditional processing (line 788) appropriately skips expensive operations when not needed. The implementation:

  • Correctly crops/resizes only when dimensions specified (line 794)
  • Applies quality settings properly (line 797)
  • Sets appropriate Content-Type from config (lines 805-808)
  • Updates metrics before response (line 809)

@eldadfux eldadfux merged commit 745e9e2 into 1.8.x Nov 1, 2025
41 checks passed
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.

5 participants