Browse Source

Merge pull request #372 from ControlPanel-gg/settings_rewrite

Settings rewrite
Dennis 3 years ago
parent
commit
4ad5722e9c
63 changed files with 2281 additions and 1402 deletions
  1. 0 48
      .env.example
  2. 5 5
      app/Classes/Pterodactyl.php
  3. 50 0
      app/Classes/Settings/Invoices.php
  4. 61 0
      app/Classes/Settings/Language.php
  5. 85 0
      app/Classes/Settings/Misc.php
  6. 61 0
      app/Classes/Settings/Payments.php
  7. 73 0
      app/Classes/Settings/System.php
  8. 0 123
      app/Http/Controllers/Admin/ConfigurationController.php
  9. 3 2
      app/Http/Controllers/Admin/CreditProductController.php
  10. 70 0
      app/Http/Controllers/Admin/InvoiceController.php
  11. 47 30
      app/Http/Controllers/Admin/PaymentController.php
  12. 2 5
      app/Http/Controllers/Admin/ProductController.php
  13. 2 1
      app/Http/Controllers/Admin/ServerController.php
  14. 47 61
      app/Http/Controllers/Admin/SettingsController.php
  15. 2 1
      app/Http/Controllers/Admin/UserController.php
  16. 3 3
      app/Http/Controllers/Api/UserController.php
  17. 13 5
      app/Http/Controllers/Auth/LoginController.php
  18. 17 19
      app/Http/Controllers/Auth/RegisterController.php
  19. 7 7
      app/Http/Controllers/Auth/SocialiteController.php
  20. 0 3
      app/Http/Controllers/HomeController.php
  21. 11 14
      app/Http/Controllers/ProfileController.php
  22. 5 5
      app/Http/Controllers/ServerController.php
  23. 5 5
      app/Http/Controllers/StoreController.php
  24. 2 0
      app/Http/Controllers/TranslationController.php
  25. 3 2
      app/Http/Middleware/GlobalNames.php
  26. 10 200
      app/Http/Middleware/SetLocale.php
  27. 2 3
      app/Listeners/UnsuspendServers.php
  28. 3 5
      app/Listeners/Verified.php
  29. 1 1
      app/Models/CreditProduct.php
  30. 0 23
      app/Models/InvoiceSettings.php
  31. 8 6
      app/Models/Settings.php
  32. 0 1
      app/Notifications/ServersSuspendedNotification.php
  33. 19 19
      app/Notifications/WelcomeMessage.php
  34. 57 4
      app/Providers/AppServiceProvider.php
  35. 8 43
      config/app.php
  36. 16 25
      config/mail.php
  37. 0 34
      database/migrations/2021_05_08_164658_create_configurations_table.php
  38. 0 48
      database/migrations/2021_12_1_174440_invoice-settings.php
  39. 61 0
      database/migrations/2022_01_05_144858_rename_configurations_table.php
  40. 34 0
      database/migrations/2022_01_14_234418_update_settings_table_allow_nullable.php
  41. 2 3
      database/seeders/DatabaseSeeder.php
  42. 0 149
      database/seeders/Seeds/ConfigurationSeeder.php
  43. 468 0
      database/seeders/Seeds/SettingsSeeder.php
  44. 1 1
      package-lock.json
  45. BIN
      public/images/discord_logo.png
  46. 14 5
      resources/lang/de.json
  47. 32 19
      resources/lang/en.json
  48. 0 63
      resources/views/admin/configurations/editModel.blade.php
  49. 0 91
      resources/views/admin/configurations/index.blade.php
  50. 6 0
      resources/views/admin/payments/index.blade.php
  51. 26 164
      resources/views/admin/settings/index.blade.php
  52. 111 0
      resources/views/admin/settings/tabs/invoices.blade.php
  53. 91 0
      resources/views/admin/settings/tabs/language.blade.php
  54. 194 0
      resources/views/admin/settings/tabs/misc.blade.php
  55. 144 0
      resources/views/admin/settings/tabs/payment.blade.php
  56. 208 0
      resources/views/admin/settings/tabs/system.blade.php
  57. 24 15
      resources/views/layouts/app.blade.php
  58. 46 34
      resources/views/layouts/main.blade.php
  59. 83 83
      resources/views/profile/index.blade.php
  60. 2 0
      resources/views/servers/create.blade.php
  61. 3 3
      resources/views/servers/index.blade.php
  62. 2 2
      resources/views/store/checkout.blade.php
  63. 31 19
      routes/web.php

+ 0 - 48
.env.example

@@ -8,18 +8,6 @@ APP_URL=http://localhost
 APP_TIMEZONE=UTC
 ### --- App Settings End --- ###
 
-### --- Localization settings --- ###
-# If set to true, Language is chosen automatically depending on the users browserlanguage.
-DYNAMIC_LOCALE = false
-# The language of the Dashboard. This is also the fallback if dynamic_locale is true but no translation is found
-LOCALE=en
-# You can grab the Language-Codes for the Datatables from this Website https://datatables.net/plug-ins/i18n/
-DATATABLE_LOCALE=en-gb
-#The languages you DO NOT want to support on your Controlpanel split by comma.
-#Remove the language to make it available
-UNSUPPORTED_LOCALES=german,italian,chinese
-### --- Localization settings End --- ###
-
 ### --- DB Settings (required) --- ###
 DB_CONNECTION=mysql
 DB_HOST=127.0.0.1
@@ -29,42 +17,6 @@ DB_USERNAME=dashboarduser
 DB_PASSWORD=
 ### --- DB Settings End --- ###
 
-### --- Payment Options (required for payments) --- ###
-# Paypal API Credentials - https://developer.paypal.com/docs/integration/direct/rest/ - Sandbox credentials are being used when APP_ENV is set to local
-PAYPAL_SANDBOX_SECRET=
-PAYPAL_SANDBOX_CLIENT_ID=
-PAYPAL_SECRET=
-PAYPAL_CLIENT_ID=
-
-# Stripe API Credentials - https://dashboard.stripe.com/account/apikeys - Test credentials are being used when APP_ENV is set to local
-STRIPE_TEST_SECRET=
-STRIPE_SECRET=
-#https://dashboard.stripe.com/webhooks -> webhook route: <your.controlpanel.gg>/payment/StripeWebhooks
-STRIPE_ENDPOINT_TEST_SECRET=
-STRIPE_ENDPOINT_SECRET=
-# Stripe payment methods - comma seperated list of payment methods that are enabled https://stripe.com/docs/payments/payment-methods/integration-options
-STRIPE_METHODS=
-### --- Payment Options End --- ###
-
-### --- Discord Settings (optional) --- ###
-# Discord API Credentials - https://discordapp.com/developers/applications/
-DISCORD_CLIENT_ID=
-DISCORD_CLIENT_SECRET=
-# Bot Settings - will join users to your discord
-DISCORD_BOT_TOKEN=
-DISCORD_GUILD_ID=
-# Discord role that will be assigned to users when they register
-DISCORD_ROLE_ID=
-### --- Discord Settings End --- ###
-
-### --- Controlpanel Settings (required) --- ###
-# Controlpanel URL Settings - URLs must not end with a slash!
-PTERODACTYL_URL=https://panel.controlpanel.gg # required
-PHPMYADMIN_URL=https://mysql.controlpanel.gg #optional. remove to remove database button
-DISCORD_INVITE_URL=https://discord.gg/vrUYdxG4wZ #optional
-# Admin API Token from Pterodactyl Panel - Nececary for the Panel to work
-PTERODACTYL_TOKEN=
-### --- Controlpanel Settings End --- ###
 
 # Google Recaptcha API Credentials - https://www.google.com/recaptcha/admin - reCaptcha V2 (not v3)
 RECAPTCHA_SITE_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI

+ 5 - 5
app/Classes/Pterodactyl.php

@@ -2,11 +2,11 @@
 
 namespace App\Classes;
 
-use App\Models\Configuration;
 use App\Models\Egg;
 use App\Models\Nest;
 use App\Models\Node;
 use App\Models\Server;
+use App\Models\Settings;
 use Exception;
 use Illuminate\Http\Client\PendingRequest;
 use Illuminate\Http\Client\Response;
