diff --git a/app/Classes/PterodactylClient.php b/app/Classes/PterodactylClient.php index a94fca74..8f1b116c 100644 --- a/app/Classes/PterodactylClient.php +++ b/app/Classes/PterodactylClient.php @@ -24,19 +24,18 @@ class PterodactylClient public PendingRequest $client; - public PendingRequest $client_admin; - + public PendingRequest $application; + public function __construct(PterodactylSettings $ptero_settings) { $server_settings = new ServerSettings(); - + try { $this->client = $this->client($ptero_settings); - $this->client_admin = $this->clientAdmin($ptero_settings); + $this->application = $this->clientAdmin($ptero_settings); $this->per_page_limit = $ptero_settings->per_page_limit; $this->allocation_limit = $server_settings->allocation_limit; - } - catch (Exception $exception) { + } catch (Exception $exception) { logger('Failed to construct Pterodactyl client, Settings table not available?', ['exception' => $exception]); } } @@ -94,7 +93,7 @@ class PterodactylClient public function getEggs(Nest $nest) { try { - $response = $this->client_admin->get("application/nests/{$nest->id}/eggs?include=nest,variables&per_page=" . $this->per_page_limit); + $response = $this->application->get("application/nests/{$nest->id}/eggs?include=nest,variables&per_page=" . $this->per_page_limit); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -113,7 +112,7 @@ class PterodactylClient public function getNodes() { try { - $response = $this->client_admin->get('application/nodes?per_page=' . $this->per_page_limit); + $response = $this->application->get('application/nodes?per_page=' . $this->per_page_limit); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -133,7 +132,7 @@ class PterodactylClient public function getNode($id) { try { - $response = $this->client_admin->get('application/nodes/' . $id); + $response = $this->application->get('application/nodes/' . $id); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -147,7 +146,7 @@ class PterodactylClient public function getServers() { try { - $response = $this->client_admin->get('application/servers?per_page=' . $this->per_page_limit); + $response = $this->application->get('application/servers?per_page=' . $this->per_page_limit); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -166,7 +165,7 @@ class PterodactylClient public function getNests() { try { - $response = $this->client_admin->get('application/nests?per_page=' . $this->per_page_limit); + $response = $this->application->get('application/nests?per_page=' . $this->per_page_limit); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -185,7 +184,7 @@ class PterodactylClient public function getLocations() { try { - $response = $this->client_admin->get('application/locations?per_page=' . $this->per_page_limit); + $response = $this->application->get('application/locations?per_page=' . $this->per_page_limit); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -240,7 +239,7 @@ class PterodactylClient public function getAllocations(Node $node) { try { - $response = $this->client_admin->get("application/nodes/{$node->id}/allocations?per_page={$this->allocation_limit}"); + $response = $this->application->get("application/nodes/{$node->id}/allocations?per_page={$this->allocation_limit}"); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -259,7 +258,7 @@ class PterodactylClient */ public function createServer(Server $server, Egg $egg, int $allocationId) { - return $this->client_admin->post('application/servers', [ + return $this->application->post('application/servers', [ 'name' => $server->name, 'external_id' => $server->id, 'user' => $server->user->pterodactyl_id, @@ -288,7 +287,7 @@ class PterodactylClient public function suspendServer(Server $server) { try { - $response = $this->client_admin->post("application/servers/$server->pterodactyl_id/suspend"); + $response = $this->application->post("application/servers/$server->pterodactyl_id/suspend"); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -302,7 +301,7 @@ class PterodactylClient public function unSuspendServer(Server $server) { try { - $response = $this->client_admin->post("application/servers/$server->pterodactyl_id/unsuspend"); + $response = $this->application->post("application/servers/$server->pterodactyl_id/unsuspend"); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -322,7 +321,7 @@ class PterodactylClient public function getUser(int $pterodactylId) { try { - $response = $this->client_admin->get("application/users/{$pterodactylId}"); + $response = $this->application->get("application/users/{$pterodactylId}"); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -342,7 +341,7 @@ class PterodactylClient public function getServerAttributes(int $pterodactylId, bool $deleteOn404 = false) { try { - $response = $this->client_admin->get("application/servers/{$pterodactylId}?include=egg,node,nest,location"); + $response = $this->application->get("application/servers/{$pterodactylId}?include=egg,node,nest,location"); } catch (Exception $e) { throw self::getException($e->getMessage()); } @@ -371,7 +370,7 @@ class PterodactylClient */ public function updateServer(Server $server, Product $product) { - return $this->client_admin->patch("application/servers/{$server->pterodactyl_id}/build", [ + return $this->application->patch("application/servers/{$server->pterodactyl_id}/build", [ 'allocation' => $server->allocation, 'memory' => $product->memory, 'swap' => $product->swap, @@ -396,7 +395,7 @@ class PterodactylClient */ public function updateServerOwner(Server $server, int $userId) { - return $this->client_admin->patch("application/servers/{$server->pterodactyl_id}/details", [ + return $this->application->patch("application/servers/{$server->pterodactyl_id}/details", [ 'name' => $server->name, 'user' => $userId, ]); @@ -435,7 +434,7 @@ class PterodactylClient public function checkNodeResources(Node $node, int $requireMemory, int $requireDisk) { try { - $response = $this->client_admin->get("application/nodes/{$node->id}"); + $response = $this->application->get("application/nodes/{$node->id}"); } catch (Exception $e) { throw self::getException($e->getMessage()); } diff --git a/app/Http/Controllers/Admin/SettingsController.php b/app/Http/Controllers/Admin/SettingsController.php index 6a7527bb..7e1c5457 100644 --- a/app/Http/Controllers/Admin/SettingsController.php +++ b/app/Http/Controllers/Admin/SettingsController.php @@ -109,26 +109,18 @@ class SettingsController extends Controller $settingsClass = new $settings_class(); foreach ($settingsClass->toArray() as $key => $value) { - switch (gettype($request->input($key))) { - case 'boolean': - $settingsClass->$key = $request->has($key); - break; - case 'string': - $settingsClass->$key = $request->input($key) ?? ''; - break; - case 'integer': - $settingsClass->$key = $request->input($key) ?? 0; - break; - case 'array': - $settingsClass->$key = $request->input($key) ?? []; - break; - case 'double': - $settingsClass->$key = $request->input($key) ?? 0.0; - break; - case 'NULL': - $settingsClass->$key = null; - break; + // Get the type of the settingsclass property + $rp = new \ReflectionProperty($settingsClass, $key); + $rpType = $rp->getType(); + + if ($rpType == 'bool') { + $settingsClass->$key = $request->has($key); + continue; } + + $nullable = $rpType->allowsNull(); + if ($nullable) $settingsClass->$key = $request->input($key) ?? null; + else $settingsClass->$key = $request->input($key); } $settingsClass->save(); diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index eafc351d..4dfcb219 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -35,7 +35,7 @@ class UserController extends Controller { $this->pterodactyl = new PterodactylClient($ptero_settings); } - + /** * Display a listing of the resource. * @@ -166,6 +166,10 @@ class UserController extends Controller */ public function destroy(User $user) { + if ($user->role === 'admin' && User::query()->where('role', 'admin')->count() === 1) { + return redirect()->back()->with('error', __('You can not delete the last admin!')); + } + $user->delete(); return redirect()->back()->with('success', __('user has been removed!')); @@ -258,8 +262,7 @@ class UserController extends Controller $users = $all ? User::all() : User::whereIn('id', $data['users'])->get(); try { Notification::send($users, new DynamicNotification($data['via'], $database, $mail)); - } - catch (Exception $e) { + } catch (Exception $e) { return redirect()->route('admin.users.notifications')->with('error', __('The attempt to send the email failed with the error: ' . $e->getMessage())); } diff --git a/app/Http/Controllers/Api/UserController.php b/app/Http/Controllers/Api/UserController.php index 797ae7b7..1a6ef3eb 100644 --- a/app/Http/Controllers/Api/UserController.php +++ b/app/Http/Controllers/Api/UserController.php @@ -43,7 +43,7 @@ class UserController extends Controller { $this->pterodactyl = new PterodactylClient($ptero_settings); } - + /** * Display a listing of the resource. * @@ -105,7 +105,7 @@ class UserController extends Controller //Update Users Password on Pterodactyl //Username,Mail,First and Lastname are required aswell - $response = $this->pterodactyl->client_admin->patch('/application/users/' . $user->pterodactyl_id, [ + $response = $this->pterodactyl->application->patch('/application/users/' . $user->pterodactyl_id, [ 'username' => $request->name, 'first_name' => $request->name, 'last_name' => $request->name, @@ -280,7 +280,7 @@ class UserController extends Controller 'referral_code' => $this->createReferralCode(), ]); - $response = $this->pterodactyl->client_admin->post('/application/users', [ + $response = $this->pterodactyl->application->post('/application/users', [ 'external_id' => App::environment('local') ? Str::random(16) : (string) $user->id, 'username' => $user->name, 'email' => $user->email, diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index fc27b7b5..29a4b6e0 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -135,11 +135,12 @@ class RegisterController extends Controller 'server_limit' => $this->initial_server_limit, 'password' => Hash::make($data['password']), 'referral_code' => $this->createReferralCode(), + 'pterodactyl_id' => Str::uuid(), ]); - $response = $this->pterodactyl->client_admin->post('/application/users', [ - 'external_id' => App::environment('local') ? Str::random(16) : (string) $user->id, + $response = $this->pterodactyl->application->post('/application/users', [ + 'external_id' => $user->pterodactyl_id, 'username' => $user->name, 'email' => $user->email, 'first_name' => $user->name, @@ -157,9 +158,8 @@ class RegisterController extends Controller ]); } - $user->update([ - 'pterodactyl_id' => $response->json()['attributes']['id'], - ]); + // delete activity log for user creation where description = 'created' or 'deleted' and subject_id = user_id + DB::table('activity_log')->where('description', 'created')->orWhere('description', 'deleted')->where('subject_id', $user->id)->delete(); //INCREMENT REFERRAL-USER CREDITS if (!empty($data['referral_code'])) { diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index ea6ffcf6..b3156f96 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -78,15 +78,15 @@ class ProfileController extends Controller $user = User::findOrFail($id); //update password if necessary - if (! is_null($request->input('new_password'))) { + if (!is_null($request->input('new_password'))) { //validate password request $request->validate([ 'current_password' => [ 'required', function ($attribute, $value, $fail) use ($user) { - if (! Hash::check($value, $user->password)) { - $fail('The '.$attribute.' is invalid.'); + if (!Hash::check($value, $user->password)) { + $fail('The ' . $attribute . ' is invalid.'); } }, ], @@ -96,7 +96,7 @@ class ProfileController extends Controller //Update Users Password on Pterodactyl //Username,Mail,First and Lastname are required aswell - $response = $this->pterodactyl->client_admin->patch('/application/users/' . $user->pterodactyl_id, [ + $response = $this->pterodactyl->application->patch('/application/users/' . $user->pterodactyl_id, [ 'password' => $request->input('new_password'), 'username' => $request->input('name'), 'first_name' => $request->input('name'), @@ -118,13 +118,13 @@ class ProfileController extends Controller //validate request $request->validate([ - 'name' => 'required|min:4|max:30|alpha_num|unique:users,name,'.$id.',id', - 'email' => 'required|email|max:64|unique:users,email,'.$id.',id', + 'name' => 'required|min:4|max:30|alpha_num|unique:users,name,' . $id . ',id', + 'email' => 'required|email|max:64|unique:users,email,' . $id . ',id', 'avatar' => 'nullable', ]); //update avatar - if (! is_null($request->input('avatar'))) { + if (!is_null($request->input('avatar'))) { $avatar = json_decode($request->input('avatar')); if ($avatar->input->size > 3000000) { abort(500); @@ -140,7 +140,7 @@ class ProfileController extends Controller } //update name and email on Pterodactyl - $response = $this->pterodactyl->client_admin->patch('/application/users/' . $user->pterodactyl_id, [ + $response = $this->pterodactyl->application->patch('/application/users/' . $user->pterodactyl_id, [ 'username' => $request->input('name'), 'first_name' => $request->input('name'), 'last_name' => $request->input('name'), diff --git a/app/Models/Server.php b/app/Models/Server.php index ca7761c3..21d2c245 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -28,9 +28,9 @@ class Server extends Model public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() - -> logOnlyDirty() - -> logOnly(['*']) - -> dontSubmitEmptyLogs(); + ->logOnlyDirty() + ->logOnly(['*']) + ->dontSubmitEmptyLogs(); } /** @@ -84,8 +84,8 @@ class Server extends Model }); static::deleting(function (Server $server) { - $response = $server->pterodactyl->client_admin->delete("/application/servers/{$server->pterodactyl_id}"); - if ($response->failed() && ! is_null($server->pterodactyl_id)) { + $response = $server->pterodactyl->application->delete("/application/servers/{$server->pterodactyl_id}"); + if ($response->failed() && !is_null($server->pterodactyl_id)) { //only return error when it's not a 404 error if ($response['errors'][0]['status'] != '404') { throw new Exception($response['errors'][0]['code']); @@ -99,7 +99,7 @@ class Server extends Model */ public function isSuspended() { - return ! is_null($this->suspended); + return !is_null($this->suspended); } /** @@ -107,7 +107,7 @@ class Server extends Model */ public function getPterodactylServer() { - return $this->pterodactyl->client_admin->get("/application/servers/{$this->pterodactyl_id}"); + return $this->pterodactyl->application->get("/application/servers/{$this->pterodactyl_id}"); } /** diff --git a/app/Models/User.php b/app/Models/User.php index 2b510e45..9584bd3d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -122,7 +122,7 @@ class User extends Authenticatable implements MustVerifyEmail $user->discordUser()->delete(); - $user->pterodactyl->client_admin->delete("/application/users/{$user->pterodactyl_id}"); + $user->pterodactyl->application->delete("/application/users/{$user->pterodactyl_id}"); }); } @@ -195,9 +195,6 @@ class User extends Authenticatable implements MustVerifyEmail return $this->suspended; } - /** - * @throws Exception - */ public function suspend() { foreach ($this->servers as $server) { @@ -211,9 +208,6 @@ class User extends Authenticatable implements MustVerifyEmail return $this; } - /** - * @throws Exception - */ public function unSuspend() { foreach ($this->getServersWithProduct() as $server) { @@ -241,23 +235,9 @@ class User extends Authenticatable implements MustVerifyEmail */ public function getAvatar() { - //TODO loading the images to confirm they exist is causing to much load time. alternative has to be found :) maybe onerror tag on the - // if ($this->discordUser()->exists()) { - // if(@getimagesize($this->discordUser->getAvatar())) { - // $avatar = $this->discordUser->getAvatar(); - // } else { - // $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email))); - // } - // } else { - // $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email))); - // } - return 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($this->email))); } - /** - * @return string - */ public function creditUsage() { $usage = 0; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 97a269c0..aad430c6 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -9,6 +9,7 @@ use Exception; use Illuminate\Pagination\Paginator; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\Validator; use Illuminate\Support\ServiceProvider; @@ -54,6 +55,14 @@ class AppServiceProvider extends ServiceProvider return $ok; }); + // Force HTTPS if APP_URL is set to https + if (config('app.url') && parse_url(config('app.url'), PHP_URL_SCHEME) === 'https') { + URL::forceScheme('https'); + } + + // Do not run this code if no APP_KEY is set + if (config('app.key') == null) return; + try { if (Schema::hasColumn('useful_links', 'position')) { $useful_links = UsefulLink::where("position", "like", "%topbar%")->get()->sortby("id"); @@ -63,6 +72,7 @@ class AppServiceProvider extends ServiceProvider Log::error("Couldnt find useful_links. Probably the installation is not completet. " . $e); } + $settings = $this->app->make(MailSettings::class); $settings->setConfig(); } diff --git a/app/Settings/DiscordSettings.php b/app/Settings/DiscordSettings.php index ecb35c07..8671fa8a 100644 --- a/app/Settings/DiscordSettings.php +++ b/app/Settings/DiscordSettings.php @@ -7,15 +7,10 @@ use Spatie\LaravelSettings\Settings; class DiscordSettings extends Settings { public ?string $bot_token; - public ?string $client_id; - public ?string $client_secret; - public ?string $guild_id; - public ?string $invite_url; - public ?string $role_id; public static function group(): string diff --git a/app/Settings/GeneralSettings.php b/app/Settings/GeneralSettings.php index 906e4696..02f8767f 100644 --- a/app/Settings/GeneralSettings.php +++ b/app/Settings/GeneralSettings.php @@ -6,7 +6,7 @@ use Spatie\LaravelSettings\Settings; class GeneralSettings extends Settings { - public bool $store_enabled = true; + public bool $store_enabled; public string $credits_display_name; public bool $recaptcha_enabled; public string $recaptcha_site_key; @@ -32,6 +32,26 @@ class GeneralSettings extends Settings ]; } + /** + * Summary of validations array + * @return array + */ + public static function getValidations() + { + return [ + 'store_enabled' => 'boolean', + 'credits_display_name' => 'required|string', + 'recaptcha_enabled' => 'nullable|boolean', + 'recaptcha_site_key' => 'nullable|string', + 'recaptcha_secret_key' => 'nullable|string', + 'phpmyadmin_url' => 'nullable|string', + 'alert_enabled' => 'nullable|boolean', + 'alert_type' => 'required|in:primary,secondary,success,danger,warning,info', + 'alert_message' => 'required|string', + 'theme' => 'required|in:default,BlueInfinity' // TODO: themes should be made/loaded dynamically + ]; + } + /** * Summary of optionTypes * Only used for the settings page @@ -110,7 +130,7 @@ class GeneralSettings extends Settings 'options' => [ 'default' => 'Default', 'BlueInfinity' => 'Blue Infinity', - ], + ], // TODO: themes should be made/loaded dynamically 'description' => 'The theme to use for the site.' ], ]; diff --git a/app/Settings/InvoiceSettings.php b/app/Settings/InvoiceSettings.php index 099565a7..46dce9c5 100644 --- a/app/Settings/InvoiceSettings.php +++ b/app/Settings/InvoiceSettings.php @@ -6,21 +6,14 @@ use Spatie\LaravelSettings\Settings; class InvoiceSettings extends Settings { - public string $company_address; - - public string $company_mail; - - public string $company_name; - - public string $company_phone; - - public string $company_vat; - - public string $company_website; - + public ?string $company_address; + public ?string $company_mail; + public ?string $company_name; + public ?string $company_phone; + public ?string $company_vat; + public ?string $company_website; public bool $enabled; - - public string $prefix; + public ?string $prefix; public static function group(): string { @@ -40,7 +33,7 @@ class InvoiceSettings extends Settings 'company_phone' => 'nullable|string', 'company_vat' => 'nullable|string', 'company_website' => 'nullable|string', - 'enabled' => 'nullable|string', + 'enabled' => 'nullable|boolean', 'prefix' => 'nullable|string', ]; } diff --git a/app/Settings/LocaleSettings.php b/app/Settings/LocaleSettings.php index 35755797..7802f499 100644 --- a/app/Settings/LocaleSettings.php +++ b/app/Settings/LocaleSettings.php @@ -6,14 +6,10 @@ use Spatie\LaravelSettings\Settings; class LocaleSettings extends Settings { - public string $available; - + public ?string $available; public bool $clients_can_change; - - public string $datatables; - + public ?string $datatables; public string $default; - public bool $dynamic; public static function group(): string @@ -29,10 +25,10 @@ class LocaleSettings extends Settings { return [ 'available' => 'nullable|array', - 'clients_can_change' => 'nullable|string', + 'clients_can_change' => 'nullable|boolean', 'datatables' => 'nullable|string', - 'default' => 'nullable|string', - 'dynamic' => 'nullable|string', + 'default' => 'required|in:' . implode(',', config('app.available_locales')), + 'dynamic' => 'nullable|boolean', ]; } diff --git a/app/Settings/MailSettings.php b/app/Settings/MailSettings.php index f43158c1..e94953ce 100644 --- a/app/Settings/MailSettings.php +++ b/app/Settings/MailSettings.php @@ -7,22 +7,14 @@ use Spatie\LaravelSettings\Settings; class MailSettings extends Settings { public ?string $mail_host; - public ?int $mail_port; - public ?string $mail_username; - public ?string $mail_password; - public ?string $mail_encryption; - public ?string $mail_from_address; - public ?string $mail_from_name; - public ?string $mail_mailer; - - public ?bool $mail_enabled; + public bool $mail_enabled; public static function group(): string { diff --git a/app/Settings/PterodactylSettings.php b/app/Settings/PterodactylSettings.php index b6335a5f..2267152e 100644 --- a/app/Settings/PterodactylSettings.php +++ b/app/Settings/PterodactylSettings.php @@ -6,12 +6,9 @@ use Spatie\LaravelSettings\Settings; class PterodactylSettings extends Settings { - public ?string $admin_token; - - public ?string $user_token; - - public ?string $panel_url; - + public string $admin_token; + public string $user_token; + public string $panel_url; public int $per_page_limit; public static function group(): string @@ -44,9 +41,9 @@ class PterodactylSettings extends Settings public static function getValidations() { return [ - 'panel_url' => 'nullable|string|url', - 'admin_token' => 'nullable|string', - 'user_token' => 'nullable|string', + 'panel_url' => 'required|string|url', + 'admin_token' => 'required|string', + 'user_token' => 'required|string', 'per_page_limit' => 'required|integer|min:1|max:10000', ]; } diff --git a/app/Settings/ReferralSettings.php b/app/Settings/ReferralSettings.php index d2be01ad..e49fbaae 100644 --- a/app/Settings/ReferralSettings.php +++ b/app/Settings/ReferralSettings.php @@ -7,16 +7,11 @@ use Spatie\LaravelSettings\Settings; class ReferralSettings extends Settings { public string $allowed; - public bool $always_give_commission; - public bool $enabled; - - public float $reward; - + public ?float $reward; public string $mode; - - public int $percentage; + public ?int $percentage; public static function group(): string { @@ -30,11 +25,11 @@ class ReferralSettings extends Settings public static function getValidations() { return [ - 'allowed' => 'nullable|string', + 'allowed' => 'required|in:Everyone,Clients', 'always_give_commission' => 'nullable|boolean', 'enabled' => 'nullable|boolean', 'reward' => 'nullable|numeric', - 'mode' => 'nullable|string', + 'mode' => 'required|in:Commission,Sign-Up,Both', 'percentage' => 'nullable|numeric', ]; } @@ -77,7 +72,7 @@ class ReferralSettings extends Settings 'type' => 'select', 'description' => 'Referral mode.', 'options' => [ - 'comission' => 'Comission', + 'commission' => 'Commission', 'sign-up' => 'Sign-Up', 'both' => 'Both', ], diff --git a/app/Settings/ServerSettings.php b/app/Settings/ServerSettings.php index 5a4a4708..ee43e667 100644 --- a/app/Settings/ServerSettings.php +++ b/app/Settings/ServerSettings.php @@ -7,11 +7,8 @@ use Spatie\LaravelSettings\Settings; class ServerSettings extends Settings { public int $allocation_limit; - public bool $creation_enabled; - public bool $enable_upgrade; - public bool $charge_first_hour; public static function group(): string @@ -27,9 +24,9 @@ class ServerSettings extends Settings { return [ 'allocation_limit' => 'required|integer|min:0', - 'creation_enabled' => 'nullable|string', - 'enable_upgrade' => 'nullable|string', - 'charge_first_hour' => 'nullable|string', + 'creation_enabled' => 'nullable|boolean', + 'enable_upgrade' => 'nullable|boolean', + 'charge_first_hour' => 'nullable|boolean', ]; } diff --git a/app/Settings/TicketSettings.php b/app/Settings/TicketSettings.php index d5dfeb4b..10ac16b1 100644 --- a/app/Settings/TicketSettings.php +++ b/app/Settings/TicketSettings.php @@ -7,7 +7,6 @@ use Spatie\LaravelSettings\Settings; class TicketSettings extends Settings { public bool $enabled; - public string $notify; public static function group(): string @@ -22,7 +21,7 @@ class TicketSettings extends Settings public static function getValidations() { return [ - 'enabled' => 'nullable|string', + 'enabled' => 'nullable|boolean', 'notify' => 'nullable|string', ]; } diff --git a/app/Settings/UserSettings.php b/app/Settings/UserSettings.php index 18f872ee..ec34f678 100644 --- a/app/Settings/UserSettings.php +++ b/app/Settings/UserSettings.php @@ -7,27 +7,16 @@ use Spatie\LaravelSettings\Settings; class UserSettings extends Settings { public float $credits_reward_after_verify_discord; - public float $credits_reward_after_verify_email; - public bool $force_discord_verification; - public bool $force_email_verification; - public float $initial_credits; - public int $initial_server_limit; - public float $min_credits_to_make_server; - public int $server_limit_after_irl_purchase; - public int $server_limit_after_verify_discord; - public int $server_limit_after_verify_email; - public bool $register_ip_check; - public bool $creation_enabled; public static function group(): string @@ -44,16 +33,16 @@ class UserSettings extends Settings return [ 'credits_reward_after_verify_discord' => 'required|numeric', 'credits_reward_after_verify_email' => 'required|numeric', - 'force_discord_verification' => 'nullable|string', - 'force_email_verification' => 'nullable|string', + 'force_discord_verification' => 'nullable|boolean', + 'force_email_verification' => 'nullable|boolean', 'initial_credits' => 'required|numeric', 'initial_server_limit' => 'required|numeric', 'min_credits_to_make_server' => 'required|numeric', 'server_limit_after_irl_purchase' => 'required|numeric', 'server_limit_after_verify_discord' => 'required|numeric', 'server_limit_after_verify_email' => 'required|numeric', - 'register_ip_check' => 'nullable|string', - 'creation_enabled' => 'nullable|string', + 'register_ip_check' => 'nullable|boolean', + 'creation_enabled' => 'nullable|boolean', ]; } diff --git a/app/Settings/WebsiteSettings.php b/app/Settings/WebsiteSettings.php index a8da99e4..e557fd93 100644 --- a/app/Settings/WebsiteSettings.php +++ b/app/Settings/WebsiteSettings.php @@ -9,19 +9,15 @@ class WebsiteSettings extends Settings public bool $show_imprint; - public bool $show_privacy; - public bool $show_tos; - public bool $useful_links_enabled; public bool $enable_login_logo; - public string $seo_title; - - public string $seo_description; + public ?string $seo_title; + public ?string $seo_description; public bool $motd_enabled; - public string $motd_message; + public ?string $motd_message; public static function group(): string { @@ -35,13 +31,13 @@ class WebsiteSettings extends Settings public static function getValidations() { return [ - 'motd_enabled' => 'nullable|string', + 'motd_enabled' => 'nullable|boolean', 'motd_message' => 'nullable|string', - 'show_imprint' => 'nullable|string', - 'show_privacy' => 'nullable|string', - 'show_tos' => 'nullable|string', - 'useful_links_enabled' => 'nullable|string', - 'enable_login_logo' => 'nullable|string', + 'show_imprint' => 'nullable|boolean', + 'show_privacy' => 'nullable|boolean', + 'show_tos' => 'nullable|boolean', + 'useful_links_enabled' => 'nullable|boolean', + 'enable_login_logo' => 'nullable|boolean', 'seo_title' => 'nullable|string', 'seo_description' => 'nullable|string', ]; diff --git a/database/migrations/2023_04_03_231829_update_users_table.php b/database/migrations/2023_04_03_231829_update_users_table.php new file mode 100644 index 00000000..3caf98aa --- /dev/null +++ b/database/migrations/2023_04_03_231829_update_users_table.php @@ -0,0 +1,32 @@ +string('pterodactyl_id')->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->integer('pterodactyl_id')->nullable->change(); + }); + } +}; diff --git a/database/settings/2023_02_01_164731_create_general_settings.php b/database/settings/2023_02_01_164731_create_general_settings.php index d93efe52..8962889a 100644 --- a/database/settings/2023_02_01_164731_create_general_settings.php +++ b/database/settings/2023_02_01_164731_create_general_settings.php @@ -20,7 +20,6 @@ class CreateGeneralSettings extends SettingsMigration $this->migrator->add('general.alert_type', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:ALERT_TYPE") : 'dark'); $this->migrator->add('general.alert_message', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:ALERT_MESSAGE") : ''); $this->migrator->add('general.theme', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:THEME") : 'default'); - $this->migrator->add('general.main_site', ''); } public function down(): void @@ -81,12 +80,6 @@ class CreateGeneralSettings extends SettingsMigration 'type' => 'string', 'description' => 'The URL to your phpMyAdmin installation.' ], - [ - 'key' => 'SETTINGS::SYSTEM:MAIN_SITE', - 'value' => $this->getNewValue('main_site'), - 'type' => 'string', - 'description' => 'The URL to your main site.' - ], ]); $this->migrator->delete('general.store_enabled'); @@ -99,7 +92,6 @@ class CreateGeneralSettings extends SettingsMigration $this->migrator->delete('general.alert_type'); $this->migrator->delete('general.alert_message'); $this->migrator->delete('general.theme'); - $this->migrator->delete('general.main_site'); } public function getNewValue(string $name) diff --git a/public/install/forms.php b/public/install/forms.php index 6355ed06..13e3d432 100644 --- a/public/install/forms.php +++ b/public/install/forms.php @@ -1,18 +1,15 @@ load(); - include 'functions.php'; + if (isset($_POST['checkDB'])) { $values = [ //SETTINGS::VALUE => REQUEST-VALUE (coming from the html-form) @@ -24,9 +21,11 @@ if (isset($_POST['checkDB'])) { 'DB_CONNECTION' => 'databasedriver', ]; + wh_log('Trying to connect to the Database', 'debug'); + $db = new mysqli($_POST['databasehost'], $_POST['databaseuser'], $_POST['databaseuserpass'], $_POST['database'], $_POST['databaseport']); if ($db->connect_error) { - wh_log($db->connect_error); + wh_log($db->connect_error, 'error'); header('LOCATION: index.php?step=2&message=Could not connect to the Database'); exit(); } @@ -36,32 +35,36 @@ if (isset($_POST['checkDB'])) { // if ($key == "DB_PASSWORD") { // $param = '"' . $_POST[$value] . '"'; // } - setEnvironmentValue($key, $param); + setenv($key, $param); } + wh_log('Database connection successful', 'debug'); header('LOCATION: index.php?step=2.5'); } if (isset($_POST['checkGeneral'])) { - $appname = '"'.$_POST['name'].'"'; + wh_log('setting app settings', 'debug'); + $appname = '"' . $_POST['name'] . '"'; $appurl = $_POST['url']; if (substr($appurl, -1) === '/') { $appurl = substr_replace($appurl, '', -1); } - setEnvironmentValue('APP_NAME', $appname); - setEnvironmentValue('APP_URL', $appurl); + setenv('APP_NAME', $appname); + setenv('APP_URL', $appurl); + wh_log('App settings set', 'debug'); header('LOCATION: index.php?step=4'); } if (isset($_POST['feedDB'])) { + wh_log('Feeding the Database', 'debug'); $logs = ''; - //$logs .= run_console(putenv('COMPOSER_HOME=' . dirname(__FILE__, 3) . '/vendor/bin/composer')); + //$logs .= run_console(setenv('COMPOSER_HOME', dirname(__FILE__, 3) . '/vendor/bin/composer')); //$logs .= run_console('composer install --no-dev --optimize-autoloader'); - if (strpos(getEnvironmentValue('APP_KEY'), 'base64') === false) { + if (!str_contains(getenv('APP_KEY'), 'base64')) { $logs .= run_console('php artisan key:generate --force'); } else { $logs .= "Key already exists. Skipping\n"; @@ -70,16 +73,19 @@ if (isset($_POST['feedDB'])) { $logs .= run_console('php artisan migrate --seed --force'); $logs .= run_console('php artisan db:seed --class=ExampleItemsSeeder --force'); - wh_log($logs); + wh_log($logs, 'debug'); - if (strpos(getEnvironmentValue('APP_KEY'), 'base64') !== false) { + if (str_contains(getenv('APP_KEY'), 'base64')) { + wh_log('Feeding the Database successful', 'debug'); header('LOCATION: index.php?step=3'); } else { + wh_log('Feeding the Database failed', 'debug'); header('LOCATION: index.php?step=2.5&message=There was an error. Please check the .txt file in /var/www/controlpanel/public/install/logs !'); } } if (isset($_POST['checkSMTP'])) { + wh_log('Checking SMTP Settings', 'debug'); try { $mail = new PHPMailer(true); @@ -103,13 +109,16 @@ if (isset($_POST['checkSMTP'])) { $mail->send(); } catch (Exception $e) { + wh_log($mail->ErrorInfo, 'error'); header('LOCATION: index.php?step=4&message=Something wasnt right when sending the E-Mail!'); exit(); } - $db = new mysqli(getEnvironmentValue('DB_HOST'), getEnvironmentValue('DB_USERNAME'), getEnvironmentValue('DB_PASSWORD'), getEnvironmentValue('DB_DATABASE'), getEnvironmentValue('DB_PORT')); + wh_log('SMTP Settings are correct', 'debug'); + wh_log('Updating Database', 'debug'); + $db = new mysqli(getenv('DB_HOST'), getenv('DB_USERNAME'), getenv('DB_PASSWORD'), getenv('DB_DATABASE'), getenv('DB_PORT')); if ($db->connect_error) { - wh_log($db->connect_error); + wh_log($db->connect_error, 'error'); header('LOCATION: index.php?step=4&message=Could not connect to the Database: '); exit(); } @@ -118,20 +127,23 @@ if (isset($_POST['checkSMTP'])) { 'mail_host' => $_POST['host'], 'mail_port' => $_POST['port'], 'mail_username' => $_POST['user'], - 'mail_password' => $_POST['pass'], + 'mail_password' => encryptSettingsValue($_POST['pass']), 'mail_encryption' => $_POST['encryption'], 'mail_from_address' => $_POST['user'], ]; foreach ($values as $key => $value) { - $query = 'UPDATE `'.getEnvironmentValue('DB_DATABASE')."`.`settings` SET `payload` = '$value' WHERE `name` = '$key' AND `group` = mail"; + $query = 'UPDATE `' . getenv('DB_DATABASE') . "`.`settings` SET `payload` = '$value' WHERE `name` = '$key' AND `group` = mail"; $db->query($query); } + wh_log('Database updated', 'debug'); header('LOCATION: index.php?step=5'); } if (isset($_POST['checkPtero'])) { + wh_log('Checking Pterodactyl Settings', 'debug'); + $url = $_POST['url']; $key = $_POST['key']; $clientkey = $_POST['clientkey']; @@ -140,67 +152,75 @@ if (isset($_POST['checkPtero'])) { $url = substr_replace($url, '', -1); } - $callpteroURL = $url.'/api/client/account'; + $callpteroURL = $url . '/api/client/account'; $call = curl_init(); curl_setopt($call, CURLOPT_URL, $callpteroURL); curl_setopt($call, CURLOPT_RETURNTRANSFER, true); curl_setopt($call, CURLOPT_HTTPHEADER, [ - 'Accept: application/json', + 'Accept: Application/vnd.pterodactyl.v1+json', 'Content-Type: application/json', - 'Authorization: Bearer '.$clientkey, + 'Authorization: Bearer ' . $clientkey, ]); $callresponse = curl_exec($call); $callresult = json_decode($callresponse, true); curl_close($call); // Close the connection - $pteroURL = $url.'/api/application/users'; + $pteroURL = $url . '/api/application/users'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $pteroURL); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Accept: application/json', + 'Accept: Application/vnd.pterodactyl.v1+json', 'Content-Type: application/json', - 'Authorization: Bearer '.$key, + 'Authorization: Bearer ' . $key, ]); $response = curl_exec($ch); $result = json_decode($response, true); curl_close($ch); // Close the connection - if (! is_array($result) or in_array($result['errors'][0]['code'], $result)) { - header('LOCATION: index.php?step=5&message=Couldnt connect to Pterodactyl. Make sure your API key has all read and write permissions!'); - wh_log('API CALL ERROR: '.$result['errors'][0]['code']); + if (!is_array($result) and $result['errors'][0] !== null) { + header('LOCATION: index.php?step=5&message=Couldn\'t connect to Pterodactyl. Make sure your API key has all read and write permissions!'); + wh_log('API CALL ERROR: ' . $result['errors'][0]['code'], 'error'); exit(); - } elseif (! is_array($callresult) or in_array($result['errors'][0]['code'], $result) or $callresult['attributes']['admin'] == false) { + } elseif (!is_array($callresult) and $callresult['errors'][0] !== null or $callresult['attributes']['admin'] == false) { header('LOCATION: index.php?step=5&message=Your ClientAPI Key is wrong or the account is not an admin!'); - wh_log('API CALL ERROR: '.$result['errors'][0]['code']); + wh_log('API CALL ERROR: ' . $callresult['errors'][0]['code'], 'error'); exit(); } else { - $query1 = 'UPDATE `'.getEnvironmentValue('DB_DATABASE')."`.`settings` SET `payload` = '$url' WHERE (`name` = 'panel_url' AND `group` = 'pterodactyl')"; - $query2 = 'UPDATE `'.getEnvironmentValue('DB_DATABASE')."`.`settings` SET `payload` = '$key' WHERE (`name` = 'admin_token' AND `group` = 'pterodactyl')"; - $query3 = 'UPDATE `'.getEnvironmentValue('DB_DATABASE')."`.`settings` SET `payload` = '$clientkey' WHERE (`name` = 'user_token' AND `group` = 'pterodactyl')"; + wh_log('Pterodactyl Settings are correct', 'debug'); + wh_log('Updating Database', 'debug'); - $db = new mysqli(getEnvironmentValue('DB_HOST'), getEnvironmentValue('DB_USERNAME'), getEnvironmentValue('DB_PASSWORD'), getEnvironmentValue('DB_DATABASE'), getEnvironmentValue('DB_PORT')); + $key = encryptSettingsValue($key); + $clientkey = encryptSettingsValue($clientkey); + + $query1 = 'UPDATE `' . getenv('DB_DATABASE') . "`.`settings` SET `payload` = '" . json_encode($url) . "' WHERE (`name` = 'panel_url' AND `group` = 'pterodactyl')"; + $query2 = 'UPDATE `' . getenv('DB_DATABASE') . "`.`settings` SET `payload` = '" . json_encode($key) . "' WHERE (`name` = 'admin_token' AND `group` = 'pterodactyl')"; + $query3 = 'UPDATE `' . getenv('DB_DATABASE') . "`.`settings` SET `payload` = '" . json_encode($clientkey) . "' WHERE (`name` = 'user_token' AND `group` = 'pterodactyl')"; + + $db = new mysqli(getenv('DB_HOST'), getenv('DB_USERNAME'), getenv('DB_PASSWORD'), getenv('DB_DATABASE'), getenv('DB_PORT')); if ($db->connect_error) { - wh_log($db->connect_error); + wh_log($db->connect_error, 'error'); header('LOCATION: index.php?step=5&message=Could not connect to the Database'); exit(); } if ($db->query($query1) && $db->query($query2) && $db->query($query3)) { + wh_log('Database updated', 'debug'); header('LOCATION: index.php?step=6'); } else { - wh_log($db->error); + wh_log($db->error, 'error'); header('LOCATION: index.php?step=5&message=Something went wrong when communicating with the Database!'); } } } if (isset($_POST['createUser'])) { - $db = new mysqli(getEnvironmentValue('DB_HOST'), getEnvironmentValue('DB_USERNAME'), getEnvironmentValue('DB_PASSWORD'), getEnvironmentValue('DB_DATABASE'), getEnvironmentValue('DB_PORT')); + wh_log('Creating User', 'debug'); + $db = new mysqli(getenv('DB_HOST'), getenv('DB_USERNAME'), getenv('DB_PASSWORD'), getenv('DB_DATABASE'), getenv('DB_PORT')); if ($db->connect_error) { - wh_log($db->connect_error); + wh_log($db->connect_error, 'error'); header('LOCATION: index.php?step=6&message=Could not connect to the Database'); exit(); } @@ -209,10 +229,11 @@ if (isset($_POST['createUser'])) { $pass = $_POST['pass']; $repass = $_POST['repass']; - $key = $db->query('SELECT `payload` FROM `'.getEnvironmentValue('DB_DATABASE')."`.`settings` WHERE `name` = 'admin_token' AND `group` = 'pterodactyl'")->fetch_assoc(); - $pterobaseurl = $db->query('SELECT `payload` FROM `'.getEnvironmentValue('DB_DATABASE')."`.`settings` WHERE `name` = 'panel_url' AND `group` = 'pterodactyl'")->fetch_assoc(); + $key = $db->query('SELECT `payload` FROM `' . getenv('DB_DATABASE') . "`.`settings` WHERE `name` = 'admin_token' AND `group` = 'pterodactyl'")->fetch_assoc(); + $key = encryptSettingsValue($key['value']); + $pterobaseurl = $db->query('SELECT `payload` FROM `' . getenv('DB_DATABASE') . "`.`settings` WHERE `name` = 'panel_url' AND `group` = 'pterodactyl'")->fetch_assoc(); - $pteroURL = $pterobaseurl['value'].'/api/application/users/'.$pteroID; + $pteroURL = $pterobaseurl['value'] . '/api/application/users/' . $pteroID; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $pteroURL); @@ -220,14 +241,14 @@ if (isset($_POST['createUser'])) { curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: application/json', 'Content-Type: application/json', - 'Authorization: Bearer '.$key['value'], + 'Authorization: Bearer ' . $key, ]); $response = curl_exec($ch); $result = json_decode($response, true); curl_close($ch); // Close the connection - if (! $result['attributes']['email']) { - header('LOCATION: index.php?step=6&message=Could not find the user with pterodactyl ID '.$pteroID); + if (!$result['attributes']['email']) { + header('LOCATION: index.php?step=6&message=Could not find the user with pterodactyl ID ' . $pteroID); exit(); } if ($pass !== $repass) { @@ -239,7 +260,7 @@ if (isset($_POST['createUser'])) { $name = $result['attributes']['username']; $pass = password_hash($pass, PASSWORD_DEFAULT); - $pteroURL = $pterobaseurl['value'].'/api/application/users/'.$pteroID; + $pteroURL = $pterobaseurl['value'] . '/api/application/users/' . $pteroID; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $pteroURL); @@ -247,7 +268,7 @@ if (isset($_POST['createUser'])) { curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: application/json', 'Content-Type: application/json', - 'Authorization: Bearer '.$key['value'], + 'Authorization: Bearer ' . $key['value'], ]); curl_setopt($ch, CURLOPT_POSTFIELDS, [ 'email' => $mail, @@ -260,19 +281,19 @@ if (isset($_POST['createUser'])) { $result = json_decode($response, true); curl_close($ch); // Close the connection - if (! is_array($result) or in_array($result['errors'][0]['code'], $result)) { - header('LOCATION: index.php?step=5&message=Couldnt connect to Pterodactyl. Make sure your API key has all read and write permissions!'); + if (!is_array($result) or in_array($result['errors'][0]['code'], $result)) { + header('LOCATION: index.php?step=5&message=Couldn\'t connect to Pterodactyl. Make sure your API key has all read and write permissions!'); exit(); } $random = generateRandomString(); - $query1 = 'INSERT INTO `'.getEnvironmentValue('DB_DATABASE')."`.`users` (`name`, `role`, `credits`, `server_limit`, `pterodactyl_id`, `email`, `password`, `created_at`, `referral_code`) VALUES ('$name', 'admin', '250', '1', '$pteroID', '$mail', '$pass', CURRENT_TIMESTAMP, '$random')"; + $query1 = 'INSERT INTO `' . getenv('DB_DATABASE') . "`.`users` (`name`, `role`, `credits`, `server_limit`, `pterodactyl_id`, `email`, `password`, `created_at`, `referral_code`) VALUES ('$name', 'admin', '250', '1', '$pteroID', '$mail', '$pass', CURRENT_TIMESTAMP, '$random')"; if ($db->query($query1)) { - wh_log('[USER MAKER] Created user with Email '.$mail.' and pterodactyl ID '.$pteroID); + wh_log('Created user with Email ' . $mail . ' and pterodactyl ID ' . $pteroID, 'info'); header('LOCATION: index.php?step=7'); } else { - wh_log($db->error); + wh_log($db->error, 'error'); header('LOCATION: index.php?step=6&message=Something went wrong when communicating with the Database'); } -} +} \ No newline at end of file diff --git a/public/install/functions.php b/public/install/functions.php index ed983c57..6ad3beca 100644 --- a/public/install/functions.php +++ b/public/install/functions.php @@ -1,6 +1,17 @@ load(); + +$required_extensions = ['openssl', 'gd', 'mysql', 'PDO', 'mbstring', 'tokenizer', 'bcmath', 'xml', 'curl', 'zip', 'intl']; $requirements = [ 'minPhp' => '8.1', @@ -8,84 +19,140 @@ $requirements = [ 'mysql' => '5.7.22', ]; -function checkPhpVersion() +/** + * Check if the minimum PHP version is present + * @return string 'OK' on success and 'not OK' on failure. + */ +function checkPhpVersion(): string { global $requirements; + + wh_log('php version: ' . phpversion(), 'debug'); if (version_compare(phpversion(), $requirements['minPhp'], '>=') && version_compare(phpversion(), $requirements['maxPhp'], '<=')) { return 'OK'; } return 'not OK'; } -function checkWriteable() + +/** + * Check if the environment file is writable + * @return bool Returns true on writable and false on not writable. + */ +function checkWriteable(): bool { return is_writable('../../.env'); } -function checkHTTPS() + +/** + * Check if the server runs using HTTPS + * @return bool Returns true on HTTPS or false on HTTP. + */ +function checkHTTPS(): bool { - return (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') - || $_SERVER['SERVER_PORT'] == 443; + $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443; + wh_log('https:', 'debug', (array)$isHttps); + return $isHttps; } -function getMySQLVersion() +/** + * Check if MySQL is installed and runs the correct version using a shell command + * @return mixed|string 'OK' if required version is met, returns MySQL version if not met. + */ +function getMySQLVersion(): mixed { global $requirements; - $output = shell_exec('mysql -V'); + wh_log('attempting to get mysql version', 'debug'); + + $output = shell_exec('mysql -V') ?? ''; preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version); $versionoutput = $version[0] ?? '0'; + wh_log('mysql version: ' . $versionoutput, 'debug'); return intval($versionoutput) > intval($requirements['mysql']) ? 'OK' : $versionoutput; } -function getZipVersion() +/** + * Check if zip is installed using a shell command + * @return string 'OK' on success and 'not OK' on failure. + */ +function getZipVersion(): string { - $output = shell_exec('zip -v'); + wh_log('attempting to get zip version', 'debug'); + $output = shell_exec('zip -v') ?? ''; preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version); $versionoutput = $version[0] ?? 0; + wh_log('zip version: ' . $versionoutput, 'debug'); return $versionoutput != 0 ? 'OK' : 'not OK'; } -function getGitVersion() +/** + * Check if git is installed using a shell command + * @return string 'OK' on success and 'not OK' on failure. + */ +function getGitVersion(): string { - $output = shell_exec('git --version'); + wh_log('attempting to get git version', 'debug'); + $output = shell_exec('git --version') ?? ''; preg_match('@[0-9]+\.[0-9]+\.[0-9]+@', $output, $version); $versionoutput = $version[0] ?? 0; + wh_log('git version: ' . $versionoutput, 'debug'); return $versionoutput != 0 ? 'OK' : 'not OK'; } -function getTarVersion() +/** + * Check if tar is installed using a shell command + * @return string 'OK' on success and 'not OK' on failure. + */ +function getTarVersion(): string { - $output = shell_exec('tar --version'); + wh_log('attempting to get tar version', 'debug'); + $output = shell_exec('tar --version') ?? ''; preg_match('@[0-9]+\.[0-9]+@', $output, $version); $versionoutput = $version[0] ?? 0; + wh_log('tar version: ' . $versionoutput, 'debug'); return $versionoutput != 0 ? 'OK' : 'not OK'; } -function checkExtensions() +/** + * Check all extensions to see if they have loaded or not + * @return array Returns an array of extensions that failed to load. + */ +function checkExtensions(): array { - global $required_extentions; + global $required_extensions; + + wh_log('checking extensions', 'debug'); $not_ok = []; $extentions = get_loaded_extensions(); - foreach ($required_extentions as $ext) { - if (! preg_grep('/^(?=.*'.$ext.').*$/', $extentions)) { + foreach ($required_extensions as $ext) { + if (!preg_grep('/^(?=.*' . $ext . ').*$/', $extentions)) { array_push($not_ok, $ext); } } + wh_log('loaded extensions:', 'debug', $extentions); + wh_log('failed extensions:', 'debug', $not_ok); return $not_ok; } -function setEnvironmentValue($envKey, $envValue) +/** + * Sets the environment variable into the env file + * @param string $envKey The environment key to set or modify + * @param string $envValue The environment variable to set + * @return bool true on success or false on failure. + */ +function setenv($envKey, $envValue) { $envFile = dirname(__FILE__, 3).'/.env'; $str = file_get_contents($envFile); @@ -102,41 +169,114 @@ function setEnvironmentValue($envKey, $envValue) fclose($fp); } -function getEnvironmentValue($envKey) +/** + * Encrypt the given value + * @param mixed $value The variable to be encrypted + * @param bool $serialize If the encryption should be serialized + * @return string Returns the encrypted variable. + */ +function encryptSettingsValue(mixed $value, $serialize = true): string { - $envFile = dirname(__FILE__, 3).'/.env'; - $str = file_get_contents($envFile); + $appKey = getenv('APP_KEY'); + $appKey = base64_decode(Str::after($appKey, 'base64:')); + $encrypter = new Encrypter($appKey); + $encryptedKey = $encrypter->encrypt($value, $serialize); - $str .= "\n"; // In case the searched variable is in the last line without \n - $keyPosition = strpos($str, "{$envKey}="); - $endOfLinePosition = strpos($str, PHP_EOL, $keyPosition); - $oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition); - $value = substr($oldLine, strpos($oldLine, '=') + 1); - - return $value; + return $encryptedKey; } -function run_console($command) +/** + * Decrypt the given value + * @param mixed $payload The payload to be decrypted + * @param bool $unserialize If the encryption should be unserialized + * @return mixed Returns the decrypted variable on success, throws otherwise. + */ + +function decryptSettingsValue(mixed $payload, $unserialize = true) { + $appKey = getenv('APP_KEY'); + $appKey = base64_decode(Str::after($appKey, 'base64:')); + $encrypter = new Encrypter($appKey); + $decryptedKey = $encrypter->decrypt($payload, $unserialize); + + return $decryptedKey; +} + +/** + * Run a shell command + * @param string $command The command string to run + * @param array|null $descriptors [optional]

+ * An indexed array where the key represents the descriptor number and the value represents how PHP will pass that descriptor to the child process. 0 is stdin, 1 is stdout, while 2 is stderr. + * Default descriptors when null are 0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w'] + *

+ * @param string|null $cwd [optional]

+ * The initial working dir for the command. This must be an + * absolute directory path, or null + * if you want to use the default value (the working dir of the current + * PHP process) + *

+ * @param array|null $options [optional]

+ * Allows you to specify additional options. + * @link https://www.php.net/manual/en/function.proc-open.php proc_open + *

+ * @return false|string|null Returns the result from the command. + */ +function run_console(string $command, array $descriptors = null, string $cwd = null, array $options = null) +{ + wh_log('running command: ' . $command, 'debug'); + $path = dirname(__FILE__, 3); - $cmd = "cd '$path' && bash -c 'exec -a ServerCPP $command' 2>&1"; + $descriptors = $descriptors ?? [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; + $handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", $descriptors, $pipes, $cwd, null, $options); - return shell_exec($cmd); + wh_log('command result: ' . stream_get_contents($pipes[1]), 'debug'); + return stream_get_contents($pipes[1]); } -function wh_log($log_msg) +/** + * Log to the default laravel.log file + * @param string $message The message to log + * @param string $level The log level to use (debug, info, warning, error, critical) + * @param array $context [optional] The context to log extra information + * @return void + */ +function wh_log(string $message, string $level = 'info', array $context = []): void { - $log_filename = 'logs'; - if (! file_exists($log_filename)) { - // create directory/folder uploads. - mkdir($log_filename, 0777, true); + $formatter = new LineFormatter(null, null, true, true); + $stream = new StreamHandler(dirname(__FILE__, 3) . '/storage/logs/installer.log', Logger::DEBUG); + $stream->setFormatter($formatter); + + $log = new Logger('ControlPanel'); + $log->pushHandler($stream); + + switch (strtolower($level)) { + case 'debug': // Only log debug messages if APP_DEBUG is true + if(getenv('APP_DEBUG') === false) return; + $log->debug($message, $context); + break; + case 'info': + $log->info($message, $context); + break; + case 'warning': + $log->warning($message, $context); + break; + case 'error': + $log->error($message, $context); + break; + case 'critical': + $log->critical($message, $context); + break; } - $log_file_data = $log_filename.'/installer.log'; - // if you don't add `FILE_APPEND`, the file will be erased each time you add a log - file_put_contents($log_file_data, '['.date('h:i:s').'] '.$log_msg."\n", FILE_APPEND); + // Prevent memory leaks by resetting the logger + $log->reset(); } -function generateRandomString($length = 8) +/** + * Generate a random string + * @param int $length The length of the random string + * @return string The randomly generated string. + */ +function generateRandomString(int $length = 8): string { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $charactersLength = strlen($characters); diff --git a/public/install/index.php b/public/install/index.php index b002282b..027ae3d1 100644 --- a/public/install/index.php +++ b/public/install/index.php @@ -52,8 +52,8 @@ $cardheader = '
'; -if (! isset($_GET['step'])) { - if (! file_exists('../../.env')) { +if (!isset($_GET['step'])) { + if (!file_exists('../../.env')) { echo run_console('cp .env.example .env'); } echo $cardheader; ?> @@ -71,11 +71,11 @@ if (! isset($_GET['step'])) {

Missing php-extentions:

+ echo count(checkExtensions()) == 0 ? '' : '(Proceed anyway)'; ?>

- @endsection