1
0
Fork 0
mirror of synced 2024-05-20 12:42:39 +12:00

Merge branch '0.7.x' of github.com:appwrite/appwrite into feat-265-realtime-support

This commit is contained in:
Eldad Fux 2020-10-31 16:59:00 +02:00
commit 1668da931f
137 changed files with 21459 additions and 23454 deletions

2
.env
View file

@ -24,6 +24,6 @@ _APP_SMTP_PORT=25
_APP_SMTP_SECURE=
_APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD=
_APP_STORAGE_LIMIT=100000000
_APP_STORAGE_LIMIT=10000000
_APP_FUNCTIONS_TIMEOUT=900
_APP_FUNCTIONS_CONTAINERS=10

View file

@ -39,4 +39,4 @@ script:
- docker-compose logs appwrite
- docker exec appwrite doctor
- docker exec appwrite vars
- docker exec appwrite test
- docker exec appwrite test

View file

@ -44,6 +44,8 @@
## Breaking Changes (Read before upgrading!)
- **Deprecated** `first` and `last` query params for documents list route in the database API
- **Deprecated** Deprectaed Pubjabi Translations ('pn')
- **Deprecated** `PATCH /account/prefs` is now updating the prefs payload and not just merging it
- **Deprecated** `PATCH /users/:userId/prefs` is now updating the prefs payload and not just merging it
- Switched order of limit and offset params in all the SDKs `listDocuments` method for better consistency
- Default `limit` param value in all the SDKs `listDocuments` method is now 25 for better consistency

View file

@ -117,10 +117,8 @@ Appwrite's current structure is a combination of both [Monolithic](https://en.wi
│ ├── Extend
│ ├── Network
│ ├── OpenSSL
│ ├── Preloader
│ ├── Resize
│ ├── Storage
│ ├── Swoole
│ ├── Task
│ ├── Template
│ ├── URL

View file

@ -75,7 +75,7 @@ ENV _APP_SERVER=swoole \
_APP_OPTIONS_ABUSE=enabled \
_APP_OPTIONS_FORCE_HTTPS=disabled \
_APP_OPENSSL_KEY_V1=your-secret-key \
_APP_STORAGE_LIMIT=100000000 \
_APP_STORAGE_LIMIT=10000000 \
_APP_STORAGE_ANTIVIRUS=enabled \
_APP_REDIS_HOST=redis \
_APP_REDIS_PORT=6379 \
@ -159,6 +159,7 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/migrate && \
chmod +x /usr/local/bin/realtime && \
chmod +x /usr/local/bin/schedule && \
chmod +x /usr/local/bin/sdks && \
chmod +x /usr/local/bin/ssl && \
chmod +x /usr/local/bin/test && \
chmod +x /usr/local/bin/vars && \
@ -181,8 +182,8 @@ RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini
RUN echo "opcache.preload_user=www-data" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.preload=/usr/src/code/app/preload.php" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.enable_cli = 1" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "default_socket_timeout = -1" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/appwrite.ini
RUN echo "default_socket_timeout=-1" >> /usr/local/etc/php/conf.d/appwrite.ini
EXPOSE 80

View file

@ -60,7 +60,7 @@ ENV TZ=Asia/Tel_Aviv \
_APP_OPTIONS_ABUSE=enabled \
_APP_OPTIONS_FORCE_HTTPS=disabled \
_APP_OPENSSL_KEY_V1=your-secret-key \
_APP_STORAGE_LIMIT=100000000 \
_APP_STORAGE_LIMIT=10000000 \
_APP_STORAGE_ANTIVIRUS=enabled \
_APP_REDIS_HOST=redis \
_APP_REDIS_PORT=6379 \

View file

@ -9,7 +9,7 @@
[![Hacktoberfest](https://badgen.net/badge/hacktoberfest/friendly/pink)](CONTRIBUTING.md)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord)](https://appwrite.io/discord)
[![Docker Pulls](https://badgen.net/docker/pulls/appwrite/appwrite)](https://travis-ci.org/appwrite/appwrite)
[![Docker Pulls](https://badgen.net/docker/pulls/appwrite/appwrite)](https://hub.docker.com/r/appwrite/appwrite)
[![Travis CI](https://badgen.net/travis/appwrite/appwrite?label=build)](https://travis-ci.com/appwrite/appwrite)
[![Twitter Account](https://badgen.net/twitter/follow/appwrite_io?label=twitter)](https://twitter.com/appwrite_io)
[![Follow Appwrite on StackShare](https://badgen.net/badge/follow%20on/stackshare/blue)](https://stackshare.io/appwrite)

View file

@ -4,7 +4,7 @@ use Utopia\App;
use Utopia\Config\Config;
use Appwrite\Database\Database;
$providers = Config::getParam('providers');
$providers = Config::getParam('providers', []);
$collections = [
'console' => [
@ -234,6 +234,7 @@ $collections = [
'default' => '',
'required' => false,
'array' => false,
'filter' => ['json']
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
@ -336,6 +337,123 @@ $collections = [
'required' => true,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OS Code',
'key' => 'osCode',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OS Name',
'key' => 'osName',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'OS Version',
'key' => 'osVersion',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Type',
'key' => 'clientType',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Code',
'key' => 'clientCode',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Name',
'key' => 'clientName',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Version',
'key' => 'clientVersion',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Engine',
'key' => 'clientEngine',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Client Engine Version',
'key' => 'clientEngineVersion',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Device Name',
'key' => 'deviceName',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Device Brand',
'key' => 'deviceBrand',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Device Model',
'key' => 'deviceModel',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Country Code',
'key' => 'countryCode',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
],
],
Database::SYSTEM_COLLECTION_MEMBERSHIPS => [
@ -1273,8 +1391,8 @@ $collections = [
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Previous',
'key' => 'previous',
'label' => 'Schedule Previous Run',
'key' => 'schedulePrevious',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
@ -1282,8 +1400,8 @@ $collections = [
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Next',
'key' => 'next',
'label' => 'Schedule Next Run',
'key' => 'scheduleNext',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
@ -1337,7 +1455,7 @@ $collections = [
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Code Path',
'key' => 'codePath',
'key' => 'path',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
@ -1346,7 +1464,7 @@ $collections = [
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Code Size',
'key' => 'codeSize',
'key' => 'size',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => '',
'required' => false,
@ -1388,6 +1506,15 @@ $collections = [
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Trigger',
'key' => 'trigger',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => '',
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Status',

View file

@ -55,4 +55,16 @@ return [
'storage.files.delete' => [
'description' => 'This event triggers when a storage file is deleted.',
],
'users.create' => [
'description' => 'This event triggers when a user is created from the users API.',
],
'users.update.status' => [
'description' => 'This event triggers when a user status is updated from the users API.',
],
'users.delete' => [
'description' => 'This event triggers when a user is deleted from users API.',
],
'users.sessions.delete' => [
'description' => 'This event triggers when a user session is deleted from users API.',
],
];

View file

@ -135,7 +135,7 @@ return [
],
[
'name' => '_APP_STORAGE_LIMIT',
'default' => '100000000',
'default' => '10000000',
'required' => false,
'question' => '',
],

View file

@ -28,23 +28,10 @@ use Utopia\Validator\ArrayList;
$oauthDefaultSuccess = App::getEnv('_APP_HOME').'/auth/oauth2/success';
$oauthDefaultFailure = App::getEnv('_APP_HOME').'/auth/oauth2/failure';
$oauth2Keys = [];
App::init(function() use (&$oauth2Keys) {
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$oauth2Keys[] = 'oauth2'.\ucfirst($key);
$oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
}, [], 'account');
App::post('/v1/account')
->desc('Create Account')
->groups(['api', 'account'])
->label('webhook', 'account.create')
->label('event', 'account.create')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@ -54,12 +41,11 @@ App::post('/v1/account')
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->action(function ($email, $password, $name, $request, $response, $project, $projectDB, $webhooks, $audits) use ($oauth2Keys) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
->action(function ($email, $password, $name, $request, $response, $project, $projectDB, $audits) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
if ('console' === $project->getId()) {
@ -120,36 +106,26 @@ App::post('/v1/account')
throw new Exception('Failed saving user to DB', 500);
}
$webhooks
->setParam('payload', [
'name' => $name,
'email' => $email,
])
;
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.create')
->setParam('resource', 'users/'.$user->getId())
;
$user
->setAttribute('roles', Authorization::getRoles())
;
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
}, ['request', 'response', 'project', 'projectDB', 'webhooks', 'audits']);
->dynamic($user, Response::MODEL_USER)
;
}, ['request', 'response', 'project', 'projectDB', 'audits']);
App::post('/v1/account/sessions')
->desc('Create Account Session')
->groups(['api', 'account'])
->label('webhook', 'account.sessions.create')
->label('event', 'account.sessions.create')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@ -159,11 +135,12 @@ App::post('/v1/account/sessions')
->label('abuse-key', 'url:{url},email:{param-email}')
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->action(function ($email, $password, $request, $response, $projectDB, $webhooks, $audits) {
/** @var Appwrite\Swoole\Request $request */
->action(function ($email, $password, $request, $response, $projectDB, $locale, $geodb, $audits) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
$protocol = $request->getProtocol();
@ -185,6 +162,23 @@ App::post('/v1/account/sessions')
throw new Exception('Invalid credentials', 401); // Wrong password or username
}
$dd = new DeviceDetector($request->getUserAgent('UNKNOWN'));
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator();
$session = new Document([
@ -195,8 +189,33 @@ App::post('/v1/account/sessions')
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
]);
$record = $geodb->get($request->getIP());
if($record) {
$session
->setAttribute('countryCode', \strtolower($record['country']['iso_code']))
;
} else {
$session
->setAttribute('countryCode', '--')
;
}
Authorization::setRole('user:'.$profile->getId());
$session = $projectDB->createDocument($session->getArrayCopy());
@ -212,14 +231,7 @@ App::post('/v1/account/sessions')
if (false === $profile) {
throw new Exception('Failed saving user to DB', 500);
}
$webhooks
->setParam('payload', [
'name' => $profile->getAttribute('name', ''),
'email' => $profile->getAttribute('email', ''),
])
;
$audits
->setParam('userId', $profile->getId())
->setParam('event', 'account.sessions.create')
@ -237,10 +249,14 @@ App::post('/v1/account/sessions')
->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
->setStatusCode(Response::STATUS_CODE_CREATED)
;
$session
->setAttribute('current', true)
->setAttribute('countryName', (isset($countries[$session->getAttribute('countryCode')])) ? $countries[$session->getAttribute('countryCode')] : $locale->getText('locale.country.unknown'))
;
$response->dynamic($session, Response::MODEL_SESSION);
;
}, ['request', 'response', 'projectDB', 'webhooks', 'audits']);
}, ['request', 'response', 'projectDB', 'locale', 'geodb', 'audits']);
App::get('/v1/account/sessions/oauth2/:provider')
->desc('Create Account Session with OAuth2')
@ -261,8 +277,8 @@ App::get('/v1/account/sessions/oauth2/:provider')
->param('failure', $oauthDefaultFailure, function ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('scopes', [], new ArrayList(new Text(128)), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes.', true)
->action(function ($provider, $success, $failure, $scopes, $request, $response, $project) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
$protocol = $request->getProtocol();
@ -306,8 +322,8 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->param('code', '', new Text(1024), 'OAuth2 code.')
->param('state', '', new Text(2048), 'Login state params.', true)
->action(function ($projectId, $provider, $code, $state, $request, $response) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
$domain = $request->getHostname();
$protocol = $request->getProtocol();
@ -331,8 +347,8 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->param('code', '', new Text(1024), 'OAuth2 code.')
->param('state', '', new Text(2048), 'Login state params.', true)
->action(function ($projectId, $provider, $code, $state, $request, $response) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
$domain = $request->getHostname();
$protocol = $request->getProtocol();
@ -348,7 +364,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('OAuth2 Redirect')
->groups(['api', 'account'])
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('webhook', 'account.sessions.create')
->label('event', 'account.sessions.create')
->label('scope', 'public')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
@ -356,12 +372,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
->param('code', '', new Text(1024), 'OAuth2 code.')
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
->action(function ($provider, $code, $state, $request, $response, $project, $user, $projectDB, $audits) use ($oauthDefaultSuccess) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
->action(function ($provider, $code, $state, $request, $response, $project, $user, $projectDB, $geodb, $audits) use ($oauthDefaultSuccess) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
$protocol = $request->getProtocol();
@ -482,6 +499,24 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
// Create session token, verify user account and update OAuth2 ID and Access Token
$dd = new DeviceDetector($request->getUserAgent('UNKNOWN'));
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$secret = Auth::tokenGenerator();
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document([
@ -492,8 +527,33 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
]);
$record = $geodb->get($request->getIP());
if($record) {
$session
->setAttribute('countryCode', \strtolower($record['country']['iso_code']))
;
} else {
$session
->setAttribute('countryCode', '--')
;
}
$user
->setAttribute('oauth2'.\ucfirst($provider), $oauth2ID)
->setAttribute('oauth2'.\ucfirst($provider).'AccessToken', $accessToken)
@ -523,7 +583,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
// Add keys for non-web platforms - TODO - add verification phase to aviod session sniffing
if (parse_url($state['success'], PHP_URL_PATH) === $oauthDefaultSuccess) {
if (parse_url($state['success'], PHP_URL_PATH) === parse_url($oauthDefaultSuccess, PHP_URL_PATH)) {
$state['success'] = URLParser::parse($state['success']);
$query = URLParser::parseQuery($state['success']['query']);
$query['project'] = $project->getId();
@ -541,7 +601,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
->redirect($state['success'])
;
}, ['request', 'response', 'project', 'user', 'projectDB', 'audits']);
}, ['request', 'response', 'project', 'user', 'projectDB', 'geodb', 'audits']);
App::get('/v1/account')
->desc('Get Account')
@ -552,20 +612,15 @@ App::get('/v1/account')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/account/get.md')
->label('sdk.response', ['200' => 'user'])
->action(function ($response, $user) use ($oauth2Keys) {
/** @var Utopia\Response $response */
->action(function ($response, $user) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'emailVerification',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$user
->setAttribute('roles', Authorization::getRoles())
;
$response->dynamic($user, Response::MODEL_USER);
}, ['response', 'user']);
App::get('/v1/account/prefs')
@ -577,17 +632,10 @@ App::get('/v1/account/prefs')
->label('sdk.method', 'getPrefs')
->label('sdk.description', '/docs/references/account/get-prefs.md')
->action(function ($response, $user) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$prefs = $user->getAttribute('prefs', '{}');
try {
$prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
}
$prefs = $user->getAttribute('prefs', new \stdClass);
$response->json($prefs);
}, ['response', 'user']);
@ -600,65 +648,34 @@ App::get('/v1/account/sessions')
->label('sdk.namespace', 'account')
->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/account/get-sessions.md')
->action(function ($response, $user, $locale, $geodb) {
/** @var Utopia\Response $response */
->action(function ($response, $user, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
$tokens = $user->getAttribute('tokens', []);
$sessions = [];
$current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
$index = 0;
$countries = $locale->getText('countries');
$current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
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';
$token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')]))
? $countries[$token->getAttribute('contryCode')]
: $locale->getText('locale.country.unknown'));
$token->setAttribute('current', ($current == $token->getId()) ? true : false);
$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' => [],
'current' => ($current == $token->getId()) ? true : false,
];
try {
$record = $geodb->get($token->getAttribute('ip', ''));
if ($record) {
$sessions[$index]['geo']['isoCode'] = \strtolower($record['country']['iso_code']);
$sessions[$index]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} else {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
}
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
}
++$index;
$sessions[] = $token;
}
$response->json($sessions);
}, ['response', 'user', 'locale', 'geodb']);
$response->dynamic(new Document([
'sum' => count($sessions),
'sessions' => $sessions
]), Response::MODEL_SESSION_LIST);
}, ['response', 'user', 'locale']);
App::get('/v1/account/logs')
->desc('Get Account Logs')
@ -669,7 +686,7 @@ App::get('/v1/account/logs')
->label('sdk.method', 'getLogs')
->label('sdk.description', '/docs/references/account/get-logs.md')
->action(function ($response, $register, $project, $user, $locale, $geodb) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
@ -710,50 +727,65 @@ App::get('/v1/account/logs')
$dd->parse();
$output[$i] = [
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$output[$i] = new Document([
'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 = $geodb->get($log['ip']);
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
]);
if ($record) {
$output[$i]['geo']['isoCode'] = \strtolower($record['country']['iso_code']);
$output[$i]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} else {
$output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = $locale->getText('locale.country.unknown');
}
$record = $geodb->get($log['ip']);
} catch (\Exception $e) {
$output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = $locale->getText('locale.country.unknown');
if ($record) {
$output[$i]['countryCode'] = (isset($countries[$record['country']['iso_code']])) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
}
}
$response->json($output);
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
}, ['response', 'register', 'project', 'user', 'locale', 'geodb']);
App::patch('/v1/account/name')
->desc('Update Account Name')
->groups(['api', 'account'])
->label('webhook', 'account.update.name')
->label('event', 'account.update.name')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateName')
->label('sdk.description', '/docs/references/account/update-name.md')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.')
->action(function ($name, $response, $user, $projectDB, $audits) use ($oauth2Keys) {
/** @var Utopia\Response $response */
->action(function ($name, $response, $user, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
@ -766,27 +798,21 @@ App::patch('/v1/account/name')
throw new Exception('Failed saving user to DB', 500);
}
$user->setAttribute('roles', Authorization::getRoles());
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.update.name')
->setParam('resource', 'users/'.$user->getId())
;
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->dynamic($user, Response::MODEL_USER);
}, ['response', 'user', 'projectDB', 'audits']);
App::patch('/v1/account/password')
->desc('Update Account Password')
->groups(['api', 'account'])
->label('webhook', 'account.update.password')
->label('event', 'account.update.password')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@ -794,8 +820,8 @@ App::patch('/v1/account/password')
->label('sdk.description', '/docs/references/account/update-password.md')
->param('password', '', new Password(), 'New user password. Must be between 6 to 32 chars.')
->param('oldPassword', '', new Password(), 'Old user password. Must be between 6 to 32 chars.')
->action(function ($password, $oldPassword, $response, $user, $projectDB, $audits) use ($oauth2Keys) {
/** @var Utopia\Response $response */
->action(function ($password, $oldPassword, $response, $user, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
@ -812,27 +838,21 @@ App::patch('/v1/account/password')
throw new Exception('Failed saving user to DB', 500);
}
$user->setAttribute('roles', Authorization::getRoles());
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.update.password')
->setParam('resource', 'users/'.$user->getId())
;
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->dynamic($user, Response::MODEL_USER);
}, ['response', 'user', 'projectDB', 'audits']);
App::patch('/v1/account/email')
->desc('Update Account Email')
->groups(['api', 'account'])
->label('webhook', 'account.update.email')
->label('event', 'account.update.email')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@ -840,8 +860,8 @@ App::patch('/v1/account/email')
->label('sdk.description', '/docs/references/account/update-email.md')
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->action(function ($email, $password, $response, $user, $projectDB, $audits) use ($oauth2Keys) {
/** @var Utopia\Response $response */
->action(function ($email, $password, $response, $user, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
@ -873,44 +893,35 @@ App::patch('/v1/account/email')
throw new Exception('Failed saving user to DB', 500);
}
$user->setAttribute('roles', Authorization::getRoles());
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.update.email')
->setParam('resource', 'users/'.$user->getId())
;
$response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
'registration',
'name',
],
$oauth2Keys
)), ['roles' => Authorization::getRoles()]));
$response->dynamic($user, Response::MODEL_USER);
}, ['response', 'user', 'projectDB', 'audits']);
App::patch('/v1/account/prefs')
->desc('Update Account Preferences')
->groups(['api', 'account'])
->label('webhook', 'account.update.prefs')
->label('event', 'account.update.prefs')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'updatePrefs')
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->label('sdk.description', '/docs/references/account/update-prefs.md')
->param('prefs', [], new Assoc(), 'Prefs key-value JSON object.')
->action(function ($prefs, $response, $user, $projectDB, $audits) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
$old = \json_decode($user->getAttribute('prefs', '{}'), true);
$old = ($old) ? $old : [];
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'prefs' => \json_encode(\array_merge($old, $prefs)),
'prefs' => $prefs,
]));
if (false === $user) {
@ -922,14 +933,7 @@ App::patch('/v1/account/prefs')
->setParam('resource', 'users/'.$user->getId())
;
$prefs = $user->getAttribute('prefs', '{}');
try {
$prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
}
$prefs = $user->getAttribute('prefs', new \stdClass);
$response->json($prefs);
}, ['response', 'user', 'projectDB', 'audits']);
@ -937,15 +941,15 @@ App::patch('/v1/account/prefs')
App::delete('/v1/account')
->desc('Delete Account')
->groups(['api', 'account'])
->label('webhook', 'account.delete')
->label('event', 'account.delete')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/account/delete.md')
->action(function ($request, $response, $user, $projectDB, $audits, $webhooks) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
@ -976,10 +980,7 @@ App::delete('/v1/account')
;
$webhooks
->setParam('payload', [
'name' => $user->getAttribute('name', ''),
'email' => $user->getAttribute('email', ''),
])
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
if (!Config::getParam('domainVerification')) {
@ -999,7 +1000,7 @@ App::delete('/v1/account/sessions/:sessionId')
->desc('Delete Account Session')
->groups(['api', 'account'])
->label('scope', 'account')
->label('webhook', 'account.sessions.delete')
->label('event', 'account.sessions.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'deleteSession')
@ -1007,8 +1008,8 @@ App::delete('/v1/account/sessions/:sessionId')
->label('abuse-limit', 100)
->param('sessionId', null, new UID(), 'Session unique ID. Use the string \'current\' to delete the current device session.')
->action(function ($sessionId, $request, $response, $user, $projectDB, $audits, $webhooks) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
@ -1034,10 +1035,7 @@ App::delete('/v1/account/sessions/:sessionId')
;
$webhooks
->setParam('payload', [
'name' => $user->getAttribute('name', ''),
'email' => $user->getAttribute('email', ''),
])
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
if (!Config::getParam('domainVerification')) {
@ -1064,15 +1062,15 @@ App::delete('/v1/account/sessions')
->desc('Delete All Account Sessions')
->groups(['api', 'account'])
->label('scope', 'account')
->label('webhook', 'account.sessions.delete')
->label('event', 'account.sessions.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'deleteSessions')
->label('sdk.description', '/docs/references/account/delete-sessions.md')
->label('abuse-limit', 100)
->action(function ($request, $response, $user, $projectDB, $audits, $webhooks) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
@ -1091,12 +1089,9 @@ App::delete('/v1/account/sessions')
->setParam('event', 'account.sessions.delete')
->setParam('resource', '/user/'.$user->getId())
;
$webhooks
->setParam('payload', [
'name' => $user->getAttribute('name', ''),
'email' => $user->getAttribute('email', ''),
])
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
if (!Config::getParam('domainVerification')) {
@ -1129,8 +1124,8 @@ App::post('/v1/account/recovery')
->param('email', '', new Email(), 'User email.')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
->action(function ($email, $url, $request, $response, $projectDB, $project, $locale, $mails, $audits) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Database\Document $project */
/** @var Utopia\Locale\Locale $locale */
@ -1237,7 +1232,7 @@ App::put('/v1/account/recovery')
->param('password', '', new Password(), 'New password. Must be between 6 to 32 chars.')
->param('passwordAgain', '', new Password(), 'New password again. Must be between 6 to 32 chars.')
->action(function ($userId, $secret, $password, $passwordAgain, $response, $projectDB, $audits) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
@ -1306,8 +1301,8 @@ App::post('/v1/account/verification')
->label('abuse-key', 'url:{url},email:{param-email}')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add built-in confirm page
->action(function ($url, $request, $response, $project, $user, $projectDB, $locale, $audits, $mails) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
@ -1402,7 +1397,7 @@ App::put('/v1/account/verification')
->param('userId', '', new UID(), 'User unique ID.')
->param('secret', '', new Text(256), 'Valid verification token.')
->action(function ($userId, $secret, $response, $user, $projectDB, $audits) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */

View file

@ -17,7 +17,7 @@ use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
$avatarCallback = function ($type, $code, $width, $height, $quality, $response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$code = \strtolower($code);
$type = \strtolower($type);
@ -143,7 +143,7 @@ App::get('/v1/avatars/image')
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, 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 */
/** @var Appwrite\Utopia\Response $response */
$quality = 80;
$output = 'png';
@ -207,7 +207,7 @@ App::get('/v1/avatars/favicon')
->label('sdk.description', '/docs/references/avatars/get-favicon.md')
->param('url', '', new URL(), 'Website URL which you want to fetch the favicon from.')
->action(function ($url, $response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$width = 56;
$height = 56;
@ -360,7 +360,7 @@ App::get('/v1/avatars/qr')
->param('margin', 1, new Range(0, 10), 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('download', false, 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 */
/** @var Appwrite\Utopia\Response $response */
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
$options = new QROptions([
@ -396,7 +396,7 @@ App::get('/v1/avatars/initials')
->param('color', '', new HexColor(), 'Changes text color. By default a random color will be picked and stay will persistent to the given name.', true)
->param('background', '', 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\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$themes = [

View file

@ -2,15 +2,11 @@
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;
use Utopia\Validator\Range;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Text;
use Utopia\Validator\ArrayList;
use Utopia\Validator\JSON;
// use Utopia\Locale\Locale;
// use Utopia\Audit\Audit;
// use Utopia\Audit\Adapters\MySQL as AuditAdapter;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\UID;
@ -20,11 +16,12 @@ use Appwrite\Database\Validator\Collection;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Exception\Authorization as AuthorizationException;
use Appwrite\Database\Exception\Structure as StructureException;
use Appwrite\Utopia\Response;
App::post('/v1/database/collections')
->desc('Create Collection')
->groups(['api', 'database'])
->label('webhook', 'database.collections.create')
->label('event', 'database.collections.create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
@ -34,10 +31,9 @@ App::post('/v1/database/collections')
->param('read', [], 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', [], 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 ($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, $webhooks, $audits) {
/** @var Utopia\Response $response */
->action(function ($name, $read, $write, $rules, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$parsedRules = [];
@ -77,26 +73,17 @@ App::post('/v1/database/collections')
throw new Exception('Failed saving collection to DB', 500);
}
$data = $data->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.collections.create')
->setParam('resource', 'database/collection/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/collection/'.$data->getId())
->setParam('data', $data->getArrayCopy())
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
->dynamic($data, Response::MODEL_COLLECTION)
;
}, ['response', 'projectDB', 'webhooks', 'audits']);
}, ['response', 'projectDB', 'audits']);
App::get('/v1/database/collections')
->desc('List Collections')
@ -111,7 +98,7 @@ App::get('/v1/database/collections')
->param('offset', 0, new Range(0, 40000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB->getCollection([
@ -126,7 +113,10 @@ App::get('/v1/database/collections')
],
]);
$response->json(['sum' => $projectDB->getSum(), 'collections' => $results]);
$response->dynamic(new Document([
'sum' => $projectDB->getSum(),
'collections' => $results
]), Response::MODEL_COLLECTION_LIST);
}, ['response', 'projectDB']);
App::get('/v1/database/collections/:collectionId')
@ -139,7 +129,7 @@ App::get('/v1/database/collections/:collectionId')
->label('sdk.description', '/docs/references/database/get-collection.md')
->param('collectionId', '', new UID(), 'Collection unique ID.')
->action(function ($collectionId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$collection = $projectDB->getDocument($collectionId, false);
@ -148,79 +138,14 @@ App::get('/v1/database/collections/:collectionId')
throw new Exception('Collection not found', 404);
}
$response->json($collection->getArrayCopy());
$response->dynamic($collection, Response::MODEL_COLLECTION);
}, ['response', 'projectDB']);
// App::get('/v1/database/collections/:collectionId/logs')
// ->desc('Get Collection Logs')
// ->groups(['api', 'database'])
// ->label('scope', 'collections.read')
// ->label('sdk.platform', [APP_PLATFORM_SERVER])
// ->label('sdk.namespace', 'database')
// ->label('sdk.method', 'getCollectionLogs')
// ->label('sdk.description', '/docs/references/database/get-collection-logs.md')
// ->param('collectionId', '', new UID(), 'Collection unique ID.')
// ->action(
// function ($collectionId) use ($response, $register, $projectDB, $project) {
// $collection = $projectDB->getDocument($collectionId, false);
// if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
// throw new Exception('Collection not found', 404);
// }
// $adapter = new AuditAdapter($register->get('db'));
// $adapter->setNamespace('app_'.$project->getId());
// $audit = new Audit($adapter);
// $countries = Locale::getText('countries');
// $logs = $audit->getLogsByResource('database/collection/'.$collection->getId());
// $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);
// }
// );
App::put('/v1/database/collections/:collectionId')
->desc('Update Collection')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('webhook', 'database.collections.update')
->label('event', 'database.collections.update')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'updateCollection')
@ -230,10 +155,9 @@ App::put('/v1/database/collections/:collectionId')
->param('read', [], 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', [], 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 ($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, ['projectDB'])
->action(function ($collectionId, $name, $read, $write, $rules, $response, $projectDB, $webhooks, $audits) {
/** @var Utopia\Response $response */
->action(function ($collectionId, $name, $read, $write, $rules, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$collection = $projectDB->getDocument($collectionId, false);
@ -277,33 +201,27 @@ App::put('/v1/database/collections/:collectionId')
throw new Exception('Failed saving collection to DB', 500);
}
$data = $collection->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.collections.update')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/collections/'.$collection->getId())
->setParam('data', $collection->getArrayCopy())
;
$response->json($collection->getArrayCopy());
}, ['response', 'projectDB', 'webhooks', 'audits']);
$response->dynamic($collection, Response::MODEL_COLLECTION);
}, ['response', 'projectDB', 'audits']);
App::delete('/v1/database/collections/:collectionId')
->desc('Delete Collection')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('webhook', 'database.collections.delete')
->label('event', 'database.collections.delete')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'deleteCollection')
->label('sdk.description', '/docs/references/database/delete-collection.md')
->param('collectionId', '', new UID(), 'Collection unique ID.')
->action(function ($collectionId, $response, $projectDB, $webhooks, $audits) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
@ -318,16 +236,14 @@ App::delete('/v1/database/collections/:collectionId')
throw new Exception('Failed to remove collection from DB', 500);
}
$data = $collection->getArrayCopy();
$webhooks
->setParam('payload', $data)
->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION))
;
$audits
->setParam('event', 'database.collections.delete')
->setParam('resource', 'database/collections/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/collections/'.$collection->getId())
->setParam('data', $collection->getArrayCopy())
;
$response->noContent();
@ -336,7 +252,7 @@ App::delete('/v1/database/collections/:collectionId')
App::post('/v1/database/collections/:collectionId/documents')
->desc('Create Document')
->groups(['api', 'database'])
->label('webhook', 'database.documents.create')
->label('event', 'database.documents.create')
->label('scope', 'documents.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@ -349,10 +265,9 @@ App::post('/v1/database/collections/:collectionId/documents')
->param('parentDocument', '', new UID(), 'Parent document unique ID. Use when you want your new document to be a child of a parent document.', true)
->param('parentProperty', '', 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, new WhiteList([Document::SET_TYPE_ASSIGN, Document::SET_TYPE_APPEND, Document::SET_TYPE_PREPEND], true), '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, $response, $projectDB, $webhooks, $audits) {
/** @var Utopia\Response $response */
->action(function ($collectionId, $data, $read, $write, $parentDocument, $parentProperty, $parentPropertyType, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@ -387,11 +302,11 @@ App::post('/v1/database/collections/:collectionId/documents')
}
/*
* 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)
*/
* 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);
@ -436,26 +351,17 @@ App::post('/v1/database/collections/:collectionId/documents')
throw new Exception('Failed saving document to DB'.$exception->getMessage(), 500);
}
$data = $data->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.documents.create')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
->setParam('data', $data->getArrayCopy())
;
/*
* View
*/
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($data)
->dynamic($data, Response::MODEL_ANY)
;
}, ['response', 'projectDB', 'webhooks', 'audits']);
}, ['response', 'projectDB', 'audits']);
App::get('/v1/database/collections/:collectionId/documents')
->desc('List Documents')
@ -474,7 +380,7 @@ App::get('/v1/database/collections/:collectionId/documents')
->param('orderCast', 'string', new WhiteList(['int', 'string', 'date', 'time', 'datetime'], true), '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', '', new Text(256), 'Search query. Enter any free text search. The database will try to find a match against all document attributes and children. Max length: 256 chars.', true)
->action(function ($collectionId, $filters, $limit, $offset, $orderField, $orderType, $orderCast, $search, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$collection = $projectDB->getDocument($collectionId, false);
@ -495,27 +401,24 @@ App::get('/v1/database/collections/:collectionId/documents')
]),
]);
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)
;
}
// 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->dynamic($collection, Response::MODEL_DOCUMENT_LIST);
}, ['response', 'projectDB']);
App::get('/v1/database/collections/:collectionId/documents/:documentId')
@ -529,8 +432,8 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
->param('collectionId', null, 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, new UID(), 'Document unique ID.')
->action(function ($collectionId, $documentId, $request, $response, $projectDB) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$document = $projectDB->getDocument($documentId, false);
@ -540,36 +443,13 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
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);
$response->dynamic($document, Response::MODEL_ANY);
}, ['request', 'response', 'projectDB']);
App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Update Document')
->groups(['api', 'database'])
->label('webhook', 'database.documents.update')
->label('event', 'database.documents.update')
->label('scope', 'documents.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@ -580,10 +460,9 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->param('data', [], new JSON(), 'Document data as JSON object.')
->param('read', [], 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', [], 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, $response, $projectDB, $webhooks, $audits) {
/** @var Utopia\Response $response */
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$collection = $projectDB->getDocument($collectionId, false);
@ -592,7 +471,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (!\is_array($data)) {
throw new Exception('Data param should be a valid JSON', 400);
throw new Exception('Data param should be a valid JSON object', 400);
}
if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
@ -621,6 +500,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
if (empty($data)) {
throw new Exception('Missing payload', 400);
}
try {
$data = $projectDB->updateDocument($data);
} catch (AuthorizationException $exception) {
@ -631,29 +511,20 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('Failed saving document to DB', 500);
}
$data = $data->getArrayCopy();
$webhooks
->setParam('payload', $data)
;
$audits
->setParam('event', 'database.documents.update')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data)
->setParam('resource', 'database/document/'.$data->getId())
->setParam('data', $data->getArrayCopy())
;
/*
* View
*/
$response->json($data);
}, ['response', 'projectDB', 'webhooks', 'audits']);
$response->dynamic($data, Response::MODEL_ANY);
}, ['response', 'projectDB', 'audits']);
App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Delete Document')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('webhook', 'database.documents.delete')
->label('event', 'database.documents.delete')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'deleteDocument')
@ -661,7 +532,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->param('collectionId', null, 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, new UID(), 'Document unique ID.')
->action(function ($collectionId, $documentId, $response, $projectDB, $webhooks, $audits) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
@ -687,16 +558,14 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('Failed to remove document from DB', 500);
}
$data = $document->getArrayCopy();
$webhooks
->setParam('payload', $data)
->setParam('payload', $response->output($document, Response::MODEL_ANY))
;
$audits
->setParam('event', 'database.documents.delete')
->setParam('resource', 'database/document/'.$data['$id'])
->setParam('data', $data) // Audit document in case of malicious or disastrous action
->setParam('resource', 'database/document/'.$document->getId())
->setParam('data', $document->getArrayCopy()) // Audit document in case of malicious or disastrous action
;
$response->noContent();

View file

@ -1,15 +1,16 @@
<?php
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\UID;
use Appwrite\Storage\Storage;
use Appwrite\Storage\Validator\File;
use Appwrite\Storage\Validator\FileSize;
use Appwrite\Storage\Validator\FileType;
use Appwrite\Storage\Validator\Upload;
use Appwrite\Utopia\Response;
use Appwrite\Task\Validator\Cron;
use Utopia\App;
use Utopia\Response;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Text;
@ -43,15 +44,15 @@ App::post('/v1/functions')
],
'dateCreated' => time(),
'dateUpdated' => time(),
'status' => 'paused',
'status' => 'disabled',
'name' => $name,
'env' => $env,
'tag' => '',
'vars' => $vars,
'events' => $events,
'schedule' => $schedule,
'previous' => null,
'next' => null,
'schedulePrevious' => null,
'scheduleNext' => null,
'timeout' => $timeout,
]);
@ -61,7 +62,7 @@ App::post('/v1/functions')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($function->getArrayCopy())
->dynamic($function, Response::MODEL_FUNCTION)
;
}, ['response', 'projectDB']);
@ -90,7 +91,10 @@ App::get('/v1/functions')
],
]);
$response->json(['sum' => $projectDB->getSum(), 'functions' => $results]);
$response->dynamic(new Document([
'sum' => $projectDB->getSum(),
'functions' => $results
]), Response::MODEL_FUNCTION_LIST);
}, ['response', 'projectDB']);
App::get('/v1/functions/:functionId')
@ -106,12 +110,125 @@ App::get('/v1/functions/:functionId')
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('function not found', 404);
throw new Exception('Function not found', 404);
}
$response->json($function->getArrayCopy());
$response->dynamic($function, Response::MODEL_FUNCTION);
}, ['response', 'projectDB']);
App::get('/v1/functions/:functionId/usage')
->desc('Get Function Usage')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
->label('sdk.platform', [APP_PLATFORM_CONSOLE])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getUsage')
->param('functionId', '', new UID(), 'Function unique ID.')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
->action(function ($functionId, $range, $response, $project, $projectDB, $register) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Registry\Registry $register */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
throw new Exception('Function not found', 404);
}
$period = [
'24h' => [
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
'group' => '30m',
],
'7d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'30d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'90d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
];
$client = $register->get('influxdb');
$executions = [];
$failures = [];
$compute = [];
if ($client) {
$start = $period[$range]['start']->format(DateTime::RFC3339);
$end = $period[$range]['end']->format(DateTime::RFC3339);
$database = $client->selectDB('telegraf');
// Executions
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$executions[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Failures
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' AND "functionStatus"=\'failed\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$failures[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
'date' => \strtotime($point['time']),
];
}
// Compute
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_time" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' AND "functionId"=\''.$function->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
$points = $result->getPoints();
foreach ($points as $point) {
$compute[] = [
'value' => round((!empty($point['value'])) ? $point['value'] / 1000 : 0, 2), // minutes
'date' => \strtotime($point['time']),
];
}
}
$response->json([
'range' => $range,
'executions' => [
'data' => $executions,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $executions)),
],
'failures' => [
'data' => $failures,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $failures)),
],
'compute' => [
'data' => $compute,
'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $compute)),
],
]);
}, ['response', 'project', 'projectDB', 'register']);
App::put('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Update Function')
@ -142,16 +259,29 @@ App::put('/v1/functions/:functionId')
'vars' => $vars,
'events' => $events,
'schedule' => $schedule,
'previous' => null,
'next' => $next,
'schedulePrevious' => null,
'scheduleNext' => $next,
'timeout' => $timeout,
]));
if ($next) {
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
]);
// ->setParam('projectId', $project->getId())
// ->setParam('event', $route->getLabel('event', ''))
// ->setParam('payload', [])
// ->setParam('functionId', null)
// ->setParam('executionId', null)
// ->setParam('trigger', 'event')
}
if (false === $function) {
throw new Exception('Failed saving function to DB', 500);
}
$response->json($function->getArrayCopy());
$response->dynamic($function, Response::MODEL_FUNCTION);
}, ['response', 'projectDB']);
App::patch('/v1/functions/:functionId/tag')
@ -182,14 +312,14 @@ App::patch('/v1/functions/:functionId/tag')
$function = $projectDB->updateDocument(array_merge($function->getArrayCopy(), [
'tag' => $tag->getId(),
'next' => $next,
'scheduleNext' => $next,
]));
if (false === $function) {
throw new Exception('Failed saving function to DB', 500);
}
$response->json($function->getArrayCopy());
$response->dynamic($function, Response::MODEL_FUNCTION);
}, ['response', 'projectDB']);
App::delete('/v1/functions/:functionId')
@ -201,7 +331,11 @@ App::delete('/v1/functions/:functionId')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/functions/delete-function.md')
->param('functionId', '', new UID(), 'Function unique ID.')
->action(function ($functionId, $response, $projectDB) {
->action(function ($functionId, $response, $projectDB, $deletes) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $deletes */
$function = $projectDB->getDocument($functionId);
if (empty($function->getId()) || Database::SYSTEM_COLLECTION_FUNCTIONS != $function->getCollection()) {
@ -212,8 +346,12 @@ App::delete('/v1/functions/:functionId')
throw new Exception('Failed to remove function from DB', 500);
}
$deletes
->setParam('document', $function->getArrayCopy())
;
$response->noContent();
}, ['response', 'projectDB']);
}, ['response', 'projectDB', 'deletes']);
App::post('/v1/functions/:functionId/tags')
->groups(['api', 'functions'])
@ -276,11 +414,11 @@ App::post('/v1/functions/:functionId/tags')
'read' => [],
'write' => [],
],
'dateCreated' => time(),
'functionId' => $function->getId(),
'dateCreated' => time(),
'command' => $command,
'codePath' => $path,
'codeSize' => $size,
'path' => $path,
'size' => $size,
]);
if (false === $tag) {
@ -288,12 +426,12 @@ App::post('/v1/functions/:functionId/tags')
}
$usage
->setParam('storage', $tag->getAttribute('codeSize', 0))
->setParam('storage', $tag->getAttribute('size', 0))
;
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($tag->getArrayCopy())
->dynamic($tag, Response::MODEL_TAG)
;
}, ['request', 'response', 'projectDB', 'usage']);
@ -330,7 +468,10 @@ App::get('/v1/functions/:functionId/tags')
],
]);
$response->json(['sum' => $projectDB->getSum(), 'tags' => $results]);
$response->dynamic(new Document([
'sum' => $projectDB->getSum(),
'tags' => $results
]), Response::MODEL_TAG_LIST);
}, ['response', 'projectDB']);
App::get('/v1/functions/:functionId/tags/:tagId')
@ -352,7 +493,7 @@ App::get('/v1/functions/:functionId/tags/:tagId')
$tag = $projectDB->getDocument($tagId);
if($tag->getAttribute('functionId') !== $function->getId()) {
if ($tag->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Tag not found', 404);
}
@ -360,7 +501,7 @@ App::get('/v1/functions/:functionId/tags/:tagId')
throw new Exception('Tag not found', 404);
}
$response->json($tag->getArrayCopy());
$response->dynamic($tag, Response::MODEL_TAG);
}, ['response', 'projectDB']);
App::delete('/v1/functions/:functionId/tags/:tagId')
@ -382,7 +523,7 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
$tag = $projectDB->getDocument($tagId);
if($tag->getAttribute('functionId') !== $function->getId()) {
if ($tag->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Tag not found', 404);
}
@ -392,14 +533,24 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
$device = Storage::getDevice('functions');
if ($device->delete($tag->getAttribute('codePath', ''))) {
if ($device->delete($tag->getAttribute('path', ''))) {
if (!$projectDB->deleteDocument($tag->getId())) {
throw new Exception('Failed to remove tag from DB', 500);
}
}
if($function->getAttribute('tag') === $tag->getId()) { // Reset function tag
$function = $projectDB->updateDocument(array_merge($function->getArrayCopy(), [
'tag' => '',
]));
if (false === $function) {
throw new Exception('Failed saving function to DB', 500);
}
}
$usage
->setParam('storage', $tag->getAttribute('codeSize', 0) * -1)
->setParam('storage', $tag->getAttribute('size', 0) * -1)
;
$response->noContent();
@ -414,9 +565,9 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.method', 'createExecution')
->label('sdk.description', '/docs/references/functions/create-execution.md')
->param('functionId', '', new UID(), 'Function unique ID.')
->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true)
->action(function ($functionId, $async, $response, $project, $projectDB) {
/** @var Utopia\Response $response */
// ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true)
->action(function ($functionId, /*$async,*/ $response, $project, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
@ -428,7 +579,7 @@ App::post('/v1/functions/:functionId/executions')
$tag = $projectDB->getDocument($function->getAttribute('tag'));
if($tag->getAttribute('functionId') !== $function->getId()) {
if ($tag->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Tag not found. Deploy tag before trying to execute a function', 404);
}
@ -444,6 +595,7 @@ App::post('/v1/functions/:functionId/executions')
],
'dateCreated' => time(),
'functionId' => $function->getId(),
'trigger' => 'http', // http / schedule / event
'status' => 'waiting', // waiting / processing / completed / failed
'exitCode' => 0,
'stdout' => '',
@ -454,21 +606,18 @@ App::post('/v1/functions/:functionId/executions')
if (false === $execution) {
throw new Exception('Failed saving execution to DB', 500);
}
if((bool)$async) {
// Issue a TLS certificate when domain is verified
Resque::enqueue('v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'functionId' => $function->getId(),
'executionId' => $execution->getId(),
'functionTag' => $tag->getId(),
'functionTrigger' => 'API',
]);
}
// Issue a TLS certificate when domain is verified
Resque::enqueue('v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'functionId' => $function->getId(),
'executionId' => $execution->getId(),
'trigger' => 'http',
]);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($execution->getArrayCopy())
->dynamic($execution, Response::MODEL_EXECUTION)
;
}, ['response', 'project', 'projectDB']);
@ -505,7 +654,10 @@ App::get('/v1/functions/:functionId/executions')
],
]);
$response->json(['sum' => $projectDB->getSum(), 'executions' => $results]);
$response->dynamic(new Document([
'sum' => $projectDB->getSum(),
'executions' => $results
]), Response::MODEL_EXECUTION_LIST);
}, ['response', 'projectDB']);
App::get('/v1/functions/:functionId/executions/:executionId')
@ -527,7 +679,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
$execution = $projectDB->getDocument($executionId);
if($execution->getAttribute('functionId') !== $function->getId()) {
if ($execution->getAttribute('functionId') !== $function->getId()) {
throw new Exception('Execution not found', 404);
}
@ -535,5 +687,5 @@ App::get('/v1/functions/:functionId/executions/:executionId')
throw new Exception('Execution not found', 404);
}
$response->json($execution->getArrayCopy());
}, ['response', 'projectDB']);
$response->dynamic($execution, Response::MODEL_EXECUTION);
}, ['response', 'projectDB']);

