1
0
Fork 0
mirror of synced 2024-05-20 04:32:37 +12:00

Removed global vars

This commit is contained in:
Eldad Fux 2020-06-30 00:43:34 +03:00
parent 8596937210
commit 3e808d3bf4
18 changed files with 2444 additions and 2435 deletions

View file

@ -2,7 +2,7 @@
require_once __DIR__.'/init.php';
global $request, $response, $register, $project;
global $register, $project;
use Utopia\App;
use Utopia\Request;
@ -18,147 +18,133 @@ use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Event\Event;
use Appwrite\Network\Validator\Origin;
$request = new Request();
$response = new Response();
// Config::setParam('domain', $request->getServer('HTTP_HOST', ''));
// Config::setParam('domainVerification', false);
// Config::setParam('version', App::getEnv('_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));
$locale = $request->getParam('locale', $request->getHeader('X-Appwrite-Locale', ''));
// \define('COOKIE_DOMAIN',
// (
// $request->getServer('HTTP_HOST', null) === 'localhost' ||
// $request->getServer('HTTP_HOST', null) === 'localhost:'.Config::getParam('port') ||
// (\filter_var(Config::getParam('hostname'), FILTER_VALIDATE_IP) !== false)
// )
// ? null
// : '.'.Config::getParam('hostname')
// );
// \define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE);
if (\in_array($locale, Config::getParam('locales'))) {
Locale::setDefault($locale);
}
// Authorization::disable();
Config::setParam('env', App::getMode());
Config::setParam('domain', $request->getServer('HTTP_HOST', ''));
Config::setParam('domainVerification', false);
Config::setParam('version', App::getEnv('_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));
// $project = $consoleDB->getDocument($request->getParam('project', $request->getHeader('X-Appwrite-Project', '')));
\define('COOKIE_DOMAIN',
(
$request->getServer('HTTP_HOST', null) === 'localhost' ||
$request->getServer('HTTP_HOST', null) === 'localhost:'.Config::getParam('port') ||
(\filter_var(Config::getParam('hostname'), FILTER_VALIDATE_IP) !== false)
)
? null
: '.'.Config::getParam('hostname')
);
\define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE);
// Authorization::enable();
Authorization::disable();
// $console = $consoleDB->getDocument('console');
$project = $consoleDB->getDocument($request->getParam('project', $request->getHeader('X-Appwrite-Project', '')));
// $mode = $request->getParam('mode', $request->getHeader('X-Appwrite-Mode', 'default'));
Authorization::enable();
// Auth::setCookieName('a_session_'.$project->getId());
$console = $consoleDB->getDocument('console');
// if (APP_MODE_ADMIN === $mode) {
// Auth::setCookieName('a_session_'.$console->getId());
// }
$mode = $request->getParam('mode', $request->getHeader('X-Appwrite-Mode', 'default'));
// $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
Auth::setCookieName('a_session_'.$project->getId());
// // Get fallback session from clients who block 3rd-party cookies
// $response->addHeader('X-Debug-Fallback', 'false');
if (APP_MODE_ADMIN === $mode) {
Auth::setCookieName('a_session_'.$console->getId());
}
// 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] : ''));
// }
$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
// Auth::$unique = $session['id'];
// Auth::$secret = $session['secret'];
// Get fallback session from clients who block 3rd-party cookies
$response->addHeader('X-Debug-Fallback', 'false');
// $projectDB = new Database();
// $projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
// $projectDB->setNamespace('app_'.$project->getId());
// $projectDB->setMocks(Config::getParam('collections', []));
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 (APP_MODE_ADMIN !== $mode) {
// $user = $projectDB->getDocument(Auth::$unique);
// }
// else {
// $user = $consoleDB->getDocument(Auth::$unique);
Auth::$unique = $session['id'];
Auth::$secret = $session['secret'];
// $user
// ->setAttribute('$id', 'admin-'.$user->getAttribute('$id'))
// ;
// }
$projectDB = new Database();
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$projectDB->setNamespace('app_'.$project->getId());
$projectDB->setMocks(Config::getParam('collections', []));
// 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) {
$user = $projectDB->getDocument(Auth::$unique);
}
else {
$user = $consoleDB->getDocument(Auth::$unique);
// 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]);
// }
// }
$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 (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(
App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM),
($project->getId() === 'console')
? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'))
: \sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name')
)
);
/*
* Configuration files
*/
$utopia = new App('Asia/Tel_Aviv');
$webhook = new Event('v1-webhooks', 'WebhooksV1');
$audit = new Event('v1-audits', 'AuditsV1');
$usage = new Event('v1-usage', 'UsageV1');
$mail = new Event('v1-mails', 'MailsV1');
$deletes = new Event('v1-deletes', 'DeletesV1');
// // Set project mail
// $register->get('smtp')
// ->setFrom(
// App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM),
// ($project->getId() === 'console')
// ? \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'))
// : \sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name')
// )
// );
/**
* 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;
// }))));
App::init(function () use ($utopia, $request, $response, &$user, $project, $console, $webhook, $audit, $usage, $clients) {
App::init(function ($utopia, $request, $response, $user, $project, $console, $webhooks, $audits, $usage, $clients, $locale) {
/** @var $locale Utopia\Locale\Locale */
$localeParam = $request->getParam('locale', $request->getHeader('X-Appwrite-Locale', ''));
if (\in_array($localeParam, Config::getParam('locales'))) {
$locale->setDefault($localeParam);
};
$route = $utopia->match($request);
if(!empty($route->getLabel('sdk.platform', [])) && empty($project->getId()) && ($route->getLabel('scope', '') !== 'public')) {
@ -312,13 +298,13 @@ App::init(function () use ($utopia, $request, $response, &$user, $project, $cons
/*
* Background Jobs
*/
$webhook
$webhooks
->setParam('projectId', $project->getId())
->setParam('event', $route->getLabel('webhook', ''))
->setParam('payload', [])
;
$audit
$audits
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
->setParam('event', '')
@ -336,10 +322,9 @@ App::init(function () use ($utopia, $request, $response, &$user, $project, $cons
->setParam('response', 0)
->setParam('storage', 0)
;
});
App::shutdown(function () use ($response, $request, $webhook, $audit, $usage, $deletes, $mode, $project, $utopia) {
}, ['utopia', 'request', 'response', 'user', 'project', 'console', 'webhook', 'audit', 'usage', 'clients', 'locale']);
App::shutdown(function ($utopia, $response, $request, $webhook, $audit, $usage, $deletes, $mode, $project) {
/*
* Trigger events for background workers
*/
@ -366,9 +351,9 @@ App::shutdown(function () use ($response, $request, $webhook, $audit, $usage, $d
->trigger()
;
}
});
}, ['utopia', 'response', 'request', 'webhook', 'audit', 'usage', 'deletes', 'mode', 'project']);
App::options(function () use ($request, $response) {
App::options(function ($request, $response) {
$origin = $request->getServer('HTTP_ORIGIN');
$response
@ -378,9 +363,11 @@ App::options(function () use ($request, $response) {
->addHeader('Access-Control-Allow-Origin', $origin)
->addHeader('Access-Control-Allow-Credentials', 'true')
->send();
});
}, ['request', 'response']);
App::error(function ($error, $utopia, $request, $response, $project) {
/** @var Exception $error */
App::error(function ($error /* @var $error Exception */) use ($request, $response, $utopia, $project) {
$version = Config::getParam('version');
switch ($error->getCode()) {
@ -450,91 +437,85 @@ App::error(function ($error /* @var $error Exception */) use ($request, $respons
$response
->json($output)
;
});
}, ['error', 'utopia', 'request', 'response', 'project']);
App::get('/manifest.json')
->desc('Progressive app manifest file')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response) {
$response->json([
'name' => APP_NAME,
'short_name' => APP_NAME,
'start_url' => '.',
'url' => 'https://appwrite.io/',
'display' => 'standalone',
'background_color' => '#fff',
'theme_color' => '#f02e65',
'description' => 'End to end backend server for frontend and mobile apps. 👩‍💻👨‍💻',
'icons' => [
[
'src' => 'images/favicon.png',
'sizes' => '256x256',
'type' => 'image/png',
],
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json([
'name' => APP_NAME,
'short_name' => APP_NAME,
'start_url' => '.',
'url' => 'https://appwrite.io/',
'display' => 'standalone',
'background_color' => '#fff',
'theme_color' => '#f02e65',
'description' => 'End to end backend server for frontend and mobile apps. 👩‍💻👨‍💻',
'icons' => [
[
'src' => 'images/favicon.png',
'sizes' => '256x256',
'type' => 'image/png',
],
]);
}
);
],
]);
}, ['response']);
App::get('/robots.txt')
->desc('Robots.txt File')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response) {
$template = new View(__DIR__.'/views/general/robots.phtml');
$response->text($template->render(false));
}
);
->action(function ($response) {
$template = new View(__DIR__.'/views/general/robots.phtml');
$response->text($template->render(false));
}, ['response']);
App::get('/humans.txt')
->desc('Humans.txt File')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response) {
$template = new View(__DIR__.'/views/general/humans.phtml');
$response->text($template->render(false));
}
);
->action(function ($response) {
$template = new View(__DIR__.'/views/general/humans.phtml');
$response->text($template->render(false));
}, ['response']);
App::get('/.well-known/acme-challenge')
->desc('SSL Verification')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($request, $response) {
$base = \realpath(APP_STORAGE_CERTIFICATES);
$path = \str_replace('/.well-known/acme-challenge/', '', $request->getParam('q'));
$absolute = \realpath($base.'/.well-known/acme-challenge/'.$path);
->action(function ($request, $response) {
$base = \realpath(APP_STORAGE_CERTIFICATES);
$path = \str_replace('/.well-known/acme-challenge/', '', $request->getParam('q'));
$absolute = \realpath($base.'/.well-known/acme-challenge/'.$path);
if(!$base) {
throw new Exception('Storage error', 500);
}
if(!$absolute) {
throw new Exception('Unknown path', 404);
}
if(!\substr($absolute, 0, \strlen($base)) === $base) {
throw new Exception('Invalid path', 401);
}
if(!\file_exists($absolute)) {
throw new Exception('Unknown path', 404);
}
$content = @\file_get_contents($absolute);
if(!$content) {
throw new Exception('Failed to get contents', 500);
}
$response->text($content);
if(!$base) {
throw new Exception('Storage error', 500);
}
);
if(!$absolute) {
throw new Exception('Unknown path', 404);
}
if(!\substr($absolute, 0, \strlen($base)) === $base) {
throw new Exception('Invalid path', 401);
}
if(!\file_exists($absolute)) {
throw new Exception('Unknown path', 404);
}
$content = @\file_get_contents($absolute);
if(!$content) {
throw new Exception('Failed to get contents', 500);
}
$response->text($content);
}, ['request', 'response']);
include_once __DIR__ . '/controllers/shared/api.php';
include_once __DIR__ . '/controllers/shared/web.php';
@ -543,9 +524,30 @@ foreach(Config::getParam('services', []) as $service) {
include_once $service['controller'];
}
App::setResource('utopia', function() use ($utopia) {return $utopia;});
App::setResource('request', function() use ($request) {return $request;});
App::setResource('response', function() use ($response) {return $response;});
App::setResource('register', function() use ($register) {return $register;});
// Runtime Execution
$utopia->run($request, $response);
App::setResource('register', function() use ($register) { return $register; });
App::setResource('layout', function($locale) {
$layout = new View(__DIR__.'/views/layouts/default.phtml');
$layout->setParam('locale', $locale);
return $layout; }, ['locale']);
App::setResource('locale', function($request) { return new Locale('en'); }, ['request']);
// Queues
App::setResource('webhook', function($register) { return $register->get('queue-webhook'); }, ['register']);
App::setResource('audit', function($register) { return $register->get('queue-audit'); }, ['register']);
App::setResource('usage', function($register) { return $register->get('queue-usage'); }, ['register']);
App::setResource('mail', function($register) { return $register->get('queue-mails'); }, ['register']);
App::setResource('deletes', function($register) { return $register->get('queue-deletes'); }, ['register']);
// Test Mock
App::setResource('clients', function() { return []; });
App::setResource('user', function() { return new Document([]); });
App::setResource('project', function() { return new Document([]); });
App::setResource('console', function() { return new Document([]); });
App::setResource('consoleDB', function() { return new Database(); });
App::setResource('projectDB', function() { return new Database([]); });
App::setResource('mode', function() { return false; });
$app = new App('Asia/Tel_Aviv');
$app->run(new Request(), new Response());

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,5 @@
<?php
global $response;
use Utopia\App;
use Utopia\Exception;
use Utopia\Validator\Boolean;
@ -20,7 +18,9 @@ use BaconQrCode\Writer;
use Utopia\Config\Config;
use Utopia\Validator\HexColor;
$avatarCallback = function ($type, $code, $width, $height, $quality) use ($response) {
$avatarCallback = function ($type, $code, $width, $height, $quality, $response) {
/** @var Utopia\Response $response */
$code = \strtolower($code);
$type = \strtolower($type);
$set = Config::getParam('avatar-'.$type, []);
@ -86,384 +86,385 @@ $avatarCallback = function ($type, $code, $width, $height, $quality) use ($respo
App::get('/v1/avatars/credit-cards/:code')
->desc('Get Credit Card Icon')
->groups(['api', 'avatars'])
->param('code', '', function () { return new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))); }, 'Credit Card Code. Possible values: '.\implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))).'.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getCreditCard')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-credit-card.md')
->action(function ($code, $width, $height, $quality) use ($avatarCallback) {
return $avatarCallback('credit-cards', $code, $width, $height, $quality);
});
->param('code', '', function () { return new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))); }, 'Credit Card Code. Possible values: '.\implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))).'.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->action(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
return $avatarCallback('credit-cards', $code, $width, $height, $quality, $response);
}, ['response']);
App::get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon')
->groups(['api', 'avatars'])
->param('code', '', function () { return new WhiteList(\array_keys(Config::getParam('avatar-browsers'))); }, 'Browser Code.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getBrowser')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-browser.md')
->action(function ($code, $width, $height, $quality) use ($avatarCallback) {
return $avatarCallback('browsers', $code, $width, $height, $quality);
});
->param('code', '', function () { return new WhiteList(\array_keys(Config::getParam('avatar-browsers'))); }, 'Browser Code.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->action(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
return $avatarCallback('browsers', $code, $width, $height, $quality, $response);
}, ['response']);
App::get('/v1/avatars/flags/:code')
->desc('Get Country Flag')
->groups(['api', 'avatars'])
->param('code', '', function () { return new WhiteList(\array_keys(Config::getParam('avatar-flags'))); }, 'Country Code. ISO Alpha-2 country code format.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFlag')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-flag.md')
->action(function ($code, $width, $height, $quality) use ($avatarCallback) {
return $avatarCallback('flags', $code, $width, $height, $quality);
});
->param('code', '', function () { return new WhiteList(\array_keys(Config::getParam('avatar-flags'))); }, 'Country Code. ISO Alpha-2 country code format.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->action(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
return $avatarCallback('flags', $code, $width, $height, $quality, $response);
}, ['response']);
App::get('/v1/avatars/image')
->desc('Get Image from URL')
->groups(['api', 'avatars'])
->param('url', '', function () { return new URL(); }, 'Image URL which you want to crop.')
->param('width', 400, function () { return new Range(0, 2000); }, 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, function () { return new Range(0, 2000); }, 'Resize preview image height, Pass an integer between 0 to 2000.', true)
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getImage')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-image.md')
->action(
function ($url, $width, $height) use ($response) {
$quality = 80;
$output = 'png';
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5('/v2/avatars/images-'.$url.'-'.$width.'/'.$height.'/'.$quality);
$type = 'png';
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 7 /* 1 week */);
->param('url', '', function () { return new URL(); }, 'Image URL which you want to crop.')
->param('width', 400, function () { return new Range(0, 2000); }, 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, function () { return new Range(0, 2000); }, 'Resize preview image height, Pass an integer between 0 to 2000.', true)
->action(function ($url, $width, $height, $response) {
/** @var Utopia\Response $response */
if ($data) {
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data, 0)
;
}
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$fetch = @\file_get_contents($url, false);
if (!$fetch) {
throw new Exception('Image not found', 404);
}
try {
$resize = new Resize($fetch);
} catch (\Exception $exception) {
throw new Exception('Unable to parse image', 500);
}
$resize->crop((int) $width, (int) $height);
$output = (empty($output)) ? $type : $output;
$quality = 80;
$output = 'png';
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5('/v2/avatars/images-'.$url.'-'.$width.'/'.$height.'/'.$quality);
$type = 'png';
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 7 /* 1 week */);
if ($data) {
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('', null)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data, 0)
;
$data = $resize->output($output, $quality);
$cache->save($key, $data);
echo $data;
unset($resize);
}
);
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$fetch = @\file_get_contents($url, false);
if (!$fetch) {
throw new Exception('Image not found', 404);
}
try {
$resize = new Resize($fetch);
} catch (\Exception $exception) {
throw new Exception('Unable to parse image', 500);
}
$resize->crop((int) $width, (int) $height);
$output = (empty($output)) ? $type : $output;
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('', null)
;
$data = $resize->output($output, $quality);
$cache->save($key, $data);
echo $data;
unset($resize);
}, ['response']);
App::get('/v1/avatars/favicon')
->desc('Get Favicon')
->groups(['api', 'avatars'])
->param('url', '', function () { return new URL(); }, 'Website URL which you want to fetch the favicon from.')
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFavicon')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-favicon.md')
->action(
function ($url) use ($response) {
$width = 56;
$height = 56;
$quality = 80;
$output = 'png';
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5('/v2/avatars/favicon-'.$url);
$type = 'png';
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
->param('url', '', function () { return new URL(); }, 'Website URL which you want to fetch the favicon from.')
->action(function ($url, $response) {
/** @var Utopia\Response $response */
if ($data) {
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data, 0)
;
}
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$curl = \curl_init();
\curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
CURLOPT_USERAGENT => \sprintf(APP_USERAGENT,
Config::getParam('version'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
]);
$html = \curl_exec($curl);
\curl_close($curl);
if (!$html) {
throw new Exception('Failed to fetch remote URL', 404);
}
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($html);
$links = $doc->getElementsByTagName('link');
$outputHref = '';
$outputExt = '';
$space = 0;
foreach ($links as $link) { /* @var $link DOMElement */
$href = $link->getAttribute('href');
$rel = $link->getAttribute('rel');
$sizes = $link->getAttribute('sizes');
$absolute = URLParse::unparse(\array_merge(\parse_url($url), \parse_url($href)));
switch (\strtolower($rel)) {
case 'icon':
case 'shortcut icon':
//case 'apple-touch-icon':
$ext = \pathinfo(\parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
switch ($ext) {
case 'ico':
case 'png':
case 'jpg':
case 'jpeg':
$size = \explode('x', \strtolower($sizes));
$sizeWidth = (isset($size[0])) ? (int) $size[0] : 0;
$sizeHeight = (isset($size[1])) ? (int) $size[1] : 0;
if (($sizeWidth * $sizeHeight) >= $space) {
$space = $sizeWidth * $sizeHeight;
$outputHref = $absolute;
$outputExt = $ext;
}
break;
}
break;
}
}
if (empty($outputHref) || empty($outputExt)) {
$default = \parse_url($url);
$outputHref = $default['scheme'].'://'.$default['host'].'/favicon.ico';
$outputExt = 'ico';
}
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
$data = @\file_get_contents($outputHref, false);
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
throw new Exception('Favicon not found', 404);
}
$cache->save($key, $data);
$response
->setContentType('image/x-icon')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send($data, 0)
;
}
$fetch = @\file_get_contents($outputHref, false);
if (!$fetch) {
throw new Exception('Icon not found', 404);
}
$resize = new Resize($fetch);
$resize->crop((int) $width, (int) $height);
$output = (empty($output)) ? $type : $output;
$width = 56;
$height = 56;
$quality = 80;
$output = 'png';
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5('/v2/avatars/favicon-'.$url);
$type = 'png';
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
if ($data) {
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('', null)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data, 0)
;
}
$data = $resize->output($output, $quality);
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$curl = \curl_init();
\curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
CURLOPT_USERAGENT => \sprintf(APP_USERAGENT,
Config::getParam('version'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
]);
$html = \curl_exec($curl);
\curl_close($curl);
if (!$html) {
throw new Exception('Failed to fetch remote URL', 404);
}
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($html);
$links = $doc->getElementsByTagName('link');
$outputHref = '';
$outputExt = '';
$space = 0;
foreach ($links as $link) { /* @var $link DOMElement */
$href = $link->getAttribute('href');
$rel = $link->getAttribute('rel');
$sizes = $link->getAttribute('sizes');
$absolute = URLParse::unparse(\array_merge(\parse_url($url), \parse_url($href)));
switch (\strtolower($rel)) {
case 'icon':
case 'shortcut icon':
//case 'apple-touch-icon':
$ext = \pathinfo(\parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
switch ($ext) {
case 'ico':
case 'png':
case 'jpg':
case 'jpeg':
$size = \explode('x', \strtolower($sizes));
$sizeWidth = (isset($size[0])) ? (int) $size[0] : 0;
$sizeHeight = (isset($size[1])) ? (int) $size[1] : 0;
if (($sizeWidth * $sizeHeight) >= $space) {
$space = $sizeWidth * $sizeHeight;
$outputHref = $absolute;
$outputExt = $ext;
}
break;
}
break;
}
}
if (empty($outputHref) || empty($outputExt)) {
$default = \parse_url($url);
$outputHref = $default['scheme'].'://'.$default['host'].'/favicon.ico';
$outputExt = 'ico';
}
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
$data = @\file_get_contents($outputHref, false);
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
throw new Exception('Favicon not found', 404);
}
$cache->save($key, $data);
echo $data;
unset($resize);
$response
->setContentType('image/x-icon')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send($data, 0)
;
}
);
$fetch = @\file_get_contents($outputHref, false);
if (!$fetch) {
throw new Exception('Icon not found', 404);
}
$resize = new Resize($fetch);
$resize->crop((int) $width, (int) $height);
$output = (empty($output)) ? $type : $output;
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('', null)
;
$data = $resize->output($output, $quality);
$cache->save($key, $data);
echo $data;
unset($resize);
}, ['response']);
App::get('/v1/avatars/qr')
->desc('Get QR Code')
->groups(['api', 'avatars'])
->param('text', '', function () { return new Text(512); }, 'Plain text to be converted to QR code image.')
->param('size', 400, function () { return new Range(0, 1000); }, 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
->param('margin', 1, function () { return new Range(0, 10); }, 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('download', false, function () { return new Boolean(true); }, 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getQR')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-qr.md')
->action(
function ($text, $size, $margin, $download) use ($response) {
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
->param('text', '', function () { return new Text(512); }, 'Plain text to be converted to QR code image.')
->param('size', 400, function () { return new Range(0, 1000); }, 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
->param('margin', 1, function () { return new Range(0, 10); }, 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('download', false, function () { return new Boolean(true); }, 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
->action(function ($text, $size, $margin, $download, $response) {
/** @var Utopia\Response $response */
$renderer = new ImageRenderer(
new RendererStyle($size, $margin),
new ImagickImageBackEnd('png', 100)
);
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
$writer = new Writer($renderer);
$renderer = new ImageRenderer(
new RendererStyle($size, $margin),
new ImagickImageBackEnd('png', 100)
);
if ($download) {
$response->addHeader('Content-Disposition', 'attachment; filename="qr.png"');
}
$writer = new Writer($renderer);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($writer->writeString($text))
;
if ($download) {
$response->addHeader('Content-Disposition', 'attachment; filename="qr.png"');
}
);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($writer->writeString($text))
;
}, ['response']);
App::get('/v1/avatars/initials')
->desc('Get User Initials')
->groups(['api', 'avatars'])
->param('name', '', function () { return new Text(512); }, 'Full Name. When empty, current user name or email will be used.', true)
->param('width', 500, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 500, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('color', '', function () { return new HexColor(); }, 'Changes text color. By default a random color will be picked and stay will persistent to the given name.', true)
->param('background', '', function () { return new HexColor(); }, 'Changes background color. By default a random color will be picked and stay will persistent to the given name.', true)
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getInitials')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-initials.md')
->action(
function ($name, $width, $height, $color, $background) use ($response, $user) {
$themes = [
['color' => '#27005e', 'background' => '#e1d2f6'], // VIOLET
['color' => '#5e2700', 'background' => '#f3d9c6'], // ORANGE
['color' => '#006128', 'background' => '#c9f3c6'], // GREEN
['color' => '#580061', 'background' => '#f2d1f5'], // FUSCHIA
['color' => '#00365d', 'background' => '#c6e1f3'], // BLUE
['color' => '#00075c', 'background' => '#d2d5f6'], // INDIGO
['color' => '#610038', 'background' => '#f5d1e6'], // PINK
['color' => '#386100', 'background' => '#dcf1bd'], // LIME
['color' => '#615800', 'background' => '#f1ecba'], // YELLOW
['color' => '#610008', 'background' => '#f6d2d5'] // RED
];
->param('name', '', function () { return new Text(512); }, 'Full Name. When empty, current user name or email will be used.', true)
->param('width', 500, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 500, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('color', '', function () { return new HexColor(); }, 'Changes text color. By default a random color will be picked and stay will persistent to the given name.', true)
->param('background', '', function () { return new HexColor(); }, 'Changes background color. By default a random color will be picked and stay will persistent to the given name.', true)
->action(function ($name, $width, $height, $color, $background, $response, $user) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$rand = \rand(0, \count($themes)-1);
$themes = [
['color' => '#27005e', 'background' => '#e1d2f6'], // VIOLET
['color' => '#5e2700', 'background' => '#f3d9c6'], // ORANGE
['color' => '#006128', 'background' => '#c9f3c6'], // GREEN
['color' => '#580061', 'background' => '#f2d1f5'], // FUSCHIA
['color' => '#00365d', 'background' => '#c6e1f3'], // BLUE
['color' => '#00075c', 'background' => '#d2d5f6'], // INDIGO
['color' => '#610038', 'background' => '#f5d1e6'], // PINK
['color' => '#386100', 'background' => '#dcf1bd'], // LIME
['color' => '#615800', 'background' => '#f1ecba'], // YELLOW
['color' => '#610008', 'background' => '#f6d2d5'] // RED
];
$name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
$words = \explode(' ', \strtoupper($name));
$initials = null;
$code = 0;
$rand = \rand(0, \count($themes)-1);
foreach ($words as $key => $w) {
$initials .= (isset($w[0])) ? $w[0] : '';
$code += (isset($w[0])) ? \ord($w[0]) : 0;
$name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
$words = \explode(' ', \strtoupper($name));
$initials = null;
$code = 0;
if ($key == 1) {
break;
}
foreach ($words as $key => $w) {
$initials .= (isset($w[0])) ? $w[0] : '';
$code += (isset($w[0])) ? \ord($w[0]) : 0;
if ($key == 1) {
break;
}
$length = \count($words);
$rand = \substr($code,-1);
$background = (!empty($background)) ? '#'.$background : $themes[$rand]['background'];
$color = (!empty($color)) ? '#'.$color : $themes[$rand]['color'];
$image = new \Imagick();
$draw = new \ImagickDraw();
$fontSize = \min($width, $height) / 2;
$draw->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf");
$image->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf");
$draw->setFillColor(new \ImagickPixel($color));
$draw->setFontSize($fontSize);
$draw->setTextAlignment(\Imagick::ALIGN_CENTER);
$draw->annotation($width / 1.97, ($height / 2) + ($fontSize / 3), $initials);
$image->newImage($width, $height, $background);
$image->setImageFormat("png");
$image->drawImage($draw);
//$image->setImageCompressionQuality(9 - round(($quality / 100) * 9));
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($image->getImageBlob())
;
}
);
$length = \count($words);
$rand = \substr($code,-1);
$background = (!empty($background)) ? '#'.$background : $themes[$rand]['background'];
$color = (!empty($color)) ? '#'.$color : $themes[$rand]['color'];
$image = new \Imagick();
$draw = new \ImagickDraw();
$fontSize = \min($width, $height) / 2;
$draw->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf");
$image->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf");
$draw->setFillColor(new \ImagickPixel($color));
$draw->setFontSize($fontSize);
$draw->setTextAlignment(\Imagick::ALIGN_CENTER);
$draw->annotation($width / 1.97, ($height / 2) + ($fontSize / 3), $initials);
$image->newImage($width, $height, $background);
$image->setImageFormat("png");
$image->drawImage($draw);
//$image->setImageCompressionQuality(9 - round(($quality / 100) * 9));
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($image->getImageBlob())
;
}, ['response', 'user']);

View file

@ -1,7 +1,5 @@
<?php
global $request, $response, $webhook, $audit, $projectDB;
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;
@ -23,9 +21,6 @@ use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Exception\Authorization as AuthorizationException;
use Appwrite\Database\Exception\Structure as StructureException;
// use DeviceDetector\DeviceDetector;
// use GeoIp2\Database\Reader;
App::post('/v1/database/collections')
->desc('Create Collection')
->groups(['api', 'database'])
@ -38,67 +33,70 @@ App::post('/v1/database/collections')
->param('name', '', function () { return new Text(256); }, 'Collection name.')
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('rules', [], function () use ($projectDB) { return new ArrayList(new Collection($projectDB, [Database::SYSTEM_COLLECTION_RULES], ['$collection' => Database::SYSTEM_COLLECTION_RULES, '$permissions' => ['read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation.')
->action(
function ($name, $read, $write, $rules) use ($response, $projectDB, $webhook, $audit) {
$parsedRules = [];
->param('rules', [], function ($projectDB) { return new ArrayList(new Collection($projectDB, [Database::SYSTEM_COLLECTION_RULES], ['$collection' => Database::SYSTEM_COLLECTION_RULES, '$permissions' => ['read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation.', false, ['projectDB'])
->action(function ($name, $read, $write, $rules, $response, $projectDB, $webhook, $audit) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
foreach ($rules as &$rule) {
$parsedRules[] = \array_merge([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => $read,
'write' => $write,
],
], $rule);
}
$parsedRules = [];
try {
$data = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'name' => $name,
'dateCreated' => \time(),
'dateUpdated' => \time(),
'structure' => true,
'$permissions' => [
'read' => $read,
'write' => $write,
],
'rules' => $parsedRules,
]);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB', 500);
}
if (false === $data) {
throw new Exception('Failed saving collection to DB', 500);
}
$data = $data->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.collections.create')
->setParam('resource', 'database/collection/'.$data['$id'])
->setParam('data', $data)
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
;
foreach ($rules as &$rule) {
$parsedRules[] = \array_merge([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => $read,
'write' => $write,
],
], $rule);
}
);
try {
$data = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'name' => $name,
'dateCreated' => \time(),
'dateUpdated' => \time(),
'structure' => true,
'$permissions' => [
'read' => $read,
'write' => $write,
],
'rules' => $parsedRules,
]);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB', 500);
}
if (false === $data) {
throw new Exception('Failed saving collection to DB', 500);
}
$data = $data->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.collections.create')
->setParam('resource', 'database/collection/'.$data['$id'])
->setParam('data', $data)
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
;
}, ['response', 'projectDB', 'webhook', 'audit']);
App::get('/v1/database/collections')
->desc('List Collections')
@ -112,42 +110,24 @@ App::get('/v1/database/collections')
->param('limit', 25, function () { return new Range(0, 100); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, function () { return new Range(0, 40000); }, 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', function () { return new WhiteList(['ASC', 'DESC']); }, 'Order result by ASC or DESC order.', true)
->action(
function ($search, $limit, $offset, $orderType) use ($response, $projectDB) {
/*$vl = new Structure($projectDB);
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
var_dump($vl->isValid(new Document([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => ['*'],
'write' => ['*'],
],
'label' => 'Platforms',
'key' => 'platforms',
'type' => 'document',
'default' => [],
'required' => false,
'array' => true,
'options' => [Database::SYSTEM_COLLECTION_PLATFORMS],
])));
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderField' => 'name',
'orderType' => $orderType,
'orderCast' => 'string',
'search' => $search,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_COLLECTIONS,
],
]);
var_dump($vl->getDescription());*/
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderField' => 'name',
'orderType' => $orderType,
'orderCast' => 'string',
'search' => $search,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_COLLECTIONS,
],
]);
$response->json(['sum' => $projectDB->getSum(), 'collections' => $results]);
}
);
$response->json(['sum' => $projectDB->getSum(), 'collections' => $results]);
}, ['response', 'projectDB']);
App::get('/v1/database/collections/:collectionId')
->desc('Get Collection')
@ -158,17 +138,18 @@ App::get('/v1/database/collections/:collectionId')
->label('sdk.method', 'getCollection')
->label('sdk.description', '/docs/references/database/get-collection.md')
->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
->action(
function ($collectionId) use ($response, $projectDB) {
$collection = $projectDB->getDocument($collectionId, false);
->action(function ($collectionId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$collection = $projectDB->getDocument($collectionId, false);
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$response->json($collection->getArrayCopy());
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
);
$response->json($collection->getArrayCopy());
}, ['response', 'projectDB']);
// App::get('/v1/database/collections/:collectionId/logs')
// ->desc('Get Collection Logs')
@ -249,64 +230,67 @@ App::put('/v1/database/collections/:collectionId')
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions(/docs/permissions) and get a full list of available permissions.')
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('rules', [], function () use ($projectDB) { return new ArrayList(new Collection($projectDB, [Database::SYSTEM_COLLECTION_RULES], ['$collection' => Database::SYSTEM_COLLECTION_RULES, '$permissions' => ['read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation.', true)
->action(
function ($collectionId, $name, $read, $write, $rules) use ($response, $projectDB, $webhook, $audit) {
$collection = $projectDB->getDocument($collectionId, false);
->action(function ($collectionId, $name, $read, $write, $rules, $response, $projectDB, $webhook, $audit) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$collection = $projectDB->getDocument($collectionId, false);
$parsedRules = [];
foreach ($rules as &$rule) {
$parsedRules[] = \array_merge([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => $read,
'write' => $write,
],
], $rule);
}
try {
$collection = $projectDB->updateDocument(\array_merge($collection->getArrayCopy(), [
'name' => $name,
'structure' => true,
'dateUpdated' => \time(),
'$permissions' => [
'read' => $read,
'write' => $write,
],
'rules' => $parsedRules,
]));
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB', 500);
}
if (false === $collection) {
throw new Exception('Failed saving collection to DB', 500);
}
$data = $collection->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.collections.update')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
;
$response->json($collection->getArrayCopy());
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
);
$parsedRules = [];
foreach ($rules as &$rule) {
$parsedRules[] = \array_merge([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => $read,
'write' => $write,
],
], $rule);
}
try {
$collection = $projectDB->updateDocument(\array_merge($collection->getArrayCopy(), [
'name' => $name,
'structure' => true,
'dateUpdated' => \time(),
'$permissions' => [
'read' => $read,
'write' => $write,
],
'rules' => $parsedRules,
]));
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB', 500);
}
if (false === $collection) {
throw new Exception('Failed saving collection to DB', 500);
}
$data = $collection->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.collections.update')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
;
$response->json($collection->getArrayCopy());
}, ['response', 'projectDB', 'webhook', 'audit']);
App::delete('/v1/database/collections/:collectionId')
->desc('Delete Collection')
@ -318,33 +302,36 @@ App::delete('/v1/database/collections/:collectionId')
->label('sdk.method', 'deleteCollection')
->label('sdk.description', '/docs/references/database/delete-collection.md')
->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
->action(
function ($collectionId) use ($response, $projectDB, $webhook, $audit) {
$collection = $projectDB->getDocument($collectionId, false);
->action(function ($collectionId, $response, $projectDB, $webhook, $audit) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$collection = $projectDB->getDocument($collectionId, false);
if (!$projectDB->deleteDocument($collectionId)) {
throw new Exception('Failed to remove collection from DB', 500);
}
$data = $collection->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.collections.delete')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
;
$response->noContent();
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
);
if (!$projectDB->deleteDocument($collectionId)) {
throw new Exception('Failed to remove collection from DB', 500);
}
$data = $collection->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.collections.delete')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
;
$response->noContent();
}, ['response', 'projectDB', 'webhook', 'audit']);
App::post('/v1/database/collections/:collectionId/documents')
->desc('Create Document')
@ -362,109 +349,112 @@ App::post('/v1/database/collections/:collectionId/documents')
->param('parentDocument', '', function () { return new UID(); }, 'Parent document unique ID. Use when you want your new document to be a child of a parent document.', true)
->param('parentProperty', '', function () { return new Key(); }, 'Parent document property name. Use when you want your new document to be a child of a parent document.', true)
->param('parentPropertyType', Document::SET_TYPE_ASSIGN, function () { return new WhiteList([Document::SET_TYPE_ASSIGN, Document::SET_TYPE_APPEND, Document::SET_TYPE_PREPEND]); }, 'Parent document property connection type. You can set this value to **assign**, **append** or **prepend**, default value is assign. Use when you want your new document to be a child of a parent document.', true)
->action(
function ($collectionId, $data, $read, $write, $parentDocument, $parentProperty, $parentPropertyType) use ($response, $projectDB, $webhook, $audit) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
->action(function ($collectionId, $data, $read, $write, $parentDocument, $parentProperty, $parentPropertyType, $response, $projectDB, $webhook, $audit) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data)) {
throw new Exception('Missing payload', 400);
if (empty($data)) {
throw new Exception('Missing payload', 400);
}
if (isset($data['$id'])) {
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
}
$collection = $projectDB->getDocument($collectionId, false);
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$data['$collection'] = $collectionId; // Adding this param to make API easier for developers
$data['$permissions'] = [
'read' => $read,
'write' => $write,
];
// Read parent document + validate not 404 + validate read / write permission like patch method
// Add payload to parent document property
if ((!empty($parentDocument)) && (!empty($parentProperty))) {
$parentDocument = $projectDB->getDocument($parentDocument, false);
if (empty($parentDocument->getArrayCopy())) { // Check empty
throw new Exception('No parent document found', 404);
}
if (isset($data['$id'])) {
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
}
$collection = $projectDB->getDocument($collectionId, false);
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$data['$collection'] = $collectionId; // Adding this param to make API easier for developers
$data['$permissions'] = [
'read' => $read,
'write' => $write,
];
// Read parent document + validate not 404 + validate read / write permission like patch method
// Add payload to parent document property
if ((!empty($parentDocument)) && (!empty($parentProperty))) {
$parentDocument = $projectDB->getDocument($parentDocument, false);
if (empty($parentDocument->getArrayCopy())) { // Check empty
throw new Exception('No parent document found', 404);
}
/*
* 1. Check child has valid structure,
* 2. Check user have write permission for parent document
* 3. Assign parent data (including child) to $data
* 4. Validate the combined result has valid structure (inside $projectDB->createDocument method)
*/
$new = new Document($data);
$structure = new Structure($projectDB);
if (!$structure->isValid($new)) {
throw new Exception('Invalid data structure: '.$structure->getDescription(), 400);
}
$authorization = new Authorization($parentDocument, 'write');
if (!$authorization->isValid($new->getPermissions())) {
throw new Exception('Unauthorized action', 401);
}
$parentDocument
->setAttribute($parentProperty, $data, $parentPropertyType);
$data = $parentDocument->getArrayCopy();
}
/**
* Set default collection values
*/
foreach ($collection->getAttribute('rules') as $key => $rule) {
$key = (isset($rule['key'])) ? $rule['key'] : '';
$default = (isset($rule['default'])) ? $rule['default'] : null;
if (!isset($data[$key])) {
$data[$key] = $default;
}
}
try {
$data = $projectDB->createDocument($data);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB'.$exception->getMessage(), 500);
}
$data = $data->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.documents.create')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
;
* 1. Check child has valid structure,
* 2. Check user have write permission for parent document
* 3. Assign parent data (including child) to $data
* 4. Validate the combined result has valid structure (inside $projectDB->createDocument method)
*/
$new = new Document($data);
$structure = new Structure($projectDB);
if (!$structure->isValid($new)) {
throw new Exception('Invalid data structure: '.$structure->getDescription(), 400);
}
$authorization = new Authorization($parentDocument, 'write');
if (!$authorization->isValid($new->getPermissions())) {
throw new Exception('Unauthorized action', 401);
}
$parentDocument
->setAttribute($parentProperty, $data, $parentPropertyType);
$data = $parentDocument->getArrayCopy();
}
);
/**
* Set default collection values
*/
foreach ($collection->getAttribute('rules') as $key => $rule) {
$key = (isset($rule['key'])) ? $rule['key'] : '';
$default = (isset($rule['default'])) ? $rule['default'] : null;
if (!isset($data[$key])) {
$data[$key] = $default;
}
}
try {
$data = $projectDB->createDocument($data);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB'.$exception->getMessage(), 500);
}
$data = $data->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.documents.create')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
;
}, ['response', 'projectDB', 'webhook', 'audit']);
App::get('/v1/database/collections/:collectionId/documents')
->desc('List Documents')
@ -482,49 +472,50 @@ App::get('/v1/database/collections/:collectionId/documents')
->param('orderType', 'ASC', function () { return new WhiteList(array('DESC', 'ASC')); }, 'Order direction. Possible values are DESC for descending order, or ASC for ascending order.', true)
->param('orderCast', 'string', function () { return new WhiteList(array('int', 'string', 'date', 'time', 'datetime')); }, 'Order field type casting. Possible values are int, string, date, time or datetime. The database will attempt to cast the order field to the value you pass here. The default value is a string.', true)
->param('search', '', function () { return new Text(256); }, 'Search query. Enter any free text search. The database will try to find a match against all document attributes and children.', true)
->action(
function ($collectionId, $filters, $offset, $limit, $orderField, $orderType, $orderCast, $search) use ($response, $projectDB) {
$collection = $projectDB->getDocument($collectionId, false);
->action(function ($collectionId, $filters, $offset, $limit, $orderField, $orderType, $orderCast, $search, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
$collection = $projectDB->getDocument($collectionId, false);
$list = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderField' => $orderField,
'orderType' => $orderType,
'orderCast' => $orderCast,
'search' => $search,
'filters' => \array_merge($filters, [
'$collection='.$collectionId,
]),
]);
if (App::isDevelopment()) {
$collection
->setAttribute('debug', $projectDB->getDebug())
->setAttribute('limit', $limit)
->setAttribute('offset', $offset)
->setAttribute('orderField', $orderField)
->setAttribute('orderType', $orderType)
->setAttribute('orderCast', $orderCast)
->setAttribute('filters', $filters)
;
}
$collection
->setAttribute('sum', $projectDB->getSum())
->setAttribute('documents', $list)
;
/*
* View
*/
$response->json($collection->getArrayCopy(/*['$id', '$collection', 'name', 'documents']*/[], ['rules']));
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
);
$list = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderField' => $orderField,
'orderType' => $orderType,
'orderCast' => $orderCast,
'search' => $search,
'filters' => \array_merge($filters, [
'$collection='.$collectionId,
]),
]);
if (App::isDevelopment()) {
$collection
->setAttribute('debug', $projectDB->getDebug())
->setAttribute('limit', $limit)
->setAttribute('offset', $offset)
->setAttribute('orderField', $orderField)
->setAttribute('orderType', $orderType)
->setAttribute('orderCast', $orderCast)
->setAttribute('filters', $filters)
;
}
$collection
->setAttribute('sum', $projectDB->getSum())
->setAttribute('documents', $list)
;
/*
* View
*/
$response->json($collection->getArrayCopy(/*['$id', '$collection', 'name', 'documents']*/[], ['rules']));
}, ['response', 'projectDB']);
App::get('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Get Document')
@ -536,41 +527,43 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.description', '/docs/references/database/get-document.md')
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
->action(
function ($collectionId, $documentId) use ($response, $request, $projectDB) {
$document = $projectDB->getDocument($documentId, false);
$collection = $projectDB->getDocument($collectionId, false);
->action(function ($collectionId, $documentId, $request, $response, $projectDB) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($document->getArrayCopy()) || $document->getCollection() != $collection->getId()) { // Check empty
$document = $projectDB->getDocument($documentId, false);
$collection = $projectDB->getDocument($collectionId, false);
if (empty($document->getArrayCopy()) || $document->getCollection() != $collection->getId()) { // Check empty
throw new Exception('No document found', 404);
}
$output = $document->getArrayCopy();
$paths = \explode('/', $request->getParam('q', ''));
$paths = \array_slice($paths, 7, \count($paths));
if (\count($paths) > 0) {
if (\count($paths) % 2 == 1) {
$output = $document->getAttribute(\implode('.', $paths));
} else {
$id = (int) \array_pop($paths);
$output = $document->search('$id', $id, $document->getAttribute(\implode('.', $paths)));
}
$output = ($output instanceof Document) ? $output->getArrayCopy() : $output;
if (!\is_array($output)) {
throw new Exception('No document found', 404);
}
$output = $document->getArrayCopy();
$paths = \explode('/', $request->getParam('q', ''));
$paths = \array_slice($paths, 7, \count($paths));
if (\count($paths) > 0) {
if (\count($paths) % 2 == 1) {
$output = $document->getAttribute(\implode('.', $paths));
} else {
$id = (int) \array_pop($paths);
$output = $document->search('$id', $id, $document->getAttribute(\implode('.', $paths)));
}
$output = ($output instanceof Document) ? $output->getArrayCopy() : $output;
if (!\is_array($output)) {
throw new Exception('No document found', 404);
}
}
/*
* View
*/
$response->json($output);
}
);
/*
* View
*/
$response->json($output);
}, ['request', 'response', 'projectDB']);
App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Update Document')
@ -586,71 +579,74 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->param('data', [], function () { return new JSON(); }, 'Document data as JSON object.')
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->action(
function ($collectionId, $documentId, $data, $read, $write) use ($response, $projectDB, $webhook, $audit) {
$collection = $projectDB->getDocument($collectionId, false);
$document = $projectDB->getDocument($documentId, false);
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $projectDB, $webhook, $audit) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
$collection = $projectDB->getDocument($collectionId, false);
$document = $projectDB->getDocument($documentId, false);
if (!\is_array($data)) {
throw new Exception('Data param should be a valid JSON', 400);
}
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
if (empty($document->getArrayCopy()) || $document->getCollection() != $collectionId) { // Check empty
throw new Exception('No document found', 404);
}
//TODO check merge read write permissions
if (!empty($read)) { // Overwrite permissions only when passed
$data['$permissions']['read'] = $read;
}
if (!empty($write)) { // Overwrite permissions only when passed
$data['$permissions']['write'] = $read;
}
$data = \array_merge($document->getArrayCopy(), $data);
$data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID
$data['$id'] = $document->getId(); // Make sure user don't switch document unique ID
if (empty($data)) {
throw new Exception('Missing payload', 400);
}
try {
$data = $projectDB->updateDocument($data);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB', 500);
}
$data = $data->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.documents.update')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
;
/*
* View
*/
$response->json($data);
if (!\is_array($data)) {
throw new Exception('Data param should be a valid JSON', 400);
}
);
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
if (empty($document->getArrayCopy()) || $document->getCollection() != $collectionId) { // Check empty
throw new Exception('No document found', 404);
}
//TODO check merge read write permissions
if (!empty($read)) { // Overwrite permissions only when passed
$data['$permissions']['read'] = $read;
}
if (!empty($write)) { // Overwrite permissions only when passed
$data['$permissions']['write'] = $read;
}
$data = \array_merge($document->getArrayCopy(), $data);
$data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID
$data['$id'] = $document->getId(); // Make sure user don't switch document unique ID
if (empty($data)) {
throw new Exception('Missing payload', 400);
}
try {
$data = $projectDB->updateDocument($data);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed saving document to DB', 500);
}
$data = $data->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.documents.update')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
;
/*
* View
*/
$response->json($data);
}, ['response', 'projectDB', 'webhook', 'audit']);
App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Delete Document')
@ -663,41 +659,44 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.description', '/docs/references/database/delete-document.md')
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
->action(
function ($collectionId, $documentId) use ($response, $projectDB, $audit, $webhook) {
$collection = $projectDB->getDocument($collectionId, false);
$document = $projectDB->getDocument($documentId, false);
->action(function ($collectionId, $documentId, $response, $projectDB, $webhook, $audit) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
if (empty($document->getArrayCopy()) || $document->getCollection() != $collectionId) { // Check empty
throw new Exception('No document found', 404);
}
$collection = $projectDB->getDocument($collectionId, false);
$document = $projectDB->getDocument($documentId, false);
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
try {
$projectDB->deleteDocument($documentId);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed to remove document from DB', 500);
}
$data = $document->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.documents.delete')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data) // Audit document in case of malicious or disastrous action
;
$response->noContent();
if (empty($document->getArrayCopy()) || $document->getCollection() != $collectionId) { // Check empty
throw new Exception('No document found', 404);
}
);
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
try {
$projectDB->deleteDocument($documentId);
} catch (AuthorizationException $exception) {
throw new Exception('Unauthorized action', 401);
} catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
} catch (\Exception $exception) {
throw new Exception('Failed to remove document from DB', 500);
}
$data = $document->getArrayCopy();
$webhook
->setParam('payload', $data)
;
$audit
->setParam('event', 'database.documents.delete')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data) // Audit document in case of malicious or disastrous action
;
$response->noContent();
}, ['response', 'projectDB', 'webhook', 'audit']);

View file

@ -1,7 +1,5 @@
<?php
global $request, $response, $register;
use Utopia\App;
use Utopia\Exception;
use Appwrite\Storage\Device\Local;
@ -16,21 +14,21 @@ App::get('/v1/health')
->label('sdk.namespace', 'health')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/health/get.md')
->action(
function () use ($response) {
$response->json(['status' => 'OK']);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['status' => 'OK']);
}, ['response']);
App::get('/v1/health/version')
->desc('Get Version')
->groups(['api', 'health'])
->label('scope', 'public')
->action(
function () use ($response) {
$response->json(['version' => APP_VERSION_STABLE]);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['version' => APP_VERSION_STABLE]);
}, ['response']);
App::get('/v1/health/db')
->desc('Get DB')
@ -40,13 +38,14 @@ App::get('/v1/health/db')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getDB')
->label('sdk.description', '/docs/references/health/get-db.md')
->action(
function () use ($response, $register) {
$register->get('db'); /* @var $db PDO */
->action(function ($response, $register) {
/** @var Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
$response->json(['status' => 'OK']);
}
);
$register->get('db'); /* @var $db PDO */
$response->json(['status' => 'OK']);
}, ['response', 'register']);
App::get('/v1/health/cache')
->desc('Get Cache')
@ -56,13 +55,13 @@ App::get('/v1/health/cache')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getCache')
->label('sdk.description', '/docs/references/health/get-cache.md')
->action(
function () use ($response, $register) {
$register->get('cache'); /* @var $cache Predis\Client */
->action(function ($response, $register) {
/** @var Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
$register->get('cache'); /* @var $cache Predis\Client */
$response->json(['status' => 'OK']);
}
);
$response->json(['status' => 'OK']);
}, ['response']);
App::get('/v1/health/time')
->desc('Get Time')
@ -72,45 +71,45 @@ App::get('/v1/health/time')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getTime')
->label('sdk.description', '/docs/references/health/get-time.md')
->action(
function () use ($response) {
/*
* Code from: @see https://www.beliefmedia.com.au/query-ntp-time-server
*/
$host = 'time.google.com'; // https://developers.google.com/time/
$gap = 60; // Allow [X] seconds gap
->action(function ($response) {
/** @var Utopia\Response $response */
/* Create a socket and connect to NTP server */
$sock = \socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
/*
* Code from: @see https://www.beliefmedia.com.au/query-ntp-time-server
*/
$host = 'time.google.com'; // https://developers.google.com/time/
$gap = 60; // Allow [X] seconds gap
\socket_connect($sock, $host, 123);
/* Create a socket and connect to NTP server */
$sock = \socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
/* Send request */
$msg = "\010".\str_repeat("\0", 47);
\socket_connect($sock, $host, 123);
\socket_send($sock, $msg, \strlen($msg), 0);
/* Send request */
$msg = "\010".\str_repeat("\0", 47);
/* Receive response and close socket */
\socket_recv($sock, $recv, 48, MSG_WAITALL);
\socket_close($sock);
\socket_send($sock, $msg, \strlen($msg), 0);
/* Interpret response */
$data = \unpack('N12', $recv);
$timestamp = \sprintf('%u', $data[9]);
/* Receive response and close socket */
\socket_recv($sock, $recv, 48, MSG_WAITALL);
\socket_close($sock);
/* NTP is number of seconds since 0000 UT on 1 January 1900
Unix time is seconds since 0000 UT on 1 January 1970 */
$timestamp -= 2208988800;
/* Interpret response */
$data = \unpack('N12', $recv);
$timestamp = \sprintf('%u', $data[9]);
$diff = ($timestamp - \time());
/* NTP is number of seconds since 0000 UT on 1 January 1900
Unix time is seconds since 0000 UT on 1 January 1970 */
$timestamp -= 2208988800;
if ($diff > $gap || $diff < ($gap * -1)) {
throw new Exception('Server time gaps detected');
}
$diff = ($timestamp - \time());
$response->json(['remote' => $timestamp, 'local' => \time(), 'diff' => $diff]);
if ($diff > $gap || $diff < ($gap * -1)) {
throw new Exception('Server time gaps detected');
}
);
$response->json(['remote' => $timestamp, 'local' => \time(), 'diff' => $diff]);
}, ['response']);
App::get('/v1/health/queue/webhooks')
->desc('Get Webhooks Queue')
@ -120,11 +119,11 @@ App::get('/v1/health/queue/webhooks')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueWebhooks')
->label('sdk.description', '/docs/references/health/get-queue-webhooks.md')
->action(
function () use ($response) {
$response->json(['size' => Resque::size('v1-webhooks')]);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['size' => Resque::size('v1-webhooks')]);
}, ['response']);
App::get('/v1/health/queue/tasks')
->desc('Get Tasks Queue')
@ -134,11 +133,11 @@ App::get('/v1/health/queue/tasks')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueTasks')
->label('sdk.description', '/docs/references/health/get-queue-tasks.md')
->action(
function () use ($response) {
$response->json(['size' => Resque::size('v1-tasks')]);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['size' => Resque::size('v1-tasks')]);
}, ['response']);
App::get('/v1/health/queue/logs')
->desc('Get Logs Queue')
@ -148,11 +147,11 @@ App::get('/v1/health/queue/logs')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueLogs')
->label('sdk.description', '/docs/references/health/get-queue-logs.md')
->action(
function () use ($response) {
$response->json(['size' => Resque::size('v1-audit')]);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['size' => Resque::size('v1-audit')]);
}, ['response']);
App::get('/v1/health/queue/usage')
->desc('Get Usage Queue')
@ -162,11 +161,11 @@ App::get('/v1/health/queue/usage')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueUsage')
->label('sdk.description', '/docs/references/health/get-queue-usage.md')
->action(
function () use ($response) {
$response->json(['size' => Resque::size('v1-usage')]);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['size' => Resque::size('v1-usage')]);
}, ['response']);
App::get('/v1/health/queue/certificates')
->desc('Get Certificate Queue')
@ -176,11 +175,11 @@ App::get('/v1/health/queue/certificates')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueCertificates')
->label('sdk.description', '/docs/references/health/get-queue-certificates.md')
->action(
function () use ($response) {
$response->json(['size' => Resque::size('v1-certificates')]);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['size' => Resque::size('v1-certificates')]);
}, ['response']);
App::get('/v1/health/queue/functions')
->desc('Get Functions Queue')
@ -190,11 +189,11 @@ App::get('/v1/health/queue/functions')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueFunctions')
->label('sdk.description', '/docs/references/health/get-queue-functions.md')
->action(
function () use ($response) {
$response->json(['size' => Resque::size('v1-functions')]);
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json(['size' => Resque::size('v1-functions')]);
}, ['response']);
App::get('/v1/health/storage/local')
->desc('Get Local Storage')
@ -204,28 +203,28 @@ App::get('/v1/health/storage/local')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getStorageLocal')
->label('sdk.description', '/docs/references/health/get-storage-local.md')
->action(
function () use ($response) {
foreach ([
'Uploads' => APP_STORAGE_UPLOADS,
'Cache' => APP_STORAGE_CACHE,
'Config' => APP_STORAGE_CONFIG,
'Certs' => APP_STORAGE_CERTIFICATES
] as $key => $volume) {
$device = new Local($volume);
->action(function ($response) {
/** @var Utopia\Response $response */
if (!\is_readable($device->getRoot())) {
throw new Exception('Device '.$key.' dir is not readable');
}
foreach ([
'Uploads' => APP_STORAGE_UPLOADS,
'Cache' => APP_STORAGE_CACHE,
'Config' => APP_STORAGE_CONFIG,
'Certs' => APP_STORAGE_CERTIFICATES
] as $key => $volume) {
$device = new Local($volume);
if (!\is_writable($device->getRoot())) {
throw new Exception('Device '.$key.' dir is not writable');
}
if (!\is_readable($device->getRoot())) {
throw new Exception('Device '.$key.' dir is not readable');
}
$response->json(['status' => 'OK']);
if (!\is_writable($device->getRoot())) {
throw new Exception('Device '.$key.' dir is not writable');
}
}
);
$response->json(['status' => 'OK']);
}, ['response']);
App::get('/v1/health/anti-virus')
->desc('Get Anti virus')
@ -235,20 +234,20 @@ App::get('/v1/health/anti-virus')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getAntiVirus')
->label('sdk.description', '/docs/references/health/get-storage-anti-virus.md')
->action(
function () use ($response) {
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
throw new Exception('Anitvirus is disabled');
}
->action(function ($response) {
/** @var Utopia\Response $response */
$antiVirus = new Network('clamav', 3310);
$response->json([
'status' => (@$antiVirus->ping()) ? 'online' : 'offline',
'version' => @$antiVirus->version(),
]);
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
throw new Exception('Anitvirus is disabled');
}
);
$antiVirus = new Network('clamav', 3310);
$response->json([
'status' => (@$antiVirus->ping()) ? 'online' : 'offline',
'version' => @$antiVirus->version(),
]);
}, ['response']);
App::get('/v1/health/stats') // Currently only used internally
->desc('Get System Stats')
@ -258,34 +257,35 @@ App::get('/v1/health/stats') // Currently only used internally
// ->label('sdk.namespace', 'health')
// ->label('sdk.method', 'getStats')
->label('docs', false)
->action(
function () use ($response, $register) {
$device = Storage::getDevice('local');
$cache = $register->get('cache');
->action(function ($response, $register) {
/** @var Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
$cacheStats = $cache->info();
$device = Storage::getDevice('local');
$cache = $register->get('cache');
$response
->json([
'server' => [
'name' => 'nginx',
'version' => \shell_exec('nginx -v 2>&1'),
],
'storage' => [
'used' => Storage::human($device->getDirectorySize($device->getRoot().'/')),
'partitionTotal' => Storage::human($device->getPartitionTotalSpace()),
'partitionFree' => Storage::human($device->getPartitionFreeSpace()),
],
'cache' => [
'uptime' => (isset($cacheStats['uptime_in_seconds'])) ? $cacheStats['uptime_in_seconds'] : 0,
'clients' => (isset($cacheStats['connected_clients'])) ? $cacheStats['connected_clients'] : 0,
'hits' => (isset($cacheStats['keyspace_hits'])) ? $cacheStats['keyspace_hits'] : 0,
'misses' => (isset($cacheStats['keyspace_misses'])) ? $cacheStats['keyspace_misses'] : 0,
'memory_used' => (isset($cacheStats['used_memory'])) ? $cacheStats['used_memory'] : 0,
'memory_used_human' => (isset($cacheStats['used_memory_human'])) ? $cacheStats['used_memory_human'] : 0,
'memory_used_peak' => (isset($cacheStats['used_memory_peak'])) ? $cacheStats['used_memory_peak'] : 0,
'memory_used_peak_human' => (isset($cacheStats['used_memory_peak_human'])) ? $cacheStats['used_memory_peak_human'] : 0,
],
]);
}
);
$cacheStats = $cache->info();
$response
->json([
'server' => [
'name' => 'nginx',
'version' => \shell_exec('nginx -v 2>&1'),
],
'storage' => [
'used' => Storage::human($device->getDirectorySize($device->getRoot().'/')),
'partitionTotal' => Storage::human($device->getPartitionTotalSpace()),
'partitionFree' => Storage::human($device->getPartitionFreeSpace()),
],
'cache' => [
'uptime' => (isset($cacheStats['uptime_in_seconds'])) ? $cacheStats['uptime_in_seconds'] : 0,
'clients' => (isset($cacheStats['connected_clients'])) ? $cacheStats['connected_clients'] : 0,
'hits' => (isset($cacheStats['keyspace_hits'])) ? $cacheStats['keyspace_hits'] : 0,
'misses' => (isset($cacheStats['keyspace_misses'])) ? $cacheStats['keyspace_misses'] : 0,
'memory_used' => (isset($cacheStats['used_memory'])) ? $cacheStats['used_memory'] : 0,
'memory_used_human' => (isset($cacheStats['used_memory_human'])) ? $cacheStats['used_memory_human'] : 0,
'memory_used_peak' => (isset($cacheStats['used_memory_peak'])) ? $cacheStats['used_memory_peak'] : 0,
'memory_used_peak_human' => (isset($cacheStats['used_memory_peak_human'])) ? $cacheStats['used_memory_peak_human'] : 0,
],
]);
}, ['response', 'register']);

View file

@ -1,9 +1,6 @@
<?php
global $request, $response, $user;
use Utopia\App;
use Utopia\Locale\Locale;
use GeoIp2\Database\Reader;
App::get('/v1/locale')
@ -14,56 +11,58 @@ App::get('/v1/locale')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/locale/get-locale.md')
->action(
function () use ($response, $request) {
$eu = include __DIR__.'/../../config/eu.php';
$currencies = include __DIR__.'/../../config/currencies.php';
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
$output = [];
$ip = $request->getIP();
$time = (60 * 60 * 24 * 45); // 45 days cache
$countries = Locale::getText('countries');
$continents = Locale::getText('continents');
->action(function ($request, $response, $locale) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
if (!App::isProduction()) {
$ip = '79.177.241.94';
}
$eu = include __DIR__.'/../../config/eu.php';
$currencies = include __DIR__.'/../../config/currencies.php';
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
$output = [];
$ip = $request->getIP();
$time = (60 * 60 * 24 * 45); // 45 days cache
$countries = $locale->getText('countries');
$continents = $locale->getText('continents');
$output['ip'] = $ip;
$currency = null;
try {
$record = $reader->country($ip);
$output['countryCode'] = $record->country->isoCode;
$output['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
//$output['countryTimeZone'] = DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $record->country->isoCode);
$output['continent'] = (isset($continents[$record->continent->code])) ? $continents[$record->continent->code] : Locale::getText('locale.country.unknown');
$output['continentCode'] = $record->continent->code;
$output['eu'] = (\in_array($record->country->isoCode, $eu)) ? true : false;
foreach ($currencies as $code => $element) {
if (isset($element['locations']) && isset($element['code']) && \in_array($record->country->isoCode, $element['locations'])) {
$currency = $element['code'];
}
}
$output['currency'] = $currency;
} catch (\Exception $e) {
$output['countryCode'] = '--';
$output['country'] = Locale::getText('locale.country.unknown');
$output['continent'] = Locale::getText('locale.country.unknown');
$output['continentCode'] = '--';
$output['eu'] = false;
$output['currency'] = $currency;
}
$response
->addHeader('Cache-Control', 'public, max-age='.$time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->json($output);
if (!App::isProduction()) {
$ip = '79.177.241.94';
}
);
$output['ip'] = $ip;
$currency = null;
try {
$record = $reader->country($ip);
$output['countryCode'] = $record->country->isoCode;
$output['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : $locale->getText('locale.country.unknown');
//$output['countryTimeZone'] = DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $record->country->isoCode);
$output['continent'] = (isset($continents[$record->continent->code])) ? $continents[$record->continent->code] : $locale->getText('locale.country.unknown');
$output['continentCode'] = $record->continent->code;
$output['eu'] = (\in_array($record->country->isoCode, $eu)) ? true : false;
foreach ($currencies as $code => $element) {
if (isset($element['locations']) && isset($element['code']) && \in_array($record->country->isoCode, $element['locations'])) {
$currency = $element['code'];
}
}
$output['currency'] = $currency;
} catch (\Exception $e) {
$output['countryCode'] = '--';
$output['country'] = $locale->getText('locale.country.unknown');
$output['continent'] = $locale->getText('locale.country.unknown');
$output['continentCode'] = '--';
$output['eu'] = false;
$output['currency'] = $currency;
}
$response
->addHeader('Cache-Control', 'public, max-age='.$time)
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->json($output);
}, ['request', 'response', 'locale']);
App::get('/v1/locale/countries')
->desc('List Countries')
@ -73,15 +72,16 @@ App::get('/v1/locale/countries')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountries')
->label('sdk.description', '/docs/references/locale/get-countries.md')
->action(
function () use ($response) {
$list = Locale::getText('countries'); /* @var $list array */
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
\asort($list);
$list = $locale->getText('countries'); /* @var $list array */
$response->json($list);
}
);
\asort($list);
$response->json($list);
}, ['response', 'locale']);
App::get('/v1/locale/countries/eu')
->desc('List EU Countries')
@ -91,23 +91,24 @@ App::get('/v1/locale/countries/eu')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountriesEU')
->label('sdk.description', '/docs/references/locale/get-countries-eu.md')
->action(
function () use ($response) {
$countries = Locale::getText('countries'); /* @var $countries array */
$eu = include __DIR__.'/../../config/eu.php';
$list = [];
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
foreach ($eu as $code) {
if (\array_key_exists($code, $countries)) {
$list[$code] = $countries[$code];
}
$countries = $locale->getText('countries'); /* @var $countries array */
$eu = include __DIR__.'/../../config/eu.php';
$list = [];
foreach ($eu as $code) {
if (\array_key_exists($code, $countries)) {
$list[$code] = $countries[$code];
}
\asort($list);
$response->json($list);
}
);
\asort($list);
$response->json($list);
}, ['response', 'locale']);
App::get('/v1/locale/countries/phones')
->desc('List Countries Phone Codes')
@ -117,23 +118,24 @@ App::get('/v1/locale/countries/phones')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountriesPhones')
->label('sdk.description', '/docs/references/locale/get-countries-phones.md')
->action(
function () use ($response) {
$list = include __DIR__.'/../../config/phones.php'; /* @var $list array */
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
$countries = Locale::getText('countries'); /* @var $countries array */
$list = include __DIR__.'/../../config/phones.php'; /* @var $list array */
foreach ($list as $code => $name) {
if (\array_key_exists($code, $countries)) {
$list[$code] = '+'.$list[$code];
}
$countries = $locale->getText('countries'); /* @var $countries array */
foreach ($list as $code => $name) {
if (\array_key_exists($code, $countries)) {
$list[$code] = '+'.$list[$code];
}
\asort($list);
$response->json($list);
}
);
\asort($list);
$response->json($list);
}, ['response', 'locale']);
App::get('/v1/locale/continents')
->desc('List Continents')
@ -143,15 +145,16 @@ App::get('/v1/locale/continents')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getContinents')
->label('sdk.description', '/docs/references/locale/get-continents.md')
->action(
function () use ($response) {
$list = Locale::getText('continents'); /* @var $list array */
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
\asort($list);
$list = $locale->getText('continents'); /* @var $list array */
$response->json($list);
}
);
\asort($list);
$response->json($list);
}, ['response', 'locale']);
App::get('/v1/locale/currencies')
@ -162,13 +165,13 @@ App::get('/v1/locale/currencies')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCurrencies')
->label('sdk.description', '/docs/references/locale/get-currencies.md')
->action(
function () use ($response) {
$currencies = include __DIR__.'/../../config/currencies.php';
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json($currencies);
}
);
$currencies = include __DIR__.'/../../config/currencies.php';
$response->json($currencies);
}, ['response']);
App::get('/v1/locale/languages')
@ -179,10 +182,10 @@ App::get('/v1/locale/languages')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getLanguages')
->label('sdk.description', '/docs/references/locale/get-languages.md')
->action(
function () use ($response) {
$languages = include __DIR__.'/../../config/languages.php';
->action(function ($response) {
/** @var Utopia\Response $response */
$response->json($languages);
}
);
$languages = include __DIR__.'/../../config/languages.php';
$response->json($languages);
}, ['response']);

