mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
refactor rename
This commit is contained in:
+4
-3
@@ -3,13 +3,14 @@
|
||||
require_once __DIR__ . '/init.php';
|
||||
require_once __DIR__ . '/controllers/general.php';
|
||||
|
||||
use Appwrite\Task\CLIPlatform;
|
||||
use Appwrite\CLI\Tasks;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
Authorization::disable();
|
||||
|
||||
$cliPlatform = new CLIPlatform();
|
||||
$cliPlatform->init('CLI');
|
||||
$cliPlatform = new Tasks();
|
||||
$cliPlatform->init(Service::TYPE_CLI);
|
||||
|
||||
$cli = $cliPlatform->getCli();
|
||||
$cli->run();
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli;
|
||||
|
||||
use Appwrite\ClamAV\Network;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Domains\Domain;
|
||||
|
||||
$cli
|
||||
->task('doctor')
|
||||
->desc('Validate server health')
|
||||
->action(function () use ($register) {
|
||||
Console::log(" __ ____ ____ _ _ ____ __ ____ ____ __ __
|
||||
/ _\ ( _ \( _ \/ )( \( _ \( )(_ _)( __) ( )/ \
|
||||
/ \ ) __/ ) __/\ /\ / ) / )( )( ) _) _ )(( O )
|
||||
\_/\_/(__) (__) (_/\_)(__\_)(__) (__) (____)(_)(__)\__/ ");
|
||||
|
||||
Console::log("\n" . '👩⚕️ Running ' . APP_NAME . ' Doctor for version ' . App::getEnv('_APP_VERSION', 'UNKNOWN') . ' ...' . "\n");
|
||||
|
||||
Console::log('Checking for production best practices...');
|
||||
|
||||
$domain = new Domain(App::getEnv('_APP_DOMAIN'));
|
||||
|
||||
if (!$domain->isKnown() || $domain->isTest()) {
|
||||
Console::log('🔴 Hostname has no public suffix (' . $domain->get() . ')');
|
||||
} else {
|
||||
Console::log('🟢 Hostname has a public suffix (' . $domain->get() . ')');
|
||||
}
|
||||
|
||||
$domain = new Domain(App::getEnv('_APP_DOMAIN_TARGET'));
|
||||
|
||||
if (!$domain->isKnown() || $domain->isTest()) {
|
||||
Console::log('🔴 CNAME target has no public suffix (' . $domain->get() . ')');
|
||||
} else {
|
||||
Console::log('🟢 CNAME target has a public suffix (' . $domain->get() . ')');
|
||||
}
|
||||
|
||||
if (App::getEnv('_APP_OPENSSL_KEY_V1') === 'your-secret-key' || empty(App::getEnv('_APP_OPENSSL_KEY_V1'))) {
|
||||
Console::log('🔴 Not using a unique secret key for encryption');
|
||||
} else {
|
||||
Console::log('🟢 Using a unique secret key for encryption');
|
||||
}
|
||||
|
||||
if (App::getEnv('_APP_ENV', 'development') !== 'production') {
|
||||
Console::log('🔴 App environment is set for development');
|
||||
} else {
|
||||
Console::log('🟢 App environment is set for production');
|
||||
}
|
||||
|
||||
if ('enabled' !== App::getEnv('_APP_OPTIONS_ABUSE', 'disabled')) {
|
||||
Console::log('🔴 Abuse protection is disabled');
|
||||
} else {
|
||||
Console::log('🟢 Abuse protection is enabled');
|
||||
}
|
||||
|
||||
$authWhitelistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', null);
|
||||
$authWhitelistEmails = App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null);
|
||||
$authWhitelistIPs = App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null);
|
||||
|
||||
if (
|
||||
empty($authWhitelistRoot)
|
||||
&& empty($authWhitelistEmails)
|
||||
&& empty($authWhitelistIPs)
|
||||
) {
|
||||
Console::log('🔴 Console access limits are disabled');
|
||||
} else {
|
||||
Console::log('🟢 Console access limits are enabled');
|
||||
}
|
||||
|
||||
if ('enabled' !== App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled')) {
|
||||
Console::log('🔴 HTTPS force option is disabled');
|
||||
} else {
|
||||
Console::log('🟢 HTTPS force option is enabled');
|
||||
}
|
||||
|
||||
|
||||
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
|
||||
if (empty($providerName) || empty($providerConfig) || !Logger::hasProvider($providerName)) {
|
||||
Console::log('🔴 Logging adapter is disabled');
|
||||
} else {
|
||||
Console::log('🟢 Logging adapter is enabled (' . $providerName . ')');
|
||||
}
|
||||
|
||||
\sleep(0.2);
|
||||
|
||||
try {
|
||||
Console::log("\n" . 'Checking connectivity...');
|
||||
} catch (\Throwable $th) {
|
||||
//throw $th;
|
||||
}
|
||||
|
||||
try {
|
||||
$register->get('db'); /* @var $db PDO */
|
||||
Console::success('Database............connected 👍');
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Database.........disconnected 👎');
|
||||
}
|
||||
|
||||
try {
|
||||
$register->get('cache');
|
||||
Console::success('Queue...............connected 👍');
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Queue............disconnected 👎');
|
||||
}
|
||||
|
||||
try {
|
||||
$register->get('cache');
|
||||
Console::success('Cache...............connected 👍');
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Cache............disconnected 👎');
|
||||
}
|
||||
|
||||
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
|
||||
try {
|
||||
$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 👍');
|
||||
} else {
|
||||
Console::error('Antivirus........disconnected 👎');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Antivirus........disconnected 👎');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
|
||||
|
||||
$mail->addAddress('demo@example.com', 'Example.com');
|
||||
$mail->Subject = 'Test SMTP Connection';
|
||||
$mail->Body = 'Hello World';
|
||||
$mail->AltBody = 'Hello World';
|
||||
|
||||
$mail->send();
|
||||
Console::success('SMTP................connected 👍');
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('SMTP.............disconnected 👎');
|
||||
}
|
||||
|
||||
$host = App::getEnv('_APP_STATSD_HOST', 'telegraf');
|
||||
$port = App::getEnv('_APP_STATSD_PORT', 8125);
|
||||
|
||||
if ($fp = @\fsockopen('udp://' . $host, $port, $errCode, $errStr, 2)) {
|
||||
Console::success('StatsD..............connected 👍');
|
||||
\fclose($fp);
|
||||
} else {
|
||||
Console::error('StatsD...........disconnected 👎');
|
||||
}
|
||||
|
||||
$host = App::getEnv('_APP_INFLUXDB_HOST', '');
|
||||
$port = App::getEnv('_APP_INFLUXDB_PORT', '');
|
||||
|
||||
if ($fp = @\fsockopen($host, $port, $errCode, $errStr, 2)) {
|
||||
Console::success('InfluxDB............connected 👍');
|
||||
\fclose($fp);
|
||||
} else {
|
||||
Console::error('InfluxDB.........disconnected 👎');
|
||||
}
|
||||
|
||||
\sleep(0.2);
|
||||
|
||||
Console::log('');
|
||||
Console::log('Checking volumes...');
|
||||
|
||||
foreach (
|
||||
[
|
||||
'Uploads' => APP_STORAGE_UPLOADS,
|
||||
'Cache' => APP_STORAGE_CACHE,
|
||||
'Config' => APP_STORAGE_CONFIG,
|
||||
'Certs' => APP_STORAGE_CERTIFICATES
|
||||
] as $key => $volume
|
||||
) {
|
||||
$device = new Local($volume);
|
||||
|
||||
if (\is_readable($device->getRoot())) {
|
||||
Console::success('🟢 ' . $key . ' Volume is readable');
|
||||
} else {
|
||||
Console::error('🔴 ' . $key . ' Volume is unreadable');
|
||||
}
|
||||
|
||||
if (\is_writable($device->getRoot())) {
|
||||
Console::success('🟢 ' . $key . ' Volume is writeable');
|
||||
} else {
|
||||
Console::error('🔴 ' . $key . ' Volume is unwriteable');
|
||||
}
|
||||
}
|
||||
|
||||
\sleep(0.2);
|
||||
|
||||
Console::log('');
|
||||
Console::log('Checking disk space usage...');
|
||||
|
||||
foreach (
|
||||
[
|
||||
'Uploads' => APP_STORAGE_UPLOADS,
|
||||
'Cache' => APP_STORAGE_CACHE,
|
||||
'Config' => APP_STORAGE_CONFIG,
|
||||
'Certs' => APP_STORAGE_CERTIFICATES
|
||||
] as $key => $volume
|
||||
) {
|
||||
$device = new Local($volume);
|
||||
|
||||
$percentage = (($device->getPartitionTotalSpace() - $device->getPartitionFreeSpace())
|
||||
/ $device->getPartitionTotalSpace()) * 100;
|
||||
|
||||
$message = $key . ' Volume has ' . Storage::human($device->getPartitionFreeSpace()) . ' free space (' . \round($percentage, 2) . '% used)';
|
||||
|
||||
if ($percentage < 80) {
|
||||
Console::success('🟢 ' . $message);
|
||||
} else {
|
||||
Console::error('🔴 ' . $message);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (App::isProduction()) {
|
||||
Console::log('');
|
||||
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost') . '/v1/health/version'), true);
|
||||
|
||||
if ($version && isset($version['version'])) {
|
||||
if (\version_compare($version['version'], App::getEnv('_APP_VERSION', 'UNKNOWN')) === 0) {
|
||||
Console::info('You are running the latest version of ' . APP_NAME . '! 🥳');
|
||||
} else {
|
||||
Console::info('A new version (' . $version['version'] . ') is available! 🥳' . "\n");
|
||||
}
|
||||
} else {
|
||||
Console::error('Failed to check for a newer version' . "\n");
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to check for a newer version' . "\n");
|
||||
}
|
||||
});
|
||||
@@ -1,242 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Docker\Compose;
|
||||
use Appwrite\Docker\Env;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\Analytics\GoogleAnalytics;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
$cli
|
||||
->task('install')
|
||||
->desc('Install 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)
|
||||
->action(function ($httpPort, $httpsPort, $organization, $image, $interactive) {
|
||||
/**
|
||||
* 1. Start - DONE
|
||||
* 2. Check for older setup and get older version - DONE
|
||||
* 2.1 If older version is equal or bigger(?) than current version, **stop setup**
|
||||
* 2.2. Get ENV vars - DONE
|
||||
* 2.2.1 Fetch from older docker-compose.yml file
|
||||
* 2.2.2 Fetch from older .env file (manually parse)
|
||||
* 2.3 Use old ENV vars as default values
|
||||
* 2.4 Ask for all required vars not given as CLI args and if in interactive mode
|
||||
* Otherwise, just use default vars. - DONE
|
||||
* 3. Ask user to backup important volumes, env vars, and SQL tables
|
||||
* In th future we can try and automate this for smaller/medium size setups
|
||||
* 4. Drop new docker-compose.yml setup (located inside the container, no network dependencies with appwrite.io) - DONE
|
||||
* 5. Run docker compose up -d - DONE
|
||||
* 6. Run data migration
|
||||
*/
|
||||
$config = Config::getParam('variables');
|
||||
$path = '/usr/src/code/appwrite';
|
||||
$defaultHTTPPort = '80';
|
||||
$defaultHTTPSPort = '443';
|
||||
$vars = [];
|
||||
|
||||
/**
|
||||
* We are using a random value every execution for identification.
|
||||
* This allows us to collect information without invading the privacy of our users.
|
||||
*/
|
||||
$analytics = new GoogleAnalytics('UA-26264668-9', uniqid('server.', true));
|
||||
|
||||
foreach ($config as $category) {
|
||||
foreach ($category['variables'] ?? [] as $var) {
|
||||
$vars[] = $var;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
Console::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$data = @file_get_contents($path . '/docker-compose.yml');
|
||||
|
||||
if ($data !== false) {
|
||||
$time = \time();
|
||||
Console::info('Compose file found, creating backup: docker-compose.yml.' . $time . '.backup');
|
||||
file_put_contents($path . '/docker-compose.yml.' . $time . '.backup', $data);
|
||||
$compose = new Compose($data);
|
||||
$appwrite = $compose->getService('appwrite');
|
||||
$oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null;
|
||||
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;
|
||||
}
|
||||
foreach ($vars as &$var) {
|
||||
if ($var['name'] === $key) {
|
||||
$var['default'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = @file_get_contents($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);
|
||||
$env = new Env($data);
|
||||
|
||||
foreach ($env->list() as $key => $value) {
|
||||
if (is_null($value)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($vars as &$var) {
|
||||
if ($var['name'] === $key) {
|
||||
$var['default'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 $key => $var) {
|
||||
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'];
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$templateForCompose = new View(__DIR__ . '/../views/install/compose.phtml');
|
||||
$templateForEnv = new View(__DIR__ . '/../views/install/env.phtml');
|
||||
|
||||
$templateForCompose
|
||||
->setParam('httpPort', $httpPort)
|
||||
->setParam('httpsPort', $httpsPort)
|
||||
->setParam('version', APP_VERSION_STABLE)
|
||||
->setParam('organization', $organization)
|
||||
->setParam('image', $image)
|
||||
;
|
||||
|
||||
$templateForEnv
|
||||
->setParam('vars', $input)
|
||||
;
|
||||
|
||||
if (!file_put_contents($path . '/docker-compose.yml', $templateForCompose->render(false))) {
|
||||
$message = 'Failed to save Docker Compose file';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::error($message);
|
||||
Console::exit(1);
|
||||
}
|
||||
|
||||
if (!file_put_contents($path . '/.env', $templateForEnv->render(false))) {
|
||||
$message = 'Failed to save environment variables file';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::error($message);
|
||||
Console::exit(1);
|
||||
}
|
||||
|
||||
$env = '';
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
foreach ($input as $key => $value) {
|
||||
if ($value) {
|
||||
$env .= $key . '=' . \escapeshellarg($value) . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
Console::log("Running \"docker compose -f {$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);
|
||||
|
||||
if ($exit !== 0) {
|
||||
$message = 'Failed to install Appwrite dockers';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::error($message);
|
||||
Console::error($stderr);
|
||||
Console::exit($exit);
|
||||
} else {
|
||||
$message = 'Appwrite installed successfully';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::success($message);
|
||||
}
|
||||
});
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli;
|
||||
global $register;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Delete;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
function getConsoleDB(): Database
|
||||
{
|
||||
global $register;
|
||||
|
||||
$attempts = 0;
|
||||
|
||||
do {
|
||||
try {
|
||||
$attempts++;
|
||||
$cache = new Cache(new RedisCache($register->get('cache')));
|
||||
$database = new Database(new MariaDB($register->get('db')), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace('_console'); // Main DB
|
||||
|
||||
if (!$database->exists($database->getDefaultDatabase(), 'certificates')) {
|
||||
throw new \Exception('Console project not ready');
|
||||
}
|
||||
|
||||
break; // leave loop if successful
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) {
|
||||
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
||||
}
|
||||
sleep(DATABASE_RECONNECT_SLEEP);
|
||||
}
|
||||
} while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS);
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
$cli
|
||||
->task('maintenance')
|
||||
->desc('Schedules maintenance tasks and publishes them to resque')
|
||||
->action(function () {
|
||||
Console::title('Maintenance V1');
|
||||
Console::success(APP_NAME . ' maintenance process v1 has started');
|
||||
|
||||
function notifyDeleteExecutionLogs(int $interval)
|
||||
{
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_EXECUTIONS)
|
||||
->setTimestamp(time() - $interval)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
function notifyDeleteAbuseLogs(int $interval)
|
||||
{
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_ABUSE)
|
||||
->setTimestamp(time() - $interval)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
function notifyDeleteAuditLogs(int $interval)
|
||||
{
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_AUDIT)
|
||||
->setTimestamp(time() - $interval)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
function notifyDeleteUsageStats(int $interval30m, int $interval1d)
|
||||
{
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_USAGE)
|
||||
->setTimestamp1d(time() - $interval1d)
|
||||
->setTimestamp30m(time() - $interval30m)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
function notifyDeleteConnections()
|
||||
{
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_REALTIME)
|
||||
->setTimestamp(time() - 60)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
function notifyDeleteExpiredSessions()
|
||||
{
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_SESSIONS)
|
||||
->setTimestamp(time() - Auth::TOKEN_EXPIRATION_LOGIN_LONG)
|
||||
->trigger();
|
||||
}
|
||||
|
||||
function renewCertificates($dbForConsole)
|
||||
{
|
||||
$time = date('d-m-Y H:i:s', time());
|
||||
$certificates = $dbForConsole->find('certificates', [
|
||||
new Query('attempts', Query::TYPE_LESSEREQUAL, [5]), // Maximum 5 attempts
|
||||
new Query('renewDate', Query::TYPE_LESSEREQUAL, [\time()]) // includes 60 days cooldown (we have 30 days to renew)
|
||||
], 200); // Limit 200 comes from LetsEncrypt (300 orders per 3 hours, keeping some for new domains)
|
||||
|
||||
|
||||
if (\count($certificates) > 0) {
|
||||
Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs.");
|
||||
|
||||
$event = new Certificate();
|
||||
foreach ($certificates as $certificate) {
|
||||
$event
|
||||
->setDomain(new Document([
|
||||
'domain' => $certificate->getAttribute('domain')
|
||||
]))
|
||||
->trigger();
|
||||
}
|
||||
} else {
|
||||
Console::info("[{$time}] No certificates for renewal.");
|
||||
}
|
||||
}
|
||||
|
||||
// # of days in seconds (1 day = 86400s)
|
||||
$interval = (int) App::getEnv('_APP_MAINTENANCE_INTERVAL', '86400');
|
||||
$executionLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', '1209600');
|
||||
$auditLogRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', '1209600');
|
||||
$abuseLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', '86400');
|
||||
$usageStatsRetention30m = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_30M', '129600'); //36 hours
|
||||
$usageStatsRetention1d = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_1D', '8640000'); // 100 days
|
||||
|
||||
Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) {
|
||||
$database = getConsoleDB();
|
||||
|
||||
$time = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds");
|
||||
notifyDeleteExecutionLogs($executionLogsRetention);
|
||||
notifyDeleteAbuseLogs($abuseLogsRetention);
|
||||
notifyDeleteAuditLogs($auditLogRetention);
|
||||
notifyDeleteUsageStats($usageStatsRetention30m, $usageStatsRetention1d);
|
||||
notifyDeleteConnections();
|
||||
notifyDeleteExpiredSessions();
|
||||
renewCertificates($database);
|
||||
}, $interval);
|
||||
});
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli, $register;
|
||||
|
||||
use Utopia\CLI\Console;
|
||||
use Appwrite\Migration\Migration;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$app = new App('UTC');
|
||||
|
||||
Console::success('Starting Data Migration to version ' . $version);
|
||||
|
||||
$db = $register->get('db', true);
|
||||
$redis = $register->get('cache', true);
|
||||
$redis->flushAll();
|
||||
$cache = new Cache(new RedisCache($redis));
|
||||
|
||||
$projectDB = new Database(new MariaDB($db), $cache);
|
||||
$projectDB->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
|
||||
$consoleDB = new Database(new MariaDB($db), $cache);
|
||||
$consoleDB->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$consoleDB->setNamespace('_project_console');
|
||||
|
||||
$console = $app->getResource('console');
|
||||
|
||||
$limit = 30;
|
||||
$sum = 30;
|
||||
$offset = 0;
|
||||
$projects = [$console];
|
||||
$count = 0;
|
||||
|
||||
try {
|
||||
$totalProjects = $consoleDB->count('projects') + 1;
|
||||
} catch (\Throwable $th) {
|
||||
$consoleDB->setNamespace('_console');
|
||||
$totalProjects = $consoleDB->count('projects') + 1;
|
||||
}
|
||||
|
||||
$class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version];
|
||||
$migration = new $class();
|
||||
|
||||
while (!empty($projects)) {
|
||||
foreach ($projects as $project) {
|
||||
try {
|
||||
$migration
|
||||
->setProject($project, $projectDB, $consoleDB)
|
||||
->execute();
|
||||
} catch (\Throwable $th) {
|
||||
throw $th;
|
||||
Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$sum = \count($projects);
|
||||
$projects = $consoleDB->find('projects', limit: $limit, offset: $offset);
|
||||
|
||||
$offset = $offset + $limit;
|
||||
$count = $count + $sum;
|
||||
|
||||
Console::log('Migrated ' . $count . '/' . $totalProjects . ' projects...');
|
||||
}
|
||||
|
||||
Swoole\Event::wait(); // Wait for Coroutines to finish
|
||||
$redis->flushAll();
|
||||
Console::success('Data Migration Completed');
|
||||
});
|
||||
@@ -1,257 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\CLI\Console;
|
||||
use Appwrite\Spec\Swagger2;
|
||||
use Appwrite\SDK\SDK;
|
||||
use Appwrite\SDK\Language\CLI;
|
||||
use Appwrite\SDK\Language\PHP;
|
||||
use Appwrite\SDK\Language\Web;
|
||||
use Appwrite\SDK\Language\Node;
|
||||
use Appwrite\SDK\Language\Python;
|
||||
use Appwrite\SDK\Language\Ruby;
|
||||
use Appwrite\SDK\Language\Dart;
|
||||
use Appwrite\SDK\Language\Deno;
|
||||
use Appwrite\SDK\Language\DotNet;
|
||||
use Appwrite\SDK\Language\Flutter;
|
||||
use Appwrite\SDK\Language\Go;
|
||||
use Appwrite\SDK\Language\Kotlin;
|
||||
use Appwrite\SDK\Language\Android;
|
||||
use Appwrite\SDK\Language\Swift;
|
||||
use Appwrite\SDK\Language\SwiftClient;
|
||||
|
||||
$cli
|
||||
->task('sdks')
|
||||
->action(function () {
|
||||
$platforms = Config::getParam('platforms');
|
||||
$selected = \strtolower(Console::confirm('Choose SDK ("*" for all):'));
|
||||
$version = Console::confirm('Choose an Appwrite version');
|
||||
$git = (Console::confirm('Should we use git push? (yes/no)') == 'yes');
|
||||
$production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false;
|
||||
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
|
||||
|
||||
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', 'latest'])) {
|
||||
throw new Exception('Unknown version given');
|
||||
}
|
||||
|
||||
foreach ($platforms as $key => $platform) {
|
||||
foreach ($platform['languages'] as $language) {
|
||||
if ($selected !== $language['key'] && $selected !== '*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$language['enabled']) {
|
||||
Console::warning($language['name'] . ' for ' . $platform['name'] . ' is disabled');
|
||||
continue;
|
||||
}
|
||||
|
||||
Console::info('Fetching API Spec for ' . $language['name'] . ' for ' . $platform['name'] . ' (version: ' . $version . ')');
|
||||
|
||||
$spec = file_get_contents(__DIR__ . '/../config/specs/swagger2-' . $version . '-' . $language['family'] . '.json');
|
||||
|
||||
$cover = 'https://appwrite.io/images/github.png';
|
||||
$result = \realpath(__DIR__ . '/..') . '/sdks/' . $key . '-' . $language['key'];
|
||||
$resultExamples = \realpath(__DIR__ . '/../..') . '/docs/examples/' . $version . '/' . $key . '-' . $language['key'];
|
||||
$target = \realpath(__DIR__ . '/..') . '/sdks/git/' . $language['key'] . '/';
|
||||
$readme = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/README.md');
|
||||
$readme = ($readme) ? \file_get_contents($readme) : '';
|
||||
$gettingStarted = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/GETTING_STARTED.md');
|
||||
$gettingStarted = ($gettingStarted) ? \file_get_contents($gettingStarted) : '';
|
||||
$examples = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/EXAMPLES.md');
|
||||
$examples = ($examples) ? \file_get_contents($examples) : '';
|
||||
$changelog = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/CHANGELOG.md');
|
||||
$changelog = ($changelog) ? \file_get_contents($changelog) : '# Change Log';
|
||||
$warning = '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check [previous releases](' . $language['url'] . '/releases).**';
|
||||
$license = 'BSD-3-Clause';
|
||||
$licenseContent = 'Copyright (c) ' . date('Y') . ' Appwrite (https://appwrite.io) and individual contributors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name Appwrite nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.';
|
||||
|
||||
switch ($language['key']) {
|
||||
case 'web':
|
||||
$config = new Web();
|
||||
$config->setNPMPackage('appwrite');
|
||||
$config->setBowerPackage('appwrite');
|
||||
break;
|
||||
case 'cli':
|
||||
$config = new CLI();
|
||||
$config->setNPMPackage('appwrite-cli');
|
||||
$config->setExecutableName('appwrite');
|
||||
$config->setLogo(json_encode("
|
||||
_ _ _ ___ __ _____
|
||||
/_\ _ __ _ ____ ___ __(_) |_ ___ / __\ / / \_ \
|
||||
//_\\\| '_ \| '_ \ \ /\ / / '__| | __/ _ \ / / / / / /\/
|
||||
/ _ \ |_) | |_) \ V V /| | | | || __/ / /___/ /___/\/ /_
|
||||
\_/ \_/ .__/| .__/ \_/\_/ |_| |_|\__\___| \____/\____/\____/
|
||||
|_| |_|
|
||||
|
||||
"));
|
||||
$config->setLogoUnescaped("
|
||||
_ _ _ ___ __ _____
|
||||
/_\ _ __ _ ____ ___ __(_) |_ ___ / __\ / / \_ \
|
||||
//_\\\| '_ \| '_ \ \ /\ / / '__| | __/ _ \ / / / / / /\/
|
||||
/ _ \ |_) | |_) \ V V /| | | | || __/ / /___/ /___/\/ /_
|
||||
\_/ \_/ .__/| .__/ \_/\_/ |_| |_|\__\___| \____/\____/\____/
|
||||
|_| |_| ");
|
||||
break;
|
||||
case 'php':
|
||||
$config = new PHP();
|
||||
$config->setComposerVendor('appwrite');
|
||||
$config->setComposerPackage('appwrite');
|
||||
break;
|
||||
case 'nodejs':
|
||||
$config = new Node();
|
||||
$config->setNPMPackage('node-appwrite');
|
||||
$config->setBowerPackage('appwrite');
|
||||
$warning = $warning . "\n\n > This is the Node.js SDK for integrating with Appwrite from your Node.js server-side code.
|
||||
If you're looking to integrate from the browser, you should check [appwrite/sdk-for-web](https://github.com/appwrite/sdk-for-web)";
|
||||
break;
|
||||
case 'deno':
|
||||
$config = new Deno();
|
||||
break;
|
||||
case 'python':
|
||||
$config = new Python();
|
||||
$config->setPipPackage('appwrite');
|
||||
$license = 'BSD License'; // license edited due to classifiers in pypi
|
||||
break;
|
||||
case 'ruby':
|
||||
$config = new Ruby();
|
||||
$config->setGemPackage('appwrite');
|
||||
break;
|
||||
case 'flutter':
|
||||
$config = new Flutter();
|
||||
$config->setPackageName('appwrite');
|
||||
break;
|
||||
case 'flutter-dev':
|
||||
$config = new Flutter();
|
||||
$config->setPackageName('appwrite_dev');
|
||||
break;
|
||||
case 'dart':
|
||||
$config = new Dart();
|
||||
$config->setPackageName('dart_appwrite');
|
||||
$warning = $warning . "\n\n > This is the Dart SDK for integrating with Appwrite from your Dart server-side code. If you're looking for the Flutter SDK you should check [appwrite/sdk-for-flutter](https://github.com/appwrite/sdk-for-flutter)";
|
||||
break;
|
||||
case 'go':
|
||||
$config = new Go();
|
||||
break;
|
||||
case 'swift':
|
||||
$config = new Swift();
|
||||
$warning = $warning . "\n\n > This is the Swift SDK for integrating with Appwrite from your Swift server-side code. If you're looking for the Apple SDK you should check [appwrite/sdk-for-apple](https://github.com/appwrite/sdk-for-apple)";
|
||||
break;
|
||||
case 'apple':
|
||||
$config = new SwiftClient();
|
||||
break;
|
||||
case 'dotnet':
|
||||
$cover = '';
|
||||
$config = new DotNet();
|
||||
break;
|
||||
case 'android':
|
||||
$config = new Android();
|
||||
break;
|
||||
case 'kotlin':
|
||||
$config = new Kotlin();
|
||||
$warning = $warning . "\n\n > This is the Kotlin SDK for integrating with Appwrite from your Kotlin server-side code. If you're looking for the Android SDK you should check [appwrite/sdk-for-android](https://github.com/appwrite/sdk-for-android)";
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Language "' . $language['key'] . '" not supported');
|
||||
break;
|
||||
}
|
||||
|
||||
Console::info("Generating {$language['name']} SDK...");
|
||||
|
||||
$sdk = new SDK($config, new Swagger2($spec));
|
||||
|
||||
$sdk
|
||||
->setName($language['name'])
|
||||
->setNamespace('io appwrite')
|
||||
->setDescription("Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the {$language['name']} SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)")
|
||||
->setShortDescription('Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API')
|
||||
->setLicense($license)
|
||||
->setLicenseContent($licenseContent)
|
||||
->setVersion($language['version'])
|
||||
->setGitURL($language['url'])
|
||||
->setGitRepo($language['gitUrl'])
|
||||
->setGitRepoName($language['gitRepoName'])
|
||||
->setGitUserName($language['gitUserName'])
|
||||
->setLogo($cover)
|
||||
->setURL('https://appwrite.io')
|
||||
->setShareText('Appwrite is a backend as a service for building web or mobile apps')
|
||||
->setShareURL('http://appwrite.io')
|
||||
->setShareTags('JS,javascript,reactjs,angular,ios,android,serverless')
|
||||
->setShareVia('appwrite')
|
||||
->setWarning($warning)
|
||||
->setReadme($readme)
|
||||
->setGettingStarted($gettingStarted)
|
||||
->setChangelog($changelog)
|
||||
->setExamples($examples)
|
||||
->setTwitter(APP_SOCIAL_TWITTER_HANDLE)
|
||||
->setDiscord(APP_SOCIAL_DISCORD_CHANNEL, APP_SOCIAL_DISCORD)
|
||||
->setDefaultHeaders([
|
||||
'X-Appwrite-Response-Format' => '0.15.0',
|
||||
]);
|
||||
|
||||
try {
|
||||
$sdk->generate($result);
|
||||
} catch (Exception $exception) {
|
||||
Console::error($exception->getMessage());
|
||||
} catch (Throwable $exception) {
|
||||
Console::error($exception->getMessage());
|
||||
}
|
||||
|
||||
$gitUrl = $language['gitUrl'];
|
||||
$gitBranch = $language['gitBranch'];
|
||||
|
||||
|
||||
if (!$production) {
|
||||
$gitUrl = 'git@github.com:aw-tests/' . $language['gitRepoName'] . '.git';
|
||||
}
|
||||
|
||||
if ($git && !empty($gitUrl)) {
|
||||
\exec('rm -rf ' . $target . ' && \
|
||||
mkdir -p ' . $target . ' && \
|
||||
cd ' . $target . ' && \
|
||||
git init --initial-branch=' . $gitBranch . ' && \
|
||||
git remote add origin ' . $gitUrl . ' && \
|
||||
git fetch && \
|
||||
git pull ' . $gitUrl . ' && \
|
||||
rm -rf ' . $target . '/* && \
|
||||
cp -r ' . $result . '/* ' . $target . '/ && \
|
||||
git add . && \
|
||||
git commit -m "' . $message . '" && \
|
||||
git push -u origin ' . $gitBranch . '
|
||||
');
|
||||
|
||||
Console::success("Pushed {$language['name']} SDK to {$gitUrl}");
|
||||
|
||||
\exec('rm -rf ' . $target);
|
||||
Console::success("Remove temp directory '{$target}' for {$language['name']} SDK");
|
||||
}
|
||||
|
||||
$docDirectories = $language['docDirectories'] ?? [''];
|
||||
|
||||
if ($version === 'latest') {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($docDirectories as $languageTitle => $path) {
|
||||
$languagePath = strtolower($languageTitle !== 0 ? '/' . $languageTitle : '');
|
||||
\exec(
|
||||
'mkdir -p ' . $resultExamples . $languagePath . ' && \
|
||||
cp -r ' . $result . '/docs/examples' . $languagePath . ' ' . $resultExamples
|
||||
);
|
||||
Console::success("Copied code examples for {$language['name']} SDK to: {$resultExamples}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console::exit();
|
||||
});
|
||||
@@ -1,256 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli;
|
||||
|
||||
use Utopia\Validator\Text;
|
||||
use Appwrite\Specification\Format\OpenAPI3;
|
||||
use Appwrite\Specification\Format\Swagger2;
|
||||
use Appwrite\Specification\Specification;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Http\Response as HttpResponse;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Request;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
$cli
|
||||
->task('specs')
|
||||
->param('version', 'latest', new Text(8), 'Spec version', true)
|
||||
->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true)
|
||||
->action(function ($version, $mode) use ($register) {
|
||||
$db = $register->get('db');
|
||||
$redis = $register->get('cache');
|
||||
$appRoutes = App::getRoutes();
|
||||
$response = new Response(new HttpResponse());
|
||||
$mocks = ($mode === 'mocks');
|
||||
|
||||
App::setResource('request', fn () => new Request());
|
||||
App::setResource('response', fn () => $response);
|
||||
App::setResource('db', fn () => $db);
|
||||
App::setResource('cache', fn () => $redis);
|
||||
|
||||
$platforms = [
|
||||
'client' => APP_PLATFORM_CLIENT,
|
||||
'server' => APP_PLATFORM_SERVER,
|
||||
'console' => APP_PLATFORM_CONSOLE,
|
||||
];
|
||||
|
||||
$authCounts = [
|
||||
'client' => 1,
|
||||
'server' => 2,
|
||||
'console' => 1,
|
||||
];
|
||||
|
||||
$keys = [
|
||||
APP_PLATFORM_CLIENT => [
|
||||
'Project' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Project',
|
||||
'description' => 'Your project ID',
|
||||
'in' => 'header',
|
||||
],
|
||||
'JWT' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-JWT',
|
||||
'description' => 'Your secret JSON Web Token',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Locale' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Locale',
|
||||
'description' => '',
|
||||
'in' => 'header',
|
||||
],
|
||||
],
|
||||
APP_PLATFORM_SERVER => [
|
||||
'Project' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Project',
|
||||
'description' => 'Your project ID',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Key' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Key',
|
||||
'description' => 'Your secret API key',
|
||||
'in' => 'header',
|
||||
],
|
||||
'JWT' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-JWT',
|
||||
'description' => 'Your secret JSON Web Token',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Locale' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Locale',
|
||||
'description' => '',
|
||||
'in' => 'header',
|
||||
],
|
||||
],
|
||||
APP_PLATFORM_CONSOLE => [
|
||||
'Project' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Project',
|
||||
'description' => 'Your project ID',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Key' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Key',
|
||||
'description' => 'Your secret API key',
|
||||
'in' => 'header',
|
||||
],
|
||||
'JWT' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-JWT',
|
||||
'description' => 'Your secret JSON Web Token',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Locale' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Locale',
|
||||
'description' => '',
|
||||
'in' => 'header',
|
||||
],
|
||||
'Mode' => [
|
||||
'type' => 'apiKey',
|
||||
'name' => 'X-Appwrite-Mode',
|
||||
'description' => '',
|
||||
'in' => 'header',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($platforms as $platform) {
|
||||
$routes = [];
|
||||
$models = [];
|
||||
$services = [];
|
||||
|
||||
foreach ($appRoutes as $key => $method) {
|
||||
foreach ($method as $route) {
|
||||
/** @var \Utopia\Route $route */
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
$sdkPlaforms = [];
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
$sdkPlaforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
$sdkPlaforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlaforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
if (!$route->getLabel('docs', true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($route->getLabel('sdk.mock', false) && !$mocks) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$route->getLabel('sdk.mock', false) && $mocks) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($route->getLabel('sdk.namespace', null))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlaforms)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routes[] = $route;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Config::getParam('services', []) as $service) {
|
||||
if (
|
||||
!isset($service['docs']) // Skip service if not part of the public API
|
||||
|| !isset($service['sdk'])
|
||||
|| !$service['docs']
|
||||
|| !$service['sdk']
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$services[] = [
|
||||
'name' => $service['key'] ?? '',
|
||||
'description' => $service['subtitle'] ?? '',
|
||||
'x-globalAttributes' => $service['globalAttributes'] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
$models = $response->getModels();
|
||||
|
||||
foreach ($models as $key => $value) {
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) {
|
||||
unset($models[$key]);
|
||||
}
|
||||
}
|
||||
// var_dump($models);
|
||||
$arguments = [new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0];
|
||||
foreach (['swagger2', 'open-api3'] as $format) {
|
||||
$formatInstance = match ($format) {
|
||||
'swagger2' => new Swagger2(...$arguments),
|
||||
'open-api3' => new OpenAPI3(...$arguments),
|
||||
default => throw new Exception('Format not found: ' . $format)
|
||||
};
|
||||
|
||||
$specs = new Specification($formatInstance);
|
||||
$endpoint = App::getEnv('_APP_HOME', '[HOSTNAME]');
|
||||
$email = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
|
||||
$formatInstance
|
||||
->setParam('name', APP_NAME)
|
||||
->setParam('description', 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)')
|
||||
->setParam('endpoint', 'https://HOSTNAME/v1')
|
||||
->setParam('version', APP_VERSION_STABLE)
|
||||
->setParam('terms', $endpoint . '/policy/terms')
|
||||
->setParam('support.email', $email)
|
||||
->setParam('support.url', $endpoint . '/support')
|
||||
->setParam('contact.name', APP_NAME . ' Team')
|
||||
->setParam('contact.email', $email)
|
||||
->setParam('contact.url', $endpoint . '/support')
|
||||
->setParam('license.name', 'BSD-3-Clause')
|
||||
->setParam('license.url', 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE')
|
||||
->setParam('docs.description', 'Full API docs, specs and tutorials')
|
||||
->setParam('docs.url', $endpoint . '/docs');
|
||||
|
||||
if ($mocks) {
|
||||
$path = __DIR__ . '/../config/specs/' . $format . '-mocks-' . $platform . '.json';
|
||||
|
||||
if (!file_put_contents($path, json_encode($specs->parse()))) {
|
||||
throw new Exception('Failed to save mocks spec file: ' . $path);
|
||||
}
|
||||
|
||||
Console::success('Saved mocks spec file: ' . realpath($path));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = __DIR__ . '/../config/specs/' . $format . '-' . $version . '-' . $platform . '.json';
|
||||
|
||||
if (!file_put_contents($path, json_encode($specs->parse()))) {
|
||||
throw new Exception('Failed to save spec file: ' . $path);
|
||||
}
|
||||
|
||||
Console::success('Saved spec file: ' . realpath($path));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli;
|
||||
|
||||
use Appwrite\Event\Certificate;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Validator\Hostname;
|
||||
|
||||
$cli
|
||||
->task('ssl')
|
||||
->desc('Validate server certificates')
|
||||
->param('domain', App::getEnv('_APP_DOMAIN', ''), new Hostname(), 'Domain to generate certificate for. If empty, main domain will be used.', true)
|
||||
->action(function ($domain) {
|
||||
Console::success('Scheduling a job to issue a TLS certificate for domain: ' . $domain);
|
||||
|
||||
(new Certificate())
|
||||
->setDomain(new Document([
|
||||
'domain' => $domain
|
||||
]))
|
||||
->setSkipRenewCheck(true)
|
||||
->trigger();
|
||||
});
|
||||
@@ -1,164 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli, $register;
|
||||
|
||||
use Appwrite\Stats\Usage;
|
||||
use Appwrite\Stats\UsageDB;
|
||||
use InfluxDB\Database as InfluxDatabase;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Logger\Log;
|
||||
|
||||
Authorization::disable();
|
||||
Authorization::setDefaultStatus(false);
|
||||
|
||||
function getDatabase(Registry &$register, string $namespace): Database
|
||||
{
|
||||
$attempts = 0;
|
||||
|
||||
do {
|
||||
try {
|
||||
$attempts++;
|
||||
|
||||
$db = $register->get('db');
|
||||
$redis = $register->get('cache');
|
||||
|
||||
$cache = new Cache(new RedisCache($redis));
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$database->setNamespace($namespace);
|
||||
|
||||
if (!$database->exists($database->getDefaultDatabase(), 'projects')) {
|
||||
throw new Exception('Projects collection not ready');
|
||||
}
|
||||
break; // leave loop if successful
|
||||
} catch (\Exception$e) {
|
||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) {
|
||||
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
||||
}
|
||||
sleep(DATABASE_RECONNECT_SLEEP);
|
||||
}
|
||||
} while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS);
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
function getInfluxDB(Registry &$register): InfluxDatabase
|
||||
{
|
||||
/** @var InfluxDB\Client $client */
|
||||
$client = $register->get('influxdb');
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
do { // check if telegraf database is ready
|
||||
try {
|
||||
$attempts++;
|
||||
$database = $client->selectDB('telegraf');
|
||||
if (in_array('telegraf', $client->listDatabases())) {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} catch (\Throwable$th) {
|
||||
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('InfluxDB database not ready yet');
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
return $database;
|
||||
}
|
||||
|
||||
$logError = function (Throwable $error, string $action = 'syncUsageStats') use ($register) {
|
||||
$logger = $register->get('logger');
|
||||
|
||||
if ($logger) {
|
||||
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$log = new Log();
|
||||
$log->setNamespace("usage");
|
||||
$log->setServer(\gethostname());
|
||||
$log->setVersion($version);
|
||||
$log->setType(Log::TYPE_ERROR);
|
||||
$log->setMessage($error->getMessage());
|
||||
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('verboseType', get_class($error));
|
||||
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Usage stats log pushed with status code: ' . $responseCode);
|
||||
}
|
||||
|
||||
Console::warning("Failed: {$error->getMessage()}");
|
||||
Console::warning($error->getTraceAsString());
|
||||
};
|
||||
|
||||
$cli
|
||||
->task('usage')
|
||||
->desc('Schedules syncing data from influxdb to Appwrite console db')
|
||||
->action(function () use ($register, $logError) {
|
||||
Console::title('Usage Aggregation V1');
|
||||
Console::success(APP_NAME . ' usage aggregation process v1 has started');
|
||||
|
||||
$interval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '30'); // 30 seconds (by default)
|
||||
|
||||
$database = getDatabase($register, '_console');
|
||||
$influxDB = getInfluxDB($register);
|
||||
|
||||
$usage = new Usage($database, $influxDB, $logError);
|
||||
$usageDB = new UsageDB($database, $logError);
|
||||
|
||||
$iterations = 0;
|
||||
Console::loop(function () use ($interval, $usage, $usageDB, &$iterations) {
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregating usage data every {$interval} seconds");
|
||||
|
||||
$loopStart = microtime(true);
|
||||
|
||||
/**
|
||||
* Aggregate InfluxDB every 30 seconds
|
||||
*/
|
||||
$usage->collect();
|
||||
|
||||
if ($iterations % 30 != 0) { // return if 30 iterations has not passed
|
||||
$iterations++;
|
||||
$loopTook = microtime(true) - $loopStart;
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||
return;
|
||||
}
|
||||
|
||||
$iterations = 0; // Reset iterations to prevent overflow when running for long time
|
||||
/**
|
||||
* Aggregate MariaDB every 15 minutes
|
||||
* Some of the queries here might contain full-table scans.
|
||||
*/
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregating database counters.");
|
||||
|
||||
$usageDB->collect();
|
||||
|
||||
$iterations++;
|
||||
$loopTook = microtime(true) - $loopStart;
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
|
||||
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||
}, $interval);
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
global $cli;
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
|
||||
$cli
|
||||
->task('vars')
|
||||
->desc('List all the server environment variables')
|
||||
->action(function () {
|
||||
$config = Config::getParam('variables', []);
|
||||
$vars = [];
|
||||
|
||||
foreach ($config as $category) {
|
||||
foreach ($category['variables'] ?? [] as $var) {
|
||||
$vars[] = $var;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($vars as $key => $value) {
|
||||
Console::log('- ' . $value['name'] . '=' . App::getEnv($value['name'], ''));
|
||||
}
|
||||
});
|
||||
Generated
+1
-1
@@ -5441,5 +5441,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.2.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\CLI;
|
||||
|
||||
use Utopia\Platform\Platform;
|
||||
|
||||
class Tasks extends Platform
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addService('cliTasks', new TasksService());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Docker\Compose;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Certificate;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\CLI\Console;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Config\Config;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Utopia\Platform\Action;
|
||||
use Appwrite\Event\Certificate;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Validator\Text;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Appwrite\Platform\Action;
|
||||
use Throwable;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI\Tasks;
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
@@ -1,10 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
namespace Appwrite\CLI;
|
||||
|
||||
use Utopia\Platform\Service;
|
||||
use Appwrite\CLI\Tasks\Doctor;
|
||||
use Appwrite\CLI\Tasks\Install;
|
||||
use Appwrite\CLI\Tasks\Maintenance;
|
||||
use Appwrite\CLI\Tasks\Migrate;
|
||||
use Appwrite\CLI\Tasks\SDKs;
|
||||
use Appwrite\CLI\Tasks\Specs;
|
||||
use Appwrite\CLI\Tasks\SSL;
|
||||
use Appwrite\CLI\Tasks\Usage;
|
||||
use Appwrite\CLI\Tasks\Vars;
|
||||
use Appwrite\CLI\Tasks\Version;
|
||||
|
||||
class Tasks extends Service
|
||||
class TasksService extends Service
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Task;
|
||||
|
||||
use Utopia\Platform\Platform;
|
||||
|
||||
class CLIPlatform extends Platform
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addService('cliTasks', new Tasks());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user