mirror of
https://github.com/appwrite/appwrite
synced 2026-05-06 06:48:22 +00:00
Merge remote-tracking branch 'origin/1.6.x' into fix-manual-templates
This commit is contained in:
commit
72e4534025
57 changed files with 55258 additions and 136 deletions
87
.github/workflows/pr-scan.yml
vendored
Normal file
87
.github/workflows/pr-scan.yml
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
name: PR Security Scan
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
- name: Build the Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
load: true
|
||||
tags: pr_image:${{ github.sha }}
|
||||
- name: Run Trivy vulnerability scanner on image
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
image-ref: 'pr_image:${{ github.sha }}'
|
||||
format: 'json'
|
||||
output: 'trivy-image-results.json'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
- name: Run Trivy vulnerability scanner on source code
|
||||
uses: aquasecurity/trivy-action@0.20.0
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
scan-ref: '.'
|
||||
format: 'json'
|
||||
output: 'trivy-fs-results.json'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
- name: Process and post Trivy scan results
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
let commentBody = '## Security Scan Results for PR\n\n';
|
||||
|
||||
function processResults(results, title) {
|
||||
let sectionBody = `### ${title}\n\n`;
|
||||
if (results.Results && results.Results.some(result => result.Vulnerabilities && result.Vulnerabilities.length > 0)) {
|
||||
sectionBody += '| Package | Version | Vulnerability | Severity |\n';
|
||||
sectionBody += '|---------|---------|----------------|----------|\n';
|
||||
|
||||
const uniqueVulns = new Set();
|
||||
results.Results.forEach(result => {
|
||||
if (result.Vulnerabilities) {
|
||||
result.Vulnerabilities.forEach(vuln => {
|
||||
const vulnKey = `${vuln.PkgName}-${vuln.InstalledVersion}-${vuln.VulnerabilityID}`;
|
||||
if (!uniqueVulns.has(vulnKey)) {
|
||||
uniqueVulns.add(vulnKey);
|
||||
sectionBody += `| ${vuln.PkgName} | ${vuln.InstalledVersion} | [${vuln.VulnerabilityID}](https://nvd.nist.gov/vuln/detail/${vuln.VulnerabilityID}) | ${vuln.Severity} |\n`;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sectionBody += '🎉 No vulnerabilities found!\n';
|
||||
}
|
||||
return sectionBody;
|
||||
}
|
||||
|
||||
try {
|
||||
const imageResults = JSON.parse(fs.readFileSync('trivy-image-results.json', 'utf8'));
|
||||
const fsResults = JSON.parse(fs.readFileSync('trivy-fs-results.json', 'utf8'));
|
||||
|
||||
commentBody += processResults(imageResults, "Docker Image Scan Results");
|
||||
commentBody += '\n';
|
||||
commentBody += processResults(fsResults, "Source Code Scan Results");
|
||||
|
||||
} catch (error) {
|
||||
commentBody += `There was an error while running the security scan: ${error.message}\n`;
|
||||
commentBody += 'Please contact the core team for assistance.';
|
||||
}
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: commentBody
|
||||
});
|
||||
83
CHANGES.md
83
CHANGES.md
|
|
@ -1,3 +1,86 @@
|
|||
# Version 1.5.10
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Notable changes
|
||||
|
||||
* Bump console to version 4.3.30 in [#8520](https://github.com/appwrite/appwrite/pull/8520)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix migration stuck at "Starting Data Migration [...]" in [#8519](https://github.com/appwrite/appwrite/pull/8519)
|
||||
|
||||
# Version 1.5.9
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Notable changes
|
||||
|
||||
* Add Darija (Moroccan Arabic) translation file in [7501](https://github.com/appwrite/appwrite/pull/7501)
|
||||
* Bump console to version 4.3.29 in [8504](https://github.com/appwrite/appwrite/pull/8504)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix domain check in [8472](https://github.com/appwrite/appwrite/pull/8472)
|
||||
* Fix "API must be called in the coroutine" in [8495](https://github.com/appwrite/appwrite/pull/8495)
|
||||
* Bump executor version from 0.5.5 to 0.5.7 in [8502](https://github.com/appwrite/appwrite/pull/8502)
|
||||
|
||||
### Miscellaneous
|
||||
* Add profiler for debugging in [8397](https://github.com/appwrite/appwrite/pull/8397)
|
||||
* Document APIs that don't support redirects in [8233](https://github.com/appwrite/appwrite/pull/8233)
|
||||
|
||||
# Version 1.5.8
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Notable changes
|
||||
|
||||
* Support Twilio messaging service SID in [8222](https://github.com/appwrite/appwrite/pull/8222)
|
||||
* Improve cache performance in [8230](https://github.com/appwrite/appwrite/pull/8230)
|
||||
* Add hk in translations in [8179](https://github.com/appwrite/appwrite/pull/8179)
|
||||
* Update pwd abuse in [8255](https://github.com/appwrite/appwrite/pull/8255)
|
||||
* Remove detailed trace in [8374](https://github.com/appwrite/appwrite/pull/8374)
|
||||
* Remove relationship attributes from realtime event payloads in [8381](https://github.com/appwrite/appwrite/pull/8381)
|
||||
* Sanitize URLs in emails in [8415](https://github.com/appwrite/appwrite/pull/8415)
|
||||
* Bump console to version 4.3.27 in [8482](https://github.com/appwrite/appwrite/pull/8482)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Ensure usage is counted for errors in [8120](https://github.com/appwrite/appwrite/pull/8120)
|
||||
* Fix MFA for OAuth2 only accounts in [8245](https://github.com/appwrite/appwrite/pull/8245)
|
||||
* Delete Expired Targets Per Project in [8239](https://github.com/appwrite/appwrite/pull/8239)
|
||||
* Don't set the target field if the existing target document is false in [8236](https://github.com/appwrite/appwrite/pull/8236)
|
||||
* Disable validation for project DBs during migration in [8298](https://github.com/appwrite/appwrite/pull/8298)
|
||||
* Add `default` to Collection Attributes in Migration in [8271](https://github.com/appwrite/appwrite/pull/8271)
|
||||
* Fix Create bucket endpoint validator for maximum file size in [8275](https://github.com/appwrite/appwrite/pull/8275)
|
||||
* Disable validation for subquery to prevent error in [8297](https://github.com/appwrite/appwrite/pull/8297)
|
||||
* Fix 'Missing required attribute "expire"' on `users.createSession()` in [8308](https://github.com/appwrite/appwrite/pull/8308)
|
||||
* Fix certificate emails in [8292](https://github.com/appwrite/appwrite/pull/8292)
|
||||
* Fix browser-cached deleted file in [8264](https://github.com/appwrite/appwrite/pull/8264)
|
||||
* Fix migration of firebase users [8377](https://github.com/appwrite/appwrite/pull/8377)
|
||||
* Fix `path` for vcs function deployments in [8408](https://github.com/appwrite/appwrite/pull/8408)
|
||||
* Fix calculations in [8431](https://github.com/appwrite/appwrite/pull/8431)
|
||||
* Fix bugs with migrations in [8442](https://github.com/appwrite/appwrite/pull/8442)
|
||||
* Fix queueForUsage not triggering for domain executions in [8463](https://github.com/appwrite/appwrite/pull/8463)
|
||||
* Fix realtime permission change in [8416](https://github.com/appwrite/appwrite/pull/8416)
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Bump base image from 0.9.0 to 0.9.1 in [8238](https://github.com/appwrite/appwrite/pull/8238)
|
||||
* Use latest Platform and add Core module in [7936](https://github.com/appwrite/appwrite/pull/7936)
|
||||
* Add Test to Validate Headers aren't Overridden in [8228](https://github.com/appwrite/appwrite/pull/8228)
|
||||
* Fix hyperlink in storage docs in [8269](https://github.com/appwrite/appwrite/pull/8269)
|
||||
* Update cache & database in [8285](https://github.com/appwrite/appwrite/pull/8285)
|
||||
* Fix flaky certificate test in [8316](https://github.com/appwrite/appwrite/pull/8316)
|
||||
* Fix flaky function test in [8317](https://github.com/appwrite/appwrite/pull/8317)
|
||||
* Update account API reference in [8305](https://github.com/appwrite/appwrite/pull/8305)
|
||||
* Update functions API reference in [8346](https://github.com/appwrite/appwrite/pull/8346)
|
||||
* Implement deploymentsStorage metric for projects API in [8258](https://github.com/appwrite/appwrite/pull/8258)
|
||||
* Add new audit events in [8424](https://github.com/appwrite/appwrite/pull/8424)
|
||||
* Move mbSeconds into 1.5.x in [8449](https://github.com/appwrite/appwrite/pull/8449)
|
||||
* Clean projects cache while migrating in [8395](https://github.com/appwrite/appwrite/pull/8395)
|
||||
* Use git tags for function template in [8445](https://github.com/appwrite/appwrite/pull/8445)
|
||||
|
||||
# Version 1.5.7
|
||||
## What's Changed
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.5.7
|
||||
appwrite/appwrite:1.5.10
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
|
@ -79,7 +79,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.5.7
|
||||
appwrite/appwrite:1.5.10
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
|
@ -89,7 +89,7 @@ docker run -it --rm `
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.5.7
|
||||
appwrite/appwrite:1.5.10
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.5.7
|
||||
appwrite/appwrite:1.5.10
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
|
@ -87,7 +87,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.5.7
|
||||
appwrite/appwrite:1.5.10
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
|
@ -97,7 +97,7 @@ docker run -it --rm `
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.5.7
|
||||
appwrite/appwrite:1.5.10
|
||||
```
|
||||
|
||||
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
|
||||
|
|
|
|||
|
|
@ -185,7 +185,6 @@ CLI::setResource('logError', function (Registry $register) {
|
|||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ return [
|
|||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.8.2',
|
||||
'version' => '0.8.3',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1781,10 +1781,10 @@ App::post('/v1/account/tokens/magic-url')
|
|||
->inject('queueForEvents')
|
||||
->inject('queueForMails')
|
||||
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) {
|
||||
|
||||
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
|
||||
}
|
||||
$url = htmlentities($url);
|
||||
|
||||
if ($phrase === true) {
|
||||
$phrase = Phrase::generate();
|
||||
|
|
@ -2981,6 +2981,7 @@ App::post('/v1/account/recovery')
|
|||
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
|
||||
}
|
||||
$url = htmlentities($url);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
|
|
@ -3244,6 +3245,7 @@ App::post('/v1/account/verification')
|
|||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
|
||||
}
|
||||
|
||||
$url = htmlentities($url);
|
||||
if ($user->getAttribute('emailVerification')) {
|
||||
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
|
||||
}
|
||||
|
|
@ -3565,7 +3567,7 @@ App::post('/v1/account/verification/phone')
|
|||
});
|
||||
|
||||
App::put('/v1/account/verification/phone')
|
||||
->desc('Create phone verification (confirmation)')
|
||||
->desc('Update phone verification (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'public')
|
||||
->label('event', 'users.[userId].verification.[tokenId].update')
|
||||
|
|
@ -3708,7 +3710,7 @@ App::get('/v1/account/mfa/factors')
|
|||
});
|
||||
|
||||
App::post('/v1/account/mfa/authenticators/:type')
|
||||
->desc('Add Authenticator')
|
||||
->desc('Create Authenticator')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
|
|
@ -3996,7 +3998,7 @@ App::delete('/v1/account/mfa/authenticators/:type')
|
|||
});
|
||||
|
||||
App::post('/v1/account/mfa/challenge')
|
||||
->desc('Create 2FA Challenge')
|
||||
->desc('Create MFA Challenge')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'users.[userId].challenges.[challengeId].create')
|
||||
|
|
|
|||
|
|
@ -2943,17 +2943,26 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
|
||||
$processDocument($collection, $document);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($document, Response::MODEL_DOCUMENT);
|
||||
|
||||
$relationships = \array_map(
|
||||
fn ($document) => $document->getAttribute('key'),
|
||||
\array_filter(
|
||||
$collection->getAttribute('attributes', []),
|
||||
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
|
||||
)
|
||||
);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('databaseId', $databaseId)
|
||||
->setParam('collectionId', $collection->getId())
|
||||
->setParam('documentId', $document->getId())
|
||||
->setContext('collection', $collection)
|
||||
->setContext('database', $database)
|
||||
;
|
||||
->setPayload($response->getPayload(), sensitive: $relationships);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($document, Response::MODEL_DOCUMENT);
|
||||
});
|
||||
|
||||
App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
|
|
@ -3524,15 +3533,23 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
|||
|
||||
$processDocument($collection, $document);
|
||||
|
||||
$response->dynamic($document, Response::MODEL_DOCUMENT);
|
||||
|
||||
$relationships = \array_map(
|
||||
fn ($document) => $document->getAttribute('key'),
|
||||
\array_filter(
|
||||
$collection->getAttribute('attributes', []),
|
||||
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
|
||||
)
|
||||
);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('databaseId', $databaseId)
|
||||
->setParam('collectionId', $collection->getId())
|
||||
->setParam('documentId', $document->getId())
|
||||
->setContext('collection', $collection)
|
||||
->setContext('database', $database)
|
||||
;
|
||||
|
||||
$response->dynamic($document, Response::MODEL_DOCUMENT);
|
||||
->setPayload($response->getPayload(), sensitive: $relationships);
|
||||
});
|
||||
|
||||
App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId')
|
||||
|
|
@ -3628,6 +3645,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
|||
|
||||
$processDocument($collection, $document);
|
||||
|
||||
$relationships = \array_map(
|
||||
fn ($document) => $document->getAttribute('key'),
|
||||
\array_filter(
|
||||
$collection->getAttribute('attributes', []),
|
||||
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
|
||||
)
|
||||
);
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_AUDIT)
|
||||
->setDocument($document);
|
||||
|
|
@ -3638,7 +3663,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
|||
->setParam('documentId', $document->getId())
|
||||
->setContext('collection', $collection)
|
||||
->setContext('database', $database)
|
||||
->setPayload($response->output($document, Response::MODEL_DOCUMENT));
|
||||
->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships);
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -531,6 +531,8 @@ App::get('/v1/functions/:functionId/usage')
|
|||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS),
|
||||
str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS)
|
||||
];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
|
|
@ -593,6 +595,10 @@ App::get('/v1/functions/:functionId/usage')
|
|||
'buildsTime' => $usage[$metrics[4]]['data'],
|
||||
'executions' => $usage[$metrics[5]]['data'],
|
||||
'executionsTime' => $usage[$metrics[6]]['data'],
|
||||
'buildsMbSecondsTotal' => $usage[$metrics[7]]['total'],
|
||||
'buildsMbSeconds' => $usage[$metrics[7]]['data'],
|
||||
'executionsMbSeconds' => $usage[$metrics[8]]['data'],
|
||||
'executionsMbSecondsTotal' => $usage[$metrics[8]]['total']
|
||||
]), Response::MODEL_USAGE_FUNCTION);
|
||||
});
|
||||
|
||||
|
|
@ -623,6 +629,8 @@ App::get('/v1/functions/usage')
|
|||
METRIC_BUILDS_COMPUTE,
|
||||
METRIC_EXECUTIONS,
|
||||
METRIC_EXECUTIONS_COMPUTE,
|
||||
METRIC_BUILDS_MB_SECONDS,
|
||||
METRIC_EXECUTIONS_MB_SECONDS,
|
||||
];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
|
||||
|
|
@ -686,6 +694,10 @@ App::get('/v1/functions/usage')
|
|||
'buildsTime' => $usage[$metrics[5]]['data'],
|
||||
'executions' => $usage[$metrics[6]]['data'],
|
||||
'executionsTime' => $usage[$metrics[7]]['data'],
|
||||
'buildsMbSecondsTotal' => $usage[$metrics[8]]['total'],
|
||||
'buildsMbSeconds' => $usage[$metrics[8]]['data'],
|
||||
'executionsMbSeconds' => $usage[$metrics[9]]['data'],
|
||||
'executionsMbSecondsTotal' => $usage[$metrics[9]]['total'],
|
||||
]), Response::MODEL_USAGE_FUNCTIONS);
|
||||
});
|
||||
|
||||
|
|
@ -872,7 +884,7 @@ App::put('/v1/functions/:functionId')
|
|||
|
||||
App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Download Deployment')
|
||||
->desc('Download deployment')
|
||||
->label('scope', 'functions.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'functions')
|
||||
|
|
@ -957,7 +969,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
|||
|
||||
App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Update function deployment')
|
||||
->desc('Update deployment')
|
||||
->label('scope', 'functions.write')
|
||||
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
|
||||
->label('audits.event', 'deployment.update')
|
||||
|
|
@ -1956,6 +1968,8 @@ App::post('/v1/functions/:functionId/executions')
|
|||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
;
|
||||
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
|
|
@ -2432,6 +2446,7 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
|||
});
|
||||
|
||||
App::get('/v1/functions/templates')
|
||||
->groups(['api'])
|
||||
->desc('List function templates')
|
||||
->label('scope', 'public')
|
||||
->label('sdk.namespace', 'functions')
|
||||
|
|
|
|||
|
|
@ -40,18 +40,23 @@ App::get('/v1/project/usage')
|
|||
$metrics = [
|
||||
'total' => [
|
||||
METRIC_EXECUTIONS,
|
||||
METRIC_EXECUTIONS_MB_SECONDS,
|
||||
METRIC_BUILDS_MB_SECONDS,
|
||||
METRIC_DOCUMENTS,
|
||||
METRIC_DATABASES,
|
||||
METRIC_USERS,
|
||||
METRIC_BUCKETS,
|
||||
METRIC_FILES_STORAGE
|
||||
METRIC_FILES_STORAGE,
|
||||
METRIC_DEPLOYMENTS_STORAGE
|
||||
],
|
||||
'period' => [
|
||||
METRIC_NETWORK_REQUESTS,
|
||||
METRIC_NETWORK_INBOUND,
|
||||
METRIC_NETWORK_OUTBOUND,
|
||||
METRIC_USERS,
|
||||
METRIC_EXECUTIONS
|
||||
METRIC_EXECUTIONS,
|
||||
METRIC_EXECUTIONS_MB_SECONDS,
|
||||
METRIC_BUILDS_MB_SECONDS
|
||||
]
|
||||
];
|
||||
|
||||
|
|
@ -144,6 +149,54 @@ App::get('/v1/project/usage')
|
|||
];
|
||||
}, $dbForProject->find('buckets'));
|
||||
|
||||
$deploymentsStorageBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
return [
|
||||
'resourceId' => $id,
|
||||
'name' => $name,
|
||||
'value' => $value['value'] ?? 0,
|
||||
];
|
||||
}, $dbForProject->find('functions'));
|
||||
|
||||
$executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
return [
|
||||
'resourceId' => $id,
|
||||
'name' => $name,
|
||||
'value' => $value['value'] ?? 0,
|
||||
];
|
||||
}, $dbForProject->find('functions'));
|
||||
|
||||
$buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
return [
|
||||
'resourceId' => $id,
|
||||
'name' => $name,
|
||||
'value' => $value['value'] ?? 0,
|
||||
];
|
||||
}, $dbForProject->find('functions'));
|
||||
|
||||
// merge network inbound + outbound
|
||||
$projectBandwidth = [];
|
||||
foreach ($usage[METRIC_NETWORK_INBOUND] as $item) {
|
||||
|
|
@ -171,13 +224,19 @@ App::get('/v1/project/usage')
|
|||
'users' => ($usage[METRIC_USERS]),
|
||||
'executions' => ($usage[METRIC_EXECUTIONS]),
|
||||
'executionsTotal' => $total[METRIC_EXECUTIONS],
|
||||
'executionsMbSecondsTotal' => $total[METRIC_EXECUTIONS_MB_SECONDS],
|
||||
'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS],
|
||||
'documentsTotal' => $total[METRIC_DOCUMENTS],
|
||||
'databasesTotal' => $total[METRIC_DATABASES],
|
||||
'usersTotal' => $total[METRIC_USERS],
|
||||
'bucketsTotal' => $total[METRIC_BUCKETS],
|
||||
'filesStorageTotal' => $total[METRIC_FILES_STORAGE],
|
||||
'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE],
|
||||
'executionsBreakdown' => $executionsBreakdown,
|
||||
'bucketsBreakdown' => $bucketsBreakdown
|
||||
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
|
||||
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
|
||||
'bucketsBreakdown' => $bucketsBreakdown,
|
||||
'deploymentsStorageBreakdown' => $deploymentsStorageBreakdown,
|
||||
]), Response::MODEL_USAGE_PROJECT);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ App::init()
|
|||
App::post('/v1/projects')
|
||||
->desc('Create project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'projects.create')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
|
|
@ -902,6 +903,7 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
|
|||
App::delete('/v1/projects/:projectId')
|
||||
->desc('Delete project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'projects.delete')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
|
|
@ -1433,6 +1435,7 @@ App::post('/v1/projects/:projectId/jwts')
|
|||
App::post('/v1/projects/:projectId/platforms')
|
||||
->desc('Create platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'platforms.create')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
|
|
@ -1596,6 +1599,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
|||
App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
->desc('Delete platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'platforms.delete')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ App::post('/v1/proxy/rules')
|
|||
}
|
||||
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
if (str_ends_with($domain, $functionsDomain)) {
|
||||
if ($functionsDomain != '' && str_ends_with($domain, $functionsDomain)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -401,6 +401,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
$url = htmlentities($url);
|
||||
if (empty($url)) {
|
||||
if (!$isAPIKey && !$isPrivilegedUser) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'URL is required');
|
||||
|
|
@ -668,6 +669,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $invitee->getId())
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId())
|
||||
;
|
||||
|
|
@ -901,6 +903,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
|||
$dbForProject->purgeCachedDocument('users', $profile->getId());
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId());
|
||||
|
||||
|
|
@ -1026,6 +1029,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId())
|
||||
;
|
||||
|
|
@ -1107,6 +1111,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
|||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('membershipId', $membership->getId())
|
||||
->setPayload($response->output($membership, Response::MODEL_MEMBERSHIP))
|
||||
|
|
|
|||
|
|
@ -329,6 +329,10 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
|
|||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->setProject($project)
|
||||
->trigger()
|
||||
;
|
||||
|
||||
if ($function->getAttribute('logging')) {
|
||||
|
|
@ -815,7 +819,6 @@ App::error()
|
|||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
$log->addExtra('roles', Authorization::getRoles());
|
||||
|
||||
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
|
||||
|
|
|
|||
|
|
@ -508,7 +508,12 @@ App::init()
|
|||
->setContentType($cacheLog->getAttribute('mimeType'))
|
||||
->send($data);
|
||||
} else {
|
||||
$response->addHeader('X-Appwrite-Cache', 'miss');
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->addHeader('Expires', 0)
|
||||
->addHeader('X-Appwrite-Cache', 'miss')
|
||||
;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -290,7 +290,6 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$log->addExtra('file', $th->getFile());
|
||||
$log->addExtra('line', $th->getLine());
|
||||
$log->addExtra('trace', $th->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $th->getTrace());
|
||||
$log->addExtra('roles', Authorization::getRoles());
|
||||
|
||||
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ const METRIC_BUILDS_STORAGE = 'builds.storage';
|
|||
const METRIC_BUILDS_COMPUTE = 'builds.compute';
|
||||
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
|
||||
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
|
||||
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
|
||||
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
|
||||
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
|
||||
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
|
||||
|
|
@ -252,10 +253,13 @@ const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.
|
|||
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
|
||||
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
|
||||
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
|
||||
const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds';
|
||||
const METRIC_EXECUTIONS = 'executions';
|
||||
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
|
||||
const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
|
||||
const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds';
|
||||
const METRIC_NETWORK_REQUESTS = 'network.requests';
|
||||
const METRIC_NETWORK_INBOUND = 'network.inbound';
|
||||
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ $logError = function (Throwable $error, string $action) use ($register) {
|
|||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
|
|
@ -381,8 +380,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
$user = $database->getDocument('users', $userId);
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
$channels = $realtime->connections[$connection]['channels'];
|
||||
|
||||
$realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']);
|
||||
$realtime->unsubscribe($connection);
|
||||
$realtime->subscribe($projectId, $connection, $roles, $channels);
|
||||
|
||||
$register->get('pools')->reclaim();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,7 +341,6 @@ $worker
|
|||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
$log->addExtra('roles', Authorization::getRoles());
|
||||
|
||||
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.12.*",
|
||||
"utopia-php/migration": "0.4.*",
|
||||
"utopia-php/migration": "0.5.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.5.*",
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
"utopia-php/storage": "0.18.*",
|
||||
"utopia-php/swoole": "0.8.*",
|
||||
"utopia-php/system": "0.8.*",
|
||||
"utopia-php/vcs": "dev-fix-unauthorized-clone as 0.7.99",
|
||||
"utopia-php/vcs": "0.8.*",
|
||||
"utopia-php/websocket": "0.1.*",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
"dragonmantank/cron-expression": "3.3.2",
|
||||
|
|
|
|||
27
composer.lock
generated
27
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "15ad1b6580df488ad53dee9760b4edde",
|
||||
"content-hash": "340aae0879435fc71eac688d47033eb4",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
|
@ -2172,16 +2172,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.4.4",
|
||||
"version": "0.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33"
|
||||
"reference": "f18d44d4459f78c292dac9edde856fd156fe497a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33",
|
||||
"reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a",
|
||||
"reference": "f18d44d4459f78c292dac9edde856fd156fe497a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2191,6 +2191,7 @@
|
|||
"require-dev": {
|
||||
"laravel/pint": "1.*",
|
||||
"phpunit/phpunit": "9.*",
|
||||
"utopia-php/cli": "^0.18.0",
|
||||
"vlucas/phpdotenv": "5.*"
|
||||
},
|
||||
"type": "library",
|
||||
|
|
@ -2213,9 +2214,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.4.4"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.5.2"
|
||||
},
|
||||
"time": "2024-05-17T05:25:31+00:00"
|
||||
"time": "2024-07-22T09:27:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
|
|
@ -2758,16 +2759,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/vcs",
|
||||
"version": "dev-fix-unauthorized-clone",
|
||||
"version": "0.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"reference": "515e796de71982f485aed6b43753ea9eb80c692b"
|
||||
"reference": "3084aa93d24ed1e70f01e75f4318fc0d07f12596"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/515e796de71982f485aed6b43753ea9eb80c692b",
|
||||
"reference": "515e796de71982f485aed6b43753ea9eb80c692b",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/3084aa93d24ed1e70f01e75f4318fc0d07f12596",
|
||||
"reference": "3084aa93d24ed1e70f01e75f4318fc0d07f12596",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2801,9 +2802,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/vcs/issues",
|
||||
"source": "https://github.com/utopia-php/vcs/tree/fix-unauthorized-clone"
|
||||
"source": "https://github.com/utopia-php/vcs/tree/0.8.1"
|
||||
},
|
||||
"time": "2024-08-13T10:33:07+00:00"
|
||||
"time": "2024-07-29T20:49:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/websocket",
|
||||
|
|
|
|||
|
|
@ -86,6 +86,9 @@ abstract class Migration
|
|||
'1.5.5' => 'V20',
|
||||
'1.5.6' => 'V20',
|
||||
'1.5.7' => 'V20',
|
||||
'1.5.8' => 'V20',
|
||||
'1.5.9' => 'V20',
|
||||
'1.5.10' => 'V20',
|
||||
'1.6.0' => 'V21'
|
||||
];
|
||||
|
||||
|
|
@ -171,29 +174,27 @@ abstract class Migration
|
|||
|
||||
Console::log('Migrating Collection ' . $collection['$id'] . ':');
|
||||
|
||||
\Co\run(function (array $collection, callable $callback) {
|
||||
foreach ($this->documentsIterator($collection['$id']) as $document) {
|
||||
go(function (Document $document, callable $callback) {
|
||||
if (empty($document->getId()) || empty($document->getCollection())) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->documentsIterator($collection['$id']) as $document) {
|
||||
go(function (Document $document, callable $callback) {
|
||||
if (empty($document->getId()) || empty($document->getCollection())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$old = $document->getArrayCopy();
|
||||
$new = call_user_func($callback, $document);
|
||||
$old = $document->getArrayCopy();
|
||||
$new = call_user_func($callback, $document);
|
||||
|
||||
if (is_null($new) || $new->getArrayCopy() == $old) {
|
||||
return;
|
||||
}
|
||||
if (is_null($new) || $new->getArrayCopy() == $old) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to update document: ' . $th->getMessage());
|
||||
return;
|
||||
}
|
||||
}, $document, $callback);
|
||||
}
|
||||
}, $collection, $callback);
|
||||
try {
|
||||
$this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to update document: ' . $th->getMessage());
|
||||
return;
|
||||
}
|
||||
}, $document, $callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -816,29 +816,27 @@ class V19 extends Migration
|
|||
|
||||
Console::log('Migrating Collection ' . $collection['$id'] . ':');
|
||||
|
||||
\Co\run(function (array $collection, callable $callback) {
|
||||
foreach ($this->documentsIterator($collection['$id']) as $document) {
|
||||
go(function (Document $document, callable $callback) {
|
||||
if (empty($document->getId()) || empty($document->getCollection())) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->documentsIterator($collection['$id']) as $document) {
|
||||
go(function (Document $document, callable $callback) {
|
||||
if (empty($document->getId()) || empty($document->getCollection())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$old = $document->getArrayCopy();
|
||||
$new = call_user_func($callback, $document);
|
||||
$old = $document->getArrayCopy();
|
||||
$new = call_user_func($callback, $document);
|
||||
|
||||
if (is_null($new) || $new->getArrayCopy() == $old) {
|
||||
return;
|
||||
}
|
||||
if (is_null($new) || $new->getArrayCopy() == $old) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to update document: ' . $th->getMessage());
|
||||
return;
|
||||
}
|
||||
}, $document, $callback);
|
||||
}
|
||||
}, $collection, $callback);
|
||||
try {
|
||||
$this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to update document: ' . $th->getMessage());
|
||||
return;
|
||||
}
|
||||
}, $document, $callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,11 +92,11 @@ class V21 extends Migration
|
|||
Console::warning("'scopes' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
// Create size attribute
|
||||
// Create specification attribute
|
||||
try {
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'size');
|
||||
$this->createAttributeFromCollection($this->projectDB, $id, 'specification');
|
||||
} catch (Throwable $th) {
|
||||
Console::warning("'size' from {$id}: {$th->getMessage()}");
|
||||
Console::warning("'specification' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -177,7 +177,7 @@ class V21 extends Migration
|
|||
$document->setAttribute('scopes', []);
|
||||
|
||||
// Add size attribute
|
||||
$document->setAttribute('size', 's-1vcpu-512m');
|
||||
$document->setAttribute('specification', APP_FUNCTION_BASE_SPECIFICATION);
|
||||
}
|
||||
|
||||
return $document;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Appwrite\Migration\Migration;
|
||||
use Redis;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
|
@ -12,10 +12,13 @@ use Utopia\Database\Query;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Migrate extends Action
|
||||
{
|
||||
protected Redis $redis;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'migrate';
|
||||
|
|
@ -27,31 +30,53 @@ class Migrate extends Action
|
|||
->desc('Migrate Appwrite to new version')
|
||||
/** @TODO APP_VERSION_STABLE needs to be defined */
|
||||
->param('version', APP_VERSION_STABLE, new Text(8), 'Version to migrate to.', true)
|
||||
->inject('cache')
|
||||
->inject('dbForConsole')
|
||||
->inject('getProjectDB')
|
||||
->inject('register')
|
||||
->callback(fn ($version, $cache, $dbForConsole, $getProjectDB, Registry $register) => $this->action($version, $cache, $dbForConsole, $getProjectDB, $register));
|
||||
->callback(function ($version, $dbForConsole, $getProjectDB, Registry $register) {
|
||||
\Co\run(function () use ($version, $dbForConsole, $getProjectDB, $register) {
|
||||
$this->action($version, $dbForConsole, $getProjectDB, $register);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function clearProjectsCache(Cache $cache, Document $project)
|
||||
private function clearProjectsCache(Document $project)
|
||||
{
|
||||
try {
|
||||
$cache->purge("cache-_{$project->getInternalId()}:*");
|
||||
$iterator = null;
|
||||
do {
|
||||
$pattern = "default-cache-_{$project->getInternalId()}:*";
|
||||
$keys = $this->redis->scan($iterator, $pattern, 1000);
|
||||
if ($keys !== false) {
|
||||
foreach ($keys as $key) {
|
||||
$this->redis->del($key);
|
||||
}
|
||||
}
|
||||
} while ($iterator > 0);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to clear project ("' . $project->getId() . '") cache with error: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function action(string $version, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register)
|
||||
public function action(string $version, Database $dbForConsole, callable $getProjectDB, Registry $register)
|
||||
{
|
||||
Authorization::disable();
|
||||
if (!array_key_exists($version, Migration::$versions)) {
|
||||
Console::error("Version {$version} not found.");
|
||||
Console::exit(1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->redis = new Redis();
|
||||
$this->redis->connect(
|
||||
System::getEnv('_APP_REDIS_HOST', null),
|
||||
System::getEnv('_APP_REDIS_PORT', 6379),
|
||||
3,
|
||||
null,
|
||||
10
|
||||
);
|
||||
|
||||
$app = new App('UTC');
|
||||
|
||||
Console::success('Starting Data Migration to version ' . $version);
|
||||
|
|
@ -87,7 +112,7 @@ class Migrate extends Action
|
|||
continue;
|
||||
}
|
||||
|
||||
$this->clearProjectsCache($cache, $project);
|
||||
$this->clearProjectsCache($project);
|
||||
|
||||
try {
|
||||
// TODO: Iterate through all project DBs
|
||||
|
|
@ -103,7 +128,7 @@ class Migrate extends Action
|
|||
throw $th;
|
||||
}
|
||||
|
||||
$this->clearProjectsCache($cache, $project);
|
||||
$this->clearProjectsCache($project);
|
||||
}
|
||||
|
||||
$sum = \count($projects);
|
||||
|
|
|
|||
|
|
@ -212,20 +212,20 @@ class Builds extends Action
|
|||
|
||||
$templateRepositoryName = $template->getAttribute('repositoryName', '');
|
||||
$templateOwnerName = $template->getAttribute('ownerName', '');
|
||||
$templateBranch = $template->getAttribute('branch', '');
|
||||
$tempalteVersion = $template->getAttribute('version', '');
|
||||
|
||||
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
|
||||
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
||||
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) {
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($tempalteVersion)) {
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
// Clone template repo
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd($buildId) . '-template';
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $tempalteVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
|
|
@ -282,8 +282,14 @@ class Builds extends Action
|
|||
$branchName = $deployment->getAttribute('providerBranch');
|
||||
$commitHash = $deployment->getAttribute('providerCommitHash', '');
|
||||
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($cloneOwner, $cloneRepository, $commitHash, GitHub::CLONE_TYPE_COMMIT, $tmpDirectory, $rootDirectory . '/*');
|
||||
$cloneVersion = $branchName;
|
||||
$cloneType = GitHub::CLONE_TYPE_BRANCH;
|
||||
if(!empty($commitHash)) {
|
||||
$cloneVersion = $commitHash;
|
||||
$cloneType = GitHub::CLONE_TYPE_COMMIT;
|
||||
}
|
||||
|
||||
$gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $cloneVersion, $cloneType, $tmpDirectory, $rootDirectory);
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr);
|
||||
|
|
@ -293,7 +299,7 @@ class Builds extends Action
|
|||
return;
|
||||
}
|
||||
|
||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
$exit = Console::execute($gitCloneCommand, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to clone code repository: ' . $stderr);
|
||||
|
|
@ -316,17 +322,18 @@ class Builds extends Action
|
|||
// Build from template
|
||||
$templateRepositoryName = $template->getAttribute('repositoryName', '');
|
||||
$templateOwnerName = $template->getAttribute('ownerName', '');
|
||||
$templateBranch = $template->getAttribute('branch', '');
|
||||
$tempalteVersion = $template->getAttribute('version', '');
|
||||
|
||||
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
|
||||
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
||||
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) {
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($tempalteVersion)) {
|
||||
// Clone template repo
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd($buildId) . '/template';
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $tempalteVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
|
|
@ -404,6 +411,9 @@ class Builds extends Action
|
|||
throw new \Exception("Unable to move file");
|
||||
}
|
||||
|
||||
$deployment->setAttribute('path', $path);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
Console::execute('rm -rf ' . $tmpPath, '', $stdout, $stderr);
|
||||
|
||||
$source = $path;
|
||||
|
|
@ -680,9 +690,11 @@ class Builds extends Action
|
|||
->addMetric(METRIC_BUILDS, 1) // per project
|
||||
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
|
||||
->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(METRIC_BUILDS_MB_SECONDS, (int)(512 * $build->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS), (int)(512 * $build->getAttribute('duration', 0)))
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -529,6 +529,8 @@ class Functions extends Action
|
|||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
|
||||
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000))
|
||||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(512 * $execution->getAttribute('duration', 0)))
|
||||
->trigger()
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,11 +300,11 @@ class Migrations extends Action
|
|||
$errorMessages = [];
|
||||
foreach ($sourceErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceType()}:{$error->getResourceId()}' from source with message: '{$error->getMessage()}'";
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceGroup()}:{$error->getResourceId()}' from source with message: '{$error->getMessage()}'";
|
||||
}
|
||||
foreach ($destinationErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceType()}:{$error->getResourceId()}' to destination with message: '{$error->getMessage()}'";
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceGroup()}:{$error->getResourceId()}' to destination with message: '{$error->getMessage()}'";
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
|
|
@ -337,11 +337,11 @@ class Migrations extends Action
|
|||
$errorMessages = [];
|
||||
foreach ($sourceErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceType()}:{$error->getResourceId()}' from source with message '{$error->getMessage()}'";
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceGroup()}:{$error->getResourceId()}' from source with message '{$error->getMessage()}'";
|
||||
}
|
||||
foreach ($destinationErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceType()}:{$error->getResourceId()}' to destination with message '{$error->getMessage()}'";
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceGroup()}:{$error->getResourceId()}' to destination with message '{$error->getMessage()}'";
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ class UsageFunction extends Model
|
|||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('buildsMbSecondsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated sum of function builds mbSeconds.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('executionsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated number of function executions.',
|
||||
|
|
@ -58,6 +64,12 @@ class UsageFunction extends Model
|
|||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('executionsMbSecondsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated sum of function executions mbSeconds.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('deployments', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of function deployments per period.',
|
||||
|
|
@ -93,6 +105,13 @@ class UsageFunction extends Model
|
|||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsMbSeconds', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of function builds mbSeconds per period.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executions', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of function executions per period.',
|
||||
|
|
@ -108,6 +127,13 @@ class UsageFunction extends Model
|
|||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsMbSeconds', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of function mbSeconds per period.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ class UsageFunctions extends Model
|
|||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('buildsMbSecondsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated sum of functions build mbSeconds.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('executionsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated number of functions execution.',
|
||||
|
|
@ -64,6 +70,12 @@ class UsageFunctions extends Model
|
|||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('executionsMbSecondsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated sum of functions execution mbSeconds.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('functions', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of functions per period.',
|
||||
|
|
@ -106,6 +118,13 @@ class UsageFunctions extends Model
|
|||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsMbSeconds', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated sum of functions build mbSeconds per period.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executions', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of functions execution per period.',
|
||||
|
|
@ -121,6 +140,13 @@ class UsageFunctions extends Model
|
|||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsMbSeconds', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of functions mbSeconds per period.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,12 +40,30 @@ class UsageProject extends Model
|
|||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('deploymentsStorageTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated sum of deployments storage size (in bytes).',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('bucketsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated number of buckets.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('executionsMbSecondsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated number of function executions mbSeconds.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('buildsMbSecondsTotal', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Total aggregated number of function builds mbSeconds.',
|
||||
'default' => 0,
|
||||
'example' => 0,
|
||||
])
|
||||
->addRule('requests', [
|
||||
'type' => Response::MODEL_METRIC,
|
||||
'description' => 'Aggregated number of requests per period.',
|
||||
|
|
@ -88,6 +106,27 @@ class UsageProject extends Model
|
|||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('executionsMbSecondsBreakdown', [
|
||||
'type' => Response::MODEL_METRIC_BREAKDOWN,
|
||||
'description' => 'Aggregated breakdown in totals of execution mbSeconds by functions.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('buildsMbSecondsBreakdown', [
|
||||
'type' => Response::MODEL_METRIC_BREAKDOWN,
|
||||
'description' => 'Aggregated breakdown in totals of build mbSeconds by functions.',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
->addRule('deploymentsStorageBreakdown', [
|
||||
'type' => Response::MODEL_METRIC_BREAKDOWN,
|
||||
'description' => 'Aggregated breakdown in totals of deployments storage size (in bytes).',
|
||||
'default' => [],
|
||||
'example' => [],
|
||||
'array' => true
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class UsageTest extends Scope
|
|||
);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(12, count($response['body']));
|
||||
$this->assertEquals(18, count($response['body']));
|
||||
$this->validateDates($response['body']['network']);
|
||||
$this->validateDates($response['body']['requests']);
|
||||
$this->validateDates($response['body']['users']);
|
||||
|
|
@ -321,7 +321,7 @@ class UsageTest extends Scope
|
|||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(12, count($response['body']));
|
||||
$this->assertEquals(18, count($response['body']));
|
||||
$this->assertEquals(1, count($response['body']['requests']));
|
||||
$this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']);
|
||||
$this->validateDates($response['body']['requests']);
|
||||
|
|
@ -542,7 +542,7 @@ class UsageTest extends Scope
|
|||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(12, count($response['body']));
|
||||
$this->assertEquals(18, count($response['body']));
|
||||
$this->assertEquals(1, count($response['body']['requests']));
|
||||
$this->assertEquals(1, count($response['body']['network']));
|
||||
$this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']);
|
||||
|
|
@ -774,14 +774,19 @@ class UsageTest extends Scope
|
|||
);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(15, count($response['body']));
|
||||
$this->assertEquals(19, count($response['body']));
|
||||
$this->assertEquals('30d', $response['body']['range']);
|
||||
$this->assertIsArray($response['body']['deployments']);
|
||||
$this->assertIsArray($response['body']['deploymentsStorage']);
|
||||
$this->assertIsNumeric($response['body']['deploymentsStorageTotal']);
|
||||
$this->assertIsNumeric($response['body']['buildsMbSecondsTotal']);
|
||||
$this->assertIsNumeric($response['body']['executionsMbSecondsTotal']);
|
||||
$this->assertIsArray($response['body']['builds']);
|
||||
$this->assertIsArray($response['body']['buildsTime']);
|
||||
$this->assertIsArray($response['body']['buildsMbSeconds']);
|
||||
$this->assertIsArray($response['body']['executions']);
|
||||
$this->assertIsArray($response['body']['executionsTime']);
|
||||
$this->assertIsArray($response['body']['executionsMbSeconds']);
|
||||
$this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']);
|
||||
$this->validateDates($response['body']['executions']);
|
||||
$this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']);
|
||||
|
|
@ -794,15 +799,17 @@ class UsageTest extends Scope
|
|||
);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(17, count($response['body']));
|
||||
$this->assertEquals(21, count($response['body']));
|
||||
$this->assertEquals($response['body']['range'], '30d');
|
||||
$this->assertIsArray($response['body']['functions']);
|
||||
$this->assertIsArray($response['body']['deployments']);
|
||||
$this->assertIsArray($response['body']['deploymentsStorage']);
|
||||
$this->assertIsArray($response['body']['builds']);
|
||||
$this->assertIsArray($response['body']['buildsTime']);
|
||||
$this->assertIsArray($response['body']['buildsMbSeconds']);
|
||||
$this->assertIsArray($response['body']['executions']);
|
||||
$this->assertIsArray($response['body']['executionsTime']);
|
||||
$this->assertIsArray($response['body']['executionsMbSeconds']);
|
||||
$this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']);
|
||||
$this->validateDates($response['body']['executions']);
|
||||
$this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']);
|
||||
|
|
|
|||
|
|
@ -4754,7 +4754,7 @@ trait DatabasesBase
|
|||
|
||||
$this->assertEquals($longtext['headers']['status-code'], 202);
|
||||
|
||||
for ($i = 0; $i < 1; $i++) {
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $data['databaseId'] . '/collections/' . $data['$id'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
|
|
|||
|
|
@ -92,23 +92,27 @@ class FunctionsConsoleClientTest extends Scope
|
|||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(15, count($response['body']));
|
||||
$this->assertEquals(19, count($response['body']));
|
||||
$this->assertEquals('24h', $response['body']['range']);
|
||||
$this->assertIsNumeric($response['body']['deploymentsTotal']);
|
||||
$this->assertIsNumeric($response['body']['deploymentsStorageTotal']);
|
||||
$this->assertIsNumeric($response['body']['buildsTotal']);
|
||||
$this->assertIsNumeric($response['body']['buildsStorageTotal']);
|
||||
$this->assertIsNumeric($response['body']['buildsTimeTotal']);
|
||||
$this->assertIsNumeric($response['body']['buildsMbSecondsTotal']);
|
||||
$this->assertIsNumeric($response['body']['executionsTotal']);
|
||||
$this->assertIsNumeric($response['body']['executionsTimeTotal']);
|
||||
$this->assertIsNumeric($response['body']['executionsMbSecondsTotal']);
|
||||
$this->assertIsArray($response['body']['deployments']);
|
||||
$this->assertIsArray($response['body']['deploymentsStorage']);
|
||||
$this->assertIsArray($response['body']['builds']);
|
||||
$this->assertIsArray($response['body']['buildsTime']);
|
||||
$this->assertIsArray($response['body']['buildsStorage']);
|
||||
$this->assertIsArray($response['body']['buildsTime']);
|
||||
$this->assertIsArray($response['body']['buildsMbSeconds']);
|
||||
$this->assertIsArray($response['body']['executions']);
|
||||
$this->assertIsArray($response['body']['executionsTime']);
|
||||
$this->assertIsArray($response['body']['executionsMbSeconds']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
|
@ -2037,6 +2038,20 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($cookie, $response['body']);
|
||||
|
||||
// Await Aggregation
|
||||
sleep(App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30));
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'range' => '24h'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(19, count($response['body']));
|
||||
$this->assertEquals('24h', $response['body']['range']);
|
||||
|
||||
// Cleanup : Delete function
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
|
||||
'content-type' => 'application/json',
|
||||
|
|
|
|||
|
|
@ -484,6 +484,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertIsNumeric($response['body']['bucketsTotal']);
|
||||
$this->assertIsNumeric($response['body']['usersTotal']);
|
||||
$this->assertIsNumeric($response['body']['filesStorageTotal']);
|
||||
$this->assertIsNumeric($response['body']['deploymentStorageTotal']);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Teams;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
|
|
@ -12,4 +13,55 @@ class TeamsCustomClientTest extends Scope
|
|||
use TeamsBaseClient;
|
||||
use ProjectCustom;
|
||||
use SideClient;
|
||||
|
||||
/**
|
||||
* @depends testUpdateTeamMembership
|
||||
*/
|
||||
public function testTeamsInviteHTMLInjection($data): array
|
||||
{
|
||||
$teamUid = $data['teamUid'] ?? '';
|
||||
$email = uniqid() . 'friend@localhost.test';
|
||||
$name = 'Friend User';
|
||||
$password = 'password';
|
||||
|
||||
// Create a user account before we create a invite so we can check if the user has permissions when it shouldn't
|
||||
$user = $this->client->call(Client::METHOD_POST, '/account', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console'], [
|
||||
'userId' => 'unique()',
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
], false);
|
||||
|
||||
$this->assertEquals(201, $user['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'email' => $email,
|
||||
'name' => $name,
|
||||
'roles' => ['admin', 'editor'],
|
||||
'url' => 'http://localhost:5000/join-us\"></a><h1>INJECTED</h1>'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
$email = $this->getLastEmail();
|
||||
$encoded = 'http://localhost:5000/join-us\"></a><h1>INJECTED</h1>?';
|
||||
|
||||
$this->assertStringNotContainsString('<h1>INJECTED</h1>', $email['html']);
|
||||
$this->assertStringContainsString($encoded, $email['html']);
|
||||
$this->assertStringContainsString($encoded, $email['text']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid . '/memberships/'.$response['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -999,6 +999,7 @@ trait UsersBase
|
|||
$this->assertEquals($user['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($user['body']['$id']);
|
||||
$this->assertEmpty($user['body']['password']);
|
||||
sleep(5);
|
||||
|
||||
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
||||
'content-type' => 'application/json',
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue