1
0
Fork 0
mirror of synced 2024-06-26 10:10:57 +12:00

Merge branch 'feat-database-indexing' into feat-prefs-improvement

This commit is contained in:
Matej Baco 2021-12-22 09:07:50 +01:00
commit 1f32bd6367
64 changed files with 1703 additions and 168 deletions

View file

@ -5,6 +5,14 @@
- Grouped auth related attributes in project collection. Introduced new attribute `auths` and removed all attributes related to auth methods and `usersAuthLimit` as well, all these are grouped under `auths` attribute
- Grouped oAuth related attributes in project collection. Introduced new attribute `providers` and removed all attributes related to OAuth2 providers. All OAuth2 attributes are grouped under `providers`
- Project model changed, `userAuth<AuthMethod>` => `auth<AuthMethod>` example `userAuthEmailPassword` => `authEmailPassword`, also `userOauth2<Provider>...` => `provider<Provider>...` example `userOauth2GithubAppid` => `providerGithubAppid`
# Version 0.12.0
## Breaking Changes (Read before upgrading!)
- Multiple HealthAPI response models were changed to new (better) schema
- Method `health.getAntiVirus()` has been renamed to `health.getAntivirus()`
# Version 0.11.0
## Features

View file

@ -1617,8 +1617,8 @@ foreach ($providers as $index => $provider) {
foreach ($auth as $index => $method) {
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => $method['name'] || '',
'key' => $method['key'] || '',
'label' => $method['name'] ?? '',
'key' => $method['key'] ?? '',
'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => true,
'required' => false,

View file

@ -487,7 +487,7 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => false,
'filters' => ['json'],
],
@ -498,7 +498,7 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => false,
'filters' => ['json'],
],
@ -509,7 +509,7 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => false,
'filters' => ['json', 'encrypt'],
],
@ -884,7 +884,7 @@ $collections = [
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
@ -895,7 +895,7 @@ $collections = [
'format' => '',
'size' => Database::LENGTH_KEY, // TODO will the length suffice after encryption?
'signed' => true,
'required' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
@ -1001,7 +1001,7 @@ $collections = [
'size' => 65535,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => false,
'filters' => ['json'],
],
@ -1045,7 +1045,7 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => true,
'filters' => ['json'],
],
@ -1056,7 +1056,7 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => true,
'filters' => ['json'],
],
@ -1067,7 +1067,7 @@ $collections = [
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => true,
'filters' => ['json'],
],
@ -1816,7 +1816,7 @@ $collections = [
'size' => 8192,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => false,
'filters' => ['json', 'encrypt'],
],

View file