View file

@ -15,7 +15,7 @@ App::get('/v1/health')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/health/get.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['status' => 'OK']);
}, ['response']);
@ -25,7 +25,7 @@ App::get('/v1/health/version')
->groups(['api', 'health'])
->label('scope', 'public')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['version' => APP_VERSION_STABLE]);
}, ['response']);
@ -52,7 +52,7 @@ App::get('/v1/health/db')
->label('sdk.method', 'getDB')
->label('sdk.description', '/docs/references/health/get-db.md')
->action(function ($response, $register) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
$register->get('db'); /* @var $db PDO */
@ -69,7 +69,7 @@ App::get('/v1/health/cache')
->label('sdk.method', 'getCache')
->label('sdk.description', '/docs/references/health/get-cache.md')
->action(function ($response, $register) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
$register->get('cache'); /* @var $cache Predis\Client */
@ -85,7 +85,7 @@ App::get('/v1/health/time')
->label('sdk.method', 'getTime')
->label('sdk.description', '/docs/references/health/get-time.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/*
* Code from: @see https://www.beliefmedia.com.au/query-ntp-time-server
@ -133,7 +133,7 @@ App::get('/v1/health/queue/webhooks')
->label('sdk.method', 'getQueueWebhooks')
->label('sdk.description', '/docs/references/health/get-queue-webhooks.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size('v1-webhooks')]);
}, ['response']);
@ -147,7 +147,7 @@ App::get('/v1/health/queue/tasks')
->label('sdk.method', 'getQueueTasks')
->label('sdk.description', '/docs/references/health/get-queue-tasks.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size('v1-tasks')]);
}, ['response']);
@ -161,7 +161,7 @@ App::get('/v1/health/queue/logs')
->label('sdk.method', 'getQueueLogs')
->label('sdk.description', '/docs/references/health/get-queue-logs.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size('v1-audit')]);
}, ['response']);
@ -175,7 +175,7 @@ App::get('/v1/health/queue/usage')
->label('sdk.method', 'getQueueUsage')
->label('sdk.description', '/docs/references/health/get-queue-usage.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size('v1-usage')]);
}, ['response']);
@ -189,7 +189,7 @@ App::get('/v1/health/queue/certificates')
->label('sdk.method', 'getQueueCertificates')
->label('sdk.description', '/docs/references/health/get-queue-certificates.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size('v1-certificates')]);
}, ['response']);
@ -203,7 +203,7 @@ App::get('/v1/health/queue/functions')
->label('sdk.method', 'getQueueFunctions')
->label('sdk.description', '/docs/references/health/get-queue-functions.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size('v1-functions')]);
}, ['response']);
@ -217,7 +217,7 @@ App::get('/v1/health/storage/local')
->label('sdk.method', 'getStorageLocal')
->label('sdk.description', '/docs/references/health/get-storage-local.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
foreach ([
'Uploads' => APP_STORAGE_UPLOADS,
@ -248,7 +248,7 @@ App::get('/v1/health/anti-virus')
->label('sdk.method', 'getAntiVirus')
->label('sdk.description', '/docs/references/health/get-storage-anti-virus.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
throw new Exception('Anitvirus is disabled');
@ -271,7 +271,7 @@ App::get('/v1/health/stats') // Currently only used internally
// ->label('sdk.method', 'getStats')
->label('docs', false)
->action(function ($response, $register) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
$device = Storage::getDevice('files');

View file

@ -1,5 +1,7 @@
<?php
use Appwrite\Database\Document;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Config\Config;
@ -12,8 +14,8 @@ App::get('/v1/locale')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/locale/get-locale.md')
->action(function ($request, $response, $locale, $geodb) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
@ -24,7 +26,7 @@ App::get('/v1/locale')
$time = (60 * 60 * 24 * 45); // 45 days cache
$countries = $locale->getText('countries');
$continents = $locale->getText('continents');
$output['ip'] = $ip;
$currency = null;
@ -57,7 +59,8 @@ App::get('/v1/locale')
$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);
;
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
}, ['request', 'response', 'locale', 'geodb']);
App::get('/v1/locale/countries')
@ -69,14 +72,22 @@ App::get('/v1/locale/countries')
->label('sdk.method', 'getCountries')
->label('sdk.description', '/docs/references/locale/get-countries.md')
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
$list = $locale->getText('countries'); /* @var $list array */
$output = [];
\asort($list);
\asort($list); // sort by abc per locale
$response->json($list);
foreach ($list as $key => $value) {
$output[] = new Document([
'name' => $value,
'code' => $key,
]);
}
$response->dynamic(new Document(['countries' => $output, 'sum' => \count($output)]), Response::MODEL_COUNTRY_LIST);
}, ['response', 'locale']);
App::get('/v1/locale/countries/eu')
@ -88,22 +99,25 @@ App::get('/v1/locale/countries/eu')
->label('sdk.method', 'getCountriesEU')
->label('sdk.description', '/docs/references/locale/get-countries-eu.md')
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
$countries = $locale->getText('countries'); /* @var $countries array */
$list = $locale->getText('countries'); /* @var $countries array */
$eu = Config::getParam('locale-eu');
$list = [];
foreach ($eu as $code) {
if (\array_key_exists($code, $countries)) {
$list[$code] = $countries[$code];
}
}
$output = [];
\asort($list);
$response->json($list);
foreach ($eu as $code) {
if (\array_key_exists($code, $list)) {
$output[] = new Document([
'name' => $list[$code],
'code' => $code,
]);
}
}
$response->dynamic(new Document(['countries' => $output, 'sum' => \count($output)]), Response::MODEL_COUNTRY_LIST);
}, ['response', 'locale']);
App::get('/v1/locale/countries/phones')
@ -115,22 +129,26 @@ App::get('/v1/locale/countries/phones')
->label('sdk.method', 'getCountriesPhones')
->label('sdk.description', '/docs/references/locale/get-countries-phones.md')
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
$list = Config::getParam('locale-phones'); /* @var $list array */
$countries = $locale->getText('countries'); /* @var $countries array */
$output = [];
\asort($list);
foreach ($list as $code => $name) {
if (\array_key_exists($code, $countries)) {
$list[$code] = '+'.$list[$code];
$output[] = new Document([
'code' => '+'.$list[$code],
'countryCode' => $code,
'countryName' => $countries[$code],
]);
}
}
\asort($list);
$response->json($list);
$response->dynamic(new Document(['phones' => $output, 'sum' => \count($output)]), Response::MODEL_PHONE_LIST);
}, ['response', 'locale']);
App::get('/v1/locale/continents')
@ -142,14 +160,21 @@ App::get('/v1/locale/continents')
->label('sdk.method', 'getContinents')
->label('sdk.description', '/docs/references/locale/get-continents.md')
->action(function ($response, $locale) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
$list = $locale->getText('continents'); /* @var $list array */
\asort($list);
foreach ($list as $key => $value) {
$output[] = new Document([
'name' => $value,
'code' => $key,
]);
}
$response->json($list);
$response->dynamic(new Document(['continents' => $output, 'sum' => \count($output)]), Response::MODEL_CONTINENT_LIST);
}, ['response', 'locale']);
App::get('/v1/locale/currencies')
@ -161,11 +186,15 @@ App::get('/v1/locale/currencies')
->label('sdk.method', 'getCurrencies')
->label('sdk.description', '/docs/references/locale/get-currencies.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$currencies = Config::getParam('locale-currencies');
$list = Config::getParam('locale-currencies');
$response->json($currencies);
$list = array_map(function($node) {
return new Document($node);
}, $list);
$response->dynamic(new Document(['currencies' => $list, 'sum' => \count($list)]), Response::MODEL_CURRENCY_LIST);
}, ['response']);
@ -178,9 +207,13 @@ App::get('/v1/locale/languages')
->label('sdk.method', 'getLanguages')
->label('sdk.description', '/docs/references/locale/get-languages.md')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$languages = Config::getParam('locale-languages');
$list = Config::getParam('locale-languages');
$response->json($languages);
$list = array_map(function($node) {
return new Document($node);
}, $list);
$response->dynamic(new Document(['languages' => $list, 'sum' => \count($list)]), Response::MODEL_LANGUAGE_LIST);
}, ['response']);

