2020-06-26 21:54:37 +12:00
< ? php
2022-05-24 02:54:50 +12:00
require_once __DIR__ . '/../vendor/autoload.php' ;
2020-06-26 21:54:37 +12:00
2024-03-07 06:34:21 +13:00
use Appwrite\Utopia\Request ;
2020-10-30 02:07:56 +13:00
use Appwrite\Utopia\Response ;
2024-02-23 00:51:19 +13:00
use Swoole\Constant ;
2020-06-26 21:54:37 +12:00
use Swoole\Http\Request as SwooleRequest ;
use Swoole\Http\Response as SwooleResponse ;
2024-03-07 06:34:21 +13:00
use Swoole\Http\Server ;
use Swoole\Process ;
use Utopia\Abuse\Adapters\TimeLimit ;
2020-06-27 00:27:58 +12:00
use Utopia\App ;
2024-03-07 06:34:21 +13:00
use Utopia\Audit\Audit ;
2020-06-27 00:27:58 +12:00
use Utopia\CLI\Console ;
2021-05-04 22:24:08 +12:00
use Utopia\Config\Config ;
2024-03-07 06:34:21 +13:00
use Utopia\Database\Database ;
use Utopia\Database\Document ;
2022-12-15 04:42:25 +13:00
use Utopia\Database\Helpers\ID ;
2022-12-15 05:04:06 +13:00
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2021-07-26 02:51:04 +12:00
use Utopia\Database\Validator\Authorization ;
2022-01-01 02:40:14 +13:00
use Utopia\Logger\Log ;
use Utopia\Logger\Log\User ;
2022-10-16 03:14:17 +13:00
use Utopia\Pools\Group ;
2024-03-07 06:34:21 +13:00
use Utopia\Swoole\Files ;
2024-04-02 00:02:47 +13:00
use Utopia\System\System ;
2020-06-26 21:54:37 +12:00
2024-02-23 00:51:19 +13:00
$http = new Server (
host : " 0.0.0.0 " ,
2024-04-02 00:02:47 +13:00
port : System :: getEnv ( 'PORT' , 80 ),
2024-02-23 00:51:19 +13:00
mode : SWOOLE_PROCESS ,
);
2020-07-01 20:55:14 +12:00
2024-05-09 21:41:02 +12:00
$payloadSize = 12 * ( 1024 * 1024 ); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
2024-04-02 00:02:47 +13:00
$workerNumber = swoole_cpu_num () * intval ( System :: getEnv ( '_APP_WORKER_PER_CORE' , 6 ));
2022-09-30 23:32:58 +13:00
2020-06-27 00:27:58 +12:00
$http
2020-06-26 21:54:37 +12:00
-> set ([
2022-03-01 00:05:11 +13:00
'worker_num' => $workerNumber ,
2020-06-26 21:54:37 +12:00
'open_http2_protocol' => true ,
2020-07-02 18:44:16 +12:00
'http_compression' => true ,
'http_compression_level' => 6 ,
2020-07-20 03:17:57 +12:00
'package_max_length' => $payloadSize ,
2021-05-25 22:19:49 +12:00
'buffer_output_size' => $payloadSize ,
2022-09-13 02:08:29 +12:00
]);
2020-06-26 21:54:37 +12:00
2024-02-23 00:51:19 +13:00
$http -> on ( Constant :: EVENT_WORKER_START , function ( $server , $workerId ) {
2022-05-24 02:54:50 +12:00
Console :: success ( 'Worker ' . ++ $workerId . ' started successfully' );
2020-06-26 21:54:37 +12:00
});
2024-02-23 00:51:19 +13:00
$http -> on ( Constant :: EVENT_BEFORE_RELOAD , function ( $server , $workerId ) {
2020-06-26 21:54:37 +12:00
Console :: success ( 'Starting reload...' );
});
2024-02-23 00:51:19 +13:00
$http -> on ( Constant :: EVENT_AFTER_RELOAD , function ( $server , $workerId ) {
2020-06-26 21:54:37 +12:00
Console :: success ( 'Reload completed...' );
});
2022-10-12 03:36:39 +13:00
Files :: load ( __DIR__ . '/../console' );
2021-05-07 09:35:05 +12:00
include __DIR__ . '/controllers/general.php' ;
2020-12-30 07:42:03 +13:00
2024-02-23 00:51:19 +13:00
$http -> on ( Constant :: EVENT_START , function ( Server $http ) use ( $payloadSize , $register ) {
2021-05-04 22:24:08 +12:00
$app = new App ( 'UTC' );
2022-05-24 02:54:50 +12:00
go ( function () use ( $register , $app ) {
2023-08-04 17:59:23 +12:00
$pools = $register -> get ( 'pools' );
/** @var Group $pools */
App :: setResource ( 'pools' , fn () => $pools );
2022-08-03 06:56:45 +12:00
2021-07-21 06:59:58 +12:00
// wait for database to be ready
$attempts = 0 ;
2021-07-21 07:11:54 +12:00
$max = 10 ;
$sleep = 1 ;
2021-07-21 06:59:58 +12:00
do {
try {
$attempts ++ ;
2023-08-04 17:59:23 +12:00
$dbForConsole = $app -> getResource ( 'dbForConsole' );
/** @var Utopia\Database\Database $dbForConsole */
2021-07-21 06:59:58 +12:00
break ; // leave the do-while if successful
2024-02-08 14:17:54 +13:00
} catch ( \Throwable $e ) {
2021-07-21 06:59:58 +12:00
Console :: warning ( " Database not ready. Retrying connection ( { $attempts } )... " );
2021-07-21 07:11:54 +12:00
if ( $attempts >= $max ) {
2022-05-24 02:54:50 +12:00
throw new \Exception ( 'Failed to connect to database: ' . $e -> getMessage ());
2021-07-21 06:59:58 +12:00
}
2021-07-21 07:11:54 +12:00
sleep ( $sleep );
2021-07-21 06:59:58 +12:00
}
2021-07-21 07:11:54 +12:00
} while ( $attempts < $max );
2021-07-05 03:14:39 +12:00
2021-12-28 01:45:23 +13:00
Console :: success ( '[Setup] - Server database init started...' );
2022-09-13 02:09:28 +12:00
2022-10-16 18:58:25 +13:00
try {
2021-12-28 01:45:23 +13:00
Console :: success ( '[Setup] - Creating database: appwrite...' );
2022-11-01 01:02:51 +13:00
$dbForConsole -> create ();
2024-02-08 14:17:54 +13:00
} catch ( \Throwable $e ) {
2021-12-28 01:45:23 +13:00
Console :: success ( '[Setup] - Skip: metadata table already exists' );
}
2022-05-24 02:54:50 +12:00
if ( $dbForConsole -> getCollection ( Audit :: COLLECTION ) -> isEmpty ()) {
2021-07-05 03:14:39 +12:00
$audit = new Audit ( $dbForConsole );
$audit -> setup ();
2021-12-28 01:45:23 +13:00
}
2021-07-05 03:14:39 +12:00
2021-12-28 01:45:23 +13:00
if ( $dbForConsole -> getCollection ( TimeLimit :: COLLECTION ) -> isEmpty ()) {
2021-07-05 03:14:39 +12:00
$adapter = new TimeLimit ( " " , 0 , 1 , $dbForConsole );
$adapter -> setup ();
2021-12-28 01:45:23 +13:00
}
2021-07-05 03:14:39 +12:00
2023-06-15 12:34:12 +12:00
/** @var array $collections */
$collections = Config :: getParam ( 'collections' , []);
$consoleCollections = $collections [ 'console' ];
foreach ( $consoleCollections as $key => $collection ) {
2022-05-24 02:54:50 +12:00
if (( $collection [ '$collection' ] ? ? '' ) !== Database :: METADATA ) {
2022-01-10 19:31:33 +13:00
continue ;
}
2022-05-24 02:54:50 +12:00
if ( ! $dbForConsole -> getCollection ( $key ) -> isEmpty ()) {
2022-01-10 19:31:33 +13:00
continue ;
}
2022-06-23 09:11:42 +12:00
2022-01-10 19:31:33 +13:00
Console :: success ( '[Setup] - Creating collection: ' . $collection [ '$id' ] . '...' );
2021-07-05 03:14:39 +12:00
2022-01-10 19:31:33 +13:00
$attributes = [];
$indexes = [];
foreach ( $collection [ 'attributes' ] as $attribute ) {
$attributes [] = new Document ([
2022-08-14 22:33:36 +12:00
'$id' => ID :: custom ( $attribute [ '$id' ]),
2022-01-10 19:31:33 +13:00
'type' => $attribute [ 'type' ],
'size' => $attribute [ 'size' ],
'required' => $attribute [ 'required' ],
'signed' => $attribute [ 'signed' ],
'array' => $attribute [ 'array' ],
'filters' => $attribute [ 'filters' ],
2022-03-27 21:01:50 +13:00
'default' => $attribute [ 'default' ] ? ? null ,
'format' => $attribute [ 'format' ] ? ? ''
2022-01-10 19:31:33 +13:00
]);
}
2021-07-05 03:14:39 +12:00
2022-01-10 19:31:33 +13:00
foreach ( $collection [ 'indexes' ] as $index ) {
$indexes [] = new Document ([
2022-08-14 22:33:36 +12:00
'$id' => ID :: custom ( $index [ '$id' ]),
2022-01-10 19:31:33 +13:00
'type' => $index [ 'type' ],
'attributes' => $index [ 'attributes' ],
'lengths' => $index [ 'lengths' ],
'orders' => $index [ 'orders' ],
]);
2021-05-04 22:24:08 +12:00
}
2021-07-05 00:05:46 +12:00
2022-08-04 12:08:16 +12:00
$dbForConsole -> createCollection ( $key , $attributes , $indexes );
2022-01-10 19:31:33 +13:00
}
2023-12-15 02:32:06 +13:00
if ( $dbForConsole -> getDocument ( 'buckets' , 'default' ) -> isEmpty () && ! $dbForConsole -> exists ( $dbForConsole -> getDatabase (), 'bucket_1' )) {
2021-12-23 21:58:11 +13:00
Console :: success ( '[Setup] - Creating default bucket...' );
$dbForConsole -> createDocument ( 'buckets' , new Document ([
2022-08-14 22:33:36 +12:00
'$id' => ID :: custom ( 'default' ),
'$collection' => ID :: custom ( 'buckets' ),
2021-12-23 21:58:11 +13:00
'name' => 'Default' ,
2024-04-02 00:02:47 +13:00
'maximumFileSize' => ( int ) System :: getEnv ( '_APP_STORAGE_LIMIT' , 0 ), // 10MB
2021-12-23 21:58:11 +13:00
'allowedFileExtensions' => [],
'enabled' => true ,
2022-08-31 14:29:06 +12:00
'compression' => 'gzip' ,
2021-12-23 21:58:11 +13:00
'encryption' => true ,
2022-01-16 20:55:55 +13:00
'antivirus' => true ,
2022-08-14 17:21:33 +12:00
'fileSecurity' => true ,
2022-08-02 21:19:15 +12:00
'$permissions' => [
2022-08-24 20:27:27 +12:00
Permission :: create ( Role :: any ()),
2022-08-14 17:21:11 +12:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-02 21:19:15 +12:00
],
2021-12-23 21:58:11 +13:00
'search' => 'buckets Default' ,
]));
2022-02-28 20:54:19 +13:00
$bucket = $dbForConsole -> getDocument ( 'buckets' , 'default' );
2022-05-24 02:54:50 +12:00
2022-01-07 23:02:38 +13:00
Console :: success ( '[Setup] - Creating files collection for default bucket...' );
2023-06-15 12:34:12 +12:00
$files = $collections [ 'buckets' ][ 'files' ] ? ? [];
2022-05-24 02:54:50 +12:00
if ( empty ( $files )) {
2022-01-07 23:02:38 +13:00
throw new Exception ( 'Files collection is not configured.' );
}
2020-12-30 07:42:03 +13:00
2021-12-28 01:45:23 +13:00
$attributes = [];
$indexes = [];
2020-07-09 03:08:14 +12:00
2022-01-07 23:02:38 +13:00
foreach ( $files [ 'attributes' ] as $attribute ) {
2021-12-28 01:45:23 +13:00
$attributes [] = new Document ([
2022-08-14 22:33:36 +12:00
'$id' => ID :: custom ( $attribute [ '$id' ]),
2021-12-28 01:45:23 +13:00
'type' => $attribute [ 'type' ],
'size' => $attribute [ 'size' ],
'required' => $attribute [ 'required' ],
'signed' => $attribute [ 'signed' ],
'array' => $attribute [ 'array' ],
'filters' => $attribute [ 'filters' ],
2022-03-27 21:01:50 +13:00
'default' => $attribute [ 'default' ] ? ? null ,
'format' => $attribute [ 'format' ] ? ? ''
2021-12-28 01:45:23 +13:00
]);
}
2022-05-24 02:54:50 +12:00
2022-01-07 23:02:38 +13:00
foreach ( $files [ 'indexes' ] as $index ) {
2021-12-28 01:45:23 +13:00
$indexes [] = new Document ([
2022-08-14 22:33:36 +12:00
'$id' => ID :: custom ( $index [ '$id' ]),
2021-12-28 01:45:23 +13:00
'type' => $index [ 'type' ],
'attributes' => $index [ 'attributes' ],
'lengths' => $index [ 'lengths' ],
'orders' => $index [ 'orders' ],
]);
2021-12-23 22:33:44 +13:00
}
2022-05-24 02:54:50 +12:00
2022-02-28 20:54:19 +13:00
$dbForConsole -> createCollection ( 'bucket_' . $bucket -> getInternalId (), $attributes , $indexes );
2022-01-10 19:31:33 +13:00
}
2021-12-28 01:45:23 +13:00
2022-10-16 03:14:17 +13:00
$pools -> reclaim ();
2022-07-10 01:41:14 +12:00
2022-01-10 19:31:33 +13:00
Console :: success ( '[Setup] - Server database init completed...' );
2021-07-05 03:14:39 +12:00
});
2021-05-04 22:24:08 +12:00
2022-05-24 02:54:50 +12:00
Console :: success ( 'Server started successfully (max payload is ' . number_format ( $payloadSize ) . ' bytes)' );
2020-07-09 03:08:14 +12:00
Console :: info ( " Master pid { $http -> master_pid } , manager pid { $http -> manager_pid } " );
2020-06-27 00:27:58 +12:00
// listen ctrl + c
Process :: signal ( 2 , function () use ( $http ) {
2020-07-09 03:08:14 +12:00
Console :: log ( 'Stop by Ctrl+C' );
2020-06-27 00:27:58 +12:00
$http -> shutdown ();
});
2020-06-26 21:54:37 +12:00
});
2021-06-12 04:09:46 +12:00
$http -> on ( 'request' , function ( SwooleRequest $swooleRequest , SwooleResponse $swooleResponse ) use ( $register ) {
2023-08-04 17:59:23 +12:00
App :: setResource ( 'swooleRequest' , fn () => $swooleRequest );
App :: setResource ( 'swooleResponse' , fn () => $swooleResponse );
2023-02-10 03:27:11 +13:00
2020-07-01 20:55:14 +12:00
$request = new Request ( $swooleRequest );
$response = new Response ( $swooleResponse );
2022-05-24 02:54:50 +12:00
if ( Files :: isFileLoaded ( $request -> getURI ())) {
2020-07-11 16:16:24 +12:00
$time = ( 60 * 60 * 24 * 365 * 2 ); // 45 days cache
2020-07-07 07:29:13 +12:00
$response
-> setContentType ( Files :: getFileMimeType ( $request -> getURI ()))
2022-05-24 02:54:50 +12:00
-> addHeader ( 'Cache-Control' , 'public, max-age=' . $time )
-> addHeader ( 'Expires' , \date ( 'D, d M Y H:i:s' , \time () + $time ) . ' GMT' ) // 45 days cache
2022-09-13 02:08:29 +12:00
-> send ( Files :: getFileContents ( $request -> getURI ()));
2020-07-07 07:29:13 +12:00
return ;
}
2021-06-28 19:19:33 +12:00
$app = new App ( 'UTC' );
2022-10-16 03:14:17 +13:00
$pools = $register -> get ( 'pools' );
2023-08-04 17:59:23 +12:00
App :: setResource ( 'pools' , fn () => $pools );
2021-06-12 04:09:46 +12:00
2020-06-27 00:27:58 +12:00
try {
2020-11-19 19:56:14 +13:00
Authorization :: cleanRoles ();
2022-08-19 16:04:33 +12:00
Authorization :: setRole ( Role :: any () -> toString ());
2020-11-19 19:56:14 +13:00
2020-07-01 20:55:14 +12:00
$app -> run ( $request , $response );
2020-06-27 00:27:58 +12:00
} catch ( \Throwable $th ) {
2024-04-02 00:02:47 +13:00
$version = System :: getEnv ( '_APP_VERSION' , 'UNKNOWN' );
2021-12-22 04:01:40 +13:00
$logger = $app -> getResource ( " logger " );
2022-05-24 02:54:50 +12:00
if ( $logger ) {
2021-12-27 23:35:51 +13:00
try {
2022-01-19 00:05:04 +13:00
/** @var Utopia\Database\Document $user */
2021-12-27 23:35:51 +13:00
$user = $app -> getResource ( 'user' );
2022-05-24 02:54:50 +12:00
} catch ( \Throwable $_th ) {
2021-12-27 23:35:51 +13:00
// All good, user is optional information for logger
}
2023-02-20 00:04:12 +13:00
$route = $app -> getRoute ();
2021-12-22 04:01:40 +13:00
2024-02-12 14:18:19 +13:00
$log = $app -> getResource ( " log " );
2021-12-22 04:01:40 +13:00
2022-05-24 02:54:50 +12:00
if ( isset ( $user ) && ! $user -> isEmpty ()) {
2021-12-22 04:01:40 +13:00
$log -> setUser ( new User ( $user -> getId ()));
}
$log -> setNamespace ( " http " );
$log -> setServer ( \gethostname ());
$log -> setVersion ( $version );
$log -> setType ( Log :: TYPE_ERROR );
$log -> setMessage ( $th -> getMessage ());
$log -> addTag ( 'method' , $route -> getMethod ());
2022-05-24 02:54:50 +12:00
$log -> addTag ( 'url' , $route -> getPath ());
2021-12-22 04:01:40 +13:00
$log -> addTag ( 'verboseType' , get_class ( $th ));
$log -> addTag ( 'code' , $th -> getCode ());
2021-12-22 04:21:30 +13:00
// $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant
2021-12-22 04:01:40 +13:00
$log -> addTag ( 'hostname' , $request -> getHostname ());
$log -> addTag ( 'locale' , ( string ) $request -> getParam ( 'locale' , $request -> getHeader ( 'x-appwrite-locale' , '' )));
$log -> addExtra ( 'file' , $th -> getFile ());
$log -> addExtra ( 'line' , $th -> getLine ());
$log -> addExtra ( 'trace' , $th -> getTraceAsString ());
2022-03-18 23:05:38 +13:00
$log -> addExtra ( 'detailedTrace' , $th -> getTrace ());
2023-03-16 22:11:04 +13:00
$log -> addExtra ( 'roles' , Authorization :: getRoles ());
2021-12-22 04:01:40 +13:00
$action = $route -> getLabel ( " sdk.namespace " , " UNKNOWN_NAMESPACE " ) . '.' . $route -> getLabel ( " sdk.method " , " UNKNOWN_METHOD " );
$log -> setAction ( $action );
2024-04-02 00:02:47 +13:00
$isProduction = System :: getEnv ( '_APP_ENV' , 'development' ) === 'production' ;
2021-12-22 04:01:40 +13:00
$log -> setEnvironment ( $isProduction ? Log :: ENVIRONMENT_PRODUCTION : Log :: ENVIRONMENT_STAGING );
$responseCode = $logger -> addLog ( $log );
2022-05-24 02:54:50 +12:00
Console :: info ( 'Log pushed with status code: ' . $responseCode );
2021-12-22 04:01:40 +13:00
}
2022-05-24 02:54:50 +12:00
Console :: error ( '[Error] Type: ' . get_class ( $th ));
Console :: error ( '[Error] Message: ' . $th -> getMessage ());
Console :: error ( '[Error] File: ' . $th -> getFile ());
Console :: error ( '[Error] Line: ' . $th -> getLine ());
2020-10-26 02:48:04 +13:00
2021-12-21 23:12:33 +13:00
$swooleResponse -> setStatusCode ( 500 );
2021-12-22 04:01:40 +13:00
$output = (( App :: isDevelopment ())) ? [
2022-05-24 02:54:50 +12:00
'message' => 'Error: ' . $th -> getMessage (),
2021-12-22 04:01:40 +13:00
'code' => 500 ,
'file' => $th -> getFile (),
'line' => $th -> getLine (),
'trace' => $th -> getTrace (),
'version' => $version ,
] : [
'message' => 'Error: Server Error' ,
'code' => 500 ,
'version' => $version ,
];
$swooleResponse -> end ( \json_encode ( $output ));
2021-06-12 04:09:46 +12:00
} finally {
2022-10-16 03:14:17 +13:00
$pools -> reclaim ();
2020-06-27 00:27:58 +12:00
}
2020-06-26 21:54:37 +12:00
});
2022-05-24 02:54:50 +12:00
$http -> start ();