@ -61,6 +61,7 @@ return [
'si', // Sinhala
'sk', // Slovakia
'sl', // Slovenian
'sn', // Shona
'sq', // Albanian
'sv', // Swedish
'ta', // Tamil

View file

@ -0,0 +1,232 @@
{
"settings.inspire": "\"Unyanzvi hwekuchenjera kuziva zvekufuratira.\"",
"settings.locale": "sn",
"settings.direction": "ltr",
"emails.sender": "Chikwata che%s",
"emails.verification.subject": "Kuratidzi kuti ndiwe muridzi weakaundi",
"emails.verification.hello": "Hesi {{name}}",
"emails.verification.body": "Tevedza chinongedzo ichi kuti uratidze kuti kero iyi ndeyako.",
"emails.verification.footer": "Kana usina kukumbira kuti uratidze kuti kero iyi ndeyako, unogona kufuratira meseji iyi.",
"emails.verification.thanks": "Ndatenda",
"emails.verification.signature": "Chikwata che{{project}}",
"emails.magicSession.subject": "Pinda",
"emails.magicSession.hello": "Hesi,",
"emails.magicSession.body": "Baya chinongedzo ichi kuti upinde muakaundi yako.",
"emails.magicSession.footer": "Kana usina kukumbira kupinda muakaundi yako uchishandisa email iyi, unogona kufuratira meseji iyi.",
"emails.magicSession.thanks": "Ndatenda",
"emails.magicSession.signature": "Chikwata che{{project}}",
"emails.recovery.subject": "Kuchinja pasiwedhi",
"emails.recovery.hello": "Mhoro {{name}}",
"emails.recovery.body": "Baya chinongedzo ichi kuti uchinje pasiwedhi yako ye{{project}}.",
"emails.recovery.footer": "Kana usina kukumbira kuchinja pasiwedhi yako, unogona kufuratira meseji iyi.",
"emails.recovery.thanks": "Ndatenda",
"emails.recovery.signature": "Chikwata che{{project}}",
"emails.invitation.subject": "Kukokwa kuchikwata che%s ku%s",
"emails.invitation.hello": "Mhoro",
"emails.invitation.body": "Tsamba iyi yatumirwa kwauri nekuti {{owner}} anga achida kuti uve nhengo yechikwata che{{team}} pachirongwa che{{project}}.",
"emails.invitation.footer": "Kana usiri kufarira kuve nhengo yechikwata ichi, unogona kufuratira meseji iyi.",
"emails.invitation.thanks": "Ndatenda",
"emails.invitation.signature": "Chikwata che{{project}}",
"locale.country.unknown": "Haizivikanwe",
"countries.af": "Afuganisitani",
"countries.ao": "Angola",
"countries.al": "Albania",
"countries.ad": "Andorra",
"countries.ae": "Mubatanidzwa wenyika dzeArab Emirates",
"countries.ar": "Argentina",
"countries.am": "Armenia",
"countries.ag": "Antigua and Barbuda",
"countries.au": "Australia",
"countries.at": "Austria",
"countries.az": "Azerbaijan",
"countries.bi": "Burundi",
"countries.be": "Belgium",
"countries.bj": "Benin",
"countries.bf": "Burkina Faso",
"countries.bd": "Bangladesh",
"countries.bg": "Bulgaria",
"countries.bh": "Bahrain",
"countries.bs": "Bahamas",
"countries.ba": "Bosnia and Herzegovina",
"countries.by": "Belarus",
"countries.bz": "Belize",
"countries.bo": "Bolivia",
"countries.br": "Brazil",
"countries.bb": "Barbados",
"countries.bn": "Brunei",
"countries.bt": "Bhutan",
"countries.bw": "Botswana",
"countries.cf": "Central African Republic",
"countries.ca": "Canada",
"countries.ch": "Switzerland",
"countries.cl": "Chile",
"countries.cn": "China",
"countries.ci": "Ivory Coast",
"countries.cm": "Cameroon",
"countries.cd": "DR Congo",
"countries.cg": "Republic of the Congo",
"countries.co": "Colombia",
"countries.km": "Comoros",
"countries.cv": "Cape Verde",
"countries.cr": "Costa Rica",
"countries.cu": "Cuba",
"countries.cy": "Cyprus",
"countries.cz": "Czechia",
"countries.de": "Germany",
"countries.dj": "Djibouti",
"countries.dm": "Dominica",
"countries.dk": "Denmark",
"countries.do": "Dominican Republic",
"countries.dz": "Algeria",
"countries.ec": "Ecuador",
"countries.eg": "Egypt",
"countries.er": "Eritrea",
"countries.es": "Spain",
"countries.ee": "Estonia",
"countries.et": "Ethiopia",
"countries.fi": "Finland",
"countries.fj": "Fiji",
"countries.fr": "France",
"countries.fm": "Micronesia",
"countries.ga": "Gabon",
"countries.gb": "United Kingdom",
"countries.ge": "Georgia",
"countries.gh": "Ghana",
"countries.gn": "Guinea",
"countries.gm": "Gambia",
"countries.gw": "Guinea-Bissau",
"countries.gq": "Equatorial Guinea",
"countries.gr": "Girisi",
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
"countries.hu": "Hungary",
"countries.id": "Indonesia",
"countries.in": "India",
"countries.ie": "Ireland",
"countries.ir": "Iran",
"countries.iq": "Iraki",
"countries.is": "Aisirendi",
"countries.il": "Izirayeri",
"countries.it": "Itari",
"countries.jm": "Jamaika",
"countries.jo": "Jodhani",
"countries.jp": "Japani",
"countries.kz": "Khazakisitani",
"countries.ke": "Kenya",
"countries.kg": "Kyrgyzstan",
"countries.kh": "Kambodhiya",
"countries.ki": "Kiribati",
"countries.kn": "Saint Kitts and Nevis",
"countries.kr": "Koria yekuChamhembe",
"countries.kw": "Kuweiti",
"countries.la": "Laos",
"countries.lb": "Lebanon",
"countries.lr": "Liberia",
"countries.ly": "Libya",
"countries.lc": "Saint Lucia",
"countries.li": "Liechtenstein",
"countries.lk": "Sri Lanka",
"countries.ls": "Lesotho",
"countries.lt": "Lithuania",
"countries.lu": "Luxembourg",
"countries.lv": "Latvia",
"countries.ma": "Morocco",
"countries.mc": "Monaco",
"countries.md": "Moldova",
"countries.mg": "Madagascar",
"countries.mv": "Maldives",
"countries.mx": "Mexico",
"countries.mh": "Marshall Islands",
"countries.mk": "Macedonia",
"countries.ml": "Mali",
"countries.mt": "Malta",
"countries.mm": "Myanmar",
"countries.me": "Montenegro",
"countries.mn": "Mongolia",
"countries.mz": "Mozambiki",
"countries.mr": "Moritaniya",
"countries.mu": "Morishiyasi",
"countries.mw": "Malawi",
"countries.my": "Malaysia",
"countries.na": "Namibia",
"countries.ne": "Naija",
"countries.ng": "Naijeriya",
"countries.ni": "Nicaragua",
"countries.nl": "Netherlands",
"countries.no": "Noweyi",
"countries.np": "Nepal",
"countries.nr": "Nauru",
"countries.nz": "New Zealand",
"countries.om": "Oman",
"countries.pk": "Pakisitani",
"countries.pa": "Panama",
"countries.pe": "Peru",
"countries.ph": "Philippines",
"countries.pw": "Palau",
"countries.pg": "Papua New Guinea",
"countries.pl": "Poland",
"countries.kp": "North Korea",
"countries.pt": "Portugal",
"countries.py": "Paraguay",
"countries.qa": "Qatar",
"countries.ro": "Romania",
"countries.ru": "Russia",
"countries.rw": "Rwanda",
"countries.sa": "Saudi Arabia",
"countries.sd": "Sudan",
"countries.sn": "Senegal",
"countries.sg": "Singapore",
"countries.sb": "Solomon Islands",
"countries.sl": "Sierra Leone",
"countries.sv": "El Salvador",
"countries.sm": "San Marino",
"countries.so": "Somalia",
"countries.rs": "Serbia",
"countries.ss": "South Sudan",
"countries.st": "São Tomé and Príncipe",
"countries.sr": "Suriname",
"countries.sk": "Slovakia",
"countries.si": "Slovenia",
"countries.se": "Sweden",
"countries.sz": "Swaziland",
"countries.sc": "Seychelles",
"countries.sy": "Syria",
"countries.td": "Chad",
"countries.tg": "Togo",
"countries.th": "Thailand",
"countries.tj": "Tajikistan",
"countries.tm": "Turkmenistan",
"countries.tl": "Timor-Leste",
"countries.to": "Tonga",
"countries.tt": "Trinidad and Tobago",
"countries.tn": "Tunisia",
"countries.tr": "Turkey",
"countries.tv": "Tuvalu",
"countries.tz": "Tanzania",
"countries.ug": "Uganda",
"countries.ua": "Ukraine",
"countries.uy": "Uruguay",
"countries.us": "United States",
"countries.uz": "Uzbekistan",
"countries.va": "Vatican City",
"countries.vc": "Saint Vincent and the Grenadines",
"countries.ve": "Venezuela",
"countries.vn": "Vietnam",
"countries.vu": "Vanuatu",
"countries.ws": "Samoa",
"countries.ye": "Yemen",
"countries.za": "South Africa",
"countries.zm": "Zambia",
"countries.zw": "Zimbabwe",
"continents.af": "Africa",
"continents.an": "Antarctica",
"continents.as": "Asia",
"continents.eu": "Europe",
"continents.na": "North America",
"continents.oc": "Oceania",
"continents.sa": "South America"
}

View file

@ -63,7 +63,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '2.0.3',
'version' => '2.1.0',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@ -334,7 +334,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '1.0.2',
'version' => '2.0.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,

View file

@ -231,6 +231,16 @@ return [ // Ordered by ABC.
'beta' => false,
'mock' => false,
],
'yammer' => [
'name' => 'Yammer',
'developers' => 'https://developer.yammer.com/docs/oauth-2',
'icon' => 'icon-yammer',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'yandex' => [
'name' => 'Yandex',
'developers' => 'https://tech.yandex.com/oauth/',

View file

@ -1,11 +1,13 @@
<?php
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Exception;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
use Appwrite\ClamAV\Network;
use Appwrite\Event\Event;
use Utopia\Database\Document;
App::get('/v1/health')
->desc('Get HTTP')
@ -15,22 +17,33 @@ App::get('/v1/health')
->label('sdk.namespace', 'health')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/health/get.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['status' => 'OK']);
$output = [
'status' => 'pass',
'ping' => 0
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
});
App::get('/v1/health/version')
->desc('Get Version')
->groups(['api', 'health'])
->label('scope', 'public')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_VERSION)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['version' => APP_VERSION_STABLE]);
$response->dynamic(new Document([ 'version' => APP_VERSION_STABLE ]), Response::MODEL_HEALTH_VERSION);
});
App::get('/v1/health/db')
@ -41,11 +54,17 @@ App::get('/v1/health/db')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getDB')
->label('sdk.description', '/docs/references/health/get-db.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->inject('utopia')
->action(function ($response, $utopia) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\App $utopia */
$checkStart = \microtime(true);
try {
$db = $utopia->getResource('db'); /* @var $db PDO */
@ -59,7 +78,12 @@ App::get('/v1/health/db')
throw new Exception('Database is not available', 500);
}
return $response->json(['status' => 'OK']);
$output = [
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
});
App::get('/v1/health/cache')
@ -70,19 +94,30 @@ App::get('/v1/health/cache')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getCache')
->label('sdk.description', '/docs/references/health/get-cache.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->inject('utopia')
->action(function ($response, $utopia) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\App $utopia */
/** @var Redis */
$checkStart = \microtime(true);
$redis = $utopia->getResource('cache');
if ($redis->ping(true)) {
return $response->json(['status' => 'OK']);
} else {
if (!$redis->ping(true)) {
throw new Exception('Cache is not available', 500);
}
$output = [
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
});
App::get('/v1/health/time')
@ -93,6 +128,9 @@ App::get('/v1/health/time')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getTime')
->label('sdk.description', '/docs/references/health/get-time.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_TIME)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
@ -131,7 +169,13 @@ App::get('/v1/health/time')
throw new Exception('Server time gaps detected');
}
$response->json(['remote' => $timestamp, 'local' => \time(), 'diff' => $diff]);
$output = [
'remoteTime' => $timestamp,
'localTime' => \time(),
'diff' => $diff
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_TIME);
});
App::get('/v1/health/queue/webhooks')
@ -142,11 +186,14 @@ App::get('/v1/health/queue/webhooks')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueWebhooks')
->label('sdk.description', '/docs/references/health/get-queue-webhooks.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size(Event::WEBHOOK_QUEUE_NAME)]);
$response->dynamic(new Document([ 'size' => Resque::size(Event::WEBHOOK_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/logs')
@ -157,11 +204,14 @@ App::get('/v1/health/queue/logs')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueLogs')
->label('sdk.description', '/docs/references/health/get-queue-logs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size(Event::AUDITS_QUEUE_NAME)]);
$response->dynamic(new Document([ 'size' => Resque::size(Event::AUDITS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/usage')
@ -172,11 +222,14 @@ App::get('/v1/health/queue/usage')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueUsage')
->label('sdk.description', '/docs/references/health/get-queue-usage.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size(Event::USAGE_QUEUE_NAME)]);
$response->dynamic(new Document([ 'size' => Resque::size(Event::USAGE_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/certificates')
@ -187,11 +240,14 @@ App::get('/v1/health/queue/certificates')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueCertificates')
->label('sdk.description', '/docs/references/health/get-queue-certificates.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME)]);
$response->dynamic(new Document([ 'size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/functions')
@ -202,11 +258,14 @@ App::get('/v1/health/queue/functions')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getQueueFunctions')
->label('sdk.description', '/docs/references/health/get-queue-functions.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$response->json(['size' => Resque::size(Event::FUNCTIONS_QUEUE_NAME)]);
$response->dynamic(new Document([ 'size' => Resque::size(Event::FUNCTIONS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/storage/local')
@ -217,10 +276,15 @@ App::get('/v1/health/storage/local')
->label('sdk.namespace', 'health')
->label('sdk.method', 'getStorageLocal')
->label('sdk.description', '/docs/references/health/get-storage-local.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_STATUS)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$checkStart = \microtime(true);
foreach ([
'Uploads' => APP_STORAGE_UPLOADS,
'Cache' => APP_STORAGE_CACHE,
@ -238,41 +302,51 @@ App::get('/v1/health/storage/local')
}
}
$response->json(['status' => 'OK']);
$output = [
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
});
App::get('/v1/health/anti-virus')
->desc('Get Anti virus')
->desc('Get Antivirus')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'health')
->label('sdk.method', 'getAntiVirus')
->label('sdk.method', 'getAntivirus')
->label('sdk.description', '/docs/references/health/get-storage-anti-virus.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_HEALTH_ANTIVIRUS)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$output = [
'status' => '',
'version' => ''
];
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
return $response->json([
'status' => 'disabled',
'version' => '',
]);
$output['status'] = 'disabled';
$output['version'] = '';
} else {
$antivirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
try {
$output['version'] = @$antivirus->version();
$output['status'] = (@$antivirus->ping()) ? 'pass' : 'fail';
} catch( \Exception $e) {
$output['status'] = 'offline';
$output['version'] = '';
}
}
$antiVirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
try {
$response->json([
'status' => (@$antiVirus->ping()) ? 'online' : 'offline',
'version' => @$antiVirus->version(),
]);
} catch (Throwable $e) {
$response->json([
'status' => 'offline',
'version' => '',
]);
}
$response->dynamic(new Document($output), Response::MODEL_HEALTH_ANTIVIRUS);
});
App::get('/v1/health/stats') // Currently only used internally

View file

@ -122,10 +122,10 @@ App::post('/v1/storage/files')
$mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
$antiVirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
$antivirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
if (!$antiVirus->fileScan($path)) {
if (!$antivirus->fileScan($path)) {
$device->delete($path);
throw new Exception('Invalid file', 403);
}
@ -882,4 +882,4 @@ App::get('/v1/storage/:bucketId/usage')
}
$response->dynamic($usage, Response::MODEL_USAGE_BUCKETS);
});
});

View file

@ -182,6 +182,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$db = null;
}
$swooleResponse->setStatusCode(500);
if(App::isDevelopment()) {
$swooleResponse->end('error: '.$th->getMessage());
}

View file

@ -21,6 +21,7 @@ use Appwrite\Extend\PDO;
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Database\Database as DatabaseOld;
use Appwrite\Event\Event;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP;
@ -153,6 +154,43 @@ if(!empty($user) || !empty($pass)) {
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', ''));
}
/**
* Old DB Filters
*/
DatabaseOld::addFilter('json',
function($value) {
if(!is_array($value)) {
return $value;
}
return json_encode($value);
},
function($value) {
return json_decode($value, true);
}
);
DatabaseOld::addFilter('encrypt',
function($value) {
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
return json_encode([
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
'iv' => bin2hex($iv),
'tag' => bin2hex($tag),
'version' => '1',
]);
},
function($value) {
$value = json_decode($value, true);
$key = App::getEnv('_APP_OPENSSL_KEY_V'.$value['version']);
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
);
/**
* New DB Filters
*/
@ -176,7 +214,7 @@ Database::addFilter('enum',
return $value;
},
function($value, Document $attribute) {
$formatOptions = json_decode($attribute->getAttribute('formatOptions', []), true);
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
if (isset($formatOptions['elements'])) {
$attribute->setAttribute('elements', $formatOptions['elements']);
}
@ -195,7 +233,7 @@ Database::addFilter('range',
return $value;
},
function($value, Document $attribute) {
$formatOptions = json_decode($attribute->getAttribute('formatOptions', []), true);
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
$attribute
->setAttribute('min', $formatOptions['min'])
@ -518,6 +556,7 @@ Locale::setLanguageFromJSON('sd', __DIR__ . '/config/locale/translations/sd.json
Locale::setLanguageFromJSON('si', __DIR__ . '/config/locale/translations/si.json');
Locale::setLanguageFromJSON('sk', __DIR__ . '/config/locale/translations/sk.json');
Locale::setLanguageFromJSON('sl', __DIR__ . '/config/locale/translations/sl.json');
Locale::setLanguageFromJSON('sn', __DIR__ . '/config/locale/translations/sn.json');
Locale::setLanguageFromJSON('sq', __DIR__ . '/config/locale/translations/sq.json');
Locale::setLanguageFromJSON('sv', __DIR__ . '/config/locale/translations/sv.json');
Locale::setLanguageFromJSON('ta', __DIR__ . '/config/locale/translations/ta.json');
@ -542,7 +581,6 @@ Locale::setLanguageFromJSON('zh-tw', __DIR__.'/config/locale/translations/zh-tw.
]);
// Runtime Execution
App::setResource('register', fn() => $register);
App::setResource('layout', function($locale) {
@ -552,36 +590,18 @@ App::setResource('layout', function($locale) {
return $layout;
}, ['locale']);
App::setResource('locale', function() {
return new Locale(App::getEnv('_APP_LOCALE', 'en'));
});
App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en')));
// Queues
App::setResource('events', function($register) {
return new Event('', '');
}, ['register']);
App::setResource('audits', function($register) {
return new Event(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME);
}, ['register']);
App::setResource('events', fn() => new Event('', ''));
App::setResource('audits', fn() => new Event(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME));
App::setResource('mails', fn() => new Event(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME));
App::setResource('deletes', fn() => new Event(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME));
App::setResource('database', fn() => new Event(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME));
App::setResource('usage', function($register) {
return new Stats($register->get('statsd'));
}, ['register']);
App::setResource('mails', function($register) {
return new Event(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME);
}, ['register']);
App::setResource('deletes', function($register) {
return new Event(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME);
}, ['register']);
App::setResource('database', function($register) {
return new Event(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME);
}, ['register']);
// Test Mock
App::setResource('clients', function($request, $console, $project) {
$console->setAttribute('platforms', [ // Always allow current host
'$collection' => 'platforms',
@ -589,7 +609,7 @@ App::setResource('clients', function($request, $console, $project) {
'type' => 'web',
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
/**
* Get All verified client URLs for both console and current projects
* + Filter for duplicated entries

View file

@ -113,17 +113,17 @@ $cli
if(App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
try {
$antiVirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
$antivirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
if((@$antiVirus->ping())) {
Console::success('AntiVirus...........connected 👍');
if((@$antivirus->ping())) {
Console::success('Antivirus...........connected 👍');
}
else {
Console::error('AntiVirus........disconnected 👎');
Console::error('Antivirus........disconnected 👎');
}
} catch (\Throwable $th) {
Console::error('AntiVirus........disconnected 👎');
Console::error('Antivirus........disconnected 👎');
}
}

View file

@ -11,55 +11,106 @@ use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Migration\Migration;
use Utopia\Validator\Text;
Config::load('collections.old', __DIR__.'/../config/collections.old.php');
Config::load('collections.old', __DIR__ . '/../config/collections.old.php');
$cli
->task('migrate')
->param('version', APP_VERSION_STABLE, new Text(8), 'Version to migrate to.', true)
->action(function ($version) use ($register) {
Authorization::disable();
if (!array_key_exists($version, Migration::$versions)) {
Console::error("Version {$version} not found.");
Console::exit(1);
return;
}
$options = [];
if (str_starts_with($version, '0.12.')) {
Console::error('--------------------');
Console::error('WARNING');
Console::error('--------------------');
Console::warning('Migrating to Version 0.12.x introduces a major breaking change within the Database Service!');
Console::warning('Before migrating, please read about the breaking changes here:');
Console::info('https://appwrite.io/guide-to-db-migration');
$confirm = Console::confirm("If you want to proceed, type 'yes':");
if ($confirm != 'yes') {
Console::exit(1);
return;
}
Console::log('');
Console::log('Collections');
Console::log('--------------------');
Console::warning('Be aware that following actions will happen during the migration:');
Console::warning('- Nested Document rules will be migrated to String attributes');
Console::warning('- Numeric rules will be migrated to float attributes');
Console::warning('- Wildcard and Markdown rules will be converted to string attributes');
Console::info("Do you want to migrate your Database Collections?");
$options['migrateCollections'] = Console::confirm("Type 'yes' or 'no':");
if ($options['migrateCollections'] === 'yes') {
Console::log('');
Console::log('Documents');
Console::log('------------------');
Console::warning('Be aware that following actions will happen during the migration:');
Console::warning('- Nested Documents will be stored as JSON values');
Console::warning('- All Numeric values will be converted to float');
Console::warning('- All Wildcard and Markdown values will be converted to string');
Console::info("Do you want to migrate your Database Documents?");
$options['migrateDocuments'] = Console::confirm("Type 'yes' or 'no':");
} else {
$options['migrateDocuments'] = 'no';
}
if (
!in_array($options['migrateDocuments'], ['yes', 'no'])
|| !in_array($options['migrateCollections'], ['yes', 'no'])
) {
Console::error("You must reply with 'yes' or 'no'!");
Console::exit(1);
return;
}
}
Config::load('collectionsold', __DIR__ . '/../config/collections.old.php');
Console::success('Starting Data Migration to version ' . $version);
Console::success('Starting Data Migration to version '.$version);
$db = $register->get('db', true);
$cache = $register->get('cache', true);
$cache->flushAll();
$consoleDB = new Database();
$consoleDB
->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache))
->setNamespace('app_console') // Main DB
->setMocks(Config::getParam('collections.old', []));
->setMocks(Config::getParam('collectionsold', []));
$projectDB = new Database();
$projectDB
->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache))
->setMocks(Config::getParam('collections.old', []));
->setMocks(Config::getParam('collectionsold', []));
$console = $consoleDB->getDocument('console');
Authorization::disable();
$limit = 30;
$sum = 30;
$offset = 0;
$projects = [$console];
$count = 0;
$class = 'Appwrite\\Migration\\Version\\'.Migration::$versions[$version];
$migration = new $class($register->get('db'));
$class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version];
$migration = new $class($register->get('db'), $register->get('cache'), $options);
while ($sum > 0) {
foreach ($projects as $project) {
try {
$migration
->setProject($project, $projectDB)
->setProject($project, $projectDB, $consoleDB)
->execute();
} catch (\Throwable $th) {
throw $th;
Console::error('Failed to update project ("'.$project->getId().'") version with error: '.$th->getMessage());
Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage());
}
}
@ -67,7 +118,7 @@ $cli
'limit' => $limit,
'offset' => $offset,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_PROJECTS,
'$collection=' . Database::SYSTEM_COLLECTION_PROJECTS,
],
]);
@ -76,9 +127,11 @@ $cli
$count = $count + $sum;
if ($sum > 0) {
Console::log('Fetched '.$count.'/'.$consoleDB->getSum().' projects...');
Console::log('Fetched ' . $count . '/' . $consoleDB->getSum() . ' projects...');
}
}
$cache->flushAll();
Swoole\Event::wait(); // Wait for Coroutines to finish
Console::success('Data Migration Completed');
});

View file

@ -259,7 +259,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
<div class="box margin-bottom-small">
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
<div class="chart background-image-no border-no margin-bottom-no">
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="CPU Time (seconds)=functionsCompute" data-colors="orange" data-height="140" data-show-y-axis="true" />
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="CPU Time (milliseconds)=functionsCompute" data-colors="orange" data-height="140" data-show-y-axis="true" />
</div>
</div>
</div>

View file

@ -3,7 +3,6 @@
$httpPort = $this->getParam('httpPort', '');
$httpsPort = $this->getParam('httpsPort', '');
$version = $this->getParam('version', '');
$version = '0.12.0-alpha'; // TODO: TEMPORARY
$organization = $this->getParam('organization', '');
$image = $this->getParam('image', '');
?>version: '3'
@ -33,7 +32,7 @@ services:
networks:
- gateway
- appwrite
appwrite:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
container_name: appwrite
@ -322,7 +321,7 @@ services:
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
appwrite-maintenance:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: maintenance

View file

@ -67,7 +67,7 @@
},
"repositories": [],
"require-dev": {
"appwrite/sdk-generator": "0.16.2",
"appwrite/sdk-generator": "0.16.3",
"phpunit/phpunit": "9.5.10",
"swoole/ide-helper": "4.8.3",
"textalk/websocket": "1.5.5",

14
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d4017e5555cb2462f367b6881d5b6a5c",
"content-hash": "c755b0ae991777da3e44b0442690fa46",
"packages": [
{
"name": "adhocore/jwt",
@ -3014,16 +3014,16 @@
},
{
"name": "appwrite/sdk-generator",
"version": "0.16.2",
"version": "0.16.3",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "e3a20c96a745a9c4aa048fd344650fcfbf41cf6f"
"reference": "6185cdfe4c4261287240639f3a7fdc05e7ae2337"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e3a20c96a745a9c4aa048fd344650fcfbf41cf6f",
"reference": "e3a20c96a745a9c4aa048fd344650fcfbf41cf6f",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6185cdfe4c4261287240639f3a7fdc05e7ae2337",
"reference": "6185cdfe4c4261287240639f3a7fdc05e7ae2337",
"shasum": ""
},
"require": {
@ -3057,9 +3057,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.16.2"
"source": "https://github.com/appwrite/sdk-generator/tree/0.16.3"
},
"time": "2021-11-12T11:09:38+00:00"
"time": "2021-12-16T23:56:47+00:00"
},
{
"name": "composer/pcre",

View file

@ -1 +1 @@
Check the Appwrite Anti Virus server is up and connection is successful.
Check the Appwrite Antivirus server is up and connection is successful.

View file

@ -1,3 +1,6 @@
## 2.0.0
- BREAKING All services and methods now return structured response objects instead of `Response` object
## 1.0.2
- Support for Appwrite 0.11

View file

@ -1,3 +1,7 @@
## 2.1.0
- Updated `flutter_we_auth` plugin now supports Flutter web for OAuth2 sessions [read more](https://github.com/appwrite/sdk-for-flutter/blob/master/README.md#web)
- Added linters and updated codebase to match the rules
## 2.0.3
- Support for Appwrite 0.11
- Fix comments on `sum` attributes

View file

@ -714,13 +714,13 @@
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 520px; margin-left: 748px;">
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">
AntiVirus (ClamAV)
Antivirus (ClamAV)
</div>
</div>
</div>
</foreignObject>
<text x="807" y="524" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">
AntiVirus (ClamAV)
Antivirus (ClamAV)
</text>
</switch>
</g>

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -0,0 +1,137 @@
<?php
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
// Reference Material
// https://developer.yammer.com/docs/oauth-2
class Yammer extends OAuth2
{
/**
* @var string
*/
private $endpoint = 'https://www.yammer.com/oauth2/';
/**
* @var array
*/
protected $user = [];
/**
* @return string
*/
public function getName(): string
{
return 'yammer';
}
/**
* @return string
*/
public function getLoginURL(): string
{
return $this->endpoint . 'oauth2/authorize?'.
\http_build_query([
'client_id' => $this->appID,
'response_type' => 'code',
'redirect_uri' => $this->callback,
'state' => \json_encode($this->state)
]);
}
/**
* @param string $code
*
* @return string
*/
public function getAccessToken(string $code): string
{
$headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request(
'POST',
$this->endpoint . 'access_token?',
$headers,
\http_build_query([
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
'code' => $code,
'grant_type' => 'authorization_code'
])
);
$accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token']['token'])) {
return $accessToken['access_token']['token'];
}
return '';
}
/**
* @param string $accessToken
*
* @return string
*/
public function getUserID(string $accessToken): string
{
$user = $this->getUser($accessToken);
if (isset($user['id'])) {
return $user['id'];
}
return '';
}
/**
* @param string $accessToken
*
* @return string
*/
public function getUserEmail(string $accessToken): string
{
$user = $this->getUser($accessToken);
if (isset($user['email'])) {
return $user['email'];
}
return '';
}
/**
* @param string $accessToken
*
* @return string
*/
public function getUserName(string $accessToken): string
{
$user = $this->getUser($accessToken);
if (isset($user['full_name'])) {
return $user['full_name'];
}
return '';
}
/**
* @param string $accessToken
*
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$headers = ['Authorization: Bearer '. \urlencode($accessToken)];
$user = $this->request('GET', 'https://www.yammer.com/api/v1/users/current.json', $headers);
$this->user = \json_decode($user, true);
}
return $this->user;
}
}

View file

@ -91,6 +91,26 @@ class Document extends ArrayObject
return $temp;
}
/**
* Get Document Attributes
*
* @return array
*/
public function getAttributes(): array
{
$attributes = [];
foreach ($this as $attribute => $value) {
if(array_key_exists($attribute, ['$id' => true, '$permissions' => true, '$collection' => true, '$execute' => []])) {
continue;
}
$attributes[$attribute] = $value;
}
return $attributes;
}
/**
* Set Attribute.
*
@ -215,7 +235,7 @@ class Document extends ArrayObject
*
* @return array
*/
public function getArrayCopy(array $whitelist = [], array $blacklist = [])
public function getArrayCopy(array $whitelist = [], array $blacklist = []): array
{
$array = parent::getArrayCopy();

View file

@ -2,34 +2,50 @@
namespace Appwrite\Migration;
use Appwrite\Database\Document;
use Appwrite\Database\Database;
use Appwrite\Database\Document as OldDocument;
use Appwrite\Database\Database as OldDatabase;
use PDO;
use Redis;
use Swoole\Runtime;
use Utopia\CLI\Console;
use Utopia\Exception;
abstract class Migration
{
/**
* @var array
*/
protected array $options;
/**
* @var PDO
*/
protected $db;
protected PDO $db;
/**
* @var Redis
*/
protected Redis $cache;
/**
* @var int
*/
protected $limit = 50;
protected int $limit = 500;
/**
* @var Document
* @var OldDocument
*/
protected $project;
protected OldDocument $project;
/**
* @var Database
* @var OldDatabase
*/
protected $projectDB;
protected OldDatabase $oldProjectDB;
/**
* @var OldDatabase
*/
protected OldDatabase $oldConsoleDB;
/**
* @var array
@ -49,32 +65,44 @@ abstract class Migration
'0.10.3' => 'V09',
'0.10.4' => 'V09',
'0.11.0' => 'V10',
'0.12.0' => 'V10',
'0.12.0' => 'V11',
];
/**
* Migration constructor.
*
* @param PDO $pdo
* @param PDO $db
* @param Redis|null $cache
* @param array $options
* @return void
*/
public function __construct(PDO $db)
public function __construct(PDO $db, Redis $cache = null, array $options = [])
{
$this->options = $options;
$this->db = $db;
if (!is_null($cache)) {
$this->cache = $cache;
}
}
/**
* Set project for migration.
*
* @param Document $project
* @param Database $projectDB
* @param OldDocument $project
* @param OldDatabase $projectDB
* @param OldDatabase $oldConsoleDB
*
* @return Migration
* @return self
*/
public function setProject(Document $project, Database $projectDB): Migration
public function setProject(OldDocument $project, OldDatabase $projectDB, OldDatabase $oldConsoleDB): self
{
$this->project = $project;
$this->projectDB = $projectDB;
$this->projectDB->setNamespace('app_' . $project->getId());
$this->oldProjectDB = $projectDB;
$this->oldProjectDB->setNamespace('app_' . $project->getId());
$this->oldConsoleDB = $oldConsoleDB;
return $this;
}
@ -117,7 +145,7 @@ abstract class Migration
}
try {
$new = $this->projectDB->overwriteDocument($document->getArrayCopy());
$new = $this->projectDB->overwriteDocument($new->getArrayCopy());
} catch (\Throwable $th) {
Console::error('Failed to update document: ' . $th->getMessage());
return;
@ -134,7 +162,14 @@ abstract class Migration
}
}
public function check_diff_multi($array1, $array2)
/**
* Checks 2 arrays for differences.
*
* @param array $array1
* @param array $array2
* @return array
*/
public function check_diff_multi(array $array1, array $array2): array
{
$result = array();

View file

@ -0,0 +1,683 @@
<?php
namespace Appwrite\Migration\Version;
use Appwrite\Database\Database as OldDatabase;
use Appwrite\Database\Document as OldDocument;
use Appwrite\Migration\Migration;
use Exception;
use PDO;
use Redis;
use Swoole\Runtime;
use Throwable;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Config\Config;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Limit;
use Utopia\Database\Exception\Authorization as ExceptionAuthorization;
use Utopia\Database\Exception\Structure;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
global $register;
class V11 extends Migration
{
protected Database $dbInternal;
protected Database $dbExternal;
protected Database $dbConsole;
protected array $oldCollections;
protected array $newCollections;
public function __construct(PDO $db, Redis $cache = null, array $options = [])
{
parent::__construct($db, $cache, $options);
$this->options = array_map(fn ($option) => $option === 'yes' ? true : false, $this->options);
if (!is_null($cache)) {
$cacheAdapter = new Cache(new RedisCache($this->cache));
$this->dbInternal = new Database(new MariaDB($this->db), $cacheAdapter); // namespace is set on execution
$this->dbExternal = new Database(new MariaDB($this->db), $cacheAdapter); // namespace is set on execution
$this->dbConsole = new Database(new MariaDB($this->db), $cacheAdapter);
$this->dbConsole->setNamespace('project_console_internal');
}
$this->newCollections = Config::getParam('collections', []);
$this->oldCollections = Config::getParam('collectionsold', []);
}
public function execute(): void
{
Authorization::disable();
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
$oldProject = $this->project;
$this->dbInternal->setNamespace('project_' . $oldProject->getId() . '_internal');
$this->dbExternal->setNamespace('project_' . $oldProject->getId() . '_external');
Console::info('');
Console::info('------------------------------------');
Console::info('Migrating project ' . $oldProject->getAttribute('name'));
Console::info('------------------------------------');
/**
* Create internal/external structure for projects and skip the console project.
*/
if ($oldProject->getId() !== 'console') {
$project = $this->dbConsole->getDocument('projects', $oldProject->getId());
/**
* Migrate Project Document.
*/
if ($project->isEmpty()) {
$newProject = $this->fixDocument($oldProject);
$newProject->setAttribute('version', '0.12.0');
$project = $this->dbConsole->createDocument('projects', $newProject);
Console::log('Created project document: ' . $oldProject->getAttribute('name') . ' (' . $oldProject->getId() . ')');
}
/**
* Create internal DB tables
*/
if (!$this->dbInternal->exists()) {
$this->dbInternal->create();
Console::log('Created internal tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
}
/**
* Create external DB tables
*/
if (!$this->dbExternal->exists()) {
$this->dbExternal->create();
Console::log('Created external tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
}
/**
* Create Audit tables
*/
if ($this->dbInternal->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($this->dbInternal);
$audit->setup();
Console::log('Created audit tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
}
/**
* Create Abuse tables
*/
if ($this->dbInternal->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
$adapter = new TimeLimit("", 0, 1, $this->dbInternal);
$adapter->setup();
Console::log('Created abuse tables for : ' . $project->getAttribute('name') . ' (' . $project->getId() . ')');
}
/**
* Create internal collections for Project
*/
foreach ($this->newCollections as $key => $collection) {
if (!$this->dbInternal->getCollection($key)->isEmpty()) continue; // Skip if project collection already exists
$attributes = [];
$indexes = [];
foreach ($collection['attributes'] as $attribute) {
$attributes[] = new Document([
'$id' => $attribute['$id'],
'type' => $attribute['type'],
'size' => $attribute['size'],
'required' => $attribute['required'],
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
]);
}
foreach ($collection['indexes'] as $index) {
$indexes[] = new Document([
'$id' => $index['$id'],
'type' => $index['type'],
'attributes' => $index['attributes'],
'lengths' => $index['lengths'],
'orders' => $index['orders'],
]);
}
$this->dbInternal->createCollection($key, $attributes, $indexes);
}
if ($this->options['migrateCollections']) {
$this->migrateExternalCollections();
}
} else {
Console::log('Skipped console project migration.');
}
$sum = $this->limit;
$offset = 0;
$total = 0;
/**
* Migrate internal documents
*/
while ($sum >= $this->limit) {
$all = $this->oldProjectDB->getCollection([
'limit' => $this->limit,
'offset' => $offset,
'orderType' => 'DESC',
'filters' => [
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_COLLECTIONS,
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_RULES,
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_TASKS,
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_PROJECTS,
'$collection!=' . OldDatabase::SYSTEM_COLLECTION_CONNECTIONS,
]
]);
$sum = \count($all);
Console::log('Migrating Internal Documents: ' . $offset . ' / ' . $this->oldProjectDB->getSum());
foreach ($all as $document) {
if (
!array_key_exists($document->getCollection(), $this->oldCollections)
) {
continue;
}
$new = $this->fixDocument($document);
if (empty($new->getId())) {
Console::warning('Skipped Document due to missing ID.');
continue;
}
try {
if ($this->dbInternal->getDocument($new->getCollection(), $new->getId())->isEmpty()) {
$this->dbInternal->createDocument($new->getCollection(), $new);
}
} catch (\Throwable $th) {
Console::error('Failed to update document: ' . $th->getMessage());
continue;
if ($document && $new->getId() !== $document->getId()) {
throw new Exception('Duplication Error');
}
}
}
$offset += $this->limit;
$total += $sum;
}
Console::log('Migrated ' . $total . ' Internal Documents.');
}
/**
* Migrate external collections for Project
*
* @return void
* @throws Exception
* @throws Throwable
* @throws Limit
* @throws ExceptionAuthorization
* @throws Structure
*/
protected function migrateExternalCollections(): void
{
$sum = $this->limit;
$offset = 0;
while ($sum >= $this->limit) {
$databaseCollections = $this->oldProjectDB->getCollection([
'limit' => $this->limit,
'offset' => $offset,
'orderType' => 'DESC',
'filters' => [
'$collection=' . OldDatabase::SYSTEM_COLLECTION_COLLECTIONS,
]
]);
$sum = \count($databaseCollections);
Console::log('Migrating Collections: ' . $offset . ' / ' . $this->oldProjectDB->getSum());
foreach ($databaseCollections as $oldCollection) {
$id = $oldCollection->getId();
$permissions = $oldCollection->getPermissions();
$name = $oldCollection->getAttribute('name');
$newCollection = $this->dbExternal->getCollection($id);
if ($newCollection->isEmpty()) {
$this->dbExternal->createCollection($id);
/**
* Migrate permissions
*/
$read = $this->migrateWildcardPermissions($permissions['read'] ?? []);
$write = $this->migrateWildcardPermissions($permissions['write'] ?? []);
/**
* Suffix collection name with a subsequent number to make it unique if possible.
*/
$suffix = 1;
while ($this->dbInternal->findOne('collections', [
new Query('name', Query::TYPE_EQUAL, [$name])
])) {
$name .= ' - ' . $suffix++;
}
$this->dbInternal->createDocument('collections', new Document([
'$id' => $id,
'$read' => [],
'$write' => [],
'permission' => 'document',
'dateCreated' => time(),
'dateUpdated' => time(),
'name' => $name,
'search' => implode(' ', [$id, $name]),
]));
} else {
Console::warning('Skipped Collection ' . $newCollection->getId() . ' from ' . $newCollection->getCollection());
}
/**
* Migrate collection rules to attributes
*/
$attributes = $this->getCollectionAttributes($oldCollection);
foreach ($attributes as $attribute) {
try {
$this->dbExternal->createAttribute(
collection: $attribute['$collection'],
id: $attribute['$id'],
type: $attribute['type'],
size: $attribute['size'],
required: $attribute['required'],
default: $attribute['default'],
signed: $attribute['signed'],
array: $attribute['array'],
format: $attribute['format'] ?? null,
formatOptions: $attribute['formatOptions'] ?? [],
filters: $attribute['filters']
);
$this->dbInternal->createDocument('attributes', new Document([
'$id' => $attribute['$collection'] . '_' . $attribute['$id'],
'key' => $attribute['$id'],
'collectionId' => $attribute['$collection'],
'type' => $attribute['type'],
'status' => 'available',
'size' => $attribute['size'],
'required' => $attribute['required'],
'signed' => $attribute['signed'],
'default' => $attribute['default'],
'array' => $attribute['array'],
'format' => $attribute['format'] ?? null,
'formatOptions' => $attribute['formatOptions'] ?? null,
'filters' => $attribute['filters']
]));
Console::log('Created "' . $attribute['$id'] . '" attribute in collection: ' . $name);
} catch (\Throwable $th) {
Console::log($th->getMessage() . ' - ("' . $attribute['$id'] . '" attribute in collection ' . $name . ')');
}
}
if ($this->options['migrateDocuments']) {
$this->migrateExternalDocuments(collection: $id);
}
}
$offset += $this->limit;
}
}
/**
* Migrate all external documents
*
* @return void
* @throws Exception
* @throws Throwable
* @throws ExceptionAuthorization
* @throws Structure
*/
protected function migrateExternalDocuments(string $collection): void
{
$sum = $this->limit;
$offset = 0;
while ($sum >= $this->limit) {
$allDocs = $this->oldProjectDB->getCollection([
'limit' => $this->limit,
'offset' => $offset,
'orderType' => 'DESC',
'filters' => [
'$collection=' . $collection
]
]);
$sum = \count($allDocs);
Console::log('Migrating External Documents for Collection ' . $collection . ': ' . $offset . ' / ' . $this->oldProjectDB->getSum());
foreach ($allDocs as $document) {
if (!$this->dbExternal->getDocument($collection, $document->getId())->isEmpty()) {
continue;
}
go(function ($document) {
foreach ($document as $key => $attr) {
/**
* Convert nested Document to JSON strings.
*/
if ($document->getAttribute($key) instanceof OldDocument) {
$document[$key] = json_encode($this->fixDocument($attr)->getArrayCopy());
}
/**
* Convert numeric Attributes to float.
*/
if (is_numeric($attr)) {
$document[$key] = floatval($attr);
}
if (\is_array($attr)) {
foreach ($attr as $index => $child) {
/**
* Convert array of nested Document to array JSON strings.
*/
if ($document->getAttribute($key)[$index] instanceof OldDocument) {
$document[$key][$index] = json_encode($this->fixDocument($child)->getArrayCopy());
}
/**
* Convert array of numeric Attributes to array float.
*/
if (is_numeric($attr)) {
$document[$key][$index] = floatval($child); // Convert any numeric to float
}
}
}
}
}, $document);
$document = new Document($document->getArrayCopy());
$document = $this->migratePermissions($document);
$this->dbExternal->createDocument($collection, $document);
}
$offset += $this->limit;
}
}
/**
* Migrates single docuemnt.
*
* @param OldDocument $oldDocument
* @return Document
* @throws Exception
*/
protected function fixDocument(OldDocument $oldDocument): Document
{
$document = new Document($oldDocument->getArrayCopy());
$document = $this->migratePermissions($document);
/**
* Check attributes and set their default values.
*/
if (array_key_exists($document->getCollection(), $this->oldCollections)) {
foreach ($this->newCollections[$document->getCollection()]['attributes'] as $attr) {
if (
(!$attr['array'] ||
($attr['array'] && array_key_exists('filter', $attr)
&& in_array('json', $attr['filter'])))
&& empty($document->getAttribute($attr['$id'], null))
) {
$document->setAttribute($attr['$id'], $attr['default'] ?? null);
}
}
}
switch ($document->getAttribute('$collection')) {
case OldDatabase::SYSTEM_COLLECTION_PLATFORMS:
$projectId = $this->getProjectIdFromReadPermissions($document);
/**
* Set Project ID
*/
if ($document->getAttribute('projectId') === null) {
$document->setAttribute('projectId', $projectId);
}
/**
* Set empty key and store if null
*/
if ($document->getAttribute('key') === null) {
$document->setAttribute('key', '');
}
if ($document->getAttribute('store') === null) {
$document->setAttribute('store', '');
}
/**
* Reset Permissions
*/
$document->setAttribute('$read', ['role:all']);
$document->setAttribute('$write', ['role:all']);
break;
case OldDatabase::SYSTEM_COLLECTION_DOMAINS:
$projectId = $this->getProjectIdFromReadPermissions($document);
/**
* Set Project ID
*/
if ($document->getAttribute('projectId') === null) {
$document->setAttribute('projectId', $projectId);
}
/**
* Set empty verification if null
*/
if ($document->getAttribute('verification') === null) {
$document->setAttribute('verification', false);
}
/**
* Reset Permissions
*/
$document->setAttribute('$read', ['role:all']);
$document->setAttribute('$write', ['role:all']);
break;
case OldDatabase::SYSTEM_COLLECTION_KEYS:
case OldDatabase::SYSTEM_COLLECTION_WEBHOOKS:
$projectId = $this->getProjectIdFromReadPermissions($document);
/**
* Set Project ID
*/
if ($document->getAttribute('projectId') === null) {
$document->setAttribute('projectId', $projectId);
}
/**
* Reset Permissions
*/
$document->setAttribute('$read', ['role:all']);
$document->setAttribute('$write', ['role:all']);
break;
case OldDatabase::SYSTEM_COLLECTION_USERS:
/**
* Set deleted attribute to false
*/
if ($document->getAttribute('deleted') === null) {
$document->setAttribute('deleted', false);
}
/**
* Remove deprecated user status 0 and replace with boolean.
*/
if ($document->getAttribute('status') === 2) {
$document->setAttribute('status', false);
} else {
$document->setAttribute('status', true);
}
/**
* Set default values for arrays if not set.
*/
if (empty($document->getAttribute('prefs', []))) {
$document->setAttribute('prefs', []);
}
if (empty($document->getAttribute('sessions', []))) {
$document->setAttribute('sessions', []);
}
if (empty($document->getAttribute('tokens', []))) {
$document->setAttribute('tokens', []);
}
if (empty($document->getAttribute('memberships', []))) {
$document->setAttribute('memberships', []);
}
/**
* Replace user:{self} with user:USER_ID
*/
$write = $document->getWrite();
$document->setAttribute('$write', str_replace('user:{self}', "user:{$document->getId()}", $write));
break;
case OldDatabase::SYSTEM_COLLECTION_FILES:
/**
* Migrating breakind changes on Files.
*/
if (!empty($document->getAttribute('fileOpenSSLVersion', null))) {
$document
->setAttribute('openSSLVersion', $document->getAttribute('fileOpenSSLVersion'))
->removeAttribute('fileOpenSSLVersion');
}
if (!empty($document->getAttribute('fileOpenSSLCipher', null))) {
$document
->setAttribute('openSSLCipher', $document->getAttribute('fileOpenSSLCipher'))
->removeAttribute('fileOpenSSLCipher');
}
if (!empty($document->getAttribute('fileOpenSSLTag', null))) {
$document
->setAttribute('openSSLTag', $document->getAttribute('fileOpenSSLTag'))
->removeAttribute('fileOpenSSLTag');
}
if (!empty($document->getAttribute('fileOpenSSLIV', null))) {
$document
->setAttribute('openSSLIV', $document->getAttribute('fileOpenSSLIV'))
->removeAttribute('fileOpenSSLIV');
}
/**
* Remove deprecated attributes.
*/
$document->removeAttribute('folderId');
$document->removeAttribute('token');
break;
}
return $document;
}
/**
* Migrates $permissions to independent $read and $write.
* @param Document $document
* @return Document
*/
protected function migratePermissions(Document $document): Document
{
if ($document->isSet('$permissions')) {
$permissions = $document->getAttribute('$permissions', []);
$read = $this->migrateWildcardPermissions($permissions['read'] ?? []);
$write = $this->migrateWildcardPermissions($permissions['write'] ?? []);
$document->setAttribute('$read', $read);
$document->setAttribute('$write', $write);
$document->removeAttribute('$permissions');
}
return $document;
}
/**
* Takes a permissions array and replaces wildcard * with role:all.
* @param array $permissions
* @return array
*/
protected function migrateWildcardPermissions(array $permissions): array
{
return array_map(function ($permission) {
if ($permission === '*') return 'role:all';
return $permission;
}, $permissions);
}
/**
* Get new collection attributes from old collection rules.
* @param OldDocument $collection
* @return array
*/
protected function getCollectionAttributes(OldDocument $collection): array
{
$attributes = [];
foreach ($collection->getAttribute('rules', []) as $key => $value) {
$collectionId = $collection->getId();
$id = $value['key'];
$array = $value['array'] ?? false;
$required = $value['required'] ?? false;
$default = $value['default'] ?? null;
$default = match ($value['type']) {
OldDatabase::SYSTEM_VAR_TYPE_NUMERIC => floatval($default),
default => $default
};
$type = match ($value['type']) {
OldDatabase::SYSTEM_VAR_TYPE_TEXT => Database::VAR_STRING,
OldDatabase::SYSTEM_VAR_TYPE_EMAIL => Database::VAR_STRING,
OldDatabase::SYSTEM_VAR_TYPE_DOCUMENT => Database::VAR_STRING,
OldDatabase::SYSTEM_VAR_TYPE_IP => Database::VAR_STRING,
OldDatabase::SYSTEM_VAR_TYPE_URL => Database::VAR_STRING,
OldDatabase::SYSTEM_VAR_TYPE_WILDCARD => Database::VAR_STRING,
OldDatabase::SYSTEM_VAR_TYPE_NUMERIC => Database::VAR_FLOAT,
OldDatabase::SYSTEM_VAR_TYPE_BOOLEAN => Database::VAR_BOOLEAN,
default => Database::VAR_STRING
};
$size = $type === Database::VAR_STRING ? 65_535 : 0; // Max size of text in MariaDB
$attributes[$key] = [
'$collection' => $collectionId,
'$id' => $id,
'type' => $type,
'size' => $size,
'required' => $required,
'default' => $default,
'array' => $array,
'signed' => true,
'filters' => []
];
if ($type === Database::VAR_FLOAT) {
$attributes[$key]['format'] = APP_DATABASE_ATTRIBUTE_FLOAT_RANGE;
$attributes[$key]['formatOptions'] = [];
$attributes[$key]['formatOptions']['min'] = -PHP_FLOAT_MAX;
$attributes[$key]['formatOptions']['max'] = PHP_FLOAT_MAX;
}
}
return $attributes;
}
/**
* @param Document $document
* @return string|null
* @throws Exception
*/
protected function getProjectIdFromReadPermissions(Document $document): string|null
{
$readPermissions = $document->getRead();
$teamId = str_replace('team:', '', reset($readPermissions));
return $this->oldConsoleDB->getCollectionFirst([
'filters' => [
'$collection=' . OldDatabase::SYSTEM_COLLECTION_PROJECTS,
'teamId=' . $teamId
]
])->getId();
}
}

View file

@ -52,6 +52,11 @@ use Appwrite\Utopia\Response\Model\Tag;
use Appwrite\Utopia\Response\Model\Token;
use Appwrite\Utopia\Response\Model\Webhook;
use Appwrite\Utopia\Response\Model\Preferences;
use Appwrite\Utopia\Response\Model\HealthAntivirus;
use Appwrite\Utopia\Response\Model\HealthQueue;
use Appwrite\Utopia\Response\Model\HealthStatus;
use Appwrite\Utopia\Response\Model\HealthTime;
use Appwrite\Utopia\Response\Model\HealthVersion;
use Appwrite\Utopia\Response\Model\Mock; // Keep last
use Appwrite\Utopia\Response\Model\Runtime;
use Appwrite\Utopia\Response\Model\UsageBuckets;
@ -160,6 +165,13 @@ class Response extends SwooleResponse
const MODEL_PLATFORM_LIST = 'platformList';
const MODEL_DOMAIN = 'domain';
const MODEL_DOMAIN_LIST = 'domainList';
// Health
const MODEL_HEALTH_STATUS = 'healthStatus';
const MODEL_HEALTH_VERSION = 'healthVersion';
const MODEL_HEALTH_QUEUE = 'healthQueue';
const MODEL_HEALTH_TIME = 'healthTime';
const MODEL_HEALTH_ANTIVIRUS = 'healthAntivirus';
// Deprecated
const MODEL_PERMISSIONS = 'permissions';
@ -255,6 +267,11 @@ class Response extends SwooleResponse
->setModel(new Language())
->setModel(new Currency())
->setModel(new Phone())
->setModel(new HealthAntivirus())
->setModel(new HealthQueue())
->setModel(new HealthStatus())
->setModel(new HealthTime())
->setModel(new HealthVersion())
->setModel(new Metric())
->setModel(new UsageDatabase())
->setModel(new UsageCollection())

View file

@ -23,7 +23,7 @@ class Any extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -58,7 +58,7 @@ class BaseList extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -85,7 +85,7 @@ class Collection extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -36,7 +36,7 @@ class Continent extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -36,7 +36,7 @@ class Country extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -66,7 +66,7 @@ class Currency extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -17,7 +17,7 @@ class Document extends Any
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -65,7 +65,7 @@ class Domain extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -42,7 +42,7 @@ class Error extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -39,7 +39,7 @@ class ErrorDev extends Error
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -85,7 +85,7 @@ class Execution extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -74,7 +74,7 @@ class File extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -110,7 +110,7 @@ class Func extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -0,0 +1,47 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class HealthAntivirus extends Model
{
public function __construct()
{
$this
->addRule('version', [
'type' => self::TYPE_STRING,
'description' => 'Antivirus version.',
'default' => '',
'example' => '1.0.0',
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Antivirus status. Possible values can are: `disabled`, `offline`, `online`',
'default' => '',
'example' => 'online',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Health Antivirus';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_HEALTH_ANTIVIRUS;
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class HealthQueue extends Model
{
public function __construct()
{
$this
->addRule('size', [
'type' => self::TYPE_INTEGER,
'description' => 'Amount of actions in the queue.',
'default' => 0,
'example' => 8,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Health Queue';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_HEALTH_QUEUE;
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class HealthStatus extends Model
{
public function __construct()
{
$this
->addRule('ping', [
'type' => self::TYPE_INTEGER,
'description' => 'Duration in milliseconds how long the health check took.',
'default' => 0,
'example' => 128,
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Service status. Possible values can are: `pass`, `fail`',
'default' => '',
'example' => 'pass',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Health Status';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_HEALTH_STATUS;
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class HealthTime extends Model
{
public function __construct()
{
$this
->addRule('remoteTime', [
'type' => self::TYPE_INTEGER,
'description' => 'Current unix timestamp on trustful remote server.',
'default' => 0,
'example' => 1639490751,
])
->addRule('localTime', [
'type' => self::TYPE_INTEGER,
'description' => 'Current unix timestamp of local server where Appwrite runs.',
'default' => 0,
'example' => 1639490844,
])
->addRule('diff', [
'type' => self::TYPE_INTEGER,
'description' => 'Difference of unix remote and local timestamps in milliseconds.',
'default' => 0,
'example' => 93,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Health Time';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_HEALTH_TIME;
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class HealthVersion extends Model
{
public function __construct()
{
$this
->addRule('version', [
'type' => self::TYPE_STRING,
'description' => 'Version of the Appwrite instance.',
'default' => '',
'example' => '0.11.0',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Health Version';
}
/**
* Get Type
*
* @return string
*/
public function getType():string
{
return Response::MODEL_HEALTH_VERSION;
}
}

View file

@ -29,7 +29,7 @@ class JWT extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -54,7 +54,7 @@ class Key extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -42,7 +42,7 @@ class Language extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -66,7 +66,7 @@ class Locale extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -150,7 +150,7 @@ class Log extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -79,7 +79,7 @@ class Membership extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -30,7 +30,7 @@ class Mock extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -23,7 +23,7 @@ class None extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -42,7 +42,7 @@ class Phone extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -76,7 +76,7 @@ class Platform extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -22,7 +22,7 @@ class Preferences extends Any
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -196,7 +196,7 @@ class Project extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -156,7 +156,7 @@ class Session extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -54,7 +54,7 @@ class Tag extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -48,7 +48,7 @@ class Team extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -48,7 +48,7 @@ class Token extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -72,7 +72,7 @@ class User extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -72,7 +72,7 @@ class Webhook extends Model
}
/**
* Get Collection
* Get Type
*
* @return string
*/

View file

@ -25,7 +25,9 @@ class HealthCustomServerTest extends Scope
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('OK', $response['body']['status']);
$this->assertEquals('pass', $response['body']['status']);
$this->assertIsInt($response['body']['ping']);
$this->assertLessThan(100, $response['body']['ping']);
/**
* Test for FAILURE
@ -45,7 +47,9 @@ class HealthCustomServerTest extends Scope
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('OK', $response['body']['status']);
$this->assertEquals('pass', $response['body']['status']);
$this->assertIsInt($response['body']['ping']);
$this->assertLessThan(100, $response['body']['ping']);
/**
* Test for FAILURE
@ -65,7 +69,9 @@ class HealthCustomServerTest extends Scope
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('OK', $response['body']['status']);
$this->assertEquals('pass', $response['body']['status']);
$this->assertIsInt($response['body']['ping']);
$this->assertLessThan(100, $response['body']['ping']);
/**
* Test for FAILURE
@ -85,10 +91,10 @@ class HealthCustomServerTest extends Scope
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['remote']);
$this->assertIsInt($response['body']['local']);
$this->assertNotEmpty($response['body']['remote']);
$this->assertNotEmpty($response['body']['local']);
$this->assertIsInt($response['body']['remoteTime']);
$this->assertIsInt($response['body']['localTime']);
$this->assertNotEmpty($response['body']['remoteTime']);
$this->assertNotEmpty($response['body']['localTime']);
$this->assertLessThan(10, $response['body']['diff']);
/**
@ -193,7 +199,9 @@ class HealthCustomServerTest extends Scope
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('OK', $response['body']['status']);
$this->assertEquals('pass', $response['body']['status']);
$this->assertIsInt($response['body']['ping']);
$this->assertLessThan(100, $response['body']['ping']);
/**
* Test for FAILURE