diff --git a/README.md b/README.md index 07e8464ea0..27d6dd9006 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ +> We just announced Timestamp Overrides for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-timestamp-overrides) -> We just announced Auto-increment support for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-auto-increment-support) +> Appwrite Cloud is now Generally Available - [Learn more](https://appwrite.io/cloud-ga) > [Get started with Appwrite](https://apwr.dev/appcloud) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index ac14421382..3cda1a619e 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -1917,7 +1917,7 @@ return [ '$id' => ID::custom('errors'), 'type' => Database::VAR_STRING, 'format' => '', - 'size' => 1000000, + 'size' => APP_FUNCTION_ERROR_LENGTH_LIMIT, 'signed' => true, 'required' => false, 'default' => null, @@ -1928,7 +1928,7 @@ return [ '$id' => ID::custom('logs'), 'type' => Database::VAR_STRING, 'format' => '', - 'size' => 1000000, + 'size' => APP_FUNCTION_LOG_LENGTH_LIMIT, 'signed' => true, 'required' => false, 'default' => null, diff --git a/app/config/templates/function.php b/app/config/templates/function.php index d8426ad900..3a91fdfbb3 100644 --- a/app/config/templates/function.php +++ b/app/config/templates/function.php @@ -2077,6 +2077,61 @@ return [ 'type' => 'text' ] ], + 'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write'] + ], + [ + 'icon' => 'icon-apple', + 'id' => 'sign-in-with-apple', + 'name' => 'Sign in with Apple', + 'score' => 6, + 'tagline' => 'Use native Apple sign-in APIs on Apple devices with Appwrite Auth', + 'permissions' => ['any'], + 'events' => [], + 'cron' => '', + 'timeout' => 15, + 'useCases' => ['auth'], + 'runtimes' => [ + ...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/sign_in_with_apple') + ], + 'instructions' => 'For documentation and instructions, check out file.', + 'vcsProvider' => 'github', + 'providerRepositoryId' => 'templates', + 'providerOwner' => 'appwrite', + 'providerVersion' => '0.2.*', + 'variables' => [ + [ + 'name' => 'BUNDLE_ID', + 'description' => 'Bundle ID of the app. Learn more.', + 'value' => '', + 'placeholder' => 'com.companyname.appname', + 'required' => true, + 'type' => 'text' + ], + [ + 'name' => 'TEAM_ID', + 'description' => 'Team ID of the Apple Developer account.', + 'value' => '', + 'placeholder' => '6K3...5PH', + 'required' => true, + 'type' => 'text' + ], + [ + 'name' => 'KEY_ID', + 'description' => 'Key ID required to communicate with Apple Developer services. Learn more.', + 'value' => '', + 'placeholder' => '9G8...6YF', + 'required' => true, + 'type' => 'text' + ], + [ + 'name' => 'KEY_CONTENTS_ENCODED', + 'description' => 'Contents of Key required to communicated with Apple Developer services, encoded in Base64. Learn more.', + 'value' => '', + 'placeholder' => '7x8aA...Ab7c', + 'required' => true, + 'type' => 'password' + ] + ], 'scopes' => ['users.read', 'users.write'] ] ]; diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 61b71930eb..8bacb24507 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1917,7 +1917,7 @@ App::post('/v1/account/tokens/magic-url') )) ->label('abuse-limit', 60) ->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}']) - ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.') ->param('email', '', new Email(), 'User email.') ->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey']) ->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true) @@ -2170,7 +2170,7 @@ App::post('/v1/account/tokens/email') )) ->label('abuse-limit', 10) ->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}']) - ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.') ->param('email', '', new Email(), 'User email.') ->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true) ->inject('request') @@ -2498,7 +2498,7 @@ App::post('/v1/account/tokens/phone') )) ->label('abuse-limit', 10) ->label('abuse-key', ['url:{url},phone:{param-phone}', 'url:{url},ip:{ip}']) - ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') + ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars. If the phone number has never been used, a new account is created using the provided userId. Otherwise, if the phone number is already attached to an account, the user ID is ignored.') ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.') ->inject('request') ->inject('response') diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 32b77433f0..975db909c6 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -23,10 +23,12 @@ use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; use Utopia\System\System; +use Utopia\Validator\AnyOf; use Utopia\Validator\Domain; use Utopia\Validator\Integer; use Utopia\Validator\Multiple; use Utopia\Validator\Text; +use Utopia\Validator\URL; use Utopia\Validator\WhiteList; App::get('/v1/health') @@ -397,7 +399,7 @@ App::get('/v1/health/certificate') ], contentType: ContentType::JSON )) - ->param('domain', null, new Multiple([new Domain(), new PublicDomain()]), Multiple::TYPE_STRING, 'Domain name') + ->param('domain', null, new Multiple([new AnyOf([new URL(), new Domain()]), new PublicDomain()]), Multiple::TYPE_STRING, 'Domain name') ->inject('response') ->action(function (string $domain, Response $response) { if (filter_var($domain, FILTER_VALIDATE_URL)) { diff --git a/app/controllers/general.php b/app/controllers/general.php index 40f861ad8c..b6c9df5577 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -615,11 +615,33 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw } } + // Truncate logs if they exceed the limit + $maxLogLength = APP_FUNCTION_LOG_LENGTH_LIMIT; + $logs = $executionResponse['logs'] ?? ''; + + if (\is_string($logs) && \strlen($logs) > $maxLogLength) { + $warningMessage = "[WARNING] Logs truncated. The output exceeded {$maxLogLength} characters.\n"; + $warningLength = \strlen($warningMessage); + $maxContentLength = $maxLogLength - $warningLength; + $logs = $warningMessage . \substr($logs, -$maxContentLength); + } + + // Truncate errors if they exceed the limit + $maxErrorLength = APP_FUNCTION_ERROR_LENGTH_LIMIT; + $errors = $executionResponse['errors'] ?? ''; + + if (\is_string($errors) && \strlen($errors) > $maxErrorLength) { + $warningMessage = "[WARNING] Errors truncated. The output exceeded {$maxErrorLength} characters.\n"; + $warningLength = \strlen($warningMessage); + $maxContentLength = $maxErrorLength - $warningLength; + $errors = $warningMessage . \substr($errors, -$maxContentLength); + } + /** Update execution status */ $status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed'; $execution->setAttribute('status', $status); - $execution->setAttribute('logs', $executionResponse['logs']); - $execution->setAttribute('errors', $executionResponse['errors']); + $execution->setAttribute('logs', $logs); + $execution->setAttribute('errors', $errors); $execution->setAttribute('responseStatusCode', $executionResponse['statusCode']); $execution->setAttribute('responseHeaders', $headersFiltered); $execution->setAttribute('duration', $executionResponse['duration']); diff --git a/app/init/constants.php b/app/init/constants.php index 62d9f16853..689ab69fa4 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -143,6 +143,8 @@ const APP_AUTH_TYPE_KEY = 'Key'; const APP_AUTH_TYPE_ADMIN = 'Admin'; // Response related const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB +const APP_FUNCTION_LOG_LENGTH_LIMIT = 1000000; +const APP_FUNCTION_ERROR_LENGTH_LIMIT = 1000000; // Function headers const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host']; const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length']; diff --git a/app/views/general/404.phtml b/app/views/general/404.phtml index 03fdedb261..6dbf158205 100644 --- a/app/views/general/404.phtml +++ b/app/views/general/404.phtml @@ -6,8 +6,28 @@ 404 Not Found - - + + + <?php echo $this->print($title); ?> - +
-
+
print($label); ?>

print($message); ?>

@@ -462,14 +486,14 @@ switch ($type) { - +
-
-
Error trace
@@ -500,6 +524,17 @@ switch ($type) {
+ \ No newline at end of file diff --git a/composer.lock b/composer.lock index cd3eb5fef7..922f5bf5ee 100644 --- a/composer.lock +++ b/composer.lock @@ -3548,16 +3548,16 @@ }, { "name": "utopia-php/database", - "version": "0.71.14", + "version": "0.71.15", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "20998e268ec528ebbf06adb77ec4230f70469fa7" + "reference": "638e3fb74524970bc589aa7be564aa0783857ee0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/20998e268ec528ebbf06adb77ec4230f70469fa7", - "reference": "20998e268ec528ebbf06adb77ec4230f70469fa7", + "url": "https://api.github.com/repos/utopia-php/database/zipball/638e3fb74524970bc589aa7be564aa0783857ee0", + "reference": "638e3fb74524970bc589aa7be564aa0783857ee0", "shasum": "" }, "require": { @@ -3598,9 +3598,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.71.14" + "source": "https://github.com/utopia-php/database/tree/0.71.15" }, - "time": "2025-08-14T07:15:06+00:00" + "time": "2025-08-19T08:35:32+00:00" }, { "name": "utopia-php/detector", @@ -3852,16 +3852,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.20", + "version": "0.33.21", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "e1c7ab4e0b5b0a9a70256b1e00912e101e76a131" + "reference": "eb0e82e90b8fa493f99b8d131bdd25173422c493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/e1c7ab4e0b5b0a9a70256b1e00912e101e76a131", - "reference": "e1c7ab4e0b5b0a9a70256b1e00912e101e76a131", + "url": "https://api.github.com/repos/utopia-php/http/zipball/eb0e82e90b8fa493f99b8d131bdd25173422c493", + "reference": "eb0e82e90b8fa493f99b8d131bdd25173422c493", "shasum": "" }, "require": { @@ -3893,9 +3893,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.20" + "source": "https://github.com/utopia-php/http/tree/0.33.21" }, - "time": "2025-05-18T23:51:21+00:00" + "time": "2025-08-19T10:52:15+00:00" }, { "name": "utopia-php/image", @@ -4917,16 +4917,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.41.29", + "version": "0.41.30", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "4af563f3b0879747efc8434eb8ed8bf97e75039f" + "reference": "14a6e52d0d1075d176f4c80cafde9afe506e7455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/4af563f3b0879747efc8434eb8ed8bf97e75039f", - "reference": "4af563f3b0879747efc8434eb8ed8bf97e75039f", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/14a6e52d0d1075d176f4c80cafde9afe506e7455", + "reference": "14a6e52d0d1075d176f4c80cafde9afe506e7455", "shasum": "" }, "require": { @@ -4962,9 +4962,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.41.29" + "source": "https://github.com/appwrite/sdk-generator/tree/0.41.30" }, - "time": "2025-08-04T04:34:45+00:00" + "time": "2025-08-18T01:39:09+00:00" }, { "name": "doctrine/annotations", diff --git a/docs/references/account/create-token-email.md b/docs/references/account/create-token-email.md index 3e49899888..3ed175c7f9 100644 --- a/docs/references/account/create-token-email.md +++ b/docs/references/account/create-token-email.md @@ -1,3 +1,3 @@ -Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST /v1/account/sessions/token](https://appwrite.io/docs/references/cloud/client-web/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes. +Sends the user an email with a secret key for creating a session. If the email address has never been used, a **new account is created** using the provided `userId`. Otherwise, if the email address is already attached to an account, the **user ID is ignored**. Then, the user will receive an email with the one-time password. Use the returned user ID and secret and submit a request to the [POST /v1/account/sessions/token](https://appwrite.io/docs/references/cloud/client-web/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes. -A user is limited to 10 active sessions at a time by default. [Learn more about session limits](https://appwrite.io/docs/authentication-security#limits). \ No newline at end of file +A user is limited to 10 active sessions at a time by default. [Learn more about session limits](https://appwrite.io/docs/authentication-security#limits). diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index ef31d5a79c..64056b7db7 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -423,13 +423,34 @@ class Create extends Base } } + $maxLogLength = APP_FUNCTION_LOG_LENGTH_LIMIT; + $logs = $executionResponse['logs'] ?? ''; + + if (\is_string($logs) && \strlen($logs) > $maxLogLength) { + $warningMessage = "[WARNING] Logs truncated. The output exceeded {$maxLogLength} characters.\n"; + $warningLength = \strlen($warningMessage); + $maxContentLength = $maxLogLength - $warningLength; + $logs = $warningMessage . \substr($logs, -$maxContentLength); + } + + // Truncate errors if they exceed the limit + $maxErrorLength = APP_FUNCTION_ERROR_LENGTH_LIMIT; + $errors = $executionResponse['errors'] ?? ''; + + if (\is_string($errors) && \strlen($errors) > $maxErrorLength) { + $warningMessage = "[WARNING] Errors truncated. The output exceeded {$maxErrorLength} characters.\n"; + $warningLength = \strlen($warningMessage); + $maxContentLength = $maxErrorLength - $warningLength; + $errors = $warningMessage . \substr($errors, -$maxContentLength); + } + /** Update execution status */ $status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed'; $execution->setAttribute('status', $status); $execution->setAttribute('responseStatusCode', $executionResponse['statusCode']); $execution->setAttribute('responseHeaders', $headersFiltered); - $execution->setAttribute('logs', $executionResponse['logs']); - $execution->setAttribute('errors', $executionResponse['errors']); + $execution->setAttribute('logs', $logs); + $execution->setAttribute('errors', $errors); $execution->setAttribute('duration', $executionResponse['duration']); } catch (\Throwable $th) { $durationEnd = \microtime(true); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 2e25248d9a..864d6451b8 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -550,14 +550,36 @@ class Functions extends Action } } + $maxLogLength = APP_FUNCTION_LOG_LENGTH_LIMIT; + $logs = $executionResponse['logs'] ?? ''; + + if (\is_string($logs) && \strlen($logs) > $maxLogLength) { + $warningMessage = "[WARNING] Logs truncated. The output exceeded {$maxLogLength} characters.\n"; + $warningLength = \strlen($warningMessage); + $maxContentLength = $maxLogLength - $warningLength; + $logs = $warningMessage . \substr($logs, -$maxContentLength); + } + + // Truncate errors if they exceed the limit + $maxErrorLength = APP_FUNCTION_ERROR_LENGTH_LIMIT; + $errors = $executionResponse['errors'] ?? ''; + + if (\is_string($errors) && \strlen($errors) > $maxErrorLength) { + $warningMessage = "[WARNING] Errors truncated. The output exceeded {$maxErrorLength} characters.\n"; + $warningLength = \strlen($warningMessage); + $maxContentLength = $maxErrorLength - $warningLength; + $errors = $warningMessage . \substr($errors, -$maxContentLength); + } + /** Update execution status */ $execution ->setAttribute('status', $status) ->setAttribute('responseStatusCode', $executionResponse['statusCode']) ->setAttribute('responseHeaders', $headersFiltered) - ->setAttribute('logs', $executionResponse['logs']) - ->setAttribute('errors', $executionResponse['errors']) + ->setAttribute('logs', $logs) + ->setAttribute('errors', $errors) ->setAttribute('duration', $executionResponse['duration']); + } catch (\Throwable $th) { $durationEnd = \microtime(true); $execution diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index ff99033fdf..6597f1af69 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -2282,4 +2282,45 @@ class FunctionsCustomServerTest extends Scope $this->cleanupFunction($functionId); } + + public function testLogAndErrorTruncation(): void + { + $functionId = $this->setupFunction([ + 'functionId' => ID::unique(), + 'name' => 'Test Log Truncation', + 'runtime' => 'node-22', + 'entrypoint' => 'index.js', + 'timeout' => 15, + ]); + + $this->setupDeployment($functionId, [ + 'code' => $this->packageFunction('log-error-truncation'), + 'activate' => true + ]); + + $execution = $this->createExecution($functionId, [ + 'async' => 'false' + ]); + + $this->assertEquals(201, $execution['headers']['status-code']); + $this->assertEquals(200, $execution['body']['responseStatusCode']); + + // Verify logs are truncated and warning message is present at the beginning + $logs = $execution['body']['logs']; + $this->assertLessThanOrEqual(APP_FUNCTION_LOG_LENGTH_LIMIT, strlen($logs)); + $this->assertStringStartsWith('[WARNING] Logs truncated', $logs); + + $this->assertStringNotContainsString('z', $logs); + $this->assertStringContainsString('a', $logs); + + // Verify errors are truncated and warning message is present at the beginning + $errors = $execution['body']['errors']; + $this->assertLessThanOrEqual(APP_FUNCTION_ERROR_LENGTH_LIMIT, strlen($errors)); + $this->assertStringStartsWith('[WARNING] Errors truncated', $errors); + + $this->assertStringNotContainsString('z', $errors); + $this->assertStringContainsString('a', $errors); + + $this->cleanupFunction($functionId); + } } diff --git a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php index ea310d5449..da23b9ab7c 100644 --- a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php +++ b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php @@ -109,15 +109,11 @@ class ProxyCustomServerTest extends Scope $rule = $this->createAPIRule('https://' . $domain); $this->assertEquals(400, $rule['headers']['status-code']); - // Unexpected I would say, but it is the current behaviour $rule = $this->createAPIRule('wss://' . $domain); - $this->assertEquals(201, $rule['headers']['status-code']); - $this->cleanupRule($rule['body']['$id']); + $this->assertEquals(400, $rule['headers']['status-code']); - // Unexpected I would say, but it is the current behaviour $rule = $this->createAPIRule($domain . '/some-path'); - $this->assertEquals(201, $rule['headers']['status-code']); - $this->cleanupRule($rule['body']['$id']); + $this->assertEquals(400, $rule['headers']['status-code']); } public function testCreateRedirectRule(): void diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index f1550e3910..f76122c00a 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -2697,4 +2697,43 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } + + public function testCookieHeader() + { + $siteId = $this->setupSite([ + 'siteId' => ID::unique(), + 'name' => 'Astro site', + 'framework' => 'astro', + 'adapter' => 'ssr', + 'buildRuntime' => 'node-22', + 'outputDirectory' => './dist', + 'buildCommand' => 'npm run build', + 'installCommand' => 'npm install', + 'fallbackFile' => '', + ]); + + $this->assertNotEmpty($siteId); + + $domain = $this->setupSiteDomain($siteId); + + $deploymentId = $this->setupDeployment($siteId, [ + 'code' => $this->packageSite('astro'), + 'activate' => 'true' + ]); + + $this->assertNotEmpty($deploymentId); + + $domain = $this->getSiteDomain($siteId); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/cookies', [ + 'cookie' => 'custom-session-id=abcd123' + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals("abcd123", $response['body']); + + $this->cleanupSite($siteId); + } } diff --git a/tests/resources/functions/log-error-truncation/index.js b/tests/resources/functions/log-error-truncation/index.js new file mode 100644 index 0000000000..7e94fa5028 --- /dev/null +++ b/tests/resources/functions/log-error-truncation/index.js @@ -0,0 +1,14 @@ +module.exports = async(context) => { + // Create a string that is 1000001 characters long (exceeds the 1000000 limit) + const longString = 'z' + 'a'.repeat(1000000); + + context.log(longString); + context.error(longString); + + return context.res.json({ + motto: 'Build like a team of hundreds_', + learn: 'https://appwrite.io/docs', + connect: 'https://appwrite.io/discord', + getInspired: 'https://builtwith.appwrite.io', + }); + }; \ No newline at end of file diff --git a/tests/resources/sites/astro/src/pages/cookies.js b/tests/resources/sites/astro/src/pages/cookies.js new file mode 100644 index 0000000000..5f5efac833 --- /dev/null +++ b/tests/resources/sites/astro/src/pages/cookies.js @@ -0,0 +1,4 @@ +export async function GET(context) { + const sessionId = context.cookies.get("custom-session-id")?.value ?? 'Custom session ID missing'; + return new Response(sessionId); +}