View file

@ -2,7 +2,6 @@
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
@ -18,6 +17,7 @@ use Appwrite\Database\Document;
use Appwrite\Database\Validator\UID;
use Appwrite\Network\Validator\CNAME;
use Appwrite\Network\Validator\Domain as DomainValidator;
use Appwrite\Utopia\Response;
use Cron\CronExpression;
App::post('/v1/projects')
@ -38,7 +38,7 @@ App::post('/v1/projects')
->param('legalAddress', '', new Text(256), 'Project legal Address. Max length: 256 chars.', true)
->param('legalTaxId', '', new Text(256), 'Project legal Tax ID. Max length: 256 chars.', true)
->action(function ($name, $teamId, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $consoleDB, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Database\Database $projectDB */
@ -70,6 +70,7 @@ App::post('/v1/projects')
'webhooks' => [],
'keys' => [],
'tasks' => [],
'domains' => [],
]
);
@ -81,7 +82,7 @@ App::post('/v1/projects')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($project->getArrayCopy())
->dynamic($project, Response::MODEL_PROJECT)
;
}, ['response', 'consoleDB', 'projectDB']);
@ -96,7 +97,7 @@ App::get('/v1/projects')
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->action(function ($search, $limit, $offset, $orderType, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$results = $consoleDB->getCollection([
@ -111,7 +112,10 @@ App::get('/v1/projects')
],
]);
$response->json(['sum' => $consoleDB->getSum(), 'projects' => $results]);
$response->dynamic(new Document([
'sum' => $consoleDB->getSum(),
'projects' => $results
]), Response::MODEL_PROJECT_LIST);
}, ['response', 'consoleDB']);
App::get('/v1/projects/:projectId')
@ -122,7 +126,7 @@ App::get('/v1/projects/:projectId')
->label('sdk.method', 'get')
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -131,7 +135,7 @@ App::get('/v1/projects/:projectId')
throw new Exception('Project not found', 404);
}
$response->json($project->getArrayCopy());
$response->dynamic($project, Response::MODEL_PROJECT);
}, ['response', 'consoleDB']);
App::get('/v1/projects/:projectId/usage')
@ -141,9 +145,9 @@ App::get('/v1/projects/:projectId/usage')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getUsage')
->param('projectId', '', new UID(), 'Project unique ID.')
->param('range', 'last30', new WhiteList(['daily', 'monthly', 'last30', 'last90'], true), 'Date range.', true)
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->action(function ($projectId, $range, $response, $consoleDB, $projectDB, $register) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Registry\Registry $register */
@ -155,31 +159,26 @@ App::get('/v1/projects/:projectId/usage')
}
$period = [
'daily' => [
'start' => DateTime::createFromFormat('U', \strtotime('today')),
'end' => DateTime::createFromFormat('U', \strtotime('tomorrow')),
'group' => '1m',
'24h' => [
'start' => DateTime::createFromFormat('U', \strtotime('-24 hours')),
'end' => DateTime::createFromFormat('U', \strtotime('+1 hour')),
'group' => '30m',
],
'monthly' => [
'start' => DateTime::createFromFormat('U', \strtotime('midnight first day of this month')),
'end' => DateTime::createFromFormat('U', \strtotime('midnight last day of this month')),
'7d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-7 days')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'last30' => [
'30d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
'end' => DateTime::createFromFormat('U', \strtotime('tomorrow')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
'last90' => [
'90d' => [
'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
'end' => DateTime::createFromFormat('U', \strtotime('today')),
'end' => DateTime::createFromFormat('U', \strtotime('now')),
'group' => '1d',
],
// 'yearly' => [
// 'start' => DateTime::createFromFormat('U', strtotime('midnight first day of january')),
// 'end' => DateTime::createFromFormat('U', strtotime('midnight last day of december')),
// 'group' => '4w',
// ],
];
$client = $register->get('influxdb');
@ -257,6 +256,7 @@ App::get('/v1/projects/:projectId/usage')
$tasksTotal = \count($project->getAttribute('tasks', []));
$response->json([
'range' => $range,
'requests' => [
'data' => $requests,
'total' => \array_sum(\array_map(function ($item) {
@ -298,7 +298,7 @@ App::get('/v1/projects/:projectId/usage')
) +
$projectDB->getCount(
[
'attribute' => 'codeSize',
'attribute' => 'size',
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_TAGS,
],
@ -326,7 +326,7 @@ App::patch('/v1/projects/:projectId')
->param('legalAddress', '', new Text(256), 'Project legal address. Max length: 256 chars.', true)
->param('legalTaxId', '', new Text(256), 'Project legal tax ID. Max length: 256 chars.', true)
->action(function ($projectId, $name, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -352,7 +352,7 @@ App::patch('/v1/projects/:projectId')
throw new Exception('Failed saving project to DB', 500);
}
$response->json($project->getArrayCopy());
$response->dynamic($project, Response::MODEL_PROJECT);
}, ['response', 'consoleDB']);
App::patch('/v1/projects/:projectId/oauth2')
@ -366,7 +366,7 @@ App::patch('/v1/projects/:projectId/oauth2')
->param('appId', '', new Text(256), 'Provider app ID. Max length: 256 chars.', true)
->param('secret', '', new text(512), 'Provider secret key. Max length: 512 chars.', true)
->action(function ($projectId, $provider, $appId, $secret, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -384,7 +384,7 @@ App::patch('/v1/projects/:projectId/oauth2')
throw new Exception('Failed saving project to DB', 500);
}
$response->json($project->getArrayCopy());
$response->dynamic($project, Response::MODEL_PROJECT);
}, ['response', 'consoleDB']);
App::delete('/v1/projects/:projectId')
@ -396,7 +396,7 @@ App::delete('/v1/projects/:projectId')
->param('projectId', '', new UID(), 'Project unique ID.')
->param('password', '', new UID(), 'Your user password for confirmation. Must be between 6 to 32 chars.')
->action(function ($projectId, $password, $response, $user, $consoleDB, $deletes) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Event\Event $deletes */
@ -450,7 +450,7 @@ App::post('/v1/projects/:projectId/webhooks')
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
->action(function ($projectId, $name, $events, $url, $security, $httpUser, $httpPass, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -489,7 +489,7 @@ App::post('/v1/projects/:projectId/webhooks')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($webhook->getArrayCopy())
->dynamic($webhook, Response::MODEL_WEBHOOK)
;
}, ['response', 'consoleDB']);
@ -501,7 +501,7 @@ App::get('/v1/projects/:projectId/webhooks')
->label('sdk.method', 'listWebhooks')
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -512,7 +512,10 @@ App::get('/v1/projects/:projectId/webhooks')
$webhooks = $project->getAttribute('webhooks', []);
$response->json($webhooks);
$response->dynamic(new Document([
'sum' => count($webhooks),
'webhooks' => $webhooks
]), Response::MODEL_WEBHOOK_LIST);
}, ['response', 'consoleDB']);
App::get('/v1/projects/:projectId/webhooks/:webhookId')
@ -524,7 +527,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.')
->action(function ($projectId, $webhookId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -539,7 +542,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception('Webhook not found', 404);
}
$response->json($webhook->getArrayCopy());
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
}, ['response', 'consoleDB']);
App::put('/v1/projects/:projectId/webhooks/:webhookId')
@ -557,7 +560,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
->action(function ($projectId, $webhookId, $name, $events, $url, $security, $httpUser, $httpPass, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -587,7 +590,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception('Failed saving webhook to DB', 500);
}
$response->json($webhook->getArrayCopy());
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
}, ['response', 'consoleDB']);
App::delete('/v1/projects/:projectId/webhooks/:webhookId')
@ -599,7 +602,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.')
->action(function ($projectId, $webhookId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -633,7 +636,7 @@ App::post('/v1/projects/:projectId/keys')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(Config::getParam('scopes'), true)), 'Key scopes list.')
->action(function ($projectId, $name, $scopes, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -667,7 +670,7 @@ App::post('/v1/projects/:projectId/keys')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($key->getArrayCopy())
->dynamic($key, Response::MODEL_KEY)
;
}, ['response', 'consoleDB']);
@ -679,7 +682,7 @@ App::get('/v1/projects/:projectId/keys')
->label('sdk.method', 'listKeys')
->param('projectId', null, new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -688,7 +691,12 @@ App::get('/v1/projects/:projectId/keys')
throw new Exception('Project not found', 404);
}
$response->json($project->getAttribute('keys', [])); //FIXME make sure array objects return correctly
$keys = $project->getAttribute('keys', []);
$response->dynamic(new Document([
'sum' => count($keys),
'keys' => $keys
]), Response::MODEL_KEY_LIST);
}, ['response', 'consoleDB']);
App::get('/v1/projects/:projectId/keys/:keyId')
@ -712,7 +720,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
throw new Exception('Key not found', 404);
}
$response->json($key->getArrayCopy());
$response->dynamic($key, Response::MODEL_KEY);
}, ['response', 'consoleDB']);
App::put('/v1/projects/:projectId/keys/:keyId')
@ -726,7 +734,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(Config::getParam('scopes'), true)), 'Key scopes list')
->action(function ($projectId, $keyId, $name, $scopes, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -750,7 +758,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
throw new Exception('Failed saving key to DB', 500);
}
$response->json($key->getArrayCopy());
$response->dynamic($key, Response::MODEL_KEY);
}, ['response', 'consoleDB']);
App::delete('/v1/projects/:projectId/keys/:keyId')
@ -762,7 +770,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('keyId', null, new UID(), 'Key unique ID.')
->action(function ($projectId, $keyId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -803,7 +811,7 @@ App::post('/v1/projects/:projectId/tasks')
->param('httpUser', '', new Text(256), 'Task HTTP user. Max length: 256 chars.', true)
->param('httpPass', '', new Text(256), 'Task HTTP password. Max length: 256 chars.', true)
->action(function ($projectId, $name, $status, $schedule, $security, $httpMethod, $httpUrl, $httpHeaders, $httpUser, $httpPass, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -857,7 +865,7 @@ App::post('/v1/projects/:projectId/tasks')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($task->getArrayCopy())
->dynamic($task, Response::MODEL_TASK)
;
}, ['response', 'consoleDB']);
@ -869,7 +877,7 @@ App::get('/v1/projects/:projectId/tasks')
->label('sdk.method', 'listTasks')
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -880,7 +888,11 @@ App::get('/v1/projects/:projectId/tasks')
$tasks = $project->getAttribute('tasks', []);
$response->json($tasks);
$response->dynamic(new Document([
'sum' => count($tasks),
'tasks' => $tasks
]), Response::MODEL_TASK_LIST);
}, ['response', 'consoleDB']);
App::get('/v1/projects/:projectId/tasks/:taskId')
@ -892,7 +904,7 @@ App::get('/v1/projects/:projectId/tasks/:taskId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->action(function ($projectId, $taskId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -907,7 +919,7 @@ App::get('/v1/projects/:projectId/tasks/:taskId')
throw new Exception('Task not found', 404);
}
$response->json($task->getArrayCopy());
$response->dynamic($task, Response::MODEL_TASK);
}, ['response', 'consoleDB']);
App::put('/v1/projects/:projectId/tasks/:taskId')
@ -928,7 +940,7 @@ App::put('/v1/projects/:projectId/tasks/:taskId')
->param('httpUser', '', new Text(256), 'Task HTTP user. Max length: 256 chars.', true)
->param('httpPass', '', new Text(256), 'Task HTTP password. Max length: 256 chars.', true)
->action(function ($projectId, $taskId, $name, $status, $schedule, $security, $httpMethod, $httpUrl, $httpHeaders, $httpUser, $httpPass, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -970,7 +982,7 @@ App::put('/v1/projects/:projectId/tasks/:taskId')
ResqueScheduler::enqueueAt($next, 'v1-tasks', 'TasksV1', $task->getArrayCopy());
}
$response->json($task->getArrayCopy());
$response->dynamic($task, Response::MODEL_TASK);
}, ['response', 'consoleDB']);
App::delete('/v1/projects/:projectId/tasks/:taskId')
@ -982,7 +994,7 @@ App::delete('/v1/projects/:projectId/tasks/:taskId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->action(function ($projectId, $taskId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1019,7 +1031,7 @@ App::post('/v1/projects/:projectId/platforms')
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
->param('hostname', '', new Text(256), 'Platform client hostname. Max length: 256 chars.', true)
->action(function ($projectId, $type, $name, $key, $store, $hostname, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1057,7 +1069,7 @@ App::post('/v1/projects/:projectId/platforms')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($platform->getArrayCopy())
->dynamic($platform, Response::MODEL_PLATFORM)
;
}, ['response', 'consoleDB']);
@ -1069,7 +1081,7 @@ App::get('/v1/projects/:projectId/platforms')
->label('sdk.method', 'listPlatforms')
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1080,7 +1092,10 @@ App::get('/v1/projects/:projectId/platforms')
$platforms = $project->getAttribute('platforms', []);
$response->json($platforms);
$response->dynamic(new Document([
'sum' => count($platforms),
'platforms' => $platforms
]), Response::MODEL_PLATFORM_LIST);
}, ['response', 'consoleDB']);
App::get('/v1/projects/:projectId/platforms/:platformId')
@ -1092,7 +1107,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.')
->action(function ($projectId, $platformId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1107,7 +1122,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
throw new Exception('Platform not found', 404);
}
$response->json($platform->getArrayCopy());
$response->dynamic($platform, Response::MODEL_PLATFORM);
}, ['response', 'consoleDB']);
App::put('/v1/projects/:projectId/platforms/:platformId')
@ -1123,7 +1138,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
->param('hostname', '', new Text(256), 'Platform client URL. Max length: 256 chars.', true)
->action(function ($projectId, $platformId, $name, $key, $store, $hostname, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1150,7 +1165,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
throw new Exception('Failed saving platform to DB', 500);
}
$response->json($platform->getArrayCopy());
$response->dynamic($platform, Response::MODEL_PLATFORM);
}, ['response', 'consoleDB']);
App::delete('/v1/projects/:projectId/platforms/:platformId')
@ -1162,7 +1177,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.')
->action(function ($projectId, $platformId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1195,7 +1210,7 @@ App::post('/v1/projects/:projectId/domains')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domain', null, new DomainValidator(), 'Domain name.')
->action(function ($projectId, $domain, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1246,7 +1261,7 @@ App::post('/v1/projects/:projectId/domains')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($domain->getArrayCopy())
->dynamic($domain, Response::MODEL_DOMAIN)
;
}, ['response', 'consoleDB']);
@ -1258,7 +1273,7 @@ App::get('/v1/projects/:projectId/domains')
->label('sdk.method', 'listDomains')
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1268,8 +1283,11 @@ App::get('/v1/projects/:projectId/domains')
}
$domains = $project->getAttribute('domains', []);
$response->json($domains);
$response->dynamic(new Document([
'sum' => count($domains),
'domains' => $domains
]), Response::MODEL_DOMAIN_LIST);
}, ['response', 'consoleDB']);
App::get('/v1/projects/:projectId/domains/:domainId')
@ -1281,7 +1299,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.')
->action(function ($projectId, $domainId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1296,7 +1314,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
throw new Exception('Domain not found', 404);
}
$response->json($domain->getArrayCopy());
$response->dynamic($domain, Response::MODEL_DOMAIN);
}, ['response', 'consoleDB']);
App::patch('/v1/projects/:projectId/domains/:domainId/verification')
@ -1308,7 +1326,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.')
->action(function ($projectId, $domainId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
@ -1330,7 +1348,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
}
if ($domain->getAttribute('verification') === true) {
return $response->json($domain->getArrayCopy());
return $response->dynamic($domain, Response::MODEL_DOMAIN);
}
// Verify Domain with DNS records
@ -1354,7 +1372,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
'domain' => $domain->getAttribute('domain'),
]);
$response->json($domain->getArrayCopy());
$response->dynamic($domain, Response::MODEL_DOMAIN);
}, ['response', 'consoleDB']);
App::delete('/v1/projects/:projectId/domains/:domainId')
@ -1366,7 +1384,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.')
->action(function ($projectId, $domainId, $response, $consoleDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);

View file

@ -2,7 +2,6 @@
use Utopia\App;
use Utopia\Exception;
use Utopia\Response;
use Utopia\Validator\ArrayList;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Range;
@ -12,6 +11,7 @@ use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Filesystem;
use Appwrite\ClamAV\Network;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\UID;
use Appwrite\Storage\Storage;
use Appwrite\Storage\Validator\File;
@ -20,13 +20,14 @@ use Appwrite\Storage\Validator\Upload;
use Appwrite\Storage\Compression\Algorithms\GZIP;
use Appwrite\Resize\Resize;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Utopia\Response;
use Utopia\Config\Config;
App::post('/v1/storage/files')
->desc('Create File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('webhook', 'storage.files.create')
->label('event', 'storage.files.create')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createFile')
@ -36,12 +37,11 @@ App::post('/v1/storage/files')
->param('file', [], new File(), 'Binary file.', false)
->param('read', [], 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', [], 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 ($file, $read, $write, $request, $response, $user, $projectDB, $webhooks, $audits, $usage) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
->action(function ($file, $read, $write, $request, $response, $user, $projectDB, $audits, $usage) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
@ -140,10 +140,6 @@ App::post('/v1/storage/files')
throw new Exception('Failed saving file to DB', 500);
}
$webhooks
->setParam('payload', $file->getArrayCopy())
;
$audits
->setParam('event', 'storage.files.create')
->setParam('resource', 'storage/files/'.$file->getId())
@ -155,9 +151,9 @@ App::post('/v1/storage/files')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($file->getArrayCopy())
->dynamic($file, Response::MODEL_FILE)
;
}, ['request', 'response', 'user', 'projectDB', 'webhooks', 'audits', 'usage']);
}, ['request', 'response', 'user', 'projectDB', 'audits', 'usage']);
App::get('/v1/storage/files')
->desc('List Files')
@ -172,7 +168,7 @@ App::get('/v1/storage/files')
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB->getCollection([
@ -187,11 +183,10 @@ App::get('/v1/storage/files')
],
]);
$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->dynamic(new Document([
'sum' => $projectDB->getSum(),
'files' => $results
]), Response::MODEL_FILE_LIST);
}, ['response', 'projectDB']);
App::get('/v1/storage/files/:fileId')
@ -204,7 +199,7 @@ App::get('/v1/storage/files/:fileId')
->label('sdk.description', '/docs/references/storage/get-file.md')
->param('fileId', '', new UID(), 'File unique ID.')
->action(function ($fileId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$file = $projectDB->getDocument($fileId);
@ -213,7 +208,7 @@ App::get('/v1/storage/files/:fileId')
throw new Exception('File not found', 404);
}
$response->json($file->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']));
$response->dynamic($file, Response::MODEL_FILE);
}, ['response', 'projectDB']);
App::get('/v1/storage/files/:fileId/preview')
@ -233,8 +228,8 @@ App::get('/v1/storage/files/:fileId/preview')
->param('background', '', 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', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
->action(function ($fileId, $width, $height, $quality, $background, $output, $request, $response, $project, $projectDB) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
@ -354,7 +349,7 @@ App::get('/v1/storage/files/:fileId/download')
->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File unique ID.')
->action(function ($fileId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$file = $projectDB->getDocument($fileId);
@ -410,7 +405,7 @@ App::get('/v1/storage/files/:fileId/view')
->param('fileId', '', new UID(), 'File unique ID.')
->param('as', '', new WhiteList(['pdf', /*'html',*/ 'text'], true), '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, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$file = $projectDB->getDocument($fileId);
@ -474,7 +469,7 @@ App::put('/v1/storage/files/:fileId')
->desc('Update File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('webhook', 'storage.files.update')
->label('event', 'storage.files.update')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'updateFile')
@ -482,10 +477,9 @@ App::put('/v1/storage/files/:fileId')
->param('fileId', '', new UID(), 'File unique ID.')
->param('read', [], 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', [], 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 ($fileId, $read, $write, $response, $projectDB, $webhooks, $audits) {
/** @var Utopia\Response $response */
->action(function ($fileId, $read, $write, $response, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
$file = $projectDB->getDocument($fileId);
@ -506,30 +500,26 @@ App::put('/v1/storage/files/:fileId')
throw new Exception('Failed saving file to DB', 500);
}
$webhooks
->setParam('payload', $file->getArrayCopy())
;
$audits
->setParam('event', 'storage.files.update')
->setParam('resource', 'storage/files/'.$file->getId())
;
$response->json($file->getArrayCopy());
$response->dynamic($file, Response::MODEL_FILE);
}, ['response', 'projectDB', 'webhooks', 'audits']);
App::delete('/v1/storage/files/:fileId')
->desc('Delete File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('webhook', 'storage.files.delete')
->label('event', 'storage.files.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteFile')
->label('sdk.description', '/docs/references/storage/delete-file.md')
->param('fileId', '', new UID(), 'File unique ID.')
->action(function ($fileId, $response, $projectDB, $webhooks, $audits, $usage) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
@ -548,11 +538,7 @@ App::delete('/v1/storage/files/:fileId')
throw new Exception('Failed to remove file from DB', 500);
}
}
$webhooks
->setParam('payload', $file->getArrayCopy())
;
$audits
->setParam('event', 'storage.files.delete')
->setParam('resource', 'storage/files/'.$file->getId())
@ -562,6 +548,10 @@ App::delete('/v1/storage/files/:fileId')
->setParam('storage', $file->getAttribute('size', 0) * -1)
;
$webhooks
->setParam('payload', $response->output($file, Response::MODEL_FILE))
;
$response->noContent();
}, ['response', 'projectDB', 'webhooks', 'audits', 'usage']);
@ -613,6 +603,5 @@ App::delete('/v1/storage/files/:fileId')
// //var_dump($antiVirus->version());
// //var_dump($antiVirus->fileScan('/storage/uploads/app-1/5/9/f/e/59fecaed49645.pdf'));
// //$response->json($antiVirus->continueScan($device->getRoot()));
// }
// );

View file

@ -18,6 +18,7 @@ use Appwrite\Database\Exception\Duplicate;
use Appwrite\Database\Validator\Key;
use Appwrite\Template\Template;
use Appwrite\Utopia\Response;
use DeviceDetector\DeviceDetector;
App::post('/v1/teams')
->desc('Create Team')
@ -30,7 +31,7 @@ App::post('/v1/teams')
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->param('roles', ['owner'], new ArrayList(new Key()), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.', true)
->action(function ($name, $roles, $response, $user, $projectDB, $mode) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var bool $mode */
@ -82,7 +83,7 @@ App::post('/v1/teams')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($team->getArrayCopy())
->dynamic($team, Response::MODEL_TEAM)
;
}, ['response', 'user', 'projectDB', 'mode']);
@ -99,7 +100,7 @@ App::get('/v1/teams')
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB->getCollection([
@ -114,7 +115,10 @@ App::get('/v1/teams')
],
]);
$response->json(['sum' => $projectDB->getSum(), 'teams' => $results]);
$response->dynamic(new Document([
'sum' => $projectDB->getSum(),
'teams' => $results
]), Response::MODEL_TEAM_LIST);
}, ['response', 'projectDB']);
App::get('/v1/teams/:teamId')
@ -127,7 +131,7 @@ App::get('/v1/teams/:teamId')
->label('sdk.description', '/docs/references/teams/get-team.md')
->param('teamId', '', new UID(), 'Team unique ID.')
->action(function ($teamId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$team = $projectDB->getDocument($teamId);
@ -136,7 +140,7 @@ App::get('/v1/teams/:teamId')
throw new Exception('Team not found', 404);
}
$response->json($team->getArrayCopy([]));
$response->dynamic($team, Response::MODEL_TEAM);
}, ['response', 'projectDB']);
App::put('/v1/teams/:teamId')
@ -150,7 +154,7 @@ App::put('/v1/teams/:teamId')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->action(function ($teamId, $name, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$team = $projectDB->getDocument($teamId);
@ -166,8 +170,8 @@ App::put('/v1/teams/:teamId')
if (false === $team) {
throw new Exception('Failed saving team to DB', 500);
}
$response->json($team->getArrayCopy());
$response->dynamic($team, Response::MODEL_TEAM);
}, ['response', 'projectDB']);
App::delete('/v1/teams/:teamId')
@ -180,7 +184,7 @@ App::delete('/v1/teams/:teamId')
->label('sdk.description', '/docs/references/teams/delete-team.md')
->param('teamId', '', new UID(), 'Team unique ID.')
->action(function ($teamId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$team = $projectDB->getDocument($teamId);
@ -225,7 +229,7 @@ App::post('/v1/teams/:teamId/memberships')
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
->action(function ($teamId, $email, $name, $roles, $url, $response, $project, $user, $projectDB, $locale, $audits, $mails, $mode) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
@ -392,19 +396,11 @@ App::post('/v1/teams/:teamId/memberships')
;
$response
->setStatusCode(Response::STATUS_CODE_CREATED) // TODO change response of this endpoint
->json(\array_merge($membership->getArrayCopy([
'$id',
'userId',
'teamId',
'roles',
'invited',
'joined',
'confirm',
]), [
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(\array_merge($membership->getArrayCopy(), [
'email' => $email,
'name' => $name,
]))
])), Response::MODEL_MEMBERSHIP)
;
}, ['response', 'project', 'user', 'projectDB', 'locale', 'audits', 'mails', 'mode']);
@ -422,7 +418,7 @@ App::get('/v1/teams/:teamId/memberships')
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->action(function ($teamId, $search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$team = $projectDB->getDocument($teamId);
@ -453,18 +449,10 @@ App::get('/v1/teams/:teamId/memberships')
$temp = $projectDB->getDocument($membership->getAttribute('userId', null))->getArrayCopy(['email', 'name']);
$users[] = \array_merge($temp, $membership->getArrayCopy([
'$id',
'userId',
'teamId',
'roles',
'invited',
'joined',
'confirm',
]));
$users[] = new Document(\array_merge($temp, $membership->getArrayCopy()));
}
$response->json(['sum' => $projectDB->getSum(), 'memberships' => $users]);
$response->dynamic(new Document(['sum' => $projectDB->getSum(), 'memberships' => $users]), Response::MODEL_MEMBERSHIP_LIST);
}, ['response', 'projectDB']);
App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
@ -479,11 +467,12 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
->param('inviteId', '', new UID(), 'Invite unique ID.')
->param('userId', '', new UID(), 'User unique ID.')
->param('secret', '', new Text(256), 'Secret key.')
->action(function ($teamId, $inviteId, $userId, $secret, $request, $response, $user, $projectDB, $audits) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
->action(function ($teamId, $inviteId, $userId, $secret, $request, $response, $user, $projectDB, $geodb, $audits) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
$protocol = $request->getProtocol();
@ -540,10 +529,28 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
;
// Log user in
$dd = new DeviceDetector($request->getUserAgent('UNKNOWN'));
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator();
$user->setAttribute('tokens', new Document([
$session = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]],
'type' => Auth::TOKEN_TYPE_LOGIN,
@ -551,7 +558,34 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
]), Document::SET_TYPE_APPEND);
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
]);
$record = $geodb->get($request->getIP());
if($record) {
$session
->setAttribute('countryCode', \strtolower($record['country']['iso_code']))
;
} else {
$session
->setAttribute('countryCode', '--')
;
}
$user->setAttribute('tokens', $session, Document::SET_TYPE_APPEND);
Authorization::setRole('user:'.$userId);
@ -594,8 +628,7 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
'email' => $user->getAttribute('email'),
'name' => $user->getAttribute('name'),
])), Response::MODEL_MEMBERSHIP);
}, ['request', 'response', 'user', 'projectDB', 'audits']);
}, ['request', 'response', 'user', 'projectDB', 'geodb', 'audits']);
App::delete('/v1/teams/:teamId/memberships/:inviteId')
->desc('Delete Team Membership')
@ -608,7 +641,7 @@ App::delete('/v1/teams/:teamId/memberships/:inviteId')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('inviteId', '', new UID(), 'Invite unique ID.')
->action(function ($teamId, $inviteId, $response, $projectDB, $audits) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */

View file

@ -9,10 +9,10 @@ use Utopia\Validator\Text;
use Utopia\Validator\Range;
use Utopia\Audit\Audit;
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
use Utopia\Config\Config;
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\Password;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Exception\Duplicate;
use Appwrite\Database\Validator\UID;
use Appwrite\Utopia\Response;
@ -21,6 +21,7 @@ use DeviceDetector\DeviceDetector;
App::post('/v1/users')
->desc('Create User')
->groups(['api', 'users'])
->label('event', 'users.create')
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@ -30,7 +31,7 @@ App::post('/v1/users')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->action(function ($email, $password, $name, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$profile = $projectDB->getCollectionFirst([ // Get user by email address
@ -65,27 +66,10 @@ App::post('/v1/users')
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' => []]));
->dynamic($user, Response::MODEL_USER)
;
}, ['response', 'projectDB']);
App::get('/v1/users')
@ -101,7 +85,7 @@ App::get('/v1/users')
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->action(function ($search, $limit, $offset, $orderType, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$results = $projectDB->getCollection([
@ -116,32 +100,10 @@ App::get('/v1/users')
],
]);
$oauth2Keys = [];
foreach (Config::getParam('providers') as $key => $provider) {
if (!$provider['enabled']) {
continue;
}
$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->dynamic(new Document([
'sum' => $projectDB->getSum(),
'users' => $results
]), Response::MODEL_USER_LIST);
}, ['response', 'projectDB']);
App::get('/v1/users/:userId')
@ -154,7 +116,7 @@ App::get('/v1/users/:userId')
->label('sdk.description', '/docs/references/users/get-user.md')
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$user = $projectDB->getDocument($userId);
@ -163,28 +125,7 @@ App::get('/v1/users/:userId')
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->dynamic($user, Response::MODEL_USER);
}, ['response', 'projectDB']);
App::get('/v1/users/:userId/prefs')
@ -197,7 +138,7 @@ App::get('/v1/users/:userId/prefs')
->label('sdk.description', '/docs/references/users/get-user-prefs.md')
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$user = $projectDB->getDocument($userId);
@ -208,13 +149,6 @@ App::get('/v1/users/:userId/prefs')
$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']);
@ -227,11 +161,10 @@ App::get('/v1/users/:userId/sessions')
->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/users/get-user-sessions.md')
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $projectDB, $locale, $geodb) {
/** @var Utopia\Response $response */
->action(function ($userId, $response, $projectDB, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
$user = $projectDB->getDocument($userId);
@ -241,7 +174,6 @@ App::get('/v1/users/:userId/sessions')
$tokens = $user->getAttribute('tokens', []);
$sessions = [];
$index = 0;
$countries = $locale->getText('countries');
foreach ($tokens as $token) { /* @var $token Document */
@ -249,47 +181,19 @@ App::get('/v1/users/:userId/sessions')
continue;
}
$userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN';
$token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')]))
? $countries[$token->getAttribute('contryCode')]
: $locale->getText('locale.country.unknown'));
$token->setAttribute('current', false);
$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 = $geodb->get($token->getAttribute('ip', ''));
if ($record) {
$sessions[$index]['geo']['isoCode'] = \strtolower($record['country']['iso_code']);
$sessions[$index]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} else {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
}
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
}
++$index;
$sessions[] = $token;
}
$response->json($sessions);
}, ['response', 'projectDB', 'locale', 'geodb']);
$response->dynamic(new Document([
'sum' => count($sessions),
'sessions' => $sessions
]), Response::MODEL_SESSION_LIST);
}, ['response', 'projectDB', 'locale']);
App::get('/v1/users/:userId/logs')
->desc('Get User Logs')
@ -301,7 +205,7 @@ App::get('/v1/users/:userId/logs')
->label('sdk.description', '/docs/references/users/get-user-logs.md')
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $register, $project, $projectDB, $locale, $geodb) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Registry\Registry $register */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $projectDB */
@ -350,41 +254,56 @@ App::get('/v1/users/:userId/logs')
$dd->parse();
$output[$i] = [
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$output[$i] = new Document([
'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 = $geodb->get($log['ip']);
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
]);
if($record){
$output[$i]['geo']['isoCode'] = \strtolower($record['country']['iso_code']);
$output[$i]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} else{
$output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = $locale->getText('locale.country.unknown');
}
$record = $geodb->get($log['ip']);
} catch (\Exception $e) {
$output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = $locale->getText('locale.country.unknown');
if ($record) {
$output[$i]['countryCode'] = (isset($countries[$record['country']['iso_code']])) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
}
}
$response->json($output);
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
}, ['response', 'register', 'project', 'projectDB', 'locale', 'geodb']);
App::patch('/v1/users/:userId/status')
->desc('Update User Status')
->groups(['api', 'users'])
->label('event', 'users.update.status')
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@ -393,7 +312,7 @@ App::patch('/v1/users/:userId/status')
->param('userId', '', new UID(), 'User unique ID.')
->param('status', '', new WhiteList([Auth::USER_STATUS_ACTIVATED, Auth::USER_STATUS_BLOCKED, Auth::USER_STATUS_UNACTIVATED], true), '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, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$user = $projectDB->getDocument($userId);
@ -409,27 +328,8 @@ App::patch('/v1/users/:userId/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->dynamic($user, Response::MODEL_USER);
}, ['response', 'projectDB']);
App::patch('/v1/users/:userId/prefs')
@ -443,7 +343,7 @@ App::patch('/v1/users/:userId/prefs')
->param('userId', '', new UID(), 'User unique ID.')
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->action(function ($userId, $prefs, $response, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
$user = $projectDB->getDocument($userId);
@ -452,32 +352,21 @@ App::patch('/v1/users/:userId/prefs')
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)),
'prefs' => $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')
->desc('Delete User Session')
->groups(['api', 'users'])
->label('event', 'users.sessions.delete')
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@ -486,9 +375,10 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('abuse-limit', 100)
->param('userId', '', new UID(), 'User unique ID.')
->param('sessionId', null, new UID(), 'User unique session ID.')
->action(function ($userId, $sessionId, $response, $projectDB) {
/** @var Utopia\Response $response */
->action(function ($userId, $sessionId, $response, $projectDB, $webhooks) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
$user = $projectDB->getDocument($userId);
@ -503,15 +393,20 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
if (!$projectDB->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB', 500);
}
$webhooks
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
}
}
$response->json(array('result' => 'success'));
}, ['response', 'projectDB']);
$response->noContent();
}, ['response', 'projectDB', 'webhooks']);
App::delete('/v1/users/:userId/sessions')
->desc('Delete User Sessions')
->groups(['api', 'users'])
->label('event', 'users.sessions.delete')
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@ -519,9 +414,10 @@ App::delete('/v1/users/:userId/sessions')
->label('sdk.description', '/docs/references/users/delete-user-sessions.md')
->label('abuse-limit', 100)
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $projectDB) {
/** @var Utopia\Response $response */
->action(function ($userId, $response, $projectDB, $webhooks) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
$user = $projectDB->getDocument($userId);
@ -537,12 +433,17 @@ App::delete('/v1/users/:userId/sessions')
}
}
$response->json(array('result' => 'success'));
}, ['response', 'projectDB']);
$webhooks
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
$response->noContent();
}, ['response', 'projectDB', 'webhooks']);
App::delete('/v1/users/:userId')
->desc('Delete User')
->groups(['api', 'users'])
->label('event', 'users.delete')
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@ -550,9 +451,10 @@ App::delete('/v1/users/:userId')
->label('sdk.description', '/docs/references/users/delete-user.md')
->label('abuse-limit', 100)
->param('userId', '', function () {return new UID();}, 'User unique ID.')
->action(function ($userId, $response, $projectDB, $deletes) {
/** @var Utopia\Response $response */
->action(function ($userId, $response, $projectDB, $webhooks, $deletes) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $deletes */
$user = $projectDB->getDocument($userId);
@ -580,7 +482,13 @@ App::delete('/v1/users/:userId')
throw new Exception('Failed saving reserved id to DB', 500);
}
$deletes->setParam('document', $user);
$deletes
->setParam('document', $user)
;
$webhooks
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
$response->noContent();
}, ['response', 'projectDB', 'deletes']);
}, ['response', 'projectDB', 'webhooks', 'deletes']);

