1
0
Fork 0
mirror of synced 2024-10-02 10:16:27 +13:00

Merge branch 'master' of https://github.com/appwrite/appwrite into feat-4803-flutter-web-platform-type

This commit is contained in:
Torsten Dittmann 2023-01-27 13:24:41 +01:00
commit 24a7caf936
32 changed files with 190 additions and 716 deletions

View file

@ -11,21 +11,17 @@ Happy contributing!
## What does this PR do? ## What does this PR do?
(Provide a description of what this PR does.) (Provide a description of what this PR does and why it's needed.)
## Test Plan ## Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.) (Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Screenshots may also be helpful.)
## Related PRs and Issues ## Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.) - (Related PR or issue)
### Have you added your change to the [Changelog](https://github.com/appwrite/appwrite/blob/master/CHANGES.md)? ## Checklist
(The CHANGES.md file tracks all the changes that make it to the `main` branch. Add your change to this file in the following format) - [ ] Have you read the [Contributing Guidelines on issues](https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md)?
- One line description of your PR [#pr_number](Link to your PR) - [ ] If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs?
### Have you read the [Contributing Guidelines on issues](https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md)?
(Write your answer here.)

Binary file not shown.

View file

@ -482,9 +482,9 @@ return [
], ],
[ [
'name' => '_APP_STORAGE_DEVICE', 'name' => '_APP_STORAGE_DEVICE',
'description' => 'Select default storage device. The default value is \'Local\'. List of supported adapters are \'Local\', \'S3\', \'DOSpaces\', \'Backblaze\', \'Linode\' and \'Wasabi\'.', 'description' => 'Select default storage device. The default value is \'local\'. List of supported adapters are \'local\', \'s3\', \'dospaces\', \'backblaze\', \'linode\' and \'wasabi\'.',
'introduction' => '0.13.0', 'introduction' => '0.13.0',
'default' => 'Local', 'default' => 'local',
'required' => false, 'required' => false,
'question' => '', 'question' => '',
], ],

View file

@ -10,8 +10,8 @@ use Appwrite\Event\Mail;
use Appwrite\Event\Phone as EventPhone; use Appwrite\Event\Phone as EventPhone;
use Appwrite\Extend\Exception; use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Host; use Utopia\Validator\Host;
use Appwrite\Network\Validator\URL; use Utopia\Validator\URL;
use Appwrite\OpenSSL\OpenSSL; use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Template\Template; use Appwrite\Template\Template;
use Appwrite\URL\URL as URLParser; use Appwrite\URL\URL as URLParser;

View file

@ -1,7 +1,7 @@
<?php <?php
use Appwrite\Extend\Exception; use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\URL; use Utopia\Validator\URL;
use Appwrite\URL\URL as URLParse; use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use chillerlan\QRCode\QRCode; use chillerlan\QRCode\QRCode;

View file

@ -33,8 +33,8 @@ use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Locale\Locale; use Utopia\Locale\Locale;
use Appwrite\Auth\Auth; use Appwrite\Auth\Auth;
use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP; use Utopia\Validator\IP;
use Appwrite\Network\Validator\URL; use Utopia\Validator\URL;
use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Query\Limit; use Appwrite\Utopia\Database\Validator\Query\Limit;
use Appwrite\Utopia\Database\Validator\Query\Offset; use Appwrite\Utopia\Database\Validator\Query\Offset;

View file

@ -6,9 +6,9 @@ use Appwrite\Event\Certificate;
use Appwrite\Event\Delete; use Appwrite\Event\Delete;
use Appwrite\Event\Validator\Event; use Appwrite\Event\Validator\Event;
use Appwrite\Network\Validator\CNAME; use Appwrite\Network\Validator\CNAME;
use Appwrite\Network\Validator\Domain as DomainValidator; use Utopia\Validator\Domain as DomainValidator;
use Appwrite\Network\Validator\Origin; use Appwrite\Network\Validator\Origin;
use Appwrite\Network\Validator\URL; use Utopia\Validator\URL;
use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Abuse\Adapters\TimeLimit;

View file

@ -65,7 +65,7 @@ App::post('/v1/storage/buckets')
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) ->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', 'none', new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true) ->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
->inject('response') ->inject('response')
@ -237,7 +237,7 @@ App::put('/v1/storage/buckets/:bucketId')
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true) ->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', 'none', new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true) ->param('compression', COMPRESSION_TYPE_NONE, new WhiteList([COMPRESSION_TYPE_NONE, COMPRESSION_TYPE_GZIP, COMPRESSION_TYPE_ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . COMPRESSION_TYPE_NONE . ', [' . COMPRESSION_TYPE_GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . COMPRESSION_TYPE_ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true)
->inject('response') ->inject('response')
@ -501,7 +501,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
} }
if ($chunksUploaded === $chunks) { if ($chunksUploaded === $chunks) {
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) { if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) === Storage::DEVICE_LOCAL) {
$antivirus = new Network( $antivirus = new Network(
App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
@ -516,14 +516,14 @@ App::post('/v1/storage/buckets/:bucketId/files')
$mimeType = $deviceFiles->getFileMimeType($path); // Get mime-type before compression and encryption $mimeType = $deviceFiles->getFileMimeType($path); // Get mime-type before compression and encryption
$data = ''; $data = '';
// Compression // Compression
$algorithm = $bucket->getAttribute('compression', 'none'); $algorithm = $bucket->getAttribute('compression', COMPRESSION_TYPE_NONE);
if ($fileSize <= APP_STORAGE_READ_BUFFER && $algorithm != 'none') { if ($fileSize <= APP_STORAGE_READ_BUFFER && $algorithm != COMPRESSION_TYPE_NONE) {
$data = $deviceFiles->read($path); $data = $deviceFiles->read($path);
switch ($algorithm) { switch ($algorithm) {
case 'zstd': case COMPRESSION_TYPE_ZSTD:
$compressor = new Zstd(); $compressor = new Zstd();
break; break;
case 'gzip': case COMPRESSION_TYPE_GZIP:
default: default:
$compressor = new GZIP(); $compressor = new GZIP();
break; break;

View file

@ -7,7 +7,7 @@ use Appwrite\Event\Event;
use Appwrite\Event\Mail; use Appwrite\Event\Mail;
use Appwrite\Extend\Exception; use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Host; use Utopia\Validator\Host;
use Appwrite\Template\Template; use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries; use Appwrite\Utopia\Database\Validator\Queries;

View file

@ -4,7 +4,7 @@ global $utopia, $request, $response;
use Appwrite\Extend\Exception; use Appwrite\Extend\Exception;
use Utopia\Database\Document; use Utopia\Database\Document;
use Appwrite\Network\Validator\Host; use Utopia\Validator\Host;
use Appwrite\Utopia\Request; use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model;

View file

@ -119,7 +119,7 @@ function logError(Throwable $error, string $action, Utopia\Route $route = null)
function getStorageDevice($root): Device function getStorageDevice($root): Device
{ {
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) { switch (strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL))) {
case Storage::DEVICE_LOCAL: case Storage::DEVICE_LOCAL:
default: default:
return new Local($root); return new Local($root);

View file

@ -33,9 +33,7 @@ use Appwrite\Extend\PDO;
use Appwrite\GraphQL\Promises\Adapter\Swoole; use Appwrite\GraphQL\Promises\Adapter\Swoole;
use Appwrite\GraphQL\Schema; use Appwrite\GraphQL\Schema;
use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP;
use Appwrite\Network\Validator\Origin; use Appwrite\Network\Validator\Origin;
use Appwrite\Network\Validator\URL;
use Appwrite\OpenSSL\OpenSSL; use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Usage\Stats; use Appwrite\Usage\Stats;
use MaxMind\Db\Reader; use MaxMind\Db\Reader;
@ -74,6 +72,8 @@ use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage; use Utopia\Storage\Storage;
use Utopia\Validator\Range; use Utopia\Validator\Range;
use Utopia\Validator\IP;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList; use Utopia\Validator\WhiteList;
const APP_NAME = 'Appwrite'; const APP_NAME = 'Appwrite';
@ -605,7 +605,7 @@ $register->set('smtp', function () {
return $mail; return $mail;
}); });
$register->set('geodb', function () { $register->set('geodb', function () {
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2022-06.mmdb'); return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2023-01.mmdb');
}); });
$register->set('db', function () { $register->set('db', function () {
// This is usually for our workers or CLI commands scope // This is usually for our workers or CLI commands scope
@ -969,7 +969,7 @@ App::setResource('deviceBuilds', function ($project) {
function getDevice($root): Device function getDevice($root): Device
{ {
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) { switch (strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL))) {
case Storage::DEVICE_LOCAL: case Storage::DEVICE_LOCAL:
default: default:
return new Local($root); return new Local($root);

View file

@ -509,6 +509,7 @@ services:
entrypoint: worker-messaging entrypoint: worker-messaging
<<: *x-logging <<: *x-logging
container_name: appwrite-worker-messaging container_name: appwrite-worker-messaging
restart: unless-stopped
networks: networks:
- appwrite - appwrite
depends_on: depends_on:

View file

@ -91,7 +91,7 @@ class BuildsV1 extends Worker
'outputPath' => '', 'outputPath' => '',
'runtime' => $function->getAttribute('runtime'), 'runtime' => $function->getAttribute('runtime'),
'source' => $deployment->getAttribute('path'), 'source' => $deployment->getAttribute('path'),
'sourceType' => App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL), 'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)),
'stdout' => '', 'stdout' => '',
'stderr' => '', 'stderr' => '',
'endTime' => null, 'endTime' => null,

View file

@ -44,9 +44,6 @@ class DeletesV1 extends Worker
case DELETE_TYPE_DATABASES: case DELETE_TYPE_DATABASES:
$this->deleteDatabase($document, $project->getId()); $this->deleteDatabase($document, $project->getId());
break; break;
case DELETE_TYPE_COLLECTIONS:
$this->deleteCollection($document, $project->getId());
break;
case DELETE_TYPE_PROJECTS: case DELETE_TYPE_PROJECTS:
$this->deleteProject($document); $this->deleteProject($document);
break; break;
@ -66,6 +63,10 @@ class DeletesV1 extends Worker
$this->deleteBucket($document, $project->getId()); $this->deleteBucket($document, $project->getId());
break; break;
default: default:
if (\str_starts_with($document->getCollection(), 'database_')) {
$this->deleteCollection($document, $project->getId());
break;
}
Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); Console::error('No lazy delete operation available for document of type: ' . $document->getCollection());
break; break;
} }
@ -172,6 +173,7 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document database document * @param Document $document database document
* @param string $projectId * @param string $projectId
* @throws Exception
*/ */
protected function deleteDatabase(Document $document, string $projectId): void protected function deleteDatabase(Document $document, string $projectId): void
{ {
@ -191,6 +193,7 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document teams document * @param Document $document teams document
* @param string $projectId * @param string $projectId
* @throws Exception
*/ */
protected function deleteCollection(Document $document, string $projectId): void protected function deleteCollection(Document $document, string $projectId): void
{ {
@ -217,6 +220,7 @@ class DeletesV1 extends Worker
/** /**
* @param string $hourlyUsageRetentionDatetime * @param string $hourlyUsageRetentionDatetime
* @throws Exception
*/ */
protected function deleteUsageStats(string $hourlyUsageRetentionDatetime) protected function deleteUsageStats(string $hourlyUsageRetentionDatetime)
{ {
@ -232,6 +236,7 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document teams document * @param Document $document teams document
* @param string $projectId * @param string $projectId
* @throws Exception
*/ */
protected function deleteMemberships(Document $document, string $projectId): void protected function deleteMemberships(Document $document, string $projectId): void
{ {
@ -245,25 +250,62 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document project document * @param Document $document project document
* @throws Exception
*/ */
protected function deleteProject(Document $document): void protected function deleteProject(Document $document): void
{ {
$projectId = $document->getId(); $projectId = $document->getId();
// Delete all DBs // Delete project domains and certificates
$this->getProjectDB($projectId)->delete($projectId); $dbForConsole = $this->getConsoleDB();
$domains = $dbForConsole->find('domains', [
Query::equal('projectInternalId', [$document->getInternalId()])
]);
foreach ($domains as $domain) {
$this->deleteCertificates($domain);
}
// Delete project tables
$dbForProject = $this->getProjectDB($projectId, $document);
while (true) {
$collections = $dbForProject->listCollections();
if (empty($collections)) {
break;
}
foreach ($collections as $collection) {
$dbForProject->deleteCollection($collection->getId());
}
}
// Delete metadata tables
try {
$dbForProject->deleteCollection('_metadata');
} catch (Exception) {
// Ignore: deleteCollection tries to delete a metadata entry after the collection is deleted,
// which will throw an exception here because the metadata collection is already deleted.
}
// Delete all storage directories // Delete all storage directories
$uploads = $this->getFilesDevice($document->getId()); $uploads = $this->getFilesDevice($projectId);
$cache = new Local(APP_STORAGE_CACHE . '/app-' . $document->getId()); $functions = $this->getFunctionsDevice($projectId);
$builds = $this->getBuildsDevice($projectId);
$cache = $this->getCacheDevice($projectId);
$uploads->delete($uploads->getRoot(), true); $uploads->delete($uploads->getRoot(), true);
$functions->delete($functions->getRoot(), true);
$builds->delete($builds->getRoot(), true);
$cache->delete($cache->getRoot(), true); $cache->delete($cache->getRoot(), true);
} }
/** /**
* @param Document $document user document * @param Document $document user document
* @param string $projectId * @param string $projectId
* @throws Exception
*/ */
protected function deleteUser(Document $document, string $projectId): void protected function deleteUser(Document $document, string $projectId): void
{ {
@ -305,6 +347,7 @@ class DeletesV1 extends Worker
/** /**
* @param string $datetime * @param string $datetime
* @throws Exception
*/ */
protected function deleteExecutionLogs(string $datetime): void protected function deleteExecutionLogs(string $datetime): void
{ {
@ -337,6 +380,7 @@ class DeletesV1 extends Worker
/** /**
* @param string $datetime * @param string $datetime
* @throws Exception
*/ */
protected function deleteRealtimeUsage(string $datetime): void protected function deleteRealtimeUsage(string $datetime): void
{ {
@ -393,6 +437,7 @@ class DeletesV1 extends Worker
/** /**
* @param string $resource * @param string $resource
* @param string $projectId * @param string $projectId
* @throws Exception
*/ */
protected function deleteAuditLogsByResource(string $resource, string $projectId): void protected function deleteAuditLogsByResource(string $resource, string $projectId): void
{ {
@ -406,6 +451,7 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document function document * @param Document $document function document
* @param string $projectId * @param string $projectId
* @throws Exception
*/ */
protected function deleteFunction(Document $document, string $projectId): void protected function deleteFunction(Document $document, string $projectId): void
{ {
@ -479,6 +525,7 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document deployment document * @param Document $document deployment document
* @param string $projectId * @param string $projectId
* @throws Exception
*/ */
protected function deleteDeployment(Document $document, string $projectId): void protected function deleteDeployment(Document $document, string $projectId): void
{ {
@ -528,9 +575,10 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document to be deleted * @param Document $document to be deleted
* @param Database $database to delete it from * @param Database $database to delete it from
* @param callable $callback to perform after document is deleted * @param callable|null $callback to perform after document is deleted
* *
* @return bool * @return bool
* @throws \Utopia\Database\Exception\Authorization
*/ */
protected function deleteById(Document $document, Database $database, callable $callback = null): bool protected function deleteById(Document $document, Database $database, callable $callback = null): bool
{ {
@ -550,6 +598,7 @@ class DeletesV1 extends Worker
/** /**
* @param callable $callback * @param callable $callback
* @throws Exception
*/ */
protected function deleteForProjectIds(callable $callback): void protected function deleteForProjectIds(callable $callback): void
{ {
@ -584,9 +633,10 @@ class DeletesV1 extends Worker
/** /**
* @param string $collection collectionID * @param string $collection collectionID
* @param Query[] $queries * @param array $queries
* @param Database $database * @param Database $database
* @param callable $callback * @param callable|null $callback
* @throws Exception
*/ */
protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
{ {
@ -620,6 +670,7 @@ class DeletesV1 extends Worker
/** /**
* @param Document $document certificates document * @param Document $document certificates document
* @throws \Utopia\Database\Exception\Authorization
*/ */
protected function deleteCertificates(Document $document): void protected function deleteCertificates(Document $document): void
{ {

View file

@ -51,7 +51,7 @@
"utopia-php/config": "0.2.*", "utopia-php/config": "0.2.*",
"utopia-php/database": "0.28.*", "utopia-php/database": "0.28.*",
"utopia-php/domains": "1.1.*", "utopia-php/domains": "1.1.*",
"utopia-php/framework": "0.25.*", "utopia-php/framework": "0.26.*",
"utopia-php/image": "0.5.*", "utopia-php/image": "0.5.*",
"utopia-php/locale": "0.4.*", "utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.3.*", "utopia-php/logger": "0.3.*",

44
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "dbc502462e4afa550b1a1bc3898020b3", "content-hash": "8782e69514f4564a3dcb44455161eedc",
"packages": [ "packages": [
{ {
"name": "adhocore/jwt", "name": "adhocore/jwt",
@ -1946,16 +1946,16 @@
}, },
{ {
"name": "utopia-php/framework", "name": "utopia-php/framework",
"version": "0.25.1", "version": "0.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/utopia-php/framework.git", "url": "https://github.com/utopia-php/framework.git",
"reference": "2391b397135586b2100d39e338827bef8d2f4ad0" "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/2391b397135586b2100d39e338827bef8d2f4ad0", "url": "https://api.github.com/repos/utopia-php/framework/zipball/e8da5576370366d3bf9c574ec855f8c96fe4f34e",
"reference": "2391b397135586b2100d39e338827bef8d2f4ad0", "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1984,9 +1984,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/utopia-php/framework/issues", "issues": "https://github.com/utopia-php/framework/issues",
"source": "https://github.com/utopia-php/framework/tree/0.25.1" "source": "https://github.com/utopia-php/framework/tree/0.26.0"
}, },
"time": "2022-11-23T18:22:23+00:00" "time": "2023-01-13T08:14:43+00:00"
}, },
{ {
"name": "utopia-php/image", "name": "utopia-php/image",
@ -2652,16 +2652,16 @@
}, },
{ {
"name": "webonyx/graphql-php", "name": "webonyx/graphql-php",
"version": "v14.11.8", "version": "v14.11.9",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/webonyx/graphql-php.git", "url": "https://github.com/webonyx/graphql-php.git",
"reference": "04a48693acd785330eefd3b0e4fa67df8dfee7c3" "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/webonyx/graphql-php/zipball/04a48693acd785330eefd3b0e4fa67df8dfee7c3", "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff91c9f3cf241db702e30b2c42bcc0920e70ac70",
"reference": "04a48693acd785330eefd3b0e4fa67df8dfee7c3", "reference": "ff91c9f3cf241db702e30b2c42bcc0920e70ac70",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2706,7 +2706,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/webonyx/graphql-php/issues", "issues": "https://github.com/webonyx/graphql-php/issues",
"source": "https://github.com/webonyx/graphql-php/tree/v14.11.8" "source": "https://github.com/webonyx/graphql-php/tree/v14.11.9"
}, },
"funding": [ "funding": [
{ {
@ -2714,7 +2714,7 @@
"type": "open_collective" "type": "open_collective"
} }
], ],
"time": "2022-09-21T15:35:03+00:00" "time": "2023-01-06T12:12:50+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -2771,30 +2771,30 @@
}, },
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",
"version": "1.4.1", "version": "1.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/instantiator.git", "url": "https://github.com/doctrine/instantiator.git",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1 || ^8.0" "php": "^7.1 || ^8.0"
}, },
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^9", "doctrine/coding-standard": "^9 || ^11",
"ext-pdo": "*", "ext-pdo": "*",
"ext-phar": "*", "ext-phar": "*",
"phpbench/phpbench": "^0.16 || ^1", "phpbench/phpbench": "^0.16 || ^1",
"phpstan/phpstan": "^1.4", "phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.22" "vimeo/psalm": "^4.30 || ^5.4"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -2821,7 +2821,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/doctrine/instantiator/issues", "issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.4.1" "source": "https://github.com/doctrine/instantiator/tree/1.5.0"
}, },
"funding": [ "funding": [
{ {
@ -2837,7 +2837,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-03-03T08:28:38+00:00" "time": "2022-12-30T00:15:36+00:00"
}, },
{ {
"name": "matthiasmullie/minify", "name": "matthiasmullie/minify",
@ -5271,5 +5271,5 @@
"platform-overrides": { "platform-overrides": {
"php": "8.0" "php": "8.0"
}, },
"plugin-api-version": "2.3.0" "plugin-api-version": "2.1.0"
} }

View file

@ -554,6 +554,7 @@ services:
entrypoint: worker-messaging entrypoint: worker-messaging
<<: *x-logging <<: *x-logging
container_name: appwrite-worker-messaging container_name: appwrite-worker-messaging
restart: unless-stopped
image: appwrite-dev image: appwrite-dev
networks: networks:
- appwrite - appwrite

View file

@ -228,18 +228,18 @@ class Mapper
case 'Appwrite\Network\Validator\CNAME': case 'Appwrite\Network\Validator\CNAME':
case 'Appwrite\Task\Validator\Cron': case 'Appwrite\Task\Validator\Cron':
case 'Appwrite\Utopia\Database\Validator\CustomId': case 'Appwrite\Utopia\Database\Validator\CustomId':
case 'Appwrite\Network\Validator\Domain': case 'Utopia\Validator\Domain':
case 'Appwrite\Network\Validator\Email': case 'Appwrite\Network\Validator\Email':
case 'Appwrite\Event\Validator\Event': case 'Appwrite\Event\Validator\Event':
case 'Utopia\Validator\HexColor': case 'Utopia\Validator\HexColor':
case 'Appwrite\Network\Validator\Host': case 'Utopia\Validator\Host':
case 'Appwrite\Network\Validator\IP': case 'Utopia\Validator\IP':
case 'Utopia\Database\Validator\Key': case 'Utopia\Database\Validator\Key':
case 'Appwrite\Network\Validator\Origin': case 'Utopia\Validator\Origin':
case 'Appwrite\Auth\Validator\Password': case 'Appwrite\Auth\Validator\Password':
case 'Utopia\Validator\Text': case 'Utopia\Validator\Text':
case 'Utopia\Database\Validator\UID': case 'Utopia\Database\Validator\UID':
case 'Appwrite\Network\Validator\URL': case 'Utopia\Validator\URL':
case 'Utopia\Validator\WhiteList': case 'Utopia\Validator\WhiteList':
default: default:
$type = Type::string(); $type = Type::string();

View file

@ -1,78 +0,0 @@
<?php
namespace Appwrite\Network\Validator;
use Utopia\Validator;
/**
* Domain
*
* Validate that an variable is a valid domain address
*
* @package Utopia\Validator
*/
class Domain extends Validator
{
/**
* Get Description
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return 'Value must be a valid domain';
}
/**
* Is valid
*
* Validation will pass when $value is valid domain.
*
* Validates domain names against RFC 1034, RFC 1035, RFC 952, RFC 1123, RFC 2732, RFC 2181, and RFC 1123.
*
* @param mixed $value
* @return bool
*/
public function isValid($value): bool
{
if (empty($value)) {
return false;
}
if (!is_string($value)) {
return false;
}
if (\filter_var($value, FILTER_VALIDATE_DOMAIN) === false) {
return false;
}
return true;
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_STRING;
}
}

View file

@ -1,84 +0,0 @@
<?php
namespace Appwrite\Network\Validator;
use Utopia\Validator\Hostname;
use Utopia\Validator;
/**
* Host
*
* Validate that a host is allowed from given whitelisted hosts list
*
* @package Utopia\Validator
*/
class Host extends Validator
{
protected $whitelist = [];
/**
* @param array $whitelist
*/
public function __construct(array $whitelist)
{
$this->whitelist = $whitelist;
}
/**
* Get Description
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return 'URL host must be one of: ' . \implode(', ', $this->whitelist);
}
/**
* Is valid
*
* Validation will pass when $value starts with one of the given hosts
*
* @param mixed $value
* @return bool
*/
public function isValid($value): bool
{
// Check if value is valid URL
$urlValidator = new URL();
if (!$urlValidator->isValid($value)) {
return false;
}
$hostname = \parse_url($value, PHP_URL_HOST);
$hostnameValidator = new Hostname($this->whitelist);
return $hostnameValidator->isValid($hostname);
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_STRING;
}
}

View file

@ -1,114 +0,0 @@
<?php
namespace Appwrite\Network\Validator;
use Exception;
use Utopia\Validator;
/**
* IP
*
* Validate that an variable is a valid IP address
*
* @package Utopia\Validator
*/
class IP extends Validator
{
public const ALL = 'all';
public const V4 = 'ipv4';
public const V6 = 'ipv6';
/**
* @var string
*/
protected $type = self::ALL;
/**
* Constructor
*
* Set a the type of IP check.
*
* @param string $type
*/
public function __construct(string $type = self::ALL)
{
if (!in_array($type, [self::ALL, self::V4, self::V6])) {
throw new Exception('Unsupported IP type');
}
$this->type = $type;
}
/**
* Get Description
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return 'Value must be a valid IP address';
}
/**
* Is valid
*
* Validation will pass when $value is valid IP address.
*
* @param mixed $value
* @return bool
*/
public function isValid($value): bool
{
switch ($this->type) {
case self::ALL:
if (\filter_var($value, FILTER_VALIDATE_IP)) {
return true;
}
break;
case self::V4:
if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return true;
}
break;
case self::V6:
if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return true;
}
break;
default:
return false;
break;
}
return false;
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_STRING;
}
}

View file

@ -1,92 +0,0 @@
<?php
namespace Appwrite\Network\Validator;
use Utopia\Validator;
/**
* URL
*
* Validate that an variable is a valid URL
*
* @package Appwrite\Network\Validator
*/
class URL extends Validator
{
protected array $allowedSchemes;
/**
* @param array $allowedSchemes
*/
public function __construct(array $allowedSchemes = [])
{
$this->allowedSchemes = $allowedSchemes;
}
/**
* Get Description
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
if (!empty($this->allowedSchemes)) {
return 'Value must be a valid URL with following schemes (' . \implode(', ', $this->allowedSchemes) . ')';
}
return 'Value must be a valid URL';
}
/**
* Is valid
*
* Validation will pass when $value is valid URL.
*
* @param mixed $value
* @return bool
*/
public function isValid($value): bool
{
$sanitizedURL = '';
foreach (str_split($value) as $character) {
$sanitizedURL .= (ord($character) > 127) ? rawurlencode($character) : $character;
}
if (\filter_var($sanitizedURL, FILTER_VALIDATE_URL) === false) {
return false;
}
if (!empty($this->allowedSchemes) && !\in_array(\parse_url($sanitizedURL, PHP_URL_SCHEME), $this->allowedSchemes)) {
return false;
}
return true;
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_STRING;
}
}

View file

@ -2,22 +2,23 @@
namespace Appwrite\Resque; namespace Appwrite\Resque;
use Exception;
use Utopia\App; use Utopia\App;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Cache\Cache;
use Utopia\CLI\Console; use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Storage\Device; use Utopia\Storage\Device;
use Utopia\Storage\Storage; use Utopia\Storage\Device\Backblaze;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\DOSpaces; use Utopia\Storage\Device\DOSpaces;
use Utopia\Storage\Device\Linode; use Utopia\Storage\Device\Linode;
use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\Backblaze;
use Utopia\Storage\Device\S3; use Utopia\Storage\Device\S3;
use Exception; use Utopia\Storage\Device\Wasabi;
use Utopia\Database\Validator\Authorization; use Utopia\Storage\Storage;
abstract class Worker abstract class Worker
{ {
@ -53,7 +54,7 @@ abstract class Worker
* @return void * @return void
* @throws \Exception|\Throwable * @throws \Exception|\Throwable
*/ */
public function init() public function init(): void
{ {
throw new Exception("Please implement init method in worker"); throw new Exception("Please implement init method in worker");
} }
@ -65,7 +66,7 @@ abstract class Worker
* @return void * @return void
* @throws \Exception|\Throwable * @throws \Exception|\Throwable
*/ */
public function run() public function run(): void
{ {
throw new Exception("Please implement run method in worker"); throw new Exception("Please implement run method in worker");
} }
@ -77,7 +78,7 @@ abstract class Worker
* @return void * @return void
* @throws \Exception|\Throwable * @throws \Exception|\Throwable
*/ */
public function shutdown() public function shutdown(): void
{ {
throw new Exception("Please implement shutdown method in worker"); throw new Exception("Please implement shutdown method in worker");
} }
@ -151,35 +152,39 @@ abstract class Worker
/** /**
* Register callback. Will be executed when error occurs. * Register callback. Will be executed when error occurs.
* @param callable $callback * @param callable $callback
* @param Throwable $error * @return void
* @return self
*/ */
public static function error(callable $callback): void public static function error(callable $callback): void
{ {
\array_push(self::$errorCallbacks, $callback); self::$errorCallbacks[] = $callback;
} }
/** /**
* Get internal project database * Get internal project database
* @param string $projectId * @param string $projectId
* @return Database * @return Database
* @throws Exception
*/ */
protected function getProjectDB(string $projectId): Database protected function getProjectDB(string $projectId, ?Document $project = null): Database
{ {
$consoleDB = $this->getConsoleDB(); if ($project === null) {
$consoleDB = $this->getConsoleDB();
if ($projectId === 'console') { if ($projectId === 'console') {
return $consoleDB; return $consoleDB;
}
/** @var Document $project */
$project = Authorization::skip(fn() => $consoleDB->getDocument('projects', $projectId));
} }
/** @var Document $project */ return $this->getDB(self::DATABASE_PROJECT, $projectId, $project->getInternalId(), $project);
$project = Authorization::skip(fn() => $consoleDB->getDocument('projects', $projectId));
return $this->getDB(self::DATABASE_PROJECT, $projectId, $project->getInternalId());
} }
/** /**
* Get console database * Get console database
* @return Database * @return Database
* @throws Exception
*/ */
protected function getConsoleDB(): Database protected function getConsoleDB(): Database
{ {
@ -187,24 +192,35 @@ abstract class Worker
} }
/** /**
* Get console database * Get database
* @param string $type One of (internal, external, console) * @param string $type One of (project, console)
* @param string $projectId of internal or external DB * @param string $projectId of project or console DB
* @param string $projectInternalId
* @param Document|null $project
* @return Database * @return Database
* @throws Exception
*/ */
private function getDB(string $type, string $projectId = '', string $projectInternalId = ''): Database private function getDB(
{ string $type,
string $projectId = '',
string $projectInternalId = '',
?Document $project = null
): Database {
global $register; global $register;
$namespace = '';
$sleep = DATABASE_RECONNECT_SLEEP; // overwritten when necessary $sleep = DATABASE_RECONNECT_SLEEP; // overwritten when necessary
if ($project !== null) {
$projectId = $project->getId();
$projectInternalId = $project->getInternalId();
}
switch ($type) { switch ($type) {
case self::DATABASE_PROJECT: case self::DATABASE_PROJECT:
if (!$projectId) { if (!$projectId) {
throw new \Exception('ProjectID not provided - cannot get database'); throw new \Exception('ProjectID not provided - cannot get database');
} }
$namespace = "_{$projectInternalId}"; $namespace = "_$projectInternalId";
break; break;
case self::DATABASE_CONSOLE: case self::DATABASE_CONSOLE:
$namespace = "_console"; $namespace = "_console";
@ -212,12 +228,11 @@ abstract class Worker
break; break;
default: default:
throw new \Exception('Unknown database type: ' . $type); throw new \Exception('Unknown database type: ' . $type);
break;
} }
$attempts = 0; $attempts = 0;
do { while (true) {
try { try {
$attempts++; $attempts++;
$cache = new Cache(new RedisCache($register->get('cache'))); $cache = new Cache(new RedisCache($register->get('cache')));
@ -225,8 +240,12 @@ abstract class Worker
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite')); $database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace($namespace); // Main DB $database->setNamespace($namespace); // Main DB
if (!empty($projectId) && !$database->getDocument('projects', $projectId)->isEmpty()) { if (
throw new \Exception("Project does not exist: {$projectId}"); $project === null
&& !empty($projectId)
&& !$database->getDocument('projects', $projectId)->isEmpty()
) {
throw new \Exception("Project does not exist: $projectId");
} }
if ($type === self::DATABASE_CONSOLE && !$database->exists($database->getDefaultDatabase(), Database::METADATA)) { if ($type === self::DATABASE_CONSOLE && !$database->exists($database->getDefaultDatabase(), Database::METADATA)) {
@ -235,13 +254,13 @@ abstract class Worker
break; // leave loop if successful break; // leave loop if successful
} catch (\Exception $e) { } catch (\Exception $e) {
Console::warning("Database not ready. Retrying connection ({$attempts})..."); Console::warning("Database not ready. Retrying connection ($attempts)...");
if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) { if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) {
throw new \Exception('Failed to connect to database: ' . $e->getMessage()); throw new \Exception('Failed to connect to database: ' . $e->getMessage());
} }
sleep($sleep); sleep($sleep);
} }
} while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS); }
return $database; return $database;
} }
@ -251,7 +270,7 @@ abstract class Worker
* @param string $projectId of the project * @param string $projectId of the project
* @return Device * @return Device
*/ */
protected function getFunctionsDevice($projectId): Device protected function getFunctionsDevice(string $projectId): Device
{ {
return $this->getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); return $this->getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId);
} }
@ -261,7 +280,7 @@ abstract class Worker
* @param string $projectId of the project * @param string $projectId of the project
* @return Device * @return Device
*/ */
protected function getFilesDevice($projectId): Device protected function getFilesDevice(string $projectId): Device
{ {
return $this->getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); return $this->getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId);
} }
@ -272,19 +291,24 @@ abstract class Worker
* @param string $projectId of the project * @param string $projectId of the project
* @return Device * @return Device
*/ */
protected function getBuildsDevice($projectId): Device protected function getBuildsDevice(string $projectId): Device
{ {
return $this->getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); return $this->getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId);
} }
protected function getCacheDevice(string $projectId): Device
{
return $this->getDevice(APP_STORAGE_CACHE . '/app-' . $projectId);
}
/** /**
* Get Device based on selected storage environment * Get Device based on selected storage environment
* @param string $root path of the device * @param string $root path of the device
* @return Device * @return Device
*/ */
public function getDevice($root): Device public function getDevice(string $root): Device
{ {
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) { switch (strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL))) {
case Storage::DEVICE_LOCAL: case Storage::DEVICE_LOCAL:
default: default:
return new Local($root); return new Local($root);

View file

@ -311,7 +311,7 @@ class OpenAPI3 extends Format
$node['schema']['format'] = 'email'; $node['schema']['format'] = 'email';
$node['schema']['x-example'] = 'email@example.com'; $node['schema']['x-example'] = 'email@example.com';
break; break;
case 'Appwrite\Network\Validator\URL': case 'Utopia\Validator\URL':
$node['schema']['type'] = $validator->getType(); $node['schema']['type'] = $validator->getType();
$node['schema']['format'] = 'url'; $node['schema']['format'] = 'url';
$node['schema']['x-example'] = 'https://example.com'; $node['schema']['x-example'] = 'https://example.com';
@ -391,7 +391,7 @@ class OpenAPI3 extends Format
case 'Utopia\Validator\Length': case 'Utopia\Validator\Length':
$node['schema']['type'] = $validator->getType(); $node['schema']['type'] = $validator->getType();
break; break;
case 'Appwrite\Network\Validator\Host': case 'Utopia\Validator\Host':
$node['schema']['type'] = $validator->getType(); $node['schema']['type'] = $validator->getType();
$node['schema']['format'] = 'url'; $node['schema']['format'] = 'url';
$node['schema']['x-example'] = 'https://example.com'; $node['schema']['x-example'] = 'https://example.com';

View file

@ -312,7 +312,7 @@ class Swagger2 extends Format
$node['format'] = 'email'; $node['format'] = 'email';
$node['x-example'] = 'email@example.com'; $node['x-example'] = 'email@example.com';
break; break;
case 'Appwrite\Network\Validator\URL': case 'Utopia\Validator\URL':
$node['type'] = $validator->getType(); $node['type'] = $validator->getType();
$node['format'] = 'url'; $node['format'] = 'url';
$node['x-example'] = 'https://example.com'; $node['x-example'] = 'https://example.com';
@ -393,7 +393,7 @@ class Swagger2 extends Format
case 'Utopia\Validator\Length': case 'Utopia\Validator\Length':
$node['type'] = $validator->getType(); $node['type'] = $validator->getType();
break; break;
case 'Appwrite\Network\Validator\Host': case 'Utopia\Validator\Host':
$node['type'] = $validator->getType(); $node['type'] = $validator->getType();
$node['format'] = 'url'; $node['format'] = 'url';
$node['x-example'] = 'https://example.com'; $node['x-example'] = 'https://example.com';

View file

@ -42,7 +42,7 @@ class Locale extends Model
]) ])
->addRule('eu', [ ->addRule('eu', [
'type' => self::TYPE_BOOLEAN, 'type' => self::TYPE_BOOLEAN,
'description' => 'True if country is part of the Europian Union.', 'description' => 'True if country is part of the European Union.',
'default' => false, 'default' => false,
'example' => false, 'example' => false,
]) ])

View file

@ -1,43 +0,0 @@
<?php
namespace Tests\Unit\Network\Validators;
use Appwrite\Network\Validator\Domain;
use PHPUnit\Framework\TestCase;
class DomainTest extends TestCase
{
protected ?Domain $domain = null;
public function setUp(): void
{
$this->domain = new Domain();
}
public function tearDown(): void
{
$this->domain = null;
}
public function testIsValid(): void
{
// Assertions
$this->assertEquals(true, $this->domain->isValid('example.com'));
$this->assertEquals(true, $this->domain->isValid('subdomain.example.com'));
$this->assertEquals(true, $this->domain->isValid('subdomain.example-app.com'));
$this->assertEquals(true, $this->domain->isValid('subdomain.example_app.com'));
$this->assertEquals(true, $this->domain->isValid('subdomain-new.example.com'));
$this->assertEquals(true, $this->domain->isValid('subdomain_new.example.com'));
$this->assertEquals(true, $this->domain->isValid('localhost'));
$this->assertEquals(true, $this->domain->isValid('appwrite.io'));
$this->assertEquals(true, $this->domain->isValid('appwrite.org'));
$this->assertEquals(true, $this->domain->isValid('appwrite.org'));
$this->assertEquals(false, $this->domain->isValid(false));
$this->assertEquals(false, $this->domain->isValid('.'));
$this->assertEquals(false, $this->domain->isValid('..'));
$this->assertEquals(false, $this->domain->isValid(''));
$this->assertEquals(false, $this->domain->isValid(['string', 'string']));
$this->assertEquals(false, $this->domain->isValid(1));
$this->assertEquals(false, $this->domain->isValid(1.2));
}
}

View file

@ -1,44 +0,0 @@
<?php
/**
* Utopia PHP Framework
*
* @package Framework
* @subpackage Tests
*
* @link https://github.com/utopia-php/framework
* @author Appwrite Team <team@appwrite.io>
* @version 1.0 RC4
* @license The MIT License (MIT) <http://www.opensource.org/licenses/mit-license.php>
*/
namespace Tests\Unit\Network\Validators;
use Appwrite\Network\Validator\Host;
use PHPUnit\Framework\TestCase;
class HostTest extends TestCase
{
protected ?Host $host = null;
public function setUp(): void
{
$this->host = new Host(['appwrite.io', 'subdomain.appwrite.test', 'localhost']);
}
public function tearDown(): void
{
$this->host = null;
}
public function testIsValid(): void
{
// Assertions
$this->assertEquals($this->host->isValid('https://appwrite.io/link'), true);
$this->assertEquals($this->host->isValid('https://localhost'), true);
$this->assertEquals($this->host->isValid('localhost'), false);
$this->assertEquals($this->host->isValid('http://subdomain.appwrite.test/path'), true);
$this->assertEquals($this->host->isValid('http://test.subdomain.appwrite.test/path'), false);
$this->assertEquals($this->host->getType(), 'string');
}
}

View file

@ -1,87 +0,0 @@
<?php
/**
* Utopia PHP Framework
*
* @package Framework
* @subpackage Tests
*
* @link https://github.com/utopia-php/framework
* @author Appwrite Team <team@appwrite.io>
* @version 1.0 RC4
* @license The MIT License (MIT) <http://www.opensource.org/licenses/mit-license.php>
*/
namespace Tests\Unit\Network\Validators;
use Appwrite\Network\Validator\IP;
use PHPUnit\Framework\TestCase;
class IPTest extends TestCase
{
protected ?IP $validator;
public function setUp(): void
{
$this->validator = new IP();
}
public function tearDown(): void
{
$this->validator = null;
}
public function testIsValidIP(): void
{
$this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true);
$this->assertEquals($this->validator->isValid('109.67.204.101'), true);
$this->assertEquals($this->validator->isValid(23.5), false);
$this->assertEquals($this->validator->isValid('23.5'), false);
$this->assertEquals($this->validator->isValid(null), false);
$this->assertEquals($this->validator->isValid(true), false);
$this->assertEquals($this->validator->isValid(false), false);
$this->assertEquals($this->validator->getType(), 'string');
}
public function testIsValidIPALL(): void
{
$this->validator = new IP(IP::ALL);
// Assertions
$this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true);
$this->assertEquals($this->validator->isValid('109.67.204.101'), true);
$this->assertEquals($this->validator->isValid(23.5), false);
$this->assertEquals($this->validator->isValid('23.5'), false);
$this->assertEquals($this->validator->isValid(null), false);
$this->assertEquals($this->validator->isValid(true), false);
$this->assertEquals($this->validator->isValid(false), false);
}
public function testIsValidIPV4(): void
{
$this->validator = new IP(IP::V4);
// Assertions
$this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), false);
$this->assertEquals($this->validator->isValid('109.67.204.101'), true);
$this->assertEquals($this->validator->isValid(23.5), false);
$this->assertEquals($this->validator->isValid('23.5'), false);
$this->assertEquals($this->validator->isValid(null), false);
$this->assertEquals($this->validator->isValid(true), false);
$this->assertEquals($this->validator->isValid(false), false);
}
public function testIsValidIPV6(): void
{
$this->validator = new IP(IP::V6);
// Assertions
$this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true);
$this->assertEquals($this->validator->isValid('109.67.204.101'), false);
$this->assertEquals($this->validator->isValid(23.5), false);
$this->assertEquals($this->validator->isValid('23.5'), false);
$this->assertEquals($this->validator->isValid(null), false);
$this->assertEquals($this->validator->isValid(true), false);
$this->assertEquals($this->validator->isValid(false), false);
}
}

View file

@ -1,57 +0,0 @@
<?php
/**
* Utopia PHP Framework
*
* @package Framework
* @subpackage Tests
*
* @link https://github.com/utopia-php/framework
* @author Appwrite Team <team@appwrite.io>
* @version 1.0 RC4
* @license The MIT License (MIT) <http://www.opensource.org/licenses/mit-license.php>
*/
namespace Tests\Unit\Network\Validators;
use Appwrite\Network\Validator\URL;
use PHPUnit\Framework\TestCase;
class URLTest extends TestCase
{
protected ?URL $url;
public function setUp(): void
{
$this->url = new URL();
}
public function tearDown(): void
{
$this->url = null;
}
public function testIsValid(): void
{
$this->assertEquals('Value must be a valid URL', $this->url->getDescription());
$this->assertEquals(true, $this->url->isValid('http://example.com'));
$this->assertEquals(true, $this->url->isValid('https://example.com'));
$this->assertEquals(true, $this->url->isValid('htts://example.com')); // does not validate protocol
$this->assertEquals(false, $this->url->isValid('example.com')); // though, requires some kind of protocol
$this->assertEquals(false, $this->url->isValid('http:/example.com'));
$this->assertEquals(true, $this->url->isValid('http://exa-mple.com'));
$this->assertEquals(false, $this->url->isValid('htt@s://example.com'));
$this->assertEquals(true, $this->url->isValid('http://www.example.com/foo%2\u00c2\u00a9zbar'));
$this->assertEquals(true, $this->url->isValid('http://www.example.com/?q=%3Casdf%3E'));
$this->assertEquals(true, $this->url->isValid('https://example.com/foo%2\u00c2\u00ä9zbär'));
}
public function testIsValidAllowedSchemes(): void
{
$this->url = new URL(['http', 'https']);
$this->assertEquals('Value must be a valid URL with following schemes (http, https)', $this->url->getDescription());
$this->assertEquals(true, $this->url->isValid('http://example.com'));
$this->assertEquals(true, $this->url->isValid('https://example.com'));
$this->assertEquals(false, $this->url->isValid('gopher://www.example.com'));
}
}