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

Added env vars and new request methods

This commit is contained in:
Eldad Fux 2020-06-30 14:09:28 +03:00
parent 1c9bf4d0ab
commit 6824cf560f
15 changed files with 1561 additions and 1473 deletions

View file

@ -17,7 +17,6 @@ use Appwrite\Network\Validator\Origin;
// 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));
@ -131,10 +130,22 @@ use Appwrite\Network\Validator\Origin;
// return false;
// }))));
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', ''));
App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $webhooks, $audits, $usage, $clients) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $console */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $mail */
/** @var Appwrite\Event\Event $deletes */
/** @var bool $mode */
/** @var array $clients */
$localeParam = (string)$request->getParam('locale', $request->getHeader('X-Appwrite-Locale', ''));
if (\in_array($localeParam, Config::getParam('locale-codes'))) {
$locale->setDefault($localeParam);
@ -175,7 +186,7 @@ App::init(function ($utopia, $request, $response, $user, $project, $console, $we
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if(Config::getParam('protocol') !== 'https') {
if($request->getProtocol() !== 'https') {
return $response->redirect('https://' . Config::getParam('domain').$request->getServer('REQUEST_URI'));
}
@ -317,7 +328,7 @@ App::init(function ($utopia, $request, $response, $user, $project, $console, $we
->setParam('response', 0)
->setParam('storage', 0)
;
}, ['utopia', 'request', 'response', 'user', 'project', 'console', 'webhook', 'audit', 'usage', 'clients', 'locale']);
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'webhook', 'audit', 'usage', 'clients']);
App::shutdown(function ($utopia, $response, $request, $webhook, $audit, $usage, $deletes, $mode, $project) {
/*
@ -363,7 +374,7 @@ App::options(function ($request, $response) {
App::error(function ($error, $utopia, $request, $response, $project) {
/** @var Exception $error */
$version = Config::getParam('version');
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
switch ($error->getCode()) {
case 400: // Error allowed publicly

View file

@ -167,7 +167,7 @@ App::post('/v1/account/sessions')
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$profile = $projectDB->getCollectionFirst([ // Get user by email address
'limit' => 1,
'filters' => [
@ -264,7 +264,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$callback = $protocol.'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
$appId = $project->getAttribute('usersOauth2'.\ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth2'.\ucfirst($provider).'Secret', '{}');
@ -304,16 +304,19 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->param('provider', '', function () { return new WhiteList(\array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'Login state params.', true)
->action(function ($projectId, $provider, $code, $state, $response) {
->action(function ($projectId, $provider, $code, $state, $request, $response) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
$domain = Config::getParam('domain');
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol.'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
.\http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
}, ['response']);
}, ['request', 'response']);
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 Callback')
@ -326,16 +329,19 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->param('provider', '', function () { return new WhiteList(\array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'Login state params.', true)
->action(function ($projectId, $provider, $code, $state, $response) {
->action(function ($projectId, $provider, $code, $state, $request, $response) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
$domain = Config::getParam('domain');
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol.'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
.\http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
}, ['response']);
}, ['request', 'response']);
App::get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('OAuth2 Redirect')
@ -357,7 +363,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audit */
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$callback = $protocol.'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$validateURL = new URL();
@ -923,14 +929,15 @@ App::delete('/v1/account')
->label('sdk.namespace', 'account')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/account/delete.md')
->action(function ($response, $user, $projectDB, $audit, $webhook) {
->action(function ($request, $response, $user, $projectDB, $audit, $webhook) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audit */
/** @var Appwrite\Event\Event $webhook */
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'status' => Auth::USER_STATUS_BLOCKED,
]));
@ -972,7 +979,7 @@ App::delete('/v1/account')
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->noContent()
;
}, ['response', 'user', 'projectDB', 'audit', 'webhook']);
}, ['request', 'response', 'user', 'projectDB', 'audit', 'webhook']);
App::delete('/v1/account/sessions/:sessionId')
->desc('Delete Account Session')
@ -985,14 +992,15 @@ App::delete('/v1/account/sessions/:sessionId')
->label('sdk.description', '/docs/references/account/delete-session.md')
->label('abuse-limit', 100)
->param('sessionId', null, function () { return new UID(); }, 'Session unique ID. Use the string \'current\' to delete the current device session.')
->action(function ($sessionId, $response, $user, $projectDB, $audit, $webhook) {
->action(function ($sessionId, $request, $response, $user, $projectDB, $audit, $webhook) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audit */
/** @var Appwrite\Event\Event $webhook */
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$sessionId = ($sessionId === 'current')
? Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)
: $sessionId;
@ -1036,7 +1044,7 @@ App::delete('/v1/account/sessions/:sessionId')
}
throw new Exception('Session not found', 404);
}, ['response', 'user', 'projectDB', 'audit', 'webhook']);
}, ['request', 'response', 'user', 'projectDB', 'audit', 'webhook']);
App::delete('/v1/account/sessions')
->desc('Delete All Account Sessions')
@ -1048,14 +1056,15 @@ App::delete('/v1/account/sessions')
->label('sdk.method', 'deleteSessions')
->label('sdk.description', '/docs/references/account/delete-sessions.md')
->label('abuse-limit', 100)
->action(function ($response, $user, $projectDB, $audit, $webhook) {
->action(function ($request, $response, $user, $projectDB, $audit, $webhook) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audit */
/** @var Appwrite\Event\Event $webhook */
$protocol = Config::getParam('protocol');
$protocol = $request->getProtocol();
$tokens = $user->getAttribute('tokens', []);
foreach ($tokens as $token) { /* @var $token Document */
@ -1091,7 +1100,7 @@ App::delete('/v1/account/sessions')
}
$response->noContent();
}, ['response', 'user', 'projectDB', 'audit', 'webhook']);
}, ['request', 'response', 'user', 'projectDB', 'audit', 'webhook']);
App::post('/v1/account/recovery')
->desc('Create Password Recovery')

View file

@ -246,7 +246,7 @@ App::get('/v1/avatars/favicon')
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
CURLOPT_USERAGENT => \sprintf(APP_USERAGENT,
Config::getParam('version'),
App::getEnv('_APP_VERSION', 'UNKNOWN'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
]);

View file

@ -242,116 +242,119 @@ App::get('/v1/storage/files/:fileId/preview')
->param('quality', 100, function () { return new Range(0, 100); }, 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->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 () { return new WhiteList(\array_merge(\array_keys(Config::getParam('storage-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) {
$storage = 'local';
->action(function ($fileId, $width, $height, $quality, $background, $output, $request, $response, $project, $projectDB) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$storage = 'local';
if (!Storage::exists($storage)) {
throw new Exception('No such storage device', 400);
}
if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
if ((\strpos($request->getServer('HTTP_ACCEPT'), 'image/webp') === false) && ('webp' == $output)) { // Fallback webp to jpeg when no browser support
$output = 'jpg';
}
if (!Storage::exists($storage)) {
throw new Exception('No such storage device', 400);
}
$inputs = Config::getParam('storage-inputs');
$outputs = Config::getParam('storage-outputs');
$fileLogos = Config::getParam('storage-logos');
if ((\strpos($request->getServer('HTTP_ACCEPT'), 'image/webp') === false) && ('webp' == $output)) { // Fallback webp to jpeg when no browser support
$output = 'jpg';
}
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5($fileId.$width.$height.$quality.$background.$storage.$output);
$inputs = Config::getParam('storage-inputs');
$outputs = Config::getParam('storage-outputs');
$fileLogos = Config::getParam('storage-logos');
$file = $projectDB->getDocument($fileId);
$date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = \md5($fileId.$width.$height.$quality.$background.$storage.$output);
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
$file = $projectDB->getDocument($fileId);
$path = $file->getAttribute('path');
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
$path = $file->getAttribute('path');
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$algorithm = $file->getAttribute('algorithm');
$cipher = $file->getAttribute('fileOpenSSLCipher');
$mime = $file->getAttribute('mimeType');
if (!\in_array($mime, $inputs)) {
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
$algorithm = null;
$cipher = null;
$background = (empty($background)) ? 'eceff1' : $background;
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$algorithm = $file->getAttribute('algorithm');
$cipher = $file->getAttribute('fileOpenSSLCipher');
$mime = $file->getAttribute('mimeType');
$key = \md5($path.$width.$height.$quality.$background.$storage.$output);
}
if (!\in_array($mime, $inputs)) {
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
$algorithm = null;
$cipher = null;
$background = (empty($background)) ? 'eceff1' : $background;
$type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$key = \md5($path.$width.$height.$quality.$background.$storage.$output);
}
$compressor = new GZIP();
$device = Storage::getDevice('local');
$compressor = new GZIP();
$device = Storage::getDevice('local');
if (!\file_exists($path)) {
throw new Exception('File not found', 404);
}
if (!\file_exists($path)) {
throw new Exception('File not found', 404);
}
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-'.$project->getId())); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
if ($data) {
$output = (empty($output)) ? $type : $output;
$response
->setContentType((\in_array($output, $outputs)) ? $outputs[$output] : $outputs['jpg'])
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data)
;
return;
}
$source = $device->read($path);
if (!empty($cipher)) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('fileOpenSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
\hex2bin($file->getAttribute('fileOpenSSLIV')),
\hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
if (!empty($algorithm)) {
$source = $compressor->decompress($source);
}
$resize = new Resize($source);
$resize->crop((int) $width, (int) $height);
if (!empty($background)) {
$resize->setBackground('#'.$background);
}
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-'.$project->getId())); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
if ($data) {
$output = (empty($output)) ? $type : $output;
$response
->setContentType($outputs[$output])
->setContentType((\in_array($output, $outputs)) ? $outputs[$output] : $outputs['jpg'])
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('')
->addHeader('X-Appwrite-Cache', 'hit')
->send($data)
;
$data = $resize->output($output, $quality);
$cache->save($key, $data);
echo $data;
unset($resize);
return;
}
);
$source = $device->read($path);
if (!empty($cipher)) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('fileOpenSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
\hex2bin($file->getAttribute('fileOpenSSLIV')),
\hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
if (!empty($algorithm)) {
$source = $compressor->decompress($source);
}
$resize = new Resize($source);
$resize->crop((int) $width, (int) $height);
if (!empty($background)) {
$resize->setBackground('#'.$background);
}
$output = (empty($output)) ? $type : $output;
$response
->setContentType($outputs[$output])
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('')
;
$data = $resize->output($output, $quality);
$cache->save($key, $data);
echo $data;
unset($resize);
}, ['request', 'response', 'project', 'projectDB']);
App::get('/v1/storage/files/:fileId/download')
->desc('Get File for Download')
@ -364,48 +367,49 @@ App::get('/v1/storage/files/:fileId/download')
->label('sdk.response.type', '*')
->label('sdk.methodType', 'location')
->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);
$path = $file->getAttribute('path', '');
if (!\file_exists($path)) {
throw new Exception('File not found in '.$path, 404);
}
$compressor = new GZIP();
$device = Storage::getDevice('local');
$source = $device->read($path);
if (!empty($file->getAttribute('fileOpenSSLCipher'))) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('fileOpenSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
\hex2bin($file->getAttribute('fileOpenSSLIV')),
\hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
$source = $compressor->decompress($source);
// Response
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Content-Disposition', 'attachment; filename="'.$file->getAttribute('name', '').'"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
->send($source)
;
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
);
$path = $file->getAttribute('path', '');
if (!\file_exists($path)) {
throw new Exception('File not found in '.$path, 404);
}
$compressor = new GZIP();
$device = Storage::getDevice('local');
$source = $device->read($path);
if (!empty($file->getAttribute('fileOpenSSLCipher'))) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('fileOpenSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
\hex2bin($file->getAttribute('fileOpenSSLIV')),
\hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
$source = $compressor->decompress($source);
// Response
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Content-Disposition', 'attachment; filename="'.$file->getAttribute('name', '').'"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
->send($source)
;
}, ['response', 'projectDB']);
App::get('/v1/storage/files/:fileId/view')
->desc('Get File for View')
@ -419,65 +423,66 @@ App::get('/v1/storage/files/:fileId/view')
->label('sdk.methodType', 'location')
->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) {
$file = $projectDB->getDocument($fileId);
$mimes = Config::getParam('storage-mimes');
->action(function ($fileId, $as, $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);
$mimes = Config::getParam('storage-mimes');
$path = $file->getAttribute('path', '');
if (!\file_exists($path)) {
throw new Exception('File not found in '.$path, 404);
}
$compressor = new GZIP();
$device = Storage::getDevice('local');
$contentType = 'text/plain';
if (\in_array($file->getAttribute('mimeType'), $mimes)) {
$contentType = $file->getAttribute('mimeType');
}
$source = $device->read($path);
if (!empty($file->getAttribute('fileOpenSSLCipher'))) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('fileOpenSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
\hex2bin($file->getAttribute('fileOpenSSLIV')),
\hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
$output = $compressor->decompress($source);
$fileName = $file->getAttribute('name', '');
$contentTypes = [
'pdf' => 'application/pdf',
'text' => 'text/plain',
];
$contentType = (\array_key_exists($as, $contentTypes)) ? $contentTypes[$as] : $contentType;
// Response
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="'.$fileName.'"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
->send($output)
;
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
);
$path = $file->getAttribute('path', '');
if (!\file_exists($path)) {
throw new Exception('File not found in '.$path, 404);
}
$compressor = new GZIP();
$device = Storage::getDevice('local');
$contentType = 'text/plain';
if (\in_array($file->getAttribute('mimeType'), $mimes)) {
$contentType = $file->getAttribute('mimeType');
}
$source = $device->read($path);
if (!empty($file->getAttribute('fileOpenSSLCipher'))) { // Decrypt
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('fileOpenSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
\hex2bin($file->getAttribute('fileOpenSSLIV')),
\hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
$output = $compressor->decompress($source);
$fileName = $file->getAttribute('name', '');
$contentTypes = [
'pdf' => 'application/pdf',
'text' => 'text/plain',
];
$contentType = (\array_key_exists($as, $contentTypes)) ? $contentTypes[$as] : $contentType;
// Response
$response
->setContentType($contentType)
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="'.$fileName.'"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->addHeader('X-Peak', \memory_get_peak_usage())
->send($output)
;
}, ['response', 'projectDB']);
App::put('/v1/storage/files/:fileId')
->desc('Update File')
@ -491,39 +496,41 @@ App::put('/v1/storage/files/:fileId')
->param('fileId', '', function () { return new UID(); }, 'File unique ID.')
->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 ($fileId, $read, $write, $folderId = '') use ($response, $projectDB, $audit, $webhook) {
$file = $projectDB->getDocument($fileId);
->action(function ($fileId, $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 */
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
$file = $projectDB->getDocument($fileId);
$file = $projectDB->updateDocument(\array_merge($file->getArrayCopy(), [
'$permissions' => [
'read' => $read,
'write' => $write,
],
'folderId' => $folderId,
]));
if (false === $file) {
throw new Exception('Failed saving file to DB', 500);
}
$webhook
->setParam('payload', $file->getArrayCopy())
;
$audit
->setParam('event', 'storage.files.update')
->setParam('resource', 'storage/files/'.$file->getId())
;
$response->json($file->getArrayCopy());
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
);
$file = $projectDB->updateDocument(\array_merge($file->getArrayCopy(), [
'$permissions' => [
'read' => $read,
'write' => $write,
],
'folderId' => '',
]));
if (false === $file) {
throw new Exception('Failed saving file to DB', 500);
}
$webhook
->setParam('payload', $file->getArrayCopy())
;
$audit
->setParam('event', 'storage.files.update')
->setParam('resource', 'storage/files/'.$file->getId())
;
$response->json($file->getArrayCopy());
}, ['response', 'projectDB', 'webhook', 'audit']);
App::delete('/v1/storage/files/:fileId')
->desc('Delete File')
@ -535,38 +542,42 @@ App::delete('/v1/storage/files/:fileId')
->label('sdk.method', 'deleteFile')
->label('sdk.description', '/docs/references/storage/delete-file.md')
->param('fileId', '', function () { return new UID(); }, 'File unique ID.')
->action(
function ($fileId) use ($response, $projectDB, $webhook, $audit, $usage) {
$file = $projectDB->getDocument($fileId);
->action(function ($fileId, $response, $projectDB, $webhook, $audit, $usage) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhook */
/** @var Appwrite\Event\Event $audit */
/** @var Appwrite\Event\Event $usage */
$file = $projectDB->getDocument($fileId);
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
$device = Storage::getDevice('local');
if ($device->delete($file->getAttribute('path', ''))) {
if (!$projectDB->deleteDocument($fileId)) {
throw new Exception('Failed to remove file from DB', 500);
}
}
$webhook
->setParam('payload', $file->getArrayCopy())
;
$audit
->setParam('event', 'storage.files.delete')
->setParam('resource', 'storage/files/'.$file->getId())
;
$usage
->setParam('storage', $file->getAttribute('size', 0) * -1)
;
$response->noContent();
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
throw new Exception('File not found', 404);
}
);
$device = Storage::getDevice('local');
if ($device->delete($file->getAttribute('path', ''))) {
if (!$projectDB->deleteDocument($fileId)) {
throw new Exception('Failed to remove file from DB', 500);
}
}
$webhook
->setParam('payload', $file->getArrayCopy())
;
$audit
->setParam('event', 'storage.files.delete')
->setParam('resource', 'storage/files/'.$file->getId())
;
$usage
->setParam('storage', $file->getAttribute('size', 0) * -1)
;
$response->noContent();
}, ['fileId', 'response', 'projectDB', 'webhook', 'audit', 'usage']);
// App::get('/v1/storage/files/:fileId/scan')
// ->desc('Scan Storage')

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,6 @@ use Utopia\Validator\Range;
use Utopia\Audit\Audit;
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
use Utopia\Config\Config;
use Utopia\Locale\Locale;
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\Password;
use Appwrite\Database\Database;
@ -31,64 +30,65 @@ App::post('/v1/users')
->param('email', '', function () { return new Email(); }, 'User email.')
->param('password', '', function () { return new Password(); }, 'User password. Must be between 6 to 32 chars.')
->param('name', '', function () { return new Text(100); }, 'User name.', true)
->action(
function ($email, $password, $name) use ($response, $projectDB) {
$profile = $projectDB->getCollectionFirst([ // Get user by email address
'limit' => 1,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'email='.$email,
],
]);
->action(function ($email, $password, $name, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (!empty($profile)) {
throw new Exception('User already registered', 409);
}
$profile = $projectDB->getCollectionFirst([ // Get user by email address
'limit' => 1,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
'email='.$email,
],
]);
try {
$user = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_USERS,
'$permissions' => [
'read' => ['*'],
'write' => ['user:{self}'],
],
'email' => $email,
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash($password),
'password-update' => \time(),
'registration' => \time(),
'reset' => false,
'name' => $name,
], ['email' => $email]);
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409);
}
$oauth2Keys = [];
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json(\array_merge($user->getArrayCopy(\array_merge([
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
], $oauth2Keys)), ['roles' => []]));
if (!empty($profile)) {
throw new Exception('User already registered', 409);
}
);
try {
$user = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_USERS,
'$permissions' => [
'read' => ['*'],
'write' => ['user:{self}'],
],
'email' => $email,
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash($password),
'password-update' => \time(),
'registration' => \time(),
'reset' => false,
'name' => $name,
], ['email' => $email]);
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409);
}
$oauth2Keys = [];
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json(\array_merge($user->getArrayCopy(\array_merge([
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
], $oauth2Keys)), ['roles' => []]));
}, ['response', 'projectDB']);
App::get('/v1/users')
->desc('List Users')
->groups(['api', 'users'])
@ -101,48 +101,49 @@ App::get('/v1/users')
->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' => 'registration',
'orderType' => $orderType,
'orderCast' => 'int',
'search' => $search,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$oauth2Keys = [];
$results = $projectDB->getCollection([
'limit' => $limit,
'offset' => $offset,
'orderField' => 'registration',
'orderType' => $orderType,
'orderCast' => 'int',
'search' => $search,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys = [];
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$results = \array_map(function ($value) use ($oauth2Keys) { /* @var $value \Database\Document */
return $value->getArrayCopy(\array_merge(
[
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
],
$oauth2Keys
));
}, $results);
$response->json(['sum' => $projectDB->getSum(), 'users' => $results]);
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
);
$results = \array_map(function ($value) use ($oauth2Keys) { /* @var $value \Database\Document */
return $value->getArrayCopy(\array_merge(
[
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
],
$oauth2Keys
));
}, $results);
$response->json(['sum' => $projectDB->getSum(), 'users' => $results]);
}, ['response', 'projectDB']);
App::get('/v1/users/:userId')
->desc('Get User')
@ -153,38 +154,39 @@ App::get('/v1/users/:userId')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/users/get-user.md')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->action(
function ($userId) use ($response, $projectDB) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$user = $projectDB->getDocument($userId);
$oauth2Keys = [];
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
],
$oauth2Keys
)), ['roles' => []]));
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
);
$oauth2Keys = [];
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
],
$oauth2Keys
)), ['roles' => []]));
}, ['response', 'projectDB']);
App::get('/v1/users/:userId/prefs')
->desc('Get User Preferences')
@ -195,26 +197,27 @@ App::get('/v1/users/:userId/prefs')
->label('sdk.method', 'getPrefs')
->label('sdk.description', '/docs/references/users/get-user-prefs.md')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->action(
function ($userId) use ($response, $projectDB) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$user = $projectDB->getDocument($userId);
$prefs = $user->getAttribute('prefs', '');
try {
$prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
}
$response->json($prefs);
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
);
$prefs = $user->getAttribute('prefs', '');
try {
$prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
}
$response->json($prefs);
}, ['response', 'projectDB']);
App::get('/v1/users/:userId/sessions')
->desc('Get User Sessions')
@ -225,60 +228,62 @@ App::get('/v1/users/:userId/sessions')
->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/users/get-user-sessions.md')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->action(
function ($userId) use ($response, $projectDB) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $response, $projectDB, $locale) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Locale\Locale $locale */
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$user = $projectDB->getDocument($userId);
$tokens = $user->getAttribute('tokens', []);
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
$sessions = [];
$index = 0;
$countries = Locale::getText('countries');
foreach ($tokens as $token) { /* @var $token Document */
if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) {
continue;
}
$userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN';
$dd = new DeviceDetector($userAgent);
// OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
// $dd->skipBotDetection();
$dd->parse();
$sessions[$index] = [
'$id' => $token->getId(),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
'brand' => $dd->getBrand(),
'model' => $dd->getModel(),
'ip' => $token->getAttribute('ip', ''),
'geo' => [],
];
try {
$record = $reader->country($token->getAttribute('ip', ''));
$sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = Locale::getText('locale.country.unknown');
}
++$index;
}
$response->json($sessions);
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
);
$tokens = $user->getAttribute('tokens', []);
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
$sessions = [];
$index = 0;
$countries = $locale->getText('countries');
foreach ($tokens as $token) { /* @var $token Document */
if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) {
continue;
}
$userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN';
$dd = new DeviceDetector($userAgent);
// OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
// $dd->skipBotDetection();
$dd->parse();
$sessions[$index] = [
'$id' => $token->getId(),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
'brand' => $dd->getBrand(),
'model' => $dd->getModel(),
'ip' => $token->getAttribute('ip', ''),
'geo' => [],
];
try {
$record = $reader->country($token->getAttribute('ip', ''));
$sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : $locale->getText('locale.country.unknown');
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
}
++$index;
}
$response->json($sessions);
}, ['response', 'projectDB', 'locale']);
App::get('/v1/users/:userId/logs')
->desc('Get User Logs')
@ -289,77 +294,81 @@ App::get('/v1/users/:userId/logs')
->label('sdk.method', 'getLogs')
->label('sdk.description', '/docs/references/users/get-user-logs.md')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->action(
function ($userId) use ($response, $register, $projectDB, $project) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $response, $register, $project, $projectDB, $locale) {
/** @var Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Locale\Locale $locale */
$user = $projectDB->getDocument($userId);
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$adapter = new AuditAdapter($register->get('db'));
$adapter->setNamespace('app_'.$project->getId());
$audit = new Audit($adapter);
$countries = Locale::getText('countries');
$logs = $audit->getLogsByUserAndActions($user->getId(), [
'account.create',
'account.delete',
'account.update.name',
'account.update.email',
'account.update.password',
'account.update.prefs',
'account.sessions.create',
'account.sessions.delete',
'account.recovery.create',
'account.recovery.update',
'account.verification.create',
'account.verification.update',
'teams.membership.create',
'teams.membership.update',
'teams.membership.delete',
]);
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$dd = new DeviceDetector($log['userAgent']);
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->parse();
$output[$i] = [
'event' => $log['event'],
'ip' => $log['ip'],
'time' => \strtotime($log['time']),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
'brand' => $dd->getBrand(),
'model' => $dd->getModel(),
'geo' => [],
];
try {
$record = $reader->country($log['ip']);
$output[$i]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$output[$i]['geo']['country'] = $record->country->name;
$output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
} catch (\Exception $e) {
$output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = Locale::getText('locale.country.unknown');
}
}
$response->json($output);
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
);
$adapter = new AuditAdapter($register->get('db'));
$adapter->setNamespace('app_'.$project->getId());
$audit = new Audit($adapter);
$countries = $locale->getText('countries');
$logs = $audit->getLogsByUserAndActions($user->getId(), [
'account.create',
'account.delete',
'account.update.name',
'account.update.email',
'account.update.password',
'account.update.prefs',
'account.sessions.create',
'account.sessions.delete',
'account.recovery.create',
'account.recovery.update',
'account.verification.create',
'account.verification.update',
'teams.membership.create',
'teams.membership.update',
'teams.membership.delete',
]);
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$dd = new DeviceDetector($log['userAgent']);
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->parse();
$output[$i] = [
'event' => $log['event'],
'ip' => $log['ip'],
'time' => \strtotime($log['time']),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
'brand' => $dd->getBrand(),
'model' => $dd->getModel(),
'geo' => [],
];
try {
$record = $reader->country($log['ip']);
$output[$i]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$output[$i]['geo']['country'] = $record->country->name;
$output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : $locale->getText('locale.country.unknown');
} catch (\Exception $e) {
$output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = $locale->getText('locale.country.unknown');
}
}
$response->json($output);
}, ['response', 'register', 'project', 'projectDB', 'locale']);
App::patch('/v1/users/:userId/status')
->desc('Update User Status')
@ -371,44 +380,45 @@ App::patch('/v1/users/:userId/status')
->label('sdk.description', '/docs/references/users/update-user-status.md')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->param('status', '', function () { return new WhiteList([Auth::USER_STATUS_ACTIVATED, Auth::USER_STATUS_BLOCKED, Auth::USER_STATUS_UNACTIVATED]); }, 'User Status code. To activate the user pass '.Auth::USER_STATUS_ACTIVATED.', to block the user pass '.Auth::USER_STATUS_BLOCKED.' and for disabling the user pass '.Auth::USER_STATUS_UNACTIVATED)
->action(
function ($userId, $status) use ($response, $projectDB) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $status, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$user = $projectDB->getDocument($userId);
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'status' => (int)$status,
]));
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$oauth2Keys = [];
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response
->json(\array_merge($user->getArrayCopy(\array_merge([
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
], $oauth2Keys)), ['roles' => []]));
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
);
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'status' => (int)$status,
]));
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$oauth2Keys = [];
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response
->json(\array_merge($user->getArrayCopy(\array_merge([
'$id',
'status',
'email',
'registration',
'emailVerification',
'name',
], $oauth2Keys)), ['roles' => []]));
}, ['response', 'projectDB']);
App::patch('/v1/users/:userId/prefs')
->desc('Update User Preferences')
@ -420,37 +430,38 @@ App::patch('/v1/users/:userId/prefs')
->label('sdk.description', '/docs/references/users/update-user-prefs.md')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->param('prefs', '', function () { return new Assoc();}, 'Prefs key-value JSON object.')
->action(
function ($userId, $prefs) use ($response, $projectDB) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $prefs, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$user = $projectDB->getDocument($userId);
$old = \json_decode($user->getAttribute('prefs', '{}'), true);
$old = ($old) ? $old : [];
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'prefs' => \json_encode(\array_merge($old, $prefs)),
]));
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$prefs = $user->getAttribute('prefs', '');
try {
$prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
}
$response->json($prefs);
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
);
$old = \json_decode($user->getAttribute('prefs', '{}'), true);
$old = ($old) ? $old : [];
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'prefs' => \json_encode(\array_merge($old, $prefs)),
]));
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$prefs = $user->getAttribute('prefs', '');
try {
$prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
}
$response->json($prefs);
}, ['response', 'projectDB']);
App::delete('/v1/users/:userId/sessions/:sessionId')
@ -464,27 +475,28 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('abuse-limit', 100)
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->param('sessionId', null, function () { return new UID(); }, 'User unique session ID.')
->action(
function ($userId, $sessionId) use ($response, $request, $projectDB) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $sessionId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$user = $projectDB->getDocument($userId);
$tokens = $user->getAttribute('tokens', []);
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
foreach ($tokens as $token) { /* @var $token Document */
if ($sessionId == $token->getId()) {
if (!$projectDB->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB', 500);
}
$tokens = $user->getAttribute('tokens', []);
foreach ($tokens as $token) { /* @var $token Document */
if ($sessionId == $token->getId()) {
if (!$projectDB->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB', 500);
}
}
$response->json(array('result' => 'success'));
}
);
$response->json(array('result' => 'success'));
}, ['response', 'projectDB']);
App::delete('/v1/users/:userId/sessions')
->desc('Delete User Sessions')
@ -496,22 +508,23 @@ App::delete('/v1/users/:userId/sessions')
->label('sdk.description', '/docs/references/users/delete-user-sessions.md')
->label('abuse-limit', 100)
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->action(
function ($userId) use ($response, $request, $projectDB) {
$user = $projectDB->getDocument($userId);
->action(function ($userId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
$user = $projectDB->getDocument($userId);
$tokens = $user->getAttribute('tokens', []);
foreach ($tokens as $token) { /* @var $token Document */
if (!$projectDB->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB', 500);
}
}
$response->json(array('result' => 'success'));
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
throw new Exception('User not found', 404);
}
);
$tokens = $user->getAttribute('tokens', []);
foreach ($tokens as $token) { /* @var $token Document */
if (!$projectDB->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB', 500);
}
}
$response->json(array('result' => 'success'));
}, ['response', 'projectDB']);