View file

@ -3,7 +3,7 @@
require_once __DIR__.'/../init.php';
use Utopia\App;
use Appwrite\Swoole\Request;
use Utopia\Swoole\Request;
use Appwrite\Utopia\Response;
use Utopia\View;
use Utopia\Exception;
@ -16,13 +16,14 @@ use Appwrite\Database\Validator\Authorization;
use Appwrite\Network\Validator\Origin;
use Appwrite\Storage\Device\Local;
use Appwrite\Storage\Storage;
use Utopia\CLI\Console;
Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $webhooks, $audits, $usage, $clients) {
/** @var Appwrite\Swoole\Request $request */
App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $webhooks, $audits, $usage, $deletes, $clients) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $console */
/** @var Appwrite\Database\Document $project */
@ -31,6 +32,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var bool $mode */
/** @var array $clients */
@ -44,7 +46,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
$route = $utopia->match($request);
if(!empty($route->getLabel('sdk.platform', [])) && empty($project->getId()) && ($route->getLabel('scope', '') !== 'public')) {
if (!empty($route->getLabel('sdk.platform', [])) && empty($project->getId()) && ($route->getLabel('scope', '') !== 'public')) {
throw new Exception('Missing or unknown project ID', 400);
}
@ -99,8 +101,8 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if($request->getProtocol() !== 'https') {
return $response->redirect('https://'.$request->getHostname().$request->getURI());
if ($request->getProtocol() !== 'https') {
return $response->redirect('https://'.$request->getHostname().$request->getURI());
}
$response->addHeader('Strict-Transport-Security', 'max-age='.(60 * 60 * 24 * 126)); // 126 days
@ -126,11 +128,11 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
$origin = $request->getOrigin($request->getReferer(''));
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
if(!$originValidator->isValid($origin)
if (!$originValidator->isValid($origin)
&& \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
&& $route->getLabel('origin', false) !== '*'
&& empty($request->getHeader('x-appwrite-key', ''))) {
throw new Exception($originValidator->getDescription(), 403);
throw new Exception($originValidator->getDescription(), 403);
}
/*
@ -183,7 +185,10 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
}
Authorization::setRole('user:'.$user->getId());
if ($user->getId()) {
Authorization::setRole('user:'.$user->getId());
}
Authorization::setRole('role:'.$role);
\array_map(function ($node) {
@ -219,7 +224,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
*/
$webhooks
->setParam('projectId', $project->getId())
->setParam('event', $route->getLabel('webhook', ''))
->setParam('event', $route->getLabel('event', ''))
->setParam('payload', [])
;
@ -242,12 +247,16 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
->setParam('networkResponseSize', 0)
->setParam('storage', 0)
;
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'webhooks', 'audits', 'usage', 'clients']);
$deletes
->setParam('projectId', $project->getId())
;
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'webhooks', 'audits', 'usage', 'deletes', 'clients']);
App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audits, $usage, $deletes, $mode) {
/** @var Utopia\App $utopia */
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $audits */
@ -256,6 +265,10 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi
/** @var bool $mode */
if (!empty($webhooks->getParam('event'))) {
if(empty($webhooks->getParam('payload'))) {
$webhooks->setParam('payload', $response->getPayload());
}
$webhooks->trigger();
}
@ -269,7 +282,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi
$route = $utopia->match($request);
if($project->getId()
if ($project->getId()
&& $mode !== APP_MODE_ADMIN
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage and admin mode
@ -282,7 +295,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi
}, ['utopia', 'request', 'response', 'project', 'webhooks', 'audits', 'usage', 'deletes', 'mode']);
App::options(function ($request, $response) {
/** @var Appwrite\Swoole\Request $request */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
$origin = $request->getOrigin();
@ -300,16 +313,21 @@ App::options(function ($request, $response) {
App::error(function ($error, $utopia, $request, $response, $layout, $project) {
/** @var Exception $error */
/** @var Utopia\App $utopia */
/** @var Utopia\Request $request */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\View $layout */
/** @var Appwrite\Database\Document $project */
if(php_sapi_name() === 'cli') {
var_dump(get_class($error));
var_dump($error->getMessage());
var_dump($error->getFile());
var_dump($error->getLine());
$route = $utopia->match($request);
$template = ($route) ? $route->getLabel('error', null) : null;
if (php_sapi_name() === 'cli') {
Console::error('[Error] Method: '.$route->getMethod());
Console::error('[Error] URL: '.$route->getURL());
Console::error('[Error] Type: '.get_class($error));
Console::error('[Error] Message: '.$error->getMessage());
Console::error('[Error] File: '.$error->getFile());
Console::error('[Error] Line: '.$error->getLine());
}
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
@ -352,10 +370,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
->addHeader('Pragma', 'no-cache')
->setStatusCode($code)
;
$route = $utopia->match($request);
$template = ($route) ? $route->getLabel('error', null) : null;
if ($template) {
$comp = new View($template);
@ -379,7 +394,6 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
$response->dynamic(new Document($output),
$utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_LOCALE);
}, ['error', 'utopia', 'request', 'response', 'layout', 'project']);
App::get('/manifest.json')
@ -387,7 +401,7 @@ App::get('/manifest.json')
->label('scope', 'public')
->label('docs', false)
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->json([
'name' => APP_NAME,
@ -413,7 +427,7 @@ App::get('/robots.txt')
->label('scope', 'public')
->label('docs', false)
->action(function ($response) {
$template = new View(__DIR__.'/views/general/robots.phtml');
$template = new View(__DIR__.'/../views/general/robots.phtml');
$response->text($template->render(false));
}, ['response']);
@ -422,7 +436,7 @@ App::get('/humans.txt')
->label('scope', 'public')
->label('docs', false)
->action(function ($response) {
$template = new View(__DIR__.'/views/general/humans.phtml');
$template = new View(__DIR__.'/../views/general/humans.phtml');
$response->text($template->render(false));
}, ['response']);
@ -435,25 +449,25 @@ App::get('/.well-known/acme-challenge')
$path = \str_replace('/.well-known/acme-challenge/', '', $request->getParam('q'));
$absolute = \realpath($base.'/.well-known/acme-challenge/'.$path);
if(!$base) {
if (!$base) {
throw new Exception('Storage error', 500);
}
if(!$absolute) {
if (!$absolute) {
throw new Exception('Unknown path', 404);
}
if(!\substr($absolute, 0, \strlen($base)) === $base) {
if (!\substr($absolute, 0, \strlen($base)) === $base) {
throw new Exception('Invalid path', 401);
}
if(!\file_exists($absolute)) {
if (!\file_exists($absolute)) {
throw new Exception('Unknown path', 404);
}
$content = @\file_get_contents($absolute);
if(!$content) {
if (!$content) {
throw new Exception('Failed to get contents', 500);
}
@ -463,6 +477,6 @@ App::get('/.well-known/acme-challenge')
include_once __DIR__ . '/shared/api.php';
include_once __DIR__ . '/shared/web.php';
foreach(Config::getParam('services', []) as $service) {
foreach (Config::getParam('services', []) as $service) {
include_once $service['controller'];
}

View file

@ -7,8 +7,8 @@ use Utopia\Abuse\Adapters\TimeLimit;
App::init(function ($utopia, $request, $response, $project, $user, $register) {
/** @var Utopia\App $utopia */
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Registry\Registry $register */

View file

@ -5,8 +5,8 @@ use Utopia\Config\Config;
App::init(function ($utopia, $request, $response, $layout) {
/** @var Utopia\App $utopia */
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\View $layout */
/* AJAX check */

View file

@ -19,7 +19,7 @@ App::init(function ($layout) {
}, ['layout'], 'console');
App::shutdown(function ($response, $layout) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\View $layout */
$header = new View(__DIR__.'/../../views/console/comps/header.phtml');
@ -213,7 +213,7 @@ App::get('/console/database/collection')
->label('scope', 'console')
->param('id', '', new UID(), 'Collection unique ID.')
->action(function ($id, $response, $layout, $projectDB) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\View $layout */
/** @var Appwrite\Database\Database $projectDB */
@ -385,10 +385,9 @@ App::get('/console/version')
try {
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost').'/v1/health/version'), true);
if($version && isset($version['version'])) {
if ($version && isset($version['version'])) {
return $response->json(['version' => $version['version']]);
}
else {
} else {
throw new Exception('Failed to check for a newer version', 500);
}
} catch (\Throwable $th) {

View file

@ -28,7 +28,7 @@ App::init(function ($layout) {
}, ['layout'], 'home');
App::shutdown(function ($response, $layout) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\View $layout */
$response->html($layout->render());
@ -39,7 +39,7 @@ App::get('/')
->label('permission', 'public')
->label('scope', 'home')
->action(function ($response) {
/** @var Utopia\Response $response */
/** @var Appwrite\Utopia\Response $response */
$response->redirect('/auth/signin');
}, ['response']);
@ -189,8 +189,8 @@ App::get('/open-api-2.json')
->param('tests', 0, new Range(0, 1), 'Include only test services.', true)
->action(function ($platform, $extensions, $tests, $utopia, $request, $response) {
/** @var Utopia\App $utopia */
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
$security = [
APP_PLATFORM_CLIENT => ['Project' => []],

View file

@ -2,9 +2,9 @@
require_once __DIR__.'/../vendor/autoload.php';
use Appwrite\Swoole\Files;
use Appwrite\Swoole\Request;
use Appwrite\Swoole\Response;
use Utopia\Swoole\Files;
use Utopia\Swoole\Request;
use Appwrite\Utopia\Response;
use Swoole\Process;
use Swoole\Http\Server;
use Swoole\Http\Request as SwooleRequest;
@ -23,7 +23,7 @@ sleep(2);
$http = new Server("0.0.0.0", 80);
$payloadSize = max(4000000 /* 4mb */, App::getEnv('_APP_STORAGE_LIMIT', 100000000));
$payloadSize = max(4000000 /* 4mb */, App::getEnv('_APP_STORAGE_LIMIT', 10000000 /* 10mb */));
$http
->set([
@ -49,7 +49,7 @@ $http->on('AfterReload', function($serv, $workerId) {
});
$http->on('start', function (Server $http) use ($payloadSize) {
Console::success('Server started succefully (max payload is '.$payloadSize.' bytes)');
Console::success('Server started succefully (max payload is '.number_format($payloadSize).' bytes)');
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");

View file

@ -36,7 +36,7 @@ const APP_EMAIL_SECURITY = 'security@localhost.test'; // Default security email
const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12;
const APP_CACHE_BUSTER = 127;
const APP_CACHE_BUSTER = 138;
const APP_VERSION_STABLE = '0.7.0';
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
@ -199,6 +199,9 @@ $register->set('smtp', function () {
return $mail;
});
$register->set('geodb', function () {
return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2020-01.mmdb');
});
$register->set('queue-webhooks', function () {
return new Event('v1-webhooks', 'WebhooksV1');
});
@ -361,8 +364,8 @@ App::setResource('clients', function($console, $project) {
}, ['console', 'project']);
App::setResource('user', function($mode, $project, $console, $request, $response, $projectDB, $consoleDB) {
/** @var Utopia\Request $request */
/** @var Utopia\Response $response */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Database $consoleDB */
/** @var Appwrite\Database\Database $projectDB */
@ -424,7 +427,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response
}, ['mode', 'project', 'console', 'request', 'response', 'projectDB', 'consoleDB']);
App::setResource('project', function($consoleDB, $request) {
/** @var Appwrite\Swoole\Request $request */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Database\Database $consoleDB */
Authorization::disable();
@ -445,7 +448,6 @@ App::setResource('consoleDB', function($register) {
$consoleDB = new Database();
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$consoleDB->setNamespace('app_console'); // Should be replaced with param if we want to have parent projects
$consoleDB->setMocks(Config::getParam('collections', []));
return $consoleDB;
@ -461,11 +463,11 @@ App::setResource('projectDB', function($register, $project) {
}, ['register', 'project']);
App::setResource('mode', function($request) {
/** @var Utopia\Request $request */
/** @var Utopia\Swoole\Request $request */
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', 'default'));
}, ['request']);
App::setResource('geodb', function($request) {
/** @var Utopia\Request $request */
return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2020-01.mmdb');
}, ['request']);
App::setResource('geodb', function($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('geodb');
}, ['register']);

View file

@ -213,7 +213,6 @@ $cli
Console::error('🔴 ' . $message);
}
}
try {
Console::log('');
@ -232,4 +231,4 @@ $cli
} catch (\Throwable $th) {
Console::error('Failed to check for a newer version'."\n");
}
});
});

View file

@ -7,15 +7,18 @@ use Utopia\CLI\Console;
use Appwrite\Database\Database;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
$callbacks = [
'0.4.0' => function() {
Console::log('I got nothing to do.');
},
'0.5.0' => function($project) use ($register, $projectDB, $requset) {
'0.5.0' => function($project) use ($register, $projectDB) {
$db = $register->get('db');
Console::log('Migrating project: '.$project->getId());
Console::log('Migrating project: '.$project->getAttribute('name').' ('.$project->getId().')');
// Update all documents $uid -> $id
@ -46,6 +49,7 @@ $callbacks = [
try {
$new = $projectDB->overwriteDocument($document->getArrayCopy());
} catch (\Throwable $th) {
var_dump($document);
Console::error('Failed to update document: '.$th->getMessage());
continue;
}
@ -107,6 +111,14 @@ function fixDocument(Document $document) {
}
}
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_WEBHOOKS){
$document->setAttribute('security', ($document->getAttribute('security')) ? true : false);
}
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_TASKS){
$document->setAttribute('security', ($document->getAttribute('security')) ? true : false);
}
if($document->getAttribute('$collection') === Database::SYSTEM_COLLECTION_USERS) {
foreach($providers as $key => $provider) {
if(!empty($document->getAttribute('oauth'.\ucfirst($key)))) {
@ -165,26 +177,38 @@ function fixDocument(Document $document) {
$cli
->task('migrate')
->action(function () use ($console, $projectDB, $consoleDB, $callbacks) {
->action(function () use ($register, $callbacks) {
Console::success('Starting Data Migration');
$consoleDB = new Database();
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$consoleDB->setNamespace('app_console'); // Main DB
$consoleDB->setMocks(Config::getParam('collections', []));
$projectDB = new Database();
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$projectDB->setMocks(Config::getParam('collections', []));
$console = $consoleDB->getDocument('console');
Authorization::disable();
$limit = 30;
$sum = 30;
$offset = 0;
$projects = [$console];
$count = 0;
while ($sum >= 30) {
foreach($projects as $project) {
$projectDB->setNamespace('app_'.$project->getId());
try {
$callbacks['0.5.0']($project);
$callbacks['0.5.0']($project, $projectDB);
} catch (\Throwable $th) {
throw $th;
Console::error('Failed to update project ("'.$project->getId().'") version with error: '.$th->getMessage());
$projectDB->setNamespace('app_console');
$projectDB->deleteDocument($project->getId());
}
}
@ -201,8 +225,9 @@ $cli
$sum = \count($projects);
$offset = $offset + $limit;
$count = $count + $sum;
Console::log('Fetched '.$sum.' projects...');
Console::log('Fetched '.$count.'/'.$consoleDB->getSum().' projects...');
}
Console::success('Data Migration Completed');

View file

@ -192,7 +192,7 @@
data-name="sessions"
data-event="load,account.deleteRemoteSession">
<ul data-ls-loop="sessions" data-ls-as="session" class="list">
<ul data-ls-loop="sessions.sessions" data-ls-as="session" class="list">
<li class="clear">
<span data-ls-if="true != {{session.current}}">
<!-- From remote session (-logout event) -->
@ -236,17 +236,18 @@
</form>
</span>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.client.short_name|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.client.name}},alt={{session.client.name}}" class="avatar trans pull-start margin-end" />
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar trans pull-start margin-end" />
<span data-ls-if="({{session.client.name}})" data-ls-bind="{{session.client.name}}"></span> <span data-ls-if="(!{{session.client.name}})">Unknown</span> <span data-ls-bind="{{session.client.version}}"></span> on <span data-ls-bind="{{session.model}}"></span> <span data-ls-if="(!{{session.OS.name}})">Unknown</span> <span data-ls-if="({{session.OS.name}})" data-ls-bind="{{session.OS.name}}"></span> <span data-ls-bind="{{session.OS.version}}"></span>
<span data-ls-if="(!{{log.clientName}})">Unknown</span>
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{session.clientName}}"></span> <span data-ls-bind="{{session.clientVersion}}"></span> on <span data-ls-bind="{{session.deviceModel}}"></span> <span data-ls-bind="{{session.osName}}"></span> <span data-ls-bind="{{session.osVersion}}"></span>
&nbsp;
<span data-ls-if="true == {{session.current}}">
<span class="tag green">Current Session</span>
</span>
<div class="margin-top-small">
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/flags/{{session.geo.isoCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.geo.country}}"></small>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-if="{{session.countryCode}} !== '--'" data-ls-attrs="src={{env.API}}/avatars/flags/{{session.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.countryName}}"></small>
</div>
</li>
</ul>
@ -289,17 +290,18 @@
<th width="90">IP</th>
</tr>
</thead>
<tbody data-ls-loop="securityLogs" data-ls-as="log">
<tbody data-ls-loop="securityLogs.logs" data-ls-as="log">
<tr>
<td data-title="Date: "><span data-ls-bind="{{log.time|dateTime}}"></span></td>
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
<td data-title="Client: ">
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.client.short_name|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.client.name}},alt={{log.client.name}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.client.name}} {{log.client.version}} on {{log.model}} {{log.OS.name}} {{log.OS.version}}"></span>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<span data-ls-if="(!{{log.clientName}})">Unknown</span>
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
</td>
<td data-title="Location: ">
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.geo.isoCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.geo.country}}"></span>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.geo.countryName}}"></span>
</td>
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
</tr>

View file

@ -183,24 +183,6 @@
data-name="projects"
data-scope="console"></div>
<div class=""
data-service="locale.get"
data-name="locale"
data-event="load"
data-scope="console"></div>
<div class=""
data-service="locale.getCountries"
data-name="locale-countries"
data-event="load"
data-scope="console"></div>
<div class=""
data-service="locale.getCountriesPhones"
data-name="locale-countries-phones"
data-event="load"
data-scope="console"></div>
<div class="load-screen" data-ls-ui-loader>
<div class="animation"><div></div><div></div><div></div><div></div></div>
<img src="/images/appwrite.svg" alt="Appwrite Light Logo" class="force-light" loading="lazy" />

View file

@ -501,7 +501,7 @@ $maxCells = 10;
<script type="text/html" id="template-validation-document-array-true">
<label data-ls-attrs="for=rule-list-{{rule.$id}}" class="margin-bottom">Allowed Collections</label>
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative" style="visibility: hidden">
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative">
<div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}">
<input type="checkbox" name="list" data-ls-attrs="value={{project.$id}}" data-ls-bind="{{rule.list}}" /> <span data-ls-bind="{{project.name}}"></span>
</div>
@ -511,7 +511,7 @@ $maxCells = 10;
<script type="text/html" id="template-validation-document-array-false">
<label data-ls-attrs="for=rule-list-{{rule.$id}}" class="margin-bottom">Allowed Collection</label>
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative" style="visibility: hidden">
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative">
<div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}">
<input type="radio" name="list" data-ls-attrs="value={{project.$id}}" data-ls-bind="{{rule.list|firstElement}}" data-cast-to="array" required /> <span data-ls-bind="{{project.name}}"></span>
</div>

View file

@ -63,7 +63,7 @@
</div>
<div data-ls-if="0 != {{project-collections.sum}}">
<ul data-ls-loop="project-collections.collections" data-ls-as="collection" data-ls-append="" class="tiles cell-3 margin-bottom-small" style="visibility: hidden">
<ul data-ls-loop="project-collections.collections" data-ls-as="collection" data-ls-append="" class="tiles cell-3 margin-bottom-small">
<li class="margin-bottom">
<a data-ls-attrs="href=/console/database/collection?id={{collection.$id}}&project={{router.params.project}}" class="box">
<div data-ls-bind="{{collection.name}}" class="text-one-liner margin-bottom text-bold">&nbsp;</div>

View file

@ -29,7 +29,7 @@ if($type === 'document') {
<button type="button" class="link"><i class="icon-down-dir"></i> Move Down</button>
</li>
<li>
<button type="button" data-ls-ui-trigger="splice-<?php echo $this->escape($namespace); ?>-{{$index}}" class="link" data-debug="1"><i class="icon-cancel"></i> Remove</button>
<button type="button" data-ls-ui-trigger="splice-<?php echo $this->escape($namespace); ?>-{{$index}}" class="link"><i class="icon-cancel"></i> Remove</button>
</li>
</ul>
</div>

View file

@ -3,12 +3,11 @@ $fileLimit = $this->getParam('fileLimit', 0);
$fileLimitHuman = $this->getParam('fileLimitHuman', 0);
$events = array_keys($this->getParam('events', []));
$timeout = $this->getParam('timeout', 900);
?>
<div
data-service="functions.get"
data-name="project-function"
data-event="load,functions.update,functions.createTag,functions.updateTag"
data-event="load,functions.update,functions.createTag,functions.updateTag,functions.deleteTag"
data-param-function-id="{{router.params.id}}"
data-success="trigger"
data-success-param-trigger-events="functions.get">
@ -64,7 +63,7 @@ $timeout = $this->getParam('timeout', 900);
data-failure="alert"
data-failure-param-alert-text="Failed to execute function"
data-failure-param-alert-classname="error">
<button style="vertical-align: top;">Execute Now</button> &nbsp; <a data-ls-attrs="href=/console/functions/function/usage?id={{router.params.id}}&project={{router.params.project}}" class="button reverse" style="vertical-align: top;">View Logs</a>
<button style="vertical-align: top;">Execute Now</button> &nbsp; <a data-ls-attrs="href=/console/functions/function/logs?id={{router.params.id}}&project={{router.params.project}}" class="button reverse" style="vertical-align: top;">View Logs</a>
</form>
</div>
</div>
@ -113,7 +112,7 @@ $timeout = $this->getParam('timeout', 900);
<b data-ls-bind="{{tag.$id}}"></b> &nbsp;
<span class="text-fade" data-ls-bind="{{tag.command}}"></span>
<div class="text-size-small margin-top-small clear">
<span class="pull-start" data-ls-bind="Created {{tag.dateCreated|timeSince}} &nbsp; | &nbsp; {{tag.codeSize|humanFileSize}}"></span>
<span class="pull-start" data-ls-bind="Created {{tag.dateCreated|timeSince}} &nbsp; | &nbsp; {{tag.size|humanFileSize}}"></span>
<form data-ls-if="{{tag.$id}} !== {{project-function.tag}}" name="functions.deleteTag" class="pull-start"
data-analytics-event="submit"
@ -241,23 +240,89 @@ $timeout = $this->getParam('timeout', 900);
</div>
</div>
</li>
<li data-state="/console/functions/function/usage?id={{router.params.id}}&project={{router.params.project}}">
<h2>Usage</h2>
<li data-state="/console/functions/function/monitors?id={{router.params.id}}&project={{router.params.project}}">
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
data-service="functions.getUsage"
data-event="submit"
data-name="usage"
data-param-function-id="{{router.params.id}}"
data-param-range="90d">
<button class="tick">90d</button>
</form>
<div class="box margin-bottom-small">
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
<div class="chart margin-bottom-no">
<div class="content" data-forms-chart="Requests=usage.requests.data,Network=usage.network.data" data-height="140"></div>
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
data-service="functions.getUsage"
data-event="submit"
data-name="usage"
data-param-function-id="{{router.params.id}}">
<button class="tick">30d</button>
</form>
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
data-service="functions.getUsage"
data-event="submit"
data-name="usage"
data-param-function-id="{{router.params.id}}"
data-param-range="24h">
<button class="tick">24h</button>
</form>
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
<h2>Monitors</h2>
<div
data-service="functions.getUsage"
data-event="load"
data-name="usage"
data-param-function-id="{{router.params.id}}">
<div class="box margin-bottom-small">
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
<div class="chart margin-bottom-no">
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Executions=usage.executions.data" data-height="140" />
</div>
</div>
</div>
<ul class="chart-notes margin-bottom-large">
<li>Executions <span data-ls-bind="({{usage.executions.total}})"></span></li>
</ul>
<div class="box margin-bottom-small">
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
<div class="chart margin-bottom-no">
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="CPU Time (seconds)=usage.compute.data" data-colors="orange" data-height="140" />
</div>
</div>
</div>
<ul class="chart-notes margin-bottom-large">
<li class="orange">CPU Time <span data-ls-bind="({{usage.compute.total|seconds2hum}})"></span></li>
</ul>
<div class="box margin-bottom-small">
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
<div class="chart margin-bottom-no">
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Failures=usage.failures.data" data-colors="red" data-height="140" />
</div>
</div>
</div>
<ul class="chart-notes margin-bottom-large">
<li class="red">Errors <span data-ls-bind="({{usage.failures.total}})"></span></li>
</ul>
</div>
</li>
<li data-state="/console/functions/function/logs?id={{router.params.id}}&project={{router.params.project}}">
<ul class="chart-notes margin-bottom-large">
<li>Invocations</li>
<li>CPU Time</li>
</ul>
<div class="text-fade text-size-small pull-end margin-top" data-ls-bind="{{project-function-executions.sum}} executions found"></div>
<h3>Logs &nbsp; <span class="text-fade text-size-small pull-end margin-top-small" data-ls-bind="{{project-function-executions.sum}} executions found"></span></h3>
<h2>Logs</h2>
<div
data-service="functions.listExecutions"
@ -277,9 +342,10 @@ $timeout = $this->getParam('timeout', 900);
<thead>
<tr>
<th width="30"></th>
<th width="160">Created</th>
<th width="150">Status</th>
<th width="170">Date</th>
<th width="100">Runtime</th>
<th width="120">Trigger</th>
<th width="80">Runtime</th>
<th></th>
</tr>
</thead>
@ -291,35 +357,42 @@ $timeout = $this->getParam('timeout', 900);
<i class="dot info" data-ls-if="{{execution.status}} === 'processing'"></i>
<i class="dot success" data-ls-if="{{execution.status}} === 'completed'"></i>
</td>
<td data-title="Date: ">
<span data-ls-bind="{{execution.dateCreated|dateTime}}"></span>
</td>
<td data-title="Status: ">
<span data-ls-bind="{{execution.status}}"></span>
<span class="text-fade text-size-small" data-ls-if="{{execution.exitCode}} !== 0" data-ls-bind=" exit code: {{execution.exitCode}}"></span>
</td>
<td data-title="Date: ">
<span data-ls-bind="{{execution.dateCreated|dateTime}}"></span>
<td data-title="Trigger: ">
<span data-ls-bind="{{execution.trigger}}"></span>
</td>
<td data-title="Runtime: ">
<span data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-ls-bind="{{execution.time|seconds2hum}}"></span>
<span data-ls-if="{{execution.status}} === 'waiting' || {{execution.status}} === 'processing'">-</span>
</td>
<td>
<td data-title="">
<div data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-title="">
<button class="desktops-only pull-end link margin-start text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Errors</button>
<button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Output</button>
<button class="phones-only tablets-only link margin-start text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Errors</button>
<button class="phones-only tablets-only link margin-start" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Output</button>
<button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Output</button>
<button class="phones-only-inline tablets-only-inline link text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Errors</button>
<div data-ui-modal class="modal width-large box close" data-button-alias="none" data-open-event="execution-stdout-{{execution.$id}}">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>STDOUT</h1>
<div class="margin-bottom">
<input type="hidden" data-ls-bind="{{execution.stdout}}" data-forms-code />
<div class="margin-bottom ide" data-ls-if="({{execution.stdout.length}})">
<pre data-ls-bind="{{execution.stdout}}"></pre>
<!-- <input type="hidden" data-ls-bind="{{execution.stdout}}" data-forms-code="bash" /> -->
</div>
<div class="margin-bottom" data-ls-if="(!{{execution.stdout.length}})">
<p>No output was logged.</p>
</div>
</div>
<div data-ui-modal class="modal width-large box close" data-button-alias="none" data-open-event="execution-stderr-{{execution.$id}}">
@ -327,8 +400,13 @@ $timeout = $this->getParam('timeout', 900);
<h1>STDERR</h1>
<div class="margin-bottom">
<input type="hidden" data-ls-bind="{{execution.stderr}}" data-forms-code />
<div class="margin-bottom ide" data-ls-if="({{execution.stderr.length}})">
<pre data-ls-bind="{{execution.stderr}}"></pre>
<!-- <input type="hidden" data-ls-bind="{{execution.stderr}}" data-forms-code="bash" /> -->
</div>
<div class="margin-bottom" data-ls-if="(!{{execution.stderr.length}})">
<p>No errors were logged.</p>
</div>
</div>
</div>
@ -412,11 +490,11 @@ $timeout = $this->getParam('timeout', 900);
<div class="text-size-small text-fade margin-bottom margin-top-negative-small">Max value is <?php echo $this->escape(number_format($timeout)); ?> seconds (<?php echo $this->escape((int) ($timeout / 60)); ?> minutes)</div>
</section>
<section class="margin-bottom">
<section class="margin-bottom" data-forms-select-all>
<label for="events" class="margin-bottom">Events <span class="tooltip small" data-tooltip="Choose which events should trigger this function."><i class="icon-info-circled"></i></span></label>
<div class="row responsive thin margin-top-small">
<?php foreach ($events as $i => $event) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large" title="<?php echo $event; ?>">
<div class="col span-6 text-one-liner margin-bottom text-height-large text-size-small" title="<?php echo $event; ?>">
<input type="checkbox" name="events" data-ls-bind="{{project-function.events}}" value="<?php echo $event; ?>" /> &nbsp; <?php echo $event; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>

View file

@ -47,8 +47,7 @@ $environments = $this->getParam('environments', []);
<span data-ls-bind="{{function.name}}"></span>
<div class="text-fade" data-ls-if="({{function.events.length}})"><span data-ls-bind="{{function.events.length}}"></span> events assigned</div>
<div class="text-fade" data-ls-if="(!{{function.events.length}})">&nbsp;</div>
<p class="text-fade margin-bottom-no" data-ls-bind="{{function.env|envName}} {{function.env|envVersion}}"></p>
</li>
</ul>
</div>

View file

@ -13,69 +13,105 @@ $graph = $this->getParam('graph', false);
<span class="title" data-ls-bind="{{console-project.name}}">&nbsp;</span>&nbsp;&nbsp;
</h1>
<ul class="margin-top-negative-small margin-bottom clear">
<ul class="desktops-only margin-top-negative-small margin-bottom clear">
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/settings?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-cog"></i> &nbsp;Settings</a> &nbsp;&nbsp;</li>
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/keys?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-key-inv"></i> &nbsp;API Keys</a> &nbsp;&nbsp;</li>
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/webhooks?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-link"></i> &nbsp;Webhooks</a> &nbsp;&nbsp;</li>
<li class="pull-start margin-end margin-bottom-small"><a data-ls-attrs="href=/console/tasks?project={{router.params.project}}" class="link-animation-enabled"><i class="icon-clock"></i> &nbsp;Tasks</a> &nbsp;&nbsp;</li>
<!-- <li class="pull-end margin-start margin-bottom-small text-size-small margin-top-tiny"><a data-ls-attrs="href=/console/tasks?project={{router.params.project}}">May 2020 &nbsp; <i class="icon-down-dir"></i></a></li> -->
</ul>
<div class="margin-bottom phones-only">&nbsp;</div>
</div>
</div>
<div class="zone xl margin-top-negative-xxl">
<div class="box margin-bottom dashboard">
<div
data-service="projects.getUsage"
data-event="load"
data-name="usage"
data-param-project-id="{{router.params.project}}"
data-param-range="last30">
<div class="zone xxl margin-top-negative-xxxl">
<div class="clear margin-bottom-small margin-top-negative">
<div class="pull-end">
<?php if (!$graph) : ?>
<form class="margin-start-small inline" data-ls-if="{{usage.range}} !== '24h'"
data-service="projects.getUsage"
data-event="submit"
data-name="usage"
data-param-project-id="{{router.params.project}}"
data-param-range="24h">
<button class="tick">24h</button>
</form>
<button class="tick margin-start-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
<form class="margin-start-small inline" data-ls-if="{{usage.range}} !== '30d'"
data-service="projects.getUsage"
data-event="submit"
data-name="usage"
data-param-project-id="{{router.params.project}}">
<button class="tick">30d</button>
</form>
<button class="tick margin-start-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
<form class="margin-start-small inline" data-ls-if="{{usage.range}} !== '90d'"
data-service="projects.getUsage"
data-event="submit"
data-name="usage"
data-param-project-id="{{router.params.project}}"
data-param-range="90d">
<button class="tick">90d</button>
</form>
<button class="tick margin-start-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
</div>
</div>
<div
data-service="projects.getUsage"
data-event="load"
data-name="usage"
data-param-project-id="{{router.params.project}}"
data-param-range="30d">
<?php if (!$graph) : ?>
<div class="box dashboard">
<div class="row responsive">
<div class="col span-9">
<div class="chart pull-end">
<div class="content" data-forms-chart="Requests=usage.requests.data"></div>
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Requests=usage.requests.data" />
</div>
<div class="chart-metric">
<div class="value margin-bottom-small"><span class="sum" data-ls-bind="{{usage.requests.total|statsTotal}}">N/A</span></div>
<div class="metric margin-bottom-small">Requests <span class="tooltip" data-tooltip="Total number of API requests last 30 days"><i class="icon-info-circled"></i></span></div>
<div class="range">Last 30 days</div>
</div>
</div>
<div class="col span-3">
<div class="value margin-bottom-small"><span class="sum" data-ls-bind="{{usage.network.total|humanFileSize}}" data-default="0">0</span></div>
<div class="metric margin-bottom-small">Bandwidth</div>
<div class="range">Last 30 days</div>
<!-- <div class="margin-top dev-feature">
<a href="">Full Usage Report <i class="icon-right-open"></i></a>
<!-- <div class="margin-top-large value small">
<b class="text-size-small sum small" data-ls-bind="{{usage.functions.total|statsTotal}}" data-default="0"></b>
<br />
<b>Func. Executions</b>
</div> -->
</div>
</div>
</div>
<?php endif; ?>
<hr />
<?php endif; ?>
<div>
<div class="row responsive">
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.documents.total|statsTotal}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Documents</b></div>
</div>
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.storage.total|humanFileSize}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Storage</b></div>
</div>
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.users.total}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Users</b></div>
</div>
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.tasks.total}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Tasks</b></div>
</div>
<div class="box dashboard">
<div class="row responsive">
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.documents.total|statsTotal}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Documents</b></div>
</div>
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.storage.total|humanFileSize}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Storage</b></div>
</div>
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.users.total}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Users</b></div>
</div>
<div class="col span-3">
<div class="value"><span class="sum" data-ls-bind="{{usage.tasks.total}}" data-default="0">0</span></div>
<div class="margin-top-small"><b class="text-size-small unit">Tasks</b></div>
</div>
</div>
</div>
@ -122,22 +158,22 @@ $graph = $this->getParam('graph', false);
<div>
<div class="pull-start margin-end avatar-container">
<img src="" data-ls-attrs="src=/images/clients/{{platform.type}}.png?v=<?php echo APP_CACHE_BUSTER; ?>" class="avatar" loading="lazy" width="60" height="60" />
<img src="" data-ls-attrs="src=/images/clients/{{platform.type}}.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Platform Logo" class="avatar" loading="lazy" width="60" height="60" />
<div data-ls-if="{{platform.type}} === 'flutter-ios'" class="corner">
<img src="" data-ls-attrs="src=/images/clients/ios.png?v=<?php echo APP_CACHE_BUSTER; ?>" class="avatar xs" loading="lazy" width="30" height="30" />
<img src="" data-ls-attrs="src=/images/clients/ios.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="iOS Logo" class="avatar xs" loading="lazy" width="30" height="30" />
</div>
<div data-ls-if="{{platform.type}} === 'flutter-android'" class="corner">
<img src="" data-ls-attrs="src=/images/clients/android.png?v=<?php echo APP_CACHE_BUSTER; ?>" class="avatar xs" loading="lazy" width="30" height="30" />
<img src="" data-ls-attrs="src=/images/clients/android.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Android Logo" class="avatar xs" loading="lazy" width="30" height="30" />
</div>
</div>
<span data-ls-bind="{{platform.name}}"></span>
<span class="text-one-liner" data-ls-bind="{{platform.name}}"></span>
</div>
<p class="margin-bottom-no"><small data-ls-bind="{{platform.hostname}}{{platform.key}}"></small></p>
<div class="phones-only-inline tablets-only-inline margin-top-tiny">
<div class="phones-only-inline tablets-only-inline margin-top-small">
<button class="link" data-ls-ui-trigger="platform-update-{{platform.$id}}">Update</button>
<button class="link danger" data-ls-ui-trigger="platform-delete-{{platform.$id}}">Delete</button>
</div>

View file

@ -35,7 +35,7 @@ $home = $this->getParam('home', '');
<p class="margin-bottom-no">No Projects Found, Create your first project now.</p>
</div>
<ul data-ls-loop="console-projects.projects" data-ls-as="project" data-ls-append="" class="tiles cell-3" style="visibility: hidden">
<ul data-ls-loop="console-projects.projects" data-ls-as="project" data-ls-append="" class="tiles cell-3">
<li class="margin-bottom">
<a data-ls-attrs="href=/console/home?project={{project.$id}}" class="box">
<div data-ls-bind="{{project.name}}" class="text-one-liner margin-bottom-tiny text-bold">&nbsp;</div>

View file

@ -18,14 +18,14 @@ $scopes = $this->getParam('scopes', []);
data-success="trigger"
data-success-param-trigger-events="projects.listKeys">
<div data-ls-if="0 == {{console-keys.length}} || undefined == {{console-keys.length}}" class="box margin-top margin-bottom">
<div data-ls-if="0 == {{console-keys.sum}} || undefined == {{console-keys.sum}}" class="box margin-top margin-bottom">
<h3 class="margin-bottom-small text-bold">No API Keys Found</h3>
<p class="margin-bottom-no">You haven't created any API keys for your project yet.</p>
</div>
<div class="box margin-bottom" data-ls-if="0 != {{console-keys.length}}">
<ul data-ls-loop="console-keys" data-ls-as="key" class="list">
<div class="box margin-bottom" data-ls-if="0 != {{console-keys.sum}}">
<ul data-ls-loop="console-keys.keys" data-ls-as="key" class="list">
<li class="clear">
<div data-ui-modal class="modal box close" data-button-alias="none" data-open-event="key-update-{{key.$id}}">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
@ -52,19 +52,21 @@ $scopes = $this->getParam('scopes', []);
<label data-ls-attrs="for=name-{{key.$id}}">Name <span class="tooltip large" data-tooltip="Choose any name that will help you distinguish between your different API keys."><i class="icon-question"></i></span></label>
<input type="text" class="full-width" data-ls-attrs="id=name-{{key.$id}}" name="name" required autocomplete="off" data-ls-bind="{{key.name}}" maxlength="128" />
<label data-ls-attrs="for=scopes-{{key.$id}}">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank" rel="noopener">Learn more</a>)</label>
<div class="row responsive thin">
<?php foreach ($scopes as $i => $scope) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large" title="<?php echo $scope; ?>">
<input type="checkbox" name="scopes" data-ls-bind="{{key.scopes}}" value="<?php echo $scope; ?>" /> &nbsp; <?php echo $scope; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<?php endforeach; ?>
</div>
<section data-forms-select-all>
<label data-ls-attrs="for=scopes-{{key.$id}}">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank" rel="noopener">Learn more</a>)</label>
<div class="row responsive thin">
<?php foreach ($scopes as $i => $scope) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large text-size-small" title="<?php echo $scope; ?>">
<input type="checkbox" name="scopes" data-ls-bind="{{key.scopes}}" value="<?php echo $scope; ?>" /> &nbsp; <?php echo $scope; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<?php endforeach; ?>
</div>
</section>
<hr class="margin-top-no" />
@ -147,19 +149,21 @@ $scopes = $this->getParam('scopes', []);
<label for="name">Name <span class="tooltip large" data-tooltip="Choose any name that will help you distinguish between your different API keys."><i class="icon-question"></i></span></label>
<input type="text" class="full-width" id="name" name="name" required autocomplete="off" maxlength="128" />
<label for="scopes">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank" rel="noopener">Learn more</a>)</label>
<div class="row responsive thin">
<?php foreach ($scopes as $i => $scope) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large" title="<?php echo $scope; ?>">
<input type="checkbox" name="scopes" value="<?php echo $scope; ?>" /> &nbsp; <?php echo $scope; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<section data-forms-select-all>
<label for="scopes">Scopes (<a data-ls-attrs="href={{env.HOME}}/docs/keys" target="_blank" rel="noopener">Learn more</a>)</label>
<div class="row responsive thin">
<?php foreach ($scopes as $i => $scope) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large text-size-small" title="<?php echo $scope; ?>">
<input type="checkbox" name="scopes" value="<?php echo $scope; ?>" /> &nbsp; <?php echo $scope; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
</section>
<hr class="margin-top-no" />

View file

@ -218,13 +218,13 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
data-success="trigger"
data-success-param-trigger-events="projects.listDomains">
<div data-ls-if="0 == {{console-domains.length}} || undefined == {{console-domains.length}}" class="box margin-top margin-bottom">
<div data-ls-if="0 == {{console-domains.sum}} || undefined == {{console-domains.sum}}" class="box margin-top margin-bottom">
<h3 class="margin-bottom-small text-bold">No Custom Domains Added</h3>
<p class="margin-bottom-no">You haven't created any custom domains for your project yet.</p>
</div>
<div class="box margin-bottom" data-ls-if="0 != {{console-domains.length}}">
<div class="box margin-bottom" data-ls-if="0 != {{console-domains.sum}}">
<table class="vertical">
<thead>
<tr>
@ -235,7 +235,7 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<th width="40"></th>
</tr>
</thead>
<tbody data-ls-loop="console-domains" data-ls-as="domain">
<tbody data-ls-loop="console-domains.domains" data-ls-as="domain">
<tr>
<td data-title="Status">
<span class="text-size-small text-danger" data-ls-if="true !== {{domain.verification}}"><i class="icon-cancel-circled"></i> Unverified&nbsp;</span>

View file

@ -15,13 +15,13 @@
data-success="trigger"
data-success-param-trigger-events="projects.listTasks">
<div data-ls-if="0 === {{console-tasks.length}} || undefined === {{console-tasks.length}}" class="box margin-top margin-bottom">
<div data-ls-if="0 === {{console-tasks.sum}} || undefined === {{console-tasks.sum}}" class="box margin-top margin-bottom">
<h3 class="margin-bottom-small text-bold">No Tasks Found</h3>
<p class="margin-bottom-no">You haven't created any tasks for your project yet.</p>
</div>
<div class="box y-scroll margin-bottom" data-ls-if="0 != {{console-tasks.length}}">
<div class="box y-scroll margin-bottom" data-ls-if="0 != {{console-tasks.sum}}">
<table class="full vertical">
<thead>
<tr>
@ -31,13 +31,13 @@
<th width="140"></th>
</tr>
</thead>
<tbody data-ls-loop="console-tasks" data-ls-as="task" class="list">
<tbody data-ls-loop="console-tasks.tasks" data-ls-as="task" class="list">
<tr>
<td>
<div class="margin-bottom-tiny text-one-liner">
<span data-ls-attrs="title={{task.name}} ({{task.failures}} errors)" data-ls-bind="{{task.name}}"></span>
<span data-ls-if="false === {{task.security}}" data-debug="1">
<span data-ls-if="false === {{task.security}}">
&nbsp; <span class="text-danger">SSL/TLS Disabled</span>
</span>
<span data-ls-if="0 !== {{task.failures}}">

View file

@ -183,13 +183,13 @@
data-param-user-id="{{router.params.id}}"
data-event="load,users.update">
<div data-ls-if="{{sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
<div data-ls-if="{{sessions.sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
No sessions available.
</div>
<div data-ls-if="{{sessions.length}} !== 0" style="display: none">
<div data-ls-if="{{sessions.sessions.length}} !== 0" style="display: none">
<div class="box margin-bottom">
<ul data-ls-loop="sessions" data-ls-as="session" class="list">
<ul data-ls-loop="sessions.sessions" data-ls-as="session" class="list">
<li class="clear">
<form class="pull-end"
data-analytics-event="submit"
@ -209,13 +209,13 @@
<button class="danger">Logout</button>
</form>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.client.short_name|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.client.name}},alt={{session.client.name}}" class="avatar trans pull-start margin-end" />
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar trans pull-start margin-end" />
<span data-ls-if="({{session.client.name}})" data-ls-bind="{{session.client.name}}"></span> <span data-ls-if="(!{{session.client.name}})">Unknown</span> <span data-ls-bind="{{session.client.version}}"></span> on <span data-ls-bind="{{session.model}}"></span> <span data-ls-if="(!{{session.OS.name}})">Unknown</span> <span data-ls-if="({{session.OS.name}})" data-ls-bind="{{session.OS.name}}"></span> <span data-ls-bind="{{session.OS.version}}"></span>
<span data-ls-bind="{{session.clientName}}"></span> <span data-ls-bind="{{session.clientVersion}}"></span> on <span data-ls-bind="{{session.deviceModel}}"></span> <span data-ls-bind="{{session.osName}}"></span> <span data-ls-bind="{{session.osVersion}}"></span>
<div class="margin-top-small">
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/flags/{{session.geo.isoCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.geo.country}}"></small>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-if="{{session.countryCode}} !== '--'" data-ls-attrs="src={{env.API}}/avatars/flags/{{session.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs margin-end-small inline" />
<small data-ls-bind="{{session.ip}}"></small> / <small data-ls-bind="{{session.countryName}}"></small>
</div>
</li>
</ul>
@ -264,18 +264,18 @@
<th width="90">IP</th>
</tr>
</thead>
<tbody data-ls-loop="logs" data-ls-as="log">
<tbody data-ls-loop="logs.logs" data-ls-as="log">
<tr>
<td data-title="Date: "><span data-ls-bind="{{log.time|dateTime}}"></span></td>
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
<td data-title="Client: ">
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-if="({{log.client.short_name}})" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.client.short_name|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.client.name}},alt={{log.client.name}}" class="avatar xxs inline margin-end-small" />
<span data-ls-if="({{log.client.name}})" data-ls-bind="{{log.client.name}} {{log.client.version}} on {{log.model}} {{log.OS.name}} {{log.OS.version}}"></span>
<div data-ls-if="(!{{log.client.name}})" class="text-align-center text-fade">Unknown</div>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-if="({{log.clientCode}})" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
<div data-ls-if="(!{{log.clientName}})" class="text-align-center text-fade">Unknown</div>
</td>
<td data-title="Location: ">
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.geo.isoCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.geo.country}}"></span>
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-if="{{log.countryCode}} !== '--'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.countryName}}"></span>
</td>
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
</tr>

View file

@ -21,14 +21,14 @@ $events = array_keys($this->getParam('events', []));
data-success="trigger"
data-success-param-trigger-events="projects.listWebhooks">
<div data-ls-if="0 == {{console-webhooks.length}} || undefined == {{console-webhooks.length}}" class="box margin-top margin-bottom">
<div data-ls-if="0 == {{console-webhooks.sum}} || undefined == {{console-webhooks.sum}}" class="box margin-top margin-bottom">
<h3 class="margin-bottom-small text-bold">No Webhooks Found</h3>
<p class="margin-bottom-no">You haven't created any webhooks for your project yet.</p>
</div>
<div class="box margin-bottom" data-ls-if="0 != {{console-webhooks.length}}">
<ul data-ls-loop="console-webhooks" data-ls-as="webhook" class="list">
<div class="box margin-bottom" data-ls-if="0 != {{console-webhooks.sum}}">
<ul data-ls-loop="console-webhooks.webhooks" data-ls-as="webhook" class="list">
<li class="clear">
<div data-ui-modal class="modal close sticky-footer" data-button-text="Update" data-button-class="pull-end">
@ -56,19 +56,21 @@ $events = array_keys($this->getParam('events', []));
<label data-ls-attrs="for=name-{{webhook.$id}}">Name</label>
<input type="text" class="full-width" data-ls-attrs="id=name-{{webhook.$id}}" name="name" required autocomplete="off" data-ls-bind="{{webhook.name}}" maxlength="128" />
<label data-ls-attrs="for=events-{{webhook.$id}}">Events</label>
<div class="row responsive thin">
<?php foreach ($events as $i => $event) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large" title="<?php echo $event; ?>">
<input type="checkbox" name="events" data-ls-bind="{{webhook.events}}" value="<?php echo $event; ?>" /> &nbsp; <?php echo $event; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<?php endforeach; ?>
</div>
<section data-forms-select-all>
<label data-ls-attrs="for=events-{{webhook.$id}}">Events</label>
<div class="row responsive thin">
<?php foreach ($events as $i => $event) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large text-size-small" title="<?php echo $event; ?>">
<input type="checkbox" name="events" data-ls-bind="{{webhook.events}}" value="<?php echo $event; ?>" /> &nbsp; <?php echo $event; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<?php endforeach; ?>
</div>
</section>
<label data-ls-attrs="for=url-{{webhook.$id}}">POST URL</label>
<input type="url" class="full-width" data-ls-attrs="id=url-{{webhook.$id}}" name="url" required autocomplete="off" placeholder="https://example.com/callback" data-ls-bind="{{webhook.url}}" />
@ -135,7 +137,7 @@ $events = array_keys($this->getParam('events', []));
</form>
<span data-ls-bind="{{webhook.name}}"></span> &nbsp; (<span data-ls-bind="{{webhook.events.length}}"></span> events)
<span data-ls-if="false === {{webhook.security}}" data-debug="1">
<span data-ls-if="false === {{webhook.security}}">
&nbsp; <small class="text-danger">(SSL/TLS Disabled)</small>
</span>
<div class="margin-top-tiny">
@ -170,19 +172,21 @@ $events = array_keys($this->getParam('events', []));
<label for="name">Name</label>
<input type="text" class="full-width" id="name" name="name" required autocomplete="off" maxlength="128" />
<label for="events">Events</label>
<div class="row responsive thin">
<?php foreach ($events as $i => $event) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large" title="<?php echo $event; ?>">
<input type="checkbox" name="events" value="<?php echo $event; ?>" /> &nbsp; <?php echo $event; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<section data-forms-select-all>
<label for="events">Events</label>
<div class="row responsive thin">
<?php foreach ($events as $i => $event) : ?>
<div class="col span-6 text-one-liner margin-bottom text-height-large text-size-small" title="<?php echo $event; ?>">
<input type="checkbox" name="events" value="<?php echo $event; ?>" /> &nbsp; <?php echo $event; ?>
</div>
<?php if (($i + 1) % 2 === 0) : ?>
</div>
<div class="row responsive thin">
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
</section>
<label for="url">POST URL</label>
<input type="url" class="full-width" id="url" name="url" required autocomplete="off" placeholder="https://example.com/callback" />

View file

@ -1,6 +1,6 @@
<div class="zone large padding margin-top" id="message" style="display: none">
<h1 class="margin-bottom">Missing Redirect URL</h1>
<p>Your OAuth login flow is missing a redirect URL. Please check the
<p>Your OAuth login flow is missing a proper redirect URL. Please check the
<a href="https://<?php echo APP_DOMAIN; ?>/docs/client/account?sdk=web#createOAuth2Session">OAuth docs</a>
and send request for new session with a valid callback URL.</p>
</div>

View file

@ -243,6 +243,9 @@ services:
- _APP_REDIS_PORT
- _APP_SMTP_HOST
- _APP_SMTP_PORT
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
appwrite-schedule:
image: appwrite/appwrite:<?php echo $version."\n"; ?>
@ -259,7 +262,7 @@ services:
- _APP_REDIS_PORT
mariadb:
image: appwrite/mariadb:1.0.3 # fix issues when upgrading using: mysql_upgrade -u root -p
image: appwrite/mariadb:1.1.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:

3
bin/sdks Normal file
View file

@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php sdks $@

View file

@ -44,6 +44,7 @@
"utopia-php/registry": "0.2.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/domains": "0.2.*",
"utopia-php/swoole": "0.2.*",
"resque/php-resque": "1.3.6",
"piwik/device-detector": "3.13.0",

377
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": "7ad12180c132b3225874c7f18594409a",
"content-hash": "5f0e8470b46e87d70f3858b3f4ad9b30",
"packages": [
{
"name": "appwrite/php-clamav",
@ -51,77 +51,28 @@
],
"time": "2020-02-29T11:35:01+00:00"
},
{
"name": "bacon/bacon-qr-code",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
"reference": "add6d9ff97336b62f95a3b94f75cea4e085465b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/add6d9ff97336b62f95a3b94f75cea4e085465b2",
"reference": "add6d9ff97336b62f95a3b94f75cea4e085465b2",
"shasum": ""
},
"require": {
"dasprid/enum": "^1.0",
"ext-iconv": "*",
"php": "^7.1"
},
"require-dev": {
"phly/keep-a-changelog": "^1.4",
"phpunit/phpunit": "^7 | ^8 | ^9",
"squizlabs/php_codesniffer": "^3.4"
},
"suggest": {
"ext-imagick": "to generate QR code images"
},
"type": "library",
"autoload": {
"psr-4": {
"BaconQrCode\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/",
"role": "Developer"
}
],
"description": "BaconQrCode is a QR code generator for PHP.",
"homepage": "https://github.com/Bacon/BaconQrCode",
"time": "2020-07-30T16:40:58+00:00"
},
{
"name": "chillerlan/php-qrcode",
"version": "4.1.0",
"version": "4.2.0",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-qrcode.git",
"reference": "2cecb32cf618319dd01d9bc1fa64dc6bb683df85"
"reference": "1972af7af51b203bc239d8fb94243f6ed2a1067a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2cecb32cf618319dd01d9bc1fa64dc6bb683df85",
"reference": "2cecb32cf618319dd01d9bc1fa64dc6bb683df85",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/1972af7af51b203bc239d8fb94243f6ed2a1067a",
"reference": "1972af7af51b203bc239d8fb94243f6ed2a1067a",
"shasum": ""
},
"require": {
"chillerlan/php-settings-container": "^2.0",
"chillerlan/php-settings-container": "^2.1",
"ext-mbstring": "*",
"php": "^7.4"
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phan/phan": "^2.7",
"phpunit/phpunit": "^9.1",
"phan/phan": "^3.2.2",
"phpunit/phpunit": "^9.4",
"setasign/fpdf": "^1.8.2"
},
"suggest": {
@ -168,29 +119,29 @@
"type": "ko_fi"
}
],
"time": "2020-06-04T17:07:12+00:00"
"time": "2020-10-07T14:41:07+00:00"
},
{
"name": "chillerlan/php-settings-container",
"version": "2.0.0",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "75888345532373074fba482a6642c0f8cda996f0"
"reference": "90ab24a3dc09569bfa0f3dbc3d44bab3aaa2a6c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/75888345532373074fba482a6642c0f8cda996f0",
"reference": "75888345532373074fba482a6642c0f8cda996f0",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/90ab24a3dc09569bfa0f3dbc3d44bab3aaa2a6c5",
"reference": "90ab24a3dc09569bfa0f3dbc3d44bab3aaa2a6c5",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.4"
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phan/phan": "^2.6",
"phpunit/phpunit": "^9.1"
"phan/phan": "^3.2.2",
"phpunit/phpunit": "9.4"
},
"type": "library",
"autoload": {
@ -217,7 +168,13 @@
"container",
"helper"
],
"time": "2020-04-16T16:56:44+00:00"
"funding": [
{
"url": "https://ko-fi.com/codemasher",
"type": "ko_fi"
}
],
"time": "2020-10-07T13:18:35+00:00"
},
{
"name": "colinmollenhour/credis",
@ -259,49 +216,6 @@
"homepage": "https://github.com/colinmollenhour/credis",
"time": "2020-10-13T23:55:13+00:00"
},
{
"name": "dasprid/enum",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/DASPRiD/Enum.git",
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "^7 | ^8 | ^9",
"squizlabs/php_codesniffer": "^3.4"
},
"type": "library",
"autoload": {
"psr-4": {
"DASPRiD\\Enum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/",
"role": "Developer"
}
],
"description": "PHP 7.1 enum implementation",
"keywords": [
"enum",
"map"
],
"time": "2020-10-02T16:03:48+00:00"
},
{
"name": "domnikl/statsd",
"version": "3.0.2",
@ -858,6 +772,7 @@
"parser",
"useragent"
],
"abandoned": "matomo/device-detector",
"time": "2020-08-17T07:37:33+00:00"
},
{
@ -1604,6 +1519,58 @@
"utopia"
],
"time": "2020-10-24T08:51:37+00:00"
},
{
"name": "utopia-php/swoole",
"version": "0.2.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/swoole.git",
"reference": "bc9dd3e113e9b8cbbf54468524637f39b2deb861"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/bc9dd3e113e9b8cbbf54468524637f39b2deb861",
"reference": "bc9dd3e113e9b8cbbf54468524637f39b2deb861",
"shasum": ""
},
"require": {
"ext-swoole": "*",
"php": ">=7.3",
"utopia-php/framework": "0.*.*"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
"swoole/ide-helper": "4.5.5",
"vimeo/psalm": "4.0.1"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\Swoole\\": "src/Swoole"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Eldad Fux",
"email": "team@appwrite.io"
}
],
"description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative",
"keywords": [
"framework",
"http",
"php",
"server",
"swoole",
"upf",
"utopia"
],
"time": "2020-10-29T12:42:38+00:00"
}
],
"packages-dev": [
@ -2828,19 +2795,19 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "ed363c3ce393560a1c300dce0298bbf0f0528b13"
"reference": "7c0684ac5818c3a5a79d9dd9d85aac2aebdadb05"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ed363c3ce393560a1c300dce0298bbf0f0528b13",
"reference": "ed363c3ce393560a1c300dce0298bbf0f0528b13",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7c0684ac5818c3a5a79d9dd9d85aac2aebdadb05",
"reference": "7c0684ac5818c3a5a79d9dd9d85aac2aebdadb05",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.8",
"nikic/php-parser": "^4.10.2",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
@ -2893,7 +2860,7 @@
"type": "github"
}
],
"time": "2020-10-26T15:46:21+00:00"
"time": "2020-10-30T14:10:23+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -2901,12 +2868,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "86daa943fbb765aa0129d16f84c5bf7aaec44582"
"reference": "437553634c79817d89b412cf2e6711ef1ccf6418"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/86daa943fbb765aa0129d16f84c5bf7aaec44582",
"reference": "86daa943fbb765aa0129d16f84c5bf7aaec44582",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/437553634c79817d89b412cf2e6711ef1ccf6418",
"reference": "437553634c79817d89b412cf2e6711ef1ccf6418",
"shasum": ""
},
"require": {
@ -2949,7 +2916,7 @@
"type": "github"
}
],
"time": "2020-10-26T04:57:30+00:00"
"time": "2020-10-30T14:12:04+00:00"
},
{
"name": "phpunit/php-invoker",
@ -2957,12 +2924,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-invoker.git",
"reference": "dd5300fef2ede06687642585706f912c073a0cc5"
"reference": "27560ee0808c5ba9144ad228887e788c608c8f58"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/dd5300fef2ede06687642585706f912c073a0cc5",
"reference": "dd5300fef2ede06687642585706f912c073a0cc5",
"url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/27560ee0808c5ba9144ad228887e788c608c8f58",
"reference": "27560ee0808c5ba9144ad228887e788c608c8f58",
"shasum": ""
},
"require": {
@ -3008,7 +2975,7 @@
"type": "github"
}
],
"time": "2020-10-26T04:57:38+00:00"
"time": "2020-10-30T14:12:14+00:00"
},
{
"name": "phpunit/php-text-template",
@ -3016,12 +2983,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "60c51e16ad53fc17844f6fd6e608e80d9743f320"
"reference": "2d6aa395e3f5a69ba3a5449d090cf7b845552081"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/60c51e16ad53fc17844f6fd6e608e80d9743f320",
"reference": "60c51e16ad53fc17844f6fd6e608e80d9743f320",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/2d6aa395e3f5a69ba3a5449d090cf7b845552081",
"reference": "2d6aa395e3f5a69ba3a5449d090cf7b845552081",
"shasum": ""
},
"require": {
@ -3063,7 +3030,7 @@
"type": "github"
}
],
"time": "2020-10-26T05:38:01+00:00"
"time": "2020-10-30T14:12:53+00:00"
},
{
"name": "phpunit/php-timer",
@ -3071,12 +3038,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
"reference": "735b06b2ce91f813d4574d0671a4e8165c8ece9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
"reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/735b06b2ce91f813d4574d0671a4e8165c8ece9c",
"reference": "735b06b2ce91f813d4574d0671a4e8165c8ece9c",
"shasum": ""
},
"require": {
@ -3118,7 +3085,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:16:10+00:00"
"time": "2020-10-30T14:12:23+00:00"
},
{
"name": "phpunit/phpunit",
@ -3126,12 +3093,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "d3b55c36f95329c062e69f6c10441106cf712f7f"
"reference": "abbb373dfdc7be1c77a1a700da67f28a7013d2e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d3b55c36f95329c062e69f6c10441106cf712f7f",
"reference": "d3b55c36f95329c062e69f6c10441106cf712f7f",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/abbb373dfdc7be1c77a1a700da67f28a7013d2e7",
"reference": "abbb373dfdc7be1c77a1a700da67f28a7013d2e7",
"shasum": ""
},
"require": {
@ -3147,7 +3114,7 @@
"phar-io/version": "^3.0.2",
"php": ">=7.3",
"phpspec/prophecy": "^1.12.1",
"phpunit/php-code-coverage": "^9.2",
"phpunit/php-code-coverage": "^9.2.3",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
@ -3217,7 +3184,7 @@
"type": "github"
}
],
"time": "2020-10-26T06:27:31+00:00"
"time": "2020-10-30T14:20:26+00:00"
},
{
"name": "psr/container",
@ -3274,12 +3241,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
"reference": "bb3529e836d10bd4d2713ae050a2c5251eb9ff3e"
"reference": "a8a95d7ddcf625e12c9eca502af7a2f72d6e3e7a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/bb3529e836d10bd4d2713ae050a2c5251eb9ff3e",
"reference": "bb3529e836d10bd4d2713ae050a2c5251eb9ff3e",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/a8a95d7ddcf625e12c9eca502af7a2f72d6e3e7a",
"reference": "a8a95d7ddcf625e12c9eca502af7a2f72d6e3e7a",
"shasum": ""
},
"require": {
@ -3318,7 +3285,7 @@
"type": "github"
}
],
"time": "2020-10-26T04:58:42+00:00"
"time": "2020-10-30T14:13:23+00:00"
},
{
"name": "sebastian/code-unit",
@ -3326,12 +3293,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit.git",
"reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
"reference": "97d3d265ae9e0692bc19553d0a28d2e99c6a13dc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
"reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/97d3d265ae9e0692bc19553d0a28d2e99c6a13dc",
"reference": "97d3d265ae9e0692bc19553d0a28d2e99c6a13dc",
"shasum": ""
},
"require": {
@ -3370,7 +3337,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:08:54+00:00"
"time": "2020-10-30T14:10:33+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -3378,12 +3345,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
"reference": "aed227b805d6b8d279d05cee266b46f5512b8ea4"
"reference": "b7b8338be2131558c2eaeb0ec0b62ee3885181a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/aed227b805d6b8d279d05cee266b46f5512b8ea4",
"reference": "aed227b805d6b8d279d05cee266b46f5512b8ea4",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/b7b8338be2131558c2eaeb0ec0b62ee3885181a8",
"reference": "b7b8338be2131558c2eaeb0ec0b62ee3885181a8",
"shasum": ""
},
"require": {
@ -3421,7 +3388,7 @@
"type": "github"
}
],
"time": "2020-10-26T04:56:19+00:00"
"time": "2020-10-30T14:10:42+00:00"
},
{
"name": "sebastian/comparator",
@ -3429,12 +3396,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "55f4261989e546dc112258c7a75935a81a7ce382"
"reference": "237324bd32872e15f51bc2eadac2a9e43e28409d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382",
"reference": "55f4261989e546dc112258c7a75935a81a7ce382",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/237324bd32872e15f51bc2eadac2a9e43e28409d",
"reference": "237324bd32872e15f51bc2eadac2a9e43e28409d",
"shasum": ""
},
"require": {
@ -3491,7 +3458,7 @@
"type": "github"
}
],
"time": "2020-10-26T15:49:45+00:00"
"time": "2020-10-30T14:10:53+00:00"
},
{
"name": "sebastian/complexity",
@ -3499,12 +3466,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
"reference": "3bd69c610bb8e2958f3289d1a9583514471b62e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/3bd69c610bb8e2958f3289d1a9583514471b62e6",
"reference": "3bd69c610bb8e2958f3289d1a9583514471b62e6",
"shasum": ""
},
"require": {
@ -3544,7 +3511,7 @@
"type": "github"
}
],
"time": "2020-10-26T15:52:27+00:00"
"time": "2020-10-30T14:13:03+00:00"
},
{
"name": "sebastian/diff",
@ -3552,12 +3519,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
"reference": "7b02596262b190492663ff4df9227100f70c7823"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7b02596262b190492663ff4df9227100f70c7823",
"reference": "7b02596262b190492663ff4df9227100f70c7823",
"shasum": ""
},
"require": {
@ -3606,7 +3573,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:10:38+00:00"
"time": "2020-10-30T14:11:04+00:00"
},
{
"name": "sebastian/environment",
@ -3614,12 +3581,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "621594614bd6158ea0c1d6af09c6e5af01d8b1e8"
"reference": "7a613938b2aa9c873233f810e6300bf06c42ca17"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/621594614bd6158ea0c1d6af09c6e5af01d8b1e8",
"reference": "621594614bd6158ea0c1d6af09c6e5af01d8b1e8",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/7a613938b2aa9c873233f810e6300bf06c42ca17",
"reference": "7a613938b2aa9c873233f810e6300bf06c42ca17",
"shasum": ""
},
"require": {
@ -3665,7 +3632,7 @@
"type": "github"
}
],
"time": "2020-10-26T04:56:46+00:00"
"time": "2020-10-30T14:11:15+00:00"
},
{
"name": "sebastian/exporter",
@ -3673,12 +3640,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "1c85999374d0f9e8c7c11ff1e93d0c75343eba8e"
"reference": "33fcbffef7131af7ee4ad12f52bf6a3d02124557"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1c85999374d0f9e8c7c11ff1e93d0c75343eba8e",
"reference": "1c85999374d0f9e8c7c11ff1e93d0c75343eba8e",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/33fcbffef7131af7ee4ad12f52bf6a3d02124557",
"reference": "33fcbffef7131af7ee4ad12f52bf6a3d02124557",
"shasum": ""
},
"require": {
@ -3738,7 +3705,7 @@
"type": "github"
}
],
"time": "2020-10-26T04:56:54+00:00"
"time": "2020-10-30T14:11:24+00:00"
},
{
"name": "sebastian/global-state",
@ -3746,12 +3713,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "a90ccbddffa067b51f574dea6eb25d5680839455"
"reference": "e9696869db3ba851de7221f4d6997b4262b623d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455",
"reference": "a90ccbddffa067b51f574dea6eb25d5680839455",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e9696869db3ba851de7221f4d6997b4262b623d9",
"reference": "e9696869db3ba851de7221f4d6997b4262b623d9",
"shasum": ""
},
"require": {
@ -3798,7 +3765,7 @@
"type": "github"
}
],
"time": "2020-10-26T15:55:19+00:00"
"time": "2020-10-30T14:11:34+00:00"
},
{
"name": "sebastian/lines-of-code",
@ -3806,12 +3773,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "acf76492a65401babcf5283296fa510782783a7a"
"reference": "edc91041d25e5f667f5adf1deb0f96a11153575e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/acf76492a65401babcf5283296fa510782783a7a",
"reference": "acf76492a65401babcf5283296fa510782783a7a",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/edc91041d25e5f667f5adf1deb0f96a11153575e",
"reference": "edc91041d25e5f667f5adf1deb0f96a11153575e",
"shasum": ""
},
"require": {
@ -3851,7 +3818,7 @@
"type": "github"
}
],
"time": "2020-10-26T17:03:56+00:00"
"time": "2020-10-30T14:13:13+00:00"
},
{
"name": "sebastian/object-enumerator",
@ -3859,12 +3826,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
"reference": "d913c474aa28f6646d5a899d18b546a9d8a94134"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
"reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d913c474aa28f6646d5a899d18b546a9d8a94134",
"reference": "d913c474aa28f6646d5a899d18b546a9d8a94134",
"shasum": ""
},
"require": {
@ -3904,7 +3871,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:12:34+00:00"
"time": "2020-10-30T14:11:44+00:00"
},
{
"name": "sebastian/object-reflector",
@ -3912,12 +3879,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
"reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
"reference": "4acdc9cb8f4eae4da631c414ece1fff21d3578b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
"reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4acdc9cb8f4eae4da631c414ece1fff21d3578b0",
"reference": "4acdc9cb8f4eae4da631c414ece1fff21d3578b0",
"shasum": ""
},
"require": {
@ -3955,7 +3922,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:14:26+00:00"
"time": "2020-10-30T14:11:55+00:00"
},
{
"name": "sebastian/recursion-context",
@ -3963,12 +3930,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "be9bb5ea038598dd67ac9d40c5c37205d73deff9"
"reference": "d09a5073917f11f0b1e2dce2443d91acb50d0671"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/be9bb5ea038598dd67ac9d40c5c37205d73deff9",
"reference": "be9bb5ea038598dd67ac9d40c5c37205d73deff9",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/d09a5073917f11f0b1e2dce2443d91acb50d0671",
"reference": "d09a5073917f11f0b1e2dce2443d91acb50d0671",
"shasum": ""
},
"require": {
@ -4014,7 +3981,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:20:23+00:00"
"time": "2020-10-30T14:12:33+00:00"
},
{
"name": "sebastian/resource-operations",
@ -4073,12 +4040,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
"reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2"
"reference": "d6eb978632b7b77cad85a9a655e5c3dbb9c7100c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
"reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/d6eb978632b7b77cad85a9a655e5c3dbb9c7100c",
"reference": "d6eb978632b7b77cad85a9a655e5c3dbb9c7100c",
"shasum": ""
},
"require": {
@ -4117,7 +4084,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:18:59+00:00"
"time": "2020-10-30T14:12:43+00:00"
},
{
"name": "sebastian/version",
@ -4170,16 +4137,16 @@
},
{
"name": "swoole/ide-helper",
"version": "4.5.4",
"version": "4.5.5",
"source": {
"type": "git",
"url": "https://github.com/swoole/ide-helper.git",
"reference": "3382a1844afb206cac064252f6b8b50115bf72bb"
"reference": "aefd9d15e00cf14b89a5ed87cfa3bd79c9889028"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/3382a1844afb206cac064252f6b8b50115bf72bb",
"reference": "3382a1844afb206cac064252f6b8b50115bf72bb",
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/aefd9d15e00cf14b89a5ed87cfa3bd79c9889028",
"reference": "aefd9d15e00cf14b89a5ed87cfa3bd79c9889028",
"shasum": ""
},
"require-dev": {
@ -4200,7 +4167,7 @@
}
],
"description": "IDE help files for Swoole.",
"time": "2020-09-16T00:12:52+00:00"
"time": "2020-10-14T18:05:12+00:00"
},
{
"name": "symfony/console",
@ -4208,12 +4175,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "4f00061f0fe49fb7c836fc31d66f365d8bff32fa"
"reference": "f372360d0dfa520b23b91fc70e2bba6a232ac62d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/4f00061f0fe49fb7c836fc31d66f365d8bff32fa",
"reference": "4f00061f0fe49fb7c836fc31d66f365d8bff32fa",
"url": "https://api.github.com/repos/symfony/console/zipball/f372360d0dfa520b23b91fc70e2bba6a232ac62d",
"reference": "f372360d0dfa520b23b91fc70e2bba6a232ac62d",
"shasum": ""
},
"require": {
@ -4294,7 +4261,7 @@
"type": "tidelift"
}
],
"time": "2020-10-24T12:08:07+00:00"
"time": "2020-10-29T07:35:47+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -4973,12 +4940,12 @@
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "78173b3c850e344cb8515fc2a05138d39a6c39e0"
"reference": "b163c7f71698165ccf13738ac77273b40d5776f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/78173b3c850e344cb8515fc2a05138d39a6c39e0",
"reference": "78173b3c850e344cb8515fc2a05138d39a6c39e0",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/b163c7f71698165ccf13738ac77273b40d5776f8",
"reference": "b163c7f71698165ccf13738ac77273b40d5776f8",
"shasum": ""
},
"require": {
@ -5040,7 +5007,7 @@
"type": "tidelift"
}
],
"time": "2020-10-21T12:45:52+00:00"
"time": "2020-10-27T19:26:34+00:00"
},
{
"name": "vimeo/psalm",

View file

@ -88,7 +88,7 @@ services:
- _APP_SMTP_PORT=25
mariadb:
image: appwrite/mariadb:1.0.3 # fix issues when upgrading using: mysql_upgrade -u root -p
image: appwrite/mariadb:1.1.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:

View file

@ -185,6 +185,7 @@ services:
depends_on:
- redis
- mariadb
- request-catcher
environment:
- _APP_ENV
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
@ -334,7 +335,7 @@ services:
- _APP_REDIS_PORT
mariadb:
image: appwrite/mariadb:1.0.3 # fix issues when upgrading using: mysql_upgrade -u root -p
image: appwrite/mariadb:1.1.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:
@ -348,10 +349,10 @@ services:
- MYSQL_DATABASE=${_APP_DB_SCHEMA}
- MYSQL_USER=${_APP_DB_USER}
- MYSQL_PASSWORD=${_APP_DB_PASS}
command: 'mysqld --innodb-flush-method=fsync'
command: 'mysqld --innodb-flush-method=fsync' # add ' --query_cache_size=0' for DB tests
# command: mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/ib_logfile0.bu && mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/ib_logfile1.bu
maildev:
maildev: # used mainly for dev tests
image: djfarrelly/maildev
container_name: appwrite-maildev
restart: unless-stopped
@ -360,6 +361,15 @@ services:
networks:
- appwrite
request-catcher: # used mainly for dev tests
image: smarterdm/http-request-catcher
container_name: appwrite-request-catcher
restart: unless-stopped
ports:
- '5000:5000'
networks:
- appwrite
# smtp:
# image: appwrite/smtp:1.2.0
# container_name: appwrite-smtp

View file

@ -55,6 +55,7 @@ const configApp = {
'public/scripts/views/forms/pell.js',
'public/scripts/views/forms/remove.js',
'public/scripts/views/forms/run.js',
'public/scripts/views/forms/select-all.js',
'public/scripts/views/forms/switch.js',
'public/scripts/views/forms/tags.js',
'public/scripts/views/forms/text-count.js',

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,14 @@ window.ls.router
template: "/auth/join?version=" + APP_ENV.CACHEBUSTER,
scope: "home"
})
.add("/auth/oauth2/success", {
template: "/auth/oauth2/success?version=" + APP_ENV.CACHEBUSTER,
scope: "home"
})
.add("/auth/oauth2/failure", {
template: "/auth/oauth2/failure?version=" + APP_ENV.CACHEBUSTER,
scope: "home"
})
.add("/console", {
template: "/console?version=" + APP_ENV.CACHEBUSTER,
scope: "console"

View file

@ -4,87 +4,110 @@
window.ls.container.get("view").add({
selector: "data-forms-chart",
controller: function(element, container, date, document) {
let wrapper = document.createElement("div");
let child = document.createElement("canvas");
let sources = element.getAttribute('data-forms-chart');
let width = element.getAttribute('data-width') || 500;
let height = element.getAttribute('data-height') || 175;
let colors = ['#29b5d9' /* blue */, '#4eb55b' /* green */, '#fba233', /* orange */,];
let colors = (element.getAttribute('data-colors') || 'blue,green,orange,red').split(',');
let themes = {'blue': '#29b5d9', 'green': '#4eb55b', 'orange': '#fba233', 'red': '#dc3232',};
let range = {'24h': 'H:i', '7d': 'd F Y', '30d': 'd F Y', '90d': 'd F Y'}
element.parentNode.insertBefore(wrapper, element.nextSibling);
wrapper.classList.add('content');
child.width = width;
child.height = height;
let config = {
type: "line",
data: {
labels: [],
datasets: []
},
options: {
responsive: true,
title: {
display: false,
text: "Stats"
},
legend: {
display: false
},
tooltips: {
mode: "index",
intersect: false,
caretPadding: 0
},
hover: {
mode: "nearest",
intersect: true
},
scales: {
xAxes: [
{
display: false
}
],
yAxes: [
{
display: false
}
]
}
}
};
sources = sources.split(',');
for (let i = 0; i < sources.length; i++) {
let label = sources[i].substring(0, sources[i].indexOf('='));
let path = sources[i].substring(sources[i].indexOf('=') + 1);
let data = container.path(path);
wrapper.appendChild(child);
config.data.labels[i] = label;
config.data.datasets[i] = {};
config.data.datasets[i].label = label;
config.data.datasets[i].borderColor = colors[i];
config.data.datasets[i].backgroundColor = colors[i] + '36';
config.data.datasets[i].borderWidth = 2;
config.data.datasets[i].data = [0, 0, 0, 0, 0, 0, 0];
config.data.datasets[i].fill = true;
let chart = null;
if(!data) {
return;
let check = function() {
let config = {
type: "line",
data: {
labels: [],
datasets: []
},
options: {
responsive: true,
title: {
display: false,
text: "Stats"
},
legend: {
display: false
},
tooltips: {
mode: "index",
intersect: false,
caretPadding: 0
},
hover: {
mode: "nearest",
intersect: true
},
scales: {
xAxes: [
{
display: false
}
],
yAxes: [
{
display: false
}
]
}
}
};
for (let i = 0; i < sources.length; i++) {
let label = sources[i].substring(0, sources[i].indexOf('='));
let path = sources[i].substring(sources[i].indexOf('=') + 1);
let data = container.path(path);
let value = JSON.parse(element.value);
config.data.labels[i] = label;
config.data.datasets[i] = {};
config.data.datasets[i].label = label;
config.data.datasets[i].borderColor = themes[colors[i]];
config.data.datasets[i].backgroundColor = themes[colors[i]] + '36';
config.data.datasets[i].borderWidth = 2;
config.data.datasets[i].data = [0, 0, 0, 0, 0, 0, 0];
config.data.datasets[i].fill = true;
if(!data) {
return;
}
let dateFormat = (value.range && range[value.range]) ? range[value.range] : 'd F Y';
for (let x = 0; x < data.length; x++) {
config.data.datasets[i].data[x] = data[x].value;
config.data.labels[x] = date.format(dateFormat, data[x].date);
}
}
for (let x = 0; x < data.length; x++) {
config.data.datasets[i].data[x] = data[x].value;
config.data.labels[x] = date.format("d F Y", data[x].date);
if(chart) {
chart.destroy();
}
else {
}
chart = new Chart(child.getContext("2d"), config);
wrapper.dataset["canvas"] = true;
}
element.innerHTML = "";
check();
element.appendChild(child);
container.set("chart", new Chart(child.getContext("2d"), config), true);
element.dataset["canvas"] = true;
element.addEventListener('change', check);
}
});
})(window);

View file

@ -0,0 +1,51 @@
(function(window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-forms-select-all",
controller: function(element) {
let select = document.createElement("button");
let unselect = document.createElement("button");
select.textContent = 'Select All';
unselect.textContent = 'Unselect All';
select.classList.add('link');
select.classList.add('margin-top-tiny');
select.classList.add('margin-start-small');
select.classList.add('text-size-small');
select.classList.add('pull-end');
unselect.classList.add('link');
unselect.classList.add('margin-top-tiny');
unselect.classList.add('margin-start-small');
unselect.classList.add('text-size-small');
unselect.classList.add('pull-end');
// select.disabled = true;
// unselect.disabled = true;
select.type = 'button';
unselect.type = 'button';
element.parentNode.insertBefore(select, element);
element.parentNode.insertBefore(unselect, element);
select.addEventListener('click', function () {
let checkboxes = document.querySelectorAll("input[type='checkbox']");
for(var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = true;
}
})
unselect.addEventListener('click', function () {
let checkboxes = document.querySelectorAll("input[type='checkbox']");
for(var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = false;
}
})
}
});
})(window);

View file

@ -4,10 +4,10 @@
visibility: hidden;
position: fixed;
padding: 0;
right: 0;
left: 0;
.func-end(0);
.func-start(0);
color: var(--config-color-normal);
z-index: 1002;
z-index: 4;
margin: 0 auto;
bottom: 15px;
max-width: 560px;
@ -34,42 +34,66 @@
font-weight: 600;
}
a {
border-bottom: dotted 1px var(--config-color-normal);
}
i {
cursor: pointer;
position: absolute;
font-size: 14px;
line-height: 22px;
top: 8px;
.func-start(8px);
line-height: 20px;
top: 9px;
.func-start(9px);
color: var(--config-color-background-dark);
background: var(--config-color-normal);
width: 22px;
height: 22px;
border-radius: 50%;
}
&.error {
color: #ffffff;
background: var(--config-color-danger);
color: #ffffff!important;
background: var(--config-color-danger)!important;
a {
color: #ffffff;
border-bottom: dotted 1px #ffffff;
color: #ffffff!important;
border-bottom: dotted 1px #ffffff!important;
}
i {
color: var(--config-color-danger);
background: #ffffff;
}
}
&.success {
color: #ffffff;
background: var(--config-color-success);
color: #ffffff!important;
background: var(--config-color-success)!important;
a {
color: #ffffff;
border-bottom: dotted 1px #ffffff;
}
i {
color: var(--config-color-success);
background: #ffffff;
}
}
&.warning {
color: #ffffff;
background: var(--config-color-success);
color: var(--config-color-normal)!important;
background: var(--config-color-warning)!important;
a {
color: var(--config-color-normal)!important;
border-bottom: dotted 1px var(--config-color-normal)!important;
}
i {
color: #ffffff;
border-bottom: dotted 1px #ffffff;
background: var(--config-color-normal)!important;
}
}
@ -91,7 +115,12 @@
a {
color: var(--config-color-focus);
font-weight: 400;
border-bottom: dotted 1px var(--config-color-focus);
border-bottom: dotted 1px var(--config-color-focus)!important;
}
i {
color: var(--config-color-focus-fade)!important;
background: var(--config-color-focus)!important;
}
}
@ -100,6 +129,7 @@
top: auto;
bottom: 0;
max-width: 100%;
.func-start(0);
li {
margin: 5px 0 0 0;
@ -110,4 +140,18 @@
}
}
}
}
.show-nav {
.alerts {
ul {
.func-start(220px);
}
@media @phones, @tablets {
ul {
.func-start(0);
}
}
}
}

