1
0
Fork 0
mirror of synced 2024-06-02 10:54:44 +12:00

Merge branch '0.13.x' of github.com:appwrite/appwrite into feat-functions-refactor

This commit is contained in:
Christy Jacob 2022-02-23 16:25:31 +04:00
commit cf2fee5b6f
69 changed files with 2040 additions and 1657 deletions

View file

@ -12,6 +12,15 @@
- Updated UI with these changes
- Updated event names from `function.tags.*` to `function.deployments.*`
# Version 0.12.3
## Bugs
- Fix update membership roles (#2799)
- Fix migration to 0.12.x to populate search fields (#2799)
## Security
- Fix URL schema Validation to only allow http/https (#2801)
# Version 0.12.2
## Bugs

View file

@ -59,7 +59,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:0.12.2
appwrite/appwrite:0.12.3
```
### Windows
@ -71,7 +71,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:0.12.2
appwrite/appwrite:0.12.3
```
#### PowerShell
@ -81,7 +81,7 @@ docker run -it --rm ,
--volume /var/run/docker.sock:/var/run/docker.sock ,
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
--entrypoint="install" ,
appwrite/appwrite:0.12.2
appwrite/appwrite:0.12.3
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。

View file

@ -62,7 +62,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:0.12.2
appwrite/appwrite:0.12.3
```
### Windows
@ -74,7 +74,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:0.12.2
appwrite/appwrite:0.12.3
```
#### PowerShell
@ -84,7 +84,7 @@ docker run -it --rm ,
--volume /var/run/docker.sock:/var/run/docker.sock ,
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ,
--entrypoint="install" ,
appwrite/appwrite:0.12.2
appwrite/appwrite:0.12.3
```
Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after installation completes.

View file

@ -205,6 +205,24 @@ return [
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
],
[
'key' => 'node-cli',
'name' => 'Command Line',
'version' => '0.13.0',
'url' => 'https://github.com/appwrite/sdk-for-node-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
'beta' => true,
'dev' => false,
'hidden' => false,
'family' => APP_PLATFORM_CONSOLE,
'prism' => 'bash',
'source' => \realpath(__DIR__ . '/../sdks/server-cli'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-cli.git',
'gitRepoName' => 'sdk-for-node-cli',
'gitUserName' => 'appwrite',
'gitBranch' => 'master',
],
[
'key' => 'deno',
'name' => 'Deno',

View file

@ -84,7 +84,7 @@ return [ // Ordered by ABC.
'github' => [
'name' => 'GitHub',
'developers' => 'https://developer.github.com/',
'icon' => 'icon-github-circled',
'icon' => 'icon-github',
'enabled' => true,
'sandbox' => false,
'form' => false,

View file

@ -15,30 +15,30 @@ return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
'video/x-ms-wmv' => __DIR__.'/logos/video.png',
// // Microsoft Word
'application/msword' => __DIR__.'/logos/word.png',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => __DIR__.'/logos/word.png',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => __DIR__.'/logos/word.png',
'application/vnd.ms-word.document.macroEnabled.12' => __DIR__.'/logos/word.png',
// 'application/msword' => __DIR__.'/logos/word.png',
// 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => __DIR__.'/logos/word.png',
// 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => __DIR__.'/logos/word.png',
// 'application/vnd.ms-word.document.macroEnabled.12' => __DIR__.'/logos/word.png',
// // Microsoft Excel
'application/vnd.ms-excel' => __DIR__.'/logos/excel.png',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => __DIR__.'/logos/excel.png',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => __DIR__.'/logos/excel.png',
'application/vnd.ms-excel.sheet.macroEnabled.12' => __DIR__.'/logos/excel.png',
'application/vnd.ms-excel.template.macroEnabled.12' => __DIR__.'/logos/excel.png',
'application/vnd.ms-excel.addin.macroEnabled.12' => __DIR__.'/logos/excel.png',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => __DIR__.'/logos/excel.png',
// 'application/vnd.ms-excel' => __DIR__.'/logos/excel.png',
// 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => __DIR__.'/logos/excel.png',
// 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => __DIR__.'/logos/excel.png',
// 'application/vnd.ms-excel.sheet.macroEnabled.12' => __DIR__.'/logos/excel.png',
// 'application/vnd.ms-excel.template.macroEnabled.12' => __DIR__.'/logos/excel.png',
// 'application/vnd.ms-excel.addin.macroEnabled.12' => __DIR__.'/logos/excel.png',
// 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => __DIR__.'/logos/excel.png',
// // Microsoft Power Point
'application/vnd.ms-powerpoint' => __DIR__.'/logos/ppt.png',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => __DIR__.'/logos/ppt.png',
'application/vnd.openxmlformats-officedocument.presentationml.template' => __DIR__.'/logos/ppt.png',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => __DIR__.'/logos/ppt.png',
'application/vnd.ms-powerpoint.addin.macroEnabled.12' => __DIR__.'/logos/ppt.png',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => __DIR__.'/logos/ppt.png',
'application/vnd.ms-powerpoint.template.macroEnabled.12' => __DIR__.'/logos/ppt.png',
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => __DIR__.'/logos/ppt.png',
// 'application/vnd.ms-powerpoint' => __DIR__.'/logos/ppt.png',
// 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => __DIR__.'/logos/ppt.png',
// 'application/vnd.openxmlformats-officedocument.presentationml.template' => __DIR__.'/logos/ppt.png',
// 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => __DIR__.'/logos/ppt.png',
// 'application/vnd.ms-powerpoint.addin.macroEnabled.12' => __DIR__.'/logos/ppt.png',
// 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => __DIR__.'/logos/ppt.png',
// 'application/vnd.ms-powerpoint.template.macroEnabled.12' => __DIR__.'/logos/ppt.png',
// 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => __DIR__.'/logos/ppt.png',
// Adobe PDF
'application/pdf' => __DIR__.'/logos/pdf.png',
// 'application/pdf' => __DIR__.'/logos/pdf.png',
];

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -99,8 +99,6 @@ App::post('/v1/storage/buckets')
]);
}
$dbForProject->createCollection('bucket_' . $bucketId, $attributes, $indexes);
$bucket = $dbForProject->createDocument('buckets', new Document([
'$id' => $bucketId,
'$collection' => 'buckets',
@ -117,6 +115,10 @@ App::post('/v1/storage/buckets')
'$write' => $write ?? [],
'search' => implode(' ', [$bucketId, $name]),
]));
$bucket = $dbForProject->getDocument('buckets', $bucketId);
$dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
} catch (Duplicate $th) {
throw new Exception('Bucket already exists', 409, Exception::STORAGE_BUCKET_ALREADY_EXISTS);
}
@ -483,11 +485,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
$path = str_ireplace($deviceFiles->getRoot(), $deviceFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucketId, $fileId) {
return $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId) {
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$file = $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
$metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)];
@ -579,11 +581,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
'metadata' => $metadata,
]);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucketId, $doc) {
return $dbForProject->createDocument('bucket_' . $bucketId, $doc);
$file = Authorization::skip(function () use ($dbForProject, $bucket, $doc) {
return $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
});
} else {
$file = $dbForProject->createDocument('bucket_' . $bucketId, $doc);
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
}
} else {
$file = $file
@ -601,11 +603,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
->setAttribute('chunksUploaded', $chunksUploaded);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucketId, $fileId, $file) {
return $dbForProject->updateDocument('bucket_' . $bucketId, $fileId, $file);
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId, $file) {
return $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
});
} else {
$file = $dbForProject->updateDocument('bucket_' . $bucketId, $fileId, $file);
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
}
}
@ -649,11 +651,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
'metadata' => $metadata,
]);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucketId, $doc) {
return $dbForProject->createDocument('bucket_' . $bucketId, $doc);
$file = Authorization::skip(function () use ($dbForProject, $bucket, $doc) {
return $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
});
} else {
$file = $dbForProject->createDocument('bucket_' . $bucketId, $doc);
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
}
} else {
$file = $file
@ -661,11 +663,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
->setAttribute('metadata', $metadata);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucketId, $fileId, $file) {
return $dbForProject->updateDocument('bucket_' . $bucketId, $fileId, $file);
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId, $file) {
return $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
});
} else {
$file = $dbForProject->updateDocument('bucket_' . $bucketId, $fileId, $file);
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
}
}
} catch (StructureException $exception) {
@ -737,10 +739,10 @@ App::get('/v1/storage/buckets/:bucketId/files')
if (!empty($cursor)) {
if ($bucket->getAttribute('permission') === 'bucket') {
$cursorFile = Authorization::skip(function () use ($dbForProject, $bucket, $cursor) {
return $dbForProject->getDocument('bucket_' . $bucket->getId(), $cursor);
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor);
});
} else {
$cursorFile = $dbForProject->getDocument('bucket_' . $bucket->getId(), $cursor);
$cursorFile = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor);
}
if ($cursorFile->isEmpty()) {
@ -755,11 +757,11 @@ App::get('/v1/storage/buckets/:bucketId/files')
}
if ($bucket->getAttribute('permission') === 'bucket') {
$files = Authorization::skip(function () use ($dbForProject, $bucketId, $queries, $limit, $offset, $cursor, $cursorDirection, $orderType) {
return $dbForProject->find('bucket_' . $bucketId, $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection);
$files = Authorization::skip(function () use ($dbForProject, $bucket, $queries, $limit, $offset, $cursor, $cursorDirection, $orderType) {
return $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection);
});
} else {
$files = $dbForProject->find('bucket_' . $bucketId, $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection);
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection);
}
$usage
@ -769,7 +771,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
$response->dynamic(new Document([
'files' => $files,
'sum' => $dbForProject->count('bucket_' . $bucketId, $queries, APP_LIMIT_COUNT),
'sum' => $dbForProject->count('bucket_' . $bucket->getInternalId(), $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FILE_LIST);
});
@ -813,11 +815,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
}
if ($bucket->getAttribute('permission') === 'bucket') {
$file = Authorization::skip(function () use ($dbForProject, $bucketId, $fileId) {
return $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId) {
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$file = $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
@ -902,11 +904,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
if ($bucket->getAttribute('permission') === 'bucket') {
// skip authorization
$file = Authorization::skip(function () use ($dbForProject, $bucketId, $fileId) {
return $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId) {
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$file = $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
@ -1061,11 +1063,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
}
if ($bucket->getAttribute('permission') === 'bucket') {
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucketId) {
return $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucket) {
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$file = $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
@ -1206,11 +1208,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
}
if ($bucket->getAttribute('permission') === 'bucket') {
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucketId) {
return $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucket) {
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$file = $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
$mimes = Config::getParam('storage-mimes');
@ -1387,11 +1389,11 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
}
if ($bucket->getAttribute('permission') === 'bucket') {
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucketId) {
return $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucket) {
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$file = $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
@ -1399,14 +1401,14 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
}
if ($bucket->getAttribute('permission') === 'bucket') {
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucketId, $file, $read, $write) {
return $dbForProject->updateDocument('bucket_' . $bucketId, $fileId, $file
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucket, $file, $read, $write) {
return $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file
->setAttribute('$read', $read)
->setAttribute('$write', $write)
);
});
} else {
$file = $dbForProject->updateDocument('bucket_' . $bucketId, $fileId, $file
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file
->setAttribute('$read', $read)
->setAttribute('$write', $write)
);
@ -1475,11 +1477,11 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
}
if ($bucket->getAttribute('permission') === 'bucket') {
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucketId) {
return $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = Authorization::skip(function () use ($dbForProject, $fileId, $bucket) {
return $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$file = $dbForProject->getDocument('bucket_' . $bucketId, $fileId);
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) {
@ -1503,11 +1505,11 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
$deviceLocal->delete($cacheDir, true);
if ($bucket->getAttribute('permission') === 'bucket') {
$deleted = Authorization::skip(function () use ($dbForProject, $fileId, $bucketId) {
return $dbForProject->deleteDocument('bucket_' . $bucketId, $fileId);
$deleted = Authorization::skip(function () use ($dbForProject, $fileId, $bucket) {
return $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
});
} else {
$deleted = $dbForProject->deleteDocument('bucket_' . $bucketId, $fileId);
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
}
if (!$deleted) {
throw new Exception('Failed to remove file from DB', 500, Exception::GENERAL_SERVER_ERROR);

View file

@ -71,7 +71,7 @@ const APP_LIMIT_ENCRYPTION = 20000000; //20MB
const APP_LIMIT_COMPRESSION = 20000000; //20MB
const APP_LIMIT_PREVIEW = 10000000; //10MB file size limit for preview endpoint
const APP_CACHE_BUSTER = 201;
const APP_VERSION_STABLE = '0.13.0';
const APP_VERSION_STABLE = '0.12.3';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';

View file

@ -236,6 +236,8 @@ $cli
$max = 10;
$sleep = 1;
$db = null;
$redis = null;
do { // connect to db
try {
$attempts++;
@ -275,47 +277,47 @@ $cli
* @var InfluxDB\Client $client
*/
$client = $register->get('influxdb');
if ($client) {
$attempts = 0;
$max = 10;
$sleep = 1;
$attempts = 0;
$max = 10;
$sleep = 1;
do { // check if telegraf database is ready
try {
$attempts++;
$database = $client->selectDB('telegraf');
if(in_array('telegraf', $client->listDatabases())) {
break; // leave the do-while if successful
}
} catch (\Throwable $th) {
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception('InfluxDB database not ready yet');
}
sleep($sleep);
do { // check if telegraf database is ready
try {
$attempts++;
$database = $client->selectDB('telegraf');
if(in_array('telegraf', $client->listDatabases())) {
break; // leave the do-while if successful
}
} while ($attempts < $max);
} catch (\Throwable $th) {
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception('InfluxDB database not ready yet');
}
sleep($sleep);
}
} while ($attempts < $max);
// sync data
foreach ($globalMetrics as $metric => $options) { //for each metrics
foreach ($periods as $period) { // aggregate data for each period
$start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339);
if (!empty($latestTime[$metric][$period['key']])) {
$start = DateTime::createFromFormat('U', $latestTime[$metric][$period['key']])->format(DateTime::RFC3339);
}
$end = DateTime::createFromFormat('U', \strtotime('now'))->format(DateTime::RFC3339);
// sync data
foreach ($globalMetrics as $metric => $options) { //for each metrics
foreach ($periods as $period) { // aggregate data for each period
$start = DateTime::createFromFormat('U', \strtotime($period['startTime']))->format(DateTime::RFC3339);
if (!empty($latestTime[$metric][$period['key']])) {
$start = DateTime::createFromFormat('U', $latestTime[$metric][$period['key']])->format(DateTime::RFC3339);
}
$end = DateTime::createFromFormat('U', \strtotime('now'))->format(DateTime::RFC3339);
$table = $options['table']; //Which influxdb table to query for this metric
$groupBy = empty($options['groupBy']) ? '' : ', "' . $options['groupBy'] . '"'; //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc
$table = $options['table']; //Which influxdb table to query for this metric
$groupBy = empty($options['groupBy']) ? '' : ', "' . $options['groupBy'] . '"'; //Some sub level metrics may be grouped by other tags like collectionId, bucketId, etc
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
if (!empty($filters)) {
$filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters)));
} else {
$filters = '';
}
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
if (!empty($filters)) {
$filters = ' AND ' . implode(' AND ', array_map(fn ($filter, $value) => "\"{$filter}\"='{$value}'", array_keys($filters), array_values($filters)));
} else {
$filters = '';
}
$query = "SELECT sum(value) AS \"value\" FROM \"{$table}\" WHERE \"time\" > '{$start}' AND \"time\" < '{$end}' AND \"metric_type\"='counter' {$filters} GROUP BY time({$period['key']}), \"projectId\" {$groupBy} FILL(null)";
$query = "SELECT sum(value) AS \"value\" FROM \"{$table}\" WHERE \"time\" > '{$start}' AND \"time\" < '{$end}' AND \"metric_type\"='counter' {$filters} GROUP BY time({$period['key']}), \"projectId\" {$groupBy} FILL(null)";
try {
$result = $database->query($query);
$points = $result->getPoints();
@ -362,124 +364,305 @@ $cli
}
}
}
} catch (\Exception $e) {
Console::warning("Failed to Query: {$e->getMessage()}");
}
}
}
if ($iterations % 30 != 0) { // Aggregate aggregate number of objects in database only after 15 minutes
$iterations++;
$loopTook = microtime(true) - $loopStart;
$now = date('d-m-Y H:i:s', time());
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
return;
}
/**
* Aggregate MariaDB every 15 minutes
* Some of the queries here might contain full-table scans.
*/
if ($iterations % 30 === 0) { // Every 15 minutes aggregate number of objects in database
$now = date('d-m-Y H:i:s', time());
Console::info("[{$now}] Aggregating database counters.");
$latestProject = null;
do { // Loop over all the projects
$attempts = 0;
$max = 10;
$sleep = 1;
$latestProject = null;
do { // Loop over all the projects
$attempts = 0;
$max = 10;
$sleep = 1;
do { // list projects
try {
$attempts++;
$projects = $dbForConsole->find('projects', [], 100, cursor: $latestProject);
break; // leave the do-while if successful
} catch (\Exception $e) {
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception('Failed access console db: ' . $e->getMessage());
}
sleep($sleep);
do { // list projects
try {
$attempts++;
$projects = $dbForConsole->find('projects', [], 100, cursor: $latestProject);
break; // leave the do-while if successful
} catch (\Exception $e) {
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception('Failed access console db: ' . $e->getMessage());
}
} while ($attempts < $max);
sleep($sleep);
}
} while ($attempts < $max);
if (empty($projects)) {
continue;
if (empty($projects)) {
continue;
}
$latestProject = $projects[array_key_last($projects)];
foreach ($projects as $project) {
$projectId = $project->getId();
// Get total storage
$dbForProject->setNamespace('_project_' . $projectId);
$storageTotal = $dbForProject->sum('tags', 'size');
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_storage.tags.total'); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'period' => '30m',
'time' => $time,
'metric' => 'storage.tags.total',
'value' => $storageTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $storageTotal)
);
}
$latestProject = $projects[array_key_last($projects)];
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_storage.tags.total'); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'period' => '1d',
'time' => $time,
'metric' => 'storage.tags.total',
'value' => $storageTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $storageTotal)
);
}
foreach ($projects as $project) {
$projectId = $project->getId();
// Get total storage
$dbForProject->setNamespace('_project_' . $projectId);
$storageTotal = $dbForProject->sum('deployments', '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
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'period' => '30m',
'time' => $time,
'metric' => 'storage.deployments.total',
'value' => $storageTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $storageTotal)
);
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_storage.deployments.total'); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'period' => '1d',
'time' => $time,
'metric' => 'storage.deployments.total',
'value' => $storageTotal,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $storageTotal)
);
}
$collections = [
'users' => [
'namespace' => '',
],
'collections' => [
'metricPrefix' => 'database',
'namespace' => '',
'subCollections' => [ // Some collections, like collections and later buckets have child collections that need counting
'documents' => [
'namespace' => '',
],
$collections = [
'users' => [
'namespace' => '',
],
'collections' => [
'metricPrefix' => 'database',
'namespace' => '',
'subCollections' => [ // Some collections, like collections and later buckets have child collections that need counting
'documents' => [
'collectionPrefix' => 'collection_',
'namespace' => '',
],
],
'buckets' => [
'metricPrefix' => 'storage',
'namespace' => '',
'subCollections' => [
'files' => [
'namespace' => '',
'collectionPrefix' => 'bucket_',
'sum' => [
'field' => 'sizeOriginal'
]
],
]
],
'buckets' => [
'metricPrefix' => 'storage',
'namespace' => '',
'subCollections' => [
'files' => [
'namespace' => '',
'collectionPrefix' => 'bucket_',
'sum' => [
'field' => 'sizeOriginal'
]
],
]
];
]
];
foreach ($collections as $collection => $options) {
try {
foreach ($collections as $collection => $options) {
try {
$dbForProject->setNamespace("_project_{$projectId}");
$count = $dbForProject->count($collection);
$metricPrefix = $options['metricPrefix'] ?? '';
$metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
$subCollections = $options['subCollections'] ?? [];
if (empty($subCollections)) {
continue;
}
$latestParent = null;
$subCollectionCounts = []; //total project level count of sub collections
$subCollectionTotals = []; //total project level sum of sub collections
do { // Loop over all the parent collection document for each sub collection
$dbForProject->setNamespace("_project_{$projectId}");
$count = $dbForProject->count($collection);
$metricPrefix = $options['metricPrefix'] ?? '';
$metric = empty($metricPrefix) ? "{$collection}.count" : "{$metricPrefix}.{$collection}.count";
$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)) {
continue;
}
$latestParent = $parents[array_key_last($parents)];
foreach ($parents as $parent) {
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
$dbForProject->setNamespace("_project_{$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}");
$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
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
// check if sum calculation is required
$sum = $subOptions['sum'] ?? [];
if(empty($sum)) {
continue;
}
$dbForProject->setNamespace("_project_{$projectId}");
$total = (int) $dbForProject->sum(($subOptions['collectionPrefix'] ?? '') . $parent->getId(), $sum['field']);
$subCollectionTotals[$subCollection] = ($ssubCollectionTotals[$subCollection] ?? 0) + $total; // Project level sum for sub collections like storage.total
$dbForProject->setNamespace("_project_{$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
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $total,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $total));
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $total,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $total));
}
}
}
} while (!empty($parents));
/**
* Inserting project level counts for sub collections like database.documents.count
*/
foreach ($subCollectionCounts as $subCollection => $count) {
$dbForProject->setNamespace("_project_{$projectId}");
$metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
@ -520,227 +703,56 @@ $cli
$document->setAttribute('value', $count)
);
}
$subCollections = $options['subCollections'] ?? [];
if (empty($subCollections)) {
continue;
}
$latestParent = null;
$subCollectionCounts = []; //total project level count of sub collections
$subCollectionTotals = []; //total project level sum of sub collections
do { // Loop over all the parent collection document for each sub collection
$dbForProject->setNamespace("_project_{$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)) {
continue;
}
$latestParent = $parents[array_key_last($parents)];
foreach ($parents as $parent) {
foreach ($subCollections as $subCollection => $subOptions) { // Sub collection counts, like database.collections.collectionId.documents.count
$dbForProject->setNamespace("_project_{$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}");
$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
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
// check if sum calculation is required
$sum = $subOptions['sum'] ?? [];
if(empty($sum)) {
continue;
}
$dbForProject->setNamespace("_project_{$projectId}");
$total = (int) $dbForProject->sum(($subOptions['collectionPrefix'] ?? '') . $parent->getId(), $sum['field']);
$subCollectionTotals[$subCollection] = ($ssubCollectionTotals[$subCollection] ?? 0) + $total; // Project level sum for sub collections like storage.total
$dbForProject->setNamespace("_project_{$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
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $total,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $total));
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $total,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $total));
}
}
}
} while (!empty($parents));
/**
* Inserting project level counts for sub collections like database.documents.count
*/
foreach ($subCollectionCounts as $subCollection => $count) {
$dbForProject->setNamespace("_project_{$projectId}");
$metric = empty($metricPrefix) ? "{$subCollection}.count" : "{$metricPrefix}.{$subCollection}.count";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument(
'stats',
$document->getId(),
$document->setAttribute('value', $count)
);
}
}
/**
* Inserting project level sums for sub collections like storage.total
*/
foreach ($subCollectionTotals as $subCollection => $count) {
$dbForProject->setNamespace("_project_{$projectId}");
$metric = empty($metricPrefix) ? "{$subCollection}.total" : "{$metricPrefix}.{$subCollection}.total";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $count));
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $count));
}
}
} catch (\Exception$e) {
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
}
/**
* Inserting project level sums for sub collections like storage.total
*/
foreach ($subCollectionTotals as $subCollection => $count) {
$dbForProject->setNamespace("_project_{$projectId}");
$metric = empty($metricPrefix) ? "{$subCollection}.total" : "{$metricPrefix}.{$subCollection}.total";
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '30m',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $count));
}
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
$id = \md5($time . '_1d_' . $metric); //Construct unique id for each metric using time, period and metric
$document = $dbForProject->getDocument('stats', $id);
if ($document->isEmpty()) {
$dbForProject->createDocument('stats', new Document([
'$id' => $id,
'time' => $time,
'period' => '1d',
'metric' => $metric,
'value' => $count,
'type' => 1,
]));
} else {
$dbForProject->updateDocument('stats', $document->getId(),
$document->setAttribute('value', $count));
}
}
} catch (\Exception$e) {
Console::warning("Failed to save database counters data for project {$collection}: {$e->getMessage()}");
}
}
} while (!empty($projects));
}
}
} while (!empty($projects));
$iterations++;
$loopTook = microtime(true) - $loopStart;

View file

@ -251,8 +251,10 @@
</span>
<div class="pull-start margin-end avatar-container">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=60&height=60&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" data-ls-if="{{session.clientCode|lowercase}} !== 'cli'"/>
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" data-ls-if="{{session.clientCode|lowercase}} === 'cli'" >
<div data-ls-if="{{session.provider}} !== 'email'" class="corner">
<img data-ls-attrs="src=/images/users/{{session.provider}}.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.provider}},alt={{session.provider}}" class="avatar xs" loading="lazy" width="30" height="30" />
</div>
@ -317,7 +319,10 @@
<td data-title="Date: "><span data-ls-bind="{{log.time|dateTime}}"></span></td>
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
<td data-title="Client: ">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" data-ls-if="{{log.clientCode|lowercase}} !== 'cli'"/>
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" />
<span data-ls-if="(!{{log.clientName}})">Unknown</span>
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
</td>

View file

@ -10,7 +10,7 @@ $version = $this->getParam('version', '') . '.' . APP_CACHE_BUSTER;
data-analytics-event="click"
data-analytics-category="console/footer"
data-analytics-label="GitHub Link"
href="https://github.com/appwrite/appwrite" target="_blank" rel="noopener"><i class="icon-github-circled"></i> GitHub</a>
href="https://github.com/appwrite/appwrite" target="_blank" rel="noopener"><i class="icon-github"></i> GitHub</a>
</li>
<li>
<a class="link-animation-enabled"

View file

@ -58,7 +58,7 @@
<i class="icon-sun-inv force-light"></i>
<i class="icon-moon-inv force-dark"></i>
</button>
</div>
</div>
</span>
</li>
</ul>
@ -222,3 +222,38 @@
</footer>
</form>
</div>
<section class="upload-box" x-data x-show="$store.uploader.files().length > 0">
<div class="upload-box-header">
<h4 class="upload-box-title">
<span class="text">Uploading files</span>
<span class="amount" x-text="$store.uploader.files().length"></span>
</h4>
<button class="icon-button" :class="$store.uploader.isOpen ? '' : 'is-open'" aria-label="toggle upload box" @click="$store.uploader.toggle()"><span class="icon-chevron-down" aria-hidden="true"></span></button>
<button class="icon-button" @click="$store.uploader.cancelAll()" aria-label="close upload box"><span class="icon-x" aria-hidden="true"></span></button>
</div>
<div class="upload-box-content" :class="$store.uploader.isOpen ? 'is-open' : ''">
<ul class="upload-box-list">
<template x-if="$store.uploader.files().length > 0">
<template x-for="file in $store.uploader.files()" :key="file.id">
<li x-show="!file.cancelled" class="upload-box-item">
<div class="upload-image u-margin-inline-end-16" :class="file.completed ? 'is-finished' : ''">
<div class="progress"
:style="'--progress-value:' + file.progress"
:aria-valuenow="file.progress"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"></div>
<span x-show="!file.completed" class="icon" x-text="file.progress + '%'"></span>
<span x-show="file.completed" class="icon icon-file"></span>
</div>
<label for="file1" class="file-name trim-inner-text" x-text="file.name" :title="file.name"></label>
<span class="pill is-failed" x-show="file.failed" :title="file.error">failed</span>
<button x-show="file.completed" class="icon-button is-success" aria-label="Uploading"><span class="icon-check"></span></button>
<button x-show="!file.completed" class="icon-button" aria-label="Uploading" @click="$store.uploader.removeFile(file.id)"><span class="icon-x"></span></button>
</li>
</template>
</template>
</ul>
</div>
</section>

View file

@ -0,0 +1,6 @@
<div x-data id="upload-modal">
<template x-for="$store.uploader.files as file">
<p x-text="file"></p>
</template>
<button @click="$store.uploader.addFile('file-x')">add</button>
</div>

View file

@ -541,7 +541,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
<input type="hidden" data-forms-key-value data-ls-attrs="name={{$index}}" data-ls-bind="{{var}}" />
</div>
<div class="col span-2">
<button type="button" data-remove class="reverse danger round pull-end"><i class="icon-cancel"></i></button>
<button type="button" data-remove class="close pull-end is-margin-top-10"><i class="icon-cancel"></i></button>
</div>
</div>
</div>
@ -554,7 +554,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
<input type="hidden" data-ls-attrs="data-forms-key-value"/>
</div>
<div class="col span-2">
<button type="button" data-remove class="reverse danger round pull-end"><i class="icon-cancel"></i></button>
<button type="button" data-remove class="close pull-end is-margin-top-10"><i class="icon-cancel"></i></button>
</div>
</div>
</div>

View file

@ -116,7 +116,7 @@ $home = $this->getParam('home', '');
data-analytics
data-analytics-type="click"
data-analytics-category="console/home"
data-analytics-label="GitHub Link"><i class="icon-github-circled"></i></a>
data-analytics-label="GitHub Link"><i class="icon-github"></i></a>
</div>
</div>
<div class="col span-6 margin-bottom">

View file

@ -241,11 +241,6 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
</p>
<?php endif; ?>
</div>
<hr />
<div class="text-align-center text-size-small text-bold text-success" data-ls-if="!!({{console-project.serviceStatusFor<?php echo ucFirst($this->escape($key)); ?>}})">Enabled</div>
<div class="text-align-center text-size-small text-bold text-danger" data-ls-if="(!{{console-project.serviceStatusFor<?php echo ucFirst($this->escape($key)); ?>}})">Disabled</div>
</div>
</li>
<?php endforeach; ?>
@ -548,7 +543,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
<div class="margin-bottom-tiny">
<span data-ls-bind="{{member.name}}"></span> &nbsp;&nbsp;<span class="tag" data-ls-bind="{{member.roles.0}}"></span> &nbsp;&nbsp;<span data-ls-if="false === {{member.confirm}}" class="tag red">Pending Approval</span>
</div>
<span class="text-size-small text-fade" data-ls-bind="{{member.email}}"></small>
<span class="text-size-small text-fade" data-ls-bind="{{member.email}}"></span>
</li>
</ul>
</div>

View file

@ -81,7 +81,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<div class="margin-bottom-small text-align-end text-size-small text-fade"><span data-ls-bind="{{project-files.sum}}"></span> files found</div>
<div class="box margin-bottom">
<table class="vertical">
<table class="vertical" style="overflow: visible">
<thead>
<tr>
<th width="40"></th>
@ -89,128 +89,143 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<th width="140">Type</th>
<th width="100">Size</th>
<th width="120">Created</th>
<th width="52"></th>
</tr>
</thead>
<tbody data-ls-loop="project-files.files" data-ls-as="file">
<tr>
<td class="hide">
<img src="" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" />
<img src="" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="pull-start avatar" width="30" height="30" loading="lazy" alt="" />
</td>
<td data-title="Name: " class="text-one-liner" data-ls-attrs="title={{file.name}}" >
<div data-ui-modal class="box modal sticky-footer width-large close" data-button-text="{{file.name}}" data-button-class="link" data-button-element="span">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<td class="col-name" data-title="Name: " data-ls-attrs="title={{file.name}}" >
<div class="trim-inner-text">
<span data-ls-ui-trigger="modal-file-update-{{file.$id}}" class="link text-one-liner" data-ls-bind="{{file.name}}"></span>
<div data-ls-attrs="data-open-event=modal-file-update-{{file.$id}}" data-button-hide="1" data-ui-modal class="box modal sticky-footer width-large close" >
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Update File</h1>
<h1>Update File</h1>
<hr />
<hr />
<div class="row responsive modalize">
<div class="col span-8">
<form class="strip"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Storage File"
data-service="storage.updateFile"
data-event="file-update-{{file.$id}}"
data-scope="sdk"
data-success="alert,trigger"
data-success-param-alert-text="File updated successfully"
data-success-param-trigger-events="storage.updateFile"
data-failure="alert"
data-failure-param-alert-text="Failed to update file"
data-failure-param-alert-classname="error">
<div class="row responsive modalize">
<div class="col span-8">
<form class="strip"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Storage File"
data-service="storage.updateFile"
data-event="file-update-{{file.$id}}"
data-scope="sdk"
data-success="alert,trigger"
data-success-param-alert-text="File updated successfully"
data-success-param-trigger-events="storage.updateFile"
data-failure="alert"
data-failure-param-alert-text="Failed to update file"
data-failure-param-alert-classname="error">
<label for="files-fileId">File ID</label>
<div class="input-copy">
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
<label for="files-fileId">File ID</label>
<div class="input-copy">
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
</div>
<input type="hidden" data-ls-attrs="id=file-bucketId-{{file.$id}}" name="bucketId" data-ls-bind="{{file.bucketId}}">
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$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="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$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>
</form>
<form class="strip"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete File"
data-service="storage.deleteFile"
data-scope="sdk"
data-event="file-delete-{{file.$id}}"
data-confirm="Are you sure you want to delete this file?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted file successfully"
data-success-param-trigger-events="storage.deleteFile"
data-failure="alert"
data-failure-param-alert-text="Failed to delete file"
data-failure-param-alert-classname="error">
<input type="hidden" name="bucketId" data-ls-bind="{{file.bucketId}}" />
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
</form>
</div>
<div class="col span-4 text-size-small">
<div class="margin-bottom-small">File Preview</div>
<div class="margin-bottom-small">
<div data-ls-if="{{file.chunksTotal}} != {{file.chunksUploaded}}" class="preview-box">
<div class="preview-box-image">
<img src="/images/default_preview.svg" width="200" height="100" alt=""/>
</div>
<span class="preview-box-text">Preview not available</span>
</div>
<img data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" src="" class="file-preview" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="225" height="160" />
</div>
<input type="hidden" data-ls-attrs="id=file-bucketId-{{file.$id}}" name="bucketId" data-ls-bind="{{file.bucketId}}">
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$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>
<div class="margin-bottom-tiny">
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
</div>
<label for="file-write">Write Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
<input type="hidden" data-ls-attrs="id=file-write-{{file.$id}}" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$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>
</form>
<div class="margin-bottom-small">
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
</div>
<form class="strip"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete File"
data-service="storage.deleteFile"
data-scope="sdk"
data-event="file-delete-{{file.$id}}"
data-confirm="Are you sure you want to delete this file?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted file successfully"
data-success-param-trigger-events="storage.deleteFile"
data-failure="alert"
data-failure-param-alert-text="Failed to delete file"
data-failure-param-alert-classname="error">
<input type="hidden" name="bucketId" data-ls-bind="{{file.bucketId}}" />
<input type="hidden" name="fileId" data-ls-bind="{{file.$id}}" />
</form>
</div>
<div data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" class="col span-4 text-size-small">
<div class="margin-bottom-small">File Preview</div>
<div class="margin-bottom-small">
<img src="" class="file-preview" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/preview?width=350&height=250&project={{router.params.project}}&mode=admin" loading="lazy" width="225" height="160" />
</div>
<div class="margin-bottom-tiny">
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/view?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> New Window <i class="icon-link-ext"></i></a>
</div>
<div class="margin-bottom-small">
<a href="" data-ls-attrs="href={{env.ENDPOINT}}/v1/storage/buckets/{{router.params.id}}/files/{{file.$id}}/download?project={{router.params.project}}&mode=admin" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Download <i class="icon-link-ext"></i></a>
</div>
<div class="margin-bottom-tiny">
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Type: <span data-ls-bind="{{file.mimeType}}"></span>
</div>
<div class="margin-bottom-tiny">
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Size: <span data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
<span data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
</div>
<div class="margin-bottom">
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.dateCreated|dateText}}"></span>
<div class="margin-bottom-tiny">
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Type: <span data-ls-bind="{{file.mimeType}}"></span>
</div>
<div class="margin-bottom-tiny">
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Size: <span data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
<span data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
</div>
<div class="margin-bottom">
<i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Created at: <span data-ls-bind="{{file.dateCreated|dateText}}"></span>
</div>
</div>
</div>
<footer>
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
<button type="button" data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
</footer>
</div>
<footer>
<button class="link pull-end text-danger" data-ls-ui-trigger="file-delete-{{file.$id}},modal-close">Delete File</button>
<button type="button" data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" data-ls-ui-trigger="file-update-{{file.$id}},modal-close">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse desktops-only-inline tablets-only-inline">Cancel</button>
</footer>
<span class="pill is-pending" data-ls-if="{{file.chunksTotal}} != {{file.chunksUploaded}}">pending</span>
</div>
</td>
<td data-title="Type: ">
<span class="text-fade text-size-small" data-ls-bind="{{file.mimeType}}"></span>
</td>
<td data-title="Size: ">
<div data-ls-if="{{file.chunksTotal}} == {{file.chunksUploaded}}" >
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
</div>
<span class="text-fade text-size-small" data-ls-if="{{file.chunksTotal}} != {{file.chunksUploaded}}">incomplete</span>
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileSize}}"></span>
<span class="text-fade text-size-small" data-ls-bind="{{file.sizeOriginal|humanFileUnit}}"></span>
</td>
<td data-title="Created: ">
<span class="text-fade text-size-small" data-ls-bind="{{file.dateCreated|dateText}}"></span>
</td>
<td data-title="" class="cell-options-more" style="overflow: visible">
<div class="drop-list end" data-ls-ui-open="" data-button-aria="File Options" data-button-class="icon-dot-3 reset-inner-button" data-blur="1">
<ul class="no-arrow">
<li data-ls-ui-trigger="modal-file-update-{{file.$id}}"><span class="margin-start-small icon icon-edit"></span> <span class="link">Update</span></li>
<li data-ls-ui-trigger="file-delete-{{file.$id}}"><span class="margin-start-small icon icon-trash-2"></span><span class="link">Delete</span></li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="pull-end text-align-center paging">
<form
data-service="storage.listFiles"
@ -254,16 +269,9 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Storage File"
data-service="storage.createFile"
data-event="submit"
data-scope="sdk"
data-loading="Uploading File..."
data-success="alert,trigger,reset"
data-success-param-alert-text="File uploaded successfully"
data-success-param-trigger-events="storage.createFile"
data-failure="alert"
data-failure-param-alert-text="Failed to upload file"
data-failure-param-alert-classname="error">
x-data
@submit.prevent="$store.uploader.uploadFile($event.target)"
>
<input type="hidden" name="bucketId" id="files-bucketId" data-ls-bind="{{router.params.id}}">
<label for="fileId">File ID</label>

View file

@ -35,7 +35,6 @@
<li class="margin-bottom">
<a data-ls-attrs="href=/console/storage/bucket?id={{bucket.$id}}&project={{router.params.project}}" class="box">
<div data-ls-bind="{{bucket.name}}" class="text-one-liner margin-bottom text-bold">&nbsp;</div>
<i class="icon-right-open"></i>
</a>
</li>

View file

@ -79,13 +79,13 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
</td>
<td data-title="Name: ">
<a data-ls-attrs="href=/console/users/user?id={{user.$id}}&project={{router.params.project}}">
<span data-ls-bind="{{user.name}}"></span>
<span data-ls-bind="{{user.name}}" data-ls-attrs="title={{user.name}}"></span>
<span data-ls-if="{{user.name|escape}} === '' && {{user.email}} !== ''">Unknown</span>
<span data-ls-if="{{user.name|escape}} === '' && {{user.email}} === ''">Anonymous User</span>
</a>
</td>
<td data-title="Email: ">
<small data-ls-bind="{{user.email}}"></span>
<small data-ls-bind="{{user.email}}" data-ls-attrs="title={{user.email}}"></span>
</td>
<td data-title="Status: ">
<span data-ls-if="{{user.emailVerification}} === true && {{user.status}} === true">
@ -245,7 +245,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
<img src="" data-ls-attrs="src={{team.name|avatar}}" data-size="45" alt="Collection Avatar" class="avatar margin-end pull-start" loading="lazy" width="30" height="30" />
</td>
<td data-title="Name: ">
<a data-ls-attrs="href=/console/users/teams/team?id={{team.$id}}&project={{router.params.project}}" data-ls-bind="{{team.name}}"></a>
<a data-ls-attrs="href=/console/users/teams/team?id={{team.$id}}&project={{router.params.project}}" data-ls-bind="{{team.name}}" data-ls-attrs="title={{team.name}}"></a>
</td>
<td data-title="Members: "><span data-ls-bind="{{team.sum}} members"></span></td>
<td data-title="Date Created: "><small data-ls-bind="{{team.dateCreated|dateText}}"></small></td>
@ -406,7 +406,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
<img src="<?php echo $this->escape($icon); ?>?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="Email/Password Logo" class="pull-start provider margin-end" />
<span class="text-size-small"><?php echo $this->escape($name); ?><?php if(!$enabled): ?> <spann class="text-fade text-size-xs">soon</span><?php endif; ?>
<span class="text-size-small"><?php echo $this->escape($name); ?><?php if(!$enabled): ?> <span class="text-fade text-size-xs">soon</span><?php endif; ?>
<?php if( in_array($key, ['usersAuthMagicURL', 'usersAuthInvites']) && !$smtpEnabled): ?>
<p class="margin-bottom-no text-one-liner text-size-small text-danger">

View file

@ -295,8 +295,8 @@
data-param-user-id="{{router.params.id}}"
data-event="load,users.update">
<div data-ls-if="{{sessions.sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
No sessions available.
<div class="box margin-top margin-bottom" data-ls-if="{{sessions.sessions.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
<h3 class="text-bold margin-bottom-no">No sessions available.</h3>
</div>
<div data-ls-if="{{sessions.sessions.length}} !== 0" style="display: none">
@ -372,8 +372,8 @@
data-param-user-id="{{router.params.id}}"
data-event="load,logs-load">
<div data-ls-if="{{logs.logs.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
No logs available.
<div class="box margin-top margin-bottom" data-ls-if="{{logs.logs.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
<h3 class="text-bold margin-bottom-no">No logs available.</h3>
</div>
<div class="box" data-ls-if="{{logs.logs.length}} !== 0" style="display: none">
@ -394,7 +394,7 @@
<td data-title="Client: ">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} !== 'cli'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
<div data-ls-if="(!{{log.clientName}})" class="text-align-center text-fade">Unknown</div>

View file

@ -541,10 +541,30 @@ class DeletesV1 extends Worker
protected function deleteBucket(Document $document, string $projectId)
{
$bucketId = $document->getId();
$dbForProject = $this->getProjectDB($projectId);
$dbForProject->deleteCollection('bucket_' . $bucketId);
$device = $this->getFilesDevice($projectId);
$device->deletePath($bucketId);
$dbForProject->deleteCollection('bucket_' . $document->getInternalId());
$device = new Local(APP_STORAGE_UPLOADS.'/app-'.$projectId);
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', '');
$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', '');
$doSpacesAcl = 'private';
$device = new DOSpaces(APP_STORAGE_UPLOADS . '/app-' . $projectId, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
break;
}
$device->deletePath($document->getId());
}
}

View file

@ -43,7 +43,7 @@
"utopia-php/analytics": "0.2.*",
"utopia-php/audit": "0.8.*",
"utopia-php/cache": "0.4.*",
"utopia-php/cli": "0.11.*",
"utopia-php/cli": "0.12.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.14.*",
"utopia-php/locale": "0.4.*",

32
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "92614eab8d44998df216484f617c2026",
"content-hash": "71d2e3bdd2ee9ed2bd4c8ae2e62ea1a2",
"packages": [
{
"name": "adhocore/jwt",
@ -2025,16 +2025,16 @@
},
{
"name": "utopia-php/cli",
"version": "0.11.0",
"version": "0.12.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cli.git",
"reference": "c7a6908a8dbe9234b8b2c954e5487d34cb079af6"
"reference": "6d164b752efeb1ca089e3a517bc274d8b383474b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/c7a6908a8dbe9234b8b2c954e5487d34cb079af6",
"reference": "c7a6908a8dbe9234b8b2c954e5487d34cb079af6",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/6d164b752efeb1ca089e3a517bc274d8b383474b",
"reference": "6d164b752efeb1ca089e3a517bc274d8b383474b",
"shasum": ""
},
"require": {
@ -2072,9 +2072,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cli/issues",
"source": "https://github.com/utopia-php/cli/tree/0.11.0"
"source": "https://github.com/utopia-php/cli/tree/0.12.0"
},
"time": "2021-04-16T15:16:08+00:00"
"time": "2022-02-18T22:10:41+00:00"
},
{
"name": "utopia-php/config",
@ -2464,21 +2464,21 @@
},
{
"name": "utopia-php/orchestration",
"version": "0.4.0",
"version": "0.4.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/orchestration.git",
"reference": "6f25a47e6d6b14540853b62db43c676aecf80519"
"reference": "67cf0ab15a096d274c093ea918aa4ace14ac7af7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/6f25a47e6d6b14540853b62db43c676aecf80519",
"reference": "6f25a47e6d6b14540853b62db43c676aecf80519",
"url": "https://api.github.com/repos/utopia-php/orchestration/zipball/67cf0ab15a096d274c093ea918aa4ace14ac7af7",
"reference": "67cf0ab15a096d274c093ea918aa4ace14ac7af7",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/cli": "0.11.*"
"utopia-php/cli": "0.12.*"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
@ -2513,9 +2513,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/orchestration/issues",
"source": "https://github.com/utopia-php/orchestration/tree/0.4.0"
"source": "https://github.com/utopia-php/orchestration/tree/0.4.1"
},
"time": "2022-02-17T11:48:37+00:00"
"time": "2022-02-20T09:23:06+00:00"
},
{
"name": "utopia-php/preloader",
@ -3075,7 +3075,7 @@
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator",
"reference": "b977fcf357a267f41299539ac9095aa7bbcc2600"
"reference": "86a8e495b14c6f112de71fa6c500d36dcded8e95"
},
"require": {
"ext-curl": "*",
@ -3110,7 +3110,7 @@
}
],
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"time": "2022-02-15T11:09:40+00:00"
"time": "2022-02-22T06:42:40+00:00"
},
{
"name": "composer/pcre",

View file

@ -72,6 +72,7 @@ services:
- ./app:/usr/src/code/app
# - ./vendor:/usr/src/code/vendor
- ./docs:/usr/src/code/docs
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
# - ./debug:/tmp
- ./dev:/usr/local/dev

View file

@ -0,0 +1 @@
# Change Log

View file

@ -0,0 +1,156 @@
## Getting Started
Before you can use the CLI, you need to login to your Appwrite account.
```sh
$ appwrite login
? Enter your email test@test.com
? Enter your password ********
✓ Success
```
This will also prompt you to enter your Appwrite endpoint ( default: http://localhost/v1 )
* ### Initialising your project
Once logged in, the CLI needs to be initialised before you can use it with your Appwrite project. You can do this with the `appwrite init project` command.
```sh
$ appwrite init project
```
The following prompt will guide you through the setup process. The `init` command also creates an `appwrite.json` file representing your Appwrite project.
The `appwrite.json` file does a lot of things.
* Provides context to the CLI
* Keeps track of all your cloud functions
* Keeps track of all your project's collections
* Helps you deploy your Appwrite project to production and more..
You can also fetch all the collections in your current project using
```sh
appwrite init collection
```
The CLI also comes with a convenient `--all` flag to perform both these steps at once.
```sh
appwrite init --all
```
* ### Creating and deploying cloud functions
The CLI makes it extremely easy to create and deploy Appwrite's cloud functions. Initialise your new function using
```
$ appwrite init function
? What would you like to name your function? My Awesome Function
? What runtime would you like to use? Node.js (node-15.5)
✓ Success
```
This will create a new function `My Awesome Function` in your current Appwrite project and also create a template function for you to get started.
```sh
$ tree My\ Awesome\ Function
My Awesome Function
├── README.md
├── index.js
├── package-lock.json
└── package.json
0 directories, 4 files
```
You can now deploy this function using
```sh
$ appwrite deploy function
? Which functions would you like to deploy? My Awesome Function (61d1a4c81dfcd95bc834)
Info Deploying function My Awesome Function ( 61d1a4c81dfcd95bc834 )
? Enter the entrypoint command node index.js
✓ Success Deployed My Awesome Function ( 61d1a4c81dfcd95bc834 )
```
Your function is now ready to be executed on your Appwrite server!
* ### Deploying Collections
Similarly, you can deploy all your collections to your Appwrite server using
```sh
appwrite deploy collections
```
The `deploy` command also comes with a convenient `--all` flag to deploy all your functions and collections at once.
```sh
appwrite deploy --all
```
> ### Note
> By default, requests to domains with self signed SSL certificates (or no certificates) are disabled. If you trust the domain, you can bypass the certificate validation using
```sh
$ appwrite client --selfSigned true
```
## Usage
The Appwrite CLI follows the following general syntax.
```sh
$ appwrite [COMMAND] --[OPTIONS]
```
A few sample commands to get you started
```sh
$ appwrite users create --userId "unique()" --email hello@appwrite.io --password very_strong_password
$ appwrite users list
```
To create a document you can use the following command
```sh
$ appwrite database createDocument --collectionId <ID> --documentId 'unique()' --data '{ "Name": "Iron Man" }' --read role:all team:abc
```
### Some Gotchas
- `data` must be a valid JSON string where each key and value are enclosed in double quotes `"` like the example above.
- Some arguments like the `read` and `write` permissions are expected to be arrays. In the Appwrite CLI, array values are passed in using space as a separator like in the example above.
To get information about the different services available, you can use
```sh
$ appwrite -h
```
To get information about a particular service and the commands available in a service you can use
```sh
$ appwrite users // or
$ appwrite users --help // or
$ appwrite users help // or
$ appwrite accounts
```
To get information about a particular command and the parameters it accepts, you can use
```sh
$ appwrite users list --help
$ appwrite account get --help
```
At any point, you can view or reset the CLI configuration using the `client` service.
```
$ appwrite client --debug
// This will display your endpoint, projectID, API key and so on.
$ appwrite client --reset
```
## CI mode
The Appwrite CLI can also work in a CI environment. The initialisation of the CLI works a bit differently in CI. In CI, you set your `endpoint`, `projectId` and `API Key` using
```sh
appwrite client --endpoint http://localhost/v1 --projectId <PROJECT_ID> --key <API KEY>
```

View file

@ -33,6 +33,7 @@ const configApp = {
'public/scripts/routes.js',
'public/scripts/filters.js',
'public/scripts/app.js',
'public/scripts/upload-modal.js',
'public/scripts/views/service.js',

View file

@ -313,7 +313,30 @@ let path='/functions/{functionId}/executions'.replace('{functionId}',functionId)
if(typeof async!=='undefined'){payload['async']=async;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getExecution:(functionId,executionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof executionId==='undefined'){throw new AppwriteException('Missing required parameter: "executionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateTag:(functionId,tag)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof tag==='undefined'){throw new AppwriteException('Missing required parameter: "tag"');}
let path='/functions/{functionId}/tag'.replace('{functionId}',functionId);let payload={};if(typeof tag!=='undefined'){payload['tag']=tag;}
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),listTags:(functionId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createTag:(functionId,command,code,onProgress=(progress)=>{})=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof command==='undefined'){throw new AppwriteException('Missing required parameter: "command"');}
if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');}
let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof command!=='undefined'){payload['command']=command;}
if(typeof code!=='undefined'){payload['code']=code;}
const uri=new URL(this.config.endpoint+path);const size=code.size;if(size<=Appwrite.CHUNK_SIZE){return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Appwrite.CHUNK_SIZE);for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=code.slice(start,end+1);payload['code']=new File([stream],code.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUpploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getTag:(functionId,tagId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof tagId==='undefined'){throw new AppwriteException('Missing required parameter: "tagId"');}
let path='/functions/{functionId}/tags/{tagId}'.replace('{functionId}',functionId).replace('{tagId}',tagId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteTag:(functionId,tagId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof tagId==='undefined'){throw new AppwriteException('Missing required parameter: "tagId"');}
let path='/functions/{functionId}/tags/{tagId}'.replace('{functionId}',functionId).replace('{tagId}',tagId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.health={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/health';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getAntivirus:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/anti-virus';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCache:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/cache';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getDB:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/db';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueCertificates:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/certificates';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueFunctions:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/functions';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueLogs:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/logs';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueUsage:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/usage';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueWebhooks:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/webhooks';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getStorageLocal:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/storage/local';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getTime:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/time';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.locale={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getContinents:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/continents';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountries:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesEU:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/eu';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesPhones:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/phones';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCurrencies:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/currencies';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getLanguages:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/languages';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.projects={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/projects';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
@ -451,7 +474,7 @@ if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createBucket:(bucketId,name,permission,read,write,maximumFileSize,allowedFileExtensions,enabled,adapter,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createBucket:(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
let path='/storage/buckets';let payload={};if(typeof bucketId!=='undefined'){payload['bucketId']=bucketId;}
@ -459,23 +482,22 @@ if(typeof name!=='undefined'){payload['name']=name;}
if(typeof permission!=='undefined'){payload['permission']=permission;}
if(typeof read!=='undefined'){payload['read']=read;}
if(typeof write!=='undefined'){payload['write']=write;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof adapter!=='undefined'){payload['adapter']=adapter;}
if(typeof encryption!=='undefined'){payload['encryption']=encryption;}
if(typeof antivirus!=='undefined'){payload['antivirus']=antivirus;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getBucket:(bucketId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateBucket:(bucketId,name,permission,read,write,maximumFileSize,allowedFileExtensions,enabled,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateBucket:(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof permission!=='undefined'){payload['permission']=permission;}
if(typeof read!=='undefined'){payload['read']=read;}
if(typeof write!=='undefined'){payload['write']=write;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof encryption!=='undefined'){payload['encryption']=encryption;}
if(typeof antivirus!=='undefined'){payload['antivirus']=antivirus;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),deleteBucket:(bucketId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
@ -486,14 +508,20 @@ if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createFile:(bucketId,fileId,file,read,write)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createFile:(bucketId,fileId,file,read,write,onProgress=(progress)=>{})=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
if(typeof file==='undefined'){throw new AppwriteException('Missing required parameter: "file"');}
let path='/storage/buckets/{bucketId}/files'.replace('{bucketId}',bucketId);let payload={};if(typeof fileId!=='undefined'){payload['fileId']=fileId;}
if(typeof file!=='undefined'){payload['file']=file;}
if(typeof read!=='undefined'){payload['read']=read;}
if(typeof write!=='undefined'){payload['write']=write;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),getFile:(bucketId,fileId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
const uri=new URL(this.config.endpoint+path);const size=file.size;if(size<=Appwrite.CHUNK_SIZE){return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Appwrite.CHUNK_SIZE);if(fileId!='unique()'){try{response=yield this.call('GET',new URL(this.config.endpoint+path+'/'+fileId),headers);counter=response.chunksUploaded;}
catch(e){}}
for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=file.slice(start,end+1);payload['file']=new File([stream],file.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUpploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getFile:(bucketId,fileId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateFile:(bucketId,fileId,read,write)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
@ -635,7 +663,7 @@ throw new AppwriteException(e.message);}});}
flatten(data,prefix=''){let output={};for(const key in data){let value=data[key];let finalKey=prefix?`${prefix}[${key}]`:key;if(Array.isArray(value)){output=Object.assign(output,this.flatten(value,finalKey));}
else{output[finalKey]=value;}}
return output;}}
class Query{}
Appwrite.CHUNK_SIZE=5*1024*1024;class Query{}
Query.equal=(attribute,value)=>Query.addQuery(attribute,"equal",value);Query.notEqual=(attribute,value)=>Query.addQuery(attribute,"notEqual",value);Query.lesser=(attribute,value)=>Query.addQuery(attribute,"lesser",value);Query.lesserEqual=(attribute,value)=>Query.addQuery(attribute,"lesserEqual",value);Query.greater=(attribute,value)=>Query.addQuery(attribute,"greater",value);Query.greaterEqual=(attribute,value)=>Query.addQuery(attribute,"greaterEqual",value);Query.search=(attribute,value)=>Query.addQuery(attribute,"search",value);Query.addQuery=(attribute,oper,value)=>value instanceof Array?`${attribute}.${oper}(${value
.map((v) => Query.parseValues(v))
.join(",")})`:`${attribute}.${oper}(${Query.parseValues(value)})`;Query.parseValues=(value)=>typeof value==="string"||value instanceof String?`"${value}"`:`${value}`;exports.Appwrite=Appwrite;exports.Query=Query;Object.defineProperty(exports,'__esModule',{value:true});}(this.window=this.window||{},null,window));(function(global,factory){typeof exports==='object'&&typeof module!=='undefined'?module.exports=factory():typeof define==='function'&&define.amd?define(factory):(global=typeof globalThis!=='undefined'?globalThis:global||self,global.Chart=factory());})(this,(function(){'use strict';function fontString(pixelSize,fontStyle,fontFamily){return fontStyle+' '+pixelSize+'px '+fontFamily;}
@ -3727,8 +3755,14 @@ 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){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){window.location=url||"/";};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
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;}
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:"",});}
target.reset();try{const response=await sdk.storage.createFile(bucketId,fileId,file,read,write,(progress)=>{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;}
document.dispatchEvent(new CustomEvent('storage.createFile'));}catch(error){if(error==='USER_CANCELLED'){await sdk.storage.deleteFile(bucketId,id);this.updateFile(id,{cancelled:false,failed:true,});this.removeFile(id);}else{this.updateFile(id,{id:id,failed:true,error:error.message??error});}
document.dispatchEvent(new CustomEvent('storage.createFile'));}}});});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}else{return url;}}};keys=keys.split(",").map(element=>element.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(node=>{node=node.split("=");let key=node[0]||"";let name=node[1]||key;let value=getValue(key,"param",serviceForm);url=updateQueryString(name,value?value:null,url);});if(url!==window.location.href){window.history.pushState({},"",url);router.reset();}};},trigger:function(events){return function(document){events=events.trim().split(",");for(let i=0;i<events.length;i++){if(""===events[i]){continue;}
document.dispatchEvent(new CustomEvent(events[i]));}};},setId:function name(params){},default:function(){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&collection.$id===document.$id){for(const[key,value]of Object.entries(document)){delete document[key];}

View file

@ -313,7 +313,30 @@ let path='/functions/{functionId}/executions'.replace('{functionId}',functionId)
if(typeof async!=='undefined'){payload['async']=async;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getExecution:(functionId,executionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof executionId==='undefined'){throw new AppwriteException('Missing required parameter: "executionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateTag:(functionId,tag)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof tag==='undefined'){throw new AppwriteException('Missing required parameter: "tag"');}
let path='/functions/{functionId}/tag'.replace('{functionId}',functionId);let payload={};if(typeof tag!=='undefined'){payload['tag']=tag;}
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),listTags:(functionId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createTag:(functionId,command,code,onProgress=(progress)=>{})=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof command==='undefined'){throw new AppwriteException('Missing required parameter: "command"');}
if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');}
let path='/functions/{functionId}/tags'.replace('{functionId}',functionId);let payload={};if(typeof command!=='undefined'){payload['command']=command;}
if(typeof code!=='undefined'){payload['code']=code;}
const uri=new URL(this.config.endpoint+path);const size=code.size;if(size<=Appwrite.CHUNK_SIZE){return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Appwrite.CHUNK_SIZE);for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=code.slice(start,end+1);payload['code']=new File([stream],code.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUpploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getTag:(functionId,tagId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof tagId==='undefined'){throw new AppwriteException('Missing required parameter: "tagId"');}
let path='/functions/{functionId}/tags/{tagId}'.replace('{functionId}',functionId).replace('{tagId}',tagId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteTag:(functionId,tagId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof tagId==='undefined'){throw new AppwriteException('Missing required parameter: "tagId"');}
let path='/functions/{functionId}/tags/{tagId}'.replace('{functionId}',functionId).replace('{tagId}',tagId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.health={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/health';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getAntivirus:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/anti-virus';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCache:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/cache';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getDB:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/db';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueCertificates:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/certificates';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueFunctions:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/functions';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueLogs:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/logs';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueUsage:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/usage';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueWebhooks:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/webhooks';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getStorageLocal:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/storage/local';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getTime:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/time';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.locale={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getContinents:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/continents';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountries:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesEU:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/eu';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesPhones:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/phones';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCurrencies:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/currencies';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getLanguages:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/languages';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.projects={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/projects';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
@ -451,7 +474,7 @@ if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createBucket:(bucketId,name,permission,read,write,maximumFileSize,allowedFileExtensions,enabled,adapter,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createBucket:(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
let path='/storage/buckets';let payload={};if(typeof bucketId!=='undefined'){payload['bucketId']=bucketId;}
@ -459,23 +482,22 @@ if(typeof name!=='undefined'){payload['name']=name;}
if(typeof permission!=='undefined'){payload['permission']=permission;}
if(typeof read!=='undefined'){payload['read']=read;}
if(typeof write!=='undefined'){payload['write']=write;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof adapter!=='undefined'){payload['adapter']=adapter;}
if(typeof encryption!=='undefined'){payload['encryption']=encryption;}
if(typeof antivirus!=='undefined'){payload['antivirus']=antivirus;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getBucket:(bucketId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateBucket:(bucketId,name,permission,read,write,maximumFileSize,allowedFileExtensions,enabled,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateBucket:(bucketId,name,permission,read,write,enabled,maximumFileSize,allowedFileExtensions,encryption,antivirus)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof permission==='undefined'){throw new AppwriteException('Missing required parameter: "permission"');}
let path='/storage/buckets/{bucketId}'.replace('{bucketId}',bucketId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof permission!=='undefined'){payload['permission']=permission;}
if(typeof read!=='undefined'){payload['read']=read;}
if(typeof write!=='undefined'){payload['write']=write;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof maximumFileSize!=='undefined'){payload['maximumFileSize']=maximumFileSize;}
if(typeof allowedFileExtensions!=='undefined'){payload['allowedFileExtensions']=allowedFileExtensions;}
if(typeof enabled!=='undefined'){payload['enabled']=enabled;}
if(typeof encryption!=='undefined'){payload['encryption']=encryption;}
if(typeof antivirus!=='undefined'){payload['antivirus']=antivirus;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),deleteBucket:(bucketId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
@ -486,14 +508,20 @@ if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createFile:(bucketId,fileId,file,read,write)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createFile:(bucketId,fileId,file,read,write,onProgress=(progress)=>{})=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
if(typeof file==='undefined'){throw new AppwriteException('Missing required parameter: "file"');}
let path='/storage/buckets/{bucketId}/files'.replace('{bucketId}',bucketId);let payload={};if(typeof fileId!=='undefined'){payload['fileId']=fileId;}
if(typeof file!=='undefined'){payload['file']=file;}
if(typeof read!=='undefined'){payload['read']=read;}
if(typeof write!=='undefined'){payload['write']=write;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),getFile:(bucketId,fileId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
const uri=new URL(this.config.endpoint+path);const size=file.size;if(size<=Appwrite.CHUNK_SIZE){return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Appwrite.CHUNK_SIZE);if(fileId!='unique()'){try{response=yield this.call('GET',new URL(this.config.endpoint+path+'/'+fileId),headers);counter=response.chunksUploaded;}
catch(e){}}
for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=file.slice(start,end+1);payload['file']=new File([stream],file.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUpploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getFile:(bucketId,fileId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateFile:(bucketId,fileId,read,write)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
@ -635,7 +663,7 @@ throw new AppwriteException(e.message);}});}
flatten(data,prefix=''){let output={};for(const key in data){let value=data[key];let finalKey=prefix?`${prefix}[${key}]`:key;if(Array.isArray(value)){output=Object.assign(output,this.flatten(value,finalKey));}
else{output[finalKey]=value;}}
return output;}}
class Query{}
Appwrite.CHUNK_SIZE=5*1024*1024;class Query{}
Query.equal=(attribute,value)=>Query.addQuery(attribute,"equal",value);Query.notEqual=(attribute,value)=>Query.addQuery(attribute,"notEqual",value);Query.lesser=(attribute,value)=>Query.addQuery(attribute,"lesser",value);Query.lesserEqual=(attribute,value)=>Query.addQuery(attribute,"lesserEqual",value);Query.greater=(attribute,value)=>Query.addQuery(attribute,"greater",value);Query.greaterEqual=(attribute,value)=>Query.addQuery(attribute,"greaterEqual",value);Query.search=(attribute,value)=>Query.addQuery(attribute,"search",value);Query.addQuery=(attribute,oper,value)=>value instanceof Array?`${attribute}.${oper}(${value
.map((v) => Query.parseValues(v))
.join(",")})`:`${attribute}.${oper}(${Query.parseValues(value)})`;Query.parseValues=(value)=>typeof value==="string"||value instanceof String?`"${value}"`:`${value}`;exports.Appwrite=Appwrite;exports.Query=Query;Object.defineProperty(exports,'__esModule',{value:true});}(this.window=this.window||{},null,window));(function(global,factory){typeof exports==='object'&&typeof module!=='undefined'?module.exports=factory():typeof define==='function'&&define.amd?define(factory):(global=typeof globalThis!=='undefined'?globalThis:global||self,global.Chart=factory());})(this,(function(){'use strict';function fontString(pixelSize,fontStyle,fontFamily){return fontStyle+' '+pixelSize+'px '+fontFamily;}

View file

@ -689,8 +689,14 @@ 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){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){window.location=url||"/";};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
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;}
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:"",});}
target.reset();try{const response=await sdk.storage.createFile(bucketId,fileId,file,read,write,(progress)=>{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;}
document.dispatchEvent(new CustomEvent('storage.createFile'));}catch(error){if(error==='USER_CANCELLED'){await sdk.storage.deleteFile(bucketId,id);this.updateFile(id,{cancelled:false,failed:true,});this.removeFile(id);}else{this.updateFile(id,{id:id,failed:true,error:error.message??error});}
document.dispatchEvent(new CustomEvent('storage.createFile'));}}});});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}else{return url;}}};keys=keys.split(",").map(element=>element.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(node=>{node=node.split("=");let key=node[0]||"";let name=node[1]||key;let value=getValue(key,"param",serviceForm);url=updateQueryString(name,value?value:null,url);});if(url!==window.location.href){window.history.pushState({},"",url);router.reset();}};},trigger:function(events){return function(document){events=events.trim().split(",");for(let i=0;i<events.length;i++){if(""===events[i]){continue;}
document.dispatchEvent(new CustomEvent(events[i]));}};},setId:function name(params){},default:function(){let collection=container.get('project-collection');let document=container.get('project-document');if(collection&&document&&collection.$id===document.$id){for(const[key,value]of Object.entries(document)){delete document[key];}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
public/fonts/icon.eot Normal file

Binary file not shown.

408
public/fonts/icon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 208 KiB

View file

@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="0" height="0" style="display:none;"><symbol id="icon-angle-circled-left"></symbol><symbol id="icon-angle-circled-right"></symbol><symbol viewBox="0 0 20 20" id="icon-archive"><path d="M13.981 2H6.018s-.996 0-.996 1h9.955c0-1-.996-1-.996-1zm2.987 3c0-1-.995-1-.995-1H4.027s-.995 0-.995 1v1h13.936V5zm1.99 1l-.588-.592V7H1.63V5.408L1.041 6C.452 6.592.03 6.75.267 8c.236 1.246 1.379 8.076 1.549 9 .186 1.014 1.217 1 1.217 1h13.936s1.03.014 1.217-1c.17-.924 1.312-7.754 1.549-9 .235-1.25-.187-1.408-.777-2zM14 11.997c0 .554-.449 1.003-1.003 1.003H7.003A1.003 1.003 0 0 1 6 11.997V10h1v2h6v-2h1v1.997z"></path></symbol><symbol id="icon-article"></symbol><symbol id="icon-asterisk"></symbol><symbol id="icon-bank"></symbol><symbol id="icon-bell"></symbol><symbol id="icon-binoculars"></symbol><symbol id="icon-bitbucket"></symbol><symbol id="icon-bold"></symbol><symbol id="icon-book-open"></symbol><symbol id="icon-boolean"></symbol><symbol viewBox="0 0 20 20" id="icon-briefcase"><path d="M9 10h2v2h9s-.149-4.459-.2-5.854C19.75 4.82 19.275 4 17.8 4h-3.208l-1.197-2.256C13.064 1.121 12.951 1 12.216 1H7.783c-.735 0-.847.121-1.179.744-.165.311-.7 1.318-1.196 2.256H2.199c-1.476 0-1.945.82-2 2.146C.145 7.473 0 12 0 12h9v-2zM7.649 2.916c.23-.432.308-.516.817-.516h3.067c.509 0 .588.084.816.516L12.924 4h-5.85l.575-1.084zM11 15H9v-2H.5s.124 1.797.199 3.322C.73 16.955.917 18 2.499 18H17.5c1.582 0 1.765-1.047 1.8-1.678.087-1.568.2-3.322.2-3.322H11v2z"></path></symbol><symbol id="icon-building-filled"></symbol><symbol id="icon-calendar"></symbol><symbol id="icon-cancel-circled"></symbol><symbol id="icon-cancel"></symbol><symbol id="icon-chart-area"></symbol><symbol id="icon-chat-alt"></symbol><symbol id="icon-check"></symbol><symbol id="icon-chevron-down"></symbol><symbol id="icon-clock"></symbol><symbol id="icon-code"></symbol><symbol id="icon-cog"></symbol><symbol id="icon-comment"></symbol><symbol id="icon-database"></symbol><symbol id="icon-dev"></symbol><symbol id="icon-discord"></symbol><symbol id="icon-docs"></symbol><symbol id="icon-dot-3">
<g>
<path d="M110 390c30.667 0 56.667 10.667 78 32c21.333 21.333 32 47.333 32 78c0 29.333 -10.667 55 -32 77c-21.333 22 -47.333 33 -78 33c-30.667 0 -56.667 -11 -78 -33c-21.333 -22 -32 -47.667 -32 -77c0 -30.667 10.667 -56.667 32 -78c21.333 -21.333 47.333 -32 78 -32c0 0 0 0 0 0m350 0c30.667 0 56.667 10.667 78 32c21.333 21.333 32 47.333 32 78c0 29.333 -11 55 -33 77c-22 22 -47.667 33 -77 33c-29.333 0 -55 -11 -77 -33c-22 -22 -33 -47.667 -33 -77c0 -30.667 10.667 -56.667 32 -78c21.333 -21.333 47.333 -32 78 -32c0 0 0 0 0 0m350 0c30.667 0 56.667 10.667 78 32c21.333 21.333 32 47.333 32 78c0 29.333 -10.667 55 -32 77c-21.333 22 -47.333 33 -78 33c-30.667 0 -56.667 -11 -78 -33c-21.333 -22 -32 -47.667 -32 -77c0 -30.667 10.667 -56.667 32 -78c21.333 -21.333 47.333 -32 78 -32c0 0 0 0 0 0"></path>
</g>
</symbol><symbol id="icon-dot-circled"></symbol><symbol id="icon-double"></symbol><symbol id="icon-down-dir"></symbol><symbol id="icon-down-open"></symbol><symbol viewBox="0 0 20 20" id="icon-download"><path d="M15 7h-3V1H8v6H5l5 5 5-5zm4.338 6.532c-.21-.224-1.611-1.723-2.011-2.114A1.503 1.503 0 0 0 16.285 11h-1.757l3.064 2.994h-3.544a.274.274 0 0 0-.24.133L12.992 16H7.008l-.816-1.873a.276.276 0 0 0-.24-.133H2.408L5.471 11H3.715c-.397 0-.776.159-1.042.418-.4.392-1.801 1.891-2.011 2.114-.489.521-.758.936-.63 1.449l.561 3.074c.128.514.691.936 1.252.936h16.312c.561 0 1.124-.422 1.252-.936l.561-3.074c.126-.513-.142-.928-.632-1.449z"></path></symbol><symbol id="icon-edit"></symbol><symbol id="icon-enum"></symbol><symbol viewBox="0 0 20 20" id="icon-export"><path d="M15 15H2V6h2.595s.689-.896 2.17-2H1a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h15a1 1 0 0 0 1-1v-3.746l-2 1.645V15zm-1.639-6.95v3.551L20 6.4l-6.639-4.999v3.131C5.3 4.532 5.3 12.5 5.3 12.5c2.282-3.748 3.686-4.45 8.061-4.45z"></path></symbol><symbol viewBox="0 0 20 20" id="icon-eye"><path d="M10 4.4C3.439 4.4 0 9.232 0 10c0 .766 3.439 5.6 10 5.6 6.56 0 10-4.834 10-5.6 0-.768-3.44-5.6-10-5.6zm0 9.907c-2.455 0-4.445-1.928-4.445-4.307S7.545 5.691 10 5.691s4.444 1.93 4.444 4.309-1.989 4.307-4.444 4.307zM10 10c-.407-.447.663-2.154 0-2.154-1.228 0-2.223.965-2.223 2.154s.995 2.154 2.223 2.154c1.227 0 2.223-.965 2.223-2.154 0-.547-1.877.379-2.223 0z"></path></symbol><symbol id="icon-facebook"></symbol><symbol viewBox="0 0 20 20" id="icon-file">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V16C4 16.5304 4.21071 17.0391 4.58579 17.4142C4.96086 17.7893 5.46957 18 6 18H14C14.5304 18 15.0391 17.7893 15.4142 17.4142C15.7893 17.0391 16 16.5304 16 16V8.71429H12.2C11.0954 8.71429 10.2 7.81885 10.2 6.71428V2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579ZM12.2 2.786V6.71428L15.8734 6.71429C15.7741 6.44856 15.6185 6.20442 15.414 6L12.2 2.786Z" fill="#9CA3AF"></path>
</symbol><symbol viewBox="0 0 20 20" id="icon-film">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 3C3.46957 3 2.96086 3.21071 2.58579 3.58579C2.21071 3.96086 2 4.46957 2 5V15C2 15.5304 2.21071 16.0391 2.58579 16.4142C2.96086 16.7893 3.46957 17 4 17H16C16.5304 17 17.0391 16.7893 17.4142 16.4142C17.7893 16.0391 18 15.5304 18 15V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3H4ZM7 5H13V9H7V5ZM15 13V15H16V13H15ZM13 11H7V15H13V11ZM15 11H16V9H15V11ZM16 7V5H15V7H16ZM5 5V7H4V5H5ZM5 9H4V11H5V9ZM4 13H5V15H4V13Z" fill="#9CA3AF"></path>
</symbol><symbol id="icon-filter"></symbol><symbol id="icon-fire"></symbol><symbol id="icon-float"></symbol><symbol viewBox="0 0 20 20" id="icon-folder"><path d="M18.405 4.799c-.111-.44-.655-.799-1.21-.799h-6.814c-.554 0-1.33-.318-1.722-.707l-.596-.588C7.671 2.316 6.896 2 6.342 2H3.087c-.555 0-1.059.447-1.12.994L1.675 6h16.931l-.201-1.201zM19.412 7H.588a.58.58 0 0 0-.577.635l.923 9.669A.77.77 0 0 0 1.7 18h16.6a.77.77 0 0 0 .766-.696l.923-9.669A.58.58 0 0 0 19.412 7z"></path></symbol><symbol id="icon-github"></symbol><symbol id="icon-gitlab"></symbol><symbol id="icon-glasses"></symbol><symbol id="icon-google"></symbol><symbol id="icon-hackernews"></symbol><symbol id="icon-header"></symbol><symbol id="icon-heart"></symbol><symbol id="icon-home"></symbol><symbol viewBox="0 0 20 20" id="icon-image"><path d="M19 2H1a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1zm-1 14H2V4h16v12zm-3.685-5.123l-3.231 1.605-3.77-6.101L4 14h12l-1.685-3.123zM13.25 9a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5z"></path></symbol><symbol id="icon-inbox"></symbol><symbol viewBox="0 0 20 20" id="icon-info-circled"><path d="M10 .4C4.697.4.399 4.698.399 10A9.6 9.6 0 0 0 10 19.601c5.301 0 9.6-4.298 9.6-9.601 0-5.302-4.299-9.6-9.6-9.6zm.896 3.466c.936 0 1.211.543 1.211 1.164 0 .775-.62 1.492-1.679 1.492-.886 0-1.308-.445-1.282-1.182 0-.621.519-1.474 1.75-1.474zM8.498 15.75c-.64 0-1.107-.389-.66-2.094l.733-3.025c.127-.484.148-.678 0-.678-.191 0-1.022.334-1.512.664l-.319-.523c1.555-1.299 3.343-2.061 4.108-2.061.64 0 .746.756.427 1.92l-.84 3.18c-.149.562-.085.756.064.756.192 0 .82-.232 1.438-.719l.362.486c-1.513 1.512-3.162 2.094-3.801 2.094z"></path></symbol><symbol id="icon-instagram"></symbol><symbol id="icon-integer"></symbol><symbol id="icon-ip"></symbol><symbol id="icon-italic"></symbol><symbol id="icon-key-inv"></symbol><symbol id="icon-key"></symbol><symbol id="icon-keyboard"></symbol><symbol viewBox="0 0 20 20" id="icon-lamp"><path d="M7.186 19.172c.789.51 1.701.855 2.814.828 1.111.027 2.025-.318 2.814-.828L12.797 17H7.203l-.017 2.172zM12.697 16c0-4.357 4.63-5.848 4.283-10.188-.218-2.738-2.073-5.81-6.98-5.81S3.238 3.074 3.019 5.813C2.672 10.152 7.303 11.643 7.303 16h5.394zM5 6c.207-2.598 2.113-4 5-4 2.886 0 4.654 1.371 4.861 3.969.113 1.424-.705 2.373-1.809 3.926C12.238 11.041 11.449 12.238 11 14H9c-.449-1.762-1.238-2.959-2.053-4.106C5.844 8.342 4.886 7.424 5 6z"></path></symbol><symbol id="icon-left-dir"></symbol><symbol id="icon-left-open"></symbol><symbol id="icon-lifebuoy"></symbol><symbol id="icon-lightning"></symbol><symbol id="icon-link-ext"></symbol><symbol id="icon-link"></symbol><symbol id="icon-linkedin"></symbol><symbol id="icon-list-bullet"></symbol><symbol id="icon-list-numbered"></symbol><symbol viewBox="0 0 512 512" id="icon-list"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M88 48C101.3 48 112 58.75 112 72V120C112 133.3 101.3 144 88 144H40C26.75 144 16 133.3 16 120V72C16 58.75 26.75 48 40 48H88zM480 64C497.7 64 512 78.33 512 96C512 113.7 497.7 128 480 128H192C174.3 128 160 113.7 160 96C160 78.33 174.3 64 192 64H480zM480 224C497.7 224 512 238.3 512 256C512 273.7 497.7 288 480 288H192C174.3 288 160 273.7 160 256C160 238.3 174.3 224 192 224H480zM480 384C497.7 384 512 398.3 512 416C512 433.7 497.7 448 480 448H192C174.3 448 160 433.7 160 416C160 398.3 174.3 384 192 384H480zM16 232C16 218.7 26.75 208 40 208H88C101.3 208 112 218.7 112 232V280C112 293.3 101.3 304 88 304H40C26.75 304 16 293.3 16 280V232zM88 368C101.3 368 112 378.7 112 392V440C112 453.3 101.3 464 88 464H40C26.75 464 16 453.3 16 440V392C16 378.7 26.75 368 40 368H88z"></path></symbol><symbol id="icon-location"></symbol><symbol id="icon-lock"></symbol><symbol id="icon-login"></symbol><symbol viewBox="0 0 20 20" id="icon-mail"><path d="M1.574 5.286l7.5 4.029c.252.135.578.199.906.199.328 0 .654-.064.906-.199l7.5-4.029c.489-.263.951-1.286.054-1.286H1.521c-.897 0-.435 1.023.053 1.286zm17.039 2.203l-7.727 4.027c-.34.178-.578.199-.906.199s-.566-.021-.906-.199-7.133-3.739-7.688-4.028C.996 7.284 1 7.523 1 7.707V15c0 .42.566 1 1 1h16c.434 0 1-.58 1-1V7.708c0-.184.004-.423-.387-.219z"></path></symbol><symbol id="icon-medium"></symbol><symbol id="icon-menu"></symbol><symbol id="icon-minus"></symbol><symbol id="icon-moon-inv"></symbol><symbol viewBox="0 0 20 20" id="icon-moon">
<path d="M17.293 13.293C15.8115 13.9631 14.161 14.1659 12.5614 13.8743C10.9617 13.5827 9.48895 12.8106 8.33919 11.6609C7.18944 10.5111 6.41734 9.0383 6.12574 7.43866C5.83415 5.83903 6.03691 4.18852 6.70701 2.70703C5.52758 3.24004 4.49505 4.05123 3.69802 5.07099C2.90099 6.09075 2.36324 7.28865 2.13092 8.56192C1.8986 9.83518 1.97864 11.1458 2.36417 12.3813C2.7497 13.6169 3.42922 14.7404 4.34442 15.6556C5.25961 16.5708 6.38318 17.2503 7.61871 17.6359C8.85424 18.0214 10.1649 18.1014 11.4381 17.8691C12.7114 17.6368 13.9093 17.0991 14.9291 16.302C15.9488 15.505 16.76 14.4725 17.293 13.293Z" fill="#9CA3AF"></path>
</symbol><symbol id="icon-more"></symbol><symbol viewBox="0 0 20 20" id="icon-network"><path d="M5.274 6.915c.2 0 .394.029.576.086a15.774 15.774 0 0 1 2.283-2.1 1.954 1.954 0 0 1 .048-1.076A14.407 14.407 0 0 0 5.17 2.171a9.25 9.25 0 0 0-2.582 2.381c.519.92 1.136 1.777 1.838 2.557.256-.124.543-.194.848-.194zM3.316 8.872c0-.275.058-.537.159-.773A15.91 15.91 0 0 1 1.78 5.87a9.165 9.165 0 0 0-.98 4.131 9.16 9.16 0 0 0 1.295 4.705 15.614 15.614 0 0 1 1.62-4.652 1.947 1.947 0 0 1-.399-1.182zm6.72-6.383c.517 0 .985.201 1.336.529a15.578 15.578 0 0 1 3.215-.992A9.154 9.154 0 0 0 10 .8a9.167 9.167 0 0 0-3.236.588 15.76 15.76 0 0 1 2.277 1.375c.292-.174.631-.274.995-.274zm2.926 9.219a1.94 1.94 0 0 1 .509-.656 14.336 14.336 0 0 0-2.672-4.803 1.956 1.956 0 0 1-1.901-.211 14.343 14.343 0 0 0-1.964 1.803 1.93 1.93 0 0 1 .207 1.617 14.252 14.252 0 0 0 5.821 2.25zm2.539 2.643a15.872 15.872 0 0 1-.081 3.082 9.216 9.216 0 0 0 3.347-4.639 15.39 15.39 0 0 1-2.181.365 1.958 1.958 0 0 1-1.085 1.192zm-2.997-1.327a15.643 15.643 0 0 1-6.21-2.484 1.953 1.953 0 0 1-1.423.248 14.219 14.219 0 0 0-1.599 5.484 9.203 9.203 0 0 0 3.145 2.205 15.662 15.662 0 0 1 6.087-5.453zm3.672-9.843a14.296 14.296 0 0 0-4.193 1.068 1.946 1.946 0 0 1-.191 1.056 15.68 15.68 0 0 1 2.969 5.291 1.961 1.961 0 0 1 1.77 1.195c.886-.09 1.748-.26 2.578-.504a9.178 9.178 0 0 0-2.933-8.106zm-2.687 10.888a14.291 14.291 0 0 0-5.723 4.856A9.187 9.187 0 0 0 10 19.2a9.165 9.165 0 0 0 3.882-.859c.19-.928.29-1.887.29-2.869 0-.355-.016-.707-.043-1.055a1.923 1.923 0 0 1-.64-.348z"></path></symbol><symbol id="icon-ok-circled"></symbol><symbol id="icon-ok"></symbol><symbol viewBox="0 0 20 20" id="icon-palette"><path d="M15.74 2.608c-3.528-1.186-7.066-.961-10.72 1.274C2.167 5.625.302 9.958.917 13.064c.728 3.671 4.351 5.995 9.243 4.651 5.275-1.449 6.549-4.546 6.379-5.334s-2.665-1.652-1.718-3.498c1.188-2.313 3.129-1.149 3.982-1.622.855-.472.539-3.442-3.063-4.653zm-3.646 10.706a1.504 1.504 0 0 1-1.843-1.059 1.5 1.5 0 0 1 1.046-1.849 1.503 1.503 0 0 1 1.843 1.059 1.501 1.501 0 0 1-1.046 1.849z"></path></symbol><symbol id="icon-pause"></symbol><symbol id="icon-phone"></symbol><symbol viewBox="0 0 20 20" id="icon-photograph">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 3C3.46957 3 2.96086 3.21071 2.58579 3.58579C2.21071 3.96086 2 4.46957 2 5V15C2 15.5304 2.21071 16.0391 2.58579 16.4142C2.96086 16.7893 3.46957 17 4 17H16C16.5304 17 17.0391 16.7893 17.4142 16.4142C17.7893 16.0391 18 15.5304 18 15V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3H4ZM16 15H4L8 7L11 13L13 9L16 15Z" fill="#9CA3AF"></path>
</symbol><symbol viewBox="0 0 20 20" id="icon-picture"><path d="M19 2H1a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1zm-1 14H2V4h16v12zm-3.685-5.123l-3.231 1.605-3.77-6.101L4 14h12l-1.685-3.123zM13.25 9a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5z"></path></symbol><symbol id="icon-pinterest"></symbol><symbol id="icon-play"></symbol><symbol id="icon-plus"></symbol><symbol id="icon-qrcode"></symbol><symbol id="icon-question"></symbol><symbol id="icon-quote-right"></symbol><symbol id="icon-random"></symbol><symbol id="icon-reddit"></symbol><symbol viewBox="0 0 24 24" id="icon-refresh">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.8001 2.40002C5.11836 2.40002 5.42358 2.52645 5.64863 2.7515C5.87367 2.97654 6.0001 3.28176 6.0001 3.60002V6.12122C6.99855 5.10275 8.23981 4.35519 9.60699 3.94896C10.9742 3.54274 12.4222 3.49121 13.8148 3.79923C15.2074 4.10725 16.4987 4.76469 17.567 5.70962C18.6353 6.65455 19.4455 7.85587 19.9213 9.20042C19.979 9.35013 20.0061 9.5099 20.001 9.67027C19.9959 9.83064 19.9587 9.98836 19.8916 10.1341C19.8245 10.2798 19.7288 10.4106 19.6103 10.5188C19.4917 10.6269 19.3527 10.7101 19.2014 10.7636C19.0501 10.817 18.8896 10.8396 18.7295 10.83C18.5693 10.8203 18.4127 10.7786 18.2689 10.7074C18.1252 10.6362 17.9971 10.5368 17.8924 10.4153C17.7877 10.2937 17.7084 10.1524 17.6593 9.99962C17.2971 8.9758 16.6638 8.06944 15.8271 7.37712C14.9904 6.68479 13.9815 6.23245 12.908 6.06828C11.8344 5.90412 10.7365 6.03429 9.73109 6.44491C8.7257 6.85554 7.85055 7.53125 7.1989 8.40002H10.8001C11.1184 8.40002 11.4236 8.52645 11.6486 8.7515C11.8737 8.97654 12.0001 9.28176 12.0001 9.60002C12.0001 9.91828 11.8737 10.2235 11.6486 10.4486C11.4236 10.6736 11.1184 10.8 10.8001 10.8H4.8001C4.48184 10.8 4.17661 10.6736 3.95157 10.4486C3.72653 10.2235 3.6001 9.91828 3.6001 9.60002V3.60002C3.6001 3.28176 3.72653 2.97654 3.95157 2.7515C4.17661 2.52645 4.48184 2.40002 4.8001 2.40002ZM4.8097 13.2684C4.9583 13.2159 5.11579 13.1932 5.27316 13.2016C5.43054 13.21 5.58473 13.2493 5.72691 13.3173C5.8691 13.3852 5.99651 13.4806 6.10186 13.5978C6.2072 13.715 6.28843 13.8518 6.3409 14.0004C6.70313 15.0242 7.33635 15.9306 8.17308 16.6229C9.00981 17.3153 10.0187 17.7676 11.0922 17.9318C12.1658 18.0959 13.2637 17.9658 14.2691 17.5551C15.2745 17.1445 16.1496 16.4688 16.8013 15.6H13.2001C12.8818 15.6 12.5766 15.4736 12.3516 15.2486C12.1265 15.0235 12.0001 14.7183 12.0001 14.4C12.0001 14.0818 12.1265 13.7765 12.3516 13.5515C12.5766 13.3265 12.8818 13.2 13.2001 13.2H19.2001C19.5184 13.2 19.8236 13.3265 20.0486 13.5515C20.2737 13.7765 20.4001 14.0818 20.4001 14.4V20.4C20.4001 20.7183 20.2737 21.0235 20.0486 21.2486C19.8236 21.4736 19.5184 21.6 19.2001 21.6C18.8818 21.6 18.5766 21.4736 18.3516 21.2486C18.1265 21.0235 18.0001 20.7183 18.0001 20.4V17.8788C17.0016 18.8973 15.7604 19.6449 14.3932 20.0511C13.026 20.4573 11.578 20.5088 10.1854 20.2008C8.79277 19.8928 7.5015 19.2354 6.43319 18.2904C5.36487 17.3455 4.55467 16.1442 4.0789 14.7996C4.02641 14.651 4.00371 14.4935 4.01209 14.3362C4.02047 14.1788 4.05977 14.0246 4.12774 13.8824C4.19572 13.7402 4.29104 13.6128 4.40825 13.5075C4.52547 13.4021 4.66229 13.3209 4.8109 13.2684H4.8097Z" fill="#9CA3AF"></path>
</symbol><symbol id="icon-right-dir"></symbol><symbol id="icon-right-open"></symbol><symbol id="icon-rocket"></symbol><symbol id="icon-search"></symbol><symbol id="icon-shield"></symbol><symbol id="icon-shuffle"></symbol><symbol id="icon-smile-o"></symbol><symbol id="icon-sort"></symbol><symbol id="icon-stackoverflow"></symbol><symbol viewBox="0 0 24 24" id="icon-stopwatch"><g><path d="M19.414 8.902c.104-.048.206-.108.293-.195l.5-.5c.391-.391.391-1.023 0-1.414s-1.023-.391-1.414 0l-.5.5-.115.173c-1.387-1.312-3.188-2.19-5.189-2.41l.011-.056v-1h1c.55 0 1-.45 1-1s-.45-1-1-1h-4c-.55 0-1 .45-1 1s.45 1 1 1h1v1l.012.057c-4.506.492-8.012 4.307-8.012 8.943 0 4.971 4.029 9 9 9s9-4.029 9-9c0-1.894-.588-3.648-1.586-5.098zm-7.414 12.098c-3.859 0-7-3.14-7-7s3.141-7 7-7 7 3.14 7 7-3.141 7-7 7zM13 13v-2c0-.55-.45-1-1-1s-1 .45-1 1v3c0 .55.45 1 1 1h3c.55 0 1-.45 1-1s-.45-1-1-1h-2zM12 8c-3.312 0-6 2.688-6 6s2.688 6 6 6 6-2.688 6-6-2.688-6-6-6zm0 11c-2.757 0-5-2.243-5-5s2.243-5 5-5 5 2.243 5 5-2.243 5-5 5z"></path></g></symbol><symbol id="icon-string"></symbol><symbol id="icon-sun-inv"></symbol><symbol id="icon-telegram"></symbol><symbol viewBox="0 0 20 20" id="icon-trash-2">
<path d="M8.33301 9.16667V14.1667M11.6663 9.16667V14.1667M3.33301 5.83333H16.6663M15.833 5.83333L15.1105 15.9517C15.0806 16.3722 14.8924 16.7657 14.5839 17.053C14.2755 17.3403 13.8696 17.5 13.448 17.5H6.55134C6.12979 17.5 5.7239 17.3403 5.41541 17.053C5.10693 16.7657 4.91877 16.3722 4.88884 15.9517L4.16634 5.83333H15.833ZM12.4997 5.83333V3.33333C12.4997 3.11232 12.4119 2.90036 12.2556 2.74408C12.0993 2.5878 11.8874 2.5 11.6663 2.5H8.33301C8.11199 2.5 7.90003 2.5878 7.74375 2.74408C7.58747 2.90036 7.49967 3.11232 7.49967 3.33333V5.83333H12.4997Z" stroke="#9F9F9F" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</symbol><symbol viewBox="0 0 20 20" id="icon-trash">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 2C8.81434 2.0001 8.63237 2.05188 8.47447 2.14955C8.31658 2.24722 8.18899 2.38692 8.106 2.553L7.382 4H4C3.73478 4 3.48043 4.10536 3.29289 4.29289C3.10536 4.48043 3 4.73478 3 5C3 5.26522 3.10536 5.51957 3.29289 5.70711C3.48043 5.89464 3.73478 6 4 6V16C4 16.5304 4.21071 17.0391 4.58579 17.4142C4.96086 17.7893 5.46957 18 6 18H14C14.5304 18 15.0391 17.7893 15.4142 17.4142C15.7893 17.0391 16 16.5304 16 16V6C16.2652 6 16.5196 5.89464 16.7071 5.70711C16.8946 5.51957 17 5.26522 17 5C17 4.73478 16.8946 4.48043 16.7071 4.29289C16.5196 4.10536 16.2652 4 16 4H12.618L11.894 2.553C11.811 2.38692 11.6834 2.24722 11.5255 2.14955C11.3676 2.05188 11.1857 2.0001 11 2H9ZM7 8C7 7.73478 7.10536 7.48043 7.29289 7.29289C7.48043 7.10536 7.73478 7 8 7C8.26522 7 8.51957 7.10536 8.70711 7.29289C8.89464 7.48043 9 7.73478 9 8V14C9 14.2652 8.89464 14.5196 8.70711 14.7071C8.51957 14.8946 8.26522 15 8 15C7.73478 15 7.48043 14.8946 7.29289 14.7071C7.10536 14.5196 7 14.2652 7 14V8ZM12 7C11.7348 7 11.4804 7.10536 11.2929 7.29289C11.1054 7.48043 11 7.73478 11 8V14C11 14.2652 11.1054 14.5196 11.2929 14.7071C11.4804 14.8946 11.7348 15 12 15C12.2652 15 12.5196 14.8946 12.7071 14.7071C12.8946 14.5196 13 14.2652 13 14V8C13 7.73478 12.8946 7.48043 12.7071 7.29289C12.5196 7.10536 12.2652 7 12 7Z" fill="#9CA3AF"></path>
</symbol><symbol id="icon-twitter"></symbol><symbol id="icon-underline"></symbol><symbol id="icon-up-dir"></symbol><symbol id="icon-up-open"></symbol><symbol id="icon-upload"></symbol><symbol viewBox="0 0 20 20" id="icon-user">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 9C10.7956 9 11.5587 8.68393 12.1213 8.12132C12.6839 7.55871 13 6.79565 13 6C13 5.20435 12.6839 4.44129 12.1213 3.87868C11.5587 3.31607 10.7956 3 10 3C9.20435 3 8.44129 3.31607 7.87868 3.87868C7.31607 4.44129 7 5.20435 7 6C7 6.79565 7.31607 7.55871 7.87868 8.12132C8.44129 8.68393 9.20435 9 10 9ZM3 18C3 17.0807 3.18106 16.1705 3.53284 15.3212C3.88463 14.4719 4.40024 13.7003 5.05025 13.0503C5.70026 12.4002 6.47194 11.8846 7.32122 11.5328C8.1705 11.1811 9.08075 11 10 11C10.9193 11 11.8295 11.1811 12.6788 11.5328C13.5281 11.8846 14.2997 12.4002 14.9497 13.0503C15.5998 13.7003 16.1154 14.4719 16.4672 15.3212C16.8189 16.1705 17 17.0807 17 18H3Z" fill="#9CA3AF"></path>
</symbol><symbol id="icon-users"></symbol><symbol id="icon-videocam"></symbol><symbol viewBox="0 0 24 24" id="icon-warning"><path d="M21.171 15.398l-5.912-9.854c-.776-1.293-1.963-2.033-3.259-2.033s-2.483.74-3.259 2.031l-5.912 9.856c-.786 1.309-.872 2.705-.235 3.83.636 1.126 1.878 1.772 3.406 1.772h12c1.528 0 2.77-.646 3.406-1.771.637-1.125.551-2.521-.235-3.831zm-9.171 2.151c-.854 0-1.55-.695-1.55-1.549 0-.855.695-1.551 1.55-1.551s1.55.696 1.55 1.551c0 .854-.696 1.549-1.55 1.549zm1.633-7.424c-.011.031-1.401 3.468-1.401 3.468-.038.094-.13.156-.231.156s-.193-.062-.231-.156l-1.391-3.438c-.09-.233-.129-.443-.129-.655 0-.965.785-1.75 1.75-1.75s1.75.785 1.75 1.75c0 .212-.039.422-.117.625z"></path></symbol><symbol id="icon-whatsapp"></symbol><symbol id="icon-wheelchair"></symbol><symbol id="icon-windows"></symbol><symbol id="icon-wrench"></symbol><symbol id="icon-x"></symbol><symbol id="icon-youtube-play"></symbol></svg>

After

Width:  |  Height:  |  Size: 21 KiB

BIN
public/fonts/icon.ttf Normal file

Binary file not shown.

BIN
public/fonts/icon.woff Normal file

Binary file not shown.

BIN
public/fonts/icon.woff2 Normal file

Binary file not shown.

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 3C3.46957 3 2.96086 3.21071 2.58579 3.58579C2.21071 3.96086 2 4.46957 2 5V15C2 15.5304 2.21071 16.0391 2.58579 16.4142C2.96086 16.7893 3.46957 17 4 17H16C16.5304 17 17.0391 16.7893 17.4142 16.4142C17.7893 16.0391 18 15.5304 18 15V5C18 4.46957 17.7893 3.96086 17.4142 3.58579C17.0391 3.21071 16.5304 3 16 3H4ZM16 15H4L8 7L11 13L13 9L16 15Z" fill="#9CA3AF"/>
</svg>

After

Width:  |  Height:  |  Size: 512 B

View file

@ -1,29 +1,29 @@
(function (exports, isomorphicFormData, crossFetch) {
'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class AppwriteException extends Error {
@ -2580,7 +2580,7 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
listExecutions: (functionId, limit, offset, search, cursor, cursorDirection) => __awaiter(this, void 0, void 0, function* () {
createTag: (functionId, command, code, onProgress = (progress) => { }) => __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
}
@ -2602,9 +2602,43 @@
payload['cursorDirection'] = cursorDirection;
}
const uri = new URL(this.config.endpoint + path);
return yield this.call('get', uri, {
'content-type': 'application/json',
}, payload);
const size = code.size;
if (size <= Appwrite.CHUNK_SIZE) {
return yield this.call('post', uri, {
'content-type': 'multipart/form-data',
}, payload);
}
let id = undefined;
let response = undefined;
const headers = {
'content-type': 'multipart/form-data',
};
let counter = 0;
const totalCounters = Math.ceil(size / Appwrite.CHUNK_SIZE);
for (counter; counter < totalCounters; counter++) {
const start = (counter * Appwrite.CHUNK_SIZE);
const end = Math.min((((counter * Appwrite.CHUNK_SIZE) + Appwrite.CHUNK_SIZE) - 1), size);
headers['content-range'] = 'bytes ' + start + '-' + end + '/' + size;
if (id) {
headers['x-appwrite-id'] = id;
}
const stream = code.slice(start, end + 1);
payload['code'] = new File([stream], code.name);
response = yield this.call('post', uri, headers, payload);
if (!id) {
id = response['$id'];
}
if (onProgress) {
onProgress({
$id: response.$id,
progress: Math.min((counter + 1) * Appwrite.CHUNK_SIZE, size) / size * 100,
sizeUpploaded: end + 1,
chunksTotal: response.chunksTotal,
chunksUploaded: response.chunksUploaded
});
}
}
return response;
}),
/**
* Create Execution
@ -3989,7 +4023,7 @@
}, payload);
}),
/**
* Create storage bucket
* Create bucket
*
* Create a new storage bucket.
*
@ -3998,16 +4032,15 @@
* @param {string} permission
* @param {string[]} read
* @param {string[]} write
* @param {boolean} enabled
* @param {number} maximumFileSize
* @param {string[]} allowedFileExtensions
* @param {boolean} enabled
* @param {string} adapter
* @param {boolean} encryption
* @param {boolean} antivirus
* @throws {AppwriteException}
* @returns {Promise}
*/
createBucket: (bucketId, name, permission, read, write, maximumFileSize, allowedFileExtensions, enabled, adapter, encryption, antivirus) => __awaiter(this, void 0, void 0, function* () {
createBucket: (bucketId, name, permission, read, write, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) => __awaiter(this, void 0, void 0, function* () {
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
@ -4034,18 +4067,15 @@
if (typeof write !== 'undefined') {
payload['write'] = write;
}
if (typeof enabled !== 'undefined') {
payload['enabled'] = enabled;
}
if (typeof maximumFileSize !== 'undefined') {
payload['maximumFileSize'] = maximumFileSize;
}
if (typeof allowedFileExtensions !== 'undefined') {
payload['allowedFileExtensions'] = allowedFileExtensions;
}
if (typeof enabled !== 'undefined') {
payload['enabled'] = enabled;
}
if (typeof adapter !== 'undefined') {
payload['adapter'] = adapter;
}
if (typeof encryption !== 'undefined') {
payload['encryption'] = encryption;
}
@ -4088,15 +4118,15 @@
* @param {string} permission
* @param {string[]} read
* @param {string[]} write
* @param {boolean} enabled
* @param {number} maximumFileSize
* @param {string[]} allowedFileExtensions
* @param {boolean} enabled
* @param {boolean} encryption
* @param {boolean} antivirus
* @throws {AppwriteException}
* @returns {Promise}
*/
updateBucket: (bucketId, name, permission, read, write, maximumFileSize, allowedFileExtensions, enabled, encryption, antivirus) => __awaiter(this, void 0, void 0, function* () {
updateBucket: (bucketId, name, permission, read, write, enabled, maximumFileSize, allowedFileExtensions, encryption, antivirus) => __awaiter(this, void 0, void 0, function* () {
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
@ -4120,15 +4150,15 @@
if (typeof write !== 'undefined') {
payload['write'] = write;
}
if (typeof enabled !== 'undefined') {
payload['enabled'] = enabled;
}
if (typeof maximumFileSize !== 'undefined') {
payload['maximumFileSize'] = maximumFileSize;
}
if (typeof allowedFileExtensions !== 'undefined') {
payload['allowedFileExtensions'] = allowedFileExtensions;
}
if (typeof enabled !== 'undefined') {
payload['enabled'] = enabled;
}
if (typeof encryption !== 'undefined') {
payload['encryption'] = encryption;
}
@ -4236,7 +4266,7 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
createFile: (bucketId, fileId, file, read, write) => __awaiter(this, void 0, void 0, function* () {
createFile: (bucketId, fileId, file, read, write, onProgress = (progress) => { }) => __awaiter(this, void 0, void 0, function* () {
if (typeof bucketId === 'undefined') {
throw new AppwriteException('Missing required parameter: "bucketId"');
}
@ -4261,9 +4291,51 @@
payload['write'] = write;
}
const uri = new URL(this.config.endpoint + path);
return yield this.call('post', uri, {
const size = file.size;
if (size <= Appwrite.CHUNK_SIZE) {
return yield this.call('post', uri, {
'content-type': 'multipart/form-data',
}, payload);
}
let id = undefined;
let response = undefined;
const headers = {
'content-type': 'multipart/form-data',
}, payload);
};
let counter = 0;
const totalCounters = Math.ceil(size / Appwrite.CHUNK_SIZE);
if (fileId != 'unique()') {
try {
response = yield this.call('GET', new URL(this.config.endpoint + path + '/' + fileId), headers);
counter = response.chunksUploaded;
}
catch (e) {
}
}
for (counter; counter < totalCounters; counter++) {
const start = (counter * Appwrite.CHUNK_SIZE);
const end = Math.min((((counter * Appwrite.CHUNK_SIZE) + Appwrite.CHUNK_SIZE) - 1), size);
headers['content-range'] = 'bytes ' + start + '-' + end + '/' + size;
if (id) {
headers['x-appwrite-id'] = id;
}
const stream = file.slice(start, end + 1);
payload['file'] = new File([stream], file.name);
response = yield this.call('post', uri, headers, payload);
if (!id) {
id = response['$id'];
}
if (onProgress) {
onProgress({
$id: response.$id,
progress: Math.min((counter + 1) * Appwrite.CHUNK_SIZE, size) / size * 100,
sizeUpploaded: end + 1,
chunksTotal: response.chunksTotal,
chunksUploaded: response.chunksUploaded
});
}
}
return response;
}),
/**
* Get File
@ -4382,7 +4454,8 @@
* Get a file preview image. Currently, this method supports preview for image
* files (jpg, png, and gif), other supported formats, like pdf, docs, slides,
* and spreadsheets, will return the file icon image. You can also pass query
* string arguments for cutting and resizing your preview image.
* string arguments for cutting and resizing your preview image. Preview is
* supported only for image files smaller than 10MB.
*
* @param {string} bucketId
* @param {string} fileId
@ -5540,6 +5613,7 @@
return output;
}
}
Appwrite.CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
class Query {
}
Query.equal = (attribute, value) => Query.addQuery(attribute, "equal", value);

View file

@ -0,0 +1,154 @@
(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;
}
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: "",
});
}
target.reset();
try {
const response = await sdk.storage.createFile(
bucketId,
fileId,
file,
read,
write,
(progress) => {
this.updateFile(id, {
id: progress.$id,
progress: Math.round(progress.progress),
error: "",
});
id = progress.$id;
const file = this.getFile(id) ?? {};
if(file.cancelled === true) {
throw 'USER_CANCELLED';
}
});
const existingFile = this.getFile(id) ?? {};
if(existingFile.cancelled) {
this.updateFile(id,{
id: response.$id,
name: response.name,
failed: false,
});
id = response.$id;
throw 'USER_CANCELLED'
} else {
this.updateFile(id,{
id: response.$id,
name: response.name,
progress: 100,
completed: true,
failed: false,
});
id = response.$id;
}
document.dispatchEvent(new CustomEvent('storage.createFile'));
} catch(error) {
if(error === 'USER_CANCELLED') {
await sdk.storage.deleteFile(bucketId, id);
this.updateFile(id, {
cancelled: false,
failed: true,
});
this.removeFile(id);
} else {
this.updateFile(id, {
id: id,
failed: true,
error: error.message ?? error
});
}
document.dispatchEvent(new CustomEvent('storage.createFile'));
}
}
});
});
})(window);

View file

@ -40,8 +40,7 @@
redirect: function(url) {
return function(router) {
//router.change(url || "/");
window.location = url || "/";
router.change(url || "/");
};
},

View file

@ -35,6 +35,7 @@
a,
.link {
display: inline-flex;
color: var(--config-color-focus);
border-left: none;
border-right: none;
@ -43,5 +44,9 @@
&:hover {
border-bottom-color: var(--config-color-focus);
}
i {
margin-right: .2rem;
}
}
}

View file

@ -18,14 +18,21 @@ body > footer {
}
a {
display: inline-flex;
color: var(--config-color-fade);
font-size: 13px;
&:hover {
border-bottom-color: var(--config-color-fade);
}
i {
margin-right: .2rem;
}
}
ul {
display: flex;
justify-content: center;
.clear;
li {

View file

@ -86,7 +86,7 @@ header {
.links a {
padding: 0;
border: none;
display: block;
display: flex;
text-align: center;
color: #788c99;
font-size: 12px;

View file

@ -162,17 +162,6 @@
pre {
overflow: auto;
}
}
button.close {
width: 30px;
height: 30px;
line-height: 30px;
padding: 0;
margin: 0;
background: var(--config-color-normal);
color: var(--config-color-background-fade);
border-radius: 50%;
}
.paging {

View file

@ -0,0 +1,15 @@
.pill {
--p-pill-text-color:var(--config-defalut-color-hsl, var(--pill-text-color));
color: hsl(var(--p-pill-text-color));
background-color: hsl(~"var(--p-pill-text-color) / 0.2");
display:inline-grid; place-content:center; padding-inline:12px; height:30px; border-radius:15px;
font-size:14px; font-weight:500;
&.is-disabled { --p-pill-text-color:var(--config-disabled-color-hsl); }
&.is-pending { --p-pill-text-color:var(--config-pending-color-hsl); }
&.is-failed { --p-pill-text-color:var(--config-failed-color-hsl); }
:root .theme-dark &{
&.is-failed {color:#FFCFCF; background-color:#B91C1C;}
}
}

View file

@ -0,0 +1,21 @@
.preview-box {
display: grid;
place-content: center;
padding: 40px;
background: var(--config-color-background-fade);
border: 1px dashed var(--config-color-background-dark);
border-radius: 8px;
&-image {
display: grid;
place-content: center;
width: 40px;
height: 40px;
margin-inline: auto;
background-color: var(--config-color-background-focus);
border-radius: 50%;
}
&-text{
margin-top: 16px;
color: var(--config-color-fade);
}
}

View file

@ -0,0 +1,111 @@
.upload-box {
--p-header-text-color:var(--config-color-background-fade);
--p-header-bg-color:var(--config-color-normal);
--p-content-text-color:var(--config-color-normal);
--p-content-bg-color:var(--config-color-background-fade);
--p-border-color:var(--config-modal-note-border);
--box-shadow:0 10px 10px rgba(0, 0, 0, 0.05);
--border-radius:6px;
--transition:0.2s;
position:fixed; z-index:1; overflow:hidden; min-width:285px;
box-shadow:var(--box-shadow);
border-radius:var(--border-radius);
font-size:14px; line-height:1;
*{all:unset; display:revert;}
&-header {
display:flex; padding:16px; padding-inline-end:21px;
color:var(--p-header-text-color); background-color:var(--p-header-bg-color);
.icon-button { margin-inline-start:16px; }
}
&-title {
align-self:center; margin-inline-end:auto; margin-block-end:0;
.amount {
&::before { content:"("; }
&::after { content:")"; }
}
}
&-content {
background-color:var(--p-content-bg-color); color:var(--p-content-text-color);
height:0; overflow:auto;
transition:var(--transition);
&.is-open {height:200px; /* consider to change to rem */}
}
&-list {}
&-item {
display:flex; align-items:center; padding:13px 20px;
.file-name {text-overflow:ellipsis; white-space:nowrap; overflow:hidden; align-self:center; margin-inline-end:auto; line-height:normal; }
.icon-button{ align-self:center; margin-inline-start:16px;}
.pill{margin-inline-start:8px;}
&:not(:last-child) { border-bottom:solid 1px var(--p-border-color); }
}
/* responsive */
@media @phones { inset-inline:16px; inset-block-end:20px; }
@media @tablets, @desktops { max-width:285px; inset-inline-end:24px; inset-block-end:24px; }
/* dark theme */
:root .theme-dark &{
--p-header-text-color:#fff;
--p-header-bg-color:var(--config-color-background-dark);
}
}
.upload-image {
@upload-image-size-default: 40px;
--p-upload-image-size:var(--upload-image-size, @upload-image-size-default);
--p-upload-bg-color:var(--config-color-fade-super);
--p-upload-icon-color:var(--config-color-fade);
position:relative; display:grid; place-content:center; flex-shrink:0;
width:var(--p-upload-image-size);
height:var(--p-upload-image-size);
background-color:var(--p-upload-bg-color);
color:var(--p-upload-icon-color);
border-radius:50%;
.icon{position:relative; z-index:20;}
.progress {
--progress:var(--progress-value, 0);
--zero-value: 0 0% 0% / 0%;
position:absolute; z-index:10; inset:0; border-radius:50%;
background:
~"radial-gradient(
var(--p-upload-bg-color) 0%,
var(--p-upload-bg-color) 64%,
transparent 64.01%,
transparent 100%),
conic-gradient(
hsl(194 66% 50%) 0%,
hsl(97 66% 50%) calc(var(--progress) * 1%),
hsl(var(--zero-value)) calc(var(--progress) * 1%),
hsl(var(--zero-value)) 100%)";
}
&.is-finished {
.progress{ display:none; }
}
/* dark theme */
:root .theme-dark &{
--p-upload-bg-color:var(--config-color-background-dark);
--p-upload-icon-color:var(--config-color-focus);
}
}
.icon-button {
--p-icon-button-size:var(--icon-button-size, 20px);
width:var(--p-icon-button-size);
height:var(--p-icon-button-size);
font-size:var(--p-icon-button-size);
transition:0.2s; line-height:1; text-align:center; flex-shrink:0; cursor:pointer;
>*::before{margin:0;}
&.is-open { transform:rotate(180deg); }
&.is-success { color:var(--config-color-success); }
&:focus, &:hover { background-color:unset; }
&:focus-visible { outline:dashed 1px var(--config-color-primary); }
}

View file

@ -20,6 +20,7 @@ img[src=""] {
@import "polyfills";
@import "forms";
@import "ide";
@import "utilities";
@import "scopes/console";
@import "scopes/home";
@import "comps/alerts";
@ -33,6 +34,9 @@ img[src=""] {
@import "comps/modal";
@import "comps/scroll";
@import "comps/tabs";
@import "comps/preview-box";
@import "comps/upload-box";
@import "comps/pill";
html {
padding: 0;
@ -54,4 +58,4 @@ ul {
margin: 0;
list-style: none;
}
}
}

View file

@ -1,828 +0,0 @@
{
"name": "",
"css_prefix_text": "icon-",
"css_use_suffix": false,
"hinting": true,
"units_per_em": 1000,
"ascent": 850,
"glyphs": [
{
"uid": "8b80d36d4ef43889db10bc1f0dc9a862",
"css": "user",
"code": 59392,
"src": "fontawesome"
},
{
"uid": "0ccb084ddeeae372673793ed0b45bb4a",
"css": "folder",
"code": 59393,
"src": "entypo"
},
{
"uid": "48b87105bd38c20315f1b705b8c7b38c",
"css": "list",
"code": 59394,
"src": "fontawesome"
},
{
"uid": "3a00327e61b997b58518bd43ed83c3df",
"css": "login",
"code": 59395,
"src": "fontawesome"
},
{
"uid": "e99461abfef3923546da8d745372c995",
"css": "cog",
"code": 59396,
"src": "fontawesome"
},
{
"uid": "8e04c98c8f5ca0a035776e3001ad2638",
"css": "facebook",
"code": 59402,
"src": "fontawesome"
},
{
"uid": "627abcdb627cb1789e009c08e2678ef9",
"css": "twitter",
"code": 59397,
"src": "fontawesome"
},
{
"uid": "0f6a2573a7b6df911ed199bb63717e27",
"css": "github-circled",
"code": 61595,
"src": "fontawesome"
},
{
"uid": "169f51b7e405de8c03cf85a6e8c740ab",
"css": "bitbucket",
"code": 61809,
"src": "fontawesome"
},
{
"uid": "f886d2d6cc8cb1b1117e26819a7bc282",
"css": "article-alt",
"code": 59398,
"src": "iconic"
},
{
"uid": "5211af474d3a9848f67f945e2ccaf143",
"css": "cancel",
"code": 59399,
"src": "fontawesome"
},
{
"uid": "d7271d490b71df4311e32cdacae8b331",
"css": "home",
"code": 59400,
"src": "fontawesome"
},
{
"uid": "2d6150442079cbda7df64522dc24f482",
"css": "down-dir",
"code": 59401,
"src": "fontawesome"
},
{
"uid": "eeec3208c90b7b48e804919d0d2d4a41",
"css": "upload",
"code": 59404,
"src": "fontawesome"
},
{
"uid": "e15f0d620a7897e2035c18c80142f6d9",
"css": "link-ext",
"code": 61582,
"src": "fontawesome"
},
{
"uid": "cb8b402e2efdc57d6d37ad7d3da819fe",
"css": "stopwatch",
"code": 59405,
"src": "typicons"
},
{
"uid": "d73eceadda1f594cec0536087539afbf",
"css": "heart",
"code": 59407,
"src": "fontawesome"
},
{
"uid": "e9107949dd6c9e8ab2b29ae07156e38c",
"css": "linkedin",
"code": 61665,
"src": "fontawesome"
},
{
"uid": "44e04715aecbca7f266a17d5a7863c68",
"css": "plus",
"code": 59408,
"src": "fontawesome"
},
{
"uid": "80cd1022bd9ea151d554bec1fa05f2de",
"css": "up-dir",
"code": 59409,
"src": "fontawesome"
},
{
"uid": "c8585e1e5b0467f28b70bce765d5840c",
"css": "docs",
"code": 61637,
"src": "fontawesome"
},
{
"uid": "559647a6f430b3aeadbecd67194451dd",
"css": "menu",
"code": 59410,
"src": "fontawesome"
},
{
"uid": "d870630ff8f81e6de3958ecaeac532f2",
"css": "left-open",
"code": 59411,
"src": "fontawesome"
},
{
"uid": "399ef63b1e23ab1b761dfbb5591fa4da",
"css": "right-open",
"code": 59412,
"src": "fontawesome"
},
{
"uid": "c7a75a25880928fe76981a53eca4f926",
"css": "inbox",
"code": 59413,
"src": "fontawesome"
},
{
"uid": "ebffa4e734c8379ffee4fbfe49264d94",
"css": "lifebuoy",
"code": 61901,
"src": "fontawesome"
},
{
"uid": "5bb103cd29de77e0e06a52638527b575",
"css": "wrench",
"code": 59414,
"src": "fontawesome"
},
{
"uid": "85528017f1e6053b2253785c31047f44",
"css": "comment",
"code": 59415,
"src": "fontawesome"
},
{
"uid": "e7d17d4d07756f4ab5eb690eea1275ca",
"css": "stackoverflow",
"code": 59416,
"src": "zocial"
},
{
"uid": "bf09b1c6561dc0ced707476e2cd83d29",
"css": "medium",
"code": 62010,
"src": "fontawesome"
},
{
"uid": "23d1c53bc15ca452df9453450e94a19c",
"css": "question",
"code": 59417,
"src": "modernpics"
},
{
"uid": "dd4b00255957a608953c409346e7d7fb",
"css": "warning",
"code": 59419,
"src": "typicons"
},
{
"uid": "e335adbc2d898c7d85d40c507796e7b4",
"css": "mail",
"code": 59420,
"src": "entypo"
},
{
"uid": "ede2ea0a583f662b79fbb181b428c20d",
"css": "building-filled",
"code": 61869,
"src": "fontawesome"
},
{
"uid": "8c0ffa714cecbf5144e022d9c3df4a1f",
"css": "bank",
"code": 61852,
"src": "fontawesome"
},
{
"uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1",
"css": "link",
"code": 59421,
"src": "fontawesome"
},
{
"uid": "c6ecc52b6db2a649a08ff54e5ea62886",
"css": "key-inv",
"code": 59422,
"src": "iconic"
},
{
"uid": "z18zsyvbrpsea0f49kd61kkue1ofsafy",
"css": "trash",
"code": 59423,
"src": "modernpics"
},
{
"uid": "b08cfe8039de2ce815686aced2caef06",
"css": "download",
"code": 59424,
"src": "entypo"
},
{
"uid": "a3662e0fce8c28f9b91a3889094cd1e9",
"css": "glasses",
"code": 59425,
"src": "elusive"
},
{
"uid": "44a0e2660ac966cdfaa04deb6aac51df",
"css": "shield",
"code": 61746,
"src": "fontawesome"
},
{
"uid": "37c5ab63f10d7ad0b84d0978dcd0c7a8",
"css": "shuffle",
"code": 59427,
"src": "fontawesome"
},
{
"uid": "25fc99a30fecc4021fdcae5fff5ba9ac",
"css": "eye",
"code": 59428,
"src": "entypo"
},
{
"uid": "c1f1975c885aa9f3dad7810c53b82074",
"css": "lock",
"code": 59429,
"src": "fontawesome"
},
{
"uid": "9dd9e835aebe1060ba7190ad2b2ed951",
"css": "search",
"code": 59430,
"src": "fontawesome"
},
{
"uid": "cd21cbfb28ad4d903cede582157f65dc",
"css": "bell",
"code": 59431,
"src": "fontawesome"
},
{
"uid": "7d1ca956f4181a023de4b9efbed92524",
"css": "chart-area",
"code": 61950,
"src": "fontawesome"
},
{
"uid": "7034e4d22866af82bef811f52fb1ba46",
"css": "code",
"code": 61729,
"src": "fontawesome"
},
{
"uid": "31972e4e9d080eaa796290349ae6c1fd",
"css": "users",
"code": 59432,
"src": "fontawesome"
},
{
"uid": "0d08dbb1dd648a43bdea81b7e6c9e036",
"css": "location",
"code": 59433,
"src": "fontawesome"
},
{
"uid": "9ea7cce5e08e5ac2c225fabf2e6dc353",
"css": "briefcase",
"code": 59434,
"src": "entypo"
},
{
"uid": "4c1ef492f1d2c39a2250ae457cee2a6e",
"css": "instagram",
"code": 59435,
"src": "fontawesome"
},
{
"uid": "cc399e4597f157dcbf016be0b7407fea",
"css": "pinterest",
"code": 62001,
"src": "fontawesome"
},
{
"uid": "8663320a860b00f26e94d3d15c9ba99a",
"css": "clock",
"code": 59436,
"src": "entypo"
},
{
"uid": "9f7e588c66cfd6891f6f507cf6f6596b",
"css": "phone",
"code": 59437,
"src": "fontawesome"
},
{
"uid": "73ffeb70554099177620847206c12457",
"css": "binoculars",
"code": 61925,
"src": "fontawesome"
},
{
"uid": "531bc468eecbb8867d822f1c11f1e039",
"css": "calendar",
"code": 59438,
"src": "fontawesome"
},
{
"uid": "7ssl5z5jrhu13tb13cd3gb4bdfkzbfzw",
"css": "print",
"code": 59439,
"src": "modernpics"
},
{
"uid": "02cca871bb69da75e8ee286b7055832c",
"css": "bold",
"code": 59441,
"src": "fontawesome"
},
{
"uid": "a8cb1c217f02b073db3670c061cc54d2",
"css": "italic",
"code": 59442,
"src": "fontawesome"
},
{
"uid": "a2a74f5e7b7d9ba054897d8c795a326a",
"css": "list-bullet",
"code": 61642,
"src": "fontawesome"
},
{
"uid": "f6766a8b042c2453a4e153af03294383",
"css": "list-numbered",
"code": 61643,
"src": "fontawesome"
},
{
"uid": "d4a4a38a40b728f46dad1de4ac950231",
"css": "underline",
"code": 61645,
"src": "fontawesome"
},
{
"uid": "9c7fd7637a41b59a358cb70893f945a5",
"css": "rocket",
"code": 59443,
"src": "entypo"
},
{
"uid": "56a21935a5d4d79b2e91ec00f760b369",
"css": "sort",
"code": 61660,
"src": "fontawesome"
},
{
"uid": "ecb97add13804c190456025e43ec003b",
"css": "keyboard",
"code": 61724,
"src": "fontawesome"
},
{
"uid": "531359183f9fedb14b23666f1308591f",
"css": "telegram",
"code": 62150,
"src": "fontawesome"
},
{
"uid": "9d3e9faf68fd4e12def853f0d4e1173b",
"css": "whatsapp",
"code": 59444,
"src": "fontawesome"
},
{
"uid": "d10920db2e79c997c5e783279291970c",
"css": "dot-3",
"code": 59445,
"src": "entypo"
},
{
"uid": "7e4164950ffa4990961958b2d6318658",
"css": "info-circled",
"code": 59446,
"src": "entypo"
},
{
"uid": "0f99ab40ab0b4d64a74f2d0deeb03e42",
"css": "videocam",
"code": 59447,
"src": "fontawesome"
},
{
"uid": "bc0af4824c8eedcad7bc0d35911dac2a",
"css": "quote-right",
"code": 59448,
"src": "iconic"
},
{
"uid": "0c708edd8fae2376b3370aa56d40cf9e",
"css": "header",
"code": 61916,
"src": "fontawesome"
},
{
"uid": "3def559c3c39b8500882e02892b7daa8",
"css": "picture",
"code": 59449,
"src": "entypo"
},
{
"uid": "80451d627470ea7f977a263fdb771d7b",
"css": "palette",
"code": 59450,
"src": "entypo"
},
{
"uid": "4b20e1deee87faf4c3fab735fbd4bc1a",
"css": "lamp",
"code": 59451,
"src": "entypo"
},
{
"uid": "8fc56819afcd07e2786e34f18d39b71b",
"css": "book-open",
"code": 59452,
"src": "iconic"
},
{
"uid": "347c38a8b96a509270fdcabc951e7571",
"css": "database",
"code": 61888,
"src": "fontawesome"
},
{
"uid": "43ab845088317bd348dee1d975700c48",
"css": "ok-circled",
"code": 59418,
"src": "fontawesome"
},
{
"uid": "12f4ece88e46abd864e40b35e05b11cd",
"css": "ok",
"code": 59453,
"src": "fontawesome"
},
{
"uid": "7cca4643f1e938c673e91c0c78058ddf",
"css": "gitlab",
"code": 62102,
"src": "fontawesome"
},
{
"uid": "f06fe7ff18d1c591bc1183cb3ab105e9",
"css": "google",
"code": 61856,
"src": "fontawesome"
},
{
"uid": "8aff323697468c4a63993cde00386ec6",
"css": "windows",
"code": 61818,
"src": "fontawesome"
},
{
"uid": "4109c474ff99cad28fd5a2c38af2ec6f",
"css": "filter",
"code": 61616,
"src": "fontawesome"
},
{
"uid": "d862a10e1448589215be19702f98f2c1",
"css": "smile",
"code": 61720,
"src": "fontawesome"
},
{
"uid": "ccf71c505b173c61a2e4e8c8cb907dfa",
"css": "chat-alt",
"code": 59454,
"src": "typicons"
},
{
"uid": "bb1090404a4d33340a9ad7302ef8b6e8",
"css": "archive",
"code": 59455,
"src": "websymbols"
},
{
"uid": "cb13afd4722a849d48056540bb74c47e",
"css": "play",
"code": 59456,
"src": "entypo"
},
{
"uid": "d8d378d0ce413f231dfa37592e39c227",
"css": "pause",
"code": 59457,
"src": "entypo"
},
{
"uid": "ccddff8e8670dcd130e3cb55fdfc2fd0",
"css": "down-open",
"code": 59458,
"src": "fontawesome"
},
{
"uid": "fe6697b391355dec12f3d86d6d490397",
"css": "up-open",
"code": 59459,
"src": "fontawesome"
},
{
"uid": "390d6d13398cbf8c8c3c5493f7d34088",
"css": "export",
"code": 59406,
"src": "entypo"
},
{
"uid": "861ab06e455e2de3232ebef67d60d708",
"css": "minus",
"code": 59460,
"src": "fontawesome"
},
{
"uid": "41087bc74d4b20b55059c60a33bf4008",
"css": "edit",
"code": 59440,
"src": "fontawesome"
},
{
"uid": "823a9e02e643318116fea40a00190e4e",
"css": "asterisk",
"code": 59403,
"src": "fontawesome"
},
{
"uid": "8a67014b864fb93503348593979ddce3",
"css": "wheelchair",
"code": 61843,
"src": "fontawesome"
},
{
"uid": "fd45bb961d08f9f2092f0df3fba29fe9",
"css": "qrcode",
"code": 59426,
"src": "fontawesome"
},
{
"uid": "e07df3e5b9ae72836b7804f38f0644a9",
"css": "exchange",
"code": 59461,
"src": "iconic"
},
{
"uid": "414fe97e4af5215a06d29be0f2cecdf5",
"css": "network",
"code": 59462,
"src": "entypo"
},
{
"uid": "94089b37297572e936b0943bcfa041d3",
"css": "angle-circled-right",
"code": 61752,
"src": "fontawesome"
},
{
"uid": "8933c2579166c2ee56ae40dc6a0b4dc6",
"css": "angle-circled-left",
"code": 61751,
"src": "fontawesome"
},
{
"uid": "c2152732d525871cf35345955854f711",
"css": "moon-inv",
"code": 59464,
"src": "iconic"
},
{
"uid": "48050e07077b01021f7fbdbbec8f7de7",
"css": "sun-inv",
"code": 59465,
"src": "iconic"
},
{
"uid": "0f4cae16f34ae243a6144c18a003f2d8",
"css": "cancel-circled",
"code": 59466,
"src": "fontawesome"
},
{
"uid": "81bb68665e8e595505272a746db07c7a",
"css": "dot-circled",
"code": 61842,
"src": "fontawesome"
},
{
"uid": "16dfcfc4bec79b89c8b147dafbb5dd37",
"css": "lightning",
"code": 59467,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M66.2 582.6L331.3 583.1 254.6 998.8 910.4 394.6 632.8 393 716.7 1.5 66.2 582.6Z",
"width": 1000
},
"search": [
"49958"
]
},
{
"uid": "ef4f1a4b96dccf59cc7ba28aae651f94",
"css": "dev",
"code": 59468,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M24.3 495.7L24.3 726.2 105.7 726.2C195.8 726.2 233.2 714.7 260.6 679.4 288 644.8 292.3 614.5 289.5 477.7 287.3 356.6 285.8 346.5 270 320.6 242.6 275.9 212.4 265.1 110.8 265.1L24.3 265.1 24.3 495.7ZM190.8 358.1C207.3 371.7 208 374.6 208 491.3 208 604.4 207.3 611.6 192.2 626.8 179.9 638.3 167 642.6 141.8 642.6L107.2 643.4 105 493.5 103.6 344.4 138.9 344.4C162.6 344.4 179.2 348.7 190.8 358.1ZM356.4 283.8C342 302.6 341.3 311.9 341.3 497.1L341.3 690.9 359.3 708.2C375.9 725.5 380.2 726.2 474.6 726.2L571.8 726.2 571.8 647.7 498.4 645.5 424.2 643.4 424.2 535.3 469.6 533.1 514.2 531 514.2 452.4 420.6 452.4 420.6 344.4 571.8 344.4 571.8 265.1 471.7 265.1C371.6 265.1 370.8 265.1 356.4 283.8ZM651.1 367.4C666.2 424.3 690.7 517.3 705.9 574.9 728.9 663.5 736.8 683 756.3 702.4 770 716.8 786.6 726.2 797.4 726.2 819.7 726.2 848.5 703.9 858.6 677.9 865.8 659.2 968.1 277.4 968.1 268.7 968.1 266.6 947.9 265.1 923.4 266.6L878.1 268.7 837.7 423.6C812.5 521.6 795.9 573.5 793 564.1 787.3 546.1 715.9 271.6 715.9 268 715.9 266.6 695.1 265.1 669.8 265.1L623.7 265.1 651.1 367.4Z",
"width": 1000
},
"search": [
"dev"
]
},
{
"uid": "fb1c799ffe5bf8fb7f8bcb647c8fe9e6",
"css": "right-dir",
"code": 59469,
"src": "fontawesome"
},
{
"uid": "9dc654095085167524602c9acc0c5570",
"css": "left-dir",
"code": 59470,
"src": "fontawesome"
},
{
"uid": "dc73bee1d58539abe8c96b958bf4c931",
"css": "fire",
"code": 59471,
"src": "elusive"
},
{
"uid": "054a8f79ee2dae6c1ba909cc0c6f0cf9",
"css": "hackernews",
"code": 59472,
"src": "zocial"
},
{
"uid": "e7f91449144fb058ad8125e2d35b2363",
"css": "reddit",
"code": 59473,
"src": "elusive"
},
{
"uid": "5b43525a8080befb3493998ea77195e0",
"css": "integer",
"code": 59475,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M381.7 825.7L279.7 825.7 313.1 625 202.7 625 202.7 533.6 327.9 533.6 347.8 409.3 240.4 409.3 240.4 316.8 363.3 316.8 397.6 113.3 498.5 113.3 465.2 316.8 541.3 316.8 574.6 113.3 676.5 113.3 642.2 316.8 757.6 316.8 757.6 409.3 626.3 409.3 605.9 533.6 720.3 533.6 720.3 625 591.5 625 558.2 825.7 457.3 825.7 490.6 625 415 625 381.7 825.7ZM449.8 409.3L429.4 533.6 505 533.6 525.3 409.3 449.8 409.3Z",
"width": 1000
},
"search": [
"integer"
]
},
{
"uid": "23359bf9000e958419520fc09f111a22",
"css": "float",
"code": 59476,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M381.7 825.7L279.7 825.7 313.1 625 202.7 625 202.7 533.6 327.9 533.6 347.8 409.3 240.4 409.3 240.4 316.8 363.3 316.8 397.6 113.3 498.5 113.3 465.2 316.8 541.3 316.8 574.6 113.3 676.5 113.3 642.2 316.8 757.6 316.8 757.6 409.3 626.3 409.3 605.9 533.6 720.3 533.6 720.3 625 591.5 625 558.2 825.7 457.3 825.7 490.6 625 415 625 381.7 825.7ZM449.8 409.3L429.4 533.6 505 533.6 525.3 409.3 449.8 409.3Z",
"width": 1000
},
"search": [
"float"
]
},
{
"uid": "bbbebc5835cb7e7dd8cecdc59a4dad47",
"css": "ip",
"code": 59477,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M378.7 683.7L147.5 683.7 147.5 606.5 196.1 594.4 196.1 365.8 138.3 353.7 138.3 276.2 330.4 276.2 330.4 594.4 378.7 606.5 378.7 683.7ZM330.4 198.9L192.3 198.9 192.3 96.1 330.4 96.1 330.4 198.9ZM640.9 840.3L409.3 840.3 409.3 762.8 460.6 750.7 460.6 365.8 402.4 353.7 402.4 276.2 586.5 276.2 591.1 322.1C602.3 305.1 615.9 291.9 631.7 282.6 647.5 273.3 666.9 268.6 689.9 268.6 722.5 268.6 750.6 277.5 774.1 295.2 797.6 312.9 815.6 337.7 828.3 369.6 840.9 401.4 847.2 438.5 847.2 480.7L847.2 488.6C847.2 529.3 840.9 564.8 828.3 595.4 815.6 625.9 797.4 649.5 773.7 666.3 750 683.2 721.4 691.6 688 691.6 667.6 691.6 649.6 687.8 634.2 680.3 618.7 672.7 605.4 661.7 594.2 647.1L594.2 750.7 640.9 762.8 640.9 840.3ZM648.2 591C672.4 591 689.3 582 698.7 564.1 708.1 546.1 712.9 521 712.9 488.6L712.9 480.7C712.9 458.3 710.7 438.9 706.4 422.3 702 405.7 695.1 392.9 685.5 383.7 675.9 374.5 663.2 369.9 647.4 369.9 635.9 369.9 625.5 372.5 616.2 377.5 606.9 382.5 599.6 390 594.2 400.1L594.2 566.2C599.6 574.4 607 580.7 616.4 584.8 625.8 589 636.4 591 648.2 591Z",
"width": 1000
},
"search": [
"ip"
]
},
{
"uid": "ffd1698833bf4ed2d4c04041051f336b",
"css": "string",
"code": 59474,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M659.6 835.5L339.5 835.5 339.5 736.9 413.8 721.5 413.8 266.2 303.8 266.2 299.4 341.6 169.9 341.6 169.9 135.4 831.2 135.4 831.2 341.6 700.2 341.6 696.2 266.2 585.3 266.2 585.3 721.5 659.6 736.9 659.6 835.5Z",
"width": 1000
},
"search": [
"string"
]
},
{
"uid": "d91a2d9df5ed8188f0eb0eaeeebd675d",
"css": "more",
"code": 59478,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M970 500C970 759.6 759.6 970 500 970 240.4 970 30 759.6 30 500 30 240.4 240.4 30 500 30 759.6 30 970 240.4 970 500ZM680.1 623.6C641.2 623.6 609.6 655.1 609.6 694 609.6 732.9 641.2 764.5 680.1 764.5 719 764.5 750.5 732.9 750.5 694 750.5 655.1 719 623.6 680.1 623.6ZM308.5 621.5C269.6 621.5 238.1 653 238.1 692 238.1 730.9 269.6 762.4 308.5 762.4 347.4 762.4 378.9 730.9 378.9 692 378.9 653 347.4 621.5 308.5 621.5ZM493.6 621.9C454.7 621.9 423.1 653.4 423.1 692.3 423.1 731.2 454.7 762.7 493.6 762.7 532.5 762.7 564 731.2 564 692.3 564 653.4 532.5 621.9 493.6 621.9ZM751.5 694C751.5 713.7 743.4 731.7 730.6 744.5 717.7 757.4 699.7 765.5 680.1 765.5 660.4 765.5 642.4 757.4 629.5 744.5 616.7 731.7 608.6 713.7 608.6 694 608.6 674.4 616.7 656.4 629.5 643.5 642.4 630.6 660.4 622.6 680.1 622.6 699.7 622.6 717.7 630.6 730.6 643.5 743.4 656.4 751.5 674.4 751.5 694ZM729.2 644.9C716.5 632.3 699.3 624.6 680.1 624.6 660.8 624.6 643.6 632.3 631 644.9 618.3 657.5 610.6 674.8 610.6 694 610.6 713.3 618.3 730.5 631 743.1 643.6 755.7 660.8 763.5 680.1 763.5 699.3 763.5 716.5 755.7 729.2 743.1 741.8 730.5 749.5 713.3 749.5 694 749.5 674.8 741.8 657.5 729.2 644.9ZM379.9 692C379.9 711.6 371.9 729.6 359 742.5 346.1 755.3 328.1 763.4 308.5 763.4 288.8 763.4 270.9 755.3 258 742.5 245.1 729.6 237.1 711.6 237.1 692 237.1 672.3 245.1 654.3 258 641.4 270.9 628.6 288.8 620.5 308.5 620.5 328.1 620.5 346.1 628.6 359 641.4 371.9 654.3 379.9 672.3 379.9 692ZM357.6 642.9C345 630.2 327.7 622.5 308.5 622.5 289.2 622.5 272 630.2 259.4 642.9 246.8 655.5 239.1 672.7 239.1 692 239.1 711.2 246.8 728.4 259.4 741.1 272 753.7 289.2 761.4 308.5 761.4 327.7 761.4 345 753.7 357.6 741.1 370.2 728.4 377.9 711.2 377.9 692 377.9 672.7 370.2 655.5 357.6 642.9ZM565 692.3C565 712 557 729.9 544.1 742.8 531.2 755.7 513.2 763.7 493.6 763.7 473.9 763.7 455.9 755.7 443.1 742.8 430.2 729.9 422.1 712 422.1 692.3 422.1 672.6 430.2 654.7 443.1 641.8 455.9 628.9 473.9 620.9 493.6 620.9 513.2 620.9 531.2 628.9 544.1 641.8 557 654.7 565 672.6 565 692.3ZM542.7 643.2C530.1 630.6 512.8 622.9 493.6 622.9 474.3 622.9 457.1 630.6 444.5 643.2 431.9 655.8 424.1 673.1 424.1 692.3 424.1 711.6 431.9 728.8 444.5 741.4 457.1 754 474.3 761.7 493.6 761.7 512.8 761.7 530.1 754 542.7 741.4 555.3 728.8 563 711.6 563 692.3 563 673.1 555.3 655.8 542.7 643.2Z",
"width": 1000
},
"search": [
"more"
]
},
{
"uid": "272e08e0e16226aadf94dcbf33aab2b2",
"css": "key",
"code": 59479,
"src": "elusive"
},
{
"uid": "3256ef03b16e7ab51235bc7378b53bb5",
"css": "boolean",
"code": 61957,
"src": "fontawesome"
},
{
"uid": "47a1f80457068fbeab69fdb83d7d0817",
"css": "youtube-play",
"code": 61802,
"src": "fontawesome"
},
{
"uid": "b6941a012434c6246e2ad74248b6453e",
"css": "discord",
"code": 59463,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M1006.7 168.6C940.3 138.1 869 115.6 794.5 102.8 793.2 102.5 791.8 103.2 791.1 104.4 781.9 120.7 771.8 142 764.7 158.7 684.6 146.7 604.9 146.7 526.4 158.7 519.3 141.6 508.7 120.7 499.5 104.4 498.8 103.2 497.5 102.6 496.1 102.8 421.7 115.6 350.4 138.1 284 168.6 283.4 168.8 282.9 169.3 282.6 169.8 147.4 371.7 110.4 568.6 128.6 763.1 128.6 764.1 129.2 765 129.9 765.5 219.1 831 305.4 870.8 390.2 897.1 391.6 897.5 393 897 393.9 895.9 413.9 868.5 431.8 839.7 447.1 809.3 448 807.5 447.2 805.4 445.3 804.7 417 794 390 780.9 364 766 361.9 764.8 361.8 761.8 363.7 760.4 369.1 756.3 374.6 752.1 379.8 747.8 380.8 747 382.1 746.8 383.2 747.3 553.8 825.2 738.5 825.2 907.1 747.3 908.2 746.8 909.5 746.9 910.5 747.7 915.7 752 921.1 756.3 926.7 760.4 928.5 761.8 928.4 764.8 926.4 766 900.4 781.1 873.4 794 845 804.7 843.2 805.4 842.3 807.5 843.2 809.3 858.9 839.6 876.8 868.5 896.5 895.9 897.3 897 898.8 897.5 900.1 897.1 985.3 870.8 1071.7 831 1160.8 765.5 1161.6 765 1162.1 764.1 1162.2 763.2 1183.9 538.3 1125.8 343 1008 169.8 1007.8 169.3 1007.3 168.8 1006.7 168.6ZM472.6 644.7C421.2 644.7 378.9 597.5 378.9 539.6 378.9 481.7 420.4 434.6 472.6 434.6 525.2 434.6 567.1 482.1 566.3 539.6 566.3 597.5 524.8 644.7 472.6 644.7ZM819 644.7C767.6 644.7 725.3 597.5 725.3 539.6 725.3 481.7 766.8 434.6 819 434.6 871.6 434.6 913.5 482.1 912.6 539.6 912.6 597.5 871.6 644.7 819 644.7Z",
"width": 1291
},
"search": [
"discord-logo-color"
]
},
{
"uid": "00d86f3e46c3c3d768e7246eb0eadd7f",
"css": "discord",
"code": 59463,
"src": "custom_icons",
"selected": false,
"svg": {
"path": "M435 432.9C411.3 432.9 392.5 453.8 392.5 479.2S411.7 525.4 435 525.4C458.8 525.4 477.5 504.6 477.5 479.2 477.9 453.8 458.8 432.9 435 432.9ZM587.1 432.9C563.3 432.9 544.6 453.8 544.6 479.2S563.8 525.4 587.1 525.4C610.8 525.4 629.6 504.6 629.6 479.2S610.8 432.9 587.1 432.9ZM789.6 83.3H231.3C184.2 83.3 145.8 121.7 145.8 169.2V732.5C145.8 780 184.2 818.3 231.3 818.3H703.8L681.7 741.3 735 790.8 785.4 837.5 875 916.7V169.2C875 121.7 836.7 83.3 789.6 83.3ZM628.8 627.5S613.8 609.6 601.3 593.8C655.8 578.3 676.7 544.2 676.7 544.2 659.6 555.4 643.3 563.3 628.8 568.8 607.9 577.5 587.9 583.3 568.3 586.7 528.3 594.2 491.7 592.1 460.4 586.3 436.7 581.7 416.3 575 399.2 568.3 389.6 564.6 379.2 560 368.8 554.2 367.5 553.3 366.3 552.9 365 552.1 364.2 551.7 363.8 551.3 363.3 550.8 355.8 546.7 351.7 543.8 351.7 543.8S371.7 577.1 424.6 592.9C412.1 608.8 396.7 627.5 396.7 627.5 304.6 624.6 269.6 564.2 269.6 564.2 269.6 430 329.6 321.3 329.6 321.3 389.6 276.3 446.7 277.5 446.7 277.5L450.8 282.5C375.8 304.2 341.3 337.1 341.3 337.1S350.4 332.1 365.8 325C410.4 305.4 445.8 300 460.4 298.8 462.9 298.3 465 297.9 467.5 297.9 492.9 294.6 521.7 293.8 551.7 297.1 591.3 301.7 633.8 313.3 677.1 337.1 677.1 337.1 644.2 305.8 573.3 284.2L579.2 277.5S636.3 276.3 696.3 321.3C696.3 321.3 756.3 430 756.3 564.2 756.3 564.2 720.8 624.6 628.8 627.5Z",
"width": 1021
},
"search": [
"discord-logo-black"
]
},
{
"uid": "097a1d40f8e785fbd260946461a6ea9c",
"css": "discord",
"code": 59463,
"src": "custom_icons",
"selected": false,
"svg": {
"path": "M1092.8 89.1C1010.5 51.3 922.3 23.5 830 7.6 828.4 7.2 826.7 8 825.8 9.6 814.5 29.7 801.9 56.1 793.1 76.8 693.9 61.9 595.2 61.9 498 76.8 489.2 55.6 476.2 29.7 464.8 9.6 463.9 8.1 462.2 7.3 460.5 7.6 368.3 23.4 280.1 51.2 197.8 89.1 197.1 89.4 196.5 89.9 196 90.5 28.7 340.6-17.2 584.4 5.3 825.3 5.4 826.5 6.1 827.6 7 828.3 117.4 909.4 224.4 958.6 329.4 991.3 331 991.8 332.8 991.2 333.9 989.8 358.7 955.9 380.9 920.1 399.8 882.5 401 880.3 399.9 877.7 397.6 876.8 362.5 863.5 329.1 847.3 296.9 828.8 294.4 827.3 294.1 823.7 296.5 822 303.3 816.9 310 811.6 316.5 806.3 317.7 805.3 319.3 805.1 320.7 805.7 531.9 902.2 760.6 902.2 969.4 805.7 970.8 805.1 972.4 805.3 973.6 806.2 980.1 811.6 986.9 816.9 993.7 822 996 823.7 995.9 827.3 993.3 828.8 961.2 847.6 927.7 863.5 892.6 876.8 890.3 877.6 889.3 880.3 890.4 882.5 909.8 920.1 931.9 955.8 956.3 989.7 957.3 991.2 959.1 991.8 960.8 991.3 1066.3 958.6 1173.3 909.4 1283.7 828.3 1284.6 827.6 1285.2 826.5 1285.4 825.3 1312.3 546.9 1240.3 305 1094.5 90.6 1094.1 89.9 1093.5 89.4 1092.8 89.1ZM431.4 678.6C367.8 678.6 315.4 620.2 315.4 548.5 315.4 476.8 366.8 418.4 431.4 418.4 496.5 418.4 548.4 477.3 547.4 548.5 547.4 620.2 496 678.6 431.4 678.6ZM860.3 678.6C796.7 678.6 744.3 620.2 744.3 548.5 744.3 476.8 795.7 418.4 860.3 418.4 925.5 418.4 977.4 477.3 976.3 548.5 976.3 620.2 925.5 678.6 860.3 678.6Z",
"width": 1291
},
"search": [
"discord-logo-color"
]
}
]
}

View file

@ -239,6 +239,20 @@ button,
}
}
button.close {
width: 30px;
height: 30px;
line-height: 30px;
padding: 0;
margin: 0;
background: var(--config-color-normal);
color: var(--config-color-background-fade);
border-radius: 50%;
.icon-cancel::before{line-height:1;}
&.is-margin-top-10{margin-top:10px;}
}
label {
margin-bottom: 15px;
display: block;
@ -324,8 +338,6 @@ input[type=url] {
text-align: left;
direction: ltr;
}
;
}
select {
@ -406,7 +418,7 @@ fieldset {
.file-preview {
background: var(--config-color-background-input) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAIElEQVQoU2NkYGAwZsAEZ9GFGIeIQix+wfQgyDODXSEAcUwGCrDSHgkAAAAASUVORK5CYII=) !important;
border: solid 1px #e2e2e2;
/*border: solid 1px #e2e2e2;*/
box-shadow: inset 0 0 3px #a0a0a0;
border-radius: 8px;
width: ~"calc(100% - 2px)";
@ -585,8 +597,8 @@ input[type=checkbox], input[type=radio] {
&:checked:after {
text-align: center;
font-family: "fontello";
content: '\e83d';
font-family: "icon";
content: '\ea14';
font-size: 16px;
line-height: 20px;
color: var(--config-color-background-fade);
@ -622,6 +634,10 @@ input[type=checkbox], input[type=radio] {
}
}
input[type=checkbox] {
&:after {border-radius:4px;}
}
.input-copy {
position: relative;
@ -1029,8 +1045,8 @@ hr {
}
&:before {
content: '\e807';
font-family: "fontello";
content: '\ea11';
font-family: 'icon';
font-size: 12px;
position: absolute;
width: 20px;
@ -1134,6 +1150,7 @@ ol {
ul {
.box;
border-radius: 4px;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.10);
display: none;
position: absolute;
@ -1159,6 +1176,7 @@ ol {
.func-end(30px);
.func-start(unset);
}
&.no-arrow::before{display:none;}
li {
border-bottom: solid 1px var(--config-color-fade-super);
@ -1168,11 +1186,11 @@ ol {
&:first-child {
border-radius: 10px 10px 0 0;
border-radius: 4px 4px 0 0;
}
&:last-child {
border-radius: 0 0 10px 10px;
border-radius: 0 0 4px 4px;
}
&:hover {
@ -1294,8 +1312,8 @@ ol {
&::before {
.pull-end;
content: '\e807';
font-family: fontello;
content: '\ea11';
font-family: 'icon';
font-style: normal;
display: inline-block;
text-align: center;
@ -1336,7 +1354,7 @@ ol {
height: 18px;
width: 18px;
line-height: 18px;
font-size: 12px;
font-size: 10px;
padding: 0;
margin: 0;
color: var(--config-color-fade);

File diff suppressed because one or more lines are too long

View file

@ -340,7 +340,7 @@
a {
padding: 8px 20px;
border: none;
display: block;
display: flex;
color: #87a5b9;
font-weight: 400;
.func-border-start(5px, transparent);

View file

@ -30,7 +30,7 @@ table {
}
thead {
box-shadow: 0 0 2px rgba(0,0,0,.25);
//box-shadow: 0 0 2px rgba(0,0,0,.25);
border-bottom: solid 1px var(--config-color-fade-super);
font-size: 14px;
@ -75,11 +75,21 @@ table {
&:last-child {
border-bottom: none;
td:first-child{border-bottom-left-radius: 6px;}
td:last-child{border-bottom-right-radius: 6px;}
}
}
tr:nth-child(even) {
background: var(--config-color-background-fade-super);
tr:nth-child(even){
@media @phones, @tablets{
background: var(--config-color-background-fade-super);
}
@media @desktops{
td{
background: var(--config-color-background-fade-super);
}
}
}
tr.selected {
@ -137,6 +147,8 @@ table {
display: block;
}
tr{ position:relative; }
tr, th {
padding-top: 12px;
padding-bottom: 12px;
@ -154,6 +166,8 @@ table {
line-height: 40px;
width: ~"calc(100% - 40px)";
&.col-name{ width: ~"calc(100% - 110px)";}
&:first-child, &:last-child {
padding: 0 10px;
}
@ -179,6 +193,24 @@ table {
thead {
display: none;
}
.cell-options-more{
position:absolute; inset-block-start:0; inset-inline-end:0; width:auto;
}
}
}
.trim-inner-text {
display: inline-flex;
align-items: center;
gap: 8px;
@media @desktops{width:100%;}
}
.cell-options-more {
button {
padding:0 12px; background-color:transparent; color:var(--config-color-fade);
&:focus-visible{outline:solid 1px var(--config-color-focus);}
}
@media @phones, @tablets{}
}
}

View file

@ -92,6 +92,13 @@
--config-console-nav-switch-background: #ececec;
--config-console-nav-switch-color: #868686;
--config-console-nav-switch-arrow: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'><path fill='%23868686' d='M7.406 7.828l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z'></path></svg>");
// HSL colors for flexibility with opacity
// States colors - HSL colors (used by - pills)
--config-defalut-color-hsl: 0 0% 62%;
--config-disabled-color-hsl: 0 0% 62%;
--config-pending-color-hsl: 36 100% 47%;
--config-failed-color-hsl: 0 74% 42%;
.theme-dark {
--config-color-primary: #f02e65;

View file

@ -39,7 +39,7 @@ a, .link {
a.link-animation-enabled,
.link.link-animation-enabled {
display: inline-block;
display: inline-flex;
&:hover {
transform: translateY(-2px);
@ -235,12 +235,6 @@ small {
display: inline-block;
}
// Icon Hacks
.icon-dot-3:before {
.func-rotate(90deg);
}
// fix icons vertical alignment
i[class^='icon-'], i[class*=' icon-']{
&:before {

View file

@ -0,0 +1 @@
.u-margin-inline-end-16{margin-inline-end:16px!important;}

View file

@ -47,7 +47,7 @@ class Detector
*/
public function getClient(): array
{
if (strpos($this->userAgent, 'Appwrite CLI') !== false) {
if (strpos($this->userAgent, 'AppwriteCLI') !== false) {
$version = explode(' ', $this->userAgent)[0];
$version = explode('/', $version)[1];
$client = [

View file

@ -275,6 +275,9 @@ class OpenAPI3 extends Format
$node['schema']['x-example'] = false;
break;
case 'Utopia\Database\Validator\UID':
$node['schema']['type'] = $validator->getType();
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Appwrite\Utopia\Database\Validator\CustomId':
if($route->getLabel('sdk.methodType', '') === 'upload') {
$node['schema']['x-upload-id'] = true;
@ -375,9 +378,13 @@ class OpenAPI3 extends Format
$body['content'][$consumes[0]]['schema']['properties'][$name] = [
'type' => $node['schema']['type'],
'description' => $node['description'],
'x-example' => $node['x-example'] ?? null,
'x-example' => $node['schema']['x-example'] ?? null
];
if($node['schema']['x-upload-id'] ?? false) {
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-upload-id'] = $node['schema']['x-upload-id'];
}
if(isset($node['default'])) {
$body['content'][$consumes[0]]['schema']['properties'][$name]['default'] = $node['default'];
}

View file

@ -262,7 +262,6 @@ class Swagger2 extends Format
$node['type'] = $validator->getType();
$node['x-example'] = false;
break;
case 'Utopia\Database\Validator\UID':
case 'Appwrite\Utopia\Database\Validator\CustomId':
if($route->getLabel('sdk.methodType', '') === 'upload') {
$node['x-upload-id'] = true;
@ -270,6 +269,10 @@ class Swagger2 extends Format
$node['type'] = $validator->getType();
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Database\Validator\UID':
$node['type'] = $validator->getType();
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Appwrite\Network\Validator\Email':
$node['type'] = $validator->getType();
$node['format'] = 'email';

View file

@ -48,6 +48,7 @@ trait RealtimeBase
$this->assertEquals('error', $payload['type']);
$this->assertEquals(1008, $payload['data']['code']);
$this->assertEquals('Missing channels', $payload['data']['message']);
\usleep(250000); // 250ms
$this->expectException(ConnectionException::class); // Check if server disconnnected client
$client->close();
}

View file

@ -21,7 +21,7 @@ abstract class MigrationTest extends TestCase
/**
* Runs every document fix twice, to prevent corrupted data on multiple migrations.
*
*
* @param Document $document
*/
protected function fixDocument(Document $document)
@ -42,6 +42,6 @@ abstract class MigrationTest extends TestCase
$this->assertTrue(class_exists('Appwrite\\Migration\\Version\\'.$class));
}
// Test if current version exists
$this->assertArrayHasKey(APP_VERSION_STABLE, Migration::$versions);
//$this->assertArrayHasKey(APP_VERSION_STABLE, Migration::$versions);
}
}