2019-05-09 18:54:39 +12:00
< ? php
2022-05-24 02:54:50 +12:00
require_once __DIR__ . '/../init.php' ;
2019-10-25 06:53:37 +13:00
2022-04-14 00:39:31 +12:00
use Appwrite\Event\Certificate ;
2024-02-22 22:40:18 +13:00
use Appwrite\Event\Event ;
use Appwrite\Event\Usage ;
2024-03-07 06:34:21 +13:00
use Appwrite\Extend\Exception as AppwriteException ;
2020-06-12 07:36:10 +12:00
use Appwrite\Network\Validator\Origin ;
2024-03-07 06:34:21 +13:00
use Appwrite\Utopia\Request ;
use Appwrite\Utopia\Request\Filters\V16 as RequestV16 ;
use Appwrite\Utopia\Request\Filters\V17 as RequestV17 ;
use Appwrite\Utopia\Response ;
2023-08-23 06:13:37 +12:00
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16 ;
2024-01-17 00:10:07 +13:00
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17 ;
2024-03-07 06:34:21 +13:00
use Appwrite\Utopia\View ;
use Executor\Executor ;
use MaxMind\Db\Reader ;
use Swoole\Http\Request as SwooleRequest ;
use Utopia\App ;
2020-10-30 11:04:53 +13:00
use Utopia\CLI\Console ;
2024-03-07 06:34:21 +13:00
use Utopia\Config\Config ;
2022-05-27 02:46:08 +12:00
use Utopia\Database\Database ;
2021-05-16 21:18:34 +12:00
use Utopia\Database\Document ;
2024-02-25 07:01:00 +13:00
use Utopia\Database\Helpers\ID ;
2024-03-07 06:34:21 +13:00
use Utopia\Database\Query ;
2021-07-26 02:51:04 +12:00
use Utopia\Database\Validator\Authorization ;
2024-03-07 06:34:21 +13:00
use Utopia\Domains\Domain ;
use Utopia\Locale\Locale ;
use Utopia\Logger\Log ;
use Utopia\Logger\Log\User ;
use Utopia\Logger\Logger ;
2022-04-20 21:31:17 +12:00
use Utopia\Validator\Hostname ;
2022-02-11 21:44:04 +13:00
use Utopia\Validator\Text ;
2019-05-09 18:54:39 +12:00
2020-06-29 08:45:36 +12:00
Config :: setParam ( 'domainVerification' , false );
2020-07-01 18:35:57 +12:00
Config :: setParam ( 'cookieDomain' , 'localhost' );
Config :: setParam ( 'cookieSamesite' , Response :: COOKIE_SAMESITE_NONE );
2020-06-30 09:43:34 +12:00
2024-02-22 22:40:18 +13:00
function router ( App $utopia , Database $dbForConsole , callable $getProjectDB , SwooleRequest $swooleRequest , Request $request , Response $response , Event $queueForEvents , Usage $queueForUsage , Reader $geodb )
2023-03-15 08:31:23 +13:00
{
2023-10-03 03:02:48 +13:00
$utopia -> getRoute () ? -> label ( 'error' , __DIR__ . '/../views/general/error.phtml' );
2023-07-28 19:56:07 +12:00
$host = $request -> getHostname () ? ? '' ;
2023-03-14 02:35:34 +13:00
2023-02-28 01:43:20 +13:00
$route = Authorization :: skip (
2023-08-07 01:11:30 +12:00
fn () => $dbForConsole -> find ( 'rules' , [
2023-02-28 01:43:20 +13:00
Query :: equal ( 'domain' , [ $host ]),
Query :: limit ( 1 )
])
)[ 0 ] ? ? null ;
2023-03-15 08:31:23 +13:00
if ( $route === null ) {
2023-10-03 03:02:48 +13:00
if ( $host === App :: getEnv ( '_APP_DOMAIN_FUNCTIONS' , '' )) {
throw new AppwriteException ( AppwriteException :: GENERAL_ACCESS_FORBIDDEN , 'This domain cannot be used for security reasons. Please use any subdomain instead.' );
}
if ( \str_ends_with ( $host , App :: getEnv ( '_APP_DOMAIN_FUNCTIONS' , '' ))) {
throw new AppwriteException ( AppwriteException :: GENERAL_ACCESS_FORBIDDEN , 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.' );
}
if ( App :: getEnv ( '_APP_OPTIONS_ROUTER_PROTECTION' , 'disabled' ) === 'enabled' ) {
if ( $host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL ) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
throw new AppwriteException ( AppwriteException :: GENERAL_ACCESS_FORBIDDEN , 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.' );
}
}
2023-09-06 03:02:07 +12:00
// Act as API - no Proxy logic
2023-10-03 03:02:48 +13:00
$utopia -> getRoute () ? -> label ( 'error' , '' );
2023-09-06 03:02:07 +12:00
return false ;
2023-02-28 01:43:20 +13:00
}
2023-03-11 01:20:24 +13:00
$projectId = $route -> getAttribute ( 'projectId' );
$project = Authorization :: skip (
2023-08-07 01:11:30 +12:00
fn () => $dbForConsole -> getDocument ( 'projects' , $projectId )
2023-03-11 01:20:24 +13:00
);
2023-03-15 08:31:23 +13:00
if ( array_key_exists ( 'proxy' , $project -> getAttribute ( 'services' , []))) {
2023-03-11 01:20:24 +13:00
$status = $project -> getAttribute ( 'services' , [])[ 'proxy' ];
2023-03-15 08:31:23 +13:00
if ( ! $status ) {
2023-03-11 01:20:24 +13:00
throw new AppwriteException ( AppwriteException :: GENERAL_SERVICE_DISABLED );
}
}
2023-03-14 02:35:34 +13:00
// Skip Appwrite Router for ACME challenge. Nessessary for certificate generation
2023-08-07 01:11:30 +12:00
$path = ( $swooleRequest -> server [ 'request_uri' ] ? ? '/' );
2023-03-15 08:31:23 +13:00
if ( \str_starts_with ( $path , '/.well-known/acme-challenge' )) {
2023-03-14 02:35:34 +13:00
return false ;
}
2023-02-28 01:43:20 +13:00
$type = $route -> getAttribute ( 'resourceType' );
2023-03-15 08:31:23 +13:00
if ( $type === 'function' ) {
2023-10-03 03:02:48 +13:00
if ( App :: getEnv ( '_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS' , 'disabled' ) === 'enabled' ) { // Force HTTPS
if ( $request -> getProtocol () !== 'https' ) {
if ( $request -> getMethod () !== Request :: METHOD_GET ) {
throw new AppwriteException ( AppwriteException :: GENERAL_PROTOCOL_UNSUPPORTED , 'Method unsupported over HTTP. Please use HTTPS instead.' );
}
return $response -> redirect ( 'https://' . $request -> getHostname () . $request -> getURI ());
}
}
2023-02-28 01:43:20 +13:00
$functionId = $route -> getAttribute ( 'resourceId' );
$projectId = $route -> getAttribute ( 'projectId' );
2023-08-07 01:11:30 +12:00
$path = ( $swooleRequest -> server [ 'request_uri' ] ? ? '/' );
2023-02-28 01:43:20 +13:00
$query = ( $swooleRequest -> server [ 'query_string' ] ? ? '' );
2023-03-15 08:31:23 +13:00
if ( ! empty ( $query )) {
2023-02-28 01:43:20 +13:00
$path .= '?' . $query ;
}
2024-02-22 22:40:18 +13:00
$body = $swooleRequest -> getContent () ? ? '' ;
$method = $swooleRequest -> server [ 'request_method' ];
2023-10-28 04:25:19 +13:00
$requestHeaders = $request -> getHeaders ();
2023-10-27 21:26:46 +13:00
2024-02-22 22:40:18 +13:00
$project = Authorization :: skip ( fn () => $dbForConsole -> getDocument ( 'projects' , $projectId ));
$dbForProject = $getProjectDB ( $project );
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
if ( $function -> isEmpty () || ! $function -> getAttribute ( 'enabled' )) {
throw new AppwriteException ( AppwriteException :: FUNCTION_NOT_FOUND );
}
$version = $function -> getAttribute ( 'version' , 'v2' );
$runtimes = Config :: getParam ( $version === 'v2' ? 'runtimes-v2' : 'runtimes' , []);
$runtime = ( isset ( $runtimes [ $function -> getAttribute ( 'runtime' , '' )])) ? $runtimes [ $function -> getAttribute ( 'runtime' , '' )] : null ;
if ( \is_null ( $runtime )) {
throw new AppwriteException ( AppwriteException :: FUNCTION_RUNTIME_UNSUPPORTED , 'Runtime "' . $function -> getAttribute ( 'runtime' , '' ) . '" is not supported' );
}
$deployment = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'deployments' , $function -> getAttribute ( 'deployment' , '' )));
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
throw new AppwriteException ( AppwriteException :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
}
if ( $deployment -> isEmpty ()) {
throw new AppwriteException ( AppwriteException :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
}
/** Check if build has completed */
$build = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' )));
if ( $build -> isEmpty ()) {
throw new AppwriteException ( AppwriteException :: BUILD_NOT_FOUND );
}
if ( $build -> getAttribute ( 'status' ) !== 'ready' ) {
throw new AppwriteException ( AppwriteException :: BUILD_NOT_READY );
}
$permissions = $function -> getAttribute ( 'execute' );
2024-03-21 04:49:05 +13:00
if ( ! ( \in_array ( 'any' , $permissions )) && ! ( \in_array ( 'guests' , $permissions ))) {
2024-02-22 22:40:18 +13:00
throw new AppwriteException ( AppwriteException :: USER_UNAUTHORIZED , 'To execute function using domain, execute permissions must include "any" or "guests"' );
}
$headers = \array_merge ([], $requestHeaders );
$headers [ 'x-appwrite-trigger' ] = 'http' ;
$headers [ 'x-appwrite-user-id' ] = '' ;
$headers [ 'x-appwrite-user-jwt' ] = '' ;
$headers [ 'x-appwrite-country-code' ] = '' ;
$headers [ 'x-appwrite-continent-code' ] = '' ;
$headers [ 'x-appwrite-continent-eu' ] = 'false' ;
$ip = $headers [ 'x-real-ip' ] ? ? '' ;
if ( ! empty ( $ip )) {
$record = $geodb -> get ( $ip );
if ( $record ) {
$eu = Config :: getParam ( 'locale-eu' );
$headers [ 'x-appwrite-country-code' ] = $record [ 'country' ][ 'iso_code' ] ? ? '' ;
$headers [ 'x-appwrite-continent-code' ] = $record [ 'continent' ][ 'code' ] ? ? '' ;
$headers [ 'x-appwrite-continent-eu' ] = ( \in_array ( $record [ 'country' ][ 'iso_code' ], $eu )) ? 'true' : 'false' ;
}
}
$headersFiltered = [];
foreach ( $headers as $key => $value ) {
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_REQUEST )) {
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
}
}
$executionId = ID :: unique ();
$execution = new Document ([
'$id' => $executionId ,
'$permissions' => [],
'functionInternalId' => $function -> getInternalId (),
'functionId' => $function -> getId (),
'deploymentInternalId' => $deployment -> getInternalId (),
'deploymentId' => $deployment -> getId (),
'trigger' => 'http' , // http / schedule / event
'status' => 'processing' , // waiting / processing / completed / failed
'responseStatusCode' => 0 ,
'responseHeaders' => [],
'requestPath' => $path ,
'requestMethod' => $method ,
'requestHeaders' => $headersFiltered ,
'errors' => '' ,
'logs' => '' ,
'duration' => 0.0 ,
'search' => implode ( ' ' , [ $functionId , $executionId ]),
2023-02-28 01:43:20 +13:00
]);
2024-02-22 22:40:18 +13:00
$queueForEvents
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executionId' , $execution -> getId ())
-> setContext ( 'function' , $function );
2023-02-28 01:43:20 +13:00
2024-02-22 22:40:18 +13:00
$durationStart = \microtime ( true );
2023-02-28 01:43:20 +13:00
2024-02-22 22:40:18 +13:00
$vars = [];
2023-02-28 01:43:20 +13:00
2024-02-22 22:40:18 +13:00
// V2 vars
if ( $version === 'v2' ) {
$vars = \array_merge ( $vars , [
'APPWRITE_FUNCTION_TRIGGER' => $headers [ 'x-appwrite-trigger' ] ? ? '' ,
'APPWRITE_FUNCTION_DATA' => $body ? ? '' ,
'APPWRITE_FUNCTION_USER_ID' => $headers [ 'x-appwrite-user-id' ] ? ? '' ,
'APPWRITE_FUNCTION_JWT' => $headers [ 'x-appwrite-user-jwt' ] ? ? ''
]);
}
2023-02-28 01:43:20 +13:00
2024-02-22 22:40:18 +13:00
// Shared vars
foreach ( $function -> getAttribute ( 'varsProject' , []) as $var ) {
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
2023-02-28 01:43:20 +13:00
}
2024-02-22 22:40:18 +13:00
// Function vars
foreach ( $function -> getAttribute ( 'vars' , []) as $var ) {
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
2023-02-28 01:43:20 +13:00
}
2024-02-22 22:40:18 +13:00
// Appwrite vars
$vars = \array_merge ( $vars , [
'APPWRITE_FUNCTION_ID' => $functionId ,
'APPWRITE_FUNCTION_NAME' => $function -> getAttribute ( 'name' ),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment -> getId (),
'APPWRITE_FUNCTION_PROJECT_ID' => $project -> getId (),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime [ 'name' ] ? ? '' ,
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime [ 'version' ] ? ? '' ,
]);
2023-02-28 01:43:20 +13:00
2024-02-22 22:40:18 +13:00
/** Execute function */
$executor = new Executor ( App :: getEnv ( '_APP_EXECUTOR_HOST' ));
try {
$version = $function -> getAttribute ( 'version' , 'v2' );
$command = $runtime [ 'startCommand' ];
$command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"' ;
$executionResponse = $executor -> createExecution (
projectId : $project -> getId (),
deploymentId : $deployment -> getId (),
body : \strlen ( $body ) > 0 ? $body : null ,
variables : $vars ,
timeout : $function -> getAttribute ( 'timeout' , 0 ),
image : $runtime [ 'image' ],
source : $build -> getAttribute ( 'path' , '' ),
entrypoint : $deployment -> getAttribute ( 'entrypoint' , '' ),
version : $version ,
path : $path ,
method : $method ,
headers : $headers ,
runtimeEntrypoint : $command ,
requestTimeout : 30
);
$headersFiltered = [];
foreach ( $executionResponse [ 'headers' ] as $key => $value ) {
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_RESPONSE )) {
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
}
2023-08-07 01:42:18 +12:00
}
2024-02-22 22:40:18 +13:00
/** Update execution status */
$status = $executionResponse [ 'statusCode' ] >= 400 ? '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 ( 'duration' , $executionResponse [ 'duration' ]);
} catch ( \Throwable $th ) {
$durationEnd = \microtime ( true );
$execution
-> setAttribute ( 'duration' , $durationEnd - $durationStart )
-> setAttribute ( 'status' , 'failed' )
-> setAttribute ( 'responseStatusCode' , 500 )
-> setAttribute ( 'errors' , $th -> getMessage () . '\nError Code: ' . $th -> getCode ());
Console :: error ( $th -> getMessage ());
} finally {
$queueForUsage
-> addMetric ( METRIC_EXECUTIONS , 1 )
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS ), 1 )
-> addMetric ( METRIC_EXECUTIONS_COMPUTE , ( int )( $execution -> getAttribute ( 'duration' ) * 1000 )) // per project
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE ), ( int )( $execution -> getAttribute ( 'duration' ) * 1000 )) // per function
;
}
if ( $function -> getAttribute ( 'logging' )) {
/** @var Document $execution */
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
2024-02-22 03:51:21 +13:00
}
2024-02-22 22:40:18 +13:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
$headers = [];
foreach (( $executionResponse [ 'headers' ] ? ? []) as $key => $value ) {
$headers [] = [ 'name' => $key , 'value' => $value ];
2023-02-28 01:43:20 +13:00
}
2024-02-22 22:40:18 +13:00
$execution -> setAttribute ( 'responseBody' , $executionResponse [ 'body' ] ? ? '' );
$execution -> setAttribute ( 'responseHeaders' , $headers );
2023-07-31 01:12:55 +12:00
$body = $execution [ 'responseBody' ] ? ? '' ;
2023-02-28 01:43:20 +13:00
2023-08-07 01:11:30 +12:00
$encodingKey = \array_search ( 'x-open-runtimes-encoding' , \array_column ( $execution [ 'responseHeaders' ], 'name' ));
2023-08-11 21:19:05 +12:00
if ( $encodingKey !== false ) {
if (( $execution [ 'responseHeaders' ][ $encodingKey ][ 'value' ] ? ? '' ) === 'base64' ) {
2023-08-07 01:11:30 +12:00
$body = \base64_decode ( $body );
}
2023-02-28 01:43:20 +13:00
}
2024-02-22 22:40:18 +13:00
$contentType = 'text/plain' ;
foreach ( $execution [ 'responseHeaders' ] as $header ) {
if ( \strtolower ( $header [ 'name' ]) === 'content-type' ) {
$contentType = $header [ 'value' ];
}
$response -> setHeader ( $header [ 'name' ], $header [ 'value' ]);
}
2023-08-07 01:42:18 +12:00
$response
-> setContentType ( $contentType )
-> setStatusCode ( $execution [ 'responseStatusCode' ] ? ? 200 )
-> send ( $body );
2023-02-28 01:43:20 +13:00
return true ;
2023-03-15 08:31:23 +13:00
} elseif ( $type === 'api' ) {
2023-09-06 19:34:02 +12:00
$utopia -> getRoute () ? -> label ( 'error' , '' );
2023-03-11 01:20:24 +13:00
return false ;
2023-02-28 01:43:20 +13:00
} else {
2023-07-28 20:27:16 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_SERVER_ERROR , 'Unknown resource type ' . $type );
2023-02-28 01:43:20 +13:00
}
2023-10-03 03:02:48 +13:00
$utopia -> getRoute () ? -> label ( 'error' , '' );
2023-02-28 01:43:20 +13:00
return false ;
}
2022-07-22 18:00:42 +12:00
App :: init ()
2023-08-07 01:42:18 +12:00
-> groups ([ 'api' , 'web' ])
2022-07-22 18:00:42 +12:00
-> inject ( 'utopia' )
2023-02-10 03:27:11 +13:00
-> inject ( 'swooleRequest' )
2022-07-22 18:00:42 +12:00
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'console' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2024-03-05 23:31:15 +13:00
-> inject ( 'getProjectDB' )
2022-07-22 18:00:42 +12:00
-> inject ( 'locale' )
2023-04-17 14:10:17 +12:00
-> inject ( 'localeCodes' )
2022-07-22 18:00:42 +12:00
-> inject ( 'clients' )
2024-03-05 23:31:15 +13:00
-> inject ( 'geodb' )
-> inject ( 'queueForUsage' )
-> inject ( 'queueForEvents' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForCertificates' )
2024-03-05 23:34:04 +13:00
-> action ( function ( App $utopia , SwooleRequest $swooleRequest , Request $request , Response $response , Document $console , Document $project , Database $dbForConsole , callable $getProjectDB , Locale $locale , array $localeCodes , array $clients , Reader $geodb , Usage $queueForUsage , Event $queueForEvents , Certificate $queueForCertificates ) {
2023-02-10 03:27:11 +13:00
/*
* Appwrite Router
*/
2023-07-28 19:56:07 +12:00
$host = $request -> getHostname () ? ? '' ;
2023-02-23 04:07:34 +13:00
$mainDomain = App :: getEnv ( '_APP_DOMAIN' , '' );
// Only run Router when external domain
2023-09-06 03:02:07 +12:00
if ( $host !== $mainDomain ) {
2024-02-22 22:40:18 +13:00
if ( router ( $utopia , $dbForConsole , $getProjectDB , $swooleRequest , $request , $response , $queueForEvents , $queueForUsage , $geodb )) {
2023-02-28 01:43:20 +13:00
return ;
2023-02-21 23:30:16 +13:00
}
}
2023-02-21 23:31:55 +13:00
2022-07-22 18:00:42 +12:00
/*
* Request format
*/
2023-02-20 00:04:12 +13:00
$route = $utopia -> getRoute ();
2022-07-22 18:00:42 +12:00
Request :: setRoute ( $route );
2023-02-21 23:31:55 +13:00
if ( $route === null ) {
2023-08-18 18:55:44 +12:00
return $response -> setStatusCode ( 404 ) -> send ( 'Not Found' );
2023-02-15 02:58:13 +13:00
}
2024-03-08 02:48:36 +13:00
$requestFormat = $request -> getHeader ( 'x-appwrite-response-format' , App :: getEnv ( '_APP_SYSTEM_RESPONSE_FORMAT' , null ));
2022-07-22 18:00:42 +12:00
if ( $requestFormat ) {
2024-03-08 02:48:36 +13:00
if ( version_compare ( $requestFormat , '1.4.0' , '<' )) {
2024-03-08 03:41:20 +13:00
$request -> addFilter ( new RequestV16 ());
2024-03-08 02:48:36 +13:00
}
if ( version_compare ( $requestFormat , '1.5.0' , '<' )) {
2024-03-08 03:41:20 +13:00
$request -> addFilter ( new RequestV17 ());
2022-07-22 18:00:42 +12:00
}
}
2021-05-11 22:47:02 +12:00
2022-07-22 18:00:42 +12:00
$domain = $request -> getHostname ();
$domains = Config :: getParam ( 'domains' , []);
if ( ! array_key_exists ( $domain , $domains )) {
$domain = new Domain ( ! empty ( $domain ) ? $domain : '' );
2021-07-16 09:14:52 +12:00
2022-07-22 18:00:42 +12:00
if ( empty ( $domain -> get ()) || ! $domain -> isKnown () || $domain -> isTest ()) {
$domains [ $domain -> get ()] = false ;
Console :: warning ( $domain -> get () . ' is not a publicly accessible domain. Skipping SSL certificate generation.' );
} elseif ( str_starts_with ( $request -> getURI (), '/.well-known/acme-challenge' )) {
Console :: warning ( 'Skipping SSL certificates generation on ACME challenge.' );
2022-03-29 23:17:56 +13:00
} else {
2022-07-22 18:00:42 +12:00
Authorization :: disable ();
$envDomain = App :: getEnv ( '_APP_DOMAIN' , '' );
$mainDomain = null ;
if ( ! empty ( $envDomain ) && $envDomain !== 'localhost' ) {
$mainDomain = $envDomain ;
} else {
2023-09-06 20:27:21 +12:00
$domainDocument = $dbForConsole -> findOne ( 'rules' , [ Query :: orderAsc ( '$id' )]);
2022-07-22 18:00:42 +12:00
$mainDomain = $domainDocument ? $domainDocument -> getAttribute ( 'domain' ) : $domain -> get ();
}
2021-12-29 00:02:39 +13:00
2022-07-22 18:00:42 +12:00
if ( $mainDomain !== $domain -> get ()) {
Console :: warning ( $domain -> get () . ' is not a main domain. Skipping SSL certificate generation.' );
} else {
2023-09-06 03:09:54 +12:00
$domainDocument = $dbForConsole -> findOne ( 'rules' , [
2022-08-12 11:53:52 +12:00
Query :: equal ( 'domain' , [ $domain -> get ()])
2022-03-29 23:17:56 +13:00
]);
2021-05-11 22:47:02 +12:00
2022-07-22 18:00:42 +12:00
if ( ! $domainDocument ) {
$domainDocument = new Document ([
'domain' => $domain -> get (),
2023-09-06 03:09:54 +12:00
'resourceType' => 'api' ,
'status' => 'verifying' ,
'projectId' => 'console' ,
2023-09-06 20:31:40 +12:00
'projectInternalId' => 'console'
2022-07-22 18:00:42 +12:00
]);
2023-09-06 03:09:54 +12:00
$domainDocument = $dbForConsole -> createDocument ( 'rules' , $domainDocument );
2022-05-12 02:12:38 +12:00
2022-07-22 18:00:42 +12:00
Console :: info ( 'Issuing a TLS certificate for the main domain (' . $domain -> get () . ') in a few seconds...' );
2022-04-26 23:15:00 +12:00
2022-12-14 00:16:12 +13:00
$queueForCertificates
2022-07-22 18:00:42 +12:00
-> setDomain ( $domainDocument )
2023-09-06 03:09:54 +12:00
-> setSkipRenewCheck ( true )
2022-07-22 18:00:42 +12:00
-> trigger ();
}
2022-05-12 22:56:25 +12:00
}
2022-07-22 18:00:42 +12:00
$domains [ $domain -> get ()] = true ;
Authorization :: reset (); // ensure authorization is re-enabled
2021-05-11 22:47:02 +12:00
}
2022-07-22 18:00:42 +12:00
Config :: setParam ( 'domains' , $domains );
}
2022-05-13 02:01:01 +12:00
2022-07-22 18:00:42 +12:00
$localeParam = ( string ) $request -> getParam ( 'locale' , $request -> getHeader ( 'x-appwrite-locale' , '' ));
2023-04-17 14:10:17 +12:00
if ( \in_array ( $localeParam , $localeCodes )) {
2022-07-22 18:00:42 +12:00
$locale -> setDefault ( $localeParam );
2021-05-11 22:47:02 +12:00
}
2022-07-22 18:00:42 +12:00
$referrer = $request -> getReferer ();
$origin = \parse_url ( $request -> getOrigin ( $referrer ), PHP_URL_HOST );
$protocol = \parse_url ( $request -> getOrigin ( $referrer ), PHP_URL_SCHEME );
$port = \parse_url ( $request -> getOrigin ( $referrer ), PHP_URL_PORT );
$refDomainOrigin = 'localhost' ;
$validator = new Hostname ( $clients );
if ( $validator -> isValid ( $origin )) {
$refDomainOrigin = $origin ;
}
$refDomain = ( ! empty ( $protocol ) ? $protocol : $request -> getProtocol ()) . '://' . $refDomainOrigin . ( ! empty ( $port ) ? ':' . $port : '' );
$refDomain = ( ! $route -> getLabel ( 'origin' , false )) // This route is publicly accessible
? $refDomain
: ( ! empty ( $protocol ) ? $protocol : $request -> getProtocol ()) . '://' . $origin . ( ! empty ( $port ) ? ':' . $port : '' );
$selfDomain = new Domain ( $request -> getHostname ());
$endDomain = new Domain (( string ) $origin );
Config :: setParam (
'domainVerification' ,
( $selfDomain -> getRegisterable () === $endDomain -> getRegisterable ()) &&
2023-08-07 01:11:30 +12:00
$endDomain -> getRegisterable () !== ''
2022-07-22 18:00:42 +12:00
);
2023-07-21 22:08:34 +12:00
$isLocalHost = $request -> getHostname () === 'localhost' || $request -> getHostname () === 'localhost:' . $request -> getPort ();
$isIpAddress = filter_var ( $request -> getHostname (), FILTER_VALIDATE_IP ) !== false ;
$isConsoleProject = $project -> getAttribute ( '$id' , '' ) === 'console' ;
$isConsoleRootSession = App :: getEnv ( '_APP_CONSOLE_ROOT_SESSION' , 'disabled' ) === 'enabled' ;
Config :: setParam (
'cookieDomain' ,
$isLocalHost || $isIpAddress
? null
2024-03-07 06:34:21 +13:00
: (
$isConsoleProject && $isConsoleRootSession
2023-07-21 22:08:34 +12:00
? '.' . $selfDomain -> getRegisterable ()
: '.' . $request -> getHostname ()
)
);
2022-07-22 18:00:42 +12:00
/*
* Response format
*/
2024-03-08 02:48:36 +13:00
$responseFormat = $request -> getHeader ( 'x-appwrite-response-format' , App :: getEnv ( '_APP_SYSTEM_RESPONSE_FORMAT' , null ));
2022-07-22 18:00:42 +12:00
if ( $responseFormat ) {
2024-03-08 02:48:36 +13:00
if ( version_compare ( $responseFormat , '1.4.0' , '<' )) {
2024-03-08 03:41:20 +13:00
$response -> addFilter ( new ResponseV16 ());
2024-03-08 02:48:36 +13:00
}
if ( version_compare ( $responseFormat , '1.5.0' , '<' )) {
2024-03-08 03:41:20 +13:00
$response -> addFilter ( new ResponseV17 ());
2019-10-25 06:53:37 +13:00
}
}
2021-08-18 22:20:49 +12:00
2020-12-29 06:03:47 +13:00
/*
2022-07-22 18:00:42 +12:00
* Security Headers
*
* As recommended at :
* @ see https :// www . owasp . org / index . php / List_of_useful_HTTP_headers
*/
if ( App :: getEnv ( '_APP_OPTIONS_FORCE_HTTPS' , 'disabled' ) === 'enabled' ) { // Force HTTPS
2023-11-01 07:31:36 +13:00
if ( $request -> getProtocol () !== 'https' && ( $swooleRequest -> header [ 'host' ] ? ? '' ) !== 'localhost' && ( $swooleRequest -> header [ 'host' ] ? ? '' ) !== APP_HOSTNAME_INTERNAL ) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
2022-07-22 18:00:42 +12:00
if ( $request -> getMethod () !== Request :: METHOD_GET ) {
2023-10-03 03:02:48 +13:00
throw new AppwriteException ( AppwriteException :: GENERAL_PROTOCOL_UNSUPPORTED , 'Method unsupported over HTTP. Please use HTTPS instead.' );
2022-07-22 18:00:42 +12:00
}
return $response -> redirect ( 'https://' . $request -> getHostname () . $request -> getURI ());
2022-06-03 21:12:19 +12:00
}
2022-11-21 16:49:45 +13:00
}
2022-06-01 03:41:12 +12:00
2022-11-21 16:49:45 +13:00
if ( $request -> getProtocol () === 'https' ) {
2022-07-22 18:00:42 +12:00
$response -> addHeader ( 'Strict-Transport-Security' , 'max-age=' . ( 60 * 60 * 24 * 126 )); // 126 days
2020-12-29 06:03:47 +13:00
}
2019-05-09 18:54:39 +12:00
2022-07-22 18:00:42 +12:00
$response
-> addHeader ( 'Server' , 'Appwrite' )
-> addHeader ( 'X-Content-Type-Options' , 'nosniff' )
-> addHeader ( 'Access-Control-Allow-Methods' , 'GET, POST, PUT, PATCH, DELETE' )
2023-12-13 04:39:24 +13:00
-> addHeader ( 'Access-Control-Allow-Headers' , 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent' )
-> addHeader ( 'Access-Control-Expose-Headers' , 'X-Appwrite-Session, X-Fallback-Cookies' )
2022-07-22 18:00:42 +12:00
-> addHeader ( 'Access-Control-Allow-Origin' , $refDomain )
2023-08-07 01:11:30 +12:00
-> addHeader ( 'Access-Control-Allow-Credentials' , 'true' );
2019-05-09 18:54:39 +12:00
2022-07-22 18:00:42 +12:00
/*
* Validate Client Domain - Check to avoid CSRF attack
* Adding Appwrite API domains to allow XDOMAIN communication
* Skip this check for non - web platforms which are not required to send an origin header
*/
$origin = $request -> getOrigin ( $request -> getReferer ( '' ));
$originValidator = new Origin ( \array_merge ( $project -> getAttribute ( 'platforms' , []), $console -> getAttribute ( 'platforms' , [])));
2019-05-09 18:54:39 +12:00
2022-05-31 23:35:59 +12:00
if (
2022-07-22 18:00:42 +12:00
! $originValidator -> isValid ( $origin )
&& \in_array ( $request -> getMethod (), [ Request :: METHOD_POST , Request :: METHOD_PUT , Request :: METHOD_PATCH , Request :: METHOD_DELETE ])
&& $route -> getLabel ( 'origin' , false ) !== '*'
&& empty ( $request -> getHeader ( 'x-appwrite-key' , '' ))
2022-05-31 23:35:59 +12:00
) {
2022-08-14 18:56:12 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_UNKNOWN_ORIGIN , $originValidator -> getDescription ());
2021-07-29 22:28:17 +12:00
}
2022-07-22 18:00:42 +12:00
});
2022-03-02 03:19:47 +13:00
2022-07-22 18:00:42 +12:00
App :: options ()
2023-07-11 20:53:40 +12:00
-> inject ( 'utopia' )
2023-02-28 01:43:20 +13:00
-> inject ( 'swooleRequest' )
2022-07-22 18:00:42 +12:00
-> inject ( 'request' )
-> inject ( 'response' )
2023-02-28 01:43:20 +13:00
-> inject ( 'dbForConsole' )
2024-02-22 22:40:18 +13:00
-> inject ( 'getProjectDB' )
-> inject ( 'queueForEvents' )
-> inject ( 'queueForUsage' )
-> inject ( 'geodb' )
-> action ( function ( App $utopia , SwooleRequest $swooleRequest , Request $request , Response $response , Database $dbForConsole , callable $getProjectDB , Event $queueForEvents , Usage $queueForUsage , Reader $geodb ) {
2023-02-28 01:43:20 +13:00
/*
* Appwrite Router
*/
2023-07-28 19:56:07 +12:00
$host = $request -> getHostname () ? ? '' ;
2023-02-28 01:43:20 +13:00
$mainDomain = App :: getEnv ( '_APP_DOMAIN' , '' );
// Only run Router when external domain
2023-09-06 03:02:07 +12:00
if ( $host !== $mainDomain ) {
2024-02-22 22:40:18 +13:00
if ( router ( $utopia , $dbForConsole , $getProjectDB , $swooleRequest , $request , $response , $queueForEvents , $queueForUsage , $geodb )) {
2023-02-28 01:43:20 +13:00
return ;
}
}
2022-07-22 18:00:42 +12:00
$origin = $request -> getOrigin ();
$response
-> addHeader ( 'Server' , 'Appwrite' )
-> addHeader ( 'Access-Control-Allow-Methods' , 'GET, POST, PUT, PATCH, DELETE' )
2023-12-13 04:39:24 +13:00
-> addHeader ( 'Access-Control-Allow-Headers' , 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent' )
-> addHeader ( 'Access-Control-Expose-Headers' , 'X-Appwrite-Session, X-Fallback-Cookies' )
2022-07-22 18:00:42 +12:00
-> addHeader ( 'Access-Control-Allow-Origin' , $origin )
-> addHeader ( 'Access-Control-Allow-Credentials' , 'true' )
-> noContent ();
});
App :: error ()
-> inject ( 'error' )
-> inject ( 'utopia' )
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'logger' )
2024-02-12 14:18:19 +13:00
-> inject ( 'log' )
-> action ( function ( Throwable $error , App $utopia , Request $request , Response $response , Document $project , ? Logger $logger , Log $log ) {
2022-07-22 18:00:42 +12:00
$version = App :: getEnv ( '_APP_VERSION' , 'UNKNOWN' );
2023-02-20 00:04:12 +13:00
$route = $utopia -> getRoute ();
2023-11-17 07:21:09 +13:00
if ( $error instanceof AppwriteException ) {
2023-11-18 07:34:57 +13:00
$publish = $error -> isPublishable ();
2024-02-12 14:18:19 +13:00
} else {
$publish = $error -> getCode () === 0 || $error -> getCode () >= 500 ;
2023-11-17 07:21:09 +13:00
}
2022-07-22 18:00:42 +12:00
2024-02-12 14:18:19 +13:00
if ( $logger && ( $publish || $error -> getCode () === 0 )) {
try {
/** @var Utopia\Database\Document $user */
$user = $utopia -> getResource ( 'user' );
} catch ( \Throwable $th ) {
// All good, user is optional information for logger
}
2022-07-22 18:00:42 +12:00
2024-02-12 14:18:19 +13:00
if ( isset ( $user ) && ! $user -> isEmpty ()) {
$log -> setUser ( new User ( $user -> getId ()));
2022-07-22 18:00:42 +12:00
}
2024-02-12 14:18:19 +13:00
$log -> setNamespace ( " http " );
$log -> setServer ( \gethostname ());
$log -> setVersion ( $version );
$log -> setType ( Log :: TYPE_ERROR );
$log -> setMessage ( $error -> getMessage ());
$log -> addTag ( 'database' , $project -> getAttribute ( 'database' , 'console' ));
$log -> addTag ( 'method' , $route -> getMethod ());
$log -> addTag ( 'url' , $route -> getPath ());
$log -> addTag ( 'verboseType' , get_class ( $error ));
$log -> addTag ( 'code' , $error -> getCode ());
$log -> addTag ( 'projectId' , $project -> getId ());
$log -> addTag ( 'hostname' , $request -> getHostname ());
$log -> addTag ( 'locale' , ( string ) $request -> getParam ( 'locale' , $request -> getHeader ( 'x-appwrite-locale' , '' )));
$log -> addExtra ( 'file' , $error -> getFile ());
$log -> addExtra ( 'line' , $error -> getLine ());
$log -> addExtra ( 'trace' , $error -> getTraceAsString ());
$log -> addExtra ( 'detailedTrace' , $error -> getTrace ());
$log -> addExtra ( 'roles' , Authorization :: getRoles ());
$action = $route -> getLabel ( " sdk.namespace " , " UNKNOWN_NAMESPACE " ) . '.' . $route -> getLabel ( " sdk.method " , " UNKNOWN_METHOD " );
$log -> setAction ( $action );
$isProduction = App :: getEnv ( '_APP_ENV' , 'development' ) === 'production' ;
$log -> setEnvironment ( $isProduction ? Log :: ENVIRONMENT_PRODUCTION : Log :: ENVIRONMENT_STAGING );
$responseCode = $logger -> addLog ( $log );
Console :: info ( 'Log pushed with status code: ' . $responseCode );
2022-07-22 18:00:42 +12:00
}
$code = $error -> getCode ();
$message = $error -> getMessage ();
$file = $error -> getFile ();
$line = $error -> getLine ();
$trace = $error -> getTrace ();
if ( php_sapi_name () === 'cli' ) {
Console :: error ( '[Error] Timestamp: ' . date ( 'c' , time ()));
if ( $route ) {
Console :: error ( '[Error] Method: ' . $route -> getMethod ());
Console :: error ( '[Error] URL: ' . $route -> getPath ());
}
Console :: error ( '[Error] Type: ' . get_class ( $error ));
Console :: error ( '[Error] Message: ' . $message );
Console :: error ( '[Error] File: ' . $file );
Console :: error ( '[Error] Line: ' . $line );
}
/** Handle Utopia Errors */
if ( $error instanceof Utopia\Exception ) {
2022-08-09 02:44:07 +12:00
$error = new AppwriteException ( AppwriteException :: GENERAL_UNKNOWN , $message , $code , $error );
2022-07-22 18:00:42 +12:00
switch ( $code ) {
case 400 :
$error -> setType ( AppwriteException :: GENERAL_ARGUMENT_INVALID );
break ;
case 404 :
$error -> setType ( AppwriteException :: GENERAL_ROUTE_NOT_FOUND );
break ;
}
2023-01-20 13:36:17 +13:00
} elseif ( $error instanceof Utopia\Database\Exception\Conflict ) {
2023-10-19 15:32:45 +13:00
$error = new AppwriteException ( AppwriteException :: DOCUMENT_UPDATE_CONFLICT , previous : $error );
$code = $error -> getCode ();
$message = $error -> getMessage ();
} elseif ( $error instanceof Utopia\Database\Exception\Timeout ) {
2023-10-19 17:12:05 +13:00
$error = new AppwriteException ( AppwriteException :: DATABASE_TIMEOUT , previous : $error );
2023-01-20 13:36:17 +13:00
$code = $error -> getCode ();
$message = $error -> getMessage ();
2022-07-22 18:00:42 +12:00
}
/** Wrap all exceptions inside Appwrite\Extend\Exception */
if ( ! ( $error instanceof AppwriteException )) {
2022-08-09 02:44:07 +12:00
$error = new AppwriteException ( AppwriteException :: GENERAL_UNKNOWN , $message , $code , $error );
2022-07-22 18:00:42 +12:00
}
switch ( $code ) { // Don't show 500 errors!
case 400 : // Error allowed publicly
case 401 : // Error allowed publicly
case 402 : // Error allowed publicly
case 403 : // Error allowed publicly
case 404 : // Error allowed publicly
2023-10-19 17:06:57 +13:00
case 408 : // Error allowed publicly
2022-07-22 18:00:42 +12:00
case 409 : // Error allowed publicly
case 412 : // Error allowed publicly
case 416 : // Error allowed publicly
case 429 : // Error allowed publicly
2024-01-31 19:53:45 +13:00
case 451 : // Error allowed publicly
2022-07-22 18:00:42 +12:00
case 501 : // Error allowed publicly
case 503 : // Error allowed publicly
2022-02-13 22:12:16 +13:00
break ;
2022-07-22 18:00:42 +12:00
default :
$code = 500 ; // All other errors get the generic 500 server error status code
$message = 'Server Error' ;
2022-02-13 22:12:16 +13:00
}
2019-10-25 06:53:37 +13:00
2022-07-22 18:00:42 +12:00
//$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised
$type = $error -> getType ();
$output = (( App :: isDevelopment ())) ? [
'message' => $message ,
'code' => $code ,
'file' => $file ,
'line' => $line ,
2024-01-24 21:37:28 +13:00
'trace' => \json_encode ( $trace , JSON_UNESCAPED_UNICODE ) === false ? [] : $trace , // check for failing encode
2022-07-22 18:00:42 +12:00
'version' => $version ,
'type' => $type ,
] : [
'message' => $message ,
'code' => $code ,
'version' => $version ,
'type' => $type ,
];
$response
-> addHeader ( 'Cache-Control' , 'no-cache, no-store, must-revalidate' )
-> addHeader ( 'Expires' , '0' )
-> addHeader ( 'Pragma' , 'no-cache' )
2023-08-07 01:11:30 +12:00
-> setStatusCode ( $code );
2019-10-25 06:53:37 +13:00
2022-07-22 18:00:42 +12:00
$template = ( $route ) ? $route -> getLabel ( 'error' , null ) : null ;
if ( $template ) {
2022-11-18 01:37:59 +13:00
$layout = new View ( $template );
2022-07-22 18:00:42 +12:00
2022-11-18 01:37:59 +13:00
$layout
-> setParam ( 'title' , $project -> getAttribute ( 'name' ) . ' - Error' )
2022-07-22 18:00:42 +12:00
-> setParam ( 'development' , App :: isDevelopment ())
-> setParam ( 'projectName' , $project -> getAttribute ( 'name' ))
-> setParam ( 'projectURL' , $project -> getAttribute ( 'url' ))
2024-02-25 07:12:27 +13:00
-> setParam ( 'message' , $output [ 'message' ] ? ? '' )
-> setParam ( 'type' , $output [ 'type' ] ? ? '' )
-> setParam ( 'code' , $output [ 'code' ] ? ? '' )
-> setParam ( 'trace' , $output [ 'trace' ] ? ? []);
2022-07-22 18:00:42 +12:00
$response -> html ( $layout -> render ());
}
2019-10-25 06:53:37 +13:00
2022-07-22 18:00:42 +12:00
$response -> dynamic (
new Document ( $output ),
$utopia -> isDevelopment () ? Response :: MODEL_ERROR_DEV : Response :: MODEL_ERROR
);
});
2019-10-25 06:53:37 +13:00
2020-06-29 05:31:21 +12:00
App :: get ( '/robots.txt' )
2019-10-25 06:53:37 +13:00
-> desc ( 'Robots.txt File' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
2020-12-27 01:19:46 +13:00
-> inject ( 'response' )
2022-05-27 02:46:08 +12:00
-> action ( function ( Response $response ) {
2022-05-31 23:35:59 +12:00
$template = new View ( __DIR__ . '/../views/general/robots.phtml' );
2020-06-30 09:43:34 +12:00
$response -> text ( $template -> render ( false ));
2020-12-27 01:19:46 +13:00
});
2019-10-25 06:53:37 +13:00
2020-06-29 05:31:21 +12:00
App :: get ( '/humans.txt' )
2019-10-25 06:53:37 +13:00
-> desc ( 'Humans.txt File' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
2020-12-27 01:19:46 +13:00
-> inject ( 'response' )
2022-05-27 02:46:08 +12:00
-> action ( function ( Response $response ) {
2022-05-31 23:35:59 +12:00
$template = new View ( __DIR__ . '/../views/general/humans.phtml' );
2020-06-30 09:43:34 +12:00
$response -> text ( $template -> render ( false ));
2020-12-27 01:19:46 +13:00
});
2019-10-25 06:53:37 +13:00
2023-06-02 23:18:34 +12:00
App :: get ( '/.well-known/acme-challenge/*' )
2020-02-19 11:13:18 +13:00
-> desc ( 'SSL Verification' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
2020-12-27 01:19:46 +13:00
-> inject ( 'request' )
-> inject ( 'response' )
2022-05-27 02:46:08 +12:00
-> action ( function ( Request $request , Response $response ) {
2022-02-11 21:44:04 +13:00
$uriChunks = \explode ( '/' , $request -> getURI ());
$token = $uriChunks [ \count ( $uriChunks ) - 1 ];
2023-03-02 01:00:36 +13:00
$validator = new Text ( 100 , allowList : [
2022-02-11 21:44:04 +13:00
... Text :: NUMBERS ,
... Text :: ALPHABET_LOWER ,
... Text :: ALPHABET_UPPER ,
'-' ,
'_'
]);
2022-02-01 04:04:30 +13:00
2022-02-11 21:44:04 +13:00
if ( ! $validator -> isValid ( $token ) || \count ( $uriChunks ) !== 4 ) {
2022-08-09 02:44:07 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_ARGUMENT_INVALID , 'Invalid challenge token.' );
2022-02-01 04:04:30 +13:00
}
2020-06-30 09:43:34 +12:00
$base = \realpath ( APP_STORAGE_CERTIFICATES );
2022-05-24 02:54:50 +12:00
$absolute = \realpath ( $base . '/.well-known/acme-challenge/' . $token );
2020-02-23 21:55:57 +13:00
2020-10-28 08:46:15 +13:00
if ( ! $base ) {
2022-08-09 02:44:07 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_SERVER_ERROR , 'Storage error' );
2020-06-30 09:43:34 +12:00
}
2020-02-19 11:13:18 +13:00
2020-10-28 08:46:15 +13:00
if ( ! $absolute ) {
2022-08-09 02:44:07 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_ROUTE_NOT_FOUND , 'Unknown path' );
2020-06-30 09:43:34 +12:00
}
2020-02-23 21:55:57 +13:00
2020-10-28 08:46:15 +13:00
if ( ! \substr ( $absolute , 0 , \strlen ( $base )) === $base ) {
2022-08-09 02:44:07 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_UNAUTHORIZED_SCOPE , 'Invalid path' );
2020-06-30 09:43:34 +12:00
}
2020-02-24 06:45:51 +13:00
2020-10-28 08:46:15 +13:00
if ( ! \file_exists ( $absolute )) {
2022-08-09 02:44:07 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_ROUTE_NOT_FOUND , 'Unknown path' );
2020-06-30 09:43:34 +12:00
}
2020-02-23 21:55:57 +13:00
2020-06-30 09:43:34 +12:00
$content = @ \file_get_contents ( $absolute );
2020-02-19 11:13:18 +13:00
2020-10-28 08:46:15 +13:00
if ( ! $content ) {
2022-08-09 02:44:07 +12:00
throw new AppwriteException ( AppwriteException :: GENERAL_SERVER_ERROR , 'Failed to get contents' );
2020-02-19 11:13:18 +13:00
}
2020-06-30 09:43:34 +12:00
$response -> text ( $content );
2020-12-27 01:19:46 +13:00
});
2020-02-19 11:13:18 +13:00
2020-07-29 19:29:34 +12:00
include_once __DIR__ . '/shared/api.php' ;
2022-11-24 20:53:52 +13:00
include_once __DIR__ . '/shared/api/auth.php' ;
2020-06-26 07:53:36 +12:00
2023-02-16 03:50:18 +13:00
App :: wildcard ()
2023-08-07 01:11:30 +12:00
-> groups ([ 'api' ])
2023-09-07 16:57:23 +12:00
-> label ( 'scope' , 'global' )
2023-02-21 23:31:55 +13:00
-> action ( function () {
2023-02-16 03:50:18 +13:00
throw new AppwriteException ( AppwriteException :: GENERAL_ROUTE_NOT_FOUND );
});
2020-10-28 08:46:15 +13:00
foreach ( Config :: getParam ( 'services' , []) as $service ) {
2020-06-26 18:14:54 +12:00
include_once $service [ 'controller' ];
2021-05-12 17:17:34 +12:00
}