diff --git a/public/mix-manifest.json b/public/mix-manifest.json index db6c374c..76039f1a 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -67,5 +67,6 @@ "/js/chunks/shared-with-me.js": "/js/chunks/shared-with-me.js", "/js/chunks/invitation.js": "/js/chunks/invitation.js", "/css/tailwind.css": "/css/tailwind.css", - "/css/app.css": "/css/app.css" + "/css/app.css": "/css/app.css", + "/js/chunks/app-server.js": "/js/chunks/app-server.js" } diff --git a/resources/js/routes/routesAdmin.js b/resources/js/routes/routesAdmin.js index 57cd3432..9616f785 100644 --- a/resources/js/routes/routesAdmin.js +++ b/resources/js/routes/routesAdmin.js @@ -321,6 +321,15 @@ const routesAdmin = [ title: 'Adsense', }, }, + { + name: 'AppServer', + path: '/admin/settings/server', + component: () => import(/* webpackChunkName: "chunks/app-server" */ '../views/Admin/AppSettings/AppSettingsTabs/Server'), + meta: { + requiresAuth: true, + title: 'Server', + }, + }, ], }, { diff --git a/resources/js/views/Admin/AppSettings/AppSettings.vue b/resources/js/views/Admin/AppSettings/AppSettings.vue index b7e67c1b..d037df80 100644 --- a/resources/js/views/Admin/AppSettings/AppSettings.vue +++ b/resources/js/views/Admin/AppSettings/AppSettings.vue @@ -45,6 +45,10 @@ export default { title: this.$t('admin_settings.tabs.email'), route: 'AppEmail', }, + { + title: this.$t('Server'), + route: 'AppServer', + }, ], } }, diff --git a/resources/js/views/Admin/AppSettings/AppSettingsTabs/Server.vue b/resources/js/views/Admin/AppSettings/AppSettingsTabs/Server.vue new file mode 100644 index 00000000..ff529ba9 --- /dev/null +++ b/resources/js/views/Admin/AppSettings/AppSettingsTabs/Server.vue @@ -0,0 +1,165 @@ + + + diff --git a/resources/js/views/Admin/Dashboard.vue b/resources/js/views/Admin/Dashboard.vue index 1fa5d146..3f88d255 100644 --- a/resources/js/views/Admin/Dashboard.vue +++ b/resources/js/views/Admin/Dashboard.vue @@ -37,11 +37,18 @@ +

As you installed app with metered subscription type, you have to create your plan as soon as possible to prevent new user registration without automatically assigned subscription plan.

+ +
+ +

We detect your cron jobs probably doesn't work correctly, please check it, you need it for running app correctly. If you set your cron job, please get back one minute later.

+
+
diff --git a/resources/js/views/SetupWizard/StatusCheck.vue b/resources/js/views/SetupWizard/StatusCheck.vue index 54e1246c..b2ddb53f 100644 --- a/resources/js/views/SetupWizard/StatusCheck.vue +++ b/resources/js/views/SetupWizard/StatusCheck.vue @@ -10,31 +10,6 @@ - -
- - Required PHP Extensions - - - -

Those PHP modules are needed for accurate running VueFileManager on your server, please check and install if some is missing.

-
- -
- - {{ module }} - -
- - - - - {{ value ? 'Module Installed' : 'Missing Module' }} - -
-
-
-
@@ -80,6 +55,31 @@
+ +
+ + Required PHP Extensions + + + +

Those PHP modules are needed for accurate running VueFileManager on your server, please check and install if some is missing.

