Merge branch 'feat-functions-refactor' into fix-rename-sum
This commit is contained in:
commit
0dec387f85
7
.env
7
.env
|
@ -1,6 +1,7 @@
|
|||
_APP_ENV=production
|
||||
_APP_ENV=development
|
||||
_APP_LOCALE=en
|
||||
_APP_WORKER_PER_CORE=6
|
||||
_APP_CONSOLE_WHITELIST_ROOT=disabled
|
||||
_APP_CONSOLE_WHITELIST_EMAILS=
|
||||
_APP_CONSOLE_WHITELIST_IPS=
|
||||
|
@ -38,9 +39,9 @@ _APP_FUNCTIONS_SIZE_LIMIT=30000000
|
|||
_APP_FUNCTIONS_TIMEOUT=900
|
||||
_APP_FUNCTIONS_BUILD_TIMEOUT=900
|
||||
_APP_FUNCTIONS_CONTAINERS=10
|
||||
_APP_FUNCTIONS_CPUS=4
|
||||
_APP_FUNCTIONS_MEMORY=2000
|
||||
_APP_FUNCTIONS_MEMORY_SWAP=2000
|
||||
_APP_FUNCTIONS_CPUS=0
|
||||
_APP_FUNCTIONS_MEMORY=0
|
||||
_APP_FUNCTIONS_MEMORY_SWAP=0
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
|
||||
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
|
||||
_APP_EXECUTOR_SECRET=a-random-secret
|
||||
|
|
5
.github/workflows/tests.yml
vendored
5
.github/workflows/tests.yml
vendored
|
@ -41,5 +41,6 @@ jobs:
|
|||
- name: Teardown
|
||||
if: always()
|
||||
run: |
|
||||
docker compose down -v
|
||||
docker ps -aq | xargs docker rm --force
|
||||
docker ps -aq | xargs docker rm --force
|
||||
docker volume prune --force
|
||||
docker network prune --force
|
|
@ -134,6 +134,7 @@ ENV DEBUG=$DEBUG
|
|||
ENV _APP_SERVER=swoole \
|
||||
_APP_ENV=production \
|
||||
_APP_LOCALE=en \
|
||||
_APP_WORKER_PER_CORE= \
|
||||
_APP_DOMAIN=localhost \
|
||||
_APP_DOMAIN_TARGET=localhost \
|
||||
_APP_HOME=https://appwrite.io \
|
||||
|
|
|
@ -291,6 +291,28 @@ return [
|
|||
'description' => 'Function with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::FUNCTION_RUNTIME_UNSUPPORTED => [
|
||||
'name' => Exception::FUNCTION_RUNTIME_UNSUPPORTED,
|
||||
'description' => 'The requested runtime is either inactive or unsupported. Please check the value of the _APP_FUNCTIONS_RUNTIMES environment variable.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Builds */
|
||||
Exception::BUILD_NOT_FOUND => [
|
||||
'name' => Exception::BUILD_NOT_FOUND,
|
||||
'description' => 'Build with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::BUILD_NOT_READY => [
|
||||
'name' => Exception::BUILD_NOT_READY,
|
||||
'description' => 'Build with the requested ID is builing and not ready for execution.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::BUILD_IN_PROGRESS => [
|
||||
'name' => Exception::BUILD_IN_PROGRESS,
|
||||
'description' => 'Build with the requested ID is already in progress. Please wait before you can retry.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Deployments */
|
||||
Exception::DEPLOYMENT_NOT_FOUND => [
|
||||
|
|
|
@ -176,6 +176,15 @@ return [
|
|||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_WORKER_PER_CORE',
|
||||
'description' => 'Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance.',
|
||||
'introduction' => '0.13.0',
|
||||
'default' => 6,
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
]
|
||||
],
|
||||
],
|
||||
|
|
|
@ -1716,7 +1716,7 @@ App::patch('/v1/account/sessions/:sessionId')
|
|||
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
|
||||
|
||||
if (!\class_exists($className)) {
|
||||
throw new Exception('Provider is not supported', 501);
|
||||
throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$oauth2 = new $className($appId, $appSecret, '', [], []);
|
||||
|
@ -1753,7 +1753,7 @@ App::patch('/v1/account/sessions/:sessionId')
|
|||
}
|
||||
}
|
||||
|
||||
throw new Exception('Session not found', 404);
|
||||
throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/sessions')
|
||||
|
|
|
@ -111,7 +111,7 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
|
|||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('collections', $collectionId);
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collectionId);
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
|
||||
|
||||
// Pass clone of $attribute object to workers
|
||||
// so we can later modify Document to fit response model
|
||||
|
@ -166,7 +166,7 @@ App::post('/v1/database/collections')
|
|||
$collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId;
|
||||
|
||||
try {
|
||||
$collection = $dbForProject->createDocument('collections', new Document([
|
||||
$dbForProject->createDocument('collections', new Document([
|
||||
'$id' => $collectionId,
|
||||
'$read' => $read ?? [], // Collection permissions for collection documents (based on permission model)
|
||||
'$write' => $write ?? [], // Collection permissions for collection documents (based on permission model)
|
||||
|
@ -177,8 +177,9 @@ App::post('/v1/database/collections')
|
|||
'name' => $name,
|
||||
'search' => implode(' ', [$collectionId, $name]),
|
||||
]));
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
$dbForProject->createCollection('collection_' . $collectionId);
|
||||
$dbForProject->createCollection('collection_' . $collection->getInternalId());
|
||||
} catch (DuplicateException $th) {
|
||||
throw new Exception('Collection already exists', 409, Exception::COLLECTION_ALREADY_EXISTS);
|
||||
} catch (LimitException $th) {
|
||||
|
@ -402,7 +403,8 @@ App::get('/v1/database/:collectionId/usage')
|
|||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
|
||||
$collection = $dbForProject->getCollection('collection_' . $collectionId);
|
||||
$collectionDocument = $dbForProject->getDocument('collections', $collectionId);
|
||||
$collection = $dbForProject->getCollection('collection_' . $collectionDocument->getInternalId());
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
|
@ -513,7 +515,8 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
|
||||
$collection = $dbForProject->getCollection('collection_' . $collectionId);
|
||||
$collectionDocument = $dbForProject->getDocument('collections', $collectionId);
|
||||
$collection = $dbForProject->getCollection('collection_' . $collectionDocument->getInternalId());
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
|
@ -676,7 +679,7 @@ App::delete('/v1/database/collections/:collectionId')
|
|||
throw new Exception('Failed to remove collection from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collectionId);
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
|
||||
|
||||
$deletes
|
||||
->setParam('type', DELETE_TYPE_DOCUMENT)
|
||||
|
@ -1259,7 +1262,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
|
|||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('collections', $collectionId);
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collectionId);
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
|
||||
|
||||
$database
|
||||
->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE)
|
||||
|
@ -1642,9 +1645,9 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
try {
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->createDocument('collection_' . $collectionId, new Document($data)));
|
||||
$document = Authorization::skip(fn() => $dbForProject->createDocument('collection_' . $collection->getInternalId(), new Document($data)));
|
||||
} else {
|
||||
$document = $dbForProject->createDocument('collection_' . $collectionId, new Document($data));
|
||||
$document = $dbForProject->createDocument('collection_' . $collection->getInternalId(), new Document($data));
|
||||
}
|
||||
$document->setAttribute('$collection', $collectionId);
|
||||
}
|
||||
|
@ -1742,8 +1745,8 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
$cursorDocument = null;
|
||||
if (!empty($cursor)) {
|
||||
$cursorDocument = $collection->getAttribute('permission') === 'collection'
|
||||
? Authorization::skip(fn () => $dbForProject->getDocument('collection_' . $collectionId, $cursor))
|
||||
: $dbForProject->getDocument('collection_' . $collectionId, $cursor);
|
||||
? Authorization::skip(fn () => $dbForProject->getDocument('collection_' . $collection->getInternalId(), $cursor))
|
||||
: $dbForProject->getDocument('collection_' . $collection->getInternalId(), $cursor);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
|
@ -1752,11 +1755,11 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document[] $documents */
|
||||
$documents = Authorization::skip(fn() => $dbForProject->find('collection_' . $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection));
|
||||
$total = Authorization::skip(fn() => $dbForProject->count('collection_' . $collectionId, $queries, APP_LIMIT_COUNT));
|
||||
$documents = Authorization::skip(fn() => $dbForProject->find('collection_' . $collection->getInternalId(), $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection));
|
||||
$total = Authorization::skip(fn() => $dbForProject->count('collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT));
|
||||
} else {
|
||||
$documents = $dbForProject->find('collection_' . $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
|
||||
$total = $dbForProject->count('collection_' . $collectionId, $queries, APP_LIMIT_COUNT);
|
||||
$documents = $dbForProject->find('collection_' . $collection->getInternalId(), $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
|
||||
$total = $dbForProject->count('collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1818,9 +1821,9 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('collection_' . $collectionId, $documentId));
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$document = $dbForProject->getDocument('collection_' . $collectionId, $documentId);
|
||||
$document = $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1872,7 +1875,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
|
|||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$document = $dbForProject->getDocument('collection_' . $collectionId, $documentId);
|
||||
$document = $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND);
|
||||
|
@ -1981,9 +1984,9 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('collection_' . $collectionId, $documentId));
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$document = $dbForProject->getDocument('collection_' . $collectionId, $documentId);
|
||||
$document = $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2031,9 +2034,9 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
try {
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->updateDocument('collection_' . $collection->getId(), $document->getId(), new Document($data)));
|
||||
$document = Authorization::skip(fn() => $dbForProject->updateDocument('collection_' . $collection->getInternalId(), $document->getId(), new Document($data)));
|
||||
} else {
|
||||
$document = $dbForProject->updateDocument('collection_' . $collection->getId(), $document->getId(), new Document($data));
|
||||
$document = $dbForProject->updateDocument('collection_' . $collection->getInternalId(), $document->getId(), new Document($data));
|
||||
}
|
||||
/**
|
||||
* Reset $collection attribute to remove prefix.
|
||||
|
@ -2116,9 +2119,9 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('collection_' . $collectionId, $documentId));
|
||||
$document = Authorization::skip(fn() => $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$document = $dbForProject->getDocument('collection_' . $collectionId, $documentId);
|
||||
$document = $dbForProject->getDocument('collection_' . $collection->getInternalId(), $documentId);
|
||||
}
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
|
@ -2126,12 +2129,12 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
Authorization::skip(fn() => $dbForProject->deleteDocument('collection_' . $collectionId, $documentId));
|
||||
Authorization::skip(fn() => $dbForProject->deleteDocument('collection_' . $collection->getInternalId(), $documentId));
|
||||
} else {
|
||||
$dbForProject->deleteDocument('collection_' . $collectionId, $documentId);
|
||||
$dbForProject->deleteDocument('collection_' . $collection->getInternalId(), $documentId);
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('collection_' . $collectionId, $documentId);
|
||||
$dbForProject->deleteCachedDocument('collection_' . $collection->getInternalId(), $documentId);
|
||||
|
||||
/**
|
||||
* Reset $collection attribute to remove prefix.
|
||||
|
|
|
@ -106,7 +106,7 @@ App::get('/v1/functions')
|
|||
$cursorFunction = $dbForProject->getDocument('functions', $cursor);
|
||||
|
||||
if ($cursorFunction->isEmpty()) {
|
||||
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ App::get('/v1/functions/:functionId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($function, Response::MODEL_FUNCTION);
|
||||
|
@ -200,7 +200,7 @@ App::get('/v1/functions/:functionId/usage')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$usage = [];
|
||||
|
@ -311,7 +311,7 @@ App::put('/v1/functions/:functionId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$original = $function->getAttribute('schedule', '');
|
||||
|
@ -371,7 +371,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($deployment->isEmpty()) {
|
||||
|
@ -379,11 +379,11 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
}
|
||||
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found', 404);
|
||||
throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($build->getAttribute('status') !== 'ready') {
|
||||
throw new Exception('Build not ready', 400);
|
||||
throw new Exception('Build not ready', 400, Exception::BUILD_NOT_READY);
|
||||
}
|
||||
|
||||
$schedule = $function->getAttribute('schedule', '');
|
||||
|
@ -430,11 +430,11 @@ App::delete('/v1/functions/:functionId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteDocument('functions', $function->getId())) {
|
||||
throw new Exception('Failed to remove function from DB', 500);
|
||||
throw new Exception('Failed to remove function from DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$deletes
|
||||
|
@ -484,7 +484,7 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = $request->getFiles('code');
|
||||
|
@ -493,7 +493,7 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
$upload = new Upload();
|
||||
|
||||
if (empty($file)) {
|
||||
throw new Exception('No file sent', 400);
|
||||
throw new Exception('No file sent', 400, Exception::STORAGE_FILE_EMPTY);
|
||||
}
|
||||
|
||||
// Make sure we handle a single file and multiple files the same way
|
||||
|
@ -502,7 +502,7 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
|
||||
throw new Exception('File type not allowed', 400);
|
||||
throw new Exception('File type not allowed', 400, Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$contentRange = $request->getHeader('content-range');
|
||||
|
@ -516,7 +516,7 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
$fileSize = $request->getContentRangeSize();
|
||||
$deploymentId = $request->getHeader('x-appwrite-id', $deploymentId);
|
||||
if(is_null($start) || is_null($end) || is_null($fileSize)) {
|
||||
throw new Exception('Invalid content-range header', 400);
|
||||
throw new Exception('Invalid content-range header', 400, Exception::STORAGE_INVALID_CONTENT_RANGE);
|
||||
}
|
||||
|
||||
if ($end === $fileSize) {
|
||||
|
@ -530,11 +530,11 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
}
|
||||
|
||||
if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
|
||||
throw new Exception('File size not allowed', 400);
|
||||
throw new Exception('File size not allowed', 400, Exception::STORAGE_INVALID_FILE_SIZE);
|
||||
}
|
||||
|
||||
if (!$upload->isValid($fileTmpName)) {
|
||||
throw new Exception('Invalid file', 403);
|
||||
throw new Exception('Invalid file', 403, Exception::STORAGE_INVALID_FILE);
|
||||
}
|
||||
|
||||
// Save to storage
|
||||
|
@ -555,7 +555,7 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
$chunksUploaded = $deviceFunctions->upload($fileTmpName, $path, $chunk, $chunks, $metadata);
|
||||
|
||||
if (empty($chunksUploaded)) {
|
||||
throw new Exception('Failed moving file', 500);
|
||||
throw new Exception('Failed moving file', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if($chunksUploaded === $chunks) {
|
||||
|
@ -664,7 +664,7 @@ App::get('/v1/functions/:functionId/deployments')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
|
@ -723,7 +723,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
@ -766,7 +766,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
@ -832,7 +832,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$runtimes = Config::getParam('runtimes', []);
|
||||
|
@ -840,7 +840,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
|
||||
|
||||
if (\is_null($runtime)) {
|
||||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400);
|
||||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400, Exception::FUNCTION_RUNTIME_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$deployment = Authorization::skip(fn() => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
|
||||
|
@ -856,17 +856,17 @@ App::post('/v1/functions/:functionId/executions')
|
|||
/** Check if build has completed */
|
||||
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found', 404);
|
||||
throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($build->getAttribute('status') !== 'ready') {
|
||||
throw new Exception('Build not ready', 400);
|
||||
throw new Exception('Build not ready', 400, Exception::BUILD_NOT_READY);
|
||||
}
|
||||
|
||||
$validator = new Authorization('execute');
|
||||
|
||||
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
|
||||
throw new Exception($validator->getDescription(), 401);
|
||||
throw new Exception($validator->getDescription(), 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$executionId = $dbForProject->getId();
|
||||
|
@ -1000,14 +1000,14 @@ App::get('/v1/functions/:functionId/executions')
|
|||
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorExecution = $dbForProject->getDocument('executions', $cursor);
|
||||
|
||||
if ($cursorExecution->isEmpty()) {
|
||||
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400);
|
||||
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1050,17 +1050,17 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
|||
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$execution = $dbForProject->getDocument('executions', $executionId);
|
||||
|
||||
if ($execution->getAttribute('functionId') !== $function->getId()) {
|
||||
throw new Exception('Execution not found', 404);
|
||||
throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($execution->isEmpty()) {
|
||||
throw new Exception('Execution not found', 404);
|
||||
throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($execution, Response::MODEL_EXECUTION);
|
||||
|
@ -1092,21 +1092,21 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
|||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception('Function not found', 404);
|
||||
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($deployment->isEmpty()) {
|
||||
throw new Exception('Deployment not found', 404);
|
||||
throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $buildId));
|
||||
|
||||
if ($build->isEmpty()) {
|
||||
throw new Exception('Build not found', 404);
|
||||
throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($build->getAttribute('status') !== 'failed') {
|
||||
throw new Exception('Build not failed', 400);
|
||||
throw new Exception('Build not failed', 400, Exception::BUILD_IN_PROGRESS);
|
||||
}
|
||||
|
||||
// Enqueue a message to start the build
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\App;
|
||||
|
||||
/**
|
||||
|
@ -18,6 +19,6 @@ App::post('/v1/graphql')
|
|||
->label('scope', 'public')
|
||||
->action(
|
||||
function () {
|
||||
throw new Exception('GraphQL support is coming soon!', 502);
|
||||
throw new Exception('GraphQL support is coming soon!', 502, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -101,10 +101,10 @@ App::post('/v1/projects')
|
|||
'auths' => $auths,
|
||||
'search' => implode(' ', [$projectId, $name]),
|
||||
]));
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
|
||||
$collections = Config::getParam('collections', []); /** @var array $collections */
|
||||
|
||||
$dbForProject->setNamespace('_project_' . $project->getId());
|
||||
$dbForProject->setNamespace("_{$project->getId()}");
|
||||
$dbForProject->create('appwrite');
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
|
@ -269,7 +269,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
],
|
||||
];
|
||||
|
||||
$dbForProject->setNamespace('_project_' . $projectId);
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metrics = [
|
||||
'requests',
|
||||
|
|
|
@ -160,13 +160,14 @@ App::post('/v1/runtimes')
|
|||
$stderr = '';
|
||||
$startTime = \time();
|
||||
$endTime = 0;
|
||||
$orchestration = $orchestrationPool->get();
|
||||
|
||||
try {
|
||||
Console::info('Building container : ' . $runtimeId);
|
||||
|
||||
/**
|
||||
* Temporary file paths in the executor
|
||||
*/
|
||||
|
||||
$tmpSource = "/tmp/$runtimeId/src/code.tar.gz";
|
||||
$tmpBuild = "/tmp/$runtimeId/builds/code.tar.gz";
|
||||
|
||||
|
@ -192,7 +193,6 @@ App::post('/v1/runtimes')
|
|||
/**
|
||||
* Create container
|
||||
*/
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$secret = \bin2hex(\random_bytes(16));
|
||||
$vars = \array_merge($vars, [
|
||||
'INTERNAL_RUNTIME_KEY' => $secret,
|
||||
|
@ -590,12 +590,12 @@ $http->on('start', function ($http) {
|
|||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
Console::info('Warming up ' . $runtime['name'] . ' ' . $runtime['version'] . ' environment...');
|
||||
// $response = $orchestration->pull($runtime['image']);
|
||||
// if ($response) {
|
||||
// Console::success("Successfully Warmed up {$runtime['name']} {$runtime['version']}!");
|
||||
// } else {
|
||||
// Console::warning("Failed to Warmup {$runtime['name']} {$runtime['version']}!");
|
||||
// }
|
||||
$response = $orchestration->pull($runtime['image']);
|
||||
if ($response) {
|
||||
Console::success("Successfully Warmed up {$runtime['name']} {$runtime['version']}!");
|
||||
} else {
|
||||
Console::warning("Failed to Warmup {$runtime['name']} {$runtime['version']}!");
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
} finally {
|
||||
$orchestrationPool->put($orchestration);
|
||||
|
|
|
@ -23,9 +23,11 @@ use Utopia\Logger\Log\User;
|
|||
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
|
||||
|
||||
$payloadSize = 6 * (1024 * 1024); // 6MB
|
||||
$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
|
||||
$http
|
||||
->set([
|
||||
'worker_num' => $workerNumber,
|
||||
'open_http2_protocol' => true,
|
||||
// 'document_root' => __DIR__.'/../public',
|
||||
// 'enable_static_handler' => true,
|
||||
|
@ -164,6 +166,8 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
'$write' => ['role:all'],
|
||||
'search' => 'buckets Default',
|
||||
]));
|
||||
|
||||
$bucket = $dbForConsole->getDocument('buckets', 'default');
|
||||
|
||||
Console::success('[Setup] - Creating files collection for default bucket...');
|
||||
$files = $collections['files'] ?? [];
|
||||
|
@ -196,7 +200,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
]);
|
||||
}
|
||||
|
||||
$dbForConsole->createCollection('bucket_' . 'default', $attributes, $indexes);
|
||||
$dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
||||
}
|
||||
|
||||
Console::success('[Setup] - Server database init completed...');
|
||||
|
|
|
@ -786,7 +786,7 @@ App::setResource('dbForProject', function($db, $cache, $project) {
|
|||
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace('_project_'.$project->getId());
|
||||
$database->setNamespace("_{$project->getId()}");
|
||||
|
||||
return $database;
|
||||
}, ['db', 'cache', 'project']);
|
||||
|
@ -796,7 +796,7 @@ App::setResource('dbForConsole', function($db, $cache) {
|
|||
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace('_project_console');
|
||||
$database->setNamespace('_console');
|
||||
|
||||
return $database;
|
||||
}, ['db', 'cache']);
|
||||
|
|
|
@ -46,9 +46,12 @@ $stats->create();
|
|||
|
||||
$containerId = uniqid();
|
||||
$statsDocument = null;
|
||||
$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
|
||||
$adapter = new Adapter\Swoole(port: App::getEnv('PORT', 80));
|
||||
$adapter->setPackageMaxLength(64000); // Default maximum Package Size (64kb)
|
||||
$adapter
|
||||
->setPackageMaxLength(64000) // Default maximum Package Size (64kb)
|
||||
->setWorkerNumber($workerNumber);
|
||||
|
||||
$server = new Server($adapter);
|
||||
|
||||
|
@ -137,7 +140,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
*/
|
||||
go(function () use ($register, $containerId, &$statsDocument, $logError) {
|
||||
try {
|
||||
[$database, $returnDatabase] = getDatabase($register, '_project_console');
|
||||
[$database, $returnDatabase] = getDatabase($register, '_console');
|
||||
$document = new Document([
|
||||
'$id' => $database->getId(),
|
||||
'$collection' => 'realtime',
|
||||
|
@ -190,7 +193,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
}
|
||||
|
||||
try {
|
||||
[$database, $returnDatabase] = getDatabase($register, '_project_console');
|
||||
[$database, $returnDatabase] = getDatabase($register, '_console');
|
||||
|
||||
$statsDocument
|
||||
->setAttribute('timestamp', time())
|
||||
|
@ -217,7 +220,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
*/
|
||||
if ($realtime->hasSubscriber('console', 'role:member', 'project')) {
|
||||
|
||||
[$database, $returnDatabase] = getDatabase($register, '_project_console');
|
||||
[$database, $returnDatabase] = getDatabase($register, '_console');
|
||||
|
||||
$payload = [];
|
||||
|
||||
|
@ -321,7 +324,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
return;
|
||||
}
|
||||
|
||||
[$database, $returnDatabase] = getDatabase($register, '_project_' . $projectId);
|
||||
[$database, $returnDatabase] = getDatabase($register, "_{$projectId}");
|
||||
|
||||
$user = $database->getDocument('users', $userId);
|
||||
|
||||
|
@ -397,7 +400,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
$cache = new Cache(new RedisCache($redis));
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace('_project_' . $project->getId());
|
||||
$database->setNamespace("_{$project->getId()}");
|
||||
|
||||
/*
|
||||
* Project Check
|
||||
|
@ -504,7 +507,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
|||
$cache = new Cache(new RedisCache($redis));
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace('_project_' . $realtime->connections[$connection]['projectId']);
|
||||
$database->setNamespace("_{$realtime->connections[$connection]['projectId']}");
|
||||
|
||||
/*
|
||||
* Abuse Check
|
||||
|
|
|
@ -46,7 +46,13 @@ $cli
|
|||
$offset = 0;
|
||||
$projects = [$console];
|
||||
$count = 0;
|
||||
$totalProjects = $consoleDB->count('projects') + 1;
|
||||
|
||||
try {
|
||||
$totalProjects = $consoleDB->count('projects') + 1;
|
||||
} catch (\Throwable $th) {
|
||||
$consoleDB->setNamespace('_console');
|
||||
$totalProjects = $consoleDB->count('projects') + 1;
|
||||
}
|
||||
|
||||
$class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version];
|
||||
$migration = new $class();
|
||||
|
|
|
@ -259,7 +259,7 @@ $cli
|
|||
$dbForConsole = new Database(new MariaDB($db), $cacheAdapter);
|
||||
$dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$dbForConsole->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$dbForConsole->setNamespace('_project_console');
|
||||
$dbForConsole->setNamespace('_console');
|
||||
|
||||
$latestTime = [];
|
||||
|
||||
|
@ -325,7 +325,7 @@ $cli
|
|||
$projectId = $point['projectId'];
|
||||
|
||||
if (!empty($projectId) && $projectId !== 'console') {
|
||||
$dbForProject->setNamespace('_project_' . $projectId);
|
||||
$dbForProject->setNamespace('_' . $projectId);
|
||||
$metricUpdated = $metric;
|
||||
|
||||
if (!empty($groupBy)) {
|
||||
|
@ -417,8 +417,8 @@ $cli
|
|||
$projectId = $project->getId();
|
||||
|
||||
// Get total storage
|
||||
$dbForProject->setNamespace('_project_' . $projectId);
|
||||
$storageTotal = (int) $dbForProject->sum('deployments', 'size');
|
||||
$dbForProject->setNamespace('_' . $projectId);
|
||||
$storageTotal = $dbForProject->sum('tags', 'size');
|
||||
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_storage.deployments.total'); //Construct unique id for each metric using time, period and metric
|
||||
|
@ -497,7 +497,7 @@ $cli
|
|||
|
||||
foreach ($collections as $collection => $options) {
|
||||
try {
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$count = $dbForProject->count($collection);
|
||||
$metricPrefix = $options['metricPrefix'] ?? '';
|
||||
$metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count";
|
||||
|
@ -553,7 +553,7 @@ $cli
|
|||
$subCollectionTotals = []; //total project level sum of sub collections
|
||||
|
||||
do { // Loop over all the parent collection document for each sub collection
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$parents = $dbForProject->find($collection, [], 100, cursor: $latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||
|
||||
if (empty($parents)) {
|
||||
|
@ -564,12 +564,12 @@ $cli
|
|||
|
||||
foreach ($parents as $parent) {
|
||||
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$count = $dbForProject->count(($subOptions['collectionPrefix'] ?? '') . $parent->getId());
|
||||
|
||||
$subCollectionCounts[$subCollection] = ($subCollectionCounts[$subCollection] ?? 0) + $count; // Project level counts for sub collections like database.documents.count
|
||||
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.count" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.count";
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
|
@ -618,12 +618,12 @@ $cli
|
|||
continue;
|
||||
}
|
||||
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$total = (int) $dbForProject->sum(($subOptions['collectionPrefix'] ?? '') . $parent->getId(), $total['field']);
|
||||
|
||||
$subCollectionTotals[$subCollection] = ($ssubCollectionTotals[$subCollection] ?? 0) + $total; // Project level sum for sub collections like storage.total
|
||||
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$collection}.{$parent->getId()}.{$subCollection}.total" : "{$metricPrefix}.{$collection}.{$parent->getId()}.{$subCollection}.total";
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
|
@ -668,7 +668,7 @@ $cli
|
|||
* Inserting project level counts for sub collections like database.documents.count
|
||||
*/
|
||||
foreach ($subCollectionCounts as $subCollection => $count) {
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count";
|
||||
|
||||
|
@ -717,7 +717,7 @@ $cli
|
|||
* Inserting project level sums for sub collections like storage.total
|
||||
*/
|
||||
foreach ($subCollectionTotals as $subCollection => $count) {
|
||||
$dbForProject->setNamespace("_project_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
|
||||
$metric = empty($metricPrefix) ? "{$subCollection}.total" : "{$metricPrefix}.{$subCollection}.total";
|
||||
|
||||
|
@ -769,4 +769,4 @@ $cli
|
|||
|
||||
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||
}, $interval);
|
||||
});
|
||||
});
|
|
@ -179,7 +179,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
</div>
|
||||
|
||||
<div class="pull-start">
|
||||
<button data-ls-ui-trigger="deploy-deployment">Create Deployment</button>
|
||||
<button data-ls-ui-trigger="create-deployment">Create Deployment</button>
|
||||
</div>
|
||||
|
||||
<div class="pull-end paging">
|
||||
|
@ -631,7 +631,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
<button type="submit" style="vertical-align: top;">Execute Now</button>
|
||||
</form>
|
||||
</div>
|
||||
<div data-ui-modal class="modal close box sticky-footer" data-button-hide="on" data-open-event="deploy-deployment">
|
||||
<div data-ui-modal class="modal close box sticky-footer" data-button-hide="on" data-open-event="create-deployment">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1 class="margin-bottom-xl">Create a New Deployment</h1>
|
||||
|
|
|
@ -439,11 +439,11 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
|
|||
<div data-ls-if="{{project-bucket.permission}} == 'bucket'">
|
||||
|
||||
<label for="bucket-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
|
||||
<input type="hidden" id="bucket-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$permissions.read}}" placeholder="User ID, Team ID or Role" />
|
||||
<input type="hidden" id="bucket-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$read}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
|
||||
<label for="bucket-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
|
||||
<input type="hidden" id="bucket-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$permissions.write}}" placeholder="User ID, Team ID or Role" />
|
||||
<input type="hidden" id="bucket-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-bucket.$write}}" placeholder="User ID, Team ID or Role" />
|
||||
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -66,6 +66,7 @@ services:
|
|||
- influxdb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_LOCALE
|
||||
- _APP_CONSOLE_WHITELIST_ROOT
|
||||
- _APP_CONSOLE_WHITELIST_EMAILS
|
||||
|
@ -145,6 +146,7 @@ services:
|
|||
- redis
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPTIONS_ABUSE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_REDIS_HOST
|
||||
|
@ -155,6 +157,8 @@ services:
|
|||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
|
||||
appwrite-executor:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Resque\Worker;
|
||||
use Cron\CronExpression;
|
||||
use Executor\Executor;
|
||||
|
@ -58,6 +59,8 @@ class BuildsV1 extends Worker
|
|||
protected function buildDeployment(string $projectId, string $functionId, string $deploymentId)
|
||||
{
|
||||
$dbForProject = $this->getProjectDB($projectId);
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
if ($function->isEmpty()) {
|
||||
|
@ -107,6 +110,16 @@ class BuildsV1 extends Worker
|
|||
$build->setAttribute('status', 'building');
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
/** Send realtime event */
|
||||
$target = Realtime::fromPayload('functions.deployments.update', $build, $project);
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $build->getArrayCopy(),
|
||||
event: 'functions.deployments.update',
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
|
||||
$source = $deployment->getAttribute('path');
|
||||
$vars = $function->getAttribute('vars', []);
|
||||
$baseImage = $runtime['image'];
|
||||
|
@ -162,6 +175,18 @@ class BuildsV1 extends Worker
|
|||
Console::error($th->getMessage());
|
||||
} finally {
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
/**
|
||||
* Send realtime Event
|
||||
*/
|
||||
$target = Realtime::fromPayload('functions.deployments.update', $build, $project);
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $build->getArrayCopy(),
|
||||
event: 'functions.deployments.update',
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ class DatabaseV1 extends Worker
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if(!$dbForProject->createAttribute('collection_' . $collectionId, $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
|
||||
if(!$dbForProject->createAttribute('collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
|
||||
throw new Exception('Failed to create Attribute');
|
||||
}
|
||||
$dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available'));
|
||||
|
@ -135,7 +135,7 @@ class DatabaseV1 extends Worker
|
|||
// - failed: attribute was never created
|
||||
// - stuck: attribute was available but cannot be removed
|
||||
try {
|
||||
if($status !== 'failed' && !$dbForProject->deleteAttribute('collection_' . $collectionId, $key)) {
|
||||
if($status !== 'failed' && !$dbForProject->deleteAttribute('collection_' . $collection->getInternalId(), $key)) {
|
||||
throw new Exception('Failed to delete Attribute');
|
||||
}
|
||||
$dbForProject->deleteDocument('attributes', $attribute->getId());
|
||||
|
@ -210,7 +210,7 @@ class DatabaseV1 extends Worker
|
|||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('collections', $collectionId);
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collectionId);
|
||||
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -233,7 +233,7 @@ class DatabaseV1 extends Worker
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if(!$dbForProject->createIndex('collection_' . $collectionId, $key, $type, $attributes, $lengths, $orders)) {
|
||||
if(!$dbForProject->createIndex('collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) {
|
||||
throw new Exception('Failed to create Index');
|
||||
}
|
||||
$dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available'));
|
||||
|
@ -276,7 +276,7 @@ class DatabaseV1 extends Worker
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if($status !== 'failed' && !$dbForProject->deleteIndex('collection_' . $collectionId, $key)) {
|
||||
if($status !== 'failed' && !$dbForProject->deleteIndex('collection_' . $collection->getInternalId(), $key)) {
|
||||
throw new Exception('Failed to delete index');
|
||||
}
|
||||
$dbForProject->deleteDocument('indexes', $index->getId());
|
||||
|
|
|
@ -548,18 +548,18 @@ class DeletesV1 extends Worker
|
|||
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_DEVICE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_DEVICE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_DEVICE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_DEVICE_S3_BUCKET', '');
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
$s3SecretKey = App::getEnv('_APP_STORAGE_S3_SECRET', '');
|
||||
$s3Region = App::getEnv('_APP_STORAGE_S3_REGION', '');
|
||||
$s3Bucket = App::getEnv('_APP_STORAGE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
$device = new S3(APP_STORAGE_UPLOADS . '/app-' . $projectId, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
break;
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DEVICE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAccessKey = App::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = App::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
|
||||
$doSpacesRegion = App::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
|
||||
$doSpacesBucket = App::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
|
||||
$doSpacesAcl = 'private';
|
||||
$device = new DOSpaces(APP_STORAGE_UPLOADS . '/app-' . $projectId, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
|
||||
break;
|
||||
|
|
|
@ -328,6 +328,13 @@ class FunctionsV1 extends Worker
|
|||
|
||||
/** Trigger realtime event */
|
||||
$target = Realtime::fromPayload('functions.executions.update', $execution);
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $execution->getArrayCopy(),
|
||||
event: 'functions.executions.update',
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
Realtime::send(
|
||||
projectId: $projectId,
|
||||
payload: $execution->getArrayCopy(),
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
"utopia-php/cache": "0.4.*",
|
||||
"utopia-php/cli": "0.12.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.14.*",
|
||||
"utopia-php/database": "0.15.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
|
|
23
composer.lock
generated
23
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "deeea4a225c55fccc04729750dd8c5ec",
|
||||
"content-hash": "318c53aaac3cdfbdb728acc5e59b8057",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -2129,16 +2129,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.14.1",
|
||||
"version": "0.15.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "ecc143f2cfe16b23675407035c6b5375ba263285"
|
||||
"reference": "eb4f61ec40d697acdfd574638ecd075e4f44b864"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/ecc143f2cfe16b23675407035c6b5375ba263285",
|
||||
"reference": "ecc143f2cfe16b23675407035c6b5375ba263285",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb4f61ec40d697acdfd574638ecd075e4f44b864",
|
||||
"reference": "eb4f61ec40d697acdfd574638ecd075e4f44b864",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2186,9 +2186,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.14.1"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.15.1"
|
||||
},
|
||||
"time": "2022-01-25T13:01:20+00:00"
|
||||
"time": "2022-02-22T09:33:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
@ -3075,7 +3075,7 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator",
|
||||
"reference": "36e459dfd97a5693746e733ca9947a02ce12b0dc"
|
||||
"reference": "4a43aa70c2f0b243edfc689f618a85f7d0817287"
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
|
@ -3110,7 +3110,7 @@
|
|||
}
|
||||
],
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"time": "2022-02-27T08:06:11+00:00"
|
||||
"time": "2022-02-22T10:51:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
|
@ -3685,6 +3685,9 @@
|
|||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"myclabs/deep-copy": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/common": "^2.6",
|
||||
|
@ -6570,5 +6573,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.2.0"
|
||||
"plugin-api-version": "2.1.0"
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ services:
|
|||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_LOCALE
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_CONSOLE_WHITELIST_ROOT
|
||||
- _APP_CONSOLE_WHITELIST_EMAILS
|
||||
- _APP_CONSOLE_WHITELIST_IPS
|
||||
|
@ -174,6 +175,7 @@ services:
|
|||
- redis
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPTIONS_ABUSE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_REDIS_HOST
|
||||
|
|
12
public/dist/scripts/app-all.js
vendored
12
public/dist/scripts/app-all.js
vendored
|
@ -3541,7 +3541,8 @@ handler2.inline=(el,{expression},{cleanup:cleanup2})=>{let root=closestRoot(el);
|
|||
root._x_refs={};root._x_refs[expression]=el;cleanup2(()=>delete root._x_refs[expression]);};directive("ref",handler2);directive("if",(el,{expression},{effect:effect3,cleanup:cleanup2})=>{let evaluate2=evaluateLater(el,expression);let show=()=>{if(el._x_currentIfEl)
|
||||
return el._x_currentIfEl;let clone2=el.content.cloneNode(true).firstElementChild;addScopeToNode(clone2,{},el);mutateDom(()=>{el.after(clone2);initTree(clone2);});el._x_currentIfEl=clone2;el._x_undoIf=()=>{clone2.remove();delete el._x_currentIfEl;};return clone2;};let hide=()=>{if(!el._x_undoIf)
|
||||
return;el._x_undoIf();delete el._x_undoIf;};effect3(()=>evaluate2((value)=>{value?show():hide();}));cleanup2(()=>el._x_undoIf&&el._x_undoIf());});mapAttributes(startingWith("@",into(prefix("on:"))));directive("on",skipDuringClone((el,{value,modifiers,expression},{cleanup:cleanup2})=>{let evaluate2=expression?evaluateLater(el,expression):()=>{};let removeListener=on(el,value,modifiers,(e)=>{evaluate2(()=>{},{scope:{$event:e},params:[e]});});cleanup2(()=>removeListener());}));alpine_default.setEvaluator(normalEvaluator);alpine_default.setReactivityEngine({reactive:reactive2,effect:effect2,release:stop,raw:toRaw});var src_default=alpine_default;window.Alpine=src_default;queueMicrotask(()=>{src_default.start();});})();window.ls.error=function(){return function(error){window.console.error(error);if(window.location.pathname!=='/console'){window.location='/console';}};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){var subscribe=document.getElementById('newsletter').checked;if(subscribe){let alerts=container.get('alerts');let loaderId=alerts.add({text:'Loading...',class:""},0);fetch('https://appwrite.io/v1/newsletter/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:form.name,email:form.email,}),}).finally(function(){alerts.remove(loaderId);window.location='/console';});}else{window.location='/console';}},function(error){window.location='/auth/signup?failure=1';});});window.addEventListener("load",async()=>{const bars=12;const realtime=window.ls.container.get('realtime');const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));let current={};window.ls.container.get('console').subscribe(['project','console'],response=>{switch(response.event){case'stats.connections':for(let project in response.payload){current[project]=response.payload[project]??0;}
|
||||
break;case'database.attributes.create':case'database.attributes.update':case'database.attributes.delete':document.dispatchEvent(new CustomEvent('database.createAttribute'));break;case'database.indexes.create':case'database.indexes.update':case'database.indexes.delete':document.dispatchEvent(new CustomEvent('database.createIndex'));break;}});while(true){let newHistory={};let createdHistory=false;for(const project in current){let history=realtime?.history??{};if(!(project in history)){history[project]=new Array(bars).fill({percentage:0,value:0});}
|
||||
break;case'database.attributes.create':case'database.attributes.update':case'database.attributes.delete':document.dispatchEvent(new CustomEvent('database.createAttribute'));break;case'database.indexes.create':case'database.indexes.update':case'database.indexes.delete':document.dispatchEvent(new CustomEvent('database.createIndex'));break;case'functions.deployments.create':case'functions.deployments.update':case'functions.deployments.delete':document.dispatchEvent(new CustomEvent('functions.createDeployment'));break;case'functions.executions.create':case'functions.executions.update':case'functions.executions.delete':console.log("Received execution event")
|
||||
document.dispatchEvent(new CustomEvent('functions.createExecution'));break;}});while(true){let newHistory={};let createdHistory=false;for(const project in current){let history=realtime?.history??{};if(!(project in history)){history[project]=new Array(bars).fill({percentage:0,value:0});}
|
||||
history=history[project];history.push({percentage:0,value:current[project]});if(history.length>=bars){history.shift();}
|
||||
const highest=history.reduce((prev,curr)=>{return(curr.value>prev)?curr.value:prev;},0);history=history.map(({percentage,value})=>{createdHistory=true;percentage=value===0?0:((Math.round((value/highest)*10)/10)*100);if(percentage>100)percentage=100;else if(percentage==0&&value!=0)percentage=5;return{percentage:percentage,value:value};})
|
||||
newHistory[project]=history;}
|
||||
|
@ -3736,7 +3737,7 @@ if(forcePlaces!==false){rounded=Number(rounded).toFixed(forcePlaces);}
|
|||
return rounded+abbr;}
|
||||
window.ls.container.get("view").add({selector:"data-acl",controller:function(element,document,router,alerts){document.body.classList.remove("console");document.body.classList.remove("home");document.body.classList.add(router.getCurrent().view.scope);if(!router.getCurrent().view.project){document.body.classList.add("hide-nav");document.body.classList.remove("show-nav");}else{document.body.classList.add("show-nav");document.body.classList.remove("hide-nav");}
|
||||
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{this._files.forEach((file)=>{if(!file.completed&&!file.failed){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});});},cancelAll(){if(confirm("Are you sure? This will cancel and remove any ungoing uploads?")){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;let read=formData.get('read');if(!file||!fileId){return;}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{if(this.hasOngoingUploads()){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});},cancelAll(){if(this.hasOngoingUploads()?confirm("Are you sure? This will cancel and remove any ongoing uploads?"):true){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},hasOngoingUploads(){let ongoing=false;this._files.some((file)=>{if(!file.completed&&!file.failed){ongoing=true;return;}});return ongoing;},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;let read=formData.get('read');if(!file||!fileId){return;}
|
||||
if(read){read=JSON.parse(read);}
|
||||
let write=formData.get('write');if(write){write=JSON.parse(wirte);}
|
||||
if(this.getFile(id)){this.updateFile(id,{name:file.name,completed:false,failed:false,cancelled:false,error:"",});}else{this.addFile({id:id,name:file.name,progress:0,completed:false,failed:false,cancelled:false,error:"",});}
|
||||
|
@ -3856,10 +3857,11 @@ if(element.required&&array.length===0){add.setCustomValidity("Please add permiss
|
|||
element.maxLength;}else{var words=element.value!==""?element.value.trim().split(" ").length:0;counter.innerText=words+" words and "+element.value.length.toString()+" chars";}};element.addEventListener("keyup",count);element.addEventListener("change",count);element.addEventListener("cut",count);element.addEventListener("paste",count);element.addEventListener("drop",count);count();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-direction",controller:function(element,rtl){var setDirection=function(){var value=element.value[0]?element.value:"";var direction="ltr";var align="left";if(rtl.isRTL(value)){direction="rtl";align="right";}
|
||||
element.style.direction=direction;element.style.textAlign=align;};element.addEventListener("keyup",setDirection);element.addEventListener("change",setDirection);element.addEventListener("cut",setDirection);element.addEventListener("paste",setDirection);element.addEventListener("drop",setDirection);setDirection();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-resize",controller:function(element,window){function resize(){var scrollLeft=window.pageXOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollLeft;var scrollTop=window.pageYOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollTop;var offset=element.offsetHeight-element.clientHeight;element.style.height="auto";element.style.height=element.scrollHeight+offset+"px";window.scrollTo(scrollLeft,scrollTop);}
|
||||
element.addEventListener("keyup",resize);element.addEventListener("change",resize);element.addEventListener("cut",resize);element.addEventListener("paste",resize);element.addEventListener("drop",resize);window.addEventListener("resize",resize);resize();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-upload",controller:function(element,container,alerts,expression,env,search){var scope=element.dataset["scope"];var project=expression.parse(element.dataset["project"]||"console");var labelButton=element.dataset["labelButton"]||"Upload";var labelLoading=element.dataset["labelLoading"]||"Uploading...";var previewWidth=element.dataset["previewWidth"]||200;var previewHeight=element.dataset["previewHeight"]||200;var previewAlt=element.dataset["previewAlt"]||200;var accept=element.dataset["accept"]||"";var searchButton=(element.dataset["search"]||0);var required=element.dataset["required"]||false;var className=element.dataset["class"]||"upload";var max=parseInt(element.dataset["max"]||4);var sdk=scope==="sdk"?container.get("sdk"):container.get("console");var output=element.value||null;var wrapper=document.createElement("div");var input=document.createElement("input");var upload=document.createElement("div");var preview=document.createElement("ul");var progress=document.createElement("div");var count=document.createElement("div");wrapper.className=className;input.type="file";input.accept=accept;input.required=required;input.tabIndex=-1;count.className="count";upload.className="button reverse margin-bottom-small";upload.innerHTML='<i class="icon icon-upload"></i> '+labelButton;upload.tabIndex=0;preview.className="preview";progress.className="progress";progress.style.width="0%";progress.style.display="none";var onComplete=function(message){alerts.remove(message);input.disabled=false;upload.classList.remove("disabled");progress.style.width="0%";progress.style.display="none";};var render=function(result){preview.innerHTML="";count.innerHTML="0 / "+max;if(!result){return;}
|
||||
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/files/"+
|
||||
result+"/preview?width="+
|
||||
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/buckets/"+
|
||||
result.bucketId+"/files/"+
|
||||
result.fileId+"/preview?width="+
|
||||
previewWidth+"&height="+
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('default','unique()',files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('default','unique()',files[0],read,write).then(function(response){onComplete(message);render({bucketId:response.bucketId,fileId:response.$id});},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-search",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-copy',repeat:false,controller:function(document,element,alerts){let button=document.createElement("i");button.type="button";button.title="Copy to Clipboard";button.className=element.getAttribute("data-class")||"icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);let copy=function(event){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
|
||||
element.classList.add('scroll-to-bottom')}
|
||||
|
|
12
public/dist/scripts/app.js
vendored
12
public/dist/scripts/app.js
vendored
|
@ -494,7 +494,8 @@ handler2.inline=(el,{expression},{cleanup:cleanup2})=>{let root=closestRoot(el);
|
|||
root._x_refs={};root._x_refs[expression]=el;cleanup2(()=>delete root._x_refs[expression]);};directive("ref",handler2);directive("if",(el,{expression},{effect:effect3,cleanup:cleanup2})=>{let evaluate2=evaluateLater(el,expression);let show=()=>{if(el._x_currentIfEl)
|
||||
return el._x_currentIfEl;let clone2=el.content.cloneNode(true).firstElementChild;addScopeToNode(clone2,{},el);mutateDom(()=>{el.after(clone2);initTree(clone2);});el._x_currentIfEl=clone2;el._x_undoIf=()=>{clone2.remove();delete el._x_currentIfEl;};return clone2;};let hide=()=>{if(!el._x_undoIf)
|
||||
return;el._x_undoIf();delete el._x_undoIf;};effect3(()=>evaluate2((value)=>{value?show():hide();}));cleanup2(()=>el._x_undoIf&&el._x_undoIf());});mapAttributes(startingWith("@",into(prefix("on:"))));directive("on",skipDuringClone((el,{value,modifiers,expression},{cleanup:cleanup2})=>{let evaluate2=expression?evaluateLater(el,expression):()=>{};let removeListener=on(el,value,modifiers,(e)=>{evaluate2(()=>{},{scope:{$event:e},params:[e]});});cleanup2(()=>removeListener());}));alpine_default.setEvaluator(normalEvaluator);alpine_default.setReactivityEngine({reactive:reactive2,effect:effect2,release:stop,raw:toRaw});var src_default=alpine_default;window.Alpine=src_default;queueMicrotask(()=>{src_default.start();});})();window.ls.error=function(){return function(error){window.console.error(error);if(window.location.pathname!=='/console'){window.location='/console';}};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){var subscribe=document.getElementById('newsletter').checked;if(subscribe){let alerts=container.get('alerts');let loaderId=alerts.add({text:'Loading...',class:""},0);fetch('https://appwrite.io/v1/newsletter/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:form.name,email:form.email,}),}).finally(function(){alerts.remove(loaderId);window.location='/console';});}else{window.location='/console';}},function(error){window.location='/auth/signup?failure=1';});});window.addEventListener("load",async()=>{const bars=12;const realtime=window.ls.container.get('realtime');const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));let current={};window.ls.container.get('console').subscribe(['project','console'],response=>{switch(response.event){case'stats.connections':for(let project in response.payload){current[project]=response.payload[project]??0;}
|
||||
break;case'database.attributes.create':case'database.attributes.update':case'database.attributes.delete':document.dispatchEvent(new CustomEvent('database.createAttribute'));break;case'database.indexes.create':case'database.indexes.update':case'database.indexes.delete':document.dispatchEvent(new CustomEvent('database.createIndex'));break;}});while(true){let newHistory={};let createdHistory=false;for(const project in current){let history=realtime?.history??{};if(!(project in history)){history[project]=new Array(bars).fill({percentage:0,value:0});}
|
||||
break;case'database.attributes.create':case'database.attributes.update':case'database.attributes.delete':document.dispatchEvent(new CustomEvent('database.createAttribute'));break;case'database.indexes.create':case'database.indexes.update':case'database.indexes.delete':document.dispatchEvent(new CustomEvent('database.createIndex'));break;case'functions.deployments.create':case'functions.deployments.update':case'functions.deployments.delete':document.dispatchEvent(new CustomEvent('functions.createDeployment'));break;case'functions.executions.create':case'functions.executions.update':case'functions.executions.delete':console.log("Received execution event")
|
||||
document.dispatchEvent(new CustomEvent('functions.createExecution'));break;}});while(true){let newHistory={};let createdHistory=false;for(const project in current){let history=realtime?.history??{};if(!(project in history)){history[project]=new Array(bars).fill({percentage:0,value:0});}
|
||||
history=history[project];history.push({percentage:0,value:current[project]});if(history.length>=bars){history.shift();}
|
||||
const highest=history.reduce((prev,curr)=>{return(curr.value>prev)?curr.value:prev;},0);history=history.map(({percentage,value})=>{createdHistory=true;percentage=value===0?0:((Math.round((value/highest)*10)/10)*100);if(percentage>100)percentage=100;else if(percentage==0&&value!=0)percentage=5;return{percentage:percentage,value:value};})
|
||||
newHistory[project]=history;}
|
||||
|
@ -689,7 +690,7 @@ if(forcePlaces!==false){rounded=Number(rounded).toFixed(forcePlaces);}
|
|||
return rounded+abbr;}
|
||||
window.ls.container.get("view").add({selector:"data-acl",controller:function(element,document,router,alerts){document.body.classList.remove("console");document.body.classList.remove("home");document.body.classList.add(router.getCurrent().view.scope);if(!router.getCurrent().view.project){document.body.classList.add("hide-nav");document.body.classList.remove("show-nav");}else{document.body.classList.add("show-nav");document.body.classList.remove("hide-nav");}
|
||||
if("/console"===router.getCurrent().path){document.body.classList.add("index");}else{document.body.classList.remove("index");}}}).add({selector:"data-prism",controller:function(window,document,element,alerts){Prism.highlightElement(element);let copy=document.createElement("i");copy.className="icon-docs copy";copy.title="Copy to Clipboard";copy.textContent="Click Here to Copy";copy.addEventListener("click",function(){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{this._files.forEach((file)=>{if(!file.completed&&!file.failed){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});});},cancelAll(){if(confirm("Are you sure? This will cancel and remove any ungoing uploads?")){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;let read=formData.get('read');if(!file||!fileId){return;}
|
||||
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}});(function(window){document.addEventListener('alpine:init',()=>{Alpine.store('uploader',{_files:[],files(){return(this._files??[]).filter((file)=>!file.cancelled);},isOpen:true,init(){window.addEventListener('beforeunload',(event)=>{if(this.hasOngoingUploads()){let confirmationMessage="There are incomplete uploads, are you sure you want to leave?";event.returnValue=confirmationMessage;return confirmationMessage;}});},cancelAll(){if(this.hasOngoingUploads()?confirm("Are you sure? This will cancel and remove any ongoing uploads?"):true){this._files.forEach(file=>{if(file.completed||file.failed){this.removeFile(file.id);}else{this.updateFile(file.id,{cancelled:true});}});}},hasOngoingUploads(){let ongoing=false;this._files.some((file)=>{if(!file.completed&&!file.failed){ongoing=true;return;}});return ongoing;},toggle(){this.isOpen=!this.isOpen;},addFile(file){this._files.push(file);},updateFile(id,file){this._files=this._files.map((oldFile)=>id==oldFile.id?{...oldFile,...file}:oldFile);},removeFile(id){const file=this.getFile(id)??{};if(file.completed||file.failed){this._files=this._files.filter((file)=>file.id!==id);}else{if(confirm("Are you sure you want to cancel the upload?")){this.updateFile(id,{cancelled:true});}}},getFile(id){return this._files.find((file)=>file.id===id);},async uploadFile(target){const formData=new FormData(target);const sdk=window.ls.container.get('sdk');const bucketId=formData.get('bucketId');const file=formData.get('file');const fileId=formData.get('fileId');let id=fileId==='unique()'?performance.now():fileId;let read=formData.get('read');if(!file||!fileId){return;}
|
||||
if(read){read=JSON.parse(read);}
|
||||
let write=formData.get('write');if(write){write=JSON.parse(wirte);}
|
||||
if(this.getFile(id)){this.updateFile(id,{name:file.name,completed:false,failed:false,cancelled:false,error:"",});}else{this.addFile({id:id,name:file.name,progress:0,completed:false,failed:false,cancelled:false,error:"",});}
|
||||
|
@ -809,10 +810,11 @@ if(element.required&&array.length===0){add.setCustomValidity("Please add permiss
|
|||
element.maxLength;}else{var words=element.value!==""?element.value.trim().split(" ").length:0;counter.innerText=words+" words and "+element.value.length.toString()+" chars";}};element.addEventListener("keyup",count);element.addEventListener("change",count);element.addEventListener("cut",count);element.addEventListener("paste",count);element.addEventListener("drop",count);count();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-direction",controller:function(element,rtl){var setDirection=function(){var value=element.value[0]?element.value:"";var direction="ltr";var align="left";if(rtl.isRTL(value)){direction="rtl";align="right";}
|
||||
element.style.direction=direction;element.style.textAlign=align;};element.addEventListener("keyup",setDirection);element.addEventListener("change",setDirection);element.addEventListener("cut",setDirection);element.addEventListener("paste",setDirection);element.addEventListener("drop",setDirection);setDirection();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-text-resize",controller:function(element,window){function resize(){var scrollLeft=window.pageXOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollLeft;var scrollTop=window.pageYOffset||(window.document.documentElement||window.document.body.parentNode||window.document.body).scrollTop;var offset=element.offsetHeight-element.clientHeight;element.style.height="auto";element.style.height=element.scrollHeight+offset+"px";window.scrollTo(scrollLeft,scrollTop);}
|
||||
element.addEventListener("keyup",resize);element.addEventListener("change",resize);element.addEventListener("cut",resize);element.addEventListener("paste",resize);element.addEventListener("drop",resize);window.addEventListener("resize",resize);resize();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-upload",controller:function(element,container,alerts,expression,env,search){var scope=element.dataset["scope"];var project=expression.parse(element.dataset["project"]||"console");var labelButton=element.dataset["labelButton"]||"Upload";var labelLoading=element.dataset["labelLoading"]||"Uploading...";var previewWidth=element.dataset["previewWidth"]||200;var previewHeight=element.dataset["previewHeight"]||200;var previewAlt=element.dataset["previewAlt"]||200;var accept=element.dataset["accept"]||"";var searchButton=(element.dataset["search"]||0);var required=element.dataset["required"]||false;var className=element.dataset["class"]||"upload";var max=parseInt(element.dataset["max"]||4);var sdk=scope==="sdk"?container.get("sdk"):container.get("console");var output=element.value||null;var wrapper=document.createElement("div");var input=document.createElement("input");var upload=document.createElement("div");var preview=document.createElement("ul");var progress=document.createElement("div");var count=document.createElement("div");wrapper.className=className;input.type="file";input.accept=accept;input.required=required;input.tabIndex=-1;count.className="count";upload.className="button reverse margin-bottom-small";upload.innerHTML='<i class="icon icon-upload"></i> '+labelButton;upload.tabIndex=0;preview.className="preview";progress.className="progress";progress.style.width="0%";progress.style.display="none";var onComplete=function(message){alerts.remove(message);input.disabled=false;upload.classList.remove("disabled");progress.style.width="0%";progress.style.display="none";};var render=function(result){preview.innerHTML="";count.innerHTML="0 / "+max;if(!result){return;}
|
||||
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/files/"+
|
||||
result+"/preview?width="+
|
||||
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/buckets/"+
|
||||
result.bucketId+"/files/"+
|
||||
result.fileId+"/preview?width="+
|
||||
previewWidth+"&height="+
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('default','unique()',files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('default','unique()',files[0],read,write).then(function(response){onComplete(message);render({bucketId:response.bucketId,fileId:response.$id});},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
|
||||
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-search",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-copy',repeat:false,controller:function(document,element,alerts){let button=document.createElement("i");button.type="button";button.title="Copy to Clipboard";button.className=element.getAttribute("data-class")||"icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);let copy=function(event){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
|
||||
window.getSelection().removeAllRanges();};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
|
||||
element.classList.add('scroll-to-bottom')}
|
||||
|
|
|
@ -76,6 +76,20 @@ window.addEventListener("load", async () => {
|
|||
document.dispatchEvent(new CustomEvent('database.createIndex'));
|
||||
|
||||
break;
|
||||
case 'functions.deployments.create':
|
||||
case 'functions.deployments.update':
|
||||
case 'functions.deployments.delete':
|
||||
document.dispatchEvent(new CustomEvent('functions.createDeployment'));
|
||||
|
||||
break;
|
||||
|
||||
case 'functions.executions.create':
|
||||
case 'functions.executions.update':
|
||||
case 'functions.executions.delete':
|
||||
document.dispatchEvent(new CustomEvent('functions.createExecution'));
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -8,17 +8,15 @@
|
|||
isOpen: true,
|
||||
init() {
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
this._files.forEach((file) => {
|
||||
if(!file.completed && !file.failed) {
|
||||
let confirmationMessage = "There are incomplete uploads, are you sure you want to leave?";
|
||||
event.returnValue = confirmationMessage;
|
||||
return confirmationMessage;
|
||||
}
|
||||
});
|
||||
if(this.hasOngoingUploads()) {
|
||||
let confirmationMessage = "There are incomplete uploads, are you sure you want to leave?";
|
||||
event.returnValue = confirmationMessage;
|
||||
return confirmationMessage;
|
||||
}
|
||||
});
|
||||
},
|
||||
cancelAll() {
|
||||
if(confirm("Are you sure? This will cancel and remove any ungoing uploads?")){
|
||||
if(this.hasOngoingUploads() ? confirm("Are you sure? This will cancel and remove any ongoing uploads?") : true){
|
||||
this._files.forEach(file => {
|
||||
if(file.completed || file.failed) {
|
||||
this.removeFile(file.id);
|
||||
|
@ -28,6 +26,16 @@
|
|||
});
|
||||
}
|
||||
},
|
||||
hasOngoingUploads() {
|
||||
let ongoing = false;
|
||||
this._files.some((file) => {
|
||||
if(!file.completed && !file.failed) {
|
||||
ongoing = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
return ongoing;
|
||||
},
|
||||
toggle() {
|
||||
this.isOpen = !this.isOpen;
|
||||
},
|
||||
|
|
|
@ -68,8 +68,10 @@
|
|||
|
||||
image.src = image.src =
|
||||
env.API +
|
||||
"/storage/files/" +
|
||||
result +
|
||||
"/storage/buckets/" +
|
||||
result.bucketId +
|
||||
"/files/" +
|
||||
result.fileId +
|
||||
"/preview?width=" +
|
||||
previewWidth +
|
||||
"&height=" +
|
||||
|
@ -108,11 +110,11 @@
|
|||
expression.parse(element.dataset["write"] || "[]")
|
||||
);
|
||||
|
||||
sdk.storage.createFile('default', 'unique()', files[0], read, write, 1).then(
|
||||
sdk.storage.createFile('default', 'unique()', files[0], read, write).then(
|
||||
function(response) {
|
||||
onComplete(message);
|
||||
|
||||
render(response.$id);
|
||||
render({bucketId: response.bucketId, fileId: response.$id});
|
||||
},
|
||||
function(error) {
|
||||
alerts.add({ text: "An error occurred!", class: "" }, 3000); // File(s) uploaded.
|
||||
|
|
|
@ -98,10 +98,16 @@ class Exception extends \Exception
|
|||
|
||||
/** Functions */
|
||||
const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||
const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
|
||||
|
||||
/** Deployments */
|
||||
const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
|
||||
|
||||
/** Builds */
|
||||
const BUILD_NOT_FOUND = 'build_not_found';
|
||||
const BUILD_NOT_READY = 'build_not_ready';
|
||||
const BUILD_IN_PROGRESS = 'build_in_progress';
|
||||
|
||||
/** Execution */
|
||||
const EXECUTION_NOT_FOUND = 'execution_not_found';
|
||||
|
||||
|
|
|
@ -307,12 +307,17 @@ class Realtime extends Adapter
|
|||
break;
|
||||
case strpos($event, 'functions.executions.') === 0:
|
||||
if (!empty($payload->getRead())) {
|
||||
$channels[] = 'console';
|
||||
$channels[] = 'executions';
|
||||
$channels[] = 'executions.' . $payload->getId();
|
||||
$channels[] = 'functions.' . $payload->getAttribute('functionId');
|
||||
$roles = $payload->getRead();
|
||||
}
|
||||
break;
|
||||
case strpos($event, 'functions.deployments.') === 0:
|
||||
$channels[] = 'console';
|
||||
$roles = ['team:' . $project->getAttribute('teamId')];
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
|
|
|
@ -38,6 +38,26 @@ abstract class Migration
|
|||
'0.13.0' => 'V12',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $collections;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->collections = array_merge([
|
||||
'_metadata' => [
|
||||
'$id' => '_metadata'
|
||||
],
|
||||
'audit' => [
|
||||
'$id' => 'audit'
|
||||
],
|
||||
'abuse' => [
|
||||
'$id' => 'abuse'
|
||||
]
|
||||
], Config::getParam('collections', []));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set project for migration.
|
||||
*
|
||||
|
@ -51,7 +71,7 @@ abstract class Migration
|
|||
{
|
||||
$this->project = $project;
|
||||
$this->projectDB = $projectDB;
|
||||
$this->projectDB->setNamespace('_project_' . $this->project->getId());
|
||||
$this->projectDB->setNamespace('_' . $this->project->getId());
|
||||
|
||||
$this->consoleDB = $consoleDB;
|
||||
|
||||
|
@ -67,10 +87,7 @@ abstract class Migration
|
|||
{
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
foreach ($this->collections as $collection) {
|
||||
$sum = 0;
|
||||
$nextDocument = null;
|
||||
$collectionCount = $this->projectDB->count($collection['$id']);
|
||||
|
|
|
@ -4,17 +4,178 @@ namespace Appwrite\Migration\Version;
|
|||
|
||||
use Appwrite\Migration\Migration;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class V12 extends Migration
|
||||
{
|
||||
/**
|
||||
* @var \PDO $pdo
|
||||
*/
|
||||
private $pdo;
|
||||
|
||||
public function execute(): void
|
||||
{
|
||||
global $register;
|
||||
Console::log('Migrating project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
|
||||
|
||||
$this->pdo = $register->get('db');
|
||||
|
||||
Console::info('Migrating Project Schemas');
|
||||
$this->migrateProjectSchema($this->project->getId());
|
||||
|
||||
/**
|
||||
* Switch to migrated Console Project
|
||||
*/
|
||||
if ($this->project->getId() === 'console') {
|
||||
$this->consoleDB->setNamespace('_console');
|
||||
$this->projectDB->setNamespace('_console');
|
||||
}
|
||||
|
||||
Console::info('Migrating Permissions');
|
||||
$this->fixPermissions();
|
||||
Console::info('Migrating Collections');
|
||||
$this->fixCollections();
|
||||
Console::info('Migrating Documents');
|
||||
$this->forEachDocument([$this, 'fixDocument']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate Project Tables.
|
||||
*
|
||||
* @param string $projectId
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
* @throws \PDOException
|
||||
*/
|
||||
private function migrateProjectSchema(string $projectId): void
|
||||
{
|
||||
/**
|
||||
* Remove empty generated Console Project.
|
||||
*/
|
||||
if ($this->consoleDB->getNamespace() === '_project_console' && $projectId === 'console') {
|
||||
$all = [];
|
||||
foreach ($this->collections as $collection) {
|
||||
$all[] = "_{$projectId}_{$collection['$id']}";
|
||||
$all[] = "_{$projectId}_{$collection['$id']}_perms";
|
||||
}
|
||||
$this->pdo->prepare('DROP TABLE IF EXISTS ' . implode(', ', $all) . ';')->execute();
|
||||
} elseif ($this->projectDB->getNamespace() === '_console') {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename Database Tables.
|
||||
*/
|
||||
foreach ($this->collections as $collection) {
|
||||
$id = $collection['$id'];
|
||||
|
||||
$this->pdo->prepare("ALTER TABLE IF EXISTS _project_{$projectId}_{$id} RENAME TO _{$projectId}_{$id}")->execute();
|
||||
$this->pdo->prepare("CREATE TABLE IF NOT EXISTS _{$projectId}_{$id}_perms (
|
||||
`_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`_type` VARCHAR(12) NOT NULL,
|
||||
`_permission` VARCHAR(255) NOT NULL,
|
||||
`_document` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`_id`),
|
||||
UNIQUE INDEX `_index1` (`_type`,`_document`,`_permission`),
|
||||
INDEX `_index2` (`_permission`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;")->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all Collection Structure.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function fixCollections(): void
|
||||
{
|
||||
foreach ($this->collections as $collection) {
|
||||
$id = $collection['$id'];
|
||||
Console::log("- {$id}");
|
||||
switch ($id) {
|
||||
case 'sessions':
|
||||
try {
|
||||
$this->projectDB->renameAttribute($id, 'providerToken', 'providerAccessToken');
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'providerAccessToken' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
$this->projectDB->createAttribute(collection: $id, id: 'providerRefreshToken', type: Database::VAR_STRING, size: 16384, signed: true, required: true, filters: ['encrypt']);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'providerRefreshToken' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
try {
|
||||
$this->projectDB->createAttribute(collection: $id, id: 'providerAccessTokenExpiry', type: Database::VAR_INTEGER, size: 0, required: true);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("'providerAccessTokenExpiry' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all Permission to new System with dedicated Table.
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function fixPermissions()
|
||||
{
|
||||
foreach ($this->collections as $collection) {
|
||||
$id = $collection['$id'];
|
||||
Console::log("- {$collection['$id']}");
|
||||
$nextDocument = null;
|
||||
|
||||
do {
|
||||
$documents = $this->projectDB->find($id, limit: $this->limit, cursor: $nextDocument);
|
||||
$count = count($documents);
|
||||
|
||||
\Co\run(function (array $documents) {
|
||||
foreach ($documents as $document) {
|
||||
go(function (Document $document) {
|
||||
$sql = "SELECT _read, _write FROM `{$this->projectDB->getDefaultDatabase()}`.`{$this->projectDB->getNamespace()}_{$document->getCollection()}` WHERE _uid = {$this->pdo->quote($document->getid())}";
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute();
|
||||
|
||||
$permissions = $stmt->fetch();
|
||||
|
||||
$read = json_decode($permissions['_read'] ?? null) ?? [];
|
||||
$write = json_decode($permissions['_write'] ?? null) ?? [];
|
||||
|
||||
$permissions = [];
|
||||
foreach ($read as $permission) {
|
||||
$permissions[] = "('read', '{$permission}', '{$document->getId()}')";
|
||||
}
|
||||
|
||||
foreach ($write as $permission) {
|
||||
$permissions[] = "('write', '{$permission}', '{$document->getId()}')";
|
||||
}
|
||||
|
||||
if (!empty($permissions)) {
|
||||
$queryPermissions = "INSERT IGNORE INTO `{$this->projectDB->getDefaultDatabase()}`.`{$this->projectDB->getNamespace()}_{$document->getCollection()}_perms` (_type, _permission, _document) VALUES " . implode(', ', $permissions);
|
||||
$stmtPermissions = $this->pdo->prepare($queryPermissions);
|
||||
$stmtPermissions->execute();
|
||||
}
|
||||
}, $document);
|
||||
}
|
||||
}, $documents);
|
||||
|
||||
if ($count !== $this->limit) {
|
||||
$nextDocument = null;
|
||||
} else {
|
||||
$nextDocument = end($documents);
|
||||
}
|
||||
} while (!is_null($nextDocument));
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout to give MariaDB some room to breath
|
||||
*/
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
protected function fixDocument(Document $document)
|
||||
{
|
||||
switch ($document->getCollection()) {
|
||||
|
@ -92,6 +253,15 @@ class V12 extends Migration
|
|||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'sessions':
|
||||
$document
|
||||
->setAttribute('providerRefreshToken', '')
|
||||
->setAttribute('providerAccessTokenExpiry', 0)
|
||||
->setAttribute('providerAccessToken', $document->getAttribute('providerToken', ''))
|
||||
->removeAttribute('providerToken');
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $document;
|
||||
|
|
|
@ -184,10 +184,10 @@ abstract class Worker
|
|||
if (!$projectId) {
|
||||
throw new \Exception('ProjectID not provided - cannot get database');
|
||||
}
|
||||
$namespace = "_project_{$projectId}";
|
||||
$namespace = "_{$projectId}";
|
||||
break;
|
||||
case self::DATABASE_CONSOLE:
|
||||
$namespace = "_project_console";
|
||||
$namespace = "_console";
|
||||
$sleep = 5; // ConsoleDB needs extra sleep time to ensure tables are created
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -205,7 +205,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
|
||||
$this->assertEquals($function['headers']['status-code'], 200);
|
||||
$this->assertEquals($function['body']['name'], 'Test');
|
||||
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
@ -281,7 +281,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$folder = 'php';
|
||||
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
|
||||
$this->packageCode($folder);
|
||||
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/deployments', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -298,7 +298,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals('index.php', $deployment['body']['entrypoint']);
|
||||
|
||||
// Wait for deployment to build.
|
||||
sleep(15);
|
||||
sleep(30);
|
||||
|
||||
return array_merge($data, ['deploymentId' => $deploymentId]);
|
||||
}
|
||||
|
@ -593,6 +593,8 @@ class FunctionsCustomServerTest extends Scope
|
|||
'async' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
|
||||
$this->assertEquals('completed', $execution['body']['status']);
|
||||
$this->assertStringContainsString($data['deploymentId'], $execution['body']['stdout']);
|
||||
$this->assertStringContainsString('Test1', $execution['body']['stdout']);
|
||||
|
@ -732,7 +734,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(201, $deployment['headers']['status-code']);
|
||||
|
||||
// Allow build step to run
|
||||
sleep(5);
|
||||
sleep(20);
|
||||
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -997,7 +999,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$folder = 'python';
|
||||
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
|
||||
$this->packageCode($folder);
|
||||
|
||||
|
||||
$entrypoint = 'main.py';
|
||||
$timeout = 2;
|
||||
|
||||
|
@ -1033,7 +1035,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(201, $deployment['headers']['status-code']);
|
||||
|
||||
// Allow build step to run
|
||||
sleep(10);
|
||||
sleep(30);
|
||||
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -1048,7 +1050,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
|
||||
$executionId = $execution['body']['$id'] ?? '';
|
||||
|
||||
sleep(10);
|
||||
sleep(30);
|
||||
|
||||
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -1138,7 +1140,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(201, $deployment['headers']['status-code']);
|
||||
|
||||
// Allow build step to run
|
||||
sleep(20);
|
||||
sleep(40);
|
||||
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
|
|
@ -1052,7 +1052,8 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(3, $response['data']['channels']);
|
||||
$this->assertCount(4, $response['data']['channels']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertContains('executions', $response['data']['channels']);
|
||||
$this->assertContains('executions.' . $execution['body']['$id'], $response['data']['channels']);
|
||||
$this->assertContains('functions.' . $execution['body']['functionId'], $response['data']['channels']);
|
||||
|
@ -1064,7 +1065,8 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertEquals('event', $responseUpdate['type']);
|
||||
$this->assertNotEmpty($responseUpdate['data']);
|
||||
$this->assertArrayHasKey('timestamp', $responseUpdate['data']);
|
||||
$this->assertCount(3, $responseUpdate['data']['channels']);
|
||||
$this->assertCount(4, $responseUpdate['data']['channels']);
|
||||
$this->assertContains('console', $responseUpdate['data']['channels']);
|
||||
$this->assertContains('executions', $responseUpdate['data']['channels']);
|
||||
$this->assertContains('executions.' . $execution['body']['$id'], $responseUpdate['data']['channels']);
|
||||
$this->assertContains('functions.' . $execution['body']['functionId'], $responseUpdate['data']['channels']);
|
||||
|
|
|
@ -149,7 +149,21 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'manchester-united.co.uk'
|
||||
'search' => 'united.co.uk'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertIsArray($response['body']);
|
||||
$this->assertIsArray($response['body']['users']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertEquals(1, $response['body']['sum']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'man'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -165,6 +179,7 @@ trait UsersBase
|
|||
], $this->getHeaders()), [
|
||||
'search' => $data['userId']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
|
|
Loading…
Reference in a new issue