diff --git a/app/config/services.php b/app/config/services.php index f02f169b88..5f8651e59f 100644 --- a/app/config/services.php +++ b/app/config/services.php @@ -214,7 +214,7 @@ return [ 'name' => 'Proxy', 'subtitle' => 'The Proxy Service allows you to configure actions for your domains beyond DNS configuration.', 'description' => '/docs/services/proxy.md', - 'controller' => 'api/proxy.php', + 'controller' => '', // Uses modules 'sdk' => true, 'docs' => true, 'docsUrl' => 'https://appwrite.io/docs/proxy', diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php deleted file mode 100644 index 4a64448335..0000000000 --- a/app/controllers/api/proxy.php +++ /dev/null @@ -1,318 +0,0 @@ -groups(['api', 'proxy']) - ->desc('List rules') - ->label('scope', 'rules.read') - ->label('sdk', new Method( - namespace: 'proxy', - group: null, - name: 'listRules', - description: '/docs/references/proxy/list-rules.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_PROXY_RULE_LIST, - ) - ] - )) - ->param('queries', [], new Rules(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Rules::ALLOWED_ATTRIBUTES), true) - ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) - ->inject('response') - ->inject('project') - ->inject('dbForPlatform') - ->action(function (array $queries, string $search, Response $response, Document $project, Database $dbForPlatform) { - try { - $queries = Query::parseQueries($queries); - } catch (QueryException $e) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); - } - - if (!empty($search)) { - $queries[] = Query::search('search', $search); - } - - $queries[] = Query::equal('projectInternalId', [$project->getSequence()]); - - /** - * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries - */ - $cursor = \array_filter($queries, function ($query) { - return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); - }); - $cursor = reset($cursor); - if ($cursor) { - /** @var Query $cursor */ - - $validator = new Cursor(); - if (!$validator->isValid($cursor)) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); - } - - $ruleId = $cursor->getValue(); - $cursorDocument = $dbForPlatform->getDocument('rules', $ruleId); - - if ($cursorDocument->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Rule '{$ruleId}' for the 'cursor' value not found."); - } - - $cursor->setValue($cursorDocument); - } - - $filterQueries = Query::groupByType($queries)['filters']; - - $rules = $dbForPlatform->find('rules', $queries); - foreach ($rules as $rule) { - $certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')); - $rule->setAttribute('logs', $certificate->getAttribute('logs', '')); - $rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', '')); - } - - $response->dynamic(new Document([ - 'rules' => $rules, - 'total' => $dbForPlatform->count('rules', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_PROXY_RULE_LIST); - }); - -App::get('/v1/proxy/rules/:ruleId') - ->groups(['api', 'proxy']) - ->desc('Get rule') - ->label('scope', 'rules.read') - ->label('sdk', new Method( - namespace: 'proxy', - group: null, - name: 'getRule', - description: '/docs/references/proxy/get-rule.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_PROXY_RULE, - ) - ] - )) - ->param('ruleId', '', new UID(), 'Rule ID.') - ->inject('response') - ->inject('project') - ->inject('dbForPlatform') - ->action(function (string $ruleId, Response $response, Document $project, Database $dbForPlatform) { - $rule = $dbForPlatform->getDocument('rules', $ruleId); - - if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { - throw new Exception(Exception::RULE_NOT_FOUND); - } - - $certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')); - $rule->setAttribute('logs', $certificate->getAttribute('logs', '')); - $rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', '')); - - $response->dynamic($rule, Response::MODEL_PROXY_RULE); - }); - -App::delete('/v1/proxy/rules/:ruleId') - ->groups(['api', 'proxy']) - ->desc('Delete rule') - ->label('scope', 'rules.write') - ->label('event', 'rules.[ruleId].delete') - ->label('audits.event', 'rules.delete') - ->label('audits.resource', 'rule/{request.ruleId}') - ->label('sdk', new Method( - namespace: 'proxy', - group: null, - name: 'deleteRule', - description: '/docs/references/proxy/delete-rule.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE - )) - ->param('ruleId', '', new UID(), 'Rule ID.') - ->inject('response') - ->inject('project') - ->inject('dbForPlatform') - ->inject('queueForDeletes') - ->inject('queueForEvents') - ->action(function (string $ruleId, Response $response, Document $project, Database $dbForPlatform, Delete $queueForDeletes, Event $queueForEvents) { - $rule = $dbForPlatform->getDocument('rules', $ruleId); - - if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { - throw new Exception(Exception::RULE_NOT_FOUND); - } - - $dbForPlatform->deleteDocument('rules', $rule->getId()); - - $queueForDeletes - ->setType(DELETE_TYPE_DOCUMENT) - ->setDocument($rule); - - $queueForEvents->setParam('ruleId', $rule->getId()); - - $response->noContent(); - }); - -App::patch('/v1/proxy/rules/:ruleId/verification') - ->desc('Update rule verification status') - ->groups(['api', 'proxy']) - ->label('scope', 'rules.write') - ->label('event', 'rules.[ruleId].update') - ->label('audits.event', 'rule.update') - ->label('audits.resource', 'rule/{response.$id}') - ->label('sdk', new Method( - namespace: 'proxy', - group: null, - name: 'updateRuleVerification', - description: '/docs/references/proxy/update-rule-verification.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_PROXY_RULE, - ) - ] - )) - ->param('ruleId', '', new UID(), 'Rule ID.') - ->inject('response') - ->inject('queueForCertificates') - ->inject('queueForEvents') - ->inject('project') - ->inject('dbForPlatform') - ->inject('log') - ->action(function (string $ruleId, Response $response, Certificate $queueForCertificates, Event $queueForEvents, Document $project, Database $dbForPlatform, Log $log) { - $rule = $dbForPlatform->getDocument('rules', $ruleId); - - if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { - throw new Exception(Exception::RULE_NOT_FOUND); - } - - $targetCNAME = null; - switch ($rule->getAttribute('type', '')) { - case 'api': - // For example: fra.cloud.appwrite.io - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); - break; - case 'redirect': - // For example: appwrite.network - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); - break; - case 'deployment': - switch ($rule->getAttribute('deploymentResourceType', '')) { - case 'function': - // For example: fra.appwrite.run - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_FUNCTIONS', '')); - break; - case 'site': - // For example: appwrite.network - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); - break; - default: - break; - } - // no break - default: - break; - } - - $validators = []; - - if (!is_null($targetCNAME)) { - if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) { - $validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME); - } - } - - if ((new IP(IP::V4))->isValid(System::getEnv('_APP_DOMAIN_TARGET_A', ''))) { - $validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_A', ''), DNS::RECORD_A); - } - if ((new IP(IP::V6))->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA', ''))) { - $validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_AAAA', ''), DNS::RECORD_AAAA); - } - - if (empty($validators)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'At least one of domain targets environment variable must be configured.'); - } - - if ($rule->getAttribute('verification') === true) { - return $response->dynamic($rule, Response::MODEL_PROXY_RULE); - } - - $validator = new AnyOf($validators, AnyOf::TYPE_STRING); - $domain = new Domain($rule->getAttribute('domain', '')); - - $validationStart = \microtime(true); - if (!$validator->isValid($domain->get())) { - $log->addExtra('dnsTiming', \strval(\microtime(true) - $validationStart)); - $log->addTag('dnsDomain', $domain->get()); - - $errors = []; - foreach ($validators as $validator) { - if (!empty($validator->getLogs())) { - $errors[] = $validator->getLogs(); - } - } - - $error = \implode("\n", $errors); - $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); - - throw new Exception(Exception::RULE_VERIFICATION_FAILED); - } - - // Ensure CAA won't block certificate issuance - if (!empty(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''))) { - $validationStart = \microtime(true); - $validator = new DNS(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''), DNS::RECORD_CAA); - if (!$validator->isValid($domain->get())) { - $log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart)); - $log->addTag('dnsDomain', $domain->get()); - $error = $validator->getDescription(); - $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); - throw new Exception(Exception::RULE_VERIFICATION_FAILED, 'Domain verification failed because CAA records do not allow Appwrite\'s certificate issuer.'); - } - } - - $dbForPlatform->updateDocument('rules', $rule->getId(), $rule->setAttribute('status', 'verifying')); - - // Issue a TLS certificate when domain is verified - $queueForCertificates - ->setDomain(new Document([ - 'domain' => $rule->getAttribute('domain'), - 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')), - ])) - ->trigger(); - - $queueForEvents->setParam('ruleId', $rule->getId()); - - $certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')); - $rule->setAttribute('logs', $certificate->getAttribute('logs', '')); - - $response->dynamic($rule, Response::MODEL_PROXY_RULE); - }); diff --git a/docs/references/proxy/delete-rule.md b/docs/references/proxy/delete-rule.md deleted file mode 100644 index 7a4823f86d..0000000000 --- a/docs/references/proxy/delete-rule.md +++ /dev/null @@ -1 +0,0 @@ -Delete a proxy rule by its unique ID. \ No newline at end of file diff --git a/docs/references/proxy/get-rule.md b/docs/references/proxy/get-rule.md deleted file mode 100644 index cfd040141e..0000000000 --- a/docs/references/proxy/get-rule.md +++ /dev/null @@ -1 +0,0 @@ -Get a proxy rule by its unique ID. \ No newline at end of file diff --git a/docs/references/proxy/list-rules.md b/docs/references/proxy/list-rules.md deleted file mode 100644 index 042d780f02..0000000000 --- a/docs/references/proxy/list-rules.md +++ /dev/null @@ -1 +0,0 @@ -Get a list of all the proxy rules. You can use the query params to filter your results. \ No newline at end of file diff --git a/docs/references/proxy/update-rule-verification.md b/docs/references/proxy/update-rule-verification.md deleted file mode 100644 index c06994bc59..0000000000 --- a/docs/references/proxy/update-rule-verification.md +++ /dev/null @@ -1 +0,0 @@ -Retry getting verification process of a proxy rule. This endpoint triggers domain verification by checking DNS records (CNAME) against the configured target domain. If verification is successful, a TLS certificate will be automatically provisioned for the domain. \ No newline at end of file diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Delete.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Delete.php new file mode 100644 index 0000000000..5d76cc161e --- /dev/null +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Delete.php @@ -0,0 +1,88 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/proxy/rules/:ruleId') + ->desc('Delete rule') + ->groups(['api', 'proxy']) + ->label('scope', 'rules.write') + ->label('event', 'rules.[ruleId].delete') + ->label('audits.event', 'rules.delete') + ->label('audits.resource', 'rule/{request.ruleId}') + ->label('sdk', new Method( + namespace: 'proxy', + group: null, + name: 'deleteRule', + description: <<param('ruleId', '', new UID(), 'Rule ID.') + ->inject('response') + ->inject('project') + ->inject('dbForPlatform') + ->inject('queueForDeletes') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + string $ruleId, + Response $response, + Document $project, + Database $dbForPlatform, + DeleteEvent $queueForDeletes, + Event $queueForEvents + ) { + $rule = $dbForPlatform->getDocument('rules', $ruleId); + + if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { + throw new Exception(Exception::RULE_NOT_FOUND); + } + + $dbForPlatform->deleteDocument('rules', $rule->getId()); + + $queueForDeletes + ->setType(DELETE_TYPE_DOCUMENT) + ->setDocument($rule); + + $queueForEvents->setParam('ruleId', $rule->getId()); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php new file mode 100644 index 0000000000..77aa3df581 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Get.php @@ -0,0 +1,73 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/proxy/rules/:ruleId') + ->desc('Get rule') + ->groups(['api', 'proxy']) + ->label('scope', 'rules.read') + ->label('sdk', new Method( + namespace: 'proxy', + group: null, + name: 'getRule', + description: <<param('ruleId', '', new UID(), 'Rule ID.') + ->inject('response') + ->inject('project') + ->inject('dbForPlatform') + ->callback($this->action(...)); + } + + public function action( + string $ruleId, + Response $response, + Document $project, + Database $dbForPlatform + ) { + $rule = $dbForPlatform->getDocument('rules', $ruleId); + + if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { + throw new Exception(Exception::RULE_NOT_FOUND); + } + + $certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')); + $rule->setAttribute('logs', $certificate->getAttribute('logs', '')); + $rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', '')); + + $response->dynamic($rule, Response::MODEL_PROXY_RULE); + } +} diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php new file mode 100644 index 0000000000..3d52d203c3 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php @@ -0,0 +1,186 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/proxy/rules/:ruleId/verification') + ->desc('Update rule verification status') + ->groups(['api', 'proxy']) + ->label('scope', 'rules.write') + ->label('event', 'rules.[ruleId].update') + ->label('audits.event', 'rule.update') + ->label('audits.resource', 'rule/{response.$id}') + ->label('sdk', new Method( + namespace: 'proxy', + group: null, + name: 'updateRuleVerification', + description: <<param('ruleId', '', new UID(), 'Rule ID.') + ->inject('response') + ->inject('queueForCertificates') + ->inject('queueForEvents') + ->inject('project') + ->inject('dbForPlatform') + ->inject('log') + ->callback($this->action(...)); + } + + public function action( + string $ruleId, + Response $response, + Certificate $queueForCertificates, + Event $queueForEvents, + Document $project, + Database $dbForPlatform, + Log $log + ) { + $rule = $dbForPlatform->getDocument('rules', $ruleId); + + if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { + throw new Exception(Exception::RULE_NOT_FOUND); + } + + $targetCNAME = null; + switch ($rule->getAttribute('type', '')) { + case 'api': + // For example: fra.cloud.appwrite.io + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); + break; + case 'redirect': + // For example: appwrite.network + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + break; + case 'deployment': + switch ($rule->getAttribute('deploymentResourceType', '')) { + case 'function': + // For example: fra.appwrite.run + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_FUNCTIONS', '')); + break; + case 'site': + // For example: appwrite.network + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + break; + default: + break; + } + // no break + default: + break; + } + + $validators = []; + + if (!is_null($targetCNAME)) { + if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) { + $validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME); + } + } + + if ((new IP(IP::V4))->isValid(System::getEnv('_APP_DOMAIN_TARGET_A', ''))) { + $validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_A', ''), DNS::RECORD_A); + } + if ((new IP(IP::V6))->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA', ''))) { + $validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_AAAA', ''), DNS::RECORD_AAAA); + } + + if (empty($validators)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'At least one of domain targets environment variable must be configured.'); + } + + if ($rule->getAttribute('verification') === true) { + return $response->dynamic($rule, Response::MODEL_PROXY_RULE); + } + + $validator = new AnyOf($validators, AnyOf::TYPE_STRING); + $domain = new Domain($rule->getAttribute('domain', '')); + + $validationStart = \microtime(true); + if (!$validator->isValid($domain->get())) { + $log->addExtra('dnsTiming', \strval(\microtime(true) - $validationStart)); + $log->addTag('dnsDomain', $domain->get()); + + $errors = []; + foreach ($validators as $validator) { + if (!empty($validator->getLogs())) { + $errors[] = $validator->getLogs(); + } + } + + $error = \implode("\n", $errors); + $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); + + throw new Exception(Exception::RULE_VERIFICATION_FAILED); + } + + // Ensure CAA won't block certificate issuance + if (!empty(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''))) { + $validationStart = \microtime(true); + $validator = new DNS(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''), DNS::RECORD_CAA); + if (!$validator->isValid($domain->get())) { + $log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart)); + $log->addTag('dnsDomain', $domain->get()); + $error = $validator->getDescription(); + $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); + throw new Exception(Exception::RULE_VERIFICATION_FAILED, 'Domain verification failed because CAA records do not allow Appwrite\'s certificate issuer.'); + } + } + + $dbForPlatform->updateDocument('rules', $rule->getId(), $rule->setAttribute('status', 'verifying')); + + // Issue a TLS certificate when domain is verified + $queueForCertificates + ->setDomain(new Document([ + 'domain' => $rule->getAttribute('domain'), + 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')), + ])) + ->trigger(); + + $queueForEvents->setParam('ruleId', $rule->getId()); + + $certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')); + $rule->setAttribute('logs', $certificate->getAttribute('logs', '')); + + $response->dynamic($rule, Response::MODEL_PROXY_RULE); + } +} diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/XList.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/XList.php new file mode 100644 index 0000000000..e084cf76b2 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/XList.php @@ -0,0 +1,118 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/proxy/rules') + ->desc('List rules') + ->groups(['api', 'proxy']) + ->label('scope', 'rules.read') + ->label('sdk', new Method( + namespace: 'proxy', + group: null, + name: 'listRules', + description: <<param('queries', [], new Rules(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Rules::ALLOWED_ATTRIBUTES), true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->inject('response') + ->inject('project') + ->inject('dbForPlatform') + ->callback($this->action(...)); + } + + public function action( + array $queries, + string $search, + Response $response, + Document $project, + Database $dbForPlatform + ) { + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + if (!empty($search)) { + $queries[] = Query::search('search', $search); + } + + $queries[] = Query::equal('projectInternalId', [$project->getSequence()]); + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + $cursor = reset($cursor); + if ($cursor) { + /** @var Query $cursor */ + + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $ruleId = $cursor->getValue(); + $cursorDocument = $dbForPlatform->getDocument('rules', $ruleId); + + if ($cursorDocument->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Rule '{$ruleId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument); + } + + $filterQueries = Query::groupByType($queries)['filters']; + + $rules = $dbForPlatform->find('rules', $queries); + foreach ($rules as $rule) { + $certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')); + $rule->setAttribute('logs', $certificate->getAttribute('logs', '')); + $rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', '')); + } + + $response->dynamic(new Document([ + 'rules' => $rules, + 'total' => $dbForPlatform->count('rules', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_PROXY_RULE_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Proxy/Services/Http.php b/src/Appwrite/Platform/Modules/Proxy/Services/Http.php index c5f11ad5be..980c64cc54 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Proxy/Services/Http.php @@ -3,9 +3,13 @@ namespace Appwrite\Platform\Modules\Proxy\Services; use Appwrite\Platform\Modules\Proxy\Http\Rules\API\Create as CreateAPIRule; +use Appwrite\Platform\Modules\Proxy\Http\Rules\Delete as DeleteRule; use Appwrite\Platform\Modules\Proxy\Http\Rules\Function\Create as CreateFunctionRule; +use Appwrite\Platform\Modules\Proxy\Http\Rules\Get as GetRule; use Appwrite\Platform\Modules\Proxy\Http\Rules\Redirect\Create as CreateRedirectRule; use Appwrite\Platform\Modules\Proxy\Http\Rules\Site\Create as CreateSiteRule; +use Appwrite\Platform\Modules\Proxy\Http\Rules\Verification\Update as UpdateRuleVerification; +use Appwrite\Platform\Modules\Proxy\Http\Rules\XList as ListRules; use Utopia\Platform\Service; class Http extends Service @@ -19,5 +23,9 @@ class Http extends Service $this->addAction(CreateSiteRule::getName(), new CreateSiteRule()); $this->addAction(CreateFunctionRule::getName(), new CreateFunctionRule()); $this->addAction(CreateRedirectRule::getName(), new CreateRedirectRule()); + $this->addAction(GetRule::getName(), new GetRule()); + $this->addAction(ListRules::getName(), new ListRules()); + $this->addAction(DeleteRule::getName(), new DeleteRule()); + $this->addAction(UpdateRuleVerification::getName(), new UpdateRuleVerification()); } }