mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
92
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbae9f859f | ||
|
|
1c857c982e | ||
|
|
40f0936dc7 | ||
|
|
bebe3a217f | ||
|
|
b3222534ed | ||
|
|
3ccb38f9ec | ||
|
|
bb3aa1a172 | ||
|
|
77deacbd40 | ||
|
|
9b97317bdc | ||
|
|
ce03231ca0 | ||
|
|
43759ae273 | ||
|
|
ecd960a6fd | ||
|
|
9da6a6f5ec | ||
|
|
20bc1c4c98 | ||
|
|
f787acecdb | ||
|
|
f2070cac15 | ||
|
|
95841c0418 | ||
|
|
08daa7cf6d | ||
|
|
47352e4062 | ||
|
|
1488bf1d20 | ||
|
|
2aa3b1e296 | ||
|
|
aaeddfe780 | ||
|
|
66fe902152 | ||
|
|
4c3ea3c073 | ||
|
|
b59d6051b6 | ||
|
|
ab2b37fc86 | ||
|
|
777340a626 | ||
|
|
b27a16ccaf | ||
|
|
92e08d3a2d | ||
|
|
d6128282f5 | ||
|
|
63ed3ae19c | ||
|
|
a294bf24fc | ||
|
|
0e9c61ac86 | ||
|
|
2cec0f5469 | ||
|
|
b2f0249146 | ||
|
|
b0807da8ee | ||
|
|
125e388ded | ||
|
|
d9ece4c761 | ||
|
|
d3607471eb | ||
|
|
d1f7bd26c8 | ||
|
|
7296027e43 | ||
|
|
1ce00b139d | ||
|
|
2ebd500d6b | ||
|
|
6b5b3ec865 | ||
|
|
5fa1da3610 | ||
|
|
e5f841e353 | ||
|
|
3a38b492f5 | ||
|
|
5f4f6cd9c5 | ||
|
|
f2a337d86e | ||
|
|
71b4c483c2 | ||
|
|
d9569df1cc | ||
|
|
d1efcd6a61 | ||
|
|
cea7ab8d4c | ||
|
|
cfc178b046 | ||
|
|
50aba6a1c2 | ||
|
|
5fd5c875fc | ||
|
|
1ceaa599b5 | ||
|
|
1138b4fd59 | ||
|
|
fae5c5b92e | ||
|
|
9a446ec0ec | ||
|
|
675ad1d0c6 | ||
|
|
a2463e114e | ||
|
|
9fab5c46f1 | ||
|
|
d730b41b5f | ||
|
|
66228a2462 | ||
|
|
fddac97835 | ||
|
|
9499cbf9f7 | ||
|
|
01f55445f1 | ||
|
|
875662c066 | ||
|
|
30ef106406 | ||
|
|
e4f653be13 | ||
|
|
53df65a3c4 | ||
|
|
2f8b526c01 | ||
|
|
c1cf6acc7a | ||
|
|
dd25899f74 | ||
|
|
fc2f1cef31 | ||
|
|
03aeea857c | ||
|
|
b2c7a3eb33 | ||
|
|
128b9d1b1b | ||
|
|
55a6732444 | ||
|
|
d8ea72b49e | ||
|
|
c32f8e49e2 | ||
|
|
1989ff873e | ||
|
|
f164676b44 | ||
|
|
970c35b31a | ||
|
|
38eb24d0ad | ||
|
|
186b92d5af | ||
|
|
7ad08bfef2 | ||
|
|
6f87757c53 | ||
|
|
4f0dd56afc | ||
|
|
7918ee2f20 | ||
|
|
748abb29cf |
@@ -27,12 +27,14 @@ _APP_DB_PASS=password
|
||||
_APP_DB_ROOT_PASS=rootsecretpassword
|
||||
_APP_CONNECTIONS_MAX=3100
|
||||
_APP_POOL_CLIENTS=14
|
||||
_APP_CONNECTIONS_DB_REPLICAS=db_fra1_03=mariadb://root:rootsecretpassword@mariadb:3306/appwrite
|
||||
_APP_CONNECTIONS_DB_PROJECT=db_fra1_02=mariadb://user:password@mariadb:3306/appwrite
|
||||
_APP_CONNECTIONS_DB_CONSOLE=db_fra1_01=mariadb://user:password@mariadb:3306/appwrite
|
||||
_APP_CONNECTIONS_CACHE=redis_fra1_01=redis://redis:6379
|
||||
_APP_CONNECTIONS_QUEUE=redis_fra1_01=redis://redis:6379
|
||||
_APP_CONNECTIONS_PUBSUB=redis_fra1_01=redis://redis:6379
|
||||
_APP_CONNECTIONS_STORAGE=local://localhost
|
||||
_APP_CONNECTIONS_BACKUPS_STORAGE=dospaces://ACCESS:SECRET@default/backups-v1?region=fra1
|
||||
_APP_STORAGE_ANTIVIRUS=disabled
|
||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav
|
||||
_APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
submodules: recursive
|
||||
ref: feat-db-pools-master
|
||||
ref: db-pools-backups
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
|
||||
@@ -11,3 +11,4 @@ debug/
|
||||
app/sdks
|
||||
dev/yasd_init.php
|
||||
.phpunit.result.cache
|
||||
/backup-data/
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = cloud-cards
|
||||
branch = cloud-1.1.x
|
||||
|
||||
@@ -123,6 +123,8 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/calc-tier-stats && \
|
||||
chmod +x /usr/local/bin/patch-delete-project-collections && \
|
||||
chmod +x /usr/local/bin/maintenance && \
|
||||
chmod +x /usr/local/bin/db-backup && \
|
||||
chmod +x /usr/local/bin/db-restore && \
|
||||
chmod +x /usr/local/bin/volume-sync && \
|
||||
chmod +x /usr/local/bin/usage && \
|
||||
chmod +x /usr/local/bin/install && \
|
||||
@@ -159,6 +161,12 @@ RUN echo "default_socket_timeout=-1" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.jit_buffer_size=100M" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
RUN echo "opcache.jit=1235" >> /usr/local/etc/php/conf.d/appwrite.ini
|
||||
|
||||
RUN \
|
||||
apk update \
|
||||
&& apk add --no-cache \
|
||||
docker \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD [ "php", "app/http.php", "-dopcache.preload=opcache.preload=/usr/src/code/app/preload.php" ]
|
||||
+1
-1
Submodule app/console updated: 104c82a0ab...59d6ebe70e
@@ -95,6 +95,7 @@ App::post('/v1/projects')
|
||||
$backups['database_db_fra1_03'] = ['from' => '10:30', 'to' => '11:15'];
|
||||
$backups['database_db_fra1_04'] = ['from' => '13:30', 'to' => '14:15'];
|
||||
$backups['database_db_fra1_05'] = ['from' => '4:30', 'to' => '5:15'];
|
||||
$backups['database_db_fra1_06'] = ['from' => '16:30', 'to' => '17:15'];
|
||||
|
||||
$databases = Config::getParam('pools-database', []);
|
||||
|
||||
@@ -118,7 +119,7 @@ App::post('/v1/projects')
|
||||
}
|
||||
}
|
||||
|
||||
if ($index = array_search('database_db_fra1_05', $databases)) {
|
||||
if ($index = array_search('database_db_fra1_06', $databases)) {
|
||||
$database = $databases[$index];
|
||||
} else {
|
||||
$database = $databases[array_rand($databases)];
|
||||
|
||||
@@ -532,6 +532,12 @@ $register->set('pools', function () {
|
||||
'multiple' => true,
|
||||
'schemes' => ['mariadb', 'mysql'],
|
||||
],
|
||||
'replica' => [
|
||||
'type' => 'database',
|
||||
'dsns' => App::getEnv('_APP_CONNECTIONS_DB_REPLICAS', $fallbackForDB),
|
||||
'multiple' => true,
|
||||
'schemes' => ['mariadb', 'mysql'],
|
||||
],
|
||||
'queue' => [
|
||||
'type' => 'queue',
|
||||
'dsns' => App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis),
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php backup $@
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php restore $@
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php upgrade $@
|
||||
+1
-1
@@ -63,7 +63,7 @@
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/queue": "0.5.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.13.*",
|
||||
"utopia-php/storage": "dev-feat-transfer as 0.13.3",
|
||||
"utopia-php/swoole": "0.5.*",
|
||||
"utopia-php/websocket": "0.1.0",
|
||||
"resque/php-resque": "1.3.6",
|
||||
|
||||
Generated
+69
-59
@@ -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": "329498d12fabaa764c4178881cb07788",
|
||||
"content-hash": "ff72579b1906a3ee181ec1209198c13f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -534,16 +534,16 @@
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
|
||||
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
|
||||
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d",
|
||||
"reference": "111166291a0f8130081195ac4556a5587d7f1b5d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -597,7 +597,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/2.0.0"
|
||||
"source": "https://github.com/guzzle/promises/tree/2.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -613,20 +613,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-21T13:50:22+00:00"
|
||||
"time": "2023-08-03T15:11:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "b635f279edd83fc275f822a1188157ffea568ff6"
|
||||
"reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
|
||||
"reference": "b635f279edd83fc275f822a1188157ffea568ff6",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/8bd7c33a0734ae1c5d074360512beb716bef3f77",
|
||||
"reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -713,7 +713,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.5.0"
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.6.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -729,7 +729,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-17T16:11:26+00:00"
|
||||
"time": "2023-08-03T15:06:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "influxdb/influxdb-php",
|
||||
@@ -2622,26 +2622,32 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/storage",
|
||||
"version": "0.13.2",
|
||||
"version": "dev-feat-transfer",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage.git",
|
||||
"reference": "ad1c00f24ca56e73888acc2af3deee4919b1194b"
|
||||
"reference": "0c510a85509719a45cc6e7f9861d3bc1b4c0f1e0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/ad1c00f24ca56e73888acc2af3deee4919b1194b",
|
||||
"reference": "ad1c00f24ca56e73888acc2af3deee4919b1194b",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/0c510a85509719a45cc6e7f9861d3bc1b4c0f1e0",
|
||||
"reference": "0c510a85509719a45cc6e7f9861d3bc1b4c0f1e0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-brotli": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-lz4": "*",
|
||||
"ext-snappy": "*",
|
||||
"ext-xz": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-zstd": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/framework": "0.*.*"
|
||||
"utopia-php/framework": "0.*.*",
|
||||
"utopia-php/system": "0.*.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
},
|
||||
@@ -2655,12 +2661,6 @@
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Eldad Fux",
|
||||
"email": "eldad@appwrite.io"
|
||||
}
|
||||
],
|
||||
"description": "A simple Storage library to manage application storage",
|
||||
"keywords": [
|
||||
"framework",
|
||||
@@ -2671,9 +2671,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/storage/issues",
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.13.2"
|
||||
"source": "https://github.com/utopia-php/storage/tree/feat-transfer"
|
||||
},
|
||||
"time": "2022-12-20T11:11:35+00:00"
|
||||
"time": "2023-08-12T10:18:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
@@ -3530,16 +3530,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.7.2",
|
||||
"version": "1.7.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d"
|
||||
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d",
|
||||
"reference": "b2fe4d22a5426f38e014855322200b97b5362c0d",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
|
||||
"reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3582,9 +3582,9 @@
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2"
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3"
|
||||
},
|
||||
"time": "2023-05-30T18:13:47+00:00"
|
||||
"time": "2023-08-12T11:01:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
@@ -3656,16 +3656,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "1.22.1",
|
||||
"version": "1.23.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0"
|
||||
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0",
|
||||
"reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26",
|
||||
"reference": "846ae76eef31c6d7790fac9bc399ecee45160b26",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3697,22 +3697,22 @@
|
||||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1"
|
||||
},
|
||||
"time": "2023-06-29T20:46:06+00:00"
|
||||
"time": "2023-08-03T16:32:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.26",
|
||||
"version": "9.2.27",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
|
||||
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1",
|
||||
"reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3768,7 +3768,8 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3776,7 +3777,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-03-06T12:58:08+00:00"
|
||||
"time": "2023-07-26T13:44:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@@ -4628,16 +4629,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
"version": "5.0.5",
|
||||
"version": "5.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/global-state.git",
|
||||
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
|
||||
"reference": "bde739e7565280bda77be70044ac1047bc007e34"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
|
||||
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
|
||||
"reference": "bde739e7565280bda77be70044ac1047bc007e34",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4680,7 +4681,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/global-state/issues",
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4688,7 +4689,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-14T08:28:10+00:00"
|
||||
"time": "2023-08-02T09:26:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
@@ -5451,16 +5452,16 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.6.1",
|
||||
"version": "v3.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd"
|
||||
"reference": "5cf942bbab3df42afa918caeba947f1b690af64b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
|
||||
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/5cf942bbab3df42afa918caeba947f1b690af64b",
|
||||
"reference": "5cf942bbab3df42afa918caeba947f1b690af64b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5506,7 +5507,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.6.1"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5518,12 +5519,21 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-06-08T12:52:13+00:00"
|
||||
"time": "2023-07-26T07:16:09+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
{
|
||||
"package": "utopia-php/storage",
|
||||
"version": "dev-feat-transfer",
|
||||
"alias": "0.13.3",
|
||||
"alias_normalized": "0.13.3.0"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {
|
||||
"utopia-php/storage": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@@ -5547,5 +5557,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
||||
+53
-1
@@ -126,6 +126,7 @@ services:
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_CONNECTIONS_DB_PROJECT
|
||||
- _APP_CONNECTIONS_DB_REPLICAS
|
||||
- _APP_CONNECTIONS_DB_CONSOLE
|
||||
- _APP_CONNECTIONS_CACHE
|
||||
- _APP_CONNECTIONS_QUEUE
|
||||
@@ -721,7 +722,7 @@ services:
|
||||
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
|
||||
|
||||
mariadb:
|
||||
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
image: mysql:8.0.33 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||
container_name: appwrite-mariadb
|
||||
<<: *x-logging
|
||||
networks:
|
||||
@@ -748,6 +749,56 @@ services:
|
||||
# - SMARTHOST_HOST=smtp
|
||||
# - SMARTHOST_PORT=587
|
||||
|
||||
xtrabackup:
|
||||
image: percona/percona-xtrabackup:8.0.33
|
||||
container_name: xtrabackup
|
||||
command: sleep infinity
|
||||
volumes:
|
||||
- backup-data:/backups:rw
|
||||
- appwrite-mariadb:/var/lib/mysql:r
|
||||
networks:
|
||||
- appwrite
|
||||
user: 'root:root'
|
||||
|
||||
appwrite-backup:
|
||||
entrypoint: db-backup
|
||||
command:
|
||||
- --database=db_fra1_03
|
||||
<<: *x-logging
|
||||
container_name: appwrite-backup
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
- backup-data:/backups:rw
|
||||
|
||||
depends_on:
|
||||
- redis
|
||||
environment:
|
||||
- _APP_CONNECTIONS_DB_REPLICAS
|
||||
- _APP_CONNECTIONS_BACKUPS_STORAGE
|
||||
|
||||
appwrite-restore:
|
||||
command: sleep infinity
|
||||
container_name: appwrite-backup-restore
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
- backup-data:/backups:rw
|
||||
- appwrite-mariadb:/var/lib/mysql:rw
|
||||
depends_on:
|
||||
- redis
|
||||
environment:
|
||||
- _APP_CONNECTIONS_DB_REPLICAS
|
||||
- _APP_CONNECTIONS_BACKUPS_STORAGE
|
||||
|
||||
redis:
|
||||
image: redis:7.0.4-alpine
|
||||
<<: *x-logging
|
||||
@@ -910,4 +961,5 @@ volumes:
|
||||
appwrite-config:
|
||||
appwrite-functions:
|
||||
appwrite-builds:
|
||||
backup-data:
|
||||
# appwrite-chronograf:
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Appwrite\Platform\Services;
|
||||
|
||||
use Appwrite\Platform\Tasks\Backup;
|
||||
use Appwrite\Platform\Tasks\Restore;
|
||||
use Utopia\Platform\Service;
|
||||
use Appwrite\Platform\Tasks\Doctor;
|
||||
use Appwrite\Platform\Tasks\Install;
|
||||
@@ -22,6 +24,7 @@ use Appwrite\Platform\Tasks\VolumeSync;
|
||||
use Appwrite\Platform\Tasks\CalcUsersStats;
|
||||
use Appwrite\Platform\Tasks\CalcTierStats;
|
||||
use Appwrite\Platform\Tasks\PatchDeleteProjectCollections;
|
||||
use Appwrite\Platform\Tasks\Upgrade;
|
||||
|
||||
class Tasks extends Service
|
||||
{
|
||||
@@ -36,6 +39,7 @@ class Tasks extends Service
|
||||
->addAction(Hamster::getName(), new Hamster())
|
||||
->addAction(Doctor::getName(), new Doctor())
|
||||
->addAction(Install::getName(), new Install())
|
||||
->addAction(Upgrade::getName(), new Upgrade())
|
||||
->addAction(Maintenance::getName(), new Maintenance())
|
||||
->addAction(PatchCreateMissingSchedules::getName(), new PatchCreateMissingSchedules())
|
||||
->addAction(ClearCardCache::getName(), new ClearCardCache())
|
||||
@@ -48,6 +52,8 @@ class Tasks extends Service
|
||||
->addAction(CalcUsersStats::getName(), new CalcUsersStats())
|
||||
->addAction(CalcTierStats::getName(), new CalcTierStats())
|
||||
->addAction(PatchDeleteProjectCollections::getName(), new PatchDeleteProjectCollections())
|
||||
->addAction(Backup::getName(), new Backup())
|
||||
->addAction(Restore::getName(), new Restore())
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Exception;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Backup extends Action
|
||||
{
|
||||
public const BACKUPS_PATH = '/backups';
|
||||
public const BACKUP_INTERVAL_SECONDS = 60 * 60 * 1; // 4 hours;
|
||||
public const COMPRESS_ALGORITHM = 'lz4';
|
||||
public const CONFIG_PATH = '/etc/my.cnf';
|
||||
protected ?DSN $dsn = null;
|
||||
protected ?string $database = null;
|
||||
protected ?DOSpaces $s3 = null;
|
||||
protected string $xtrabackupContainerId;
|
||||
protected int $processors = 1;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->checkEnvVariables();
|
||||
|
||||
$this
|
||||
->desc('Backup a database')
|
||||
->param('database', null, new Text(20), 'Database name, for example db_fra1_01')
|
||||
->inject('pools')
|
||||
->callback(fn(string $database, Group $pools) => $this->action($database, $pools));
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'backup';
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function action(string $database, Group $pools): void
|
||||
{
|
||||
$this->database = $database;
|
||||
$this->dsn = $this->getDsn($database);
|
||||
if (is_null($this->dsn)) {
|
||||
Console::error('No DSN match');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
console::info('Trying to connect to ' . $this->dsn->getHost() . ':' . $this->dsn->getPort() .
|
||||
'user:' . $this->dsn->getUser() . 'password:' . $this->dsn->getPassword());
|
||||
|
||||
try {
|
||||
$dsn = new DSN(App::getEnv('_APP_CONNECTIONS_BACKUPS_STORAGE', ''));
|
||||
$this->s3 = new DOSpaces('/' . $database . '/full', $dsn->getUser(), $dsn->getPassword(), $dsn->getPath(), $dsn->getParam('region'));
|
||||
} catch (\Exception $e) {
|
||||
Console::error($e->getMessage());
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 5;
|
||||
|
||||
do {
|
||||
try {
|
||||
$attempts++;
|
||||
$pools
|
||||
->get('replica_' . $database)
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
break; // leave the do-while if successful
|
||||
} catch (Exception $e) {
|
||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new Exception('Failed to connect to database: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
|
||||
$this->setProcessors();
|
||||
$this->setContainerId();
|
||||
|
||||
sleep(20);
|
||||
|
||||
Console::loop(function () {
|
||||
$this->start();
|
||||
}, self::BACKUP_INTERVAL_SECONDS);
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$start = microtime(true);
|
||||
$time = date('Y_m_d_H_i_s');
|
||||
|
||||
self::log('--- Backup Start ' . $time . ' --- ');
|
||||
|
||||
$filename = $time . '.tar.gz';
|
||||
$local = new Local(self::BACKUPS_PATH . '/' . $this->database . '/full/' . $time);
|
||||
$local->setTransferChunkSize(5 * 1024 * 1024); // 5MB
|
||||
|
||||
$backups = $local->getRoot() . '/files';
|
||||
$tarFile = $local->getPath($filename);
|
||||
|
||||
$this->fullBackup($backups);
|
||||
$this->tar($backups, $tarFile);
|
||||
$this->upload($tarFile, $local);
|
||||
|
||||
self::log('--- Backup Finish ' . (microtime(true) - $start) . ' seconds --- ' . PHP_EOL . PHP_EOL);
|
||||
}
|
||||
|
||||
public function fullBackup(string $target)
|
||||
{
|
||||
if (!file_exists(self::BACKUPS_PATH)) {
|
||||
Console::error('Mount directory does not exist');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!file_exists($target) && !mkdir($target, 0755, true)) {
|
||||
Console::error('Error creating directory: ' . $target);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$logfile = $target . '/../backup.log';
|
||||
|
||||
$args = [
|
||||
'xtrabackup',
|
||||
'--user=' . $this->dsn->getUser(),
|
||||
'--password=' . $this->dsn->getPassword(),
|
||||
'--host=' . $this->dsn->getHost(),
|
||||
'--port=' . $this->dsn->getPort(),
|
||||
'--backup',
|
||||
'--strict',
|
||||
'--history=' . $this->database, // logs PERCONA_SCHEMA.xtrabackup_history name attribute
|
||||
'--slave-info',
|
||||
'--safe-slave-backup',
|
||||
'--safe-slave-backup-timeout=300',
|
||||
'--check-privileges', // checks if Percona XtraBackup has all the required privileges.
|
||||
'--target-dir=' . $target,
|
||||
'--parallel=' . $this->processors,
|
||||
'--compress=' . self::COMPRESS_ALGORITHM,
|
||||
'--compress-threads=' . $this->processors,
|
||||
'--rsync', // https://docs.percona.com/percona-xtrabackup/8.0/accelerate-backup-process.html
|
||||
'2> ' . $logfile,
|
||||
];
|
||||
|
||||
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
|
||||
self::log('Xtrabackup start');
|
||||
shell_exec($cmd);
|
||||
|
||||
$stderr = shell_exec('tail -1 ' . $logfile);
|
||||
|
||||
if (!str_contains($stderr, 'completed OK!') || !file_exists($target . '/xtrabackup_checkpoints')) {
|
||||
Console::error(date('Y-m-d H:i:s') . ' Backup failed:' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!unlink($logfile)) {
|
||||
Console::error('Error deleting: ' . $logfile);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public function tar(string $directory, string $file)
|
||||
{
|
||||
self::log('Tar start');
|
||||
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
$cmd = 'cd ' . $directory . ' && tar zcf ' . $file . ' . && cd ' . getcwd();
|
||||
Console::execute($cmd, '', $stdout, $stderr);
|
||||
if (!empty($stderr)) {
|
||||
Console::error(date('Y-m-d H:i:s') . ' Tar failed:' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!file_exists($file)) {
|
||||
Console::error('Can\'t find tar file: ' . $file);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$filesize = \filesize($file);
|
||||
if ($filesize < (2 * 1024 * 1024)) {
|
||||
Console::error('Tar file size is very small: ' . $file);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public function upload(string $file, Device $local)
|
||||
{
|
||||
self::log('Upload start');
|
||||
$filename = basename($file);
|
||||
|
||||
if (!$this->s3->exists('/')) {
|
||||
Console::error('Can\'t read s3 root directory');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$destination = $this->s3->getRoot() . '/' . $filename;
|
||||
|
||||
if (!$local->transfer($file, $destination, $this->s3)) {
|
||||
Console::error('Error uploading to ' . $destination);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!$this->s3->exists($destination)) {
|
||||
Console::error('File not found in destination: ' . $destination);
|
||||
Console::exit();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Console::error($e->getMessage());
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!unlink($file)) {
|
||||
Console::error('Error deleting: ' . $file);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public static function log(string $message): void
|
||||
{
|
||||
if (!empty($message)) {
|
||||
Console::log(date('Y-m-d H:i:s') . ' ' . $message);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkEnvVariables(): void
|
||||
{
|
||||
foreach (
|
||||
[
|
||||
'_APP_CONNECTIONS_BACKUPS_STORAGE',
|
||||
'_APP_CONNECTIONS_DB_REPLICAS',
|
||||
] as $env
|
||||
) {
|
||||
if (empty(App::getEnv($env))) {
|
||||
Console::error('Can\'t read ' . $env);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDsn(string $database): ?DSN
|
||||
{
|
||||
foreach (explode(',', App::getEnv('_APP_CONNECTIONS_DB_REPLICAS', '')) as $project) {
|
||||
[$db, $dsn] = explode('=', $project);
|
||||
if ($db === $database) {
|
||||
return new DSN($dsn);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setProcessors()
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('nproc', '', $stdout, $stderr);
|
||||
if (!empty($stderr)) {
|
||||
Console::error('Error setting processors: ' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$processors = str_replace(PHP_EOL, '', $stdout);
|
||||
$processors = intval($processors);
|
||||
$processors = $processors === 0 ? 1 : $processors;
|
||||
$this->processors = $processors;
|
||||
}
|
||||
|
||||
public function setContainerId()
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('docker ps -f "name=xtrabackup" --format "{{.ID}}"', '', $stdout, $stderr);
|
||||
if (!empty($stderr)) {
|
||||
Console::error('Error setting container Id: ' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$containerId = str_replace(PHP_EOL, '', $stdout);
|
||||
|
||||
if (empty($containerId)) {
|
||||
Console::error('Xtrabackup Container ID not found');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$this->xtrabackupContainerId = $containerId;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,8 @@ use Appwrite\Auth\Auth;
|
||||
use Appwrite\Docker\Compose;
|
||||
use Appwrite\Docker\Env;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\Analytics\GoogleAnalytics;
|
||||
use Utopia\Analytics\Adapter\GoogleAnalytics;
|
||||
use Utopia\Analytics\Event;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Validator\Text;
|
||||
@@ -14,6 +15,8 @@ use Utopia\Platform\Action;
|
||||
|
||||
class Install extends Action
|
||||
{
|
||||
protected string $path = '/usr/src/code/appwrite';
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'install';
|
||||
@@ -50,7 +53,6 @@ class Install extends Action
|
||||
* 6. Run data migration
|
||||
*/
|
||||
$config = Config::getParam('variables');
|
||||
$path = '/usr/src/code/appwrite';
|
||||
$defaultHTTPPort = '80';
|
||||
$defaultHTTPSPort = '443';
|
||||
$vars = [];
|
||||
@@ -70,19 +72,28 @@ class Install extends Action
|
||||
Console::success('Starting Appwrite installation...');
|
||||
|
||||
// Create directory with write permissions
|
||||
if (null !== $path && !\file_exists(\dirname($path))) {
|
||||
if (!@\mkdir(\dirname($path), 0755, true)) {
|
||||
Console::error('Can\'t create directory ' . \dirname($path));
|
||||
if (null !== $this->path && !\file_exists(\dirname($this->path))) {
|
||||
if (!@\mkdir(\dirname($this->path), 0755, true)) {
|
||||
Console::error('Can\'t create directory ' . \dirname($this->path));
|
||||
Console::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$data = @file_get_contents($path . '/docker-compose.yml');
|
||||
$data = @file_get_contents($this->path . '/docker-compose.yml');
|
||||
|
||||
if ($data !== false) {
|
||||
if ($interactive == 'Y' && Console::isInteractive()) {
|
||||
$answer = Console::confirm('Previous installation found, do you want to overwrite it? (Y/n)');
|
||||
|
||||
if ($answer !== 'Y') {
|
||||
Console::info('No action taken.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$time = \time();
|
||||
Console::info('Compose file found, creating backup: docker-compose.yml.' . $time . '.backup');
|
||||
file_put_contents($path . '/docker-compose.yml.' . $time . '.backup', $data);
|
||||
file_put_contents($this->path . '/docker-compose.yml.' . $time . '.backup', $data);
|
||||
$compose = new Compose($data);
|
||||
$appwrite = $compose->getService('appwrite');
|
||||
$oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null;
|
||||
@@ -116,11 +127,11 @@ class Install extends Action
|
||||
}
|
||||
}
|
||||
|
||||
$data = @file_get_contents($path . '/.env');
|
||||
$data = @file_get_contents($this->path . '/.env');
|
||||
|
||||
if ($data !== false) { // Fetch all env vars from previous .env file
|
||||
Console::info('Env file found, creating backup: .env.' . $time . '.backup');
|
||||
file_put_contents($path . '/.env.' . $time . '.backup', $data);
|
||||
file_put_contents($this->path . '/.env.' . $time . '.backup', $data);
|
||||
$env = new Env($data);
|
||||
|
||||
foreach ($env->list() as $key => $value) {
|
||||
@@ -198,8 +209,8 @@ class Install extends Action
|
||||
}
|
||||
}
|
||||
|
||||
$templateForCompose = new View(__DIR__ . '/../views/install/compose.phtml');
|
||||
$templateForEnv = new View(__DIR__ . '/../views/install/env.phtml');
|
||||
$templateForCompose = new View(__DIR__ . '/../../../../app/views/install/compose.phtml');
|
||||
$templateForEnv = new View(__DIR__ . '/../../../../app/views/install/env.phtml');
|
||||
|
||||
$templateForCompose
|
||||
->setParam('httpPort', $httpPort)
|
||||
@@ -213,16 +224,24 @@ class Install extends Action
|
||||
->setParam('vars', $input)
|
||||
;
|
||||
|
||||
if (!file_put_contents($path . '/docker-compose.yml', $templateForCompose->render(false))) {
|
||||
if (!file_put_contents($this->path . '/docker-compose.yml', $templateForCompose->render(false))) {
|
||||
$message = 'Failed to save Docker Compose file';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
$event = new Event();
|
||||
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
|
||||
->addProp('category', 'install/server')
|
||||
->addProp('action', 'install');
|
||||
$analytics->createEvent($event);
|
||||
Console::error($message);
|
||||
Console::exit(1);
|
||||
}
|
||||
|
||||
if (!file_put_contents($path . '/.env', $templateForEnv->render(false))) {
|
||||
if (!file_put_contents($this->path . '/.env', $templateForEnv->render(false))) {
|
||||
$message = 'Failed to save environment variables file';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
$event = new Event();
|
||||
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
|
||||
->addProp('category', 'install/server')
|
||||
->addProp('action', 'install');
|
||||
$analytics->createEvent($event);
|
||||
Console::error($message);
|
||||
Console::exit(1);
|
||||
}
|
||||
@@ -237,19 +256,27 @@ class Install extends Action
|
||||
}
|
||||
}
|
||||
|
||||
Console::log("Running \"docker compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\"");
|
||||
Console::log("Running \"docker compose -f {$this->path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\"");
|
||||
|
||||
$exit = Console::execute("${env} docker compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
|
||||
$exit = Console::execute("${env} docker compose -f {$this->path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
$message = 'Failed to install Appwrite dockers';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
$event = new Event();
|
||||
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
|
||||
->addProp('category', 'install/server')
|
||||
->addProp('action', 'install');
|
||||
$analytics->createEvent($event);
|
||||
Console::error($message);
|
||||
Console::error($stderr);
|
||||
Console::exit($exit);
|
||||
} else {
|
||||
$message = 'Appwrite installed successfully';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
$event = new Event();
|
||||
$event->setName(APP_VERSION_STABLE . ' - ' . $message)
|
||||
->addProp('category', 'install/server')
|
||||
->addProp('action', 'install');
|
||||
$analytics->createEvent($event);
|
||||
Console::success($message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Exception;
|
||||
use Utopia\App;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Restore extends Action
|
||||
{
|
||||
public const BACKUPS_PATH = '/backups';
|
||||
public const DATADIR = '/var/lib/mysql';
|
||||
protected ?DSN $dsn = null;
|
||||
protected string $database;
|
||||
protected ?DOSpaces $s3 = null;
|
||||
protected string $xtrabackupContainerId;
|
||||
protected int $processors = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->checkEnvVariables();
|
||||
$this->setProcessors();
|
||||
$this->setContainerId();
|
||||
|
||||
$this
|
||||
->desc('Restore a DB')
|
||||
->param('id', '', new Text(20), 'The backup identification')
|
||||
->param('cloud', null, new Boolean(true), 'Download backup from cloud or use local directory')
|
||||
->param('database', null, new Text(10), 'The Database name for example db_fra1_01')
|
||||
->callback(fn ($id, $cloud, $project) => $this->action($id, $cloud, $project));
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'restore';
|
||||
}
|
||||
|
||||
public function action(string $id, string $cloud, string $database): void
|
||||
{
|
||||
$this->database = $database;
|
||||
$datadir = self::DATADIR;
|
||||
$this->dsn = $this->getDsn($database);
|
||||
if (is_null($this->dsn)) {
|
||||
Console::error('No DSN match');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = new DSN(App::getEnv('_APP_CONNECTIONS_BACKUPS_STORAGE', ''));
|
||||
$this->s3 = new DOSpaces('/' . $database . '/full', $dsn->getUser(), $dsn->getPassword(), $dsn->getPath(), $dsn->getParam('region'));
|
||||
} catch (\Exception $e) {
|
||||
Console::error($e->getMessage() . 'Invalid DSN.');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!file_exists($datadir)) {
|
||||
Console::error('Datadir not found: ' . $datadir);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (file_exists($datadir . '/sys') || file_exists($datadir . '/appwrite')) {
|
||||
Console::error('Datadir ' . $datadir . ' must be empty!');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$this->log('--- Restore Start ' . $id . ' --- ');
|
||||
|
||||
$filename = $id . '.tar.gz';
|
||||
$start = microtime(true);
|
||||
$cloud = $cloud === 'true' || $cloud === '1';
|
||||
|
||||
if ($cloud) {
|
||||
$local = new Local(self::BACKUPS_PATH . '/downloads/' . $id);
|
||||
|
||||
$files = $local->getRoot() . '/files';
|
||||
|
||||
if (!file_exists($files) && !mkdir($files, 0755, true)) {
|
||||
Console::error('Error creating directory: ' . $files);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$file = $local->getPath($filename);
|
||||
|
||||
if (!file_exists($file)) {
|
||||
$this->download($file, $local);
|
||||
}
|
||||
|
||||
$this->untar($file, $files);
|
||||
} else {
|
||||
$local = new Local(self::BACKUPS_PATH . '/' . $database . '/full/' . $id);
|
||||
$files = $local->getRoot() . '/files';
|
||||
}
|
||||
|
||||
if (!file_exists($files)) {
|
||||
Console::error('Directory not found: ' . $files);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$this->decompress($files);
|
||||
$this->prepare($files);
|
||||
$this->restore($files, $cloud, $datadir);
|
||||
|
||||
$this->log('Restore Finish in ' . (microtime(true) - $start) . ' seconds');
|
||||
}
|
||||
|
||||
public function download(string $file, Device $local)
|
||||
{
|
||||
$this->log('Download start');
|
||||
|
||||
$filename = basename($file);
|
||||
try {
|
||||
$path = $this->s3->getPath($filename);
|
||||
|
||||
if (!$this->s3->exists($path)) {
|
||||
Console::error('File: ' . $path . ' does not exist on cloud');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!$this->s3->transfer($path, $file, $local)) {
|
||||
Console::error('Error Downloading ' . $file);
|
||||
Console::exit();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Console::error($e->getMessage());
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public function untar(string $file, string $directory)
|
||||
{
|
||||
$this->log('Untar Start');
|
||||
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
$cmd = 'tar -xzf ' . $file . ' -C ' . $directory;
|
||||
Console::execute($cmd, '', $stdout, $stderr);
|
||||
if (!empty($stderr)) {
|
||||
Console::error($stderr);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
if (!file_exists($file)) {
|
||||
Console::error('Restore file not found: ' . $file);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public function decompress(string $target)
|
||||
{
|
||||
$this->log('Decompress start');
|
||||
|
||||
$logfile = $target . '/../log.txt';
|
||||
|
||||
$args = [
|
||||
'xtrabackup',
|
||||
'--user=' . $this->dsn->getUser(),
|
||||
'--password=' . $this->dsn->getPassword(),
|
||||
'--host=' . $this->dsn->getHost(),
|
||||
'--decompress',
|
||||
'--strict',
|
||||
'--remove-original', // Removes *.lz4 compressed files
|
||||
'--parallel=' . $this->processors,
|
||||
'--compress-threads=' . $this->processors,
|
||||
'--target-dir=' . $target,
|
||||
'2> ' . $logfile,
|
||||
];
|
||||
|
||||
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
|
||||
shell_exec($cmd);
|
||||
|
||||
$stderr = shell_exec('tail -1 ' . $logfile);
|
||||
|
||||
if (!str_contains($stderr, 'completed OK!')) {
|
||||
Console::error('Decompress failed');
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public function prepare(string $target)
|
||||
{
|
||||
$this->log('Prepare start');
|
||||
|
||||
if (!file_exists($target)) {
|
||||
Console::error('prepare error directory not found: ' . $target);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$logfile = $target . '/../log.txt';
|
||||
|
||||
$args = [
|
||||
'xtrabackup',
|
||||
'--user=' . $this->dsn->getUser(),
|
||||
'--password=' . $this->dsn->getPassword(),
|
||||
'--host=' . $this->dsn->getHost(),
|
||||
'--prepare',
|
||||
'--strict',
|
||||
'--target-dir=' . $target,
|
||||
'2> ' . $logfile,
|
||||
];
|
||||
|
||||
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
|
||||
shell_exec($cmd);
|
||||
|
||||
$stderr = shell_exec('tail -1 ' . $logfile);
|
||||
|
||||
if (!str_contains($stderr, 'completed OK!')) {
|
||||
Console::error(date('Y-m-d H:i:s') . ' Prepare failed:' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public function restore(string $target, bool $cloud, string $datadir)
|
||||
{
|
||||
$this->log('Restore start');
|
||||
|
||||
if (!file_exists($target)) {
|
||||
Console::error('restore error directory not found: ' . $target);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$logfile = $target . '/../log.txt';
|
||||
|
||||
$args = [
|
||||
'xtrabackup',
|
||||
'--user=' . $this->dsn->getUser(),
|
||||
'--password=' . $this->dsn->getPassword(),
|
||||
'--host=' . $this->dsn->getHost(),
|
||||
$cloud ? '--move-back' : '--copy-back',
|
||||
'--strict',
|
||||
'--target-dir=' . $target,
|
||||
'--datadir=' . $datadir,
|
||||
'--parallel=' . $this->processors,
|
||||
'2> ' . $logfile,
|
||||
];
|
||||
|
||||
$cmd = 'docker exec ' . $this->xtrabackupContainerId . ' ' . implode(' ', $args);
|
||||
shell_exec($cmd);
|
||||
|
||||
$stderr = shell_exec('tail -1 ' . $logfile);
|
||||
|
||||
if (!str_contains($stderr, 'completed OK!')) {
|
||||
Console::error(date('Y-m-d H:i:s') . ' Restore failed:' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
|
||||
public function checkEnvVariables(): void
|
||||
{
|
||||
foreach (
|
||||
[
|
||||
'_APP_CONNECTIONS_BACKUPS_STORAGE',
|
||||
'_APP_CONNECTIONS_DB_REPLICAS',
|
||||
] as $env
|
||||
) {
|
||||
if (empty(App::getEnv($env))) {
|
||||
Console::error('Can\'t read ' . $env);
|
||||
Console::exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function log(string $message): void
|
||||
{
|
||||
if (!empty($message)) {
|
||||
Console::log(date('Y-m-d H:i:s') . ' ' . $message);
|
||||
}
|
||||
}
|
||||
|
||||
public function getDsn(string $database): ?DSN
|
||||
{
|
||||
foreach (explode(',', App::getEnv('_APP_CONNECTIONS_DB_REPLICAS', '')) as $project) {
|
||||
[$db, $dsn] = explode('=', $project);
|
||||
if ($db === $database) {
|
||||
return new DSN($dsn);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setProcessors()
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('nproc', '', $stdout, $stderr);
|
||||
if (!empty($stderr)) {
|
||||
Console::error('Error setting processors: ' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$processors = str_replace(PHP_EOL, '', $stdout);
|
||||
$processors = intval($processors);
|
||||
$processors = $processors === 0 ? 1 : $processors;
|
||||
$this->processors = $processors;
|
||||
}
|
||||
|
||||
public function setContainerId()
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('docker ps -f "name=xtrabackup" --format "{{.ID}}"', '', $stdout, $stderr);
|
||||
if (!empty($stderr)) {
|
||||
Console::error('Error setting container Id: ' . $stderr);
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$containerId = str_replace(PHP_EOL, '', $stdout);
|
||||
if (empty($containerId)) {
|
||||
Console::error('Xtrabackup Container ID not found');
|
||||
Console::exit();
|
||||
}
|
||||
|
||||
$this->xtrabackupContainerId = $containerId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Tasks;
|
||||
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Upgrade extends Install
|
||||
{
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'upgrade';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Upgrade Appwrite')
|
||||
->param('httpPort', '', new Text(4), 'Server HTTP port', true)
|
||||
->param('httpsPort', '', new Text(4), 'Server HTTPS port', true)
|
||||
->param('organization', 'appwrite', new Text(0), 'Docker Registry organization', true)
|
||||
->param('image', 'appwrite', new Text(0), 'Main appwrite docker image', true)
|
||||
->param('interactive', 'Y', new Text(1), 'Run an interactive session', true)
|
||||
->callback(fn ($httpPort, $httpsPort, $organization, $image, $interactive) => $this->action($httpPort, $httpsPort, $organization, $image, $interactive));
|
||||
}
|
||||
|
||||
public function action(string $httpPort, string $httpsPort, string $organization, string $image, string $interactive): void
|
||||
{
|
||||
// Check for previous installation
|
||||
$data = @file_get_contents($this->path . '/docker-compose.yml');
|
||||
if (empty($data)) {
|
||||
Console::error('Appwrite installation not found.');
|
||||
Console::log('The command was not run in the parent folder of your appwrite installation.');
|
||||
Console::log('Please navigate to the parent directory of the Appwrite installation and try again.');
|
||||
Console::log(' parent_directory <= you run the command in this directory');
|
||||
Console::log(' └── appwrite');
|
||||
Console::log(' └── docker-compose.yml');
|
||||
Console::exit(1);
|
||||
}
|
||||
parent::action($httpPort, $httpsPort, $organization, $image, $interactive);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user