commit
d2945ed223
5 changed files with 242 additions and 1 deletions
|
@ -13,6 +13,7 @@
|
||||||
- New UI micro-interactions and styles fixes (@AnatoleLucet)
|
- New UI micro-interactions and styles fixes (@AnatoleLucet)
|
||||||
- UI performance & accessibility improvments
|
- UI performance & accessibility improvments
|
||||||
- Updated ClamAV conntainer to version 1.0.9
|
- Updated ClamAV conntainer to version 1.0.9
|
||||||
|
- New Doctor CLI to debug the Appwrite server ([#415](https://github.com/appwrite/appwrite/issues/415))
|
||||||
- All emails are now sent asynchronously for improved performance (@TorstenDittmann)
|
- All emails are now sent asynchronously for improved performance (@TorstenDittmann)
|
||||||
- Updated grid for OAuth2 providers list in the console
|
- Updated grid for OAuth2 providers list in the console
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,7 @@ COPY ./docker/supervisord.conf /etc/supervisord.conf
|
||||||
|
|
||||||
# Executables
|
# Executables
|
||||||
RUN chmod +x /usr/local/bin/start
|
RUN chmod +x /usr/local/bin/start
|
||||||
|
RUN chmod +x /usr/local/bin/doctor
|
||||||
RUN chmod +x /usr/local/bin/migrate
|
RUN chmod +x /usr/local/bin/migrate
|
||||||
RUN chmod +x /usr/local/bin/test
|
RUN chmod +x /usr/local/bin/test
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,15 @@ $utopia->get('/v1/health')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$utopia->get('/v1/health/version')
|
||||||
|
->desc('Get Version')
|
||||||
|
->label('scope', 'public')
|
||||||
|
->action(
|
||||||
|
function () use ($response) {
|
||||||
|
$response->json(['version' => APP_VERSION_STABLE]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$utopia->get('/v1/health/db')
|
$utopia->get('/v1/health/db')
|
||||||
->desc('Get DB')
|
->desc('Get DB')
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
|
@ -124,7 +133,7 @@ $utopia->get('/v1/health/queue/tasks')
|
||||||
);
|
);
|
||||||
|
|
||||||
$utopia->get('/v1/health/queue/logs')
|
$utopia->get('/v1/health/queue/logs')
|
||||||
->desc('Get Logs Queue')
|
->desc('Get Logs Queue')
|
||||||
->label('scope', 'health.read')
|
->label('scope', 'health.read')
|
||||||
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
->label('sdk.platform', [APP_PLATFORM_SERVER])
|
||||||
->label('sdk.namespace', 'health')
|
->label('sdk.namespace', 'health')
|
||||||
|
|
|
@ -5,8 +5,12 @@ require_once __DIR__.'/../init.php';
|
||||||
|
|
||||||
global $request;
|
global $request;
|
||||||
|
|
||||||
|
use Appwrite\ClamAV\Network;
|
||||||
|
use Appwrite\Storage\Device\Local;
|
||||||
|
use Appwrite\Storage\Storage;
|
||||||
use Utopia\CLI\CLI;
|
use Utopia\CLI\CLI;
|
||||||
use Utopia\CLI\Console;
|
use Utopia\CLI\Console;
|
||||||
|
use Utopia\Domains\Domain;
|
||||||
|
|
||||||
$cli = new CLI();
|
$cli = new CLI();
|
||||||
|
|
||||||
|
@ -26,4 +30,227 @@ $cli
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$cli
|
||||||
|
->task('doctor')
|
||||||
|
->desc('Validate server health')
|
||||||
|
->action(function () use ($request, $register) {
|
||||||
|
Console::log(" __ ____ ____ _ _ ____ __ ____ ____ __ __
|
||||||
|
/ _\ ( _ \( _ \/ )( \( _ \( )(_ _)( __) ( )/ \
|
||||||
|
/ \ ) __/ ) __/\ /\ / ) / )( )( ) _) _ )(( O )
|
||||||
|
\_/\_/(__) (__) (_/\_)(__\_)(__) (__) (____)(_)(__)\__/ ");
|
||||||
|
|
||||||
|
Console::log("\n".'👩⚕️ Running '.APP_NAME.' Doctor for version '.$request->getServer('_APP_VERSION', 'UNKNOWN').' ...'."\n");
|
||||||
|
|
||||||
|
Console::log('Checking for production best practices...');
|
||||||
|
|
||||||
|
$domain = new Domain($request->getServer('_APP_DOMAIN'));
|
||||||
|
|
||||||
|
if(!$domain->isKnown() || $domain->isTest()) {
|
||||||
|
Console::log('🔴 Hostname has a public suffix');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console::log('🟢 Hostname has a public suffix');
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain = new Domain($request->getServer('_APP_DOMAIN_TARGET'));
|
||||||
|
|
||||||
|
if(!$domain->isKnown() || $domain->isTest()) {
|
||||||
|
Console::log('🔴 CNAME target has a public suffix');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console::log('🟢 CNAME target has a public suffix');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($request->getServer('_APP_OPENSSL_KEY_V1', 'your-secret-key') === 'your-secret-key') {
|
||||||
|
Console::log('🔴 Using a unique secret key for encryption');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console::log('🟢 Using a unique secret key for encryption');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($request->getServer('_APP_ENV', 'development') === 'development') {
|
||||||
|
Console::log('🔴 App enviornment is set for production');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console::log('🟢 App enviornment is set for production');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($request->getServer('_APP_OPTIONS_ABUSE', 'disabled') === 'disabled') {
|
||||||
|
Console::log('🔴 Abuse protection is enabled');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console::log('🟢 Abuse protection is enabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
$authWhitelistEmails = $request->getServer('_APP_CONSOLE_WHITELIST_EMAILS', null);
|
||||||
|
$authWhitelistIPs = $request->getServer('_APP_CONSOLE_WHITELIST_IPS', null);
|
||||||
|
$authWhitelistDomains = $request->getServer('_APP_CONSOLE_WHITELIST_DOMAINS', null);
|
||||||
|
|
||||||
|
if(empty($authWhitelistEmails)
|
||||||
|
&& empty($authWhitelistDomains)
|
||||||
|
&& empty($authWhitelistIPs)
|
||||||
|
) {
|
||||||
|
Console::log('🔴 Console access limits are disabled');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console::log('🟢 Console access limits are enabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($request->getServer('_APP_OPTIONS_FORCE_HTTPS', null))) {
|
||||||
|
Console::log('🔴 HTTP force option is disabled');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console::log('🟢 HTTP force option is enabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
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($request->getServer('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
|
||||||
|
$antiVirus = new Network('clamav', 3310);
|
||||||
|
|
||||||
|
if((@$antiVirus->ping())) {
|
||||||
|
Console::success('AntiVirus...........connected 👍');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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 = $request->getServer('_APP_STATSD_HOST', 'telegraf');
|
||||||
|
$port = $request->getServer('_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 = $request->getServer('_APP_INFLUXDB_HOST', '');
|
||||||
|
$port = $request->getServer('_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 {
|
||||||
|
Console::log('');
|
||||||
|
$version = json_decode(@file_get_contents($request->getServer('_APP_HOME', 'http://localhost').'/v1/health/version'), true);
|
||||||
|
|
||||||
|
if($version && isset($version['version'])) {
|
||||||
|
if(version_compare($version['version'], $request->getServer('_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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$cli->run();
|
$cli->run();
|
||||||
|
|
3
bin/doctor
Executable file
3
bin/doctor
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
php /usr/share/nginx/html/app/tasks/init.php doctor
|
Loading…
Reference in a new issue