@@ -27,10 +27,10 @@ class Pterodactyl
     public static function client()
     {
         return Http::withHeaders([
-            'Authorization' => 'Bearer ' . env('PTERODACTYL_TOKEN', false),
+            'Authorization' => 'Bearer ' . config("SETTINGS::SYSTEM:PTERODACTYL:TOKEN"),
             'Content-type'  => 'application/json',
             'Accept'        => 'Application/vnd.pterodactyl.v1+json',
-        ])->baseUrl(env('PTERODACTYL_URL') . '/api');
+        ])->baseUrl(config("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/api');
     }
 
     /**
@@ -141,7 +141,7 @@ class Pterodactyl
      */
     public static function getAllocations(Node $node)
     {
-        $per_page = Configuration::getValueByKey('ALLOCATION_LIMIT', 200);
+        $per_page = config('SETTINGS::SERVER:ALLOCATION_LIMIT', 200);
         try {
             $response = self::client()->get("/application/nodes/{$node->id}/allocations?per_page={$per_page}");
         } catch (Exception $e) {
@@ -158,7 +158,7 @@ class Pterodactyl
      */
     public static function url(string $route): string
     {
-        return env('PTERODACTYL_URL') . $route;
+        return config("SETTINGS::SYSTEM:PTERODACTYL:URL") . $route;
     }
 
     /**

+ 50 - 0
app/Classes/Settings/Invoices.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Classes\Settings;
+
+use App\Models\Settings;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+
+class Invoices
+{
+    public function __construct()
+    {
+        return;
+    }
+
+
+    public function updateSettings(Request $request)
+    {
+        $request->validate([
+            'logo' => 'nullable|max:10000|mimes:jpg,png,jpeg',
+        ]);
+
+        $values = [
+            //SETTINGS::VALUE => REQUEST-VALUE (coming from the html-form)
+            "SETTINGS::INVOICE:COMPANY_NAME" => "company-name",
+            "SETTINGS::INVOICE:COMPANY_ADDRESS" => "company-address",
+            "SETTINGS::INVOICE:COMPANY_PHONE" => "company-phone",
+            "SETTINGS::INVOICE:COMPANY_MAIL" => "company-mail",
+            "SETTINGS::INVOICE:COMPANY_VAT" => "company-vat",
+            "SETTINGS::INVOICE:COMPANY_WEBSITE" => "company-web",
+            "SETTINGS::INVOICE:PREFIX" => "invoice-prefix",
+            "SETTINGS::INVOICE:ENABLED" => "enable-invoices",
+        ];
+
+        foreach ($values as $key => $value) {
+            $param = $request->get($value);
+
+            Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
+            Cache::forget("setting" . ':' . $key);
+        }
+
+
+        if ($request->hasFile('logo')) {
+            $request->file('logo')->storeAs('public', 'logo.png');
+        }
+
+
+        return redirect(route('admin.settings.index') . '#invoices')->with('success', __('Invoice settings updated!'));
+    }
+}

+ 61 - 0
app/Classes/Settings/Language.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App\Classes\Settings;
+
+use App\Models\Settings;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Session;
+use Illuminate\Support\Facades\Validator;
+
+
+class Language
+{
+    public function __construct()
+    {
+        return;
+    }
+
+
+    public function updateSettings(Request $request)
+    {
+        $validator = Validator::make($request->all(), [
+            'autotranslate' => 'string',
+            'canClientChangeLanguage' => 'string',
+            'defaultLanguage' => 'required|string',
+            'languages' => 'required|array',
+            'languages.*' => 'required|string',
+            'datatable-language' => 'required|string',
+        ]);
+
+
+        if ($validator->fails()) {
+            return redirect(route('admin.settings.index') . '#language')->with('error', __('Language settings have not been updated!'))->withErrors($validator);
+        }
+
+        $values = [
+            //SETTINGS::VALUE => REQUEST-VALUE (coming from the html-form)
+            "SETTINGS::LOCALE:DEFAULT" => "defaultLanguage",
+            "SETTINGS::LOCALE:DYNAMIC" => "autotranslate",
+            "SETTINGS::LOCALE:CLIENTS_CAN_CHANGE" => "canClientChangeLanguage",
+            "SETTINGS::LOCALE:AVAILABLE" => "languages",
+            "SETTINGS::LOCALE:DATATABLES" => "datatable-language"
+        ];
+
+
+        foreach ($values as $key => $value) {
+            $param = $request->get($value);
+
+            if (is_array($param)) {
+                $param = implode(",", $param);
+            }
+
+            Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
+            Cache::forget("setting" . ':' . $key);
+            Session::remove("locale");
+        }
+
+
+        return redirect(route('admin.settings.index') . '#language')->with('success', __('Language settings updated!'));
+    }
+}

+ 85 - 0
app/Classes/Settings/Misc.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Classes\Settings;
+
+use App\Models\Settings;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Validator;
+
+
+class Misc
+{
+    public function __construct()
+    {
+        return;
+    }
+
+    public function updateSettings(Request $request)
+    {
+        $validator = Validator::make($request->all(), [
+            'icon' => 'nullable|max:10000|mimes:jpg,png,jpeg',
+            'favicon' => 'nullable|max:10000|mimes:ico',
+            'discord-bot-token' => 'nullable|string',
+            'discord-client-id' => 'nullable|string',
+            'discord-client-secret' => 'nullable|string',
+            'discord-guild-id' => 'nullable|string',
+            'discord-invite-url' => 'nullable|string',
+            'discord-role-id' => 'nullable|string',
+            'recaptcha-site-key' => 'nullable|string',
+            'recaptcha-secret-key' => 'nullable|string',
+            'enable-recaptcha' => 'nullable|string',
+            'mailservice' => 'nullable|string',
+            'mailhost' => 'nullable|string',
+            'mailport' => 'nullable|string',
+            'mailusername' => 'nullable|string',
+            'mailpassword' => 'nullable|string',
+            'mailencryption' => 'nullable|string',
+            'mailfromadress' => 'nullable|string',
+            'mailfromname' => 'nullable|string',
+        ]);
+
+        if ($validator->fails()) {
+            return redirect(route('admin.settings.index') . '#misc')->with('error', __('Misc settings have not been updated!'))->withErrors($validator)
+                ->withInput();
+        }
+
+        if ($request->hasFile('icon')) {
+            $request->file('icon')->storeAs('public', 'icon.png');
+        }
+        if ($request->hasFile('favicon')) {
+            $request->file('favicon')->storeAs('public', 'favicon.ico');
+        }
+
+        $values = [
+            "SETTINGS::DISCORD:BOT_TOKEN" => "discord-bot-token",
+            "SETTINGS::DISCORD:CLIENT_ID" => "discord-client-id",
+            "SETTINGS::DISCORD:CLIENT_SECRET" => "discord-client-secret",
+            "SETTINGS::DISCORD:GUILD_ID" => "discord-guild-id",
+            "SETTINGS::DISCORD:INVITE_URL" => "discord-invite-url",
+            "SETTINGS::DISCORD:ROLE_ID" => "discord-role-id",
+            "SETTINGS::RECAPTCHA:SITE_KEY" => "recaptcha-site-key",
+            "SETTINGS::RECAPTCHA:SECRET_KEY" => "recaptcha-secret-key",
+            "SETTINGS::RECAPTCHA:ENABLED" => "enable-recaptcha",
+            "SETTINGS::MAIL:MAILER" => "mailservice",
+            "SETTINGS::MAIL:HOST" => "mailhost",
+            "SETTINGS::MAIL:PORT" => "mailport",
+            "SETTINGS::MAIL:USERNAME" => "mailusername",
+            "SETTINGS::MAIL:PASSWORD" => "mailpassword",
+            "SETTINGS::MAIL:ENCRYPTION" => "mailencryption",
+            "SETTINGS::MAIL:FROM_ADDRESS" => "mailfromadress",
+            "SETTINGS::MAIL:FROM_NAME" => "mailfromname",
+
+        ];
+
+        foreach ($values as $key => $value) {
+            $param = $request->get($value);
+
+            Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
+            Cache::forget("setting" . ':' . $key);
+        }
+
+
+        return redirect(route('admin.settings.index') . '#misc')->with('success', __('Misc settings updated!'));
+    }
+}

+ 61 - 0
app/Classes/Settings/Payments.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App\Classes\Settings;
+
+use App\Models\Settings;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Validator;
+
+
+class Payments
+{
+    public function __construct()
+    {
+        return;
+    }
+
+
+    public function updateSettings(Request $request)
+    {
+        $validator = Validator::make($request->all(), [
+            "paypal-client_id" => "nullable|string",
+            "paypal-client-secret" => "nullable|string",
+            "paypal-sandbox-secret" => "nullable|string",
+            "stripe-secret-key" => "nullable|string",
+            "stripe-endpoint-secret" => "nullable|string",
+            "stripe-test-secret-key" => "nullable|string",
+            "stripe-test-endpoint-secret" => "nullable|string",
+            "stripe-methods" => "nullable|string",
+            "sales-tax" => "nullable|numeric",
+        ]);
+        if ($validator->fails()) {
+            return redirect(route('admin.settings.index') . '#payment')->with('error', __('Payment settings have not been updated!'))->withErrors($validator)
+                ->withInput();
+        }
+
+        $values = [
+            //SETTINGS::VALUE => REQUEST-VALUE (coming from the html-form)
+            "SETTINGS::PAYMENTS:PAYPAL:SECRET" => "paypal-client-secret",
+            "SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID" => "paypal-client-id",
+            "SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET" => "paypal-sandbox-secret",
+            "SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID" => "paypal-sandbox-id",
+            "SETTINGS::PAYMENTS:STRIPE:SECRET" => "stripe-secret",
+            "SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET" => "stripe-endpoint-secret",
+            "SETTINGS::PAYMENTS:STRIPE:TEST_SECRET" => "stripe-test-secret",
+            "SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET" => "stripe-endpoint-test-secret",
+            "SETTINGS::PAYMENTS:STRIPE:METHODS" => "stripe-methods",
+            "SETTINGS::PAYMENTS:SALES_TAX" => "sales-tax"
+        ];
+
+
+        foreach ($values as $key => $value) {
+            $param = $request->get($value);
+
+            Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
+            Cache::forget("setting" . ':' . $key);
+        }
+
+        return redirect(route('admin.settings.index') . '#payment')->with('success', __('Payment settings updated!'));
+    }
+}

+ 73 - 0
app/Classes/Settings/System.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace App\Classes\Settings;
+
+use App\Models\Settings;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Validator;
+
+class System
+{
+
+
+    public function __construct()
+    {
+        return;
+    }
+
+
+
+    public function updateSettings(Request $request)
+    {
+        $validator = Validator::make($request->all(), [
+            "register-ip-check" => "string",
+            "server-create-charge-first-hour" => "string",
+            "credits-display-name" => "required|string",
+            "allocation-limit" => "required|min:0|integer",
+            "force-email-verification" => "string",
+            "force-discord-verification" => "string",
+            "initial-credits" => "required|min:0|integer",
+            "initial-server-limit" => "required|min:0|integer",
+            "credits-reward-amount-discord" => "required|min:0|integer",
+            "credits-reward-amount-email" => "required|min:0|integer",
+            "server-limit-discord" => "required|min:0|integer",
+            "server-limit-email" => "required|min:0|integer",
+            "pterodactyl-api-key" => "required|string",
+            "pterodactyl-url" => "required|string",
+
+        ]);
+        if ($validator->fails()) {
+            return redirect(route('admin.settings.index') . '#system')->with('error', __('System settings have not been updated!'))->withErrors($validator)
+                ->withInput();
+        }
+
+
+        $values = [
+            "SETTINGS::SYSTEM:REGISTER_IP_CHECK" => "register-ip-check",
+            "SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR" => "server-create-charge-first-hour",
+            "SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME" => "credits-display-name",
+            "SETTINGS::SERVER:ALLOCATION_LIMIT" => "allocation-limit",
+            "SETTINGS::USER:FORCE_DISCORD_VERIFICATION" => "force-discord-verification",
+            "SETTINGS::USER:FORCE_EMAIL_VERIFICATION" => "force-email-verification",
+            "SETTINGS::USER:INITIAL_CREDITS" => "initial-credits",
+            "SETTINGS::USER:INITIAL_SERVER_LIMIT" => "initial-server-limit",
+            "SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD" => "credits-reward-amount-discord",
+            "SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL" => "credits-reward-amount-email",
+            "SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD" => "server-limit-discord",
+            "SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL" => "server-limit-email",
+            "SETTINGS::MISC:PHPMYADMIN:URL" => "phpmyadmin-url",
+            "SETTINGS::SYSTEM:PTERODACTYL:URL" => "pterodactyl-url",
+            "SETTINGS::SYSTEM:PTERODACTYL:TOKEN" => "pterodactyl-api-key",
+        ];
+
+
+        foreach ($values as $key => $value) {
+            $param = $request->get($value);
+
+            Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
+            Cache::forget("setting" . ':' . $key);
+        }
+        return redirect(route('admin.settings.index') . '#system')->with('success', __('System settings updated!'));
+    }
+}

+ 0 - 123
app/Http/Controllers/Admin/ConfigurationController.php

@@ -1,123 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin;
-
-use App\Http\Controllers\Controller;
-use App\Models\Configuration;
-use Illuminate\Contracts\Foundation\Application;
-use Illuminate\Contracts\View\Factory;
-use Illuminate\Contracts\View\View;
-use Illuminate\Http\Request;
-use Illuminate\Http\Response;
-
-class ConfigurationController extends Controller
-{
-    /**
-     * Display a listing of the resource.
-     *
-     * @return Application|Factory|View|Response
-     */
-    public function index()
-    {
-        return view('admin.configurations.index');
-    }
-
-    /**
-     * Show the form for creating a new resource.
-     *
-     * @return Response
-     */
-    public function create()
-    {
-        //
-    }
-
-    /**
-     * Store a newly created resource in storage.
-     *
-     * @param Request $request
-     * @return Response
-     */
-    public function store(Request $request)
-    {
-        //
-    }
-
-    /**
-     * Display the specified resource.
-     *
-     * @param Configuration $configuration
-     * @return Response
-     */
-    public function show(Configuration $configuration)
-    {
-        //
-    }
-
-    /**
-     * Show the form for editing the specified resource.
-     *
-     * @param Configuration $configuration
-     * @return Response
-     */
-    public function edit(Configuration $configuration)
-    {
-        //
-    }
-
-    /**
-     * Update the specified resource in storage.
-     *
-     * @param Request $request
-     * @param Configuration $configuration
-     * @return Response
-     */
-    public function update(Request $request, Configuration $configuration)
-    {
-        //
-    }
-
-    /**
-     * @param Request $request
-     * @return \Illuminate\Http\RedirectResponse
-     */
-    public function updatevalue(Request $request)
-    {
-        $configuration = Configuration::findOrFail($request->input('key'));
-
-        $request->validate([
-            'key'   => 'required|string|max:191',
-            'value' => 'required|string|max:191',
-        ]);
-
-        $configuration->update($request->all());
-
-        return redirect()->route('admin.configurations.index')->with('success', __('configuration has been updated!'));
-    }
-
-    /**
-     * Remove the specified resource from storage.
-     *
-     * @param Configuration $configuration
-     * @return Response
-     */
-    public function destroy(Configuration $configuration)
-    {
-        //
-    }
-
-    public function datatable()
-    {
-        $query = Configuration::query();
-
-        return datatables($query)
-            ->addColumn('actions', function (Configuration $configuration) {
-                return '<button data-content="'.__("Edit").'" data-toggle="popover" data-trigger="hover" data-placement="top" onclick="configuration.parse(\'' . $configuration->key . '\',\'' . $configuration->value . '\',\'' . $configuration->type . '\')" data-content="Edit" data-trigger="hover" data-toggle="tooltip" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></button> ';
-            })
-            ->editColumn('created_at', function (Configuration $configuration) {
-                return $configuration->created_at ? $configuration->created_at->diffForHumans() : '';
-            })
-            ->rawColumns(['actions'])
-            ->make();
-    }
-}

+ 3 - 2
app/Http/Controllers/Admin/CreditProductController.php

@@ -3,6 +3,7 @@
 namespace App\Http\Controllers\Admin;
 
 use App\Models\CreditProduct;
+use App\Models\Settings;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\View\Factory;
 use Illuminate\Contracts\View\View;
@@ -25,8 +26,8 @@ class CreditProductController extends Controller
 
         if (
             env('APP_ENV') == 'local' ||
-            env('PAYPAL_SECRET') && env('PAYPAL_CLIENT_ID') ||
-            env('STRIPE_SECRET') && env('STRIPE_ENDPOINT_SECRET') && env('STRIPE_METHODS')
+            Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:SECRET") && Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID") ||
+            Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:SECRET") && Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET") && Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:METHODS")
         ) $isPaymentSetup = true;
 
         return view('admin.store.index', [

+ 70 - 0
app/Http/Controllers/Admin/InvoiceController.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Models\Invoice;
+use Illuminate\Http\Request;
+use Throwable;
+use ZipArchive;
+
+class InvoiceController extends Controller
+{
+
+    public function downloadAllInvoices()
+    {
+        $zip = new ZipArchive;
+        $zip_safe_path = storage_path('invoices.zip');
+        $res = $zip->open($zip_safe_path, ZipArchive::CREATE | ZipArchive::OVERWRITE);
+        $result = $dthis::rglob(storage_path('app/invoice/*'));
+        if ($res === TRUE) {
+            $zip->addFromString("1. Info.txt", __("Created at") . " " . now()->format("d.m.Y"));
+            foreach ($result as $file) {
+                if (file_exists($file) && is_file($file)) {
+                    $zip->addFile($file, basename($file));
+                }
+            }
+            $zip->close();
+        }
+        return response()->download($zip_safe_path);
+    }
+
+    /**
+     * @param $pattern
+     * @param $flags
+     * @return array|false
+     */
+    public function rglob($pattern, $flags = 0)
+    {
+        $files = glob($pattern, $flags);
+        foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
+            $files = array_merge($files, $this::rglob($dir . '/' . basename($pattern), $flags));
+        }
+        return $files;
+    }
+
+    /**
+     * @param $paymentID
+     * @param $date
+     */
+    public function downloadSingleInvoice(Request $request)
+    {
+        $id = $request->id;
+        try {
+            $query = Invoice::where('payment_id', '=', $id)->firstOrFail();
+        } catch (Throwable $e) {
+            return redirect()->back()->with("error", __("Error!"));
+        }
+
+        $invoice_path = storage_path('app/invoice/' . $query->invoice_user . '/' . $query->created_at->format("Y") . '/' . $query->invoice_name . '.pdf');
+
+        if (!file_exists($invoice_path)) {
+            return redirect()->back()->with("error", __("Error!"));
+        }
+
+
+        return response()->download($invoice_path);
+
+    }
+
+}

+ 47 - 30
app/Http/Controllers/Admin/PaymentController.php

@@ -4,10 +4,10 @@ namespace App\Http\Controllers\Admin;
 
 use App\Events\UserUpdateCreditsEvent;
 use App\Http\Controllers\Controller;
-use App\Models\Configuration;
 use App\Models\InvoiceSettings;
 use App\Models\Payment;
 use App\Models\CreditProduct;
+use App\Models\Settings;
 use App\Models\User;
 use App\Notifications\InvoiceNotification;
 use App\Notifications\ConfirmPaymentNotification;
@@ -134,7 +134,7 @@ class PaymentController extends Controller
      */
     protected function getPaypalClientId()
     {
-        return env('APP_ENV') == 'local' ? env('PAYPAL_SANDBOX_CLIENT_ID') : env('PAYPAL_CLIENT_ID');
+        return env('APP_ENV') == 'local' ?  Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID") : Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID");
     }
 
     /**
@@ -142,7 +142,7 @@ class PaymentController extends Controller
      */
     protected function getPaypalClientSecret()
     {
-        return env('APP_ENV') == 'local' ? env('PAYPAL_SANDBOX_SECRET') : env('PAYPAL_SECRET');
+        return env('APP_ENV') == 'local' ? Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET") : Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:SECRET");
     }
 
     /**
@@ -167,9 +167,9 @@ class PaymentController extends Controller
                 $user->increment('credits', $creditProduct->quantity);
 
                 //update server limit
-                if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
-                    if ($user->server_limit < Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
-                        $user->update(['server_limit' => Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
+                if (Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
+                    if ($user->server_limit < Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
+                        $user->update(['server_limit' => Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
                     }
                 }
 
@@ -197,7 +197,11 @@ class PaymentController extends Controller
 
                 event(new UserUpdateCreditsEvent($user));
 
-                $this->createInvoice($user, $payment, 'paid');
+                //only create invoice if SETTINGS::INVOICE:ENABLED is true
+                if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
+                    $this->createInvoice($user, $payment, 'paid');
+                }
+
 
                 //redirect back to home
                 return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
@@ -266,7 +270,7 @@ class PaymentController extends Controller
             ],
 
             'mode' => 'payment',
-            "payment_method_types" => str_getcsv(str_replace(' ', '', env('STRIPE_METHODS'))),
+            "payment_method_types" => str_getcsv(config("SETTINGS::PAYMENTS:STRIPE:METHODS")),
             'success_url' => route('payment.StripeSuccess',  ['product' => $creditProduct->id]) . '&session_id={CHECKOUT_SESSION_ID}',
             'cancel_url' => route('payment.Cancel'),
         ]);
@@ -304,9 +308,9 @@ class PaymentController extends Controller
                 $user->increment('credits', $creditProduct->quantity);
 
                 //update server limit
-                if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
-                    if ($user->server_limit < Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
-                        $user->update(['server_limit' => Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
+                if (Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
+                    if ($user->server_limit < Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
+                        $user->update(['server_limit' => Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
                     }
                 }
 
@@ -336,7 +340,10 @@ class PaymentController extends Controller
 
                 event(new UserUpdateCreditsEvent($user));
 
-                $this->createInvoice($user, $payment, 'paid');
+                //only create invoice if SETTINGS::INVOICE:ENABLED is true
+                if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
+                    $this->createInvoice($user, $payment, 'paid');
+                }
 
                 //redirect back to home
                 return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
@@ -359,7 +366,10 @@ class PaymentController extends Controller
                         'credit_product_id' => $creditProduct->id,
                     ]);
 
-                    $this->createInvoice($user, $payment, 'processing');
+                    //only create invoice if SETTINGS::INVOICE:ENABLED is true
+                    if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
+                        $this->createInvoice($user, $payment, 'paid');
+                    }
 
                     //redirect back to home
                     return redirect()->route('home')->with('success', __('Your payment is being processed!'));
@@ -398,9 +408,9 @@ class PaymentController extends Controller
                 $user->increment('credits', $payment->amount);
 
                 //update server limit
-                if (Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
-                    if ($user->server_limit < Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
-                        $user->update(['server_limit' => Configuration::getValueByKey('SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
+                if (Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
+                    if ($user->server_limit < Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
+                        $user->update(['server_limit' => Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
                     }
                 }
 
@@ -416,7 +426,10 @@ class PaymentController extends Controller
                 $user->notify(new ConfirmPaymentNotification($payment));
                 event(new UserUpdateCreditsEvent($user));
 
-                $this->createInvoice($user, $payment, 'paid');
+                //only create invoice if SETTINGS::INVOICE:ENABLED is true
+                if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
+                    $this->createInvoice($user, $payment, 'paid');
+                }
             }
         } catch (HttpException $ex) {
             abort(422);
@@ -474,8 +487,8 @@ class PaymentController extends Controller
     protected function getStripeSecret()
     {
         return env('APP_ENV') == 'local'
-            ?  env('STRIPE_TEST_SECRET')
-            :  env('STRIPE_SECRET');
+            ?  Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:TEST_SECRET")
+            :  Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:SECRET");
     }
 
     /**
@@ -484,8 +497,8 @@ class PaymentController extends Controller
     protected function getStripeEndpointSecret()
     {
         return env('APP_ENV') == 'local'
-            ?  env('STRIPE_ENDPOINT_TEST_SECRET')
-            :  env('STRIPE_ENDPOINT_SECRET');
+            ?  Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET")
+            :  Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET");
     }
 
 
@@ -495,17 +508,16 @@ class PaymentController extends Controller
         //create invoice
         $lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id");
         $newInvoiceID = $lastInvoiceID + 1;
-        $InvoiceSettings = InvoiceSettings::query()->first();
         $logoPath = storage_path('app/public/logo.png');
 
         $seller = new Party([
-            'name' => $InvoiceSettings->company_name,
-            'phone' => $InvoiceSettings->company_phone,
-            'address' => $InvoiceSettings->company_adress,
-            'vat' => $InvoiceSettings->company_vat,
+            'name' => Settings::getValueByKey("SETTINGS::INVOICE:COMPANY_NAME"),
+            'phone' => Settings::getValueByKey("SETTINGS::INVOICE:COMPANY_PHONE"),
+            'address' => Settings::getValueByKey("SETTINGS::INVOICE:COMPANY_ADDRESS"),
+            'vat' => Settings::getValueByKey("SETTINGS::INVOICE:COMPANY_VAT"),
             'custom_fields' => [
-                'E-Mail' => $InvoiceSettings->company_mail,
-                "Web" => $InvoiceSettings->company_web
+                'E-Mail' => Settings::getValueByKey("SETTINGS::INVOICE:COMPANY_MAIL"),
+                "Web" => Settings::getValueByKey("SETTINGS::INVOICE:COMPANY_WEBSITE")
             ],
         ]);
 
@@ -540,7 +552,7 @@ class PaymentController extends Controller
             ->series(now()->format('mY'))
             ->delimiter("-")
             ->sequence($newInvoiceID)
-            ->serialNumberFormat($InvoiceSettings->invoice_prefix . '{DELIMITER}{SERIES}{SEQUENCE}')
+            ->serialNumberFormat(Settings::getValueByKey("SETTINGS::INVOICE:PREFIX") . '{DELIMITER}{SERIES}{SEQUENCE}')
             ->notes($notes);
 
         if (file_exists($logoPath)) {
@@ -591,6 +603,11 @@ class PaymentController extends Controller
             ->editColumn('created_at', function (Payment $payment) {
                 return $payment->created_at ? $payment->created_at->diffForHumans() : '';
             })
-            ->make();
+            ->addColumn('actions', function (Payment $payment) {
+                return ' <a data-content="' . __("Download") . '" data-toggle="popover" data-trigger="hover" data-placement="top"  href="' . route('admin.invoices.downloadSingleInvoice', "id=" . $payment->payment_id) . '" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-file-download"></i></a>
+';
+            })
+            ->rawColumns(['actions'])
+            ->make(true);
     }
 }

+ 2 - 5
app/Http/Controllers/Admin/ProductController.php

@@ -3,12 +3,10 @@
 namespace App\Http\Controllers\Admin;
 
 use App\Http\Controllers\Controller;
-use App\Models\Egg;
 use App\Models\Location;
 use App\Models\Nest;
-use App\Models\Node;
-use App\Models\Configuration;
 use App\Models\Product;
+use App\Models\Settings;
 use Exception;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\View\Factory;
@@ -16,7 +14,6 @@ use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
-use Illuminate\Http\Response;
 
 class ProductController extends Controller
 {
@@ -97,7 +94,7 @@ class ProductController extends Controller
     {
         return view('admin.products.show', [
             'product' => $product,
-            'minimum_credits' => Configuration::getValueByKey("MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER"),
+            'minimum_credits' => Settings::getValueByKey("SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER"),
         ]);
     }
 

+ 2 - 1
app/Http/Controllers/Admin/ServerController.php

@@ -6,6 +6,7 @@ use App\Classes\Pterodactyl;
 use App\Classes\PterodactylWrapper;
 use App\Http\Controllers\Controller;
 use App\Models\Server;
+use App\Models\Settings;
 use Exception;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\View\Factory;
@@ -161,7 +162,7 @@ class ServerController extends Controller
                 return $server->suspended ? $server->suspended->diffForHumans() : '';
             })
             ->editColumn('name', function (Server $server) {
-                return '<a class="text-info" target="_blank" href="' . env('PTERODACTYL_URL', 'http://localhost') . '/admin/servers/view/' . $server->pterodactyl_id . '">' . $server->name . '</a>';
+                return '<a class="text-info" target="_blank" href="' . Settings::getValueByKey("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/admin/servers/view/' . $server->pterodactyl_id . '">' . $server->name . '</a>';
             })
             ->rawColumns(['user', 'actions', 'status', 'name'])
             ->make();

+ 47 - 61
app/Http/Controllers/Admin/SettingsController.php

@@ -3,13 +3,12 @@
 namespace App\Http\Controllers\Admin;
 
 use App\Http\Controllers\Controller;
-use App\Models\InvoiceSettings;
+use App\Models\Settings;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\View\Factory;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
-use ZipArchive;
 
 class SettingsController extends Controller
 {
@@ -20,81 +19,68 @@ class SettingsController extends Controller
      */
     public function index()
     {
-        /** @var InvoiceSettings $invoiceSettings */
-        $invoiceSettings = InvoiceSettings::first();
-
-        return view('admin.settings.index', $invoiceSettings->toArray());
-    }
-
-    public function updateIcons(Request $request)
-    {
-        $request->validate([
-            'icon' => 'nullable|max:10000|mimes:jpg,png,jpeg',
-            'favicon' => 'nullable|max:10000|mimes:ico',
-        ]);
-
-        if ($request->hasFile('icon')) {
-            $request->file('icon')->storeAs('public', 'icon.png');
+        //Get all tabs as laravel view paths
+        $tabs = [];
+        foreach (glob(resource_path('views/admin/settings/tabs/*.blade.php')) as $filename) {
+            $tabs[] = 'admin.settings.tabs.' . basename($filename, '.blade.php');
         }
 
-        if ($request->hasFile('favicon')) {
-            $request->file('favicon')->storeAs('public', 'favicon.ico');
+        //Generate a html list item for each tab based on tabs file basename, set first tab as active
+        $tabListItems = [];
+        foreach ($tabs as $tab) {
+            $tabName = str_replace('admin.settings.tabs.', '', $tab);
+            $tabListItems[] = '<li class="nav-item">
+            <a class="nav-link ' . (empty($tabListItems) ? 'active' : '') . '" data-toggle="pill" href="#' . $tabName . '">
+            ' . __(ucfirst($tabName)) . '
+            </a></li>';
         }
 
-        return redirect()->route('admin.settings.index')->with('success', __('Icons updated!'));
+        return view('admin.settings.index', [
+            'tabs' => $tabs,
+            'tabListItems' => $tabListItems,
+        ]);
     }
 
-    public function updateInvoiceSettings(Request $request)
+
+    public function updatevalue(Request $request)
     {
-        $request->validate([
-            'logo' => 'nullable|max:10000|mimes:jpg,png,jpeg',
-        ]);
+        $setting = Settings::findOrFail($request->input('key'));
 
-        InvoiceSettings::updateOrCreate([
-            'id' => "1"
-        ], [
-            'company_name' => $request->get('company-name'),
-            'company_adress' => $request->get('company-address'),
-            'company_phone' => $request->get('company-phone'),
-            'company_mail' => $request->get('company-mail'),
-            'company_vat' => $request->get('company-vat'),
-            'company_web' => $request->get('company-web'),
-            'invoice_prefix' => $request->get('invoice-prefix'),
+        $request->validate([
+            'key'   => 'required|string|max:191',
+            'value' => 'required|string|max:191',
         ]);
 
-        if ($request->hasFile('logo')) {
-            $request->file('logo')->storeAs('public', 'logo.png');
-        }
+        $setting->update($request->all());
 
-
-        return redirect()->route('admin.settings.index')->with('success', 'Invoice settings updated!');
+        return redirect()->route('admin.settings.index')->with('success', __('configuration has been updated!'));
     }
 
-    public function downloadAllInvoices()
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param Settings $setting
+     * @return Response
+     */
+    public function destroy(Settings $setting)
     {
-        $zip = new ZipArchive;
-        $zip_safe_path = storage_path('invoices.zip');
-        $res = $zip->open($zip_safe_path, ZipArchive::CREATE | ZipArchive::OVERWRITE);
-        $result = $this::rglob(storage_path('app/invoice/*'));
-        if ($res === TRUE) {
-            $zip->addFromString("1. Info.txt", "This Archive contains all Invoices from all Users!\nIf there are no Invoices here, no Invoices have ever been created!");
-            foreach ($result as $file) {
-                if (file_exists($file) && is_file($file)) {
-                    $zip->addFile($file, basename($file));
-                }
-            }
-            $zip->close();
-        }
-        return response()->download($zip_safe_path);
+        //
     }
 
-    public function rglob($pattern, $flags = 0)
+    public function datatable()
     {
-        $files = glob($pattern, $flags);
-        foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
-            $files = array_merge($files, $this::rglob($dir . '/' . basename($pattern), $flags));
-        }
-        return $files;
+        $query = Settings::where('key', 'like', '%SYSTEM%')
+            ->orWhere('key', 'like', '%USER%')
+            ->orWhere('key', 'like', '%SERVER%');
+
+        return datatables($query)
+            ->addColumn('actions', function (Settings $setting) {
+                return '<button data-content="' . __("Edit") . '" data-toggle="popover" data-trigger="hover" data-placement="top" onclick="configuration.parse(\'' . $setting->key . '\',\'' . $setting->value . '\',\'' . $setting->type . '\')" data-content="Edit" data-trigger="hover" data-toggle="tooltip" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></button> ';
+            })
+            ->editColumn('created_at', function (Settings $setting) {
+                return $setting->created_at ? $setting->created_at->diffForHumans() : '';
+            })
+            ->rawColumns(['actions'])
+            ->make();
     }
-
 }

+ 2 - 1
app/Http/Controllers/Admin/UserController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 use App\Classes\Pterodactyl;
 use App\Events\UserUpdateCreditsEvent;
 use App\Http\Controllers\Controller;
+use App\Models\Settings;
 use App\Models\User;
 use App\Notifications\DynamicNotification;
 use Spatie\QueryBuilder\QueryBuilder;
@@ -300,7 +301,7 @@ class UserController extends Controller
                 return '<span class="badge ' . $badgeColor . '">' . $user->role . '</span>';
             })
             ->editColumn('name', function (User $user) {
-                return '<a class="text-info" target="_blank" href="' . env('PTERODACTYL_URL', 'http://localhost') . '/admin/users/view/' . $user->pterodactyl_id . '">' . $user->name . '</a>';
+                return '<a class="text-info" target="_blank" href="' . Settings::getValueByKey("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/admin/users/view/' . $user->pterodactyl_id . '">' . $user->name . '</a>';
             })
             ->orderColumn('last_seen', function ($query, $order) {
                 $query->orderBy('last_seen', $order);

+ 3 - 3
app/Http/Controllers/Api/UserController.php

@@ -5,8 +5,8 @@ namespace App\Http\Controllers\Api;
 use App\Classes\Pterodactyl;
 use App\Events\UserUpdateCreditsEvent;
 use App\Http\Controllers\Controller;
-use App\Models\Configuration;
 use App\Models\DiscordUser;
+use App\Models\Settings;
 use App\Models\User;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\Pagination\LengthAwarePaginator;
@@ -242,8 +242,8 @@ class UserController extends Controller
         $user = User::create([
             'name' => $request->input('name'),
             'email' => $request->input('email'),
-            'credits' => Configuration::getValueByKey('INITIAL_CREDITS', 150),
-            'server_limit' => Configuration::getValueByKey('INITIAL_SERVER_LIMIT', 1),
+            'credits' => Settings::getValueByKey('SETTINGS::USER:INITIAL_CREDITS', 150),
+            'server_limit' => Settings::getValueByKey('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1),
             'password' => Hash::make($request->input('password')),
         ]);
 

+ 13 - 5
app/Http/Controllers/Auth/LoginController.php

@@ -41,17 +41,25 @@ class LoginController extends Controller
 
     public function login(Request $request)
     {
-        $request->validate([
+
+        $validationRules = [
             $this->username()      => 'required|string',
             'password'             => 'required|string',
-            'g-recaptcha-response' => ['required','recaptcha'],
-        ]);
+        ];
+        if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
+            $validationRules['g-recaptcha-response'] = ['required', 'recaptcha'];
+        }
+        $request->validate($validationRules);
+
+
 
         // If the class is using the ThrottlesLogins trait, we can automatically throttle
         // the login attempts for this application. We'll key this by the username and
         // the IP address of the client making these requests into this application.
-        if (method_exists($this, 'hasTooManyLoginAttempts') &&
-            $this->hasTooManyLoginAttempts($request)) {
+        if (
+            method_exists($this, 'hasTooManyLoginAttempts') &&
+            $this->hasTooManyLoginAttempts($request)
+        ) {
             $this->fireLockoutEvent($request);
 
             return $this->sendLockoutResponse($request);

+ 17 - 19
app/Http/Controllers/Auth/RegisterController.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Auth;
 
 use App\Classes\Pterodactyl;
 use App\Http\Controllers\Controller;
-use App\Models\Configuration;
+use App\Models\Settings;
 use App\Models\User;
 use App\Providers\RouteServiceProvider;
 use Illuminate\Foundation\Auth\RegistersUsers;
@@ -54,30 +54,28 @@ class RegisterController extends Controller
      */
     protected function validator(array $data)
     {
-        if (Configuration::getValueByKey('REGISTER_IP_CHECK', 'true') == 'true') {
+        $validationRules = [
+            'name'                 => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
+            'email'                => ['required', 'string', 'email', 'max:64', 'unique:users'],
+            'password'             => ['required', 'string', 'min:8', 'confirmed'],
+        ];
+        if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
+            $validationRules['g-recaptcha-response'] = ['required', 'recaptcha'];
+        }
+
+        if (config('SETTINGS::SYSTEM:REGISTER_IP_CHECK', 'true') == 'true') {
 
             //check if ip has already made an account
             $data['ip'] = session()->get('ip') ?? request()->ip();
             if (User::where('ip', '=', request()->ip())->exists()) session()->put('ip', request()->ip());
+            $validationRules['ip']  = ['unique:users'];
+            return Validator::make($data, $validationRules, [
+                'ip.unique' => "You have already made an account! Please contact support if you think this is incorrect."
 
-            return Validator::make($data, [
-                'name'                 => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
-                'email'                => ['required', 'string', 'email', 'max:64', 'unique:users'],
-                'password'             => ['required', 'string', 'min:8', 'confirmed'],
-                'g-recaptcha-response' => ['recaptcha'],
-                'ip'                   => ['unique:users'],
-            ], [
-                'ip.unique' => __("You have already made an account with us! Please contact support if you think this is incorrect.")
             ]);
         }
 
-        return Validator::make($data, [
-            'name'                 => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
-            'email'                => ['required', 'string', 'email', 'max:64', 'unique:users'],
-            'password'             => ['required', 'string', 'min:8', 'confirmed'],
-            'g-recaptcha-response' => ['recaptcha'],
-        ]);
-
+        return Validator::make($data, $validationRules);
     }
 
     /**
@@ -91,8 +89,8 @@ class RegisterController extends Controller
         $user = User::create([
             'name'         => $data['name'],
             'email'        => $data['email'],
-            'credits'      => Configuration::getValueByKey('INITIAL_CREDITS', 150),
-            'server_limit' => Configuration::getValueByKey('INITIAL_SERVER_LIMIT', 1),
+            'credits'      => config('SETTINGS::USER:INITIAL_CREDITS', 150),
+            'server_limit' => config('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1),
             'password'     => Hash::make($data['password']),
         ]);
 

+ 7 - 7
app/Http/Controllers/Auth/SocialiteController.php

@@ -3,8 +3,8 @@
 namespace App\Http\Controllers\Auth;
 
 use App\Http\Controllers\Controller;
-use App\Models\Configuration;
 use App\Models\DiscordUser;
+use App\Models\Settings;
 use App\Models\User;
 use App\Models\Voucher;
 use Illuminate\Support\Facades\Auth;
@@ -15,7 +15,7 @@ class SocialiteController extends Controller
 {
     public function redirect()
     {
-        $scopes = !empty(env('DISCORD_BOT_TOKEN')) && !empty(env('DISCORD_GUILD_ID')) ? ['guilds.join'] : [];
+        $scopes = !empty(Settings::getValueByKey("SETTINGS::DISCORD:BOT_TOKEN")) && !empty(Settings::getValueByKey("SETTINGS::DISCORD:GUILD_ID")) ? ['guilds.join'] : [];
 
         return Socialite::driver('discord')
             ->scopes($scopes)
@@ -31,17 +31,17 @@ class SocialiteController extends Controller
         /** @var User $user */
         $user = Auth::user();
         $discord = Socialite::driver('discord')->user();
-        $botToken = env('DISCORD_BOT_TOKEN');
-        $guildId = env('DISCORD_GUILD_ID');
-        $roleId = env('DISCORD_ROLE_ID');
+        $botToken = Settings::getValueByKey("SETTINGS::DISCORD:BOT_TOKEN");
+        $guildId = Settings::getValueByKey("SETTINGS::DISCORD:GUILD_ID");
+        $roleId = Settings::getValueByKey("SETTINGS::DISCORD:ROLE_ID");
 
         //save / update discord_users
         if (is_null($user->discordUser)) {
             //create discord user in db
             DiscordUser::create(array_merge($discord->user, ['user_id' => Auth::user()->id]));
             //update user
-            Auth::user()->increment('credits', Configuration::getValueByKey('CREDITS_REWARD_AFTER_VERIFY_DISCORD'));
-            Auth::user()->increment('server_limit', Configuration::getValueByKey('SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD'));
+            Auth::user()->increment('credits', Settings::getValueByKey('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD'));
+            Auth::user()->increment('server_limit', Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD'));
             Auth::user()->update(['discord_verified_at' => now()]);
         } else {
             $user->discordUser->update($discord->user);

+ 0 - 3
app/Http/Controllers/HomeController.php

@@ -2,10 +2,7 @@
 
 namespace App\Http\Controllers;
 
-use App\Models\Egg;
-use App\Models\Product;
 use App\Models\UsefulLink;
-use App\Models\Configuration;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
 

+ 11 - 14
app/Http/Controllers/ProfileController.php

@@ -2,14 +2,11 @@
 
 namespace App\Http\Controllers;
 
+
 use App\Classes\Pterodactyl;
-use App\Models\Configuration;
 use App\Models\User;
-use Illuminate\Contracts\View\Factory;
-use Illuminate\Contracts\View\View;
 use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
-use Illuminate\Http\Response;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Hash;
 use Illuminate\Validation\ValidationException;
@@ -21,9 +18,9 @@ class ProfileController extends Controller
     {
         return view('profile.index')->with([
             'user' => Auth::user(),
-            'credits_reward_after_verify_discord' => Configuration::getValueByKey('CREDITS_REWARD_AFTER_VERIFY_DISCORD'),
-            'force_email_verification' => Configuration::getValueByKey('FORCE_EMAIL_VERIFICATION'),
-            'force_discord_verification' => Configuration::getValueByKey('FORCE_DISCORD_VERIFICATION'),
+            'credits_reward_after_verify_discord' => config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD'),
+            'force_email_verification' => config('SETTINGS::USER:FORCE_EMAIL_VERIFICATION'),
+            'force_discord_verification' => config('SETTINGS::USER:FORCE_DISCORD_VERIFICATION'),
         ]);
     }
 
@@ -39,15 +36,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' ,
+                    'required',
                     function ($attribute, $value, $fail) use ($user) {
                         if (!Hash::check($value, $user->password)) {
-                            $fail('The '.$attribute.' is invalid.');
+                            $fail('The ' . $attribute . ' is invalid.');
                         }
                     },
                 ],
@@ -80,13 +77,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);
 
@@ -121,6 +118,6 @@ class ProfileController extends Controller
         ]);
         $user->sendEmailVerificationNotification();
 
-        return redirect()->route('profile.index')->with('success' , __('Profile updated'));
+        return redirect()->route('profile.index')->with('success', __('Profile updated'));
     }
 }

+ 5 - 5
app/Http/Controllers/ServerController.php

@@ -3,13 +3,13 @@
 namespace App\Http\Controllers;
 
 use App\Classes\Pterodactyl;
-use App\Models\Configuration;
 use App\Models\Egg;
 use App\Models\Location;
 use App\Models\Nest;
 use App\Models\Node;
 use App\Models\Product;
 use App\Models\Server;
+use App\Models\Settings;
 use App\Notifications\ServerCreationError;
 use Exception;
 use Illuminate\Database\Eloquent\Builder;
@@ -107,7 +107,7 @@ class ServerController extends Controller
             if (
                 Auth::user()->credits <
                 ($product->minimum_credits == -1
-                    ? Configuration::getValueByKey('MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER', 50)
+                    ? config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER', 50)
                     : $product->minimum_credits)
             ) {
                 return redirect()->route('servers.index')->with('error', "You do not have the required amount of " . CREDITS_DISPLAY_NAME . " to use this product!");
@@ -115,12 +115,12 @@ class ServerController extends Controller
         }
 
         //Required Verification for creating an server
-        if (Configuration::getValueByKey('FORCE_EMAIL_VERIFICATION', 'false') === 'true' && !Auth::user()->hasVerifiedEmail()) {
+        if (config('SETTINGS::USER:FORCE_EMAIL_VERIFICATION', 'false') === 'true' && !Auth::user()->hasVerifiedEmail()) {
             return redirect()->route('profile.index')->with('error', __("You are required to verify your email address before you can create a server."));
         }
 
         //Required Verification for creating an server
-        if (Configuration::getValueByKey('FORCE_DISCORD_VERIFICATION', 'false') === 'true' && !Auth::user()->discordUser) {
+        if (config('SETTINGS::USER:FORCE_DISCORD_VERIFICATION', 'false') === 'true' && !Auth::user()->discordUser) {
             return redirect()->route('profile.index')->with('error', __("You are required to link your discord account before you can create a server."));
         }
 
@@ -168,7 +168,7 @@ class ServerController extends Controller
             'identifier'     => $serverAttributes['identifier']
         ]);
 
-        if (Configuration::getValueByKey('SERVER_CREATE_CHARGE_FIRST_HOUR', 'true') == 'true') {
+        if (config('SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR', 'true') == 'true') {
             if ($request->user()->credits >= $server->product->getHourlyPrice()) {
                 $request->user()->decrement('credits', $server->product->getHourlyPrice());
             }

+ 5 - 5
app/Http/Controllers/StoreController.php

@@ -2,8 +2,8 @@
 
 namespace App\Http\Controllers;
 
-use App\Models\Configuration;
 use App\Models\CreditProduct;
+use App\Models\Settings;
 use Illuminate\Support\Facades\Auth;
 
 class StoreController extends Controller
@@ -15,17 +15,17 @@ class StoreController extends Controller
 
         if (
             env('APP_ENV') == 'local' ||
-            env('PAYPAL_SECRET') && env('PAYPAL_CLIENT_ID') ||
-            env('STRIPE_SECRET') && env('STRIPE_ENDPOINT_SECRET') && env('STRIPE_METHODS')
+            Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:SECRET") && Settings::getValueByKey("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID") ||
+            Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:SECRET") && Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET") && Settings::getValueByKey("SETTINGS::PAYMENTS:STRIPE:METHODS")
         ) $isPaymentSetup = true;
 
         //Required Verification for creating an server
-        if (Configuration::getValueByKey('FORCE_EMAIL_VERIFICATION', false) === 'true' && !Auth::user()->hasVerifiedEmail()) {
+        if (Settings::getValueByKey('SETTINGS::USER:FORCE_EMAIL_VERIFICATION', false) === 'true' && !Auth::user()->hasVerifiedEmail()) {
             return redirect()->route('profile.index')->with('error', __("You are required to verify your email address before you can purchase credits."));
         }
 
         //Required Verification for creating an server
-        if (Configuration::getValueByKey('FORCE_DISCORD_VERIFICATION', false) === 'true' && !Auth::user()->discordUser) {
+        if (Settings::getValueByKey('SETTINGS::USER:FORCE_DISCORD_VERIFICATION', false) === 'true' && !Auth::user()->discordUser) {
             return redirect()->route('profile.index')->with('error', __("You are required to link your discord account before you can purchase Credits"));
         }
 

+ 2 - 0
app/Http/Controllers/TranslationController.php

@@ -18,4 +18,6 @@ class TranslationController extends Controller
         Session::put('locale', $request->inputLocale);
         return redirect()->back();
     }
+
+
 }

+ 3 - 2
app/Http/Middleware/GlobalNames.php

@@ -3,6 +3,7 @@
 namespace App\Http\Middleware;
 
 use App\Models\Configuration;
+use App\Models\Settings;
 use Closure;
 use Illuminate\Http\Request;
 
@@ -17,10 +18,10 @@ class GlobalNames
      */
     public function handle(Request $request, Closure $next)
     {
-        define('CREDITS_DISPLAY_NAME' , Configuration::getValueByKey('CREDITS_DISPLAY_NAME' , 'Credits'));
+        define('CREDITS_DISPLAY_NAME', config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME', 'Credits'));
 
         $unsupported_lang_array = explode(',', config("app.unsupported_locales"));
-        $unsupported_lang_array = array_map( 'strtolower', $unsupported_lang_array );
+        $unsupported_lang_array = array_map('strtolower', $unsupported_lang_array);
         define('UNSUPPORTED_LANGS', $unsupported_lang_array);
 
         return $next($request);

+ 10 - 200
app/Http/Middleware/SetLocale.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Middleware;
 
+use App\Models\Settings;
 use Closure;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\App;
@@ -9,194 +10,6 @@ use Illuminate\Support\Facades\Session;
 
 class SetLocale
 {
-    function getLocaleCodeForDisplayLanguage($name){
-        $languageCodes = array(
-            "aa" => "Afar",
-            "ab" => "Abkhazian",
-            "ae" => "Avestan",
-            "af" => "Afrikaans",
-            "ak" => "Akan",
-            "am" => "Amharic",
-            "an" => "Aragonese",
-            "ar" => "Arabic",
-            "as" => "Assamese",
-            "av" => "Avaric",
-            "ay" => "Aymara",
-            "az" => "Azerbaijani",
-            "ba" => "Bashkir",
-            "be" => "Belarusian",
-            "bg" => "Bulgarian",
-            "bh" => "Bihari",
-            "bi" => "Bislama",
-            "bm" => "Bambara",
-            "bn" => "Bengali",
-            "bo" => "Tibetan",
-            "br" => "Breton",
-            "bs" => "Bosnian",
-            "ca" => "Catalan",
-            "ce" => "Chechen",
-            "ch" => "Chamorro",
-            "co" => "Corsican",
-            "cr" => "Cree",
-            "cs" => "Czech",
-            "cu" => "Church Slavic",
-            "cv" => "Chuvash",
-            "cy" => "Welsh",
-            "da" => "Danish",
-            "de" => "German",
-            "dv" => "Divehi",
-            "dz" => "Dzongkha",
-            "ee" => "Ewe",
-            "el" => "Greek",
-            "en" => "English",
-            "eo" => "Esperanto",
-            "es" => "Spanish",
-            "et" => "Estonian",
-            "eu" => "Basque",
-            "fa" => "Persian",
-            "ff" => "Fulah",
-            "fi" => "Finnish",
-            "fj" => "Fijian",
-            "fo" => "Faroese",
-            "fr" => "French",
-            "fy" => "Western Frisian",
-            "ga" => "Irish",
-            "gd" => "Scottish Gaelic",
-            "gl" => "Galician",
-            "gn" => "Guarani",
-            "gu" => "Gujarati",
-            "gv" => "Manx",
-            "ha" => "Hausa",
-            "he" => "Hebrew",
-            "hi" => "Hindi",
-            "ho" => "Hiri Motu",
-            "hr" => "Croatian",
-            "ht" => "Haitian",
-            "hu" => "Hungarian",
-            "hy" => "Armenian",
-            "hz" => "Herero",
-            "ia" => "Interlingua (International Auxiliary Language Association)",
-            "id" => "Indonesian",
-            "ie" => "Interlingue",
-            "ig" => "Igbo",
-            "ii" => "Sichuan Yi",
-            "ik" => "Inupiaq",
-            "io" => "Ido",
-            "is" => "Icelandic",
-            "it" => "Italian",
-            "iu" => "Inuktitut",
-            "ja" => "Japanese",
-            "jv" => "Javanese",
-            "ka" => "Georgian",
-            "kg" => "Kongo",
-            "ki" => "Kikuyu",
-            "kj" => "Kwanyama",
-            "kk" => "Kazakh",
-            "kl" => "Kalaallisut",
-            "km" => "Khmer",
-            "kn" => "Kannada",
-            "ko" => "Korean",
-            "kr" => "Kanuri",
-            "ks" => "Kashmiri",
-            "ku" => "Kurdish",
-            "kv" => "Komi",
-            "kw" => "Cornish",
-            "ky" => "Kirghiz",
-            "la" => "Latin",
-            "lb" => "Luxembourgish",
-            "lg" => "Ganda",
-            "li" => "Limburgish",
-            "ln" => "Lingala",
-            "lo" => "Lao",
-            "lt" => "Lithuanian",
-            "lu" => "Luba-Katanga",
-            "lv" => "Latvian",
-            "mg" => "Malagasy",
-            "mh" => "Marshallese",
-            "mi" => "Maori",
-            "mk" => "Macedonian",
-            "ml" => "Malayalam",
-            "mn" => "Mongolian",
-            "mr" => "Marathi",
-            "ms" => "Malay",
-            "mt" => "Maltese",
-            "my" => "Burmese",
-            "na" => "Nauru",
-            "nb" => "Norwegian Bokmal",
-            "nd" => "North Ndebele",
-            "ne" => "Nepali",
-            "ng" => "Ndonga",
-            "nl" => "Dutch",
-            "nn" => "Norwegian Nynorsk",
-            "no" => "Norwegian",
-            "nr" => "South Ndebele",
-            "nv" => "Navajo",
-            "ny" => "Chichewa",
-            "oc" => "Occitan",
-            "oj" => "Ojibwa",
-            "om" => "Oromo",
-            "or" => "Oriya",
-            "os" => "Ossetian",
-            "pa" => "Panjabi",
-            "pi" => "Pali",
-            "pl" => "Polish",
-            "ps" => "Pashto",
-            "pt" => "Portuguese",
-            "qu" => "Quechua",
-            "rm" => "Raeto-Romance",
-            "rn" => "Kirundi",
-            "ro" => "Romanian",
-            "ru" => "Russian",
-            "rw" => "Kinyarwanda",
-            "sa" => "Sanskrit",
-            "sc" => "Sardinian",
-            "sd" => "Sindhi",
-            "se" => "Northern Sami",
-            "sg" => "Sango",
-            "si" => "Sinhala",
-            "sk" => "Slovak",
-            "sl" => "Slovenian",
-            "sm" => "Samoan",
-            "so" => "Somali",
-            "sq" => "Albanian",
-            "sr" => "Serbian",
-            "ss" => "Swati",
-            "st" => "Southern Sotho",
-            "su" => "Sundanese",
-            "sv" => "Swedish",
-            "sw" => "Swahili",
-            "ta" => "Tamil",
-            "te" => "Telugu",
-            "tg" => "Tajik",
-            "th" => "Thai",
-            "ti" => "Tigrinya",
-            "tk" => "Turkmen",
-            "tl" => "Tagalog",
-            "tn" => "Tswana",
-            "to" => "Tonga",
-            "tr" => "Turkish",
-            "ts" => "Tsonga",
-            "tt" => "Tatar",
-            "tw" => "Twi",
-            "ty" => "Tahitian",
-            "ug" => "Uighur",
-            "uk" => "Ukrainian",
-            "ur" => "Urdu",
-            "uz" => "Uzbek",
-            "ve" => "Venda",
-            "vi" => "Vietnamese",
-            "vo" => "Volapuk",
-            "wa" => "Walloon",
-            "wo" => "Wolof",
-            "xh" => "Xhosa",
-            "yi" => "Yiddish",
-            "yo" => "Yoruba",
-            "za" => "Zhuang",
-            "zh" => "Chinese",
-            "zu" => "Zulu"
-        );
-        return array_search($name, array_flip($languageCodes));
-    }
 
     /**
      *
@@ -208,22 +21,19 @@ class SetLocale
      */
     public function handle($request, Closure $next)
     {
-
-            if (Session::has('locale')) {
-                $locale = Session::get('locale', config('app.locale'));
+        if (Session::has('locale')) {
+            $locale = Session::get('locale', config("SETTINGS::LOCALE:DEFAULT"));
+        } else {
+            if (config("SETTINGS::LOCALE:DYNAMIC") !== "true") {
+                $locale = config("SETTINGS::LOCALE:DEFAULT");
             } else {
-                if (!config('app.dynamic_locale')) {
-                    $locale = config('app.locale');
-                }else{
-                    $locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
-
-                    if (!in_array($locale, config('app.available_locales'))
-                        || in_array(strtolower($this->getLocaleCodeForDisplayLanguage($locale)), UNSUPPORTED_LANGS)) {
-                        $locale = config('app.locale');
-                    }
+                $locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
 
+                if (!in_array($locale, explode(',', config("SETTINGS::LOCALE:AVAILABLE")))) {
+                    $locale = config("SETTINGS::LOCALE:DEFAULT");
                 }
             }
+        }
         App::setLocale($locale);
 
         return $next($request);

+ 2 - 3
app/Listeners/UnsuspendServers.php

@@ -3,11 +3,10 @@
 namespace App\Listeners;
 
 use App\Events\UserUpdateCreditsEvent;
-use App\Models\Configuration;
 use App\Models\Server;
+use App\Models\Settings;
 use Exception;
 use Illuminate\Contracts\Queue\ShouldQueue;
-use Illuminate\Queue\InteractsWithQueue;
 
 class UnsuspendServers implements ShouldQueue
 {
@@ -20,7 +19,7 @@ class UnsuspendServers implements ShouldQueue
      */
     public function handle(UserUpdateCreditsEvent $event)
     {
-       if ($event->user->credits > Configuration::getValueByKey('MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER' , 50)){
+       if ($event->user->credits > Settings::getValueByKey('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER' , 50)){
            /** @var Server $server */
            foreach ($event->user->servers as $server){
                if ($server->isSuspended()) $server->unSuspend();

+ 3 - 5
app/Listeners/Verified.php

@@ -2,9 +2,7 @@
 
 namespace App\Listeners;
 
-use App\Models\Configuration;
-use Illuminate\Contracts\Queue\ShouldQueue;
-use Illuminate\Queue\InteractsWithQueue;
+use App\Models\Settings;
 
 class Verified
 {
@@ -26,7 +24,7 @@ class Verified
      */
     public function handle($event)
     {
-        $event->user->increment('server_limit' , Configuration::getValueByKey('SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL'));
-        $event->user->increment('credits' , Configuration::getValueByKey('CREDITS_REWARD_AFTER_VERIFY_EMAIL'));
+        $event->user->increment('server_limit' , Settings::getValueByKey('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL'));
+        $event->user->increment('credits' , Settings::getValueByKey('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL'));
     }
 }

+ 1 - 1
app/Models/CreditProduct.php

@@ -59,7 +59,7 @@ class CreditProduct extends Model
     */
     public function getTaxPercent()
     {
-        $tax = Configuration::getValueByKey("SALES_TAX");
+        $tax = Settings::getValueByKey("SETTINGS::PAYMENTS:SALES_TAX");
         return $tax < 0 ? 0 : $tax;
     }
 

+ 0 - 23
app/Models/InvoiceSettings.php

@@ -1,23 +0,0 @@
-<?php
-
-namespace App\Models;
-
-use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Model;
-
-class InvoiceSettings extends Model
-{
-    use HasFactory;
-
-    protected $table = 'invoice_settings';
-
-    protected $fillable = [
-        'company_name',
-        'company_adress',
-        'company_phone',
-        'company_mail',
-        'company_vat',
-        'company_web',
-        'invoice_prefix'
-    ];
-}

+ 8 - 6
app/Models/Configuration.php → app/Models/Settings.php

@@ -6,11 +6,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Facades\Cache;
 
-class Configuration extends Model
+class Settings extends Model
 {
     use HasFactory;
 
-    public const CACHE_TAG = 'configuration';
+    protected $table = 'settings';
+
+    public const CACHE_TAG = 'setting';
 
     public $primaryKey = 'key';
 
@@ -28,8 +30,8 @@ class Configuration extends Model
     {
         parent::boot();
 
-        static::updated(function (Configuration $configuration) {
-            Cache::forget(self::CACHE_TAG .':'. $configuration->key);
+        static::updated(function (Settings $settings) {
+            Cache::forget(self::CACHE_TAG .':'. $settings->key);
         });
     }
 
@@ -41,8 +43,8 @@ class Configuration extends Model
     public static function getValueByKey(string $key, $default = null)
     {
         return Cache::rememberForever(self::CACHE_TAG .':'. $key, function () use ($default, $key) {
-            $configuration = self::find($key);
-            return $configuration ? $configuration->value : $default;
+            $settings = self::find($key);
+            return $settings ? $settings->value : $default;
         });
     }
 }

+ 0 - 1
app/Notifications/ServersSuspendedNotification.php

@@ -2,7 +2,6 @@
 
 namespace App\Notifications;
 
-use App\Models\Configuration;
 use Illuminate\Bus\Queueable;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Notifications\Messages\MailMessage;

+ 19 - 19
app/Notifications/WelcomeMessage.php

@@ -2,7 +2,7 @@
 
 namespace App\Notifications;
 
-use App\Models\Configuration;
+use App\Models\Settings;
 use App\Models\User;
 use Illuminate\Bus\Queueable;
 use Illuminate\Contracts\Queue\ShouldQueue;
@@ -38,25 +38,25 @@ class WelcomeMessage extends Notification implements ShouldQueue
         return ['database'];
     }
     public function AdditionalLines()
-        {
-
-            $AdditionalLine = "";
-            if(Configuration::getValueByKey('CREDITS_REWARD_AFTER_VERIFY_EMAIL') != 0) {
-                $AdditionalLine .= "Verifying your e-mail address will grant you ".Configuration::getValueByKey('CREDITS_REWARD_AFTER_VERIFY_EMAIL')." additional " . Configuration::getValueByKey('CREDITS_DISPLAY_NAME') . ". <br />";
-            }
-            if(Configuration::getValueByKey('SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL') != 0) {
-                $AdditionalLine .= "Verifying your e-mail will also increase your Server Limit by " . Configuration::getValueByKey('SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL') . ". <br />";
-            }
-            $AdditionalLine .="<br />";
-            if(Configuration::getValueByKey('CREDITS_REWARD_AFTER_VERIFY_DISCORD') != 0) {
-                $AdditionalLine .=  "You can also verify your discord account to get another " . Configuration::getValueByKey('CREDITS_REWARD_AFTER_VERIFY_DISCORD') . " " . Configuration::getValueByKey('CREDITS_DISPLAY_NAME') . ". <br />";
-            }
-            if(Configuration::getValueByKey('SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD') != 0) {
-                $AdditionalLine .=  "Verifying your Discord account will also increase your Server Limit by " . Configuration::getValueByKey('SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD') . ". <br />";
-            }
+    {
 
-            return $AdditionalLine;
+        $AdditionalLine = "";
+        if (config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL') != 0) {
+            $AdditionalLine .= "Verifying your e-mail address will grant you " . config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL') . " additional " . config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME') . ". <br />";
+        }
+        if (config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL') != 0) {
+            $AdditionalLine .= "Verifying your e-mail will also increase your Server Limit by " . config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL') . ". <br />";
+        }
+        $AdditionalLine .= "<br />";
+        if (config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD') != 0) {
+            $AdditionalLine .=  "You can also verify your discord account to get another " . config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD') . " " . config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME') . ". <br />";
         }
+        if (config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD') != 0) {
+            $AdditionalLine .=  "Verifying your Discord account will also increase your Server Limit by " . config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD') . ". <br />";
+        }
+
+        return $AdditionalLine;
+    }
     /**
      * Get the array representation of the notification.
      *
@@ -72,7 +72,7 @@ class WelcomeMessage extends Notification implements ShouldQueue
                 <h5>Verification</h5>
                 <p>You can verify your e-mail address and link/verify your Discord account.</p>
                 <p>
-                  ".$this->AdditionalLines()."
+                  " . $this->AdditionalLines() . "
                 </p>
                 <h5>Information</h5>
                 <p>This dashboard can be used to create and delete servers.<br /> These servers can be used and managed on our pterodactyl panel.<br /> If you have any questions, please join our Discord server and #create-a-ticket.</p>

+ 57 - 4
app/Providers/AppServiceProvider.php

@@ -2,11 +2,13 @@
 
 namespace App\Providers;
 
+use App\Models\Settings;
 use Illuminate\Pagination\Paginator;
+use Illuminate\Support\Facades\Artisan;
 use Illuminate\Support\Facades\Schema;
 use Illuminate\Support\Facades\Validator;
 use Illuminate\Support\ServiceProvider;
-use Spatie\QueryBuilder\QueryBuilderRequest;
+
 
 class AppServiceProvider extends ServiceProvider
 {
@@ -31,14 +33,11 @@ class AppServiceProvider extends ServiceProvider
         Schema::defaultStringLength(191);
 
         Validator::extend('multiple_date_format', function ($attribute, $value, $parameters, $validator) {
-
             $ok = true;
-
             $result = [];
 
             // iterate through all formats
             foreach ($parameters as $parameter) {
-
                 //validate with laravels standard date format validation
                 $result[] = $validator->validateDateFormat($attribute, $value, [$parameter]);
             }
@@ -51,5 +50,59 @@ class AppServiceProvider extends ServiceProvider
 
             return $ok;
         });
+
+
+        // TODO: Check if Installer Lockfile exists instead of "running in console"
+        $settings = Settings::all();
+        // Set all configs from database
+        foreach ($settings as $setting) {
+            config([$setting->key => $setting->value]);
+        }
+
+        // Set Mail Config
+        //only update config if mail settings have changed in DB
+        if (
+            config('mail.default') != config('SETTINGS:MAIL:MAILER') ||
+            config('mail.mailers.smtp.host') != config('SETTINGS:MAIL:HOST') ||
+            config('mail.mailers.smtp.port') != config('SETTINGS:MAIL:PORT') ||
+            config('mail.mailers.smtp.username') != config('SETTINGS:MAIL:USERNAME') ||
+            config('mail.mailers.smtp.password') != config('SETTINGS:MAIL:PASSWORD') ||
+            config('mail.mailers.smtp.encryption') != config('SETTINGS:MAIL:ENCRYPTION') ||
+            config('mail.from.address') != config('SETTINGS:MAIL:FROM_ADDRESS') ||
+            config('mail.from.name') != config('SETTINGS:MAIL:FROM_NAME')
+        ) {
+            config(['mail.default' => config('SETTINGS::MAIL:MAILER')]);
+            config(['mail.mailers.smtp' => [
+                'transport' => 'smtp',
+                'host' => config('SETTINGS::MAIL:HOST'),
+                'port' => config('SETTINGS::MAIL:PORT'),
+                'encryption' => config('SETTINGS::MAIL:ENCRYPTION'),
+                'username' => config('SETTINGS::MAIL:USERNAME'),
+                'password' => config('SETTINGS::MAIL:PASSWORD'),
+                'timeout' => null,
+                'auth_mode' => null,
+            ]]);
+            config(['mail.from' => ['address' => config('SETTINGS::MAIL:FROM_ADDRESS'), 'name' => config('SETTINGS::MAIL:FROM_NAME')]]);
+
+            Artisan::call('queue:restart');
+        }
+
+
+        // Set Recaptcha API Config
+        //only update config if recaptcha settings have changed in DB
+        if (
+            config('recaptcha.api_site_key') != config('SETTINGS::RECAPTCHA:SITE_KEY') ||
+            config('recaptcha.api_secret_key') != config('SETTINGS::RECAPTCHA:SECRET_KEY')
+        ) {
+            config(['recaptcha.api_site_key' => config('SETTINGS::RECAPTCHA:SITE_KEY')]);
+            config(['recaptcha.api_secret_key' => config('SETTINGS::RECAPTCHA:SECRET_KEY')]);
+
+            Artisan::call('config:clear');
+            Artisan::call('cache:clear');
+        }
+
+        // Set Discord-API Config
+        config(['services.discord.client_id' => config('SETTINGS::DISCORD:CLIENT_ID')]);
+        config(['services.discord.client_secret' => config('SETTINGS::DISCORD:CLIENT_SECRET')]);
     }
 }

+ 8 - 43
config/app.php

@@ -1,5 +1,7 @@
 <?php
 
+use App\Models\Settings;
+
 return [
 
     'version' => '0.6.2',
@@ -70,16 +72,6 @@ return [
 
     'timezone' => env('APP_TIMEZONE', 'UTC'),
 
-    /*
-    |--------------------------------------------------------------------------
-    | Dyamic Locales
-    |--------------------------------------------------------------------------
-    |
-    | Change the Locale depending on the Users Browserlanguage
-    | Can either be true or false
-    |
-    */
-    'dynamic_locale' => env('DYNAMIC_LOCALE', false),
 
     /*
     |--------------------------------------------------------------------------
@@ -92,47 +84,20 @@ return [
     |
     */
 
-    'locale' => env('LOCALE', 'en'),
-
-
-    /*
-    |--------------------------------------------------------------------------
-    | Available Locales
-    |--------------------------------------------------------------------------
-    |
-    | You should not change this
-    | If the dashboard is 100% translated in a certain language, it will be added here
-    |
-    */
-    'available_locales' => array('English'=>'en','German'=>'de','Italian'=>'it','Chinese'=>'zh', 'Czech'=>'cs', 'Spanish'=>'es', 'Polish'=>'pl'),
-
+    'locale' =>"en",
 
     /*
     |--------------------------------------------------------------------------
-    | Unsupported Locales
+    | Available Languages
     |--------------------------------------------------------------------------
     |
-    | Locales the Owner of the Dashboard does not want to support
-    |
-    |
-    */
-
-    'unsupported_locales' => env("UNSUPPORTED_LOCALES", ""),
-
-
-    /*
-    |--------------------------------------------------------------------------
-    | Datatable Language Setting
-    |--------------------------------------------------------------------------
-    |
-    | This is the Language-Code used on the Datatables.
-    | You can grab the Language-Codes from this Website
-    | https://datatables.net/plug-ins/i18n/
+    | The application locale determines the default locale that will be used
+    | by the translation service provider. You are free to set this value
+    | to any of the locales which will be supported by the application.
     |
     */
 
-    'datatable_locale' => env('DATATABLE_LOCALE', 'en-gb'),
-
+    'available_locales' =>["en","cs","de","es","fr","hi","it","pl","zh"],
 
     /*
     |--------------------------------------------------------------------------

+ 16 - 25
config/mail.php

@@ -45,31 +45,22 @@ return [
             'auth_mode' => null,
         ],
 
-        'ses' => [
-            'transport' => 'ses',
-        ],
-
-        'mailgun' => [
-            'transport' => 'mailgun',
-        ],
-
-        'postmark' => [
-            'transport' => 'postmark',
-        ],
-
-        'sendmail' => [
-            'transport' => 'sendmail',
-            'path' => '/usr/sbin/sendmail -bs',
-        ],
-
-        'log' => [
-            'transport' => 'log',
-            'channel' => env('MAIL_LOG_CHANNEL'),
-        ],
-
-        'array' => [
-            'transport' => 'array',
-        ],
+        // 'ses' => [
+        // 'transport' => 'ses',
+        // ],
+        //
+        // 'mailgun' => [
+        // 'transport' => 'mailgun',
+        // ],
+        //
+        // 'postmark' => [
+        // 'transport' => 'postmark',
+        // ],
+        //
+        // 'sendmail' => [
+        // 'transport' => 'sendmail',
+        // 'path' => '/usr/sbin/sendmail -bs',
+        // ],
     ],
 
     /*

+ 0 - 34
database/migrations/2021_05_08_164658_create_configurations_table.php

@@ -1,34 +0,0 @@
-<?php
-
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Support\Facades\Schema;
-
-class CreateConfigurationsTable extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('configurations', function (Blueprint $table) {
-            $table->string('key')->primary();
-            $table->string('value');
-            $table->string('type')->default('string');
-            $table->text('description')->nullable();
-            $table->timestamps();
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('configurations');
-    }
-}

+ 0 - 48
database/migrations/2021_12_1_174440_invoice-settings.php

@@ -1,48 +0,0 @@
-<?php
-
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Schema;
-
-class InvoiceSettings extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('invoice_settings', function (Blueprint $table) {
-            $table->id();
-            $table->string('company_name')->nullable();
-            $table->string('company_adress')->nullable();
-            $table->string('company_phone')->nullable();
-            $table->string('company_vat')->nullable();
-            $table->string('company_mail')->nullable();
-            $table->string('company_web')->nullable()->default(env("APP_URL",""));
-            $table->string('invoice_prefix')->nullable();
-            $table->timestamps();
-        });
-
-        DB::table('invoice_settings')->insert(
-            array(
-                'company_name' => env("APP_NAME","MyCompany"),
-                'company_web' => env("APP_URL",""),
-                'invoice_prefix' => "INV"
-
-            )
-        );
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('invoice_settings');
-    }
-}

+ 61 - 0
database/migrations/2022_01_05_144858_rename_configurations_table.php

@@ -0,0 +1,61 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+
+class RenameConfigurationsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::rename('configurations', 'settings');
+
+        DB::table('settings')->where('key', 'INITIAL_CREDITS')->update(['key' => 'SETTINGS::USER:INITIAL_CREDITS']);
+        DB::table('settings')->where('key', 'INITIAL_SERVER_LIMIT')->update(['key' => 'SETTINGS::USER:INITIAL_SERVER_LIMIT']);
+        DB::table('settings')->where('key', 'CREDITS_REWARD_AFTER_VERIFY_EMAIL')->update(['key' => 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL']);
+        DB::table('settings')->where('key', 'SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL')->update(['key' => 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL']);
+        DB::table('settings')->where('key', 'CREDITS_REWARD_AFTER_VERIFY_DISCORD')->update(['key' => 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD']);
+        DB::table('settings')->where('key', 'SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD')->update(['key' => 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD']);
+        DB::table('settings')->where('key', 'MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER')->update(['key' => 'SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER']);
+        DB::table('settings')->where('key', 'SERVER_LIMIT_AFTER_IRL_PURCHASE')->update(['key' => 'SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE']);
+        DB::table('settings')->where('key', 'FORCE_EMAIL_VERIFICATION')->update(['key' => 'SETTINGS::USER:FORCE_EMAIL_VERIFICATION']);
+        DB::table('settings')->where('key', 'FORCE_DISCORD_VERIFICATION')->update(['key' => 'SETTINGS::USER:FORCE_DISCORD_VERIFICATION']);
+        DB::table('settings')->where('key', 'REGISTER_IP_CHECK')->update(['key' => 'SETTINGS::SYSTEM:REGISTER_IP_CHECK']);
+        DB::table('settings')->where('key', 'CREDITS_DISPLAY_NAME')->update(['key' => 'SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME']);
+        DB::table('settings')->where('key', 'ALLOCATION_LIMIT')->update(['key' => 'SETTINGS::SERVER:ALLOCATION_LIMIT']);
+        DB::table('settings')->where('key', 'SERVER_CREATE_CHARGE_FIRST_HOUR')->update(['key' => 'SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR']);
+        DB::table('settings')->where('key', 'SALES_TAX')->update(['key' => 'SETTINGS::PAYMENTS:SALES_TAX']);
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::rename('settings', 'configurations');
+
+        DB::table('configurations')->where('key', 'SETTINGS::USER:INITIAL_CREDITS')->update(['key' => 'INITIAL_CREDITS']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:INITIAL_SERVER_LIMIT')->update(['key' => 'INITIAL_SERVER_LIMIT']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL')->update(['key' => 'CREDITS_REWARD_AFTER_VERIFY_EMAIL']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL')->update(['key' => 'SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD')->update(['key' => 'CREDITS_REWARD_AFTER_VERIFY_DISCORD']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD')->update(['key' => 'SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER')->update(['key' => 'MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')->update(['key' => 'SERVER_LIMIT_AFTER_IRL_PURCHASE']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:FORCE_EMAIL_VERIFICATION')->update(['key' => 'FORCE_EMAIL_VERIFICATION']);
+        DB::table('configurations')->where('key', 'SETTINGS::USER:FORCE_DISCORD_VERIFICATION')->update(['key' => 'FORCE_DISCORD_VERIFICATION']);
+        DB::table('configurations')->where('key', 'SETTINGS::SYSTEM:REGISTER_IP_CHECK')->update(['key' => 'REGISTER_IP_CHECK']);
+        DB::table('configurations')->where('key', 'SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR')->update(['key' => 'SERVER_CREATE_CHARGE_FIRST_HOUR']);
+        DB::table('configurations')->where('key', 'SETTINGS::SERVER:ALLOCATION_LIMIT')->update(['key' => 'ALLOCATION_LIMIT']);
+        DB::table('configurations')->where('key', 'SETTINGS::SERVER:CREDITS_DISPLAY_NAME')->update(['key' => 'SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME']);
+        DB::table('configurations')->where('key', 'SETTINGS::PAYMENTS:SALES_TAX')->update(['key' => 'SALES_TAX']);
+    }
+}

+ 34 - 0
database/migrations/2022_01_14_234418_update_settings_table_allow_nullable.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class UpdateSettingsTableAllowNullable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        //allow value column in settings table to be nullable
+        Schema::table('settings', function (Blueprint $table) {
+            $table->string('value')->nullable()->change();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        //disallow value column in settings table to be nullable
+        Schema::table('settings', function (Blueprint $table) {
+            $table->string('value')->nullable(false)->change();
+        });
+    }
+}

+ 2 - 3
database/seeders/DatabaseSeeder.php

@@ -2,8 +2,7 @@
 
 namespace Database\Seeders;
 
-use Database\Seeders\Seeds\ConfigurationSeeder;
-use Database\Seeders\Seeds\InvoiceSettingsSeeder;
+use Database\Seeders\Seeds\SettingsSeeder;
 use Illuminate\Database\Seeder;
 
 class DatabaseSeeder extends Seeder
@@ -16,7 +15,7 @@ class DatabaseSeeder extends Seeder
     public function run()
     {
         $this->call([
-            ConfigurationSeeder::class,
+            SettingsSeeder::class,
         ]);
 
     }

+ 0 - 149
database/seeders/Seeds/ConfigurationSeeder.php

@@ -1,149 +0,0 @@
-<?php
-
-namespace Database\Seeders\Seeds;
-
-use App\Models\Configuration;
-use Illuminate\Database\Seeder;
-
-class ConfigurationSeeder extends Seeder
-{
-    /**
-     * Run the database seeds.
-     *
-     * @return void
-     */
-    public function run()
-    {
-        //initials
-        Configuration::firstOrCreate([
-            'key' => 'INITIAL_CREDITS',
-        ], [
-            'value'       => '250',
-            'type'        => 'integer',
-            'description' => 'The initial amount of credits the user starts with.'
-        ]);
-
-        Configuration::firstOrCreate([
-            'key' => 'INITIAL_SERVER_LIMIT',
-        ], [
-            'value'       => '1',
-            'type'        => 'integer',
-            'description' => 'The initial server limit the user starts with.'
-        ]);
-
-        //verify email event
-        Configuration::firstOrCreate([
-            'key' => 'CREDITS_REWARD_AFTER_VERIFY_EMAIL',
-        ], [
-            'value'       => '250',
-            'type'        => 'integer',
-            'description' => 'Increase in credits after the user has verified their email account.'
-        ]);
-
-        Configuration::firstOrCreate([
-            'key' => 'SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL',
-        ], [
-            'value'       => '2',
-            'type'        => 'integer',
-            'description' => 'Increase in server limit after the user has verified their email account.'
-        ]);
-
-        //verify discord event
-        Configuration::firstOrCreate([
-            'key' => 'CREDITS_REWARD_AFTER_VERIFY_DISCORD',
-        ], [
-            'value'       => '375',
-            'type'        => 'integer',
-            'description' => 'Increase in credits after the user has verified their discord account.'
-        ]);
-
-        Configuration::firstOrCreate([
-            'key' => 'SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD',
-        ], [
-            'value'       => '2',
-            'type'        => 'integer',
-            'description' => 'Increase in server limit after the user has verified their discord account.'
-        ]);
-
-        //other
-        Configuration::firstOrCreate([
-            'key' => 'MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER',
-        ], [
-            'value'       => '50',
-            'type'        => 'integer',
-            'description' => 'The minimum amount of credits the user would need to make a server.'
-        ]);
-
-        //purchasing
-        Configuration::firstOrCreate([
-            'key' => 'SERVER_LIMIT_AFTER_IRL_PURCHASE',
-        ], [
-            'value'       => '10',
-            'type'        => 'integer',
-            'description' => 'updates the users server limit to this amount (unless the user already has a higher server limit) after making a purchase with real money, set to 0 to ignore this.',
-        ]);
-
-
-        //force email and discord verification
-        Configuration::firstOrCreate([
-            'key' => 'FORCE_EMAIL_VERIFICATION',
-        ], [
-            'value'       => 'false',
-            'type'        => 'boolean',
-            'description' => 'Force an user to verify the email adress before creating a server / buying credits.'
-        ]);
-
-        Configuration::firstOrCreate([
-            'key' => 'FORCE_DISCORD_VERIFICATION',
-        ], [
-            'value'       => 'false',
-            'type'        => 'boolean',
-            'description' => 'Force an user to link an Discord Account before creating a server / buying credits.'
-        ]);
-
-        //disable ip check on register
-        Configuration::firstOrCreate([
-            'key' => 'REGISTER_IP_CHECK',
-        ], [
-            'value'       => 'true',
-            'type'        => 'boolean',
-            'description' => 'Prevent users from making multiple accounts using the same IP address'
-        ]);
-
-        //per_page on allocations request
-        Configuration::firstOrCreate([
-            'key' => 'ALLOCATION_LIMIT',
-        ], [
-            'value'       => '200',
-            'type'        => 'integer',
-            'description' => 'The maximum amount of allocations to pull per node for automatic deployment, if more allocations are being used than this limit is set to, no new servers can be created!'
-        ]);
-
-        //credits display name
-        Configuration::firstOrCreate([
-            'key' => 'CREDITS_DISPLAY_NAME',
-        ], [
-            'value'       => 'Credits',
-            'type'        => 'string',
-            'description' => 'Set the display name of your currency :)'
-        ]);
-
-        //credits display name
-        Configuration::firstOrCreate([
-            'key' => 'SERVER_CREATE_CHARGE_FIRST_HOUR',
-        ], [
-            'value'       => 'true',
-            'type'        => 'boolean',
-            'description' => 'Charges the first hour worth of credits upon creating a server.'
-        ]);
-        //sales tax
-        Configuration::firstOrCreate([
-            'key'   => 'SALES_TAX',
-        ], [
-            'value' => '0',
-            'type'  => 'integer',
-            'description'  => 'The %-value of tax that will be added to the product price on checkout'
-        ]);
-
-    }
-}

+ 468 - 0
database/seeders/Seeds/SettingsSeeder.php

@@ -0,0 +1,468 @@
+<?php
+
+namespace Database\Seeders\Seeds;
+
+use App\Models\Settings;
+use Illuminate\Database\Seeder;
+
+class SettingsSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //initials
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:INITIAL_CREDITS',
+        ], [
+            'value'       => '250',
+            'type'        => 'integer',
+            'description' => 'The initial amount of credits the user starts with.'
+        ]);
+
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:NITIAL_SERVER_LIMIT',
+        ], [
+            'value'       => '1',
+            'type'        => 'integer',
+            'description' => 'The initial server limit the user starts with.'
+        ]);
+
+        //verify email event
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL',
+        ], [
+            'value'       => '250',
+            'type'        => 'integer',
+            'description' => 'Increase in credits after the user has verified their email account.'
+        ]);
+
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL',
+        ], [
+            'value'       => '2',
+            'type'        => 'integer',
+            'description' => 'Increase in server limit after the user has verified their email account.'
+        ]);
+
+        //verify discord event
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD',
+        ], [
+            'value'       => '375',
+            'type'        => 'integer',
+            'description' => 'Increase in credits after the user has verified their discord account.'
+        ]);
+
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD',
+        ], [
+            'value'       => '2',
+            'type'        => 'integer',
+            'description' => 'Increase in server limit after the user has verified their discord account.'
+        ]);
+
+        //other
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER',
+        ], [
+            'value'       => '50',
+            'type'        => 'integer',
+            'description' => 'The minimum amount of credits the user would need to make a server.'
+        ]);
+
+        //purchasing
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE',
+        ], [
+            'value'       => '10',
+            'type'        => 'integer',
+            'description' => 'updates the users server limit to this amount (unless the user already has a higher server limit) after making a purchase with real money, set to 0 to ignore this.',
+        ]);
+
+
+        //force email and discord verification
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:FORCE_EMAIL_VERIFICATION',
+        ], [
+            'value'       => 'false',
+            'type'        => 'boolean',
+            'description' => 'Force an user to verify the email adress before creating a server / buying credits.'
+        ]);
+
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::USER:FORCE_DISCORD_VERIFICATION',
+        ], [
+            'value'       => 'false',
+            'type'        => 'boolean',
+            'description' => 'Force an user to link an Discord Account before creating a server / buying credits.'
+        ]);
+
+        //disable ip check on register
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::SYSTEM:REGISTER_IP_CHECK',
+        ], [
+            'value'       => 'true',
+            'type'        => 'boolean',
+            'description' => 'Prevent users from making multiple accounts using the same IP address'
+        ]);
+
+        //per_page on allocations request
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::SERVER:ALLOCATION_LIMIT',
+        ], [
+            'value'       => '200',
+            'type'        => 'integer',
+            'description' => 'The maximum amount of allocations to pull per node for automatic deployment, if more allocations are being used than this limit is set to, no new servers can be created!'
+        ]);
+
+        //credits display name
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME',
+        ], [
+            'value'       => 'Credits',
+            'type'        => 'string',
+            'description' => 'Set the display name of your currency :)'
+        ]);
+
+        //credits display name
+        Settings::firstOrCreate([
+            'key' => 'SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR',
+        ], [
+            'value'       => 'true',
+            'type'        => 'boolean',
+            'description' => 'Charges the first hour worth of credits upon creating a server.'
+        ]);
+        //sales tax
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:SALES_TAX',
+        ], [
+            'value' => '0',
+            'type'  => 'integer',
+            'description'  => 'The %-value of tax that will be added to the product price on checkout'
+        ]);
+        //Invoices enabled
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:ENABLED',
+        ], [
+            'value' => 'false',
+            'type'  => 'boolean',
+        ]);
+        //Invoice company name
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:COMPANY_NAME',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The name of the Company on the Invoices'
+        ]);
+        //Invoice company address
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:COMPANY_ADDRESS',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The address of the Company on the Invoices'
+        ]);
+        //Invoice company phone
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:COMPANY_PHONE',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The phone number of the Company on the Invoices'
+        ]);
+
+        //Invoice company mail
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:COMPANY_MAIL',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The email address of the Company on the Invoices'
+        ]);
+
+        //Invoice VAT
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:COMPANY_VAT',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The VAT-Number of the Company on the Invoices'
+        ]);
+
+        //Invoice Website
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:COMPANY_WEBSITE',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The Website of the Company on the Invoices'
+        ]);
+
+        //Invoice Website
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::INVOICE:PREFIX',
+        ], [
+            'value' => 'INV',
+            'type'  => 'string',
+            'description'  => 'The invoice prefix'
+        ]);
+
+        //Locale
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::LOCALE:DEFAULT',
+        ], [
+            'value' => 'en',
+            'type'  => 'string',
+            'description'  => 'The default Language the dashboard will be shown in'
+        ]);
+        //Dynamic locale
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::LOCALE:DYNAMIC',
+        ], [
+            'value' => 'false',
+            'type'  => 'boolean',
+            'description'  => 'If this is true, the Language will change to the Clients browserlanguage or default.'
+        ]);
+        //User can change Locale
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::LOCALE:CLIENTS_CAN_CHANGE',
+        ], [
+            'value' => 'false',
+            'type'  => 'boolean',
+            'description'  => 'If this is true, the clients will be able to change their Locale.'
+        ]);
+        //Locale
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::LOCALE:AVAILABLE',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The available languages'
+        ]);
+        //Locale
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::LOCALE:DATATABLES',
+        ], [
+            'value' => 'en-gb',
+            'type'  => 'string',
+            'description'  => 'The Language of the Datatables. Grab the Language-Codes from here https://datatables.net/plug-ins/i18n/'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:PAYPAL:SECRET',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your PayPal Secret-Key ( https://developer.paypal.com/docs/integration/direct/rest/)'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your PayPal Client_ID'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your PayPal SANDBOX Secret-Key used for testing '
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your PayPal SANDBOX Client-ID used for testing '
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:STRIPE:SECRET',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your Stripe  Secret-Key  ( https://dashboard.stripe.com/account/apikeys )'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your Stripe endpoint secret-key'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:STRIPE:TEST_SECRET',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your Stripe test secret-key'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Your Stripe endpoint test secret-key'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::PAYMENTS:STRIPE:METHODS',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Comma seperated list of payment methods that are enabled (https://stripe.com/docs/payments/payment-methods/integration-options)'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::DISCORD:CLIENT_ID',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Discord API Credentials - https://discordapp.com/developers/applications/'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::DISCORD:CLIENT_SECRET',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Discord API Credentials - https://discordapp.com/developers/applications/'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::DISCORD:BOT_TOKEN',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Discord API Credentials - https://discordapp.com/developers/applications/'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::DISCORD:GUILD_ID',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Discord API Credentials - https://discordapp.com/developers/applications/'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::DISCORD:ROLE_ID',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Discord role that will be assigned to users when they register'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::DISCORD:INVITE_URL',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The invite URL to your Discord Server'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::SYSTEM:PTERODACTYL:TOKEN',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Admin API Token from Pterodactyl Panel - necessary for the Panel to work. The Key needs all read&write permissions!'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::SYSTEM:PTERODACTYL:URL',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The URL to your Pterodactyl Panel. Must not end with a / '
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MISC:PHPMYADMIN:URL',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'The URL to your PHPMYADMIN Panel. Must not end with a /, remove to remove database button'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::RECAPTCHA:SITE_KEY',
+        ], [
+            'value' => '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI',
+            'type'  => 'string',
+            'description'  => 'Google Recaptcha API Credentials - https://www.google.com/recaptcha/admin - reCaptcha V2 (not v3)'
+        ]);
+
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::RECAPTCHA:SECRET_KEY',
+        ], [
+            'value' => '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe',
+            'type'  => 'string',
+            'description'  => 'Google Recaptcha API Credentials - https://www.google.com/recaptcha/admin - reCaptcha V2 (not v3)'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::RECAPTCHA:ENABLED',
+        ], [
+            'value' => 'true',
+            'type'  => 'boolean',
+            'description'  => 'Enables or disables the ReCaptcha feature on the registration/login page'
+
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:MAILER',
+        ], [
+            'value' => 'smtp',
+            'type'  => 'string',
+            'description'  => 'Selected Mailer (smtp, mailgun, sendgrid, mailtrap)'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:HOST',
+        ], [
+            'value' => 'localhost',
+            'type'  => 'string',
+            'description'  => 'Mailer Host Adress'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:PORT',
+        ], [
+            'value' => '1025',
+            'type'  => 'string',
+            'description'  => 'Mailer Server Port'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:USERNAME',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Mailer Username'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:PASSWORD',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Mailer Password'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:ENCRYPTION',
+        ], [
+            'value' => 'tls',
+            'type'  => 'string',
+            'description'  => 'Mailer Encryption (tls, ssl)'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:FROM_ADDRESS',
+        ], [
+            'value' => '',
+            'type'  => 'string',
+            'description'  => 'Mailer From Address'
+        ]);
+        Settings::firstOrCreate([
+            'key'   => 'SETTINGS::MAIL:FROM_NAME',
+        ], [
+            'value' => env('APP_NAME', 'Controlpanel'),
+            'type'  => 'string',
+            'description'  => 'Mailer From Name'
+        ]);
+    }
+}

+ 1 - 1
package-lock.json

@@ -1,5 +1,5 @@
 {
-    "name": "bitsec-dashboard",
+    "name": "controllpanelgg",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {

BIN
public/images/discord_logo.png


+ 14 - 5
resources/lang/de.json

@@ -68,7 +68,7 @@
     "User ID": "User-ID",
     "Server Creation Error": "Fehler beim erstellen des Servers",
     "Your servers have been suspended!": "Deine Server wurden pausiert",
-    "To automatically re-enable your server\/s, you need to purchase more credits.": "Um deine Server zu reaktivieren, musst du mehr Credits kaufen!",
+    "To automatically re-enable your server/s, you need to purchase more credits.": "Um deine Server zu reaktivieren, musst du mehr Credits kaufen!",
     "Purchase credits": "Credits kaufen",
     "If you have any questions please let us know.": "Solltest du weiter fragen haben, melde dich gerne beim Support!",
     "Regards": "mit freundlichen Grüßen",
@@ -217,7 +217,7 @@
     "A voucher can only be used one time per user. Uses specifies the number of different users that can use this voucher.": "Ein Gutschein kann von einem User nur einmal eingelöst werden. \"Benutzungen\" setzt die Anzahl an Usern die diesen Gutschein einlösen können.",
     "Max": "Max",
     "Expires at": "Läuft ab am",
-    "Used \/ Uses": "Benutzungen",
+    "Used / Uses": "Benutzungen",
     "Expires": "Ablauf",
     "Sign in to start your session": "Melde dich an um das Dashboard zu benutzen",
     "Password": "Passwort",
@@ -287,7 +287,7 @@
     "No nodes have been linked!": "Es wurde keine Nodes verknüpft",
     "No nests available!": "Keine Nests verfügbar",
     "No eggs have been linked!": "Es wurde keine Eggs verknüpft",
-    "Software \/ Games": "Software \/ Spiele",
+    "Software / Games": "Software / Spiele",
     "Please select software ...": "Bitte Software auswählen",
     "---": "---",
     "Specification ": "Spezifikation",
@@ -350,5 +350,14 @@
     "Notes": "Notizen",
     "Amount in words": "Betrag in Worten",
     "Please pay until": "Zahlbar bis",
-    "Account already exists on Pterodactyl. Please contact the Support!": "Der Account existiert bereits bei Pterodactyl. Kontaktiere den Support!"
-}
+    "Account already exists on Pterodactyl. Please contact the Support!": "Der Account existiert bereits bei Pterodactyl. Kontaktiere den Support!",
+    "de": "Deutsch",
+    "en": "Englisch",
+    "fr": "Französisch",
+    "cs": "Tschechisch",
+    "es": "Spanisch",
+    "hi": "Hindi",
+    "it": "Italienisch",
+    "pl": "Polnisch",
+    "zh": "Chinesisch"
+}

+ 32 - 19
resources/lang/en.json

@@ -4,11 +4,12 @@
     "api key has been removed!": "api key has been removed!",
     "Edit": "Edit",
     "Delete": "Delete",
-    "configuration has been updated!": "configuration has been updated!",
     "Store item has been created!": "Store item has been created!",
     "Store item has been updated!": "Store item has been updated!",
     "Product has been updated!": "Product has been updated!",
     "Store item has been removed!": "Store item has been removed!",
+    "Created at": "Created at",
+    "Error!": "Error!",
     "unknown": "unknown",
     "Pterodactyl synced": "Pterodactyl synced",
     "Your credit balance has been increased!": "Your credit balance has been increased!",
@@ -16,6 +17,7 @@
     "Your payment has been canceled!": "Your payment has been canceled!",
     "Payment method": "Payment method",
     "Invoice": "Invoice",
+    "Download": "Download",
     "Product has been created!": "Product has been created!",
     "Product has been removed!": "Product has been removed!",
     "Show": "Show",
@@ -26,6 +28,7 @@
     "Unsuspend": "Unsuspend",
     "Suspend": "Suspend",
     "Icons updated!": "Icons updated!",
+    "configuration has been updated!": "configuration has been updated!",
     "link has been created!": "link has been created!",
     "link has been updated!": "link has been updated!",
     "product has been removed!": "product has been removed!",
@@ -80,7 +83,6 @@
     "Check the docs for it here": "Check the docs for it here",
     "Causer": "Causer",
     "Description": "Description",
-    "Created at": "Created at",
     "Application API": "Application API",
     "Create": "Create",
     "Memo": "Memo",
@@ -89,13 +91,6 @@
     "Token": "Token",
     "Last used": "Last used",
     "Are you sure you wish to delete?": "Are you sure you wish to delete?",
-    "Edit Configuration": "Edit Configuration",
-    "Text Field": "Text Field",
-    "Cancel": "Cancel",
-    "Save": "Save",
-    "Configurations": "Configurations",
-    "Key": "Key",
-    "Value": "Value",
     "Nests": "Nests",
     "Sync": "Sync",
     "Active": "Active",
@@ -118,6 +113,7 @@
     "Locations": "Locations",
     "Eggs": "Eggs",
     "Last updated :date": "Last updated :date",
+    "Download all Invoices": "Download all Invoices",
     "Product Price": "Product Price",
     "Tax Value": "Tax Value",
     "Tax Percentage": "Tax Percentage",
@@ -151,11 +147,15 @@
     "Config": "Config",
     "Suspended at": "Suspended at",
     "Settings": "Settings",
-    "Dashboard icons": "Dashboard icons",
-    "Invoice Settings": "Invoice Settings",
+    "Key": "Key",
+    "Value": "Value",
+    "Edit Configuration": "Edit Configuration",
+    "Text Field": "Text Field",
+    "Cancel": "Cancel",
+    "Save": "Save",
     "Select panel icon": "Select panel icon",
     "Select panel favicon": "Select panel favicon",
-    "Download all Invoices": "Download all Invoices",
+    "Images and Icons may be cached, reload without cache to see your changes appear": "Images and Icons may be cached, reload without cache to see your changes appear",
     "Enter your companys name": "Enter your companys name",
     "Enter your companys address": "Enter your companys address",
     "Enter your companys phone number": "Enter your companys phone number",
@@ -165,6 +165,15 @@
     "Enter your custom invoice prefix": "Enter your custom invoice prefix",
     "Logo": "Logo",
     "Select Invoice Logo": "Select Invoice Logo",
+    "Available languages": "Available languages",
+    "Default language": "Default language",
+    "The fallback Language, if something goes wrong": "The fallback Language, if something goes wrong",
+    "Datable language": "Datable language",
+    "The Language of the Datatables. Grab the Language-Codes from here": "The Language of the Datatables. Grab the Language-Codes from here",
+    "Auto-translate": "Auto-translate",
+    "If this is checked, the Dashboard will translate itself to the Clients language, if available": "If this is checked, the Dashboard will translate itself to the Clients language, if available",
+    "Let the Client change the Language": "Let the Client change the Language",
+    "If this is checked, Clients will have the ability to manually change their Dashboard language": "If this is checked, Clients will have the ability to manually change their Dashboard language",
     "Store": "Store",
     "Currency code": "Currency code",
     "Checkout the paypal docs to select the appropriate code": "Checkout the paypal docs to select the appropriate code",
@@ -242,7 +251,7 @@
     "per month": "per month",
     "Out of Credits in": "Out of Credits in",
     "Home": "Home",
-    "Languages": "Languages",
+    "Language": "Language",
     "See all Notifications": "See all Notifications",
     "Redeem code": "Redeem code",
     "Profile": "Profile",
@@ -280,7 +289,6 @@
     "Re-Sync Discord": "Re-Sync Discord",
     "Save Changes": "Save Changes",
     "Server configuration": "Server configuration",
-    "Error!": "Error!",
     "Make sure to link your products to nodes and eggs.": "Make sure to link your products to nodes and eggs.",
     "There has to be at least 1 valid product for server creation": "There has to be at least 1 valid product for server creation",
     "No products available!": "No products available!",
@@ -321,9 +329,6 @@
     "Canceled ...": "Canceled ...",
     "Deletion has been canceled.": "Deletion has been canceled.",
     "Date": "Date",
-    "To": "To",
-    "From": "From",
-    "Pending": "Pending",
     "Subtotal": "Subtotal",
     "Payment Methods": "Payment Methods",
     "Amount Due": "Amount Due",
@@ -350,5 +355,13 @@
     "Notes": "Notes",
     "Amount in words": "Amount in words",
     "Please pay until": "Please pay until",
-    "Account already exists on Pterodactyl. Please contact the Support!": "Account already exists on Pterodactyl. Please contact the Support!"
-}
+    "fr": "French",
+    "cs": "Czech",
+    "en": "English",
+    "es": "Spanish",
+    "de": "German",
+    "hi": "Hindi",
+    "it": "Italian",
+    "pl": "Polish",
+    "zh": "Chinese"
+}

+ 0 - 63
resources/views/admin/configurations/editModel.blade.php

@@ -1,63 +0,0 @@
-<!-- The Modal -->
-<div class="modal fade" id="editConfigurationModel">
-    <div class="modal-dialog">
-        <div class="modal-content ">
-
-            <form method="post" action="{{route('admin.configurations.updatevalue')}}">
-                @csrf
-                    @method('PATCH')
-                <!-- Modal Header -->
-                <div class="modal-header">
-                    <h4 class="modal-title">{{__('Edit Configuration')}}</h4>
-                    <button type="button" class="close" data-dismiss="modal">&times;</button>
-                </div>
-
-                <!-- Modal body -->
-                <div class="modal-body">
-                    <div class="form-group">
-                        <label id="keyLabel" for="value">{{__('Text Field')}}</label>
-                        <div class="input-group">
-                            <div class="input-group-prepend">
-                                <div class="input-group-text">
-                                    <i class="fa fa-cog"></i>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-
-                    <div class="form-group">
-                        <input hidden id="key" name="key" type="text" class="form-control" required="required">
-                    </div>
-                </div>
-
-                <!-- Modal footer -->
-                <div class="modal-footer">
-                    <button type="button" class="btn btn-danger" data-dismiss="modal">{{__('Cancel')}}</button>
-                    <button type="submit" class="btn btn-primary">{{__('Save')}}</button>
-                </div>
-            </form>
-        </div>
-    </div>
-</div>
-
-<script>
-    window.configuration = {
-        parse(key, value, type) {
-            $('#keyLabel').html(key)
-            $('#key').val(key)
-            $('#value').remove();
-            if (type === 'integer') {
-                $('.input-group').append('<input id="value" name="value" type="number" class="form-control" required="required">')
-            } else if (type === 'boolean') {
-                $('.input-group').append('<select id="value" name="value" class="form-control" required=required>' +
-                    '<option value="true">true</option>' +
-                    '<option value="false">false</option>' +
-                    '</select>')
-            } else if (type === 'string') {
-                $('.input-group').append('<input id="value" name="value" type="text" class="form-control" required="required">')
-            }
-            $('#value').val(value)
-            $('#editConfigurationModel').modal('show')
-        }
-    }
-</script>

+ 0 - 91
resources/views/admin/configurations/index.blade.php

@@ -1,91 +0,0 @@
-@extends('layouts.main')
-
-@section('content')
-    <!-- CONTENT HEADER -->
-    <section class="content-header">
-        <div class="container-fluid">
-            <div class="row mb-2">
-                <div class="col-sm-6">
-                    <h1>{{__('Configurations')}}</h1>
-                </div>
-                <div class="col-sm-6">
-                    <ol class="breadcrumb float-sm-right">
-                        <li class="breadcrumb-item"><a href="{{route('home')}}"{{__('Dashboard')}}</a></li>
-                        <li class="breadcrumb-item"><a class="text-muted"
-                                                       href="{{route('admin.configurations.index')}}">{{__('Configurations')}}</a></li>
-                    </ol>
-                </div>
-            </div>
-        </div>
-    </section>
-    <!-- END CONTENT HEADER -->
-
-    <!-- MAIN CONTENT -->
-    <section class="content">
-        <div class="container-fluid">
-
-            <div class="card">
-
-                <div class="card-header">
-                    <div class="d-flex justify-content-between">
-                        <h5 class="card-title"><i class="fas fa-cog mr-2"></i>{{__('Configurations')}}</h5>
-                    </div>
-                </div>
-
-                <div class="card-body table-responsive">
-
-                    <table id="datatable" class="table table-striped">
-                        <thead>
-                        <tr>
-                            <th>{{__('Key')}}</th>
-                            <th>{{__('Value')}}</th>
-                            <th>{{__('Type')}}</th>
-                            <th width="600">{{__('Description')}}</th>
-                            <th>{{__('Created at')}}</th>
-                            <th></th>
-                        </tr>
-                        </thead>
-                        <tbody>
-                        </tbody>
-                    </table>
-
-                </div>
-            </div>
-
-
-        </div>
-        <!-- END CUSTOM CONTENT -->
-
-    </section>
-    <!-- END CONTENT -->
-
-    @include('admin.configurations.editModel')
-
-    <script>
-        document.addEventListener("DOMContentLoaded", function () {
-            $('#datatable').DataTable({
-                language: {
-                    url: '//cdn.datatables.net/plug-ins/1.11.3/i18n/{{config("app.datatable_locale")}}.json'
-                },
-                processing: true,
-                serverSide: true,
-                stateSave: true,
-                ajax: "{{route('admin.configurations.datatable')}}",
-                columns: [
-                    {data: 'key'},
-                    {data: 'value'},
-                    {data: 'type'},
-                    {data: 'description'},
-                    {data: 'created_at'},
-                    {data: 'actions', sortable: false},
-                ],
-                fnDrawCallback: function( oSettings ) {
-                    $('[data-toggle="popover"]').popover();
-                }
-            });
-        });
-    </script>
-
-
-
-@endsection

+ 6 - 0
resources/views/admin/payments/index.blade.php

@@ -27,6 +27,10 @@
             <div class="card">
                 <div class="card-header">
                     <h5 class="card-title"><i class="fas fa-money-bill-wave mr-2"></i>{{ __('Payments') }}</h5>
+                    <div class="float-right">
+                        <a href="{{ route('admin.invoices.downloadAllInvoices') }}"><button
+                                class="btn btn-info">{{ __('Download all Invoices') }}</button></a>
+                    </div>
                 </div>
 
                 <div class="card-body table-responsive">
@@ -43,6 +47,7 @@
                                 <th>{{ __('Payment ID') }}</th>
                                 <th>{{ __('Payment Method') }}</th>
                                 <th>{{ __('Created at') }}</th>
+                                <th></th>
                             </tr>
                         </thead>
                         <tbody>
@@ -78,6 +83,7 @@
                     {data: 'payment_id'},
                     {data: 'payment_method'},
                     {data: 'created_at'},