View file

@ -11,8 +11,8 @@
top: 0;
bottom: 0;
background: #0c0c0c;
opacity: 0.5;
z-index: 4;
opacity: 0.75;
z-index: 5;
}
}

View file

@ -13,6 +13,12 @@ input:-moz-placeholder {
text-align: @config-start;
}
form {
&.inline {
display: inline-block;
}
}
input, textarea {
background: var(--config-color-background-input);
}
@ -98,6 +104,22 @@ button,
font-size: 13px;
}
&.tick {
background: var(--config-color-fade-light);
color: var(--config-color-dark);
border-radius: 20px;
padding: 0 10px;
line-height: 30px;
height: 30px;
font-size: 12px;
display: inline-block;
&.selected {
background: var(--config-color-dark);
color: var(--config-color-fade);
}
}
&.round {
width: 52px;
padding: 0;

View file

@ -143,6 +143,10 @@
margin-top: -100px!important;
}
.margin-top-negative-xxxl {
margin-top: -150px!important;
}
.margin-bottom-xxl {
margin-bottom: 140px!important;
}

View file

@ -408,10 +408,10 @@
.dashboard {
padding: 20px;
min-height: 95px;
overflow: hidden;
position: relative;
z-index: 1;
margin-bottom: 2px;
.chart {
width: 80%;
@ -422,7 +422,7 @@
}
hr {
margin: 20px -20px;
margin: 20px -25px;
height: 2px;
background: var(--config-console-background);
@ -452,7 +452,6 @@
display: block;
width: 2px;
background: var(--config-console-background);
height: ~"calc(100% + 110px)";
position: absolute;
top: -20px;
bottom: -20px;
@ -473,11 +472,20 @@
vertical-align: bottom;
line-height: 45px;
&.small {
line-height: 35px;
}
.sum {
font-size: 45px;
line-height: 45px;
font-weight: 700;
vertical-align: bottom;
&.small {
font-size: 25px;
line-height: 25px;
}
}
}
@ -578,7 +586,7 @@
vertical-align: middle;
}
&:nth-child(1) {
&:nth-child(1), &.blue {
color: #29b5d9;
&::before {
@ -586,7 +594,7 @@
}
}
&:nth-child(2) {
&:nth-child(2), &.green {
color: #4eb55b;
&::before {
@ -594,13 +602,21 @@
}
}
&:nth-child(3) {
&:nth-child(3), &.orange {
color: #ec9323;
&::before {
background: #ec9323;
}
}
&:nth-child(4), &.red {
color: #dc3232;
&::before {
background: #dc3232;
}
}
}
}

View file

@ -9,9 +9,14 @@ use Appwrite\Auth\OAuth2;
class Box extends OAuth2
{
/**
* @var string
*/
private $endpoint = 'https://account.box.com/api/oauth2/';
/**
* @var string
*/
private $resourceEndpoint = 'https://api.box.com/2.0/';
/**
@ -19,6 +24,9 @@ class Box extends OAuth2
*/
protected $user = [];
/**
* @var array
*/
protected $scopes = [
'manage_app_users',
];
@ -148,5 +156,4 @@ class Box extends OAuth2
return $this->user;
}
}

