appwrite/src/Appwrite/Platform/Tasks/Install.php

241 lines
9.2 KiB
PHP
Raw Normal View History

2022-07-13 07:02:55 +00:00
<?php
2022-07-14 02:04:31 +00:00
2022-11-14 10:01:41 +00:00
namespace Appwrite\Platform\Tasks;
2022-07-13 07:02:55 +00:00
use Appwrite\Auth\Auth;
use Appwrite\Docker\Compose;
use Appwrite\Docker\Env;
use Appwrite\Utopia\View;
use Utopia\CLI\Console;
use Utopia\Config\Config;
2024-10-01 14:30:47 +00:00
use Utopia\Http\Validator\Boolean;
use Utopia\Http\Validator\Text;
2024-03-08 12:57:20 +00:00
use Utopia\Platform\Action;
2022-07-13 07:02:55 +00:00
2022-07-14 02:04:31 +00:00
class Install extends Action
{
protected string $path = '/usr/src/code/appwrite';
2022-08-02 01:58:36 +00:00
public static function getName(): string
{
return 'install';
}
2022-07-14 02:04:31 +00:00
2022-07-13 07:02:55 +00:00
public function __construct()
{
$this
->desc('Install Appwrite')
->param('http-port', '', new Text(4), 'Server HTTP port', true)
->param('https-port', '', new Text(4), 'Server HTTPS port', true)
2022-07-13 07:02:55 +00:00
->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)
->param('no-start', false, new Boolean(true), 'Run an interactive session', true)
->callback(fn ($httpPort, $httpsPort, $organization, $image, $interactive, $noStart) => $this->action($httpPort, $httpsPort, $organization, $image, $interactive, $noStart));
2022-07-13 07:02:55 +00:00
}
public function action(string $httpPort, string $httpsPort, string $organization, string $image, string $interactive, bool $noStart): void
2022-07-13 07:02:55 +00:00
{
$config = Config::getParam('variables');
$defaultHTTPPort = '80';
$defaultHTTPSPort = '443';
/** @var array<string, array<string, string>> $vars array whre key is variable name and value is variable */
2022-07-13 07:02:55 +00:00
$vars = [];
foreach ($config as $category) {
foreach ($category['variables'] ?? [] as $var) {
$vars[$var['name']] = $var;
2022-07-13 07:02:55 +00:00
}
}
Console::success('Starting Appwrite installation...');
// Create directory with write permissions
if (!\file_exists(\dirname($this->path))) {
if (!@\mkdir(\dirname($this->path), 0755, true)) {
Console::error('Can\'t create directory ' . \dirname($this->path));
2022-07-13 07:02:55 +00:00
Console::exit(1);
}
}
$data = @file_get_contents($this->path . '/docker-compose.yml');
2022-07-13 07:02:55 +00:00
if ($data !== false) {
if ($interactive == 'Y' && Console::isInteractive()) {
$answer = Console::confirm('Previous installation found, do you want to overwrite it (a backup will be created before overwriting)? (Y/n)');
if (\strtolower($answer) !== 'y') {
Console::info('No action taken.');
return;
}
}
2022-07-13 07:02:55 +00:00
$time = \time();
Console::info('Compose file found, creating backup: docker-compose.yml.' . $time . '.backup');
file_put_contents($this->path . '/docker-compose.yml.' . $time . '.backup', $data);
2022-07-13 07:02:55 +00:00
$compose = new Compose($data);
$appwrite = $compose->getService('appwrite');
2023-10-26 20:25:35 +00:00
$oldVersion = $appwrite?->getImageVersion();
2022-07-13 07:02:55 +00:00
try {
$ports = $compose->getService('traefik')->getPorts();
} catch (\Throwable $th) {
$ports = [
$defaultHTTPPort => $defaultHTTPPort,
$defaultHTTPSPort => $defaultHTTPSPort
];
Console::warning('Traefik not found. Falling back to default ports.');
}
if ($oldVersion) {
foreach ($compose->getServices() as $service) { // Fetch all env vars from previous compose file
if (!$service) {
continue;
}
$env = $service->getEnvironment()->list();
foreach ($env as $key => $value) {
if (is_null($value)) {
continue;
}
$configVar = $vars[$key] ?? [];
if (!empty($configVar) && !($configVar['overwrite'] ?? false)) {
$vars[$key]['default'] = $value;
2022-07-13 07:02:55 +00:00
}
}
}
$data = @file_get_contents($this->path . '/.env');
2022-07-13 07:02:55 +00:00
if ($data !== false) { // Fetch all env vars from previous .env file
Console::info('Env file found, creating backup: .env.' . $time . '.backup');
file_put_contents($this->path . '/.env.' . $time . '.backup', $data);
2022-07-13 07:02:55 +00:00
$env = new Env($data);
foreach ($env->list() as $key => $value) {
if (is_null($value)) {
continue;
}
$configVar = $vars[$key] ?? [];
if (!empty($configVar) && !($configVar['overwrite'] ?? false)) {
$vars[$key]['default'] = $value;
2022-07-13 07:02:55 +00:00
}
}
}
foreach ($ports as $key => $value) {
if ($value === $defaultHTTPPort) {
$defaultHTTPPort = $key;
}
if ($value === $defaultHTTPSPort) {
$defaultHTTPSPort = $key;
}
}
}
}
if (empty($httpPort)) {
$httpPort = Console::confirm('Choose your server HTTP port: (default: ' . $defaultHTTPPort . ')');
$httpPort = ($httpPort) ? $httpPort : $defaultHTTPPort;
}
if (empty($httpsPort)) {
$httpsPort = Console::confirm('Choose your server HTTPS port: (default: ' . $defaultHTTPSPort . ')');
$httpsPort = ($httpsPort) ? $httpsPort : $defaultHTTPSPort;
}
$input = [];
foreach ($vars as $var) {
2022-07-13 07:02:55 +00:00
if (!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) {
if ($data && $var['default'] !== null) {
$input[$var['name']] = $var['default'];
continue;
}
if ($var['filter'] === 'token') {
$input[$var['name']] = Auth::tokenGenerator();
continue;
}
if ($var['filter'] === 'password') {
$input[$var['name']] = Auth::passwordGenerator();
continue;
}
}
if (!$var['required'] || !Console::isInteractive() || $interactive !== 'Y') {
$input[$var['name']] = $var['default'];
continue;
}
$input[$var['name']] = Console::confirm($var['question'] . ' (default: \'' . $var['default'] . '\')');
if (empty($input[$var['name']])) {
$input[$var['name']] = $var['default'];
}
2022-10-14 09:32:17 +00:00
if ($var['filter'] === 'domainTarget') {
if ($input[$var['name']] !== 'localhost') {
Console::warning("\nIf you haven't already done so, set the following record for {$input[$var['name']]} on your DNS provider:\n");
$mask = "%-15.15s %-10.10s %-30.30s\n";
printf($mask, "Type", "Name", "Value");
printf($mask, "A or AAAA", "@", "<YOUR PUBLIC IP>");
Console::warning("\nUse 'AAAA' if you're using an IPv6 address and 'A' if you're using an IPv4 address.\n");
}
}
2022-07-13 07:02:55 +00:00
}
2023-08-28 22:09:37 +00:00
$templateForCompose = new View(__DIR__ . '/../../../../app/views/install/compose.phtml');
$templateForEnv = new View(__DIR__ . '/../../../../app/views/install/env.phtml');
2022-07-13 07:02:55 +00:00
$templateForCompose
->setParam('httpPort', $httpPort)
->setParam('httpsPort', $httpsPort)
->setParam('version', APP_VERSION_STABLE)
->setParam('organization', $organization)
2023-08-28 22:09:37 +00:00
->setParam('image', $image);
2022-07-13 07:02:55 +00:00
2023-08-28 22:09:37 +00:00
$templateForEnv->setParam('vars', $input);
2022-07-13 07:02:55 +00:00
if (!file_put_contents($this->path . '/docker-compose.yml', $templateForCompose->render(false))) {
2022-07-13 07:02:55 +00:00
$message = 'Failed to save Docker Compose file';
Console::error($message);
Console::exit(1);
}
if (!file_put_contents($this->path . '/.env', $templateForEnv->render(false))) {
2022-07-13 07:02:55 +00:00
$message = 'Failed to save environment variables file';
Console::error($message);
Console::exit(1);
}
$env = '';
2024-10-01 14:30:47 +00:00
$output = '';
2022-07-13 07:02:55 +00:00
foreach ($input as $key => $value) {
if ($value) {
$env .= $key . '=' . \escapeshellarg($value) . ' ';
}
}
$exit = 0;
if (!$noStart) {
Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\"");
2024-10-01 14:30:47 +00:00
$exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $output);
}
2022-07-13 07:02:55 +00:00
if ($exit !== 0) {
$message = 'Failed to install Appwrite dockers';
Console::error($message);
2024-10-01 14:30:47 +00:00
Console::error($output);
2022-07-13 07:02:55 +00:00
Console::exit($exit);
} else {
$message = 'Appwrite installed successfully';
Console::success($message);
}
}
2022-07-14 02:04:31 +00:00
}