+                    {data: 'actions' , sortable : false},
                 ],
                 fnDrawCallback: function(oSettings) {
                     $('[data-toggle="popover"]').popover();

+ 26 - 164
resources/views/admin/settings/index.blade.php

@@ -6,13 +6,13 @@
         <div class="container-fluid">
             <div class="row mb-2">
                 <div class="col-sm-6">
-                    <h1>{{__('Settings')}}</h1>
+                    <h1>{{ __('Settings') }}</h1>
                 </div>
                 <div class="col-sm-6">
                     <ol class="breadcrumb float-sm-right">
-                        <li class="breadcrumb-item"><a href="{{route('home')}}">{{__('Dashboard')}}</a></li>
+                        <li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
                         <li class="breadcrumb-item"><a class="text-muted"
-                                                       href="{{route('admin.settings.index')}}">{{__('Settings')}}</a></li>
+                                href="{{ route('admin.settings.index') }}">{{ __('Settings') }}</a></li>
                     </ol>
                 </div>
             </div>
@@ -28,7 +28,7 @@
 
                 <div class="card-header">
                     <div class="d-flex justify-content-between">
-                        <h5 class="card-title"><i class="fas fa-tools mr-2"></i>{{__('Settings')}}</h5>
+                        <h5 class="card-title"><i class="fas fa-tools mr-2"></i>{{ __('Settings') }}</h5>
                     </div>
                 </div>
 
@@ -36,169 +36,18 @@
 
                     <!-- Nav pills -->
                     <ul class="nav nav-tabs">
-                        <li class="nav-item">
-                            <a class="nav-link active" data-toggle="pill" href="#dashboard-icons">{{__('Dashboard icons')}}</a>
-                        </li>
-                        <li class="nav-item">
-                            <a class="nav-link" data-toggle="pill" href="#invoice-settings">{{__('Invoice Settings')}}</a>
-                        </li>
+
+                        @foreach ($tabListItems as $tabListItem)
+                            {!! $tabListItem !!}
+                        @endforeach
                     </ul>
 
                     <!-- Tab panes -->
                     <div class="tab-content">
-                        <div class="tab-pane mt-3 active" id="dashboard-icons">
-
-                            <form method="POST" enctype="multipart/form-data" class="mb-3"
-                                  action="{{route('admin.settings.update.icons')}}">
-                                @csrf
-                                @method('PATCH')
-
-                                <div class="row">
-                                    <div class="col-md-6 col-lg-4 col-12">
-                                        <div class="form-group">
-                                            <div class="custom-file mb-3 mt-3">
-                                                <input type="file" accept="image/png,image/jpeg,image/jpg"
-                                                       class="custom-file-input" name="icon" id="icon">
-                                                <label class="custom-file-label selected"
-                                                       for="icon">{{__('Select panel icon')}}</label>
-                                            </div>
-                                            @error('icon')
-                                            <span class="text-danger">
-                                                   {{$message}}
-                                               </span>
-                                            @enderror
-                                        </div>
-                                        <div class="form-group">
-                                            <div class="custom-file mb-3">
-                                                <input type="file" accept="image/x-icon" class="custom-file-input"
-                                                       name="favicon" id="favicon">
-                                                <label class="custom-file-label selected"
-                                                       for="favicon">{{__('Select panel favicon')}}</label>
-                                            </div>
-                                            @error('favicon')
-                                            <span class="text-danger">
-                                                   {{$message}}
-                                               </span>
-                                            @enderror
-                                        </div>
-                                    </div>
-                                </div>
-
-                                <button class="btn btn-primary">{{__('Submit')}}</button>
-                            </form>
-
-                            <p class="text-muted">Images and Icons may be cached, use <code>CNTRL + F5</code><sup>(google
-                                    chrome hotkey)</sup> to reload without cache to see your changes appear :)</p>
-
-                        </div>
-
-                        <div class="tab-pane mt-3" id="invoice-settings">
-                            <div class="float-right">
-                                <a href="{{route('admin.settings.downloadAllInvoices')}}"><button class="btn btn-success">{{__('Download all Invoices')}}</button></a>
-                            </div>
-
-                            <form method="POST" enctype="multipart/form-data" class="mb-3"
-                                  action="{{route('admin.settings.update.invoicesettings')}}">
-                                @csrf
-                                @method('PATCH')
-
-                                <div class="row">
-                                    <div class="col-md-6 col-lg-4 col-12">
-                                        <!-- Name -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label for="company-name">{{__('Enter your companys name' )}}</label>
-                                                <input x-model="company-name" id="company-name" name="company-name"
-                                                       type="text" value="{{$company_name}}"
-                                                       class="form-control @error('company-name') is-invalid @enderror">
-                                            </div>
-                                        </div>
-                                        <!-- address -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label
-                                                    for="company-address">{{__('Enter your companys address' )}}</label>
-                                                <input x-model="company-address" id="company-address"
-                                                       name="company-address" type="text" value="{{$company_adress}}"
-                                                       class="form-control @error('company-address') is-invalid @enderror">
-                                            </div>
-                                        </div>
-                                        <!-- Phone -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label
-                                                    for="company-phone">{{__('Enter your companys phone number' )}}</label>
-                                                <input x-model="company-phone" id="company-phone" name="company-phone"
-                                                       type="text" value="{{$company_phone}}"
-                                                       class="form-control @error('company-phone') is-invalid @enderror">
-                                            </div>
-                                        </div>
-
-                                        <!-- VAT -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label for="company-vat">{{__('Enter your companys VAT id' )}}</label>
-                                                <input x-model="company-vat" id="company-vat" name="company-vat"
-                                                       type="text" value="{{$company_vat}}"
-                                                       class="form-control @error('company-vat') is-invalid @enderror">
-                                            </div>
-                                        </div>
-
-                                        <!-- email -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label
-                                                    for="company-mail">{{__('Enter your companys email address' )}}</label>
-                                                <input x-model="company-mail" id="company-mail" name="company-mail"
-                                                       type="text" value="{{$company_mail}}"
-                                                       class="form-control @error('company-mail') is-invalid @enderror">
-                                            </div>
-                                        </div>
-                                        <!-- website -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label
-                                                    for="company-web">{{__('Enter your companys website' )}}</label>
-                                                <input x-model="company-web" id="company-web" name="company-web"
-                                                       type="text" value="{{$company_web}}"
-                                                       class="form-control @error('company-web') is-invalid @enderror">
-                                            </div>
-                                        </div>
-
-                                        <!-- website -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label
-                                                    for="invoice-prefix">{{__('Enter your custom invoice prefix' )}}</label>
-                                                <input x-model="invoice-prefix" id="invoice-prefix" name="invoice-prefix"
-                                                       type="text" value="{{$invoice_prefix}}"
-                                                       class="form-control @error('invoice-prefix') is-invalid @enderror">
-                                            </div>
-                                        </div>
-
-                                        <!-- logo -->
-                                        <div class="form-group">
-                                            <div class="custom-control mb-3">
-                                                <label for="logo">{{__('Logo')}}</label>
-                                            <div class="custom-file mb-3">
-                                                <input type="file" accept="image/png,image/jpeg,image/jpg" class="custom-file-input"
-                                                       name="logo" id="logo">
-                                                <label class="custom-file-label selected"
-                                                       for="favicon">{{__('Select Invoice Logo')}}</label>
-                                            </div>
-                                            </div>
-                                            @error('logo')
-                                            <span class="text-danger">
-                                                   {{$message}}
-                                               </span>
-                                            @enderror
-                                        </div>
-                                    </div>
-                                </div>
-                                <button class="btn btn-primary">{{__('Submit')}}</button>
-                                <!-- end -->
-
-                        </div>
+
+                        @foreach ($tabs as $tab)
+                            @include($tab)
+                        @endforeach
                     </div>
 
 
@@ -218,12 +67,25 @@
 
     <script>
         // Add the following code if you want the name of the file appear on select