View file

@ -125,7 +125,7 @@ class Github extends OAuth2
* @return array
*/
protected function getUser(string $accessToken)
{
{
if (empty($this->user)) {
$this->user = \json_decode($this->request('GET', 'https://api.github.com/user', ['Authorization: token '.\urlencode($accessToken)]), true);
}

View file

@ -232,7 +232,7 @@ class MySQL extends Adapter
// Handle array of relations
if (self::DATA_TYPE_ARRAY === $type) {
if(!is_array($value)) { // Property should be of type array, if not = skip
if (!is_array($value)) { // Property should be of type array, if not = skip
continue;
}
@ -459,6 +459,7 @@ class MySQL extends Adapter
throw new Exception('Empty namespace');
}
$unique = 'app_'.$namespace.'.database.unique';
$documents = 'app_'.$namespace.'.database.documents';
$properties = 'app_'.$namespace.'.database.properties';
$relationships = 'app_'.$namespace.'.database.relationships';
@ -466,6 +467,7 @@ class MySQL extends Adapter
$abuse = 'app_'.$namespace.'.abuse.abuse';
try {
$this->getPDO()->prepare('DROP TABLE `'.$unique.'`;')->execute();
$this->getPDO()->prepare('DROP TABLE `'.$documents.'`;')->execute();
$this->getPDO()->prepare('DROP TABLE `'.$properties.'`;')->execute();
$this->getPDO()->prepare('DROP TABLE `'.$relationships.'`;')->execute();

View file

@ -158,7 +158,7 @@ class Database
$results = $this->adapter->getCollection($options);
foreach ($results as &$node) {
$node = new Document($node);
$node = $this->decode(new Document($node));
}
return $results;
@ -317,13 +317,19 @@ class Database
throw new AuthorizationException($validator->getDescription()); // var_dump($validator->getDescription()); return false;
}
$new = $this->encode($new);
$validator = new Structure($this);
if (!$validator->isValid($new)) { // Make sure updated structure still apply collection rules (if any)
throw new StructureException($validator->getDescription()); // var_dump($validator->getDescription()); return false;
}
return new Document($this->adapter->updateDocument($data));
$new = new Document($this->adapter->updateDocument($new->getArrayCopy()));
$new = $this->decode($new);
return $new;
}
/**
@ -452,7 +458,7 @@ class Database
$filters = $rule->getAttribute('filter', null);
$value = $document->getAttribute($key, null);
if(($value !== null) && is_array($filters)) {
if (($value !== null) && is_array($filters)) {
foreach ($filters as $filter) {
$value = $this->encodeAttribute($filter, $value);
$document->setAttribute($key, $value);
@ -473,7 +479,7 @@ class Database
$filters = $rule->getAttribute('filter', null);
$value = $document->getAttribute($key, null);
if(($value !== null) && is_array($filters)) {
if (($value !== null) && is_array($filters)) {
foreach (array_reverse($filters) as $filter) {
$value = $this->decodeAttribute($filter, $value);
$document->setAttribute($key, $value);
@ -492,7 +498,7 @@ class Database
*/
static protected function encodeAttribute(string $name, $value)
{
if(!isset(self::$filters[$name])) {
if (!isset(self::$filters[$name])) {
throw new Exception('Filter not found');
}
@ -513,7 +519,7 @@ class Database
*/
static protected function decodeAttribute(string $name, $value)
{
if(!isset(self::$filters[$name])) {
if (!isset(self::$filters[$name])) {
throw new Exception('Filter not found');
}

View file

@ -219,7 +219,7 @@ class Document extends ArrayObject
{
$array = parent::getArrayCopy();
$output = array();
$output = [];
foreach ($array as $key => &$value) {
if (!empty($whitelist) && !\in_array($key, $whitelist)) { // Export only whitelisted fields

View file

@ -157,7 +157,7 @@ class Structure extends Validator
foreach ($array as $key => $value) {
$rule = $collection->search('key', $key, $rules);
if(!$rule) {
if (!$rule) {
continue;
}

View file

@ -37,7 +37,7 @@ class UID extends Validator
return false;
}
if(mb_strlen($value) > 32) {
if (mb_strlen($value) > 32) {
return false;
}

View file

@ -48,7 +48,7 @@ class Compose
*/
public function getService(string $name): Service
{
if(!isset($this->compose['services'][$name])) {
if (!isset($this->compose['services'][$name])) {
throw new Exception('Service not found');
}

View file

@ -16,15 +16,14 @@ class Env
*/
public function __construct(string $data)
{
$data = explode("\n", $data);
foreach($data as &$row) {
foreach ($data as &$row) {
$row = explode('=', $row);
$key = (isset($row[0])) ? trim($row[0]) : null;
$value = (isset($row[1])) ? trim($row[1]) : null;
if($key) {
if ($key) {
$this->vars[$key] = $value;
}
}

View file

@ -73,15 +73,15 @@ class PDOStatement extends PDOStatementNative
$this->pdo = $this->pdo->reconnect();
$this->PDOStatement = $this->pdo->prepare($this->PDOStatement->queryString, []);
foreach($this->values as $key => $set) {
foreach ($this->values as $key => $set) {
$this->PDOStatement->bindValue($key, $set['value'], $set['data_type']);
}
foreach($this->params as $key => $set) {
foreach ($this->params as $key => $set) {
$this->PDOStatement->bindParam($key, $set['variable'], $set['data_type'], $set['length'], $set['driver_options']);
}
foreach($this->columns as $key => $set) {
foreach ($this->columns as $key => $set) {
$this->PDOStatement->bindColumn($key, $set['param'], $set['type'], $set['maxlen'], $set['driverdata']);
}

View file

@ -36,7 +36,7 @@ class CNAME extends Validator
*/
public function isValid($domain)
{
if(!is_string($domain)) {
if (!is_string($domain)) {
return false;
}

View file

@ -37,11 +37,11 @@ class Domain extends Validator
*/
public function isValid($value)
{
if(empty($value)) {
if (empty($value)) {
return false;
}
if(!is_string($value)) {
if (!is_string($value)) {
return false;
}

View file

@ -99,7 +99,7 @@ class Origin extends Validator
*/
public function isValid($origin)
{
if(!is_string($origin)) {
if (!is_string($origin)) {
return false;
}

View file

@ -13,7 +13,7 @@ class Storage
*
* @var array
*/
public static $devices = array();
public static $devices = [];
/**
* Set Device.

View file

@ -24,7 +24,7 @@ class FileName extends Validator
return false;
}
if(!is_string($name)) {
if (!is_string($name)) {
return false;
}

View file

@ -35,7 +35,7 @@ class FileSize extends Validator
*/
public function isValid($fileSize)
{
if(!is_int($fileSize)) {
if (!is_int($fileSize)) {
return false;
}

View file

@ -20,7 +20,7 @@ class Upload extends Validator
*/
public function isValid($path)
{
if(!is_string($path)) {
if (!is_string($path)) {
return false;
}

View file

@ -1,176 +0,0 @@
<?php
namespace Appwrite\Swoole;
use Exception;
class Files
{
/**
* @var array
*/
static protected $loaded = [];
/**
* @var int
*/
static protected $count = 0;
/**
* @var array
*/
static protected $mimeTypes = [];
/**
* @var array
*/
static protected $extensions = [
'css' => 'text/css',
'js' => 'text/javascript',
'svg' => 'image/svg+xml',
];
/**
* Add MimeType
*
* @var string $mimeType
*
* @return void
*/
public static function addMimeType(string $mimeType): void
{
self::$mimeTypes[$mimeType] = true;
}
/**
* Remove MimeType
*
* @var string $mimeType
*
* @return void
*/
public static function removeMimeType(string $mimeType): void
{
if(isset(self::$mimeTypes[$mimeType])) {
unset(self::$mimeTypes[$mimeType]);
}
}
/**
* Get MimeType List
*
* @return array
*/
public static function getMimeTypes(): array
{
return self::$mimeTypes;
}
/**
* Get Files Loaded Count
*
* @return int
*/
public static function getCount(): int
{
return self::$count;
}
/**
* Load
*
* @var string $path
*
* @return void
*/
public static function load(string $directory, string $root = null): void
{
if(!is_readable($directory)) {
throw new Exception('Failed to load directory: '.$directory);
}
$directory = realpath($directory);
$root = ($root) ? $root : $directory;
$handle = opendir($directory);
while ($path = readdir($handle)) {
$extension = pathinfo($path, PATHINFO_EXTENSION);
if (in_array($path, ['.', '..'])) {
continue;
}
if (in_array($extension, ['php', 'phtml'])) {
continue;
}
if(substr($path, 0, 1) === '.') {
continue;
}
if (is_dir($directory.'/'.$path)) {
self::load($directory.'/'.$path, $root);
continue;
}
self::$count++;
self::$loaded[substr($directory.'/'.$path , strlen($root))] = [
'contents' => file_get_contents($directory.'/'.$path),
'mimeType' => (array_key_exists($extension, self::$extensions))
? self::$extensions[$extension]
: mime_content_type($directory.'/'.$path)
];
}
closedir($handle);
if($directory === $root) {
echo '[Static Files] Loadded '.self::$count.' files'.PHP_EOL;
}
}
/**
* Is File Loaded
*
* @var string $uri
*/
public static function isFileLoaded(string $uri): bool
{
if(!array_key_exists($uri, self::$loaded)) {
return false;
}
return true;
}
/**
* Get File Contants
*
* @var string $uri
*/
public static function getFileContents(string $uri): string
{
if(!array_key_exists($uri, self::$loaded)) {
throw new Exception('File not found or not loaded: '.$uri);
}
return self::$loaded[$uri]['contents'];
}
/**
* Get File MimeType
*
* @var string $uri
*/
public static function getFileMimeType(string $uri): string
{
if(!array_key_exists($uri, self::$loaded)) {
throw new Exception('File not found or not loaded: '.$uri);
}
return self::$loaded[$uri]['mimeType'];
}
}

View file

@ -1,339 +0,0 @@
<?php
namespace Appwrite\Swoole;
use Utopia\Request as UtopiaRequest;
use Swoole\Http\Request as SwooleRequest;
class Request extends UtopiaRequest
{
/**
* Swoole Request Object
*
* @var SwooleRequest
*/
protected $swoole;
/**
* Request constructor.
*/
public function __construct(SwooleRequest $request)
{
$this->swoole = $request;
}
/**
* Get Param
*
* Get param by current method name
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function getParam(string $key, $default = null)
{
switch($this->getMethod()) {
case self::METHOD_GET:
return $this->getQuery($key, $default);
break;
case self::METHOD_POST:
case self::METHOD_PUT:
case self::METHOD_PATCH:
case self::METHOD_DELETE:
return $this->getPayload($key, $default);
break;
default:
return $this->getQuery($key, $default);
}
}
/**
* Get Params
*
* Get all params of current method
*
* @return array
*/
public function getParams(): array
{
switch($this->getMethod()) {
case self::METHOD_GET:
return (!empty($this->swoole->get)) ? $this->swoole->get : [];
break;
case self::METHOD_POST:
case self::METHOD_PUT:
case self::METHOD_PATCH:
case self::METHOD_DELETE:
return $this->generateInput();
break;
default:
return (!empty($this->swoole->get)) ? $this->swoole->get : [];
}
return [];
}
/**
* Get Query
*
* Method for querying HTTP GET request parameters. If $key is not found $default value will be returned.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function getQuery(string $key, $default = null)
{
return (isset($this->swoole->get[$key])) ? $this->swoole->get[$key] : $default;
}
/**
* Get payload
*
* Method for querying HTTP request payload parameters. If $key is not found $default value will be returned.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function getPayload(string $key, $default = null)
{
$payload = $this->generateInput();
return (isset($payload[$key])) ? $payload[$key] : $default;
}
/**
* Get server
*
* Method for querying server parameters. If $key is not found $default value will be returned.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function getServer(string $key, $default = null)
{
return (isset($this->swoole->server) && isset($this->swoole->server[$key])) ? $this->swoole->server[$key] : $default;
}
/**
* Get IP
*
* Returns users IP address.
* Support HTTP_X_FORWARDED_FOR header usually return
* from different proxy servers or PHP default REMOTE_ADDR
*/
public function getIP(): string
{
return $this->getHeader('x-forwarded-for', $this->getServer('remote_addr', '0.0.0.0'));
}
/**
* Get Protocol
*
* Returns request protocol.
* Support HTTP_X_FORWARDED_PROTO header usually return
* from different proxy servers or PHP default REQUEST_SCHEME
*
* @return string
*/
public function getProtocol(): string
{
$protocol = $this->getHeader('x-forwarded-proto', $this->getServer('server_protocol', 'https'));
if($protocol === 'HTTP/1.1') {
return 'http';
}
return $protocol;
}
/**
* Get Port
*
* Returns request port.
*
* @return string
*/
public function getPort(): string
{
return $this->getHeader('x-forwarded-port', (string)\parse_url($this->getProtocol().'://'.$this->getHeader('x-forwarded-host', $this->getHeader('host')), PHP_URL_PORT));
}
/**
* Get Hostname
*
* Returns request hostname.
*
* @return string
*/
public function getHostname(): string
{
return \parse_url($this->getProtocol().'://'.$this->getHeader('x-forwarded-host', $this->getHeader('host')), PHP_URL_HOST);
}
/**
* Get Method
*
* Return HTTP request method
*
* @return string
*/
public function getMethod(): string
{
return $this->getServer('request_method', 'UNKNOWN');
}
/**
* Get URI
*
* Return HTTP request URI
*
* @return string
*/
public function getURI(): string
{
return $this->getServer('request_uri', '');
}
/**
* Get Referer
*
* Return HTTP referer header
*
* @return string
*/
public function getReferer(string $default = ''): string
{
return $this->getHeader('referer', '');
}
/**
* Get Origin
*
* Return HTTP origin header
*
* @return string
*/
public function getOrigin(string $default = ''): string
{
return $this->getHeader('origin', $default);
}
/**
* Get User Agent
*
* Return HTTP user agent header
*
* @return string
*/
public function getUserAgent(string $default = ''): string
{
return $this->getHeader('user-agent', $default);
}
/**
* Get Accept
*
* Return HTTP accept header
*
* @return string
*/
public function getAccept(string $default = ''): string
{
return $this->getHeader('accept', $default);
}
/**
* Get files
*
* Method for querying upload files data. If $key is not found empty array will be returned.
*
* @param string $key
* @return array
*/
public function getFiles($key): array
{
$key = strtolower($key);
return (isset($this->swoole->files[$key])) ? $this->swoole->files[$key] : [];
}
/**
* Get cookie
*
* Method for querying HTTP cookie parameters. If $key is not found $default value will be returned.
*
* @param string $key
* @param string $default
*
* @return string
*/
public function getCookie(string $key, string $default = ''): string
{
$key = strtolower($key);
return (isset($this->swoole->cookie[$key])) ? $this->swoole->cookie[$key] : $default;
}
/**
* Get header
*
* Method for querying HTTP header parameters. If $key is not found $default value will be returned.
*
* @param string $key
* @param string $default
* @return string
*/
public function getHeader(string $key, string $default = ''): string
{
return (isset($this->swoole->header[$key])) ? $this->swoole->header[$key] : $default;
}
/**
* Generate input
*
* Generate PHP input stream and parse it as an array in order to handle different content type of requests
*
* @return array
*/
protected function generateInput(): array
{
if (null === $this->payload) {
$contentType = $this->getHeader('content-type');
// Get content-type without the charset
$length = strpos($contentType, ';');
$length = (empty($length)) ? strlen($contentType) : $length;
$contentType = substr($contentType, 0, $length);
switch ($contentType) {
case 'application/json':
$this->payload = json_decode($this->swoole->rawContent(), true);
break;
default:
$this->payload = $this->swoole->post;
break;
}
if(empty($this->payload)) { // Make sure we return same data type even if json payload is empty or failed
$this->payload = [];
}
}
return $this->payload;
}
/**
* Generate headers
*
* Parse request headers as an array for easy querying using the getHeader method
*
* @return array
*/
protected function generateHeaders(): array
{
return $this->swoole->header;
}
}

View file

@ -1,138 +0,0 @@
<?php
namespace Appwrite\Swoole;
use Appwrite\Utopia\Response as UtopiaResponse;
use Swoole\Http\Response as SwooleResponse;
class Response extends UtopiaResponse
{
/**
* Swoole Response Object
*
* @var SwooleResponse
*/
protected $swoole;
/**
* Mime Types
* with compression support
*
* @var array
*/
protected $compressed = [
'text/plain' => true,
'text/css' => true,
'text/javascript' => true,
'application/javascript' => true,
'text/html' => true,
'text/html; charset=UTF-8' => true,
'application/json' => true,
'application/json; charset=UTF-8' => true,
'image/svg+xml' => true,
'application/xml+rss' => true,
];
/**
* Response constructor.
*/
public function __construct(SwooleResponse $response)
{
$this->swoole = $response;
parent::__construct(\microtime(true));
}
/**
* Output response
*
* Generate HTTP response output including the response header (+cookies) and body and prints them.
*
* @param string $body
* @param int $exit exit code or don't exit if code is null
*
* @return void
*/
public function send(string $body = '', int $exit = null): void
{
if(!$this->disablePayload) {
$this->addHeader('X-Debug-Speed', (string)(microtime(true) - $this->startTime));
$this
->appendCookies()
->appendHeaders()
;
$chunk = 2000000; // Max chunk of 2 mb
$length = strlen($body);
$this->size = $this->size + strlen(implode("\n", $this->headers)) + $length;
if(array_key_exists(
$this->contentType,
$this->compressed
) && ($length <= $chunk)) { // Dont compress with GZIP / Brotli if header is not listed and size is bigger than 2mb
$this->swoole->end($body);
}
else {
for ($i=0; $i < ceil($length / $chunk); $i++) {
$this->swoole->write(substr($body, ($i * $chunk), min((($i * $chunk) + $chunk), $length)));
}
$this->swoole->end();
}
$this->disablePayload();
}
}
/**
* Append headers
*
* Iterating over response headers to generate them using native PHP header function.
* This method is also responsible for generating the response and content type headers.
*
* @return self
*/
protected function appendHeaders(): self
{
// Send status code header
$this->swoole->status((string)$this->statusCode);
// Send content type header
$this
->addHeader('Content-Type', $this->contentType)
;
// Set application headers
foreach ($this->headers as $key => $value) {
$this->swoole->header($key, $value);
}
return $this;
}
/**
* Append cookies
*
* Iterating over response cookies to generate them using native PHP cookie function.
*
* @return self
*/
protected function appendCookies(): self
{
foreach ($this->cookies as $cookie) {
$this->swoole->cookie(
$cookie['name'],
$cookie['value'],
$cookie['expire'],
$cookie['path'],
$cookie['domain'],
$cookie['secure'],
$cookie['httponly'],
$cookie['samesite'],
);
}
return $this;
}
}

View file

@ -30,7 +30,7 @@ class Cron extends Validator
*/
public function isValid($value)
{
if(empty($value)) {
if (empty($value)) {
return true;
}

View file

@ -3,46 +3,82 @@
namespace Appwrite\Utopia;
use Exception;
use Utopia\Swoole\Response as SwooleResponse;
use Swoole\Http\Response as SwooleHTTPResponse;
use Appwrite\Database\Document;
use Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response\Model\Any;
use Appwrite\Utopia\Response\Model\BaseList;
use Appwrite\Utopia\Response\Model\Collection;
use Appwrite\Utopia\Response\Model\Continent;
use Appwrite\Utopia\Response\Model\Country;
use Appwrite\Utopia\Response\Model\Currency;
use Appwrite\Utopia\Response\Model\Domain;
use Appwrite\Utopia\Response\Model\Error;
use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution;
use Appwrite\Utopia\Response\Model\File;
use Appwrite\Utopia\Response\Model\Func;
use Appwrite\Utopia\Response\Model\Key;
use Appwrite\Utopia\Response\Model\Language;
use Appwrite\Utopia\Response\Model\User;
use Appwrite\Utopia\Response\Model\Session;
use Appwrite\Utopia\Response\Model\Team;
use Appwrite\Utopia\Response\Model\TeamList;
use Appwrite\Utopia\Response\Model\Locale;
use Appwrite\Utopia\Response\Model\Log;
use Appwrite\Utopia\Response\Model\Membership;
use Appwrite\Utopia\Response\Model\MembershipList;
use Utopia\Response as UtopiaResponse;
use Appwrite\Utopia\Response\Model\Phone;
use Appwrite\Utopia\Response\Model\Platform;
use Appwrite\Utopia\Response\Model\Project;
use Appwrite\Utopia\Response\Model\Rule;
use Appwrite\Utopia\Response\Model\Tag;
use Appwrite\Utopia\Response\Model\Task;
use Appwrite\Utopia\Response\Model\Webhook;
class Response extends UtopiaResponse
/**
* @method public function setStatusCode(int $code = 200): Response
*/
class Response extends SwooleResponse
{
// General
const MODEL_LOG = 'log'; // - Missing
const MODEL_ANY = 'any';
const MODEL_LOG = 'log';
const MODEL_LOG_LIST = 'logList';
const MODEL_ERROR = 'error';
const MODEL_ERROR_DEV = 'errorDev';
const MODEL_BASE_LIST = 'baseList';
const MODEL_PERMISSIONS = 'permissions';
// Database
const MODEL_COLLECTION = 'collection';
const MODEL_COLLECTION_LIST = 'collectionList';
const MODEL_RULE = 'rule';
const MODEL_DOCUMENT_LIST = 'documentList';
// Users
const MODEL_USER = 'user';
const MODEL_USER_LIST = 'userList';
const MODEL_SESSION = 'session';
const MODEL_SESSION_LIST = 'sessionList';
const MODEL_TOKEN = 'token'; // - Missing
// Database
const MODEL_COLLECTION = 'collection'; // - Missing
// Storage
const MODEL_FILE = 'file';
const MODEL_FILE_LIST = 'fileList';
const MODEL_BUCKET = 'bucket'; // - Missing
// Locale
const MODEL_LOCALE = 'locale';
const MODEL_COUNTRY = 'country'; // - Missing
const MODEL_CONTINENT = 'continent'; // - Missing
const MODEL_CURRENCY = 'currency'; // - Missing
const MODEL_LANGUAGE = 'langauge'; // - Missing
const MODEL_PHONE = 'phone'; // - Missing
// Storage
const MODEL_FILE = 'file'; // - Missing
const MODEL_BUCKET = 'bucket'; // - Missing
const MODEL_COUNTRY = 'country';
const MODEL_COUNTRY_LIST = 'countryList';
const MODEL_CONTINENT = 'continent';
const MODEL_CONTINENT_LIST = 'continentList';
const MODEL_CURRENCY = 'currency';
const MODEL_CURRENCY_LIST = 'currencyList';
const MODEL_LANGUAGE = 'langauge';
const MODEL_LANGUAGE_LIST = 'langaugeList';
const MODEL_PHONE = 'phone';
const MODEL_PHONE_LIST = 'phoneList';
// Teams
const MODEL_TEAM = 'team';
@ -50,26 +86,97 @@ class Response extends UtopiaResponse
const MODEL_MEMBERSHIP = 'membership';
const MODEL_MEMBERSHIP_LIST = 'membershipList';
// Functions
const MODEL_FUNCTION = 'function';
const MODEL_FUNCTION_LIST = 'functionList';
const MODEL_TAG = 'tag';
const MODEL_TAG_LIST = 'tagList';
const MODEL_EXECUTION = 'execution';
const MODEL_EXECUTION_LIST = 'executionList';
// Project
const MODEL_PROJECT = 'project';
const MODEL_PROJECT_LIST = 'projectsList';
const MODEL_WEBHOOK = 'webhook';
const MODEL_WEBHOOK_LIST = 'webhookList';
const MODEL_KEY = 'key';
const MODEL_KEY_LIST = 'keyList';
const MODEL_TASK = 'task';
const MODEL_TASK_LIST = 'taskList';
const MODEL_PLATFORM = 'platform';
const MODEL_PLATFORM_LIST = 'platformList';
const MODEL_DOMAIN = 'domain';
const MODEL_DOMAIN_LIST = 'domainList';
/**
* @var array
*/
protected $payload = [];
/**
* Response constructor.
*
* @param float $time
*/
public function __construct(float $time = 0)
public function __construct(SwooleHTTPResponse $response)
{
$this
// General
->setModel(new Error())
->setModel(new ErrorDev())
// Lists
->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION))
->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_ANY))
->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER))
->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION))
->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG, false))
->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE))
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
->setModel(new BaseList('Tags List', self::MODEL_TAG_LIST, 'tags', self::MODEL_TAG))
->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION))
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT))
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK))
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY))
->setModel(new BaseList('Tasks List', self::MODEL_TASK_LIST, 'tasks', self::MODEL_TASK))
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM))
->setModel(new BaseList('Domains List', self::MODEL_DOMAIN_LIST, 'domains', self::MODEL_DOMAIN))
->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY))
->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT))
->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE))
->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY))
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
// Entities
->setModel(new Any())
->setModel(new Collection())
->setModel(new Rule())
->setModel(new Log())
->setModel(new User())
->setModel(new Session())
->setModel(new Locale())
->setModel(new File())
->setModel(new Team())
->setModel(new TeamList())
->setModel(new Membership())
->setModel(new MembershipList())
->setModel(new Func())
->setModel(new Tag())
->setModel(new Execution())
->setModel(new Project())
->setModel(new Webhook())
->setModel(new Key())
->setModel(new Task())
->setModel(new Domain())
->setModel(new Platform())
->setModel(new Country())
->setModel(new Continent())
->setModel(new Language())
->setModel(new Currency())
->setModel(new Phone())
// Verification
// Recovery
;
parent::__construct($time);
parent::__construct($response);
}
/**
@ -101,7 +208,7 @@ class Response extends UtopiaResponse
*/
public function getModel(string $key): Model
{
if(!isset($this->models[$key])) {
if (!isset($this->models[$key])) {
throw new Exception('Undefined model: '.$key);
}
@ -111,38 +218,55 @@ class Response extends UtopiaResponse
/**
* Validate response objects and outputs
* the response according to given format type
*
* @param Document $document
* @param string $model
*
* return void
*/
public function dynamic(Document $document, string $model)
public function dynamic(Document $document, string $model): void
{
return $this->json($this->output($document, $model));
$this->json($this->output($document, $model));
}
/**
* Generate valid response object from document data
*
* @param Document $document
* @param string $model
*
* return array
*/
protected function output(Document $document, string $model): array
public function output(Document $document, string $model): array
{
$data = $document;
$model = $this->getModel($model);
$output = [];
foreach($model->getRules() as $key => $rule) {
if(!$document->isSet($key)) {
if(!is_null($rule['default'])) {
if ($model->isAny()) {
return $document->getArrayCopy();
}
foreach ($model->getRules() as $key => $rule) {
if (!$document->isSet($key)) {
if (!is_null($rule['default'])) {
$document->setAttribute($key, $rule['default']);
}
else {
throw new Exception('Missing response key: '.$key);
} else {
throw new Exception('Model '.$model->getName().' is missing response key: '.$key);
}
}
if($rule['array']) {
if(!is_array($data[$key])) {
throw new Exception($key.' must be an array of '.$rule['type'].' types');
if ($rule['array']) {
if (!is_array($data[$key])) {
throw new Exception($key.' must be an array of type '.$rule['type']);
}
foreach ($data[$key] as &$item) {
if(array_key_exists($rule['type'], $this->models) && $item instanceof Document) {
if ($item instanceof Document) {
if (!array_key_exists($rule['type'], $this->models)) {
throw new Exception('Missing model for rule: '. $rule['type']);
}
$item = $this->output($item, $rule['type']);
}
}
@ -151,6 +275,8 @@ class Response extends UtopiaResponse
$output[$key] = $data[$key];
}
$this->payload = $output;
return $output;
}
@ -168,7 +294,7 @@ class Response extends UtopiaResponse
*/
public function yaml(array $data): void
{
if(!extension_loaded('yaml')) {
if (!extension_loaded('yaml')) {
throw new Exception('Missing yaml extension. Learn more at: https://www.php.net/manual/en/book.yaml.php');
}
@ -177,4 +303,12 @@ class Response extends UtopiaResponse
->send(yaml_emit($data, YAML_UTF8_ENCODING))
;
}
/**
* @return array
*/
public function getPayload():array
{
return $this->payload;
}
}

View file

@ -5,7 +5,12 @@ namespace Appwrite\Utopia\Response;
abstract class Model
{
/**
* @return array
* @var bool
*/
protected $any = false;
/**
* @var array
*/
protected $rules = [];
@ -48,4 +53,9 @@ abstract class Model
return $this;
}
public function isAny(): bool
{
return $this->any;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Any extends Model
{
/**
* @var bool
*/
protected $any = true;
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Any';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_ANY;
}
}

View file

@ -5,17 +5,36 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
abstract class BaseList extends Model
class BaseList extends Model
{
public function __construct()
/**
* @var string
*/
protected $name = '';
/**
* @var string
*/
protected $type = '';
public function __construct(string $name, string $type, string $key, string $model, bool $paging = true)
{
$this
->addRule('sum', [
$this->name = $name;
$this->type = $type;
if ($paging) {
$this->addRule('sum', [
'type' => 'integer',
'description' => 'Total sum of items in the list.',
'example' => '5',
])
;
]);
}
$this->addRule($key, [
'type' => $model,
'description' => 'List of '.$key.'.',
'example' => [],
'array' => true,
]);
}
/**
@ -25,7 +44,7 @@ abstract class BaseList extends Model
*/
public function getName():string
{
return 'Base List';
return $this->name;
}
/**
@ -35,6 +54,6 @@ abstract class BaseList extends Model
*/
public function getType():string
{
return Response::MODEL_BASE_LIST;
return $this->type;
}
}

View file

@ -0,0 +1,68 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Collection extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'description' => 'Collection ID.',
'example' => '5e5ea5c16897e',
])
->addRule('$permissions', [
'type' => Response::MODEL_PERMISSIONS,
'description' => 'Collection permissions.',
'example' => new \stdClass,
'array' => false,
])
->addRule('name', [
'type' => 'string',
'description' => 'Collection name.',
'example' => 'Movies',
])
->addRule('dateCreated', [
'type' => 'integer',
'description' => 'Collection creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('dateUpdated', [
'type' => 'integer',
'description' => 'Collection creation date in Unix timestamp.',
'example' => 1592981550,
])
->addRule('rules', [
'type' => Response::MODEL_RULE,
'description' => 'Collection rules.',
'example' => [],
'default' => [],
'array' => true,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Collection';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_COLLECTION;
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Continent extends Model
{
public function __construct()
{
$this
->addRule('name', [
'type' => 'string',
'description' => 'Continent name.',
'example' => 'Europe',
])
->addRule('code', [
'type' => 'string',
'description' => 'Continent two letter code.',
'example' => 'EU',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Continent';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_CONTINENT;
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Country extends Model
{
public function __construct()
{
$this
->addRule('name', [
'type' => 'string',
'description' => 'Country name.',
'example' => 'United States',
])
->addRule('code', [
'type' => 'string',
'description' => 'Country two-character ISO 3166-1 alpha code.',
'example' => 'US',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Country';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_COUNTRY;
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Currency extends Model
{
public function __construct()
{
$this
->addRule('symbol', [
'type' => 'string',
'description' => 'Currency symbol.',
'example' => '$',
])
->addRule('name', [
'type' => 'string',
'description' => 'Currency name.',
'example' => 'US dollar',
])
->addRule('symbolNative', [
'type' => 'string',
'description' => 'Currency native symbol.',
'example' => '$',
])
->addRule('decimalDigits', [
'type' => 'integer',
'description' => 'Number of decimal digits.',
'example' => 2,
])
->addRule('rounding', [
'type' => 'float',
'description' => 'Currency digit rounding.',
'example' => 0,
])
->addRule('code', [
'type' => 'string',
'description' => 'Currency code in [ISO 4217-1](http://en.wikipedia.org/wiki/ISO_4217) three-character format.',
'example' => 'USD',
])
->addRule('namePlural', [
'type' => 'string',
'description' => 'Currency plural name',
'example' => 'US dollars',
])
// ->addRule('locations', [
// 'type' => 'string',
// 'description' => 'Currency locations list. List of location in two-character ISO 3166-1 alpha code.',
// 'example' => ['US'],
// 'array' => true,
// ])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Currency';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_CURRENCY;
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Domain extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'description' => 'Domain ID.',
'example' => '5e5ea5c16897e',
])
->addRule('domain', [
'type' => 'string',
'description' => 'Domain name.',
'example' => 'appwrite.company.com',
])
->addRule('registerable', [
'type' => 'string',
'description' => 'Registerable domain name.',
'example' => 'company.com',
])
->addRule('tld', [
'type' => 'string',
'description' => 'TLD name.',
'example' => 'com',
])
->addRule('verification', [
'type' => 'boolean',
'description' => 'Verification process status.',
'example' => true,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Domain';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_DOMAIN;
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Execution extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'description' => 'Execution ID.',
'example' => '5e5ea5c16897e',
])
->addRule('functionId', [
'type' => 'string',
'description' => 'Function ID.',
'example' => '5e5ea6g16897e',
])
->addRule('dateCreated', [
'type' => 'integer',
'description' => 'The execution creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('trigger', [
'type' => 'string',
'description' => 'The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.',
'example' => 'http',
])
->addRule('status', [
'type' => 'string',
'description' => 'The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.',
'example' => 'processing',
])
->addRule('exitCode', [
'type' => 'integer',
'description' => 'The script exit code.',
'example' => 0,
])
->addRule('stdout', [
'type' => 'string',
'description' => 'The script stdout output string.',
'example' => '',
])
->addRule('stderr', [
'type' => 'string',
'description' => 'The script stderr output string.',
'example' => '',
])
->addRule('time', [
'type' => 'float',
'description' => 'The script execution time in seconds.',
'example' => 0.400,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Execution';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_EXECUTION;
}
}

View file

@ -9,7 +9,44 @@ class File extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'description' => 'File ID.',
'example' => '5e5ea5c16897e',
])
->addRule('$permissions', [
'type' => Response::MODEL_PERMISSIONS,
'description' => 'File permissions.',
'example' => new \stdClass,
'array' => false,
])
->addRule('name', [
'type' => 'string',
'description' => 'File name.',
'example' => 'Pink.png',
])
->addRule('dateCreated', [
'type' => 'integer',
'description' => 'File creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('signature', [
'type' => 'string',
'description' => 'File MD5 signature.',
'example' => '5d529fd02b544198ae075bd57c1762bb',
])
->addRule('mimeType', [
'type' => 'string',
'description' => 'File mime type.',
'example' => 'image/png',
])
->addRule('sizeOriginal', [
'type' => 'integer',
'description' => 'File original size in bytes.',
'example' => 17890,
])
;
}
/**
@ -29,6 +66,6 @@ class File extends Model
*/
public function getType():string
{
return Response::MODEL_LOCALE;
return Response::MODEL_FILE;
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Func extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'description' => 'Function ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'description' => 'Function name.',
'example' => 'My Function',
])
->addRule('dateCreated', [
'type' => 'integer',
'description' => 'Function creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('dateUpdated', [
'type' => 'integer',
'description' => 'Function update date in Unix timestamp.',
'example' => 1592981257,
])
->addRule('status', [
'type' => 'string',
'description' => 'Function status. Possible values: disabled, enabled',
'example' => 'enabled',
])
->addRule('env', [
'type' => 'string',
'description' => 'Function execution environment.',
'example' => 'python-3.8',
])
->addRule('tag', [
'type' => 'string',
'description' => 'Function active tag ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('vars', [
'type' => 'json',
'description' => 'Function environment variables.',
'default' => new \stdClass,
'example' => ['key' => 'value'],
])
->addRule('events', [
'type' => 'string',
'description' => 'Function trigger events.',
'default' => [],
'example' => ['account.create'],
'array' => true,
])
->addRule('schedule', [
'type' => 'string',
'description' => 'Function execution schedult in CRON format.',
'default' => '',
'example' => '5 4 * * *',
])
->addRule('scheduleNext', [
'type' => 'integer',
'description' => 'Function next scheduled execution date in Unix timestamp.',
'example' => 1592981292,
'default' => 0,
])
->addRule('schedulePrevious', [
'type' => 'integer',
'description' => 'Function next scheduled execution date in Unix timestamp.',
'example' => 1592981237,
'default' => 0,
])
->addRule('timeout', [
'type' => 'integer',
'description' => 'Function execution timeout in seconds.',
'default' => 15,
'example' => 1592981237,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Function';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_FUNCTION;
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Key extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'description' => 'Key ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'description' => 'Key name.',
'example' => 'My API Key',
])
->addRule('scopes', [
'type' => 'string',
'description' => 'Allowed permission scopes.',
'default' => [],
'example' => ['users.read', 'documents.write'],
'array' => true,
])
->addRule('secret', [
'type' => 'string',
'description' => 'Secret key.',
'example' => '919c2d18fb5d4...a2ae413da83346ad2',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Key';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_KEY;
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Language extends Model
{
public function __construct()
{
$this
->addRule('name', [
'type' => 'string',
'description' => 'Language name.',
'example' => 'Italian',
])
->addRule('code', [
'type' => 'string',
'description' => 'Language two-character ISO 639-1 codes.',
'example' => 'it',
])
->addRule('nativeName', [
'type' => 'string',
'description' => 'Language native name.',
'example' => 'Italiano',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Language';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_LANGUAGE;
}
}

Some files were not shown because too many files have changed in this diff Show more