mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge conflicts in CHANGES.md
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = 4.3.0
|
||||
branch = 4.3.2
|
||||
|
||||
+32
-1
@@ -1,9 +1,40 @@
|
||||
# Version 1.5.7
|
||||
## What's Changed
|
||||
|
||||
## Fixes
|
||||
### Notable Changes
|
||||
|
||||
* Prevent functions domain to be used as custom domain in [#7934](https://github.com/appwrite/appwrite/pull/7934)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix auth mode check in [#7980](https://github.com/appwrite/appwrite/pull/7980)
|
||||
* Fix templates not copying hidden files in [#7610](https://github.com/appwrite/appwrite/pull/7610)
|
||||
* Use `resourceInternalId` for Querying Function Deployments in [#8038](https://github.com/appwrite/appwrite/pull/8038)
|
||||
* Fix Email OTP not verifying account in [#8084](https://github.com/appwrite/appwrite/pull/8084)
|
||||
* Fix MFA email verification code font in [#8082](https://github.com/appwrite/appwrite/pull/8082)
|
||||
* Don't kick user and require verification after enabling MFA in [#8081](https://github.com/appwrite/appwrite/pull/8081)
|
||||
* Fix typo in credit-cards.php credit card image filename in [#8074](https://github.com/appwrite/appwrite/pull/8074)
|
||||
* Fix Deprecated Warning in Doctor.php in [#8105](https://github.com/appwrite/appwrite/pull/8105)
|
||||
* Set limit to retrieve all stats for the usage range in [#8117](https://github.com/appwrite/appwrite/pull/8117)
|
||||
* Fix email used for name when user is created via Apple OAuth2 in [#8102](https://github.com/appwrite/appwrite/pull/8102)
|
||||
* Change executor hostname back to exc1 to prevent name too long errors in the swoole table [#8147](https://github.com/appwrite/appwrite/pull/8147)
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Add GitHub action to close stale issues in [#7927](https://github.com/appwrite/appwrite/pull/7927)
|
||||
* Document the standard we follow for country codes in [#8014](https://github.com/appwrite/appwrite/pull/8014)
|
||||
* Add OSV Scanner for vulnerability scans in [#6506](https://github.com/appwrite/appwrite/pull/6506)
|
||||
* Fix stale action close reason in [#8046](https://github.com/appwrite/appwrite/pull/8046)
|
||||
* Add OSV Scanner for vulnerability scans in [#8021](https://github.com/appwrite/appwrite/pull/8021)
|
||||
* Fix some typos in comments in [#7993](https://github.com/appwrite/appwrite/pull/7993)
|
||||
* Replace missing domain paths in README.md in [#8049](https://github.com/appwrite/appwrite/pull/8049)
|
||||
* Add the React Native SDK in [#7776](https://github.com/appwrite/appwrite/pull/7776)
|
||||
* Bump database in [#8080](https://github.com/appwrite/appwrite/pull/8080)
|
||||
* Add documentation for metrics in [#8088](https://github.com/appwrite/appwrite/pull/8088)
|
||||
* Add new country Palestine with its translations in [#8031](https://github.com/appwrite/appwrite/pull/8031)
|
||||
* Update users create token description in [#8129](https://github.com/appwrite/appwrite/pull/8129)
|
||||
* Bump dependencies in [#8130](https://github.com/appwrite/appwrite/pull/8130)
|
||||
|
||||
# Version 1.5.5
|
||||
## What's Changed
|
||||
### Notable changes
|
||||
|
||||
+10
-2
@@ -148,6 +148,14 @@ Learn more at our [Technology Stack](#technology-stack) section.
|
||||
- [Microservices vs Monolithic](https://www.mulesoft.com/resources/api/microservices-vs-monolithic#:~:text=Microservices%20architecture%20vs%20monolithic%20architecture&text=A%20monolithic%20application%20is%20built%20as%20a%20single%20unit.&text=To%20make%20any%20alterations%20to,formally%20with%20business%2Doriented%20APIs.)
|
||||
- [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) - Appwrite console architecture
|
||||
|
||||
##### Container Namespace Conventions
|
||||
To keep our services easy to understand within Docker we follow a naming convention for all our containers depending on it's intended use.
|
||||
|
||||
`appwrite-worker-X` - Workers (`src/Appwrite/Platform/Workers/*`)
|
||||
`appwrite-task-X` - Tasks (`src/Appwrite/Platform/Tasks/*`)
|
||||
|
||||
Other containes should be named the same as their service, for example `redis` should just be called `redis`.
|
||||
|
||||
##### Security
|
||||
|
||||
- [Appwrite Auth and ACL](https://github.com/appwrite/appwrite/blob/master/docs/specs/authentication.drawio.svg)
|
||||
@@ -481,9 +489,9 @@ Things to remember when releasing SDKs:
|
||||
|
||||
## Debug
|
||||
|
||||
Appwrite uses [XDebug](https://github.com/xdebug/xdebug) debugger, which can be made available during build of Appwrite. You can connect to the debugger using VS Code's [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug) extension.
|
||||
Appwrite uses [XDebug](https://github.com/xdebug/xdebug) debugger, which can be made available during build of Appwrite. You can connect to the debugger using VS Code's [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug) extension.
|
||||
|
||||
If you are in PHP Storm you don't need any plugin. Below are the settings required for remote debugger connection:
|
||||
If you are in PHP Storm you don't need any plugin. Below are the settings required for remote debugger connection:
|
||||
|
||||
1. Set **DEBUG** build arg in **appwrite** service in **docker-compose.yml** file.
|
||||
2. If needed edit the **dev/xdebug.ini** file to your needs.
|
||||
|
||||
-25
@@ -72,9 +72,6 @@ RUN mkdir -p /storage/uploads && \
|
||||
chown -Rf www-data.www-data /storage/functions && chmod -Rf 0755 /storage/functions && \
|
||||
chown -Rf www-data.www-data /storage/debug && chmod -Rf 0755 /storage/debug
|
||||
|
||||
# Development Executables
|
||||
RUN chmod +x /usr/local/bin/dev-generate-translations
|
||||
|
||||
# Executables
|
||||
RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/install && \
|
||||
@@ -99,35 +96,13 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/worker-databases && \
|
||||
chmod +x /usr/local/bin/worker-deletes && \
|
||||
chmod +x /usr/local/bin/worker-functions && \
|
||||
chmod +x /usr/local/bin/worker-hamster && \
|
||||
chmod +x /usr/local/bin/worker-mails && \
|
||||
chmod +x /usr/local/bin/worker-messaging && \
|
||||
chmod +x /usr/local/bin/worker-migrations && \
|
||||
chmod +x /usr/local/bin/worker-webhooks && \
|
||||
chmod +x /usr/local/bin/worker-hamster && \
|
||||
chmod +x /usr/local/bin/worker-usage && \
|
||||
chmod +x /usr/local/bin/worker-usage-dump
|
||||
|
||||
|
||||
# Cloud Executabless
|
||||
RUN chmod +x /usr/local/bin/calc-tier-stats && \
|
||||
chmod +x /usr/local/bin/calc-users-stats && \
|
||||
chmod +x /usr/local/bin/clear-card-cache && \
|
||||
chmod +x /usr/local/bin/delete-orphaned-projects && \
|
||||
chmod +x /usr/local/bin/get-migration-stats && \
|
||||
chmod +x /usr/local/bin/hamster && \
|
||||
chmod +x /usr/local/bin/patch-delete-project-collections && \
|
||||
chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \
|
||||
chmod +x /usr/local/bin/patch-recreate-repositories-documents && \
|
||||
chmod +x /usr/local/bin/volume-sync && \
|
||||
chmod +x /usr/local/bin/patch-delete-project-collections && \
|
||||
chmod +x /usr/local/bin/delete-orphaned-projects && \
|
||||
chmod +x /usr/local/bin/clear-card-cache && \
|
||||
chmod +x /usr/local/bin/calc-users-stats && \
|
||||
chmod +x /usr/local/bin/calc-tier-stats && \
|
||||
chmod +x /usr/local/bin/get-migration-stats && \
|
||||
chmod +x /usr/local/bin/create-inf-metric
|
||||
|
||||
# Letsencrypt Permissions
|
||||
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
|
||||
|
||||
|
||||
+3
-3
@@ -67,7 +67,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:1.5.5
|
||||
appwrite/appwrite:1.5.6
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -79,7 +79,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:1.5.5
|
||||
appwrite/appwrite:1.5.6
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -89,7 +89,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:1.5.5
|
||||
appwrite/appwrite:1.5.6
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
@@ -75,7 +75,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:1.5.5
|
||||
appwrite/appwrite:1.5.6
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -87,7 +87,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:1.5.5
|
||||
appwrite/appwrite:1.5.6
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -97,7 +97,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:1.5.5
|
||||
appwrite/appwrite:1.5.6
|
||||
```
|
||||
|
||||
Once the Docker installation is complete, 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 completing the installation.
|
||||
|
||||
@@ -6,7 +6,6 @@ require_once __DIR__ . '/controllers/general.php';
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Hamster;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
@@ -131,9 +130,6 @@ CLI::setResource('queue', function (Group $pools) {
|
||||
CLI::setResource('queueForFunctions', function (Connection $queue) {
|
||||
return new Func($queue);
|
||||
}, ['queue']);
|
||||
CLI::setResource('queueForHamster', function (Connection $queue) {
|
||||
return new Hamster($queue);
|
||||
}, ['queue']);
|
||||
CLI::setResource('queueForDeletes', function (Connection $queue) {
|
||||
return new Delete($queue);
|
||||
}, ['queue']);
|
||||
|
||||
@@ -519,6 +519,11 @@ return [
|
||||
'description' => 'Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::FUNCTION_SYNCHRONOUS_TIMEOUT => [
|
||||
'name' => Exception::FUNCTION_SYNCHRONOUS_TIMEOUT,
|
||||
'description' => 'Synchronous function execution timed out. Use asynchronous execution instead, or ensure the execution duration doesn\'t exceed 30 seconds.',
|
||||
'code' => 408,
|
||||
],
|
||||
|
||||
/** Builds */
|
||||
Exception::BUILD_NOT_FOUND => [
|
||||
|
||||
@@ -926,12 +926,12 @@ return [
|
||||
[
|
||||
"code" => "zh-cn",
|
||||
"name" => "Chinese (Simplified)",
|
||||
"nativeName" => "中国人"
|
||||
"nativeName" => "中文"
|
||||
],
|
||||
[
|
||||
"code" => "zh-tw",
|
||||
"name" => "Chinese (Traditional)",
|
||||
"nativeName" => "中國人"
|
||||
"nativeName" => "中文"
|
||||
],
|
||||
[
|
||||
"code" => "zu",
|
||||
|
||||
+18
-18
@@ -15,7 +15,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '14.0.0',
|
||||
'version' => '14.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -63,7 +63,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '12.0.3',
|
||||
'version' => '12.0.4',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -81,7 +81,7 @@ return [
|
||||
[
|
||||
'key' => 'apple',
|
||||
'name' => 'Apple',
|
||||
'version' => '5.0.0',
|
||||
'version' => '6.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
@@ -116,7 +116,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '5.0.0',
|
||||
'version' => '5.1.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
@@ -138,7 +138,7 @@ return [
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.3.0',
|
||||
'version' => '0.3.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -203,7 +203,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Console',
|
||||
'version' => '0.6.1',
|
||||
'version' => '0.6.3',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-console',
|
||||
'package' => '',
|
||||
'enabled' => true,
|
||||
@@ -221,7 +221,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '5.0.2',
|
||||
'version' => '5.0.5',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -249,7 +249,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '12.0.1',
|
||||
'version' => '13.0.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -267,7 +267,7 @@ return [
|
||||
[
|
||||
'key' => 'deno',
|
||||
'name' => 'Deno',
|
||||
'version' => '10.0.1',
|
||||
'version' => '10.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
||||
'package' => 'https://deno.land/x/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -285,7 +285,7 @@ return [
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '11.0.1',
|
||||
'version' => '11.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -303,7 +303,7 @@ return [
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '5.0.2',
|
||||
'version' => '5.0.3',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -321,7 +321,7 @@ return [
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '11.0.1',
|
||||
'version' => '11.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -339,7 +339,7 @@ return [
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => '4.0.0',
|
||||
'version' => '4.0.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => '',
|
||||
'enabled' => false,
|
||||
@@ -357,7 +357,7 @@ return [
|
||||
[
|
||||
'key' => 'java',
|
||||
'name' => 'Java',
|
||||
'version' => '4.0.1',
|
||||
'version' => '4.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-java',
|
||||
'package' => '',
|
||||
'enabled' => false,
|
||||
@@ -375,7 +375,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.8.1',
|
||||
'version' => '0.8.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -393,7 +393,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '11.0.2',
|
||||
'version' => '11.0.3',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -411,7 +411,7 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '5.0.1',
|
||||
'version' => '5.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
@@ -433,7 +433,7 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '5.0.1',
|
||||
'version' => '5.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -9,6 +9,7 @@ use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Usage;
|
||||
use Appwrite\Event\Validator\FunctionEvent;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Task\Validator\Cron;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
@@ -1750,6 +1751,10 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->setAttribute('responseStatusCode', 500)
|
||||
->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode());
|
||||
Console::error($th->getMessage());
|
||||
|
||||
if ($th instanceof AppwriteException) {
|
||||
throw $th;
|
||||
}
|
||||
} finally {
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_EXECUTIONS, 1)
|
||||
@@ -1757,11 +1762,11 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
;
|
||||
}
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
/** @var Document $execution */
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
if ($function->getAttribute('logging')) {
|
||||
/** @var Document $execution */
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
|
||||
@@ -856,8 +856,7 @@ App::get('/v1/health/queue/failed/:name')
|
||||
Event::CERTIFICATES_QUEUE_NAME,
|
||||
Event::BUILDS_QUEUE_NAME,
|
||||
Event::MESSAGING_QUEUE_NAME,
|
||||
Event::MIGRATIONS_QUEUE_NAME,
|
||||
Event::HAMSTER_CLASS_NAME
|
||||
Event::MIGRATIONS_QUEUE_NAME
|
||||
]), 'The name of the queue')
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->label('sdk.description', '/docs/references/health/get-failed-queue-jobs.md')
|
||||
|
||||
@@ -453,7 +453,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
if (empty($invitee)) { // Create new user if no user with same email found
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
|
||||
if (!$isPrivilegedUser && !$isAppUser && $limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
|
||||
$total = $dbForProject->count('users', [], APP_LIMIT_USERS);
|
||||
|
||||
if ($total >= $limit) {
|
||||
|
||||
@@ -288,6 +288,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
$execution->setAttribute('logs', $executionResponse['logs']);
|
||||
$execution->setAttribute('errors', $executionResponse['errors']);
|
||||
$execution->setAttribute('duration', $executionResponse['duration']);
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$durationEnd = \microtime(true);
|
||||
|
||||
@@ -297,6 +298,10 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
->setAttribute('responseStatusCode', 500)
|
||||
->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode());
|
||||
Console::error($th->getMessage());
|
||||
|
||||
if ($th instanceof AppwriteException) {
|
||||
throw $th;
|
||||
}
|
||||
} finally {
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_EXECUTIONS, 1)
|
||||
@@ -304,11 +309,11 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
;
|
||||
}
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
/** @var Document $execution */
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
if ($function->getAttribute('logging')) {
|
||||
/** @var Document $execution */
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
}
|
||||
|
||||
$execution->setAttribute('logs', '');
|
||||
|
||||
@@ -411,7 +411,7 @@ App::init()
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
if ($useCache) {
|
||||
$key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
@@ -666,7 +666,7 @@ App::shutdown()
|
||||
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
}
|
||||
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER;
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$signature = md5($data['payload']);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
||||
|
||||
+2
-2
@@ -112,8 +112,8 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return
|
||||
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 430;
|
||||
const APP_VERSION_STABLE = '1.5.5';
|
||||
const APP_CACHE_BUSTER = 432;
|
||||
const APP_VERSION_STABLE = '1.5.6';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
|
||||
@@ -574,11 +574,11 @@ services:
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||
|
||||
appwrite-maintenance:
|
||||
appwrite-task-maintenance:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: maintenance
|
||||
<<: *x-logging
|
||||
container_name: appwrite-maintenance
|
||||
container_name: appwrite-task-maintenance
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -665,10 +665,10 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-scheduler-functions:
|
||||
appwrite-task-scheduler-functions:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: schedule-functions
|
||||
container_name: appwrite-scheduler-functions
|
||||
container_name: appwrite-task-scheduler-functions
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
@@ -690,10 +690,10 @@ services:
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
|
||||
appwrite-scheduler-messages:
|
||||
appwrite-task-scheduler-messages:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: schedule-messages
|
||||
container_name: appwrite-scheduler-messages
|
||||
container_name: appwrite-task-scheduler-messages
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
@@ -731,7 +731,7 @@ services:
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.4.12
|
||||
image: openruntimes/executor:0.5.5
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
|
||||
@@ -9,7 +9,6 @@ use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Hamster;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Messaging;
|
||||
use Appwrite\Event\Migration;
|
||||
@@ -194,10 +193,6 @@ Server::setResource('queueForMigrations', function (Connection $queue) {
|
||||
return new Migration($queue);
|
||||
}, ['queue']);
|
||||
|
||||
Server::setResource('queueForHamster', function (Connection $queue) {
|
||||
return new Hamster($queue);
|
||||
}, ['queue']);
|
||||
|
||||
Server::setResource('logger', function (Registry $register) {
|
||||
return $register->get('logger');
|
||||
}, ['register']);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php calc-tier-stats $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php calc-users-stats $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php clear-card-cache $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php create-inf-metric $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php delete-orphaned-projects $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php dev-generate-translations $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php get-migration-stats $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php hamster $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php patch-delete-project-collections $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php patch-delete-schedule-updated-at-attribute $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php patch-recreate-repositories-documents $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php volume-sync $@
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php hamster $@
|
||||
Generated
+24
-24
@@ -1556,16 +1556,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.49.9",
|
||||
"version": "0.49.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "ee93c14b99820f791c669648854f094fe399a085"
|
||||
"reference": "216209121bc97a2010f67a39c561fafe1e936bec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/ee93c14b99820f791c669648854f094fe399a085",
|
||||
"reference": "ee93c14b99820f791c669648854f094fe399a085",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/216209121bc97a2010f67a39c561fafe1e936bec",
|
||||
"reference": "216209121bc97a2010f67a39c561fafe1e936bec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1606,9 +1606,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.49.9"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.49.10"
|
||||
},
|
||||
"time": "2024-05-12T23:58:34+00:00"
|
||||
"time": "2024-05-20T02:14:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
@@ -1966,16 +1966,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "117be70da329dac047d22b4250dfa435a725e187"
|
||||
"reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/117be70da329dac047d22b4250dfa435a725e187",
|
||||
"reference": "117be70da329dac047d22b4250dfa435a725e187",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33",
|
||||
"reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2007,9 +2007,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.4.3"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.4.4"
|
||||
},
|
||||
"time": "2024-05-15T04:49:28+00:00"
|
||||
"time": "2024-05-17T05:25:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
@@ -2498,16 +2498,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/vcs",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"reference": "104e47ea8e38c156ec0e0bd415caa3dcd5046fe2"
|
||||
"reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/104e47ea8e38c156ec0e0bd415caa3dcd5046fe2",
|
||||
"reference": "104e47ea8e38c156ec0e0bd415caa3dcd5046fe2",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/e538264cfee5e3efdfe1771efba04750cf20b2c4",
|
||||
"reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2541,9 +2541,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/vcs/issues",
|
||||
"source": "https://github.com/utopia-php/vcs/tree/0.6.5"
|
||||
"source": "https://github.com/utopia-php/vcs/tree/0.6.6"
|
||||
},
|
||||
"time": "2024-01-08T17:11:12+00:00"
|
||||
"time": "2024-05-17T09:36:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/websocket",
|
||||
@@ -2730,16 +2730,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.38.4",
|
||||
"version": "0.38.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "af7e4b53e9d5467fcb03d482d539669bf2eacdd8"
|
||||
"reference": "d7016d6d72545e84709892faca972eb4bf5bd699"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/af7e4b53e9d5467fcb03d482d539669bf2eacdd8",
|
||||
"reference": "af7e4b53e9d5467fcb03d482d539669bf2eacdd8",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d7016d6d72545e84709892faca972eb4bf5bd699",
|
||||
"reference": "d7016d6d72545e84709892faca972eb4bf5bd699",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2775,9 +2775,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.38.4"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.38.6"
|
||||
},
|
||||
"time": "2024-05-15T00:35:29+00:00"
|
||||
"time": "2024-05-20T18:00:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
|
||||
+7
-64
@@ -627,10 +627,10 @@ services:
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||
|
||||
appwrite-maintenance:
|
||||
appwrite-task-maintenance:
|
||||
entrypoint: maintenance
|
||||
<<: *x-logging
|
||||
container_name: appwrite-maintenance
|
||||
container_name: appwrite-task-maintenance
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -726,10 +726,10 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-scheduler-functions:
|
||||
appwrite-task-scheduler-functions:
|
||||
entrypoint: schedule-functions
|
||||
<<: *x-logging
|
||||
container_name: appwrite-scheduler-functions
|
||||
container_name: appwrite-task-scheduler-functions
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -753,10 +753,10 @@ services:
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
|
||||
appwrite-scheduler-messages:
|
||||
appwrite-task-scheduler-messages:
|
||||
entrypoint: schedule-messages
|
||||
<<: *x-logging
|
||||
container_name: appwrite-scheduler-messages
|
||||
container_name: appwrite-task-scheduler-messages
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -788,69 +788,12 @@ services:
|
||||
environment:
|
||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||
|
||||
appwrite-worker-hamster:
|
||||
entrypoint: worker-hamster
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-hamster
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_MIXPANEL_TOKEN
|
||||
|
||||
appwrite-hamster-scheduler:
|
||||
entrypoint: hamster
|
||||
<<: *x-logging
|
||||
container_name: appwrite-hamster-scheduler
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_HAMSTER_TIME
|
||||
- _APP_HAMSTER_INTERVAL
|
||||
|
||||
openruntimes-executor:
|
||||
container_name: openruntimes-executor
|
||||
hostname: exc1
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.4.12
|
||||
image: openruntimes/executor:0.5.5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
@@ -7,7 +7,7 @@ let client = Client()
|
||||
|
||||
let account = Account(client)
|
||||
|
||||
let user = try await account.deleteMfaAuthenticator(
|
||||
let result = try await account.deleteMfaAuthenticator(
|
||||
type: .totp,
|
||||
otp: "<OTP>"
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ mutation {
|
||||
accountCreateAnonymousSession {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -3,35 +3,6 @@ mutation {
|
||||
type: "totp",
|
||||
otp: "<OTP>"
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
name
|
||||
password
|
||||
hash
|
||||
hashOptions
|
||||
registration
|
||||
status
|
||||
labels
|
||||
passwordUpdate
|
||||
email
|
||||
phone
|
||||
emailVerification
|
||||
phoneVerification
|
||||
mfa
|
||||
prefs {
|
||||
data
|
||||
}
|
||||
targets {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
name
|
||||
userId
|
||||
providerId
|
||||
providerType
|
||||
identifier
|
||||
}
|
||||
accessedAt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ query {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -3,5 +3,6 @@ query {
|
||||
totp
|
||||
phone
|
||||
email
|
||||
recoveryCode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ query {
|
||||
sessions {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -4,6 +4,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -10,3 +10,4 @@ appwrite messaging updateEmail \
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ const result = await messaging.updateEmail(
|
||||
false, // html (optional)
|
||||
[], // cc (optional)
|
||||
[], // bcc (optional)
|
||||
'' // scheduledAt (optional)
|
||||
'', // scheduledAt (optional)
|
||||
[] // attachments (optional)
|
||||
);
|
||||
|
||||
console.log(response);
|
||||
|
||||
@@ -19,4 +19,5 @@ Message result = await messaging.updateEmail(
|
||||
cc: [], // (optional)
|
||||
bcc: [], // (optional)
|
||||
scheduledAt: '', // (optional)
|
||||
attachments: [], // (optional)
|
||||
);
|
||||
|
||||
@@ -18,5 +18,6 @@ const response = await messaging.updateEmail(
|
||||
false, // html (optional)
|
||||
[], // cc (optional)
|
||||
[], // bcc (optional)
|
||||
'' // scheduledAt (optional)
|
||||
'', // scheduledAt (optional)
|
||||
[] // attachments (optional)
|
||||
);
|
||||
|
||||
@@ -20,5 +20,6 @@ Message result = await messaging.UpdateEmail(
|
||||
html: false, // optional
|
||||
cc: new List<string>(), // optional
|
||||
bcc: new List<string>(), // optional
|
||||
scheduledAt: "" // optional
|
||||
scheduledAt: "", // optional
|
||||
attachments: new List<string>() // optional
|
||||
);
|
||||
@@ -2,6 +2,7 @@ mutation {
|
||||
accountCreateAnonymousSession {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -3,35 +3,6 @@ mutation {
|
||||
type: "totp",
|
||||
otp: "<OTP>"
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
name
|
||||
password
|
||||
hash
|
||||
hashOptions
|
||||
registration
|
||||
status
|
||||
labels
|
||||
passwordUpdate
|
||||
email
|
||||
phone
|
||||
emailVerification
|
||||
phoneVerification
|
||||
mfa
|
||||
prefs {
|
||||
data
|
||||
}
|
||||
targets {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
name
|
||||
userId
|
||||
providerId
|
||||
providerType
|
||||
identifier
|
||||
}
|
||||
accessedAt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ query {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -3,5 +3,6 @@ query {
|
||||
totp
|
||||
phone
|
||||
email
|
||||
recoveryCode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ query {
|
||||
sessions {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,6 +5,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -4,6 +4,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -10,7 +10,8 @@ mutation {
|
||||
html: false,
|
||||
cc: [],
|
||||
bcc: [],
|
||||
scheduledAt: ""
|
||||
scheduledAt: "",
|
||||
attachments: []
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
|
||||
@@ -4,6 +4,7 @@ mutation {
|
||||
) {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -5,5 +5,6 @@ query {
|
||||
totp
|
||||
phone
|
||||
email
|
||||
recoveryCode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ query {
|
||||
sessions {
|
||||
_id
|
||||
_createdAt
|
||||
_updatedAt
|
||||
userId
|
||||
expire
|
||||
provider
|
||||
|
||||
@@ -21,6 +21,7 @@ messaging.updateEmail(
|
||||
listOf(), // cc (optional)
|
||||
listOf(), // bcc (optional)
|
||||
"", // scheduledAt (optional)
|
||||
listOf(), // attachments (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -20,5 +20,6 @@ val response = messaging.updateEmail(
|
||||
html = false, // optional
|
||||
cc = listOf(), // optional
|
||||
bcc = listOf(), // optional
|
||||
scheduledAt = "" // optional
|
||||
scheduledAt = "", // optional
|
||||
attachments = listOf() // optional
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ const functions = new sdk.Functions(client);
|
||||
|
||||
const result = await functions.createDeployment(
|
||||
'<FUNCTION_ID>', // functionId
|
||||
InputFile.fromPath('/path/to/file.png', 'file.png'), // code
|
||||
InputFile.fromPath('/path/to/file', 'filename'), // code
|
||||
false, // activate
|
||||
'<ENTRYPOINT>', // entrypoint (optional)
|
||||
'<COMMANDS>' // commands (optional)
|
||||
|
||||
@@ -18,5 +18,6 @@ const result = await messaging.updateEmail(
|
||||
false, // html (optional)
|
||||
[], // cc (optional)
|
||||
[], // bcc (optional)
|
||||
'' // scheduledAt (optional)
|
||||
'', // scheduledAt (optional)
|
||||
[] // attachments (optional)
|
||||
);
|
||||
|
||||
@@ -11,6 +11,6 @@ const storage = new sdk.Storage(client);
|
||||
const result = await storage.createFile(
|
||||
'<BUCKET_ID>', // bucketId
|
||||
'<FILE_ID>', // fileId
|
||||
InputFile.fromPath('/path/to/file.png', 'file.png'), // file
|
||||
InputFile.fromPath('/path/to/file', 'filename'), // file
|
||||
["read("any")"] // permissions (optional)
|
||||
);
|
||||
|
||||
@@ -21,5 +21,6 @@ $result = $messaging->updateEmail(
|
||||
html: false, // optional
|
||||
cc: [], // optional
|
||||
bcc: [], // optional
|
||||
scheduledAt: '' // optional
|
||||
scheduledAt: '', // optional
|
||||
attachments: [] // optional
|
||||
);
|
||||
@@ -18,5 +18,6 @@ result = messaging.update_email(
|
||||
html = False, # optional
|
||||
cc = [], # optional
|
||||
bcc = [], # optional
|
||||
scheduled_at = '' # optional
|
||||
scheduled_at = '', # optional
|
||||
attachments = [] # optional
|
||||
)
|
||||
|
||||
@@ -15,5 +15,6 @@ X-Appwrite-Key: 919c2d18fb5d4...a2ae413da83346ad2
|
||||
"html": false,
|
||||
"cc": [],
|
||||
"bcc": [],
|
||||
"scheduledAt":
|
||||
"scheduledAt": ,
|
||||
"attachments": []
|
||||
}
|
||||
|
||||
@@ -20,5 +20,6 @@ result = messaging.update_email(
|
||||
html: false, # optional
|
||||
cc: [], # optional
|
||||
bcc: [], # optional
|
||||
scheduled_at: '' # optional
|
||||
scheduled_at: '', # optional
|
||||
attachments: [] # optional
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ let client = Client()
|
||||
|
||||
let account = Account(client)
|
||||
|
||||
let user = try await account.deleteMfaAuthenticator(
|
||||
let result = try await account.deleteMfaAuthenticator(
|
||||
type: .totp,
|
||||
otp: "<OTP>"
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ let message = try await messaging.updateEmail(
|
||||
html: false, // optional
|
||||
cc: [], // optional
|
||||
bcc: [], // optional
|
||||
scheduledAt: "" // optional
|
||||
scheduledAt: "", // optional
|
||||
attachments: [] // optional
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 11.0.3
|
||||
|
||||
* Minor bugfixes
|
||||
|
||||
## 11.0.2
|
||||
|
||||
* Fixed MSG91 missing template ID
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 12.0.4
|
||||
|
||||
* Fixed concurrent modification error when closing realtime socket
|
||||
|
||||
## 12.0.3
|
||||
|
||||
* Upgrade dependencies
|
||||
|
||||
@@ -45,9 +45,6 @@ class Event
|
||||
public const MIGRATIONS_QUEUE_NAME = 'v1-migrations';
|
||||
public const MIGRATIONS_CLASS_NAME = 'MigrationsV1';
|
||||
|
||||
public const HAMSTER_QUEUE_NAME = 'v1-hamster';
|
||||
public const HAMSTER_CLASS_NAME = 'HamsterV1';
|
||||
|
||||
protected string $queue = '';
|
||||
protected string $class = '';
|
||||
protected string $event = '';
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Queue\Client;
|
||||
use Utopia\Queue\Connection;
|
||||
|
||||
class Hamster extends Event
|
||||
{
|
||||
protected string $type = '';
|
||||
protected ?Document $project = null;
|
||||
protected ?Document $organization = null;
|
||||
protected ?Document $user = null;
|
||||
|
||||
public const TYPE_PROJECT = 'project';
|
||||
public const TYPE_ORGANISATION = 'organisation';
|
||||
public const TYPE_USER = 'user';
|
||||
|
||||
public function __construct(protected Connection $connection)
|
||||
{
|
||||
parent::__construct($connection);
|
||||
|
||||
$this
|
||||
->setQueue(Event::HAMSTER_QUEUE_NAME)
|
||||
->setClass(Event::HAMSTER_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type for the hamster event.
|
||||
*
|
||||
* @param string $type
|
||||
* @return self
|
||||
*/
|
||||
public function setType(string $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set type for the hamster event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the project for the hamster event.
|
||||
*
|
||||
* @param Document $project
|
||||
*/
|
||||
public function setProject(Document $project): self
|
||||
{
|
||||
$this->project = $project;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set project for the hamster event.
|
||||
*
|
||||
* @return Document
|
||||
*/
|
||||
public function getProject(): Document
|
||||
{
|
||||
return $this->project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the organization for the hamster event.
|
||||
*
|
||||
* @param Document $organization
|
||||
*/
|
||||
public function setOrganization(Document $organization): self
|
||||
{
|
||||
$this->organization = $organization;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set organization for the hamster event.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrganization(): Document
|
||||
{
|
||||
return $this->organization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user for the hamster event.
|
||||
*
|
||||
* @param Document $user
|
||||
*/
|
||||
public function setUser(Document $user): self
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set user for the hamster event.
|
||||
*
|
||||
* @return Document
|
||||
*/
|
||||
public function getUser(): Document
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function event and sends it to the functions worker.
|
||||
*
|
||||
* @return string|bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
if ($this->paused) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$client = new Client($this->queue, $this->connection);
|
||||
|
||||
$events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null;
|
||||
|
||||
return $client->enqueue([
|
||||
'type' => $this->type,
|
||||
'project' => $this->project,
|
||||
'organization' => $this->organization,
|
||||
'user' => $this->user,
|
||||
'events' => $events,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a function event from a base event
|
||||
*
|
||||
* @param Event $event
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
*/
|
||||
public function from(Event $event): self
|
||||
{
|
||||
$this->event = $event->getEvent();
|
||||
$this->params = $event->getParams();
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,7 @@ class Exception extends \Exception
|
||||
public const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||
public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
|
||||
public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing';
|
||||
public const FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout';
|
||||
|
||||
/** Deployments */
|
||||
public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
|
||||
|
||||
@@ -84,6 +84,7 @@ abstract class Migration
|
||||
'1.5.3' => 'V20',
|
||||
'1.5.4' => 'V20',
|
||||
'1.5.5' => 'V20',
|
||||
'1.5.6' => 'V20',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,17 +2,10 @@
|
||||
|
||||
namespace Appwrite\Platform\Services;
|
||||
|
||||
use Appwrite\Platform\Tasks\CalcTierStats;
|
||||
use Appwrite\Platform\Tasks\CreateInfMetric;
|
||||
use Appwrite\Platform\Tasks\DeleteOrphanedProjects;
|
||||
use Appwrite\Platform\Tasks\DevGenerateTranslations;
|
||||
use Appwrite\Platform\Tasks\Doctor;
|
||||
use Appwrite\Platform\Tasks\GetMigrationStats;
|
||||
use Appwrite\Platform\Tasks\Hamster;
|
||||
use Appwrite\Platform\Tasks\Install;
|
||||
use Appwrite\Platform\Tasks\Maintenance;
|
||||
use Appwrite\Platform\Tasks\Migrate;
|
||||
use Appwrite\Platform\Tasks\PatchRecreateRepositoriesDocuments;
|
||||
use Appwrite\Platform\Tasks\QueueCount;
|
||||
use Appwrite\Platform\Tasks\QueueRetry;
|
||||
use Appwrite\Platform\Tasks\ScheduleFunctions;
|
||||
@@ -23,7 +16,6 @@ use Appwrite\Platform\Tasks\SSL;
|
||||
use Appwrite\Platform\Tasks\Upgrade;
|
||||
use Appwrite\Platform\Tasks\Vars;
|
||||
use Appwrite\Platform\Tasks\Version;
|
||||
use Appwrite\Platform\Tasks\VolumeSync;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
class Tasks extends Service
|
||||
@@ -32,17 +24,10 @@ class Tasks extends Service
|
||||
{
|
||||
$this->type = self::TYPE_CLI;
|
||||
$this
|
||||
->addAction(CalcTierStats::getName(), new CalcTierStats())
|
||||
->addAction(CreateInfMetric::getName(), new CreateInfMetric())
|
||||
->addAction(DeleteOrphanedProjects::getName(), new DeleteOrphanedProjects())
|
||||
->addAction(DevGenerateTranslations::getName(), new DevGenerateTranslations())
|
||||
->addAction(Doctor::getName(), new Doctor())
|
||||
->addAction(GetMigrationStats::getName(), new GetMigrationStats())
|
||||
->addAction(Hamster::getName(), new Hamster())
|
||||
->addAction(Install::getName(), new Install())
|
||||
->addAction(Maintenance::getName(), new Maintenance())
|
||||
->addAction(Migrate::getName(), new Migrate())
|
||||
->addAction(PatchRecreateRepositoriesDocuments::getName(), new PatchRecreateRepositoriesDocuments())
|
||||
->addAction(QueueCount::getName(), new QueueCount())
|
||||
->addAction(QueueRetry::getName(), new QueueRetry())
|
||||
->addAction(SDKs::getName(), new SDKs())
|
||||
@@ -53,7 +38,6 @@ class Tasks extends Service
|
||||
->addAction(Upgrade::getName(), new Upgrade())
|
||||
->addAction(Vars::getName(), new Vars())
|
||||
->addAction(Version::getName(), new Version())
|
||||
->addAction(VolumeSync::getName(), new VolumeSync())
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use Appwrite\Platform\Workers\Certificates;
|
||||
use Appwrite\Platform\Workers\Databases;
|
||||
use Appwrite\Platform\Workers\Deletes;
|
||||
use Appwrite\Platform\Workers\Functions;
|
||||
use Appwrite\Platform\Workers\Hamster;
|
||||
use Appwrite\Platform\Workers\Mails;
|
||||
use Appwrite\Platform\Workers\Messaging;
|
||||
use Appwrite\Platform\Workers\Migrations;
|
||||
@@ -32,7 +31,6 @@ class Workers extends Service
|
||||
->addAction(Mails::getName(), new Mails())
|
||||
->addAction(Messaging::getName(), new Messaging())
|
||||
->addAction(Webhooks::getName(), new Webhooks())
|
||||
->addAction(Hamster::getName(), new Hamster())
|
||||
->addAction(UsageDump::getName(), new UsageDump())
|
||||
->addAction(Usage::getName(), new Usage())
|
||||
->addAction(Migrations::getName(), new Migrations())
|
||||
|
||||
@@ -1,407 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use League\Csv\Writer;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class CalcTierStats extends Action
|
||||
{
|
||||
/*
|
||||
* Csv cols headers
|
||||
*/
|
||||
private array $columns = [
|
||||
'Project ID',
|
||||
'Organization ID',
|
||||
'Organization Email',
|
||||
'Organization Members',
|
||||
'Teams',
|
||||
'Users',
|
||||
'Requests',
|
||||
'Bandwidth',
|
||||
'Domains',
|
||||
'Api keys',
|
||||
'Webhooks',
|
||||
'Platforms',
|
||||
'Buckets',
|
||||
'Files',
|
||||
'Storage (bytes)',
|
||||
'Max File Size (bytes)',
|
||||
'Databases',
|
||||
'Functions',
|
||||
'Deployments',
|
||||
'Executions',
|
||||
'Migrations',
|
||||
];
|
||||
|
||||
protected string $directory = '/usr/local';
|
||||
protected string $path;
|
||||
protected string $date;
|
||||
|
||||
private array $usageStats = [
|
||||
'network.requests' => 'Requests',
|
||||
'network.inbound' => 'Inbound',
|
||||
'network.outbound' => 'Outbound',
|
||||
|
||||
];
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'calc-tier-stats';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$this
|
||||
->desc('Get stats for projects')
|
||||
->param('after', '', new Text(36), 'After cursor', true)
|
||||
->param('projectId', '', new Text(36), 'Select project to validate', true)
|
||||
->inject('pools')
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->inject('getProjectDB')
|
||||
->inject('register')
|
||||
->callback(function ($after, $projectId, Group $pools, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register) {
|
||||
$this->action($after, $projectId, $pools, $cache, $dbForConsole, $getProjectDB, $register);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function action(string $after, string $projectId, Group $pools, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register): void
|
||||
{
|
||||
//docker compose exec -t appwrite calc-tier-stats
|
||||
|
||||
Console::title('Cloud free tier stats calculation V1');
|
||||
Console::success(APP_NAME . ' cloud free tier stats calculation has started');
|
||||
|
||||
/** CSV stuff */
|
||||
$this->date = date('Y-m-d');
|
||||
$this->path = "{$this->directory}/tier_stats_{$this->date}.csv";
|
||||
$csv = Writer::createFromPath($this->path, 'w');
|
||||
$csv->insertOne($this->columns);
|
||||
|
||||
if (!empty($projectId)) {
|
||||
try {
|
||||
console::log("Project " . $projectId);
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$data = $this->getData($project, $dbForConsole, $dbForProject);
|
||||
$csv->insertOne($data);
|
||||
$this->sendMail($register);
|
||||
|
||||
return;
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
|
||||
if (!empty($after)) {
|
||||
Console::info("Iterating remaining projects after project with ID {$after}");
|
||||
$project = $dbForConsole->getDocument('projects', $after);
|
||||
$queries = [Query::cursorAfter($project)];
|
||||
} else {
|
||||
Console::info("Iterating all projects");
|
||||
}
|
||||
|
||||
$this->foreachDocument($dbForConsole, 'projects', $queries, function (Document $project) use ($getProjectDB, $dbForConsole, $csv) {
|
||||
$projectId = $project->getId();
|
||||
console::log("Project " . $projectId);
|
||||
try {
|
||||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$data = $this->getData($project, $dbForConsole, $dbForProject);
|
||||
$csv->insertOne($data);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
}
|
||||
});
|
||||
|
||||
$this->sendMail($register);
|
||||
}
|
||||
|
||||
private function foreachDocument(Database $database, string $collection, array $queries = [], callable $callback = null): void
|
||||
{
|
||||
$limit = 1000;
|
||||
$results = [];
|
||||
$sum = $limit;
|
||||
$latestDocument = null;
|
||||
|
||||
while ($sum === $limit) {
|
||||
$newQueries = $queries;
|
||||
|
||||
if ($latestDocument != null) {
|
||||
array_unshift($newQueries, Query::cursorAfter($latestDocument));
|
||||
}
|
||||
$newQueries[] = Query::limit($limit);
|
||||
$results = $database->find('projects', $newQueries);
|
||||
|
||||
if (empty($results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
foreach ($results as $document) {
|
||||
if (is_callable($callback)) {
|
||||
$callback($document);
|
||||
}
|
||||
}
|
||||
$latestDocument = $results[array_key_last($results)];
|
||||
}
|
||||
}
|
||||
|
||||
private function sendMail(Registry $register): void
|
||||
{
|
||||
/** @var PHPMailer $mail */
|
||||
$mail = $register->get('smtp');
|
||||
$mail->clearAddresses();
|
||||
$mail->clearAllRecipients();
|
||||
$mail->clearReplyTos();
|
||||
$mail->clearAttachments();
|
||||
$mail->clearBCCs();
|
||||
$mail->clearCCs();
|
||||
|
||||
try {
|
||||
/** Addresses */
|
||||
$mail->setFrom(System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster');
|
||||
$recipients = explode(',', System::getEnv('_APP_USERS_STATS_RECIPIENTS', ''));
|
||||
foreach ($recipients as $recipient) {
|
||||
$mail->addAddress($recipient);
|
||||
}
|
||||
|
||||
/** Attachments */
|
||||
$mail->addAttachment($this->path);
|
||||
|
||||
/** Content */
|
||||
$mail->Subject = "Cloud Report for {$this->date}";
|
||||
$mail->Body = "Please find the daily cloud report atttached";
|
||||
$mail->send();
|
||||
Console::success('Email has been sent!');
|
||||
} catch (\Throwable $e) {
|
||||
Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function getData(Document $project, Database $dbForConsole, Database $dbForProject): array
|
||||
{
|
||||
$stats['Project ID'] = $project->getId();
|
||||
$stats['Organization ID'] = $project->getAttribute('teamId', null);
|
||||
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', null);
|
||||
|
||||
if ($teamInternalId) {
|
||||
$membership = $dbForConsole->findOne('memberships', [
|
||||
Query::equal('teamInternalId', [$teamInternalId]),
|
||||
]);
|
||||
|
||||
if (!$membership || $membership->isEmpty()) {
|
||||
Console::error('Membership not found. Skipping project : ' . $project->getId());
|
||||
}
|
||||
|
||||
$userId = $membership->getAttribute('userId', null);
|
||||
if ($userId) {
|
||||
$user = $dbForConsole->getDocument('users', $userId);
|
||||
$stats['Organization Email'] = $user->getAttribute('email', null);
|
||||
}
|
||||
} else {
|
||||
Console::error("Email was not found for this Organization ID :{$teamInternalId}");
|
||||
}
|
||||
|
||||
/** Get Total Members */
|
||||
if ($teamInternalId) {
|
||||
$stats['Organization Members'] = $dbForConsole->count('memberships', [
|
||||
Query::equal('teamInternalId', [$teamInternalId])
|
||||
]);
|
||||
} else {
|
||||
$stats['Organization Members'] = 0;
|
||||
}
|
||||
|
||||
/** Get Total internal Teams */
|
||||
try {
|
||||
$stats['Teams'] = $dbForProject->count('teams', []);
|
||||
} catch (\Throwable) {
|
||||
$stats['Teams'] = 0;
|
||||
}
|
||||
|
||||
/** Get Total users */
|
||||
try {
|
||||
$stats['Users'] = $dbForProject->count('users', []);
|
||||
} catch (\Throwable) {
|
||||
$stats['Users'] = 0;
|
||||
}
|
||||
|
||||
/** Get Usage stats */
|
||||
$range = '30d';
|
||||
$periods = [
|
||||
'30d' => [
|
||||
'period' => '1d',
|
||||
'limit' => 30,
|
||||
]
|
||||
];
|
||||
|
||||
$tmp = [];
|
||||
$metrics = $this->usageStats;
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$tmp) {
|
||||
foreach ($metrics as $metric => $name) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForProject->find('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', [$period]),
|
||||
Query::limit($limit),
|
||||
Query::orderDesc('time'),
|
||||
]);
|
||||
|
||||
$tmp[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
if (empty($requestDoc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tmp[$metric][] = [
|
||||
'value' => $requestDoc->getAttribute('value'),
|
||||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
$tmp[$metric] = array_reverse($tmp[$metric]);
|
||||
$tmp[$metric] = array_sum(array_column($tmp[$metric], 'value'));
|
||||
}
|
||||
});
|
||||
|
||||
foreach ($tmp as $key => $value) {
|
||||
$stats[$metrics[$key]] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround to combine network.inbound+network.outbound as network.
|
||||
*/
|
||||
$stats['Bandwidth'] = ($stats['Inbound'] ?? 0) + ($stats['Outbound'] ?? 0);
|
||||
unset($stats['Inbound']);
|
||||
unset($stats['Outbound']);
|
||||
|
||||
try {
|
||||
/** Get Domains */
|
||||
$stats['Domains'] = $dbForConsole->count('rules', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
]);
|
||||
} catch (\Throwable) {
|
||||
$stats['Domains'] = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
/** Get Api keys */
|
||||
$stats['Api keys'] = $dbForConsole->count('keys', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
]);
|
||||
} catch (\Throwable) {
|
||||
$stats['Api keys'] = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
/** Get Webhooks */
|
||||
$stats['Webhooks'] = $dbForConsole->count('webhooks', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
]);
|
||||
} catch (\Throwable) {
|
||||
$stats['Webhooks'] = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
/** Get Platforms */
|
||||
$stats['Platforms'] = $dbForConsole->count('platforms', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
]);
|
||||
} catch (\Throwable) {
|
||||
$stats['Platforms'] = 0;
|
||||
}
|
||||
|
||||
/** Get Files & Buckets */
|
||||
$filesCount = 0;
|
||||
$filesSum = 0;
|
||||
$maxFileSize = 0;
|
||||
$counter = 0;
|
||||
try {
|
||||
$buckets = $dbForProject->find('buckets', []);
|
||||
foreach ($buckets as $bucket) {
|
||||
$file = $dbForProject->findOne('bucket_' . $bucket->getInternalId(), [Query::orderDesc('sizeOriginal'),]);
|
||||
if (empty($file)) {
|
||||
continue;
|
||||
}
|
||||
$filesSum += $dbForProject->sum('bucket_' . $bucket->getInternalId(), 'sizeOriginal', []);
|
||||
$filesCount += $dbForProject->count('bucket_' . $bucket->getInternalId(), []);
|
||||
if ($file->getAttribute('sizeOriginal') > $maxFileSize) {
|
||||
$maxFileSize = $file->getAttribute('sizeOriginal');
|
||||
}
|
||||
$counter++;
|
||||
}
|
||||
} catch (\Throwable $t) {
|
||||
Console::error("Error while counting buckets: {$project->getId()}");
|
||||
}
|
||||
$stats['Buckets'] = $counter;
|
||||
$stats['Files'] = $filesCount;
|
||||
$stats['Storage (bytes)'] = $filesSum;
|
||||
$stats['Max File Size (bytes)'] = $maxFileSize;
|
||||
|
||||
|
||||
try {
|
||||
/** Get Total Functions */
|
||||
$stats['Databases'] = $dbForProject->count('databases', []);
|
||||
} catch (\Throwable) {
|
||||
$stats['Databases'] = 0;
|
||||
}
|
||||
|
||||
/** Get Total Functions */
|
||||
try {
|
||||
$stats['Functions'] = $dbForProject->count('functions', []);
|
||||
} catch (\Throwable) {
|
||||
$stats['Functions'] = 0;
|
||||
}
|
||||
|
||||
/** Get Total Deployments */
|
||||
try {
|
||||
$stats['Deployments'] = $dbForProject->count('deployments', []);
|
||||
} catch (\Throwable) {
|
||||
$stats['Deployments'] = 0;
|
||||
}
|
||||
|
||||
/** Get Total Executions */
|
||||
try {
|
||||
$stats['Executions'] = $dbForProject->count('executions', []);
|
||||
} catch (\Throwable) {
|
||||
$stats['Executions'] = 0;
|
||||
}
|
||||
|
||||
/** Get Total Migrations */
|
||||
try {
|
||||
$stats['Migrations'] = $dbForProject->count('migrations', []);
|
||||
} catch (\Throwable) {
|
||||
$stats['Migrations'] = 0;
|
||||
}
|
||||
|
||||
return array_values($stats);
|
||||
}
|
||||
}
|
||||
@@ -1,409 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class CreateInfMetric extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'create-inf-metric';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$this
|
||||
->desc('Create infinity stats metric')
|
||||
->param('after', '', new Text(36), 'After cursor', true)
|
||||
->param('projectId', '', new Text(36), 'Select project to validate', true)
|
||||
->inject('getProjectDB')
|
||||
->inject('dbForConsole')
|
||||
->callback(function (string $after, string $projectId, callable $getProjectDB, Database $dbForConsole) {
|
||||
$this->action($after, $projectId, $getProjectDB, $dbForConsole);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws Exception\Timeout
|
||||
* @throws Exception\Query
|
||||
*/
|
||||
public function action(string $after, string $projectId, callable $getProjectDB, Database $dbForConsole): void
|
||||
{
|
||||
|
||||
Console::title('Create infinity metric V1');
|
||||
Console::success(APP_NAME . ' Create infinity metric started');
|
||||
|
||||
if (!empty($projectId)) {
|
||||
try {
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->getUsageData($dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
}
|
||||
} else {
|
||||
$queries = [];
|
||||
if (!empty($after)) {
|
||||
Console::info("Iterating remaining projects after project with ID {$after}");
|
||||
$project = $dbForConsole->getDocument('projects', $after);
|
||||
$queries = [Query::cursorAfter($project)];
|
||||
} else {
|
||||
Console::info("Iterating all projects");
|
||||
}
|
||||
$this->foreachDocument($dbForConsole, 'projects', $queries, function (Document $project) use ($getProjectDB) {
|
||||
$projectId = $project->getId();
|
||||
|
||||
try {
|
||||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->getUsageData($dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
* @param string $collection
|
||||
* @param array $queries
|
||||
* @param callable|null $callback
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
* @throws Exception\Timeout
|
||||
*/
|
||||
private function foreachDocument(Database $database, string $collection, array $queries = [], callable $callback = null): void
|
||||
{
|
||||
$limit = 1000;
|
||||
$results = [];
|
||||
$sum = $limit;
|
||||
$latestDocument = null;
|
||||
|
||||
while ($sum === $limit) {
|
||||
$newQueries = $queries;
|
||||
|
||||
if ($latestDocument != null) {
|
||||
array_unshift($newQueries, Query::cursorAfter($latestDocument));
|
||||
}
|
||||
$newQueries[] = Query::limit($limit);
|
||||
$results = $database->find($collection, $newQueries);
|
||||
|
||||
if (empty($results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
foreach ($results as $document) {
|
||||
if (is_callable($callback)) {
|
||||
$callback($document);
|
||||
}
|
||||
}
|
||||
$latestDocument = $results[array_key_last($results)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Database $dbForProject
|
||||
* @param Document $project
|
||||
* @return void
|
||||
*/
|
||||
private function getUsageData(Database $dbForProject, Document $project): void
|
||||
{
|
||||
try {
|
||||
$this->network($dbForProject);
|
||||
$this->sessions($dbForProject);
|
||||
$this->users($dbForProject);
|
||||
$this->teams($dbForProject);
|
||||
$this->databases($dbForProject);
|
||||
$this->functions($dbForProject);
|
||||
$this->storage($dbForProject);
|
||||
} catch (\Throwable $th) {
|
||||
var_dump($th->getMessage());
|
||||
}
|
||||
|
||||
Console::log('Finished project ' . $project->getId() . ' ' . $project->getInternalId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Database $dbForProject
|
||||
* @param string $metric
|
||||
* @param int|float $value
|
||||
* @return void
|
||||
* @throws Exception
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Structure
|
||||
*/
|
||||
private function createInfMetric(database $dbForProject, string $metric, int|float $value): void
|
||||
{
|
||||
|
||||
try {
|
||||
$id = \md5("_inf_{$metric}");
|
||||
$dbForProject->deleteDocument('stats', $id);
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
'metric' => $metric,
|
||||
'period' => 'inf',
|
||||
'value' => (int)$value,
|
||||
'time' => null,
|
||||
'region' => 'default',
|
||||
]));
|
||||
} catch (Duplicate $th) {
|
||||
console::log("Error while creating inf metric: duplicate id {$metric} {$id}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Database $dbForProject
|
||||
* @param string $metric
|
||||
* @return int|float
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getFromMetric(database $dbForProject, string $metric): int|float
|
||||
{
|
||||
|
||||
return $dbForProject->sum('stats', 'value', [
|
||||
Query::equal('metric', [
|
||||
$metric,
|
||||
]),
|
||||
Query::equal('period', ['1d']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Database $dbForProject
|
||||
* @throws Exception
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Structure
|
||||
*/
|
||||
private function network(database $dbForProject)
|
||||
{
|
||||
$this->createInfMetric($dbForProject, 'network.inbound', $this->getFromMetric($dbForProject, 'network.inbound'));
|
||||
$this->createInfMetric($dbForProject, 'network.outbound', $this->getFromMetric($dbForProject, 'network.outbound'));
|
||||
$this->createInfMetric($dbForProject, 'network.requests', $this->getFromMetric($dbForProject, 'network.requests'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception\Timeout
|
||||
* @throws Exception\Structure
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
*/
|
||||
private function storage(database $dbForProject)
|
||||
{
|
||||
$bucketsCount = 0;
|
||||
$filesCount = 0;
|
||||
$filesStorageSum = 0;
|
||||
|
||||
$buckets = $dbForProject->find('buckets');
|
||||
foreach ($buckets as $bucket) {
|
||||
$files = $dbForProject->count('bucket_' . $bucket->getInternalId());
|
||||
$this->createInfMetric($dbForProject, $bucket->getInternalId() . '.files', $files);
|
||||
|
||||
$filesStorage = $dbForProject->sum('bucket_' . $bucket->getInternalId(), 'sizeOriginal');
|
||||
$this->createInfMetric($dbForProject, $bucket->getInternalId() . '.files.storage', $filesStorage);
|
||||
|
||||
$bucketsCount++;
|
||||
$filesCount += $files;
|
||||
$filesStorageSum += $filesStorage;
|
||||
}
|
||||
|
||||
$this->createInfMetric($dbForProject, 'buckets', $bucketsCount);
|
||||
$this->createInfMetric($dbForProject, 'files', $filesCount);
|
||||
$this->createInfMetric($dbForProject, 'files.storage', $filesStorageSum);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Timeout
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Structure
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
*/
|
||||
private function functions(Database $dbForProject)
|
||||
{
|
||||
$functionsCount = 0;
|
||||
$deploymentsCount = 0;
|
||||
$buildsCount = 0;
|
||||
$buildsStorageSum = 0;
|
||||
$buildsComputeSum = 0;
|
||||
$executionsCount = 0;
|
||||
$executionsComputeSum = 0;
|
||||
$deploymentsStorageSum = 0;
|
||||
|
||||
//functions
|
||||
$functions = $dbForProject->find('functions');
|
||||
foreach ($functions as $function) {
|
||||
//deployments
|
||||
$deployments = $dbForProject->find('deployments', [
|
||||
Query::equal('resourceType', ['functions']),
|
||||
Query::equal('resourceInternalId', [$function->getInternalId()]),
|
||||
]);
|
||||
|
||||
$deploymentCount = 0;
|
||||
$deploymentStorageSum = 0;
|
||||
foreach ($deployments as $deployment) {
|
||||
//builds
|
||||
$builds = $dbForProject->count('builds', [
|
||||
Query::equal('deploymentInternalId', [$deployment->getInternalId()]),
|
||||
]);
|
||||
|
||||
$buildsCompute = $dbForProject->sum('builds', 'duration', [
|
||||
Query::equal('deploymentInternalId', [$deployment->getInternalId()]),
|
||||
]);
|
||||
|
||||
$buildsStorage = $dbForProject->sum('builds', 'size', [
|
||||
Query::equal('deploymentInternalId', [$deployment->getInternalId()]),
|
||||
]);
|
||||
|
||||
$this->createInfMetric($dbForProject, $function->getInternalId() . '.builds', $builds);
|
||||
$this->createInfMetric($dbForProject, $function->getInternalId() . '.builds.storage', $buildsCompute * 1000);
|
||||
$this->createInfMetric($dbForProject, $function->getInternalId() . '.builds.compute', $buildsStorage);
|
||||
|
||||
$buildsCount += $builds;
|
||||
$buildsComputeSum += $buildsCompute;
|
||||
$buildsStorageSum += $buildsStorage;
|
||||
|
||||
|
||||
$deploymentCount++;
|
||||
$deploymentsCount++;
|
||||
$deploymentsStorageSum += $deployment['size'];
|
||||
$deploymentStorageSum += $deployment['size'];
|
||||
}
|
||||
$this->createInfMetric($dbForProject, 'functions.' . $function->getInternalId() . '.deployments', $deploymentCount);
|
||||
$this->createInfMetric($dbForProject, 'functions.' . $function->getInternalId() . '.deployments.storage', $deploymentStorageSum);
|
||||
|
||||
//executions
|
||||
$executions = $dbForProject->count('executions', [
|
||||
Query::equal('functionInternalId', [$function->getInternalId()]),
|
||||
]);
|
||||
|
||||
$executionsCompute = $dbForProject->sum('executions', 'duration', [
|
||||
Query::equal('functionInternalId', [$function->getInternalId()]),
|
||||
]);
|
||||
|
||||
$this->createInfMetric($dbForProject, $function->getInternalId() . '.executions', $executions);
|
||||
$this->createInfMetric($dbForProject, $function->getInternalId() . '.executions.compute', $executionsCompute * 1000);
|
||||
$executionsCount += $executions;
|
||||
$executionsComputeSum += $executionsCompute;
|
||||
|
||||
$functionsCount++;
|
||||
}
|
||||
|
||||
$this->createInfMetric($dbForProject, 'functions', $functionsCount);
|
||||
$this->createInfMetric($dbForProject, 'deployments', $deploymentsCount);
|
||||
$this->createInfMetric($dbForProject, 'deployments.storage', $deploymentsStorageSum);
|
||||
$this->createInfMetric($dbForProject, 'builds', $buildsCount);
|
||||
$this->createInfMetric($dbForProject, 'builds.compute', $buildsComputeSum * 1000);
|
||||
$this->createInfMetric($dbForProject, 'builds.storage', $buildsStorageSum);
|
||||
$this->createInfMetric($dbForProject, 'executions', $executionsCount);
|
||||
$this->createInfMetric($dbForProject, 'executions.compute', $executionsComputeSum * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Timeout
|
||||
* @throws Exception\Structure
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
*/
|
||||
private function databases(Database $dbForProject)
|
||||
{
|
||||
$databasesCount = 0;
|
||||
$collectionsCount = 0;
|
||||
$documentsCount = 0;
|
||||
$databases = $dbForProject->find('databases');
|
||||
foreach ($databases as $database) {
|
||||
$collectionCount = 0;
|
||||
$collections = $dbForProject->find('database_' . $database->getInternalId());
|
||||
foreach ($collections as $collection) {
|
||||
$documents = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
|
||||
$this->createInfMetric($dbForProject, $database->getInternalId() . '.' . $collection->getInternalId() . '.documents', $documents);
|
||||
$documentsCount += $documents;
|
||||
$collectionCount++;
|
||||
$collectionsCount++;
|
||||
}
|
||||
$this->createInfMetric($dbForProject, $database->getInternalId() . '.collections', $collectionCount);
|
||||
$this->createInfMetric($dbForProject, $database->getInternalId() . '.documents', $documentsCount);
|
||||
$databasesCount++;
|
||||
}
|
||||
$this->createInfMetric($dbForProject, 'collections', $collectionsCount);
|
||||
$this->createInfMetric($dbForProject, 'databases', $databasesCount);
|
||||
$this->createInfMetric($dbForProject, 'documents', $documentsCount);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Structure
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception
|
||||
*/
|
||||
private function users(Database $dbForProject)
|
||||
{
|
||||
$users = $dbForProject->count('users');
|
||||
$this->createInfMetric($dbForProject, 'users', $users);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Structure
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception
|
||||
*/
|
||||
private function sessions(Database $dbForProject)
|
||||
{
|
||||
$users = $dbForProject->count('sessions');
|
||||
$this->createInfMetric($dbForProject, 'sessions', $users);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception\Authorization
|
||||
* @throws Exception\Structure
|
||||
* @throws Exception\Restricted
|
||||
* @throws Exception\Conflict
|
||||
* @throws Exception
|
||||
*/
|
||||
private function teams(Database $dbForProject)
|
||||
{
|
||||
$teams = $dbForProject->count('teams');
|
||||
$this->createInfMetric($dbForProject, 'teams', $teams);
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
class DeleteOrphanedProjects extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'delete-orphaned-projects';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$this
|
||||
->desc('Delete orphaned projects')
|
||||
->param('commit', false, new Boolean(true), 'Commit project deletion', true)
|
||||
->inject('pools')
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->inject('register')
|
||||
->callback(function (bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register) {
|
||||
$this->action($commit, $pools, $cache, $dbForConsole, $register);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function action(bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void
|
||||
{
|
||||
|
||||
Console::title('Delete orphaned projects V1');
|
||||
Console::success(APP_NAME . ' Delete orphaned projects started');
|
||||
|
||||
/** @var array $collections */
|
||||
$collectionsConfig = Config::getParam('collections', [])['projects'] ?? [];
|
||||
|
||||
$collectionsConfig = array_merge([
|
||||
'audit' => [
|
||||
'$id' => ID::custom('audit'),
|
||||
'$collection' => Database::METADATA
|
||||
],
|
||||
'abuse' => [
|
||||
'$id' => ID::custom('abuse'),
|
||||
'$collection' => Database::METADATA
|
||||
]
|
||||
], $collectionsConfig);
|
||||
|
||||
/* Initialise new Utopia app */
|
||||
$app = new App('UTC');
|
||||
$console = $app->getResource('console');
|
||||
$projects = [$console];
|
||||
|
||||
/** Database connections */
|
||||
$totalProjects = $dbForConsole->count('projects');
|
||||
Console::success("Found a total of: {$totalProjects} projects");
|
||||
|
||||
$orphans = 1;
|
||||
$cnt = 0;
|
||||
$count = 0;
|
||||
$limit = 30;
|
||||
$sum = 30;
|
||||
$offset = 0;
|
||||
while (!empty($projects)) {
|
||||
foreach ($projects as $project) {
|
||||
|
||||
/**
|
||||
* Skip user projects with id 'console'
|
||||
*/
|
||||
if ($project->getId() === 'console') {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = $project->getAttribute('database');
|
||||
$adapter = $pools
|
||||
->get($db)
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
$dbForProject->setDatabase('appwrite');
|
||||
$dbForProject->setNamespace('_' . $project->getInternalId());
|
||||
|
||||
$collectionsCreated = 0;
|
||||
$cnt++;
|
||||
if ($dbForProject->exists($dbForProject->getDatabase(), Database::METADATA)) {
|
||||
$collectionsCreated = $dbForProject->count(Database::METADATA);
|
||||
}
|
||||
|
||||
$msg = '(' . $cnt . ') found (' . $collectionsCreated . ') collections on project (' . $project->getInternalId() . ') , database (' . $project['database'] . ')';
|
||||
|
||||
if ($collectionsCreated >= count($collectionsConfig)) {
|
||||
Console::log($msg . ' ignoring....');
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::log($msg);
|
||||
|
||||
if ($collectionsCreated > 0) {
|
||||
$collections = $dbForProject->find(Database::METADATA, []);
|
||||
foreach ($collections as $collection) {
|
||||
if ($commit) {
|
||||
$dbForProject->deleteCollection($collection->getId());
|
||||
$dbForConsole->purgeCachedCollection($collection->getId());
|
||||
}
|
||||
Console::info('--Deleting collection (' . $collection->getId() . ') project no (' . $project->getInternalId() . ')');
|
||||
}
|
||||
}
|
||||
|
||||
if ($commit) {
|
||||
$dbForConsole->deleteDocument('projects', $project->getId());
|
||||
$dbForConsole->purgeCachedDocument('projects', $project->getId());
|
||||
|
||||
if ($dbForProject->exists($dbForProject->getDefaultDatabase(), Database::METADATA)) {
|
||||
try {
|
||||
$dbForProject->deleteCollection(Database::METADATA);
|
||||
$dbForProject->purgeCachedCollection(Database::METADATA);
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning('Metadata collection does not exist');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console::info('--Deleting project no (' . $project->getInternalId() . ')');
|
||||
|
||||
$orphans++;
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Error: ' . $th->getMessage() . ' ' . $th->getTraceAsString());
|
||||
} finally {
|
||||
$pools
|
||||
->get($db)
|
||||
->reclaim();
|
||||
}
|
||||
}
|
||||
|
||||
$sum = \count($projects);
|
||||
|
||||
$projects = $dbForConsole->find('projects', [
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
]);
|
||||
|
||||
$offset = $offset + $limit;
|
||||
$count = $count + $sum;
|
||||
}
|
||||
|
||||
Console::log('Iterated through ' . $count - 1 . '/' . $totalProjects . ' projects found ' . $orphans - 1 . ' orphans');
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Exception;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Fetch\Client;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class DevGenerateTranslations extends Action
|
||||
{
|
||||
private string $apiKey = '';
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'dev-generate-translations';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Generate translations in all languages')
|
||||
->param('dry-run', 'true', new Boolean(true), 'If action should do a dry run. Dry run does not write into files', true)
|
||||
->param('api-key', '', new Text(256), 'Open AI API key. Only used during non-dry runs to generate translations.', true)
|
||||
->callback(fn ($dryRun, $apiKey) => $this->action($dryRun, $apiKey));
|
||||
}
|
||||
|
||||
public function action(bool|string $dryRun, string $apiKey): void
|
||||
{
|
||||
$dryRun = \strval($dryRun) === 'true';
|
||||
|
||||
Console::info("Started");
|
||||
|
||||
if (!$dryRun && empty($apiKey)) {
|
||||
Console::error("Please specify --api-key='OPEN_AI_API_KEY' or run with --dry-run");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->apiKey = $apiKey;
|
||||
|
||||
$dir = __DIR__ . '/../../../../app/config/locale/translations';
|
||||
$mainFile = 'en.json';
|
||||
|
||||
$mainJson = \json_decode(\file_get_contents($dir . '/' . $mainFile), true);
|
||||
$mainKeys = \array_keys($mainJson);
|
||||
|
||||
$files = array_diff(scandir($dir), array('.', '..', $mainFile));
|
||||
|
||||
foreach ($files as $file) {
|
||||
$fileJson = \json_decode(\file_get_contents($dir . '/' . $file), true);
|
||||
$fileKeys = \array_keys($fileJson);
|
||||
|
||||
// Trick to clear specific key from all translation files:
|
||||
// $json = \json_decode(\file_get_contents($dir . '/' . $file), true);
|
||||
// unset($json['emails.magicSession.optionUrl']);
|
||||
// \file_put_contents($dir . '/' . $file, \json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | 0));
|
||||
// continue;
|
||||
|
||||
foreach ($mainKeys as $key) {
|
||||
if (!(\in_array($key, $fileKeys))) {
|
||||
if ($dryRun) {
|
||||
Console::warning("{$file} missing translation for {$key}");
|
||||
} else {
|
||||
$language = \explode('.', $file)[0];
|
||||
$translation = $this->generateTranslation($language, $mainJson[$key]);
|
||||
|
||||
if (!empty($translation)) {
|
||||
$json = \json_decode(\file_get_contents($dir . '/' . $file), true);
|
||||
$json[$key] = $translation;
|
||||
\file_put_contents($dir . '/' . $file, \json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | 0));
|
||||
|
||||
Console::success("Generated {$key} for {$language}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console::info("Done");
|
||||
}
|
||||
|
||||
private function generateTranslation(string $targetLanguage, string $enTranslation): string
|
||||
{
|
||||
$list = Config::getParam('locale-languages');
|
||||
foreach ($list as $language) {
|
||||
if ($language['code'] === $targetLanguage) {
|
||||
$languageObject = $language;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($languageObject)) {
|
||||
Console::error("{$targetLanguage} language not found");
|
||||
return '';
|
||||
}
|
||||
|
||||
$targetLanguageName = $languageObject['name'];
|
||||
|
||||
$response = Client::fetch('https://api.openai.com/v1/chat/completions', [
|
||||
'content-type' => Client::CONTENT_TYPE_APPLICATION_JSON,
|
||||
'Authorization' => 'Bearer ' . $this->apiKey
|
||||
], Client::METHOD_POST, [
|
||||
'model' => 'gpt-4-1106-preview', // https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo
|
||||
'messages' => [
|
||||
[
|
||||
'role' => 'system',
|
||||
'content' => "Please translate the message user provides from English language to {$targetLanguageName}. Do not translate text inside {{ and }} placeholders. Provide only translated text."
|
||||
],
|
||||
[
|
||||
'role' => 'user',
|
||||
'content' => $enTranslation
|
||||
]
|
||||
]
|
||||
], [], 60);
|
||||
|
||||
$body = \json_decode($response->getBody(), true);
|
||||
|
||||
if ($response->getStatusCode() >= 400) {
|
||||
throw new Exception($response->getBody() . ' with status code ' . $response->getStatusCode() . ' for language ' . $targetLanguage . ' and message ' . $enTranslation);
|
||||
}
|
||||
|
||||
$answer = $body['choices'][0]['message']['content'];
|
||||
|
||||
$failureDetectors = [ 'sorry', 'confusion', 'country code', 'misunderstanding', 'correct', 'clarify', 'specific', 'cannot', 'unable', 'language', 'appears' ];
|
||||
|
||||
foreach ($failureDetectors as $detector) {
|
||||
if (\str_contains($answer, $detector)) {
|
||||
Console::error("Translation of '{$enTranslation}' for {$targetLanguage} is incorrect: {$answer}");
|
||||
}
|
||||
}
|
||||
|
||||
return $answer;
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use League\Csv\CannotInsertRecord;
|
||||
use League\Csv\Writer;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
|
||||
class GetMigrationStats extends Action
|
||||
{
|
||||
/*
|
||||
* Csv cols headers
|
||||
*/
|
||||
private array $columns = [
|
||||
'Project ID',
|
||||
'$id',
|
||||
'$createdAt',
|
||||
'status',
|
||||
'stage',
|
||||
'source'
|
||||
];
|
||||
|
||||
protected string $directory = '/usr/local';
|
||||
protected string $path;
|
||||
protected string $date;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'get-migration-stats';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
$this
|
||||
->desc('Get stats for projects')
|
||||
->inject('pools')
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->inject('register')
|
||||
->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register) {
|
||||
$this->action($pools, $cache, $dbForConsole, $register);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Utopia\Exception
|
||||
* @throws CannotInsertRecord
|
||||
*/
|
||||
public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void
|
||||
{
|
||||
//docker compose exec -t appwrite get-migration-stats
|
||||
|
||||
Console::title('Migration stats calculation V1');
|
||||
Console::success(APP_NAME . ' Migration stats calculation has started');
|
||||
|
||||
/* Initialise new Utopia app */
|
||||
$app = new App('UTC');
|
||||
$console = $app->getResource('console');
|
||||
|
||||
/** CSV stuff */
|
||||
$this->date = date('Y-m-d');
|
||||
$this->path = "{$this->directory}/migration_stats_{$this->date}.csv";
|
||||
$csv = Writer::createFromPath($this->path, 'w');
|
||||
$csv->insertOne($this->columns);
|
||||
|
||||
/** Database connections */
|
||||
$totalProjects = $dbForConsole->count('projects');
|
||||
Console::success("Found a total of: {$totalProjects} projects");
|
||||
|
||||
$projects = [$console];
|
||||
$count = 0;
|
||||
$limit = 100;
|
||||
$sum = 100;
|
||||
$offset = 0;
|
||||
while (!empty($projects)) {
|
||||
foreach ($projects as $project) {
|
||||
|
||||
/**
|
||||
* Skip user projects with id 'console'
|
||||
*/
|
||||
if ($project->getId() === 'console') {
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::info("Getting stats for {$project->getId()}");
|
||||
|
||||
try {
|
||||
$db = $project->getAttribute('database');
|
||||
$adapter = $pools
|
||||
->get($db)
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
$dbForProject->setDefaultDatabase('appwrite');
|
||||
$dbForProject->setNamespace('_' . $project->getInternalId());
|
||||
|
||||
/** Get Project ID */
|
||||
$stats['Project ID'] = $project->getId();
|
||||
|
||||
/** Get Migration details */
|
||||
$migrations = $dbForProject->find('migrations', [
|
||||
Query::limit(500)
|
||||
]);
|
||||
|
||||
$migrations = array_map(function ($migration) use ($project) {
|
||||
return [
|
||||
$project->getId(),
|
||||
$migration->getAttribute('$id'),
|
||||
$migration->getAttribute('$createdAt'),
|
||||
$migration->getAttribute('status'),
|
||||
$migration->getAttribute('stage'),
|
||||
$migration->getAttribute('source'),
|
||||
];
|
||||
}, $migrations);
|
||||
|
||||
if (!empty($migrations)) {
|
||||
$csv->insertAll($migrations);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed on project ("' . $project->getId() . '") with error on File: ' . $th->getFile() . ' line no: ' . $th->getline() . ' with message: ' . $th->getMessage());
|
||||
} finally {
|
||||
$pools
|
||||
->get($db)
|
||||
->reclaim();
|
||||
}
|
||||
}
|
||||
|
||||
$sum = \count($projects);
|
||||
|
||||
$projects = $dbForConsole->find('projects', [
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
]);
|
||||
|
||||
$offset = $offset + $limit;
|
||||
$count = $count + $sum;
|
||||
}
|
||||
|
||||
Console::log('Iterated through ' . $count - 1 . '/' . $totalProjects . ' projects...');
|
||||
|
||||
$pools
|
||||
->get('console')
|
||||
->reclaim();
|
||||
|
||||
/** @var PHPMailer $mail */
|
||||
$mail = $register->get('smtp');
|
||||
|
||||
$mail->clearAddresses();
|
||||
$mail->clearAllRecipients();
|
||||
$mail->clearReplyTos();
|
||||
$mail->clearAttachments();
|
||||
$mail->clearBCCs();
|
||||
$mail->clearCCs();
|
||||
|
||||
try {
|
||||
/** Addresses */
|
||||
$mail->setFrom(System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster');
|
||||
$recipients = explode(',', System::getEnv('_APP_USERS_STATS_RECIPIENTS', ''));
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
$mail->addAddress($recipient);
|
||||
}
|
||||
|
||||
/** Attachments */
|
||||
$mail->addAttachment($this->path);
|
||||
|
||||
/** Content */
|
||||
$mail->Subject = "Migration Report for {$this->date}";
|
||||
$mail->Body = "Please find the migration report atttached";
|
||||
$mail->send();
|
||||
Console::success('Email has been sent!');
|
||||
} catch (\Throwable $e) {
|
||||
Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Event\Hamster as EventHamster;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Hamster extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'hamster';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Get stats for projects')
|
||||
->inject('queueForHamster')
|
||||
->inject('dbForConsole')
|
||||
->callback(function (EventHamster $queueForHamster, Database $dbForConsole) {
|
||||
$this->action($queueForHamster, $dbForConsole);
|
||||
});
|
||||
}
|
||||
|
||||
public function action(EventHamster $queueForHamster, Database $dbForConsole): void
|
||||
{
|
||||
Console::title('Cloud Hamster V1');
|
||||
Console::success(APP_NAME . ' cloud hamster process has started');
|
||||
|
||||
$sleep = (int) System::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default)
|
||||
|
||||
$jobInitTime = System::getEnv('_APP_HAMSTER_TIME', '22:00'); // (hour:minutes)
|
||||
|
||||
$now = new \DateTime();
|
||||
$now->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
|
||||
$next = new \DateTime($now->format("Y-m-d $jobInitTime"));
|
||||
$next->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
|
||||
$delay = $next->getTimestamp() - $now->getTimestamp();
|
||||
/**
|
||||
* If time passed for the target day.
|
||||
*/
|
||||
if ($delay <= 0) {
|
||||
$next->add(\DateInterval::createFromDateString('1 days'));
|
||||
$delay = $next->getTimestamp() - $now->getTimestamp();
|
||||
}
|
||||
|
||||
Console::log('[' . $now->format("Y-m-d H:i:s.v") . '] Delaying for ' . $delay . ' setting loop to [' . $next->format("Y-m-d H:i:s.v") . ']');
|
||||
|
||||
Console::loop(function () use ($queueForHamster, $dbForConsole, $sleep) {
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Queuing Cloud Usage Stats every {$sleep} seconds");
|
||||
$loopStart = microtime(true);
|
||||
|
||||
Console::info('Queuing stats for all projects');
|
||||
$this->getStatsPerProject($queueForHamster, $dbForConsole, $loopStart);
|
||||
Console::success('Completed queuing stats for all projects');
|
||||
|
||||
Console::info('Queuing stats for all organizations');
|
||||
$this->getStatsPerOrganization($queueForHamster, $dbForConsole, $loopStart);
|
||||
Console::success('Completed queuing stats for all organizations');
|
||||
|
||||
Console::info('Queuing stats for all users');
|
||||
$this->getStatsPerUser($queueForHamster, $dbForConsole, $loopStart);
|
||||
Console::success('Completed queuing stats for all users');
|
||||
|
||||
$loopTook = microtime(true) - $loopStart;
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Cloud Stats took {$loopTook} seconds");
|
||||
}, $sleep, $delay);
|
||||
}
|
||||
|
||||
protected function calculateByGroup(string $collection, Database $database, callable $callback)
|
||||
{
|
||||
$count = 0;
|
||||
$chunk = 0;
|
||||
$limit = 50;
|
||||
$results = [];
|
||||
$sum = $limit;
|
||||
|
||||
$executionStart = \microtime(true);
|
||||
|
||||
while ($sum === $limit) {
|
||||
$chunk++;
|
||||
|
||||
$results = $database->find($collection, \array_merge([
|
||||
Query::limit($limit),
|
||||
Query::offset($count)
|
||||
]));
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
Console::log('Processing chunk #' . $chunk . '. Found ' . $sum . ' documents');
|
||||
|
||||
foreach ($results as $document) {
|
||||
call_user_func($callback, $database, $document);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$executionEnd = \microtime(true);
|
||||
|
||||
Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
|
||||
}
|
||||
|
||||
protected function getStatsPerOrganization(EventHamster $hamster, Database $dbForConsole, float $loopStart)
|
||||
{
|
||||
$this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $organization) use ($hamster, $loopStart) {
|
||||
try {
|
||||
$organization->setAttribute('$time', $loopStart);
|
||||
$hamster
|
||||
->setType(EventHamster::TYPE_ORGANISATION)
|
||||
->setOrganization($organization)
|
||||
->trigger();
|
||||
} catch (\Throwable $e) {
|
||||
Console::error($e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function getStatsPerProject(EventHamster $hamster, Database $dbForConsole, float $loopStart)
|
||||
{
|
||||
$this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($hamster, $loopStart) {
|
||||
try {
|
||||
$project->setAttribute('$time', $loopStart);
|
||||
$hamster
|
||||
->setType(EventHamster::TYPE_PROJECT)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
} catch (\Throwable $e) {
|
||||
Console::error($e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function getStatsPerUser(EventHamster $hamster, Database $dbForConsole, float $loopStart)
|
||||
{
|
||||
$this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $user) use ($hamster, $loopStart) {
|
||||
try {
|
||||
$user->setAttribute('$time', $loopStart);
|
||||
$hamster
|
||||
->setType(EventHamster::TYPE_USER)
|
||||
->setUser($user)
|
||||
->trigger();
|
||||
} catch (\Throwable $e) {
|
||||
Console::error($e->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user