+
         document.addEventListener('DOMContentLoaded', () => {
-            $(".custom-file-input").on("change", function () {
+            $(".custom-file-input").on("change", function() {
                 var fileName = $(this).val().split("\\").pop();
                 $(this).siblings(".custom-file-label").addClass("selected").html(fileName);
             });
         })
+
+        const tabPaneHash = window.location.hash;
+        if (tabPaneHash) {
+            $('.nav-tabs a[href="' + tabPaneHash + '"]').tab('show');
+        }
+
+        $('.nav-tabs a').click(function(e) {
+            $(this).tab('show');
+            const scrollmem = $('body').scrollTop();
+            window.location.hash = this.hash;
+            $('html,body').scrollTop(scrollmem);
+        });
     </script>
 
 

+ 111 - 0
resources/views/admin/settings/tabs/invoices.blade.php

@@ -0,0 +1,111 @@
+<div class="tab-pane mt-3 active" id="invoices">
+    <form method="POST" enctype="multipart/form-data" class="mb-3"
+        action="{{ route('admin.settings.update.invoicesettings') }}">
+        @csrf
+        @method('PATCH')
+
+        <div class="row">
+            <div class="col-md-3 p-3">
+                <!-- Name -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="company-name">{{ __('Company Name') }}:</label>
+                        <input x-model="company-name" id="company-name" name="company-name" type="text" required
+                            value="{{ config('SETTINGS::INVOICE:COMPANY_NAME') }}"
+                            class="form-control @error('company-name') is-invalid @enderror">
+                    </div>
+                </div>
+                <!-- address -->
+                <div class="form-group mb-3">
+                    <div class="custom-control  p-0">
+                        <label for="company-address">{{ __('Company Adress') }}:</label>
+                        <input x-model="company-address" id="company-address" name="company-address" type="text"
+                            value="{{ config('SETTINGS::INVOICE:COMPANY_ADDRESS') }}"
+                            class="form-control @error('company-address') is-invalid @enderror">
+                    </div>
+                </div>
+                <!-- Phone -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="company-phone">{{ __('Company Phonenumber') }}:</label>
+                        <input x-model="company-phone" id="company-phone" name="company-phone" type="text"
+                            value="{{ config('SETTINGS::INVOICE:COMPANY_PHONE') }}"
+                            class="form-control @error('company-phone') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <!-- VAT -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="company-vat">{{ __('VAT ID') }}:</label>
+                        <input x-model="company-vat" id="company-vat" name="company-vat" type="text"
+                            value="{{ config('SETTINGS::INVOICE:COMPANY_VAT') }}"
+                            class="form-control @error('company-vat') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+
+            <div class="col-md-3 p-3">
+                <!-- email -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="company-mail">{{ __('Company E-Mail Adress') }}:</label>
+                        <input x-model="company-mail" id="company-mail" name="company-mail" type="text"
+                            value="{{ config('SETTINGS::INVOICE:COMPANY_MAIL') }}"
+                            class="form-control @error('company-mail') is-invalid @enderror">
+                    </div>
+                </div>
+                <!-- website -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="company-web">{{ __('Company Website') }}:</label>
+                        <input x-model="company-web" id="company-web" name="company-web" type="text"
+                            value="{{ config('SETTINGS::INVOICE:COMPANY_WEBSITE') }}"
+                            class="form-control @error('company-web') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <!-- prefix -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="invoice-prefix">{{ __('Invoice Prefix') }}:</label>
+                        <input x-model="invoice-prefix" id="invoice-prefix" name="invoice-prefix" type="text" required
+                            value="{{ config('SETTINGS::INVOICE:PREFIX') }}"
+                            class="form-control @error('invoice-prefix') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+            <div class="col-md-3 p-3">
+                <div class="custom-control mb-3 p-0">
+                    <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                        <div>
+                            <input value="true" id="enable-invoices" name="enable-invoices"
+                                {{ config('SETTINGS::INVOICE:ENABLED') == 'true' ? 'checked' : '' }} type="checkbox">
+                            <label for="enable-invoices">{{ __('Enable Invoices') }} </label>
+                        </div>
+                    </div>
+                </div>
+                <!-- logo -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="logo">{{ __('Logo') }}:</label>
+                        <div class="custom-file mb-3">
+                            <input type="file" accept="image/png,image/jpeg,image/jpg" class="custom-file-input"
+                                name="logo" id="logo">
+                            <label class="custom-file-label selected"
+                                for="favicon">{{ __('Select Invoice Logo') }}</label>
+                        </div>
+                    </div>
+                    @error('logo')
+                        <span class="text-danger">
+                        </span>
+                    @enderror
+                </div>
+            </div>
+        </div>
+
+        <div class="row">
+            <button class="btn btn-primary mt-3 ml-3">{{ __('Submit') }}</button>
+        </div>
+    </form>
+</div>

+ 91 - 0
resources/views/admin/settings/tabs/language.blade.php

@@ -0,0 +1,91 @@
+<div class="tab-pane mt-3" id="language">
+    <form method="POST" enctype="multipart/form-data" class="mb-3"
+        action="{{ route('admin.settings.update.languagesettings') }}">
+        @csrf
+        @method('PATCH')
+
+        <div class="row">
+            <div class="col-md-3 p-3">
+                <div class="form-group">
+                    <!-- AVAILABLE LANGUAGES -->
+                    <div class="custom-control mb-3 p-0">
+                        <label for="languages">{{ __('Available languages') }}:</label>
+                        <select id="languages" style="width:100%" class="custom-select" name="languages[]" required
+                            multiple="multiple" autocomplete="off" @error('defaultLanguage') is-invalid @enderror>
+
+                            @foreach (config('app.available_locales') as $lang)
+                                <option value="{{ $lang }}" @if (str_contains(config('SETTINGS::LOCALE:AVAILABLE'), $lang))  selected @endif>
+                                    {{ __($lang) }}
+                                </option>
+                            @endforeach
+                        </select>
+                    </div>
+
+                    <!-- DEFAULT LANGUAGE -->
+
+                    <div class="custom-control mb-3 p-0">
+                        <label for="defaultLanguage">{{ __('Default language') }}:
+                            <i data-toggle="popover" data-trigger="hover"
+                                data-content="{{ __('The fallback Language, if something goes wrong') }}"
+                                class="fas fa-info-circle"></i>
+                        </label>
+
+                        <select id="defaultLanguage" style="width:100%" class="custom-select" name="defaultLanguage"
+                            required autocomplete="off" @error('defaultLanguage') is-invalid @enderror>
+                            @foreach (config('app.available_locales') as $lang)
+                                <option value="{{ $lang }}" @if (config('SETTINGS::LOCALE:DEFAULT') == $lang) selected
+                            @endif>{{ __($lang) }}</option>
+                            @endforeach
+                        </select>
+                    </div>
+
+                    <div class="custom-control mb-3 p-0">
+                        <!--DATATABLE LANGUAGE -->
+                        <label for="datatable-language">{{ __('Datable language') }} <i data-toggle="popover"
+                                data-trigger="hover" data-html="true"
+                                data-content="{{ __('The datatables lang-code. <br><strong>Example:</strong> en-gb, fr_fr, de_de<br>More Information: ') }} https://datatables.net/plug-ins/i18n/"
+                                class="fas fa-info-circle"></i></label>
+                        <input x-model="datatable-language" id="datatable-language" name="datatable-language"
+                            type="text" required value="{{ config('SETTINGS::LOCALE:DATATABLES') }}"
+                            class="form-control @error('datatable-language') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+
+
+            <div class="col-md-3 p-3">
+
+                <!-- AUTO TRANSLATE -->
+                <div class="form-group">
+                    <input value="true" id="autotranslate" name="autotranslate"
+                        {{ config('SETTINGS::LOCALE:DYNAMIC') == 'true' ? 'checked' : '' }} type="checkbox">
+                    <label for="autotranslate">{{ __('Auto-translate') }} <i data-toggle="popover"
+                            data-trigger="hover"
+                            data-content="{{ __('If this is checked, the Dashboard will translate itself to the Clients language, if available') }}"
+                            class="fas fa-info-circle"></i></label>
+
+                    <br />
+
+                    <!-- CLIENTS CAN CHANGE -->
+                    <input value="true" id="canClientChangeLanguage" name="canClientChangeLanguage"
+                        {{ config('SETTINGS::LOCALE:CLIENTS_CAN_CHANGE') == 'true' ? 'checked' : '' }}
+                        type="checkbox">
+                    <label for="canClientChangeLanguage">{{ __('Client Language-Switch') }} <i data-toggle="popover"
+                            data-trigger="hover"
+                            data-content="{{ __('If this is checked, Clients will have the ability to manually change their Dashboard language') }}"
+                            class="fas fa-info-circle"></i></label>
+
+                </div>
+            </div>
+        </div>
+        <div class="row">
+            <button class="btn btn-primary mt-3 ml-3">{{ __('Submit') }}</button>
+        </div>
+    </form>
+</div>
+
+<script>
+    document.addEventListener('DOMContentLoaded', (event) => {
+        $('.custom-select').select2();
+    })
+</script>

+ 194 - 0
resources/views/admin/settings/tabs/misc.blade.php

@@ -0,0 +1,194 @@
+<div class="tab-pane mt-3" id="misc">
+    <form method="POST" enctype="multipart/form-data" class="mb-3"
+        action="{{ route('admin.settings.update.miscsettings') }}">
+        @csrf
+        @method('PATCH')
+
+        <div class="row">
+
+            {{-- E-Mail --}}
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>E-Mail</h1>
+                    </div>
+                </div>
+
+                <div class="custom-control mb-3 p-0">
+                    <label for="mailservice">{{ __('Mail Service') }}:
+                        <i data-toggle="popover" data-trigger="hover"
+                            data-content="{{ __('The Mailer to send e-mails with') }}" class="fas fa-info-circle"></i>
+                    </label>
+                    <select id="mailservice" style="width:100%" class="custom-select" name="mailservice" required
+                        autocomplete="off" @error('mailservice') is-invalid @enderror>
+                        @foreach (array_keys(config('mail.mailers')) as $mailer)
+                            <option value="{{ $mailer }}" @if (config('SETTINGS::MAIL:MAILER') == $mailer) selected
+                        @endif>{{ __($mailer) }}</option>
+                        @endforeach
+                    </select>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="mailhost">{{ __('Mail Host') }}:</label>
+                        <input x-model="mailhost" id="mailhost" name="mailhost" type="text"
+                            value="{{ config('SETTINGS::MAIL:HOST') }}"
+                            class="form-control @error('mailhost') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="mailport">{{ __('Mail Port') }}:</label>
+                        <input x-model="mailhost" id="mailport" name="mailport" type="text"
+                            value="{{ config('SETTINGS::MAIL:PORT') }}"
+                            class="form-control @error('mailport') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="mailusername">{{ __('Mail Username') }}:</label>
+                        <input x-model="mailusername" id="mailusername" name="mailusername" type="text"
+                            value="{{ config('SETTINGS::MAIL:USERNAME') }}"
+                            class="form-control @error('mailusername') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="mailpassword">{{ __('Mail Password') }}:</label>
+                        <input x-model="mailpassword" id="mailpassword" name="mailpassword" type="text"
+                            value="{{ config('SETTINGS::MAIL:PASSWORD') }}"
+                            class="form-control @error('mailpassword') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="mailencryption">{{ __('Mail Encryption') }}:</label>
+                        <input x-model="mailencryption" id="mailencryption" name="mailencryption" type="text"
+                            value="{{ config('SETTINGS::MAIL:ENCRYPTION') }}"
+                            class="form-control @error('mailencryption') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="mailfromadress">{{ __('Mail From Adress') }}:</label>
+                        <input x-model="mailfromadress" id="mailfromadress" name="mailfromadress" type="text"
+                            value="{{ config('SETTINGS::MAIL:FROM_ADDRESS') }}"
+                            class="form-control @error('mailfromadress') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="mailfromname">{{ __('Mail From Name') }}:</label>
+                        <input x-model="mailfromname" id="mailfromname" name="mailfromname" type="text"
+                            value="{{ config('SETTINGS::MAIL:FROM_NAME') }}"
+                            class="form-control @error('mailfromname') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+
+            <!-- DISCORD -->
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>Discord</h1>
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="discord-client-id">{{ __('Discord Client-ID') }}:</label>
+                        <input x-model="discord-client-id" id="discord-client-id" name="discord-client-id" type="text"
+                            value="{{ config('SETTINGS::DISCORD:CLIENT_ID') }}"
+                            class="form-control @error('discord-client-id') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="discord-client-secret">{{ __('Discord Client-Secret') }}:</label>
+                        <input x-model="discord-client-secret" id="discord-client-secret" name="discord-client-secret"
+                            type="text" value="{{ config('SETTINGS::DISCORD:CLIENT_SECRET') }}"
+                            class="form-control @error('discord-client-secret') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="discord-client-secret">{{ __('Discord Bot-Token') }}:</label>
+                        <input x-model="discord-bot-token" id="discord-bot-token" name="discord-bot-token" type="text"
+                            value="{{ config('SETTINGS::DISCORD:BOT_TOKEN') }}"
+                            class="form-control @error('discord-bot-token') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="discord-client-secret">{{ __('Discord Guild-ID') }}:</label>
+                        <input x-model="discord-guild-id" id="discord-guild-id" name="discord-guild-id" type="number"
+                            value="{{ config('SETTINGS::DISCORD:GUILD_ID') }}"
+                            class="form-control @error('discord-guild-id') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="discord-invite-url">{{ __('Discord Invite-URL') }}:</label>
+                        <input x-model="discord-invite-url" id="discord-invite-url" name="discord-invite-url"
+                            type="text" value="{{ config('SETTINGS::DISCORD:INVITE_URL') }}"
+                            class="form-control @error('discord-invite-url') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="discord-role-id">{{ __('Discord Role-ID') }}:</label>
+                        <input x-model="discord-role-id" id="discord-role-id" name="discord-role-id" type="number"
+                            value="{{ config('SETTINGS::DISCORD:ROLE_ID') }}"
+                            class="form-control @error('discord-role-id') is-invalid @enderror">
+                    </div>
+                </div>
+
+            </div>
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>ReCaptcha</h1>
+                    </div>
+                </div>
+
+                <div class="custom-control mb-3 p-0">
+                    <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                        <div>
+                            <input value="true" id="enable-recaptcha" name="enable-recaptcha"
+                                {{ config('SETTINGS::RECAPTCHA:ENABLED') == 'true' ? 'checked' : '' }}
+                                type="checkbox">
+                            <label for="enable-recaptcha">{{ __('Enable ReCaptcha') }} </label>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="recaptcha-site-key">{{ __('ReCaptcha Site-Key') }}:</label>
+                        <input x-model="recaptcha-site-key" id="recaptcha-site-key" name="recaptcha-site-key"
+                            type="text" value="{{ config('SETTINGS::RECAPTCHA:SITE_KEY') }}"
+                            class="form-control @error('recaptcha-site-key') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="recaptcha-secret-key">{{ __('ReCaptcha Secret-Key') }}:</label>
+                        <input x-model="recaptcha-secret-key" id="recaptcha-secret-key" name="recaptcha-secret-key"
+                            type="text" value="{{ config('SETTINGS::RECAPTCHA:SECRET_KEY') }}"
+                            class="form-control @error('recaptcha-secret-key') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+
+
+        </div>
+        <div class="row">
+            <button class="btn btn-primary mt-3 ml-3">{{ __('Submit') }}</button>
+        </div>
+    </form>
+</div>

+ 144 - 0
resources/views/admin/settings/tabs/payment.blade.php

@@ -0,0 +1,144 @@
+<div class="tab-pane mt-3" id="payment">
+    <form method="POST" enctype="multipart/form-data" class="mb-3"
+        action="{{ route('admin.settings.update.paymentsettings') }}">
+        @csrf
+        @method('PATCH')
+
+        <div class="row">
+            {{-- PayPal --}}
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>PayPal</h1>
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="paypal-client-id">{{ __('PayPal Client-ID') }}:</label>
+                        <input x-model="paypal-client-id" id="paypal-client-id" name="paypal-client-id" type="text"
+                            value="{{ config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID') }}"
+                            class="form-control @error('paypal-client-id') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="paypal-client-secret">{{ __('PayPal Secret-Key') }}:</label>
+                        <input x-model="paypal-client-secret" id="paypal-client-secret" name="paypal-client-secret"
+                            type="text" value="{{ config('SETTINGS::PAYMENTS:PAYPAL:SECRET') }}"
+                            class="form-control @error('paypal-client-secret') is-invalid @enderror">
+                    </div>
+                </div>
+
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="paypal-sandbox-id">{{ __('PayPal Sandbox Client-ID') }}:</label>
+                        <small class="text-muted">({{ __('optional') }})</small>
+                        <input x-model="paypal-sandbox-id" id="paypal-sandbox-id" name="paypal-sandbox-id" type="text"
+                            value="{{ config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID') }}"
+                            class="form-control @error('paypal-sandbox-id') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="paypal-sandbox-secret">{{ __('PayPal Sandbox Secret-Key') }}:</label>
+                        <small class="text-muted">({{ __('optional') }})</small>
+                        <input x-model="paypal-sandbox-secret" id="paypal-sandbox-secret" name="paypal-sandbox-secret"
+                            type="text" value="{{ config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET') }}"
+                            class="form-control @error('paypal-sandbox-secret') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+
+            {{-- Stripe --}}
+            <div class="col-md-3 px-3">
+
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>Stripe</h1>
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="stripe-secret">{{ __('Stripe Secret-Key') }}:</label>
+                        <input x-model="stripe-secret" id="stripe-secret" name="stripe-secret" type="text"
+                            value="{{ config('SETTINGS::PAYMENTS:STRIPE:SECRET') }}"
+                            class="form-control @error('stripe-secret') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="stripe-endpoint-secret">{{ __('Stripe Endpoint-Secret-Key') }}:</label>
+                        <input x-model="stripe-endpoint-secret" id="stripe-endpoint-secret"
+                            name="stripe-endpoint-secret" type="text"
+                            value="{{ config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') }}"
+                            class="form-control @error('stripe-endpoint-secret') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="stripe-test-secret">{{ __('Stripe Test Secret-Key') }}:</label>
+                        <small class="text-muted">({{ __('optional') }})</small>
+                        <input x-model="stripe-test-secret" id="stripe-test-secret" name="stripe-test-secret"
+                            type="text" value="{{ config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') }}"
+                            class="form-control @error('stripe-test-secret') is-invalid @enderror">
+                    </div>
+                </div>
+
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <label for="stripe-endpoint-test-secret">{{ __('Stripe Test Endpoint-Secret-Key') }}:</label>
+                        <small class="text-muted">({{ __('optional') }})</small>
+                        <input x-model="stripe-endpoint-test-secret" id="stripe-endpoint-test-secret"
+                            name="stripe-endpoint-test-secret" type="text"
+                            value="{{ config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET') }}"
+                            class="form-control @error('stripe-endpoint-test-secret') is-invalid @enderror">
+                    </div>
+                </div>
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <label for="stripe-methods">{{ __('Payment Methods') }}:</label>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="Comma separated list of payment methods without whitespaces. <br><br> Example: card,klarna,sepa"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                        <input x-model="stripe-methods" id="stripe-methods" name="stripe-methods" type="text"
+                            value="{{ config('SETTINGS::PAYMENTS:STRIPE:METHODS') }}"
+                            class="form-control @error('stripe-methods') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+
+            {{-- Other --}}
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>Other</h1>
+                    </div>
+                </div>
+                <!-- Tax -->
+                <div class="form-group mb-3">
+                    <div class="custom-control p-0">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <label for="sales-tax">{{ __('Tax Value in %') }}:</label>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="Tax Value that will be added to the total price of the order. <br><br> Example: 19 results in (19%)"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                        <input x-model="sales-tax" id="sales-tax" name="sales-tax" type="number"
+                            value="{{ config('SETTINGS::PAYMENTS:SALES_TAX') }}"
+                            class="form-control @error('sales-tax') is-invalid @enderror">
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="row">
+            <button class="btn btn-primary ml-3 mt-3">{{ __('Submit') }}</button>
+        </div>
+    </form>
+</div>

+ 208 - 0
resources/views/admin/settings/tabs/system.blade.php

@@ -0,0 +1,208 @@
+<div class="tab-pane mt-3" id="system">
+    <form method="POST" enctype="multipart/form-data" class="mb-3"
+        action="{{ route('admin.settings.update.systemsettings') }}">
+        @csrf
+        @method('PATCH')
+
+        <div class="row">
+            {{-- System --}}
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>{{ __('System') }}</h1>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <div class="custom-control mb-1 p-0">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <div>
+                                <input value="true" id="register-ip-check" name="register-ip-check"
+                                    {{ config('SETTINGS::SYSTEM:REGISTER_IP_CHECK') == 'true' ? 'checked' : '' }}
+                                    type="checkbox">
+                                <label for="register-ip-check">{{ __('Register IP Check') }} </label>
+                            </div>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="{{ __('Prevent users from making multiple accounts using the same IP address.') }}"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                    </div>
+                    <div class="custom-control mb-3 p-0">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <div>
+                                <input value="true" id="server-create-charge-first" name="server-create-charge-first"
+                                    {{ config('SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR') == 'true' ? 'checked' : '' }}
+                                    type="checkbox">
+                                <label for="server-create-charge-first">{{ __('Charge first hour at creation') }}
+                                </label>
+                            </div>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="{{ __('Charges the first hour worth of credits upon creating a server.') }}"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                    </div>
+
+                    <div class="custom-control mb-3 p-0">
+                        <label for="credits-display-name">{{ __('Credits Display Name') }}</label>
+                        <input x-model="credits-display-name" id="credits-display-name" name="credits-display-name"
+                            type="text" value="{{ config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME', 'Credits') }}"
+                            class="form-control @error('credits-display-name') is-invalid @enderror" required>
+                    </div>
+                    <div class="custom-control p-0 mb-3">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <label for="phpmyadmin-url">{{ __('PHPMyAdmin URL') }}</label>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="{{ __('Enter the URL to your PHPMyAdmin installation. <strong>Without a trailing slash!</strong>') }}"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                        <input x-model="phpmyadmin-url" id="phpmyadmin-url" name="phpmyadmin-url" type="text"
+                            value="{{ config('SETTINGS::MISC:PHPMYADMIN:URL') }}"
+                            class="form-control @error('phpmyadmin-url') is-invalid @enderror">
+                    </div>
+                    <div class="custom-control p-0 mb-3">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <label for="pterodactyl-url">{{ __('Pterodactyl URL') }}</label>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="{{ __('Enter the URL to your Pterodactyl installation. <strong>Without a trailing slash!</strong>') }}"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                        <input x-model="pterodactyl-url" id="pterodactyl-url" name="pterodactyl-url" type="text"
+                            value="{{ config('SETTINGS::SYSTEM:PTERODACTYL:URL') }}"
+                            class="form-control @error('pterodactyl-url') is-invalid @enderror" required>
+                    </div>
+                    <div class="custom-control p-0 mb-3">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <label for="pterodactyl-api-key">{{ __('Pterodactyl API Key') }}</label>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="{{ __('Enter the API Key to your Pterodactyl installation.') }}"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                        <input x-model="pterodactyl-api-key" id="pterodactyl-api-key" name="pterodactyl-api-key"
+                            type="text" value="{{ config('SETTINGS::SYSTEM:PTERODACTYL:TOKEN') }}"
+                            class="form-control @error('pterodactyl-api-key') is-invalid @enderror" required>
+                    </div>
+
+                </div>
+
+            </div>
+
+            {{-- User --}}
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>{{ __('User') }}</h1>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <div class="custom-control mb-1 p-0">
+                        <input value="true" id="force-discord-verification" name="force-discord-verification"
+                            {{ config('SETTINGS::USER:FORCE_DISCORD_VERIFICATION') == 'true' ? 'checked' : '' }}
+                            type="checkbox">
+                        <label for="force-discord-verification">{{ __('Force Discord verification') }}
+                        </label>
+                    </div>
+                    <div class="custom-control mb-3 p-0">
+                        <input value="true" id="force-email-verification" name="force-email-verification"
+                            {{ config('SETTINGS::USER:FORCE_EMAIL_VERIFICATION') == 'true' ? 'checked' : '' }}
+                            type="checkbox">
+                        <label for="force-email-verification">{{ __('Force E-Mail verification') }} </label>
+                    </div>
+
+                    <div class="custom-control mb-3 p-0">
+                        <label for="initial-credits">{{ __('Initial Credits') }}</label>
+                        <input x-model="initial-credits" id="initial-credits" name="initial-credits" type="number"
+                            value="{{ config('SETTINGS::USER:INITIAL_CREDITS') }}"
+                            class="form-control @error('initial-credits') is-invalid @enderror" required>
+                    </div>
+                    <div class="custom-control mb-3 p-0">
+                        <label for="initial-server-limit">{{ __('Initial Server Limit') }}</label>
+                        <input x-model="initial-server-limit" id="initial-server-limit" name="initial-server-limit"
+                            type="number" value="{{ config('SETTINGS::USER:INITIAL_SERVER_LIMIT') }}"
+                            class="form-control @error('initial-server-limit') is-invalid @enderror" required>
+                    </div>
+                    <div class="custom-control mb-3 p-0">
+                        <label
+                            for="credits-reward-amount-discord">{{ __('Credits Reward Amount - Discord') }}</label>
+                        <input x-model="credits-reward-amount-discord" id="credits-reward-amount-discord"
+                            name="credits-reward-amount-discord" type="number"
+                            value="{{ config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD') }}"
+                            class="form-control @error('credits-reward-amount-discord') is-invalid @enderror" required>
+                    </div>
+
+                    <div class="custom-control mb-3 p-0">
+                        <label for="credits-reward-amount-email">{{ __('Credits Reward Amount - E-Mail') }}</label>
+                        <input x-model="credits-reward-amount-email" id="credits-reward-amount-email"
+                            name="credits-reward-amount-email" type="number"
+                            value="{{ config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL') }}"
+                            class="form-control @error('credits-reward-amount-email') is-invalid @enderror" required>
+                    </div>
+                    <div class="custom-control mb-3 p-0">
+                        <label for="server-limit-discord">{{ __('Server Limit Increase - Discord') }}</label>
+                        <input x-model="server-limit-discord" id="server-limit-discord" name="server-limit-discord"
+                            type="number"
+                            value="{{ config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD') }}"
+                            class="form-control @error('server-limit-discord') is-invalid @enderror" required>
+                    </div>
+                    <div class="custom-control mb-3 p-0">
+                        <label for="server-limit-email">{{ __('Server Limit Increase - E-Mail') }}</label>
+                        <input x-model="server-limit-email" id="server-limit-email" name="server-limit-email"
+                            type="number"
+                            value="{{ config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL') }}"
+                            class="form-control @error('server-limit-email') is-invalid @enderror" required>
+                    </div>
+                </div>
+            </div>
+
+            {{-- Server --}}
+            <div class="col-md-3 px-3">
+                <div class="row mb-2">
+                    <div class="col text-center">
+                        <h1>{{ __('Server') }}</h1>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <div class="custom-control mb-3 p-0">
+                        <div class="col m-0 p-0 d-flex justify-content-between align-items-center">
+                            <label for="initial-credits">{{ __('Server Allocation Limit') }}</label>
+                            <i data-toggle="popover" data-trigger="hover" data-html="true"
+                                data-content="{{ __('The maximum amount of allocations to pull per node for automatic deployment, if more allocations are being used than this limit is set to, no new servers can be created!') }}"
+                                class="fas fa-info-circle"></i>
+                        </div>
+                        <input x-model="allocation-limit" id="allocation-limit" name="allocation-limit" type="number"
+                            value="{{ config('SETTINGS::SERVER:ALLOCATION_LIMIT') }}"
+                            class="form-control @error('allocation-limit') is-invalid @enderror" required>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <div class="custom-file mb-3 mt-3">
+                        <input type="file" accept="image/png,image/jpeg,image/jpg" class="custom-file-input" name="icon"
+                            id="icon">
+                        <label class="custom-file-label selected" for="icon">{{ __('Select panel icon') }}</label>
+                    </div>
+                    @error('icon')
+                        <span class="text-danger">
+                            {{ $message }}
+                        </span>
+                    @enderror
+
+
+                    <div class="form-group">
+                        <div class="custom-file mb-3">
+                            <input type="file" accept="image/x-icon" class="custom-file-input" name="favicon"
+                                id="favicon">
+                            <label class="custom-file-label selected"
+                                for="favicon">{{ __('Select panel favicon') }}</label>
+                        </div>
+                        @error('favicon')
+                            <span class="text-danger">
+                                {{ $message }}
+                            </span>
+                        @enderror
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="row">
+            <button class="btn btn-primary ml-3 mt-3">{{ __('Submit') }}</button>
+        </div>
+    </form>
+</div>

+ 24 - 15
resources/views/layouts/app.blade.php

@@ -1,5 +1,6 @@
 <!doctype html>
 <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
+
 <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -8,7 +9,9 @@
     <meta name="csrf-token" content="{{ csrf_token() }}">
 
     <title>{{ config('app.name', 'Laravel') }}</title>
-    <link rel="icon" href="{{\Illuminate\Support\Facades\Storage::disk('public')->exists('favicon.ico') ? \Illuminate\Support\Facades\Storage::disk('public')->url('favicon.ico') : asset('favicon.ico')}}" type="image/x-icon">
+    <link rel="icon"
+        href="{{ \Illuminate\Support\Facades\Storage::disk('public')->exists('favicon.ico') ? \Illuminate\Support\Facades\Storage::disk('public')->url('favicon.ico') : asset('favicon.ico') }}"
+        type="image/x-icon">
 
     <!-- Scripts -->
     <script src="{{ asset('js/app.js') }}" defer></script>
@@ -17,27 +20,32 @@
     <link rel="dns-prefetch" href="//fonts.gstatic.com">
     <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
 
-    <link rel="stylesheet" href="{{asset('css/app.css')}}">
-    <link rel="preload" href="{{asset('plugins/fontawesome-free/css/all.min.css')}}" as="style" onload="this.onload=null;this.rel='stylesheet'">
-    <noscript><link rel="stylesheet" href="{{asset('plugins/fontawesome-free/css/all.min.css')}}"></noscript>
-    {!! htmlScriptTagJsApi() !!}
+    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
+    <link rel="preload" href="{{ asset('plugins/fontawesome-free/css/all.min.css') }}" as="style"
+        onload="this.onload=null;this.rel='stylesheet'">
+    <noscript>
+        <link rel="stylesheet" href="{{ asset('plugins/fontawesome-free/css/all.min.css') }}">
+    </noscript>
+    @if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true')
+        {!! htmlScriptTagJsApi() !!}
+    @endif
 </head>
 @yield('content')
 
 <script src="https://cdn.jsdelivr.net/npm/sweetalert2@10.14.1/dist/sweetalert2.all.min.js"></script>
 <script>
-    @if(Session::has('error'))
-    Swal.fire({
+    @if (Session::has('error'))
+        Swal.fire({
         icon: 'error',
         title: 'Oops...',
-        html: '{{Session::get('error')}}',
-    })
+        html: '{{ Session::get('error') }}',
+        })
     @endif
 
-    @if(Session::has('success'))
-    Swal.fire({
+    @if (Session::has('success'))
+        Swal.fire({
         icon: 'success',
-        title: '{{Session::get('success')}}',
+        title: '{{ Session::get('success') }}',
         position: 'top-end',
         showConfirmButton: false,
         background : '#343a40',
@@ -45,10 +53,11 @@
         timer: 3000,
         timerProgressBar: true,
         didOpen: (toast) => {
-            toast.addEventListener('mouseenter', Swal.stopTimer)
-            toast.addEventListener('mouseleave', Swal.resumeTimer)
+        toast.addEventListener('mouseenter', Swal.stopTimer)
+        toast.addEventListener('mouseleave', Swal.resumeTimer)
         }
-    })
+        })
     @endif
 </script>
+
 </html>

+ 46 - 34
resources/views/layouts/main.blade.php

@@ -32,6 +32,7 @@
     <noscript>
         <link rel="stylesheet" href="{{ asset('plugins/fontawesome-free/css/all.min.css') }}">
     </noscript>
+    <script src="{{ asset('js/app.js') }}"></script>
 </head>
 
 <body class="sidebar-mini layout-fixed dark-mode" style="height: auto;">
@@ -53,7 +54,6 @@
 
     <!-- Select2 -->
     <script src={{ asset('plugins/select2/js/select2.min.js') }}></script>
-
     <div class="wrapper">
         <!-- Navbar -->
         <nav class="main-header sticky-top navbar navbar-expand navbar-dark navbar-light">
@@ -68,34 +68,34 @@
                             class="fas fa-home mr-2"></i>{{ __('Home') }}</a>
                 </li>
                 <li class="nav-item d-none d-sm-inline-block">
-                    <a href="{{ env('DISCORD_INVITE_URL') }}" class="nav-link" target="__blank"><i
+                    <a href="{{ config('SETTINGS::DISCORD:INVITE_URL') }}" class="nav-link" target="__blank"><i
                             class="fab fa-discord mr-2"></i>{{ __('Discord') }}</a>
                 </li>
                 <!-- Language Selection -->
-                <li class="nav-item dropdown">
-                    <a class="nav-link" href="#" id="languageDropdown" role="button" data-toggle="dropdown"
-                        aria-haspopup="true" aria-expanded="false">
-                        <span class="mr-1 d-lg-inline text-gray-600">
-                            <small><i class="fa fa-language mr-2"></i></small>{{ __('Languages') }}
-                        </span>
-                    </a>
-                    <div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"
-                        aria-labelledby="changeLocale">
-                        <form method="post" action="{{ route('changeLocale') }}" class="nav-item text-center">
-                            @csrf
-                            @foreach (config('app.available_locales') as $key => $value)
-                                @if (!in_array(strtolower($key), UNSUPPORTED_LANGS))
-                                    <button class="dropdown-item" name="inputLocale" value="{{ $value }}">
-                                        {{ $key }}
+                @if (config('SETTINGS::LOCALE:CLIENTS_CAN_CHANGE') == 'true')
+                    <li class="nav-item dropdown">
+                        <a class="nav-link" href="#" id="languageDropdown" role="button" data-toggle="dropdown"
+                            aria-haspopup="true" aria-expanded="false">
+                            <span class="mr-1 d-lg-inline text-gray-600">
+                                <small><i class="fa fa-language mr-2"></i></small>{{ __('Language') }}
+                            </span>
+                        </a>
+                        <div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"
+                            aria-labelledby="changeLocale">
+                            <form method="post" action="{{ route('changeLocale') }}" class="nav-item text-center">
+                                @csrf
+                                @foreach (explode(',', config('SETTINGS::LOCALE:AVAILABLE')) as $key)
+                                    <button class="dropdown-item" name="inputLocale" value="{{ $key }}">
+                                        {{ __($key) }}
                                     </button>
-                                @endif
 
-                            @endforeach
+                                @endforeach
 
-                        </form>
-                    </div>
-                </li>
-                <!-- End Language Selection -->
+                            </form>
+                        </div>
+                    </li>
+                    <!-- End Language Selection -->
+                @endif
             </ul>
 
             <!-- Right navbar links -->
@@ -230,7 +230,7 @@
                             </a>
                         </li>
 
-                        @if ((env('PAYPAL_SECRET') && env('PAYPAL_CLIENT_ID')) || env('APP_ENV', 'local') == 'local')
+                        @if ((config('SETTINGS::PAYMENTS:PAYPAL:SECRET') && config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID')) || env('APP_ENV', 'local') == 'local')
                             <li class="nav-item">
                                 <a href="{{ route('store.index') }}" class="nav-link @if (Request::routeIs('store.*') || Request::routeIs('checkout')) active @endif">
                                     <i class="nav-icon fa fa-coins"></i>
@@ -251,13 +251,6 @@
                                 </a>
                             </li>
 
-                            <li class="nav-item">
-                                <a href="{{ route('admin.configurations.index') }}"
-                                    class="nav-link @if (Request::routeIs('admin.configurations.*')) active @endif">
-                                    <i class="nav-icon fas fa-cogs"></i>
-                                    <p>{{ __('Configurations') }}</p>
-                                </a>
-                            </li>
 
                             <li class="nav-item">
                                 <a href="{{ route('admin.settings.index') }}"
@@ -300,8 +293,7 @@
                             </li>
 
                             <li class="nav-item">
-                                <a href="{{ route('admin.store.index') }}"
-                                    class="nav-link @if (Request::routeIs('admin.store.*')) active @endif">
+                                <a href="{{ route('admin.store.index') }}" class="nav-link @if (Request::routeIs('admin.store.*')) active @endif">
                                     <i class="nav-icon fas fa-shopping-basket"></i>
                                     <p>{{ __('Store') }}</p>
                                 </a>
@@ -411,6 +403,27 @@
     </div>
     <!-- ./wrapper -->
 
+    <!-- Scripts -->
+    {{-- <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> --}}
+    {{-- <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> --}}
+    {{-- <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> --}}
+    {{-- <script src="{{ asset('js/adminlte.min.js') }}"></script> --}}
+    <script src="https://cdn.jsdelivr.net/npm/sweetalert2@10.14.1/dist/sweetalert2.all.min.js"></script>
+
+    <script type="text/javascript" src="https://cdn.datatables.net/v/bs4/dt-1.10.24/datatables.min.js"></script>
+    <!-- Summernote -->
+    <script src="{{ asset('plugins/summernote/summernote-bs4.min.js') }}"></script>
+    <!-- select2 -->
+    <script src="{{ asset('plugins/select2/js/select2.min.js') }}"></script>
+
+    <!-- Moment.js -->
+    <script src="{{ asset('plugins/moment/moment.min.js') }}"></script>
+
+    <!-- Datetimepicker -->
+    <script src="{{ asset('plugins/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js') }}"></script>
+
+    <!-- Select2 -->
+    <script src={{ asset('plugins/select2/js/select2.min.js') }}></script>
     <script>
         $(document).ready(function() {
             $('[data-toggle="popover"]').popover();
@@ -430,7 +443,6 @@
             html: '{{ Session::get('error') }}',
             })
         @endif
-
         @if (Session::has('success'))
             Swal.fire({
             icon: 'success',

+ 83 - 83
resources/views/profile/index.blade.php

@@ -6,13 +6,13 @@
         <div class="container-fluid">
             <div class="row mb-2">
                 <div class="col-sm-6">
-                    <h1>{{__('Profile')}}</h1>
+                    <h1>{{ __('Profile') }}</h1>
                 </div>
                 <div class="col-sm-6">
                     <ol class="breadcrumb float-sm-right">
-                        <li class="breadcrumb-item"><a href="{{route('home')}}">{{__('Dashboard')}}</a></li>
+                        <li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
                         <li class="breadcrumb-item"><a class="text-muted"
-                                                       href="{{route('profile.index')}}">{{__('Profile')}}</a>
+                                href="{{ route('profile.index') }}">{{ __('Profile') }}</a>
                         </li>
                     </ol>
                 </div>
@@ -27,36 +27,35 @@
 
             <div class="row">
                 <div class="col-lg-12 px-0">
-                    @if(!Auth::user()->hasVerifiedEmail() && strtolower($force_email_verification) == 'true')
+                    @if (!Auth::user()->hasVerifiedEmail() && strtolower($force_email_verification) == 'true')
                         <div class="alert alert-warning p-2 m-2">
-                            <h5><i class="icon fas fa-exclamation-circle"></i>{{__('Required Email verification!')}}
+                            <h5><i class="icon fas fa-exclamation-circle"></i>{{ __('Required Email verification!') }}
                             </h5>
-                            {{__('You have not yet verified your email address')}}
+                            {{ __('You have not yet verified your email address') }}
                             <a class="text-primary"
-                               href="{{route('verification.send')}}">{{__('Click here to resend verification email')}}</a>
+                                href="{{ route('verification.send') }}">{{ __('Click here to resend verification email') }}</a>
                             <br>
-                            {{__('Please contact support If you didnt receive your verification email.')}}
+                            {{ __('Please contact support If you didnt receive your verification email.') }}
+
                         </div>
                     @endif
 
-                    @if(is_null(Auth::user()->discordUser) && strtolower($force_discord_verification) == 'true')
-                        @if(!empty(env('DISCORD_CLIENT_ID')) && !empty(env('DISCORD_CLIENT_SECRET')))
+                    @if (is_null(Auth::user()->discordUser) && strtolower($force_discord_verification) == 'true')
+                        @if (!empty(config('SETTINGS::DISCORD:CLIENT_ID')) && !empty(config('SETTINGS::DISCORD:CLIENT_SECRET')))
                             <div class="alert alert-warning p-2 m-2">
-                                <h5>
-                                    <i class="icon fas fa-exclamation-circle"></i>{{__('Required Discord verification!')}}
+                                <h5><i class="icon fas fa-exclamation-circle"></i>{{ __('Required Discord verification!') }}
                                 </h5>
-                                {{__('You have not yet verified your discord account')}}
+                                {{ __('You have not yet verified your discord account') }}
                                 <a class="text-primary"
-                                   href="{{route('auth.redirect')}}">{{__('Login with discord')}}</a> <br>
-                                {{__('Please contact support If you face any issues.')}}
+                                    href="{{ route('auth.redirect') }}">{{ __('Login with discord') }}</a> <br>
+                                {{ __('Please contact support If you face any issues.') }}
                             </div>
                         @else
                             <div class="alert alert-danger p-2 m-2">
-                                <h5>
-                                    <i class="icon fas fa-exclamation-circle"></i>{{__('Required Discord verification!')}}
+                                <h5><i class="icon fas fa-exclamation-circle"></i>{{ __('Required Discord verification!') }}
                                 </h5>
-                                {{__('Due to system settings you are required to verify your discord account!')}} <br>
-                                {{__('It looks like this hasnt been set-up correctly! Please contact support.')}}'
+                                {{ __('Due to system settings you are required to verify your discord account!') }} <br>
+                                {{ __('It looks like this hasnt been set-up correctly! Please contact support.') }}'
                             </div>
                         @endif
                     @endif
@@ -64,7 +63,7 @@
                 </div>
             </div>
 
-            <form class="form" action="{{route('profile.update' , Auth::user()->id)}}" method="post">
+            <form class="form" action="{{ route('profile.update', Auth::user()->id) }}" method="post">
                 @csrf
                 @method('PATCH')
                 <div class="card">
@@ -73,43 +72,42 @@
                             <div class="row">
                                 <div class="col-12 col-sm-auto mb-4">
                                     <div class="slim rounded-circle  border-secondary border text-gray-dark"
-                                         data-label="Change your avatar"
-                                         data-max-file-size="3"
-                                         data-save-initial-image="true"
-                                         style="width: 140px;height:140px; cursor: pointer"
-                                         data-size="140,140">
-                                        <img src="{{$user->getAvatar()}}" alt="avatar">
+                                        data-label="Change your avatar" data-max-file-size="3"
+                                        data-save-initial-image="true" style="width: 140px;height:140px; cursor: pointer"
+                                        data-size="140,140">
+                                        <img src="{{ $user->getAvatar() }}" alt="avatar">
                                     </div>
                                 </div>
                                 <div class="col d-flex flex-column flex-sm-row justify-content-between mb-3">
-                                    <div class="text-center text-sm-left mb-2 mb-sm-0"><h4
-                                            class="pt-sm-2 pb-1 mb-0 text-nowrap">{{$user->name}}</h4>
-                                        <p class="mb-0">{{$user->email}}
-                                            @if($user->hasVerifiedEmail())
+                                    <div class="text-center text-sm-left mb-2 mb-sm-0">
+                                        <h4 class="pt-sm-2 pb-1 mb-0 text-nowrap">{{ $user->name }}</h4>
+                                        <p class="mb-0">{{ $user->email }}
+                                            @if ($user->hasVerifiedEmail())
                                                 <i data-toggle="popover" data-trigger="hover" data-content="Verified"
-                                                   class="text-success fas fa-check-circle"></i>
+                                                    class="text-success fas fa-check-circle"></i>
                                             @else
-                                                <i data-toggle="popover" data-trigger="hover"
-                                                   data-content="Not verified"
-                                                   class="text-danger fas fa-exclamation-circle"></i>
+                                                <i data-toggle="popover" data-trigger="hover" data-content="Not verified"
+                                                    class="text-danger fas fa-exclamation-circle"></i>
                                             @endif
 
                                         </p>
                                         <div class="mt-1">
-                                            <span class="badge badge-primary"><i class="fa fa-coins mr-2"></i>{{$user->Credits()}}</span>
+                                            <span class="badge badge-primary"><i
+                                                    class="fa fa-coins mr-2"></i>{{ $user->Credits() }}</span>
                                         </div>
                                     </div>
 
                                     <div class="text-center text-sm-right"><span
-                                            class="badge badge-secondary">{{$user->role}}</span>
-                                        <div class="text-muted"><small>{{$user->created_at->isoFormat('LL')}}</small>
+                                            class="badge badge-secondary">{{ $user->role }}</span>
+                                        <div class="text-muted">
+                                            <small>{{ $user->created_at->isoFormat('LL') }}</small>
                                         </div>
                                     </div>
                                 </div>
                             </div>
                             <ul class="nav nav-tabs">
                                 <li class="nav-item"><a href="javasript:void(0)"
-                                                        class="active nav-link">{{__('Settings')}}</a>
+                                        class="active nav-link">{{ __('Settings') }}</a>
                                 </li>
                             </ul>
                             <div class="tab-content pt-3">
@@ -134,29 +132,28 @@
                                                         @endif
                                                     <div class="form-group"><label>{{__('Name')}}</label> <input
                                                             class="form-control @error('name') is-invalid @enderror"
-                                                            type="text" name="name"
-                                                            placeholder="{{$user->name}}" value="{{$user->name}}">
+                                                            type="text" name="name" placeholder="{{ $user->name }}"
+                                                            value="{{ $user->name }}">
 
                                                         @error('name')
-                                                        <div class="invalid-feedback">
-                                                            {{$message}}
-                                                        </div>
+                                                            <div class="invalid-feedback">
+                                                                {{ $message }}
+                                                            </div>
                                                         @enderror
                                                     </div>
                                                 </div>
                                             </div>
                                             <div class="row">
                                                 <div class="col">
-                                                    <div class="form-group"><label>{{__('Email')}}</label> <input
+                                                    <div class="form-group"><label>{{ __('Email') }}</label> <input
                                                             class="form-control @error('email') is-invalid @enderror"
-                                                            type="text"
-                                                            placeholder="{{$user->email}}" name="email"
-                                                            value="{{$user->email}}">
+                                                            type="text" placeholder="{{ $user->email }}" name="email"
+                                                            value="{{ $user->email }}">
 
                                                         @error('email')
-                                                        <div class="invalid-feedback">
-                                                            {{$message}}
-                                                        </div>
+                                                            <div class="invalid-feedback">
+                                                                {{ $message }}
+                                                            </div>
                                                         @enderror
                                                     </div>
                                                 </div>
@@ -165,33 +162,34 @@
                                     </div>
                                     <div class="row">
                                         <div class="col-12 col-sm-6 mb-3">
-                                            <div class="mb-3"><b>{{__('Change Password')}}</b></div>
+                                            <div class="mb-3"><b>{{ __('Change Password') }}</b></div>
                                             <div class="row">
                                                 <div class="col">
-                                                    <div class="form-group"><label>{{__('Current Password')}}</label>
+                                                    <div class="form-group">
+                                                        <label>{{ __('Current Password') }}</label>
                                                         <input
                                                             class="form-control @error('current_password') is-invalid @enderror"
-                                                            name="current_password" type="password"
-                                                            placeholder="••••••">
+                                                            name="current_password" type="password" placeholder="••••••">
 
                                                         @error('current_password')
-                                                        <div class="invalid-feedback">
-                                                            {{$message}}
-                                                        </div>
+                                                            <div class="invalid-feedback">
+                                                                {{ $message }}
+                                                            </div>
                                                         @enderror
                                                     </div>
                                                 </div>
                                             </div>
                                             <div class="row">
                                                 <div class="col">
-                                                    <div class="form-group"><label>{{__('New Password')}}</label> <input
+                                                    <div class="form-group"><label>{{ __('New Password') }}</label>
+                                                        <input
                                                             class="form-control @error('new_password') is-invalid @enderror"
                                                             name="new_password" type="password" placeholder="••••••">
 
                                                         @error('new_password')
-                                                        <div class="invalid-feedback">
-                                                            {{$message}}
-                                                        </div>
+                                                            <div class="invalid-feedback">
+                                                                {{ $message }}
+                                                            </div>
                                                         @enderror
                                                     </div>
                                                 </div>
@@ -199,60 +197,62 @@
                                             <div class="row">
                                                 <div class="col">
                                                     <div class="form-group">
-                                                        <label>{{__('Confirm Password')}}</span></label>
+                                                        <label>{{ __('Confirm Password') }}</span></label>
                                                         <input
                                                             class="form-control @error('new_password_confirmation') is-invalid @enderror"
                                                             name="new_password_confirmation" type="password"
                                                             placeholder="••••••">
 
                                                         @error('new_password_confirmation')
-                                                        <div class="invalid-feedback">
-                                                            {{$message}}
-                                                        </div>
+                                                            <div class="invalid-feedback">
+                                                                {{ $message }}
+                                                            </div>
                                                         @enderror
                                                     </div>
                                                 </div>
                                             </div>
                                         </div>
-                                        @if(!empty(env('DISCORD_CLIENT_ID')) && !empty(env('DISCORD_CLIENT_SECRET')))
+                                        @if (!empty(config('SETTINGS::DISCORD:CLIENT_ID')) && !empty(config('SETTINGS::DISCORD:CLIENT_SECRET')))
                                             <div class="col-12 col-sm-5 offset-sm-1 mb-3">
-                                                @if(is_null(Auth::user()->discordUser))
-                                                    <b>{{__('Link your discord account!')}}</b>
+                                                @if (is_null(Auth::user()->discordUser))
+                                                    <b>{{ __('Link your discord account!') }}</b>
                                                     <div class="verify-discord">
                                                         <div class="mb-3">
-                                                            @if($credits_reward_after_verify_discord)
-                                                                <p>{{__('By verifying your discord account, you receive extra Credits and increased Server amounts')}}
+                                                            @if ($credits_reward_after_verify_discord)
+                                                                <p>{{ __('By verifying your discord account, you receive extra Credits and increased Server amounts') }}
                                                                 </p>
                                                             @endif
                                                         </div>
                                                     </div>
 
-                                                    <a class="btn btn-light" href="{{route('auth.redirect')}}">
-                                                        <i class="fab fa-discord mr-2"></i>{{__('Login with Discord')}}
+                                                    <a class="btn btn-light" href="{{ route('auth.redirect') }}">
+                                                        <i class="fab fa-discord mr-2"></i>{{ __('Login with Discord') }}
                                                     </a>
                                                 @else
                                                     <div class="verified-discord">
                                                         <div class="my-3 callout callout-info">
-                                                            <p>{{__('You are verified!')}}</p>
+                                                            <p>{{ __('You are verified!') }}</p>
                                                         </div>
                                                     </div>
                                                     <div class="row pl-2">
                                                         <div class="small-box bg-dark">
                                                             <div class="d-flex justify-content-between">
                                                                 <div class="p-3">
-                                                                    <h3>{{$user->discordUser->username}}
-                                                                        <sup>{{$user->discordUser->locale}}</sup></h3>
-                                                                    <p>{{$user->discordUser->id}}
+                                                                    <h3>{{ $user->discordUser->username }}
+                                                                        <sup>{{ $user->discordUser->locale }}</sup>
+                                                                    </h3>
+                                                                    <p>{{ $user->discordUser->id }}
                                                                     </p>
                                                                 </div>
-                                                                <div class="p-3"><img width="100px" height="100px"
-                                                                                      class="rounded-circle"
-                                                                                      src="{{$user->discordUser->getAvatar()}}"
-                                                                                      alt="avatar"></div>
+                                                                <div class="p-3"><img width="100px"
+                                                                        height="100px" class="rounded-circle"
+                                                                        src="{{ $user->discordUser->getAvatar() }}"
+                                                                        alt="avatar"></div>
                                                             </div>
                                                             <div class="small-box-footer">
-                                                                <a href="{{route('auth.redirect')}}">
-                                                                    <i class="fab fa-discord mr-1"></i>{{__('Re-Sync Discord')}}
+                                                                <a href="{{ route('auth.redirect') }}">
+                                                                    <i
+                                                                        class="fab fa-discord mr-1"></i>{{ __('Re-Sync Discord') }}
                                                                 </a>
                                                             </div>
                                                         </div>
@@ -265,7 +265,7 @@
                                     <div class="row">
                                         <div class="col d-flex justify-content-end">
                                             <button class="btn btn-primary"
-                                                    type="submit">{{__('Save Changes')}}</button>
+                                                type="submit">{{ __('Save Changes') }}</button>
                                         </div>
                                     </div>
 

+ 2 - 0
resources/views/servers/create.blade.php

@@ -42,7 +42,9 @@
                                     @if (Auth::user()->role == 'admin')
                                         {{ __('Make sure to link your products to nodes and eggs.') }} <br>
                                         {{ __('There has to be at least 1 valid product for server creation') }}
+                                        <a href="{{route('admin.overview.sync')}}">{{ __('Sync now') }}</a>
                                     @endif
+
                                 </p>
                                 <ul>
                                     @if ($productCount === 0)

+ 3 - 3
resources/views/servers/index.blade.php

@@ -55,8 +55,8 @@
                                         </a>
                                         <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
                                             aria-labelledby="dropdownMenuLink">
-                                            @if (!empty(env('PHPMYADMIN_URL')))
-                                                <a href="{{ env('PHPMYADMIN_URL', 'http://localhost') }}"
+                                            @if (!empty(config('SETTINGS::MISC:PHPMYADMIN:URL')))
+                                                <a href="{{ config('SETTINGS::MISC:PHPMYADMIN:URL') }}"
                                                     class="dropdown-item text-info" target="__blank"><i title="manage"
                                                         class="fas fa-database mr-2"></i><span>{{ __('Database') }}</span></a>
                                             @endif
@@ -149,7 +149,7 @@
                         </div>
 
                         <div class="card-footer d-flex align-items-center justify-content-between">
-                            <a href="{{ env('PTERODACTYL_URL', 'http://localhost') }}/server/{{ $server->identifier }}"
+                            <a href="{{ config('SETTINGS::SYSTEM:PTERODACTYL:URL') }}/server/{{ $server->identifier }}"
                                 target="__blank"
                                 class="btn btn-info mx-3 w-100 align-items-center justify-content-center d-flex">
                                 <i class="fas fa-tools mr-2"></i>

+ 2 - 2
resources/views/store/checkout.blade.php

@@ -78,7 +78,7 @@
                                 <p class="lead">{{ __('Payment Methods') }}:</p>
 
                                 <div>
-                                    @if (env('PAYPAL_SANDBOX_SECRET') || env('PAYPAL_SECRET'))
+                                    @if (config('SETTINGS::PAYMENTS:PAYPAL:SECRET') || config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET'))
                                         <label class="text-center " for="paypal">
                                             <img class="mb-3" height="50"
                                                 src="{{ url('/images/paypal_logo.png') }}"></br>
@@ -88,7 +88,7 @@
                                             </input>
                                         </label>
                                     @endif
-                                    @if (env('STRIPE_TEST_SECRET') || env('STRIPE_SECRET'))
+                                    @if (config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') || config('SETTINGS::PAYMENTS:STRIPE:SECRET'))
                                         <label class="ml-5 text-center " for="stripe">
                                             <img class="mb-3" height="50"
                                                 src="{{ url('/images/stripe_logo.png') }}" /></br>

+ 31 - 19
routes/web.php

@@ -1,8 +1,10 @@
 <?php
 
+use App\Classes\Settings\Misc;
+use App\Classes\Settings\Payments;
 use App\Http\Controllers\Admin\ActivityLogController;
 use App\Http\Controllers\Admin\ApplicationApiController;
-use App\Http\Controllers\Admin\ConfigurationController;
+use App\Http\Controllers\Admin\InvoiceController;
 use App\Http\Controllers\Admin\OverViewController;
 use App\Http\Controllers\Admin\PaymentController;
 use App\Http\Controllers\Admin\CreditProductController;
@@ -23,6 +25,9 @@ use App\Http\Controllers\TranslationController;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Route;
+use App\Classes\Settings\Language;
+use App\Classes\Settings\Invoices;
+use App\Classes\Settings\System;
 
 /*
 |--------------------------------------------------------------------------
@@ -72,8 +77,6 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
     Route::get('payment/StripeSuccess', [PaymentController::class, 'StripeSuccess'])->name('payment.StripeSuccess');
     Route::get('payment/Cancel', [PaymentController::class, 'Cancel'])->name('payment.Cancel');
 
-
-
     Route::get('users/logbackin', [UserController::class, 'logBackIn'])->name('users.logbackin');
 
     #discord
@@ -85,14 +88,18 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
 
     #switch language
     Route::post('changelocale', [TranslationController::class, 'changeLocale'])->name('changeLocale');
+
+
     #admin
     Route::prefix('admin')->name('admin.')->middleware('admin')->group(function () {
 
+        #overview
         Route::get('overview', [OverViewController::class, 'index'])->name('overview.index');
         Route::get('overview/sync', [OverViewController::class, 'syncPterodactyl'])->name('overview.sync');
 
         Route::resource('activitylogs', ActivityLogController::class);
 
+        #users
         Route::get("users.json", [UserController::class, "json"])->name('users.json');
         Route::get('users/loginas/{user}', [UserController::class, 'loginAs'])->name('users.loginas');
         Route::get('users/datatable', [UserController::class, 'datatable'])->name('users.datatable');
@@ -101,50 +108,55 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
         Route::post('users/togglesuspend/{user}', [UserController::class, 'toggleSuspended'])->name('users.togglesuspend');
         Route::resource('users', UserController::class);
 
+        #servers
         Route::get('servers/datatable', [AdminServerController::class, 'datatable'])->name('servers.datatable');
         Route::post('servers/togglesuspend/{server}', [AdminServerController::class, 'toggleSuspended'])->name('servers.togglesuspend');
         Route::resource('servers', AdminServerController::class);
 
+        #products
         Route::get('products/datatable', [ProductController::class, 'datatable'])->name('products.datatable');
         Route::get('products/clone/{product}', [ProductController::class, 'clone'])->name('products.clone');
         Route::patch('products/disable/{product}', [ProductController::class, 'disable'])->name('products.disable');
         Route::resource('products', ProductController::class);
 
+        #store
         Route::get('store/datatable', [CreditProductController::class, 'datatable'])->name('store.datatable');
         Route::patch('store/disable/{creditProduct}', [CreditProductController::class, 'disable'])->name('store.disable');
         Route::resource('store', CreditProductController::class)->parameters([
             'store' => 'creditProduct',
         ]);
 
+        #payments
         Route::get('payments/datatable', [PaymentController::class, 'datatable'])->name('payments.datatable');
         Route::get('payments', [PaymentController::class, 'index'])->name('payments.index');
 
-//        Route::get('nodes/datatable', [NodeController::class, 'datatable'])->name('nodes.datatable');
-//        Route::get('nodes/sync', [NodeController::class, 'sync'])->name('nodes.sync');
-//        Route::resource('nodes', NodeController::class);
-//
-//        Route::get('nests/datatable', [NestsController::class, 'datatable'])->name('nests.datatable');
-//        Route::get('nests/sync', [NestsController::class, 'sync'])->name('nests.sync');
-//        Route::resource('nests', NestsController::class);
-
-        Route::get('configurations/datatable', [ConfigurationController::class, 'datatable'])->name('configurations.datatable');
-        Route::patch('configurations/updatevalue', [ConfigurationController::class, 'updatevalue'])->name('configurations.updatevalue');
-        Route::resource('configurations', ConfigurationController::class);
-        Route::resource('configurations', ConfigurationController::class);
-
-        Route::patch('settings/update/icons', [SettingsController::class, 'updateIcons'])->name('settings.update.icons');
-        Route::patch('settings/update/invoice-settings', [SettingsController::class, 'updateInvoiceSettings'])->name('settings.update.invoicesettings');
-        Route::get('settings/download-invoices', [SettingsController::class, 'downloadAllInvoices'])->name('settings.downloadAllInvoices');;
+        #settings
+        Route::get('settings/datatable', [SettingsController::class, 'datatable'])->name('settings.datatable');
+        Route::patch('settings/updatevalue', [SettingsController::class, 'updatevalue'])->name('settings.updatevalue');
+
+        #settings
+        Route::patch('settings/update/invoice-settings', [Invoices::class, 'updateSettings'])->name('settings.update.invoicesettings');
+        Route::patch('settings/update/language', [Language::class, 'updateSettings'])->name('settings.update.languagesettings');
+        Route::patch('settings/update/payment', [Payments::class, 'updateSettings'])->name('settings.update.paymentsettings');
+        Route::patch('settings/update/misc', [Misc::class, 'updateSettings'])->name('settings.update.miscsettings');
+        Route::patch('settings/update/system', [System::class, 'updateSettings'])->name('settings.update.systemsettings');
         Route::resource('settings', SettingsController::class)->only('index');
 
+        #invoices
+        Route::get('invoices/download-invoices', [InvoiceController::class, 'downloadAllInvoices'])->name('invoices.downloadAllInvoices');;
+        Route::get('invoices/download-single-invoice', [InvoiceController::class, 'downloadSingleInvoice'])->name('invoices.downloadSingleInvoice');;
+
+        #usefullinks
         Route::get('usefullinks/datatable', [UsefulLinkController::class, 'datatable'])->name('usefullinks.datatable');
         Route::resource('usefullinks', UsefulLinkController::class);
 
+        #vouchers
         Route::get('vouchers/datatable', [VoucherController::class, 'datatable'])->name('vouchers.datatable');
         Route::get('vouchers/{voucher}/usersdatatable', [VoucherController::class, 'usersdatatable'])->name('vouchers.usersdatatable');
         Route::get('vouchers/{voucher}/users', [VoucherController::class, 'users'])->name('vouchers.users');
         Route::resource('vouchers', VoucherController::class);
 
+        #api-keys
         Route::get('api/datatable', [ApplicationApiController::class, 'datatable'])->name('api.datatable');
         Route::resource('api', ApplicationApiController::class)->parameters([
             'api' => 'applicationApi',