+
+ +
+ + {{ module }} + +
+ + + + + {{ value ? 'Module Installed' : 'Missing Module' }} + +
+
+
+
@@ -126,12 +126,11 @@ import FormLabel from '../../components/Others/Forms/FormLabel' import InfoBox from '../../components/Others/Forms/InfoBox' import AuthContent from '../../components/Auth/AuthContent' import AuthButton from '../../components/Auth/AuthButton' -import { SettingsIcon } from 'vue-feather-icons' import { required } from 'vee-validate/dist/rules' import Headline from '../Auth/Headline' import { mapGetters } from 'vuex' import axios from 'axios' -import { CheckIcon, XIcon } from 'vue-feather-icons' +import { CheckIcon, XIcon, SettingsIcon } from 'vue-feather-icons' export default { name: 'StatusCheck', @@ -187,11 +186,7 @@ export default { axios .get('/api/ping') .then((response) => { - if (response.data === 'pong') { - this.apiRunning = true - } else { - this.apiRunning = false - } + this.apiRunning = response.data === 'pong'; }) .catch(() => { this.apiRunning = false diff --git a/routes/admin.php b/routes/admin.php index f125132c..748893d0 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -2,6 +2,7 @@ use Domain\Admin\Controllers\Users\UserController; use Domain\Pages\Controllers\AdminPagesController; +use Domain\Settings\Controllers\GetServerStatusController; use Domain\Settings\Controllers\StoreEmailCredentialsController; use Domain\Settings\Controllers\FlushCacheController; use Domain\Localization\Controllers\LanguageController; @@ -57,3 +58,6 @@ Route::group(['prefix' => 'settings'], function () { // Language Route::patch('/languages/{language}/strings', UpdateLanguageStringController::class); Route::apiResource('/languages', LanguageController::class); + +// Server Status +Route::get('/status', GetServerStatusController::class); diff --git a/src/App/Console/Kernel.php b/src/App/Console/Kernel.php index 31878905..8550bfa1 100644 --- a/src/App/Console/Kernel.php +++ b/src/App/Console/Kernel.php @@ -1,6 +1,7 @@ command('backup:clean') ->daily() ->at('00:15'); + $schedule->command('backup:run --only-db') ->daily() ->at('00:20'); + + // Store latest cron timestamp + cache()->set('latest_cron_update', now()->toString()); } /** diff --git a/src/Domain/Admin/Controllers/Dashboard/GetDashboardDataController.php b/src/Domain/Admin/Controllers/Dashboard/GetDashboardDataController.php index 76705996..738dae54 100644 --- a/src/Domain/Admin/Controllers/Dashboard/GetDashboardDataController.php +++ b/src/Domain/Admin/Controllers/Dashboard/GetDashboardDataController.php @@ -39,10 +39,11 @@ class GetDashboardDataController extends Controller 'records' => $upload, ], ], - 'app' => [ - 'license' => get_settings('license'), - 'version' => config('vuefilemanager.version'), - 'earnings' => format_currency($totalEarnings, 'USD'), // todo: refactor currency to global setup + 'app' => [ + 'isRunningCron' => isRunningCron(), + 'license' => get_settings('license'), + 'version' => config('vuefilemanager.version'), + 'earnings' => format_currency($totalEarnings, 'USD'), // todo: refactor currency to global setup or plan currency ], ]); } diff --git a/src/Domain/Homepage/Controllers/IndexController.php b/src/Domain/Homepage/Controllers/IndexController.php index 3ee570a6..aa036186 100644 --- a/src/Domain/Homepage/Controllers/IndexController.php +++ b/src/Domain/Homepage/Controllers/IndexController.php @@ -7,9 +7,14 @@ use Domain\Pages\Models\Page; use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\Foundation\Application; +use Support\Status\Actions\GetServerSetupStatusAction; class IndexController { + public function __construct( + public GetServerSetupStatusAction $getServerSetupStatus, + ) {} + /** * Show index page */ @@ -32,58 +37,7 @@ class IndexController } catch (PDOException $e) {} if ($setup_status === 'installation-needed') { - - // Required parameters - $upload_max_filesize = 128; - $post_max_size = 128; - $memory_limit = 512; - $max_execution_time = 600; - $php_version = '8.0'; - - $status_check = [ - 'modules' => [ - 'tokenizer' => extension_loaded('tokenizer'), - 'fileinfo' => extension_loaded('fileinfo'), - 'mbstring' => extension_loaded('mbstring'), - 'openssl' => extension_loaded('openssl'), - 'sqlite3' => extension_loaded('sqlite3'), - 'bcmath' => extension_loaded('bcmath'), - 'ctype' => extension_loaded('ctype'), - 'json' => extension_loaded('json'), - 'exif' => extension_loaded('exif'), - 'intl' => extension_loaded('intl'), - 'pdo' => extension_loaded('pdo'), - 'xml' => extension_loaded('xml'), - 'gd' => extension_loaded('gd'), - ], - 'ini' => [ - 'upload_max_filesize' => [ - 'current' => intval(ini_get('upload_max_filesize')), - 'minimal' => $upload_max_filesize, - 'status' => intval(ini_get('upload_max_filesize')) >= $upload_max_filesize, - ], - 'post_max_size' => [ - 'current' => intval(ini_get('post_max_size')), - 'minimal' => $post_max_size, - 'status' => intval(ini_get('post_max_size')) >= $post_max_size, - ], - 'memory_limit' => [ - 'current' => intval(ini_get('memory_limit')), - 'minimal' => $memory_limit, - 'status' => intval(ini_get('memory_limit')) >= $memory_limit, - ], - 'max_execution_time' => [ - 'current' => intval(ini_get('max_execution_time')), - 'minimal' => $max_execution_time, - 'status' => intval(ini_get('max_execution_time')) >= $max_execution_time, - ], - ], - 'php_version' => [ - 'acceptable' => version_compare(PHP_VERSION, $php_version, '>='), - 'current' => phpversion(), - 'minimal' => $php_version, - ], - ]; + $status_check = ($this->getServerSetupStatus)(); } return view('index') diff --git a/src/Domain/Settings/Controllers/GetServerStatusController.php b/src/Domain/Settings/Controllers/GetServerStatusController.php new file mode 100644 index 00000000..86e1b17d --- /dev/null +++ b/src/Domain/Settings/Controllers/GetServerStatusController.php @@ -0,0 +1,27 @@ +getServerSetupStatus)(); + + // Add cron info + $status['cron'] = [ + 'running' => isRunningCron(), + 'lastUpdate' => isRunningCron() ? format_date(cache()->get('latest_cron_update')) : '', + ]; + + return $status; + } +} \ No newline at end of file diff --git a/src/Support/Status/Actions/GetServerSetupStatusAction.php b/src/Support/Status/Actions/GetServerSetupStatusAction.php new file mode 100644 index 00000000..7f67f3eb --- /dev/null +++ b/src/Support/Status/Actions/GetServerSetupStatusAction.php @@ -0,0 +1,61 @@ + [ + 'tokenizer' => extension_loaded('tokenizer'), + 'fileinfo' => extension_loaded('fileinfo'), + 'mbstring' => extension_loaded('mbstring'), + 'openssl' => extension_loaded('openssl'), + 'sqlite3' => extension_loaded('sqlite3'), + 'bcmath' => extension_loaded('bcmath'), + 'ctype' => extension_loaded('ctype'), + 'json' => extension_loaded('json'), + 'exif' => extension_loaded('exif'), + 'intl' => extension_loaded('intl'), + 'pdo' => extension_loaded('pdo'), + 'xml' => extension_loaded('xml'), + 'gd' => extension_loaded('gd'), + ], + 'ini' => [ + 'upload_max_filesize' => [ + 'current' => intval(ini_get('upload_max_filesize')), + 'minimal' => $upload_max_filesize, + 'status' => intval(ini_get('upload_max_filesize')) >= $upload_max_filesize, + ], + 'post_max_size' => [ + 'current' => intval(ini_get('post_max_size')), + 'minimal' => $post_max_size, + 'status' => intval(ini_get('post_max_size')) >= $post_max_size, + ], + 'memory_limit' => [ + 'current' => intval(ini_get('memory_limit')), + 'minimal' => $memory_limit, + 'status' => intval(ini_get('memory_limit')) >= $memory_limit, + ], + 'max_execution_time' => [ + 'current' => intval(ini_get('max_execution_time')), + 'minimal' => $max_execution_time, + 'status' => intval(ini_get('max_execution_time')) >= $max_execution_time, + ], + ], + 'php_version' => [ + 'acceptable' => version_compare(PHP_VERSION, $php_version, '>='), + 'current' => phpversion(), + 'minimal' => $php_version, + ], + ]; + } +} \ No newline at end of file diff --git a/src/Support/helpers.php b/src/Support/helpers.php index 8786e4d0..69ed3d85 100644 --- a/src/Support/helpers.php +++ b/src/Support/helpers.php @@ -17,6 +17,16 @@ use Domain\Localization\Models\Language; use Intervention\Image\ImageManagerStatic as Image; use Illuminate\Database\Eloquent\ModelNotFoundException; +if (! function_exists('isRunningCron')) { + /** + * Check if cron is running + */ + function isRunningCron(): bool + { + return cache()->has('latest_cron_update') && Carbon::parse(cache()->get('latest_cron_update'))->diffInMinutes(now()) < 5; + } +} + if (! function_exists('obfuscate_email')) { /** * Obfuscate email diff --git a/tests/Domain/Admin/AdminTest.php b/tests/Domain/Admin/AdminTest.php index b82c1e2d..1179905c 100644 --- a/tests/Domain/Admin/AdminTest.php +++ b/tests/Domain/Admin/AdminTest.php @@ -14,6 +14,21 @@ use App\Users\Notifications\ResetPassword; class AdminTest extends TestCase { + /** + * @test + */ + public function it_get_server_status() + { + $admin = User::factory() + ->hasSettings() + ->create(['role' => 'admin']); + + $this + ->actingAs($admin) + ->getJson("/api/admin/status") + ->assertOk(); + } + /** * @test */ @@ -53,7 +68,6 @@ class AdminTest extends TestCase ->hasSettings() ->create(['role' => 'admin']); - // TODO: pridat exactjson po refaktorovani userresource $this ->actingAs($admin) ->getJson("/api/admin/users/$user->id")