mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge remote-tracking branch 'origin/1.9.x' into codex/resolve-12284-conflict
# Conflicts: # app/controllers/shared/api.php
This commit is contained in:
@@ -427,6 +427,7 @@ jobs:
|
||||
FunctionsSchedule,
|
||||
GraphQL,
|
||||
Health,
|
||||
Advisor,
|
||||
Locale,
|
||||
Projects,
|
||||
Realtime,
|
||||
@@ -449,6 +450,7 @@ jobs:
|
||||
paratest_processes: 3
|
||||
timeout_minutes: 30
|
||||
- service: TablesDB
|
||||
runner: runs-on=${{ github.run_id }}/runner=8cpu-linux-x64/volume=120g/spot=false
|
||||
paratest_processes: 3
|
||||
timeout_minutes: 30
|
||||
- service: Migrations
|
||||
|
||||
@@ -115,6 +115,14 @@ Common injections: `$response`, `$request`, `$dbForProject`, `$dbForPlatform`, `
|
||||
- Never hardcode credentials -- use environment variables.
|
||||
- Code changes may require container restart. No central log location -- check relevant containers.
|
||||
|
||||
## Tracing with Utopia Span
|
||||
|
||||
In handlers, only call `Span::add($key, $value)`. **Never** call `Span::init`, `Span::error`, or `Span::finish` -- lifecycle is owned by the entry-point harness (`app/http.php`, `app/worker.php`, `app/realtime.php`, `Bus::dispatch`). For selective export, filter in the sampler in `app/init/span.php`.
|
||||
|
||||
Keys are `snake_case` with dots only for child relationships: `project.id` (id of project), `storage.bucket.id`. No dot otherwise: `inbound_bytes`, not `inbound.bytes`. No camelCase, no bare top-level keys (`function.id`, not `functionId`).
|
||||
|
||||
Cross-cutting identifiers (`project.id`, `function.id`, `user.id`) live at the top level, not under a subsystem (no `realtime.project.id`). The trace sampler and downstream filters look them up by the canonical key.
|
||||
|
||||
## Patch release process
|
||||
|
||||
For bumping patch versions (e.g., `1.9.0` -> `1.9.1`), follow the checklist in `.claude/skills/patch-release-checklist/SKILL.md`. It covers the 4 files that must be updated, console image bumps, CHANGES.md updates, and common pitfalls to avoid.
|
||||
|
||||
+5
-4
@@ -4,8 +4,8 @@ require_once __DIR__ . '/init.php';
|
||||
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Publisher\Certificate as CertificatePublisher;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Publisher\StatsResources as StatsResourcesPublisher;
|
||||
use Appwrite\Event\Publisher\Usage as UsagePublisher;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
@@ -281,9 +281,10 @@ $container->set('publisherForStatsResources', fn (Publisher $publisher) => new S
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME))
|
||||
), ['publisher']);
|
||||
$container->set('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
$container->set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher(
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL)
|
||||
), ['publisher']);
|
||||
$container->set('queueForDeletes', function (Publisher $publisher) {
|
||||
return new Delete($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
@@ -1956,6 +1956,440 @@ $platformCollections = [
|
||||
'attributes' => [],
|
||||
'indexes' => []
|
||||
],
|
||||
|
||||
'reports' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('reports'),
|
||||
'name' => 'Reports',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('projectInternalId'),
|
||||
'type' => Database::VAR_ID,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('projectId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('appInternalId'),
|
||||
'type' => Database::VAR_ID,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('appId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('type'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('title'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('summary'),
|
||||
'type' => Database::VAR_TEXT,
|
||||
'format' => '',
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
// Resource type the report is about. Plural noun, e.g. databases, sites, urls.
|
||||
'$id' => ID::custom('targetType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
// Free-form target identifier (URL for lighthouse, resource ID for db).
|
||||
// Indexed by `_key_project_target` with an explicit prefix length.
|
||||
'$id' => ID::custom('target'),
|
||||
'type' => Database::VAR_TEXT,
|
||||
'format' => '',
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
// Category strings, e.g. 'performance', 'accessibility'. Native array
|
||||
// column — we never query on individual entries (MySQL JSON-array
|
||||
// indexes are weak), this is read+rewrite only.
|
||||
'$id' => ID::custom('categories'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
// Virtual attribute — insights live in the `insights` collection
|
||||
// back-referenced by `reportInternalId`. The subQuery filter joins
|
||||
// them at read time.
|
||||
'$id' => ID::custom('insights'),
|
||||
'type' => Database::VAR_TEXT,
|
||||
'format' => '',
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['subQueryReportInsights'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('analyzedAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_project_app_type'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'appInternalId', 'type'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_target'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'appInternalId', 'targetType', 'target'],
|
||||
'lengths' => [null, null, null, 700],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'insights' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('insights'),
|
||||
'name' => 'Insights',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('projectInternalId'),
|
||||
'type' => Database::VAR_ID,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('projectId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('reportInternalId'),
|
||||
'type' => Database::VAR_ID,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('reportId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('type'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('severity'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('status'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => 'active',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceInternalId'),
|
||||
'type' => Database::VAR_ID,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('parentResourceType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 64,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('parentResourceId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('parentResourceInternalId'),
|
||||
'type' => Database::VAR_ID,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('title'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('summary'),
|
||||
'type' => Database::VAR_TEXT,
|
||||
'format' => '',
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('ctas'),
|
||||
'type' => Database::VAR_TEXT,
|
||||
'format' => '',
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('analyzedAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('dismissedAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('dismissedBy'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_project_report'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'reportInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_resource'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'resourceType', 'resourceId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_parent_resource'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'parentResourceType', 'parentResourceId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_type'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'type'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_severity'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'severity'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_status'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'status'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_dismissedAt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'dismissedAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC, Database::ORDER_DESC],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
// Organization API keys subquery
|
||||
|
||||
@@ -1453,4 +1453,28 @@ return [
|
||||
'description' => 'The maximum number of mock phones for this project has been reached.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Advisor */
|
||||
Exception::INSIGHT_NOT_FOUND => [
|
||||
'name' => Exception::INSIGHT_NOT_FOUND,
|
||||
'description' => 'Insight with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::INSIGHT_ALREADY_EXISTS => [
|
||||
'name' => Exception::INSIGHT_ALREADY_EXISTS,
|
||||
'description' => 'Insight with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Reports */
|
||||
Exception::REPORT_NOT_FOUND => [
|
||||
'name' => Exception::REPORT_NOT_FOUND,
|
||||
'description' => 'Report with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::REPORT_ALREADY_EXISTS => [
|
||||
'name' => Exception::REPORT_ALREADY_EXISTS,
|
||||
'description' => 'Report with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.',
|
||||
'code' => 409,
|
||||
],
|
||||
];
|
||||
|
||||
+29
-1
@@ -426,5 +426,33 @@ return [
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a proxy rule is updated.',
|
||||
]
|
||||
]
|
||||
],
|
||||
'reports' => [
|
||||
'$model' => Response::MODEL_REPORT,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any report event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when a report is created.',
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a report is updated.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when a report is deleted.',
|
||||
],
|
||||
'insights' => [
|
||||
'$model' => Response::MODEL_INSIGHT,
|
||||
'$resource' => true,
|
||||
'$description' => 'This event triggers on any insight event.',
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when an insight is created.',
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when an insight is updated.',
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when an insight is deleted.',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -103,6 +103,10 @@ $admins = [
|
||||
'tokens.write',
|
||||
'schedules.read',
|
||||
'schedules.write',
|
||||
'insights.read',
|
||||
'insights.write',
|
||||
'reports.read',
|
||||
'reports.write',
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
@@ -361,4 +361,22 @@ return [
|
||||
'description' => 'Access to create, update, and delete resources under VCS service.',
|
||||
'category' => 'Other',
|
||||
],
|
||||
|
||||
// Advisor
|
||||
'insights.read' => [
|
||||
'description' => 'Access to read insights under Advisor service.',
|
||||
'category' => 'Advisor',
|
||||
],
|
||||
'insights.write' => [
|
||||
'description' => 'Reserved for Advisor insight ingestion outside CE.',
|
||||
'category' => 'Advisor',
|
||||
],
|
||||
'reports.read' => [
|
||||
'description' => 'Access to read reports under Advisor service.',
|
||||
'category' => 'Advisor',
|
||||
],
|
||||
'reports.write' => [
|
||||
'description' => 'Access to delete reports under Advisor service.',
|
||||
'category' => 'Advisor',
|
||||
],
|
||||
];
|
||||
|
||||
+15
-1
@@ -308,5 +308,19 @@ return [
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/messaging.png',
|
||||
'platforms' => ['client', 'server', 'console'],
|
||||
]
|
||||
],
|
||||
'advisor' => [
|
||||
'key' => 'advisor',
|
||||
'name' => 'Advisor',
|
||||
'subtitle' => 'The Advisor service surfaces actionable reports about your project resources, with CTA descriptors for one-click remediation in the console.',
|
||||
'description' => '/docs/services/advisor.md',
|
||||
'controller' => '', // Uses modules
|
||||
'sdk' => true,
|
||||
'docs' => true,
|
||||
'docsUrl' => 'https://appwrite.io/docs/server/advisor',
|
||||
'tests' => true,
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/insights.png',
|
||||
'platforms' => ['server', 'console'],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
use Appwrite\Auth\Validator\MockNumber;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
@@ -27,37 +24,6 @@ Http::init()
|
||||
}
|
||||
});
|
||||
|
||||
Http::get('/v1/projects/:projectId')
|
||||
->desc('Get project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'get',
|
||||
description: '/docs/references/projects/get.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $projectId, Response $response, Database $dbForPlatform) {
|
||||
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
// Backwards compatibility
|
||||
Http::patch('/v1/projects/:projectId/oauth2')
|
||||
->desc('Update project OAuth2')
|
||||
|
||||
@@ -29,6 +29,7 @@ use Appwrite\Utopia\Request\Filters\V22 as RequestV22;
|
||||
use Appwrite\Utopia\Request\Filters\V23 as RequestV23;
|
||||
use Appwrite\Utopia\Request\Filters\V24 as RequestV24;
|
||||
use Appwrite\Utopia\Request\Filters\V25 as RequestV25;
|
||||
use Appwrite\Utopia\Request\Filters\V26 as RequestV26;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
|
||||
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
|
||||
@@ -40,6 +41,7 @@ use Appwrite\Utopia\Response\Filters\V22 as ResponseV22;
|
||||
use Appwrite\Utopia\Response\Filters\V23 as ResponseV23;
|
||||
use Appwrite\Utopia\Response\Filters\V24 as ResponseV24;
|
||||
use Appwrite\Utopia\Response\Filters\V25 as ResponseV25;
|
||||
use Appwrite\Utopia\Response\Filters\V26 as ResponseV26;
|
||||
use Appwrite\Utopia\View;
|
||||
use Executor\Exception\Timeout as ExecutorTimeout;
|
||||
use Executor\Executor;
|
||||
@@ -914,6 +916,9 @@ Http::init()
|
||||
if (version_compare($requestFormat, '1.9.4', '<')) {
|
||||
$request->addFilter(new RequestV25());
|
||||
}
|
||||
if (version_compare($requestFormat, '1.9.5', '<')) {
|
||||
$request->addFilter(new RequestV26());
|
||||
}
|
||||
}
|
||||
|
||||
$localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
|
||||
@@ -938,6 +943,9 @@ Http::init()
|
||||
*/
|
||||
$responseFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
|
||||
if ($responseFormat) {
|
||||
if (version_compare($responseFormat, '1.9.5', '<')) {
|
||||
$response->addFilter(new ResponseV26());
|
||||
}
|
||||
if (version_compare($responseFormat, '1.9.4', '<')) {
|
||||
$response->addFilter(new ResponseV25());
|
||||
}
|
||||
@@ -1274,7 +1282,7 @@ Http::error()
|
||||
if (!$publish && $project->getId() !== 'console') {
|
||||
$errorUser = new DBUser();
|
||||
try {
|
||||
$resolvedUser = $utopia->getResource('user');
|
||||
$resolvedUser = $utopia->context()->get('user');
|
||||
if ($resolvedUser instanceof DBUser) {
|
||||
$errorUser = $resolvedUser;
|
||||
}
|
||||
@@ -1293,7 +1301,7 @@ Http::error()
|
||||
if ($logger && $publish) {
|
||||
try {
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$user = $utopia->getResource('user');
|
||||
$user = $utopia->context()->get('user');
|
||||
} catch (\Throwable) {
|
||||
// All good, user is optional information for logger
|
||||
}
|
||||
@@ -1494,7 +1502,7 @@ Http::error()
|
||||
// the cors resource (which depends on rule -> DB) would cascade.
|
||||
// Uses override:true to avoid duplicate headers if init() already set them.
|
||||
try {
|
||||
$cors = $utopia->getResource('cors');
|
||||
$cors = $utopia->context()->get('cors');
|
||||
foreach ($cors->headers($request->getOrigin()) as $name => $value) {
|
||||
$response
|
||||
->removeHeader($name)
|
||||
|
||||
@@ -7,10 +7,11 @@ use Appwrite\Event\Context\Audit as AuditContext;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Audit as AuditMessage;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Message\Usage as UsageMessage;
|
||||
use Appwrite\Event\Publisher\Audit;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Publisher\Usage as UsagePublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\Webhook;
|
||||
@@ -567,7 +568,7 @@ Http::init()
|
||||
->inject('queueForDeletes')
|
||||
->inject('queueForDatabase')
|
||||
->inject('usage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('dbForProject')
|
||||
->inject('resourceToken')
|
||||
->inject('mode')
|
||||
@@ -577,7 +578,7 @@ Http::init()
|
||||
->inject('platform')
|
||||
->inject('authorization')
|
||||
->inject('cacheControlForStorage')
|
||||
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Delete $queueForDeletes, EventDatabase $queueForDatabase, Context $usage, Func $queueForFunctions, Database $dbForProject, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Telemetry $telemetry, array $platform, Authorization $authorization, callable $cacheControlForStorage) {
|
||||
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Delete $queueForDeletes, EventDatabase $queueForDatabase, Context $usage, FunctionPublisher $publisherForFunctions, Database $dbForProject, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Telemetry $telemetry, array $platform, Authorization $authorization, callable $cacheControlForStorage) {
|
||||
|
||||
$response->setUser($user);
|
||||
$request->setUser($user);
|
||||
@@ -626,10 +627,6 @@ Http::init()
|
||||
/* Auto-set projects */
|
||||
$queueForDeletes->setProject($project);
|
||||
$queueForDatabase->setProject($project);
|
||||
$queueForFunctions->setProject($project);
|
||||
|
||||
/* Auto-set platforms */
|
||||
$queueForFunctions->setPlatform($platform);
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
$storageCacheOperationsCounter = $telemetry->createCounter('storage.cache.operations.load');
|
||||
@@ -823,7 +820,7 @@ Http::shutdown()
|
||||
->inject('publisherForUsage')
|
||||
->inject('queueForDeletes')
|
||||
->inject('queueForDatabase')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('queueForRealtime')
|
||||
->inject('dbForProject')
|
||||
@@ -833,7 +830,7 @@ Http::shutdown()
|
||||
->inject('bus')
|
||||
->inject('apiKey')
|
||||
->inject('mode')
|
||||
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Audit $publisherForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey, string $mode) use ($parseLabel) {
|
||||
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Audit $publisherForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, FunctionPublisher $publisherForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey, string $mode) use ($parseLabel) {
|
||||
|
||||
$responsePayload = $response->getPayload();
|
||||
|
||||
@@ -862,9 +859,15 @@ Http::shutdown()
|
||||
if (! empty($functionsEvents)) {
|
||||
foreach ($generatedEvents as $event) {
|
||||
if (isset($functionsEvents[$event])) {
|
||||
$queueForFunctions
|
||||
->from($queueForEvents)
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $queueForEvents->getEvent(),
|
||||
params: $queueForEvents->getParams(),
|
||||
project: $queueForEvents->getProject(),
|
||||
user: $queueForEvents->getUser(),
|
||||
userId: $queueForEvents->getUserId(),
|
||||
payload: $queueForEvents->getPayload(),
|
||||
platform: $queueForEvents->getPlatform(),
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-39
@@ -3,7 +3,7 @@
|
||||
require_once __DIR__ . '/init.php';
|
||||
require_once __DIR__ . '/init/span.php';
|
||||
|
||||
$registerRequestResources = require __DIR__ . '/init/resources/request.php';
|
||||
$setRequestContext = require __DIR__ . '/init/resources/request.php';
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
@@ -26,6 +26,7 @@ use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\DI\Container;
|
||||
use Utopia\Http\Adapter\Swoole\Server;
|
||||
use Utopia\Http\Files;
|
||||
use Utopia\Http\Http;
|
||||
@@ -57,7 +58,7 @@ $container->set('pools', function ($register) {
|
||||
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
|
||||
$totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
|
||||
$swooleAdapter = new Server(
|
||||
$swoole = new Server(
|
||||
host: "0.0.0.0",
|
||||
port: System::getEnv('PORT', 80),
|
||||
settings: [
|
||||
@@ -69,10 +70,10 @@ $swooleAdapter = new Server(
|
||||
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
|
||||
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
|
||||
],
|
||||
container: $container,
|
||||
resources: $container,
|
||||
);
|
||||
|
||||
$http = $swooleAdapter->getServer();
|
||||
$http = $swoole->getServer();
|
||||
|
||||
/**
|
||||
* Assigns HTTP requests to worker threads by analyzing its payload/content.
|
||||
@@ -190,13 +191,11 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
$container->set('bus', function ($register) use ($swooleAdapter) {
|
||||
return $register->get('bus')->setResolver(fn (string $name) => $swooleAdapter->getContainer()->get($name));
|
||||
}, ['register']);
|
||||
$container->set('bus', fn ($register) => $register->get('bus')->setResolver(fn (string $name) => $swoole->context()->get($name)), ['register']);
|
||||
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
function createDatabase(Http $app, string $resourceKey, string $dbName, array $collections, mixed $pools, ?callable $extraSetup = null): void
|
||||
function createDatabase(Container $resources, string $resourceKey, string $dbName, array $collections, mixed $pools, ?callable $extraSetup = null): void
|
||||
{
|
||||
$max = 15;
|
||||
$sleep = 2;
|
||||
@@ -205,7 +204,7 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
|
||||
while (true) {
|
||||
try {
|
||||
$attempts++;
|
||||
$resource = $app->getResource($resourceKey);
|
||||
$resource = $resources->get($resourceKey);
|
||||
/* @var $database Database */
|
||||
$database = is_callable($resource) ? $resource() : $resource;
|
||||
break; // exit loop on success
|
||||
@@ -288,23 +287,21 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
|
||||
Span::current()?->finish();
|
||||
}
|
||||
|
||||
$http->on(Constant::EVENT_START, function ($http) use ($payloadSize, $totalWorkers, $swooleAdapter) {
|
||||
$app = new Http($swooleAdapter, 'UTC');
|
||||
|
||||
$http->on(Constant::EVENT_START, function ($http) use ($payloadSize, $totalWorkers, $container) {
|
||||
/** @var \Utopia\Pools\Group $pools */
|
||||
$pools = $app->getResource('pools');
|
||||
$pools = $container->get('pools');
|
||||
|
||||
go(function () use ($app, $pools) {
|
||||
go(function () use ($container, $pools) {
|
||||
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
|
||||
// create logs database first, `getLogsDB` is a callable.
|
||||
createDatabase($app, 'getLogsDB', 'logs', $collections['logs'], $pools);
|
||||
createDatabase($container, 'getLogsDB', 'logs', $collections['logs'], $pools);
|
||||
|
||||
// create appwrite database, `dbForPlatform` is a direct access call.
|
||||
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections, $app) {
|
||||
$authorization = $app->getResource('authorization');
|
||||
createDatabase($container, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections, $container) {
|
||||
$authorization = $container->get('authorization');
|
||||
|
||||
if ($dbForPlatform->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
|
||||
$adapter = new AdapterDatabase($dbForPlatform);
|
||||
@@ -416,7 +413,7 @@ $http->on(Constant::EVENT_START, function ($http) use ($payloadSize, $totalWorke
|
||||
$documentsSharedTables = \explode(',', System::getEnv('_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES', ''));
|
||||
$vectorSharedTables = \explode(',', System::getEnv('_APP_DATABASE_VECTORSDB_SHARED_TABLES', ''));
|
||||
|
||||
$cache = $app->getResource('cache');
|
||||
$cache = $container->get('cache');
|
||||
|
||||
// All shared tables pools that need project metadata collections
|
||||
$allSharedTables = \array_values(\array_unique(\array_filter([
|
||||
@@ -502,7 +499,7 @@ $http->on(Constant::EVENT_START, function ($http) use ($payloadSize, $totalWorke
|
||||
});
|
||||
});
|
||||
|
||||
$swooleAdapter->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files, $swooleAdapter, $registerRequestResources) {
|
||||
$swoole->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files, $swoole, $setRequestContext) {
|
||||
Span::init('http.request');
|
||||
|
||||
$request = new Request($utopiaRequest->getSwooleRequest());
|
||||
@@ -522,21 +519,18 @@ $swooleAdapter->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files
|
||||
return;
|
||||
}
|
||||
|
||||
$requestContainer = $swooleAdapter->getContainer();
|
||||
$requestContainer->set('container', fn () => $requestContainer);
|
||||
$requestContainer->set('request', fn () => $request);
|
||||
$requestContainer->set('response', fn () => $response);
|
||||
$app = new Http($swoole, 'UTC');
|
||||
$app->context()->set('request', fn () => $request);
|
||||
$app->context()->set('response', fn () => $response);
|
||||
$app->context()->set('utopia', fn () => $app);
|
||||
|
||||
$app = new Http($swooleAdapter, 'UTC');
|
||||
$requestContainer->set('utopia', fn () => $app);
|
||||
|
||||
$registerRequestResources($requestContainer);
|
||||
$setRequestContext($app->context());
|
||||
|
||||
$app->setCompression(System::getEnv('_APP_COMPRESSION_ENABLED', 'enabled') === 'enabled');
|
||||
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
|
||||
|
||||
try {
|
||||
$authorization = $app->getResource('authorization');
|
||||
$authorization = $app->context()->get('authorization');
|
||||
|
||||
$request->setAuthorization($authorization);
|
||||
$response->setAuthorization($authorization);
|
||||
@@ -552,18 +546,18 @@ $swooleAdapter->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files
|
||||
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$logger = $app->getResource("logger");
|
||||
$logger = $app->context()->get("logger");
|
||||
if ($logger) {
|
||||
try {
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$user = $app->getResource('user');
|
||||
$user = $app->context()->get('user');
|
||||
} catch (\Throwable $_th) {
|
||||
// All good, user is optional information for logger
|
||||
}
|
||||
|
||||
$route = $app->getRoute();
|
||||
|
||||
$log = $app->getResource("log");
|
||||
$log = $app->context()->get("log");
|
||||
|
||||
if (isset($user) && !$user->isEmpty()) {
|
||||
$log->setUser(new User($user->getId()));
|
||||
@@ -642,18 +636,16 @@ $swooleAdapter->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files
|
||||
});
|
||||
|
||||
// Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory
|
||||
$http->on(Constant::EVENT_TASK, function () use ($swooleAdapter) {
|
||||
$http->on(Constant::EVENT_TASK, function () use ($container) {
|
||||
$lastSyncUpdate = null;
|
||||
|
||||
$app = new Http($swooleAdapter, 'UTC');
|
||||
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
$dbForPlatform = $app->getResource('dbForPlatform');
|
||||
$dbForPlatform = $container->get('dbForPlatform');
|
||||
|
||||
/** @var \Swoole\Table $riskyDomains */
|
||||
$riskyDomains = $app->getResource('riskyDomains');
|
||||
$riskyDomains = $container->get('riskyDomains');
|
||||
|
||||
Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $riskyDomains, &$lastSyncUpdate, $app) {
|
||||
Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $riskyDomains, &$lastSyncUpdate, $container) {
|
||||
try {
|
||||
$time = DateTime::now();
|
||||
$limit = 1000;
|
||||
@@ -670,7 +662,7 @@ $http->on(Constant::EVENT_TASK, function () use ($swooleAdapter) {
|
||||
}
|
||||
$results = [];
|
||||
try {
|
||||
$authorization = $app->getResource('authorization');
|
||||
$authorization = $container->get('authorization');
|
||||
$results = $authorization->skip(fn () => $dbForPlatform->find('rules', $queries));
|
||||
} catch (Throwable $th) {
|
||||
Console::error('rules ' . $th->getMessage());
|
||||
@@ -720,4 +712,4 @@ $http->on(Constant::EVENT_TASK, function () use ($swooleAdapter) {
|
||||
});
|
||||
});
|
||||
|
||||
$swooleAdapter->start();
|
||||
$swoole->start();
|
||||
|
||||
+58
-2
@@ -1,5 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Platform\Modules\Advisor\Enums\InsightCTAMethod;
|
||||
use Appwrite\Platform\Modules\Advisor\Enums\InsightCTAService;
|
||||
use Appwrite\Platform\Modules\Advisor\Enums\InsightSeverity;
|
||||
use Appwrite\Platform\Modules\Advisor\Enums\InsightStatus;
|
||||
use Appwrite\Platform\Modules\Advisor\Enums\InsightType;
|
||||
use Appwrite\Platform\Modules\Advisor\Enums\ReportType;
|
||||
use Appwrite\Platform\Modules\Compute\Specification;
|
||||
use Utopia\System\System;
|
||||
|
||||
@@ -44,8 +50,8 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 4325;
|
||||
const APP_VERSION_STABLE = '1.9.4';
|
||||
const APP_CACHE_BUSTER = 4326;
|
||||
const APP_VERSION_STABLE = '1.9.5';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
@@ -222,6 +228,7 @@ const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
|
||||
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
|
||||
const DELETE_TYPE_CSV_EXPORTS = 'csv_exports';
|
||||
const DELETE_TYPE_MAINTENANCE = 'maintenance';
|
||||
const DELETE_TYPE_REPORT = 'report';
|
||||
|
||||
// Rule statuses
|
||||
const RULE_STATUS_CREATED = 'created'; // This is also the status when domain DNS verification fails.
|
||||
@@ -424,6 +431,55 @@ const RESOURCE_TYPE_MESSAGES = 'messages';
|
||||
const RESOURCE_TYPE_EXECUTIONS = 'executions';
|
||||
const RESOURCE_TYPE_VCS = 'vcs';
|
||||
const RESOURCE_TYPE_EMBEDDINGS_TEXT = 'embeddingsText';
|
||||
const RESOURCE_TYPE_INSIGHTS = 'insights';
|
||||
const RESOURCE_TYPE_REPORTS = 'reports';
|
||||
|
||||
// Insight types — engine-specific so the CTA action can reference the right public API.
|
||||
const ADVISOR_INSIGHT_TYPES = [
|
||||
InsightType::DATABASE_INDEX->value, // legacy databases.createIndex
|
||||
InsightType::TABLES_DB_INDEX->value, // tablesDB.createIndex
|
||||
InsightType::DOCUMENTS_DB_INDEX->value, // documentsDB.createIndex
|
||||
InsightType::VECTORS_DB_INDEX->value, // vectorsDB.createIndex
|
||||
InsightType::DATABASE_PERFORMANCE->value,
|
||||
InsightType::SITE_PERFORMANCE->value,
|
||||
InsightType::SITE_ACCESSIBILITY->value,
|
||||
InsightType::SITE_SEO->value,
|
||||
InsightType::FUNCTION_PERFORMANCE->value,
|
||||
];
|
||||
|
||||
// Public API services (SDK namespaces) that an insight CTA's `service` can reference.
|
||||
// Analyzers must pick the one matching the engine the resource lives in.
|
||||
const ADVISOR_CTA_SERVICES = [
|
||||
InsightCTAService::DATABASES->value, // legacy
|
||||
InsightCTAService::TABLES_DB->value,
|
||||
InsightCTAService::DOCUMENTS_DB->value,
|
||||
InsightCTAService::VECTORS_DB->value,
|
||||
];
|
||||
|
||||
// Public API method names that an insight CTA's `method` can reference for index suggestions.
|
||||
const ADVISOR_CTA_METHODS = [
|
||||
InsightCTAMethod::CREATE_INDEX->value,
|
||||
];
|
||||
|
||||
// Insight severities
|
||||
const ADVISOR_SEVERITIES = [
|
||||
InsightSeverity::INFO->value,
|
||||
InsightSeverity::WARNING->value,
|
||||
InsightSeverity::CRITICAL->value,
|
||||
];
|
||||
|
||||
// Insight statuses
|
||||
const ADVISOR_STATUSES = [
|
||||
InsightStatus::ACTIVE->value,
|
||||
InsightStatus::DISMISSED->value,
|
||||
];
|
||||
|
||||
// Report types
|
||||
const ADVISOR_REPORT_TYPES = [
|
||||
ReportType::LIGHTHOUSE->value,
|
||||
ReportType::AUDIT->value,
|
||||
ReportType::DATABASE_ANALYZER->value,
|
||||
];
|
||||
|
||||
// Resource types for Tokens
|
||||
const TOKENS_RESOURCE_TYPE_FILES = 'files';
|
||||
|
||||
@@ -475,3 +475,17 @@ Database::addFilter(
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'subQueryReportInsights',
|
||||
function (mixed $value) {
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database->getAuthorization()->skip(fn () => $database->find('insights', [
|
||||
Query::equal('projectInternalId', [$document->getAttribute('projectInternalId')]),
|
||||
Query::equal('reportInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -92,6 +92,8 @@ use Appwrite\Utopia\Response\Model\HealthTime;
|
||||
use Appwrite\Utopia\Response\Model\HealthVersion;
|
||||
use Appwrite\Utopia\Response\Model\Identity;
|
||||
use Appwrite\Utopia\Response\Model\Index;
|
||||
use Appwrite\Utopia\Response\Model\Insight;
|
||||
use Appwrite\Utopia\Response\Model\InsightCTA;
|
||||
use Appwrite\Utopia\Response\Model\Installation;
|
||||
use Appwrite\Utopia\Response\Model\JWT;
|
||||
use Appwrite\Utopia\Response\Model\Key;
|
||||
@@ -173,12 +175,16 @@ use Appwrite\Utopia\Response\Model\PolicySessionLimit;
|
||||
use Appwrite\Utopia\Response\Model\PolicyUserLimit;
|
||||
use Appwrite\Utopia\Response\Model\Preferences;
|
||||
use Appwrite\Utopia\Response\Model\Project;
|
||||
use Appwrite\Utopia\Response\Model\ProjectAuthMethod;
|
||||
use Appwrite\Utopia\Response\Model\ProjectProtocol;
|
||||
use Appwrite\Utopia\Response\Model\ProjectService;
|
||||
use Appwrite\Utopia\Response\Model\Provider;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepository;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepositoryFramework;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepositoryFrameworkList;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntime;
|
||||
use Appwrite\Utopia\Response\Model\ProviderRepositoryRuntimeList;
|
||||
use Appwrite\Utopia\Response\Model\Report;
|
||||
use Appwrite\Utopia\Response\Model\ResourceToken;
|
||||
use Appwrite\Utopia\Response\Model\Row;
|
||||
use Appwrite\Utopia\Response\Model\Rule;
|
||||
@@ -288,6 +294,8 @@ Response::setModel(new BaseList('Specifications List', Response::MODEL_SPECIFICA
|
||||
Response::setModel(new BaseList('VCS Content List', Response::MODEL_VCS_CONTENT_LIST, 'contents', Response::MODEL_VCS_CONTENT));
|
||||
Response::setModel(new BaseList('VectorsDB Collections List', Response::MODEL_VECTORSDB_COLLECTION_LIST, 'collections', Response::MODEL_VECTORSDB_COLLECTION));
|
||||
Response::setModel(new BaseList('Embedding list', Response::MODEL_EMBEDDING_LIST, 'embeddings', Response::MODEL_EMBEDDING));
|
||||
Response::setModel(new BaseList('Insights List', Response::MODEL_INSIGHT_LIST, 'insights', Response::MODEL_INSIGHT));
|
||||
Response::setModel(new BaseList('Reports List', Response::MODEL_REPORT_LIST, 'reports', Response::MODEL_REPORT));
|
||||
|
||||
// Entities
|
||||
Response::setModel(new Database());
|
||||
@@ -397,6 +405,9 @@ Response::setModel(new FrameworkAdapter());
|
||||
Response::setModel(new Deployment());
|
||||
Response::setModel(new Execution());
|
||||
Response::setModel(new Project());
|
||||
Response::setModel(new ProjectAuthMethod());
|
||||
Response::setModel(new ProjectService());
|
||||
Response::setModel(new ProjectProtocol());
|
||||
Response::setModel(new Webhook());
|
||||
Response::setModel(new Key());
|
||||
Response::setModel(new EphemeralKey());
|
||||
@@ -509,6 +520,9 @@ Response::setModel(new Target());
|
||||
Response::setModel(new Migration());
|
||||
Response::setModel(new MigrationReport());
|
||||
Response::setModel(new MigrationFirebaseProject());
|
||||
Response::setModel(new Insight());
|
||||
Response::setModel(new InsightCTA());
|
||||
Response::setModel(new Report());
|
||||
|
||||
// Tests (keep last)
|
||||
Response::setModel(new Mock());
|
||||
|
||||
@@ -5,6 +5,7 @@ use Appwrite\Event\Publisher\Audit as AuditPublisher;
|
||||
use Appwrite\Event\Publisher\Build as BuildPublisher;
|
||||
use Appwrite\Event\Publisher\Certificate as CertificatePublisher;
|
||||
use Appwrite\Event\Publisher\Execution as ExecutionPublisher;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Publisher\Mail as MailPublisher;
|
||||
use Appwrite\Event\Publisher\Messaging as MessagingPublisher;
|
||||
use Appwrite\Event\Publisher\Migration as MigrationPublisher;
|
||||
@@ -108,6 +109,10 @@ $container->set('publisherForExecutions', fn (Publisher $publisher) => new Execu
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_EXECUTIONS_QUEUE_NAME', Event::EXECUTIONS_QUEUE_NAME))
|
||||
), ['publisher']);
|
||||
$container->set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher(
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL)
|
||||
), ['publisher']);
|
||||
$container->set('publisherForMigrations', fn (Publisher $publisher) => new MigrationPublisher(
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME))
|
||||
|
||||
+93
-119
@@ -8,7 +8,8 @@ use Appwrite\Event\Context\Audit as AuditContext;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Extend\Exception;
|
||||
@@ -48,6 +49,7 @@ use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Queue\Queue;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
@@ -59,26 +61,18 @@ use Utopia\Validator\WhiteList;
|
||||
* These resources depend (directly or transitively) on request/response
|
||||
* and must be fresh for each HTTP request.
|
||||
*/
|
||||
return function (Container $container): void {
|
||||
$container->set('utopia:graphql', function ($utopia) {
|
||||
return $utopia;
|
||||
}, ['utopia']);
|
||||
return function (Container $context): void {
|
||||
$context->set('utopia:graphql', fn ($utopia) => $utopia, ['utopia']);
|
||||
|
||||
$container->set('log', fn () => new Log(), []);
|
||||
$context->set('log', fn () => new Log(), []);
|
||||
|
||||
$container->set('logger', function ($register) {
|
||||
return $register->get('logger');
|
||||
}, ['register']);
|
||||
$context->set('logger', fn ($register) => $register->get('logger'), ['register']);
|
||||
|
||||
$container->set('authorization', function () {
|
||||
return new Authorization();
|
||||
}, []);
|
||||
$context->set('authorization', fn () => new Authorization(), []);
|
||||
|
||||
$container->set('store', function (): Store {
|
||||
return new Store();
|
||||
}, []);
|
||||
$context->set('store', fn (): Store => new Store(), []);
|
||||
|
||||
$container->set('proofForPassword', function (): Password {
|
||||
$context->set('proofForPassword', function (): Password {
|
||||
$hash = new Argon2();
|
||||
$hash
|
||||
->setMemoryCost(7168)
|
||||
@@ -92,21 +86,21 @@ return function (Container $container): void {
|
||||
return $password;
|
||||
});
|
||||
|
||||
$container->set('proofForToken', function (): Token {
|
||||
$context->set('proofForToken', function (): Token {
|
||||
$token = new Token();
|
||||
$token->setHash(new Sha());
|
||||
|
||||
return $token;
|
||||
});
|
||||
|
||||
$container->set('proofForCode', function (): Code {
|
||||
$context->set('proofForCode', function (): Code {
|
||||
$code = new Code();
|
||||
$code->setHash(new Sha());
|
||||
|
||||
return $code;
|
||||
});
|
||||
|
||||
$container->set('locale', function () {
|
||||
$context->set('locale', function () {
|
||||
$locale = new Locale(System::getEnv('_APP_LOCALE', 'en'));
|
||||
$locale->setFallback(System::getEnv('_APP_LOCALE', 'en'));
|
||||
|
||||
@@ -114,32 +108,19 @@ return function (Container $container): void {
|
||||
});
|
||||
|
||||
// Per-request queue resources (stateful, accumulate event data during request)
|
||||
$container->set('queueForDatabase', function (Publisher $publisher) {
|
||||
return new EventDatabase($publisher);
|
||||
}, ['publisher']);
|
||||
$container->set('queueForDeletes', function (Publisher $publisher) {
|
||||
return new Delete($publisher);
|
||||
}, ['publisher']);
|
||||
$container->set('queueForEvents', function (Publisher $publisher) {
|
||||
return new Event($publisher);
|
||||
}, ['publisher']);
|
||||
$container->set('queueForWebhooks', function (Publisher $publisher) {
|
||||
return new Webhook($publisher);
|
||||
}, ['publisher']);
|
||||
$container->set('queueForRealtime', function () {
|
||||
return new Realtime();
|
||||
}, []);
|
||||
$container->set('usage', function () {
|
||||
return new UsageContext();
|
||||
}, []);
|
||||
$container->set('auditContext', fn () => new AuditContext(), []);
|
||||
$container->set('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
$container->set('eventProcessor', function () {
|
||||
return new EventProcessor();
|
||||
}, []);
|
||||
$container->set('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$context->set('queueForDatabase', fn (Publisher $publisher) => new EventDatabase($publisher), ['publisher']);
|
||||
$context->set('queueForDeletes', fn (Publisher $publisher) => new Delete($publisher), ['publisher']);
|
||||
$context->set('queueForEvents', fn (Publisher $publisher) => new Event($publisher), ['publisher']);
|
||||
$context->set('queueForWebhooks', fn (Publisher $publisher) => new Webhook($publisher), ['publisher']);
|
||||
$context->set('queueForRealtime', fn () => new Realtime(), []);
|
||||
$context->set('usage', fn () => new UsageContext(), []);
|
||||
$context->set('auditContext', fn () => new AuditContext(), []);
|
||||
$context->set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher(
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL)
|
||||
), ['publisher']);
|
||||
$context->set('eventProcessor', fn () => new EventProcessor(), []);
|
||||
$context->set('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
@@ -157,7 +138,7 @@ return function (Container $container): void {
|
||||
return $database;
|
||||
}, ['pools', 'cache', 'authorization']);
|
||||
|
||||
$container->set('getProjectDB', function (Group $pools, Database $dbForPlatform, Cache $cache, Authorization $authorization) {
|
||||
$context->set('getProjectDB', function (Group $pools, Database $dbForPlatform, Cache $cache, Authorization $authorization) {
|
||||
$adapters = [];
|
||||
|
||||
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$adapters) {
|
||||
@@ -214,7 +195,7 @@ return function (Container $container): void {
|
||||
};
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
|
||||
|
||||
$container->set('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$context->set('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$adapter = null;
|
||||
|
||||
return function (?Document $project = null) use ($pools, $cache, $authorization, &$adapter) {
|
||||
@@ -246,7 +227,7 @@ return function (Container $container): void {
|
||||
/**
|
||||
* List of allowed request hostnames for the request.
|
||||
*/
|
||||
$container->set('allowedHostnames', function (array $platform, Document $project, Document $rule, Document $devKey, Request $request) {
|
||||
$context->set('allowedHostnames', function (array $platform, Document $project, Document $rule, Document $devKey, Request $request) {
|
||||
$allowed = [...($platform['hostnames'] ?? [])];
|
||||
|
||||
/* Add platform configured hostnames */
|
||||
@@ -290,7 +271,7 @@ return function (Container $container): void {
|
||||
/**
|
||||
* List of allowed request schemes for the request.
|
||||
*/
|
||||
$container->set('allowedSchemes', function (array $platform, Document $project) {
|
||||
$context->set('allowedSchemes', function (array $platform, Document $project) {
|
||||
$allowed = [...($platform['schemas'] ?? [])];
|
||||
|
||||
if (! $project->isEmpty() && $project->getId() !== 'console') {
|
||||
@@ -310,7 +291,7 @@ return function (Container $container): void {
|
||||
/**
|
||||
* Whether the request origin is verified against the request hostname.
|
||||
*/
|
||||
$container->set('domainVerification', function (Request $request) {
|
||||
$context->set('domainVerification', function (Request $request) {
|
||||
$origin = \parse_url($request->getOrigin($request->getReferer('')), PHP_URL_HOST);
|
||||
$selfDomain = new Domain($request->getHostname());
|
||||
$endDomain = new Domain((string) $origin);
|
||||
@@ -322,7 +303,7 @@ return function (Container $container): void {
|
||||
/**
|
||||
* Cookie domain for the current request.
|
||||
*/
|
||||
$container->set('cookieDomain', function (Request $request, Document $project) {
|
||||
$context->set('cookieDomain', function (Request $request, Document $project) {
|
||||
$localHosts = ['localhost', 'localhost:' . $request->getPort()];
|
||||
|
||||
$migrationHost = System::getEnv('_APP_MIGRATION_HOST');
|
||||
@@ -356,7 +337,7 @@ return function (Container $container): void {
|
||||
/**
|
||||
* Rule associated with a request origin.
|
||||
*/
|
||||
$container->set('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
|
||||
$context->set('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
|
||||
$domain = \parse_url($request->getOrigin(), PHP_URL_HOST);
|
||||
|
||||
if (empty($domain)) {
|
||||
@@ -406,7 +387,7 @@ return function (Container $container): void {
|
||||
/**
|
||||
* CORS service
|
||||
*/
|
||||
$container->set('cors', function (array $allowedHostnames) {
|
||||
$context->set('cors', function (array $allowedHostnames) {
|
||||
$corsConfig = Config::getParam('cors');
|
||||
|
||||
return new Cors(
|
||||
@@ -418,23 +399,23 @@ return function (Container $container): void {
|
||||
);
|
||||
}, ['allowedHostnames']);
|
||||
|
||||
$container->set('originValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
|
||||
if (! $devKey->isEmpty()) {
|
||||
return new URL();
|
||||
}
|
||||
$context->set(
|
||||
'originValidator',
|
||||
fn (Document $devKey, array $allowedHostnames, array $allowedSchemes) => $devKey->isEmpty()
|
||||
? new Origin($allowedHostnames, $allowedSchemes)
|
||||
: new URL(),
|
||||
['devKey', 'allowedHostnames', 'allowedSchemes']
|
||||
);
|
||||
|
||||
return new Origin($allowedHostnames, $allowedSchemes);
|
||||
}, ['devKey', 'allowedHostnames', 'allowedSchemes']);
|
||||
$context->set(
|
||||
'redirectValidator',
|
||||
fn (Document $devKey, array $allowedHostnames, array $allowedSchemes) => $devKey->isEmpty()
|
||||
? new Redirect($allowedHostnames, $allowedSchemes)
|
||||
: new URL(),
|
||||
['devKey', 'allowedHostnames', 'allowedSchemes']
|
||||
);
|
||||
|
||||
$container->set('redirectValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
|
||||
if (! $devKey->isEmpty()) {
|
||||
return new URL();
|
||||
}
|
||||
|
||||
return new Redirect($allowedHostnames, $allowedSchemes);
|
||||
}, ['devKey', 'allowedHostnames', 'allowedSchemes']);
|
||||
|
||||
$container->set('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) {
|
||||
$context->set('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) {
|
||||
/**
|
||||
* Handles user authentication and session validation.
|
||||
*
|
||||
@@ -605,7 +586,7 @@ return function (Container $container): void {
|
||||
return $user;
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken', 'authorization']);
|
||||
|
||||
$container->set('project', function ($dbForPlatform, $request, $console, $authorization, Http $utopia) {
|
||||
$context->set('project', function ($dbForPlatform, $request, $console, $authorization, Http $utopia) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
/** @var Utopia\Database\Document $console */
|
||||
@@ -638,7 +619,7 @@ return function (Container $container): void {
|
||||
return $project;
|
||||
}, ['dbForPlatform', 'request', 'console', 'authorization', 'utopia']);
|
||||
|
||||
$container->set('session', function (User $user, Store $store, Token $proofForToken) {
|
||||
$context->set('session', function (User $user, Store $store, Token $proofForToken) {
|
||||
if ($user->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -659,7 +640,7 @@ return function (Container $container): void {
|
||||
return;
|
||||
}, ['user', 'store', 'proofForToken']);
|
||||
|
||||
$container->set('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Response $response, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Func $queueForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime, UsageContext $usage, Authorization $authorization, Request $request) {
|
||||
$context->set('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Response $response, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, FunctionPublisher $publisherForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime, UsageContext $usage, Authorization $authorization, Request $request) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
@@ -715,7 +696,7 @@ return function (Container $container): void {
|
||||
* Accounts can be created in many ways beyond `createAccount`
|
||||
* (anonymous, OAuth, phone, etc.), and those flows are probably not covered in event tests; so we handle this here.
|
||||
*/
|
||||
$eventDatabaseListener = function (Document $project, Document $document, Response $response, Event $queueForEvents, Func $queueForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime) {
|
||||
$eventDatabaseListener = function (Document $project, Document $document, Response $response, Event $queueForEvents, FunctionPublisher $publisherForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime) {
|
||||
// Only trigger events for user creation with the database listener.
|
||||
if ($document->getCollection() !== 'users') {
|
||||
return;
|
||||
@@ -727,9 +708,15 @@ return function (Container $container): void {
|
||||
->setPayload($response->output($document, Response::MODEL_USER));
|
||||
|
||||
// Trigger functions, webhooks, and realtime events
|
||||
$queueForFunctions
|
||||
->from($queueForEvents)
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $queueForEvents->getEvent(),
|
||||
params: $queueForEvents->getParams(),
|
||||
project: $queueForEvents->getProject(),
|
||||
user: $queueForEvents->getUser(),
|
||||
userId: $queueForEvents->getUserId(),
|
||||
payload: $queueForEvents->getPayload(),
|
||||
platform: $queueForEvents->getPlatform(),
|
||||
));
|
||||
|
||||
/** Trigger webhooks events only if a project has them enabled */
|
||||
if (! empty($project->getAttribute('webhooks'))) {
|
||||
@@ -909,7 +896,6 @@ return function (Container $container): void {
|
||||
// Clone the queues, to prevent events triggered by the database listener
|
||||
// from overwriting the events that are supposed to be triggered in the shutdown hook.
|
||||
$queueForEventsClone = new Event($publisher);
|
||||
$queueForFunctions = new Func($publisherFunctions);
|
||||
$queueForWebhooks = new Webhook($publisherWebhooks);
|
||||
$queueForRealtime = new Realtime();
|
||||
|
||||
@@ -924,7 +910,7 @@ return function (Container $container): void {
|
||||
$document,
|
||||
$response,
|
||||
$queueForEventsClone->from($queueForEvents),
|
||||
$queueForFunctions->from($queueForEvents),
|
||||
$publisherForFunctions,
|
||||
$queueForWebhooks->from($queueForEvents),
|
||||
$queueForRealtime->from($queueForEvents)
|
||||
))
|
||||
@@ -933,9 +919,9 @@ return function (Container $container): void {
|
||||
->on(Database::EVENT_DOCUMENT_DELETE, 'purge-function-events-cache', fn ($event, $document) => $functionsEventsCacheListener($event, $document, $project, $database));
|
||||
|
||||
return $database;
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'project', 'response', 'publisher', 'publisherFunctions', 'publisherWebhooks', 'queueForEvents', 'queueForFunctions', 'queueForWebhooks', 'queueForRealtime', 'usage', 'authorization', 'request']);
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'project', 'response', 'publisher', 'publisherFunctions', 'publisherWebhooks', 'queueForEvents', 'publisherForFunctions', 'queueForWebhooks', 'queueForRealtime', 'usage', 'authorization', 'request']);
|
||||
|
||||
$container->set('schema', function ($utopia, $dbForProject, $authorization) {
|
||||
$context->set('schema', function ($utopia, $dbForProject, $authorization) {
|
||||
|
||||
$complexity = function (int $complexity, array $args) {
|
||||
$queries = Query::parseQueries($args['queries'] ?? []);
|
||||
@@ -1022,13 +1008,9 @@ return function (Container $container): void {
|
||||
);
|
||||
}, ['utopia', 'dbForProject', 'authorization']);
|
||||
|
||||
$container->set('audit', function ($dbForProject) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$context->set('audit', fn ($dbForProject) => new Audit(new AdapterDatabase($dbForProject)), ['dbForProject']);
|
||||
|
||||
return new Audit($adapter);
|
||||
}, ['dbForProject']);
|
||||
|
||||
$container->set('mode', function ($request, Document $project) {
|
||||
$context->set('mode', function ($request, Document $project) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
/**
|
||||
@@ -1046,7 +1028,7 @@ return function (Container $container): void {
|
||||
return $mode;
|
||||
}, ['request', 'project']);
|
||||
|
||||
$container->set('requestTimestamp', function ($request) {
|
||||
$context->set('requestTimestamp', function ($request) {
|
||||
// TODO: Move this to the Request class itself
|
||||
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
|
||||
$requestTimestamp = null;
|
||||
@@ -1061,7 +1043,7 @@ return function (Container $container): void {
|
||||
return $requestTimestamp;
|
||||
}, ['request']);
|
||||
|
||||
$container->set('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform, Authorization $authorization) {
|
||||
$context->set('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform, Authorization $authorization) {
|
||||
$devKey = $request->getHeader('x-appwrite-dev-key', $request->getParam('devKey', ''));
|
||||
|
||||
// Check if given key match project's development keys
|
||||
@@ -1110,7 +1092,7 @@ return function (Container $container): void {
|
||||
return $key;
|
||||
}, ['request', 'project', 'servers', 'dbForPlatform', 'authorization']);
|
||||
|
||||
$container->set('team', function (Document $project, Database $dbForPlatform, Http $utopia, Request $request, Authorization $authorization) {
|
||||
$context->set('team', function (Document $project, Database $dbForPlatform, Http $utopia, Request $request, Authorization $authorization) {
|
||||
$teamInternalId = '';
|
||||
if ($project->getId() !== 'console') {
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', '');
|
||||
@@ -1153,7 +1135,7 @@ return function (Container $container): void {
|
||||
return $team;
|
||||
}, ['project', 'dbForPlatform', 'utopia', 'request', 'authorization']);
|
||||
|
||||
$container->set('previewHostname', function (Request $request, ?Key $apiKey) {
|
||||
$context->set('previewHostname', function (Request $request, ?Key $apiKey) {
|
||||
$allowed = false;
|
||||
|
||||
if (Http::isDevelopment()) {
|
||||
@@ -1172,7 +1154,7 @@ return function (Container $container): void {
|
||||
return '';
|
||||
}, ['request', 'apiKey']);
|
||||
|
||||
$container->set('apiKey', function (Request $request, Document $project, Document $team, Document $user): ?Key {
|
||||
$context->set('apiKey', function (Request $request, Document $project, Document $team, Document $user): ?Key {
|
||||
$key = $request->getHeader('x-appwrite-key');
|
||||
|
||||
if (empty($key)) {
|
||||
@@ -1206,7 +1188,7 @@ return function (Container $container): void {
|
||||
return $key;
|
||||
}, ['request', 'project', 'team', 'user']);
|
||||
|
||||
$container->set('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
|
||||
$context->set('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
|
||||
$tokenJWT = $request->getParam('token');
|
||||
|
||||
if (! empty($tokenJWT) && ! $project->isEmpty()) { // JWT authentication
|
||||
@@ -1273,10 +1255,10 @@ return function (Container $container): void {
|
||||
return new Document([]);
|
||||
}, ['project', 'dbForProject', 'request', 'authorization']);
|
||||
|
||||
$container->set('getDatabasesDB', function (Group $pools, Cache $cache, Document $project, Request $request, UsageContext $usage, Authorization $authorization) {
|
||||
$context->set('getDatabasesDB', function (Group $pools, Cache $cache, Document $project, Request $request, UsageContext $usage, Authorization $authorization) {
|
||||
|
||||
return function (Document $database) use ($pools, $cache, $project, $request, $usage, $authorization): Database {
|
||||
$databaseDSN = $database->getAttribute('database', $project->getAttribute('database', ''));
|
||||
$databaseDSN = $database->getAttribute('database') ?: $project->getAttribute('database', '');
|
||||
$databaseType = $database->getAttribute('type', '');
|
||||
|
||||
try {
|
||||
@@ -1435,35 +1417,27 @@ return function (Container $container): void {
|
||||
|
||||
}, ['pools', 'cache', 'project', 'request', 'usage', 'authorization']);
|
||||
|
||||
$container->set('transactionState', function (Database $dbForProject, Authorization $authorization, callable $getDatabasesDB) {
|
||||
return new TransactionState($dbForProject, $authorization, $getDatabasesDB);
|
||||
}, ['dbForProject', 'authorization', 'getDatabasesDB']);
|
||||
$context->set(
|
||||
'transactionState',
|
||||
fn (Database $dbForProject, Authorization $authorization, callable $getDatabasesDB) => new TransactionState($dbForProject, $authorization, $getDatabasesDB),
|
||||
['dbForProject', 'authorization', 'getDatabasesDB']
|
||||
);
|
||||
|
||||
$container->set('executionsRetentionCount', function (Document $project, array $plan) {
|
||||
if ($project->getId() === 'console' || empty($plan)) {
|
||||
return 0;
|
||||
}
|
||||
$context->set(
|
||||
'executionsRetentionCount',
|
||||
fn (Document $project, array $plan) => ($project->getId() === 'console' || empty($plan))
|
||||
? 0
|
||||
: (int) ($plan['executionsRetentionCount'] ?? 100),
|
||||
['project', 'plan']
|
||||
);
|
||||
|
||||
return (int) ($plan['executionsRetentionCount'] ?? 100);
|
||||
}, ['project', 'plan']);
|
||||
$context->set('deviceForFiles', fn ($project, Telemetry $telemetry) => new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId())), ['project', 'telemetry']);
|
||||
$context->set('deviceForSites', fn ($project, Telemetry $telemetry) => new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId())), ['project', 'telemetry']);
|
||||
$context->set('deviceForMigrations', fn ($project, Telemetry $telemetry) => new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId())), ['project', 'telemetry']);
|
||||
$context->set('deviceForFunctions', fn ($project, Telemetry $telemetry) => new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId())), ['project', 'telemetry']);
|
||||
$context->set('deviceForBuilds', fn ($project, Telemetry $telemetry) => new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId())), ['project', 'telemetry']);
|
||||
|
||||
$container->set('deviceForFiles', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
$container->set('deviceForSites', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
$container->set('deviceForMigrations', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
$container->set('deviceForFunctions', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
$container->set('deviceForBuilds', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
$container->set('embeddingAgent', function ($register) {
|
||||
$context->set('embeddingAgent', function ($register) {
|
||||
$adapter = new Ollama();
|
||||
$adapter->setEndpoint(System::getEnv('_APP_EMBEDDING_ENDPOINT', 'http://ollama:11434/api/embed'));
|
||||
$adapter->setTimeout((int) System::getEnv('_APP_EMBEDDING_TIMEOUT', '30000'));
|
||||
|
||||
+20
-1
@@ -3,11 +3,30 @@
|
||||
use Utopia\Span\Exporter;
|
||||
use Utopia\Span\Span;
|
||||
use Utopia\Span\Storage;
|
||||
use Utopia\System\System;
|
||||
|
||||
Span::setStorage(new Storage\Coroutine());
|
||||
Span::addExporter(new Exporter\Pretty(), function (Span $span): bool {
|
||||
|
||||
// Resolve trace filters once at boot to avoid repeated env lookups per span.
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
$traceEnabled = $traceProjectId !== '' || $traceFunctionId !== '';
|
||||
|
||||
Span::addExporter(new Exporter\Pretty(), function (Span $span) use ($traceEnabled, $traceProjectId, $traceFunctionId): bool {
|
||||
if (\str_starts_with($span->getAction(), 'listener.')) {
|
||||
return $span->getError() !== null;
|
||||
}
|
||||
|
||||
// Selective tracing: when _APP_TRACE_PROJECT_ID / _APP_TRACE_FUNCTION_ID are set,
|
||||
// only export spans tagged with matching project.id / function.id.
|
||||
if ($traceEnabled) {
|
||||
if ($traceProjectId !== '' && $span->get('project.id') !== $traceProjectId) {
|
||||
return false;
|
||||
}
|
||||
if ($traceFunctionId !== '' && $span->get('function.id') !== $traceFunctionId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Usage\Context;
|
||||
@@ -23,6 +23,7 @@ use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Queue\Queue;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Storage\Device\Telemetry as TelemetryDevice;
|
||||
use Utopia\System\System;
|
||||
@@ -343,9 +344,10 @@ return function (Container $container): void {
|
||||
return new Webhook($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
$container->set('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
$container->set('publisherForFunctions', fn (Publisher $publisher) => new FunctionPublisher(
|
||||
$publisher,
|
||||
new Queue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', Event::FUNCTIONS_QUEUE_TTL)
|
||||
), ['publisher']);
|
||||
|
||||
$container->set('queueForRealtime', function () {
|
||||
return new Realtime();
|
||||
|
||||
+25
-25
@@ -728,8 +728,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
$success = false;
|
||||
|
||||
Span::init('realtime.open');
|
||||
Span::add('realtime.connectionId', $connection);
|
||||
Span::add('realtime.inboundBytes', $rawSize);
|
||||
Span::add('realtime.connection.id', $connection);
|
||||
Span::add('realtime.inbound_bytes', $rawSize);
|
||||
if (!empty($request->getOrigin())) {
|
||||
Span::add('realtime.origin', $request->getOrigin());
|
||||
}
|
||||
@@ -936,16 +936,16 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
Span::error($th);
|
||||
} finally {
|
||||
Span::add('realtime.success', $success);
|
||||
Span::add('realtime.responseCode', $responseCode);
|
||||
Span::add('realtime.subscriptionMode', $subscriptionMode);
|
||||
Span::add('realtime.channelCount', $channelCount);
|
||||
Span::add('realtime.subscriptionCount', $subscriptionCount);
|
||||
Span::add('realtime.outboundBytes', $outboundBytes);
|
||||
Span::add('realtime.response_code', $responseCode);
|
||||
Span::add('realtime.subscription_mode', $subscriptionMode);
|
||||
Span::add('realtime.channel_count', $channelCount);
|
||||
Span::add('realtime.subscription_count', $subscriptionCount);
|
||||
Span::add('realtime.outbound_bytes', $outboundBytes);
|
||||
if (!empty($project?->getId())) {
|
||||
Span::add('realtime.projectId', $project->getId());
|
||||
Span::add('project.id', $project->getId());
|
||||
}
|
||||
if (!empty($logUser?->getId())) {
|
||||
Span::add('realtime.userId', $logUser->getId());
|
||||
Span::add('user.id', $logUser->getId());
|
||||
}
|
||||
Span::current()?->finish();
|
||||
}
|
||||
@@ -965,9 +965,9 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
$success = false;
|
||||
|
||||
Span::init('realtime.message');
|
||||
Span::add('realtime.connectionId', $connection);
|
||||
Span::add('realtime.inboundBytes', $rawSize);
|
||||
Span::add('realtime.containerId', $containerId);
|
||||
Span::add('realtime.connection.id', $connection);
|
||||
Span::add('realtime.inbound_bytes', $rawSize);
|
||||
Span::add('realtime.container.id', $containerId);
|
||||
|
||||
try {
|
||||
$response = new Response(new SwooleResponse());
|
||||
@@ -1352,15 +1352,15 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
Span::error($th);
|
||||
} finally {
|
||||
Span::add('realtime.success', $success);
|
||||
Span::add('realtime.responseCode', $responseCode);
|
||||
Span::add('realtime.subscriptionDelta', $subscriptionDelta);
|
||||
Span::add('realtime.subscriptionsRequested', $subscriptionsRequested);
|
||||
Span::add('realtime.subscriptionsRemoved', $subscriptionsRemoved);
|
||||
Span::add('realtime.subscribe.subscriptionsCount', $subscriptionsRequested);
|
||||
Span::add('realtime.outboundBytes', $outboundBytes);
|
||||
Span::add('realtime.projectId', $project?->getId() ?? $projectId);
|
||||
Span::add('realtime.userId', $realtime->connections[$connection]['userId'] ?? null);
|
||||
Span::add('realtime.messageType', $messageType);
|
||||
Span::add('realtime.response_code', $responseCode);
|
||||
Span::add('realtime.subscription_delta', $subscriptionDelta);
|
||||
Span::add('realtime.subscriptions_requested', $subscriptionsRequested);
|
||||
Span::add('realtime.subscriptions_removed', $subscriptionsRemoved);
|
||||
Span::add('realtime.subscribe.subscriptions_count', $subscriptionsRequested);
|
||||
Span::add('realtime.outbound_bytes', $outboundBytes);
|
||||
Span::add('project.id', $project?->getId() ?? $projectId);
|
||||
Span::add('user.id', $realtime->connections[$connection]['userId'] ?? null);
|
||||
Span::add('realtime.message_type', $messageType);
|
||||
Span::current()?->finish();
|
||||
}
|
||||
});
|
||||
@@ -1372,7 +1372,7 @@ $server->onClose(function (int $connection) use ($realtime, $stats, $register) {
|
||||
$success = false;
|
||||
|
||||
Span::init('realtime.close');
|
||||
Span::add('realtime.connectionId', $connection);
|
||||
Span::add('realtime.connection.id', $connection);
|
||||
|
||||
if (array_key_exists($connection, $realtime->connections)) {
|
||||
$projectId = $realtime->connections[$connection]['projectId'] ?? null;
|
||||
@@ -1411,12 +1411,12 @@ $server->onClose(function (int $connection) use ($realtime, $stats, $register) {
|
||||
|
||||
Span::add('realtime.success', $success);
|
||||
if (!empty($projectId)) {
|
||||
Span::add('realtime.projectId', $projectId);
|
||||
Span::add('project.id', $projectId);
|
||||
}
|
||||
if (!empty($userId)) {
|
||||
Span::add('realtime.userId', $userId);
|
||||
Span::add('user.id', $userId);
|
||||
}
|
||||
Span::add('realtime.subscriptionsBeforeClose', $subscriptionsBeforeClose);
|
||||
Span::add('realtime.subscriptions_before_close', $subscriptionsBeforeClose);
|
||||
Span::current()?->finish();
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -16,6 +16,7 @@ use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Adapter\Swoole;
|
||||
use Utopia\Queue\Broker\Pool as BrokerPool;
|
||||
use Utopia\Queue\Server;
|
||||
use Utopia\Span\Span;
|
||||
use Utopia\System\System;
|
||||
|
||||
Runtime::enableCoroutine();
|
||||
@@ -91,8 +92,13 @@ $adapter = new Swoole(
|
||||
$worker = new Server($adapter, $container);
|
||||
|
||||
try {
|
||||
$worker->init()->action(function () use ($worker, $registerWorkerMessageResources) {
|
||||
$worker->init()->action(function () use ($worker, $registerWorkerMessageResources, $queueName) {
|
||||
$registerWorkerMessageResources($worker->getContainer());
|
||||
Span::init("worker.{$queueName}");
|
||||
});
|
||||
|
||||
$worker->shutdown()->action(function () {
|
||||
Span::current()?->finish();
|
||||
});
|
||||
|
||||
$container->set('bus', function ($register) use ($worker) {
|
||||
@@ -120,6 +126,8 @@ $worker
|
||||
->action(function (Throwable $error, ?Logger $logger, Log $log, Document $project, Authorization $authorization) use ($queueName) {
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
Span::error($error);
|
||||
|
||||
if ($logger) {
|
||||
$log->setNamespace('appwrite-worker');
|
||||
$log->setServer(System::getEnv('_APP_LOGGING_SERVICE_IDENTIFIER', \gethostname()));
|
||||
|
||||
+8
-8
@@ -54,20 +54,20 @@
|
||||
"utopia-php/abuse": "1.3.*",
|
||||
"utopia-php/agents": "1.2.*",
|
||||
"utopia-php/analytics": "0.15.*",
|
||||
"utopia-php/audit": "2.2.*",
|
||||
"utopia-php/audit": "2.3.*",
|
||||
"utopia-php/auth": "0.5.*",
|
||||
"utopia-php/cache": "1.0.*",
|
||||
"utopia-php/cache": "^2.1",
|
||||
"utopia-php/cli": "0.23.*",
|
||||
"utopia-php/compression": "0.1.*",
|
||||
"utopia-php/config": "1.*",
|
||||
"utopia-php/console": "0.1.*",
|
||||
"utopia-php/database": "5.*",
|
||||
"utopia-php/detector": "0.2.*",
|
||||
"utopia-php/domains": "1.*",
|
||||
"utopia-php/emails": "0.6.*",
|
||||
"utopia-php/dns": "1.6.*",
|
||||
"utopia-php/domains": "2.*",
|
||||
"utopia-php/emails": "0.7.*",
|
||||
"utopia-php/dns": "1.7.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/http": "0.34.*",
|
||||
"utopia-php/http": "^2.0@RC",
|
||||
"utopia-php/fetch": "^1.1",
|
||||
"utopia-php/validators": "0.2.*",
|
||||
"utopia-php/image": "0.8.*",
|
||||
@@ -75,7 +75,7 @@
|
||||
"utopia-php/logger": "0.8.*",
|
||||
"utopia-php/messaging": "0.22.*",
|
||||
"utopia-php/migration": "1.*",
|
||||
"utopia-php/platform": "0.13.*",
|
||||
"utopia-php/platform": "^1.0@RC",
|
||||
"utopia-php/pools": "1.*",
|
||||
"utopia-php/span": "1.1.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
@@ -85,7 +85,7 @@
|
||||
"utopia-php/storage": "2.*",
|
||||
"utopia-php/system": "0.10.*",
|
||||
"utopia-php/telemetry": "0.2.*",
|
||||
"utopia-php/vcs": "3.*",
|
||||
"utopia-php/vcs": "4.*",
|
||||
"utopia-php/websocket": "1.0.*",
|
||||
"matomo/device-detector": "6.4.*",
|
||||
"dragonmantank/cron-expression": "3.4.*",
|
||||
|
||||
Generated
+185
-71
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d58736fec3028d1f9aedd055e6d82684",
|
||||
"content-hash": "9377e1b56bca8dbaf213ee3572ca15c0",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -3510,22 +3510,23 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/audit",
|
||||
"version": "2.2.3",
|
||||
"version": "2.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/audit.git",
|
||||
"reference": "95e9961fa286d2fdb6bf3eaa198f21d51bf58d9c"
|
||||
"reference": "e7b4049fc2ee9be34bcc18771fa593db3b0e9fe3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/95e9961fa286d2fdb6bf3eaa198f21d51bf58d9c",
|
||||
"reference": "95e9961fa286d2fdb6bf3eaa198f21d51bf58d9c",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/e7b4049fc2ee9be34bcc18771fa593db3b0e9fe3",
|
||||
"reference": "e7b4049fc2ee9be34bcc18771fa593db3b0e9fe3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"php": ">=8.4",
|
||||
"utopia-php/database": "5.*",
|
||||
"utopia-php/fetch": "^1.1",
|
||||
"utopia-php/query": "0.1.*",
|
||||
"utopia-php/validators": "0.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -3553,9 +3554,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/audit/issues",
|
||||
"source": "https://github.com/utopia-php/audit/tree/2.2.3"
|
||||
"source": "https://github.com/utopia-php/audit/tree/2.3.2"
|
||||
},
|
||||
"time": "2026-05-08T10:38:23+00:00"
|
||||
"time": "2026-05-14T04:00:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/auth",
|
||||
@@ -3614,23 +3615,24 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cache",
|
||||
"version": "1.0.3",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/cache.git",
|
||||
"reference": "ef52a04e8bfa314c621e3d3326ffcf50db3dfdfa"
|
||||
"reference": "fc3b9ae33c4b83e0e2c91ecf60b4f40fb7ee8f8e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/cache/zipball/ef52a04e8bfa314c621e3d3326ffcf50db3dfdfa",
|
||||
"reference": "ef52a04e8bfa314c621e3d3326ffcf50db3dfdfa",
|
||||
"url": "https://api.github.com/repos/utopia-php/cache/zipball/fc3b9ae33c4b83e0e2c91ecf60b4f40fb7ee8f8e",
|
||||
"reference": "fc3b9ae33c4b83e0e2c91ecf60b4f40fb7ee8f8e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-memcached": "*",
|
||||
"ext-redis": "*",
|
||||
"php": ">=8.0",
|
||||
"php": ">=8.3",
|
||||
"utopia-php/circuit-breaker": "0.3.*",
|
||||
"utopia-php/pools": "1.*",
|
||||
"utopia-php/telemetry": "*"
|
||||
},
|
||||
@@ -3638,6 +3640,7 @@
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpstan/phpstan": "^1.12",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"swoole/ide-helper": "^6.0",
|
||||
"vimeo/psalm": "4.13.1"
|
||||
},
|
||||
"type": "library",
|
||||
@@ -3660,9 +3663,71 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/cache/issues",
|
||||
"source": "https://github.com/utopia-php/cache/tree/1.0.3"
|
||||
"source": "https://github.com/utopia-php/cache/tree/2.1.0"
|
||||
},
|
||||
"time": "2026-05-11T11:02:13+00:00"
|
||||
"time": "2026-05-12T15:03:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/circuit-breaker",
|
||||
"version": "0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/circuit-breaker.git",
|
||||
"reference": "064243c1667778c00abf027ff53a735a228776de"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/circuit-breaker/zipball/064243c1667778c00abf027ff53a735a228776de",
|
||||
"reference": "064243c1667778c00abf027ff53a735a228776de",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.29",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"utopia-php/telemetry": "0.2.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-opentelemetry": "Required by utopia-php/telemetry when using OpenTelemetry metrics.",
|
||||
"ext-protobuf": "Required by utopia-php/telemetry when using OpenTelemetry metrics.",
|
||||
"ext-redis": "Required when using Utopia\\CircuitBreaker\\Adapter\\Redis with the phpredis extension.",
|
||||
"ext-swoole": "Required when using Utopia\\CircuitBreaker\\Adapter\\SwooleTable.",
|
||||
"utopia-php/telemetry": "Required when passing telemetry adapters or running the local telemetry demo."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Utopia\\CircuitBreaker\\": "src/CircuitBreaker"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Team Appwrite",
|
||||
"email": "team@appwrite.io"
|
||||
}
|
||||
],
|
||||
"description": "Light & simple Circuit Breaker for PHP to prevent cascading failures in distributed systems.",
|
||||
"keywords": [
|
||||
"circuit-breaker",
|
||||
"fault-tolerance",
|
||||
"framework",
|
||||
"php",
|
||||
"resilience",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/circuit-breaker/issues",
|
||||
"source": "https://github.com/utopia-php/circuit-breaker/tree/0.3.0"
|
||||
},
|
||||
"time": "2026-05-12T04:27:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cli",
|
||||
@@ -3858,16 +3923,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "5.7.0",
|
||||
"version": "5.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511"
|
||||
"reference": "3391c97318f0e7f94d2c1ea0f7d09e5ba8aad696"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
|
||||
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/3391c97318f0e7f94d2c1ea0f7d09e5ba8aad696",
|
||||
"reference": "3391c97318f0e7f94d2c1ea0f7d09e5ba8aad696",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3876,7 +3941,7 @@
|
||||
"ext-pdo": "*",
|
||||
"ext-redis": "*",
|
||||
"php": ">=8.4",
|
||||
"utopia-php/cache": "1.*",
|
||||
"utopia-php/cache": "^2.0",
|
||||
"utopia-php/console": "0.1.*",
|
||||
"utopia-php/mongo": "1.*",
|
||||
"utopia-php/pools": "1.*",
|
||||
@@ -3912,9 +3977,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/5.7.0"
|
||||
"source": "https://github.com/utopia-php/database/tree/5.8.0"
|
||||
},
|
||||
"time": "2026-05-06T01:04:08+00:00"
|
||||
"time": "2026-05-12T12:52:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
@@ -4014,21 +4079,21 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/dns",
|
||||
"version": "1.6.6",
|
||||
"version": "1.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/dns.git",
|
||||
"reference": "917901ecfe5f09a540e4f689b6cbb80b9f55035d"
|
||||
"reference": "90bf1bc4a51ceca93590d09e7365317b28d1eb89"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/dns/zipball/917901ecfe5f09a540e4f689b6cbb80b9f55035d",
|
||||
"reference": "917901ecfe5f09a540e4f689b6cbb80b9f55035d",
|
||||
"url": "https://api.github.com/repos/utopia-php/dns/zipball/90bf1bc4a51ceca93590d09e7365317b28d1eb89",
|
||||
"reference": "90bf1bc4a51ceca93590d09e7365317b28d1eb89",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.3",
|
||||
"utopia-php/domains": "1.0.*",
|
||||
"utopia-php/domains": "^2.0",
|
||||
"utopia-php/span": "1.1.*",
|
||||
"utopia-php/telemetry": "*",
|
||||
"utopia-php/validators": "0.*"
|
||||
@@ -4065,27 +4130,27 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/dns/issues",
|
||||
"source": "https://github.com/utopia-php/dns/tree/1.6.6"
|
||||
"source": "https://github.com/utopia-php/dns/tree/1.7.0"
|
||||
},
|
||||
"time": "2026-03-27T11:13:50+00:00"
|
||||
"time": "2026-05-13T07:11:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
"version": "1.0.6",
|
||||
"version": "2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/domains.git",
|
||||
"reference": "c87ba0a1da4cbf75d2cff9d3ea0262b78f1d86f6"
|
||||
"reference": "7f76390998359ef67fcea168f614cbd63a4001e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/domains/zipball/c87ba0a1da4cbf75d2cff9d3ea0262b78f1d86f6",
|
||||
"reference": "c87ba0a1da4cbf75d2cff9d3ea0262b78f1d86f6",
|
||||
"url": "https://api.github.com/repos/utopia-php/domains/zipball/7f76390998359ef67fcea168f614cbd63a4001e8",
|
||||
"reference": "7f76390998359ef67fcea168f614cbd63a4001e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"utopia-php/cache": "1.0.*",
|
||||
"utopia-php/cache": "^2.0",
|
||||
"utopia-php/validators": "0.*"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -4127,9 +4192,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/domains/issues",
|
||||
"source": "https://github.com/utopia-php/domains/tree/1.0.6"
|
||||
"source": "https://github.com/utopia-php/domains/tree/2.0.0"
|
||||
},
|
||||
"time": "2026-04-29T11:08:10+00:00"
|
||||
"time": "2026-05-12T12:52:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/dsn",
|
||||
@@ -4180,21 +4245,21 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/emails",
|
||||
"version": "0.6.10",
|
||||
"version": "0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/emails.git",
|
||||
"reference": "2e397754ce68c2ba918564b9f31d9923c0a90429"
|
||||
"reference": "115e24aa908e2b1f06c7ff3b94434a0bdbed9107"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/emails/zipball/2e397754ce68c2ba918564b9f31d9923c0a90429",
|
||||
"reference": "2e397754ce68c2ba918564b9f31d9923c0a90429",
|
||||
"url": "https://api.github.com/repos/utopia-php/emails/zipball/115e24aa908e2b1f06c7ff3b94434a0bdbed9107",
|
||||
"reference": "115e24aa908e2b1f06c7ff3b94434a0bdbed9107",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/domains": "^1.0",
|
||||
"utopia-php/domains": "^2.0",
|
||||
"utopia-php/validators": "0.*"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -4235,9 +4300,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/emails/issues",
|
||||
"source": "https://github.com/utopia-php/emails/tree/0.6.10"
|
||||
"source": "https://github.com/utopia-php/emails/tree/0.7.0"
|
||||
},
|
||||
"time": "2026-05-08T10:16:22+00:00"
|
||||
"time": "2026-05-13T05:01:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/fetch",
|
||||
@@ -4281,16 +4346,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/http",
|
||||
"version": "0.34.25",
|
||||
"version": "2.0.0-rc1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/http.git",
|
||||
"reference": "76be330d4197bae680eb4ccc29c573456fe91904"
|
||||
"reference": "3e3b431d443844c6bf810120dee735f45880856f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/76be330d4197bae680eb4ccc29c573456fe91904",
|
||||
"reference": "76be330d4197bae680eb4ccc29c573456fe91904",
|
||||
"url": "https://api.github.com/repos/utopia-php/http/zipball/3e3b431d443844c6bf810120dee735f45880856f",
|
||||
"reference": "3e3b431d443844c6bf810120dee735f45880856f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4331,9 +4396,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/http/issues",
|
||||
"source": "https://github.com/utopia-php/http/tree/0.34.25"
|
||||
"source": "https://github.com/utopia-php/http/tree/2.0.0-rc1"
|
||||
},
|
||||
"time": "2026-05-05T04:39:15+00:00"
|
||||
"time": "2026-05-05T15:00:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/image",
|
||||
@@ -4657,16 +4722,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/platform",
|
||||
"version": "0.13.2",
|
||||
"version": "1.0.0-rc1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/platform.git",
|
||||
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9"
|
||||
"reference": "36c0a8b2f3d96ca056d724701a302a127111e933"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
|
||||
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
|
||||
"url": "https://api.github.com/repos/utopia-php/platform/zipball/36c0a8b2f3d96ca056d724701a302a127111e933",
|
||||
"reference": "36c0a8b2f3d96ca056d724701a302a127111e933",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4674,7 +4739,7 @@
|
||||
"ext-redis": "*",
|
||||
"php": ">=8.3",
|
||||
"utopia-php/cli": "0.23.3",
|
||||
"utopia-php/http": "0.34.25",
|
||||
"utopia-php/http": "^2.0@RC",
|
||||
"utopia-php/queue": "0.18.2",
|
||||
"utopia-php/servers": "0.4.0"
|
||||
},
|
||||
@@ -4702,9 +4767,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/platform/issues",
|
||||
"source": "https://github.com/utopia-php/platform/tree/0.13.2"
|
||||
"source": "https://github.com/utopia-php/platform/tree/1.0.0-rc1"
|
||||
},
|
||||
"time": "2026-05-05T06:00:26+00:00"
|
||||
"time": "2026-05-05T15:09:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/pools",
|
||||
@@ -4812,6 +4877,52 @@
|
||||
},
|
||||
"time": "2020-10-24T07:04:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/query",
|
||||
"version": "0.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/query.git",
|
||||
"reference": "964a10ed3185490505f4c0062f2eb7b89287fb27"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/query/zipball/964a10ed3185490505f4c0062f2eb7b89287fb27",
|
||||
"reference": "964a10ed3185490505f4c0062f2eb7b89287fb27",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "*",
|
||||
"phpstan/phpstan": "*",
|
||||
"phpunit/phpunit": "^12.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Utopia\\Query\\": "src/Query"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A simple library providing a query abstraction for filtering, ordering, and pagination",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"php",
|
||||
"query",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/query/issues",
|
||||
"source": "https://github.com/utopia-php/query/tree/0.1.1"
|
||||
},
|
||||
"time": "2026-03-03T09:05:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/queue",
|
||||
"version": "0.18.2",
|
||||
@@ -5238,22 +5349,22 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/vcs",
|
||||
"version": "3.2.1",
|
||||
"version": "4.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"reference": "03ccd12b75d67d29094eb760b468fddde4b6b5e5"
|
||||
"reference": "c14ec4d1188e6cc2e8f5256a4b26e531e4f9ac4e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/03ccd12b75d67d29094eb760b468fddde4b6b5e5",
|
||||
"reference": "03ccd12b75d67d29094eb760b468fddde4b6b5e5",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/c14ec4d1188e6cc2e8f5256a4b26e531e4f9ac4e",
|
||||
"reference": "c14ec4d1188e6cc2e8f5256a4b26e531e4f9ac4e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"adhocore/jwt": "^1.1",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cache": "1.0.*",
|
||||
"php": ">=8.2",
|
||||
"utopia-php/cache": "^2.0",
|
||||
"utopia-php/fetch": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -5281,9 +5392,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/vcs/issues",
|
||||
"source": "https://github.com/utopia-php/vcs/tree/3.2.1"
|
||||
"source": "https://github.com/utopia-php/vcs/tree/4.0.0"
|
||||
},
|
||||
"time": "2026-05-08T10:13:53+00:00"
|
||||
"time": "2026-05-13T04:20:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/websocket",
|
||||
@@ -5476,16 +5587,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "1.28.4",
|
||||
"version": "1.29.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "38de925e8c9e7f0f720d45187be54a291aaf696b"
|
||||
"reference": "31248a984a4d478d20a780dda8f5897984ee4e8f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/38de925e8c9e7f0f720d45187be54a291aaf696b",
|
||||
"reference": "38de925e8c9e7f0f720d45187be54a291aaf696b",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/31248a984a4d478d20a780dda8f5897984ee4e8f",
|
||||
"reference": "31248a984a4d478d20a780dda8f5897984ee4e8f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5521,9 +5632,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/1.28.4"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.29.2"
|
||||
},
|
||||
"time": "2026-05-11T13:55:49+00:00"
|
||||
"time": "2026-05-13T04:47:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
@@ -8455,7 +8566,10 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {},
|
||||
"stability-flags": {
|
||||
"utopia-php/http": 5,
|
||||
"utopia-php/platform": 5
|
||||
},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
||||
+1
-1
@@ -255,7 +255,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:7.8.45
|
||||
image: appwrite/console:8
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Delete an analyzer report by its unique ID. Nested insights and CTA metadata are removed asynchronously by the deletes worker.
|
||||
@@ -0,0 +1 @@
|
||||
Get an insight by its unique ID, scoped to its parent report.
|
||||
@@ -0,0 +1 @@
|
||||
Get an analyzer report by its unique ID. The response includes the report's metadata and the nested insights it produced.
|
||||
@@ -0,0 +1 @@
|
||||
List the insights produced under a single analyzer report. You can use the query params to filter your results further.
|
||||
@@ -0,0 +1 @@
|
||||
Get a list of all the project's analyzer reports. You can use the query params to filter your results.
|
||||
@@ -0,0 +1,3 @@
|
||||
The Advisor service provides read access to analyzer reports and their nested insights for a project.
|
||||
|
||||
Use the reports endpoints to list and fetch analyzer runs, then use the insights endpoints to inspect individual findings attached to a report.
|
||||
@@ -38,6 +38,7 @@
|
||||
<directory>./tests/e2e/Services/Messaging</directory>
|
||||
<directory>./tests/e2e/Services/Migrations</directory>
|
||||
<directory>./tests/e2e/Services/Project</directory>
|
||||
<directory>./tests/e2e/Services/Advisor</directory>
|
||||
<file>./tests/e2e/Services/Functions/FunctionsBase.php</file>
|
||||
<file>./tests/e2e/Services/Functions/FunctionsCustomServerTest.php</file>
|
||||
<file>./tests/e2e/Services/Functions/FunctionsCustomClientTest.php</file>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Advisor\Validator;
|
||||
|
||||
use Utopia\Validator;
|
||||
|
||||
class CTAs extends Validator
|
||||
{
|
||||
public const MAX_COUNT_DEFAULT = 16;
|
||||
|
||||
protected string $message = 'Value must be an array of CTA descriptors. Each entry must define `label`, `service`, `method`, and an optional `params` object.';
|
||||
protected array $allowedServices;
|
||||
protected array $allowedMethods;
|
||||
|
||||
public function __construct(
|
||||
protected int $maxCount = self::MAX_COUNT_DEFAULT,
|
||||
?array $allowedServices = null,
|
||||
?array $allowedMethods = null,
|
||||
) {
|
||||
$this->allowedServices = $allowedServices ?? ADVISOR_CTA_SERVICES;
|
||||
$this->allowedMethods = $allowedMethods ?? ADVISOR_CTA_METHODS;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function isArray(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_ARRAY;
|
||||
}
|
||||
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (!\is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\count($value) > $this->maxCount) {
|
||||
$this->message = "A maximum of {$this->maxCount} CTAs are allowed per insight.";
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($value as $entry) {
|
||||
if (!\is_array($entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$maxLengths = ['label' => 256, 'service' => 64, 'method' => 64];
|
||||
foreach ($maxLengths as $required => $maxLength) {
|
||||
if (!isset($entry[$required]) || !\is_string($entry[$required]) || $entry[$required] === '') {
|
||||
return false;
|
||||
}
|
||||
if (\strlen($entry[$required]) > $maxLength) {
|
||||
$this->message = "CTA `{$required}` must not exceed {$maxLength} characters.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->allowedServices) && !\in_array($entry['service'], $this->allowedServices, true)) {
|
||||
$this->message = "CTA `service` must be one of: " . \implode(', ', $this->allowedServices) . '.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($this->allowedMethods) && !\in_array($entry['method'], $this->allowedMethods, true)) {
|
||||
$this->message = "CTA `method` must be one of: " . \implode(', ', $this->allowedMethods) . '.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($entry['params']) && !\is_array($entry['params']) && !\is_object($entry['params'])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ use Appwrite\Event\Publisher\Execution as ExecutionPublisher;
|
||||
use Utopia\Bus\Listener;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Span\Span;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Log extends Listener
|
||||
{
|
||||
@@ -34,20 +33,13 @@ class Log extends Listener
|
||||
{
|
||||
$project = new Document($event->project);
|
||||
$execution = new Document($event->execution);
|
||||
|
||||
if ($execution->getAttribute('resourceType', '') === 'functions') {
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
$resourceId = $execution->getAttribute('resourceId', '');
|
||||
if ($traceProjectId !== '' && $traceFunctionId !== '' && $project->getId() === $traceProjectId && $resourceId === $traceFunctionId) {
|
||||
Span::init('execution.trace.v1_executions_enqueue');
|
||||
Span::add('datetime', gmdate('c'));
|
||||
Span::add('projectId', $project->getId());
|
||||
Span::add('functionId', $resourceId);
|
||||
Span::add('executionId', $execution->getId());
|
||||
Span::add('deploymentId', $execution->getAttribute('deploymentId', ''));
|
||||
Span::add('status', $execution->getAttribute('status', ''));
|
||||
Span::current()?->finish();
|
||||
}
|
||||
Span::add('project.id', $project->getId());
|
||||
Span::add('function.id', $execution->getAttribute('resourceId', ''));
|
||||
Span::add('execution.id', $execution->getId());
|
||||
Span::add('deployment.id', $execution->getAttribute('deploymentId', ''));
|
||||
Span::add('execution.status', $execution->getAttribute('status', ''));
|
||||
}
|
||||
|
||||
$publisherForExecutions->enqueue(new ExecutionMessage(
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event\Message;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
final class Func extends Base
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ?Document $project = null,
|
||||
public readonly ?Document $user = null,
|
||||
public readonly ?string $userId = null,
|
||||
public readonly ?Document $function = null,
|
||||
public readonly ?string $functionId = null,
|
||||
public readonly ?Document $execution = null,
|
||||
public readonly string $type = '',
|
||||
public readonly string $jwt = '',
|
||||
public readonly array $payload = [],
|
||||
public readonly array $events = [],
|
||||
public readonly string $body = '',
|
||||
public readonly string $path = '',
|
||||
public readonly array $headers = [],
|
||||
public readonly string $method = '',
|
||||
public readonly array $platform = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public static function fromEvent(
|
||||
string $event,
|
||||
array $params,
|
||||
?Document $project = null,
|
||||
?Document $user = null,
|
||||
?string $userId = null,
|
||||
array $payload = [],
|
||||
array $platform = [],
|
||||
): static {
|
||||
return new self(
|
||||
project: $project,
|
||||
user: $user,
|
||||
userId: $userId,
|
||||
payload: $payload,
|
||||
events: $event !== '' ? Event::generateEvents($event, $params) : [],
|
||||
platform: $platform,
|
||||
);
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$platform = !empty($this->platform) ? $this->platform : Config::getParam('platform', []);
|
||||
|
||||
return [
|
||||
'project' => $this->project?->getArrayCopy(),
|
||||
'user' => $this->user?->getArrayCopy(),
|
||||
'userId' => $this->userId,
|
||||
'function' => $this->function?->getArrayCopy(),
|
||||
'functionId' => $this->functionId,
|
||||
'execution' => $this->execution?->getArrayCopy(),
|
||||
'type' => $this->type,
|
||||
'jwt' => $this->jwt,
|
||||
'payload' => $this->payload,
|
||||
'events' => $this->events,
|
||||
'body' => $this->body,
|
||||
'path' => $this->path,
|
||||
'headers' => $this->headers,
|
||||
'method' => $this->method,
|
||||
'platform' => $platform,
|
||||
];
|
||||
}
|
||||
|
||||
public static function fromArray(array $data): static
|
||||
{
|
||||
return new self(
|
||||
project: !empty($data['project']) ? new Document($data['project']) : null,
|
||||
user: !empty($data['user']) ? new Document($data['user']) : null,
|
||||
userId: $data['userId'] ?? null,
|
||||
function: !empty($data['function']) ? new Document($data['function']) : null,
|
||||
functionId: $data['functionId'] ?? null,
|
||||
execution: !empty($data['execution']) ? new Document($data['execution']) : null,
|
||||
type: $data['type'] ?? '',
|
||||
jwt: $data['jwt'] ?? '',
|
||||
payload: $data['payload'] ?? [],
|
||||
events: $data['events'] ?? [],
|
||||
body: $data['body'] ?? '',
|
||||
path: $data['path'] ?? '',
|
||||
headers: $data['headers'] ?? [],
|
||||
method: $data['method'] ?? '',
|
||||
platform: $data['platform'] ?? [],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event\Publisher;
|
||||
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Queue\Queue;
|
||||
|
||||
readonly class Func extends Base
|
||||
{
|
||||
public function __construct(
|
||||
Publisher $publisher,
|
||||
protected Queue $queue,
|
||||
) {
|
||||
parent::__construct($publisher);
|
||||
}
|
||||
|
||||
public function enqueue(FunctionMessage $message, ?Queue $queue = null): string|bool
|
||||
{
|
||||
return $this->publish($queue ?? $this->queue, $message);
|
||||
}
|
||||
|
||||
public function getSize(bool $failed = false, ?Queue $queue = null): int
|
||||
{
|
||||
return $this->getQueueSize($queue ?? $this->queue, $failed);
|
||||
}
|
||||
}
|
||||
@@ -406,6 +406,14 @@ class Exception extends \Exception
|
||||
public const string TOKEN_EXPIRED = 'token_expired';
|
||||
public const string TOKEN_RESOURCE_TYPE_INVALID = 'token_resource_type_invalid';
|
||||
|
||||
/** Advisor */
|
||||
public const string INSIGHT_NOT_FOUND = 'insight_not_found';
|
||||
public const string INSIGHT_ALREADY_EXISTS = 'insight_already_exists';
|
||||
|
||||
/** Reports */
|
||||
public const string REPORT_NOT_FOUND = 'report_not_found';
|
||||
public const string REPORT_ALREADY_EXISTS = 'report_already_exists';
|
||||
|
||||
protected string $type = '';
|
||||
protected array $errors = [];
|
||||
protected bool $publish;
|
||||
|
||||
@@ -6,7 +6,6 @@ use Appwrite\GraphQL\Exception as GQLException;
|
||||
use Appwrite\Promises\Swoole;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\DI\Container;
|
||||
use Utopia\Http\Exception;
|
||||
use Utopia\Http\Http;
|
||||
use Utopia\Http\Route;
|
||||
@@ -53,22 +52,6 @@ class Resolvers
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current request container.
|
||||
*/
|
||||
private static function getResolverContainer(Http $utopia): Container
|
||||
{
|
||||
$container = $utopia->getResource('container');
|
||||
|
||||
if ($container instanceof Container || (\is_object($container) && \method_exists($container, 'get') && \method_exists($container, 'set'))) {
|
||||
/** @var Container $container */
|
||||
return $container;
|
||||
}
|
||||
|
||||
/** @var callable(): Container $container */
|
||||
return $container();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request-scoped lock shared by GraphQL resolver coroutines
|
||||
* for the current HTTP request.
|
||||
@@ -95,9 +78,9 @@ class Resolvers
|
||||
?Route $route,
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(function (callable $resolve, callable $reject) use ($utopia, $route, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
$utopia = $utopia->context()->get('utopia:graphql');
|
||||
$request = $utopia->context()->get('request');
|
||||
$response = $utopia->context()->get('response');
|
||||
|
||||
self::resolve(
|
||||
$utopia,
|
||||
@@ -167,9 +150,9 @@ class Resolvers
|
||||
callable $url,
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
$utopia = $utopia->context()->get('utopia:graphql');
|
||||
$request = $utopia->context()->get('request');
|
||||
$response = $utopia->context()->get('response');
|
||||
|
||||
self::resolve(
|
||||
$utopia,
|
||||
@@ -203,9 +186,9 @@ class Resolvers
|
||||
callable $params,
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
$utopia = $utopia->context()->get('utopia:graphql');
|
||||
$request = $utopia->context()->get('request');
|
||||
$response = $utopia->context()->get('response');
|
||||
|
||||
$beforeResolve = function ($payload) {
|
||||
return $payload['documents'];
|
||||
@@ -245,9 +228,9 @@ class Resolvers
|
||||
callable $params,
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
$utopia = $utopia->context()->get('utopia:graphql');
|
||||
$request = $utopia->context()->get('request');
|
||||
$response = $utopia->context()->get('response');
|
||||
|
||||
self::resolve(
|
||||
$utopia,
|
||||
@@ -282,9 +265,9 @@ class Resolvers
|
||||
callable $params,
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
$utopia = $utopia->context()->get('utopia:graphql');
|
||||
$request = $utopia->context()->get('request');
|
||||
$response = $utopia->context()->get('response');
|
||||
|
||||
self::resolve(
|
||||
$utopia,
|
||||
@@ -317,9 +300,9 @@ class Resolvers
|
||||
callable $url,
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $args) {
|
||||
$utopia = $utopia->getResource('utopia:graphql');
|
||||
$request = $utopia->getResource('request');
|
||||
$response = $utopia->getResource('response');
|
||||
$utopia = $utopia->context()->get('utopia:graphql');
|
||||
$request = $utopia->context()->get('request');
|
||||
$response = $utopia->context()->get('response');
|
||||
|
||||
self::resolve(
|
||||
$utopia,
|
||||
@@ -373,10 +356,9 @@ class Resolvers
|
||||
}
|
||||
|
||||
/** @var Response $resolverResponse */
|
||||
$resolverResponse = clone $utopia->getResource('response');
|
||||
$container = self::getResolverContainer($utopia);
|
||||
$container->set('request', static fn () => $request);
|
||||
$container->set('response', static fn () => $resolverResponse);
|
||||
$resolverResponse = clone $utopia->context()->get('response');
|
||||
$utopia->context()->set('request', static fn () => $request);
|
||||
$utopia->context()->set('response', static fn () => $resolverResponse);
|
||||
$resolverResponse->setContentType(Response::CONTENT_TYPE_NULL);
|
||||
$resolverResponse->setSent(false);
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class Schema
|
||||
protected static function api(Http $utopia, callable $complexity): array
|
||||
{
|
||||
Mapper::init($utopia
|
||||
->getResource('response')
|
||||
->context()->get('response')
|
||||
->getModels());
|
||||
|
||||
$queries = [];
|
||||
|
||||
@@ -254,7 +254,7 @@ class Mapper
|
||||
array $injections
|
||||
): Type {
|
||||
$validator = \is_callable($validator)
|
||||
? \call_user_func_array($validator, $utopia->getResources($injections))
|
||||
? \call_user_func_array($validator, \array_map($utopia->context()->get(...), $injections))
|
||||
: $validator;
|
||||
|
||||
$isNullable = $validator instanceof Nullable;
|
||||
|
||||
@@ -774,6 +774,21 @@ class Realtime extends MessagingAdapter
|
||||
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
|
||||
}
|
||||
break;
|
||||
case 'reports':
|
||||
// Plain report event: `reports.{reportId}.{action}`
|
||||
$channels[] = 'reports';
|
||||
if (isset($parts[1])) {
|
||||
$channels[] = 'reports.' . $parts[1];
|
||||
}
|
||||
// Nested insight event: `reports.{reportId}.insights.{insightId}.{action}`
|
||||
if (isset($parts[2]) && $parts[2] === 'insights') {
|
||||
$channels[] = 'reports.' . $parts[1] . '.insights';
|
||||
if (isset($parts[3])) {
|
||||
$channels[] = 'reports.' . $parts[1] . '.insights.' . $parts[3];
|
||||
}
|
||||
}
|
||||
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
|
||||
break;
|
||||
}
|
||||
|
||||
// Action is the last segment for plain CRUD events (e.g. `documents.X.create`),
|
||||
|
||||
@@ -97,6 +97,7 @@ abstract class Migration
|
||||
'1.9.2' => 'V24',
|
||||
'1.9.3' => 'V24',
|
||||
'1.9.4' => 'V24',
|
||||
'1.9.5' => 'V24',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Appwrite\Platform;
|
||||
|
||||
use Appwrite\Platform\Modules\Account;
|
||||
use Appwrite\Platform\Modules\Advisor;
|
||||
use Appwrite\Platform\Modules\Avatars;
|
||||
use Appwrite\Platform\Modules\Console;
|
||||
use Appwrite\Platform\Modules\Core;
|
||||
@@ -42,5 +43,6 @@ class Appwrite extends Platform
|
||||
$this->addModule(new Webhooks\Module());
|
||||
$this->addModule(new Migrations\Module());
|
||||
$this->addModule(new Project\Module());
|
||||
$this->addModule(new Advisor\Module());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ class Server
|
||||
|
||||
$nativeServer = $adapter->getNativeServer();
|
||||
|
||||
$container = $adapter->getContainer();
|
||||
$container = $adapter->resources();
|
||||
$container->set('installerState', fn () => $state);
|
||||
$container->set('installerConfig', fn () => $config);
|
||||
$container->set('installerPaths', fn () => $paths);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Enums;
|
||||
|
||||
enum InsightCTAMethod: string
|
||||
{
|
||||
case CREATE_INDEX = 'createIndex';
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Enums;
|
||||
|
||||
enum InsightCTAService: string
|
||||
{
|
||||
case DATABASES = 'databases';
|
||||
case TABLES_DB = 'tablesDB';
|
||||
case DOCUMENTS_DB = 'documentsDB';
|
||||
case VECTORS_DB = 'vectorsDB';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Enums;
|
||||
|
||||
enum InsightSeverity: string
|
||||
{
|
||||
case INFO = 'info';
|
||||
case WARNING = 'warning';
|
||||
case CRITICAL = 'critical';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Enums;
|
||||
|
||||
enum InsightStatus: string
|
||||
{
|
||||
case ACTIVE = 'active';
|
||||
case DISMISSED = 'dismissed';
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Enums;
|
||||
|
||||
enum InsightType: string
|
||||
{
|
||||
case DATABASE_INDEX = 'databaseIndex';
|
||||
case TABLES_DB_INDEX = 'tablesDBIndex';
|
||||
case DOCUMENTS_DB_INDEX = 'documentsDBIndex';
|
||||
case VECTORS_DB_INDEX = 'vectorsDBIndex';
|
||||
case DATABASE_PERFORMANCE = 'databasePerformance';
|
||||
case SITE_PERFORMANCE = 'sitePerformance';
|
||||
case SITE_ACCESSIBILITY = 'siteAccessibility';
|
||||
case SITE_SEO = 'siteSeo';
|
||||
case FUNCTION_PERFORMANCE = 'functionPerformance';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Enums;
|
||||
|
||||
enum ReportType: string
|
||||
{
|
||||
case LIGHTHOUSE = 'lighthouse';
|
||||
case AUDIT = 'audit';
|
||||
case DATABASE_ANALYZER = 'databaseAnalyzer';
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Http\Insights;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getInsight';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/reports/:reportId/insights/:insightId')
|
||||
->desc('Get insight')
|
||||
->groups(['api', 'advisor'])
|
||||
->label('scope', 'insights.read')
|
||||
->label('resourceType', RESOURCE_TYPE_INSIGHTS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'advisor',
|
||||
group: 'insights',
|
||||
name: 'getInsight',
|
||||
description: '/docs/references/advisor/get-insight.md',
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_INSIGHT,
|
||||
),
|
||||
]
|
||||
))
|
||||
->param('reportId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Parent report ID.', false, ['dbForPlatform'])
|
||||
->param('insightId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Insight ID.', false, ['dbForPlatform'])
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $reportId,
|
||||
string $insightId,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
// Skip the insights subquery — we only need ownership metadata.
|
||||
$report = $dbForPlatform->skipFilters(
|
||||
fn () => $dbForPlatform->getDocument('reports', $reportId),
|
||||
['subQueryReportInsights'],
|
||||
);
|
||||
|
||||
if ($report->isEmpty() || $report->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::REPORT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$insight = $dbForPlatform->getDocument('insights', $insightId);
|
||||
|
||||
if (
|
||||
$insight->isEmpty()
|
||||
|| $insight->getAttribute('projectInternalId') !== $project->getSequence()
|
||||
|| $insight->getAttribute('reportInternalId') !== $report->getSequence()
|
||||
) {
|
||||
throw new Exception(Exception::INSIGHT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($insight, Response::MODEL_INSIGHT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Http\Insights;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Insights;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
class XList extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'listInsights';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/reports/:reportId/insights')
|
||||
->desc('List insights')
|
||||
->groups(['api', 'advisor'])
|
||||
->label('scope', 'insights.read')
|
||||
->label('resourceType', RESOURCE_TYPE_INSIGHTS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'advisor',
|
||||
group: 'insights',
|
||||
name: 'listInsights',
|
||||
description: '/docs/references/advisor/list-insights.md',
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_INSIGHT_LIST,
|
||||
),
|
||||
]
|
||||
))
|
||||
->param('reportId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Parent report ID.', false, ['dbForPlatform'])
|
||||
->param('queries', [], new Insights(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Insights::ALLOWED_ATTRIBUTES), true)
|
||||
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $reportId,
|
||||
array $queries,
|
||||
bool $includeTotal,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
// Skip the insights subquery — we're about to fetch a filtered, paginated slice ourselves.
|
||||
$report = $dbForPlatform->skipFilters(
|
||||
fn () => $dbForPlatform->getDocument('reports', $reportId),
|
||||
['subQueryReportInsights'],
|
||||
);
|
||||
|
||||
if ($report->isEmpty() || $report->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::REPORT_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getSequence()]);
|
||||
$queries[] = Query::equal('reportInternalId', [$report->getSequence()]);
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$insightId = $cursor->getValue();
|
||||
$cursorDocument = $dbForPlatform->getDocument('insights', $insightId);
|
||||
|
||||
if (
|
||||
$cursorDocument->isEmpty()
|
||||
|| $cursorDocument->getAttribute('projectInternalId') !== $project->getSequence()
|
||||
|| $cursorDocument->getAttribute('reportInternalId') !== $report->getSequence()
|
||||
) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Insight '{$insightId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$insights = $dbForPlatform->find('insights', $queries);
|
||||
$total = $includeTotal ? $dbForPlatform->count('insights', $filterQueries, APP_LIMIT_COUNT) : 0;
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'insights' => $insights,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_INSIGHT_LIST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Http\Reports;
|
||||
|
||||
use Appwrite\Event\Delete as DeleteEvent;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
class Delete extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'deleteReport';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE)
|
||||
->setHttpPath('/v1/reports/:reportId')
|
||||
->desc('Delete report')
|
||||
->groups(['api', 'advisor'])
|
||||
->label('scope', 'reports.write')
|
||||
->label('event', 'reports.[reportId].delete')
|
||||
->label('resourceType', RESOURCE_TYPE_REPORTS)
|
||||
->label('audits.event', 'report.delete')
|
||||
->label('audits.resource', 'report/{request.reportId}')
|
||||
->label('abuse-key', 'projectId:{projectId},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'advisor',
|
||||
group: 'reports',
|
||||
name: 'deleteReport',
|
||||
description: '/docs/references/advisor/delete-report.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_NOCONTENT,
|
||||
model: Response::MODEL_NONE,
|
||||
),
|
||||
],
|
||||
contentType: ContentType::NONE
|
||||
))
|
||||
->param('reportId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Report ID.', false, ['dbForPlatform'])
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->inject('queueForDeletes')
|
||||
->inject('queueForEvents')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $reportId,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform,
|
||||
DeleteEvent $queueForDeletes,
|
||||
Event $queueForEvents
|
||||
): void {
|
||||
$report = $dbForPlatform->skipFilters(
|
||||
fn () => $dbForPlatform->getDocument('reports', $reportId),
|
||||
['subQueryReportInsights'],
|
||||
);
|
||||
|
||||
if ($report->isEmpty() || $report->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::REPORT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$dbForPlatform->deleteDocument('reports', $report->getId())) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove report from DB');
|
||||
}
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_REPORT)
|
||||
->setDocument($report);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('reportId', $report->getId())
|
||||
->setPayload($response->output($report, Response::MODEL_REPORT));
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Http\Reports;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getReport';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/reports/:reportId')
|
||||
->desc('Get report')
|
||||
->groups(['api', 'advisor'])
|
||||
->label('scope', 'reports.read')
|
||||
->label('resourceType', RESOURCE_TYPE_REPORTS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'advisor',
|
||||
group: 'reports',
|
||||
name: 'getReport',
|
||||
description: '/docs/references/advisor/get-report.md',
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_REPORT,
|
||||
),
|
||||
]
|
||||
))
|
||||
->param('reportId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Report ID.', false, ['dbForPlatform'])
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $reportId,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
$report = $dbForPlatform->skipFilters(
|
||||
fn () => $dbForPlatform->getDocument('reports', $reportId),
|
||||
['subQueryReportInsights'],
|
||||
);
|
||||
|
||||
if ($report->isEmpty() || $report->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::REPORT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$insights = $dbForPlatform->find('insights', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::equal('reportInternalId', [$report->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
|
||||
$report->setAttribute('insights', $insights);
|
||||
|
||||
$response->dynamic($report, Response::MODEL_REPORT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Http\Reports;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Reports;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
||||
class XList extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'listReports';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/reports')
|
||||
->desc('List reports')
|
||||
->groups(['api', 'advisor'])
|
||||
->label('scope', 'reports.read')
|
||||
->label('resourceType', RESOURCE_TYPE_REPORTS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'advisor',
|
||||
group: 'reports',
|
||||
name: 'listReports',
|
||||
description: '/docs/references/advisor/list-reports.md',
|
||||
auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_REPORT_LIST,
|
||||
),
|
||||
]
|
||||
))
|
||||
->param('queries', [], new Reports(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Reports::ALLOWED_ATTRIBUTES), true)
|
||||
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
array $queries,
|
||||
bool $includeTotal,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getSequence()]);
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$reportId = $cursor->getValue();
|
||||
$cursorDocument = $dbForPlatform->skipFilters(
|
||||
fn () => $dbForPlatform->getDocument('reports', $reportId),
|
||||
['subQueryReportInsights'],
|
||||
);
|
||||
|
||||
if ($cursorDocument->isEmpty() || $cursorDocument->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Report '{$reportId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$reports = $dbForPlatform->skipFilters(
|
||||
fn () => $dbForPlatform->find('reports', $queries),
|
||||
['subQueryReportInsights'],
|
||||
);
|
||||
$total = $includeTotal ? $dbForPlatform->count('reports', $filterQueries, APP_LIMIT_COUNT) : 0;
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
if (!empty($reports)) {
|
||||
$reportSequences = \array_map(fn (Document $r) => $r->getSequence(), $reports);
|
||||
|
||||
$insights = $dbForPlatform->find('insights', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::equal('reportInternalId', $reportSequences),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
|
||||
$insightsByReport = [];
|
||||
foreach ($insights as $insight) {
|
||||
$insightsByReport[$insight->getAttribute('reportInternalId')][] = $insight;
|
||||
}
|
||||
|
||||
foreach ($reports as $report) {
|
||||
$report->setAttribute('insights', $insightsByReport[$report->getSequence()] ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'reports' => $reports,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_REPORT_LIST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor;
|
||||
|
||||
use Appwrite\Platform\Modules\Advisor\Services\Http;
|
||||
use Utopia\Platform;
|
||||
|
||||
class Module extends Platform\Module
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addService('http', new Http());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Advisor\Services;
|
||||
|
||||
use Appwrite\Platform\Modules\Advisor\Http\Insights\Get as GetInsight;
|
||||
use Appwrite\Platform\Modules\Advisor\Http\Insights\XList as ListInsights;
|
||||
use Appwrite\Platform\Modules\Advisor\Http\Reports\Delete as DeleteReport;
|
||||
use Appwrite\Platform\Modules\Advisor\Http\Reports\Get as GetReport;
|
||||
use Appwrite\Platform\Modules\Advisor\Http\Reports\XList as ListReports;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
class Http extends Service
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->type = Service::TYPE_HTTP;
|
||||
|
||||
$this->addAction(GetReport::getName(), new GetReport());
|
||||
$this->addAction(ListReports::getName(), new ListReports());
|
||||
$this->addAction(DeleteReport::getName(), new DeleteReport());
|
||||
|
||||
$this->addAction(GetInsight::getName(), new GetInsight());
|
||||
$this->addAction(ListInsights::getName(), new ListInsights());
|
||||
}
|
||||
}
|
||||
+13
-6
@@ -3,6 +3,8 @@
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Functions\EventProcessor;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Action as DatabasesAction;
|
||||
@@ -421,7 +423,7 @@ abstract class Action extends DatabasesAction
|
||||
* @param Document[] $documents
|
||||
* @param Event $queueForEvents
|
||||
* @param Event $queueForRealtime
|
||||
* @param Event $queueForFunctions
|
||||
* @param FunctionPublisher $publisherForFunctions
|
||||
* @param Event $queueForWebhooks
|
||||
* @param Database $dbForProject
|
||||
* @param EventProcessor $eventProcessor
|
||||
@@ -434,7 +436,7 @@ abstract class Action extends DatabasesAction
|
||||
array $documents,
|
||||
Event $queueForEvents,
|
||||
Event $queueForRealtime,
|
||||
Event $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Event $queueForWebhooks,
|
||||
Database $dbForProject,
|
||||
EventProcessor $eventProcessor
|
||||
@@ -472,9 +474,15 @@ abstract class Action extends DatabasesAction
|
||||
if (!empty($functionsEvents)) {
|
||||
foreach ($generatedEvents as $event) {
|
||||
if (isset($functionsEvents[$event])) {
|
||||
$queueForFunctions
|
||||
->from($queueForEvents)
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $queueForEvents->getEvent(),
|
||||
params: $queueForEvents->getParams(),
|
||||
project: $queueForEvents->getProject(),
|
||||
user: $queueForEvents->getUser(),
|
||||
userId: $queueForEvents->getUserId(),
|
||||
payload: $queueForEvents->getPayload(),
|
||||
platform: $queueForEvents->getPlatform(),
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -494,7 +502,6 @@ abstract class Action extends DatabasesAction
|
||||
|
||||
$queueForEvents->reset();
|
||||
$queueForRealtime->reset();
|
||||
$queueForFunctions->reset();
|
||||
$queueForWebhooks->reset();
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -3,6 +3,7 @@
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Functions\EventProcessor;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Action;
|
||||
@@ -80,14 +81,14 @@ class Delete extends Action
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, EventProcessor $eventProcessor): void
|
||||
public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, Event $queueForEvents, Event $queueForRealtime, FunctionPublisher $publisherForFunctions, Event $queueForWebhooks, array $plan, EventProcessor $eventProcessor): void
|
||||
{
|
||||
$database = $dbForProject->getDocument('databases', $databaseId);
|
||||
if ($database->isEmpty()) {
|
||||
@@ -206,7 +207,7 @@ class Delete extends Action
|
||||
$documents,
|
||||
$queueForEvents,
|
||||
$queueForRealtime,
|
||||
$queueForFunctions,
|
||||
$publisherForFunctions,
|
||||
$queueForWebhooks,
|
||||
$dbForProject,
|
||||
$eventProcessor
|
||||
|
||||
+4
-3
@@ -3,6 +3,7 @@
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Functions\EventProcessor;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Action;
|
||||
@@ -84,14 +85,14 @@ class Update extends Action
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, string|array $data, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, EventProcessor $eventProcessor): void
|
||||
public function action(string $databaseId, string $collectionId, string|array $data, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, Event $queueForEvents, Event $queueForRealtime, FunctionPublisher $publisherForFunctions, Event $queueForWebhooks, array $plan, EventProcessor $eventProcessor): void
|
||||
{
|
||||
$data = \is_string($data)
|
||||
? \json_decode($data, true)
|
||||
@@ -237,7 +238,7 @@ class Update extends Action
|
||||
$documents,
|
||||
$queueForEvents,
|
||||
$queueForRealtime,
|
||||
$queueForFunctions,
|
||||
$publisherForFunctions,
|
||||
$queueForWebhooks,
|
||||
$dbForProject,
|
||||
$eventProcessor
|
||||
|
||||
+4
-3
@@ -3,6 +3,7 @@
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Functions\EventProcessor;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Action;
|
||||
@@ -82,14 +83,14 @@ class Upsert extends Action
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, EventProcessor $eventProcessor): void
|
||||
public function action(string $databaseId, string $collectionId, array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, Context $usage, Event $queueForEvents, Event $queueForRealtime, FunctionPublisher $publisherForFunctions, Event $queueForWebhooks, array $plan, EventProcessor $eventProcessor): void
|
||||
{
|
||||
$database = $dbForProject->getDocument('databases', $databaseId);
|
||||
if ($database->isEmpty()) {
|
||||
@@ -212,7 +213,7 @@ class Upsert extends Action
|
||||
$upserted,
|
||||
$queueForEvents,
|
||||
$queueForRealtime,
|
||||
$queueForFunctions,
|
||||
$publisherForFunctions,
|
||||
$queueForWebhooks,
|
||||
$dbForProject,
|
||||
$eventProcessor
|
||||
|
||||
+4
-3
@@ -3,6 +3,7 @@
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Functions\EventProcessor;
|
||||
use Appwrite\SDK\AuthType;
|
||||
@@ -137,7 +138,7 @@ class Create extends Action
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('authorization')
|
||||
@@ -145,7 +146,7 @@ class Create extends Action
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, User $user, Event $queueForEvents, Context $usage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization, EventProcessor $eventProcessor): void
|
||||
public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, User $user, Event $queueForEvents, Context $usage, Event $queueForRealtime, FunctionPublisher $publisherForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization, EventProcessor $eventProcessor): void
|
||||
{
|
||||
$data = \is_string($data)
|
||||
? \json_decode($data, true)
|
||||
@@ -517,7 +518,7 @@ class Create extends Action
|
||||
$created,
|
||||
$queueForEvents,
|
||||
$queueForRealtime,
|
||||
$queueForFunctions,
|
||||
$publisherForFunctions,
|
||||
$queueForWebhooks,
|
||||
$dbForProject,
|
||||
$eventProcessor
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions;
|
||||
use Appwrite\Databases\TransactionState;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Functions\EventProcessor;
|
||||
use Appwrite\SDK\AuthType;
|
||||
@@ -77,7 +79,7 @@ class Update extends Action
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('authorization')
|
||||
->inject('eventProcessor')
|
||||
@@ -97,7 +99,7 @@ class Update extends Action
|
||||
* @param Event $queueForEvents
|
||||
* @param Context $usage
|
||||
* @param Event $queueForRealtime
|
||||
* @param Event $queueForFunctions
|
||||
* @param FunctionPublisher $publisherForFunctions
|
||||
* @param Event $queueForWebhooks
|
||||
* @param EventProcessor $eventProcessor
|
||||
* @return void
|
||||
@@ -108,7 +110,7 @@ class Update extends Action
|
||||
* @throws StructureException
|
||||
* @throws \Utopia\Http\Exception
|
||||
*/
|
||||
public function action(string $transactionId, bool $commit, bool $rollback, Document $project, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, User $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, Context $usage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, Authorization $authorization, EventProcessor $eventProcessor): void
|
||||
public function action(string $transactionId, bool $commit, bool $rollback, Document $project, UtopiaResponse $response, Database $dbForProject, callable $getDatabasesDB, User $user, TransactionState $transactionState, Delete $queueForDeletes, Event $queueForEvents, Context $usage, Event $queueForRealtime, FunctionPublisher $publisherForFunctions, Event $queueForWebhooks, Authorization $authorization, EventProcessor $eventProcessor): void
|
||||
{
|
||||
if (!$commit && !$rollback) {
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Either commit or rollback must be true');
|
||||
@@ -461,7 +463,15 @@ class Update extends Action
|
||||
if (!empty($functionsEvents)) {
|
||||
foreach ($generatedEvents as $event) {
|
||||
if (isset($functionsEvents[$event])) {
|
||||
$queueForFunctions->from($queueForEvents)->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $queueForEvents->getEvent(),
|
||||
params: $queueForEvents->getParams(),
|
||||
project: $queueForEvents->getProject(),
|
||||
user: $queueForEvents->getUser(),
|
||||
userId: $queueForEvents->getUserId(),
|
||||
payload: $queueForEvents->getPayload(),
|
||||
platform: $queueForEvents->getPlatform(),
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -480,7 +490,6 @@ class Update extends Action
|
||||
|
||||
$queueForEvents->reset();
|
||||
$queueForRealtime->reset();
|
||||
$queueForFunctions->reset();
|
||||
$queueForWebhooks->reset();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class Delete extends DocumentsDelete
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ class Update extends DocumentsUpdate
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ class Upsert extends DocumentsUpsert
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ class Create extends DocumentCreate
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('authorization')
|
||||
|
||||
@@ -60,7 +60,7 @@ class Update extends TransactionsUpdate
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('authorization')
|
||||
->inject('eventProcessor')
|
||||
|
||||
@@ -65,7 +65,7 @@ class Delete extends DocumentsDelete
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
@@ -67,7 +67,7 @@ class Update extends DocumentsUpdate
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
@@ -67,7 +67,7 @@ class Upsert extends DocumentsUpsert
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
@@ -109,7 +109,7 @@ class Create extends DocumentCreate
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('authorization')
|
||||
|
||||
@@ -61,7 +61,7 @@ class Update extends TransactionsUpdate
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('authorization')
|
||||
->inject('eventProcessor')
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class Delete extends DocumentsDelete
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ class Update extends DocumentsUpdate
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ class Upsert extends DocumentsUpsert
|
||||
->inject('usage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('eventProcessor')
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ class Create extends DocumentCreate
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('plan')
|
||||
->inject('authorization')
|
||||
|
||||
@@ -60,7 +60,7 @@ class Update extends TransactionsUpdate
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('authorization')
|
||||
->inject('eventProcessor')
|
||||
|
||||
@@ -5,7 +5,8 @@ namespace Appwrite\Platform\Modules\Functions\Http\Executions;
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Event\Delete as DeleteEvent;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Functions\Validator\Headers;
|
||||
@@ -95,7 +96,7 @@ class Create extends Base
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('usage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('geodb')
|
||||
->inject('store')
|
||||
->inject('proofForToken')
|
||||
@@ -123,7 +124,7 @@ class Create extends Base
|
||||
User $user,
|
||||
Event $queueForEvents,
|
||||
Context $usage,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Reader $geodb,
|
||||
Store $store,
|
||||
Token $proofForToken,
|
||||
@@ -294,20 +295,19 @@ class Create extends Base
|
||||
if ($async) {
|
||||
if (is_null($scheduledAt)) {
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
$queueForFunctions
|
||||
->setType('http')
|
||||
->setExecution($execution)
|
||||
->setFunction($function)
|
||||
->setBody($body)
|
||||
->setHeaders($headers)
|
||||
->setPath($path)
|
||||
->setMethod($method)
|
||||
->setJWT($jwt)
|
||||
->setProject($project)
|
||||
->setUser($user)
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('executionId', $execution->getId())
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(new FunctionMessage(
|
||||
project: $project,
|
||||
user: $user,
|
||||
function: $function,
|
||||
functionId: $function->getId(),
|
||||
execution: $execution,
|
||||
type: 'http',
|
||||
jwt: $jwt,
|
||||
body: $body,
|
||||
path: $path,
|
||||
headers: $headers,
|
||||
method: $method,
|
||||
));
|
||||
} else {
|
||||
$data = [
|
||||
'headers' => $headers,
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Functions;
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Build as BuildMessage;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Build as BuildPublisher;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\Validator\FunctionEvent;
|
||||
use Appwrite\Event\Webhook;
|
||||
@@ -119,7 +120,7 @@ class Create extends Base
|
||||
->inject('publisherForBuilds')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('dbForPlatform')
|
||||
->inject('request')
|
||||
->inject('gitHub')
|
||||
@@ -161,7 +162,7 @@ class Create extends Base
|
||||
BuildPublisher $publisherForBuilds,
|
||||
Realtime $queueForRealtime,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Database $dbForPlatform,
|
||||
Request $request,
|
||||
GitHub $github,
|
||||
@@ -423,9 +424,15 @@ class Create extends Base
|
||||
->trigger();
|
||||
|
||||
/** Trigger Functions */
|
||||
$queueForFunctions
|
||||
->from($ruleCreate)
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $ruleCreate->getEvent(),
|
||||
params: $ruleCreate->getParams(),
|
||||
project: $ruleCreate->getProject(),
|
||||
user: $ruleCreate->getUser(),
|
||||
userId: $ruleCreate->getUserId(),
|
||||
payload: $ruleCreate->getPayload(),
|
||||
platform: $ruleCreate->getPlatform(),
|
||||
));
|
||||
|
||||
/** Trigger Realtime Events */
|
||||
$queueForRealtime
|
||||
|
||||
@@ -287,7 +287,29 @@ class Update extends Base
|
||||
}
|
||||
|
||||
// Inform scheduler if function is still active
|
||||
$schedule = $dbForPlatform->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule = $authorization->skip(fn () => $dbForPlatform->getDocument('schedules', $function->getAttribute('scheduleId')));
|
||||
|
||||
// Re-create schedule if missing
|
||||
if ($schedule->isEmpty()) {
|
||||
$schedule = $authorization->skip(
|
||||
fn () => $dbForPlatform->createDocument('schedules', new Document([
|
||||
'region' => $project->getAttribute('region'),
|
||||
'resourceType' => SCHEDULE_RESOURCE_TYPE_FUNCTION,
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceInternalId' => $function->getSequence(),
|
||||
'resourceUpdatedAt' => DateTime::now(),
|
||||
'projectId' => $project->getId(),
|
||||
'schedule' => $function->getAttribute('schedule'),
|
||||
'active' => false,
|
||||
]))
|
||||
);
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'scheduleId' => $schedule->getId(),
|
||||
'scheduleInternalId' => $schedule->getSequence(),
|
||||
]));
|
||||
}
|
||||
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
|
||||
@@ -4,8 +4,9 @@ namespace Appwrite\Platform\Modules\Functions\Workers;
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Message\Usage as UsageMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Publisher\Screenshot;
|
||||
use Appwrite\Event\Publisher\Usage as UsagePublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
@@ -63,7 +64,7 @@ class Builds extends Action
|
||||
->inject('queueForEvents')
|
||||
->inject('publisherForScreenshots')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForRealtime')
|
||||
->inject('usage')
|
||||
->inject('publisherForUsage')
|
||||
@@ -89,7 +90,7 @@ class Builds extends Action
|
||||
Event $queueForEvents,
|
||||
Screenshot $publisherForScreenshots,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime,
|
||||
Context $usage,
|
||||
UsagePublisher $publisherForUsage,
|
||||
@@ -131,7 +132,7 @@ class Builds extends Action
|
||||
$deviceForFiles,
|
||||
$publisherForScreenshots,
|
||||
$queueForWebhooks,
|
||||
$queueForFunctions,
|
||||
$publisherForFunctions,
|
||||
$queueForRealtime,
|
||||
$queueForEvents,
|
||||
$usage,
|
||||
@@ -167,7 +168,7 @@ class Builds extends Action
|
||||
Device $deviceForFiles,
|
||||
Screenshot $publisherForScreenshots,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime,
|
||||
Event $queueForEvents,
|
||||
Context $usage,
|
||||
@@ -186,11 +187,11 @@ class Builds extends Action
|
||||
array $platform,
|
||||
int $timeout
|
||||
): void {
|
||||
Span::add('projectId', $project->getId());
|
||||
Span::add('resourceId', $resource->getId());
|
||||
Span::add('resourceType', $resource->getCollection());
|
||||
Span::add('deploymentId', $deployment->getId());
|
||||
Span::add('timeout', $timeout);
|
||||
Span::add('project.id', $project->getId());
|
||||
Span::add('resource.id', $resource->getId());
|
||||
Span::add('resource.type', $resource->getCollection());
|
||||
Span::add('deployment.id', $deployment->getId());
|
||||
Span::add('build.timeout', $timeout);
|
||||
|
||||
Console::info('Deployment action started');
|
||||
|
||||
@@ -232,12 +233,12 @@ class Builds extends Action
|
||||
|
||||
$version = $this->getVersion($resource);
|
||||
$runtime = $this->getRuntime($resource, $version);
|
||||
Span::add('runtime', $resource->getAttribute($resource->getCollection() === 'sites' ? 'buildRuntime' : 'runtime', ''));
|
||||
Span::add('version', $version);
|
||||
Span::add('build.runtime', $resource->getAttribute($resource->getCollection() === 'sites' ? 'buildRuntime' : 'runtime', ''));
|
||||
Span::add('build.version', $version);
|
||||
|
||||
$spec = Config::getParam('specifications')[$resource->getAttribute('buildSpecification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
|
||||
Span::add('cpus', (float) ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT));
|
||||
Span::add('memory', (int) ($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT));
|
||||
Span::add('build.cpus', (float) ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT));
|
||||
Span::add('build.memory', (int) ($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT));
|
||||
|
||||
// Realtime preparation
|
||||
$event = "{$resource->getCollection()}.[{$resourceKey}].deployments.[deploymentId].update";
|
||||
@@ -570,9 +571,15 @@ class Builds extends Action
|
||||
->trigger();
|
||||
|
||||
/** Trigger Functions */
|
||||
$queueForFunctions
|
||||
->from($deploymentUpdate)
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $deploymentUpdate->getEvent(),
|
||||
params: $deploymentUpdate->getParams(),
|
||||
project: $deploymentUpdate->getProject(),
|
||||
user: $deploymentUpdate->getUser(),
|
||||
userId: $deploymentUpdate->getUserId(),
|
||||
payload: $deploymentUpdate->getPayload(),
|
||||
platform: $deploymentUpdate->getPlatform(),
|
||||
));
|
||||
|
||||
/** Trigger Realtime Event */
|
||||
$queueForRealtime
|
||||
|
||||
@@ -5,10 +5,10 @@ namespace Appwrite\Platform\Modules\Health\Http\Health\Queue\Failed;
|
||||
use Appwrite\Event\Database;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Publisher\Audit;
|
||||
use Appwrite\Event\Publisher\Build as BuildPublisher;
|
||||
use Appwrite\Event\Publisher\Certificate;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Publisher\Mail as MailPublisher;
|
||||
use Appwrite\Event\Publisher\Messaging as MessagingPublisher;
|
||||
use Appwrite\Event\Publisher\Migration as MigrationPublisher;
|
||||
@@ -78,7 +78,7 @@ class Get extends Base
|
||||
->inject('queueForDeletes')
|
||||
->inject('publisherForAudits')
|
||||
->inject('publisherForMails')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('publisherForStatsResources')
|
||||
->inject('publisherForUsage')
|
||||
->inject('queueForWebhooks')
|
||||
@@ -98,7 +98,7 @@ class Get extends Base
|
||||
Delete $queueForDeletes,
|
||||
Audit $publisherForAudits,
|
||||
MailPublisher $publisherForMails,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
StatsResourcesPublisher $publisherForStatsResources,
|
||||
UsagePublisher $publisherForUsage,
|
||||
Webhook $queueForWebhooks,
|
||||
@@ -115,7 +115,7 @@ class Get extends Base
|
||||
System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME) => $queueForDeletes,
|
||||
System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME) => $publisherForAudits,
|
||||
System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME) => $publisherForMails,
|
||||
System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME) => $queueForFunctions,
|
||||
System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME) => $publisherForFunctions,
|
||||
System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME) => $publisherForStatsResources,
|
||||
System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME) => $publisherForUsage,
|
||||
System::getEnv('_APP_WEBHOOK_QUEUE_NAME', Event::WEBHOOK_QUEUE_NAME) => $queueForWebhooks,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Health\Http\Health\Queue\Functions;
|
||||
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Platform\Modules\Health\Http\Health\Queue\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
@@ -42,16 +42,16 @@ class Get extends Base
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('response')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(int|string $threshold, Func $queueForFunctions, Response $response): void
|
||||
public function action(int|string $threshold, FunctionPublisher $publisherForFunctions, Response $response): void
|
||||
{
|
||||
$threshold = (int) $threshold;
|
||||
|
||||
$size = $queueForFunctions->getSize();
|
||||
$size = $publisherForFunctions->getSize();
|
||||
|
||||
$this->assertQueueThreshold($size, $threshold);
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Project\Http\Project;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getProject';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/project')
|
||||
->httpAlias('/v1/projects/:projectId')
|
||||
->desc('Get project')
|
||||
->groups(['api', 'project'])
|
||||
->label('scope', 'project.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'get',
|
||||
description: <<<EOT
|
||||
Get a project.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::NONE
|
||||
))
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
Response $response,
|
||||
Document $project,
|
||||
) {
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Project\Services;
|
||||
use Appwrite\Platform\Modules\Project\Http\Init;
|
||||
use Appwrite\Platform\Modules\Project\Http\Project\AuthMethods\Update as UpdateAuthMethod;
|
||||
use Appwrite\Platform\Modules\Project\Http\Project\Delete as DeleteProject;
|
||||
use Appwrite\Platform\Modules\Project\Http\Project\Get as GetProject;
|
||||
use Appwrite\Platform\Modules\Project\Http\Project\Keys\Create as CreateKey;
|
||||
use Appwrite\Platform\Modules\Project\Http\Project\Keys\Delete as DeleteKey;
|
||||
use Appwrite\Platform\Modules\Project\Http\Project\Keys\Ephemeral\Create as CreateEphemeralKey;
|
||||
@@ -110,6 +111,7 @@ class Http extends Service
|
||||
|
||||
// Project
|
||||
$this->addAction(DeleteProject::getName(), new DeleteProject());
|
||||
$this->addAction(GetProject::getName(), new GetProject());
|
||||
$this->addAction(UpdateProjectLabels::getName(), new UpdateProjectLabels());
|
||||
$this->addAction(UpdateProjectProtocol::getName(), new UpdateProjectProtocol());
|
||||
$this->addAction(UpdateProjectService::getName(), new UpdateProjectService());
|
||||
|
||||
@@ -28,7 +28,6 @@ use Utopia\Pools\Group;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
class Create extends Action
|
||||
@@ -72,15 +71,6 @@ class Create extends Action
|
||||
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('region', System::getEnv('_APP_REGION', 'default'), new WhiteList(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true)
|
||||
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
|
||||
->param('logo', '', new Text(1024), 'Project logo.', true)
|
||||
->param('url', '', new URL(), 'Project URL.', true)
|
||||
->param('legalName', '', new Text(256), 'Project legal Name. Max length: 256 chars.', true)
|
||||
->param('legalCountry', '', new Text(256), 'Project legal Country. Max length: 256 chars.', true)
|
||||
->param('legalState', '', new Text(256), 'Project legal State. Max length: 256 chars.', true)
|
||||
->param('legalCity', '', new Text(256), 'Project legal City. Max length: 256 chars.', true)
|
||||
->param('legalAddress', '', new Text(256), 'Project legal Address. Max length: 256 chars.', true)
|
||||
->param('legalTaxId', '', new Text(256), 'Project legal Tax ID. Max length: 256 chars.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
@@ -90,7 +80,7 @@ class Create extends Action
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForPlatform, Cache $cache, Group $pools, Hooks $hooks)
|
||||
public function action(string $projectId, string $name, string $teamId, string $region, Request $request, Response $response, Database $dbForPlatform, Cache $cache, Group $pools, Hooks $hooks)
|
||||
{
|
||||
$team = $dbForPlatform->getDocument('teams', $teamId);
|
||||
|
||||
@@ -175,16 +165,7 @@ class Create extends Action
|
||||
'teamInternalId' => $team->getSequence(),
|
||||
'teamId' => $team->getId(),
|
||||
'region' => $region,
|
||||
'description' => $description,
|
||||
'logo' => $logo,
|
||||
'url' => $url,
|
||||
'version' => APP_VERSION_STABLE,
|
||||
'legalName' => $legalName,
|
||||
'legalCountry' => $legalCountry,
|
||||
'legalState' => $legalState,
|
||||
'legalCity' => $legalCity,
|
||||
'legalAddress' => $legalAddress,
|
||||
'legalTaxId' => ID::custom($legalTaxId),
|
||||
'services' => new \stdClass(),
|
||||
'platforms' => null,
|
||||
'oAuthProviders' => [],
|
||||
|
||||
@@ -59,9 +59,9 @@ trait Deployment
|
||||
$resourceType = $repository->getAttribute('resourceType');
|
||||
|
||||
$logBase = "vcs.github.event.repo.{$repositoryId}";
|
||||
Span::add("{$logBase}.projectId", $projectId);
|
||||
Span::add("{$logBase}.resourceId", $resourceId);
|
||||
Span::add("{$logBase}.resourceType", $resourceType);
|
||||
Span::add('project.id', $projectId);
|
||||
Span::add("{$logBase}.resource.id", $resourceId);
|
||||
Span::add("{$logBase}.resource.type", $resourceType);
|
||||
|
||||
if ($resourceType !== "function" && $resourceType !== "site") {
|
||||
continue;
|
||||
|
||||
@@ -101,11 +101,11 @@ class Interval extends Action
|
||||
]);
|
||||
|
||||
$scanned = \count($rules);
|
||||
Span::add("interval.domainVerification.scanned", $scanned);
|
||||
Span::add("interval.domain_verification.scanned", $scanned);
|
||||
|
||||
if ($scanned === 0) {
|
||||
Span::add("interval.domainVerification.processed", 0);
|
||||
Span::add("interval.domainVerification.failed", 0);
|
||||
Span::add("interval.domain_verification.processed", 0);
|
||||
Span::add("interval.domain_verification.failed", 0);
|
||||
return; // No rules to verify
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class Interval extends Action
|
||||
}
|
||||
}
|
||||
|
||||
Span::add("interval.domainVerification.processed", $processed);
|
||||
Span::add("interval.domainVerification.failed", $failed);
|
||||
Span::add("interval.domain_verification.processed", $processed);
|
||||
Span::add("interval.domain_verification.failed", $failed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
->setGitRepo($language['gitUrl'])
|
||||
->setGitRepoName($language['gitRepoName'])
|
||||
->setGitUserName($language['gitUserName'])
|
||||
->setLogo($cover)
|
||||
->setCoverImage($cover)
|
||||
->setURL('https://appwrite.io')
|
||||
->setShareText('Appwrite is a backend as a service for building web or mobile apps')
|
||||
->setShareURL('http://appwrite.io')
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Swoole\Coroutine as Co;
|
||||
use Utopia\Database\Database;
|
||||
|
||||
@@ -36,7 +37,10 @@ class ScheduleExecutions extends ScheduleBase
|
||||
{
|
||||
$intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds');
|
||||
|
||||
$queueForFunctions = new Func($this->publisherFunctions);
|
||||
$publisherForFunctions = new FunctionPublisher(
|
||||
$this->publisherFunctions,
|
||||
new \Utopia\Queue\Queue(\Utopia\System\System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', \Appwrite\Event\Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', \Appwrite\Event\Event::FUNCTIONS_QUEUE_TTL)
|
||||
);
|
||||
|
||||
foreach ($this->schedules as $schedule) {
|
||||
if (!$schedule['active']) {
|
||||
@@ -63,23 +67,22 @@ class ScheduleExecutions extends ScheduleBase
|
||||
|
||||
$this->updateProjectAccess($schedule['project'], $dbForPlatform);
|
||||
|
||||
\go(function () use ($queueForFunctions, $schedule, $scheduledAt, $delay, $data, $dbForPlatform) {
|
||||
\go(function () use ($publisherForFunctions, $schedule, $scheduledAt, $delay, $data, $dbForPlatform) {
|
||||
if ($delay > 0) {
|
||||
Co::sleep($delay);
|
||||
}
|
||||
|
||||
$queueForFunctions->setType('schedule')
|
||||
// Set functionId instead of function as we don't have $dbForProject
|
||||
// TODO: Refactor to use function instead of functionId
|
||||
->setFunctionId($schedule['resource']['resourceId'])
|
||||
->setExecution($schedule['resource'])
|
||||
->setMethod($data['method'] ?? 'POST')
|
||||
->setPath($data['path'] ?? '/')
|
||||
->setHeaders($data['headers'] ?? [])
|
||||
->setBody($data['body'] ?? '')
|
||||
->setProject($schedule['project'])
|
||||
->setUserId($data['userId'] ?? '')
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(new FunctionMessage(
|
||||
project: $schedule['project'],
|
||||
userId: $data['userId'] ?? '',
|
||||
functionId: $schedule['resource']['resourceId'],
|
||||
execution: $schedule['resource'],
|
||||
type: 'schedule',
|
||||
body: $data['body'] ?? '',
|
||||
path: $data['path'] ?? '/',
|
||||
headers: $data['headers'] ?? [],
|
||||
method: $data['method'] ?? 'POST',
|
||||
));
|
||||
|
||||
$dbForPlatform->deleteDocument(
|
||||
'schedules',
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Cron\CronExpression;
|
||||
use Utopia\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Span\Span;
|
||||
use Utopia\System\System;
|
||||
|
||||
/**
|
||||
* ScheduleFunctions
|
||||
@@ -98,31 +98,29 @@ class ScheduleFunctions extends ScheduleBase
|
||||
|
||||
$this->updateProjectAccess($schedule['project'], $dbForPlatform);
|
||||
|
||||
$queueForFunctions = new Func($this->publisherFunctions);
|
||||
$publisherForFunctions = new FunctionPublisher(
|
||||
$this->publisherFunctions,
|
||||
new \Utopia\Queue\Queue(\Utopia\System\System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', \Appwrite\Event\Event::FUNCTIONS_QUEUE_NAME), 'utopia-queue', \Appwrite\Event\Event::FUNCTIONS_QUEUE_TTL)
|
||||
);
|
||||
|
||||
$queueForFunctions
|
||||
->setType('schedule')
|
||||
->setFunction($schedule['resource'])
|
||||
->setMethod('POST')
|
||||
->setPath('/')
|
||||
->setProject($schedule['project']);
|
||||
Span::init('schedule.functions.enqueue');
|
||||
try {
|
||||
Span::add('project.id', $schedule['project']->getId());
|
||||
Span::add('function.id', $schedule['resource']->getId());
|
||||
Span::add('schedule.id', $schedule['$id'] ?? '');
|
||||
|
||||
$projectDoc = $schedule['project'];
|
||||
$functionDoc = $schedule['resource'];
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
if ($traceProjectId !== '' && $traceFunctionId !== '' && $projectDoc->getId() === $traceProjectId && $functionDoc->getId() === $traceFunctionId) {
|
||||
Span::init('execution.trace.v1_functions_enqueue');
|
||||
Span::add('datetime', gmdate('c'));
|
||||
Span::add('projectId', $projectDoc->getId());
|
||||
Span::add('functionId', $functionDoc->getId());
|
||||
Span::add('scheduleId', $schedule['$id'] ?? '');
|
||||
$publisherForFunctions->enqueue(new FunctionMessage(
|
||||
project: $schedule['project'],
|
||||
function: $schedule['resource'],
|
||||
type: 'schedule',
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
));
|
||||
|
||||
$this->recordEnqueueDelay($delayConfig['nextDate']);
|
||||
} finally {
|
||||
Span::current()?->finish();
|
||||
}
|
||||
|
||||
$queueForFunctions->trigger();
|
||||
|
||||
$this->recordEnqueueDelay($delayConfig['nextDate']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ namespace Appwrite\Platform\Workers;
|
||||
|
||||
use Appwrite\Certificates\Adapter as CertificatesAdapter;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Message\Mail as MailMessage;
|
||||
use Appwrite\Event\Publisher\Certificate;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Publisher\Mail as MailPublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\Webhook;
|
||||
@@ -54,7 +55,7 @@ class Certificates extends Action
|
||||
->inject('publisherForMails')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForRealtime')
|
||||
->inject('publisherForCertificates')
|
||||
->inject('log')
|
||||
@@ -70,7 +71,7 @@ class Certificates extends Action
|
||||
* @param MailPublisher $publisherForMails
|
||||
* @param Event $queueForEvents
|
||||
* @param Webhook $queueForWebhooks
|
||||
* @param Func $queueForFunctions
|
||||
* @param FunctionPublisher $publisherForFunctions
|
||||
* @param Realtime $queueForRealtime
|
||||
* @param Certificate $publisherForCertificates
|
||||
* @param Log $log
|
||||
@@ -87,7 +88,7 @@ class Certificates extends Action
|
||||
MailPublisher $publisherForMails,
|
||||
Event $queueForEvents,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime,
|
||||
Certificate $publisherForCertificates,
|
||||
Log $log,
|
||||
@@ -113,11 +114,11 @@ class Certificates extends Action
|
||||
|
||||
switch ($action) {
|
||||
case \Appwrite\Event\Certificate::ACTION_DOMAIN_VERIFICATION:
|
||||
$this->handleDomainVerificationAction($domain, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $publisherForCertificates, $log, $authorization, $validationDomain);
|
||||
$this->handleDomainVerificationAction($domain, $dbForPlatform, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime, $publisherForCertificates, $log, $authorization, $validationDomain);
|
||||
break;
|
||||
|
||||
case \Appwrite\Event\Certificate::ACTION_GENERATION:
|
||||
$this->handleCertificateGenerationAction($domain, $domainType, $dbForPlatform, $publisherForMails, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $log, $certificates, $authorization, $skipRenewCheck, $plan, $validationDomain);
|
||||
$this->handleCertificateGenerationAction($domain, $domainType, $dbForPlatform, $publisherForMails, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime, $log, $certificates, $authorization, $skipRenewCheck, $plan, $validationDomain);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -130,7 +131,7 @@ class Certificates extends Action
|
||||
* @param Database $dbForPlatform
|
||||
* @param Event $queueForEvents
|
||||
* @param Webhook $queueForWebhooks
|
||||
* @param Func $queueForFunctions
|
||||
* @param FunctionPublisher $publisherForFunctions
|
||||
* @param Realtime $queueForRealtime
|
||||
* @param Certificate $publisherForCertificates
|
||||
* @param Log $log
|
||||
@@ -146,7 +147,7 @@ class Certificates extends Action
|
||||
Database $dbForPlatform,
|
||||
Event $queueForEvents,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime,
|
||||
Certificate $publisherForCertificates,
|
||||
Log $log,
|
||||
@@ -185,7 +186,7 @@ class Certificates extends Action
|
||||
$rule->setAttribute('logs', $logs);
|
||||
} finally {
|
||||
// Update rule and emit events
|
||||
$this->updateRuleAndSendEvents($rule, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime);
|
||||
$this->updateRuleAndSendEvents($rule, $dbForPlatform, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime);
|
||||
}
|
||||
|
||||
// Issue a TLS certificate when domain is verified
|
||||
@@ -213,7 +214,7 @@ class Certificates extends Action
|
||||
* @param MailPublisher $publisherForMails
|
||||
* @param Event $queueForEvents
|
||||
* @param Webhook $queueForWebhooks
|
||||
* @param Func $queueForFunctions
|
||||
* @param FunctionPublisher $publisherForFunctions
|
||||
* @param Realtime $queueForRealtime
|
||||
* @param Log $log
|
||||
* @param CertificatesAdapter $certificates
|
||||
@@ -237,7 +238,7 @@ class Certificates extends Action
|
||||
MailPublisher $publisherForMails,
|
||||
Event $queueForEvents,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime,
|
||||
Log $log,
|
||||
CertificatesAdapter $certificates,
|
||||
@@ -370,7 +371,7 @@ class Certificates extends Action
|
||||
// Update rule and emit events
|
||||
$rule->setAttribute('certificateId', $certificate->getId());
|
||||
$rule->setAttribute('logs', $logs);
|
||||
$this->updateRuleAndSendEvents($rule, $dbForPlatform, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime);
|
||||
$this->updateRuleAndSendEvents($rule, $dbForPlatform, $queueForEvents, $queueForWebhooks, $publisherForFunctions, $queueForRealtime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +417,7 @@ class Certificates extends Action
|
||||
* @param Database $dbForPlatform Database connection for console
|
||||
* @param Event $queueForEvents Event publisher for events
|
||||
* @param Webhook $queueForWebhooks Webhook publisher for webhooks
|
||||
* @param Func $queueForFunctions Function publisher for functions
|
||||
* @param FunctionPublisher $publisherForFunctions Function publisher for functions
|
||||
* @param Realtime $queueForRealtime Realtime publisher for realtime events
|
||||
*
|
||||
* @return void
|
||||
@@ -426,7 +427,7 @@ class Certificates extends Action
|
||||
Database $dbForPlatform,
|
||||
Event $queueForEvents,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime
|
||||
): void {
|
||||
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
@@ -459,9 +460,15 @@ class Certificates extends Action
|
||||
->trigger();
|
||||
|
||||
/** Trigger Functions */
|
||||
$queueForFunctions
|
||||
->from($queueForEvents)
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $queueForEvents->getEvent(),
|
||||
params: $queueForEvents->getParams(),
|
||||
project: $queueForEvents->getProject(),
|
||||
user: $queueForEvents->getUser(),
|
||||
userId: $queueForEvents->getUserId(),
|
||||
payload: $queueForEvents->getPayload(),
|
||||
platform: $queueForEvents->getPlatform(),
|
||||
));
|
||||
|
||||
/** Trigger Realtime Events */
|
||||
$queueForRealtime
|
||||
|
||||
@@ -216,11 +216,25 @@ class Deletes extends Action
|
||||
$this->deleteExpiredTransactions($project, $getProjectDB);
|
||||
$this->deleteOldDeployments($queueForDeletes, $project, $getProjectDB);
|
||||
break;
|
||||
case DELETE_TYPE_REPORT:
|
||||
$this->deleteReport($dbForPlatform, $project, $document);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('No delete operation for type: ' . \strval($type));
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteReport(Database $dbForPlatform, Document $project, Document $report): void
|
||||
{
|
||||
$projectInternalId = $project->getSequence();
|
||||
$reportInternalId = $report->getSequence();
|
||||
|
||||
$this->deleteByGroup('insights', [
|
||||
Query::equal('projectInternalId', [$projectInternalId]),
|
||||
Query::equal('reportInternalId', [$reportInternalId]),
|
||||
], $dbForPlatform);
|
||||
}
|
||||
|
||||
private function cleanDatabase(
|
||||
Document $databaseDoc,
|
||||
callable $executionActionPerDatabase,
|
||||
@@ -716,6 +730,26 @@ class Deletes extends Action
|
||||
Console::error('Failed to delete schedules: ' . $th->getMessage());
|
||||
}
|
||||
|
||||
// Delete Advisor insights
|
||||
try {
|
||||
$this->deleteByGroup('insights', [
|
||||
Query::equal('projectInternalId', [$projectInternalId]),
|
||||
Query::orderAsc()
|
||||
], $dbForPlatform);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Failed to delete insights: ' . $th->getMessage());
|
||||
}
|
||||
|
||||
// Delete Advisor reports
|
||||
try {
|
||||
$this->deleteByGroup('reports', [
|
||||
Query::equal('projectInternalId', [$projectInternalId]),
|
||||
Query::orderAsc()
|
||||
], $dbForPlatform);
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Failed to delete reports: ' . $th->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Database $dbForProject
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\Span\Span;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Executions extends Action
|
||||
{
|
||||
@@ -41,19 +40,11 @@ class Executions extends Action
|
||||
throw new Exception('Missing execution');
|
||||
}
|
||||
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
$resourceId = $execution->getAttribute('resourceId', '');
|
||||
if ($traceProjectId !== '' && $traceFunctionId !== '' && $executionMessage->project->getId() === $traceProjectId && $resourceId === $traceFunctionId) {
|
||||
Span::init('execution.trace.executions_worker_upsert');
|
||||
Span::add('datetime', gmdate('c'));
|
||||
Span::add('projectId', $executionMessage->project->getId());
|
||||
Span::add('functionId', $resourceId);
|
||||
Span::add('executionId', $execution->getId());
|
||||
Span::add('deploymentId', $execution->getAttribute('deploymentId', ''));
|
||||
Span::add('resourceType', $execution->getAttribute('resourceType', ''));
|
||||
Span::current()?->finish();
|
||||
}
|
||||
Span::add('project.id', $executionMessage->project->getId());
|
||||
Span::add('function.id', $execution->getAttribute('resourceId', ''));
|
||||
Span::add('execution.id', $execution->getId());
|
||||
Span::add('deployment.id', $execution->getAttribute('deploymentId', ''));
|
||||
Span::add('resource.type', $execution->getAttribute('resourceType', ''));
|
||||
|
||||
$dbForProject->upsertDocument('executions', $execution);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ namespace Appwrite\Platform\Workers;
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Bus\Events\ExecutionCompleted;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Message\Func as FunctionMessage;
|
||||
use Appwrite\Event\Publisher\Func as FunctionPublisher;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
@@ -46,7 +47,7 @@ class Functions extends Action
|
||||
->inject('message')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForWebhooks')
|
||||
->inject('queueForFunctions')
|
||||
->inject('publisherForFunctions')
|
||||
->inject('queueForRealtime')
|
||||
->inject('queueForEvents')
|
||||
->inject('bus')
|
||||
@@ -61,7 +62,7 @@ class Functions extends Action
|
||||
Message $message,
|
||||
Database $dbForProject,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime,
|
||||
Event $queueForEvents,
|
||||
Bus $bus,
|
||||
@@ -78,20 +79,27 @@ class Functions extends Action
|
||||
);
|
||||
}
|
||||
|
||||
$type = $payload['type'] ?? '';
|
||||
$functionMessage = FunctionMessage::fromArray($payload);
|
||||
$type = $functionMessage->type;
|
||||
|
||||
$events = $payload['events'] ?? [];
|
||||
$data = $payload['body'] ?? '';
|
||||
$eventData = $payload['payload'] ?? '';
|
||||
$platform = $payload['platform'] ?? Config::getParam('platform', []);
|
||||
$function = new Document($payload['function'] ?? []);
|
||||
$functionId = $payload['functionId'] ?? '';
|
||||
$user = new Document($payload['user'] ?? []);
|
||||
$userId = $payload['userId'] ?? '';
|
||||
$method = $payload['method'] ?? 'POST';
|
||||
$headers = $payload['headers'] ?? [];
|
||||
$path = $payload['path'] ?? '/';
|
||||
$jwt = $payload['jwt'] ?? '';
|
||||
Span::add('project.id', $project->getId());
|
||||
Span::add('payload.type', $type);
|
||||
Span::add('queue.pid', $message->getPid());
|
||||
Span::add('queue.name', $message->getQueue());
|
||||
Span::add('message.timestamp', (string) $message->getTimestamp());
|
||||
|
||||
$events = $functionMessage->events;
|
||||
$data = $functionMessage->body;
|
||||
$eventData = $functionMessage->payload;
|
||||
$platform = !empty($functionMessage->platform) ? $functionMessage->platform : Config::getParam('platform', []);
|
||||
$function = $functionMessage->function ?? new Document();
|
||||
$functionId = $functionMessage->functionId ?? '';
|
||||
$user = $functionMessage->user ?? new Document();
|
||||
$userId = $functionMessage->userId ?? '';
|
||||
$method = $functionMessage->method ?: 'POST';
|
||||
$headers = $functionMessage->headers;
|
||||
$path = $functionMessage->path ?: '/';
|
||||
$jwt = $functionMessage->jwt;
|
||||
|
||||
if ($user->isEmpty() && !empty($userId)) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
@@ -118,19 +126,7 @@ class Functions extends Action
|
||||
$log->addTag('type', $type);
|
||||
|
||||
if (empty($events) && !$function->isEmpty()) {
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
if ($traceProjectId !== '' && $traceFunctionId !== '' && $project->getId() === $traceProjectId && $function->getId() === $traceFunctionId) {
|
||||
Span::init('execution.trace.functions_worker_dequeue');
|
||||
Span::add('datetime', gmdate('c'));
|
||||
Span::add('projectId', $project->getId());
|
||||
Span::add('functionId', $function->getId());
|
||||
Span::add('payloadType', $type);
|
||||
Span::add('queuePid', $message->getPid());
|
||||
Span::add('queueName', $message->getQueue());
|
||||
Span::add('messageTimestamp', (string) $message->getTimestamp());
|
||||
Span::current()?->finish();
|
||||
}
|
||||
Span::add('function.id', $function->getId());
|
||||
}
|
||||
|
||||
if (!empty($events)) {
|
||||
@@ -172,7 +168,7 @@ class Functions extends Action
|
||||
log: $log,
|
||||
dbForProject: $dbForProject,
|
||||
queueForWebhooks: $queueForWebhooks,
|
||||
queueForFunctions: $queueForFunctions,
|
||||
publisherForFunctions: $publisherForFunctions,
|
||||
queueForRealtime: $queueForRealtime,
|
||||
queueForEvents: $queueForEvents,
|
||||
bus: $bus,
|
||||
@@ -191,7 +187,7 @@ class Functions extends Action
|
||||
user: $user,
|
||||
jwt: null,
|
||||
event: $events[0],
|
||||
eventData: \is_string($eventData) ? $eventData : \json_encode($eventData),
|
||||
eventData: \json_encode($eventData) ?: null,
|
||||
executionId: null,
|
||||
);
|
||||
Console::success('Triggered function: ' . $events[0]);
|
||||
@@ -216,7 +212,7 @@ class Functions extends Action
|
||||
log: $log,
|
||||
dbForProject: $dbForProject,
|
||||
queueForWebhooks: $queueForWebhooks,
|
||||
queueForFunctions: $queueForFunctions,
|
||||
publisherForFunctions: $publisherForFunctions,
|
||||
queueForRealtime: $queueForRealtime,
|
||||
queueForEvents: $queueForEvents,
|
||||
bus: $bus,
|
||||
@@ -242,7 +238,7 @@ class Functions extends Action
|
||||
log: $log,
|
||||
dbForProject: $dbForProject,
|
||||
queueForWebhooks: $queueForWebhooks,
|
||||
queueForFunctions: $queueForFunctions,
|
||||
publisherForFunctions: $publisherForFunctions,
|
||||
queueForRealtime: $queueForRealtime,
|
||||
queueForEvents: $queueForEvents,
|
||||
bus: $bus,
|
||||
@@ -322,19 +318,11 @@ class Functions extends Action
|
||||
'duration' => 0.0,
|
||||
]);
|
||||
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
if ($traceProjectId !== '' && $traceFunctionId !== '' && $project->getId() === $traceProjectId && $function->getId() === $traceFunctionId) {
|
||||
Span::init('execution.trace.functions_worker_before_execution_completed_bus_fail');
|
||||
Span::add('datetime', gmdate('c'));
|
||||
Span::add('projectId', $project->getId());
|
||||
Span::add('functionId', $function->getId());
|
||||
Span::add('executionId', $execution->getId());
|
||||
Span::add('deploymentId', $execution->getAttribute('deploymentId', ''));
|
||||
Span::add('trigger', $trigger);
|
||||
Span::add('status', $execution->getAttribute('status', ''));
|
||||
Span::current()?->finish();
|
||||
}
|
||||
Span::add('function.id', $function->getId());
|
||||
Span::add('execution.id', $execution->getId());
|
||||
Span::add('deployment.id', $execution->getAttribute('deploymentId', ''));
|
||||
Span::add('execution.trigger', $trigger);
|
||||
Span::add('execution.status', $execution->getAttribute('status', ''));
|
||||
|
||||
$bus->dispatch(new ExecutionCompleted(
|
||||
execution: $execution->getArrayCopy(),
|
||||
@@ -345,7 +333,7 @@ class Functions extends Action
|
||||
/**
|
||||
* @param Log $log
|
||||
* @param Database $dbForProject
|
||||
* @param Func $queueForFunctions
|
||||
* @param FunctionPublisher $publisherForFunctions
|
||||
* @param Realtime $queueForRealtime
|
||||
* @param Event $queueForEvents
|
||||
* @param Document $project
|
||||
@@ -367,7 +355,7 @@ class Functions extends Action
|
||||
Log $log,
|
||||
Database $dbForProject,
|
||||
Webhook $queueForWebhooks,
|
||||
Func $queueForFunctions,
|
||||
FunctionPublisher $publisherForFunctions,
|
||||
Realtime $queueForRealtime,
|
||||
Event $queueForEvents,
|
||||
Bus $bus,
|
||||
@@ -391,6 +379,10 @@ class Functions extends Action
|
||||
$deploymentId = $function->getAttribute('deploymentId', '');
|
||||
$spec = Config::getParam('specifications')[$function->getAttribute('runtimeSpecification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
|
||||
|
||||
Span::add('function.id', $functionId);
|
||||
Span::add('deployment.id', $deploymentId);
|
||||
Span::add('execution.trigger', $trigger);
|
||||
|
||||
$log->addTag('deploymentId', $deploymentId);
|
||||
|
||||
/** Check if deployment exists */
|
||||
@@ -450,6 +442,8 @@ class Functions extends Action
|
||||
}
|
||||
$headers['x-appwrite-execution-id'] = $executionId;
|
||||
|
||||
Span::add('execution.id', $executionId);
|
||||
|
||||
$headersFiltered = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) {
|
||||
@@ -554,18 +548,6 @@ class Functions extends Action
|
||||
$source = $deployment->getAttribute('buildPath', '');
|
||||
$extension = str_ends_with($source, '.tar') ? 'tar' : 'tar.gz';
|
||||
$command = $version === 'v2' ? '' : "cp /tmp/code.$extension /mnt/code/code.$extension && nohup helpers/start.sh \"$command\"";
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
if ($traceProjectId !== '' && $traceFunctionId !== '' && $project->getId() === $traceProjectId && $functionId === $traceFunctionId) {
|
||||
Span::init('execution.trace.functions_worker_before_executor');
|
||||
Span::add('datetime', gmdate('c'));
|
||||
Span::add('projectId', $project->getId());
|
||||
Span::add('functionId', $functionId);
|
||||
Span::add('executionId', $executionId);
|
||||
Span::add('deploymentId', $deployment->getId());
|
||||
Span::add('trigger', $trigger);
|
||||
Span::current()?->finish();
|
||||
}
|
||||
try {
|
||||
$executionResponse = $executor->createExecution(
|
||||
projectId: $project->getId(),
|
||||
@@ -642,19 +624,8 @@ class Functions extends Action
|
||||
$errorCode = $th->getCode();
|
||||
} finally {
|
||||
/** Persist final execution status and record usage */
|
||||
$traceProjectId = System::getEnv('_APP_TRACE_PROJECT_ID', '');
|
||||
$traceFunctionId = System::getEnv('_APP_TRACE_FUNCTION_ID', '');
|
||||
if ($traceProjectId !== '' && $traceFunctionId !== '' && $project->getId() === $traceProjectId && $functionId === $traceFunctionId) {
|
||||
Span::init('execution.trace.functions_worker_before_execution_completed_bus');
|
||||
Span::add('datetime', gmdate('c'));
|
||||
Span::add('projectId', $project->getId());
|
||||
Span::add('functionId', $functionId);
|
||||
Span::add('executionId', $execution->getId());
|
||||
Span::add('deploymentId', $execution->getAttribute('deploymentId', ''));
|
||||
Span::add('status', $execution->getAttribute('status', ''));
|
||||
Span::add('trigger', $trigger);
|
||||
Span::current()?->finish();
|
||||
}
|
||||
Span::add('execution.status', $execution->getAttribute('status', ''));
|
||||
|
||||
$bus->dispatch(new ExecutionCompleted(
|
||||
execution: $execution->getArrayCopy(),
|
||||
project: $project->getArrayCopy(),
|
||||
@@ -680,9 +651,15 @@ class Functions extends Action
|
||||
->trigger();
|
||||
|
||||
/** Trigger Functions */
|
||||
$queueForFunctions
|
||||
->from($queueForEvents)
|
||||
->trigger();
|
||||
$publisherForFunctions->enqueue(FunctionMessage::fromEvent(
|
||||
event: $queueForEvents->getEvent(),
|
||||
params: $queueForEvents->getParams(),
|
||||
project: $queueForEvents->getProject(),
|
||||
user: $queueForEvents->getUser(),
|
||||
userId: $queueForEvents->getUserId(),
|
||||
payload: $queueForEvents->getPayload(),
|
||||
platform: $queueForEvents->getPlatform(),
|
||||
));
|
||||
|
||||
/** Trigger Realtime Events */
|
||||
$queueForRealtime
|
||||
|
||||
@@ -106,27 +106,20 @@ class Messaging extends Action
|
||||
|
||||
Span::add('message.type', $type);
|
||||
|
||||
try {
|
||||
switch ($type) {
|
||||
case MESSAGE_SEND_TYPE_INTERNAL:
|
||||
$message = new Document($payload['message'] ?? []);
|
||||
$recipients = $payload['recipients'] ?? [];
|
||||
switch ($type) {
|
||||
case MESSAGE_SEND_TYPE_INTERNAL:
|
||||
$message = new Document($payload['message'] ?? []);
|
||||
$recipients = $payload['recipients'] ?? [];
|
||||
|
||||
$this->sendInternalSMSMessage($message, $project, $recipients, $log);
|
||||
break;
|
||||
case MESSAGE_SEND_TYPE_EXTERNAL:
|
||||
$message = $dbForProject->getDocument('messages', $payload['messageId']);
|
||||
$this->sendInternalSMSMessage($message, $project, $recipients, $log);
|
||||
break;
|
||||
case MESSAGE_SEND_TYPE_EXTERNAL:
|
||||
$message = $dbForProject->getDocument('messages', $payload['messageId']);
|
||||
|
||||
$this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project, $publisherForUsage);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Unknown message type: ' . $type);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Span::error($e);
|
||||
throw $e;
|
||||
} finally {
|
||||
Span::current()?->finish();
|
||||
$this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project, $publisherForUsage);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Unknown message type: ' . $type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,21 +155,26 @@ class Messaging extends Action
|
||||
}
|
||||
|
||||
if (\count($userIds) > 0) {
|
||||
$users = $dbForProject->find('users', [
|
||||
Query::equal('$id', $userIds),
|
||||
Query::limit(\count($userIds)),
|
||||
]);
|
||||
foreach ($users as $user) {
|
||||
$targets = \array_filter($user->getAttribute('targets'), function (Document $target) use ($providerType) {
|
||||
return $target->getAttribute('providerType') === $providerType;
|
||||
});
|
||||
$limit = 1000;
|
||||
$offset = 0;
|
||||
|
||||
do {
|
||||
$targets = $dbForProject->find('targets', [
|
||||
Query::equal('userId', $userIds),
|
||||
Query::select(['providerId', 'identifier']),
|
||||
Query::equal('providerType', [$providerType]),
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
]);
|
||||
|
||||
\array_push($allTargets, ...$targets);
|
||||
}
|
||||
$offset += \count($targets);
|
||||
} while (\count($targets) === $limit);
|
||||
}
|
||||
|
||||
if (\count($targetIds) > 0) {
|
||||
$targets = $dbForProject->find('targets', [
|
||||
Query::select(['providerId', 'identifier']),
|
||||
Query::equal('$id', $targetIds),
|
||||
Query::equal('providerType', [$providerType]),
|
||||
Query::limit(\count($targetIds)),
|
||||
|
||||
@@ -8,7 +8,7 @@ use Utopia\DI\Container;
|
||||
|
||||
class Swoole extends Promise
|
||||
{
|
||||
private const REQUEST_CONTAINER_CONTEXT_KEY = '__utopia_http_request_container';
|
||||
private const REQUEST_CONTAINER_CONTEXT_KEY = '__utopia__';
|
||||
|
||||
public function __construct(?callable $executor = null)
|
||||
{
|
||||
|
||||
@@ -757,28 +757,28 @@ abstract class Format
|
||||
case 'updateAuthMethod':
|
||||
switch ($param) {
|
||||
case 'methodId':
|
||||
return 'AuthMethod';
|
||||
return 'ProjectAuthMethodId';
|
||||
}
|
||||
break;
|
||||
case 'getPolicy':
|
||||
switch ($param) {
|
||||
case 'policyId':
|
||||
return 'ProjectPolicy';
|
||||
return 'ProjectPolicyId';
|
||||
}
|
||||
break;
|
||||
case 'getOAuth2Provider':
|
||||
switch ($param) {
|
||||
case 'providerId':
|
||||
return 'OAuthProvider';
|
||||
return 'ProjectOAuthProviderId';
|
||||
}
|
||||
break;
|
||||
case 'getEmailTemplate':
|
||||
case 'updateEmailTemplate':
|
||||
switch ($param) {
|
||||
case 'templateId':
|
||||
return 'EmailTemplateType';
|
||||
return 'ProjectEmailTemplateId';
|
||||
case 'locale':
|
||||
return 'EmailTemplateLocale';
|
||||
return 'ProjectEmailTemplateLocale';
|
||||
}
|
||||
break;
|
||||
case 'getUsage':
|
||||
@@ -787,6 +787,39 @@ abstract class Format
|
||||
return 'ProjectUsageRange';
|
||||
}
|
||||
break;
|
||||
case 'updateProtocol':
|
||||
switch ($param) {
|
||||
case 'protocolId':
|
||||
return 'ProjectProtocolId';
|
||||
}
|
||||
break;
|
||||
case 'updateService':
|
||||
switch ($param) {
|
||||
case 'serviceId':
|
||||
return 'ProjectServiceId';
|
||||
}
|
||||
break;
|
||||
case 'updateSMTP':
|
||||
case 'createSMTPTest':
|
||||
switch ($param) {
|
||||
case 'secure':
|
||||
return 'ProjectSMTPSecure';
|
||||
}
|
||||
break;
|
||||
case 'updateOAuth2Google':
|
||||
switch ($param) {
|
||||
case 'prompt':
|
||||
return 'ProjectOAuth2GooglePrompt';
|
||||
}
|
||||
break;
|
||||
case 'createKey':
|
||||
case 'createEphemeralKey':
|
||||
case 'updateKey':
|
||||
switch ($param) {
|
||||
case 'scopes':
|
||||
return 'ProjectKeyScopes';
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'projects':
|
||||
@@ -1019,8 +1052,12 @@ abstract class Format
|
||||
return self::REQUEST_PARAMETER_OVERRIDES;
|
||||
}
|
||||
|
||||
public function getResponseEnumName(string $model, string $param): ?string
|
||||
public function getResponseEnumName(string $model, string $param, ?string $enumSDKName = null): ?string
|
||||
{
|
||||
if ($enumSDKName) {
|
||||
return $enumSDKName;
|
||||
}
|
||||
|
||||
if ($param === 'type' && \str_starts_with($model, 'platform') && $model !== 'platformList') {
|
||||
return 'PlatformType';
|
||||
}
|
||||
|
||||
@@ -988,13 +988,13 @@ class OpenAPI3 extends Format
|
||||
if ($rule['type'] === 'enum' && !empty($rule['enum'])) {
|
||||
if ($rule['array']) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name]['items']['enum'] = \array_values($rule['enum']);
|
||||
$enumName = $this->getResponseEnumName($model->getType(), $name);
|
||||
$enumName = $this->getResponseEnumName($model->getType(), $name, $rule['enumSDKName'] ?? null);
|
||||
if ($enumName) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name]['items']['x-enum-name'] = $enumName;
|
||||
}
|
||||
} else {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name]['enum'] = \array_values($rule['enum']);
|
||||
$enumName = $this->getResponseEnumName($model->getType(), $name);
|
||||
$enumName = $this->getResponseEnumName($model->getType(), $name, $rule['enumSDKName'] ?? null);
|
||||
if ($enumName) {
|
||||
$output['components']['schemas'][$model->getType()]['properties'][$name]['x-enum-name'] = $enumName;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user