View file

@ -4,7 +4,11 @@ use Utopia\App;
use Utopia\View;
use Utopia\Config\Config;
App::init(function ($utopia, $response, $request, $layout) {
App::init(function ($utopia, $request, $response, $layout) {
/** @var Utopia\App $utopia */
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\View $layout */
/* AJAX check */
if (!empty($request->getQuery('version', ''))) {
@ -12,7 +16,7 @@ App::init(function ($utopia, $response, $request, $layout) {
}
$layout
->setParam('title', APP_NAME)
->setParam('protocol', Config::getParam('protocol'))
->setParam('protocol', $request->getProtocol())
->setParam('domain', Config::getParam('domain'))
->setParam('home', App::getEnv('_APP_HOME'))
->setParam('setup', App::getEnv('_APP_SETUP'))
@ -36,8 +40,8 @@ App::init(function ($utopia, $response, $request, $layout) {
$route = $utopia->match($request);
$scope = $route->getLabel('scope', '');
$layout
->setParam('version', Config::getParam('version'))
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
->setParam('isDev', App::isDevelopment())
->setParam('class', $scope)
;
}, ['utopia', 'response', 'request', 'layout'], 'web');
}, ['utopia', 'request', 'response', 'layout'], 'web');

View file

@ -29,7 +29,7 @@ App::shutdown(function ($response, $layout) {
$footer
->setParam('home', App::getEnv('_APP_HOME', ''))
->setParam('version', Config::getParam('version'))
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
;
$layout

View file

@ -9,11 +9,13 @@ use Utopia\Validator\WhiteList;
use Utopia\Validator\Range;
App::init(function ($layout) {
/** @var Utopia\View $layout */
$header = new View(__DIR__.'/../../views/home/comps/header.phtml');
$footer = new View(__DIR__.'/../../views/home/comps/footer.phtml');
$footer
->setParam('version', Config::getParam('version'))
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
;
$layout
@ -27,6 +29,9 @@ App::init(function ($layout) {
}, ['layout'], 'home');
App::shutdown(function ($response, $layout) {
/** @var Utopia\Response $response */
/** @var Utopia\View $layout */
$response->send($layout->render());
}, ['response', 'layout'], 'home');
@ -34,90 +39,102 @@ App::get('/')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(
function () use ($response) {
$response->redirect('/auth/signin');
}
);
->action(function ($response) {
/** @var Utopia\Response $response */
$response->redirect('/auth/signin');
}, ['response']);
App::get('/auth/signin')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/signin.phtml');
$layout
->setParam('title', 'Sign In - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/auth/signup')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/signup.phtml');
$layout
->setParam('title', 'Sign Up - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/auth/recovery')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($request, $layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/recovery.phtml');
$layout
->setParam('title', 'Password Recovery - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/auth/confirm')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/confirm.phtml');
$layout
->setParam('title', 'Account Confirmation - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/auth/join')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/join.phtml');
$layout
->setParam('title', 'Invitation - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/auth/recovery/reset')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/recovery/reset.phtml');
$layout
->setParam('title', 'Password Reset - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/auth/oauth2/success')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
$layout
@ -126,13 +143,15 @@ App::get('/auth/oauth2/success')
->setParam('header', [])
->setParam('footer', [])
;
});
}, ['layout']);
App::get('/auth/oauth2/failure')
->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
->action(function ($layout) {
/** @var Utopia\View $layout */
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
$layout
@ -141,14 +160,16 @@ App::get('/auth/oauth2/failure')
->setParam('header', [])
->setParam('footer', [])
;
});
}, ['layout']);
App::get('/error/:code')
->groups(['web', 'home'])
->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
@ -158,7 +179,7 @@ App::get('/error/:code')
$layout
->setParam('title', 'Error'.' - '.APP_NAME)
->setParam('body', $page);
});
}, ['layout']);
App::get('/open-api-2.json')
->groups(['web', 'home'])
@ -167,401 +188,402 @@ App::get('/open-api-2.json')
->param('platform', APP_PLATFORM_CLIENT, function () {return new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE]);}, 'Choose target platform.', true)
->param('extensions', 0, function () {return new Range(0, 1);}, 'Show extra data.', true)
->param('tests', 0, function () {return new Range(0, 1);}, 'Include only test services.', true)
->action(
function ($platform, $extensions, $tests) use ($response, $utopia) {
$services = Config::getParam('services', []);
->action(function ($platform, $extensions, $tests, $utopia, $response) {
/** @var Utopia\App $utopia */
/** @var Utopia\Response $response */
$services = Config::getParam('services', []);
function fromCamelCase($input)
{
\preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == \strtoupper($match) ? \strtolower($match) : \lcfirst($match);
}
return \implode('_', $ret);
}
function fromCamelCaseToDash($input)
{
return \str_replace([' ', '_'], '-', \strtolower(\preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $input)));
}
foreach ($services as $service) { /* @noinspection PhpIncludeInspection */
if ($tests && !isset($service['tests'])) {
continue;
}
if ($tests && !$service['tests']) {
continue;
}
function fromCamelCase($input)
{
\preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == \strtoupper($match) ? \strtolower($match) : \lcfirst($match);
}
if (!$tests && !$service['sdk']) {
continue;
}
/** @noinspection PhpIncludeInspection */
include_once \realpath(__DIR__.'/../../'.$service['controller']);
}
return \implode('_', $ret);
$security = [
APP_PLATFORM_CLIENT => ['Project' => []],
APP_PLATFORM_SERVER => ['Project' => [], 'Key' => []],
APP_PLATFORM_CONSOLE => ['Project' => [], 'Key' => []],
];
$platforms = [
'client' => APP_PLATFORM_CLIENT,
'server' => APP_PLATFORM_SERVER,
'all' => APP_PLATFORM_CONSOLE,
];
$keys = [
APP_PLATFORM_CLIENT => [
'Project' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Project',
'description' => 'Your project ID',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
'description' => '',
'in' => 'header',
],
],
APP_PLATFORM_SERVER => [
'Project' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Project',
'description' => 'Your project ID',
'in' => 'header',
],
'Key' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Key',
'description' => 'Your secret API key',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
'description' => '',
'in' => 'header',
],
],
APP_PLATFORM_CONSOLE => [
'Project' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Project',
'description' => 'Your project ID',
'in' => 'header',
],
'Key' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Key',
'description' => 'Your secret API key',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
'description' => '',
'in' => 'header',
],
'Mode' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Mode',
'description' => '',
'in' => 'header',
],
],
];
/*
* Specifications (v3.0.0):
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
*/
$output = [
'swagger' => '2.0',
'info' => [
'version' => APP_VERSION_STABLE,
'title' => APP_NAME,
'description' => 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)',
'termsOfService' => 'https://appwrite.io/policy/terms',
'contact' => [
'name' => 'Appwrite Team',
'url' => 'https://appwrite.io/support',
'email' => App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM),
],
'license' => [
'name' => 'BSD-3-Clause',
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE',
],
],
'host' => \parse_url(App::getEnv('_APP_HOME', Config::getParam('domain')), PHP_URL_HOST),
'basePath' => '/v1',
'schemes' => ['https'],
'consumes' => ['application/json', 'multipart/form-data'],
'produces' => ['application/json'],
'securityDefinitions' => $keys[$platform],
'paths' => [],
'definitions' => [
// 'Pet' => [
// 'required' => ['id', 'name'],
// 'properties' => [
// 'id' => [
// 'type' => 'integer',
// 'format' => 'int64',
// ],
// 'name' => [
// 'type' => 'string',
// ],
// 'tag' => [
// 'type' => 'string',
// ],
// ],
// ],
// 'Pets' => array(
// 'type' => 'array',
// 'items' => array(
// '$ref' => '#/definitions/Pet',
// ),
// ),
'Error' => array(
'required' => array(
0 => 'code',
1 => 'message',
),
'properties' => array(
'code' => array(
'type' => 'integer',
'format' => 'int32',
),
'message' => array(
'type' => 'string',
),
),
),
],
'externalDocs' => [
'description' => 'Full API docs, specs and tutorials',
'url' => Config::getParam('protocol').'://'.Config::getParam('domain').'/docs',
],
];
if ($extensions) {
if (isset($output['securityDefinitions']['Project'])) {
$output['securityDefinitions']['Project']['extensions'] = ['demo' => '5df5acd0d48c2'];
}
if (isset($output['securityDefinitions']['Key'])) {
$output['securityDefinitions']['Key']['extensions'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
}
if (isset($output['securityDefinitions']['Locale'])) {
$output['securityDefinitions']['Locale']['extensions'] = ['demo' => 'en'];
}
function fromCamelCaseToDash($input)
{
return \str_replace([' ', '_'], '-', \strtolower(\preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $input)));
if (isset($output['securityDefinitions']['Mode'])) {
$output['securityDefinitions']['Mode']['extensions'] = ['demo' => ''];
}
}
foreach ($services as $service) { /* @noinspection PhpIncludeInspection */
if ($tests && !isset($service['tests'])) {
foreach ($utopia->getRoutes() as $key => $method) {
foreach ($method as $route) { /* @var $route \Utopia\Route */
if (!$route->getLabel('docs', true)) {
continue;
}
if ($tests && !$service['tests']) {
if (empty($route->getLabel('sdk.namespace', null))) {
continue;
}
if (!$tests && !$service['sdk']) {
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $route->getLabel('sdk.platform', []))) {
continue;
}
/** @noinspection PhpIncludeInspection */
include_once \realpath(__DIR__.'/../../'.$service['controller']);
}
$security = [
APP_PLATFORM_CLIENT => ['Project' => []],
APP_PLATFORM_SERVER => ['Project' => [], 'Key' => []],
APP_PLATFORM_CONSOLE => ['Project' => [], 'Key' => []],
];
$url = \str_replace('/v1', '', $route->getURL());
$scope = $route->getLabel('scope', '');
$hide = $route->getLabel('sdk.hide', false);
$consumes = ['application/json'];
$platforms = [
'client' => APP_PLATFORM_CLIENT,
'server' => APP_PLATFORM_SERVER,
'all' => APP_PLATFORM_CONSOLE,
];
if ($hide) {
continue;
}
$keys = [
APP_PLATFORM_CLIENT => [
'Project' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Project',
'description' => 'Your project ID',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
'description' => '',
'in' => 'header',
],
],
APP_PLATFORM_SERVER => [
'Project' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Project',
'description' => 'Your project ID',
'in' => 'header',
],
'Key' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Key',
'description' => 'Your secret API key',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
'description' => '',
'in' => 'header',
],
],
APP_PLATFORM_CONSOLE => [
'Project' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Project',
'description' => 'Your project ID',
'in' => 'header',
],
'Key' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Key',
'description' => 'Your secret API key',
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
'description' => '',
'in' => 'header',
],
'Mode' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Mode',
'description' => '',
'in' => 'header',
],
],
];
/*
* Specifications (v3.0.0):
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
*/
$output = [
'swagger' => '2.0',
'info' => [
'version' => APP_VERSION_STABLE,
'title' => APP_NAME,
'description' => 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)',
'termsOfService' => 'https://appwrite.io/policy/terms',
'contact' => [
'name' => 'Appwrite Team',
'url' => 'https://appwrite.io/support',
'email' => App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM),
],
'license' => [
'name' => 'BSD-3-Clause',
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE',
],
],
'host' => \parse_url(App::getEnv('_APP_HOME', Config::getParam('domain')), PHP_URL_HOST),
'basePath' => '/v1',
'schemes' => ['https'],
'consumes' => ['application/json', 'multipart/form-data'],
'produces' => ['application/json'],
'securityDefinitions' => $keys[$platform],
'paths' => [],
'definitions' => [
// 'Pet' => [
// 'required' => ['id', 'name'],
// 'properties' => [
// 'id' => [
// 'type' => 'integer',
// 'format' => 'int64',
// ],
// 'name' => [
// 'type' => 'string',
// ],
// 'tag' => [
// 'type' => 'string',
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath('../'.$route->getLabel('sdk.description', '')) : null;
$temp = [
'summary' => $route->getDesc(),
'operationId' => $route->getLabel('sdk.method', \uniqid()),
'consumes' => [],
'tags' => [$route->getLabel('sdk.namespace', 'default')],
'description' => ($desc) ? \file_get_contents($desc) : '',
// 'responses' => [
// 200 => [
// 'description' => 'An paged array of pets',
// 'schema' => [
// '$ref' => '#/definitions/Pet',
// ],
// ],
// ],
// 'Pets' => array(
// 'type' => 'array',
// 'items' => array(
// '$ref' => '#/definitions/Pet',
// ),
// ),
'Error' => array(
'required' => array(
0 => 'code',
1 => 'message',
),
'properties' => array(
'code' => array(
'type' => 'integer',
'format' => 'int32',
),
'message' => array(
'type' => 'string',
),
),
),
],
'externalDocs' => [
'description' => 'Full API docs, specs and tutorials',
'url' => Config::getParam('protocol').'://'.Config::getParam('domain').'/docs',
],
];
];
if ($extensions) {
if (isset($output['securityDefinitions']['Project'])) {
$output['securityDefinitions']['Project']['extensions'] = ['demo' => '5df5acd0d48c2'];
}
if (isset($output['securityDefinitions']['Key'])) {
$output['securityDefinitions']['Key']['extensions'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
}
if (isset($output['securityDefinitions']['Locale'])) {
$output['securityDefinitions']['Locale']['extensions'] = ['demo' => 'en'];
}
if ($extensions) {
$platformList = $route->getLabel('sdk.platform', []);
if (isset($output['securityDefinitions']['Mode'])) {
$output['securityDefinitions']['Mode']['extensions'] = ['demo' => ''];
}
}
foreach ($utopia->getRoutes() as $key => $method) {
foreach ($method as $route) { /* @var $route \Utopia\Route */
if (!$route->getLabel('docs', true)) {
continue;
}
if (empty($route->getLabel('sdk.namespace', null))) {
continue;
}
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $route->getLabel('sdk.platform', []))) {
continue;
}
$url = \str_replace('/v1', '', $route->getURL());
$scope = $route->getLabel('scope', '');
$hide = $route->getLabel('sdk.hide', false);
$consumes = ['application/json'];
if ($hide) {
continue;
}
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath('../'.$route->getLabel('sdk.description', '')) : null;
$temp = [
'summary' => $route->getDesc(),
'operationId' => $route->getLabel('sdk.method', \uniqid()),
'consumes' => [],
'tags' => [$route->getLabel('sdk.namespace', 'default')],
'description' => ($desc) ? \file_get_contents($desc) : '',
// 'responses' => [
// 200 => [
// 'description' => 'An paged array of pets',
// 'schema' => [
// '$ref' => '#/definitions/Pet',
// ],
// ],
// ],
$temp['extensions'] = [
'weight' => $route->getOrder(),
'cookies' => $route->getLabel('sdk.cookies', false),
'type' => $route->getLabel('sdk.methodType', ''),
'demo' => 'docs/examples/'.fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.fromCamelCaseToDash($temp['operationId']).'.md',
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
'rate-limit' => $route->getLabel('abuse-limit', 0),
'rate-time' => $route->getLabel('abuse-time', 3600),
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
'scope' => $route->getLabel('scope', ''),
'platforms' => $platformList,
];
}
if ($extensions) {
$platformList = $route->getLabel('sdk.platform', []);
if ((!empty($scope))) { // && 'public' != $scope
$temp['security'][] = $route->getLabel('sdk.security', $security[$platform]);
}
$temp['extensions'] = [
'weight' => $route->getOrder(),
'cookies' => $route->getLabel('sdk.cookies', false),
'type' => $route->getLabel('sdk.methodType', ''),
'demo' => 'docs/examples/'.fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.fromCamelCaseToDash($temp['operationId']).'.md',
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
'rate-limit' => $route->getLabel('abuse-limit', 0),
'rate-time' => $route->getLabel('abuse-time', 3600),
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
'scope' => $route->getLabel('scope', ''),
'platforms' => $platformList,
];
}
if ((!empty($scope))) { // && 'public' != $scope
$temp['security'][] = $route->getLabel('sdk.security', $security[$platform]);
}
$requestBody = [
'content' => [
'application/x-www-form-urlencoded' => [
'schema' => [
'type' => 'object',
'properties' => [],
],
'required' => [],
$requestBody = [
'content' => [
'application/x-www-form-urlencoded' => [
'schema' => [
'type' => 'object',
'properties' => [],
],
'required' => [],
],
],
];
foreach ($route->getParams() as $name => $param) {
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $utopia->getResources($param['resources'])) : $param['validator']; /* @var $validator \Utopia\Validator */
$node = [
'name' => $name,
'description' => $param['description'],
'required' => !$param['optional'],
];
foreach ($route->getParams() as $name => $param) {
$validator = (\is_callable($param['validator'])) ? $param['validator']() : $param['validator']; /* @var $validator \Utopia\Validator */
$node = [
'name' => $name,
'description' => $param['description'],
'required' => !$param['optional'],
];
switch ((!empty($validator)) ? \get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Utopia\Validator\Boolean':
$node['type'] = 'boolean';
$node['x-example'] = false;
break;
case 'Appwrite\Database\Validator\UID':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Utopia\Validator\Email':
$node['type'] = 'string';
$node['format'] = 'email';
$node['x-example'] = 'email@example.com';
break;
case 'Utopia\Validator\URL':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\JSON':
case 'Utopia\Validator\Mock':
case 'Utopia\Validator\Assoc':
$node['type'] = 'object';
$node['type'] = 'object';
$node['x-example'] = '{}';
//$node['format'] = 'json';
break;
case 'Appwrite\Storage\Validator\File':
$consumes = ['multipart/form-data'];
$node['type'] = 'file';
break;
case 'Utopia\Validator\ArrayList':
$node['type'] = 'array';
$node['collectionFormat'] = 'multi';
$node['items'] = [
'type' => 'string',
];
break;
case 'Appwrite\Auth\Validator\Password':
$node['type'] = 'string';
$node['format'] = 'format';
$node['x-example'] = 'password';
break;
case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */
$node['type'] = 'integer';
$node['format'] = 'int32';
$node['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
$node['type'] = 'integer';
$node['format'] = 'int32';
break;
case 'Utopia\Validator\Length':
$node['type'] = 'string';
break;
case 'Utopia\Validator\Host':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */
$node['type'] = 'string';
$node['x-example'] = $validator->getList()[0];
break;
default:
$node['type'] = 'string';
break;
}
if ($param['optional'] && !\is_null($param['default'])) { // Param has default value
$node['default'] = $param['default'];
}
if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
$node['in'] = 'path';
$temp['parameters'][] = $node;
} elseif ($key == 'GET') { // Param is in query
$node['in'] = 'query';
$temp['parameters'][] = $node;
} else { // Param is in payload
$node['in'] = 'formData';
$temp['parameters'][] = $node;
$requestBody['content']['application/x-www-form-urlencoded']['schema']['properties'][] = $node;
if (!$param['optional']) {
$requestBody['content']['application/x-www-form-urlencoded']['required'][] = $name;
}
}
$url = \str_replace(':'.$name, '{'.$name.'}', $url);
switch ((!empty($validator)) ? \get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Utopia\Validator\Boolean':
$node['type'] = 'boolean';
$node['x-example'] = false;
break;
case 'Appwrite\Database\Validator\UID':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Utopia\Validator\Email':
$node['type'] = 'string';
$node['format'] = 'email';
$node['x-example'] = 'email@example.com';
break;
case 'Utopia\Validator\URL':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\JSON':
case 'Utopia\Validator\Mock':
case 'Utopia\Validator\Assoc':
$node['type'] = 'object';
$node['type'] = 'object';
$node['x-example'] = '{}';
//$node['format'] = 'json';
break;
case 'Appwrite\Storage\Validator\File':
$consumes = ['multipart/form-data'];
$node['type'] = 'file';
break;
case 'Utopia\Validator\ArrayList':
$node['type'] = 'array';
$node['collectionFormat'] = 'multi';
$node['items'] = [
'type' => 'string',
];
break;
case 'Appwrite\Auth\Validator\Password':
$node['type'] = 'string';
$node['format'] = 'format';
$node['x-example'] = 'password';
break;
case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */
$node['type'] = 'integer';
$node['format'] = 'int32';
$node['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
$node['type'] = 'integer';
$node['format'] = 'int32';
break;
case 'Utopia\Validator\Length':
$node['type'] = 'string';
break;
case 'Utopia\Validator\Host':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */
$node['type'] = 'string';
$node['x-example'] = $validator->getList()[0];
break;
default:
$node['type'] = 'string';
break;
}
$temp['consumes'] = $consumes;
if ($param['optional'] && !\is_null($param['default'])) { // Param has default value
$node['default'] = $param['default'];
}
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
$node['in'] = 'path';
$temp['parameters'][] = $node;
} elseif ($key == 'GET') { // Param is in query
$node['in'] = 'query';
$temp['parameters'][] = $node;
} else { // Param is in payload
$node['in'] = 'formData';
$temp['parameters'][] = $node;
$requestBody['content']['application/x-www-form-urlencoded']['schema']['properties'][] = $node;
if (!$param['optional']) {
$requestBody['content']['application/x-www-form-urlencoded']['required'][] = $name;
}
}
$url = \str_replace(':'.$name, '{'.$name.'}', $url);
}
$temp['consumes'] = $consumes;
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
}
/*foreach ($consoleDB->getMocks() as $mock) {
var_dump($mock['name']);
}*/
\ksort($output['paths']);
$response
->json($output);
}
);
/*foreach ($consoleDB->getMocks() as $mock) {
var_dump($mock['name']);
}*/
\ksort($output['paths']);
$response
->json($output);
}, ['utopia', 'response']);

