Merge remote-tracking branch 'origin/fix-role-clobbering' into feat-graphql-support
# Conflicts: # composer.lock
This commit is contained in:
commit
d019937e4f
27 changed files with 285 additions and 103 deletions
|
@ -3,6 +3,7 @@ tasks:
|
||||||
init: |
|
init: |
|
||||||
docker compose pull
|
docker compose pull
|
||||||
docker compose build
|
docker compose build
|
||||||
|
docker pull composer
|
||||||
command: |
|
command: |
|
||||||
docker run --rm --interactive --tty \
|
docker run --rm --interactive --tty \
|
||||||
--volume $PWD:/app \
|
--volume $PWD:/app \
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
- Added support for selfhosted Gitlab (Oauth)
|
||||||
|
|
||||||
# Version 0.14.2
|
# Version 0.14.2
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
|
@ -107,7 +107,7 @@ return [ // Ordered by ABC.
|
||||||
'icon' => 'icon-gitlab',
|
'icon' => 'icon-gitlab',
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'sandbox' => false,
|
'sandbox' => false,
|
||||||
'form' => false,
|
'form' => 'gitlab.phtml',
|
||||||
'beta' => false,
|
'beta' => false,
|
||||||
'mock' => false,
|
'mock' => false,
|
||||||
],
|
],
|
||||||
|
|
|
@ -1859,7 +1859,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
->label('sdk.response.model', Response::MODEL_DOCUMENT)
|
->label('sdk.response.model', Response::MODEL_DOCUMENT)
|
||||||
->param('collectionId', null, new UID(), 'Collection ID.')
|
->param('collectionId', null, new UID(), 'Collection ID.')
|
||||||
->param('documentId', null, new UID(), 'Document ID.')
|
->param('documentId', null, new UID(), 'Document ID.')
|
||||||
->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.')
|
->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true)
|
||||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
|
@ -1900,8 +1900,8 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||||
|
|
||||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data) && empty($read) && empty($write)) {
|
||||||
throw new Exception('Missing payload', 400, Exception::DOCUMENT_MISSING_PAYLOAD);
|
throw new Exception('Missing payload or read/write permissions', 400, Exception::DOCUMENT_MISSING_PAYLOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!\is_array($data)) {
|
if (!\is_array($data)) {
|
||||||
|
|
|
@ -470,7 +470,7 @@ App::post('/v1/functions/:functionId/deployments')
|
||||||
->inject('project')
|
->inject('project')
|
||||||
->inject('deviceFunctions')
|
->inject('deviceFunctions')
|
||||||
->inject('deviceLocal')
|
->inject('deviceLocal')
|
||||||
->action(function (string $functionId, string $entrypoint, array $file, bool $activate, Request $request, Response $response, Database $dbForProject, Stats $usage, Event $events, Document $project, Device $deviceFunctions, Device $deviceLocal) {
|
->action(function (string $functionId, string $entrypoint, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Stats $usage, Event $events, Document $project, Device $deviceFunctions, Device $deviceLocal) {
|
||||||
|
|
||||||
$function = $dbForProject->getDocument('functions', $functionId);
|
$function = $dbForProject->getDocument('functions', $functionId);
|
||||||
|
|
||||||
|
|
|
@ -356,7 +356,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
->inject('mode')
|
->inject('mode')
|
||||||
->inject('deviceFiles')
|
->inject('deviceFiles')
|
||||||
->inject('deviceLocal')
|
->inject('deviceLocal')
|
||||||
->action(function (string $bucketId, string $fileId, array $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Audit $audits, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) {
|
->action(function (string $bucketId, string $fileId, mixed $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Audit $audits, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) {
|
||||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -292,11 +292,11 @@ App::init(function (App $utopia, Request $request, Response $response, Document
|
||||||
|
|
||||||
$service = $route->getLabel('sdk.namespace', '');
|
$service = $route->getLabel('sdk.namespace', '');
|
||||||
if (!empty($service)) {
|
if (!empty($service)) {
|
||||||
$roles = Authorization::getRoles();
|
$serviceRoles = Authorization::getRoles();
|
||||||
if (
|
if (
|
||||||
array_key_exists($service, $project->getAttribute('services', []))
|
array_key_exists($service, $project->getAttribute('services', []))
|
||||||
&& !$project->getAttribute('services', [])[$service]
|
&& !$project->getAttribute('services', [])[$service]
|
||||||
&& !(Auth::isPrivilegedUser($roles) || Auth::isAppUser($roles))
|
&& !(Auth::isPrivilegedUser($serviceRoles) || Auth::isAppUser($serviceRoles))
|
||||||
) {
|
) {
|
||||||
throw new AppwriteException('Service is disabled', 503, AppwriteException::GENERAL_SERVICE_DISABLED);
|
throw new AppwriteException('Service is disabled', 503, AppwriteException::GENERAL_SERVICE_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,7 +237,7 @@ App::post('/v1/mock/tests/general/upload')
|
||||||
->param('file', [], new File(), 'Sample file param', false)
|
->param('file', [], new File(), 'Sample file param', false)
|
||||||
->inject('request')
|
->inject('request')
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->action(function (string $x, int $y, array $z, array $file, Request $request, Response $response) {
|
->action(function (string $x, int $y, array $z, mixed $file, Request $request, Response $response) {
|
||||||
|
|
||||||
$file = $request->getFiles('file');
|
$file = $request->getFiles('file');
|
||||||
|
|
||||||
|
|
|
@ -429,7 +429,7 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
|
||||||
* Registry
|
* Registry
|
||||||
*/
|
*/
|
||||||
$register->set('logger', function () {
|
$register->set('logger', function () {
|
||||||
// Register error logger
|
// Register error logger
|
||||||
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
|
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||||
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
|
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
|
||||||
|
|
||||||
|
|
|
@ -567,7 +567,16 @@ $logs = $this->getParam('logs', null);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="margin-bottom-large text-fade text-size-small">
|
<ul class="margin-bottom-large text-fade text-size-small">
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||||
|
<button data-ls-ui-trigger="open-json"
|
||||||
|
class="link text-size-small"
|
||||||
|
data-analytics
|
||||||
|
data-analytics-event="click"
|
||||||
|
data-analytics-category="console"
|
||||||
|
data-analytics-label="View as JSON (Collection)">
|
||||||
|
View as JSON
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-collection.dateUpdated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-collection.dateUpdated|dateText}}"></span></li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-collection.dateCreated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-collection.dateCreated|dateText}}"></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -346,7 +346,16 @@ $logs = $this->getParam('logs', null);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="margin-bottom-large text-fade text-size-small" data-ls-if="({{project-document.$id}})">
|
<ul class="margin-bottom-large text-fade text-size-small" data-ls-if="({{project-document.$id}})">
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||||
|
<button data-ls-ui-trigger="open-json"
|
||||||
|
class="link text-size-small"
|
||||||
|
data-analytics
|
||||||
|
data-analytics-event="click"
|
||||||
|
data-analytics-category="console"
|
||||||
|
data-analytics-label="View as JSON (Document)">
|
||||||
|
View as JSON
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div data-ls-if="({{project-document.$id}})">
|
<div data-ls-if="({{project-document.$id}})">
|
||||||
|
|
|
@ -245,7 +245,16 @@ sort($patterns);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="margin-bottom-large text-fade text-size-small">
|
<ul class="margin-bottom-large text-fade text-size-small">
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||||
|
<button data-ls-ui-trigger="open-json"
|
||||||
|
class="link text-size-small"
|
||||||
|
data-analytics
|
||||||
|
data-analytics-event="click"
|
||||||
|
data-analytics-category="console"
|
||||||
|
data-analytics-label="View as JSON (Function)">
|
||||||
|
View as JSON
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.dateUpdated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.dateUpdated|dateText}}"></span></li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.dateCreated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.dateCreated|dateText}}"></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -595,7 +604,16 @@ sort($patterns);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="margin-bottom-large text-fade text-size-small">
|
<ul class="margin-bottom-large text-fade text-size-small">
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||||
|
<button data-ls-ui-trigger="open-json"
|
||||||
|
class="link text-size-small"
|
||||||
|
data-analytics
|
||||||
|
data-analytics-event="click"
|
||||||
|
data-analytics-category="console"
|
||||||
|
data-analytics-label="View as JSON (Function)">
|
||||||
|
View as JSON
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.dateUpdated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-function.dateUpdated|dateText}}"></span></li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.dateCreated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-function.dateCreated|dateText}}"></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -471,7 +471,16 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="margin-bottom-large text-fade text-size-small">
|
<ul class="margin-bottom-large text-fade text-size-small">
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||||
|
<button data-ls-ui-trigger="open-json"
|
||||||
|
class="link text-size-small"
|
||||||
|
data-analytics
|
||||||
|
data-analytics-event="click"
|
||||||
|
data-analytics-category="console"
|
||||||
|
data-analytics-label="View as JSON (Bucket)">
|
||||||
|
View as JSON
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-bucket.dateUpdated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Last Updated: <span data-ls-bind="{{project-bucket.dateUpdated|dateText}}"></span></li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-bucket.dateCreated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{project-bucket.dateCreated|dateText}}"></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -453,7 +453,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
||||||
data-analytics-activity
|
data-analytics-activity
|
||||||
data-analytics-event="submit"
|
data-analytics-event="submit"
|
||||||
data-analytics-category="console"
|
data-analytics-category="console"
|
||||||
data-analytics-label="Update Project OAuth2"
|
data-analytics-label="Update Project OAuth2 (<?php echo $this->escape($name); ?>)"
|
||||||
data-service="projects.updateOAuth2"
|
data-service="projects.updateOAuth2"
|
||||||
data-scope="console"
|
data-scope="console"
|
||||||
data-event="submit"
|
data-event="submit"
|
||||||
|
|
12
app/views/console/users/oauth/gitlab.phtml
Normal file
12
app/views/console/users/oauth/gitlab.phtml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
$provider = $this->getParam('provider', '');
|
||||||
|
?>
|
||||||
|
|
||||||
|
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">App ID</label>
|
||||||
|
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}" />
|
||||||
|
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret">App Secret</label>
|
||||||
|
<input name="clientSecret" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret" type="text" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>ClientSecret}}" />
|
||||||
|
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Endpoint">Endpoint (optional)<span class="tooltip" data-tooltip="If you have self-hosted instance, provide the host."><i class="icon-info-circled"></i></span></label>
|
||||||
|
<input name="endpoint" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Endpoint" type="text" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Endpoint}}" placeholder="https://gitlab.com" />
|
||||||
|
|
||||||
|
<input name="secret" data-forms-oauth-custom="<?php echo $this->escape(ucfirst($provider)); ?>" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="hidden" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}" />
|
|
@ -206,7 +206,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="margin-bottom-large text-fade text-size-small">
|
<ul class="margin-bottom-large text-fade text-size-small">
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||||
|
<button data-ls-ui-trigger="open-json"
|
||||||
|
class="link text-size-small"
|
||||||
|
data-analytics
|
||||||
|
data-analytics-event="click"
|
||||||
|
data-analytics-category="console"
|
||||||
|
data-analytics-label="View as JSON (Team)">
|
||||||
|
View as JSON
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{team.dateCreated|dateText}}"></span></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> Created: <span data-ls-bind="{{team.dateCreated|dateText}}"></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
@ -239,7 +239,16 @@
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-update-name" class="link text-size-small">Update name</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-update-name" class="link text-size-small">Update name</button></li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-update-email" class="link text-size-small">Update Email</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-update-email" class="link text-size-small">Update Email</button></li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-update-password" class="link text-size-small">Update Password</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-update-password" class="link text-size-small">Update Password</button></li>
|
||||||
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
|
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i>
|
||||||
|
<button data-ls-ui-trigger="open-json"
|
||||||
|
class="link text-size-small"
|
||||||
|
data-analytics
|
||||||
|
data-analytics-event="click"
|
||||||
|
data-analytics-category="console"
|
||||||
|
data-analytics-label="View as JSON (User)">
|
||||||
|
View as JSON
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div data-ls-if="{{user.emailVerification}} === false" style="display: none">
|
<div data-ls-if="{{user.emailVerification}} === false" style="display: none">
|
||||||
|
|
|
@ -591,6 +591,11 @@ services:
|
||||||
container_name: appwrite-redis
|
container_name: appwrite-redis
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
command: >
|
||||||
|
redis-server
|
||||||
|
--maxmemory 512mb
|
||||||
|
--maxmemory-policy allkeys-lru
|
||||||
|
--maxmemory-samples 5
|
||||||
networks:
|
networks:
|
||||||
- appwrite
|
- appwrite
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
@ -647,6 +647,11 @@ services:
|
||||||
image: redis:6.2-alpine
|
image: redis:6.2-alpine
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-redis
|
container_name: appwrite-redis
|
||||||
|
command: >
|
||||||
|
redis-server
|
||||||
|
--maxmemory 512mb
|
||||||
|
--maxmemory-policy allkeys-lru
|
||||||
|
--maxmemory-samples 5
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379"
|
- "6379:6379"
|
||||||
networks:
|
networks:
|
||||||
|
|
|
@ -76,126 +76,84 @@ use Appwrite\Auth\OAuth2;
|
||||||
|
|
||||||
class [PROVIDER NAME] extends OAuth2
|
class [PROVIDER NAME] extends OAuth2
|
||||||
{
|
{
|
||||||
/**
|
private string $endpoint = '[ENDPOINT API URL]';
|
||||||
* @var string
|
protected array $user = [];
|
||||||
*/
|
protected array $tokens = [];
|
||||||
private $endpoint = '[ENDPOINT API URL]';
|
protected array $scopes = [
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $scopes = [
|
|
||||||
// [ARRAY_OF_REQUIRED_SCOPES]
|
// [ARRAY_OF_REQUIRED_SCOPES]
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $user = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $tokens = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return '[PROVIDER NAME]';
|
return '[PROVIDER NAME]';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLoginURL(): string
|
public function getLoginURL(): string
|
||||||
{
|
{
|
||||||
$url = $this->endpoint . '[LOGIN_URL_STUFF]';
|
$url = $this->endpoint . '[LOGIN_URL_STUFF]';
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $code
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getTokens(string $code): array
|
protected function getTokens(string $code): array
|
||||||
{
|
{
|
||||||
if(empty($this->tokens)) {
|
if (empty($this->tokens)) {
|
||||||
// TODO: Fire request to oauth API to generate access_token
|
// TODO: Fire request to oauth API to generate access_token
|
||||||
// Make sure to use '$this->getScopes()' to include all scopes properly
|
// Make sure to use '$this->getScopes()' to include all scopes properly
|
||||||
$this->tokens = "[FETCH TOKEN RESPONSE]";
|
$this->tokens = ["[FETCH TOKEN RESPONSE]"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->tokens;
|
return $this->tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function refreshTokens(string $refreshToken): array
|
||||||
/**
|
|
||||||
* @param string $refreshToken
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function refreshTokens(string $refreshToken):array
|
|
||||||
{
|
{
|
||||||
// TODO: Fire request to oauth API to generate access_token using refresh token
|
// TODO: Fire request to oauth API to generate access_token using refresh token
|
||||||
$this->tokens = "[FETCH TOKEN RESPONSE]";
|
$this->tokens = ["[FETCH TOKEN RESPONSE]"];
|
||||||
|
|
||||||
return $this->tokens;
|
return $this->tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $accessToken
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUserID(string $accessToken): string
|
public function getUserID(string $accessToken): string
|
||||||
{
|
{
|
||||||
$user = $this->getUser($accessToken);
|
$user = $this->getUser($accessToken);
|
||||||
|
|
||||||
// TODO: Pick user ID from $user response
|
// TODO: Pick user ID from $user response
|
||||||
$userId = "[USER ID]";
|
$userId = "[USER ID]";
|
||||||
|
|
||||||
return $userId;
|
return $userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $accessToken
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUserEmail(string $accessToken): string
|
public function getUserEmail(string $accessToken): string
|
||||||
{
|
{
|
||||||
$user = $this->getUser($accessToken);
|
$user = $this->getUser($accessToken);
|
||||||
|
|
||||||
// TODO: Pick user email from $user response
|
// TODO: Pick user email from $user response
|
||||||
$userEmail = "[USER EMAIL]";
|
$userEmail = "[USER EMAIL]";
|
||||||
|
|
||||||
return $userEmail;
|
return $userEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function isEmailVerified(string $accessToken): bool
|
||||||
* @param string $accessToken
|
{
|
||||||
*
|
$user = $this->getUser($accessToken);
|
||||||
* @return string
|
|
||||||
*/
|
// TODO: Pick user verification status from $user response
|
||||||
|
$isVerified = "[USER VERIFICATION STATUS]";
|
||||||
|
|
||||||
|
return $isVerified;
|
||||||
|
}
|
||||||
|
|
||||||
public function getUserName(string $accessToken): string
|
public function getUserName(string $accessToken): string
|
||||||
{
|
{
|
||||||
$user = $this->getUser($accessToken);
|
$user = $this->getUser($accessToken);
|
||||||
|
|
||||||
// TODO: Pick username from $user response
|
// TODO: Pick username from $user response
|
||||||
$username = "[USERNAME]";
|
$username = "[USERNAME]";
|
||||||
|
|
||||||
return $username;
|
return $username;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function getUser(string $accessToken): array
|
||||||
* @param string $accessToken
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getUser(string $accessToken)
|
|
||||||
{
|
{
|
||||||
if (empty($this->user)) {
|
if (empty($this->user)) {
|
||||||
// TODO: Fire request to oauth API to get information about users
|
// TODO: Fire request to oauth API to get information about users
|
||||||
|
@ -205,6 +163,7 @@ class [PROVIDER NAME] extends OAuth2
|
||||||
return $this->user;
|
return $this->user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> If you copy this template, make sure to replace all placeholders wrapped like `[THIS]` and to implement everything marked as `TODO:`.
|
> If you copy this template, make sure to replace all placeholders wrapped like `[THIS]` and to implement everything marked as `TODO:`.
|
||||||
|
@ -233,7 +192,7 @@ First of all, commit the changes with the message `Added XXX OAuth2 Provider` an
|
||||||
|
|
||||||
## 🤕 Stuck ?
|
## 🤕 Stuck ?
|
||||||
|
|
||||||
If you need any help with the contribution, feel free to head over to [our discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
|
If you need any help with the contribution, feel free to head over to [our Discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
|
||||||
|
|
||||||
## 😉 Need more freedom
|
## 😉 Need more freedom
|
||||||
|
|
||||||
|
|
|
@ -254,4 +254,4 @@ First of all, commit the changes with the message `Added XXX Runtime` and push i
|
||||||
|
|
||||||
## ![face_with_head_bandage](https://github.githubassets.com/images/icons/emoji/unicode/1f915.png) Stuck ?
|
## ![face_with_head_bandage](https://github.githubassets.com/images/icons/emoji/unicode/1f915.png) Stuck ?
|
||||||
|
|
||||||
If you need any help with the contribution, feel free to head over to [our discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
|
If you need any help with the contribution, feel free to head over to [our Discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
|
||||||
|
|
|
@ -179,4 +179,4 @@ If you can see countries names translated, everything works, and you are ready f
|
||||||
First of all, commit the changes with the message `Added YYY translations` where `YYY` is the translated language and push it. This will publish a new branch to your forked version of Appwrite. If you visit it at `github.com/YOUR_USERNAME/appwrite`, you will see a new alert saying you are ready to submit a pull request. Follow the steps GitHub provides, and at the end, you will have your pull request submitted.
|
First of all, commit the changes with the message `Added YYY translations` where `YYY` is the translated language and push it. This will publish a new branch to your forked version of Appwrite. If you visit it at `github.com/YOUR_USERNAME/appwrite`, you will see a new alert saying you are ready to submit a pull request. Follow the steps GitHub provides, and at the end, you will have your pull request submitted.
|
||||||
|
|
||||||
## 🤕 Stuck ?
|
## 🤕 Stuck ?
|
||||||
If you need any help with the contribution, feel free to head over to [our discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
|
If you need any help with the contribution, feel free to head over to [our Discord channel](https://appwrite.io/discord) and we'll be happy to help you out.
|
||||||
|
|
2
public/dist/scripts/app-all.js
vendored
2
public/dist/scripts/app-all.js
vendored
|
@ -3868,7 +3868,7 @@ list["filters-"+filter.key]=params[key][i];}}}}
|
||||||
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
||||||
element.setAttribute("data-event","none");}
|
element.setAttribute("data-event","none");}
|
||||||
break;default:break;}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-key-value",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";key.required=true;value.type="text";value.className="margin-bottom-no";value.placeholder="Value";value.required=true;wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.name=key.value;element.value=value.value;};let syncB=function(){key.value=element.name||"";value.value=element.value||"";};syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){console.log('down',element.offsetHeight);element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){console.log('up',element);element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
break;default:break;}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-key-value",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";key.required=true;value.type="text";value.className="margin-bottom-no";value.placeholder="Value";value.required=true;wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.name=key.value;element.value=value.value;};let syncB=function(){key.value=element.name||"";value.value=element.value||"";};syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){console.log('down',element.offsetHeight);element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){console.log('up',element);element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
||||||
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"}}
|
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"},"Gitlab":{"endpoint":"oauth2GitlabEndpoint","clientSecret":"oauth2GitlabClientSecret",},}
|
||||||
let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")}
|
let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")}
|
||||||
let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}}
|
let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}}
|
||||||
function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}}
|
function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}}
|
||||||
|
|
2
public/dist/scripts/app.js
vendored
2
public/dist/scripts/app.js
vendored
|
@ -796,7 +796,7 @@ list["filters-"+filter.key]=params[key][i];}}}}
|
||||||
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y<events.length;y++){if(events[y]==="init"){element.addEventListener("rendered",function(){apply(form.toJson(element));},{once:true});}else{}
|
||||||
element.setAttribute("data-event","none");}
|
element.setAttribute("data-event","none");}
|
||||||
break;default:break;}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-key-value",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";key.required=true;value.type="text";value.className="margin-bottom-no";value.placeholder="Value";value.required=true;wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.name=key.value;element.value=value.value;};let syncB=function(){key.value=element.name||"";value.value=element.value||"";};syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){console.log('down',element.offsetHeight);element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){console.log('up',element);element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
break;default:break;}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-headers",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";value.type="text";value.className="margin-bottom-no";value.placeholder="Value";wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.value=key.value.toLowerCase()+":"+value.value.toLowerCase();};let syncB=function(){let split=element.value.toLowerCase().split(":");key.value=split[0]||"";value.value=split[1]||"";key.value=key.value.trim();value.value=value.value.trim();};syncB();}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-key-value",controller:function(element){let key=document.createElement("input");let value=document.createElement("input");let wrap=document.createElement("div");let cell1=document.createElement("div");let cell2=document.createElement("div");key.type="text";key.className="margin-bottom-no";key.placeholder="Key";key.required=true;value.type="text";value.className="margin-bottom-no";value.placeholder="Value";value.required=true;wrap.className="row thin margin-bottom-small";cell1.className="col span-6";cell2.className="col span-6";element.parentNode.insertBefore(wrap,element);cell1.appendChild(key);cell2.appendChild(value);wrap.appendChild(cell1);wrap.appendChild(cell2);key.addEventListener("input",function(){syncA();});value.addEventListener("input",function(){syncA();});element.addEventListener("change",function(){syncB();});let syncA=function(){element.name=key.value;element.value=value.value;};let syncB=function(){key.value=element.name||"";value.value=element.value||"";};syncB();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-down",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-down]")).map(function(obj){obj.addEventListener("click",function(){if(element.nextElementSibling){console.log('down',element.offsetHeight);element.parentNode.insertBefore(element.nextElementSibling,element);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-move-up",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-move-up]")).map(function(obj){obj.addEventListener("click",function(){if(element.previousElementSibling){console.log('up',element);element.parentNode.insertBefore(element,element.previousElementSibling);element.scrollIntoView({block:'center'});}});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-nav",repeat:false,controller:function(element,view,container,document){let titles=document.querySelectorAll('[data-forms-nav-anchor]');let links=element.querySelectorAll('[data-forms-nav-link]');let minLink=null;let check=function(){let minDistance=null;let minElement=null;for(let i=0;i<titles.length;++i){let title=titles[i];let distance=title.getBoundingClientRect().top;console.log(i);if((minDistance===null||minDistance>=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');}
|
||||||
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"}}
|
console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"},"Gitlab":{"endpoint":"oauth2GitlabEndpoint","clientSecret":"oauth2GitlabClientSecret",},}
|
||||||
let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")}
|
let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")}
|
||||||
let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}}
|
let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}}
|
||||||
function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}}
|
function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}}
|
||||||
|
|
|
@ -25,7 +25,11 @@
|
||||||
"Auth0": {
|
"Auth0": {
|
||||||
"clientSecret": "oauth2Auth0ClientSecret",
|
"clientSecret": "oauth2Auth0ClientSecret",
|
||||||
"auth0Domain": "oauth2Auth0Domain"
|
"auth0Domain": "oauth2Auth0Domain"
|
||||||
}
|
},
|
||||||
|
"Gitlab": {
|
||||||
|
"endpoint": "oauth2GitlabEndpoint",
|
||||||
|
"clientSecret": "oauth2GitlabClientSecret",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
let provider = element.getAttribute("data-forms-oauth-custom");
|
let provider = element.getAttribute("data-forms-oauth-custom");
|
||||||
if (!provider || !providers.hasOwnProperty(provider)) { console.error("Provider for custom form not set or unknown") }
|
if (!provider || !providers.hasOwnProperty(provider)) { console.error("Provider for custom form not set or unknown") }
|
||||||
|
|
|
@ -39,7 +39,7 @@ class Gitlab extends OAuth2
|
||||||
*/
|
*/
|
||||||
public function getLoginURL(): string
|
public function getLoginURL(): string
|
||||||
{
|
{
|
||||||
return 'https://gitlab.com/oauth/authorize?' . \http_build_query([
|
return $this->getEndpoint() . '/oauth/authorize?' . \http_build_query([
|
||||||
'client_id' => $this->appID,
|
'client_id' => $this->appID,
|
||||||
'redirect_uri' => $this->callback,
|
'redirect_uri' => $this->callback,
|
||||||
'scope' => \implode(' ', $this->getScopes()),
|
'scope' => \implode(' ', $this->getScopes()),
|
||||||
|
@ -58,10 +58,10 @@ class Gitlab extends OAuth2
|
||||||
if (empty($this->tokens)) {
|
if (empty($this->tokens)) {
|
||||||
$this->tokens = \json_decode($this->request(
|
$this->tokens = \json_decode($this->request(
|
||||||
'POST',
|
'POST',
|
||||||
'https://gitlab.com/oauth/token?' . \http_build_query([
|
$this->getEndpoint() . '/oauth/token?' . \http_build_query([
|
||||||
'code' => $code,
|
'code' => $code,
|
||||||
'client_id' => $this->appID,
|
'client_id' => $this->appID,
|
||||||
'client_secret' => $this->appSecret,
|
'client_secret' => $this->getAppSecret()['clientSecret'],
|
||||||
'redirect_uri' => $this->callback,
|
'redirect_uri' => $this->callback,
|
||||||
'grant_type' => 'authorization_code'
|
'grant_type' => 'authorization_code'
|
||||||
])
|
])
|
||||||
|
@ -80,10 +80,10 @@ class Gitlab extends OAuth2
|
||||||
{
|
{
|
||||||
$this->tokens = \json_decode($this->request(
|
$this->tokens = \json_decode($this->request(
|
||||||
'POST',
|
'POST',
|
||||||
'https://gitlab.com/oauth/token?' . \http_build_query([
|
$this->getEndpoint() . '/oauth/token?' . \http_build_query([
|
||||||
'refresh_token' => $refreshToken,
|
'refresh_token' => $refreshToken,
|
||||||
'client_id' => $this->appID,
|
'client_id' => $this->appID,
|
||||||
'client_secret' => $this->appSecret,
|
'client_secret' => $this->getAppSecret()['clientSecret'],
|
||||||
'grant_type' => 'refresh_token'
|
'grant_type' => 'refresh_token'
|
||||||
])
|
])
|
||||||
), true);
|
), true);
|
||||||
|
@ -163,10 +163,39 @@ class Gitlab extends OAuth2
|
||||||
protected function getUser(string $accessToken): array
|
protected function getUser(string $accessToken): array
|
||||||
{
|
{
|
||||||
if (empty($this->user)) {
|
if (empty($this->user)) {
|
||||||
$user = $this->request('GET', 'https://gitlab.com/api/v4/user?access_token=' . \urlencode($accessToken));
|
$user = $this->request('GET', $this->getEndpoint() . '/api/v4/user?access_token=' . \urlencode($accessToken));
|
||||||
$this->user = \json_decode($user, true);
|
$this->user = \json_decode($user, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->user;
|
return $this->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the JSON stored in appSecret
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getAppSecret(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR);
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
throw new \Exception('Invalid secret');
|
||||||
|
}
|
||||||
|
return $secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getEndpoint(): string
|
||||||
|
{
|
||||||
|
$defaultEndpoint = 'https://gitlab.com';
|
||||||
|
$secret = $this->getAppSecret();
|
||||||
|
$endpoint = $secret['endpoint'] ?? $defaultEndpoint;
|
||||||
|
return empty($endpoint) ? $defaultEndpoint : $endpoint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2181,4 +2181,106 @@ trait DatabaseBase
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUpdatePermissionsWithEmptyPayload(): array
|
||||||
|
{
|
||||||
|
// Create collection
|
||||||
|
$movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'collectionId' => 'unique()',
|
||||||
|
'name' => 'Movies',
|
||||||
|
'read' => [],
|
||||||
|
'write' => [],
|
||||||
|
'permission' => 'document',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($movies['headers']['status-code'], 201);
|
||||||
|
$this->assertEquals($movies['body']['name'], 'Movies');
|
||||||
|
|
||||||
|
$moviesId = $movies['body']['$id'];
|
||||||
|
|
||||||
|
// create attribute
|
||||||
|
$title = $this->client->call(Client::METHOD_POST, '/database/collections/' . $moviesId . '/attributes/string', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'key' => 'title',
|
||||||
|
'size' => 256,
|
||||||
|
'required' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals($title['headers']['status-code'], 201);
|
||||||
|
|
||||||
|
// wait for database worker to create attributes
|
||||||
|
sleep(2);
|
||||||
|
|
||||||
|
// add document
|
||||||
|
$document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $moviesId . '/documents', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'documentId' => 'unique()',
|
||||||
|
'data' => [
|
||||||
|
'title' => 'Captain America',
|
||||||
|
],
|
||||||
|
'read' => ['role:all'],
|
||||||
|
'write' => ['role:all'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$id = $document['body']['$id'];
|
||||||
|
|
||||||
|
$this->assertEquals($document['headers']['status-code'], 201);
|
||||||
|
$this->assertCount(1, $document['body']['$read']);
|
||||||
|
$this->assertCount(1, $document['body']['$write']);
|
||||||
|
$this->assertEquals(['role:all'], $document['body']['$read']);
|
||||||
|
$this->assertEquals(['role:all'], $document['body']['$write']);
|
||||||
|
|
||||||
|
// Send only read permission
|
||||||
|
$document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $moviesId . '/documents/' . $id, array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'read' => ['user:' . $this->getUser()['$id']],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($this->getSide() == 'client') {
|
||||||
|
$this->assertEquals($document['headers']['status-code'], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getSide() == 'server') {
|
||||||
|
$this->assertEquals($document['headers']['status-code'], 200);
|
||||||
|
$this->assertCount(1, $document['body']['$read']);
|
||||||
|
$this->assertCount(1, $document['body']['$write']);
|
||||||
|
$this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$read']);
|
||||||
|
$this->assertEquals(['role:all'], $document['body']['$write']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send only write permission
|
||||||
|
$document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $moviesId . '/documents/' . $id, array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'write' => ['user:' . $this->getUser()['$id']],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($this->getSide() == 'server') {
|
||||||
|
$this->assertEquals($document['headers']['status-code'], 200);
|
||||||
|
$this->assertCount(1, $document['body']['$read']);
|
||||||
|
$this->assertCount(1, $document['body']['$write']);
|
||||||
|
$this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$read']);
|
||||||
|
$this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$write']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove collection
|
||||||
|
$this->client->call(Client::METHOD_DELETE, '/database/collections/' . $moviesId, array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
], $this->getHeaders()));
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue