From ab77d9efaebf9d4a6ed194b4959ddeb41c271f6e Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Fri, 26 Jun 2020 15:27:58 +0300 Subject: [PATCH] Expirements --- Dockerfile | 7 +- app/app.php | 41 +++--- app/config/collections.php | 10 +- app/controllers/api/account.php | 4 +- app/controllers/api/storage.php | 6 +- app/init.php | 204 +++++++++++++-------------- app/server.php | 79 +++++++---- composer.lock | 3 +- docker-compose.nginx.yml | 211 ++++++++++++++++++++++++++++ docker-compose.yml | 4 +- src/Appwrite/Utopia/Request.php | 232 +++++++++++++++++++++++++++++++ src/Appwrite/Utopia/Response.php | 93 ++++++++++++- 12 files changed, 730 insertions(+), 164 deletions(-) create mode 100644 docker-compose.nginx.yml create mode 100644 src/Appwrite/Utopia/Request.php diff --git a/Dockerfile b/Dockerfile index e59cb29915..19c253966a 100755 --- a/Dockerfile +++ b/Dockerfile @@ -130,10 +130,9 @@ RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini EXPOSE 9501 -# CMD [ "php" , "app/server.php" ] -CMD [ "php" , "-i" ] +CMD [ "php" , "app/server.php" ] +# CMD [ "php" , "-i" ] # static files: https://gist.github.com/ezimuel/a2e0ff7308952f2aa946f828a1302a63 -# docker build -t saw . -# docker run -it --rm --name saw-run saw \ No newline at end of file +# docker build -t saw . && docker run -it -p 9501:9501 --rm --name saw-run saw \ No newline at end of file diff --git a/app/app.php b/app/app.php index 5e5d30150c..d13c49dc62 100644 --- a/app/app.php +++ b/app/app.php @@ -31,28 +31,29 @@ $deletes = new Event('v1-deletes', 'DeletesV1'); * Get All verified client URLs for both console and current projects * + Filter for duplicated entries */ -$clientsConsole = \array_map(function ($node) { - return $node['hostname']; - }, \array_filter($console->getAttribute('platforms', []), function ($node) { - if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { - return true; - } +// $clientsConsole = \array_map(function ($node) { +// return $node['hostname']; +// }, \array_filter($console->getAttribute('platforms', []), function ($node) { +// if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { +// return true; +// } - return false; - })); +// return false; +// })); -$clients = \array_unique(\array_merge($clientsConsole, \array_map(function ($node) { - return $node['hostname']; - }, \array_filter($project->getAttribute('platforms', []), function ($node) { - if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { - return true; - } +// $clients = \array_unique(\array_merge($clientsConsole, \array_map(function ($node) { +// return $node['hostname']; +// }, \array_filter($project->getAttribute('platforms', []), function ($node) { +// if (isset($node['type']) && $node['type'] === 'web' && isset($node['hostname']) && !empty($node['hostname'])) { +// return true; +// } - return false; - })))); +// return false; +// })))); -$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $console, $webhook, $mail, $audit, $usage, $clients) { - +$utopia->init(function () { + global $utopia, $request, $response, $user, $project, $console, $webhook, $audit, $usage, $clients; + var_dump(1); $route = $utopia->match($request); if(!empty($route->getLabel('sdk.platform', [])) && empty($project->getId()) && ($route->getLabel('scope', '') !== 'public')) { @@ -427,6 +428,4 @@ include_once __DIR__ . '/controllers/shared/web.php'; foreach(Config::getParam('services', []) as $service) { include_once $service['controller']; -} - -$utopia->run($request, $response); \ No newline at end of file +} \ No newline at end of file diff --git a/app/config/collections.php b/app/config/collections.php index 3141419c1b..b7e2dc5c7d 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1,6 +1,6 @@ Database::SYSTEM_COLLECTION_PLATFORMS, 'name' => 'Current Host', 'type' => 'web', - 'hostname' => \parse_url('https://'.$request->getServer('HTTP_HOST'), PHP_URL_HOST), + 'hostname' => \parse_url('https://'.$utopia->getEnv('HTTP_HOST'), PHP_URL_HOST), ], ], 'legalName' => '', @@ -50,9 +50,9 @@ $collections = [ 'legalCity' => '', 'legalAddress' => '', 'legalTaxId' => '', - 'authWhitelistEmails' => (!empty($request->getServer('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', $request->getServer('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty($request->getServer('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', $request->getServer('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'authWhitelistDomains' => (!empty($request->getServer('_APP_CONSOLE_WHITELIST_DOMAINS', null))) ? \explode(',', $request->getServer('_APP_CONSOLE_WHITELIST_DOMAINS', null)) : [], + 'authWhitelistEmails' => (!empty($utopia->getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', $utopia->getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty($utopia->getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', $utopia->getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'authWhitelistDomains' => (!empty($utopia->getEnv('_APP_CONSOLE_WHITELIST_DOMAINS', null))) ? \explode(',', $utopia->getEnv('_APP_CONSOLE_WHITELIST_DOMAINS', null)) : [], ], Database::SYSTEM_COLLECTION_COLLECTIONS => [ '$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ade11aeb0e..4faa5b45a1 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -29,8 +29,8 @@ use DeviceDetector\DeviceDetector; use GeoIp2\Database\Reader; use Utopia\Validator\ArrayList; -$oauthDefaultSuccess = $request->getServer('_APP_HOME').'/auth/oauth2/success'; -$oauthDefaultFailure = $request->getServer('_APP_HOME').'/auth/oauth2/failure'; +$oauthDefaultSuccess = $utopia->getEnv('_APP_HOME').'/auth/oauth2/success'; +$oauthDefaultFailure = $utopia->getEnv('_APP_HOME').'/auth/oauth2/failure'; $oauth2Keys = []; diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 9a12b56048..cdf7c60474 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -23,8 +23,6 @@ use Appwrite\Storage\Compression\Algorithms\GZIP; use Appwrite\Resize\Resize; use Appwrite\OpenSSL\OpenSSL; -Storage::addDevice('local', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId())); - $fileLogos = [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554 'default' => __DIR__.'/../../config/files/none.png', @@ -131,6 +129,10 @@ $mimes = [ 'application/pdf', ]; +$utopia->init(function () use ($project) { + Storage::addDevice('local', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId())); +}, 'storage'); + $utopia->post('/v1/storage/files') ->desc('Create File') ->groups(['api', 'storage']) diff --git a/app/init.php b/app/init.php index 09413f187d..45b59417c6 100644 --- a/app/init.php +++ b/app/init.php @@ -25,6 +25,11 @@ use Appwrite\Database\Adapter\Redis as RedisAdapter; use Appwrite\Utopia\Response; use PHPMailer\PHPMailer\PHPMailer; +$locale = 'en'; +$clients = []; +$console = new Document([]); +$mode = ''; + const APP_NAME = 'Appwrite'; const APP_DOMAIN = 'appwrite.io'; const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address @@ -47,11 +52,6 @@ const APP_SOCIAL_GITHUB = 'https://github.com/appwrite'; const APP_SOCIAL_DISCORD = 'https://discord.gg/GSeTUeA'; const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; -$register = new Registry(); -$request = new Request(); -$response = new Response(); -$utopia = new App('Asia/Tel_Aviv'); - $utopia->setMode($utopia->getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION)); /* @@ -66,20 +66,20 @@ Config::load('roles', __DIR__.'/../app/config/roles.php'); // User roles and sc Config::load('services', __DIR__.'/../app/config/services.php'); // List of services Config::setParam('env', $utopia->getMode()); -Config::setParam('domain', $request->getServer('HTTP_HOST', '')); +Config::setParam('domain', $utopia->getEnv('HTTP_HOST', '')); Config::setParam('domainVerification', false); -Config::setParam('version', $request->getServer('_APP_VERSION', 'UNKNOWN')); -Config::setParam('protocol', $request->getServer('HTTP_X_FORWARDED_PROTO', $request->getServer('REQUEST_SCHEME', 'https'))); -Config::setParam('port', (string) \parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_PORT)); -Config::setParam('hostname', \parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', null), PHP_URL_HOST)); +Config::setParam('version', $utopia->getEnv('_APP_VERSION', 'UNKNOWN')); +Config::setParam('protocol', $utopia->getEnv('HTTP_X_FORWARDED_PROTO', $utopia->getEnv('REQUEST_SCHEME', 'https'))); +Config::setParam('port', (string) \parse_url(Config::getParam('protocol').'://'.$utopia->getEnv('HTTP_HOST', ''), PHP_URL_PORT)); +Config::setParam('hostname', \parse_url(Config::getParam('protocol').'://'.$utopia->getEnv('HTTP_HOST', null), PHP_URL_HOST)); -Resque::setBackend($request->getServer('_APP_REDIS_HOST', '') - .':'.$request->getServer('_APP_REDIS_PORT', '')); +Resque::setBackend($utopia->getEnv('_APP_REDIS_HOST', '') + .':'.$utopia->getEnv('_APP_REDIS_PORT', '')); \define('COOKIE_DOMAIN', ( - $request->getServer('HTTP_HOST', null) === 'localhost' || - $request->getServer('HTTP_HOST', null) === 'localhost:'.Config::getParam('port') || + $utopia->getEnv('HTTP_HOST', null) === 'localhost' || + $utopia->getEnv('HTTP_HOST', null) === 'localhost:'.Config::getParam('port') || (\filter_var(Config::getParam('hostname'), FILTER_VALIDATE_IP) !== false) ) ? null @@ -90,11 +90,11 @@ Resque::setBackend($request->getServer('_APP_REDIS_HOST', '') /* * Registry */ -$register->set('db', function () use ($request) { // Register DB connection - $dbHost = $request->getServer('_APP_DB_HOST', ''); - $dbUser = $request->getServer('_APP_DB_USER', ''); - $dbPass = $request->getServer('_APP_DB_PASS', ''); - $dbScheme = $request->getServer('_APP_DB_SCHEMA', ''); +$register->set('db', function () use ($utopia) { // Register DB connection + $dbHost = $utopia->getEnv('_APP_DB_HOST', ''); + $dbUser = $utopia->getEnv('_APP_DB_USER', ''); + $dbPass = $utopia->getEnv('_APP_DB_PASS', ''); + $dbScheme = $utopia->getEnv('_APP_DB_SCHEMA', ''); $pdo = new PDO("mysql:host={$dbHost};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4', @@ -107,9 +107,9 @@ $register->set('db', function () use ($request) { // Register DB connection return $pdo; }); -$register->set('influxdb', function () use ($request) { // Register DB connection - $host = $request->getServer('_APP_INFLUXDB_HOST', ''); - $port = $request->getServer('_APP_INFLUXDB_PORT', ''); +$register->set('influxdb', function () use ($utopia) { // Register DB connection + $host = $utopia->getEnv('_APP_INFLUXDB_HOST', ''); + $port = $utopia->getEnv('_APP_INFLUXDB_PORT', ''); if (empty($host) || empty($port)) { return; @@ -119,43 +119,43 @@ $register->set('influxdb', function () use ($request) { // Register DB connectio return $client; }); -$register->set('statsd', function () use ($request) { // Register DB connection - $host = $request->getServer('_APP_STATSD_HOST', 'telegraf'); - $port = $request->getServer('_APP_STATSD_PORT', 8125); +$register->set('statsd', function () use ($utopia) { // Register DB connection + $host = $utopia->getEnv('_APP_STATSD_HOST', 'telegraf'); + $port = $utopia->getEnv('_APP_STATSD_PORT', 8125); $connection = new \Domnikl\Statsd\Connection\UdpSocket($host, $port); $statsd = new \Domnikl\Statsd\Client($connection); return $statsd; }); -$register->set('cache', function () use ($request) { // Register cache connection +$register->set('cache', function () use ($utopia) { // Register cache connection $redis = new Redis(); - $redis->connect($request->getServer('_APP_REDIS_HOST', ''), - $request->getServer('_APP_REDIS_PORT', '')); + $redis->connect($utopia->getEnv('_APP_REDIS_HOST', ''), + $utopia->getEnv('_APP_REDIS_PORT', '')); return $redis; }); -$register->set('smtp', function () use ($request) { +$register->set('smtp', function () use ($utopia) { $mail = new PHPMailer(true); $mail->isSMTP(); - $username = $request->getServer('_APP_SMTP_USERNAME', null); - $password = $request->getServer('_APP_SMTP_PASSWORD', null); + $username = $utopia->getEnv('_APP_SMTP_USERNAME', null); + $password = $utopia->getEnv('_APP_SMTP_PASSWORD', null); $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = $request->getServer('_APP_SMTP_HOST', 'smtp'); - $mail->Port = $request->getServer('_APP_SMTP_PORT', 25); + $mail->Host = $utopia->getEnv('_APP_SMTP_HOST', 'smtp'); + $mail->Port = $utopia->getEnv('_APP_SMTP_PORT', 25); $mail->SMTPAuth = (!empty($username) && !empty($password)); $mail->Username = $username; $mail->Password = $password; - $mail->SMTPSecure = $request->getServer('_APP_SMTP_SECURE', false); + $mail->SMTPSecure = $utopia->getEnv('_APP_SMTP_SECURE', false); $mail->SMTPAutoTLS = false; $mail->CharSet = 'UTF-8'; - $from = \urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')); - $email = $request->getServer('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $from = \urldecode($utopia->getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')); + $email = $utopia->getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); $mail->setFrom($email, $from); $mail->addReplyTo($email, $from); @@ -168,7 +168,7 @@ $register->set('smtp', function () use ($request) { /* * Localization */ -$locale = $request->getParam('locale', $request->getHeader('X-Appwrite-Locale', '')); +// $locale = $request->getParam('locale', $request->getHeader('X-Appwrite-Locale', '')); Locale::$exceptions = false; @@ -230,89 +230,89 @@ if (\in_array($locale, Config::getParam('locales'))) { 'method' => 'GET', 'user_agent' => \sprintf(APP_USERAGENT, Config::getParam('version'), - $request->getServer('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)), + $utopia->getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)), 'timeout' => 2, ], ]); -/* - * Auth & Project Scope - */ -$consoleDB = new Database(); -$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); -$consoleDB->setNamespace('app_console'); // Should be replaced with param if we want to have parent projects +// /* +// * Auth & Project Scope +// */ +// $consoleDB = new Database(); +// $consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); +// $consoleDB->setNamespace('app_console'); // Should be replaced with param if we want to have parent projects -$consoleDB->setMocks(Config::getParam('collections', [])); -Authorization::disable(); +// $consoleDB->setMocks(Config::getParam('collections', [])); +// Authorization::disable(); -$project = $consoleDB->getDocument($request->getParam('project', $request->getHeader('X-Appwrite-Project', ''))); +// $project = $consoleDB->getDocument($request->getParam('project', $request->getHeader('X-Appwrite-Project', ''))); -Authorization::enable(); +// Authorization::enable(); -$console = $consoleDB->getDocument('console'); +// $console = $consoleDB->getDocument('console'); -$mode = $request->getParam('mode', $request->getHeader('X-Appwrite-Mode', 'default')); +// $mode = $request->getParam('mode', $request->getHeader('X-Appwrite-Mode', 'default')); -Auth::setCookieName('a_session_'.$project->getId()); +// Auth::setCookieName('a_session_'.$project->getId()); -if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_'.$console->getId()); -} +// if (APP_MODE_ADMIN === $mode) { +// Auth::setCookieName('a_session_'.$console->getId()); +// } -$session = Auth::decodeSession( - $request->getCookie(Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName.'_legacy', // Get fallback session from old clients (no SameSite support) - $request->getHeader('X-Appwrite-Key', '')))); // Get API Key +// $session = Auth::decodeSession( +// $request->getCookie(Auth::$cookieName, // Get sessions +// $request->getCookie(Auth::$cookieName.'_legacy', // Get fallback session from old clients (no SameSite support) +// $request->getHeader('X-Appwrite-Key', '')))); // Get API Key -// Get fallback session from clients who block 3rd-party cookies -$response->addHeader('X-Debug-Fallback', 'false'); +// // Get fallback session from clients who block 3rd-party cookies +// $response->addHeader('X-Debug-Fallback', 'false'); -if(empty($session['id']) && empty($session['secret'])) { - $response->addHeader('X-Debug-Fallback', 'true'); - $fallback = $request->getHeader('X-Fallback-Cookies', ''); - $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); -} +// if(empty($session['id']) && empty($session['secret'])) { +// $response->addHeader('X-Debug-Fallback', 'true'); +// $fallback = $request->getHeader('X-Fallback-Cookies', ''); +// $fallback = \json_decode($fallback, true); +// $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); +// } -Auth::$unique = $session['id']; -Auth::$secret = $session['secret']; +// Auth::$unique = $session['id']; +// Auth::$secret = $session['secret']; -$projectDB = new Database(); -$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); -$projectDB->setNamespace('app_'.$project->getId()); -$projectDB->setMocks(Config::getParam('collections', [])); +// $projectDB = new Database(); +// $projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register)); +// $projectDB->setNamespace('app_'.$project->getId()); +// $projectDB->setMocks(Config::getParam('collections', [])); -if (APP_MODE_ADMIN !== $mode) { - $user = $projectDB->getDocument(Auth::$unique); -} -else { - $user = $consoleDB->getDocument(Auth::$unique); +// if (APP_MODE_ADMIN !== $mode) { +// $user = $projectDB->getDocument(Auth::$unique); +// } +// else { +// $user = $consoleDB->getDocument(Auth::$unique); - $user - ->setAttribute('$id', 'admin-'.$user->getAttribute('$id')) - ; -} +// $user +// ->setAttribute('$id', 'admin-'.$user->getAttribute('$id')) +// ; +// } -if (empty($user->getId()) // Check a document has been found in the DB - || Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document - || !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token - $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); -} +// if (empty($user->getId()) // Check a document has been found in the DB +// || Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document +// || !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token +// $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); +// } -if (APP_MODE_ADMIN === $mode) { - if (!empty($user->search('teamId', $project->getAttribute('teamId'), $user->getAttribute('memberships')))) { - Authorization::disable(); - } else { - $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); - } -} +// if (APP_MODE_ADMIN === $mode) { +// if (!empty($user->search('teamId', $project->getAttribute('teamId'), $user->getAttribute('memberships')))) { +// Authorization::disable(); +// } else { +// $user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]); +// } +// } -// Set project mail -$register->get('smtp') - ->setFrom( - $request->getServer('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), - ($project->getId() === 'console') - ? \urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')) - : \sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name') - ) - ); +// // Set project mail +// $register->get('smtp') +// ->setFrom( +// $utopia->getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), +// ($project->getId() === 'console') +// ? \urldecode($utopia->getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server')) +// : \sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name') +// ) +// ); diff --git a/app/server.php b/app/server.php index c43b254704..e9a35654e3 100644 --- a/app/server.php +++ b/app/server.php @@ -1,22 +1,24 @@ set([ 'open_http2_protocol' => true, 'document_root' => __DIR__ . '/../public', @@ -25,26 +27,53 @@ $server ]) ; -$server->on('WorkerStart', function($serv, $workerId) { - Console::success('Server started succefully'); +$http->on('WorkerStart', function($serv, $workerId) { + Console::success('Worker '.$workerId.' started succefully'); }); -$server->on('BeforeReload', function($serv, $workerId) { +$http->on('BeforeReload', function($serv, $workerId) { Console::success('Starting reload...'); }); -$server->on('AfterReload', function($serv, $workerId) { +$http->on('AfterReload', function($serv, $workerId) { Console::success('Reload completed...'); }); -// http && http2 -$server->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) { +$http->on('start', function (Server $http) { + Console::success('Server started succefully'); + printf("master pid %d, manager pid %d\n", $http->master_pid, $http->manager_pid); + + // listen ctrl + c + Process::signal(2, function () use ($http) { + echo "Stop by Ctrl+C\n"; + $http->shutdown(); + }); +}); + +$register = new Registry(); +$utopia = new App('Asia/Tel_Aviv'); +/** + * @var $request Request + */ +$request &= null; +$response &= null; + +include 'init.php'; +include 'app.php'; + +$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) { + global $request, $response, $utopia; + $request = new Request($swooleRequest); + $response = new Response($swooleResponse); + try { + $utopia->run($request, $response); + } catch (\Throwable $th) { + var_dump($th->getMessage()); + var_dump($th->getFile()); + var_dump($th->getLine()); + $swooleResponse->end('error: '.$th->getMessage()); + } }); -// websocket -$server->on('message', function (Server $server, Frame $frame) { - $server->push($frame->fd, 'Hello ' . $frame->data); -}); - -$server->start(); \ No newline at end of file +$http->start(); diff --git a/composer.lock b/composer.lock index 9cb06696f4..e08ed94b4c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "950a131ba83024caad4b96de7c184b58", + "content-hash": "06dff1e6bbf24c18592e978fd9baab7d", "packages": [ { "name": "appwrite/php-clamav", @@ -3461,6 +3461,7 @@ "ext-imagick": "*", "ext-mbstring": "*", "ext-json": "*", + "ext-yaml": "*", "ext-dom": "*", "ext-redis": "*", "ext-pdo": "*", diff --git a/docker-compose.nginx.yml b/docker-compose.nginx.yml new file mode 100644 index 0000000000..26a2ed016f --- /dev/null +++ b/docker-compose.nginx.yml @@ -0,0 +1,211 @@ +version: '3' + +services: + traefik: + image: traefik:v2.1.4 + container_name: appwrite_traefik + command: + - --log.level=DEBUG + - --api.insecure=true + - --providers.file.directory=/storage/config + - --providers.file.watch=true + - --providers.docker=true + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --accesslog=true + restart: unless-stopped + ports: + - 80:80 + - 443:443 + - 8080:8080 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - appwrite-config:/storage/config:ro + - appwrite-certificates:/storage/certificates:ro + depends_on: + - appwrite + networks: + - gateway + - appwrite + + appwrite: + container_name: appwrite + build: + context: . + args: + - TESTING=true + - VERSION=dev + restart: unless-stopped + networks: + - appwrite + labels: + - traefik.http.routers.appwrite.rule=PathPrefix(`/`) + - traefik.http.routers.appwrite-secure.rule=PathPrefix(`/`) + - traefik.http.routers.appwrite-secure.tls=true + volumes: + - appwrite-uploads:/storage/uploads:rw + - appwrite-cache:/storage/cache:rw + - appwrite-config:/storage/config:rw + - appwrite-certificates:/storage/certificates:rw + - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml + - ./tests:/usr/share/nginx/html/tests + - ./app:/usr/share/nginx/html/app + # - ./vendor:/usr/share/nginx/html/vendor + - ./docs:/usr/share/nginx/html/docs + - ./public:/usr/share/nginx/html/public + - ./src:/usr/share/nginx/html/src + depends_on: + - mariadb + - redis + # - smtp + - clamav + - influxdb + - telegraf + - maildev + environment: + #- _APP_ENV=production + - _APP_ENV=development + - _APP_OPTIONS_ABUSE=disabled + - _APP_OPTIONS_FORCE_HTTPS=disabled + - _APP_OPENSSL_KEY_V1=your-secret-key + - _APP_DOMAIN=demo.appwrite.io + - _APP_DOMAIN_TARGET=demo.appwrite.io + - _APP_REDIS_HOST=redis + - _APP_REDIS_PORT=6379 + - _APP_DB_HOST=mariadb + - _APP_DB_PORT=3306 + - _APP_DB_SCHEMA=appwrite + - _APP_DB_USER=user + - _APP_DB_PASS=password + - _APP_INFLUXDB_HOST=influxdb + - _APP_INFLUXDB_PORT=8086 + - _APP_STATSD_HOST=telegraf + - _APP_STATSD_PORT=8125 + - _APP_SMTP_HOST=maildev + - _APP_SMTP_PORT=25 + + mariadb: + image: appwrite/mariadb:1.0.3 # fix issues when upgrading using: mysql_upgrade -u root -p + container_name: appwrite_mariadb + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-mariadb:/var/lib/mysql:rw + ports: + - "3306:3306" + environment: + - MYSQL_ROOT_PASSWORD=rootsecretpassword + - MYSQL_DATABASE=appwrite + - MYSQL_USER=user + - MYSQL_PASSWORD=password + command: 'mysqld --innodb-flush-method=fsync' + + maildev: + image: djfarrelly/maildev + container_name: appwrite_maildev + restart: unless-stopped + ports: + - '1080:80' + networks: + - appwrite + + # smtp: + # image: appwrite/smtp:1.0.1 + # container_name: appwrite_smtp + # restart: unless-stopped + # networks: + # - appwrite + # environment: + # - MAILNAME=appwrite + # - RELAY_NETWORKS=:192.168.0.0/24:10.0.0.0/16 + + redis: + image: redis:5.0 + container_name: appwrite_redis + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-redis:/data:rw + + clamav: + image: appwrite/clamav:1.0.9 + container_name: appwrite_clamav + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-uploads:/storage/uploads + + influxdb: + image: influxdb:1.6 + container_name: appwrite_influxdb + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-influxdb:/var/lib/influxdb:rw + + telegraf: + image: appwrite/telegraf:1.0.0 + container_name: appwrite_telegraf + restart: unless-stopped + networks: + - appwrite + + # redis-commander: + # image: rediscommander/redis-commander:latest + # restart: unless-stopped + # networks: + # - appwrite + # environment: + # - REDIS_HOSTS=redis + # ports: + # - "8081:8081" + + # resque: + # image: registry.gitlab.com/appwrite/appwrite/resque-web:v1.0.2 + # restart: unless-stopped + # networks: + # - appwrite + # ports: + # - "5678:5678" + # environment: + # - RESQUE_WEB_HOST=redis + # - RESQUE_WEB_PORT=6379 + # - RESQUE_WEB_HTTP_BASIC_AUTH_USER=user + # - RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=password + + chronograf: + image: chronograf:1.5 + container_name: appwrite_chronograf + restart: unless-stopped + networks: + - appwrite + volumes: + - appwrite-chronograf:/var/lib/chronograf + ports: + - "8888:8888" + environment: + - INFLUXDB_URL=http://influxdb:8086 + - KAPACITOR_URL=http://kapacitor:9092 + - AUTH_DURATION=48h + - TOKEN_SECRET=duperduper5674829!jwt + - GH_CLIENT_ID=d86f7145a41eacfc52cc + - GH_CLIENT_SECRET=9e0081062367a2134e7f2ea95ba1a32d08b6c8ab + - GH_ORGS=appwrite + +networks: + gateway: + appwrite: + +volumes: + appwrite-mariadb: + appwrite-redis: + appwrite-cache: + appwrite-uploads: + appwrite-certificates: + appwrite-influxdb: + appwrite-chronograf: + appwrite-config: diff --git a/docker-compose.yml b/docker-compose.yml index 26a2ed016f..ba3470822a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,8 @@ services: - TESTING=true - VERSION=dev restart: unless-stopped + ports: + - 9501:9501 networks: - appwrite labels: @@ -50,7 +52,7 @@ services: - ./phpunit.xml:/usr/share/nginx/html/phpunit.xml - ./tests:/usr/share/nginx/html/tests - ./app:/usr/share/nginx/html/app - # - ./vendor:/usr/share/nginx/html/vendor + - ./vendor:/usr/share/nginx/html/vendor - ./docs:/usr/share/nginx/html/docs - ./public:/usr/share/nginx/html/public - ./src:/usr/share/nginx/html/src diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php new file mode 100644 index 0000000000..fb692e16c6 --- /dev/null +++ b/src/Appwrite/Utopia/Request.php @@ -0,0 +1,232 @@ +swoole = $request; + } + + /** + * Get Param + * + * Get param by current method name + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getParam(string $key, $default = null) + { + switch($this->getServer('request_method', '')) { + case self::METHOD_GET: + return $this->getQuery($key, $default); + break; + case self::METHOD_POST: + case self::METHOD_PUT: + case self::METHOD_PATCH: + case self::METHOD_DELETE: + return $this->getPayload($key, $default); + break; + default: + return $this->getQuery($key, $default); + } + } + + /** + * Get Params + * + * Get all params of current method + * + * @return array + */ + public function getParams(): array + { + switch($this->getServer('REQUEST_METHOD', '')) { + case self::METHOD_GET: + return (!empty($this->swoole->get)) ? $this->swoole->get : []; + break; + case self::METHOD_POST: + case self::METHOD_PUT: + case self::METHOD_PATCH: + return $this->generateInput(); + break; + default: + return (!empty($this->swoole->get)) ? $this->swoole->get : []; + } + + return []; + } + + /** + * Get Query + * + * Method for querying HTTP GET request parameters. If $key is not found $default value will be returned. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getQuery(string $key, $default = null) + { + return (isset($this->swoole->get[$key])) ? $this->swoole->get[$key] : $default; + } + + /** + * Get payload + * + * Method for querying HTTP request payload parameters. If $key is not found $default value will be returned. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getPayload(string $key, $default = null) + { + $payload = $this->generateInput(); + + return (isset($payload[$key])) ? $payload[$key] : $default; + } + + /** + * Get server + * + * Method for querying server parameters. If $key is not found $default value will be returned. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function getServer(string $key, $default = null) + { + $key = strtolower($key); + return (isset($this->swoole->server) && isset($this->swoole->server[$key])) ? $this->swoole->server[$key] : $default; + } + + /** + * Get IP + * + * Returns users IP address. + * Support HTTP_X_FORWARDED_FOR header usually return + * from different proxy servers or PHP default REMOTE_ADDR + */ + public function getIP(): string + { + return $this->getServer('http_x_forwarded_for', $this->getServer('remote_addr', '0.0.0.0')); + } + + /** + * Get Method + * + * Return HTTP request method + * + * @return string + */ + public function getMethod():string + { + return $this->getServer('request_method', 'UNKNOWN'); + } + + /** + * Get files + * + * Method for querying upload files data. If $key is not found empty array will be returned. + * + * @param string $key + * @return array + */ + public function getFiles($key): array + { + return (isset($this->swoole->files[$key])) ? $this->swoole->files[$key] : []; + } + + /** + * Get cookie + * + * Method for querying HTTP cookie parameters. If $key is not found $default value will be returned. + * + * @param string $key + * @param string $default + * @return mixed + */ + public function getCookie(string $key, string $default = ''): string + { + return (isset($this->swoole->cookie[$key])) ? $this->swoole->cookie[$key] : $default; + } + + /** + * Get header + * + * Method for querying HTTP header parameters. If $key is not found $default value will be returned. + * + * @param string $key + * @param string $default + * @return string + */ + public function getHeader(string $key, string $default = ''): string + { + return (isset($this->swoole->headers[$key])) ? $this->swoole->headers[$key] : $default; + } + + /** + * Generate input + * + * Generate PHP input stream and parse it as an array in order to handle different content type of requests + * + * @return array + */ + protected function generateInput(): array + { + if (null === $this->payload) { + $contentType = $this->getHeader('content-type'); + + // Get content-type without the charset + $length = strpos($contentType, ';'); + $length = (empty($length)) ? strlen($contentType) : $length; + $contentType = substr($contentType, 0, $length); + + switch ($contentType) { + case 'application/json': + $this->payload = json_decode($this->swoole->rawContent()); + break; + + default: + $this->payload = $this->swoole->post; + break; + } + + if(empty($this->payload)) { // Make sure we return same data type even if json payload is empty or failed + $this->payload = []; + } + } + + return $this->payload; + } + + /** + * Generate headers + * + * Parse request headers as an array for easy querying using the getHeader method + * + * @return array + */ + protected function generateHeaders(): array + { + return $this->swoole->header; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 81276d0fe6..09521c001b 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -15,6 +15,7 @@ use Appwrite\Utopia\Response\Model\Locale; use Appwrite\Utopia\Response\Model\Membership; use Appwrite\Utopia\Response\Model\MembershipList; use Utopia\Response as UtopiaResponse; +use Swoole\Http\Response as SwooleResponse; class Response extends UtopiaResponse { @@ -50,8 +51,19 @@ class Response extends UtopiaResponse const MODEL_MEMBERSHIP = 'membership'; const MODEL_MEMBERSHIP_LIST = 'membershipList'; - public function __construct() + /** + * Swoole Response Object + * + * @var SwooleResponse + */ + protected $swoole = null; + + /** + * Response constructor. + */ + public function __construct(SwooleResponse $response) { + $this->swoole = $response; $this ->setModel(new Error()) ->setModel(new ErrorDev()) @@ -168,4 +180,83 @@ class Response extends UtopiaResponse ->send(yaml_emit($data, YAML_UTF8_ENCODING)) ; } + + /** + * Output response + * + * Generate HTTP response output including the response header (+cookies) and body and prints them. + * + * @param string $body + * @param int $exit exit code or don't exit if code is null + * + * @return self + */ + public function send(string $body = '', int $exit = null): void + { + if(!$this->disablePayload) { + $this->addHeader('X-Debug-Speed', microtime(true) - $this->startTime); + + $this + ->appendCookies() + ->appendHeaders() + ; + + $this->size = $this->size + mb_strlen(implode("\n", $this->headers)) + mb_strlen($body, '8bit'); + + $this->swoole->end($body); + + $this->disablePayload(); + } + } + + /** + * Append headers + * + * Iterating over response headers to generate them using native PHP header function. + * This method is also responsible for generating the response and content type headers. + * + * @return self + */ + protected function appendHeaders(): self + { + // Send status code header + $this->swoole->status($this->statusCode); + + // Send content type header + $this + ->addHeader('Content-Type', $this->contentType . '; charset=UTF-8') + ; + + // Set application headers + foreach ($this->headers as $key => $value) { + $this->swoole->header($key, $value); + } + + return $this; + } + + /** + * Append cookies + * + * Iterating over response cookies to generate them using native PHP cookie function. + * + * @return self + */ + protected function appendCookies(): self + { + foreach ($this->cookies as $cookie) { + $this->swoole->cookie( + $cookie['name'], + $cookie['value'], + $cookie['expire'], + $cookie['path'], + $cookie['domain'], + $cookie['secure'], + $cookie['httponly'], + $cookie['samesite'], + ); + } + + return $this; + } }