View file

@ -1,7 +1,5 @@
<?php
global $response, $register, $user, $consoleDB, $projectDB, $deletes;
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;

View file

@ -1,7 +1,5 @@
<?php
global $request, $response, $user, $audit, $usage, $project, $projectDB;
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;
@ -23,48 +21,7 @@ use Appwrite\Storage\Validator\Upload;
use Appwrite\Storage\Compression\Algorithms\GZIP;
use Appwrite\Resize\Resize;
use Appwrite\OpenSSL\OpenSSL;
$fileLogos = [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
'default' => __DIR__.'/../../config/files/none.png',
// Video Files
'video/mp4' => __DIR__.'/../../config/files/video.png',
'video/x-flv' => __DIR__.'/../../config/files/video.png',
'application/x-mpegURL' => __DIR__.'/../../config/files/video.png',
'video/MP2T' => __DIR__.'/../../config/files/video.png',
'video/3gpp' => __DIR__.'/../../config/files/video.png',
'video/quicktime' => __DIR__.'/../../config/files/video.png',
'video/x-msvideo' => __DIR__.'/../../config/files/video.png',
'video/x-ms-wmv' => __DIR__.'/../../config/files/video.png',
// // Microsoft Word
'application/msword' => __DIR__.'/../../config/files/word.png',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => __DIR__.'/../../config/files/word.png',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => __DIR__.'/../../config/files/word.png',
'application/vnd.ms-word.document.macroEnabled.12' => __DIR__.'/../../config/files/word.png',
// // Microsoft Excel
'application/vnd.ms-excel' => __DIR__.'/../../config/files/excel.png',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => __DIR__.'/../../config/files/excel.png',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => __DIR__.'/../../config/files/excel.png',
'application/vnd.ms-excel.sheet.macroEnabled.12' => __DIR__.'/../../config/files/excel.png',
'application/vnd.ms-excel.template.macroEnabled.12' => __DIR__.'/../../config/files/excel.png',
'application/vnd.ms-excel.addin.macroEnabled.12' => __DIR__.'/../../config/files/excel.png',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => __DIR__.'/../../config/files/excel.png',
// // Microsoft Power Point
'application/vnd.ms-powerpoint' => __DIR__.'/../../config/files/ppt.png',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => __DIR__.'/../../config/files/ppt.png',
'application/vnd.openxmlformats-officedocument.presentationml.template' => __DIR__.'/../../config/files/ppt.png',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => __DIR__.'/../../config/files/ppt.png',
'application/vnd.ms-powerpoint.addin.macroEnabled.12' => __DIR__.'/../../config/files/ppt.png',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => __DIR__.'/../../config/files/ppt.png',
'application/vnd.ms-powerpoint.template.macroEnabled.12' => __DIR__.'/../../config/files/ppt.png',
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => __DIR__.'/../../config/files/ppt.png',
// Adobe PDF
'application/pdf' => __DIR__.'/../../config/files/pdf.png',
];
use Utopia\Config\Config;
$inputs = [
'jpg' => 'image/jpeg',
@ -81,58 +38,9 @@ $outputs = [
'webp' => 'image/webp',
];
$mimes = [
'image/jpeg',
'image/jpeg',
'image/gif',
'image/png',
'image/webp',
// Video Files
'video/mp4',
'video/x-flv',
'application/x-mpegURL',
'video/MP2T',
'video/3gpp',
'video/quicktime',
'video/x-msvideo',
'video/x-ms-wmv',
// Microsoft Word
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'application/vnd.ms-word.document.macroEnabled.12',
// Microsoft Excel
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'application/vnd.ms-excel.sheet.macroEnabled.12',
'application/vnd.ms-excel.template.macroEnabled.12',
'application/vnd.ms-excel.addin.macroEnabled.12',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
// Microsoft Power Point
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.openxmlformats-officedocument.presentationml.template',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.ms-powerpoint.template.macroEnabled.12',
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
// Microsoft Access
'application/vnd.ms-access',
// Adobe PDF
'application/pdf',
];
App::init(function () use ($project) {
App::init(function ($project) {
Storage::addDevice('local', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
}, 'storage');
}, ['project'], 'storage');
App::post('/v1/storage/files')
->desc('Create File')
@ -148,128 +56,133 @@ App::post('/v1/storage/files')
->param('file', [], function () { return new File(); }, 'Binary File.', false)
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
// ->param('folderId', '', function () { return new UID(); }, 'Folder to associate files with.', true)
->action(
function ($file, $read, $write, $folderId = '') use ($request, $response, $user, $projectDB, $webhook, $audit, $usage) {
$file = $request->getFiles('file');
$read = (empty($read)) ? ['user:'.$user->getId()] : $read;
$write = (empty($write)) ? ['user:'.$user->getId()] : $write;
->action(function ($file, $read, $write, $request, $response, $user, $projectDB, $webhook, $audit, $usage) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
/** @var Appwrite\Event\Event $usage */
/*
* Validators
*/
//$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG));
$fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
$upload = new Upload();
$file = $request->getFiles('file');
$read = (empty($read)) ? ['user:'.$user->getId()] : $read;
$write = (empty($write)) ? ['user:'.$user->getId()] : $write;
if (empty($file)) {
throw new Exception('No file sent', 400);
}
/*
* Validators
*/
//$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG));
$fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
$upload = new Upload();
// Make sure we handle a single file and multiple files the same way
$file['name'] = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
$file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
$file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
if (empty($file)) {
throw new Exception('No file sent', 400);
}
// Check if file type is allowed (feature for project settings?)
//if (!$fileType->isValid($file['tmp_name'])) {
//throw new Exception('File type not allowed', 400);
//}
// Make sure we handle a single file and multiple files the same way
$file['name'] = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
$file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
$file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
// Check if file type is allowed (feature for project settings?)
//if (!$fileType->isValid($file['tmp_name'])) {
//throw new Exception('File type not allowed', 400);
//}
// Check if file size is exceeding allowed limit
if (!$fileSize->isValid($file['size'])) {
throw new Exception('File size not allowed', 400);
}
/*
* Models
*/
$device = Storage::getDevice('local');
if (!$upload->isValid($file['tmp_name'])) {
throw new Exception('Invalid file', 403);
}
// Save to storage
$size = $device->getFileSize($file['tmp_name']);
$path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION));
if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move'
throw new Exception('Failed moving file', 500);
}
$mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
$antiVirus = new Network('clamav', 3310);
// Check if file size is exceeding allowed limit
if (!$fileSize->isValid($file['size'])) {
throw new Exception('File size not allowed', 400);
}
/*
* Models
*/
$device = Storage::getDevice('local');
if (!$upload->isValid($file['tmp_name'])) {
if (!$antiVirus->fileScan($path)) {
$device->delete($path);
throw new Exception('Invalid file', 403);
}
// Save to storage
$size = $device->getFileSize($file['tmp_name']);
$path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION));
if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move'
throw new Exception('Failed moving file', 500);
}
$mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
$antiVirus = new Network('clamav', 3310);
// Check if file size is exceeding allowed limit
if (!$antiVirus->fileScan($path)) {
$device->delete($path);
throw new Exception('Invalid file', 403);
}
}
// Compression
$compressor = new GZIP();
$data = $device->read($path);
$data = $compressor->compress($data);
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
if (!$device->write($path, $data)) {
throw new Exception('Failed to save file', 500);
}
$sizeActual = $device->getFileSize($path);
$file = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_FILES,
'$permissions' => [
'read' => $read,
'write' => $write,
],
'dateCreated' => \time(),
'folderId' => $folderId,
'name' => $file['name'],
'path' => $path,
'signature' => $device->getFileHash($path),
'mimeType' => $mimeType,
'sizeOriginal' => $size,
'sizeActual' => $sizeActual,
'algorithm' => $compressor->getName(),
'token' => \bin2hex(\random_bytes(64)),
'comment' => '',
'fileOpenSSLVersion' => '1',
'fileOpenSSLCipher' => OpenSSL::CIPHER_AES_128_GCM,
'fileOpenSSLTag' => \bin2hex($tag),
'fileOpenSSLIV' => \bin2hex($iv),
]);
if (false === $file) {
throw new Exception('Failed saving file to DB', 500);
}
$webhook
->setParam('payload', $file->getArrayCopy())
;
$audit
->setParam('event', 'storage.files.create')
->setParam('resource', 'storage/files/'.$file->getId())
;
$usage
->setParam('storage', $sizeActual)
;
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($file->getArrayCopy())
;
}
);
// Compression
$compressor = new GZIP();
$data = $device->read($path);
$data = $compressor->compress($data);
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
if (!$device->write($path, $data)) {
throw new Exception('Failed to save file', 500);
}
$sizeActual = $device->getFileSize($path);
$file = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_FILES,
'$permissions' => [
'read' => $read,
'write' => $write,
],
'dateCreated' => \time(),
'folderId' => '',
'name' => $file['name'],
'path' => $path,
'signature' => $device->getFileHash($path),
'mimeType' => $mimeType,
'sizeOriginal' => $size,
'sizeActual' => $sizeActual,
'algorithm' => $compressor->getName(),
'token' => \bin2hex(\random_bytes(64)),
'comment' => '',
'fileOpenSSLVersion' => '1',
'fileOpenSSLCipher' => OpenSSL::CIPHER_AES_128_GCM,
'fileOpenSSLTag' => \bin2hex($tag),
'fileOpenSSLIV' => \bin2hex($iv),
]);
if (false === $file) {
throw new Exception('Failed saving file to DB', 500);
}
$webhook
->setParam('payload', $file->getArrayCopy())
;
$audit
->setParam('event', 'storage.files.create')
->setParam('resource', 'storage/files/'.$file->getId())
;
$usage
->setParam('storage', $sizeActual)
;
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($file->getArrayCopy())
;
}, ['request', 'response', 'user', 'projectDB', 'webhook', 'audit', 'usage']);
App::get('/v1/storage/files')
->desc('List Files')
@ -283,27 +196,28 @@ App::get('/v1/storage/files')
->param('limit', 25, function () { return new Range(0, 100); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, function () { return new Range(0, 2000); }, 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', function () { return new WhiteList(['ASC', 'DESC']); }, 'Order result by ASC or DESC order.', true)
->action(
function ($search, $limit, $offset, $orderType) use ($response, $projectDB) {
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderField' => 'dateCreated',
'orderType' => $orderType,
'orderCast' => 'int',
'search' => $search,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_FILES,
],
]);
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$results = \array_map(function ($value) { /* @var $value \Database\Document */
return $value->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']);
}, $results);
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderField' => 'dateCreated',
'orderType' => $orderType,
'orderCast' => 'int',
'search' => $search,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_FILES,
],
]);
$response->json(['sum' => $projectDB->getSum(), 'files' => $results]);
}
);
$results = \array_map(function ($value) { /* @var $value \Database\Document */
return $value->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']);
}, $results);
$response->json(['sum' => $projectDB->getSum(), 'files' => $results]);
}, ['response', 'projectDB']);
App::get('/v1/storage/files/:fileId')
->desc('Get File')
@ -314,17 +228,18 @@ App::get('/v1/storage/files/:fileId')
->label('sdk.method', 'getFile')
->label('sdk.description', '/docs/references/storage/get-file.md')
->param('fileId', '', function () { return new UID(); }, 'File unique ID.')
->action(
function ($fileId) use ($response, $projectDB) {
$file = $projectDB->getDocument($fileId);
->action(function ($fileId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
$file = $projectDB->getDocument($fileId);
$response->json($file->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']));
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
);
$response->json($file->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']));
}, ['response', 'projectDB']);
App::get('/v1/storage/files/:fileId/preview')
->desc('Get File Preview')
@ -343,7 +258,7 @@ App::get('/v1/storage/files/:fileId/preview')
->param('background', '', function () { return new HexColor(); }, 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true)
->param('output', null, function () use ($outputs) { return new WhiteList(\array_merge(\array_keys($outputs), [null])); }, 'Output format type (jpeg, jpg, png, gif and webp).', true)
->action(
function ($fileId, $width, $height, $quality, $background, $output) use ($request, $response, $projectDB, $project, $inputs, $outputs, $fileLogos) {
function ($fileId, $width, $height, $quality, $background, $output) use ($request, $response, $projectDB, $project, $inputs, $outputs) {
$storage = 'local';
if (!\extension_loaded('imagick')) {
@ -372,6 +287,7 @@ App::get('/v1/storage/files/:fileId/preview')
$algorithm = $file->getAttribute('algorithm');
$cipher = $file->getAttribute('fileOpenSSLCipher');
$mime = $file->getAttribute('mimeType');
$fileLogos = Config::getParam('storage-logos');
if (!\in_array($mime, $inputs)) {
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
@ -516,8 +432,9 @@ App::get('/v1/storage/files/:fileId/view')
->param('fileId', '', function () { return new UID(); }, 'File unique ID.')
->param('as', '', function () { return new WhiteList(['pdf', /*'html',*/ 'text']); }, 'Choose a file format to convert your file to. Currently you can only convert word and pdf files to pdf or txt. This option is currently experimental only, use at your own risk.', true)
->action(
function ($fileId, $as) use ($response, $projectDB, $mimes) {
$file = $projectDB->getDocument($fileId);
function ($fileId, $as) use ($response, $projectDB) {
$file = $projectDB->getDocument($fileId);
$mimes = Config::getParam('storage-mimes');
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);

View file

@ -1,7 +1,5 @@
<?php
global $request, $response, $projectDB, $project, $user, $audit, $mail, $mode, $clients;
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;

View file

@ -1,7 +1,5 @@
<?php
global $response, $projectDB;
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;

View file

@ -10,8 +10,6 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\Host;
use Appwrite\Storage\Validator\File;
$result = [];
App::get('/v1/mock/tests/foo')
->desc('Mock a get request for SDK tests')
->label('scope', 'public')
@ -335,7 +333,8 @@ App::get('/v1/mock/tests/general/oauth2/failure')
}
);
App::shutdown(function() use ($response, $request, &$result, $utopia) {
App::shutdown(function($response, $request, $utopia) {
$result = [];
$route = $utopia->match($request);
$path = APP_STORAGE_CACHE.'/tests.json';
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
@ -353,4 +352,4 @@ App::shutdown(function() use ($response, $request, &$result, $utopia) {
}
$response->json(['result' => $route->getMethod() . ':' . $route->getURL() . ':passed']);
}, 'mock');
}, ['response', 'request', 'utopia'], 'mock');

View file

@ -7,7 +7,7 @@ use Utopia\Abuse\Adapters\TimeLimit;
global $utopia, $request, $response, $register, $user, $project;
App::init(function () use ($utopia, $request, $response, $register, $user, $project) {
App::init(function ($utopia, $request, $response, $register, $user, $project) {
$route = $utopia->match($request);
if (empty($project->getId()) && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope
@ -47,4 +47,4 @@ App::init(function () use ($utopia, $request, $response, $register, $user, $proj
if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') {
throw new Exception('Too many requests', 429);
}
}, 'api');
}, ['utopia', 'request', 'response', 'register', 'user', 'project'], 'api');

View file

@ -4,9 +4,7 @@ use Utopia\App;
use Utopia\View;
use Utopia\Config\Config;
$layout = new View(__DIR__.'/../../views/layouts/default.phtml');
App::init(function () use ($utopia, $response, $request, $layout) {
App::init(function ($utopia, $response, $request, $layout) {
/* AJAX check */
if (!empty($request->getQuery('version', ''))) {
@ -29,7 +27,6 @@ App::init(function () use ($utopia, $response, $request, $layout) {
;
$time = (60 * 60 * 24 * 45); // 45 days cache
$isDev = (\Utopia\App::MODE_TYPE_DEVELOPMENT == Config::getParam('env'));
$response
->addHeader('Cache-Control', 'public, max-age='.$time)
@ -40,7 +37,7 @@ App::init(function () use ($utopia, $response, $request, $layout) {
$scope = $route->getLabel('scope', '');
$layout
->setParam('version', Config::getParam('version'))
->setParam('isDev', $isDev)
->setParam('isDev', App::isDevelopment())
->setParam('class', $scope)
;
}, 'web');
}, ['utopia', 'response', 'request', 'layout'], 'web');

View file

@ -11,14 +11,19 @@ use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Validator\UID;
use Appwrite\Storage\Storage;
App::init(function () use ($layout) {
App::init(function ($layout) {
/** @var Utopia\View $layout */
$layout
->setParam('description', 'Appwrite Console allows you to easily manage, monitor, and control your entire backend API and tools.')
->setParam('analytics', 'UA-26264668-5')
;
}, 'console');
}, ['layout'], 'console');
App::shutdown(function ($response, $layout) {
/** @var Utopia\Response $response */
/** @var Utopia\View $layout */
App::shutdown(function () use ($response, $layout) {
$header = new View(__DIR__.'/../../views/console/comps/header.phtml');
$footer = new View(__DIR__.'/../../views/console/comps/footer.phtml');
@ -33,14 +38,16 @@ App::shutdown(function () use ($response, $layout) {
;
$response->send($layout->render());
}, 'console');
}, ['response', 'layout'], 'console');
App::get('/error/:code')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'home')
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
->action(function ($code) use ($layout) {
->action(function ($code, $layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/error.phtml');
$page
@ -50,13 +57,15 @@ App::get('/error/:code')
$layout
->setParam('title', APP_NAME.' - Error')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/index.phtml');
$page
@ -66,13 +75,15 @@ App::get('/console')
$layout
->setParam('title', APP_NAME.' - Console')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/account')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/account/index.phtml');
$cc = new View(__DIR__.'/../../views/console/forms/credit-card.phtml');
@ -84,37 +95,43 @@ App::get('/console/account')
$layout
->setParam('title', 'Account - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/notifications')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/v1/console/notifications/index.phtml');
$layout
->setParam('title', APP_NAME.' - Notifications')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/home')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/home/index.phtml');
$layout
->setParam('title', APP_NAME.' - Console')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/settings')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
$page = new View(__DIR__.'/../../views/console/settings/index.phtml');
@ -127,13 +144,15 @@ App::get('/console/settings')
$layout
->setParam('title', APP_NAME.' - Settings')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/webhooks')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/webhooks/index.phtml');
$page
@ -143,13 +162,15 @@ App::get('/console/webhooks')
$layout
->setParam('title', APP_NAME.' - Webhooks')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/keys')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$scopes = include __DIR__.'/../../../app/config/scopes.php';
$page = new View(__DIR__.'/../../views/console/keys/index.phtml');
@ -158,38 +179,46 @@ App::get('/console/keys')
$layout
->setParam('title', APP_NAME.' - API Keys')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/tasks')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/tasks/index.phtml');
$layout
->setParam('title', APP_NAME.' - Tasks')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/database')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/database/index.phtml');
$layout
->setParam('title', APP_NAME.' - Database')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/database/collection')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->param('id', '', function () { return new UID(); }, 'Collection unique ID.')
->action(function ($id) use ($response, $layout, $projectDB) {
->action(function ($id, $response, $layout, $projectDB) {
/** @var Utopia\Response $response */
/** @var Utopia\View $layout */
/** @var Appwrite\Database\Database $projectDB */
Authorization::disable();
$collection = $projectDB->getDocument($id, false);
Authorization::reset();
@ -214,14 +243,17 @@ App::get('/console/database/collection')
->addHeader('Expires', 0)
->addHeader('Pragma', 'no-cache')
;
});
}, ['response', 'layout', 'projectDB']);
App::get('/console/database/document')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->param('collection', '', function () { return new UID(); }, 'Collection unique ID.')
->action(function ($collection) use ($layout, $projectDB) {
->action(function ($collection, $layout, $projectDB) {
/** @var Utopia\View $layout */
/** @var Appwrite\Database\Database $projectDB */
Authorization::disable();
$collection = $projectDB->getDocument($collection, false);
Authorization::reset();
@ -244,13 +276,14 @@ App::get('/console/database/document')
$layout
->setParam('title', APP_NAME.' - Database Document')
->setParam('body', $page);
});
}, ['layout', 'projectDB']);
App::get('/console/storage')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($request, $layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/storage/index.phtml');
$page
@ -262,13 +295,15 @@ App::get('/console/storage')
$layout
->setParam('title', APP_NAME.' - Storage')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/users')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/users/index.phtml');
$page->setParam('providers', Config::getParam('providers'));
@ -276,28 +311,32 @@ App::get('/console/users')
$layout
->setParam('title', APP_NAME.' - Users')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/users/user')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/users/user.phtml');
$layout
->setParam('title', APP_NAME.' - User')
->setParam('body', $page);
});
}, ['layout']);
App::get('/console/users/teams/team')
->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/console/users/team.phtml');
$layout
->setParam('title', APP_NAME.' - Team')
->setParam('body', $page);
});
}, ['layout']);

View file

@ -8,7 +8,7 @@ use Utopia\Config\Config;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Range;
App::init(function () use ($layout) {
App::init(function ($layout) {
$header = new View(__DIR__.'/../../views/home/comps/header.phtml');
$footer = new View(__DIR__.'/../../views/home/comps/footer.phtml');
@ -24,11 +24,11 @@ App::init(function () use ($layout) {
->setParam('header', [$header])
->setParam('footer', [$footer])
;
}, 'home');
}, ['layout'], 'home');
App::shutdown(function () use ($response, $layout) {
App::shutdown(function ($response, $layout) {
$response->send($layout->render());
}, 'home');
}, ['response', 'layout'], 'home');
App::get('/')
->groups(['web', 'home'])

View file

@ -60,6 +60,8 @@ Config::load('services', __DIR__.'/../app/config/services.php'); // List of ser
Config::load('avatar-browsers', __DIR__.'/../app/config/avatars/browsers.php');
Config::load('avatar-credit-cards', __DIR__.'/../app/config/avatars/credit-cards.php');
Config::load('avatar-flags', __DIR__.'/../app/config/avatars/flags.php');
Config::load('storage-logos', __DIR__.'/../app/config/storage/logos.php');
Config::load('storage-mimes', __DIR__.'/../app/config/storage/mimes.php');
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '')
.':'.App::getEnv('_APP_REDIS_PORT', ''));
@ -141,10 +143,10 @@ $register->set('smtp', function () {
return $mail;
});
$register->set('queue-webhooks', function () {
$register->set('queue-webhook', function () {
return new Event('v1-webhooks', 'WebhooksV1');
});
$register->set('queue-audits', function () {
$register->set('queue-audit', function () {
return new Event('v1-audits', 'AuditsV1');
});
$register->set('queue-usage', function () {
@ -208,8 +210,6 @@ Locale::setLanguage('vi', include __DIR__.'/config/locales/vi.php');
Locale::setLanguage('zh-cn', include __DIR__.'/config/locales/zh-cn.php');
Locale::setLanguage('zh-tw', include __DIR__.'/config/locales/zh-tw.php');
Locale::setDefault('en');
\stream_context_set_default([ // Set global user agent and http settings
'http' => [
'method' => 'GET',

View file

@ -11,6 +11,7 @@ $litespeed = $this->getParam('litespeed', true);
$analytics = $this->getParam('analytics', 'UA-26264668-9');
$env = $this->getParam('env', '');
$canonical = $this->getParam('canonical', '');
$locale = $this->getParam('locale', null);
if(!empty($platforms)) {
$platforms = array_map(function($platform) {
@ -30,14 +31,14 @@ if(!empty($platforms)) {
}
?><!DOCTYPE html><!--
<?php echo Locale::getText('settings.inspire'); ?>
<?php echo $locale->getText('settings.inspire'); ?>
--><html lang="<?php echo Locale::getText('settings.locale'); ?>" class="<?php echo $this->getParam('class', 'none'); ?> <?php echo $env; ?>">
--><html lang="<?php echo $locale->getText('settings.locale'); ?>" class="<?php echo $this->getParam('class', 'none'); ?> <?php echo $env; ?>">
<head>
<link rel="manifest" href="/manifest.json">
<title><?php echo $this->getParam('title', ''); ?></title>
<meta name="description" content="<?php echo $this->getParam('description', ''); ?>" />
<link rel="stylesheet" media="all" type="text/css" href="/dist/styles/default-<?php echo Locale::getText('settings.direction'); ?>.css?v=<?php echo APP_CACHE_BUSTER; ?>.<?php echo $version; ?>" />
<link rel="stylesheet" media="all" type="text/css" href="/dist/styles/default-<?php echo $locale->getText('settings.direction'); ?>.css?v=<?php echo APP_CACHE_BUSTER; ?>.<?php echo $version; ?>" />
<link rel="icon" type="image/png" href="<?php echo $this->escape($this->getParam('icon', '')); ?>?v=<?php echo APP_CACHE_BUSTER; ?>" />
<link rel="apple-touch-icon" href="/images/apple.png">
<!-- <link rel="preconnect" href="" /> -->
@ -76,7 +77,7 @@ if(!empty($platforms)) {
API: '/v1',
PROJECT: 'console',
PLATFORMS: <?php echo json_encode($platforms); ?>,
LOCALE: '<?php echo $this->escape(Locale::getText('settings.locale')); ?>',
LOCALE: '<?php echo $this->escape($locale->getText('settings.locale')); ?>',
PREFIX: '<?php echo $this->escape($this->getParam('prefix')); ?>',
ROLES: <?PHP echo json_encode($this->getParam('roles', [])); ?>,
PAGING_LIMIT: <?PHP echo APP_PAGING_LIMIT; ?>

View file

@ -14,5 +14,6 @@ ini_set('display_errors', 0);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
trigger_error('hide errors in prod', E_USER_NOTICE);
include __DIR__ . '/../app/app.php';