mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 08:58:35 +00:00
Merge pull request #9147 from JoshiJoshiJoshi/feat-extend-function-headers
Feat: Add executionId and client IP to function headers
This commit is contained in:
commit
8a76979ba8
8 changed files with 38 additions and 9 deletions
|
|
@ -356,11 +356,16 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
}
|
||||
}
|
||||
|
||||
$executionId = ID::unique();
|
||||
|
||||
$headers = \array_merge([], $requestHeaders);
|
||||
$headers['x-appwrite-execution-id'] = $executionId ?? '';
|
||||
$headers['x-appwrite-user-id'] = '';
|
||||
$headers['x-appwrite-country-code'] = '';
|
||||
$headers['x-appwrite-continent-code'] = '';
|
||||
$headers['x-appwrite-continent-eu'] = 'false';
|
||||
$ip = $request->getIP();
|
||||
$headers['x-appwrite-client-ip'] = $ip;
|
||||
|
||||
$jwtExpiry = $resource->getAttribute('timeout', 900) + 60; // 1min extra to account for possible cold-starts
|
||||
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
|
||||
|
|
@ -372,7 +377,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
$headers['x-appwrite-trigger'] = 'http';
|
||||
$headers['x-appwrite-user-jwt'] = '';
|
||||
|
||||
$ip = $headers['x-real-ip'] ?? '';
|
||||
if (!empty($ip)) {
|
||||
$record = $geodb->get($ip);
|
||||
|
||||
|
|
@ -392,8 +396,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
}
|
||||
}
|
||||
|
||||
$executionId = ID::unique();
|
||||
|
||||
$execution = new Document([
|
||||
'$id' => $executionId,
|
||||
'$permissions' => [],
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ 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_REQUEST = ['content-type', 'agent', 'content-length', 'host', 'x-appwrite-client-ip'];
|
||||
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
|
||||
// Message types
|
||||
const MESSAGE_TYPE_EMAIL = 'email';
|
||||
|
|
|
|||
|
|
@ -221,6 +221,8 @@ class Create extends Base
|
|||
'scopes' => $function->getAttribute('scopes', [])
|
||||
]);
|
||||
|
||||
$executionId = ID::unique();
|
||||
$headers['x-appwrite-execution-id'] = $executionId ?? '';
|
||||
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
|
||||
$headers['x-appwrite-trigger'] = 'http';
|
||||
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
|
||||
|
|
@ -228,8 +230,9 @@ class Create extends Base
|
|||
$headers['x-appwrite-country-code'] = '';
|
||||
$headers['x-appwrite-continent-code'] = '';
|
||||
$headers['x-appwrite-continent-eu'] = 'false';
|
||||
$ip = $request->getIP();
|
||||
$headers['x-appwrite-client-ip'] = $ip;
|
||||
|
||||
$ip = $headers['x-real-ip'] ?? '';
|
||||
if (!empty($ip)) {
|
||||
$record = $geodb->get($ip);
|
||||
|
||||
|
|
@ -249,7 +252,7 @@ class Create extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$executionId = ID::unique();
|
||||
|
||||
|
||||
$status = $async ? 'waiting' : 'processing';
|
||||
|
||||
|
|
|
|||
|
|
@ -264,6 +264,8 @@ class Functions extends Action
|
|||
string $jwt = null,
|
||||
string $event = null,
|
||||
): void {
|
||||
$executionId = ID::unique();
|
||||
$headers['x-appwrite-execution-id'] = $executionId ?? '';
|
||||
$headers['x-appwrite-trigger'] = $trigger;
|
||||
$headers['x-appwrite-event'] = $event ?? '';
|
||||
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
|
||||
|
|
@ -276,7 +278,6 @@ class Functions extends Action
|
|||
}
|
||||
}
|
||||
|
||||
$executionId = ID::unique();
|
||||
$execution = new Document([
|
||||
'$id' => $executionId,
|
||||
'$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))],
|
||||
|
|
@ -397,6 +398,7 @@ class Functions extends Action
|
|||
'scopes' => $function->getAttribute('scopes', [])
|
||||
]);
|
||||
|
||||
$headers['x-appwrite-execution-id'] = $executionId ?? '';
|
||||
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
|
||||
$headers['x-appwrite-trigger'] = $trigger;
|
||||
$headers['x-appwrite-event'] = $event ?? '';
|
||||
|
|
@ -409,6 +411,8 @@ class Functions extends Action
|
|||
/** Create execution or update execution status */
|
||||
$execution = $dbForProject->getDocument('executions', $executionId ?? '');
|
||||
if ($execution->isEmpty()) {
|
||||
$executionId = ID::unique();
|
||||
$headers['x-appwrite-execution-id'] = $executionId;
|
||||
$headersFiltered = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) {
|
||||
|
|
@ -416,7 +420,6 @@ class Functions extends Action
|
|||
}
|
||||
}
|
||||
|
||||
$executionId = ID::unique();
|
||||
$execution = new Document([
|
||||
'$id' => $executionId,
|
||||
'$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))],
|
||||
|
|
|
|||
|
|
@ -172,6 +172,11 @@ class FunctionsCustomClientTest extends Scope
|
|||
$this->assertNotEmpty($output['APPWRITE_FUNCTION_JWT']);
|
||||
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
|
||||
|
||||
$executionId = $execution['body']['$id'] ?? '';
|
||||
$this->assertNotEmpty($output['APPWRITE_FUNCTION_EXECUTION_ID']);
|
||||
$this->assertEquals($executionId, $output['APPWRITE_FUNCTION_EXECUTION_ID']);
|
||||
$this->assertNotEmpty($output['APPWRITE_FUNCTION_CLIENT_IP']);
|
||||
|
||||
$execution = $this->createExecution($functionId, [
|
||||
'body' => 'foobar',
|
||||
'async' => true
|
||||
|
|
|
|||
|
|
@ -1173,6 +1173,12 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']);
|
||||
$this->assertEquals(1024, $output['APPWRITE_FUNCTION_MEMORY']);
|
||||
|
||||
// Test execution ID and client IP
|
||||
$executionId = $execution['body']['$id'] ?? '';
|
||||
$this->assertNotEmpty($output['APPWRITE_FUNCTION_EXECUTION_ID']);
|
||||
$this->assertEquals($executionId, $output['APPWRITE_FUNCTION_EXECUTION_ID']);
|
||||
$this->assertNotEmpty($output['APPWRITE_FUNCTION_CLIENT_IP']);
|
||||
|
||||
// Change the specs to 1vcpu 512mb
|
||||
$function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
|
@ -1545,6 +1551,9 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(204, $lastExecution['responseStatusCode']);
|
||||
$this->assertStringContainsString($userId, $lastExecution['logs']);
|
||||
$this->assertStringContainsString('Event User', $lastExecution['logs']);
|
||||
$this->assertNotEmpty($lastExecution['$id']);
|
||||
$headers = array_column($lastExecution['requestHeaders'] ?? [], 'value', 'name');
|
||||
$this->assertEmpty($headers['x-appwrite-client-ip'] ?? '');
|
||||
}, 20_000, 500);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ class FunctionsScheduleTest extends Scope
|
|||
$this->assertNotEmpty($asyncExecution['logs']);
|
||||
$this->assertNotEmpty($asyncExecution['errors']);
|
||||
$this->assertGreaterThan(0, $asyncExecution['duration']);
|
||||
$this->assertNotEmpty($asyncExecution['$id']);
|
||||
$headers = array_column($asyncExecution['requestHeaders'] ?? [], 'value', 'name');
|
||||
$this->assertEmpty($headers['x-appwrite-client-ip'] ?? '');
|
||||
}, 60000, 500);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
|
|
@ -118,7 +121,9 @@ class FunctionsScheduleTest extends Scope
|
|||
$this->assertEquals('scheduled', $execution['body']['status']);
|
||||
$this->assertEquals('PATCH', $execution['body']['requestMethod']);
|
||||
$this->assertEquals('/custom-path', $execution['body']['requestPath']);
|
||||
$this->assertCount(0, $execution['body']['requestHeaders']);
|
||||
$this->assertCount(1, $execution['body']['requestHeaders']);
|
||||
$this->assertEquals('x-appwrite-client-ip', $execution['body']['requestHeaders'][0]['name']);
|
||||
$this->assertNotEmpty($execution['body']['requestHeaders'][0]['value']);
|
||||
|
||||
\sleep(120);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ module.exports = async(context) => {
|
|||
'APPWRITE_FUNCTION_PROJECT_ID' : process.env.APPWRITE_FUNCTION_PROJECT_ID,
|
||||
'APPWRITE_FUNCTION_MEMORY' : process.env.APPWRITE_FUNCTION_MEMORY,
|
||||
'APPWRITE_FUNCTION_CPUS' : process.env.APPWRITE_FUNCTION_CPUS,
|
||||
'APPWRITE_FUNCTION_EXECUTION_ID': context.req.headers['x-appwrite-execution-id'] ?? '',
|
||||
'APPWRITE_FUNCTION_CLIENT_IP': context.req.headers['x-appwrite-client-ip'] ?? '',
|
||||
'CUSTOM_VARIABLE' : process.env.CUSTOM_VARIABLE
|
||||
}, +statusCode);
|
||||
}
|
||||
Loading…
Reference in a new issue