View file

@ -222,7 +222,7 @@ Locale::setLanguage('zh-tw', include __DIR__.'/config/locale/translations/zh-tw.
'http' => [
'method' => 'GET',
'user_agent' => \sprintf(APP_USERAGENT,
Config::getParam('version'),
App::getEnv('_APP_VERSION', 'UNKNOWN'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)),
'timeout' => 2,
],

View file

@ -96,7 +96,7 @@ class TasksV1
\curl_setopt($ch, CURLOPT_HEADER, 0);
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(APP_USERAGENT,
Config::getParam('version'),
App::getEnv('_APP_VERSION', 'UNKNOWN'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
));
\curl_setopt(

View file

@ -1,6 +1,6 @@
<?php
use Utopia\Config\Config;
use Utopia\App;
require_once __DIR__.'/../init.php';
@ -31,7 +31,7 @@ class UsageV1
$statsd = $register->get('statsd', true);
$tags = ",project={$projectId},version=".Config::getParam('version').'';
$tags = ",project={$projectId},version=".App::getEnv('_APP_VERSION', 'UNKNOWN').'';
// the global namespace is prepended to every key (optional)
$statsd->setNamespace('appwrite.usage');

View file

@ -6,7 +6,6 @@ require_once __DIR__.'/../init.php';
echo APP_NAME.' webhooks worker v1 has started';
use Utopia\Config\Config;
use Appwrite\Database\Database;
use Appwrite\Database\Validator\Authorization;
use Utopia\App;
@ -61,7 +60,7 @@ class WebhooksV1
\curl_setopt($ch, CURLOPT_HEADER, 0);
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(APP_USERAGENT,
Config::getParam('version'),
App::getEnv('_APP_VERSION', 'UNKNOWN'),
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
));
\curl_setopt(

View file

@ -32,7 +32,7 @@
"appwrite/php-clamav": "1.0.*",
"utopia-php/framework": "0.7.1",
"utopia-php/framework": "0.7.2",
"utopia-php/abuse": "0.2.*",
"utopia-php/audit": "0.3.*",
"utopia-php/cache": "0.2.*",

22
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b8ee06f97c395bc83a05f92939679724",
"content-hash": "2693761ec4a5bb1305ac226bffd1555d",
"packages": [
{
"name": "appwrite/php-clamav",
@ -1596,16 +1596,16 @@
},
{
"name": "utopia-php/framework",
"version": "0.7.1",
"version": "0.7.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/framework.git",
"reference": "3810789c1caf16a9ad7811fd38067a35249e75f8"
"reference": "e592b7bdea5eeb48a3bf7ae18bc6d3e622e54cf3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/3810789c1caf16a9ad7811fd38067a35249e75f8",
"reference": "3810789c1caf16a9ad7811fd38067a35249e75f8",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/e592b7bdea5eeb48a3bf7ae18bc6d3e622e54cf3",
"reference": "e592b7bdea5eeb48a3bf7ae18bc6d3e622e54cf3",
"shasum": ""
},
"require": {
@ -1636,20 +1636,20 @@
"php",
"upf"
],
"time": "2020-06-29T16:02:35+00:00"
"time": "2020-06-30T09:43:41+00:00"
},
{
"name": "utopia-php/locale",
"version": "0.3.0",
"version": "0.3.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/locale.git",
"reference": "32c32a3bf5c295f3de93569cead7f412fa29ad13"
"reference": "89c488fbff65fc87c048786c3d76b6003fbaa833"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/locale/zipball/32c32a3bf5c295f3de93569cead7f412fa29ad13",
"reference": "32c32a3bf5c295f3de93569cead7f412fa29ad13",
"url": "https://api.github.com/repos/utopia-php/locale/zipball/89c488fbff65fc87c048786c3d76b6003fbaa833",
"reference": "89c488fbff65fc87c048786c3d76b6003fbaa833",
"shasum": ""
},
"require": {
@ -1682,7 +1682,7 @@
"upf",
"utopia"
],
"time": "2020-06-29T12:39:35+00:00"
"time": "2020-06-29T20:53:16+00:00"
},
{
"name": "utopia-php/registry",