mirror of
https://github.com/appwrite/appwrite
synced 2026-05-22 16:38:32 +00:00
Merge pull request #295 from appwrite/restify
Making the API more REST compatible and predictable
This commit is contained in:
commit
0c4fa5f5b6
998 changed files with 15272 additions and 22512 deletions
4
.env
4
.env
|
|
@ -1,4 +0,0 @@
|
|||
TESTS_FACEBOOK_APP_ID=dbase
|
||||
TESTS_FACEBOOK_APP_KEY=SDASDHAJSHDAJSHDJHSD
|
||||
DB_PW=dbpassword
|
||||
DB_ROOT_PW=dbrootpw
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -8,4 +8,5 @@
|
|||
/tests/resources/storage/
|
||||
/.idea/
|
||||
.DS_Store
|
||||
.php_cs.cache
|
||||
.php_cs.cache
|
||||
.env
|
||||
28
CHANGES.md
28
CHANGES.md
|
|
@ -4,14 +4,38 @@
|
|||
|
||||
* Upgraded core API PHP version to 7.4
|
||||
* New database rule validation options
|
||||
* Update docs example with auth info
|
||||
* Allow non-web platform skip origin header
|
||||
* Allow non-web platform to skip origin header
|
||||
* Limited console dashboard to show max 5 alerts at the same time
|
||||
* Added more webhooks events
|
||||
* Normailized all webhooks event names
|
||||
* Added support for SameSite cookie option with fallback cookie for old clients
|
||||
* Added a new Discord OAuth adapter
|
||||
* Added a new Twitch OAuth adapter
|
||||
* Added a new Spotify OAuth adapter
|
||||
* Added a new Yahoo OAuth adapter
|
||||
* Added a new Salesforce OAuth adapter
|
||||
* Added a new Yandex OAuth adapter
|
||||
* Added a new Paypal OAuth adapter
|
||||
* Added a new Bitly OAuth adapter
|
||||
* Upgraded MariaDB image to version 1.0.2
|
||||
* Upgraded SMTP image to version 1.0.1
|
||||
* File upload route (POST /v1/storage/files) now accept a single file per request
|
||||
* Added ENV vars to change system email sender name and address
|
||||
* Usage for requests made by project admin in the console are not counted as API usage
|
||||
* Added ENV var to change default file upload size limit. New default value is 100MB
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed bug where user status was saved as a string instead of an integer
|
||||
* Fixed gravatar icons not showing up correctly on the console
|
||||
* Fixed code location of project not found error
|
||||
* Fixed bug where tags element would ignore tab key for parsing new tags
|
||||
* Fixed OAuth login error saying project UID is missing when its not
|
||||
* Fixed wrong input validation for user preferences
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* Merged Auth and Account service route to make the API REST compatible
|
||||
|
||||
# Version 0.4.0 (PRE-RELEASE)
|
||||
|
||||
|
|
|
|||
|
|
@ -169,5 +169,6 @@ docker exec appwrite /bin/bash -c '/usr/share/nginx/html/vendor/bin/phpunit'
|
|||
|
||||
From time to time, our team will add tutorials that will help contributors find their way in the Appwrite source code. Below is a list of currently available tutorials:
|
||||
|
||||
* [Adding Support for a New OAuth Provider](./docs/tutorials/add-oauth-provider.md)
|
||||
* [Appwrite Environment Variables](./docs/tutorials/add-oauth-provider.md)
|
||||
* [Adding Support for a New OAuth2 Provider](./docs/tutorials/add-oauth2-provider.md)
|
||||
* [Appwrite Environment Variables](./docs/tutorials/environment-variables.md)
|
||||
* [Running in Production](./docs/tutorials/running-in-production.md)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ ENV TZ=Asia/Tel_Aviv \
|
|||
_APP_EDITION=community \
|
||||
_APP_OPTIONS_ABUSE=enabled \
|
||||
_APP_OPENSSL_KEY_V1=your-secret-key \
|
||||
_APP_STORAGE_LIMIT=104857600 \
|
||||
_APP_REDIS_HOST=redis \
|
||||
_APP_REDIS_PORT=6379 \
|
||||
_APP_DB_HOST=mariadb \
|
||||
|
|
@ -63,6 +64,7 @@ ENV TZ=Asia/Tel_Aviv \
|
|||
_APP_STATSD_PORT=8125 \
|
||||
_APP_SMTP_HOST=smtp \
|
||||
_APP_SMTP_PORT=25 \
|
||||
_APP_SETUP=self-hosted \
|
||||
_APP_VERSION=$VERSION
|
||||
#ENV _APP_SMTP_SECURE ''
|
||||
#ENV _APP_SMTP_USERNAME ''
|
||||
|
|
@ -94,8 +96,9 @@ RUN \
|
|||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set Upload Limit
|
||||
RUN echo "upload_max_filesize = 4M" > /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
|
||||
# Set Upload Limit (default to 100MB)
|
||||
RUN echo "upload_max_filesize = ${_APP_STORAGE_LIMIT}" > /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
|
||||
RUN echo "post_max_size = ${_APP_STORAGE_LIMIT}" > /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
|
||||
|
||||
# Nginx Configuration (with self-signed ssl certificates)
|
||||
COPY ./docker/nginx.conf /etc/nginx/nginx.conf
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ Appwrite API services aim to make developer's life a lot easier by hiding the co
|
|||
|
||||
Using Appwrite, you can easily manage user authentication with multiple sign-in methods, a database for storing and querying user and team data, storage and file management, image manipulation and cropping, schedule cron tasks and many other features to help you get more results in faster times and with a lot less code.
|
||||
|
||||
Appwrite can also integrate really well with your backend. Appwrite can word behind your own proxy facing your internal network, or alongside your own custom backend. You can use Appwrite server SDK to integrate your backend with Appwrite's APIs and webhooks.
|
||||
|
||||
[https://appwrite.io](https://appwrite.io)
|
||||
|
||||

|
||||
|
|
@ -51,7 +53,7 @@ docker-compose up -d --remove-orphans
|
|||
```
|
||||
|
||||
|
||||
Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please notice that on non-linux native hosts, the server might take a few minutes to start after installation completes.
|
||||
Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-linux native hosts, the server might take a few minutes to start after installation completes.
|
||||
|
||||
|
||||
For advanced production and custom installation, check out our Docker [environment variables](docs/tutorials/environment-variables.md) docs.
|
||||
|
|
@ -71,8 +73,7 @@ Getting started with Appwrite is as easy as creating a new project, choosing you
|
|||
|
||||
### Services
|
||||
|
||||
* [**Auth**](https://appwrite.io/docs/auth) - Manage user authentication using multiple sign-in methods and account recovery.
|
||||
* [**Account**](https://appwrite.io/docs/account) - Manage current user account. Track and manage the user sessions, devices, and security audit log.
|
||||
* [**Account**](https://appwrite.io/docs/account) - Manage current user authentication and account. Track and manage the user sessions, devices, sigin methods, and security logs.
|
||||
* [**Users**](https://appwrite.io/docs/users) - Manage and list all project users when in admin mode.
|
||||
* [**Teams**](https://appwrite.io/docs/teams) - Manage and group users in teams. Manage memberships, invites and user roles within a team.
|
||||
* [**Database**](https://appwrite.io/docs/database) - Manage database collections and documents. Read, create, update and delete documents and filter lists of documents collections using an advanced filter with graph-like capabilities.
|
||||
|
|
|
|||
207
app/app.php
207
app/app.php
|
|
@ -13,6 +13,7 @@ use Utopia\Validator\Range;
|
|||
use Utopia\View;
|
||||
use Utopia\Exception;
|
||||
use Auth\Auth;
|
||||
use Database\Database;
|
||||
use Database\Document;
|
||||
use Database\Validator\Authorization;
|
||||
use Event\Event;
|
||||
|
|
@ -22,7 +23,6 @@ use Utopia\Validator\WhiteList;
|
|||
* Configuration files
|
||||
*/
|
||||
$roles = include __DIR__.'/config/roles.php'; // User roles and scopes
|
||||
$sdks = include __DIR__.'/config/sdks.php'; // List of SDK clients
|
||||
$services = include __DIR__.'/config/services.php'; // List of services
|
||||
|
||||
$webhook = new Event('v1-webhooks', 'WebhooksV1');
|
||||
|
|
@ -54,6 +54,7 @@ $clients = array_unique(array_merge($clientsConsole, array_map(function ($node)
|
|||
}))));
|
||||
|
||||
$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $roles, $webhook, $audit, $usage, $domain, $clients) {
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$referrer = $request->getServer('HTTP_REFERER', '');
|
||||
|
|
@ -119,19 +120,19 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
|
|||
|
||||
$scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route
|
||||
$scopes = $roles[$role]['scopes']; // Allowed scopes for user role
|
||||
|
||||
|
||||
// Check if given key match project API keys
|
||||
$key = $project->search('secret', $request->getHeader('X-Appwrite-Key', ''), $project->getAttribute('keys', []));
|
||||
|
||||
|
||||
/*
|
||||
* Try app auth when we have project key and no user
|
||||
* Mock user to app and grant API key scopes in addition to default app scopes
|
||||
*/
|
||||
if (null !== $key && $user->isEmpty()) {
|
||||
$user = new Document([
|
||||
'$uid' => 0,
|
||||
'$id' => 0,
|
||||
'status' => Auth::USER_STATUS_ACTIVATED,
|
||||
'email' => 'app.'.$project->getUid().'@service.'.$domain,
|
||||
'email' => 'app.'.$project->getId().'@service.'.$domain,
|
||||
'password' => '',
|
||||
'name' => $project->getAttribute('name', 'Untitled'),
|
||||
]);
|
||||
|
|
@ -139,10 +140,10 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
|
|||
$role = Auth::USER_ROLE_APP;
|
||||
$scopes = array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
|
||||
|
||||
Authorization::disable(); // Cancel security segmentation for API keys.
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
|
||||
}
|
||||
|
||||
Authorization::setRole('user:'.$user->getUid());
|
||||
Authorization::setRole('user:'.$user->getId());
|
||||
Authorization::setRole('role:'.$role);
|
||||
|
||||
array_map(function ($node) {
|
||||
|
|
@ -158,6 +159,10 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
|
|||
// TDOO Check if user is god
|
||||
|
||||
if (!in_array($scope, $scopes)) {
|
||||
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS !== $project->getCollection()) { // Check if permission is denied because project is missing
|
||||
throw new Exception('Project not found', 404);
|
||||
}
|
||||
|
||||
throw new Exception($user->getAttribute('email', 'Guest').' (role: '.strtolower($roles[$role]['label']).') missing scope ('.$scope.')', 401);
|
||||
}
|
||||
|
||||
|
|
@ -173,14 +178,14 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
|
|||
* Background Jobs
|
||||
*/
|
||||
$webhook
|
||||
->setParam('projectId', $project->getUid())
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('event', $route->getLabel('webhook', ''))
|
||||
->setParam('payload', [])
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('projectId', $project->getUid())
|
||||
->setParam('userId', $user->getUid())
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('event', '')
|
||||
->setParam('resource', '')
|
||||
->setParam('userAgent', $request->getServer('HTTP_USER_AGENT', ''))
|
||||
|
|
@ -189,7 +194,7 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
|
|||
;
|
||||
|
||||
$usage
|
||||
->setParam('projectId', $project->getUid())
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('url', $request->getServer('HTTP_HOST', '').$request->getServer('REQUEST_URI', ''))
|
||||
->setParam('method', $request->getServer('REQUEST_METHOD', 'UNKNOWN'))
|
||||
->setParam('request', 0)
|
||||
|
|
@ -198,7 +203,7 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
|
|||
;
|
||||
});
|
||||
|
||||
$utopia->shutdown(function () use ($response, $request, $webhook, $audit, $usage) {
|
||||
$utopia->shutdown(function () use ($response, $request, $webhook, $audit, $usage, $mode, $project, $utopia) {
|
||||
|
||||
/*
|
||||
* Trigger Events for background jobs
|
||||
|
|
@ -206,16 +211,22 @@ $utopia->shutdown(function () use ($response, $request, $webhook, $audit, $usage
|
|||
if (!empty($webhook->getParam('event'))) {
|
||||
$webhook->trigger();
|
||||
}
|
||||
|
||||
|
||||
if (!empty($audit->getParam('event'))) {
|
||||
$audit->trigger();
|
||||
}
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$usage
|
||||
->setParam('request', $request->getSize())
|
||||
->setParam('response', $response->getSize())
|
||||
->trigger()
|
||||
;
|
||||
if($project->getId()
|
||||
&& $mode !== APP_MODE_ADMIN
|
||||
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage and admin mode
|
||||
$usage
|
||||
->setParam('request', $request->getSize())
|
||||
->setParam('response', $response->getSize())
|
||||
->trigger()
|
||||
;
|
||||
}
|
||||
});
|
||||
|
||||
$utopia->options(function () use ($request, $response, $domain, $project) {
|
||||
|
|
@ -236,6 +247,7 @@ $utopia->error(function ($error /* @var $error Exception */) use ($request, $res
|
|||
case 402: // Error allowed publicly
|
||||
case 403: // Error allowed publicly
|
||||
case 404: // Error allowed publicly
|
||||
case 409: // Error allowed publicly
|
||||
case 412: // Error allowed publicly
|
||||
case 429: // Error allowed publicly
|
||||
$code = $error->getCode();
|
||||
|
|
@ -330,10 +342,8 @@ $utopia->get('/robots.txt')
|
|||
->label('docs', false)
|
||||
->action(
|
||||
function () use ($response) {
|
||||
$response->text('# robotstxt.org/
|
||||
|
||||
User-agent: *
|
||||
');
|
||||
$template = new View(__DIR__.'/views/general/robots.phtml');
|
||||
$response->text($template->render(false));
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -343,33 +353,27 @@ $utopia->get('/humans.txt')
|
|||
->label('docs', false)
|
||||
->action(
|
||||
function () use ($response) {
|
||||
$response->text('# humanstxt.org/
|
||||
# The humans responsible & technology colophon
|
||||
|
||||
# TEAM
|
||||
<name> -- <role> -- <twitter>
|
||||
|
||||
# THANKS
|
||||
<name>');
|
||||
$template = new View(__DIR__.'/views/general/humans.phtml');
|
||||
$response->text($template->render(false));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/info') // This is only visible to gods
|
||||
->label('scope', 'god')
|
||||
->label('scope', 'god')
|
||||
->label('docs', false)
|
||||
->action(
|
||||
function () use ($response, $user, $project, $version, $env) { //TODO CONSIDER BLOCKING THIS ACTION TO ROLE GOD
|
||||
function () use ($response, $user, $project, $version, $env) {
|
||||
$response->json([
|
||||
'name' => 'API',
|
||||
'version' => $version,
|
||||
'environment' => $env,
|
||||
'time' => date('Y-m-d H:i:s', time()),
|
||||
'user' => [
|
||||
'id' => $user->getUid(),
|
||||
'id' => $user->getId(),
|
||||
'name' => $user->getAttribute('name', ''),
|
||||
],
|
||||
'project' => [
|
||||
'id' => $project->getUid(),
|
||||
'id' => $project->getId(),
|
||||
'name' => $project->getAttribute('name', ''),
|
||||
],
|
||||
]);
|
||||
|
|
@ -407,7 +411,7 @@ $utopia->get('/v1/proxy')
|
|||
$utopia->get('/v1/open-api-2.json')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->param('platform', 'client', function () {return new WhiteList(['client', 'server']);}, 'Choose target platform.', true)
|
||||
->param('platform', 'client', function () {return new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE]);}, 'Choose target platform.', true)
|
||||
->param('extensions', 0, function () {return new Range(0, 1);}, 'Show extra data.', true)
|
||||
->param('tests', 0, function () {return new Range(0, 1);}, 'Include only test services.', true)
|
||||
->action(
|
||||
|
|
@ -436,14 +440,21 @@ $utopia->get('/v1/open-api-2.json')
|
|||
if (!$tests && !$service['sdk']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
include_once $service['controller'];
|
||||
}
|
||||
|
||||
$security = [
|
||||
'client' => ['Project' => []],
|
||||
'server' => ['Project' => [], 'Key' => []],
|
||||
APP_PLATFORM_CLIENT => ['Project' => []],
|
||||
APP_PLATFORM_SERVER => ['Project' => [], 'Key' => []],
|
||||
APP_PLATFORM_CONSOLE => ['Project' => [], 'Key' => []],
|
||||
];
|
||||
|
||||
$platforms = [
|
||||
'client' => APP_PLATFORM_CLIENT,
|
||||
'server' => APP_PLATFORM_SERVER,
|
||||
'all' => APP_PLATFORM_CONSOLE,
|
||||
];
|
||||
|
||||
/*
|
||||
|
|
@ -476,13 +487,13 @@ $utopia->get('/v1/open-api-2.json')
|
|||
'Project' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Project',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'description' => 'Your project ID',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Key' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Key',
|
||||
'description' => 'Your Appwrite project secret key',
|
||||
'description' => 'Your secret API key',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Locale' => [
|
||||
|
|
@ -543,6 +554,13 @@ $utopia->get('/v1/open-api-2.json')
|
|||
],
|
||||
];
|
||||
|
||||
if ($extensions) {
|
||||
$output['securityDefinitions']['Project']['extensions'] = ['demo' => '5df5acd0d48c2'];
|
||||
$output['securityDefinitions']['Key']['extensions'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
|
||||
$output['securityDefinitions']['Locale']['extensions'] = ['demo' => 'en'];
|
||||
$output['securityDefinitions']['Mode']['extensions'] = ['demo' => ''];
|
||||
}
|
||||
|
||||
foreach ($utopia->getRoutes() as $key => $method) {
|
||||
foreach ($method as $route) { /* @var $route \Utopia\Route */
|
||||
if (!$route->getLabel('docs', true)) {
|
||||
|
|
@ -553,6 +571,10 @@ $utopia->get('/v1/open-api-2.json')
|
|||
continue;
|
||||
}
|
||||
|
||||
if($platform !== APP_PLATFORM_CONSOLE && !in_array($platforms[$platform], $route->getLabel('sdk.platform', []))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$url = str_replace('/v1', '', $route->getURL());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
|
|
@ -582,6 +604,17 @@ $utopia->get('/v1/open-api-2.json')
|
|||
];
|
||||
|
||||
if ($extensions) {
|
||||
$platformList = $route->getLabel('sdk.platform', []);
|
||||
|
||||
if(in_array(APP_PLATFORM_CLIENT, $platformList)) {
|
||||
$platformList = array_merge($platformList, [
|
||||
APP_PLATFORM_WEB,
|
||||
APP_PLATFORM_IOS,
|
||||
APP_PLATFORM_ANDROID,
|
||||
APP_PLATFORM_FLUTTER,
|
||||
]);
|
||||
}
|
||||
|
||||
$temp['extensions'] = [
|
||||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
|
|
@ -591,10 +624,11 @@ $utopia->get('/v1/open-api-2.json')
|
|||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
'rate-time' => $route->getLabel('abuse-time', 3600),
|
||||
'scope' => $route->getLabel('scope', ''),
|
||||
'platforms' => $platformList,
|
||||
];
|
||||
}
|
||||
|
||||
if ((!empty($scope) && 'public' != $scope)) {
|
||||
if ((!empty($scope))) { // && 'public' != $scope
|
||||
$temp['security'][] = $route->getLabel('sdk.security', $security[$platform]);
|
||||
}
|
||||
|
||||
|
|
@ -640,8 +674,9 @@ $utopia->get('/v1/open-api-2.json')
|
|||
break;
|
||||
case 'Utopia\Validator\JSON':
|
||||
case 'Utopia\Validator\Mock':
|
||||
case 'Utopia\Validator\Assoc':
|
||||
$node['type'] = 'object';
|
||||
$node['type'] = 'object';
|
||||
$node['type'] = 'string';
|
||||
$node['x-example'] = '{}';
|
||||
//$node['format'] = 'json';
|
||||
break;
|
||||
|
|
@ -722,7 +757,93 @@ $utopia->get('/v1/open-api-2.json')
|
|||
|
||||
ksort($output['paths']);
|
||||
|
||||
$response->json($output);
|
||||
$response
|
||||
->json($output);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
$utopia->get('/v1/debug')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->action(
|
||||
function () use ($response, $request, $utopia, $domain, $services) {
|
||||
$output = [
|
||||
'scopes' => [],
|
||||
'webhooks' => [],
|
||||
'methods' => [],
|
||||
'routes' => [],
|
||||
'docs' => [],
|
||||
];
|
||||
|
||||
foreach ($services as $service) { /* @noinspection PhpIncludeInspection */
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
if($service['tests']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
include_once $service['controller'];
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
|
||||
foreach ($utopia->getRoutes() as $key => $method) {
|
||||
foreach ($method as $route) { /* @var $route \Utopia\Route */
|
||||
if (!$route->getLabel('docs', true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($route->getLabel('sdk.namespace', null))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($route->getLabel('scope', false)) {
|
||||
$output['scopes'][$route->getLabel('scope', false)] = $route->getMethod().' '.$route->getURL();
|
||||
}
|
||||
|
||||
if ($route->getLabel('sdk.description', false)) {
|
||||
if(!realpath(__DIR__.'/../'.$route->getLabel('sdk.description', false))) {
|
||||
throw new Exception('Docs file ('.$route->getLabel('sdk.description', false).') is missing', 500);
|
||||
}
|
||||
|
||||
if(array_key_exists($route->getLabel('sdk.description', false), $output['docs'])) {
|
||||
throw new Exception('Docs file ('.$route->getLabel('sdk.description', false).') is already in use by another route', 500);
|
||||
}
|
||||
|
||||
$output['docs'][$route->getLabel('sdk.description', false)] = $route->getMethod().' '.$route->getURL();
|
||||
}
|
||||
|
||||
if ($route->getLabel('webhook', false)) {
|
||||
if(array_key_exists($route->getLabel('webhook', false), $output['webhooks'])) {
|
||||
//throw new Exception('Webhook ('.$route->getLabel('webhook', false).') is already in use by another route', 500);
|
||||
}
|
||||
|
||||
$output['webhooks'][$route->getLabel('webhook', false)] = $route->getMethod().' '.$route->getURL();
|
||||
}
|
||||
|
||||
if ($route->getLabel('sdk.namespace', false)) {
|
||||
$method = $route->getLabel('sdk.namespace', false).'->'.$route->getLabel('sdk.method', false).'()';
|
||||
if(array_key_exists($method, $output['methods'])) {
|
||||
throw new Exception('Method ('.$method.') is already in use by another route', 500);
|
||||
}
|
||||
|
||||
$output['methods'][$method] = $route->getMethod().' '.$route->getURL();
|
||||
}
|
||||
|
||||
$output['routes'][$route->getURL().' ('.$route->getMethod().')'] = [];
|
||||
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($output['scopes']);
|
||||
ksort($output['webhooks']);
|
||||
ksort($output['methods']);
|
||||
ksort($output['routes']);
|
||||
ksort($output['docs']);
|
||||
|
||||
$response
|
||||
->json($output);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use Database\Database;
|
|||
|
||||
$collections = [
|
||||
'console' => [
|
||||
'$uid' => 'console',
|
||||
'$id' => 'console',
|
||||
'$collection' => 'projects',
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Appwrite',
|
||||
|
|
@ -71,7 +71,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_COLLECTIONS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Collections',
|
||||
'structure' => true,
|
||||
|
|
@ -126,7 +126,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_RULES => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'$id' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Collections Rule',
|
||||
'structure' => true,
|
||||
|
|
@ -198,7 +198,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_USERS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'User',
|
||||
'structure' => true,
|
||||
|
|
@ -268,8 +268,8 @@ $collections = [
|
|||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'Confirmation Status',
|
||||
'key' => 'confirm',
|
||||
'label' => 'Email Verification Status',
|
||||
'key' => 'emailVerification',
|
||||
'type' => 'boolean',
|
||||
'default' => '',
|
||||
'required' => true,
|
||||
|
|
@ -308,7 +308,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_TOKENS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Token',
|
||||
'structure' => true,
|
||||
|
|
@ -362,7 +362,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_MEMBERSHIPS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Membership',
|
||||
'structure' => true,
|
||||
|
|
@ -434,7 +434,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_TEAMS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_TEAMS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_TEAMS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Team',
|
||||
'structure' => true,
|
||||
|
|
@ -470,7 +470,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_PROJECTS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_PROJECTS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_PROJECTS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Project',
|
||||
'structure' => true,
|
||||
|
|
@ -610,7 +610,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_WEBHOOKS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_WEBHOOKS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_WEBHOOKS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Webhook',
|
||||
'structure' => true,
|
||||
|
|
@ -673,7 +673,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_KEYS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_KEYS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_KEYS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Key',
|
||||
'structure' => true,
|
||||
|
|
@ -708,7 +708,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_TASKS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_TASKS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_TASKS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Task',
|
||||
'structure' => true,
|
||||
|
|
@ -861,7 +861,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_PLATFORMS => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_PLATFORMS,
|
||||
'$id' => Database::SYSTEM_COLLECTION_PLATFORMS,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'Platform',
|
||||
'structure' => true,
|
||||
|
|
@ -933,7 +933,7 @@ $collections = [
|
|||
],
|
||||
Database::SYSTEM_COLLECTION_FILES => [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
'$uid' => Database::SYSTEM_COLLECTION_FILES,
|
||||
'$id' => Database::SYSTEM_COLLECTION_FILES,
|
||||
'$permissions' => ['read' => ['*']],
|
||||
'name' => 'File',
|
||||
'structure' => true,
|
||||
|
|
@ -1078,7 +1078,7 @@ $collections = [
|
|||
];
|
||||
|
||||
/*
|
||||
* Add enabled OAuth providers to default data rules
|
||||
* Add enabled OAuth2 providers to default data rules
|
||||
*/
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider['enabled']) {
|
||||
|
|
@ -1087,8 +1087,8 @@ foreach ($providers as $key => $provider) {
|
|||
|
||||
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'OAuth '.ucfirst($key).' ID',
|
||||
'key' => 'usersOauth'.ucfirst($key).'Appid',
|
||||
'label' => 'OAuth2 '.ucfirst($key).' ID',
|
||||
'key' => 'usersOauth2'.ucfirst($key).'Appid',
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
|
|
@ -1097,8 +1097,8 @@ foreach ($providers as $key => $provider) {
|
|||
|
||||
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'OAuth '.ucfirst($key).' Secret',
|
||||
'key' => 'usersOauth'.ucfirst($key).'Secret',
|
||||
'label' => 'OAuth2 '.ucfirst($key).' Secret',
|
||||
'key' => 'usersOauth2'.ucfirst($key).'Secret',
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
|
|
@ -1107,8 +1107,8 @@ foreach ($providers as $key => $provider) {
|
|||
|
||||
$collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'OAuth '.ucfirst($key).' ID',
|
||||
'key' => 'oauth'.ucfirst($key),
|
||||
'label' => 'OAuth2 '.ucfirst($key).' ID',
|
||||
'key' => 'oauth2'.ucfirst($key),
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
|
|
@ -1117,8 +1117,8 @@ foreach ($providers as $key => $provider) {
|
|||
|
||||
$collections[Database::SYSTEM_COLLECTION_USERS]['rules'][] = [
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'OAuth '.ucfirst($key).' Access Token',
|
||||
'key' => 'oauth'.ucfirst($key).'AccessToken',
|
||||
'label' => 'OAuth2 '.ucfirst($key).' Access Token',
|
||||
'key' => 'oauth2'.ucfirst($key).'AccessToken',
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s span',
|
||||
'auth.emails.confirm.title' => 'Profiel Bevestiging',
|
||||
'auth.emails.confirm.body' => 'af.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Verander Wagwoord',
|
||||
'auth.emails.recovery.body' => 'af.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Uitnodiging na %s span by %s',
|
||||
'auth.emails.invitation.body' => 'af.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s span',
|
||||
'account.emails.verification.title' => 'Profiel Bevestiging',
|
||||
'account.emails.verification.body' => 'af.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Verander Wagwoord',
|
||||
'account.emails.recovery.body' => 'af.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Uitnodiging na %s span by %s',
|
||||
'account.emails.invitation.body' => 'af.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Onbekend',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'rtl',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'فريق %s',
|
||||
'auth.emails.confirm.title' => 'تأكيد الحساب',
|
||||
'auth.emails.confirm.body' => 'ar.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'إعادة تعيين كلمة المرور',
|
||||
'auth.emails.recovery.body' => 'ar.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'دعوة لفريق %s في %s',
|
||||
'auth.emails.invitation.body' => 'ar.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'فريق %s',
|
||||
'account.emails.verification.title' => 'تأكيد الحساب',
|
||||
'account.emails.verification.body' => 'ar.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'إعادة تعيين كلمة المرور',
|
||||
'account.emails.recovery.body' => 'ar.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'دعوة لفريق %s في %s',
|
||||
'account.emails.invitation.body' => 'ar.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'مجهول',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s টীম',
|
||||
'auth.emails.confirm.title' => 'অ্যাকাউন্ট নিশ্চিতকরণ',
|
||||
'auth.emails.confirm.body' => 'bn.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'পাসওয়ার্ড রিসেট',
|
||||
'auth.emails.recovery.body' => 'bn.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'আমন্ত্রণ %s এই টীম-এ যেটি এখানের %s',
|
||||
'auth.emails.invitation.body' => 'bn.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s টীম',
|
||||
'account.emails.verification.title' => 'অ্যাকাউন্ট নিশ্চিতকরণ',
|
||||
'account.emails.verification.body' => 'bn.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'পাসওয়ার্ড রিসেট',
|
||||
'account.emails.recovery.body' => 'bn.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'আমন্ত্রণ %s এই টীম-এ যেটি এখানের %s',
|
||||
'account.emails.invitation.body' => 'bn.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'অজানা',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Equip',
|
||||
'auth.emails.confirm.title' => 'Confirmació del compte',
|
||||
'auth.emails.confirm.body' => 'cat.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Restablir contrasenya',
|
||||
'auth.emails.recovery.body' => 'cat.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Invitació a l\'Equip %s a %s',
|
||||
'auth.emails.invitation.body' => 'cat.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Equip',
|
||||
'account.emails.verification.title' => 'Confirmació del compte',
|
||||
'account.emails.verification.body' => 'cat.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Restablir contrasenya',
|
||||
'account.emails.recovery.body' => 'cat.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Invitació a l\'Equip %s a %s',
|
||||
'account.emails.invitation.body' => 'cat.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Desconegut',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s tým',
|
||||
'auth.emails.confirm.title' => 'Potvrzení účtu',
|
||||
'auth.emails.confirm.body' => 'cz.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Resetovat heslo',
|
||||
'auth.emails.recovery.body' => 'cz.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Pozvánka do týmu% s na% s',
|
||||
'auth.emails.invitation.body' => 'cz.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s tým',
|
||||
'account.emails.verification.title' => 'Potvrzení účtu',
|
||||
'account.emails.verification.body' => 'cz.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Resetovat heslo',
|
||||
'account.emails.recovery.body' => 'cz.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Pozvánka do týmu% s na% s',
|
||||
'account.emails.invitation.body' => 'cz.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Neznámý',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Team',
|
||||
'auth.emails.confirm.title' => 'Accountbestätigung',
|
||||
'auth.emails.confirm.body' => 'de.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Passwort zurücksetzen',
|
||||
'auth.emails.recovery.body' => 'de.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Einladung zum %s Team bei %s',
|
||||
'auth.emails.invitation.body' => 'de.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Team',
|
||||
'account.emails.verification.title' => 'Accountbestätigung',
|
||||
'account.emails.verification.body' => 'de.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Passwort zurücksetzen',
|
||||
'account.emails.recovery.body' => 'de.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Einladung zum %s Team bei %s',
|
||||
'account.emails.invitation.body' => 'de.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Unbekannt',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Team',
|
||||
'auth.emails.confirm.title' => 'Account Confirmation',
|
||||
'auth.emails.confirm.body' => 'en.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Password Reset',
|
||||
'auth.emails.recovery.body' => 'en.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Invitation to %s Team at %s',
|
||||
'auth.emails.invitation.body' => 'en.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Team',
|
||||
'account.emails.verification.title' => 'Account Verification',
|
||||
'account.emails.verification.body' => 'en.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Password Reset',
|
||||
'account.emails.recovery.body' => 'en.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Invitation to %s Team at %s',
|
||||
'account.emails.invitation.body' => 'en.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Unknown',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Equipo %s',
|
||||
'auth.emails.confirm.title' => 'Confirmación de la cuenta',
|
||||
'auth.emails.confirm.body' => 'es.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Reestablecer contraseña',
|
||||
'auth.emails.recovery.body' => 'es.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Invitación al Equipo %s en %s',
|
||||
'auth.emails.invitation.body' => 'es.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Equipo %s',
|
||||
'account.emails.verification.title' => 'Confirmación de la cuenta',
|
||||
'account.emails.verification.body' => 'es.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Reestablecer contraseña',
|
||||
'account.emails.recovery.body' => 'es.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Invitación al Equipo %s en %s',
|
||||
'account.emails.invitation.body' => 'es.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Desconocido',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Tiimi',
|
||||
'auth.emails.confirm.title' => 'Tilin Vahvistus',
|
||||
'auth.emails.confirm.body' => 'en.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Salasanan Nollaus',
|
||||
'auth.emails.recovery.body' => 'en.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Kutsu %s Tiimiin %s',
|
||||
'auth.emails.invitation.body' => 'en.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Tiimi',
|
||||
'account.emails.verification.title' => 'Tilin Vahvistus',
|
||||
'account.emails.verification.body' => 'en.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Salasanan Nollaus',
|
||||
'account.emails.recovery.body' => 'en.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Kutsu %s Tiimiin %s',
|
||||
'account.emails.invitation.body' => 'en.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Unknown',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Lið',
|
||||
'auth.emails.confirm.title' => 'Vátta brúkari',
|
||||
'auth.emails.confirm.body' => 'fo.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Glómt passord',
|
||||
'auth.emails.recovery.body' => 'fo.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Innbjóðing til %s Lið hjá %s',
|
||||
'auth.emails.invitation.body' => 'fo.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Lið',
|
||||
'account.emails.verification.title' => 'Vátta brúkari',
|
||||
'account.emails.verification.body' => 'fo.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Glómt passord',
|
||||
'account.emails.recovery.body' => 'fo.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Innbjóðing til %s Lið hjá %s',
|
||||
'account.emails.invitation.body' => 'fo.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Ókjent',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Équipe %s',
|
||||
'auth.emails.confirm.title' => 'Confirmation de création de compte',
|
||||
'auth.emails.confirm.body' => 'fr.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Réinitialisation de mot de passe',
|
||||
'auth.emails.recovery.body' => 'fr.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Invitation pour l\'équipe %s au projet %s',
|
||||
'auth.emails.invitation.body' => 'fr.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Équipe %s',
|
||||
'account.emails.verification.title' => 'Confirmation de création de compte',
|
||||
'account.emails.verification.body' => 'fr.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Réinitialisation de mot de passe',
|
||||
'account.emails.recovery.body' => 'fr.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Invitation pour l\'équipe %s au projet %s',
|
||||
'account.emails.invitation.body' => 'fr.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Inconnu',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Ομάδα %s',
|
||||
'auth.emails.confirm.title' => 'Επιβεβαίωση Λογαριασμού',
|
||||
'auth.emails.confirm.body' => 'gr.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Επαναφορά Κωδικού Πρόσβασης',
|
||||
'auth.emails.recovery.body' => 'gr.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Πρόσκληση στην ομάδα %s στο %s',
|
||||
'auth.emails.invitation.body' => 'gr.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Ομάδα %s',
|
||||
'account.emails.verification.title' => 'Επιβεβαίωση Λογαριασμού',
|
||||
'account.emails.verification.body' => 'gr.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Επαναφορά Κωδικού Πρόσβασης',
|
||||
'account.emails.recovery.body' => 'gr.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Πρόσκληση στην ομάδα %s στο %s',
|
||||
'account.emails.invitation.body' => 'gr.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Άγνωστο',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'rtl',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'צוות %s',
|
||||
'auth.emails.confirm.title' => 'אימות חשבון',
|
||||
'auth.emails.confirm.body' => 'he.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'איפוס סיסמא',
|
||||
'auth.emails.recovery.body' => 'he.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'הזמנה לצוות של %s ב־%s',
|
||||
'auth.emails.invitation.body' => 'he.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'צוות %s',
|
||||
'account.emails.verification.title' => 'אימות חשבון',
|
||||
'account.emails.verification.body' => 'he.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'איפוס סיסמא',
|
||||
'account.emails.recovery.body' => 'he.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'הזמנה לצוות של %s ב־%s',
|
||||
'account.emails.invitation.body' => 'he.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'לא ידוע',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s टीम',
|
||||
'auth.emails.confirm.title' => 'अकाउंट कन्फर्मेशन ',
|
||||
'auth.emails.confirm.body' => 'hi.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'पासवर्ड रिसेट ',
|
||||
'auth.emails.recovery.body' => 'hi.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'इनविटेशन %s टीम %s',
|
||||
'auth.emails.invitation.body' => 'hi.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s टीम',
|
||||
'account.emails.verification.title' => 'अकाउंट कन्फर्मेशन ',
|
||||
'account.emails.verification.body' => 'hi.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'पासवर्ड रिसेट ',
|
||||
'account.emails.recovery.body' => 'hi.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'इनविटेशन %s टीम %s',
|
||||
'account.emails.invitation.body' => 'hi.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'अज्ञात',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Csapat',
|
||||
'auth.emails.confirm.title' => 'Fiók megerősítés',
|
||||
'auth.emails.confirm.body' => 'hu.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Jelszó Visszaállítás',
|
||||
'auth.emails.recovery.body' => 'hu.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Meghívás a %s Csapathoz %s',
|
||||
'auth.emails.invitation.body' => 'hu.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Csapat',
|
||||
'account.emails.verification.title' => 'Fiók megerősítés',
|
||||
'account.emails.verification.body' => 'hu.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Jelszó Visszaállítás',
|
||||
'account.emails.recovery.body' => 'hu.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Meghívás a %s Csapathoz %s',
|
||||
'account.emails.invitation.body' => 'hu.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Ismeretlen',
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ return [
|
|||
'settings.locale' => 'ru',
|
||||
'settings.direction' => 'ltr',
|
||||
|
||||
'auth.emails.team' => 'Թիմ %s',
|
||||
'auth.emails.confirm.title' => 'Հաշվեհամարի հաստատում',
|
||||
'auth.emails.confirm.body' => 'hy.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Գաղտնաբառի փոփոխում',
|
||||
'auth.emails.recovery.body' => 'hy.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Հրավիրում ենք %s թիմ, %s պրոեկտի համար',
|
||||
'auth.emails.invitation.body' => 'hy.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Թիմ %s',
|
||||
'account.emails.verification.title' => 'Հաշվեհամարի հաստատում',
|
||||
'account.emails.verification.body' => 'hy.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Գաղտնաբառի փոփոխում',
|
||||
'account.emails.recovery.body' => 'hy.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Հրավիրում ենք %s թիմ, %s պրոեկտի համար',
|
||||
'account.emails.invitation.body' => 'hy.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Անհայտ',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Tim %s',
|
||||
'auth.emails.confirm.title' => 'Konfirmasi Akun',
|
||||
'auth.emails.confirm.body' => 'id.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Reset Kata Sandi',
|
||||
'auth.emails.recovery.body' => 'id.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Undangan ke Tim %s di %s',
|
||||
'auth.emails.invitation.body' => 'id.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Tim %s',
|
||||
'account.emails.verification.title' => 'Konfirmasi Akun',
|
||||
'account.emails.verification.body' => 'id.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Reset Kata Sandi',
|
||||
'account.emails.recovery.body' => 'id.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Undangan ke Tim %s di %s',
|
||||
'account.emails.invitation.body' => 'id.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Tidak diketahui',
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ return [
|
|||
'settings.locale' => 'is',
|
||||
'settings.direction' => 'ltr',
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Teymi',
|
||||
'auth.emails.confirm.title' => 'Staðfesting reiknings',
|
||||
'auth.emails.confirm.body' => 'is.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Núllstilla lykilorð',
|
||||
'auth.emails.recovery.body' => 'is.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Boð í %s lið í %s',
|
||||
'auth.emails.invitation.body' => 'is.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Teymi',
|
||||
'account.emails.verification.title' => 'Staðfesting reiknings',
|
||||
'account.emails.verification.body' => 'is.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Núllstilla lykilorð',
|
||||
'account.emails.recovery.body' => 'is.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Boð í %s lið í %s',
|
||||
'account.emails.invitation.body' => 'is.email.auth.invitation.tpl',
|
||||
'locale.country.unknown' => 'Óþekktur',
|
||||
'countries' => include 'is.countries.php',
|
||||
'continents' => include 'is.continents.php',
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Servizio - Utenti
|
||||
'auth.emails.team' => 'Team %s',
|
||||
'auth.emails.confirm.title' => 'Conferma dell\'account',
|
||||
'auth.emails.confirm.body' => 'it.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Recupero password',
|
||||
'auth.emails.recovery.body' => 'it.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Invito al team %s di %s',
|
||||
'auth.emails.invitation.body' => 'it.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Team %s',
|
||||
'account.emails.verification.title' => 'Conferma dell\'account',
|
||||
'account.emails.verification.body' => 'it.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Recupero password',
|
||||
'account.emails.recovery.body' => 'it.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Invito al team %s di %s',
|
||||
'account.emails.invitation.body' => 'it.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Sconosciuto',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s チーム',
|
||||
'auth.emails.confirm.title' => 'アカウント確認のお願い',
|
||||
'auth.emails.confirm.body' => 'ja.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'パスワード再設定のお願い',
|
||||
'auth.emails.recovery.body' => 'ja.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => '%s チーム(%s プロジェクト)への招待',
|
||||
'auth.emails.invitation.body' => 'ja.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s チーム',
|
||||
'account.emails.verification.title' => 'アカウント確認のお願い',
|
||||
'account.emails.verification.body' => 'ja.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'パスワード再設定のお願い',
|
||||
'account.emails.recovery.body' => 'ja.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => '%s チーム(%s プロジェクト)への招待',
|
||||
'account.emails.invitation.body' => 'ja.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => '不明',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Tim %s',
|
||||
'auth.emails.confirm.title' => 'Konfirmasi akun',
|
||||
'auth.emails.confirm.body' => 'jv.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Reset Sandi',
|
||||
'auth.emails.recovery.body' => 'jv.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Undangan menyang %s Tim ing %s',
|
||||
'auth.emails.invitation.body' => 'jv.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Tim %s',
|
||||
'account.emails.verification.title' => 'Konfirmasi akun',
|
||||
'account.emails.verification.body' => 'jv.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Reset Sandi',
|
||||
'account.emails.recovery.body' => 'jv.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Undangan menyang %s Tim ing %s',
|
||||
'account.emails.invitation.body' => 'jv.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Ora dingerteni',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s 팀',
|
||||
'auth.emails.confirm.title' => '계정 확인',
|
||||
'auth.emails.confirm.body' => 'ko.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => '비밀번호 재설정',
|
||||
'auth.emails.recovery.body' => 'ko.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => '%s 팀(%s 프로젝트)에 합류 초대',
|
||||
'auth.emails.invitation.body' => 'ko.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s 팀',
|
||||
'account.emails.verification.title' => '계정 확인',
|
||||
'account.emails.verification.body' => 'ko.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => '비밀번호 재설정',
|
||||
'account.emails.recovery.body' => 'ko.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => '%s 팀(%s 프로젝트)에 합류 초대',
|
||||
'account.emails.invitation.body' => 'ko.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => '알려지지 않은',
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ return [
|
|||
'settings.inspire' => '"Menas būti išmintingu — tai menas žinoti, ką galima paniekti."', // This is the line printed in the homepage and console 'view-source'
|
||||
'settings.locale' => 'lt',
|
||||
'settings.direction' => 'ltr',
|
||||
'auth.emails.team' => 'Komanda %s',
|
||||
'auth.emails.confirm.title' => 'Paskyros patvirtinimas',
|
||||
'auth.emails.confirm.body' => 'lt.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Atnaujinti slaptažodį',
|
||||
'auth.emails.recovery.body' => 'lt.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Pakeisti į komandą %s projektui %s',
|
||||
'auth.emails.invitation.body' => 'lt.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Komanda %s',
|
||||
'account.emails.verification.title' => 'Paskyros patvirtinimas',
|
||||
'account.emails.verification.body' => 'lt.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Atnaujinti slaptažodį',
|
||||
'account.emails.recovery.body' => 'lt.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Pakeisti į komandą %s projektui %s',
|
||||
'account.emails.invitation.body' => 'lt.email.auth.invitation.tpl',
|
||||
'locale.country.unknown' => 'Nežinoma',
|
||||
'countries' => include 'lt.countries.php',
|
||||
'continents' => include 'lt.continents.php',
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ return [
|
|||
'settings.locale' => 'ml',
|
||||
'settings.direction' => 'ltr',
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s ടീം',
|
||||
'auth.emails.confirm.title' => 'അക്കൗണ്ട് കൺഫർമേഷൻ',
|
||||
'auth.emails.confirm.body' => 'ml.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'പാസ്വേഡ് റീസെറ്റ്',
|
||||
'auth.emails.recovery.body' => 'ml.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'ഇൻവിറ്റേഷൻ- %s ടീം, %s പ്രോജക്ട്',
|
||||
'auth.emails.invitation.body' => 'ml.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s ടീം',
|
||||
'account.emails.verification.title' => 'അക്കൗണ്ട് കൺഫർമേഷൻ',
|
||||
'account.emails.verification.body' => 'ml.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'പാസ്വേഡ് റീസെറ്റ്',
|
||||
'account.emails.recovery.body' => 'ml.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'ഇൻവിറ്റേഷൻ- %s ടീം, %s പ്രോജക്ട്',
|
||||
'account.emails.invitation.body' => 'ml.email.auth.invitation.tpl',
|
||||
'locale.country.unknown' => 'Unknown',
|
||||
'countries' => include 'ml.countries.php',
|
||||
'continents' => include 'ml.continents.php',
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Team',
|
||||
'auth.emails.confirm.title' => 'Pengesahan akaun',
|
||||
'auth.emails.confirm.body' => 'ms.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Tetapkan semula kata laluan',
|
||||
'auth.emails.recovery.body' => 'ms.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Undangan ke dalam kumpulan %s di %s',
|
||||
'auth.emails.invitation.body' => 'ms.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Team',
|
||||
'account.emails.verification.title' => 'Pengesahan akaun',
|
||||
'account.emails.verification.body' => 'ms.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Tetapkan semula kata laluan',
|
||||
'account.emails.recovery.body' => 'ms.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Undangan ke dalam kumpulan %s di %s',
|
||||
'account.emails.invitation.body' => 'ms.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Tidak Diketahui',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Team',
|
||||
'auth.emails.confirm.title' => 'Account Bevestiging',
|
||||
'auth.emails.confirm.body' => 'nl.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Wachtwoord herstellen',
|
||||
'auth.emails.recovery.body' => 'nl.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Uitnodiging naar %s Team voor %s',
|
||||
'auth.emails.invitation.body' => 'nl.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Team',
|
||||
'account.emails.verification.title' => 'Account Bevestiging',
|
||||
'account.emails.verification.body' => 'nl.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Wachtwoord herstellen',
|
||||
'account.emails.recovery.body' => 'nl.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Uitnodiging naar %s Team voor %s',
|
||||
'account.emails.invitation.body' => 'nl.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Onbekend',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Team',
|
||||
'auth.emails.confirm.title' => 'Bekreftelse av konto',
|
||||
'auth.emails.confirm.body' => 'no.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Reset passord',
|
||||
'auth.emails.recovery.body' => 'no.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Invitasjon til %s Team på %s',
|
||||
'auth.emails.invitation.body' => 'no.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Team',
|
||||
'account.emails.verification.title' => 'Bekreftelse av konto',
|
||||
'account.emails.verification.body' => 'no.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Reset passord',
|
||||
'account.emails.recovery.body' => 'no.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Invitasjon til %s Team på %s',
|
||||
'account.emails.invitation.body' => 'no.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Ukjent',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Pangkat ng %s',
|
||||
'auth.emails.confirm.title' => 'Pagkumpirma ng Account',
|
||||
'auth.emails.confirm.body' => 'ph.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Pagreset ng Password',
|
||||
'auth.emails.recovery.body' => 'ph.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Paanyaya sa Pangkat ng %s sa %s',
|
||||
'auth.emails.invitation.body' => 'ph.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Pangkat ng %s',
|
||||
'account.emails.verification.title' => 'Pagkumpirma ng Account',
|
||||
'account.emails.verification.body' => 'ph.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Pagreset ng Password',
|
||||
'account.emails.recovery.body' => 'ph.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Paanyaya sa Pangkat ng %s sa %s',
|
||||
'account.emails.invitation.body' => 'ph.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Hindi alam',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Zespół %s',
|
||||
'auth.emails.confirm.title' => 'Potwierdzenie konta',
|
||||
'auth.emails.confirm.body' => 'en.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Zresetowanie hasła',
|
||||
'auth.emails.recovery.body' => 'en.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Zaproszenie do zespołu %s - %s',
|
||||
'auth.emails.invitation.body' => 'en.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Zespół %s',
|
||||
'account.emails.verification.title' => 'Potwierdzenie konta',
|
||||
'account.emails.verification.body' => 'en.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Zresetowanie hasła',
|
||||
'account.emails.recovery.body' => 'en.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Zaproszenie do zespołu %s - %s',
|
||||
'account.emails.invitation.body' => 'en.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Nieznany',
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ return [
|
|||
'settings.locale' => 'pn',
|
||||
'settings.direction' => 'ltr',
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s ਟੀਮ(Priyanka)',
|
||||
'auth.emails.confirm.title' => 'ਖਾਤਾ ਪੁਸ਼ਟੀਕਰਣ',
|
||||
'auth.emails.confirm.body' => 'app/config/locales/templates/pn.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'ਪਾਸਵਰਡ ਰੀਸੈੱਟ',
|
||||
'auth.emails.recovery.body' => 'app/config/locales/templates/pn.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => '% S ਟੀਮ% s ਤੇ ਸੱਦਾ',
|
||||
'auth.emails.invitation.body' => 'app/config/locales/templates/pn.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s ਟੀਮ(Priyanka)',
|
||||
'account.emails.verification.title' => 'ਖਾਤਾ ਪੁਸ਼ਟੀਕਰਣ',
|
||||
'account.emails.verification.body' => 'app/config/locales/templates/pn.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'ਪਾਸਵਰਡ ਰੀਸੈੱਟ',
|
||||
'account.emails.recovery.body' => 'app/config/locales/templates/pn.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => '% S ਟੀਮ% s ਤੇ ਸੱਦਾ',
|
||||
'account.emails.invitation.body' => 'app/config/locales/templates/pn.email.auth.invitation.tpl',
|
||||
'locale.country.unknown' => 'India',
|
||||
'countries' => include 'pn.countries.php',
|
||||
'continents' => include 'pn.continents.php',
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Time %s',
|
||||
'auth.emails.confirm.title' => 'Confirmação de Conta',
|
||||
'auth.emails.confirm.body' => 'pt-br.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Redefinição de Senha',
|
||||
'auth.emails.recovery.body' => 'pt-br.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Convite para a Equipe %s em %s',
|
||||
'auth.emails.invitation.body' => 'pt-br.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Time %s',
|
||||
'account.emails.verification.title' => 'Confirmação de Conta',
|
||||
'account.emails.verification.body' => 'pt-br.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Redefinição de Senha',
|
||||
'account.emails.recovery.body' => 'pt-br.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Convite para a Equipe %s em %s',
|
||||
'account.emails.invitation.body' => 'pt-br.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Desconhecido',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Equipa %s',
|
||||
'auth.emails.confirm.title' => 'Confirmação de Conta',
|
||||
'auth.emails.confirm.body' => 'pt-pt.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Repor palavra-passe',
|
||||
'auth.emails.recovery.body' => 'pt-pt.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Convite para a Equipa %s em %s',
|
||||
'auth.emails.invitation.body' => 'pt-pt.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Equipa %s',
|
||||
'account.emails.verification.title' => 'Confirmação de Conta',
|
||||
'account.emails.verification.body' => 'pt-pt.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Repor palavra-passe',
|
||||
'account.emails.recovery.body' => 'pt-pt.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Convite para a Equipa %s em %s',
|
||||
'account.emails.invitation.body' => 'pt-pt.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Desconhecido',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Echipa',
|
||||
'auth.emails.confirm.title' => 'Confirmă Contul',
|
||||
'auth.emails.confirm.body' => 'ro.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Resetează Parola',
|
||||
'auth.emails.recovery.body' => 'ro.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Invitație în Echipa %s la %s',
|
||||
'auth.emails.invitation.body' => 'ro.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Echipa',
|
||||
'account.emails.verification.title' => 'Confirmă Contul',
|
||||
'account.emails.verification.body' => 'ro.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Resetează Parola',
|
||||
'account.emails.recovery.body' => 'ro.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Invitație în Echipa %s la %s',
|
||||
'account.emails.invitation.body' => 'ro.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Necunoscut',
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ return [
|
|||
'settings.locale' => 'ru',
|
||||
'settings.direction' => 'ltr',
|
||||
|
||||
'auth.emails.team' => 'Команда %s',
|
||||
'auth.emails.confirm.title' => 'Подтверждение аккаунта',
|
||||
'auth.emails.confirm.body' => 'ru.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Сброс пароля',
|
||||
'auth.emails.recovery.body' => 'ru.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Приглашение в команду %s по проекту %s',
|
||||
'auth.emails.invitation.body' => 'ru.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Команда %s',
|
||||
'account.emails.verification.title' => 'Подтверждение аккаунта',
|
||||
'account.emails.verification.body' => 'ru.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Сброс пароля',
|
||||
'account.emails.recovery.body' => 'ru.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Приглашение в команду %s по проекту %s',
|
||||
'account.emails.invitation.body' => 'ru.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Неизвестно',
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ return [
|
|||
'settings.locale' => 'si',
|
||||
'settings.direction' => 'ltr',
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s කණ්ඩායම',
|
||||
'auth.emails.confirm.title' => 'ගිණුම් තහවුරු කිරීම',
|
||||
'auth.emails.confirm.body' => 'si.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'මුරපදය යළි පිහිටුවන්න',
|
||||
'auth.emails.recovery.body' => 'si.email.auth.recovery.tpl',
|
||||
'account.emails.team' => '%s කණ්ඩායම',
|
||||
'account.emails.verification.title' => 'ගිණුම් තහවුරු කිරීම',
|
||||
'account.emails.verification.body' => 'si.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'මුරපදය යළි පිහිටුවන්න',
|
||||
'account.emails.recovery.body' => 'si.email.auth.recovery.tpl',
|
||||
|
||||
'auth.emails.invitation.title' => 'ආරාධනයයි , %s කණ්ඩායමට %s ව්යාපෘතියෙහි',
|
||||
'auth.emails.invitation.body' => 'si.email.auth.invitation.tpl',
|
||||
'account.emails.invitation.title' => 'ආරාධනයයි , %s කණ්ඩායමට %s ව්යාපෘතියෙහි',
|
||||
'account.emails.invitation.body' => 'si.email.auth.invitation.tpl',
|
||||
'locale.country.unknown' => 'Unknown',
|
||||
'countries' => include 'si.countries.php',
|
||||
'continents' => include 'si.continents.php',
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Ekipa',
|
||||
'auth.emails.confirm.title' => 'Potrditev računa',
|
||||
'auth.emails.confirm.body' => 'sl.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Ponastavitev gesla',
|
||||
'auth.emails.recovery.body' => 'sl.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Povabilo v %s Ekipo za %s',
|
||||
'auth.emails.invitation.body' => 'sl.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Ekipa',
|
||||
'account.emails.verification.title' => 'Potrditev računa',
|
||||
'account.emails.verification.body' => 'sl.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Ponastavitev gesla',
|
||||
'account.emails.recovery.body' => 'sl.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Povabilo v %s Ekipo za %s',
|
||||
'account.emails.invitation.body' => 'sl.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Neznano',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => 'Grup %s',
|
||||
'auth.emails.confirm.title' => 'Konfirmimi i llogarisë',
|
||||
'auth.emails.confirm.body' => 'sq.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Rivendosni fjalëkalimin',
|
||||
'auth.emails.recovery.body' => 'sq.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Ftesë në grup %s në %s',
|
||||
'auth.emails.invitation.body' => 'sq.email.auth.invitation.tpl',
|
||||
'account.emails.team' => 'Grup %s',
|
||||
'account.emails.verification.title' => 'Konfirmimi i llogarisë',
|
||||
'account.emails.verification.body' => 'sq.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Rivendosni fjalëkalimin',
|
||||
'account.emails.recovery.body' => 'sq.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Ftesë në grup %s në %s',
|
||||
'account.emails.invitation.body' => 'sq.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'I panjohur',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s-teamet',
|
||||
'auth.emails.confirm.title' => 'Kontobekräftelse',
|
||||
'auth.emails.confirm.body' => 'sv.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Ändra lösenord',
|
||||
'auth.emails.recovery.body' => 'sv.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Inbjudan till %s-teamet i %s',
|
||||
'auth.emails.invitation.body' => 'sv.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s-teamet',
|
||||
'account.emails.verification.title' => 'Kontobekräftelse',
|
||||
'account.emails.verification.body' => 'sv.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Ändra lösenord',
|
||||
'account.emails.recovery.body' => 'sv.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Inbjudan till %s-teamet i %s',
|
||||
'account.emails.invitation.body' => 'sv.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Okänt',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s குழு',
|
||||
'auth.emails.confirm.title' => 'கணக்கினை உறுதி செய்தல்',
|
||||
'auth.emails.confirm.body' => 'ta.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'கடவுச்சொல் மீட்டமைப்பு',
|
||||
'auth.emails.recovery.body' => 'ta.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => '%s குழுவின் அழைப்பு: %s',
|
||||
'auth.emails.invitation.body' => 'ta.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s குழு',
|
||||
'account.emails.verification.title' => 'கணக்கினை உறுதி செய்தல்',
|
||||
'account.emails.verification.body' => 'ta.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'கடவுச்சொல் மீட்டமைப்பு',
|
||||
'account.emails.recovery.body' => 'ta.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => '%s குழுவின் அழைப்பு: %s',
|
||||
'account.emails.invitation.body' => 'ta.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'அறியவில்லை',
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<a href="{{redirect}}">{{redirect}}</a>
|
||||
<br />
|
||||
<br />
|
||||
If you didn’t ask to reset your password, you can ignore this message.
|
||||
If you didn't ask to reset your password, you can ignore this message.
|
||||
<br />
|
||||
<br />
|
||||
Thanks,
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s ทีม',
|
||||
'auth.emails.confirm.title' => 'ยืนยันบัญชี',
|
||||
'auth.emails.confirm.body' => 'th.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'รีเซ็ตรหัสผ่าน',
|
||||
'auth.emails.recovery.body' => 'th.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'ขอเชิญเข้าร่วม %s ทีมที่ %s',
|
||||
'auth.emails.invitation.body' => 'th.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s ทีม',
|
||||
'account.emails.verification.title' => 'ยืนยันบัญชี',
|
||||
'account.emails.verification.body' => 'th.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'รีเซ็ตรหัสผ่าน',
|
||||
'account.emails.recovery.body' => 'th.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'ขอเชิญเข้าร่วม %s ทีมที่ %s',
|
||||
'account.emails.invitation.body' => 'th.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'ไม่ทราบ',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Takımı',
|
||||
'auth.emails.confirm.title' => 'Hesap Doğrulama',
|
||||
'auth.emails.confirm.body' => 'tr.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Şifre Sıfırlama',
|
||||
'auth.emails.recovery.body' => 'tr.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => '%s takımına davet %s',
|
||||
'auth.emails.invitation.body' => 'tr.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Takımı',
|
||||
'account.emails.verification.title' => 'Hesap Doğrulama',
|
||||
'account.emails.verification.body' => 'tr.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Şifre Sıfırlama',
|
||||
'account.emails.recovery.body' => 'tr.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => '%s takımına davet %s',
|
||||
'account.emails.invitation.body' => 'tr.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Bilinmeyen',
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ return [
|
|||
'settings.locale' => 'ua',
|
||||
'settings.direction' => 'ltr',
|
||||
|
||||
'auth.emails.team' => '%s Команда',
|
||||
'auth.emails.confirm.title' => 'Підтвердження Акаунту' ,
|
||||
'auth.emails.confirm.body' => 'ua.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Скидання пароля',
|
||||
'auth.emails.recovery.body' => 'ua.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Запрошення до %s Команди у %s',
|
||||
'auth.emails.invitation.body' => 'ua.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Команда',
|
||||
'account.emails.verification.title' => 'Підтвердження Акаунту' ,
|
||||
'account.emails.verification.body' => 'ua.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Скидання пароля',
|
||||
'account.emails.recovery.body' => 'ua.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Запрошення до %s Команди у %s',
|
||||
'account.emails.invitation.body' => 'ua.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Невідомо',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s Team',
|
||||
'auth.emails.confirm.title' => 'Xác nhận tài khoản',
|
||||
'auth.emails.confirm.body' => 'vi.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => 'Đặt lại mật khẩu',
|
||||
'auth.emails.recovery.body' => 'vi.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => 'Lời mới vào %s Team ở %s',
|
||||
'auth.emails.invitation.body' => 'vi.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s Team',
|
||||
'account.emails.verification.title' => 'Xác nhận tài khoản',
|
||||
'account.emails.verification.body' => 'vi.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => 'Đặt lại mật khẩu',
|
||||
'account.emails.recovery.body' => 'vi.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => 'Lời mới vào %s Team ở %s',
|
||||
'account.emails.invitation.body' => 'vi.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => 'Chưa xác định',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s 小组',
|
||||
'auth.emails.confirm.title' => '账户确认',
|
||||
'auth.emails.confirm.body' => 'zh.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => '重设密码',
|
||||
'auth.emails.recovery.body' => 'zh.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => '邀请加入%s小组(%s)', // I used brackets to keep the order
|
||||
'auth.emails.invitation.body' => 'zh.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s 小组',
|
||||
'account.emails.verification.title' => '账户确认',
|
||||
'account.emails.verification.body' => 'zh.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => '重设密码',
|
||||
'account.emails.recovery.body' => 'zh.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => '邀请加入%s小组(%s)', // I used brackets to keep the order
|
||||
'account.emails.invitation.body' => 'zh.email.auth.invitation.tpl',
|
||||
|
||||
'locale.country.unknown' => '未知',
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ return [
|
|||
'settings.direction' => 'ltr',
|
||||
|
||||
// Service - Users
|
||||
'auth.emails.team' => '%s 小組',
|
||||
'auth.emails.confirm.title' => '賬戶確認',
|
||||
'auth.emails.confirm.body' => 'zh-tw.email.auth.confirm.tpl',
|
||||
'auth.emails.recovery.title' => '重設密碼',
|
||||
'auth.emails.recovery.body' => 'zh.email.auth.recovery.tpl',
|
||||
'auth.emails.invitation.title' => '邀請加入%s小組(%s)',
|
||||
'auth.emails.invitation.body' => 'zh-tw.email.auth.invitation.tpl',
|
||||
'account.emails.team' => '%s 小組',
|
||||
'account.emails.verification.title' => '賬戶確認',
|
||||
'account.emails.verification.body' => 'zh-tw.email.auth.confirm.tpl',
|
||||
'account.emails.recovery.title' => '重設密碼',
|
||||
'account.emails.recovery.body' => 'zh.email.auth.recovery.tpl',
|
||||
'account.emails.invitation.title' => '邀請加入%s小組(%s)',
|
||||
'account.emails.invitation.body' => 'zh-tw.email.auth.invitation.tpl',
|
||||
'locale.country.unknown' => '未知',
|
||||
'countries' => include 'zh-tw.countries.php',
|
||||
'continents' => include 'zh-tw.continents.php',
|
||||
|
|
|
|||
|
|
@ -5,135 +5,243 @@ const APP_PLATFORM_IOS = 'ios';
|
|||
const APP_PLATFORM_ANDROID = 'android';
|
||||
const APP_PLATFORM_UNITY = 'unity';
|
||||
const APP_PLATFORM_FLUTTER = 'flutter';
|
||||
|
||||
const APP_PLATFORM_SERVER = 'server';
|
||||
const APP_PLATFORM_CLIENT = 'client';
|
||||
const APP_PLATFORM_CONSOLE = 'console';
|
||||
|
||||
return [
|
||||
APP_PLATFORM_WEB => [
|
||||
'key' => APP_PLATFORM_WEB,
|
||||
'name' => 'Web',
|
||||
'description' => 'Client libraries for integrating with '.APP_NAME.' to build web-based applications and websites. Read the [getting started for web](/docs/getting-started-for-web) tutorial to start building your first web application.',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'languages' => [
|
||||
[
|
||||
'key' => 'javascript',
|
||||
'name' => 'JS',
|
||||
'repository' => 'https://github.com/appwrite/sdk-for-js',
|
||||
'name' => 'JavaScript',
|
||||
'version' => '1.0.28',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-js',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
'prism' => 'javascript',
|
||||
'source' => realpath(__DIR__ . '/../sdks/js'),
|
||||
'source' => realpath(__DIR__ . '/../sdks/javascript'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-js.git',
|
||||
'gitRepoName' => 'sdk-for-js',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
[
|
||||
'key' => 'typescript',
|
||||
'name' => 'TypeScript',
|
||||
'repository' => '',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'url' => '',
|
||||
'enabled' => false,
|
||||
'beta' => true,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
'prism' => 'typescript',
|
||||
'source' => '',
|
||||
'source' => false,
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-typescript.git',
|
||||
'gitRepoName' => 'sdk-for-typescript',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
APP_PLATFORM_IOS => [
|
||||
'key' => APP_PLATFORM_IOS,
|
||||
'name' => 'iOS',
|
||||
'description' => 'Client libraries for integrating with '.APP_NAME.' to build iOS applications. Read the [getting started for iOS](/docs/getting-started-for-ios) tutorial to start building your first iOS application.',
|
||||
'enabled' => false,
|
||||
'beta' => false,
|
||||
'languages' => [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'repository' => '',
|
||||
'url' => '',
|
||||
'enabled' => false,
|
||||
'beta' => false,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
'prism' => 'swift',
|
||||
'source' => '',
|
||||
'source' => false,
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-swift.git',
|
||||
'gitRepoName' => 'sdk-for-swift',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
[
|
||||
'key' => 'objective-c',
|
||||
'name' => 'Objective C',
|
||||
'repository' => '',
|
||||
'url' => '',
|
||||
'enabled' => false,
|
||||
'beta' => false,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
'prism' => '',
|
||||
'source' => '',
|
||||
'source' => false,
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-objective-c.git',
|
||||
'gitRepoName' => 'sdk-for-objective-c',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
APP_PLATFORM_ANDROID => [
|
||||
'key' => APP_PLATFORM_ANDROID,
|
||||
'name' => 'Android',
|
||||
'description' => 'Client libraries for integrating with '.APP_NAME.' to build Android applications. Read the [getting started for Android](/docs/getting-started-for-android) tutorial to start building your first Android application.',
|
||||
'enabled' => false,
|
||||
'beta' => false,
|
||||
'languages' => [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'repository' => '',
|
||||
'url' => '',
|
||||
'enabled' => false,
|
||||
'beta' => false,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
'prism' => 'kotlin',
|
||||
'source' => false,
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-kotlin.git',
|
||||
'gitRepoName' => 'sdk-for-kotlin',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
[
|
||||
'key' => 'java',
|
||||
'name' => 'Java',
|
||||
'repository' => '',
|
||||
'url' => '',
|
||||
'enabled' => false,
|
||||
'beta' => false,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
'prism' => 'java',
|
||||
'source' => false,
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-java.git',
|
||||
'gitRepoName' => 'sdk-for-java',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
APP_PLATFORM_FLUTTER => [
|
||||
'key' => APP_PLATFORM_FLUTTER,
|
||||
'name' => 'Flutter',
|
||||
'description' => 'Client libraries for integrating with '.APP_NAME.' to build cross-platform Flutter applications. Read the [getting started for Flutter](/docs/getting-started-for-flutter) tutorial to start building your first Flutter application.',
|
||||
'enabled' => false,
|
||||
'beta' => true,
|
||||
'languages' => [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '0.0.6',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'family' => APP_PLATFORM_CLIENT,
|
||||
'prism' => 'dart',
|
||||
'source' => realpath(__DIR__ . '/../sdks/dart'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-dart.git',
|
||||
'gitRepoName' => 'sdk-for-dart',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
// APP_PLATFORM_CONSOLE => [
|
||||
// 'name' => 'Console',
|
||||
// 'enabled' => false,
|
||||
// 'beta' => false,
|
||||
// 'languages' => [
|
||||
// [
|
||||
// 'key' => 'javascript',
|
||||
// 'name' => 'JS',
|
||||
// 'version' => '1.0.0',
|
||||
// 'url' => 'https://github.com/appwrite/sdk-for-console',
|
||||
// 'enabled' => true,
|
||||
// 'beta' => false,
|
||||
// 'family' => APP_PLATFORM_CONSOLE,
|
||||
// 'prism' => 'console',
|
||||
// 'source' => realpath(__DIR__ . '/../sdks/console'),
|
||||
// 'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git',
|
||||
// 'gitRepoName' => 'sdk-for-console',
|
||||
// 'gitUserName' => 'appwrite',
|
||||
// ],
|
||||
// ],
|
||||
// ],
|
||||
|
||||
APP_PLATFORM_SERVER => [
|
||||
'key' => APP_PLATFORM_SERVER,
|
||||
'name' => 'Server',
|
||||
'description' => 'Libraries for integrating with '.APP_NAME.' to build server side integrations. Read the [getting started for server](/docs/getting-started-for-server) tutorial to start building your first server integration.',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'languages' => [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'repository' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'version' => '1.0.31',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'family' => APP_PLATFORM_SERVER,
|
||||
'prism' => 'javascript',
|
||||
'source' => realpath(__DIR__ . '/../sdks/node'),
|
||||
'source' => realpath(__DIR__ . '/../sdks/nodejs'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-node.git',
|
||||
'gitRepoName' => 'sdk-for-node',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'repository' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'version' => '1.0.16',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'family' => APP_PLATFORM_SERVER,
|
||||
'prism' => 'php',
|
||||
'source' => realpath(__DIR__ . '/../sdks/php'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-php.git',
|
||||
'gitRepoName' => 'sdk-for-php',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'repository' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'version' => '0.0.3',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'family' => APP_PLATFORM_SERVER,
|
||||
'prism' => 'python',
|
||||
'source' => realpath(__DIR__ . '/../sdks/python'),
|
||||
],
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'repository' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'prism' => 'go',
|
||||
'source' => realpath(__DIR__ . '/../sdks/go'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-python.git',
|
||||
'gitRepoName' => 'sdk-for-python',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'repository' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'version' => '1.0.8',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'family' => APP_PLATFORM_SERVER,
|
||||
'prism' => 'ruby',
|
||||
'source' => realpath(__DIR__ . '/../sdks/ruby'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-ruby.git',
|
||||
'gitRepoName' => 'sdk-for-ruby',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => '0.0.5',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'family' => APP_PLATFORM_SERVER,
|
||||
'prism' => 'go',
|
||||
'source' => realpath(__DIR__ . '/../sdks/go'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-go.git',
|
||||
'gitRepoName' => 'sdk-for-go',
|
||||
'gitUserName' => 'appwrite',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ $logged = [
|
|||
'public',
|
||||
'home',
|
||||
'console',
|
||||
'auth',
|
||||
'account',
|
||||
'teams.read',
|
||||
'teams.write',
|
||||
|
|
@ -29,10 +28,27 @@ $logged = [
|
|||
];
|
||||
|
||||
$admins = [
|
||||
'teams.read',
|
||||
'teams.write',
|
||||
'documents.read',
|
||||
'documents.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
'users.read',
|
||||
'users.write',
|
||||
'collections.read',
|
||||
'collections.write',
|
||||
'platforms.read',
|
||||
'platforms.write',
|
||||
'keys.read',
|
||||
'keys.write',
|
||||
'tasks.read',
|
||||
'tasks.write',
|
||||
'webhooks.read',
|
||||
'webhooks.write',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'health.read',
|
||||
];
|
||||
|
||||
return [
|
||||
|
|
@ -42,7 +58,6 @@ return [
|
|||
'public',
|
||||
'home',
|
||||
'console',
|
||||
'auth',
|
||||
'files.read',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
|
|
@ -55,11 +70,11 @@ return [
|
|||
],
|
||||
ROLE_ADMIN => [
|
||||
'label' => 'Admin',
|
||||
'scopes' => array_merge($logged, $admins, []),
|
||||
'scopes' => array_merge($admins, []),
|
||||
],
|
||||
ROLE_DEVELOPER => [
|
||||
'label' => 'Developer',
|
||||
'scopes' => array_merge($logged, $admins, []),
|
||||
'scopes' => array_merge($admins, []),
|
||||
],
|
||||
ROLE_OWNER => [
|
||||
'label' => 'Owner',
|
||||
|
|
@ -67,6 +82,6 @@ return [
|
|||
],
|
||||
ROLE_APP => [
|
||||
'label' => 'Application',
|
||||
'scopes' => ['public'],
|
||||
'scopes' => ['health.read'],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
24
app/config/scopes.php
Normal file
24
app/config/scopes.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
return [ // List of publicly visible scopes
|
||||
'users.read',
|
||||
'users.write',
|
||||
'teams.read',
|
||||
'teams.write',
|
||||
'collections.read',
|
||||
'collections.write',
|
||||
'documents.read',
|
||||
'documents.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
// 'platforms.read',
|
||||
// 'platforms.write',
|
||||
// 'keys.read',
|
||||
// 'keys.write',
|
||||
// 'tasks.read',
|
||||
// 'tasks.write',
|
||||
// 'webhooks.read',
|
||||
// 'webhooks.write',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
];;
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
'label' => 'JS',
|
||||
'versions' => ['v1.0.0'],
|
||||
'logo' => 'js.svg',
|
||||
'link' => 'https://github.com/appwrite/sdk-for-js',
|
||||
],
|
||||
[
|
||||
'label' => 'Node.js',
|
||||
'versions' => ['v1.0.0'],
|
||||
'logo' => 'nodejs.svg',
|
||||
'link' => 'https://github.com/appwrite/sdk-for-node',
|
||||
],
|
||||
[
|
||||
'label' => 'Ruby',
|
||||
'versions' => ['v1.0.0'],
|
||||
'logo' => 'ruby.svg',
|
||||
'link' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
],
|
||||
[
|
||||
'label' => 'Python',
|
||||
'versions' => ['v1.0.0'],
|
||||
'logo' => 'python.svg',
|
||||
'link' => 'https://github.com/appwrite/sdk-for-python',
|
||||
],
|
||||
[
|
||||
'label' => 'PHP',
|
||||
'versions' => ['v1.0.0'],
|
||||
'logo' => 'php.svg',
|
||||
'link' => 'https://github.com/appwrite/sdk-for-php',
|
||||
],
|
||||
[
|
||||
'label' => 'Go',
|
||||
'versions' => [],
|
||||
'logo' => 'go.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
[
|
||||
'label' => 'Scala',
|
||||
'versions' => [],
|
||||
'logo' => 'scala.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
[
|
||||
'label' => 'Swift',
|
||||
'versions' => [],
|
||||
'logo' => 'swift.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
[
|
||||
'label' => 'C',
|
||||
'versions' => [],
|
||||
'logo' => 'c.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
[
|
||||
'label' => 'C#',
|
||||
'versions' => [],
|
||||
'logo' => 'csharp.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
[
|
||||
'label' => 'Erlang',
|
||||
'versions' => [],
|
||||
'logo' => 'erlang.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
[
|
||||
'label' => 'Kotlin',
|
||||
'versions' => [],
|
||||
'logo' => 'kotlin.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
[
|
||||
'label' => 'Java',
|
||||
'versions' => [],
|
||||
'logo' => 'java.svg',
|
||||
'link' => 'https://...',
|
||||
'tag' => 'Soon',
|
||||
],
|
||||
];
|
||||
|
|
@ -3,81 +3,74 @@
|
|||
return [
|
||||
'/' => [
|
||||
'name' => 'Homepage',
|
||||
'controller' => 'controllers/home.php',
|
||||
'controller' => 'controllers/web/home.php',
|
||||
'sdk' => false,
|
||||
'tests' => false,
|
||||
],
|
||||
'console/' => [
|
||||
'name' => 'Console',
|
||||
'controller' => 'controllers/console.php',
|
||||
'controller' => 'controllers/web/console.php',
|
||||
'sdk' => false,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/account' => [
|
||||
'name' => 'Account',
|
||||
'description' => '/docs/services/account.md',
|
||||
'controller' => 'controllers/account.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/auth' => [ // Add to docs later: You can also learn how to [configure support for our supported OAuth providers](/docs/oauth)
|
||||
'name' => 'Auth',
|
||||
'description' => '/docs/services/auth.md',
|
||||
'controller' => 'controllers/auth.php',
|
||||
'controller' => 'controllers/api/account.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/avatars' => [
|
||||
'name' => 'Avatars',
|
||||
'description' => '/docs/services/avatars.md',
|
||||
'controller' => 'controllers/avatars.php',
|
||||
'controller' => 'controllers/api/avatars.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/database' => [
|
||||
'name' => 'Database',
|
||||
'description' => '/docs/services/database.md',
|
||||
'controller' => 'controllers/database.php',
|
||||
'controller' => 'controllers/api/database.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/locale' => [
|
||||
'name' => 'Locale',
|
||||
'description' => '/docs/services/locale.md',
|
||||
'controller' => 'controllers/locale.php',
|
||||
'controller' => 'controllers/api/locale.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/health' => [
|
||||
'name' => 'Health',
|
||||
'controller' => 'controllers/health.php',
|
||||
'controller' => 'controllers/api/health.php',
|
||||
'sdk' => false,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/projects' => [
|
||||
'name' => 'Projects',
|
||||
'controller' => 'controllers/projects.php',
|
||||
'controller' => 'controllers/api/projects.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/storage' => [
|
||||
'name' => 'Storage',
|
||||
'description' => '/docs/services/storage.md',
|
||||
'controller' => 'controllers/storage.php',
|
||||
'controller' => 'controllers/api/storage.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/teams' => [
|
||||
'name' => 'Teams',
|
||||
'description' => '/docs/services/teams.md',
|
||||
'controller' => 'controllers/teams.php',
|
||||
'controller' => 'controllers/api/teams.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
'v1/users' => [
|
||||
'name' => 'Users',
|
||||
'description' => '/docs/services/users.md',
|
||||
'controller' => 'controllers/users.php',
|
||||
'controller' => 'controllers/api/users.php',
|
||||
'sdk' => true,
|
||||
'tests' => false,
|
||||
],
|
||||
|
|
@ -88,4 +81,11 @@ return [
|
|||
'sdk' => false,
|
||||
'tests' => true,
|
||||
],
|
||||
'v1/graphql' => [
|
||||
'name' => 'GraphQL',
|
||||
'description' => 'GraphQL Endpoint',
|
||||
'controller' => 'controllers/api/graphql.php',
|
||||
'sdk' => false,
|
||||
'tests' => false,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,357 +0,0 @@
|
|||
<?php
|
||||
|
||||
global $utopia, $register, $response, $user, $audit, $project, $projectDB, $providers;
|
||||
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Email;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
|
||||
use Utopia\Locale\Locale;
|
||||
use Auth\Auth;
|
||||
use Auth\Validator\Password;
|
||||
use Database\Database;
|
||||
use Database\Validator\Authorization;
|
||||
use DeviceDetector\DeviceDetector;
|
||||
use GeoIp2\Database\Reader;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
|
||||
$utopia->get('/v1/account')
|
||||
->desc('Get Account')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'get')
|
||||
->label('sdk.description', '/docs/references/account/get.md')
|
||||
->action(
|
||||
function () use ($response, &$user, $providers) {
|
||||
$oauthKeys = [];
|
||||
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider['enabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key);
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
|
||||
}
|
||||
|
||||
$response->json(array_merge($user->getArrayCopy(array_merge(
|
||||
[
|
||||
'$uid',
|
||||
'email',
|
||||
'registration',
|
||||
'confirm',
|
||||
'name',
|
||||
],
|
||||
$oauthKeys
|
||||
)), ['roles' => Authorization::getRoles()]));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/account/prefs')
|
||||
->desc('Get Account Preferences')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'getPrefs')
|
||||
->label('sdk.description', '/docs/references/account/get-prefs.md')
|
||||
->action(
|
||||
function () use ($response, $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);
|
||||
}
|
||||
|
||||
$response->json($prefs);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/account/sessions')
|
||||
->desc('Get Account Active Sessions')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'getSessions')
|
||||
->label('sdk.description', '/docs/references/account/get-sessions.md')
|
||||
->action(
|
||||
function () use ($response, $user) {
|
||||
$tokens = $user->getAttribute('tokens', []);
|
||||
$reader = new Reader(__DIR__.'/../db/DBIP/dbip-country-lite-2020-01.mmdb');
|
||||
$sessions = [];
|
||||
$current = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
|
||||
$index = 0;
|
||||
$countries = Locale::getText('countries');
|
||||
|
||||
foreach ($tokens as $token) { /* @var $token Document */
|
||||
if (Auth::TOKEN_TYPE_LOGIN != $token->getAttribute('type')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$userAgent = (!empty($token->getAttribute('userAgent'))) ? $token->getAttribute('userAgent') : 'UNKNOWN';
|
||||
|
||||
$dd = new DeviceDetector($userAgent);
|
||||
|
||||
// OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
// $dd->skipBotDetection();
|
||||
|
||||
$dd->parse();
|
||||
|
||||
$sessions[$index] = [
|
||||
'id' => $token->getUid(),
|
||||
'OS' => $dd->getOs(),
|
||||
'client' => $dd->getClient(),
|
||||
'device' => $dd->getDevice(),
|
||||
'brand' => $dd->getBrand(),
|
||||
'model' => $dd->getModel(),
|
||||
'ip' => $token->getAttribute('ip', ''),
|
||||
'geo' => [],
|
||||
'current' => ($current == $token->getUid()) ? true : false,
|
||||
];
|
||||
|
||||
try {
|
||||
$record = $reader->country($token->getAttribute('ip', ''));
|
||||
$sessions[$index]['geo']['isoCode'] = strtolower($record->country->isoCode);
|
||||
$sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
|
||||
} catch (\Exception $e) {
|
||||
$sessions[$index]['geo']['isoCode'] = '--';
|
||||
$sessions[$index]['geo']['country'] = Locale::getText('locale.country.unknown');
|
||||
}
|
||||
|
||||
++$index;
|
||||
}
|
||||
|
||||
$response->json($sessions);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/account/security')
|
||||
->desc('Get Account Security Log')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'getSecurity')
|
||||
->label('sdk.description', '/docs/references/account/get-security.md')
|
||||
->action(
|
||||
function () use ($response, $register, $project, $user) {
|
||||
$adapter = new AuditAdapter($register->get('db'));
|
||||
$adapter->setNamespace('app_'.$project->getUid());
|
||||
$audit = new Audit($adapter);
|
||||
$countries = Locale::getText('countries');
|
||||
|
||||
$logs = $audit->getLogsByUserAndActions($user->getUid(), [
|
||||
'auth.register',
|
||||
'auth.confirm',
|
||||
'auth.login',
|
||||
'auth.logout',
|
||||
'auth.recovery',
|
||||
'auth.recovery.reset',
|
||||
'auth.oauth.login',
|
||||
'auth.invite',
|
||||
'auth.join',
|
||||
'auth.leave',
|
||||
'account.delete',
|
||||
'account.update.name',
|
||||
'account.update.email',
|
||||
'account.update.password',
|
||||
]);
|
||||
|
||||
$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('79.176.229.216');
|
||||
$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);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/account/name')
|
||||
->desc('Update Account Name')
|
||||
->label('webhook', 'account.update-name')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateName')
|
||||
->label('sdk.description', '/docs/references/account/update-name.md')
|
||||
->param('name', '', function () { return new Text(100); }, 'User name')
|
||||
->action(
|
||||
function ($name) use ($response, $user, $projectDB, $audit) {
|
||||
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
|
||||
'name' => $name,
|
||||
]));
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$audit->setParam('event', 'account.update.name');
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/account/password')
|
||||
->desc('Update Account Password')
|
||||
->label('webhook', 'account.update-password')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePassword')
|
||||
->label('sdk.description', '/docs/references/account/update-password.md')
|
||||
->param('password', '', function () { return new Password(); }, 'New password')
|
||||
->param('old-password', '', function () { return new Password(); }, 'Old password')
|
||||
->action(
|
||||
function ($password, $oldPassword) use ($response, $user, $projectDB, $audit) {
|
||||
if (!Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password
|
||||
throw new Exception('Invalid credentials', 401);
|
||||
}
|
||||
|
||||
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
|
||||
'password' => Auth::passwordHash($password),
|
||||
]));
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$audit->setParam('event', 'account.update.password');
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/account/email')
|
||||
->desc('Update Account Email')
|
||||
->label('webhook', 'account.update-email')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updateEmail')
|
||||
->label('sdk.description', '/docs/references/account/update-email.md')
|
||||
->param('email', '', function () { return new Email(); }, 'Email Address')
|
||||
->param('password', '', function () { return new Password(); }, 'User Password')
|
||||
->action(
|
||||
function ($email, $password) use ($response, $user, $projectDB, $audit) {
|
||||
if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password
|
||||
throw new Exception('Invalid credentials', 401);
|
||||
}
|
||||
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'email='.$email,
|
||||
],
|
||||
]);
|
||||
|
||||
if (!empty($profile)) {
|
||||
throw new Exception('User already registered', 400);
|
||||
}
|
||||
|
||||
// TODO after this user needs to confirm mail again
|
||||
|
||||
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
|
||||
'email' => $email,
|
||||
]));
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$audit->setParam('event', 'account.update.email');
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/account/prefs')
|
||||
->desc('Update Account Prefs')
|
||||
->label('webhook', 'account')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePrefs')
|
||||
->param('prefs', '', function () { return new Assoc();}, 'Prefs key-value JSON object.')
|
||||
->label('sdk.description', '/docs/references/account/update-prefs.md')
|
||||
->action(
|
||||
function ($prefs) use ($response, $user, $projectDB, $audit) {
|
||||
$old = json_decode($user->getAttribute('prefs', '{}'), true);
|
||||
$old = ($old) ? $old : [];
|
||||
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
|
||||
'prefs' => json_encode(array_merge($old, $prefs)),
|
||||
]));
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$audit->setParam('event', 'account.update.prefs');
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->delete('/v1/account')
|
||||
->desc('Delete Account')
|
||||
->label('webhook', 'account.delete')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'delete')
|
||||
->label('sdk.description', '/docs/references/account/delete.md')
|
||||
->action(
|
||||
function () use ($response, $request, $user, $projectDB, $audit) {
|
||||
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
|
||||
'status' => Auth::USER_STATUS_BLOCKED,
|
||||
]));
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
//TODO delete all tokens or only current session?
|
||||
//TODO delete all user data according to GDPR. Make sure everything is backed up and backups are deleted later
|
||||
/*
|
||||
* Data to delete
|
||||
* * Tokens
|
||||
* * Memberships
|
||||
*/
|
||||
|
||||
$audit
|
||||
->setParam('event', 'account.delete')
|
||||
->setParam('data', $user->getArrayCopy())
|
||||
;
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
|
||||
->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
1248
app/controllers/api/account.php
Normal file
1248
app/controllers/api/account.php
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -15,12 +15,12 @@ use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
|
|||
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
|
||||
use BaconQrCode\Writer;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
$types = [
|
||||
'browsers' => include __DIR__.'/../config/avatars/browsers.php',
|
||||
'credit-cards' => include __DIR__.'/../config/avatars/credit-cards.php',
|
||||
'flags' => include __DIR__.'/../config/avatars/flags.php',
|
||||
'browsers' => include __DIR__.'/../../config/avatars/browsers.php',
|
||||
'credit-cards' => include __DIR__.'/../../config/avatars/credit-cards.php',
|
||||
'flags' => include __DIR__.'/../../config/avatars/flags.php',
|
||||
];
|
||||
|
||||
$avatarCallback = function ($type, $code, $width, $height, $quality) use ($types, $response, $request) {
|
||||
|
|
@ -90,10 +90,11 @@ $avatarCallback = function ($type, $code, $width, $height, $quality) use ($types
|
|||
$utopia->get('/v1/avatars/credit-cards/:code')
|
||||
->desc('Get Credit Card Icon')
|
||||
->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['credit-cards'])); }, 'Credit Card Code. Possible values: '.implode(', ', array_keys($types['credit-cards'])).'.')
|
||||
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100', true)
|
||||
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100', true)
|
||||
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'avatars')
|
||||
->label('sdk.method', 'getCreditCard')
|
||||
->label('sdk.description', '/docs/references/avatars/get-credit-card.md')
|
||||
|
|
@ -103,10 +104,11 @@ $utopia->get('/v1/avatars/credit-cards/:code')
|
|||
$utopia->get('/v1/avatars/browsers/:code')
|
||||
->desc('Get Browser Icon')
|
||||
->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['browsers'])); }, 'Browser Code.')
|
||||
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100', true)
|
||||
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100', true)
|
||||
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'avatars')
|
||||
->label('sdk.method', 'getBrowser')
|
||||
->label('sdk.description', '/docs/references/avatars/get-browser.md')
|
||||
|
|
@ -116,10 +118,11 @@ $utopia->get('/v1/avatars/browsers/:code')
|
|||
$utopia->get('/v1/avatars/flags/:code')
|
||||
->desc('Get Country Flag')
|
||||
->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['flags'])); }, 'Country Code. ISO Alpha-2 country code format.')
|
||||
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100', true)
|
||||
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100', true)
|
||||
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'avatars')
|
||||
->label('sdk.method', 'getFlag')
|
||||
->label('sdk.description', '/docs/references/avatars/get-flag.md')
|
||||
|
|
@ -129,9 +132,10 @@ $utopia->get('/v1/avatars/flags/:code')
|
|||
$utopia->get('/v1/avatars/image')
|
||||
->desc('Get Image from URL')
|
||||
->param('url', '', function () { return new URL(); }, 'Image URL which you want to crop.')
|
||||
->param('width', 400, function () { return new Range(0, 2000); }, 'Resize preview image width, Pass an integer between 0 to 4000', true)
|
||||
->param('height', 400, function () { return new Range(0, 2000); }, 'Resize preview image height, Pass an integer between 0 to 4000', true)
|
||||
->param('width', 400, function () { return new Range(0, 2000); }, 'Resize preview image width, Pass an integer between 0 to 2000.', true)
|
||||
->param('height', 400, function () { return new Range(0, 2000); }, 'Resize preview image height, Pass an integer between 0 to 2000.', true)
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'avatars')
|
||||
->label('sdk.method', 'getImage')
|
||||
->label('sdk.description', '/docs/references/avatars/get-image.md')
|
||||
|
|
@ -197,6 +201,7 @@ $utopia->get('/v1/avatars/favicon')
|
|||
->desc('Get Favicon')
|
||||
->param('url', '', function () { return new URL(); }, 'Website URL which you want to fetch the favicon from.')
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'avatars')
|
||||
->label('sdk.method', 'getFavicon')
|
||||
->label('sdk.description', '/docs/references/avatars/get-favicon.md')
|
||||
|
|
@ -344,11 +349,12 @@ $utopia->get('/v1/avatars/favicon')
|
|||
|
||||
$utopia->get('/v1/avatars/qr')
|
||||
->desc('Get QR Code')
|
||||
->param('text', '', function () { return new Text(512); }, 'Plain text to be converted to QR code image')
|
||||
->param('text', '', function () { return new Text(512); }, 'Plain text to be converted to QR code image.')
|
||||
->param('size', 400, function () { return new Range(0, 1000); }, 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
|
||||
->param('margin', 1, function () { return new Range(0, 10); }, 'Margin From Edge. Pass an integer between 0 to 10. Defaults to 1.', true)
|
||||
->param('download', 0, function () { return new Range(0, 1); }, 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'avatars')
|
||||
->label('sdk.method', 'getQR')
|
||||
->label('sdk.description', '/docs/references/avatars/get-qr.md')
|
||||
|
|
@ -19,87 +19,22 @@ use Database\Validator\Authorization;
|
|||
use Database\Exception\Authorization as AuthorizationException;
|
||||
use Database\Exception\Structure as StructureException;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
$isDev = (App::ENV_TYPE_PRODUCTION !== $utopia->getEnv());
|
||||
|
||||
$utopia->get('/v1/database')
|
||||
->desc('List Collections')
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.method', 'listCollections')
|
||||
->label('sdk.description', '/docs/references/database/list-collections.md')
|
||||
->param('search', '', function () { return new Text(256); }, 'Search term to filter your list results.', true)
|
||||
->param('limit', 25, function () { return new Range(0, 100); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, function () { return new Range(0, 40000); }, 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('orderType', 'ASC', function () { return new WhiteList(['ASC', 'DESC']); }, 'Order result by ASC or DESC order.', true)
|
||||
->action(
|
||||
function ($search, $limit, $offset, $orderType) use ($response, $projectDB) {
|
||||
/*$vl = new Structure($projectDB);
|
||||
|
||||
var_dump($vl->isValid(new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['*'],
|
||||
],
|
||||
'label' => 'Platforms',
|
||||
'key' => 'platforms',
|
||||
'type' => 'document',
|
||||
'default' => [],
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
'options' => [Database::SYSTEM_COLLECTION_PLATFORMS],
|
||||
])));
|
||||
|
||||
var_dump($vl->getDescription());*/
|
||||
|
||||
$results = $projectDB->getCollection([
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'orderField' => 'name',
|
||||
'orderType' => $orderType,
|
||||
'orderCast' => 'string',
|
||||
'search' => $search,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
],
|
||||
]);
|
||||
|
||||
$response->json(['sum' => $projectDB->getSum(), 'collections' => $results]);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/database/:collectionId')
|
||||
->desc('Get Collection')
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.method', 'getCollection')
|
||||
->label('sdk.description', '/docs/references/database/get-collection.md')
|
||||
->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
|
||||
->action(
|
||||
function ($collectionId) use ($response, $projectDB) {
|
||||
$collection = $projectDB->getDocument($collectionId, false);
|
||||
|
||||
if (empty($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$response->json($collection->getArrayCopy());
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/database')
|
||||
$utopia->post('/v1/database/collections')
|
||||
->desc('Create Collection')
|
||||
->label('webhook', 'database.collections.create')
|
||||
->label('scope', 'collections.write')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'createCollection')
|
||||
->label('sdk.description', '/docs/references/database/create-collection.md')
|
||||
->param('name', '', function () { return new Text(256); }, 'Collection name.')
|
||||
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('rules', [], function () use ($projectDB) { return new ArrayList(new Collection($projectDB, [Database::SYSTEM_COLLECTION_RULES], ['$collection' => Database::SYSTEM_COLLECTION_RULES, '$permissions' => ['read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation')
|
||||
->param('rules', [], function () use ($projectDB) { return new ArrayList(new Collection($projectDB, [Database::SYSTEM_COLLECTION_RULES], ['$collection' => Database::SYSTEM_COLLECTION_RULES, '$permissions' => ['read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation.')
|
||||
->action(
|
||||
function ($name, $read, $write, $rules) use ($response, $projectDB, $webhook, $audit) {
|
||||
$parsedRules = [];
|
||||
|
|
@ -143,7 +78,7 @@ $utopia->post('/v1/database')
|
|||
|
||||
$audit
|
||||
->setParam('event', 'database.collections.create')
|
||||
->setParam('resource', 'database/collection/'.$data['$uid'])
|
||||
->setParam('resource', 'database/collection/'.$data['$id'])
|
||||
->setParam('data', $data)
|
||||
;
|
||||
|
||||
|
|
@ -157,22 +92,92 @@ $utopia->post('/v1/database')
|
|||
}
|
||||
);
|
||||
|
||||
$utopia->put('/v1/database/:collectionId')
|
||||
$utopia->get('/v1/database/collections')
|
||||
->desc('List Collections')
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'listCollections')
|
||||
->label('sdk.description', '/docs/references/database/list-collections.md')
|
||||
->param('search', '', function () { return new Text(256); }, 'Search term to filter your list results.', true)
|
||||
->param('limit', 25, function () { return new Range(0, 100); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, function () { return new Range(0, 40000); }, 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('orderType', 'ASC', function () { return new WhiteList(['ASC', 'DESC']); }, 'Order result by ASC or DESC order.', true)
|
||||
->action(
|
||||
function ($search, $limit, $offset, $orderType) use ($response, $projectDB) {
|
||||
/*$vl = new Structure($projectDB);
|
||||
|
||||
var_dump($vl->isValid(new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['*'],
|
||||
],
|
||||
'label' => 'Platforms',
|
||||
'key' => 'platforms',
|
||||
'type' => 'document',
|
||||
'default' => [],
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
'options' => [Database::SYSTEM_COLLECTION_PLATFORMS],
|
||||
])));
|
||||
|
||||
var_dump($vl->getDescription());*/
|
||||
|
||||
$results = $projectDB->getCollection([
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'orderField' => 'name',
|
||||
'orderType' => $orderType,
|
||||
'orderCast' => 'string',
|
||||
'search' => $search,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_COLLECTIONS,
|
||||
],
|
||||
]);
|
||||
|
||||
$response->json(['sum' => $projectDB->getSum(), 'collections' => $results]);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/database/collections/:collectionId')
|
||||
->desc('Get Collection')
|
||||
->label('scope', 'collections.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'getCollection')
|
||||
->label('sdk.description', '/docs/references/database/get-collection.md')
|
||||
->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
|
||||
->action(
|
||||
function ($collectionId) use ($response, $projectDB) {
|
||||
$collection = $projectDB->getDocument($collectionId, false);
|
||||
|
||||
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$response->json($collection->getArrayCopy());
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->put('/v1/database/collections/:collectionId')
|
||||
->desc('Update Collection')
|
||||
->label('scope', 'collections.write')
|
||||
->label('webhook', 'database.collections.update')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'updateCollection')
|
||||
->label('sdk.description', '/docs/references/database/update-collection.md')
|
||||
->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
|
||||
->param('name', null, function () { return new Text(256); }, 'Collection name.')
|
||||
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions(/docs/permissions) and get a full list of available permissions.')
|
||||
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('rules', [], function () use ($projectDB) { return new ArrayList(new Collection($projectDB, [Database::SYSTEM_COLLECTION_RULES], ['$collection' => Database::SYSTEM_COLLECTION_RULES, '$permissions' => ['read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation', true)
|
||||
->param('rules', [], function () use ($projectDB) { return new ArrayList(new Collection($projectDB, [Database::SYSTEM_COLLECTION_RULES], ['$collection' => Database::SYSTEM_COLLECTION_RULES, '$permissions' => ['read' => [], 'write' => []]])); }, 'Array of [rule objects](/docs/rules). Each rule define a collection field name, data type and validation.', true)
|
||||
->action(
|
||||
function ($collectionId, $name, $read, $write, $rules) use ($response, $projectDB) {
|
||||
function ($collectionId, $name, $read, $write, $rules) use ($response, $projectDB, $webhook, $audit) {
|
||||
$collection = $projectDB->getDocument($collectionId, false);
|
||||
|
||||
if (empty($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -203,157 +208,68 @@ $utopia->put('/v1/database/:collectionId')
|
|||
throw new Exception('Failed saving collection to DB', 500);
|
||||
}
|
||||
|
||||
$data = $collection->getArrayCopy();
|
||||
|
||||
$webhook
|
||||
->setParam('payload', $data)
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('event', 'database.collections.update')
|
||||
->setParam('resource', 'database/collections/'.$data['$id'])
|
||||
->setParam('data', $data)
|
||||
;
|
||||
|
||||
$response->json($collection->getArrayCopy());
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->delete('/v1/database/:collectionId')
|
||||
$utopia->delete('/v1/database/collections/:collectionId')
|
||||
->desc('Delete Collection')
|
||||
->label('scope', 'collections.write')
|
||||
->label('webhook', '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', '', function () { return new UID(); }, 'Collection unique ID.')
|
||||
->action(
|
||||
function ($collectionId) use ($response, $projectDB, $audit) {
|
||||
function ($collectionId) use ($response, $projectDB, $webhook, $audit) {
|
||||
$collection = $projectDB->getDocument($collectionId, false);
|
||||
|
||||
if (empty($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
if (!$projectDB->deleteDocument($collectionId)) {
|
||||
throw new Exception('Failed to remove collection from DB', 500);
|
||||
}
|
||||
|
||||
$data = $collection->getArrayCopy();
|
||||
|
||||
$webhook
|
||||
->setParam('payload', $data)
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('event', 'database.collections.create')
|
||||
->setParam('resource', 'database/collection/'.$collection->getUid())
|
||||
->setParam('data', $collection->getArrayCopy()) // Audit document in case of malicious or disastrous action
|
||||
->setParam('event', 'database.collections.delete')
|
||||
->setParam('resource', 'database/collections/'.$data['$id'])
|
||||
->setParam('data', $data)
|
||||
;
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/database/:collectionId/documents')
|
||||
->desc('List Documents')
|
||||
->label('scope', 'documents.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.method', 'listDocuments')
|
||||
->label('sdk.description', '/docs/references/database/list-documents.md')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID.')
|
||||
->param('filters', [], function () { return new ArrayList(new Text(128)); }, 'Array of filter strings. Each filter is constructed from a key name, comparison operator (=, !=, >, <, <=, >=) and a value. You can also use a dot (.) separator in attribute names to filter by child document attributes. Examples: \'name=John Doe\' or \'category.$uid>=5bed2d152c362\'', true)
|
||||
->param('offset', 0, function () { return new Range(0, 900000000); }, 'Offset value. Use this value to manage pagination.', true)
|
||||
->param('limit', 50, function () { return new Range(0, 1000); }, 'Maximum number of documents to return in response. Use this value to manage pagination.', true)
|
||||
->param('order-field', '$uid', function () { return new Text(128); }, 'Document field that results will be sorted by.', true)
|
||||
->param('order-type', 'ASC', function () { return new WhiteList(array('DESC', 'ASC')); }, 'Order direction. Possible values are DESC for descending order, or ASC for ascending order.', true)
|
||||
->param('order-cast', 'string', function () { return new WhiteList(array('int', 'string', 'date', 'time', 'datetime')); }, 'Order field type casting. Possible values are int, string, date, time or datetime. The database will attempt to cast the order field to the value you pass here. The default value is a string.', true)
|
||||
->param('search', '', function () { return new Text(256); }, 'Search query. Enter any free text search. The database will try to find a match against all document attributes and children.', true)
|
||||
->param('first', 0, function () { return new Range(0, 1); }, 'Return only first document. Pass 1 for true or 0 for false. The default value is 0.', true)
|
||||
->param('last', 0, function () { return new Range(0, 1); }, 'Return only last document. Pass 1 for true or 0 for false. The default value is 0.', true)
|
||||
->action(
|
||||
function ($collectionId, $filters, $offset, $limit, $orderField, $orderType, $orderCast, $search, $first, $last) use ($response, $projectDB, $isDev) {
|
||||
$collection = $projectDB->getDocument($collectionId, $isDev);
|
||||
|
||||
if (is_null($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$list = $projectDB->getCollection([
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'orderField' => $orderField,
|
||||
'orderType' => $orderType,
|
||||
'orderCast' => $orderCast,
|
||||
'search' => $search,
|
||||
'first' => (bool) $first,
|
||||
'last' => (bool) $last,
|
||||
'filters' => array_merge($filters, [
|
||||
'$collection='.$collectionId,
|
||||
]),
|
||||
]);
|
||||
|
||||
if ($first || $last) {
|
||||
$response->json((!empty($list) ? $list->getArrayCopy() : []));
|
||||
} else {
|
||||
if ($isDev) {
|
||||
$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(/*['$uid', '$collection', 'name', 'documents']*/[], ['rules']));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/database/:collectionId/documents/:documentId')
|
||||
->desc('Get Document')
|
||||
->label('scope', 'documents.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.method', 'getDocument')
|
||||
->label('sdk.description', '/docs/references/database/get-document.md')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID')
|
||||
->param('documentId', null, function () { return new UID(); }, 'Document unique ID')
|
||||
->action(
|
||||
function ($collectionId, $documentId) use ($response, $request, $projectDB, $isDev) {
|
||||
$document = $projectDB->getDocument($documentId, $isDev);
|
||||
$collection = $projectDB->getDocument($collectionId, $isDev);
|
||||
|
||||
if (empty($document->getArrayCopy()) || $document->getCollection() != $collection->getUid()) { // Check empty
|
||||
throw new Exception('No document found', 404);
|
||||
}
|
||||
|
||||
$output = $document->getArrayCopy();
|
||||
|
||||
$paths = explode('/', $request->getParam('q', ''));
|
||||
$paths = array_slice($paths, 6, 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('$uid', $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);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/database/:collectionId/documents')
|
||||
$utopia->post('/v1/database/collections/:collectionId/documents')
|
||||
->desc('Create Document')
|
||||
->label('webhook', 'database.documents.create')
|
||||
->label('scope', 'documents.write')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'createDocument')
|
||||
->label('sdk.description', '/docs/references/database/create-document.md')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID.')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
|
||||
->param('data', [], function () { return new \Utopia\Validator\Mock(); }, 'Document data as JSON string.')
|
||||
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
|
|
@ -368,13 +284,13 @@ $utopia->post('/v1/database/:collectionId/documents')
|
|||
throw new Exception('Missing payload', 400);
|
||||
}
|
||||
|
||||
if (isset($data['$uid'])) {
|
||||
throw new Exception('$uid is not allowed for creating new documents, try update instead', 400);
|
||||
if (isset($data['$id'])) {
|
||||
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
|
||||
}
|
||||
|
||||
$collection = $projectDB->getDocument($collectionId/*, $isDev*/);
|
||||
|
||||
if (is_null($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +354,7 @@ $utopia->post('/v1/database/:collectionId/documents')
|
|||
|
||||
$audit
|
||||
->setParam('event', 'database.documents.create')
|
||||
->setParam('resource', 'database/document/'.$data['$uid'])
|
||||
->setParam('resource', 'database/document/'.$data['$id'])
|
||||
->setParam('data', $data)
|
||||
;
|
||||
|
||||
|
|
@ -452,16 +368,129 @@ $utopia->post('/v1/database/:collectionId/documents')
|
|||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/database/:collectionId/documents/:documentId')
|
||||
$utopia->get('/v1/database/collections/:collectionId/documents')
|
||||
->desc('List Documents')
|
||||
->label('scope', 'documents.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'listDocuments')
|
||||
->label('sdk.description', '/docs/references/database/list-documents.md')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
|
||||
->param('filters', [], function () { return new ArrayList(new Text(128)); }, 'Array of filter strings. Each filter is constructed from a key name, comparison operator (=, !=, >, <, <=, >=) and a value. You can also use a dot (.) separator in attribute names to filter by child document attributes. Examples: \'name=John Doe\' or \'category.$id>=5bed2d152c362\'.', true)
|
||||
->param('offset', 0, function () { return new Range(0, 900000000); }, 'Offset value. Use this value to manage pagination.', true)
|
||||
->param('limit', 50, function () { return new Range(0, 1000); }, 'Maximum number of documents to return in response. Use this value to manage pagination.', true)
|
||||
->param('order-field', '$id', function () { return new Text(128); }, 'Document field that results will be sorted by.', true)
|
||||
->param('order-type', 'ASC', function () { return new WhiteList(array('DESC', 'ASC')); }, 'Order direction. Possible values are DESC for descending order, or ASC for ascending order.', true)
|
||||
->param('order-cast', 'string', function () { return new WhiteList(array('int', 'string', 'date', 'time', 'datetime')); }, 'Order field type casting. Possible values are int, string, date, time or datetime. The database will attempt to cast the order field to the value you pass here. The default value is a string.', true)
|
||||
->param('search', '', function () { return new Text(256); }, 'Search query. Enter any free text search. The database will try to find a match against all document attributes and children.', true)
|
||||
->param('first', 0, function () { return new Range(0, 1); }, 'Return only first document. Pass 1 for true or 0 for false. The default value is 0.', true)
|
||||
->param('last', 0, function () { return new Range(0, 1); }, 'Return only last document. Pass 1 for true or 0 for false. The default value is 0.', true)
|
||||
->action(
|
||||
function ($collectionId, $filters, $offset, $limit, $orderField, $orderType, $orderCast, $search, $first, $last) use ($response, $projectDB, $isDev) {
|
||||
$collection = $projectDB->getDocument($collectionId, $isDev);
|
||||
|
||||
if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$list = $projectDB->getCollection([
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'orderField' => $orderField,
|
||||
'orderType' => $orderType,
|
||||
'orderCast' => $orderCast,
|
||||
'search' => $search,
|
||||
'first' => (bool) $first,
|
||||
'last' => (bool) $last,
|
||||
'filters' => array_merge($filters, [
|
||||
'$collection='.$collectionId,
|
||||
]),
|
||||
]);
|
||||
|
||||
if ($first || $last) {
|
||||
$response->json((!empty($list) ? $list->getArrayCopy() : []));
|
||||
} else {
|
||||
if ($isDev) {
|
||||
$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']));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/database/collections/:collectionId/documents/:documentId')
|
||||
->desc('Get Document')
|
||||
->label('scope', 'documents.read')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'getDocument')
|
||||
->label('sdk.description', '/docs/references/database/get-document.md')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
|
||||
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
|
||||
->action(
|
||||
function ($collectionId, $documentId) use ($response, $request, $projectDB, $isDev) {
|
||||
$document = $projectDB->getDocument($documentId, $isDev);
|
||||
$collection = $projectDB->getDocument($collectionId, $isDev);
|
||||
|
||||
if (empty($document->getArrayCopy()) || $document->getCollection() != $collection->getId()) { // Check empty
|
||||
throw new Exception('No document found', 404);
|
||||
}
|
||||
|
||||
$output = $document->getArrayCopy();
|
||||
|
||||
$paths = explode('/', $request->getParam('q', ''));
|
||||
$paths = array_slice($paths, 7, count($paths));
|
||||
|
||||
if (count($paths) > 0) {
|
||||
if (count($paths) % 2 == 1) {
|
||||
$output = $document->getAttribute(implode('.', $paths));
|
||||
} else {
|
||||
$id = (int) array_pop($paths);
|
||||
$output = $document->search('$id', $id, $document->getAttribute(implode('.', $paths)));
|
||||
}
|
||||
|
||||
$output = ($output instanceof Document) ? $output->getArrayCopy() : $output;
|
||||
|
||||
if (!is_array($output)) {
|
||||
throw new Exception('No document found', 404);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
$response->json($output);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||
->desc('Update Document')
|
||||
->label('webhook', 'database.documents.patch')
|
||||
->label('webhook', 'database.documents.update')
|
||||
->label('scope', 'documents.write')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'updateDocument')
|
||||
->label('sdk.description', '/docs/references/database/update-document.md')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID')
|
||||
->param('documentId', null, function () { return new UID(); }, 'Document unique ID')
|
||||
->param('data', [], function () { return new \Utopia\Validator\Mock(); }, 'Document data as JSON string')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
|
||||
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
|
||||
->param('data', [], function () { return new \Utopia\Validator\Mock(); }, 'Document data as JSON string.')
|
||||
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->action(
|
||||
|
|
@ -475,7 +504,7 @@ $utopia->patch('/v1/database/:collectionId/documents/:documentId')
|
|||
throw new Exception('Data param should be a valid JSON', 400);
|
||||
}
|
||||
|
||||
if (is_null($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -495,8 +524,8 @@ $utopia->patch('/v1/database/:collectionId/documents/:documentId')
|
|||
|
||||
$data = array_merge($document->getArrayCopy(), $data);
|
||||
|
||||
$data['$collection'] = $collection->getUid(); // Make sure user don't switch collectionID
|
||||
$data['$uid'] = $document->getUid(); // Make sure user don't switch document unique ID
|
||||
$data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID
|
||||
$data['$id'] = $document->getId(); // Make sure user don't switch document unique ID
|
||||
|
||||
if (empty($data)) {
|
||||
throw new Exception('Missing payload', 400);
|
||||
|
|
@ -518,8 +547,8 @@ $utopia->patch('/v1/database/:collectionId/documents/:documentId')
|
|||
;
|
||||
|
||||
$audit
|
||||
->setParam('event', 'database.documents.patch')
|
||||
->setParam('resource', 'database/document/'.$data['$uid'])
|
||||
->setParam('event', 'database.documents.update')
|
||||
->setParam('resource', 'database/document/'.$data['$id'])
|
||||
->setParam('data', $data)
|
||||
;
|
||||
|
||||
|
|
@ -530,16 +559,18 @@ $utopia->patch('/v1/database/:collectionId/documents/:documentId')
|
|||
}
|
||||
);
|
||||
|
||||
$utopia->delete('/v1/database/:collectionId/documents/:documentId')
|
||||
$utopia->delete('/v1/database/collections/:collectionId/documents/:documentId')
|
||||
->desc('Delete Document')
|
||||
->label('scope', 'documents.write')
|
||||
->label('webhook', 'database.documents.delete')
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.method', 'deleteDocument')
|
||||
->label('sdk.description', '/docs/references/database/delete-document.md')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID')
|
||||
->param('documentId', null, function () { return new UID(); }, 'Document unique ID')
|
||||
->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
|
||||
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
|
||||
->action(
|
||||
function ($collectionId, $documentId) use ($response, $projectDB, $audit, $isDev) {
|
||||
function ($collectionId, $documentId) use ($response, $projectDB, $audit, $webhook, $isDev) {
|
||||
$collection = $projectDB->getDocument($collectionId, $isDev);
|
||||
$document = $projectDB->getDocument($documentId, $isDev);
|
||||
|
||||
|
|
@ -547,7 +578,7 @@ $utopia->delete('/v1/database/:collectionId/documents/:documentId')
|
|||
throw new Exception('No document found', 404);
|
||||
}
|
||||
|
||||
if (is_null($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -561,10 +592,16 @@ $utopia->delete('/v1/database/:collectionId/documents/:documentId')
|
|||
throw new Exception('Failed to remove document from DB', 500);
|
||||
}
|
||||
|
||||
$data = $document->getArrayCopy();
|
||||
|
||||
$webhook
|
||||
->setParam('payload', $data)
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('event', 'database.documents.delete')
|
||||
->setParam('resource', 'database/document/'.$documentId)
|
||||
->setParam('data', $document->getArrayCopy()) // Audit document in case of malicious or disastrous action
|
||||
->setParam('resource', 'database/document/'.$data['$id'])
|
||||
->setParam('data', $data) // Audit document in case of malicious or disastrous action
|
||||
;
|
||||
|
||||
$response->noContent();
|
||||
22
app/controllers/api/graphql.php
Normal file
22
app/controllers/api/graphql.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
global $utopia;
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* 1. Map all objects, object-params, object-fields
|
||||
* 2. Parse GraphQL request payload (use: https://github.com/webonyx/graphql-php)
|
||||
* 3. Route request to relevant controllers (of REST API?) / resolvers and aggergate data
|
||||
* 4. Handle errors if any
|
||||
* 5. Returen JSON response
|
||||
* 6. Write tests!
|
||||
*/
|
||||
|
||||
$utopia->post('/v1/graphql')
|
||||
->desc('GraphQL Endpoint')
|
||||
->label('scope', 'public')
|
||||
->action(
|
||||
function () {
|
||||
throw new Exception('GraphQL support is coming soon!', 502);
|
||||
}
|
||||
);
|
||||
|
|
@ -10,8 +10,9 @@ use Appwrite\ClamAV\Network;
|
|||
$utopia->get('/v1/health')
|
||||
->desc('Check API HTTP Health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getDB')
|
||||
->label('sdk.method', 'get')
|
||||
->label('docs', false)
|
||||
->action(
|
||||
function () use ($response) {
|
||||
|
|
@ -22,6 +23,7 @@ $utopia->get('/v1/health')
|
|||
$utopia->get('/v1/health/db')
|
||||
->desc('Check DB Health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getDB')
|
||||
->label('docs', false)
|
||||
|
|
@ -36,6 +38,7 @@ $utopia->get('/v1/health/db')
|
|||
$utopia->get('/v1/health/cache')
|
||||
->desc('Check Cache Health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getCache')
|
||||
->label('docs', false)
|
||||
|
|
@ -50,6 +53,7 @@ $utopia->get('/v1/health/cache')
|
|||
$utopia->get('/v1/health/time')
|
||||
->desc('Check Webhooks Health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getTime')
|
||||
->label('docs', false)
|
||||
|
|
@ -96,6 +100,7 @@ $utopia->get('/v1/health/time')
|
|||
$utopia->get('/v1/health/webhooks')
|
||||
->desc('Check Time Health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getWebhooks')
|
||||
->label('docs', false)
|
||||
|
|
@ -108,6 +113,7 @@ $utopia->get('/v1/health/webhooks')
|
|||
$utopia->get('/v1/health/storage/local')
|
||||
->desc('Check File System Health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getStorageLocal')
|
||||
->label('docs', false)
|
||||
|
|
@ -130,6 +136,7 @@ $utopia->get('/v1/health/storage/local')
|
|||
$utopia->get('/v1/health/storage/anti-virus')
|
||||
->desc('Check Anti virus Health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getStorageAntiVirus')
|
||||
->label('docs', false)
|
||||
|
|
@ -147,6 +154,7 @@ $utopia->get('/v1/health/storage/anti-virus')
|
|||
$utopia->get('/v1/health/stats')
|
||||
->desc('System Stats')
|
||||
->label('scope', 'god')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'health')
|
||||
->label('sdk.method', 'getStats')
|
||||
->label('docs', false)
|
||||
|
|
@ -164,9 +172,9 @@ $utopia->get('/v1/health/stats')
|
|||
'version' => shell_exec('nginx -v 2>&1'),
|
||||
],
|
||||
'storage' => [
|
||||
'used' => $device->human($device->getDirectorySize($device->getRoot().'/')),
|
||||
'partitionTotal' => $device->human($device->getPartitionTotalSpace()),
|
||||
'partitionFree' => $device->human($device->getPartitionFreeSpace()),
|
||||
'used' => Storage::human($device->getDirectorySize($device->getRoot().'/')),
|
||||
'partitionTotal' => Storage::human($device->getPartitionTotalSpace()),
|
||||
'partitionFree' => Storage::human($device->getPartitionFreeSpace()),
|
||||
],
|
||||
'cache' => [
|
||||
'uptime' => (isset($cacheStats['uptime_in_seconds'])) ? $cacheStats['uptime_in_seconds'] : 0,
|
||||
|
|
@ -6,19 +6,20 @@ use Utopia\App;
|
|||
use Utopia\Locale\Locale;
|
||||
use GeoIp2\Database\Reader;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
$utopia->get('/v1/locale')
|
||||
->desc('Get User Locale')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getLocale')
|
||||
->label('sdk.method', 'get')
|
||||
->label('sdk.description', '/docs/references/locale/get-locale.md')
|
||||
->action(
|
||||
function () use ($response, $request, $utopia) {
|
||||
$eu = include __DIR__.'/../config/eu.php';
|
||||
$currencies = include __DIR__.'/../config/currencies.php';
|
||||
$reader = new Reader(__DIR__.'/../db/DBIP/dbip-country-lite-2020-01.mmdb');
|
||||
$eu = include __DIR__.'/../../config/eu.php';
|
||||
$currencies = include __DIR__.'/../../config/currencies.php';
|
||||
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
|
||||
$output = [];
|
||||
$ip = $request->getIP();
|
||||
$time = (60 * 60 * 24 * 45); // 45 days cache
|
||||
|
|
@ -68,6 +69,7 @@ $utopia->get('/v1/locale')
|
|||
$utopia->get('/v1/locale/countries')
|
||||
->desc('List Countries')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCountries')
|
||||
->label('sdk.description', '/docs/references/locale/get-countries.md')
|
||||
|
|
@ -84,13 +86,14 @@ $utopia->get('/v1/locale/countries')
|
|||
$utopia->get('/v1/locale/countries/eu')
|
||||
->desc('List EU Countries')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCountriesEU')
|
||||
->label('sdk.description', '/docs/references/locale/get-countries-eu.md')
|
||||
->action(
|
||||
function () use ($response) {
|
||||
$countries = Locale::getText('countries'); /* @var $countries array */
|
||||
$eu = include __DIR__.'/../config/eu.php';
|
||||
$eu = include __DIR__.'/../../config/eu.php';
|
||||
$list = [];
|
||||
|
||||
foreach ($eu as $code) {
|
||||
|
|
@ -108,12 +111,13 @@ $utopia->get('/v1/locale/countries/eu')
|
|||
$utopia->get('/v1/locale/countries/phones')
|
||||
->desc('List Countries Phone Codes')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCountriesPhones')
|
||||
->label('sdk.description', '/docs/references/locale/get-countries-phones.md')
|
||||
->action(
|
||||
function () use ($response) {
|
||||
$list = include __DIR__.'/../config/phones.php'; /* @var $list array */
|
||||
$list = include __DIR__.'/../../config/phones.php'; /* @var $list array */
|
||||
|
||||
$countries = Locale::getText('countries'); /* @var $countries array */
|
||||
|
||||
|
|
@ -132,6 +136,7 @@ $utopia->get('/v1/locale/countries/phones')
|
|||
$utopia->get('/v1/locale/continents')
|
||||
->desc('List Countries')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getContinents')
|
||||
->label('sdk.description', '/docs/references/locale/get-continents.md')
|
||||
|
|
@ -149,12 +154,13 @@ $utopia->get('/v1/locale/continents')
|
|||
$utopia->get('/v1/locale/currencies')
|
||||
->desc('List Currencies')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'locale')
|
||||
->label('sdk.method', 'getCurrencies')
|
||||
->label('sdk.description', '/docs/references/locale/get-currencies.md')
|
||||
->action(
|
||||
function () use ($response) {
|
||||
$currencies = include __DIR__.'/../config/currencies.php';
|
||||
$currencies = include __DIR__.'/../../config/currencies.php';
|
||||
|
||||
$response->json($currencies);
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -23,9 +23,9 @@ use Storage\Compression\Algorithms\GZIP;
|
|||
use Resize\Resize;
|
||||
use OpenSSL\OpenSSL;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
Storage::addDevice('local', new Local('/storage/uploads/app-'.$project->getUid()));
|
||||
Storage::addDevice('local', new Local('/storage/uploads/app-'.$project->getId()));
|
||||
|
||||
$fileLogos = [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
||||
'default' => 'default.gif',
|
||||
|
|
@ -116,9 +116,144 @@ $mimes = [
|
|||
'application/pdf',
|
||||
];
|
||||
|
||||
$utopia->post('/v1/storage/files')
|
||||
->desc('Create File')
|
||||
->label('scope', 'files.write')
|
||||
->label('webhook', 'storage.files.create')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'createFile')
|
||||
->label('sdk.description', '/docs/references/storage/create-file.md')
|
||||
->label('sdk.consumes', 'multipart/form-data')
|
||||
->param('file', [], function () { return new File(); }, 'Binary File.', false)
|
||||
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
// ->param('folderId', '', function () { return new UID(); }, 'Folder to associate files with.', true)
|
||||
->action(
|
||||
function ($file, $read, $write, $folderId = '') use ($request, $response, $user, $projectDB, $webhook, $audit, $usage) {
|
||||
$file = $request->getFiles('file');
|
||||
$read = (empty($read)) ? ['user:'.$user->getId()] : $read;
|
||||
$write = (empty($write)) ? ['user:'.$user->getId()] : $write;
|
||||
|
||||
/*
|
||||
* Validators
|
||||
*/
|
||||
//$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG));
|
||||
$fileSize = new FileSize($request->getServer('_APP_STORAGE_LIMIT', 0));
|
||||
$upload = new Upload();
|
||||
|
||||
if (empty($file)) {
|
||||
throw new Exception('No file sent', 400);
|
||||
}
|
||||
|
||||
// Make sure we handle a single file and multiple files the same way
|
||||
$file['name'] = (is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
|
||||
$file['tmp_name'] = (is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
|
||||
$file['size'] = (is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
// Check if file type is allowed (feature for project settings?)
|
||||
//if (!$fileType->isValid($file['tmp_name'])) {
|
||||
//throw new Exception('File type not allowed', 400);
|
||||
//}
|
||||
|
||||
// Check if file size is exceeding allowed limit
|
||||
if (!$fileSize->isValid($file['size'])) {
|
||||
throw new Exception('File size not allowed', 400);
|
||||
}
|
||||
|
||||
$antiVirus = new Network('clamav', 3310);
|
||||
|
||||
/*
|
||||
* Models
|
||||
*/
|
||||
$list = [];
|
||||
$device = Storage::getDevice('local');
|
||||
|
||||
if (!$upload->isValid($file['tmp_name'])) {
|
||||
throw new Exception('Invalid file', 403);
|
||||
}
|
||||
|
||||
// Save to storage
|
||||
$size = $device->getFileSize($file['tmp_name']);
|
||||
$path = $device->getPath(uniqid().'.'.pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
|
||||
if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move'
|
||||
throw new Exception('Failed moving file', 500);
|
||||
}
|
||||
|
||||
$mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption
|
||||
|
||||
// Check if file size is exceeding allowed limit
|
||||
if (!$antiVirus->fileScan($path)) {
|
||||
$device->delete($path);
|
||||
throw new Exception('Invalid file', 403);
|
||||
}
|
||||
|
||||
// Compression
|
||||
$compressor = new GZIP();
|
||||
$data = $device->read($path);
|
||||
$data = $compressor->compress($data);
|
||||
$key = $request->getServer('_APP_OPENSSL_KEY_V1');
|
||||
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
|
||||
|
||||
if(!$device->write($path, $data)) {
|
||||
throw new Exception('Failed to save file', 500);
|
||||
}
|
||||
|
||||
$sizeActual = $device->getFileSize($path);
|
||||
|
||||
$file = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_FILES,
|
||||
'$permissions' => [
|
||||
'read' => $read,
|
||||
'write' => $write,
|
||||
],
|
||||
'dateCreated' => time(),
|
||||
'folderId' => $folderId,
|
||||
'name' => $file['name'],
|
||||
'path' => $path,
|
||||
'signature' => $device->getFileHash($path),
|
||||
'mimeType' => $mimeType,
|
||||
'sizeOriginal' => $size,
|
||||
'sizeActual' => $sizeActual,
|
||||
'algorithm' => $compressor->getName(),
|
||||
'token' => bin2hex(random_bytes(64)),
|
||||
'comment' => '',
|
||||
'fileOpenSSLVersion' => '1',
|
||||
'fileOpenSSLCipher' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'fileOpenSSLTag' => bin2hex($tag),
|
||||
'fileOpenSSLIV' => bin2hex($iv),
|
||||
]);
|
||||
|
||||
if (false === $file) {
|
||||
throw new Exception('Failed saving file to DB', 500);
|
||||
}
|
||||
|
||||
$webhook
|
||||
->setParam('payload', $file->getArrayCopy())
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('event', 'storage.files.create')
|
||||
->setParam('resource', 'storage/files/'.$file->getId())
|
||||
;
|
||||
|
||||
$usage
|
||||
->setParam('storage', $sizeActual)
|
||||
;
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->json($file->getArrayCopy())
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/storage/files')
|
||||
->desc('List Files')
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'listFiles')
|
||||
->label('sdk.description', '/docs/references/storage/list-files.md')
|
||||
|
|
@ -141,7 +276,7 @@ $utopia->get('/v1/storage/files')
|
|||
]);
|
||||
|
||||
$results = array_map(function ($value) { /* @var $value \Database\Document */
|
||||
return $value->getArrayCopy(['$uid', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']);
|
||||
return $value->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']);
|
||||
}, $results);
|
||||
|
||||
$response->json(['sum' => $projectDB->getSum(), 'files' => $results]);
|
||||
|
|
@ -151,6 +286,7 @@ $utopia->get('/v1/storage/files')
|
|||
$utopia->get('/v1/storage/files/:fileId')
|
||||
->desc('Get File')
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFile')
|
||||
->label('sdk.description', '/docs/references/storage/get-file.md')
|
||||
|
|
@ -159,26 +295,29 @@ $utopia->get('/v1/storage/files/:fileId')
|
|||
function ($fileId) use ($response, $projectDB) {
|
||||
$file = $projectDB->getDocument($fileId);
|
||||
|
||||
if (empty($file->getUid()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
throw new Exception('File not found', 404);
|
||||
}
|
||||
|
||||
$response->json($file->getArrayCopy(['$uid', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']));
|
||||
$response->json($file->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/storage/files/:fileId/preview')
|
||||
->desc('Get File Preview')
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFilePreview')
|
||||
->label('sdk.description', '/docs/references/storage/get-file-preview.md')
|
||||
->label('sdk.response.type', 'image/*')
|
||||
->label('sdk.location', true)
|
||||
->param('fileId', '', function () { return new UID(); }, 'File unique ID')
|
||||
->param('width', 0, function () { return new Range(0, 4000); }, 'Resize preview image width, Pass an integer between 0 to 4000', true)
|
||||
->param('height', 0, function () { return new Range(0, 4000); }, 'Resize preview image height, Pass an integer between 0 to 4000', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100', true)
|
||||
->param('width', 0, function () { return new Range(0, 4000); }, 'Resize preview image width, Pass an integer between 0 to 4000.', true)
|
||||
->param('height', 0, function () { return new Range(0, 4000); }, 'Resize preview image height, Pass an integer between 0 to 4000.', true)
|
||||
->param('quality', 100, function () { return new Range(0, 100); }, 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->param('background', '', function () { return new HexColor(); }, 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true)
|
||||
->param('output', null, function () use ($outputs) { return new WhiteList(array_merge(array_keys($outputs), [null])); }, 'Output format type (jpeg, jpg, png, gif and webp)', true)
|
||||
->param('output', null, function () use ($outputs) { return new WhiteList(array_merge(array_keys($outputs), [null])); }, 'Output format type (jpeg, jpg, png, gif and webp).', true)
|
||||
//->param('storage', 'local', function () {return new WhiteList(array('local'));}, 'Selected storage device. defaults to local')
|
||||
//->param('token', '', function () {return new Text(128);}, 'Preview token', true)
|
||||
->action(
|
||||
|
|
@ -202,7 +341,7 @@ $utopia->get('/v1/storage/files/:fileId/preview')
|
|||
|
||||
$file = $projectDB->getDocument($fileId);
|
||||
|
||||
if (empty($file->getUid()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
throw new Exception('File not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -218,7 +357,7 @@ $utopia->get('/v1/storage/files/:fileId/preview')
|
|||
throw new Exception('File not found in '.$path, 404);
|
||||
}
|
||||
|
||||
$cache = new Cache(new Filesystem('/storage/cache/app-'.$project->getUid())); // Limit file number or size
|
||||
$cache = new Cache(new Filesystem('/storage/cache/app-'.$project->getId())); // Limit file number or size
|
||||
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
|
||||
|
||||
if ($data) {
|
||||
|
|
@ -228,8 +367,10 @@ $utopia->get('/v1/storage/files/:fileId/preview')
|
|||
->setContentType((in_array($output, $outputs)) ? $outputs[$output] : $outputs['jpg'])
|
||||
->addHeader('Expires', $date)
|
||||
->addHeader('X-Appwrite-Cache', 'hit')
|
||||
->send($data, 0)
|
||||
->send($data)
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$source = $device->read($path);
|
||||
|
|
@ -263,7 +404,7 @@ $utopia->get('/v1/storage/files/:fileId/preview')
|
|||
->setContentType($outputs[$output])
|
||||
->addHeader('Expires', $date)
|
||||
->addHeader('X-Appwrite-Cache', 'miss')
|
||||
->send('', null)
|
||||
->send('')
|
||||
;
|
||||
|
||||
$data = $resize->output($output, $quality);
|
||||
|
|
@ -273,23 +414,24 @@ $utopia->get('/v1/storage/files/:fileId/preview')
|
|||
echo $data;
|
||||
|
||||
unset($resize);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/storage/files/:fileId/download')
|
||||
->desc('Get File for Download')
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileDownload')
|
||||
->label('sdk.description', '/docs/references/storage/get-file-download.md')
|
||||
->label('sdk.response.type', '*')
|
||||
->label('sdk.location', true)
|
||||
->param('fileId', '', function () { return new UID(); }, 'File unique ID.')
|
||||
->action(
|
||||
function ($fileId) use ($response, $request, $projectDB) {
|
||||
$file = $projectDB->getDocument($fileId);
|
||||
|
||||
if (empty($file->getUid()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
throw new Exception('File not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -331,16 +473,19 @@ $utopia->get('/v1/storage/files/:fileId/download')
|
|||
$utopia->get('/v1/storage/files/:fileId/view')
|
||||
->desc('Get File for View')
|
||||
->label('scope', 'files.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileView')
|
||||
->label('sdk.description', '/docs/references/storage/get-file-view.md')
|
||||
->label('sdk.response.type', '*')
|
||||
->label('sdk.location', true)
|
||||
->param('fileId', '', function () { return new UID(); }, 'File unique ID.')
|
||||
->param('as', '', function () { return new WhiteList(['pdf', /*'html',*/ 'text']); }, 'Choose a file format to convert your file to. Currently you can only convert word and pdf files to pdf or txt. This option is currently experimental only, use at your own risk.', true)
|
||||
->action(
|
||||
function ($fileId, $as) use ($response, $request, $projectDB, $mimes) {
|
||||
$file = $projectDB->getDocument($fileId);
|
||||
|
||||
if (empty($file->getUid()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
throw new Exception('File not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -395,146 +540,11 @@ $utopia->get('/v1/storage/files/:fileId/view')
|
|||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/storage/files')
|
||||
->desc('Create File')
|
||||
->label('scope', 'files.write')
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'createFile')
|
||||
->label('sdk.description', '/docs/references/storage/create-file.md')
|
||||
->label('sdk.consumes', 'multipart/form-data')
|
||||
->param('files', [], function () { return new File(); }, 'Binary Files.', false)
|
||||
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
// ->param('folderId', '', function () { return new UID(); }, 'Folder to associate files with.', true)
|
||||
->action(
|
||||
function ($files, $read, $write, $folderId = '') use ($request, $response, $user, $projectDB, $audit, $usage) {
|
||||
$files = $request->getFiles('files');
|
||||
$read = (empty($read)) ? ['user:'.$user->getUid()] : $read;
|
||||
$write = (empty($write)) ? ['user:'.$user->getUid()] : $write;
|
||||
|
||||
/*
|
||||
* Validators
|
||||
*/
|
||||
//$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG));
|
||||
$fileSize = new FileSize(2097152 * 2); // 4MB
|
||||
$upload = new Upload();
|
||||
|
||||
if (empty($files)) {
|
||||
throw new Exception('No files sent', 400);
|
||||
}
|
||||
|
||||
// Make sure we handle single file and multiple files the same way
|
||||
$files['name'] = (is_array($files['name'])) ? $files['name'] : [$files['name']];
|
||||
$files['tmp_name'] = (is_array($files['tmp_name'])) ? $files['tmp_name'] : [$files['tmp_name']];
|
||||
$files['size'] = (is_array($files['size'])) ? $files['size'] : [$files['size']];
|
||||
|
||||
// Check if file type is allowed
|
||||
//foreach ($files['tmp_name'] as $tmpName) {
|
||||
//if (!$fileType->isValid($tmpName)) {
|
||||
//throw new Exception('File type not allowed', 400);
|
||||
//}
|
||||
//}
|
||||
|
||||
// Check if file size is exceeding allowed limit
|
||||
foreach ($files['size'] as $tmpSize) {
|
||||
if (!$fileSize->isValid($tmpSize)) {
|
||||
throw new Exception('File size not allowed', 400);
|
||||
}
|
||||
}
|
||||
|
||||
$antiVirus = new Network('clamav', 3310);
|
||||
|
||||
/*
|
||||
* Models
|
||||
*/
|
||||
$list = [];
|
||||
$device = Storage::getDevice('local');
|
||||
|
||||
foreach ($files['tmp_name'] as $i => $tmpName) {
|
||||
if (!$upload->isValid($tmpName)) {
|
||||
throw new Exception('Invalid file', 403);
|
||||
}
|
||||
|
||||
// Save to storage
|
||||
$name = $files['name'][$i];
|
||||
$size = $device->getFileSize($tmpName);
|
||||
$path = $device->getPath(uniqid().'.'.pathinfo($name, PATHINFO_EXTENSION));
|
||||
|
||||
if (!$device->upload($tmpName, $path)) { // TODO deprecate 'upload' and replace with 'move'
|
||||
throw new Exception('Failed moving file', 500);
|
||||
}
|
||||
|
||||
$mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption
|
||||
|
||||
// Check if file size is exceeding allowed limit
|
||||
if (!$antiVirus->fileScan($path)) {
|
||||
$device->delete($path);
|
||||
throw new Exception('Invalid file', 403);
|
||||
}
|
||||
|
||||
// Compression
|
||||
$compressor = new GZIP();
|
||||
$data = $device->read($path);
|
||||
$data = $compressor->compress($data);
|
||||
$key = $request->getServer('_APP_OPENSSL_KEY_V1');
|
||||
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
|
||||
|
||||
if(!$device->write($path, $data)) {
|
||||
throw new Exception('Failed to save file', 500);
|
||||
}
|
||||
|
||||
$sizeActual = $device->getFileSize($path);
|
||||
|
||||
$file = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_FILES,
|
||||
'$permissions' => [
|
||||
'read' => $read,
|
||||
'write' => $write,
|
||||
],
|
||||
'dateCreated' => time(),
|
||||
'folderId' => $folderId,
|
||||
'name' => $name,
|
||||
'path' => $path,
|
||||
'signature' => $device->getFileHash($path),
|
||||
'mimeType' => $mimeType,
|
||||
'sizeOriginal' => $size,
|
||||
'sizeActual' => $sizeActual,
|
||||
'algorithm' => $compressor->getName(),
|
||||
'token' => bin2hex(random_bytes(64)),
|
||||
'comment' => '',
|
||||
'fileOpenSSLVersion' => '1',
|
||||
'fileOpenSSLCipher' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
'fileOpenSSLTag' => bin2hex($tag),
|
||||
'fileOpenSSLIV' => bin2hex($iv),
|
||||
]);
|
||||
|
||||
if (false === $file) {
|
||||
throw new Exception('Failed saving file to DB', 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('event', 'storage.upload')
|
||||
->setParam('resource', 'storage/file/'.$file->getUid())
|
||||
;
|
||||
|
||||
$usage
|
||||
->setParam('storage', $sizeActual)
|
||||
;
|
||||
|
||||
$list[] = $file->getArrayCopy();
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->json($list)
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->put('/v1/storage/files/:fileId')
|
||||
->desc('Update File')
|
||||
->label('scope', 'files.write')
|
||||
->label('webhook', 'storage.files.update')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'updateFile')
|
||||
->label('sdk.description', '/docs/references/storage/update-file.md')
|
||||
|
|
@ -543,10 +553,10 @@ $utopia->put('/v1/storage/files/:fileId')
|
|||
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
//->param('folderId', '', function () { return new UID(); }, 'Folder to associate files with.', true)
|
||||
->action(
|
||||
function ($fileId, $read, $write, $folderId = '') use ($response, $projectDB) {
|
||||
function ($fileId, $read, $write, $folderId = '') use ($response, $projectDB, $audit, $webhook) {
|
||||
$file = $projectDB->getDocument($fileId);
|
||||
|
||||
if (empty($file->getUid()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
throw new Exception('File not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -562,6 +572,15 @@ $utopia->put('/v1/storage/files/:fileId')
|
|||
throw new Exception('Failed saving file to DB', 500);
|
||||
}
|
||||
|
||||
$webhook
|
||||
->setParam('payload', $file->getArrayCopy())
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('event', 'storage.files.update')
|
||||
->setParam('resource', 'storage/files/'.$file->getId())
|
||||
;
|
||||
|
||||
$response->json($file->getArrayCopy());
|
||||
}
|
||||
);
|
||||
|
|
@ -569,15 +588,17 @@ $utopia->put('/v1/storage/files/:fileId')
|
|||
$utopia->delete('/v1/storage/files/:fileId')
|
||||
->desc('Delete File')
|
||||
->label('scope', 'files.write')
|
||||
->label('webhook', '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', '', function () { return new UID(); }, 'File unique ID.')
|
||||
->action(
|
||||
function ($fileId) use ($response, $projectDB, $audit, $usage) {
|
||||
function ($fileId) use ($response, $projectDB, $webhook, $audit, $usage) {
|
||||
$file = $projectDB->getDocument($fileId);
|
||||
|
||||
if (empty($file->getUid()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
throw new Exception('File not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -589,9 +610,13 @@ $utopia->delete('/v1/storage/files/:fileId')
|
|||
}
|
||||
}
|
||||
|
||||
$webhook
|
||||
->setParam('payload', $file->getArrayCopy())
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('event', 'storage.delete')
|
||||
->setParam('resource', 'storage/file/'.$file->getUid())
|
||||
->setParam('event', 'storage.files.delete')
|
||||
->setParam('resource', 'storage/files/'.$file->getId())
|
||||
;
|
||||
|
||||
$usage
|
||||
|
|
@ -605,17 +630,17 @@ $utopia->delete('/v1/storage/files/:fileId')
|
|||
$utopia->get('/v1/storage/files/:fileId/scan')
|
||||
->desc('Scan Storage')
|
||||
->label('scope', 'god')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'getFileScan')
|
||||
->label('sdk.hide', true)
|
||||
->param('fileId', '', function () { return new UID(); }, 'File unique ID.')
|
||||
->param('storage', 'local', function () { return new WhiteList(['local']);
|
||||
})
|
||||
->param('storage', 'local', function () { return new WhiteList(['local']);})
|
||||
->action(
|
||||
function ($fileId, $storage) use ($response, $request, $projectDB) {
|
||||
$file = $projectDB->getDocument($fileId);
|
||||
|
||||
if (empty($file->getUid()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
if (empty($file->getId()) || Database::SYSTEM_COLLECTION_FILES != $file->getCollection()) {
|
||||
throw new Exception('File not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -18,13 +18,77 @@ use Database\Validator\Authorization;
|
|||
use Template\Template;
|
||||
use Auth\Auth;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
$utopia->post('/v1/teams')
|
||||
->desc('Create Team')
|
||||
->label('scope', 'teams.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'create')
|
||||
->label('sdk.description', '/docs/references/teams/create-team.md')
|
||||
->param('name', null, function () { return new Text(100); }, 'Team name.')
|
||||
->param('roles', ['owner'], function () { return new ArrayList(new Text(128)); }, '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).', true)
|
||||
->action(
|
||||
function ($name, $roles) use ($response, $projectDB, $user, $mode) {
|
||||
Authorization::disable();
|
||||
|
||||
$team = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TEAMS,
|
||||
'$permissions' => [
|
||||
'read' => ['team:{self}'],
|
||||
'write' => ['team:{self}/owner'],
|
||||
],
|
||||
'name' => $name,
|
||||
'sum' => ($mode !== APP_MODE_ADMIN && $user->getId()) ? 1 : 0,
|
||||
'dateCreated' => time(),
|
||||
]);
|
||||
|
||||
Authorization::reset();
|
||||
|
||||
if (false === $team) {
|
||||
throw new Exception('Failed saving team to DB', 500);
|
||||
}
|
||||
|
||||
if ($mode !== APP_MODE_ADMIN && $user->getId()) { // Don't add user on server mode
|
||||
$membership = new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'$permissions' => [
|
||||
'read' => ['user:'.$user->getId(), 'team:'.$team->getId()],
|
||||
'write' => ['user:'.$user->getId(), 'team:'.$team->getId().'/owner'],
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'teamId' => $team->getId(),
|
||||
'roles' => $roles,
|
||||
'invited' => time(),
|
||||
'joined' => time(),
|
||||
'confirm' => true,
|
||||
'secret' => '',
|
||||
]);
|
||||
|
||||
// Attach user to team
|
||||
$user->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
|
||||
|
||||
$user = $projectDB->updateDocument($user->getArrayCopy());
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->json($team->getArrayCopy())
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/teams')
|
||||
->desc('List Teams')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'listTeams')
|
||||
->label('sdk.method', 'list')
|
||||
->label('sdk.description', '/docs/references/teams/list-teams.md')
|
||||
->param('search', '', function () { return new Text(256); }, 'Search term to filter your list results.', true)
|
||||
->param('limit', 25, function () { return new Range(0, 100); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
|
|
@ -51,15 +115,16 @@ $utopia->get('/v1/teams')
|
|||
$utopia->get('/v1/teams/:teamId')
|
||||
->desc('Get Team')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'getTeam')
|
||||
->label('sdk.method', 'get')
|
||||
->label('sdk.description', '/docs/references/teams/get-team.md')
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->action(
|
||||
function ($teamId) use ($response, $projectDB) {
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -67,125 +132,12 @@ $utopia->get('/v1/teams/:teamId')
|
|||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/teams/:teamId/members')
|
||||
->desc('Get Team Members')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'getTeamMembers')
|
||||
->label('sdk.description', '/docs/references/teams/get-team-members.md')
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->action(
|
||||
function ($teamId) use ($response, $projectDB) {
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
$memberships = $projectDB->getCollection([
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'teamId='.$teamId,
|
||||
],
|
||||
]);
|
||||
|
||||
$users = [];
|
||||
|
||||
foreach ($memberships as $member) {
|
||||
if (empty($member->getAttribute('userId', null))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp = $projectDB->getDocument($member->getAttribute('userId', null))->getArrayCopy(['$uid', 'email', 'name']);
|
||||
|
||||
$temp['inviteId'] = $member->getUid();
|
||||
$temp['roles'] = $member->getAttribute('roles', []);
|
||||
$temp['confirm'] = $member->getAttribute('confirm', false);
|
||||
$temp['joined'] = $member->getAttribute('joined', 0);
|
||||
$users[] = $temp;
|
||||
}
|
||||
|
||||
usort($users, function ($a, $b) {
|
||||
if ($a['joined'] === 0 || $b['joined'] === 0) {
|
||||
return $b['joined'] - $a['joined'];
|
||||
}
|
||||
|
||||
return $a['joined'] - $b['joined'];
|
||||
});
|
||||
|
||||
$response->json($users);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/teams')
|
||||
->desc('Create Team')
|
||||
->label('scope', 'teams.write')
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'createTeam')
|
||||
->label('sdk.description', '/docs/references/teams/create-team.md')
|
||||
->param('name', null, function () { return new Text(100); }, 'Team name.')
|
||||
->param('roles', ['owner'], function () { return new ArrayList(new Text(128)); }, 'User roles array. Use this param to set the roles in the team for the user who created the team. The default role is **owner**, a role can be any string.', true)
|
||||
->action(
|
||||
function ($name, $roles) use ($response, $projectDB, $user, $mode) {
|
||||
Authorization::disable();
|
||||
|
||||
$team = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TEAMS,
|
||||
'$permissions' => [
|
||||
'read' => ['team:{self}'],
|
||||
'write' => ['team:{self}/owner'],
|
||||
],
|
||||
'name' => $name,
|
||||
'sum' => ($mode !== APP_MODE_ADMIN) ? 1 : 0,
|
||||
'dateCreated' => time(),
|
||||
]);
|
||||
|
||||
Authorization::enable();
|
||||
|
||||
if (false === $team) {
|
||||
throw new Exception('Failed saving team to DB', 500);
|
||||
}
|
||||
|
||||
if ($mode !== APP_MODE_ADMIN) { // Don't add user on admin mode
|
||||
$membership = new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'$permissions' => [
|
||||
'read' => ['user:'.$user->getUid(), 'team:'.$team->getUid()],
|
||||
'write' => ['user:'.$user->getUid(), 'team:'.$team->getUid().'/owner'],
|
||||
],
|
||||
'userId' => $user->getUid(),
|
||||
'teamId' => $team->getUid(),
|
||||
'roles' => $roles,
|
||||
'invited' => time(),
|
||||
'joined' => time(),
|
||||
'confirm' => true,
|
||||
'secret' => '',
|
||||
]);
|
||||
|
||||
// Attach user to team
|
||||
$user->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND);
|
||||
|
||||
$user = $projectDB->updateDocument($user->getArrayCopy());
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->json($team->getArrayCopy())
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->put('/v1/teams/:teamId')
|
||||
->desc('Update Team')
|
||||
->label('scope', 'teams.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'updateTeam')
|
||||
->label('sdk.method', 'update')
|
||||
->label('sdk.description', '/docs/references/teams/update-team.md')
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->param('name', null, function () { return new Text(100); }, 'Team name.')
|
||||
|
|
@ -193,7 +145,7 @@ $utopia->put('/v1/teams/:teamId')
|
|||
function ($teamId, $name) use ($response, $projectDB) {
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -212,15 +164,16 @@ $utopia->put('/v1/teams/:teamId')
|
|||
$utopia->delete('/v1/teams/:teamId')
|
||||
->desc('Delete Team')
|
||||
->label('scope', 'teams.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'deleteTeam')
|
||||
->label('sdk.method', 'delete')
|
||||
->label('sdk.description', '/docs/references/teams/delete-team.md')
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->action(
|
||||
function ($teamId) use ($response, $projectDB) {
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +187,7 @@ $utopia->delete('/v1/teams/:teamId')
|
|||
]);
|
||||
|
||||
foreach ($memberships as $member) {
|
||||
if (!$projectDB->deleteDocument($member)) {
|
||||
if (!$projectDB->deleteDocument($member->getId())) {
|
||||
throw new Exception('Failed to remove membership for team from DB', 500);
|
||||
}
|
||||
}
|
||||
|
|
@ -247,25 +200,24 @@ $utopia->delete('/v1/teams/:teamId')
|
|||
}
|
||||
);
|
||||
|
||||
// Memberships
|
||||
|
||||
$utopia->post('/v1/teams/:teamId/memberships')
|
||||
->desc('Create Team Membership')
|
||||
->label('scope', 'account')
|
||||
->label('scope', 'teams.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'createTeamMembership')
|
||||
->label('sdk.method', 'createMembership')
|
||||
->label('sdk.description', '/docs/references/teams/create-team-membership.md')
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->param('email', '', function () { return new Email(); }, 'New team member email address.')
|
||||
->param('email', '', function () { return new Email(); }, 'New team member email.')
|
||||
->param('name', '', function () { return new Text(100); }, 'New team member name.', true)
|
||||
->param('roles', [], function () { return new ArrayList(new Text(128)); }, 'Invite roles array. Learn more about [roles and permissions](/docs/permissions).')
|
||||
->param('redirect', '', function () use ($clients) { return new Host($clients); }, 'Reset page to redirect user back to your app from the invitation email.')
|
||||
->param('roles', [], function () { return new ArrayList(new Text(128)); }, '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).')
|
||||
->param('url', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email.') // TODO add our own built-in confirm page
|
||||
->action(
|
||||
function ($teamId, $email, $name, $roles, $redirect) use ($response, $register, $project, $user, $audit, $projectDB) {
|
||||
function ($teamId, $email, $name, $roles, $url) use ($response, $register, $project, $user, $audit, $projectDB) {
|
||||
$name = (empty($name)) ? $email : $name;
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +226,7 @@ $utopia->post('/v1/teams/:teamId/memberships')
|
|||
'offset' => 0,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'teamId='.$team->getUid(),
|
||||
'teamId='.$team->getId(),
|
||||
],
|
||||
]);
|
||||
|
||||
|
|
@ -298,17 +250,17 @@ $utopia->post('/v1/teams/:teamId/memberships')
|
|||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'confirm' => false,
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
'tokens' => [],
|
||||
]);
|
||||
|
||||
Authorization::enable();
|
||||
Authorization::reset();
|
||||
|
||||
if (false === $invitee) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
|
|
@ -318,11 +270,11 @@ $utopia->post('/v1/teams/:teamId/memberships')
|
|||
$isOwner = false;
|
||||
|
||||
foreach ($memberships as $member) {
|
||||
if ($member->getAttribute('userId') == $invitee->getUid()) {
|
||||
throw new Exception('User has already been invited or is already a member of this team', 400);
|
||||
if ($member->getAttribute('userId') == $invitee->getId()) {
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409);
|
||||
}
|
||||
|
||||
if ($member->getAttribute('userId') == $user->getUid() && in_array('owner', $member->getAttribute('roles', []))) {
|
||||
if ($member->getAttribute('userId') == $user->getId() && in_array('owner', $member->getAttribute('roles', []))) {
|
||||
$isOwner = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -337,10 +289,10 @@ $utopia->post('/v1/teams/:teamId/memberships')
|
|||
'$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:'.$invitee->getUid(), 'team:'.$team->getUid().'/owner'],
|
||||
'write' => ['user:'.$invitee->getId(), 'team:'.$team->getId().'/owner'],
|
||||
],
|
||||
'userId' => $invitee->getUid(),
|
||||
'teamId' => $team->getUid(),
|
||||
'userId' => $invitee->getId(),
|
||||
'teamId' => $team->getId(),
|
||||
'roles' => $roles,
|
||||
'invited' => time(),
|
||||
'joined' => 0,
|
||||
|
|
@ -354,210 +306,179 @@ $utopia->post('/v1/teams/:teamId/memberships')
|
|||
throw new Exception('Failed saving membership to DB', 500);
|
||||
}
|
||||
|
||||
$redirect = Template::parseURL($redirect);
|
||||
$redirect['query'] = Template::mergeQuery(((isset($redirect['query'])) ? $redirect['query'] : ''), ['inviteId' => $membership->getUid(), 'teamId' => $team->getUid(), 'userId' => $invitee->getUid(), 'secret' => $secret]);
|
||||
$redirect = Template::unParseURL($redirect);
|
||||
$url = Template::parseURL($url);
|
||||
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['inviteId' => $membership->getId(), 'teamId' => $team->getId(), 'userId' => $invitee->getId(), 'secret' => $secret, 'teamId' => $teamId]);
|
||||
$url = Template::unParseURL($url);
|
||||
|
||||
$body = new Template(__DIR__.'/../config/locales/templates/'.Locale::getText('auth.emails.invitation.body'));
|
||||
$body = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.invitation.body'));
|
||||
$body
|
||||
->setParam('{{direction}}', Locale::getText('settings.direction'))
|
||||
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
|
||||
->setParam('{{team}}', $team->getAttribute('name', '[TEAM-NAME]'))
|
||||
->setParam('{{owner}}', $user->getAttribute('name', ''))
|
||||
->setParam('{{redirect}}', $redirect)
|
||||
->setParam('{{redirect}}', $url)
|
||||
;
|
||||
|
||||
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
|
||||
|
||||
$mail->addAddress($email, $name);
|
||||
|
||||
$mail->Subject = sprintf(Locale::getText('auth.emails.invitation.title'), $team->getAttribute('name', '[TEAM-NAME]'), $project->getAttribute('name', ['[APP-NAME]']));
|
||||
$mail->Subject = sprintf(Locale::getText('account.emails.invitation.title'), $team->getAttribute('name', '[TEAM-NAME]'), $project->getAttribute('name', ['[APP-NAME]']));
|
||||
$mail->Body = $body->render();
|
||||
$mail->AltBody = strip_tags($body->render());
|
||||
|
||||
try {
|
||||
$mail->send();
|
||||
} catch (\Exception $error) {
|
||||
//throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
|
||||
throw new Exception('Error sending mail: ' . $error->getMessage(), 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('userId', $invitee->getUid())
|
||||
->setParam('event', 'auth.invite')
|
||||
->setParam('userId', $invitee->getId())
|
||||
->setParam('event', 'teams.membership.create')
|
||||
->setParam('resource', 'teams/'.$teamId)
|
||||
;
|
||||
|
||||
$response
|
||||
//->setStatusCode(Response::STATUS_CODE_CREATED) TODO change response of this endpoint
|
||||
->noContent();
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED) // TODO change response of this endpoint
|
||||
->json(array_merge($membership->getArrayCopy([
|
||||
'$id',
|
||||
'userId',
|
||||
'teamId',
|
||||
'roles',
|
||||
'invited',
|
||||
'joined',
|
||||
'confirm',
|
||||
]), [
|
||||
'email' => $email,
|
||||
'name' => $name,
|
||||
]))
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/teams/:teamId/memberships/:inviteId/resend')
|
||||
->desc('Create Team Membership (Resend)')
|
||||
->label('scope', 'account')
|
||||
$utopia->get('/v1/teams/:teamId/memberships')
|
||||
->desc('Get Team Memberships')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'createTeamMembershipResend')
|
||||
->label('sdk.description', '/docs/references/teams/create-team-membership-resend.md')
|
||||
->label('sdk.method', 'getMemberships')
|
||||
->label('sdk.description', '/docs/references/teams/get-team-members.md')
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->param('inviteId', '', function () { return new UID(); }, 'Invite unique ID.')
|
||||
->param('redirect', '', function () use ($clients) { return new Host($clients); }, 'Reset page to redirect user back to your app from the invitation email.')
|
||||
->action(
|
||||
function ($teamId, $inviteId, $redirect) use ($response, $register, $project, $user, $audit, $projectDB) {
|
||||
$membership = $projectDB->getDocument($inviteId);
|
||||
function ($teamId) use ($response, $projectDB) {
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
if (empty($membership->getUid()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
|
||||
throw new Exception('Membership not found', 404);
|
||||
}
|
||||
|
||||
$team = $projectDB->getDocument($membership->getAttribute('teamId'));
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
if ($team->getUid() !== $teamId) {
|
||||
throw new Exception('Team IDs don\'t match', 404);
|
||||
$memberships = $projectDB->getCollection([
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,
|
||||
'teamId='.$teamId,
|
||||
],
|
||||
]);
|
||||
|
||||
$users = [];
|
||||
|
||||
foreach ($memberships as $membership) {
|
||||
if (empty($membership->getAttribute('userId', null))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp = $projectDB->getDocument($membership->getAttribute('userId', null))->getArrayCopy(['email', 'name']);
|
||||
|
||||
$users[] = array_merge($temp, $membership->getArrayCopy([
|
||||
'$id',
|
||||
'userId',
|
||||
'teamId',
|
||||
'roles',
|
||||
'invited',
|
||||
'joined',
|
||||
'confirm',
|
||||
]));
|
||||
}
|
||||
|
||||
$invitee = $projectDB->getDocument($membership->getAttribute('userId'));
|
||||
usort($users, function ($a, $b) {
|
||||
if ($a['joined'] === 0 || $b['joined'] === 0) {
|
||||
return $b['joined'] - $a['joined'];
|
||||
}
|
||||
|
||||
if (empty($invitee->getUid()) || Database::SYSTEM_COLLECTION_USERS != $invitee->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
return $a['joined'] - $b['joined'];
|
||||
});
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
||||
$membership = $projectDB->updateDocument(array_merge($membership->getArrayCopy(), ['secret' => Auth::hash($secret)]));
|
||||
|
||||
if (false === $membership) {
|
||||
throw new Exception('Failed updating membership to DB', 500);
|
||||
}
|
||||
|
||||
$redirect = Template::parseURL($redirect);
|
||||
$redirect['query'] = Template::mergeQuery(((isset($redirect['query'])) ? $redirect['query'] : ''), ['inviteId' => $membership->getUid(), 'userId' => $membership->getAttribute('userId'), 'secret' => $secret]);
|
||||
$redirect = Template::unParseURL($redirect);
|
||||
|
||||
$body = new Template(__DIR__.'/../config/locales/templates/'.Locale::getText('auth.emails.invitation.body'));
|
||||
$body
|
||||
->setParam('{{direction}}', Locale::getText('settings.direction'))
|
||||
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
|
||||
->setParam('{{team}}', $team->getAttribute('name', '[TEAM-NAME]'))
|
||||
->setParam('{{owner}}', $user->getAttribute('name', ''))
|
||||
->setParam('{{redirect}}', $redirect)
|
||||
;
|
||||
|
||||
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
|
||||
|
||||
$mail->addAddress($invitee->getAttribute('email'), $invitee->getAttribute('name'));
|
||||
|
||||
$mail->Subject = sprintf(Locale::getText('auth.emails.invitation.title'), $team->getAttribute('name', '[TEAM-NAME]'), $project->getAttribute('name', ['[APP-NAME]']));
|
||||
$mail->Body = $body->render();
|
||||
$mail->AltBody = strip_tags($body->render());
|
||||
|
||||
try {
|
||||
$mail->send();
|
||||
} catch (\Exception $error) {
|
||||
//throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('userId', $user->getUid())
|
||||
->setParam('event', 'auth.invite.resend')
|
||||
;
|
||||
|
||||
$response
|
||||
// ->setStatusCode(Response::STATUS_CODE_CREATED) TODO change response of this endpoint
|
||||
->noContent()
|
||||
;
|
||||
$response->json($users);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
|
||||
->desc('Update Team Membership Status')
|
||||
->label('scope', 'auth')
|
||||
->label('scope', 'public')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'updateTeamMembershipStatus')
|
||||
->label('sdk.method', 'updateMembershipStatus')
|
||||
->label('sdk.description', '/docs/references/teams/update-team-membership-status.md')
|
||||
->label('sdk.cookies', true)
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->param('inviteId', '', function () { return new UID(); }, 'Invite unique ID')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID')
|
||||
->param('secret', '', function () { return new Text(256); }, 'Secret Key')
|
||||
->param('success', null, function () use ($clients) { return new Host($clients); }, 'Redirect when registration succeed', true)
|
||||
->param('failure', null, function () use ($clients) { return new Host($clients); }, 'Redirect when registration failed', true)
|
||||
->param('inviteId', '', function () { return new UID(); }, 'Invite unique ID.')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
->param('secret', '', function () { return new Text(256); }, 'Secret key.')
|
||||
->action(
|
||||
function ($teamId, $inviteId, $userId, $secret, $success, $failure) use ($response, $request, $user, $audit, $projectDB) {
|
||||
$invite = $projectDB->getDocument($inviteId);
|
||||
|
||||
if (empty($invite->getUid()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $invite->getCollection()) {
|
||||
if ($failure) {
|
||||
$response->redirect($failure);
|
||||
|
||||
return;
|
||||
}
|
||||
function ($teamId, $inviteId, $userId, $secret) use ($response, $request, $user, $audit, $projectDB) {
|
||||
$membership = $projectDB->getDocument($inviteId);
|
||||
|
||||
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
|
||||
throw new Exception('Invite not found', 404);
|
||||
}
|
||||
|
||||
if ($invite->getAttribute('teamId')->getUid() !== $teamId) {
|
||||
if ($membership->getAttribute('teamId') !== $teamId) {
|
||||
throw new Exception('Team IDs don\'t match', 404);
|
||||
}
|
||||
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
Authorization::disable();
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
Authorization::reset();
|
||||
|
||||
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
if (Auth::hash($secret) !== $invite->getAttribute('secret')) {
|
||||
if ($failure) {
|
||||
$response->redirect($failure);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Auth::hash($secret) !== $membership->getAttribute('secret')) {
|
||||
throw new Exception('Secret key not valid', 401);
|
||||
}
|
||||
|
||||
if ($userId != $invite->getAttribute('userId')) {
|
||||
if ($failure) {
|
||||
$response->redirect($failure);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($userId != $membership->getAttribute('userId')) {
|
||||
throw new Exception('Invite not belong to current user ('.$user->getAttribute('email').')', 401);
|
||||
}
|
||||
|
||||
if (empty($user->getUid())) {
|
||||
if (empty($user->getId())) {
|
||||
$user = $projectDB->getCollection([ // Get user
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'$uid='.$userId,
|
||||
'$id='.$userId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($invite->getAttribute('userId') !== $user->getUid()) {
|
||||
if ($failure) {
|
||||
$response->redirect($failure);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('userId') !== $user->getId()) {
|
||||
throw new Exception('Invite not belong to current user ('.$user->getAttribute('email').')', 401);
|
||||
}
|
||||
|
||||
$invite // Attach user to team
|
||||
$membership // Attach user to team
|
||||
->setAttribute('joined', time())
|
||||
->setAttribute('confirm', true)
|
||||
;
|
||||
|
||||
$user
|
||||
->setAttribute('confirm', true)
|
||||
->setAttribute('memberships', $invite, Document::SET_TYPE_APPEND);
|
||||
->setAttribute('emailVerification', true)
|
||||
->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND)
|
||||
;
|
||||
|
||||
// Log user in
|
||||
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
|
|
@ -565,7 +486,7 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
|
|||
|
||||
$user->setAttribute('tokens', new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['user:'.$user->getUid()], 'write' => ['user:'.$user->getUid()]],
|
||||
'$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]],
|
||||
'type' => Auth::TOKEN_TYPE_LOGIN,
|
||||
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
|
||||
'expire' => $expiry,
|
||||
|
|
@ -581,56 +502,71 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
|
|||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
Authorization::disable();
|
||||
|
||||
$team = $projectDB->updateDocument(array_merge($team->getArrayCopy(), [
|
||||
'sum' => $team->getAttribute('sum', 0) + 1,
|
||||
]));
|
||||
|
||||
Authorization::reset();
|
||||
|
||||
if (false === $team) {
|
||||
throw new Exception('Failed saving team to DB', 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('userId', $user->getUid())
|
||||
->setParam('event', 'auth.join')
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('event', 'teams.membership.update')
|
||||
->setParam('resource', 'teams/'.$teamId)
|
||||
;
|
||||
|
||||
$response->addCookie(Auth::$cookieName, Auth::encodeSession($user->getUid(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE);
|
||||
|
||||
if ($success) {
|
||||
$response->redirect($success);
|
||||
}
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
$response
|
||||
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
|
||||
->json(array_merge($membership->getArrayCopy([
|
||||
'$id',
|
||||
'userId',
|
||||
'teamId',
|
||||
'roles',
|
||||
'invited',
|
||||
'joined',
|
||||
'confirm',
|
||||
]), [
|
||||
'email' => $user->getAttribute('email'),
|
||||
'name' => $user->getAttribute('name'),
|
||||
]))
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->delete('/v1/teams/:teamId/memberships/:inviteId')
|
||||
->desc('Delete Team Membership')
|
||||
->label('scope', 'account')
|
||||
->label('scope', 'teams.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'deleteTeamMembership')
|
||||
->label('sdk.method', 'deleteMembership')
|
||||
->label('sdk.description', '/docs/references/teams/delete-team-membership.md')
|
||||
->param('teamId', '', function () { return new UID(); }, 'Team unique ID.')
|
||||
->param('inviteId', '', function () { return new UID(); }, 'Invite unique ID')
|
||||
->param('inviteId', '', function () { return new UID(); }, 'Invite unique ID.')
|
||||
->action(
|
||||
function ($teamId, $inviteId) use ($response, $projectDB, $audit) {
|
||||
$invite = $projectDB->getDocument($inviteId);
|
||||
$membership = $projectDB->getDocument($inviteId);
|
||||
|
||||
if (empty($invite->getUid()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $invite->getCollection()) {
|
||||
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
|
||||
throw new Exception('Invite not found', 404);
|
||||
}
|
||||
|
||||
if ($invite->getAttribute('teamId') !== $teamId) {
|
||||
if ($membership->getAttribute('teamId') !== $teamId) {
|
||||
throw new Exception('Team IDs don\'t match', 404);
|
||||
}
|
||||
|
||||
$team = $projectDB->getDocument($teamId);
|
||||
|
||||
if (empty($team->getUid()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
if (empty($team->getId()) || Database::SYSTEM_COLLECTION_TEAMS != $team->getCollection()) {
|
||||
throw new Exception('Team not found', 404);
|
||||
}
|
||||
|
||||
if (!$projectDB->deleteDocument($invite->getUid())) {
|
||||
if (!$projectDB->deleteDocument($membership->getId())) {
|
||||
throw new Exception('Failed to remove membership from DB', 500);
|
||||
}
|
||||
|
||||
|
|
@ -643,8 +579,9 @@ $utopia->delete('/v1/teams/:teamId/memberships/:inviteId')
|
|||
}
|
||||
|
||||
$audit
|
||||
->setParam('userId', $invite->getAttribute('userId'))
|
||||
->setParam('event', 'auth.leave')
|
||||
->setParam('userId', $membership->getAttribute('userId'))
|
||||
->setParam('event', 'teams.membership.delete')
|
||||
->setParam('resource', 'teams/'.$teamId)
|
||||
;
|
||||
|
||||
$response->noContent();
|
||||
|
|
@ -6,6 +6,7 @@ use Auth\Auth;
|
|||
use Auth\Validator\Password;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Response;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Validator\Email;
|
||||
use Utopia\Validator\Text;
|
||||
|
|
@ -18,13 +19,79 @@ use Database\Validator\UID;
|
|||
use DeviceDetector\DeviceDetector;
|
||||
use GeoIp2\Database\Reader;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
$utopia->post('/v1/users')
|
||||
->desc('Create User')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'create')
|
||||
->label('sdk.description', '/docs/references/users/create-user.md')
|
||||
->param('email', '', function () { return new Email(); }, 'User email.')
|
||||
->param('password', '', function () { return new Password(); }, 'User password.')
|
||||
->param('name', '', function () { return new Text(100); }, 'User name.', true)
|
||||
->action(
|
||||
function ($email, $password, $name) use ($response, $register, $projectDB, $providers) {
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'email='.$email,
|
||||
],
|
||||
]);
|
||||
|
||||
if (!empty($profile)) {
|
||||
throw new Exception('User already registered', 400);
|
||||
}
|
||||
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash($password),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'emailVerification' => false,
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$oauth2Keys = [];
|
||||
|
||||
foreach ($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' => []]));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/users')
|
||||
->desc('List Users')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'listUsers')
|
||||
->label('sdk.method', 'list')
|
||||
->label('sdk.description', '/docs/references/users/list-users.md')
|
||||
->param('search', '', function () { return new Text(256); }, 'Search term to filter your list results.', true)
|
||||
->param('limit', 25, function () { return new Range(0, 100); }, 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
|
|
@ -44,28 +111,28 @@ $utopia->get('/v1/users')
|
|||
],
|
||||
]);
|
||||
|
||||
$oauthKeys = [];
|
||||
$oauth2Keys = [];
|
||||
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider['enabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key);
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
|
||||
$oauth2Keys[] = 'oauth2'.ucfirst($key);
|
||||
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
|
||||
}
|
||||
|
||||
$results = array_map(function ($value) use ($oauthKeys) { /* @var $value \Database\Document */
|
||||
$results = array_map(function ($value) use ($oauth2Keys) { /* @var $value \Database\Document */
|
||||
return $value->getArrayCopy(array_merge(
|
||||
[
|
||||
'$uid',
|
||||
'$id',
|
||||
'status',
|
||||
'email',
|
||||
'registration',
|
||||
'confirm',
|
||||
'emailVerification',
|
||||
'name',
|
||||
],
|
||||
$oauthKeys
|
||||
$oauth2Keys
|
||||
));
|
||||
}, $results);
|
||||
|
||||
|
|
@ -76,66 +143,65 @@ $utopia->get('/v1/users')
|
|||
$utopia->get('/v1/users/:userId')
|
||||
->desc('Get User')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'getUser')
|
||||
->label('sdk.method', 'get')
|
||||
->label('sdk.description', '/docs/references/users/get-user.md')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
->action(
|
||||
function ($userId) use ($response, $projectDB, $providers) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$oauthKeys = [];
|
||||
$oauth2Keys = [];
|
||||
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider['enabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key);
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
|
||||
$oauth2Keys[] = 'oauth2'.ucfirst($key);
|
||||
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
|
||||
}
|
||||
|
||||
$response->json(array_merge($user->getArrayCopy(array_merge(
|
||||
[
|
||||
'$uid',
|
||||
'$id',
|
||||
'status',
|
||||
'email',
|
||||
'registration',
|
||||
'confirm',
|
||||
'emailVerification',
|
||||
'name',
|
||||
],
|
||||
$oauthKeys
|
||||
$oauth2Keys
|
||||
)), ['roles' => []]));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/users/:userId/prefs')
|
||||
->desc('Get User Prefs')
|
||||
->desc('Get User Preferences')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'getUserPrefs')
|
||||
->label('sdk.method', 'getPrefs')
|
||||
->label('sdk.description', '/docs/references/users/get-user-prefs.md')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
->action(
|
||||
function ($userId) use ($response, $projectDB) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$prefs = $user->getAttribute('prefs', '');
|
||||
|
||||
if (empty($prefs)) {
|
||||
$prefs = '[]';
|
||||
}
|
||||
|
||||
try {
|
||||
$prefs = json_decode($prefs, true);
|
||||
$prefs = ($prefs) ? $prefs : [];
|
||||
} catch (\Exception $error) {
|
||||
throw new Exception('Failed to parse prefs', 500);
|
||||
}
|
||||
|
|
@ -147,20 +213,21 @@ $utopia->get('/v1/users/:userId/prefs')
|
|||
$utopia->get('/v1/users/:userId/sessions')
|
||||
->desc('Get User Sessions')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'getUserSessions')
|
||||
->label('sdk.method', 'getSessions')
|
||||
->label('sdk.description', '/docs/references/users/get-user-sessions.md')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
->action(
|
||||
function ($userId) use ($response, $projectDB) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$tokens = $user->getAttribute('tokens', []);
|
||||
$reader = new Reader(__DIR__.'/../db/DBIP/dbip-country-lite-2020-01.mmdb');
|
||||
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
|
||||
$sessions = [];
|
||||
$index = 0;
|
||||
$countries = Locale::getText('countries');
|
||||
|
|
@ -180,7 +247,7 @@ $utopia->get('/v1/users/:userId/sessions')
|
|||
$dd->parse();
|
||||
|
||||
$sessions[$index] = [
|
||||
'$uid' => $token->getUid(),
|
||||
'$id' => $token->getId(),
|
||||
'OS' => $dd->getOs(),
|
||||
'client' => $dd->getClient(),
|
||||
'device' => $dd->getDevice(),
|
||||
|
|
@ -209,28 +276,29 @@ $utopia->get('/v1/users/:userId/sessions')
|
|||
$utopia->get('/v1/users/:userId/logs')
|
||||
->desc('Get User Logs')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'getUserLogs')
|
||||
->label('sdk.method', 'getLogs')
|
||||
->label('sdk.description', '/docs/references/users/get-user-logs.md')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
->action(
|
||||
function ($userId) use ($response, $register, $projectDB, $project) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$adapter = new AuditAdapter($register->get('db'));
|
||||
$adapter->setNamespace('app_'.$project->getUid());
|
||||
$adapter->setNamespace('app_'.$project->getId());
|
||||
|
||||
$audit = new Audit($adapter);
|
||||
|
||||
$countries = Locale::getText('countries');
|
||||
|
||||
$logs = $audit->getLogsByUser($user->getUid());
|
||||
$logs = $audit->getLogsByUser($user->getId());
|
||||
|
||||
$reader = new Reader(__DIR__.'/../db/DBIP/dbip-country-lite-2020-01.mmdb');
|
||||
$reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
|
||||
$output = [];
|
||||
|
||||
foreach ($logs as $i => &$log) {
|
||||
|
|
@ -269,75 +337,12 @@ $utopia->get('/v1/users/:userId/logs')
|
|||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/users')
|
||||
->desc('Create User')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'createUser')
|
||||
->label('sdk.description', '/docs/references/users/create-user.md')
|
||||
->param('email', '', function () { return new Email(); }, 'User account email.')
|
||||
->param('password', '', function () { return new Password(); }, 'User account password.')
|
||||
->param('name', '', function () { return new Text(100); }, 'User account name.', true)
|
||||
->action(
|
||||
function ($email, $password, $name) use ($response, $register, $projectDB, $providers) {
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'email='.$email,
|
||||
],
|
||||
]);
|
||||
|
||||
if (!empty($profile)) {
|
||||
throw new Exception('User already registered', 400);
|
||||
}
|
||||
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash($password),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'confirm' => false,
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$oauthKeys = [];
|
||||
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider['enabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key);
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->json(array_merge($user->getArrayCopy(array_merge([
|
||||
'$uid',
|
||||
'status',
|
||||
'email',
|
||||
'registration',
|
||||
'confirm',
|
||||
'name',
|
||||
], $oauthKeys)), ['roles' => []]));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/users/:userId/status')
|
||||
->desc('Update User Status')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateUserStatus')
|
||||
->label('sdk.method', 'updateStatus')
|
||||
->label('sdk.description', '/docs/references/users/update-user-status.md')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
->param('status', '', function () { return new WhiteList([Auth::USER_STATUS_ACTIVATED, Auth::USER_STATUS_BLOCKED, Auth::USER_STATUS_UNACTIVATED]); }, 'User Status code. To activate the user pass '.Auth::USER_STATUS_ACTIVATED.', to block the user pass '.Auth::USER_STATUS_BLOCKED.' and for disabling the user pass '.Auth::USER_STATUS_UNACTIVATED)
|
||||
|
|
@ -345,7 +350,7 @@ $utopia->patch('/v1/users/:userId/status')
|
|||
function ($userId, $status) use ($response, $projectDB, $providers) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -357,47 +362,51 @@ $utopia->patch('/v1/users/:userId/status')
|
|||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$oauthKeys = [];
|
||||
$oauth2Keys = [];
|
||||
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider['enabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key);
|
||||
$oauthKeys[] = 'oauth'.ucfirst($key).'AccessToken';
|
||||
$oauth2Keys[] = 'oauth2'.ucfirst($key);
|
||||
$oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
|
||||
}
|
||||
|
||||
$response
|
||||
->json(array_merge($user->getArrayCopy(array_merge([
|
||||
'$uid',
|
||||
'$id',
|
||||
'status',
|
||||
'email',
|
||||
'registration',
|
||||
'confirm',
|
||||
'emailVerification',
|
||||
'name',
|
||||
], $oauthKeys)), ['roles' => []]));
|
||||
], $oauth2Keys)), ['roles' => []]));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->patch('/v1/users/:userId/prefs')
|
||||
->desc('Update User Prefs')
|
||||
->desc('Update User Preferences')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateUserPrefs')
|
||||
->label('sdk.method', 'updatePrefs')
|
||||
->label('sdk.description', '/docs/references/users/update-user-prefs.md')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
->param('prefs', '', function () { return new \Utopia\Validator\Mock(); }, 'Prefs key-value JSON object string.')
|
||||
->param('prefs', '', function () { return new Assoc();}, 'Prefs key-value JSON object.')
|
||||
->action(
|
||||
function ($userId, $prefs) use ($response, $projectDB, $providers) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$old = json_decode($user->getAttribute('prefs', '{}'), true);
|
||||
$old = ($old) ? $old : [];
|
||||
|
||||
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
|
||||
'prefs' => json_encode(array_merge(json_decode($user->getAttribute('prefs', '{}'), true), $prefs)),
|
||||
'prefs' => json_encode(array_merge($old, $prefs)),
|
||||
]));
|
||||
|
||||
if (false === $user) {
|
||||
|
|
@ -406,12 +415,9 @@ $utopia->patch('/v1/users/:userId/prefs')
|
|||
|
||||
$prefs = $user->getAttribute('prefs', '');
|
||||
|
||||
if (empty($prefs)) {
|
||||
$prefs = '[]';
|
||||
}
|
||||
|
||||
try {
|
||||
$prefs = json_decode($prefs, true);
|
||||
$prefs = ($prefs) ? $prefs : [];
|
||||
} catch (\Exception $error) {
|
||||
throw new Exception('Failed to parse prefs', 500);
|
||||
}
|
||||
|
|
@ -424,8 +430,9 @@ $utopia->patch('/v1/users/:userId/prefs')
|
|||
$utopia->delete('/v1/users/:userId/sessions/:session')
|
||||
->desc('Delete User Session')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'deleteUserSession')
|
||||
->label('sdk.method', 'deleteSession')
|
||||
->label('sdk.description', '/docs/references/users/delete-user-session.md')
|
||||
->label('abuse-limit', 100)
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
|
|
@ -434,15 +441,15 @@ $utopia->delete('/v1/users/:userId/sessions/:session')
|
|||
function ($userId, $sessionId) use ($response, $request, $projectDB) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$tokens = $user->getAttribute('tokens', []);
|
||||
|
||||
foreach ($tokens as $token) { /* @var $token Document */
|
||||
if ($sessionId == $token->getUid()) {
|
||||
if (!$projectDB->deleteDocument($token->getUid())) {
|
||||
if ($sessionId == $token->getId()) {
|
||||
if (!$projectDB->deleteDocument($token->getId())) {
|
||||
throw new Exception('Failed to remove token from DB', 500);
|
||||
}
|
||||
}
|
||||
|
|
@ -455,8 +462,9 @@ $utopia->delete('/v1/users/:userId/sessions/:session')
|
|||
$utopia->delete('/v1/users/:userId/sessions')
|
||||
->desc('Delete User Sessions')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'deleteUserSessions')
|
||||
->label('sdk.method', 'deleteSessions')
|
||||
->label('sdk.description', '/docs/references/users/delete-user-sessions.md')
|
||||
->label('abuse-limit', 100)
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
|
||||
|
|
@ -464,14 +472,14 @@ $utopia->delete('/v1/users/:userId/sessions')
|
|||
function ($userId) use ($response, $request, $projectDB) {
|
||||
$user = $projectDB->getDocument($userId);
|
||||
|
||||
if (empty($user->getUid()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) {
|
||||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$tokens = $user->getAttribute('tokens', []);
|
||||
|
||||
foreach ($tokens as $token) { /* @var $token Document */
|
||||
if (!$projectDB->deleteDocument($token->getUid())) {
|
||||
if (!$projectDB->deleteDocument($token->getId())) {
|
||||
throw new Exception('Failed to remove token from DB', 500);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,822 +0,0 @@
|
|||
<?php
|
||||
|
||||
global $utopia, $register, $request, $response, $user, $audit, $webhook, $project, $domain, $projectDB, $providers, $clients;
|
||||
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Email;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Locale\Locale;
|
||||
use Auth\Auth;
|
||||
use Auth\Validator\Password;
|
||||
use Database\Database;
|
||||
use Database\Document;
|
||||
use Database\Validator\Authorization;
|
||||
use Database\Validator\UID;
|
||||
use Template\Template;
|
||||
use OpenSSL\OpenSSL;
|
||||
|
||||
include_once 'shared/api.php';
|
||||
|
||||
$utopia->post('/v1/auth/register')
|
||||
->desc('Register')
|
||||
->label('webhook', 'auth.register')
|
||||
->label('scope', 'auth')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'register')
|
||||
->label('sdk.description', '/docs/references/auth/register.md')
|
||||
->label('sdk.cookies', true)
|
||||
->label('abuse-limit', 10)
|
||||
->param('email', '', function () { return new Email(); }, 'Account email')
|
||||
->param('password', '', function () { return new Password(); }, 'User password')
|
||||
->param('confirm', '', function () use ($clients) { return new Host($clients); }, 'Confirmation URL to redirect user after confirm token has been sent to user email') // TODO add our own built-in confirm page
|
||||
->param('success', null, function () use ($clients) { return new Host($clients); }, 'Redirect when registration succeed', true)
|
||||
->param('failure', null, function () use ($clients) { return new Host($clients); }, 'Redirect when registration failed', true)
|
||||
->param('name', '', function () { return new Text(100); }, 'User name', true)
|
||||
->action(
|
||||
function ($email, $password, $confirm, $success, $failure, $name) use ($request, $response, $register, $audit, $projectDB, $project, $webhook) {
|
||||
if ('console' === $project->getUid()) {
|
||||
$whitlistEmails = $project->getAttribute('authWhitelistEmails');
|
||||
$whitlistIPs = $project->getAttribute('authWhitelistIPs');
|
||||
$whitlistDomains = $project->getAttribute('authWhitelistDomains');
|
||||
|
||||
if (!empty($whitlistEmails) && !in_array($email, $whitlistEmails)) {
|
||||
throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401);
|
||||
}
|
||||
|
||||
if (!empty($whitlistIPs) && !in_array($request->getIP(), $whitlistIPs)) {
|
||||
throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401);
|
||||
}
|
||||
|
||||
if (!empty($whitlistDomains) && !in_array(substr(strrchr($email, '@'), 1), $whitlistDomains)) {
|
||||
throw new Exception('Console registration is restricted to specific domains. Contact your administrator for more information.', 401);
|
||||
}
|
||||
}
|
||||
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'email='.$email,
|
||||
],
|
||||
]);
|
||||
|
||||
if (!empty($profile)) {
|
||||
if ($failure) {
|
||||
$response->redirect($failure); // .'?message=User already registered'
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception('User already registered', 400);
|
||||
}
|
||||
|
||||
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$confirmSecret = Auth::tokenGenerator();
|
||||
$loginSecret = Auth::tokenGenerator();
|
||||
|
||||
Authorization::disable();
|
||||
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => [
|
||||
'read' => ['*'],
|
||||
'write' => ['user:{self}'],
|
||||
],
|
||||
'email' => $email,
|
||||
'status' => Auth::USER_STATUS_UNACTIVATED,
|
||||
'password' => Auth::passwordHash($password),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'confirm' => false,
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
Authorization::enable();
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
Authorization::setRole('user:'.$user->getUid());
|
||||
|
||||
$user
|
||||
->setAttribute('tokens', new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['user:'.$user->getUid()], 'write' => ['user:'.$user->getUid()]],
|
||||
'type' => Auth::TOKEN_TYPE_CONFIRM,
|
||||
'secret' => Auth::hash($confirmSecret), // On way hash encryption to protect DB leak
|
||||
'expire' => time() + Auth::TOKEN_EXPIRATION_CONFIRM,
|
||||
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
]), Document::SET_TYPE_APPEND)
|
||||
->setAttribute('tokens', new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['user:'.$user->getUid()], 'write' => ['user:'.$user->getUid()]],
|
||||
'type' => Auth::TOKEN_TYPE_LOGIN,
|
||||
'secret' => Auth::hash($loginSecret), // On way hash encryption to protect DB leak
|
||||
'expire' => $expiry,
|
||||
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
]), Document::SET_TYPE_APPEND)
|
||||
;
|
||||
|
||||
$user = $projectDB->createDocument($user->getArrayCopy());
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving tokens to DB', 500);
|
||||
}
|
||||
|
||||
// Send email address confirmation email
|
||||
|
||||
$confirm = Template::parseURL($confirm);
|
||||
$confirm['query'] = Template::mergeQuery(((isset($confirm['query'])) ? $confirm['query'] : ''), ['userId' => $user->getUid(), 'token' => $confirmSecret]);
|
||||
$confirm = Template::unParseURL($confirm);
|
||||
|
||||
$body = new Template(__DIR__.'/../config/locales/templates/'.Locale::getText('auth.emails.confirm.body'));
|
||||
$body
|
||||
->setParam('{{direction}}', Locale::getText('settings.direction'))
|
||||
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
|
||||
->setParam('{{name}}', $name)
|
||||
->setParam('{{redirect}}', $confirm)
|
||||
;
|
||||
|
||||
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
|
||||
|
||||
$mail->addAddress($email, $name);
|
||||
|
||||
$mail->Subject = Locale::getText('auth.emails.confirm.title');
|
||||
$mail->Body = $body->render();
|
||||
$mail->AltBody = strip_tags($body->render());
|
||||
|
||||
try {
|
||||
$mail->send();
|
||||
} catch (\Exception $error) {
|
||||
// if($failure) {
|
||||
// $response->redirect($failure);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
|
||||
}
|
||||
|
||||
$webhook
|
||||
->setParam('payload', [
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
])
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('userId', $user->getUid())
|
||||
->setParam('event', 'auth.register')
|
||||
;
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getUid(), $loginSecret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE);
|
||||
|
||||
if ($success) {
|
||||
$response->redirect($success);
|
||||
}
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/auth/register/confirm')
|
||||
->desc('Confirmation')
|
||||
->label('webhook', 'auth.confirm')
|
||||
->label('scope', 'public')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'confirm')
|
||||
->label('sdk.description', '/docs/references/auth/confirm.md')
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{param-userId}')
|
||||
->param('userId', '', function () { return new UID(); }, 'User unique ID')
|
||||
->param('token', '', function () { return new Text(256); }, 'Confirmation secret token')
|
||||
->action(
|
||||
function ($userId, $token) use ($response, $request, $projectDB, $audit) {
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'$uid='.$userId,
|
||||
],
|
||||
]);
|
||||
|
||||
if (empty($profile)) {
|
||||
throw new Exception('User not found', 404); // TODO maybe hide this
|
||||
}
|
||||
|
||||
$token = Auth::tokenVerify($profile->getAttribute('tokens', []), Auth::TOKEN_TYPE_CONFIRM, $token);
|
||||
|
||||
if (!$token) {
|
||||
throw new Exception('Confirmation token is not valid', 401);
|
||||
}
|
||||
|
||||
$profile = $projectDB->updateDocument(array_merge($profile->getArrayCopy(), [
|
||||
'status' => Auth::USER_STATUS_ACTIVATED,
|
||||
'confirm' => true,
|
||||
]));
|
||||
|
||||
if (false === $profile) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
if (!$projectDB->deleteDocument($token)) {
|
||||
throw new Exception('Failed to remove token from DB', 500);
|
||||
}
|
||||
|
||||
$audit->setParam('event', 'auth.confirm');
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/auth/register/confirm/resend')
|
||||
->desc('Resend Confirmation')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'confirmResend')
|
||||
->label('sdk.description', '/docs/references/auth/confirm-resend.md')
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{param-userId}')
|
||||
->param('confirm', '', function () use ($clients) { return new Host($clients); }, 'Confirmation URL to redirect user to your app after confirm token has been sent to user email.')
|
||||
->action(
|
||||
function ($confirm) use ($response, $request, $projectDB, $user, $register, $project) {
|
||||
if ($user->getAttribute('confirm', false)) {
|
||||
throw new Exception('Email address is already confirmed', 400);
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
||||
$user->setAttribute('tokens', new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['user:'.$user->getUid()], 'write' => ['user:'.$user->getUid()]],
|
||||
'type' => Auth::TOKEN_TYPE_CONFIRM,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'expire' => time() + Auth::TOKEN_EXPIRATION_CONFIRM,
|
||||
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
]), Document::SET_TYPE_APPEND);
|
||||
|
||||
$user = $projectDB->updateDocument($user->getArrayCopy());
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$confirm = Template::parseURL($confirm);
|
||||
$confirm['query'] = Template::mergeQuery(((isset($confirm['query'])) ? $confirm['query'] : ''), ['userId' => $user->getUid(), 'token' => $secret]);
|
||||
$confirm = Template::unParseURL($confirm);
|
||||
|
||||
$body = new Template(__DIR__.'/../config/locales/templates/'.Locale::getText('auth.emails.confirm.body'));
|
||||
$body
|
||||
->setParam('{{direction}}', Locale::getText('settings.direction'))
|
||||
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
|
||||
->setParam('{{name}}', $user->getAttribute('name'))
|
||||
->setParam('{{redirect}}', $confirm)
|
||||
;
|
||||
|
||||
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
|
||||
|
||||
$mail->addAddress($user->getAttribute('email'), $user->getAttribute('name'));
|
||||
|
||||
$mail->Subject = Locale::getText('auth.emails.confirm.title');
|
||||
$mail->Body = $body->render();
|
||||
$mail->AltBody = strip_tags($body->render());
|
||||
|
||||
try {
|
||||
$mail->send();
|
||||
} catch (\Exception $error) {
|
||||
//throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
|
||||
}
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/auth/login')
|
||||
->desc('Login')
|
||||
->label('webhook', 'auth.login')
|
||||
->label('scope', 'auth')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'login')
|
||||
->label('sdk.description', '/docs/references/auth/login.md')
|
||||
->label('sdk.cookies', true)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->param('email', '', function () { return new Email(); }, 'User account email address')
|
||||
->param('password', '', function () { return new Password(); }, 'User account password')
|
||||
->param('success', null, function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt.', true)
|
||||
->param('failure', null, function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt.', true)
|
||||
->action(
|
||||
function ($email, $password, $success, $failure) use ($response, $request, $projectDB, $audit, $webhook) {
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'email='.$email,
|
||||
],
|
||||
]);
|
||||
|
||||
if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) {
|
||||
$audit
|
||||
//->setParam('userId', $profile->getUid())
|
||||
->setParam('event', 'auth.failure')
|
||||
;
|
||||
|
||||
if ($failure) {
|
||||
$response->redirect($failure);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception('Invalid credentials', 401); // Wrong password or username
|
||||
}
|
||||
|
||||
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
||||
$profile->setAttribute('tokens', new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['user:'.$profile->getUid()], 'write' => ['user:'.$profile->getUid()]],
|
||||
'type' => Auth::TOKEN_TYPE_LOGIN,
|
||||
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
|
||||
'expire' => $expiry,
|
||||
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
]), Document::SET_TYPE_APPEND);
|
||||
|
||||
Authorization::setRole('user:'.$profile->getUid());
|
||||
|
||||
$profile = $projectDB->updateDocument($profile->getArrayCopy());
|
||||
|
||||
if (false === $profile) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$webhook
|
||||
->setParam('payload', [
|
||||
'name' => $profile->getAttribute('name', ''),
|
||||
'email' => $profile->getAttribute('email', ''),
|
||||
])
|
||||
;
|
||||
|
||||
$audit
|
||||
->setParam('userId', $profile->getUid())
|
||||
->setParam('event', 'auth.login')
|
||||
;
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getUid(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE);
|
||||
|
||||
if ($success) {
|
||||
$response->redirect($success);
|
||||
}
|
||||
|
||||
$response
|
||||
->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/auth/login/oauth/:provider')
|
||||
->desc('Login with OAuth')
|
||||
->label('error', __DIR__.'/../views/general/error.phtml')
|
||||
->label('scope', 'auth')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'oauth')
|
||||
->label('sdk.description', '/docs/references/auth/login-oauth.md')
|
||||
->label('sdk.location', true)
|
||||
->label('sdk.cookies', true)
|
||||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth Provider. Currently, supported providers are: ' . implode(', ', array_keys($providers)))
|
||||
->param('success', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt.')
|
||||
->param('failure', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt.')
|
||||
->param('scopes', [], function () { return new ArrayList(new Text(128)); }, 'An array of string where each can be max 128 chars', true)
|
||||
->action(
|
||||
function ($provider, $success, $failure, $scopes) use ($response, $request, $project) {
|
||||
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/auth/login/oauth/callback/'.$provider.'/'.$project->getUid();
|
||||
$appId = $project->getAttribute('usersOauth'.ucfirst($provider).'Appid', '');
|
||||
$appSecret = $project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}');
|
||||
|
||||
$appSecret = json_decode($appSecret, true);
|
||||
|
||||
if (!empty($appSecret) && isset($appSecret['version'])) {
|
||||
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$appSecret['version']);
|
||||
$appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, hex2bin($appSecret['iv']), hex2bin($appSecret['tag']));
|
||||
}
|
||||
|
||||
if (empty($appId) || empty($appSecret)) {
|
||||
throw new Exception('Provider is undefined, configure provider app ID and app secret key to continue', 412);
|
||||
}
|
||||
|
||||
$classname = 'Auth\\OAuth\\'.ucfirst($provider);
|
||||
|
||||
if (!class_exists($classname)) {
|
||||
throw new Exception('Provider is not supported', 501);
|
||||
}
|
||||
|
||||
$oauth = new $classname($appId, $appSecret, $callback, ['success' => $success, 'failure' => $failure], $scopes);
|
||||
|
||||
$response->redirect($oauth->getLoginURL());
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/auth/login/oauth/callback/:provider/:projectId')
|
||||
->desc('OAuth Callback')
|
||||
->label('error', __DIR__.'/../views/general/error.phtml')
|
||||
->label('scope', 'auth')
|
||||
->label('docs', false)
|
||||
->param('projectId', '', function () { return new Text(1024); }, 'Project unique ID')
|
||||
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth provider')
|
||||
->param('code', '', function () { return new Text(1024); }, 'OAuth code')
|
||||
->param('state', '', function () { return new Text(2048); }, 'Login state params', true)
|
||||
->action(
|
||||
function ($projectId, $provider, $code, $state) use ($response, $request, $domain) {
|
||||
$response->redirect($request->getServer('REQUEST_SCHEME', 'https').'://'.$domain.'/v1/auth/login/oauth/'.$provider.'/redirect?'
|
||||
.http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/auth/login/oauth/:provider/redirect')
|
||||
->desc('OAuth Redirect')
|
||||
->label('error', __DIR__.'/../views/general/error.phtml')
|
||||
->label('webhook', 'auth.oauth')
|
||||
->label('scope', 'auth')
|
||||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->label('docs', false)
|
||||
->param('provider', '', function () use ($providers) { return new WhiteList(array_keys($providers)); }, 'OAuth provider')
|
||||
->param('code', '', function () { return new Text(1024); }, 'OAuth code')
|
||||
->param('state', '', function () { return new Text(2048); }, 'OAuth state params', true)
|
||||
->action(
|
||||
function ($provider, $code, $state) use ($response, $request, $user, $projectDB, $project, $audit) {
|
||||
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/auth/login/oauth/callback/'.$provider.'/'.$project->getUid();
|
||||
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
||||
$validateURL = new URL();
|
||||
|
||||
$appId = $project->getAttribute('usersOauth'.ucfirst($provider).'Appid', '');
|
||||
$appSecret = $project->getAttribute('usersOauth'.ucfirst($provider).'Secret', '{}');
|
||||
|
||||
$appSecret = json_decode($appSecret, true);
|
||||
|
||||
if (!empty($appSecret) && isset($appSecret['version'])) {
|
||||
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$appSecret['version']);
|
||||
$appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, hex2bin($appSecret['iv']), hex2bin($appSecret['tag']));
|
||||
}
|
||||
|
||||
$classname = 'Auth\\OAuth\\'.ucfirst($provider);
|
||||
|
||||
if (!class_exists($classname)) {
|
||||
throw new Exception('Provider is not supported', 501);
|
||||
}
|
||||
|
||||
$oauth = new $classname($appId, $appSecret, $callback);
|
||||
|
||||
if (!empty($state)) {
|
||||
try {
|
||||
$state = array_merge($defaultState, $oauth->parseState($state));
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception('Failed to parse login state params as passed from OAuth provider');
|
||||
}
|
||||
} else {
|
||||
$state = $defaultState;
|
||||
}
|
||||
|
||||
if (!$validateURL->isValid($state['success'])) {
|
||||
throw new Exception('Invalid redirect URL for success login', 400);
|
||||
}
|
||||
|
||||
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
|
||||
throw new Exception('Invalid redirect URL for failure login', 400);
|
||||
}
|
||||
|
||||
$accessToken = $oauth->getAccessToken($code);
|
||||
|
||||
if (empty($accessToken)) {
|
||||
if (!empty($state['failure'])) {
|
||||
$response->redirect($state['failure'], 301, 0);
|
||||
}
|
||||
|
||||
throw new Exception('Failed to obtain access token');
|
||||
}
|
||||
|
||||
$oauthID = $oauth->getUserID($accessToken);
|
||||
|
||||
if (empty($oauthID)) {
|
||||
if (!empty($state['failure'])) {
|
||||
$response->redirect($state['failure'], 301, 0);
|
||||
}
|
||||
|
||||
throw new Exception('Missing ID from OAuth provider', 400);
|
||||
}
|
||||
|
||||
$current = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
|
||||
|
||||
if ($current) {
|
||||
$projectDB->deleteDocument($current); //throw new Exception('User already logged in', 401);
|
||||
}
|
||||
|
||||
$user = (empty($user->getUid())) ? $projectDB->getCollection([ // Get user by provider id
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'oauth'.ucfirst($provider).'='.$oauthID,
|
||||
],
|
||||
]) : $user;
|
||||
|
||||
if (empty($user)) { // No user logged in or with oauth provider ID, create new one or connect with account with same email
|
||||
$name = $oauth->getUserName($accessToken);
|
||||
$email = $oauth->getUserEmail($accessToken);
|
||||
|
||||
$user = $projectDB->getCollection([ // Get user by provider email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'email='.$email,
|
||||
],
|
||||
]);
|
||||
|
||||
if (!$user || empty($user->getUid())) { // Last option -> create user alone, generate random password
|
||||
Authorization::disable();
|
||||
|
||||
$user = $projectDB->createDocument([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_USERS,
|
||||
'$permissions' => ['read' => ['*'], 'write' => ['user:{self}']],
|
||||
'email' => $email,
|
||||
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth provider
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
'password-update' => time(),
|
||||
'registration' => time(),
|
||||
'confirm' => true,
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
Authorization::enable();
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create login token, confirm user account and update OAuth ID and Access Token
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
$expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
|
||||
$user
|
||||
->setAttribute('oauth'.ucfirst($provider), $oauthID)
|
||||
->setAttribute('oauth'.ucfirst($provider).'AccessToken', $accessToken)
|
||||
->setAttribute('status', Auth::USER_STATUS_ACTIVATED)
|
||||
->setAttribute('tokens', new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['user:'.$user['$uid']], 'write' => ['user:'.$user['$uid']]],
|
||||
'type' => Auth::TOKEN_TYPE_LOGIN,
|
||||
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
|
||||
'expire' => $expiry,
|
||||
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
]), Document::SET_TYPE_APPEND)
|
||||
;
|
||||
|
||||
Authorization::setRole('user:'.$user->getUid());
|
||||
|
||||
$user = $projectDB->updateDocument($user->getArrayCopy());
|
||||
|
||||
if (false === $user) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('userId', $user->getUid())
|
||||
->setParam('event', 'auth.oauth.login')
|
||||
->setParam('data', ['provider' => $provider])
|
||||
;
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getUid(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
|
||||
;
|
||||
|
||||
$response->redirect($state['success']);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->delete('/v1/auth/logout')
|
||||
->desc('Logout Current Session')
|
||||
->label('webhook', 'auth.logout')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'logout')
|
||||
->label('sdk.description', '/docs/references/auth/logout.md')
|
||||
->label('abuse-limit', 100)
|
||||
->action(
|
||||
function () use ($response, $request, $user, $projectDB, $audit, $webhook) {
|
||||
$token = Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret);
|
||||
|
||||
if (!$projectDB->deleteDocument($token)) {
|
||||
throw new Exception('Failed to remove token from DB', 500);
|
||||
}
|
||||
|
||||
$webhook
|
||||
->setParam('payload', [
|
||||
'name' => $user->getAttribute('name', ''),
|
||||
'email' => $user->getAttribute('email', ''),
|
||||
])
|
||||
;
|
||||
|
||||
$audit->setParam('event', 'auth.logout');
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
|
||||
->json(array('result' => 'success'))
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->delete('/v1/auth/logout/:id')
|
||||
->desc('Logout Specific Session')
|
||||
->label('scope', 'account')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'logoutBySession')
|
||||
->label('sdk.description', '/docs/references/auth/logout-by-session.md')
|
||||
->label('abuse-limit', 100)
|
||||
->param('id', null, function () { return new UID(); }, 'User specific session unique ID number. if 0 delete all sessions.')
|
||||
->action(
|
||||
function ($id) use ($response, $request, $user, $projectDB, $audit) {
|
||||
$tokens = $user->getAttribute('tokens', []);
|
||||
|
||||
foreach ($tokens as $token) { /* @var $token Document */
|
||||
if (($id == $token->getUid() || ($id == 0)) && Auth::TOKEN_TYPE_LOGIN == $token->getAttribute('type')) {
|
||||
if (!$projectDB->deleteDocument($token->getUid())) {
|
||||
throw new Exception('Failed to remove token from DB', 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('event', 'auth.logout')
|
||||
->setParam('resource', '/auth/token/'.$token->getUid())
|
||||
;
|
||||
|
||||
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete cookies
|
||||
$response->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->post('/v1/auth/recovery')
|
||||
->desc('Password Recovery')
|
||||
->label('scope', 'auth')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'recovery')
|
||||
->label('sdk.description', '/docs/references/auth/recovery.md')
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->param('email', '', function () { return new Email(); }, 'User account email address.')
|
||||
->param('reset', '', function () use ($clients) { return new Host($clients); }, 'Reset URL in your app to redirect the user after the reset token has been sent to the user email.')
|
||||
->action(
|
||||
function ($email, $reset) use ($request, $response, $projectDB, $register, $audit, $project) {
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'email='.$email,
|
||||
],
|
||||
]);
|
||||
|
||||
if (empty($profile)) {
|
||||
throw new Exception('User not found', 404); // TODO maybe hide this
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
|
||||
$profile->setAttribute('tokens', new Document([
|
||||
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
|
||||
'$permissions' => ['read' => ['user:'.$profile->getUid()], 'write' => ['user:'.$profile->getUid()]],
|
||||
'type' => Auth::TOKEN_TYPE_RECOVERY,
|
||||
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
|
||||
'expire' => time() + Auth::TOKEN_EXPIRATION_RECOVERY,
|
||||
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
]), Document::SET_TYPE_APPEND);
|
||||
|
||||
Authorization::setRole('user:'.$profile->getUid());
|
||||
|
||||
$profile = $projectDB->updateDocument($profile->getArrayCopy());
|
||||
|
||||
if (false === $profile) {
|
||||
throw new Exception('Failed to save user to DB', 500);
|
||||
}
|
||||
|
||||
$reset = Template::parseURL($reset);
|
||||
$reset['query'] = Template::mergeQuery(((isset($reset['query'])) ? $reset['query'] : ''), ['userId' => $profile->getUid(), 'token' => $secret]);
|
||||
$reset = Template::unParseURL($reset);
|
||||
|
||||
$body = new Template(__DIR__.'/../config/locales/templates/'.Locale::getText('auth.emails.recovery.body'));
|
||||
$body
|
||||
->setParam('{{direction}}', Locale::getText('settings.direction'))
|
||||
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
|
||||
->setParam('{{name}}', $profile->getAttribute('name'))
|
||||
->setParam('{{redirect}}', $reset)
|
||||
;
|
||||
|
||||
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
|
||||
|
||||
$mail->addAddress($profile->getAttribute('email', ''), $profile->getAttribute('name', ''));
|
||||
|
||||
$mail->Subject = Locale::getText('auth.emails.recovery.title');
|
||||
$mail->Body = $body->render();
|
||||
$mail->AltBody = strip_tags($body->render());
|
||||
|
||||
try {
|
||||
$mail->send();
|
||||
} catch (\Exception $error) {
|
||||
//throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('userId', $profile->getUid())
|
||||
->setParam('event', 'auth.recovery')
|
||||
;
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->put('/v1/auth/recovery/reset')
|
||||
->desc('Password Reset')
|
||||
->label('scope', 'auth')
|
||||
->label('sdk.namespace', 'auth')
|
||||
->label('sdk.method', 'recoveryReset')
|
||||
->label('sdk.description', '/docs/references/auth/recovery-reset.md')
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{param-userId}')
|
||||
->param('userId', '', function () { return new UID(); }, 'User account email address.')
|
||||
->param('token', '', function () { return new Text(256); }, 'Valid reset token.')
|
||||
->param('password-a', '', function () { return new Password(); }, 'New password.')
|
||||
->param('password-b', '', function () {return new Password(); }, 'New password again.')
|
||||
->action(
|
||||
function ($userId, $token, $passwordA, $passwordB) use ($response, $projectDB, $audit) {
|
||||
if ($passwordA !== $passwordB) {
|
||||
throw new Exception('Passwords must match', 400);
|
||||
}
|
||||
|
||||
$profile = $projectDB->getCollection([ // Get user by email address
|
||||
'limit' => 1,
|
||||
'first' => true,
|
||||
'filters' => [
|
||||
'$collection='.Database::SYSTEM_COLLECTION_USERS,
|
||||
'$uid='.$userId,
|
||||
],
|
||||
]);
|
||||
|
||||
if (empty($profile)) {
|
||||
throw new Exception('User not found', 404); // TODO maybe hide this
|
||||
}
|
||||
|
||||
$token = Auth::tokenVerify($profile->getAttribute('tokens', []), Auth::TOKEN_TYPE_RECOVERY, $token);
|
||||
|
||||
if (!$token) {
|
||||
throw new Exception('Recovery token is not valid', 401);
|
||||
}
|
||||
|
||||
Authorization::setRole('user:'.$profile->getUid());
|
||||
|
||||
$profile = $projectDB->updateDocument(array_merge($profile->getArrayCopy(), [
|
||||
'password' => Auth::passwordHash($passwordA),
|
||||
'password-update' => time(),
|
||||
'confirm' => true,
|
||||
]));
|
||||
|
||||
if (false === $profile) {
|
||||
throw new Exception('Failed saving user to DB', 500);
|
||||
}
|
||||
|
||||
if (!$projectDB->deleteDocument($token)) {
|
||||
throw new Exception('Failed to remove token from DB', 500);
|
||||
}
|
||||
|
||||
$audit
|
||||
->setParam('userId', $profile->getUid())
|
||||
->setParam('event', 'auth.recovery.reset')
|
||||
;
|
||||
|
||||
$response->json(array('result' => 'success'));
|
||||
}
|
||||
);
|
||||
|
|
@ -6,6 +6,7 @@ use Utopia\Validator\Numeric;
|
|||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Storage\Validators\File;
|
||||
use Utopia\Response;
|
||||
use Utopia\Validator\Host;
|
||||
|
||||
$result = [];
|
||||
|
|
@ -175,9 +176,23 @@ $utopia->post('/v1/mock/tests/general/upload')
|
|||
function ($x, $y, $z, $file) use ($request) {
|
||||
$file = $request->getFiles('file');
|
||||
$file['tmp_name'] = (is_array($file['tmp_name'])) ? $file['tmp_name'] : [$file['tmp_name']];
|
||||
$file['name'] = (is_array($file['name'])) ? $file['name'] : [$file['name']];
|
||||
$file['size'] = (is_array($file['size'])) ? $file['size'] : [$file['size']];
|
||||
|
||||
foreach ($file['name'] as $i => $name) {
|
||||
if($name !== 'file.png') {
|
||||
throw new Exception('Wrong file name', 400);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($file['size'] as $i => $size) {
|
||||
if($size !== 38756) {
|
||||
throw new Exception('Wrong file size', 400);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($file['tmp_name'] as $i => $tmpName) {
|
||||
if(md5(file_get_contents($tmpName)) !== 'asdasdasd') {
|
||||
if(md5(file_get_contents($tmpName)) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') {
|
||||
throw new Exception('Wrong file uploaded', 400);
|
||||
}
|
||||
}
|
||||
|
|
@ -216,38 +231,39 @@ $utopia->get('/v1/mock/tests/general/empty')
|
|||
->action(
|
||||
function () use ($response) {
|
||||
$response->noContent();
|
||||
exit();
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/mock/tests/general/oauth/login')
|
||||
->desc('Mock an OAuth login route')
|
||||
$utopia->get('/v1/mock/tests/general/oauth2')
|
||||
->desc('Mock an OAuth2 login route')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->param('client_id', '', function () { return new Text(100); }, 'OAuth Client ID.')
|
||||
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth Redirect URI.') // Important to deny an open redirect attack
|
||||
->param('scope', '', function () { return new Text(100); }, 'OAuth scope list.')
|
||||
->param('state', '', function () { return new Text(100); }, 'OAuth state.')
|
||||
->param('client_id', '', function () { return new Text(100); }, 'OAuth2 Client ID.')
|
||||
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth2 Redirect URI.') // Important to deny an open redirect attack
|
||||
->param('scope', '', function () { return new Text(100); }, 'OAuth2 scope list.')
|
||||
->param('state', '', function () { return new Text(1024); }, 'OAuth2 state.')
|
||||
->action(
|
||||
function ($clientId, $redirectURI, $scope, $state) use ($response) {
|
||||
$response->redirect($redirectURI);
|
||||
$response->redirect($redirectURI.'?'.http_build_query(['code' => 'abcdef', 'state' => $state]));
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/mock/tests/general/oauth/token')
|
||||
->desc('Mock an OAuth login route')
|
||||
$utopia->get('/v1/mock/tests/general/oauth2/token')
|
||||
->desc('Mock an OAuth2 login route')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->param('client_id', '', function () { return new Text(100); }, 'OAuth Client ID.')
|
||||
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth Redirect URI.')
|
||||
->param('client_secret', '', function () { return new Text(100); }, 'OAuth scope list.')
|
||||
->param('code', '', function () { return new Text(100); }, 'OAuth state.')
|
||||
->param('client_id', '', function () { return new Text(100); }, 'OAuth2 Client ID.')
|
||||
->param('redirect_uri', '', function () { return new Host(['http://localhost']); }, 'OAuth2 Redirect URI.')
|
||||
->param('client_secret', '', function () { return new Text(100); }, 'OAuth2 scope list.')
|
||||
->param('code', '', function () { return new Text(100); }, 'OAuth2 state.')
|
||||
->action(
|
||||
function ($clientId, $redirectURI, $clientSecret, $code) use ($response) {
|
||||
if($clientId != '1') {
|
||||
throw new Exception('Invalid client ID');
|
||||
}
|
||||
|
||||
if($clientSecret != 'secret') {
|
||||
if($clientSecret != '123456') {
|
||||
throw new Exception('Invalid client secret');
|
||||
}
|
||||
|
||||
|
|
@ -259,11 +275,11 @@ $utopia->get('/v1/mock/tests/general/oauth/token')
|
|||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/mock/tests/general/oauth/user')
|
||||
->desc('Mock an OAuth user route')
|
||||
$utopia->get('/v1/mock/tests/general/oauth2/user')
|
||||
->desc('Mock an OAuth2 user route')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->param('token', '', function () { return new Text(100); }, 'OAuth Access Token.')
|
||||
->param('token', '', function () { return new Text(100); }, 'OAuth2 Access Token.')
|
||||
->action(
|
||||
function ($token) use ($response) {
|
||||
if($token != '123456') {
|
||||
|
|
@ -273,11 +289,35 @@ $utopia->get('/v1/mock/tests/general/oauth/user')
|
|||
$response->json([
|
||||
'id' => 1,
|
||||
'name' => 'User Name',
|
||||
'email' => 'user@localhost',
|
||||
'email' => 'user@localhost.test',
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/mock/tests/general/oauth2/success')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->action(
|
||||
function () use ($response) {
|
||||
$response->json([
|
||||
'result' => 'success',
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->get('/v1/mock/tests/general/oauth2/failure')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->action(
|
||||
function () use ($response) {
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_BAD_REQUEST)
|
||||
->json([
|
||||
'result' => 'failure',
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
$utopia->shutdown(function() use ($response, $request, &$result, $utopia) {
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
use Utopia\Exception;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Database\Database;
|
||||
|
||||
global $utopia, $request, $response, $register, $user, $project;
|
||||
|
||||
|
|
@ -17,9 +16,9 @@ $utopia->init(function () use ($utopia, $request, $response, $register, $user, $
|
|||
$timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), function () use ($register) {
|
||||
return $register->get('db');
|
||||
});
|
||||
$timeLimit->setNamespace('app_'.$project->getUid());
|
||||
$timeLimit->setNamespace('app_'.$project->getId());
|
||||
$timeLimit
|
||||
->setParam('{userId}', $user->getUid())
|
||||
->setParam('{userId}', $user->getId())
|
||||
->setParam('{userAgent}', $request->getServer('HTTP_USER_AGENT', ''))
|
||||
->setParam('{ip}', $request->getIP())
|
||||
->setParam('{url}', $request->getServer('HTTP_HOST', '').$route->getURL())
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ $layout
|
|||
->setParam('protocol', $request->getServer('REQUEST_SCHEME', 'https'))
|
||||
->setParam('domain', $domain)
|
||||
->setParam('home', $request->getServer('_APP_HOME'))
|
||||
->setParam('api', $request->getServer('_APP_APPWRITE_HOST_CLIENT'))
|
||||
->setParam('project', $request->getServer('_APP_APPWRITE_ID'))
|
||||
->setParam('setup', $request->getServer('_APP_SETUP'))
|
||||
->setParam('class', 'unknown')
|
||||
->setParam('icon', '/images/favicon.png')
|
||||
->setParam('roles', $roles)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
<?php
|
||||
|
||||
include_once 'shared/web.php';
|
||||
include_once __DIR__ . '/../shared/web.php';
|
||||
|
||||
global $utopia, $response, $request, $layout, $version, $providers;
|
||||
global $utopia, $response, $request, $layout, $version, $providers, $projectDB;
|
||||
|
||||
use Utopia\View;
|
||||
use Database\Database;
|
||||
use Database\Validator\UID;
|
||||
use Storage\Storage;
|
||||
|
||||
$utopia->init(function () use ($layout, $utopia) {
|
||||
$layout
|
||||
|
|
@ -15,8 +16,8 @@ $utopia->init(function () use ($layout, $utopia) {
|
|||
});
|
||||
|
||||
$utopia->shutdown(function () use ($utopia, $response, $request, $layout, $version) {
|
||||
$header = new View(__DIR__.'/../views/console/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../views/console/comps/footer.phtml');
|
||||
$header = new View(__DIR__.'/../../views/console/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/console/comps/footer.phtml');
|
||||
|
||||
$footer
|
||||
->setParam('home', $request->getServer('_APP_HOME', ''))
|
||||
|
|
@ -44,7 +45,7 @@ $utopia->get('/error/:code')
|
|||
->label('scope', 'home')
|
||||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->action(function ($code) use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/error.phtml');
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
|
||||
$page
|
||||
->setParam('code', $code)
|
||||
|
|
@ -59,7 +60,7 @@ $utopia->get('/console')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout, $request) {
|
||||
$page = new View(__DIR__.'/../views/console/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('home', $request->getServer('_APP_HOME', ''))
|
||||
|
|
@ -74,9 +75,9 @@ $utopia->get('/console/account')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/console/account/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/account/index.phtml');
|
||||
|
||||
$cc = new View(__DIR__.'/../views/console/forms/credit-card.phtml');
|
||||
$cc = new View(__DIR__.'/../../views/console/forms/credit-card.phtml');
|
||||
|
||||
$page
|
||||
->setParam('cc', $cc)
|
||||
|
|
@ -92,7 +93,7 @@ $utopia->get('/console/notifications')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/v1/console/notifications/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/v1/console/notifications/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Notifications')
|
||||
|
|
@ -104,7 +105,7 @@ $utopia->get('/console/home')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/console/home/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/home/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Console')
|
||||
|
|
@ -116,7 +117,7 @@ $utopia->get('/console/settings')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/console/settings/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/settings/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Settings')
|
||||
|
|
@ -128,7 +129,7 @@ $utopia->get('/console/webhooks')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/console/webhooks/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Webhooks')
|
||||
|
|
@ -140,7 +141,10 @@ $utopia->get('/console/keys')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/console/keys/index.phtml');
|
||||
$scopes = include __DIR__.'/../../../app/config/scopes.php';
|
||||
$page = new View(__DIR__.'/../../views/console/keys/index.phtml');
|
||||
|
||||
$page->setParam('scopes', $scopes);
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - API Keys')
|
||||
|
|
@ -152,7 +156,7 @@ $utopia->get('/console/tasks')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/console/tasks/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/tasks/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Tasks')
|
||||
|
|
@ -164,7 +168,7 @@ $utopia->get('/console/database')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/console/database/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/database/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database')
|
||||
|
|
@ -179,11 +183,11 @@ $utopia->get('/console/database/collection')
|
|||
->action(function ($id) use ($layout, $projectDB) {
|
||||
$collection = $projectDB->getDocument($id, false);
|
||||
|
||||
if (empty($collection->getUid()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$page = new View(__DIR__.'/../views/console/database/collection.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
|
||||
|
||||
$page
|
||||
->setParam('collection', $collection->getArrayCopy())
|
||||
|
|
@ -199,10 +203,12 @@ $utopia->get('/console/storage')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($request, $layout) {
|
||||
$page = new View(__DIR__.'/../views/console/storage/index.phtml');
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/storage/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('home', $request->getServer('_APP_HOME', ''))
|
||||
->setParam('home', $request->getServer('_APP_HOME', 0))
|
||||
->setParam('fileLimit', $request->getServer('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human($request->getServer('_APP_STORAGE_LIMIT', 0)))
|
||||
;
|
||||
|
||||
$layout
|
||||
|
|
@ -215,7 +221,7 @@ $utopia->get('/console/users')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout, $providers) {
|
||||
$page = new View(__DIR__.'/../views/console/users/index.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/users/index.phtml');
|
||||
|
||||
$page->setParam('providers', $providers);
|
||||
|
||||
|
|
@ -229,7 +235,7 @@ $utopia->get('/console/users/view')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->action(function () use ($layout, $providers) {
|
||||
$page = new View(__DIR__.'/../views/console/users/view.phtml');
|
||||
$page = new View(__DIR__.'/../../views/console/users/view.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - View User')
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
<?php
|
||||
|
||||
include_once 'shared/web.php';
|
||||
include_once __DIR__ . '/../shared/web.php';
|
||||
|
||||
global $utopia, $response, $request, $layout, $version, $providers, $sdks, $platforms;
|
||||
global $utopia, $response, $request, $layout, $version, $providers, $platforms;
|
||||
|
||||
use Utopia\View;
|
||||
|
||||
$header = new View(__DIR__.'/../views/home/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../views/home/comps/footer.phtml');
|
||||
$header = new View(__DIR__.'/../../views/home/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/home/comps/footer.phtml');
|
||||
|
||||
$footer
|
||||
->setParam('version', $version)
|
||||
|
|
@ -40,7 +40,7 @@ $utopia->get('/auth/signin')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/home/auth/signin.phtml');
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signin.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Sign In - '.APP_NAME)
|
||||
|
|
@ -52,7 +52,7 @@ $utopia->get('/auth/signup')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/home/auth/signup.phtml');
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signup.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Sign Up - '.APP_NAME)
|
||||
|
|
@ -64,7 +64,7 @@ $utopia->get('/auth/recovery')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->action(function () use ($request, $layout) {
|
||||
$page = new View(__DIR__.'/../views/home/auth/recovery.phtml');
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Password Recovery - '.APP_NAME)
|
||||
|
|
@ -76,7 +76,7 @@ $utopia->get('/auth/confirm')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/home/auth/confirm.phtml');
|
||||
$page = new View(__DIR__.'/../../views/home/auth/confirm.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Account Confirmation - '.APP_NAME)
|
||||
|
|
@ -88,7 +88,7 @@ $utopia->get('/auth/join')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/home/auth/join.phtml');
|
||||
$page = new View(__DIR__.'/../../views/home/auth/join.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Invitation - '.APP_NAME)
|
||||
|
|
@ -100,7 +100,7 @@ $utopia->get('/auth/recovery/reset')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->action(function () use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/home/auth/recovery/reset.phtml');
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery/reset.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Password Reset - '.APP_NAME)
|
||||
|
|
@ -113,7 +113,7 @@ $utopia->get('/error/:code')
|
|||
->label('scope', 'home')
|
||||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->action(function ($code) use ($layout) {
|
||||
$page = new View(__DIR__.'/../views/error.phtml');
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
|
||||
$page
|
||||
->setParam('code', $code)
|
||||
49
app/init.php
49
app/init.php
|
|
@ -25,7 +25,7 @@ const APP_EMAIL_SECURITY = 'security@'.APP_DOMAIN;
|
|||
const APP_USERAGENT = APP_NAME.'-Server/%s Please report abuse at '.APP_EMAIL_SECURITY;
|
||||
const APP_MODE_ADMIN = 'admin';
|
||||
const APP_PAGING_LIMIT = 15;
|
||||
const APP_VERSION_STABLE = '0.4.0';
|
||||
const APP_VERSION_STABLE = '0.5.0';
|
||||
|
||||
$register = new Registry();
|
||||
$request = new Request();
|
||||
|
|
@ -37,10 +37,10 @@ $response = new Response();
|
|||
$env = $request->getServer('_APP_ENV', App::ENV_TYPE_PRODUCTION);
|
||||
$domain = $request->getServer('HTTP_HOST', '');
|
||||
$version = $request->getServer('_APP_VERSION', 'UNKNOWN');
|
||||
$providers = include __DIR__.'/../app/config/providers.php'; // OAuth providers list
|
||||
$providers = include __DIR__.'/../app/config/providers.php'; // OAuth2 providers list
|
||||
$platforms = include __DIR__.'/../app/config/platforms.php';
|
||||
$locales = include __DIR__.'/../app/config/locales.php'; // OAuth providers list
|
||||
$collections = include __DIR__.'/../app/config/collections.php'; // OAuth providers list
|
||||
$locales = include __DIR__.'/../app/config/locales.php'; // Locales list
|
||||
$collections = include __DIR__.'/../app/config/collections.php'; // Collections list
|
||||
$redisHost = $request->getServer('_APP_REDIS_HOST', '');
|
||||
$redisPort = $request->getServer('_APP_REDIS_PORT', '');
|
||||
$utopia = new App('Asia/Tel_Aviv', $env);
|
||||
|
|
@ -57,7 +57,7 @@ define('COOKIE_DOMAIN',
|
|||
)
|
||||
? null
|
||||
: '.'.parse_url($scheme.'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_HOST));
|
||||
define('COOKIE_SAMESITE', null); // Response::COOKIE_SAMESITE_NONE
|
||||
define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
/*
|
||||
* Registry
|
||||
|
|
@ -112,8 +112,8 @@ $register->set('smtp', function () use ($request) {
|
|||
|
||||
$mail->isSMTP();
|
||||
|
||||
$username = $request->getServer('_APP_SMTP_USERNAME', '');
|
||||
$password = $request->getServer('_APP_SMTP_PASSWORD', '');
|
||||
$username = $request->getServer('_APP_SMTP_USERNAME', null);
|
||||
$password = $request->getServer('_APP_SMTP_PASSWORD', null);
|
||||
|
||||
$mail->XMailer = 'Appwrite Mailer';
|
||||
$mail->Host = $request->getServer('_APP_SMTP_HOST', 'smtp');
|
||||
|
|
@ -121,10 +121,11 @@ $register->set('smtp', function () use ($request) {
|
|||
$mail->SMTPAuth = (!empty($username) && !empty($password));
|
||||
$mail->Username = $username;
|
||||
$mail->Password = $password;
|
||||
$mail->SMTPSecure = $request->getServer('_APP_SMTP_SECURE', '');
|
||||
$mail->SMTPSecure = $request->getServer('_APP_SMTP_SECURE', false);
|
||||
$mail->SMTPAutoTLS = false;
|
||||
|
||||
$from = $request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Team');
|
||||
$email = $request->getServer('_APP_SYSTEM_EMAIL_ADDRESS', 'team@appwrite.io');
|
||||
$from = urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'));
|
||||
$email = $request->getServer('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
|
||||
$mail->setFrom($email, $from);
|
||||
$mail->addReplyTo($email, $from);
|
||||
|
|
@ -219,19 +220,22 @@ $console = $consoleDB->getDocument('console');
|
|||
|
||||
$mode = $request->getParam('mode', $request->getHeader('X-Appwrite-Mode', 'default'));
|
||||
|
||||
Auth::setCookieName('a_session_'.$project->getUid());
|
||||
Auth::setCookieName('a_session_'.$project->getId());
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
Auth::setCookieName('a_session_'.$console->getUid());
|
||||
Auth::setCookieName('a_session_'.$console->getId());
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession($request->getCookie(Auth::$cookieName, $request->getHeader('X-Appwrite-Key', '')));
|
||||
$session = Auth::decodeSession(
|
||||
$request->getCookie(Auth::$cookieName, // Get sessions
|
||||
$request->getCookie(Auth::$cookieName.'_legacy', // Get fallback session from old clients (no SameSite support)
|
||||
$request->getHeader('X-Appwrite-Key', '')))); // Get API Key
|
||||
Auth::$unique = $session['id'];
|
||||
Auth::$secret = $session['secret'];
|
||||
|
||||
$projectDB = new Database();
|
||||
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
|
||||
$projectDB->setNamespace('app_'.$project->getUid());
|
||||
$projectDB->setNamespace('app_'.$project->getId());
|
||||
$projectDB->setMocks($collections);
|
||||
|
||||
$user = $projectDB->getDocument(Auth::$unique);
|
||||
|
|
@ -240,23 +244,30 @@ if (APP_MODE_ADMIN === $mode) {
|
|||
$user = $consoleDB->getDocument(Auth::$unique);
|
||||
|
||||
$user
|
||||
->setAttribute('$uid', 'admin-'.$user->getAttribute('$uid'))
|
||||
->setAttribute('$id', 'admin-'.$user->getAttribute('$id'))
|
||||
;
|
||||
}
|
||||
|
||||
if (empty($user->getUid()) // Check a document has been found in the DB
|
||||
if (empty($user->getId()) // Check a document has been found in the DB
|
||||
|| Database::SYSTEM_COLLECTION_USERS !== $user->getCollection() // Validate returned document is really a user document
|
||||
|| !Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)) { // Validate user has valid login token
|
||||
$user = new Document(['$uid' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]);
|
||||
$user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]);
|
||||
}
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
if (!empty($user->search('teamId', $project->getAttribute('teamId'), $user->getAttribute('memberships')))) {
|
||||
Authorization::disable();
|
||||
} else {
|
||||
$user = new Document(['$uid' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]);
|
||||
$user = new Document(['$id' => '', '$collection' => Database::SYSTEM_COLLECTION_USERS]);
|
||||
}
|
||||
}
|
||||
|
||||
// Set project mail
|
||||
$register->get('smtp')->setFrom(APP_EMAIL_TEAM, sprintf(Locale::getText('auth.emails.team'), $project->getAttribute('name')));
|
||||
$register->get('smtp')
|
||||
->setFrom(
|
||||
$request->getServer('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM),
|
||||
($project->getId() === 'console')
|
||||
? urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'))
|
||||
: sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name')
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
# Generated by pub on 2019-12-05 18:55:11.878585.
|
||||
charcode:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib/
|
||||
collection:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/collection-1.14.12/lib/
|
||||
cookie_jar:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/cookie_jar-1.0.1/lib/
|
||||
dio:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/dio-3.0.3/lib/
|
||||
dio_cookie_manager:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/dio_cookie_manager-1.0.0/lib/
|
||||
http_parser:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib/
|
||||
meta:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/meta-1.1.7/lib/
|
||||
path:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/path-1.6.4/lib/
|
||||
source_span:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/source_span-1.5.5/lib/
|
||||
string_scanner:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.5/lib/
|
||||
term_glyph:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.1.0/lib/
|
||||
typed_data:file:///Users/eldadfux/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib/
|
||||
appwrite:lib/
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# Appwrite SDK for Dart
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
**WORK IN PROGRESS - NOT READY FOR USAGE - Want to help us improve this client SDK? Send a pull request to Appwrite [SDK generator repository](https://github.com/appwrite/sdk-generator).**
|
||||
**This SDK is compatible with Appwrite server version . For older versions, please check previous releases.**
|
||||
|
||||
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
export 'package:appwrite/services/account.dart';
|
||||
export 'package:appwrite/services/auth.dart';
|
||||
export 'package:appwrite/services/avatars.dart';
|
||||
export 'package:appwrite/services/database.dart';
|
||||
export 'package:appwrite/services/locale.dart';
|
||||
export 'package:appwrite/services/projects.dart';
|
||||
export 'package:appwrite/services/storage.dart';
|
||||
export 'package:appwrite/services/teams.dart';
|
||||
export 'package:appwrite/services/users.dart';
|
||||
export 'package:appwrite/client.dart';
|
||||
export 'package:dio/dio.dart' show Response;
|
||||
|
|
@ -23,7 +23,7 @@ class Client {
|
|||
}
|
||||
|
||||
|
||||
/// Your Appwrite project ID
|
||||
/// Your project ID
|
||||
Client setProject(value) {
|
||||
this.addHeader('X-Appwrite-Project', value);
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ class Client {
|
|||
}
|
||||
|
||||
|
||||
/// Your Appwrite project secret key
|
||||
/// Your secret API key
|
||||
Client setKey(value) {
|
||||
this.addHeader('X-Appwrite-Key', value);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,23 @@ class Account extends Service {
|
|||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to allow a new user to register a new account in your
|
||||
/// project. After the user registration completes successfully, you can use
|
||||
/// the [/account/verfication](/docs/account#createVerification) route to start
|
||||
/// verifying the user email address. To allow your new user to login to his
|
||||
/// new account, you need to create a new [account
|
||||
/// session](/docs/account#createSession).
|
||||
Future<Response> create({email, password, name = null}) async {
|
||||
String path = '/account';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'password': password,
|
||||
'name': name,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Delete a currently logged in user account. Behind the scene, the user
|
||||
/// record is not deleted but permanently blocked from any access. This is done
|
||||
|
|
@ -41,6 +58,16 @@ class Account extends Service {
|
|||
};
|
||||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
}
|
||||
/// Get currently logged in user list of latest security activity logs. Each
|
||||
/// log returns user IP address, location and date and time of log.
|
||||
Future<Response> getLogs() async {
|
||||
String path = '/account/logs';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Update currently logged in user account name.
|
||||
Future<Response> updateName({name}) async {
|
||||
|
|
@ -64,7 +91,7 @@ class Account extends Service {
|
|||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
}
|
||||
/// Get currently logged in user preferences key-value object.
|
||||
/// Get currently logged in user preferences as a key-value object.
|
||||
Future<Response> getPrefs() async {
|
||||
String path = '/account/prefs';
|
||||
|
||||
|
|
@ -84,15 +111,42 @@ class Account extends Service {
|
|||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
}
|
||||
/// Get currently logged in user list of latest security activity logs. Each
|
||||
/// log returns user IP address, location and date and time of log.
|
||||
Future<Response> getSecurity() async {
|
||||
String path = '/account/security';
|
||||
/// Sends the user an email with a temporary secret key for password reset.
|
||||
/// When the user clicks the confirmation link he is redirected back to your
|
||||
/// app password reset URL with the secret key and email address values
|
||||
/// attached to the URL query string. Use the query string params to submit a
|
||||
/// request to the [PUT /account/recovery](/docs/account#updateRecovery)
|
||||
/// endpoint to complete the process.
|
||||
Future<Response> createRecovery({email, url}) async {
|
||||
String path = '/account/recovery';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'url': url,
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to complete the user account password reset. Both the
|
||||
/// **userId** and **secret** arguments will be passed as query parameters to
|
||||
/// the redirect URL you have provided when sending your request to the [POST
|
||||
/// /account/recovery](/docs/account#createRecovery) endpoint.
|
||||
///
|
||||
/// Please note that in order to avoid a [Redirect
|
||||
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URLs are the ones from domains you have set when
|
||||
/// adding your platforms in the console interface.
|
||||
Future<Response> updateRecovery({userId, secret, passwordA, passwordB}) async {
|
||||
String path = '/account/recovery';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'userId': userId,
|
||||
'secret': secret,
|
||||
'password-a': passwordA,
|
||||
'password-b': passwordB,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
/// Get currently logged in user list of active sessions across different
|
||||
/// devices.
|
||||
|
|
@ -104,4 +158,87 @@ class Account extends Service {
|
|||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Allow the user to login into his account by providing a valid email and
|
||||
/// password combination. This route will create a new session for the user.
|
||||
Future<Response> createSession({email, password}) async {
|
||||
String path = '/account/sessions';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'password': password,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Delete all sessions from the user account and remove any sessions cookies
|
||||
/// from the end client.
|
||||
Future<Response> deleteSessions() async {
|
||||
String path = '/account/sessions';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Allow the user to login to his account using the OAuth provider of his
|
||||
/// choice. Each OAuth provider should be enabled from the Appwrite console
|
||||
/// first. Use the success and failure arguments to provide a redirect URL's
|
||||
/// back to your app when login is completed.
|
||||
Future<Response> createOAuth2Session({provider, success, failure}) async {
|
||||
String path = '/account/sessions/oauth2/{provider}'.replaceAll(RegExp('{provider}'), provider);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'success': success,
|
||||
'failure': failure,
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to log out the currently logged in user from all his
|
||||
/// account sessions across all his different devices. When using the option id
|
||||
/// argument, only the session unique ID provider will be deleted.
|
||||
Future<Response> deleteSession({sessionId}) async {
|
||||
String path = '/account/sessions/{sessionId}'.replaceAll(RegExp('{sessionId}'), sessionId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to send a verification message to your user email address
|
||||
/// to confirm they are the valid owners of that address. Both the **userId**
|
||||
/// and **secret** arguments will be passed as query parameters to the URL you
|
||||
/// have provider to be attached to the verification email. The provided URL
|
||||
/// should redirect the user back for your app and allow you to complete the
|
||||
/// verification process by verifying both the **userId** and **secret**
|
||||
/// parameters. Learn more about how to [complete the verification
|
||||
/// process](/docs/account#updateAccountVerification).
|
||||
///
|
||||
/// Please note that in order to avoid a [Redirect
|
||||
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URLs are the ones from domains you have set when
|
||||
/// adding your platforms in the console interface.
|
||||
Future<Response> createVerification({url}) async {
|
||||
String path = '/account/verification';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'url': url,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to complete the user email verification process. Use both
|
||||
/// the **userId** and **secret** parameters that were attached to your app URL
|
||||
/// to verify the user email ownership. If confirmed this route will return a
|
||||
/// 200 status code.
|
||||
Future<Response> updateVerification({userId, secret}) async {
|
||||
String path = '/account/verification';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'userId': userId,
|
||||
'secret': secret,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
import "package:appwrite/service.dart";
|
||||
import "package:appwrite/client.dart";
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
class Auth extends Service {
|
||||
|
||||
Auth(Client client): super(client);
|
||||
|
||||
/// Allow the user to login into his account by providing a valid email and
|
||||
/// password combination. Use the success and failure arguments to provide a
|
||||
/// redirect URL\'s back to your app when login is completed.
|
||||
///
|
||||
/// Please notice that in order to avoid a [Redirect
|
||||
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URLs are the ones from domains you have set when
|
||||
/// adding your platforms in the console interface.
|
||||
///
|
||||
/// When accessing this route using Javascript from the browser, success and
|
||||
/// failure parameter URLs are required. Appwrite server will respond with a
|
||||
/// 301 redirect status code and will set the user session cookie. This
|
||||
/// behavior is enforced because modern browsers are limiting 3rd party cookies
|
||||
/// in XHR of fetch requests to protect user privacy.
|
||||
Future<Response> login({email, password, success = null, failure = null}) async {
|
||||
String path = '/auth/login';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'password': password,
|
||||
'success': success,
|
||||
'failure': failure,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Allow the user to login to his account using the OAuth provider of his
|
||||
/// choice. Each OAuth provider should be enabled from the Appwrite console
|
||||
/// first. Use the success and failure arguments to provide a redirect URL's
|
||||
/// back to your app when login is completed.
|
||||
Future<Response> oauth({provider, success, failure}) async {
|
||||
String path = '/auth/login/oauth/{provider}'.replaceAll(RegExp('{provider}'), provider);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'success': success,
|
||||
'failure': failure,
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to log out the currently logged in user from his account.
|
||||
/// When successful this endpoint will delete the user session and remove the
|
||||
/// session secret cookie from the user client.
|
||||
Future<Response> logout() async {
|
||||
String path = '/auth/logout';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to log out the currently logged in user from all his
|
||||
/// account sessions across all his different devices. When using the option id
|
||||
/// argument, only the session unique ID provider will be deleted.
|
||||
Future<Response> logoutBySession({id}) async {
|
||||
String path = '/auth/logout/{id}'.replaceAll(RegExp('{id}'), id);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Sends the user an email with a temporary secret token for password reset.
|
||||
/// When the user clicks the confirmation link he is redirected back to your
|
||||
/// app password reset redirect URL with a secret token and email address
|
||||
/// values attached to the URL query string. Use the query string params to
|
||||
/// submit a request to the /auth/password/reset endpoint to complete the
|
||||
/// process.
|
||||
Future<Response> recovery({email, reset}) async {
|
||||
String path = '/auth/recovery';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'reset': reset,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to complete the user account password reset. Both the
|
||||
/// **userId** and **token** arguments will be passed as query parameters to
|
||||
/// the redirect URL you have provided when sending your request to the
|
||||
/// /auth/recovery endpoint.
|
||||
///
|
||||
/// Please notice that in order to avoid a [Redirect
|
||||
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URLs are the ones from domains you have set when
|
||||
/// adding your platforms in the console interface.
|
||||
Future<Response> recoveryReset({userId, token, passwordA, passwordB}) async {
|
||||
String path = '/auth/recovery/reset';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'userId': userId,
|
||||
'token': token,
|
||||
'password-a': passwordA,
|
||||
'password-b': passwordB,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to allow a new user to register an account in your
|
||||
/// project. Use the success and failure URLs to redirect users back to your
|
||||
/// application after signup completes.
|
||||
///
|
||||
/// If registration completes successfully user will be sent with a
|
||||
/// confirmation email in order to confirm he is the owner of the account email
|
||||
/// address. Use the confirmation parameter to redirect the user from the
|
||||
/// confirmation email back to your app. When the user is redirected, use the
|
||||
/// /auth/confirm endpoint to complete the account confirmation.
|
||||
///
|
||||
/// Please notice that in order to avoid a [Redirect
|
||||
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URLs are the ones from domains you have set when
|
||||
/// adding your platforms in the console interface.
|
||||
///
|
||||
/// When accessing this route using Javascript from the browser, success and
|
||||
/// failure parameter URLs are required. Appwrite server will respond with a
|
||||
/// 301 redirect status code and will set the user session cookie. This
|
||||
/// behavior is enforced because modern browsers are limiting 3rd party cookies
|
||||
/// in XHR of fetch requests to protect user privacy.
|
||||
Future<Response> register({email, password, confirm, success = null, failure = null, name = null}) async {
|
||||
String path = '/auth/register';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'password': password,
|
||||
'confirm': confirm,
|
||||
'success': success,
|
||||
'failure': failure,
|
||||
'name': name,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to complete the confirmation of the user account email
|
||||
/// address. Both the **userId** and **token** arguments will be passed as
|
||||
/// query parameters to the redirect URL you have provided when sending your
|
||||
/// request to the /auth/register endpoint.
|
||||
Future<Response> confirm({userId, token}) async {
|
||||
String path = '/auth/register/confirm';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'userId': userId,
|
||||
'token': token,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// This endpoint allows the user to request your app to resend him his email
|
||||
/// confirmation message. The redirect arguments act the same way as in
|
||||
/// /auth/register endpoint.
|
||||
///
|
||||
/// Please notice that in order to avoid a [Redirect
|
||||
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URLs are the ones from domains you have set when
|
||||
/// adding your platforms in the console interface.
|
||||
Future<Response> confirmResend({confirm}) async {
|
||||
String path = '/auth/register/confirm/resend';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'confirm': confirm,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,74 +6,12 @@ class Database extends Service {
|
|||
|
||||
Database(Client client): super(client);
|
||||
|
||||
/// Get a list of all the user collections. You can use the query params to
|
||||
/// filter your results. On admin mode, this endpoint will return a list of all
|
||||
/// of the project collections. [Learn more about different API
|
||||
/// modes](/docs/admin).
|
||||
Future<Response> listCollections({search = null, limit = 25, offset = null, orderType = 'ASC'}) async {
|
||||
String path = '/database';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'search': search,
|
||||
'limit': limit,
|
||||
'offset': offset,
|
||||
'orderType': orderType,
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Create a new Collection.
|
||||
Future<Response> createCollection({name, read, write, rules}) async {
|
||||
String path = '/database';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'read': read,
|
||||
'write': write,
|
||||
'rules': rules,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Get collection by its unique ID. This endpoint response returns a JSON
|
||||
/// object with the collection metadata.
|
||||
Future<Response> getCollection({collectionId}) async {
|
||||
String path = '/database/{collectionId}'.replaceAll(RegExp('{collectionId}'), collectionId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Update collection by its unique ID.
|
||||
Future<Response> updateCollection({collectionId, name, read, write, rules = const []}) async {
|
||||
String path = '/database/{collectionId}'.replaceAll(RegExp('{collectionId}'), collectionId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'read': read,
|
||||
'write': write,
|
||||
'rules': rules,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
/// Delete a collection by its unique ID. Only users with write permissions
|
||||
/// have access to delete this resource.
|
||||
Future<Response> deleteCollection({collectionId}) async {
|
||||
String path = '/database/{collectionId}'.replaceAll(RegExp('{collectionId}'), collectionId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Get a list of all the user documents. You can use the query params to
|
||||
/// filter your results. On admin mode, this endpoint will return a list of all
|
||||
/// of the project documents. [Learn more about different API
|
||||
/// modes](/docs/admin).
|
||||
Future<Response> listDocuments({collectionId, filters = const [], offset = null, limit = 50, orderField = '\$uid', orderType = 'ASC', orderCast = 'string', search = null, first = null, last = null}) async {
|
||||
String path = '/database/{collectionId}/documents'.replaceAll(RegExp('{collectionId}'), collectionId);
|
||||
String path = '/database/collections/{collectionId}/documents'.replaceAll(RegExp('{collectionId}'), collectionId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'filters': filters,
|
||||
|
|
@ -91,7 +29,7 @@ class Database extends Service {
|
|||
}
|
||||
/// Create a new Document.
|
||||
Future<Response> createDocument({collectionId, data, read, write, parentDocument = null, parentProperty = null, parentPropertyType = 'assign'}) async {
|
||||
String path = '/database/{collectionId}/documents'.replaceAll(RegExp('{collectionId}'), collectionId);
|
||||
String path = '/database/collections/{collectionId}/documents'.replaceAll(RegExp('{collectionId}'), collectionId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'data': data,
|
||||
|
|
@ -107,7 +45,7 @@ class Database extends Service {
|
|||
/// Get document by its unique ID. This endpoint response returns a JSON object
|
||||
/// with the document data.
|
||||
Future<Response> getDocument({collectionId, documentId}) async {
|
||||
String path = '/database/{collectionId}/documents/{documentId}'.replaceAll(RegExp('{collectionId}'), collectionId).replaceAll(RegExp('{documentId}'), documentId);
|
||||
String path = '/database/collections/{collectionId}/documents/{documentId}'.replaceAll(RegExp('{collectionId}'), collectionId).replaceAll(RegExp('{documentId}'), documentId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
|
@ -115,7 +53,7 @@ class Database extends Service {
|
|||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> updateDocument({collectionId, documentId, data, read, write}) async {
|
||||
String path = '/database/{collectionId}/documents/{documentId}'.replaceAll(RegExp('{collectionId}'), collectionId).replaceAll(RegExp('{documentId}'), documentId);
|
||||
String path = '/database/collections/{collectionId}/documents/{documentId}'.replaceAll(RegExp('{collectionId}'), collectionId).replaceAll(RegExp('{documentId}'), documentId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'data': data,
|
||||
|
|
@ -129,7 +67,7 @@ class Database extends Service {
|
|||
/// documents, his attributes and relations to other documents. Child documents
|
||||
/// **will not** be deleted.
|
||||
Future<Response> deleteDocument({collectionId, documentId}) async {
|
||||
String path = '/database/{collectionId}/documents/{documentId}'.replaceAll(RegExp('{collectionId}'), collectionId).replaceAll(RegExp('{documentId}'), documentId);
|
||||
String path = '/database/collections/{collectionId}/documents/{documentId}'.replaceAll(RegExp('{collectionId}'), collectionId).replaceAll(RegExp('{documentId}'), documentId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ class Locale extends Service {
|
|||
/// country code, country name, continent name, continent code, ip address and
|
||||
/// suggested currency. You can use the locale header to get the data in a
|
||||
/// supported language.
|
||||
Future<Response> getLocale() async {
|
||||
///
|
||||
/// ([IP Geolocation by DB-IP](https://db-ip.com))
|
||||
Future<Response> get() async {
|
||||
String path = '/locale';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
|
|
@ -39,8 +41,7 @@ class Locale extends Service {
|
|||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// List of all countries that are currently members of the EU. You can use the
|
||||
/// locale header to get the data in a supported language. UK brexit date is
|
||||
/// currently set to 2019-10-31 and will be updated if and when needed.
|
||||
/// locale header to get the data in a supported language.
|
||||
Future<Response> getCountriesEU() async {
|
||||
String path = '/locale/countries/eu';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,292 +0,0 @@
|
|||
import "package:appwrite/service.dart";
|
||||
import "package:appwrite/client.dart";
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
class Projects extends Service {
|
||||
|
||||
Projects(Client client): super(client);
|
||||
|
||||
Future<Response> listProjects() async {
|
||||
String path = '/projects';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> createProject({name, teamId, description = null, logo = null, url = null, legalName = null, legalCountry = null, legalState = null, legalCity = null, legalAddress = null, legalTaxId = null}) async {
|
||||
String path = '/projects';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'teamId': teamId,
|
||||
'description': description,
|
||||
'logo': logo,
|
||||
'url': url,
|
||||
'legalName': legalName,
|
||||
'legalCountry': legalCountry,
|
||||
'legalState': legalState,
|
||||
'legalCity': legalCity,
|
||||
'legalAddress': legalAddress,
|
||||
'legalTaxId': legalTaxId,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
Future<Response> getProject({projectId}) async {
|
||||
String path = '/projects/{projectId}'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> updateProject({projectId, name, description = null, logo = null, url = null, legalName = null, legalCountry = null, legalState = null, legalCity = null, legalAddress = null, legalTaxId = null}) async {
|
||||
String path = '/projects/{projectId}'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'description': description,
|
||||
'logo': logo,
|
||||
'url': url,
|
||||
'legalName': legalName,
|
||||
'legalCountry': legalCountry,
|
||||
'legalState': legalState,
|
||||
'legalCity': legalCity,
|
||||
'legalAddress': legalAddress,
|
||||
'legalTaxId': legalTaxId,
|
||||
};
|
||||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
}
|
||||
Future<Response> deleteProject({projectId}) async {
|
||||
String path = '/projects/{projectId}'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
Future<Response> listKeys({projectId}) async {
|
||||
String path = '/projects/{projectId}/keys'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> createKey({projectId, name, scopes}) async {
|
||||
String path = '/projects/{projectId}/keys'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'scopes': scopes,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
Future<Response> getKey({projectId, keyId}) async {
|
||||
String path = '/projects/{projectId}/keys/{keyId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{keyId}'), keyId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> updateKey({projectId, keyId, name, scopes}) async {
|
||||
String path = '/projects/{projectId}/keys/{keyId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{keyId}'), keyId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'scopes': scopes,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
Future<Response> deleteKey({projectId, keyId}) async {
|
||||
String path = '/projects/{projectId}/keys/{keyId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{keyId}'), keyId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
Future<Response> updateProjectOAuth({projectId, provider, appId = null, secret = null}) async {
|
||||
String path = '/projects/{projectId}/oauth'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'provider': provider,
|
||||
'appId': appId,
|
||||
'secret': secret,
|
||||
};
|
||||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
}
|
||||
Future<Response> listPlatforms({projectId}) async {
|
||||
String path = '/projects/{projectId}/platforms'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> createPlatform({projectId, type, name, key = null, store = null, url = null}) async {
|
||||
String path = '/projects/{projectId}/platforms'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'type': type,
|
||||
'name': name,
|
||||
'key': key,
|
||||
'store': store,
|
||||
'url': url,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
Future<Response> getPlatform({projectId, platformId}) async {
|
||||
String path = '/projects/{projectId}/platforms/{platformId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{platformId}'), platformId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> updatePlatform({projectId, platformId, name, key = null, store = null, url = null}) async {
|
||||
String path = '/projects/{projectId}/platforms/{platformId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{platformId}'), platformId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'key': key,
|
||||
'store': store,
|
||||
'url': url,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
Future<Response> deletePlatform({projectId, platformId}) async {
|
||||
String path = '/projects/{projectId}/platforms/{platformId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{platformId}'), platformId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
Future<Response> listTasks({projectId}) async {
|
||||
String path = '/projects/{projectId}/tasks'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> createTask({projectId, name, status, schedule, security, httpMethod, httpUrl, httpHeaders = null, httpUser = null, httpPass = null}) async {
|
||||
String path = '/projects/{projectId}/tasks'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'status': status,
|
||||
'schedule': schedule,
|
||||
'security': security,
|
||||
'httpMethod': httpMethod,
|
||||
'httpUrl': httpUrl,
|
||||
'httpHeaders': httpHeaders,
|
||||
'httpUser': httpUser,
|
||||
'httpPass': httpPass,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
Future<Response> getTask({projectId, taskId}) async {
|
||||
String path = '/projects/{projectId}/tasks/{taskId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{taskId}'), taskId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> updateTask({projectId, taskId, name, status, schedule, security, httpMethod, httpUrl, httpHeaders = null, httpUser = null, httpPass = null}) async {
|
||||
String path = '/projects/{projectId}/tasks/{taskId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{taskId}'), taskId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'status': status,
|
||||
'schedule': schedule,
|
||||
'security': security,
|
||||
'httpMethod': httpMethod,
|
||||
'httpUrl': httpUrl,
|
||||
'httpHeaders': httpHeaders,
|
||||
'httpUser': httpUser,
|
||||
'httpPass': httpPass,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
Future<Response> deleteTask({projectId, taskId}) async {
|
||||
String path = '/projects/{projectId}/tasks/{taskId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{taskId}'), taskId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
Future<Response> getProjectUsage({projectId}) async {
|
||||
String path = '/projects/{projectId}/usage'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> listWebhooks({projectId}) async {
|
||||
String path = '/projects/{projectId}/webhooks'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> createWebhook({projectId, name, events, url, security, httpUser = null, httpPass = null}) async {
|
||||
String path = '/projects/{projectId}/webhooks'.replaceAll(RegExp('{projectId}'), projectId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'events': events,
|
||||
'url': url,
|
||||
'security': security,
|
||||
'httpUser': httpUser,
|
||||
'httpPass': httpPass,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
Future<Response> getWebhook({projectId, webhookId}) async {
|
||||
String path = '/projects/{projectId}/webhooks/{webhookId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{webhookId}'), webhookId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
Future<Response> updateWebhook({projectId, webhookId, name, events, url, security, httpUser = null, httpPass = null}) async {
|
||||
String path = '/projects/{projectId}/webhooks/{webhookId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{webhookId}'), webhookId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'name': name,
|
||||
'events': events,
|
||||
'url': url,
|
||||
'security': security,
|
||||
'httpUser': httpUser,
|
||||
'httpPass': httpPass,
|
||||
};
|
||||
|
||||
return await this.client.call('put', path: path, params: params);
|
||||
}
|
||||
Future<Response> deleteWebhook({projectId, webhookId}) async {
|
||||
String path = '/projects/{projectId}/webhooks/{webhookId}'.replaceAll(RegExp('{projectId}'), projectId).replaceAll(RegExp('{webhookId}'), webhookId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,11 +24,11 @@ class Storage extends Service {
|
|||
/// Create a new file. The user who creates the file will automatically be
|
||||
/// assigned to read and write access unless he has passed custom values for
|
||||
/// read and write arguments.
|
||||
Future<Response> createFile({files, read, write}) async {
|
||||
Future<Response> createFile({file, read, write}) async {
|
||||
String path = '/storage/files';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'files': files,
|
||||
'file': file,
|
||||
'read': read,
|
||||
'write': write,
|
||||
};
|
||||
|
|
@ -78,9 +78,9 @@ class Storage extends Service {
|
|||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Get file preview image. Currently, this method supports preview for image
|
||||
/// Get a file preview image. Currently, this method supports preview for image
|
||||
/// files (jpg, png, and gif), other supported formats, like pdf, docs, slides,
|
||||
/// and spreadsheets will return file icon image. You can also pass query
|
||||
/// and spreadsheets, will return the file icon image. You can also pass query
|
||||
/// string arguments for cutting and resizing your preview image.
|
||||
Future<Response> getFilePreview({fileId, width = null, height = null, quality = 100, background = null, output = null}) async {
|
||||
String path = '/storage/files/{fileId}/preview'.replaceAll(RegExp('{fileId}'), fileId);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class Teams extends Service {
|
|||
/// Get a list of all the current user teams. You can use the query params to
|
||||
/// filter your results. On admin mode, this endpoint will return a list of all
|
||||
/// of the project teams. [Learn more about different API modes](/docs/admin).
|
||||
Future<Response> listTeams({search = null, limit = 25, offset = null, orderType = 'ASC'}) async {
|
||||
Future<Response> list({search = null, limit = 25, offset = null, orderType = 'ASC'}) async {
|
||||
String path = '/teams';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
|
|
@ -25,7 +25,7 @@ class Teams extends Service {
|
|||
/// assigned as the owner of the team. The team owner can invite new members,
|
||||
/// who will be able add new owners and update or delete the team from your
|
||||
/// project.
|
||||
Future<Response> createTeam({name, roles = const ["owner"]}) async {
|
||||
Future<Response> create({name, roles = const ["owner"]}) async {
|
||||
String path = '/teams';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
|
|
@ -37,7 +37,7 @@ class Teams extends Service {
|
|||
}
|
||||
/// Get team by its unique ID. All team members have read access for this
|
||||
/// resource.
|
||||
Future<Response> getTeam({teamId}) async {
|
||||
Future<Response> get({teamId}) async {
|
||||
String path = '/teams/{teamId}'.replaceAll(RegExp('{teamId}'), teamId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
|
|
@ -47,7 +47,7 @@ class Teams extends Service {
|
|||
}
|
||||
/// Update team by its unique ID. Only team owners have write access for this
|
||||
/// resource.
|
||||
Future<Response> updateTeam({teamId, name}) async {
|
||||
Future<Response> update({teamId, name}) async {
|
||||
String path = '/teams/{teamId}'.replaceAll(RegExp('{teamId}'), teamId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
|
|
@ -58,7 +58,7 @@ class Teams extends Service {
|
|||
}
|
||||
/// Delete team by its unique ID. Only team owners have write access for this
|
||||
/// resource.
|
||||
Future<Response> deleteTeam({teamId}) async {
|
||||
Future<Response> delete({teamId}) async {
|
||||
String path = '/teams/{teamId}'.replaceAll(RegExp('{teamId}'), teamId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
|
|
@ -68,42 +68,43 @@ class Teams extends Service {
|
|||
}
|
||||
/// Get team members by the team unique ID. All team members have read access
|
||||
/// for this list of resources.
|
||||
Future<Response> getTeamMembers({teamId}) async {
|
||||
String path = '/teams/{teamId}/members'.replaceAll(RegExp('{teamId}'), teamId);
|
||||
Future<Response> getMemberships({teamId}) async {
|
||||
String path = '/teams/{teamId}/memberships'.replaceAll(RegExp('{teamId}'), teamId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to invite a new member to your team. An email with a link
|
||||
/// to join the team will be sent to the new member email address. If member
|
||||
/// doesn't exists in the project it will be automatically created.
|
||||
/// Use this endpoint to invite a new member to join your team. An email with a
|
||||
/// link to join the team will be sent to the new member email address if the
|
||||
/// member doesn't exist in the project it will be created automatically.
|
||||
///
|
||||
/// Use the redirect parameter to redirect the user from the invitation email
|
||||
/// back to your app. When the user is redirected, use the
|
||||
/// /teams/{teamId}/memberships/{inviteId}/status endpoint to finally join the
|
||||
/// user to the team.
|
||||
/// Use the 'URL' parameter to redirect the user from the invitation email back
|
||||
/// to your app. When the user is redirected, use the [Update Team Membership
|
||||
/// Status](/docs/teams#updateMembershipStatus) endpoint to allow the user to
|
||||
/// accept the invitation to the team.
|
||||
///
|
||||
/// Please notice that in order to avoid a [Redirect
|
||||
/// Please note that in order to avoid a [Redirect
|
||||
/// Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URL's are the once from domains you have set when
|
||||
/// added your platforms in the console interface.
|
||||
Future<Response> createTeamMembership({teamId, email, roles, redirect, name = null}) async {
|
||||
Future<Response> createMembership({teamId, email, roles, url, name = null}) async {
|
||||
String path = '/teams/{teamId}/memberships'.replaceAll(RegExp('{teamId}'), teamId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'name': name,
|
||||
'roles': roles,
|
||||
'redirect': redirect,
|
||||
'url': url,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// This endpoint allows a user to leave a team or for a team owner to delete
|
||||
/// the membership of any other team member.
|
||||
Future<Response> deleteTeamMembership({teamId, inviteId}) async {
|
||||
/// the membership of any other team member. You can also use this endpoint to
|
||||
/// delete a user membership even if he didn't accept it.
|
||||
Future<Response> deleteMembership({teamId, inviteId}) async {
|
||||
String path = '/teams/{teamId}/memberships/{inviteId}'.replaceAll(RegExp('{teamId}'), teamId).replaceAll(RegExp('{inviteId}'), inviteId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
|
|
@ -111,40 +112,15 @@ class Teams extends Service {
|
|||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to resend your invitation email for a user to join a
|
||||
/// team.
|
||||
Future<Response> createTeamMembershipResend({teamId, inviteId, redirect}) async {
|
||||
String path = '/teams/{teamId}/memberships/{inviteId}/resend'.replaceAll(RegExp('{teamId}'), teamId).replaceAll(RegExp('{inviteId}'), inviteId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'redirect': redirect,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Use this endpoint to let user accept an invitation to join a team after he
|
||||
/// is being redirect back to your app from the invitation email. Use the
|
||||
/// success and failure URL's to redirect users back to your application after
|
||||
/// the request completes.
|
||||
///
|
||||
/// Please notice that in order to avoid a [Redirect
|
||||
/// Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
/// the only valid redirect URL's are the once from domains you have set when
|
||||
/// added your platforms in the console interface.
|
||||
///
|
||||
/// When not using the success or failure redirect arguments this endpoint will
|
||||
/// result with a 200 status code on success and with 401 status error on
|
||||
/// failure. This behavior was applied to help the web clients deal with
|
||||
/// browsers who don't allow to set 3rd party HTTP cookies needed for saving
|
||||
/// the account session token.
|
||||
Future<Response> updateTeamMembershipStatus({teamId, inviteId, userId, secret, success = null, failure = null}) async {
|
||||
/// Use this endpoint to allow a user to accept an invitation to join a team
|
||||
/// after he is being redirected back to your app from the invitation email he
|
||||
/// was sent.
|
||||
Future<Response> updateMembershipStatus({teamId, inviteId, userId, secret}) async {
|
||||
String path = '/teams/{teamId}/memberships/{inviteId}/status'.replaceAll(RegExp('{teamId}'), teamId).replaceAll(RegExp('{inviteId}'), inviteId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'userId': userId,
|
||||
'secret': secret,
|
||||
'success': success,
|
||||
'failure': failure,
|
||||
};
|
||||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
|
|
|
|||
|
|
@ -1,111 +0,0 @@
|
|||
import "package:appwrite/service.dart";
|
||||
import "package:appwrite/client.dart";
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
class Users extends Service {
|
||||
|
||||
Users(Client client): super(client);
|
||||
|
||||
/// Get a list of all the project users. You can use the query params to filter
|
||||
/// your results.
|
||||
Future<Response> listUsers({search = null, limit = 25, offset = null, orderType = 'ASC'}) async {
|
||||
String path = '/users';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'search': search,
|
||||
'limit': limit,
|
||||
'offset': offset,
|
||||
'orderType': orderType,
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Create a new user.
|
||||
Future<Response> createUser({email, password, name = null}) async {
|
||||
String path = '/users';
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'email': email,
|
||||
'password': password,
|
||||
'name': name,
|
||||
};
|
||||
|
||||
return await this.client.call('post', path: path, params: params);
|
||||
}
|
||||
/// Get user by its unique ID.
|
||||
Future<Response> getUser({userId}) async {
|
||||
String path = '/users/{userId}'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Get user activity logs list by its unique ID.
|
||||
Future<Response> getUserLogs({userId}) async {
|
||||
String path = '/users/{userId}/logs'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Get user preferences by its unique ID.
|
||||
Future<Response> getUserPrefs({userId}) async {
|
||||
String path = '/users/{userId}/prefs'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Update user preferences by its unique ID. You can pass only the specific
|
||||
/// settings you wish to update.
|
||||
Future<Response> updateUserPrefs({userId, prefs}) async {
|
||||
String path = '/users/{userId}/prefs'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'prefs': prefs,
|
||||
};
|
||||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
}
|
||||
/// Get user sessions list by its unique ID.
|
||||
Future<Response> getUserSessions({userId}) async {
|
||||
String path = '/users/{userId}/sessions'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('get', path: path, params: params);
|
||||
}
|
||||
/// Delete all user sessions by its unique ID.
|
||||
Future<Response> deleteUserSessions({userId}) async {
|
||||
String path = '/users/{userId}/sessions'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Delete user sessions by its unique ID.
|
||||
Future<Response> deleteUserSession({userId, sessionId}) async {
|
||||
String path = '/users/{userId}/sessions/:session'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'sessionId': sessionId,
|
||||
};
|
||||
|
||||
return await this.client.call('delete', path: path, params: params);
|
||||
}
|
||||
/// Update user status by its unique ID.
|
||||
Future<Response> updateUserStatus({userId, status}) async {
|
||||
String path = '/users/{userId}/status'.replaceAll(RegExp('{userId}'), userId);
|
||||
|
||||
Map<String, dynamic> params = {
|
||||
'status': status,
|
||||
};
|
||||
|
||||
return await this.client.call('patch', path: path, params: params);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.14.12"
|
||||
cookie_jar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cookie_jar
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
dio_cookie_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio_cookie_manager
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.7"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.4"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.5"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.6"
|
||||
sdks:
|
||||
dart: ">2.4.0 <3.0.0"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# Appwrite SDK for Go
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
**WORK IN PROGRESS - NOT READY FOR USAGE - Want to help us improve this client SDK? Send a pull request to Appwrite [SDK generator repository](https://github.com/appwrite/sdk-generator).**
|
||||
**This SDK is compatible with Appwrite server version . For older versions, please check previous releases.**
|
||||
|
||||
Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
package appwrite
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
// Account service
|
||||
type Account struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Get get currently logged in user data as JSON object.
|
||||
func (srv *Account) Get() (map[string]interface{}, error) {
|
||||
path := "/account"
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
||||
return srv.client.Call("GET", path, nil, params)
|
||||
}
|
||||
|
||||
// Delete delete a currently logged in user account. Behind the scene, the
|
||||
// user record is not deleted but permanently blocked from any access. This is
|
||||
// done to avoid deleted accounts being overtaken by new users with the same
|
||||
// email address. Any user-related resources like documents or storage files
|
||||
// should be deleted separately.
|
||||
func (srv *Account) Delete() (map[string]interface{}, error) {
|
||||
path := "/account"
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
||||
return srv.client.Call("DELETE", path, nil, params)
|
||||
}
|
||||
|
||||
// UpdateEmail update currently logged in user account email address. After
|
||||
// changing user address, user confirmation status is being reset and a new
|
||||
// confirmation mail is sent. For security measures, user password is required
|
||||
// to complete this request.
|
||||
func (srv *Account) UpdateEmail(Email string, Password string) (map[string]interface{}, error) {
|
||||
path := "/account/email"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"email": Email,
|
||||
"password": Password,
|
||||
}
|
||||
|
||||
return srv.client.Call("PATCH", path, nil, params)
|
||||
}
|
||||
|
||||
// UpdateName update currently logged in user account name.
|
||||
func (srv *Account) UpdateName(Name string) (map[string]interface{}, error) {
|
||||
path := "/account/name"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"name": Name,
|
||||
}
|
||||
|
||||
return srv.client.Call("PATCH", path, nil, params)
|
||||
}
|
||||
|
||||
// UpdatePassword update currently logged in user password. For validation,
|
||||
// user is required to pass the password twice.
|
||||
func (srv *Account) UpdatePassword(Password string, OldPassword string) (map[string]interface{}, error) {
|
||||
path := "/account/password"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"password": Password,
|
||||
"old-password": OldPassword,
|
||||
}
|
||||
|
||||
return srv.client.Call("PATCH", path, nil, params)
|
||||
}
|
||||
|
||||
// GetPrefs get currently logged in user preferences key-value object.
|
||||
func (srv *Account) GetPrefs() (map[string]interface{}, error) {
|
||||
path := "/account/prefs"
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
||||
return srv.client.Call("GET", path, nil, params)
|
||||
}
|
||||
|
||||
// UpdatePrefs update currently logged in user account preferences. You can
|
||||
// pass only the specific settings you wish to update.
|
||||
func (srv *Account) UpdatePrefs(Prefs string) (map[string]interface{}, error) {
|
||||
path := "/account/prefs"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"prefs": Prefs,
|
||||
}
|
||||
|
||||
return srv.client.Call("PATCH", path, nil, params)
|
||||
}
|
||||
|
||||
// GetSecurity get currently logged in user list of latest security activity
|
||||
// logs. Each log returns user IP address, location and date and time of log.
|
||||
func (srv *Account) GetSecurity() (map[string]interface{}, error) {
|
||||
path := "/account/security"
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
||||
return srv.client.Call("GET", path, nil, params)
|
||||
}
|
||||
|
||||
// GetSessions get currently logged in user list of active sessions across
|
||||
// different devices.
|
||||
func (srv *Account) GetSessions() (map[string]interface{}, error) {
|
||||
path := "/account/sessions"
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
||||
return srv.client.Call("GET", path, nil, params)
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
package appwrite
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Auth service
|
||||
type Auth struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Login allow the user to login into his account by providing a valid email
|
||||
// and password combination. Use the success and failure arguments to provide
|
||||
// a redirect URL\'s back to your app when login is completed.
|
||||
//
|
||||
// Please notice that in order to avoid a [Redirect
|
||||
// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
// the only valid redirect URLs are the ones from domains you have set when
|
||||
// adding your platforms in the console interface.
|
||||
//
|
||||
// When accessing this route using Javascript from the browser, success and
|
||||
// failure parameter URLs are required. Appwrite server will respond with a
|
||||
// 301 redirect status code and will set the user session cookie. This
|
||||
// behavior is enforced because modern browsers are limiting 3rd party cookies
|
||||
// in XHR of fetch requests to protect user privacy.
|
||||
func (srv *Auth) Login(Email string, Password string, Success string, Failure string) (map[string]interface{}, error) {
|
||||
path := "/auth/login"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"email": Email,
|
||||
"password": Password,
|
||||
"success": Success,
|
||||
"failure": Failure,
|
||||
}
|
||||
|
||||
return srv.client.Call("POST", path, nil, params)
|
||||
}
|
||||
|
||||
// Oauth allow the user to login to his account using the OAuth provider of
|
||||
// his choice. Each OAuth provider should be enabled from the Appwrite console
|
||||
// first. Use the success and failure arguments to provide a redirect URL's
|
||||
// back to your app when login is completed.
|
||||
func (srv *Auth) Oauth(Provider string, Success string, Failure string) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{provider}", Provider)
|
||||
path := r.Replace("/auth/login/oauth/{provider}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
"success": Success,
|
||||
"failure": Failure,
|
||||
}
|
||||
|
||||
return srv.client.Call("GET", path, nil, params)
|
||||
}
|
||||
|
||||
// Logout use this endpoint to log out the currently logged in user from his
|
||||
// account. When successful this endpoint will delete the user session and
|
||||
// remove the session secret cookie from the user client.
|
||||
func (srv *Auth) Logout() (map[string]interface{}, error) {
|
||||
path := "/auth/logout"
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
||||
return srv.client.Call("DELETE", path, nil, params)
|
||||
}
|
||||
|
||||
// LogoutBySession use this endpoint to log out the currently logged in user
|
||||
// from all his account sessions across all his different devices. When using
|
||||
// the option id argument, only the session unique ID provider will be
|
||||
// deleted.
|
||||
func (srv *Auth) LogoutBySession(Id string) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{id}", Id)
|
||||
path := r.Replace("/auth/logout/{id}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
||||
return srv.client.Call("DELETE", path, nil, params)
|
||||
}
|
||||
|
||||
// Recovery sends the user an email with a temporary secret token for password
|
||||
// reset. When the user clicks the confirmation link he is redirected back to
|
||||
// your app password reset redirect URL with a secret token and email address
|
||||
// values attached to the URL query string. Use the query string params to
|
||||
// submit a request to the /auth/password/reset endpoint to complete the
|
||||
// process.
|
||||
func (srv *Auth) Recovery(Email string, Reset string) (map[string]interface{}, error) {
|
||||
path := "/auth/recovery"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"email": Email,
|
||||
"reset": Reset,
|
||||
}
|
||||
|
||||
return srv.client.Call("POST", path, nil, params)
|
||||
}
|
||||
|
||||
// RecoveryReset use this endpoint to complete the user account password
|
||||
// reset. Both the **userId** and **token** arguments will be passed as query
|
||||
// parameters to the redirect URL you have provided when sending your request
|
||||
// to the /auth/recovery endpoint.
|
||||
//
|
||||
// Please notice that in order to avoid a [Redirect
|
||||
// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
// the only valid redirect URLs are the ones from domains you have set when
|
||||
// adding your platforms in the console interface.
|
||||
func (srv *Auth) RecoveryReset(UserId string, Token string, PasswordA string, PasswordB string) (map[string]interface{}, error) {
|
||||
path := "/auth/recovery/reset"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"userId": UserId,
|
||||
"token": Token,
|
||||
"password-a": PasswordA,
|
||||
"password-b": PasswordB,
|
||||
}
|
||||
|
||||
return srv.client.Call("PUT", path, nil, params)
|
||||
}
|
||||
|
||||
// Register use this endpoint to allow a new user to register an account in
|
||||
// your project. Use the success and failure URLs to redirect users back to
|
||||
// your application after signup completes.
|
||||
//
|
||||
// If registration completes successfully user will be sent with a
|
||||
// confirmation email in order to confirm he is the owner of the account email
|
||||
// address. Use the confirmation parameter to redirect the user from the
|
||||
// confirmation email back to your app. When the user is redirected, use the
|
||||
// /auth/confirm endpoint to complete the account confirmation.
|
||||
//
|
||||
// Please notice that in order to avoid a [Redirect
|
||||
// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
// the only valid redirect URLs are the ones from domains you have set when
|
||||
// adding your platforms in the console interface.
|
||||
//
|
||||
// When accessing this route using Javascript from the browser, success and
|
||||
// failure parameter URLs are required. Appwrite server will respond with a
|
||||
// 301 redirect status code and will set the user session cookie. This
|
||||
// behavior is enforced because modern browsers are limiting 3rd party cookies
|
||||
// in XHR of fetch requests to protect user privacy.
|
||||
func (srv *Auth) Register(Email string, Password string, Confirm string, Success string, Failure string, Name string) (map[string]interface{}, error) {
|
||||
path := "/auth/register"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"email": Email,
|
||||
"password": Password,
|
||||
"confirm": Confirm,
|
||||
"success": Success,
|
||||
"failure": Failure,
|
||||
"name": Name,
|
||||
}
|
||||
|
||||
return srv.client.Call("POST", path, nil, params)
|
||||
}
|
||||
|
||||
// Confirm use this endpoint to complete the confirmation of the user account
|
||||
// email address. Both the **userId** and **token** arguments will be passed
|
||||
// as query parameters to the redirect URL you have provided when sending your
|
||||
// request to the /auth/register endpoint.
|
||||
func (srv *Auth) Confirm(UserId string, Token string) (map[string]interface{}, error) {
|
||||
path := "/auth/register/confirm"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"userId": UserId,
|
||||
"token": Token,
|
||||
}
|
||||
|
||||
return srv.client.Call("POST", path, nil, params)
|
||||
}
|
||||
|
||||
// ConfirmResend this endpoint allows the user to request your app to resend
|
||||
// him his email confirmation message. The redirect arguments act the same way
|
||||
// as in /auth/register endpoint.
|
||||
//
|
||||
// Please notice that in order to avoid a [Redirect
|
||||
// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
|
||||
// the only valid redirect URLs are the ones from domains you have set when
|
||||
// adding your platforms in the console interface.
|
||||
func (srv *Auth) ConfirmResend(Confirm string) (map[string]interface{}, error) {
|
||||
path := "/auth/register/confirm/resend"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"confirm": Confirm,
|
||||
}
|
||||
|
||||
return srv.client.Call("POST", path, nil, params)
|
||||
}
|
||||
|
|
@ -6,7 +6,15 @@ import (
|
|||
|
||||
// Avatars service
|
||||
type Avatars struct {
|
||||
client *Client
|
||||
client Client
|
||||
}
|
||||
|
||||
func NewAvatars(clt Client) Avatars {
|
||||
service := Avatars{
|
||||
client: clt,
|
||||
}
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
// GetBrowser you can use this endpoint to show different browser icons to
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package appwrite
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
|
@ -31,23 +31,21 @@ func (clt *Client) AddHeader(key string, value string) {
|
|||
clt.headers[key] = value
|
||||
}
|
||||
|
||||
// SetProjectHeader add the 'X-Appwrite-Project' header for the Client. Your Appwrite project ID
|
||||
func (clt *Client) SetProjectHeader(value string) {
|
||||
// Your project ID
|
||||
func (clt *Client) SetProject(value string) {
|
||||
clt.headers["X-Appwrite-Project"] = value
|
||||
}
|
||||
|
||||
// SetKeyHeader add the 'X-Appwrite-Key' header for the Client. Your Appwrite project secret key
|
||||
func (clt *Client) SetKeyHeader(value string) {
|
||||
// Your secret API key
|
||||
func (clt *Client) SetKey(value string) {
|
||||
clt.headers["X-Appwrite-Key"] = value
|
||||
}
|
||||
|
||||
// SetLocaleHeader add the 'X-Appwrite-Locale' header for the Client.
|
||||
func (clt *Client) SetLocaleHeader(value string) {
|
||||
func (clt *Client) SetLocale(value string) {
|
||||
clt.headers["X-Appwrite-Locale"] = value
|
||||
}
|
||||
|
||||
// SetModeHeader add the 'X-Appwrite-Mode' header for the Client.
|
||||
func (clt *Client) SetModeHeader(value string) {
|
||||
func (clt *Client) SetMode(value string) {
|
||||
clt.headers["X-Appwrite-Mode"] = value
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,15 @@ import (
|
|||
|
||||
// Database service
|
||||
type Database struct {
|
||||
client *Client
|
||||
client Client
|
||||
}
|
||||
|
||||
func NewDatabase(clt Client) Database {
|
||||
service := Database{
|
||||
client: clt,
|
||||
}
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
// ListCollections get a list of all the user collections. You can use the
|
||||
|
|
@ -14,7 +22,7 @@ type Database struct {
|
|||
// return a list of all of the project collections. [Learn more about
|
||||
// different API modes](/docs/admin).
|
||||
func (srv *Database) ListCollections(Search string, Limit int, Offset int, OrderType string) (map[string]interface{}, error) {
|
||||
path := "/database"
|
||||
path := "/database/collections"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"search": Search,
|
||||
|
|
@ -28,7 +36,7 @@ func (srv *Database) ListCollections(Search string, Limit int, Offset int, Order
|
|||
|
||||
// CreateCollection create a new Collection.
|
||||
func (srv *Database) CreateCollection(Name string, Read []interface{}, Write []interface{}, Rules []interface{}) (map[string]interface{}, error) {
|
||||
path := "/database"
|
||||
path := "/database/collections"
|
||||
|
||||
params := map[string]interface{}{
|
||||
"name": Name,
|
||||
|
|
@ -44,7 +52,7 @@ func (srv *Database) CreateCollection(Name string, Read []interface{}, Write []i
|
|||
// returns a JSON object with the collection metadata.
|
||||
func (srv *Database) GetCollection(CollectionId string) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId)
|
||||
path := r.Replace("/database/{collectionId}")
|
||||
path := r.Replace("/database/collections/{collectionId}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
|
@ -55,7 +63,7 @@ func (srv *Database) GetCollection(CollectionId string) (map[string]interface{},
|
|||
// UpdateCollection update collection by its unique ID.
|
||||
func (srv *Database) UpdateCollection(CollectionId string, Name string, Read []interface{}, Write []interface{}, Rules []interface{}) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId)
|
||||
path := r.Replace("/database/{collectionId}")
|
||||
path := r.Replace("/database/collections/{collectionId}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
"name": Name,
|
||||
|
|
@ -71,7 +79,7 @@ func (srv *Database) UpdateCollection(CollectionId string, Name string, Read []i
|
|||
// write permissions have access to delete this resource.
|
||||
func (srv *Database) DeleteCollection(CollectionId string) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId)
|
||||
path := r.Replace("/database/{collectionId}")
|
||||
path := r.Replace("/database/collections/{collectionId}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
|
@ -85,7 +93,7 @@ func (srv *Database) DeleteCollection(CollectionId string) (map[string]interface
|
|||
// modes](/docs/admin).
|
||||
func (srv *Database) ListDocuments(CollectionId string, Filters []interface{}, Offset int, Limit int, OrderField string, OrderType string, OrderCast string, Search string, First int, Last int) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId)
|
||||
path := r.Replace("/database/{collectionId}/documents")
|
||||
path := r.Replace("/database/collections/{collectionId}/documents")
|
||||
|
||||
params := map[string]interface{}{
|
||||
"filters": Filters,
|
||||
|
|
@ -103,9 +111,9 @@ func (srv *Database) ListDocuments(CollectionId string, Filters []interface{}, O
|
|||
}
|
||||
|
||||
// CreateDocument create a new Document.
|
||||
func (srv *Database) CreateDocument(CollectionId string, Data string, Read []interface{}, Write []interface{}, ParentDocument string, ParentProperty string, ParentPropertyType string) (map[string]interface{}, error) {
|
||||
func (srv *Database) CreateDocument(CollectionId string, Data object, Read []interface{}, Write []interface{}, ParentDocument string, ParentProperty string, ParentPropertyType string) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId)
|
||||
path := r.Replace("/database/{collectionId}/documents")
|
||||
path := r.Replace("/database/collections/{collectionId}/documents")
|
||||
|
||||
params := map[string]interface{}{
|
||||
"data": Data,
|
||||
|
|
@ -123,7 +131,7 @@ func (srv *Database) CreateDocument(CollectionId string, Data string, Read []int
|
|||
// JSON object with the document data.
|
||||
func (srv *Database) GetDocument(CollectionId string, DocumentId string) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId, "{documentId}", DocumentId)
|
||||
path := r.Replace("/database/{collectionId}/documents/{documentId}")
|
||||
path := r.Replace("/database/collections/{collectionId}/documents/{documentId}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
|
@ -132,9 +140,9 @@ func (srv *Database) GetDocument(CollectionId string, DocumentId string) (map[st
|
|||
}
|
||||
|
||||
// UpdateDocument
|
||||
func (srv *Database) UpdateDocument(CollectionId string, DocumentId string, Data string, Read []interface{}, Write []interface{}) (map[string]interface{}, error) {
|
||||
func (srv *Database) UpdateDocument(CollectionId string, DocumentId string, Data object, Read []interface{}, Write []interface{}) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId, "{documentId}", DocumentId)
|
||||
path := r.Replace("/database/{collectionId}/documents/{documentId}")
|
||||
path := r.Replace("/database/collections/{collectionId}/documents/{documentId}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
"data": Data,
|
||||
|
|
@ -150,7 +158,7 @@ func (srv *Database) UpdateDocument(CollectionId string, DocumentId string, Data
|
|||
// Child documents **will not** be deleted.
|
||||
func (srv *Database) DeleteDocument(CollectionId string, DocumentId string) (map[string]interface{}, error) {
|
||||
r := strings.NewReplacer("{collectionId}", CollectionId, "{documentId}", DocumentId)
|
||||
path := r.Replace("/database/{collectionId}/documents/{documentId}")
|
||||
path := r.Replace("/database/collections/{collectionId}/documents/{documentId}")
|
||||
|
||||
params := map[string]interface{}{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
# Account Examples
|
||||
|
||||
## Delete
|
||||
|
||||
```go
|
||||
package appwrite-delete
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/appwrite/sdk-for-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a Client
|
||||
var clt := appwrite.Client{}
|
||||
|
||||
// Set Client required headers
|
||||
clt.SetProject("")
|
||||
clt.SetKey("")
|
||||
|
||||
// Create a new Account service passing Client
|
||||
var srv := appwrite.Account{
|
||||
client: &clt
|
||||
}
|
||||
|
||||
// Call Delete method and handle results
|
||||
var res, err := srv.Delete()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(res)
|
||||
}
|
||||
```
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# Account Examples
|
||||
|
||||
## Get
|
||||
|
||||
```go
|
||||
package appwrite-get
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/appwrite/sdk-for-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a Client
|
||||
var clt := appwrite.Client{}
|
||||
|
||||
// Set Client required headers
|
||||
clt.SetProject("")
|
||||
clt.SetKey("")
|
||||
|
||||
// Create a new Account service passing Client
|
||||
var srv := appwrite.Account{
|
||||
client: &clt
|
||||
}
|
||||
|
||||
// Call Get method and handle results
|
||||
var res, err := srv.Get()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(res)
|
||||
}
|
||||
```
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# Account Examples
|
||||
|
||||
## GetPrefs
|
||||
|
||||
```go
|
||||
package appwrite-getprefs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/appwrite/sdk-for-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a Client
|
||||
var clt := appwrite.Client{}
|
||||
|
||||
// Set Client required headers
|
||||
clt.SetProject("")
|
||||
clt.SetKey("")
|
||||
|
||||
// Create a new Account service passing Client
|
||||
var srv := appwrite.Account{
|
||||
client: &clt
|
||||
}
|
||||
|
||||
// Call GetPrefs method and handle results
|
||||
var res, err := srv.GetPrefs()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(res)
|
||||
}
|
||||
```
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# Account Examples
|
||||
|
||||
## GetSecurity
|
||||
|
||||
```go
|
||||
package appwrite-getsecurity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/appwrite/sdk-for-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a Client
|
||||
var clt := appwrite.Client{}
|
||||
|
||||
// Set Client required headers
|
||||
clt.SetProject("")
|
||||
clt.SetKey("")
|
||||
|
||||
// Create a new Account service passing Client
|
||||
var srv := appwrite.Account{
|
||||
client: &clt
|
||||
}
|
||||
|
||||
// Call GetSecurity method and handle results
|
||||
var res, err := srv.GetSecurity()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(res)
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue