Compare commits

...

15 commits

Author SHA1 Message Date
Dennis 4358d071b3
Fix old name and dead links (#949) 2024-05-07 09:35:04 +02:00
MrWeez e7aad7ee6c Update app.php 2024-05-07 04:59:29 +03:00
MrWeez 6623f957a1 Fix old name and dead links 2024-05-07 04:58:15 +03:00
Dennis 335951b7e6
Update app.php (#943) 2024-04-24 10:50:21 +02:00
MrWeez 9299eac552
Update app.php 2024-04-12 22:20:25 +03:00
Ferks-FK aed5b9d16e fix: change alpine script link 2024-04-12 21:05:51 +02:00
Dennis 2d6f6f6848
GH-930 Repair docker image for PHP-FPM (#930) 2024-02-28 12:17:01 +01:00
Krzysztof Haller 36626f2d76 Fix dockerimage with docker-compose 2024-02-28 09:26:30 +01:00
Jens 9fab5451f5 Update app.php
Switch version to 0.9.6 and fix old name
2023-09-17 13:21:18 +02:00
Ferks-FK 79670657a4 Change the place where this is called. 2023-09-16 21:42:34 +02:00
Ferks-FK 441d89767d fix: 🐛 Fix credits exploit and infinite logs 2023-09-16 21:42:34 +02:00
Dennis 43ea446b2a
Update GetGithubVersion.php 2023-05-02 20:21:01 +02:00
IceToast 63692b557c Update app.php 2023-05-01 13:12:43 +02:00
Dennis 31b303b9fd
Revert "Merge branch 'development' into main" (#797) 2023-04-25 09:23:23 +02:00
1day2die 0c067e26c8 Revert "Merge branch 'development' into main"
This reverts commit 3810b487cd, reversing
changes made to d9a41840ce.
2023-04-25 09:22:30 +02:00
183 changed files with 5083 additions and 7530 deletions

View file

@ -1,5 +1,5 @@
### --- App Settings --- ###
APP_NAME=Controlpanel.gg
APP_NAME=Ctrlpanel.gg
APP_ENV=production
APP_KEY=
APP_DEBUG=false
@ -62,6 +62,3 @@ PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Settings Cache
SETTINGS_CACHE_ENABLED=true

View file

@ -31,9 +31,9 @@ body:
validations:
required: false
- type: textarea
id: controlpanel-logs
id: ctrlpanel-logs
attributes:
label: Controlpanel Logs
label: Ctrlpanel Logs
description: Please copy and paste your laravel-log output. You may also provide a link to it using the following command `tail -n 100 /var/www/controlpanel/storage/logs/laravel.log | nc pteropaste.com 99`
render: Shell
- type: textarea

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 ControlPanel.gg
Copyright (c) 2021 CtrlPanel.gg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -17,19 +17,19 @@
- Theme Support
- and so much more!
# ControlPanel-gg
# CtrlPanel-gg
![controlpanel](https://user-images.githubusercontent.com/67899387/214684708-739c1d21-06e8-4dec-a4f1-81533a46cc7e.png)
![ctrlpanel](https://user-images.githubusercontent.com/67899387/214684708-739c1d21-06e8-4dec-a4f1-81533a46cc7e.png)
![](https://img.shields.io/endpoint?label=v0.9%20Installations&url=https%3A%2F%2Fmarket.ctrlpanel.gg%2Fcallhome.php%3Fgetinstalls)
![](https://img.shields.io/badge/Overall%20Installations-5000%2B-green)
![](https://img.shields.io/github/stars/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/forks/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/tag/ControlPanel-gg/dashboard) [![Crowdin](https://badges.crowdin.net/controlpanelgg/localized.svg)](https://crowdin.com/project/controlpanelgg) ![](https://img.shields.io/github/issues/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/license/ControlPanel-gg/dashboard) ![](https://img.shields.io/discord/787829714483019826)
![](https://img.shields.io/github/stars/CtrlPanel-gg/panel) ![](https://img.shields.io/github/forks/CtrlPanel-gg/panel) ![](https://img.shields.io/github/tag/CtrlPanel-gg/panel) [![Crowdin](https://badges.crowdin.net/controlpanelgg/localized.svg)](https://crowdin.com/project/controlpanelgg) ![](https://img.shields.io/github/issues/CtrlPanel-gg/panel) ![](https://img.shields.io/github/license/CtrlPanel-gg/panel) ![](https://img.shields.io/discord/787829714483019826)
## About
ControlPanel's Dashboard is a dashboard application designed to offer clients a management tool to manage their pterodactyl servers. This dashboard comes with a credit-based billing solution that credits users hourly for each server they have and suspends them if they run out of credits.
CtrlPanel's Dashboard is a dashboard application designed to offer clients a management tool to manage their pterodactyl servers. This dashboard comes with a credit-based billing solution that credits users hourly for each server they have and suspends them if they run out of credits.
This dashboard offers an easy to use and free billing solution for all starting and experienced hosting providers. This dashboard has many customisation options and added discord Oauth verification to offer a solid link between your discord server and your dashboard. You can check our [Demo here](https://demo.controlpanel.gg "Demo").
This dashboard offers an easy to use and free billing solution for all starting and experienced hosting providers. This dashboard has many customisation options and added discord Oauth verification to offer a solid link between your discord server and your dashboard. You can check our [Demo here](https://demo.ctrlpanel.gg "Demo").
### [Installation](https://ctrlpanel.gg/docs/intro "Installation")

View file

@ -2,68 +2,46 @@
namespace App\Classes;
use App\Models\Pterodactyl\Egg;
use App\Models\Pterodactyl\Nest;
use App\Models\Pterodactyl\Node;
use App\Models\Egg;
use App\Models\Nest;
use App\Models\Node;
use App\Models\Product;
use App\Models\Server;
use App\Models\User;
use Exception;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use App\Settings\PterodactylSettings;
use App\Settings\ServerSettings;
class PterodactylClient
class Pterodactyl
{
//TODO: Extend error handling (maybe logger for more errors when debugging)
private int $per_page_limit = 200;
private int $allocation_limit = 200;
public PendingRequest $client;
public PendingRequest $application;
public function __construct(PterodactylSettings $ptero_settings)
{
$server_settings = new ServerSettings();
try {
$this->client = $this->client($ptero_settings);
$this->application = $this->clientAdmin($ptero_settings);
$this->per_page_limit = $ptero_settings->per_page_limit;
$this->allocation_limit = $server_settings->allocation_limit;
} catch (Exception $exception) {
logger('Failed to construct Pterodactyl client, Settings table not available?', ['exception' => $exception]);
}
}
/**
* @return PendingRequest
*/
public function client(PterodactylSettings $ptero_settings)
public static function client()
{
return Http::withHeaders([
'Authorization' => 'Bearer ' . $ptero_settings->user_token,
'Authorization' => 'Bearer ' . config('SETTINGS::SYSTEM:PTERODACTYL:TOKEN'),
'Content-type' => 'application/json',
'Accept' => 'Application/vnd.pterodactyl.v1+json',
])->baseUrl($ptero_settings->getUrl() . 'api' . '/');
])->baseUrl(config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/api');
}
public function clientAdmin(PterodactylSettings $ptero_settings)
public static function clientAdmin()
{
return Http::withHeaders([
'Authorization' => 'Bearer ' . $ptero_settings->admin_token,
'Authorization' => 'Bearer ' . config('SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN'),
'Content-type' => 'application/json',
'Accept' => 'Application/vnd.pterodactyl.v1+json',
])->baseUrl($ptero_settings->getUrl() . 'api' . '/');
])->baseUrl(config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/api');
}
/**
* @return Exception
*/
private function getException(string $message = '', int $status = 0): Exception
private static function getException(string $message = '', int $status = 0): Exception
{
if ($status == 404) {
return new Exception('Ressource does not exist on pterodactyl - ' . $message, 404);
@ -90,10 +68,10 @@ class PterodactylClient
*
* @throws Exception
*/
public function getEggs(Nest $nest)
public static function getEggs(Nest $nest)
{
try {
$response = $this->application->get("application/nests/{$nest->id}/eggs?include=nest,variables&per_page=" . $this->per_page_limit);
$response = self::client()->get("/application/nests/{$nest->id}/eggs?include=nest,variables&per_page=" . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -109,10 +87,10 @@ class PterodactylClient
*
* @throws Exception
*/
public function getNodes()
public static function getNodes()
{
try {
$response = $this->application->get('application/nodes?per_page=' . $this->per_page_limit);
$response = self::client()->get('/application/nodes?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -129,10 +107,10 @@ class PterodactylClient
* @throws Exception
* @description Returns the infos of a single node
*/
public function getNode($id)
public static function getNode($id)
{
try {
$response = $this->application->get('application/nodes/' . $id);
$response = self::client()->get('/application/nodes/' . $id);
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -143,10 +121,10 @@ class PterodactylClient
return $response->json()['attributes'];
}
public function getServers()
public static function getServers()
{
try {
$response = $this->application->get('application/servers?per_page=' . $this->per_page_limit);
$response = self::client()->get('/application/servers?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -162,10 +140,10 @@ class PterodactylClient
*
* @throws Exception
*/
public function getNests()
public static function getNests()
{
try {
$response = $this->application->get('application/nests?per_page=' . $this->per_page_limit);
$response = self::client()->get('/application/nests?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -181,10 +159,10 @@ class PterodactylClient
*
* @throws Exception
*/
public function getLocations()
public static function getLocations()
{
try {
$response = $this->application->get('application/locations?per_page=' . $this->per_page_limit);
$response = self::client()->get('/application/locations?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -201,7 +179,7 @@ class PterodactylClient
*
* @throws Exception
*/
public function getFreeAllocationId(Node $node)
public static function getFreeAllocationId(Node $node)
{
return self::getFreeAllocations($node)[0]['attributes']['id'] ?? null;
}
@ -212,7 +190,7 @@ class PterodactylClient
*
* @throws Exception
*/
public function getFreeAllocations(Node $node)
public static function getFreeAllocations(Node $node)
{
$response = self::getAllocations($node);
$freeAllocations = [];
@ -236,10 +214,11 @@ class PterodactylClient
*
* @throws Exception
*/
public function getAllocations(Node $node)
public static function getAllocations(Node $node)
{
$per_page = config('SETTINGS::SERVER:ALLOCATION_LIMIT', 200);
try {
$response = $this->application->get("application/nodes/{$node->id}/allocations?per_page={$this->allocation_limit}");
$response = self::client()->get("/application/nodes/{$node->id}/allocations?per_page={$per_page}");
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -250,15 +229,24 @@ class PterodactylClient
return $response->json();
}
/**
* @param string $route
* @return string
*/
public static function url(string $route): string
{
return config('SETTINGS::SYSTEM:PTERODACTYL:URL') . $route;
}
/**
* @param Server $server
* @param Egg $egg
* @param int $allocationId
* @return Response
*/
public function createServer(Server $server, Egg $egg, int $allocationId)
public static function createServer(Server $server, Egg $egg, int $allocationId)
{
return $this->application->post('application/servers', [
return self::client()->post('/application/servers', [
'name' => $server->name,
'external_id' => $server->id,
'user' => $server->user->pterodactyl_id,
@ -284,10 +272,10 @@ class PterodactylClient
]);
}
public function suspendServer(Server $server)
public static function suspendServer(Server $server)
{
try {
$response = $this->application->post("application/servers/$server->pterodactyl_id/suspend");
$response = self::client()->post("/application/servers/$server->pterodactyl_id/suspend");
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -298,10 +286,10 @@ class PterodactylClient
return $response;
}
public function unSuspendServer(Server $server)
public static function unSuspendServer(Server $server)
{
try {
$response = $this->application->post("application/servers/$server->pterodactyl_id/unsuspend");
$response = self::client()->post("/application/servers/$server->pterodactyl_id/unsuspend");
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -321,7 +309,7 @@ class PterodactylClient
public function getUser(int $pterodactylId)
{
try {
$response = $this->application->get("application/users/{$pterodactylId}");
$response = self::client()->get("/application/users/{$pterodactylId}");
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -338,10 +326,10 @@ class PterodactylClient
* @param int $pterodactylId
* @return mixed
*/
public function getServerAttributes(int $pterodactylId, bool $deleteOn404 = false)
public static function getServerAttributes(int $pterodactylId, bool $deleteOn404 = false)
{
try {
$response = $this->application->get("application/servers/{$pterodactylId}?include=egg,node,nest,location");
$response = self::client()->get("/application/servers/{$pterodactylId}?include=egg,node,nest,location");
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
@ -368,9 +356,9 @@ class PterodactylClient
* @param Product $product
* @return Response
*/
public function updateServer(Server $server, Product $product)
public static function updateServer(Server $server, Product $product)
{
return $this->application->patch("application/servers/{$server->pterodactyl_id}/build", [
return self::client()->patch("/application/servers/{$server->pterodactyl_id}/build", [
'allocation' => $server->allocation,
'memory' => $product->memory,
'swap' => $product->swap,
@ -393,9 +381,9 @@ class PterodactylClient
* @param Server $server
* @return mixed
*/
public function updateServerOwner(Server $server, int $userId)
public static function updateServerOwner(Server $server, int $userId)
{
return $this->application->patch("application/servers/{$server->pterodactyl_id}/details", [
return self::client()->patch("/application/servers/{$server->pterodactyl_id}/details", [
'name' => $server->name,
'user' => $userId,
]);
@ -408,9 +396,9 @@ class PterodactylClient
* @param string $action
* @return Response
*/
public function powerAction(Server $server, $action)
public static function powerAction(Server $server, $action)
{
return $this->client->post("client/servers/{$server->identifier}/power", [
return self::clientAdmin()->post("/client/servers/{$server->identifier}/power", [
'signal' => $action,
]);
}
@ -418,9 +406,9 @@ class PterodactylClient
/**
* Get info about user
*/
public function getClientUser()
public static function getClientUser()
{
return $this->client->get('client/account');
return self::clientAdmin()->get('/client/account');
}
/**
@ -431,10 +419,10 @@ class PterodactylClient
* @param int $requireDisk
* @return bool
*/
public function checkNodeResources(Node $node, int $requireMemory, int $requireDisk)
public static function checkNodeResources(Node $node, int $requireMemory, int $requireDisk)
{
try {
$response = $this->application->get("application/nodes/{$node->id}");
$response = self::client()->get("/application/nodes/{$node->id}");
} catch (Exception $e) {
throw self::getException($e->getMessage());
}

View file

@ -0,0 +1,47 @@
<?php
namespace App\Classes\Settings;
use App\Models\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class Invoices
{
public function __construct()
{
}
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!'));
}
}

View file

@ -0,0 +1,56 @@
<?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()
{
}
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!'));
}
}

View file

@ -0,0 +1,107 @@
<?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()
{
}
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',
'enable_referral' => 'nullable|string',
'referral_reward' => 'nullable|numeric',
'referral_allowed' => 'nullable|string',
'always_give_commission' => 'nullable|string',
'referral_percentage' => 'nullable|numeric',
'referral_mode' => 'nullable|string',
'ticket_enabled' => 'nullable|string',
'ticket_notify' => 'string',
]);
$validator->after(function ($validator) use ($request) {
// if enable-recaptcha is true then recaptcha-site-key and recaptcha-secret-key must be set
if ($request->get('enable-recaptcha') == 'true' && (! $request->get('recaptcha-site-key') || ! $request->get('recaptcha-secret-key'))) {
$validator->errors()->add('recaptcha-site-key', 'The site key is required if recaptcha is enabled.');
$validator->errors()->add('recaptcha-secret-key', 'The secret key is required if recaptcha is enabled.');
}
});
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',
'SETTINGS::REFERRAL::ENABLED' => 'enable_referral',
'SETTINGS::REFERRAL::REWARD' => 'referral_reward',
'SETTINGS::REFERRAL::ALLOWED' => 'referral_allowed',
'SETTINGS::REFERRAL:MODE' => 'referral_mode',
'SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION' => 'always_give_commission',
'SETTINGS::REFERRAL:PERCENTAGE' => 'referral_percentage',
'SETTINGS::TICKET:ENABLED' => 'ticket_enabled',
'SETTINGS::TICKET:NOTIFY' => 'ticket_notify',
];
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!'));
}
}

View file

@ -0,0 +1,58 @@
<?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()
{
}
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!'));
}
}

View file

@ -0,0 +1,154 @@
<?php
namespace App\Classes\Settings;
use App\Classes\Pterodactyl;
use App\Models\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Validator;
use Qirolab\Theme\Theme;
class System
{
public function __construct()
{
}
public function checkPteroClientkey()
{
$response = Pterodactyl::getClientUser();
if ($response->failed()) {
return redirect()->back()->with('error', __('Your Key or URL is not correct'));
}
return redirect()->back()->with('success', __('Everything is good!'));
}
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',
'server-limit-purchase' => 'required|min:0|integer',
'pterodactyl-api-key' => 'required|string',
'pterodactyl-url' => 'required|string',
'per-page-limit' => 'required|min:0|integer',
'pterodactyl-admin-api-key' => 'required|string',
'enable-upgrades' => 'string',
'enable-disable-servers' => 'string',
'enable-disable-new-users' => 'string',
'show-imprint' => 'string',
'show-privacy' => 'string',
'show-tos' => 'string',
'alert-enabled' => 'string',
'alter-type' => 'string',
'alert-message' => 'string|nullable',
'motd-enabled' => 'string',
'usefullinks-enabled' => 'string',
'motd-message' => 'string|nullable',
'seo-title' => 'string|nullable',
'seo-description' => 'string|nullable',
]);
$validator->after(function ($validator) use ($request) {
// if enable-recaptcha is true then recaptcha-site-key and recaptcha-secret-key must be set
if ($request->get('enable-upgrades') == 'true' && (! $request->get('pterodactyl-admin-api-key'))) {
$validator->errors()->add('pterodactyl-admin-api-key', 'The admin api key is required when upgrades are enabled.');
}
});
if ($validator->fails()) {
return redirect(route('admin.settings.index').'#system')->with('error', __('System settings have not been updated!'))->withErrors($validator)
->withInput();
}
// update Icons from request
$this->updateIcons($request);
$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:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER" => "minimum-credits",
"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::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE" => "server-limit-purchase",
"SETTINGS::MISC:PHPMYADMIN:URL" => "phpmyadmin-url",
"SETTINGS::SYSTEM:PTERODACTYL:URL" => "pterodactyl-url",
'SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT' => "per-page-limit",
"SETTINGS::SYSTEM:PTERODACTYL:TOKEN" => "pterodactyl-api-key",
"SETTINGS::SYSTEM:ENABLE_LOGIN_LOGO" => "enable-login-logo",
"SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN" => "pterodactyl-admin-api-key",
"SETTINGS::SYSTEM:ENABLE_UPGRADE" => "enable-upgrade",
"SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS" => "enable-disable-servers",
"SETTINGS::SYSTEM:CREATION_OF_NEW_USERS" => "enable-disable-new-users",
"SETTINGS::SYSTEM:SHOW_IMPRINT" => "show-imprint",
"SETTINGS::SYSTEM:SHOW_PRIVACY" => "show-privacy",
"SETTINGS::SYSTEM:SHOW_TOS" => "show-tos",
"SETTINGS::SYSTEM:ALERT_ENABLED" => "alert-enabled",
"SETTINGS::SYSTEM:ALERT_TYPE" => "alert-type",
"SETTINGS::SYSTEM:ALERT_MESSAGE" => "alert-message",
"SETTINGS::SYSTEM:THEME" => "theme",
"SETTINGS::SYSTEM:MOTD_ENABLED" => "motd-enabled",
"SETTINGS::SYSTEM:MOTD_MESSAGE" => "motd-message",
"SETTINGS::SYSTEM:USEFULLINKS_ENABLED" => "usefullinks-enabled",
"SETTINGS::SYSTEM:SEO_TITLE" => "seo-title",
"SETTINGS::SYSTEM:SEO_DESCRIPTION" => "seo-description",
];
foreach ($values as $key => $value) {
$param = $request->get($value);
Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
Cache::forget('setting'.':'.$key);
}
//SET THEME
$theme = $request->get('theme');
Theme::set($theme);
return redirect(route('admin.settings.index').'#system')->with('success', __('System settings updated!'));
}
private function updateIcons(Request $request)
{
$request->validate([
'icon' => 'nullable|max:10000|mimes:jpg,png,jpeg',
'logo' => 'nullable|max:10000|mimes:jpg,png,jpeg',
'favicon' => 'nullable|max:10000|mimes:ico',
]);
if ($request->hasFile('icon')) {
$request->file('icon')->storeAs('public', 'icon.png');
}
if ($request->hasFile('logo')) {
$request->file('logo')->storeAs('public', 'logo.png');
}
if ($request->hasFile('favicon')) {
$request->file('favicon')->storeAs('public', 'favicon.ico');
}
}
}

View file

@ -32,7 +32,7 @@ class GetGithubVersion extends Command
public function handle()
{
try{
$latestVersion = Http::get('https://api.github.com/repos/controlpanel-gg/dashboard/tags')->json()[0]['name'];
$latestVersion = Http::get('https://api.github.com/repos/ctrlpanel-gg/panel/tags')->json()[0]['name'];
Storage::disk('local')->put('latestVersion', $latestVersion);
} catch (Exception $e) {
Storage::disk('local')->put('latestVersion', "unknown");

View file

@ -2,20 +2,18 @@
namespace App\Console\Commands;
use App\Classes\PterodactylClient;
use App\Classes\Pterodactyl;
use App\Models\User;
use App\Settings\PterodactylSettings;
use App\Traits\Referral;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
class MakeUserCommand extends Command
{
use Referral;
private $pterodactyl;
/**
* The name and signature of the console command.
*
@ -30,14 +28,17 @@ class MakeUserCommand extends Command
*/
protected $description = 'Create an admin account with the Artisan Console';
private Pterodactyl $pterodactyl;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
public function __construct(Pterodactyl $pterodactyl)
{
parent::__construct();
$this->pterodactyl = $pterodactyl;
}
@ -46,9 +47,8 @@ class MakeUserCommand extends Command
*
* @return int
*/
public function handle(PterodactylSettings $ptero_settings)
public function handle()
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
$ptero_id = $this->option('ptero_id') ?? $this->ask('Please specify your Pterodactyl ID.');
$password = $this->secret('password') ?? $this->ask('Please specify your password.');

View file

@ -1,146 +0,0 @@
<?php
namespace App\Extensions\PaymentGateways\Mollie;
use App\Helpers\AbstractExtension;
use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent;
use App\Models\PartnerDiscount;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\User;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Http;
/**
* Summary of PayPalExtension
*/
class MollieExtension extends AbstractExtension
{
public static function getConfig(): array
{
return [
"name" => "Mollie",
"RoutesIgnoreCsrf" => [
"payment/MollieWebhook"
],
];
}
static function pay(Request $request): void
{
$url = 'https://api.mollie.com/v2/payments';
$settings = new MollieSettings();
$user = Auth::user();
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
$discount = PartnerDiscount::getDiscount();
// create a new payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => null,
'payment_method' => 'mollie',
'type' => $shopProduct->type,
'status' => 'open',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
'tax_value' => $shopProduct->getTaxValue(),
'tax_percent' => $shopProduct->getTaxPercent(),
'total_price' => $shopProduct->getTotalPrice(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
try {
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $settings->api_key,
])->post($url, [
'amount' => [
'currency' => $shopProduct->currency_code,
'value' => number_format($shopProduct->getTotalPrice(), 2, '.', ''),
],
'description' => "Order #{$payment->id} - " . $shopProduct->name,
'redirectUrl' => route('payment.MollieSuccess'),
'cancelUrl' => route('payment.Cancel'),
'webhookUrl' => url('/extensions/payment/MollieWebhook'),
'metadata' => [
'payment_id' => $payment->id,
],
]);
if ($response->status() != 201) {
Log::error('Mollie Payment: ' . $response->body());
$payment->delete();
Redirect::route('store.index')->with('error', __('Payment failed'))->send();
return;
}
$payment->update([
'payment_id' => $response->json()['id'],
]);
Redirect::away($response->json()['_links']['checkout']['href'])->send();
return;
} catch (Exception $ex) {
Log::error('Mollie Payment: ' . $ex->getMessage());
$payment->delete();
Redirect::route('store.index')->with('error', __('Payment failed'))->send();
return;
}
}
static function success(Request $request): void
{
$payment = Payment::findOrFail($request->input('payment'));
$payment->status = 'pending';
Redirect::route('home')->with('success', 'Your payment is being processed')->send();
return;
}
static function webhook(Request $request): JsonResponse
{
$url = 'https://api.mollie.com/v2/payments/' . $request->id;
$settings = new MollieSettings();
try {
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $settings->api_key,
])->get($url);
if ($response->status() != 200) {
Log::error('Mollie Payment Webhook: ' . $response->json()['title']);
return response()->json(['success' => false]);
}
$payment = Payment::findOrFail($response->json()['metadata']['payment_id']);
$payment->status->update([
'status' => $response->json()['status'],
]);
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
event(new PaymentEvent($payment, $payment, $shopProduct));
if ($response->json()['status'] == 'paid') {
$user = User::findOrFail($payment->user_id);
event(new UserUpdateCreditsEvent($user));
}
} catch (Exception $ex) {
Log::error('Mollie Payment Webhook: ' . $ex->getMessage());
return response()->json(['success' => false]);
}
// return a 200 status code
return response()->json(['success' => true]);
}
}

View file

@ -1,41 +0,0 @@
<?php
namespace App\Extensions\PaymentGateways\Mollie;
use Spatie\LaravelSettings\Settings;
class MollieSettings extends Settings
{
public bool $enabled = false;
public ?string $api_key;
public static function group(): string
{
return 'mollie';
}
public static function encrypted(): array
{
return [
'api_key',
];
}
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-dollar-sign',
'api_key' => [
'type' => 'string',
'label' => 'API Key',
'description' => 'The API Key of your Mollie App',
],
'enabled' => [
'type' => 'boolean',
'label' => 'Enabled',
'description' => 'Enable or disable this payment gateway',
],
];
}
}

View file

@ -1,18 +0,0 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
class CreateMollieSettings extends SettingsMigration
{
public function up(): void
{
$this->migrator->addEncrypted('mollie.api_key', null);
$this->migrator->add('mollie.enabled', false);
}
public function down(): void
{
$this->migrator->delete('mollie.api_key');
$this->migrator->delete('mollie.enabled');
}
}

View file

@ -1,22 +0,0 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Extensions\PaymentGateways\Mollie\MollieExtension;
Route::middleware(['web', 'auth'])->group(function () {
Route::get('payment/MolliePay/{shopProduct}', function () {
MollieExtension::pay(request());
})->name('payment.MolliePay');
Route::get(
'payment/MollieSuccess',
function () {
MollieExtension::success(request());
}
)->name('payment.MollieSuccess');
});
Route::post('payment/MollieWebhook', function () {
MollieExtension::webhook(request());
})->name('payment.MollieWebhook');

View file

@ -1,197 +0,0 @@
<?php
namespace App\Extensions\PaymentGateways\PayPal;
use App\Helpers\AbstractExtension;
use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent;
use App\Extensions\PaymentGateways\PayPal\PayPalSettings;
use App\Models\PartnerDiscount;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Log;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalHttp\HttpException;
/**
* Summary of PayPalExtension
*/
class PayPalExtension extends AbstractExtension
{
public static function getConfig(): array
{
return [
"name" => "PayPal",
"RoutesIgnoreCsrf" => [],
];
}
static function PaypalPay(Request $request): void
{
/** @var User $user */
$user = Auth::user();
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
$discount = PartnerDiscount::getDiscount();
// create a new payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => null,
'payment_method' => 'paypal',
'type' => $shopProduct->type,
'status' => 'open',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
'tax_value' => $shopProduct->getTaxValue(),
'tax_percent' => $shopProduct->getTaxPercent(),
'total_price' => $shopProduct->getTotalPrice(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
$request = new OrdersCreateRequest();
$request->prefer('return=representation');
$request->body = [
"intent" => "CAPTURE",
"purchase_units" => [
[
"reference_id" => uniqid(),
"description" => $shopProduct->display . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""),
"amount" => [
"value" => $shopProduct->getTotalPrice(),
'currency_code' => strtoupper($shopProduct->currency_code),
'breakdown' => [
'item_total' =>
[
'currency_code' => strtoupper($shopProduct->currency_code),
'value' => $shopProduct->getPriceAfterDiscount(),
],
'tax_total' =>
[
'currency_code' => strtoupper($shopProduct->currency_code),
'value' => $shopProduct->getTaxValue(),
]
]
]
]
],
"application_context" => [
"cancel_url" => route('payment.Cancel'),
"return_url" => route('payment.PayPalSuccess', ['payment' => $payment->id]),
'brand_name' => config('app.name', 'Controlpanel.GG'),
'shipping_preference' => 'NO_SHIPPING'
]
];
try {
// Call API with your client and get a response for your call
$response = self::getPayPalClient()->execute($request);
// check for any errors in the response
if ($response->statusCode != 201) {
throw new \Exception($response->statusCode);
}
// make sure the link is not empty
if (empty($response->result->links[1]->href)) {
throw new \Exception('No redirect link found');
}
Redirect::away($response->result->links[1]->href)->send();
return;
} catch (HttpException $ex) {
Log::error('PayPal Payment: ' . $ex->getMessage());
$payment->delete();
Redirect::route('store.index')->with('error', __('Payment failed'))->send();
return;
}
}
static function PaypalSuccess(Request $laravelRequest): void
{
$user = Auth::user();
$user = User::findOrFail($user->id);
$payment = Payment::findOrFail($laravelRequest->payment);
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
$request = new OrdersCaptureRequest($laravelRequest->input('token'));
$request->prefer('return=representation');
try {
// Call API with your client and get a response for your call
$response = self::getPayPalClient()->execute($request);
if ($response->statusCode == 201 || $response->statusCode == 200) {
//update payment
$payment->update([
'status' => 'paid',
'payment_id' => $response->result->id,
]);
event(new UserUpdateCreditsEvent($user));
event(new PaymentEvent($user, $payment, $shopProduct));
// redirect to the payment success page with success message
Redirect::route('home')->with('success', 'Payment successful')->send();
} elseif (env('APP_ENV') == 'local') {
// If call returns body in response, you can get the deserialized version from the result attribute of the response
$payment->delete();
dd($response);
} else {
$payment->update([
'status' => 'cancelled',
'payment_id' => $response->result->id,
]);
abort(500);
}
} catch (HttpException $ex) {
if (env('APP_ENV') == 'local') {
echo $ex->statusCode;
$payment->delete();
dd($ex->getMessage());
} else {
$payment->update([
'status' => 'cancelled',
'payment_id' => $response->result->id,
]);
abort(422);
}
}
}
static function getPayPalClient(): PayPalHttpClient
{
$environment = env('APP_ENV') == 'local'
? new SandboxEnvironment(self::getPaypalClientId(), self::getPaypalClientSecret())
: new ProductionEnvironment(self::getPaypalClientId(), self::getPaypalClientSecret());
return new PayPalHttpClient($environment);
}
/**
* @return string
*/
static function getPaypalClientId(): string
{
$settings = new PayPalSettings();
return env('APP_ENV') == 'local' ? $settings->sandbox_client_id : $settings->client_id;
}
/**
* @return string
*/
static function getPaypalClientSecret(): string
{
$settings = new PayPalSettings();
return env('APP_ENV') == 'local' ? $settings->sandbox_client_secret : $settings->client_secret;
}
}

View file

@ -1,67 +0,0 @@
<?php
namespace App\Extensions\PaymentGateways\PayPal;
use Spatie\LaravelSettings\Settings;
class PayPalSettings extends Settings
{
public bool $enabled = false;
public ?string $client_id;
public ?string $client_secret;
public ?string $sandbox_client_id;
public ?string $sandbox_client_secret;
public static function group(): string
{
return 'paypal';
}
public static function encrypted(): array
{
return [
'client_id',
'client_secret',
'sandbox_client_id',
'sandbox_client_secret'
];
}
/**
* Summary of optionInputData array
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-dollar-sign',
'client_id' => [
'type' => 'string',
'label' => 'Client ID',
'description' => 'The Client ID of your PayPal App',
],
'client_secret' => [
'type' => 'string',
'label' => 'Client Secret',
'description' => 'The Client Secret of your PayPal App',
],
'enabled' => [
'type' => 'boolean',
'label' => 'Enabled',
'description' => 'Enable this payment gateway',
],
'sandbox_client_id' => [
'type' => 'string',
'label' => 'Sandbox Client ID',
'description' => 'The Sandbox Client ID used when app_env = local',
],
'sandbox_client_secret' => [
'type' => 'string',
'label' => 'Sandbox Client Secret',
'description' => 'The Sandbox Client Secret used when app_env = local',
],
];
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Extensions\PaymentGateways\PayPal;
function getConfig()
{
return [
"name" => "PayPal",
"description" => "PayPal payment gateway",
"RoutesIgnoreCsrf" => [],
"enabled" => (config('SETTINGS::PAYMENTS:PAYPAL:SECRET') && config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID')) || (config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET') && config('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID') && env("APP_ENV") === "local"),
];
}

View file

@ -0,0 +1,186 @@
<?php
use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent;
use App\Models\PartnerDiscount;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Log;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalHttp\HttpException;
/**
* @param Request $request
* @param ShopProduct $shopProduct
*/
function PaypalPay(Request $request)
{
/** @var User $user */
$user = Auth::user();
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
$discount = PartnerDiscount::getDiscount();
// create a new payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => null,
'payment_method' => 'paypal',
'type' => $shopProduct->type,
'status' => 'open',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
'tax_value' => $shopProduct->getTaxValue(),
'tax_percent' => $shopProduct->getTaxPercent(),
'total_price' => $shopProduct->getTotalPrice(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
$request = new OrdersCreateRequest();
$request->prefer('return=representation');
$request->body = [
"intent" => "CAPTURE",
"purchase_units" => [
[
"reference_id" => uniqid(),
"description" => $shopProduct->display . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""),
"amount" => [
"value" => $shopProduct->getTotalPrice(),
'currency_code' => strtoupper($shopProduct->currency_code),
'breakdown' => [
'item_total' =>
[
'currency_code' => strtoupper($shopProduct->currency_code),
'value' => $shopProduct->getPriceAfterDiscount(),
],
'tax_total' =>
[
'currency_code' => strtoupper($shopProduct->currency_code),
'value' => $shopProduct->getTaxValue(),
]
]
]
]
],
"application_context" => [
"cancel_url" => route('payment.Cancel'),
"return_url" => route('payment.PayPalSuccess', ['payment' => $payment->id]),
'brand_name' => config('app.name', 'CtrlPanel.gg'),
'shipping_preference' => 'NO_SHIPPING'
]
];
try {
// Call API with your client and get a response for your call
$response = getPayPalClient()->execute($request);
// check for any errors in the response
if ($response->statusCode != 201) {
throw new \Exception($response->statusCode);
}
// make sure the link is not empty
if (empty($response->result->links[1]->href)) {
throw new \Exception('No redirect link found');
}
Redirect::away($response->result->links[1]->href)->send();
return;
} catch (HttpException $ex) {
Log::error('PayPal Payment: ' . $ex->getMessage());
$payment->delete();
Redirect::route('store.index')->with('error', __('Payment failed'))->send();
return;
}
}
/**
* @param Request $laravelRequest
*/
function PaypalSuccess(Request $laravelRequest)
{
$user = Auth::user();
$user = User::findOrFail($user->id);
$payment = Payment::findOrFail($laravelRequest->payment);
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
$request = new OrdersCaptureRequest($laravelRequest->input('token'));
$request->prefer('return=representation');
try {
// Call API with your client and get a response for your call
$response = getPayPalClient()->execute($request);
if ($response->statusCode == 201 || $response->statusCode == 200) {
//update payment
$payment->update([
'status' => 'paid',
'payment_id' => $response->result->id,
]);
event(new UserUpdateCreditsEvent($user));
event(new PaymentEvent($user, $payment, $shopProduct));
// redirect to the payment success page with success message
Redirect::route('home')->with('success', 'Payment successful')->send();
} elseif (env('APP_ENV') == 'local') {
// If call returns body in response, you can get the deserialized version from the result attribute of the response
$payment->delete();
dd($response);
} else {
$payment->update([
'status' => 'cancelled',
'payment_id' => $response->result->id,
]);
abort(500);
}
} catch (HttpException $ex) {
if (env('APP_ENV') == 'local') {
echo $ex->statusCode;
$payment->delete();
dd($ex->getMessage());
} else {
$payment->update([
'status' => 'cancelled',
'payment_id' => $response->result->id,
]);
abort(422);
}
}
}
/**
* @return PayPalHttpClient
*/
function getPayPalClient()
{
$environment = env('APP_ENV') == 'local'
? new SandboxEnvironment(getPaypalClientId(), getPaypalClientSecret())
: new ProductionEnvironment(getPaypalClientId(), getPaypalClientSecret());
return new PayPalHttpClient($environment);
}
/**
* @return string
*/
function getPaypalClientId()
{
return env('APP_ENV') == 'local' ? config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID") : config("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID");
}
/**
* @return string
*/
function getPaypalClientSecret()
{
return env('APP_ENV') == 'local' ? config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET") : config("SETTINGS::PAYMENTS:PAYPAL:SECRET");
}

View file

@ -1,100 +0,0 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use Illuminate\Support\Facades\DB;
class CreatePayPalSettings extends SettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
$this->migrator->addEncrypted('paypal.client_id', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID') : null);
$this->migrator->addEncrypted('paypal.client_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:SECRET') : null);
$this->migrator->addEncrypted('paypal.sandbox_client_id', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID') : null);
$this->migrator->addEncrypted('paypal.sandbox_client_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET') : null);
$this->migrator->add('paypal.enabled', false);
}
public function down(): void
{
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID',
'value' => $this->getNewValue('client_id'),
'type' => 'string',
'description' => 'The Client ID of your PayPal App'
],
[
'key' => 'SETTINGS::PAYMENTS:PAYPAL:SECRET',
'value' => $this->getNewValue('client_secret'),
'type' => 'string',
'description' => 'The Client Secret of your PayPal App'
],
[
'key' => 'SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID',
'value' => $this->getNewValue('sandbox_client_id'),
'type' => 'string',
'description' => 'The Sandbox Client ID of your PayPal App'
],
[
'key' => 'SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET',
'value' => $this->getNewValue('sandbox_client_secret'),
'type' => 'string',
'description' => 'The Sandbox Client Secret of your PayPal App'
]
]);
$this->migrator->delete('paypal.client_id');
$this->migrator->delete('paypal.client_secret');
$this->migrator->delete('paypal.enabled');
$this->migrator->delete('paypal.sandbox_client_id');
$this->migrator->delete('paypal.sandbox_client_secret');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'paypal'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
}
// remove the quotes from the string
if (substr($new_value->payload, 0, 1) === '"' && substr($new_value->payload, -1) === '"') {
return substr($new_value->payload, 1, -1);
}
return $new_value->payload;
}
public function getOldValue(string $key)
{
// Always get the first value of the key.
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
// Handle the old values to return without it being a string in all cases.
if ($old_value->type === "string" || $old_value->type === "text") {
if (is_null($old_value->value)) {
return '';
}
// Some values have the type string, but their values are boolean.
if ($old_value->value === "false" || $old_value->value === "true") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return $old_value->value;
}
if ($old_value->type === "boolean") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return filter_var($old_value->value, FILTER_VALIDATE_INT);
}
}

View file

@ -1,17 +1,18 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Extensions\PaymentGateways\PayPal\PayPalExtension;
include_once(__DIR__ . '/index.php');
Route::middleware(['web', 'auth'])->group(function () {
Route::get('payment/PayPalPay/{shopProduct}', function () {
PayPalExtension::PaypalPay(request());
PaypalPay(request());
})->name('payment.PayPalPay');
Route::get(
'payment/PayPalSuccess',
function () {
PayPalExtension::PaypalSuccess(request());
PaypalSuccess(request());
}
)->name('payment.PayPalSuccess');
});

View file

@ -1,390 +0,0 @@
<?php
namespace App\Extensions\PaymentGateways\Stripe;
use App\Helpers\AbstractExtension;
use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent;
use App\Extensions\PaymentGateways\Stripe\StripeSettings;
use App\Models\PartnerDiscount;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\User;
use App\Notifications\ConfirmPaymentNotification;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Stripe\Exception\SignatureVerificationException;
use Stripe\Stripe;
use Stripe\StripeClient;
class StripeExtension extends AbstractExtension
{
public static function getConfig(): array
{
return [
"name" => "Stripe",
"RoutesIgnoreCsrf" => [
"payment/StripeWebhooks",
],
];
}
/**
* @param Request $request
* @param ShopProduct $shopProduct
*/
public static function StripePay(Request $request)
{
$user = Auth::user();
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
// check if the price is valid for stripe
if (!self::checkPriceAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), 'stripe')) {
Redirect::route('home')->with('error', __('The product you chose can\'t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'))->send();
return;
}
$discount = PartnerDiscount::getDiscount();
// create payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => null,
'payment_method' => 'stripe',
'type' => $shopProduct->type,
'status' => 'open',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
'tax_value' => $shopProduct->getTaxValue(),
'total_price' => $shopProduct->getTotalPrice(),
'tax_percent' => $shopProduct->getTaxPercent(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
$stripeClient = self::getStripeClient();
$request = $stripeClient->checkout->sessions->create([
'line_items' => [
[
'price_data' => [
'currency' => $shopProduct->currency_code,
'product_data' => [
'name' => $shopProduct->display . ($discount ? (' (' . __('Discount') . ' ' . $discount . '%)') : ''),
'description' => $shopProduct->description,
],
'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2),
],
'quantity' => 1,
],
[
'price_data' => [
'currency' => $shopProduct->currency_code,
'product_data' => [
'name' => __('Tax'),
'description' => $shopProduct->getTaxPercent() . '%',
],
'unit_amount_decimal' => round($shopProduct->getTaxValue(), 2) * 100,
],
'quantity' => 1,
],
],
'mode' => 'payment',
'success_url' => route('payment.StripeSuccess', ['payment' => $payment->id]) . '&session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('payment.Cancel'),
'payment_intent_data' => [
'metadata' => [
'payment_id' => $payment->id,
],
],
]);
Redirect::to($request->url)->send();
}
/**
* @param Request $request
*/
public static function StripeSuccess(Request $request)
{
$user = Auth::user();
$user = User::findOrFail($user->id);
$payment = Payment::findOrFail($request->input('payment'));
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
Redirect::route('home')->with('success', 'Please wait for success')->send();
$stripeClient = self::getStripeClient();
try {
//get stripe data
$paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id'));
$paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent);
//get DB entry of this payment ID if existing
$paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count();
// check if payment is 100% completed and payment does not exist in db already
if ($paymentSession->status == 'complete' && $paymentIntent->status == 'succeeded' && $paymentDbEntry == 0) {
//update payment
$payment->update([
'payment_id' => $paymentSession->payment_intent,
'status' => 'paid',
]);
//payment notification
$user->notify(new ConfirmPaymentNotification($payment));
event(new UserUpdateCreditsEvent($user));
event(new PaymentEvent($user, $payment, $shopProduct));
//redirect back to home
Redirect::route('home')->with('success', 'Payment successful')->send();
} else {
if ($paymentIntent->status == 'processing') {
//update payment
$payment->update([
'payment_id' => $paymentSession->payment_intent,
'status' => 'processing',
]);
event(new PaymentEvent($user, $payment, $shopProduct));
Redirect::route('home')->with('success', 'Your payment is being processed')->send();
}
if ($paymentDbEntry == 0 && $paymentIntent->status != 'processing') {
$stripeClient->paymentIntents->cancel($paymentIntent->id);
//redirect back to home
Redirect::route('home')->with('info', __('Your payment has been canceled!'))->send();
} else {
abort(402);
}
}
} catch (Exception $e) {
if (env('APP_ENV') == 'local') {
dd($e->getMessage());
} else {
abort(422);
}
}
}
/**
* @param Request $request
*/
public static function handleStripePaymentSuccessHook($paymentIntent)
{
try {
$payment = Payment::where('id', $paymentIntent->metadata->payment_id)->with('user')->first();
$user = User::where('id', $payment->user_id)->first();
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
if ($paymentIntent->status == 'succeeded' && $payment->status == 'processing') {
//update payment db entry status
$payment->update([
'payment_id' => $payment->payment_id ?? $paymentIntent->id,
'status' => 'paid'
]);
//payment notification
$user->notify(new ConfirmPaymentNotification($payment));
event(new UserUpdateCreditsEvent($user));
event(new PaymentEvent($user, $payment, $shopProduct));
}
// return 200
return response()->json(['success' => true], 200);
} catch (Exception $ex) {
abort(422);
}
}
/**
* @param Request $request
*/
public static function StripeWebhooks(Request $request)
{
Stripe::setApiKey(self::getStripeSecret());
try {
$payload = @file_get_contents('php://input');
$sig_header = $request->header('Stripe-Signature');
$event = null;
$event = \Stripe\Webhook::constructEvent(
$payload,
$sig_header,
self::getStripeEndpointSecret()
);
} catch (\UnexpectedValueException $e) {
// Invalid payload
abort(400);
} catch (SignatureVerificationException $e) {
// Invalid signature
abort(400);
}
// Handle the event
switch ($event->type) {
case 'payment_intent.succeeded':
$paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
self::handleStripePaymentSuccessHook($paymentIntent);
break;
default:
echo 'Received unknown event type ' . $event->type;
}
}
/**
* @return \Stripe\StripeClient
*/
public static function getStripeClient()
{
return new StripeClient(self::getStripeSecret());
}
/**
* @return string
*/
public static function getStripeSecret()
{
$settings = new StripeSettings();
return env('APP_ENV') == 'local'
? $settings->test_secret_key
: $settings->secret_key;
}
/**
* @return string
*/
public static function getStripeEndpointSecret()
{
$settings = new StripeSettings();
return env('APP_ENV') == 'local'
? $settings->test_endpoint_secret
: $settings->endpoint_secret;
}
/**
* @param $amount
* @param $currencyCode
* @param $payment_method
* @return bool
* @description check if the amount is higher than the minimum amount for the stripe gateway
*/
public static function checkPriceAmount($amount, $currencyCode, $payment_method)
{
$minimums = [
"USD" => [
"paypal" => 0,
"stripe" => 0.5
],
"AED" => [
"paypal" => 0,
"stripe" => 2
],
"AUD" => [
"paypal" => 0,
"stripe" => 0.5
],
"BGN" => [
"paypal" => 0,
"stripe" => 1
],
"BRL" => [
"paypal" => 0,
"stripe" => 0.5
],
"CAD" => [
"paypal" => 0,
"stripe" => 0.5
],
"CHF" => [
"paypal" => 0,
"stripe" => 0.5
],
"CZK" => [
"paypal" => 0,
"stripe" => 15
],
"DKK" => [
"paypal" => 0,
"stripe" => 2.5
],
"EUR" => [
"paypal" => 0,
"stripe" => 0.5
],
"GBP" => [
"paypal" => 0,
"stripe" => 0.3
],
"HKD" => [
"paypal" => 0,
"stripe" => 4
],
"HRK" => [
"paypal" => 0,
"stripe" => 0.5
],
"HUF" => [
"paypal" => 0,
"stripe" => 175
],
"INR" => [
"paypal" => 0,
"stripe" => 0.5
],
"JPY" => [
"paypal" => 0,
"stripe" => 0.5
],
"MXN" => [
"paypal" => 0,
"stripe" => 10
],
"MYR" => [
"paypal" => 0,
"stripe" => 2
],
"NOK" => [
"paypal" => 0,
"stripe" => 3
],
"NZD" => [
"paypal" => 0,
"stripe" => 0.5
],
"PLN" => [
"paypal" => 0,
"stripe" => 2
],
"RON" => [
"paypal" => 0,
"stripe" => 2
],
"SEK" => [
"paypal" => 0,
"stripe" => 3
],
"SGD" => [
"paypal" => 0,
"stripe" => 0.5
],
"THB" => [
"paypal" => 0,
"stripe" => 10
]
];
return $amount >= $minimums[$currencyCode][$payment_method];
}
}

View file

@ -1,63 +0,0 @@
<?php
namespace App\Extensions\PaymentGateways\Stripe;
use Spatie\LaravelSettings\Settings;
class StripeSettings extends Settings
{
public bool $enabled = false;
public ?string $secret_key;
public ?string $endpoint_secret;
public ?string $test_secret_key;
public ?string $test_endpoint_secret;
public static function group(): string
{
return 'stripe';
}
public static function encrypted(): array
{
return [
"secret_key",
"endpoint_secret",
"test_secret_key",
"test_endpoint_secret"
];
}
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-dollar-sign',
'secret_key' => [
'type' => 'string',
'label' => 'Secret Key',
'description' => 'The Secret Key of your Stripe App',
],
'endpoint_secret' => [
'type' => 'string',
'label' => 'Endpoint Secret',
'description' => 'The Endpoint Secret of your Stripe App',
],
'test_secret_key' => [
'type' => 'string',
'label' => 'Test Secret Key',
'description' => 'The Test Secret Key used when app_env = local',
],
'test_endpoint_secret' => [
'type' => 'string',
'label' => 'Test Endpoint Secret',
'description' => 'The Test Endpoint Secret used when app_env = local',
],
'enabled' => [
'type' => 'boolean',
'label' => 'Enabled',
'description' => 'Enable this payment gateway',
]
];
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace App\Extensions\PaymentGateways\Stripe;
function getConfig()
{
return [
"name" => "Stripe",
"description" => "Stripe payment gateway",
"RoutesIgnoreCsrf" => [
"payment/StripeWebhooks",
],
"enabled" => config('SETTINGS::PAYMENTS:STRIPE:SECRET') && config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') || config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET') && config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') && env("APP_ENV") === "local",
];
}

View file

@ -0,0 +1,373 @@
<?php
use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent;
use App\Models\PartnerDiscount;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\User;
use App\Notifications\ConfirmPaymentNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Stripe\Exception\SignatureVerificationException;
use Stripe\Stripe;
use Stripe\StripeClient;
/**
* @param Request $request
* @param ShopProduct $shopProduct
*/
function StripePay(Request $request)
{
$user = Auth::user();
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
// check if the price is valid for stripe
if (!checkPriceAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), 'stripe')) {
Redirect::route('home')->with('error', __('The product you chose can\'t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'))->send();
return;
}
$discount = PartnerDiscount::getDiscount();
// create payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => null,
'payment_method' => 'stripe',
'type' => $shopProduct->type,
'status' => 'open',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
'tax_value' => $shopProduct->getTaxValue(),
'total_price' => $shopProduct->getTotalPrice(),
'tax_percent' => $shopProduct->getTaxPercent(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
$stripeClient = getStripeClient();
$request = $stripeClient->checkout->sessions->create([
'line_items' => [
[
'price_data' => [
'currency' => $shopProduct->currency_code,
'product_data' => [
'name' => $shopProduct->display . ($discount ? (' (' . __('Discount') . ' ' . $discount . '%)') : ''),
'description' => $shopProduct->description,
],
'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2),
],
'quantity' => 1,
],
[
'price_data' => [
'currency' => $shopProduct->currency_code,
'product_data' => [
'name' => __('Tax'),
'description' => $shopProduct->getTaxPercent() . '%',
],
'unit_amount_decimal' => round($shopProduct->getTaxValue(), 2) * 100,
],
'quantity' => 1,
],
],
'mode' => 'payment',
'payment_method_types' => str_getcsv(config('SETTINGS::PAYMENTS:STRIPE:METHODS')),
'success_url' => route('payment.StripeSuccess', ['payment' => $payment->id]) . '&session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('payment.Cancel'),
'payment_intent_data' => [
'metadata' => [
'payment_id' => $payment->id,
],
],
]);
Redirect::to($request->url)->send();
}
/**
* @param Request $request
*/
function StripeSuccess(Request $request)
{
$user = Auth::user();
$user = User::findOrFail($user->id);
$payment = Payment::findOrFail($request->input('payment'));
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
Redirect::route('home')->with('success', 'Please wait for success')->send();
$stripeClient = getStripeClient();
try {
//get stripe data
$paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id'));
$paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent);
//get DB entry of this payment ID if existing
$paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count();
// check if payment is 100% completed and payment does not exist in db already
if ($paymentSession->status == 'complete' && $paymentIntent->status == 'succeeded' && $paymentDbEntry == 0) {
//update payment
$payment->update([
'payment_id' => $paymentSession->payment_intent,
'status' => 'paid',
]);
//payment notification
$user->notify(new ConfirmPaymentNotification($payment));
event(new UserUpdateCreditsEvent($user));
event(new PaymentEvent($user, $payment, $shopProduct));
//redirect back to home
Redirect::route('home')->with('success', 'Payment successful')->send();
} else {
if ($paymentIntent->status == 'processing') {
//update payment
$payment->update([
'payment_id' => $paymentSession->payment_intent,
'status' => 'processing',
]);
event(new PaymentEvent($user, $payment, $shopProduct));
Redirect::route('home')->with('success', 'Your payment is being processed')->send();
}
if ($paymentDbEntry == 0 && $paymentIntent->status != 'processing') {
$stripeClient->paymentIntents->cancel($paymentIntent->id);
//redirect back to home
Redirect::route('home')->with('info', __('Your payment has been canceled!'))->send();
} else {
abort(402);
}
}
} catch (Exception $e) {
if (env('APP_ENV') == 'local') {
dd($e->getMessage());
} else {
abort(422);
}
}
}
/**
* @param Request $request
*/
function handleStripePaymentSuccessHook($paymentIntent)
{
try {
$payment = Payment::where('id', $paymentIntent->metadata->payment_id)->with('user')->first();
$user = User::where('id', $payment->user_id)->first();
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
if ($paymentIntent->status == 'succeeded' && $payment->status == 'processing') {
//update payment db entry status
$payment->update([
'payment_id' => $payment->payment_id ?? $paymentIntent->id,
'status' => 'paid'
]);
//payment notification
$user->notify(new ConfirmPaymentNotification($payment));
event(new UserUpdateCreditsEvent($user));
event(new PaymentEvent($user, $payment, $shopProduct));
}
// return 200
return response()->json(['success' => true], 200);
} catch (Exception $ex) {
abort(422);
}
}
/**
* @param Request $request
*/
function StripeWebhooks(Request $request)
{
Stripe::setApiKey(getStripeSecret());
try {
$payload = @file_get_contents('php://input');
$sig_header = $request->header('Stripe-Signature');
$event = null;
$event = \Stripe\Webhook::constructEvent(
$payload,
$sig_header,
getStripeEndpointSecret()
);
} catch (\UnexpectedValueException $e) {
// Invalid payload
abort(400);
} catch (SignatureVerificationException $e) {
// Invalid signature
abort(400);
}
// Handle the event
switch ($event->type) {
case 'payment_intent.succeeded':
$paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
handleStripePaymentSuccessHook($paymentIntent);
break;
default:
echo 'Received unknown event type ' . $event->type;
}
}
/**
* @return \Stripe\StripeClient
*/
function getStripeClient()
{
return new StripeClient(getStripeSecret());
}
/**
* @return string
*/
function getStripeSecret()
{
return env('APP_ENV') == 'local'
? config('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET')
: config('SETTINGS::PAYMENTS:STRIPE:SECRET');
}
/**
* @return string
*/
function getStripeEndpointSecret()
{
return env('APP_ENV') == 'local'
? config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET')
: config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET');
}
/**
* @param $amount
* @param $currencyCode
* @param $payment_method
* @return bool
* @description check if the amount is higher than the minimum amount for the stripe gateway
*/
function checkPriceAmount($amount, $currencyCode, $payment_method)
{
$minimums = [
"USD" => [
"paypal" => 0,
"stripe" => 0.5
],
"AED" => [
"paypal" => 0,
"stripe" => 2
],
"AUD" => [
"paypal" => 0,
"stripe" => 0.5
],
"BGN" => [
"paypal" => 0,
"stripe" => 1
],
"BRL" => [
"paypal" => 0,
"stripe" => 0.5
],
"CAD" => [
"paypal" => 0,
"stripe" => 0.5
],
"CHF" => [
"paypal" => 0,
"stripe" => 0.5
],
"CZK" => [
"paypal" => 0,
"stripe" => 15
],
"DKK" => [
"paypal" => 0,
"stripe" => 2.5
],
"EUR" => [
"paypal" => 0,
"stripe" => 0.5
],
"GBP" => [
"paypal" => 0,
"stripe" => 0.3
],
"HKD" => [
"paypal" => 0,
"stripe" => 4
],
"HRK" => [
"paypal" => 0,
"stripe" => 0.5
],
"HUF" => [
"paypal" => 0,
"stripe" => 175
],
"INR" => [
"paypal" => 0,
"stripe" => 0.5
],
"JPY" => [
"paypal" => 0,
"stripe" => 0.5
],
"MXN" => [
"paypal" => 0,
"stripe" => 10
],
"MYR" => [
"paypal" => 0,
"stripe" => 2
],
"NOK" => [
"paypal" => 0,
"stripe" => 3
],
"NZD" => [
"paypal" => 0,
"stripe" => 0.5
],
"PLN" => [
"paypal" => 0,
"stripe" => 2
],
"RON" => [
"paypal" => 0,
"stripe" => 2
],
"SEK" => [
"paypal" => 0,
"stripe" => 3
],
"SGD" => [
"paypal" => 0,
"stripe" => 0.5
],
"THB" => [
"paypal" => 0,
"stripe" => 10
]
];
return $amount >= $minimums[$currencyCode][$payment_method];
}

View file

@ -1,98 +0,0 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateStripeSettings extends SettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
$this->migrator->addEncrypted('stripe.secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:SECRET') : null);
$this->migrator->addEncrypted('stripe.endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') : null);
$this->migrator->addEncrypted('stripe.test_secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') : null);
$this->migrator->addEncrypted('stripe.test_endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET') : null);
$this->migrator->add('stripe.enabled', false);
}
public function down(): void
{
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::PAYMENTS:STRIPE:SECRET',
'value' => $this->getNewValue('secret_key'),
'type' => 'string',
'description' => 'The Secret Key of your Stripe App'
],
[
'key' => 'SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET',
'value' => $this->getNewValue('endpoint_secret'),
'type' => 'string',
'description' => 'The Endpoint Secret of your Stripe App'
],
[
'key' => 'SETTINGS::PAYMENTS:STRIPE:TEST_SECRET',
'value' => $this->getNewValue('test_secret_key'),
'type' => 'string',
'description' => 'The Test Secret Key of your Stripe App'
],
[
'key' => 'SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET',
'value' => $this->getNewValue('test_endpoint_secret'),
'type' => 'string',
'description' => 'The Test Endpoint Secret of your Stripe App'
]
]);
$this->migrator->delete('stripe.secret_key');
$this->migrator->delete('stripe.endpoint_secret');
$this->migrator->delete('stripe.enabled');
$this->migrator->delete('stripe.test_secret_key');
$this->migrator->delete('stripe.test_endpoint_secret');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'stripe'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
}
// remove the quotes from the string
if (substr($new_value->payload, 0, 1) === '"' && substr($new_value->payload, -1) === '"') {
return substr($new_value->payload, 1, -1);
}
return $new_value->payload;
}
public function getOldValue(string $key)
{
// Always get the first value of the key.
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
// Handle the old values to return without it being a string in all cases.
if ($old_value->type === "string" || $old_value->type === "text") {
if (is_null($old_value->value)) {
return '';
}
// Some values have the type string, but their values are boolean.
if ($old_value->value === "false" || $old_value->value === "true") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return $old_value->value;
}
if ($old_value->type === "boolean") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return filter_var($old_value->value, FILTER_VALIDATE_INT);
}
}

View file

@ -1,17 +1,17 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Extensions\PaymentGateways\Stripe\StripeExtension;
include_once(__DIR__ . '/index.php');
Route::middleware(['web', 'auth'])->group(function () {
Route::get('payment/StripePay/{shopProduct}', function () {
StripeExtension::StripePay(request());
StripePay(request());
})->name('payment.StripePay');
Route::get(
'payment/StripeSuccess',
function () {
StripeExtension::StripeSuccess(request());
StripeSuccess(request());
}
)->name('payment.StripeSuccess');
});
@ -19,5 +19,5 @@ Route::middleware(['web', 'auth'])->group(function () {
// Stripe WebhookRoute -> validation in Route Handler
Route::post('payment/StripeWebhooks', function () {
StripeExtension::StripeWebhooks(request());
StripeWebhooks(request());
})->name('payment.StripeWebhooks');

View file

@ -1,9 +0,0 @@
<?php
namespace App\Helpers;
// create a abstract class for the extension that will contain all the methods that will be used in the extension
abstract class AbstractExtension
{
abstract public static function getConfig(): array;
}

View file

@ -2,14 +2,65 @@
namespace App\Helpers;
/**
* Summary of ExtensionHelper
*/
class ExtensionHelper
{
/**
* Get a config of an extension by its name
* @param string $extensionName
* @param string $configname
*/
public static function getExtensionConfig(string $extensionName, string $configname)
{
$extensions = ExtensionHelper::getAllExtensions();
// call the getConfig function of the config file of the extension like that
// call_user_func("App\\Extensions\\PaymentGateways\\Stripe" . "\\getConfig");
foreach ($extensions as $extension) {
if (!(basename($extension) == $extensionName)) {
continue;
}
$configFile = $extension . '/config.php';
if (file_exists($configFile)) {
include_once $configFile;
$config = call_user_func('App\\Extensions\\' . basename(dirname($extension)) . '\\' . basename($extension) . "\\getConfig");
}
if (isset($config[$configname])) {
return $config[$configname];
}
}
return null;
}
public static function getAllCsrfIgnoredRoutes()
{
$extensions = ExtensionHelper::getAllExtensions();
$routes = [];
foreach ($extensions as $extension) {
$configFile = $extension . '/config.php';
if (file_exists($configFile)) {
include_once $configFile;
$config = call_user_func('App\\Extensions\\' . basename(dirname($extension)) . '\\' . basename($extension) . "\\getConfig");
}
if (isset($config['RoutesIgnoreCsrf'])) {
$routes = array_merge($routes, $config['RoutesIgnoreCsrf']);
}
// map over the routes and add the extension name as prefix
$result = array_map(fn ($item) => "extensions/{$item}", $routes);
}
return $result;
}
/**
* Get all extensions
* @return array array of all extensions e.g. ["App\Extensions\PayPal", "App\Extensions\Stripe"]
* @return array
*/
public static function getAllExtensions()
{
@ -18,195 +69,14 @@ class ExtensionHelper
foreach ($extensionNamespaces as $extensionNamespace) {
$extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
}
// remove base path from every extension but keep app/Extensions/...
$extensions = array_map(fn ($item) => str_replace('/', '\\', str_replace(app_path() . '/', 'App/', $item)), $extensions);
return $extensions;
}
/**
* Get all extensions by namespace
* @param string $namespace case sensitive namespace of the extension e.g. PaymentGateways
* @return array array of all extensions e.g. ["App\Extensions\PayPal", "App\Extensions\Stripe"]
*/
public static function getAllExtensionsByNamespace(string $namespace)
{
$extensions = glob(app_path() . '/Extensions/' . $namespace . '/*', GLOB_ONLYDIR);
// remove base path from every extension but keep app/Extensions/...
$extensions = array_map(fn ($item) => str_replace('/', '\\', str_replace(app_path() . '/', 'App/', $item)), $extensions);
return $extensions;
}
/**
* Get an extension by its name
* @param string $extensionName case sensitive name of the extension e.g. PayPal
* @return string|null the path of the extension e.g. App\Extensions\PayPal
*/
public static function getExtension(string $extensionName)
{
$extensions = self::getAllExtensions();
// filter the extensions by the extension name
$extensions = array_filter($extensions, fn ($item) => basename($item) == $extensionName);
// return the only extension
return array_shift($extensions);
}
/**
* Get all extension classes
* @return array array of all extension classes e.g. ["App\Extensions\PayPal\PayPalExtension", "App\Extensions\Stripe\StripeExtension"]
*/
public static function getAllExtensionClasses()
{
$extensions = self::getAllExtensions();
// add the ExtensionClass to the end of the namespace
$extensions = array_map(fn ($item) => $item . '\\' . basename($item) . 'Extension', $extensions);
// filter out non existing extension classes
$extensions = array_filter($extensions, fn ($item) => class_exists($item));
return $extensions;
}
/**
* Get all extension classes by namespace
* @param string $namespace case sensitive namespace of the extension e.g. PaymentGateways
* @return array array of all extension classes e.g. ["App\Extensions\PayPal\PayPalExtension", "App\Extensions\Stripe\StripeExtension"]
*/
public static function getAllExtensionClassesByNamespace(string $namespace)
{
$extensions = self::getAllExtensionsByNamespace($namespace);
// add the ExtensionClass to the end of the namespace
$extensions = array_map(fn ($item) => $item . '\\' . basename($item) . 'Extension', $extensions);
// filter out non existing extension classes
$extensions = array_filter($extensions, fn ($item) => class_exists($item));
return $extensions;
}
/**
* Get the class of an extension by its name
* @param string $extensionName case sensitive name of the extension e.g. PayPal
* @return string|null the class name of the extension e.g. App\Extensions\PayPal\PayPalExtension
*/
public static function getExtensionClass(string $extensionName)
{
$extensions = self::getAllExtensions();
foreach ($extensions as $extension) {
if (!(basename($extension) == $extensionName)) {
continue;
}
$extensionClass = $extension . '\\' . $extensionName . 'Extension';
return $extensionClass;
}
}
/**
* Get a config of an extension by its name
* @param string $extensionName
* @param string $configname
*/
public static function getExtensionConfig(string $extensionName, string $configname)
{
$extension = self::getExtensionClass($extensionName);
$config = $extension::getConfig();
if (isset($config[$configname])) {
return $config[$configname];
}
return null;
}
public static function getAllCsrfIgnoredRoutes()
{
$extensions = self::getAllExtensionClasses();
$routes = [];
foreach ($extensions as $extension) {
$config = $extension::getConfig();
if (isset($config['RoutesIgnoreCsrf'])) {
$routes = array_merge($routes, $config['RoutesIgnoreCsrf']);
}
}
// map over the routes and add the extension name as prefix
$result = array_map(fn ($item) => "extensions/{$item}", $routes);
return $result;
}
/**
* Summary of getAllExtensionMigrations
* @return array of all migration paths look like: app/Extensions/ExtensionNamespace/ExtensionName/migrations/
*/
public static function getAllExtensionMigrations()
{
$extensions = self::getAllExtensions();
// Transform the extensions to a path
$extensions = array_map(fn ($item) => self::extensionNameToPath($item), $extensions);
// get all migration directories of the extensions and return them as array
$migrations = [];
foreach ($extensions as $extension) {
$migrationDir = $extension . '/migrations';
if (file_exists($migrationDir)) {
$migrations[] = $migrationDir;
}
}
return $migrations;
}
/**
* Summary of getAllExtensionSettings
* @return array of all setting classes look like: App\Extensions\PaymentGateways\PayPal\PayPalSettings
*/
public static function getAllExtensionSettingsClasses()
{
$extensions = self::getAllExtensions();
$settings = [];
foreach ($extensions as $extension) {
$extensionName = basename($extension);
$settingsClass = $extension . '\\' . $extensionName . 'Settings';
if (class_exists($settingsClass)) {
$settings[] = $settingsClass;
}
}
return $settings;
}
public static function getExtensionSettings(string $extensionName)
{
$extension = self::getExtension($extensionName);
$settingClass = $extension . '\\' . $extensionName . 'Settings';
if (class_exists($settingClass)) {
return new $settingClass();
}
}
/**
* Transforms a extension name to a path
* @param string $extensionName e.g. App\Extensions\PaymentGateways\PayPal
* @return string e.g. C:\xampp\htdocs\laravel\app/Extensions/PaymentGateways/PayPal
*/
private static function extensionNameToPath(string $extensionName)
{
return app_path() . '/' . str_replace('\\', '/', str_replace('App\\', '', $extensionName));
}
}

View file

@ -4,7 +4,6 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\ApplicationApi;
use App\Settings\LocaleSettings;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -21,11 +20,9 @@ class ApplicationApiController extends Controller
*
* @return Application|Factory|View|Response
*/
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('admin.api.index', [
'locale_datatables' => $locale_settings->datatables
]);
return view('admin.api.index');
}
/**

View file

@ -15,7 +15,7 @@ class InvoiceController extends Controller
$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/*'));
$result = $this::rglob(storage_path('app/invoice/*'));
if ($res === true) {
$zip->addFromString('1. Info.txt', __('Created at').' '.now()->format('d.m.Y'));
foreach ($result as $file) {
@ -38,7 +38,7 @@ class InvoiceController extends Controller
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this->rglob($dir.'/'.basename($pattern), $flags));
$files = array_merge($files, $this::rglob($dir.'/'.basename($pattern), $flags));
}
return $files;

View file

@ -2,14 +2,12 @@
namespace App\Http\Controllers\Admin;
use App\Classes\PterodactylClient;
use App\Settings\PterodactylSettings;
use App\Settings\GeneralSettings;
use App\Classes\Pterodactyl;
use App\Http\Controllers\Controller;
use App\Models\Pterodactyl\Egg;
use App\Models\Pterodactyl\Location;
use App\Models\Pterodactyl\Nest;
use App\Models\Pterodactyl\Node;
use App\Models\Egg;
use App\Models\Location;
use App\Models\Nest;
use App\Models\Node;
use App\Models\Payment;
use App\Models\Product;
use App\Models\Server;
@ -21,14 +19,7 @@ class OverViewController extends Controller
{
public const TTL = 86400;
private $pterodactyl;
public function __construct(PterodactylSettings $ptero_settings)
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
public function index(GeneralSettings $general_settings)
public function index()
{
//Get counters
$counters = collect();
@ -143,7 +134,7 @@ class OverViewController extends Controller
//Get node information and prepare collection
$pteroNodeIds = [];
foreach ($this->pterodactyl->getNodes() as $pteroNode) {
foreach (Pterodactyl::getNodes() as $pteroNode) {
array_push($pteroNodeIds, $pteroNode['attributes']['id']);
}
$nodes = collect();
@ -154,7 +145,7 @@ class OverViewController extends Controller
} //Check if node exists on pterodactyl too, if not, skip
$nodes->put($nodeId, collect());
$nodes[$nodeId]->name = $DBnode['name'];
$pteroNode = $this->pterodactyl->getNode($nodeId);
$pteroNode = Pterodactyl::getNode($nodeId);
$nodes[$nodeId]->usagePercent = round(max($pteroNode['allocated_resources']['memory'] / ($pteroNode['memory'] * ($pteroNode['memory_overallocate'] + 100) / 100), $pteroNode['allocated_resources']['disk'] / ($pteroNode['disk'] * ($pteroNode['disk_overallocate'] + 100) / 100)) * 100, 2);
$counters['totalUsagePercent'] += $nodes[$nodeId]->usagePercent;
@ -165,7 +156,7 @@ class OverViewController extends Controller
}
$counters['totalUsagePercent'] = ($DBnodes->count()) ? round($counters['totalUsagePercent'] / $DBnodes->count(), 2) : 0;
foreach ($this->pterodactyl->getServers() as $server) { //gets all servers from Pterodactyl and calculates total of credit usage for each node separately + total
foreach (Pterodactyl::getServers() as $server) { //gets all servers from Pterodactyl and calculates total of credit usage for each node separately + total
$nodeId = $server['attributes']['node'];
if ($CPServer = Server::query()->where('pterodactyl_id', $server['attributes']['id'])->first()) {
@ -216,7 +207,6 @@ class OverViewController extends Controller
'deletedNodesPresent' => ($DBnodes->count() != count($pteroNodeIds)) ? true : false,
'perPageLimit' => ($counters['servers']->total != Server::query()->count()) ? true : false,
'tickets' => $tickets,
'credits_display_name' => $general_settings->credits_display_name
]);
}

View file

@ -5,17 +5,13 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\PartnerDiscount;
use App\Models\User;
use App\Settings\LocaleSettings;
use App\Settings\ReferralSettings;
use Illuminate\Http\Request;
class PartnerController extends Controller
{
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('admin.partners.index', [
'locale_datatables' => $locale_settings->datatables
]);
return view('admin.partners.index');
}
/**
@ -121,19 +117,19 @@ class PartnerController extends Controller
';
})
->addColumn('user', function (PartnerDiscount $partner) {
return ($user = User::where('id', $partner->user_id)->first()) ? '<a href="'.route('admin.users.show', $partner->user_id) . '">' . $user->name . '</a>' : __('Unknown user');
return ($user = User::where('id', $partner->user_id)->first()) ? '<a href="'.route('admin.users.show', $partner->user_id).'">'.$user->name.'</a>' : __('Unknown user');
})
->editColumn('created_at', function (PartnerDiscount $partner) {
return $partner->created_at ? $partner->created_at->diffForHumans() : '';
})
->editColumn('partner_discount', function (PartnerDiscount $partner) {
return $partner->partner_discount ? $partner->partner_discount . '%' : '0%';
return $partner->partner_discount ? $partner->partner_discount.'%' : '0%';
})
->editColumn('registered_user_discount', function (PartnerDiscount $partner) {
return $partner->registered_user_discount ? $partner->registered_user_discount . '%' : '0%';
return $partner->registered_user_discount ? $partner->registered_user_discount.'%' : '0%';
})
->editColumn('referral_system_commission', function (PartnerDiscount $partner, ReferralSettings $referral_settings) {
return $partner->referral_system_commission >= 0 ? $partner->referral_system_commission . '%' : __('Default') . ' ('.$referral_settings->percentage . '%)';
->editColumn('referral_system_commission', function (PartnerDiscount $partner) {
return $partner->referral_system_commission >= 0 ? $partner->referral_system_commission.'%' : __('Default').' ('.config('SETTINGS::REFERRAL:PERCENTAGE').'%)';
})
->rawColumns(['user', 'actions'])
->make();

View file

@ -18,19 +18,17 @@ use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Helpers\ExtensionHelper;
use App\Settings\GeneralSettings;
use App\Settings\LocaleSettings;
class PaymentController extends Controller
{
/**
* @return Application|Factory|View
*/
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('admin.payments.index')->with([
'payments' => Payment::paginate(15),
'locale_datatables' => $locale_settings->datatables
]);
}
@ -39,7 +37,7 @@ class PaymentController extends Controller
* @param ShopProduct $shopProduct
* @return Application|Factory|View
*/
public function checkOut(ShopProduct $shopProduct, GeneralSettings $general_settings)
public function checkOut(ShopProduct $shopProduct)
{
$discount = PartnerDiscount::getDiscount();
$price = $shopProduct->price - ($shopProduct->price * $discount / 100);
@ -51,10 +49,7 @@ class PaymentController extends Controller
// build a paymentgateways array that contains the routes for the payment gateways and the image path for the payment gateway which lays in public/images/Extensions/PaymentGateways with the extensionname in lowercase
foreach ($extensions as $extension) {
$extensionName = basename($extension);
$extensionSettings = ExtensionHelper::getExtensionSettings($extensionName);
if ($extensionSettings->enabled == false) continue;
if (!ExtensionHelper::getExtensionConfig($extensionName, 'enabled')) continue; // skip if not enabled
$payment = new \stdClass();
$payment->name = ExtensionHelper::getExtensionConfig($extensionName, 'name');
@ -63,6 +58,11 @@ class PaymentController extends Controller
}
}
return view('store.checkout')->with([
'product' => $shopProduct,
'discountpercent' => $discount,
@ -73,7 +73,6 @@ class PaymentController extends Controller
'total' => $shopProduct->getTotalPrice(),
'paymentGateways' => $paymentGateways,
'productIsFree' => $price <= 0,
'credits_display_name' => $general_settings->credits_display_name
]);
}

View file

@ -3,12 +3,9 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Pterodactyl\Location;
use App\Models\Pterodactyl\Nest;
use App\Models\Location;
use App\Models\Nest;
use App\Models\Product;
use App\Settings\GeneralSettings;
use App\Settings\LocaleSettings;
use App\Settings\UserSettings;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -24,11 +21,9 @@ class ProductController extends Controller
*
* @return Application|Factory|View
*/
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('admin.products.index', [
'locale_datatables' => $locale_settings->datatables
]);
return view('admin.products.index');
}
/**
@ -36,16 +31,15 @@ class ProductController extends Controller
*
* @return Application|Factory|View
*/
public function create(GeneralSettings $general_settings)
public function create()
{
return view('admin.products.create', [
'locations' => Location::with('nodes')->get(),
'nests' => Nest::with('eggs')->get(),
'credits_display_name' => $general_settings->credits_display_name
]);
}
public function clone(Product $product)
public function clone(Request $request, Product $product)
{
return view('admin.products.create', [
'product' => $product,
@ -96,12 +90,11 @@ class ProductController extends Controller
* @param Product $product
* @return Application|Factory|View
*/
public function show(Product $product, UserSettings $user_settings, GeneralSettings $general_settings)
public function show(Product $product)
{
return view('admin.products.show', [
'product' => $product,
'minimum_credits' => $user_settings->min_credits_to_make_server,
'credits_display_name' => $general_settings->credits_display_name
'minimum_credits' => config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER'),
]);
}
@ -111,13 +104,12 @@ class ProductController extends Controller
* @param Product $product
* @return Application|Factory|View
*/
public function edit(Product $product, GeneralSettings $general_settings)
public function edit(Product $product)
{
return view('admin.products.edit', [
'product' => $product,
'locations' => Location::with('nodes')->get(),
'nests' => Nest::with('eggs')->get(),
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -165,7 +157,7 @@ class ProductController extends Controller
* @param Product $product
* @return RedirectResponse
*/
public function disable(Product $product)
public function disable(Request $request, Product $product)
{
$product->update(['disabled' => ! $product->disabled]);
@ -198,6 +190,7 @@ class ProductController extends Controller
public function dataTable()
{
$query = Product::with(['servers']);
return datatables($query)
->addColumn('actions', function (Product $product) {
return '
@ -226,18 +219,18 @@ class ProductController extends Controller
$checked = $product->disabled == false ? 'checked' : '';
return '
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.products.disable', $product->id).'">
'.csrf_field().'
'.method_field('PATCH').'
<div class="custom-control custom-switch">
<input '.$checked.' name="disabled" onchange="this.form.submit()" type="checkbox" class="custom-control-input" id="switch'.$product->id.'">
<label class="custom-control-label" for="switch'.$product->id.'"></label>
</div>
</form>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.products.disable', $product->id).'">
'.csrf_field().'
'.method_field('PATCH').'
<div class="custom-control custom-switch">
<input '.$checked.' name="disabled" onchange="this.form.submit()" type="checkbox" class="custom-control-input" id="switch'.$product->id.'">
<label class="custom-control-label" for="switch'.$product->id.'"></label>
</div>
</form>
';
})
->editColumn('minimum_credits', function (Product $product, UserSettings $user_settings) {
return $product->minimum_credits==-1 ? $user_settings->min_credits_to_make_server : $product->minimum_credits;
->editColumn('minimum_credits', function (Product $product) {
return $product->minimum_credits==-1 ? config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER') : $product->minimum_credits;
})
->editColumn('created_at', function (Product $product) {
return $product->created_at ? $product->created_at->diffForHumans() : '';

View file

@ -2,12 +2,10 @@
namespace App\Http\Controllers\Admin;
use App\Classes\Pterodactyl;
use App\Http\Controllers\Controller;
use App\Models\Server;
use App\Models\User;
use App\Settings\LocaleSettings;
use App\Settings\PterodactylSettings;
use App\Classes\PterodactylClient;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -20,23 +18,14 @@ use Illuminate\Support\Facades\Log;
class ServerController extends Controller
{
private $pterodactyl;
public function __construct(PterodactylSettings $ptero_settings)
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
/**
* Display a listing of the resource.
*
* @return Application|Factory|View|Response
*/
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('admin.servers.index', [
'locale_datatables' => $locale_settings->datatables
]);
return view('admin.servers.index');
}
/**
@ -76,7 +65,7 @@ class ServerController extends Controller
// try to update the owner on pterodactyl
try {
$response = $this->pterodactyl->updateServerOwner($server, $user->pterodactyl_id);
$response = Pterodactyl::updateServerOwner($server, $user->pterodactyl_id);
if ($response->getStatusCode() != 200) {
return redirect()->back()->with('error', 'Failed to update server owner on pterodactyl');
}
@ -129,6 +118,7 @@ class ServerController extends Controller
public function syncServers()
{
$pteroServers = Pterodactyl::getServers();
$CPServers = Server::get();
$CPIDArray = [];
@ -139,7 +129,7 @@ class ServerController extends Controller
}
}
foreach ($this->pterodactyl->getServers() as $server) { //go thru all ptero servers, if server exists, change value to true in array.
foreach ($pteroServers as $server) { //go thru all ptero servers, if server exists, change value to true in array.
if (isset($CPIDArray[$server['attributes']['id']])) {
$CPIDArray[$server['attributes']['id']] = true;
@ -159,7 +149,7 @@ class ServerController extends Controller
}, ARRAY_FILTER_USE_BOTH); //Array of servers, that dont exist on ptero (value == false)
$deleteCount = 0;
foreach ($filteredArray as $key => $CPID) { //delete servers that dont exist on ptero anymore
if (!$this->pterodactyl->getServerAttributes($key, true)) {
if (!Pterodactyl::getServerAttributes($key, true)) {
$deleteCount++;
}
}
@ -226,8 +216,8 @@ class ServerController extends Controller
->editColumn('suspended', function (Server $server) {
return $server->suspended ? $server->suspended->diffForHumans() : '';
})
->editColumn('name', function (Server $server, PterodactylSettings $ptero_settings) {
return '<a class="text-info" target="_blank" href="' . $ptero_settings->panel_url . '/admin/servers/view/' . $server->pterodactyl_id . '">' . strip_tags($server->name) . '</a>';
->editColumn('name', function (Server $server) {
return '<a class="text-info" target="_blank" href="' . config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/admin/servers/view/' . $server->pterodactyl_id . '">' . strip_tags($server->name) . '</a>';
})
->rawColumns(['user', 'actions', 'status', 'name'])
->make();

View file

@ -2,15 +2,11 @@
namespace App\Http\Controllers\Admin;
use App\Helpers\ExtensionHelper;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Validator;
use Qirolab\Theme\Theme;
class SettingsController extends Controller
@ -23,109 +19,37 @@ class SettingsController extends Controller
public function index()
{
// get all other settings in app/Settings directory
// group items by file name like $categories
$settings = collect();
$settings_classes = [];
// get all app settings
$app_settings = scandir(app_path('Settings'));
$app_settings = array_diff($app_settings, ['.', '..']);
// append App\Settings to class name
foreach ($app_settings as $app_setting) {
$settings_classes[] = 'App\\Settings\\' . str_replace('.php', '', $app_setting);
}
// get all extension settings
$settings_files = array_merge($settings_classes, ExtensionHelper::getAllExtensionSettingsClasses());
foreach ($settings_files as $file) {
$className = $file;
// instantiate the class and call toArray method to get all options
$options = (new $className())->toArray();
// call getOptionInputData method to get all options
if (method_exists($className, 'getOptionInputData')) {
$optionInputData = $className::getOptionInputData();
} else {
$optionInputData = [];
}
// collect all option input data
$optionsData = [];
foreach ($options as $key => $value) {
$optionsData[$key] = [
'value' => $value,
'label' => $optionInputData[$key]['label'] ?? ucwords(str_replace('_', ' ', $key)),
'type' => $optionInputData[$key]['type'] ?? 'string',
'description' => $optionInputData[$key]['description'] ?? '',
'options' => $optionInputData[$key]['options'] ?? [],
];
}
// collect category icon if available
if (isset($optionInputData['category_icon'])) {
$optionsData['category_icon'] = $optionInputData['category_icon'];
}
$optionsData['settings_class'] = $className;
$settings[str_replace('Settings', '', class_basename($className))] = $optionsData;
//Get all tabs as laravel view paths
$tabs = [];
if(file_exists(Theme::getViewPaths()[0] . '/admin/settings/tabs/')){
$tabspath = glob(Theme::getViewPaths()[0] . '/admin/settings/tabs/*.blade.php');
}else{
$tabspath = glob(Theme::path($path = 'views', $themeName = 'default').'/admin/settings/tabs/*.blade.php');
}
$settings->sort();
foreach ($tabspath as $filename) {
$tabs[] = 'admin.settings.tabs.'.basename($filename, '.blade.php');
}
//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>';
}
$themes = array_diff(scandir(base_path('themes')), array('..', '.'));
return view('admin.settings.index', [
'settings' => $settings->all(),
'tabs' => $tabs,
'tabListItems' => $tabListItems,
'themes' => $themes,
'active_theme' => Theme::active(),
]);
}
/**
* Update the specified resource in storage.
*
*/
public function update(Request $request)
{
$category = request()->get('category');
$settings_class = request()->get('settings_class');
if (method_exists($settings_class, 'getValidations')) {
$validations = $settings_class::getValidations();
} else {
$validations = [];
}
$validator = Validator::make($request->all(), $validations);
if ($validator->fails()) {
return Redirect::to('admin/settings' . '#' . $category)->withErrors($validator)->withInput();
}
$settingsClass = new $settings_class();
foreach ($settingsClass->toArray() as $key => $value) {
// Get the type of the settingsclass property
$rp = new \ReflectionProperty($settingsClass, $key);
$rpType = $rp->getType();
if ($rpType == 'bool') {
$settingsClass->$key = $request->has($key);
continue;
}
$nullable = $rpType->allowsNull();
if ($nullable) $settingsClass->$key = $request->input($key) ?? null;
else $settingsClass->$key = $request->input($key);
}
$settingsClass->save();
return Redirect::to('admin/settings' . '#' . $category)->with('success', 'Settings updated successfully.');
}
}

View file

@ -3,8 +3,6 @@
namespace App\Http\Controllers\Admin;
use App\Models\ShopProduct;
use App\Settings\GeneralSettings;
use App\Settings\LocaleSettings;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
@ -22,14 +20,20 @@ class ShopProductController extends Controller
*
* @return Application|Factory|View|Response
*/
public function index(LocaleSettings $locale_settings, GeneralSettings $general_settings)
public function index(Request $request)
{
$isStoreEnabled = $general_settings->store_enabled;
$isPaymentSetup = false;
if (
env('APP_ENV') == 'local' ||
config('SETTINGS::PAYMENTS:PAYPAL:SECRET') && config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID') ||
config('SETTINGS::PAYMENTS:STRIPE:SECRET') && config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') && config('SETTINGS::PAYMENTS:STRIPE:METHODS')
) {
$isPaymentSetup = true;
}
return view('admin.store.index', [
'isStoreEnabled' => $isStoreEnabled,
'locale_datatables' => $locale_settings->datatables
'isPaymentSetup' => $isPaymentSetup,
]);
}
@ -38,11 +42,10 @@ class ShopProductController extends Controller
*
* @return Application|Factory|View|Response
*/
public function create(GeneralSettings $general_settings)
public function create()
{
return view('admin.store.create', [
'currencyCodes' => config('currency_codes'),
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -76,12 +79,11 @@ class ShopProductController extends Controller
* @param ShopProduct $shopProduct
* @return Application|Factory|View|Response
*/
public function edit(ShopProduct $shopProduct, GeneralSettings $general_settings)
public function edit(ShopProduct $shopProduct)
{
return view('admin.store.edit', [
'currencyCodes' => config('currency_codes'),
'shopProduct' => $shopProduct,
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -115,7 +117,7 @@ class ShopProductController extends Controller
* @param ShopProduct $shopProduct
* @return RedirectResponse
*/
public function disable(ShopProduct $shopProduct)
public function disable(Request $request, ShopProduct $shopProduct)
{
$shopProduct->update(['disabled' => !$shopProduct->disabled]);

View file

@ -5,7 +5,6 @@ namespace App\Http\Controllers\Admin;
use App\Enums\UsefulLinkLocation;
use App\Http\Controllers\Controller;
use App\Models\UsefulLink;
use App\Settings\LocaleSettings;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
@ -20,11 +19,9 @@ class UsefulLinkController extends Controller
*
* @return Application|Factory|View|Response
*/
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('admin.usefullinks.index', [
'locale_datatables' => $locale_settings->datatables
]);
return view('admin.usefullinks.index');
}
/**

View file

@ -2,14 +2,11 @@
namespace App\Http\Controllers\Admin;
use App\Classes\Pterodactyl;
use App\Events\UserUpdateCreditsEvent;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Notifications\DynamicNotification;
use App\Settings\LocaleSettings;
use App\Settings\PterodactylSettings;
use App\Classes\PterodactylClient;
use App\Settings\GeneralSettings;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -29,11 +26,12 @@ use Spatie\QueryBuilder\QueryBuilder;
class UserController extends Controller
{
private $pterodactyl;
public function __construct(PterodactylSettings $ptero_settings)
private Pterodactyl $pterodactyl;
public function __construct(Pterodactyl $pterodactyl)
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
$this->pterodactyl = $pterodactyl;
}
/**
@ -42,12 +40,9 @@ class UserController extends Controller
* @param Request $request
* @return Application|Factory|View|Response
*/
public function index(LocaleSettings $locale_settings, GeneralSettings $general_settings)
public function index(Request $request)
{
return view('admin.users.index', [
'locale_datatables' => $locale_settings->datatables,
'credits_display_name' => $general_settings->credits_display_name
]);
return view('admin.users.index');
}
/**
@ -56,7 +51,7 @@ class UserController extends Controller
* @param User $user
* @return Application|Factory|View|Response
*/
public function show(User $user, LocaleSettings $locale_settings, GeneralSettings $general_settings)
public function show(User $user)
{
//QUERY ALL REFERRALS A USER HAS
//i am not proud of this at all.
@ -70,8 +65,6 @@ class UserController extends Controller
return view('admin.users.show')->with([
'user' => $user,
'referrals' => $allReferals,
'locale_datatables' => $locale_settings->datatables,
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -106,11 +99,10 @@ class UserController extends Controller
* @param User $user
* @return Application|Factory|View|Response
*/
public function edit(User $user, GeneralSettings $general_settings)
public function edit(User $user)
{
return view('admin.users.edit')->with([
'user' => $user,
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -166,10 +158,6 @@ class UserController extends Controller
*/
public function destroy(User $user)
{
if ($user->role === 'admin' && User::query()->where('role', 'admin')->count() === 1) {
return redirect()->back()->with('error', __('You can not delete the last admin!'));
}
$user->delete();
return redirect()->back()->with('success', __('user has been removed!'));
@ -181,7 +169,7 @@ class UserController extends Controller
* @param User $user
* @return RedirectResponse
*/
public function verifyEmail(User $user)
public function verifyEmail(Request $request, User $user)
{
$user->verifyEmail();
@ -219,7 +207,7 @@ class UserController extends Controller
* @param User $user
* @return Application|Factory|View|Response
*/
public function notifications()
public function notifications(User $user)
{
return view('admin.users.notifications');
}
@ -260,11 +248,7 @@ class UserController extends Controller
}
$all = $data['all'] ?? false;
$users = $all ? User::all() : User::whereIn('id', $data['users'])->get();
try {
Notification::send($users, new DynamicNotification($data['via'], $database, $mail));
} catch (Exception $e) {
return redirect()->route('admin.users.notifications')->with('error', __('The attempt to send the email failed with the error: ' . $e->getMessage()));
}
Notification::send($users, new DynamicNotification($data['via'], $database, $mail));
return redirect()->route('admin.users.notifications')->with('success', __('Notification sent!'));
}
@ -349,8 +333,8 @@ class UserController extends Controller
->editColumn('last_seen', function (User $user) {
return $user->last_seen ? $user->last_seen->diffForHumans() : __('Never');
})
->editColumn('name', function (User $user, PterodactylSettings $ptero_settings) {
return '<a class="text-info" target="_blank" href="' . $ptero_settings->panel_url . '/admin/users/view/' . $user->pterodactyl_id . '">' . strip_tags($user->name) . '</a>';
->editColumn('name', function (User $user) {
return '<a class="text-info" target="_blank" href="' . config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/admin/users/view/' . $user->pterodactyl_id . '">' . strip_tags($user->name) . '</a>';
})
->rawColumns(['avatar', 'name', 'credits', 'role', 'usage', 'actions'])
->make();

View file

@ -6,8 +6,6 @@ use App\Events\UserUpdateCreditsEvent;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\Voucher;
use App\Settings\GeneralSettings;
use App\Settings\LocaleSettings;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
@ -24,12 +22,9 @@ class VoucherController extends Controller
*
* @return Application|Factory|View
*/
public function index(LocaleSettings $locale_settings, GeneralSettings $general_settings)
public function index()
{
return view('admin.vouchers.index', [
'locale_datatables' => $locale_settings->datatables,
'credits_display_name' => $general_settings->credits_display_name
]);
return view('admin.vouchers.index');
}
/**
@ -37,11 +32,9 @@ class VoucherController extends Controller
*
* @return Application|Factory|View
*/
public function create(GeneralSettings $general_settings)
public function create()
{
return view('admin.vouchers.create', [
'credits_display_name' => $general_settings->credits_display_name
]);
return view('admin.vouchers.create');
}
/**
@ -82,11 +75,10 @@ class VoucherController extends Controller
* @param Voucher $voucher
* @return Application|Factory|View
*/
public function edit(Voucher $voucher, GeneralSettings $general_settings)
public function edit(Voucher $voucher)
{
return view('admin.vouchers.edit', [
'voucher' => $voucher,
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -125,12 +117,10 @@ class VoucherController extends Controller
return redirect()->back()->with('success', __('voucher has been removed!'));
}
public function users(Voucher $voucher, LocaleSettings $locale_settings, GeneralSettings $general_settings)
public function users(Voucher $voucher)
{
return view('admin.vouchers.users', [
'voucher' => $voucher,
'locale_datatables' => $locale_settings->datatables,
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -140,7 +130,7 @@ class VoucherController extends Controller
*
* @throws ValidationException
*/
public function redeem(Request $request, GeneralSettings $general_settings)
public function redeem(Request $request)
{
//general validations
$request->validate([
@ -171,7 +161,7 @@ class VoucherController extends Controller
if ($request->user()->credits + $voucher->credits >= 99999999) {
throw ValidationException::withMessages([
'code' => "You can't redeem this voucher because you would exceed the limit of " . $general_settings->credits_display_name,
'code' => "You can't redeem this voucher because you would exceed the limit of ".CREDITS_DISPLAY_NAME,
]);
}
@ -181,7 +171,7 @@ class VoucherController extends Controller
event(new UserUpdateCreditsEvent($request->user()));
return response()->json([
'success' => "{$voucher->credits} ". $general_settings->credits_display_name .' '.__('have been added to your balance!'),
'success' => "{$voucher->credits} ".CREDITS_DISPLAY_NAME.' '.__('have been added to your balance!'),
]);
}

View file

@ -14,7 +14,6 @@ use Illuminate\Support\Facades\Notification;
use Illuminate\Support\HtmlString;
use Illuminate\Validation\ValidationException;
use Spatie\ValidationRules\Rules\Delimited;
use Exception;
class NotificationController extends Controller
{
@ -105,12 +104,8 @@ class NotificationController extends Controller
'users' => ['No users found!'],
]);
}
try {
Notification::send($users, new DynamicNotification($via, $database, $mail));
}
catch (Exception $e) {
return response()->json(['message' => 'The attempt to send the email failed with the error: ' . $e->getMessage()], 500);
}
Notification::send($users, new DynamicNotification($via, $database, $mail));
return response()->json(['message' => 'Notification successfully sent.', 'user_count' => $users->count()]);
}

View file

@ -2,16 +2,13 @@
namespace App\Http\Controllers\Api;
use App\Classes\Pterodactyl;
use App\Events\UserUpdateCreditsEvent;
use App\Http\Controllers\Controller;
use App\Models\DiscordUser;
use App\Models\User;
use App\Notifications\ReferralNotification;
use App\Traits\Referral;
use App\Settings\PterodactylSettings;
use App\Classes\PterodactylClient;
use App\Settings\ReferralSettings;
use App\Settings\UserSettings;
use Carbon\Carbon;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
@ -37,13 +34,6 @@ class UserController extends Controller
const ALLOWED_FILTERS = ['name', 'server_limit', 'email', 'pterodactyl_id', 'role', 'suspended'];
private $pterodactyl;
public function __construct(PterodactylSettings $ptero_settings)
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
/**
* Display a listing of the resource.
*
@ -105,7 +95,7 @@ class UserController extends Controller
//Update Users Password on Pterodactyl
//Username,Mail,First and Lastname are required aswell
$response = $this->pterodactyl->application->patch('/application/users/' . $user->pterodactyl_id, [
$response = Pterodactyl::client()->patch('/application/users/' . $user->pterodactyl_id, [
'username' => $request->name,
'first_name' => $request->name,
'last_name' => $request->name,
@ -213,7 +203,7 @@ class UserController extends Controller
*
* @throws ValidationException
*/
public function suspend(int $id)
public function suspend(Request $request, int $id)
{
$discordUser = DiscordUser::find($id);
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
@ -237,7 +227,7 @@ class UserController extends Controller
*
* @throws ValidationException
*/
public function unsuspend(int $id)
public function unsuspend(Request $request, int $id)
{
$discordUser = DiscordUser::find($id);
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
@ -256,7 +246,7 @@ class UserController extends Controller
/**
* @throws ValidationException
*/
public function store(Request $request, UserSettings $user_settings, ReferralSettings $referral_settings)
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
@ -265,7 +255,7 @@ class UserController extends Controller
]);
// Prevent the creation of new users via API if this is enabled.
if (!$user_settings->creation_enabled) {
if (!config('SETTINGS::SYSTEM:CREATION_OF_NEW_USERS', 'true')) {
throw ValidationException::withMessages([
'error' => 'The creation of new users has been blocked by the system administrator.',
]);
@ -274,13 +264,13 @@ class UserController extends Controller
$user = User::create([
'name' => $request->input('name'),
'email' => $request->input('email'),
'credits' => $user_settings->initial_credits,
'server_limit' => $user_settings->initial_server_limit,
'credits' => config('SETTINGS::USER:INITIAL_CREDITS', 150),
'server_limit' => config('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1),
'password' => Hash::make($request->input('password')),
'referral_code' => $this->createReferralCode(),
]);
$response = $this->pterodactyl->application->post('/application/users', [
$response = Pterodactyl::client()->post('/application/users', [
'external_id' => App::environment('local') ? Str::random(16) : (string) $user->id,
'username' => $user->name,
'email' => $user->email,
@ -307,8 +297,8 @@ class UserController extends Controller
$ref_code = $request->input('referral_code');
$new_user = $user->id;
if ($ref_user = User::query()->where('referral_code', '=', $ref_code)->first()) {
if ($referral_settings->mode === 'register' || $referral_settings->mode === 'both') {
$ref_user->increment('credits', $referral_settings->reward);
if (config('SETTINGS::REFERRAL:MODE') == 'register' || config('SETTINGS::REFERRAL:MODE') == 'both') {
$ref_user->increment('credits', config('SETTINGS::REFERRAL::REWARD'));
$ref_user->notify(new ReferralNotification($ref_user->id, $new_user));
}
//INSERT INTO USER_REFERRALS TABLE

View file

@ -3,7 +3,6 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Settings\GeneralSettings;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
@ -32,13 +31,13 @@ class ForgotPasswordController extends Controller
$this->middleware('guest');
}
protected function validateEmail(Request $request, GeneralSettings $general_settings)
protected function validateEmail(Request $request)
{
$this->validate($request, [
'email' => ['required', 'string', 'email', 'max:255'],
]);
if ($general_settings->recaptcha_enabled) {
if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
$this->validate($request, [
'g-recaptcha-response' => 'required|recaptcha',
]);

View file

@ -4,7 +4,6 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Settings\GeneralSettings;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@ -54,13 +53,13 @@ class LoginController extends Controller
return $field;
}
public function login(Request $request, GeneralSettings $general_settings)
public function login(Request $request)
{
$validationRules = [
$this->username() => 'required|string',
'password' => 'required|string',
];
if ($general_settings->recaptcha_enabled) {
if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
$validationRules['g-recaptcha-response'] = ['required', 'recaptcha'];
}
$request->validate($validationRules);

View file

@ -2,18 +2,13 @@
namespace App\Http\Controllers\Auth;
use App\Classes\Pterodactyl;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Notifications\ReferralNotification;
use App\Providers\RouteServiceProvider;
use App\Traits\Referral;
use Carbon\Carbon;
use App\Settings\PterodactylSettings;
use App\Classes\PterodactylClient;
use App\Settings\GeneralSettings;
use App\Settings\ReferralSettings;
use App\Settings\UserSettings;
use App\Settings\WebsiteSettings;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
@ -25,24 +20,6 @@ use Illuminate\Validation\ValidationException;
class RegisterController extends Controller
{
private $pterodactyl;
private $credits_display_name;
private $recaptcha_enabled;
private $website_show_tos;
private $register_ip_check;
private $initial_credits;
private $initial_server_limit;
private $referral_mode;
private $referral_reward;
/*
|--------------------------------------------------------------------------
| Register Controller
@ -68,18 +45,9 @@ class RegisterController extends Controller
*
* @return void
*/
public function __construct(PterodactylSettings $ptero_settings, GeneralSettings $general_settings, WebsiteSettings $website_settings, UserSettings $user_settings, ReferralSettings $referral_settings)
public function __construct()
{
$this->middleware('guest');
$this->pterodactyl = new PterodactylClient($ptero_settings);
$this->credits_display_name = $general_settings->credits_display_name;
$this->recaptcha_enabled = $general_settings->recaptcha_enabled;
$this->website_show_tos = $website_settings->show_tos;
$this->register_ip_check = $user_settings->register_ip_check;
$this->initial_credits = $user_settings->initial_credits;
$this->initial_server_limit = $user_settings->initial_server_limit;
$this->referral_mode = $referral_settings->mode;
$this->referral_reward = $referral_settings->reward;
}
/**
@ -95,14 +63,14 @@ class RegisterController extends Controller
'email' => ['required', 'string', 'email', 'max:64', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
];
if ($this->recaptcha_enabled) {
if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
$validationRules['g-recaptcha-response'] = ['required', 'recaptcha'];
}
if ($this->website_show_tos) {
if (config('SETTINGS::SYSTEM:SHOW_TOS') == 'true') {
$validationRules['terms'] = ['required'];
}
if ($this->register_ip_check) {
if (config('SETTINGS::SYSTEM:REGISTER_IP_CHECK', 'true') == 'true') {
//check if ip has already made an account
$data['ip'] = session()->get('ip') ?? request()->ip();
@ -131,16 +99,15 @@ class RegisterController extends Controller
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'credits' => $this->initial_credits,
'server_limit' => $this->initial_server_limit,
'credits' => config('SETTINGS::USER:INITIAL_CREDITS', 150),
'server_limit' => config('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1),
'password' => Hash::make($data['password']),
'referral_code' => $this->createReferralCode(),
'pterodactyl_id' => Str::uuid(),
]);
$response = $this->pterodactyl->application->post('/application/users', [
'external_id' => $user->pterodactyl_id,
$response = Pterodactyl::client()->post('/application/users', [
'external_id' => App::environment('local') ? Str::random(16) : (string) $user->id,
'username' => $user->name,
'email' => $user->email,
'first_name' => $user->name,
@ -158,23 +125,24 @@ class RegisterController extends Controller
]);
}
// delete activity log for user creation where description = 'created' or 'deleted' and subject_id = user_id
DB::table('activity_log')->where('description', 'created')->orWhere('description', 'deleted')->where('subject_id', $user->id)->delete();
$user->update([
'pterodactyl_id' => $response->json()['attributes']['id'],
]);
//INCREMENT REFERRAL-USER CREDITS
if (!empty($data['referral_code'])) {
$ref_code = $data['referral_code'];
$new_user = $user->id;
if ($ref_user = User::query()->where('referral_code', '=', $ref_code)->first()) {
if ($this->referral_mode === 'sign-up' || $this->referral_mode === 'both') {
$ref_user->increment('credits', $this->referral_reward);
if (config('SETTINGS::REFERRAL:MODE') == 'sign-up' || config('SETTINGS::REFERRAL:MODE') == 'both') {
$ref_user->increment('credits', config('SETTINGS::REFERRAL::REWARD'));
$ref_user->notify(new ReferralNotification($ref_user->id, $new_user));
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained ' . $this->referral_reward . ' ' . $this->credits_display_name . ' for sign-up-referral of ' . $user->name . ' (ID:' . $user->id . ')');
->log('gained ' . config('SETTINGS::REFERRAL::REWARD') . ' ' . config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME') . ' for sign-up-referral of ' . $user->name . ' (ID:' . $user->id . ')');
}
//INSERT INTO USER_REFERRALS TABLE
DB::table('user_referrals')->insert([

View file

@ -5,24 +5,22 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\DiscordUser;
use App\Models\User;
use App\Settings\DiscordSettings;
use App\Settings\UserSettings;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Laravel\Socialite\Facades\Socialite;
class SocialiteController extends Controller
{
public function redirect(DiscordSettings $discord_settings)
public function redirect()
{
$scopes = !empty($discord_settings->bot_token) && !empty($discord_settings->guild_id) ? ['guilds.join'] : [];
$scopes = ! empty(config('SETTINGS::DISCORD:BOT_TOKEN')) && ! empty(config('SETTINGS::DISCORD:GUILD_ID')) ? ['guilds.join'] : [];
return Socialite::driver('discord')
->scopes($scopes)
->redirect();
}
public function callback(DiscordSettings $discord_settings, UserSettings $user_settings)
public function callback()
{
if (Auth::guest()) {
return abort(500);
@ -31,9 +29,9 @@ class SocialiteController extends Controller
/** @var User $user */
$user = Auth::user();
$discord = Socialite::driver('discord')->user();
$botToken = $discord_settings->bot_token;
$guildId = $discord_settings->guild_id;
$roleId = $discord_settings->role_id;
$botToken = config('SETTINGS::DISCORD:BOT_TOKEN');
$guildId = config('SETTINGS::DISCORD:GUILD_ID');
$roleId = config('SETTINGS::DISCORD:ROLE_ID');
//save / update discord_users
@ -51,8 +49,8 @@ class SocialiteController extends Controller
DiscordUser::create(array_merge($discord->user, ['user_id' => Auth::user()->id]));
//update user
Auth::user()->increment('credits', $user_settings->credits_reward_after_verify_discord);
Auth::user()->increment('server_limit', $user_settings->server_limit_after_verify_discord);
Auth::user()->increment('credits', config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD'));
Auth::user()->increment('server_limit', config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD'));
Auth::user()->update(['discord_verified_at' => now()]);
} else {
$user->discordUser->update($discord->user);

View file

@ -4,9 +4,7 @@ namespace App\Http\Controllers;
use App\Models\PartnerDiscount;
use App\Models\UsefulLink;
use App\Settings\GeneralSettings;
use App\Settings\WebsiteSettings;
use App\Settings\ReferralSettings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
@ -34,7 +32,7 @@ class HomeController extends Controller
if (Storage::exists('callHome')) {
return;
}
Http::asForm()->post('https://market.controlpanel.gg/callhome.php', [
Http::asForm()->post('https://market.ctrlpanel.gg/callhome.php', [
'id' => Hash::make(URL::current()),
]);
Storage::put('callHome', 'This is only used to count the installations of cpgg.');
@ -91,7 +89,7 @@ class HomeController extends Controller
}
/** Show the application dashboard. */
public function index(GeneralSettings $general_settings, WebsiteSettings $website_settings, ReferralSettings $referral_settings)
public function index(Request $request)
{
$usage = Auth::user()->creditUsage();
$credits = Auth::user()->Credits();
@ -122,9 +120,6 @@ class HomeController extends Controller
'numberOfReferrals' => DB::table('user_referrals')->where('referral_id', '=', Auth::user()->id)->count(),
'partnerDiscount' => PartnerDiscount::where('user_id', Auth::user()->id)->first(),
'myDiscount' => PartnerDiscount::getDiscount(),
'general_settings' => $general_settings,
'website_settings' => $website_settings,
'referral_settings' => $referral_settings
]);
}
}

View file

@ -10,23 +10,20 @@ use App\Models\TicketCategory;
use App\Models\TicketComment;
use App\Models\User;
use App\Notifications\Ticket\User\ReplyNotification;
use App\Settings\LocaleSettings;
use App\Settings\PterodactylSettings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class TicketsController extends Controller
{
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('moderator.ticket.index', [
'tickets' => Ticket::orderBy('id', 'desc')->paginate(10),
'ticketcategories' => TicketCategory::all(),
'locale_datatables' => $locale_settings->datatables
]);
$tickets = Ticket::orderBy('id', 'desc')->paginate(10);
$ticketcategories = TicketCategory::all();
return view('moderator.ticket.index', compact('tickets', 'ticketcategories'));
}
public function show($ticket_id, PterodactylSettings $ptero_settings)
public function show($ticket_id)
{
try {
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
@ -37,9 +34,8 @@ class TicketsController extends Controller
$ticketcomments = $ticket->ticketcomments;
$ticketcategory = $ticket->ticketcategory;
$server = Server::where('id', $ticket->server)->first();
$pterodactyl_url = $ptero_settings->panel_url;
return view('moderator.ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server', 'pterodactyl_url'));
return view('moderator.ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server'));
}
public function changeStatus($ticket_id)
@ -168,11 +164,9 @@ class TicketsController extends Controller
->make(true);
}
public function blacklist(LocaleSettings $locale_settings)
public function blacklist()
{
return view('moderator.ticket.blacklist', [
'locale_datatables' => $locale_settings->datatables
]);
return view('moderator.ticket.blacklist');
}
public function blacklistAdd(Request $request)

View file

@ -2,26 +2,18 @@
namespace App\Http\Controllers;
use App\Classes\PterodactylClient;
use App\Models\Pterodactyl\Egg;
use App\Models\Pterodactyl\Location;
use App\Models\Pterodactyl\Node;
use App\Classes\Pterodactyl;
use App\Models\Egg;
use App\Models\Location;
use App\Models\Node;
use App\Models\Product;
use App\Settings\PterodactylSettings;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
class ProductController extends Controller
{
private $pterodactyl;
public function __construct(PterodactylSettings $ptero_settings)
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
{
/**
* @description get product locations based on selected egg
*
@ -68,7 +60,7 @@ class ProductController extends Controller
{
$nodes = $this->getNodesBasedOnEgg($request, $egg);
foreach ($nodes as $key => $node) {
$pteroNode = $this->pterodactyl->getNode($node->id);
$pteroNode = Pterodactyl::getNode($node->id);
if ($pteroNode['allocated_resources']['memory'] >= ($pteroNode['memory'] * ($pteroNode['memory_overallocate'] + 100) / 100) || $pteroNode['allocated_resources']['disk'] >= ($pteroNode['disk'] * ($pteroNode['disk_overallocate'] + 100) / 100)) {
$nodes->forget($key);
}
@ -117,7 +109,7 @@ class ProductController extends Controller
})
->get();
$pteroNode = $this->pterodactyl->getNode($node->id);
$pteroNode = Pterodactyl::getNode($node->id);
foreach ($products as $key => $product) {
if ($product->memory > ($pteroNode['memory'] * ($pteroNode['memory_overallocate'] + 100) / 100) - $pteroNode['allocated_resources']['memory'] || $product->disk > ($pteroNode['disk'] * ($pteroNode['disk_overallocate'] + 100) / 100) - $pteroNode['allocated_resources']['disk']) {
$product->doesNotFit = true;

View file

@ -2,12 +2,8 @@
namespace App\Http\Controllers;
use App\Classes\Pterodactyl;
use App\Models\User;
use App\Settings\UserSettings;
use App\Settings\PterodactylSettings;
use App\Classes\PterodactylClient;
use App\Settings\DiscordSettings;
use App\Settings\ReferralSettings;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@ -16,15 +12,8 @@ use Illuminate\Validation\ValidationException;
class ProfileController extends Controller
{
private $pterodactyl;
public function __construct(PterodactylSettings $ptero_settings)
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
/** Display a listing of the resource. */
public function index(UserSettings $user_settings, DiscordSettings $discord_settings, ReferralSettings $referral_settings)
public function index()
{
switch (Auth::user()->role) {
case 'admin':
@ -43,14 +32,10 @@ class ProfileController extends Controller
return view('profile.index')->with([
'user' => Auth::user(),
'credits_reward_after_verify_discord' => $user_settings->credits_reward_after_verify_discord,
'force_email_verification' => $user_settings->force_email_verification,
'force_discord_verification' => $user_settings->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'),
'badgeColor' => $badgeColor,
'discord_client_id' => $discord_settings->client_id,
'discord_client_secret' => $discord_settings->client_secret,
'referral_enabled' => $referral_settings->enabled,
'referral_allowed' => $referral_settings->allowed
]);
}
@ -78,15 +63,15 @@ class ProfileController extends Controller
$user = User::findOrFail($id);
//update password if necessary
if (!is_null($request->input('new_password'))) {
if (! is_null($request->input('new_password'))) {
//validate password request
$request->validate([
'current_password' => [
'required',
function ($attribute, $value, $fail) use ($user) {
if (!Hash::check($value, $user->password)) {
$fail('The ' . $attribute . ' is invalid.');
if (! Hash::check($value, $user->password)) {
$fail('The '.$attribute.' is invalid.');
}
},
],
@ -96,7 +81,7 @@ class ProfileController extends Controller
//Update Users Password on Pterodactyl
//Username,Mail,First and Lastname are required aswell
$response = $this->pterodactyl->application->patch('/application/users/' . $user->pterodactyl_id, [
$response = Pterodactyl::client()->patch('/application/users/'.$user->pterodactyl_id, [
'password' => $request->input('new_password'),
'username' => $request->input('name'),
'first_name' => $request->input('name'),
@ -118,13 +103,13 @@ class ProfileController extends Controller
//validate request
$request->validate([
'name' => 'required|min:4|max:30|alpha_num|unique:users,name,' . $id . ',id',
'email' => 'required|email|max:64|unique:users,email,' . $id . ',id',
'name' => 'required|min:4|max:30|alpha_num|unique:users,name,'.$id.',id',
'email' => 'required|email|max:64|unique:users,email,'.$id.',id',
'avatar' => 'nullable',
]);
//update avatar
if (!is_null($request->input('avatar'))) {
if (! is_null($request->input('avatar'))) {
$avatar = json_decode($request->input('avatar'));
if ($avatar->input->size > 3000000) {
abort(500);
@ -140,7 +125,7 @@ class ProfileController extends Controller
}
//update name and email on Pterodactyl
$response = $this->pterodactyl->application->patch('/application/users/' . $user->pterodactyl_id, [
$response = Pterodactyl::client()->patch('/application/users/'.$user->pterodactyl_id, [
'username' => $request->input('name'),
'first_name' => $request->input('name'),
'last_name' => $request->input('name'),

View file

@ -2,18 +2,14 @@
namespace App\Http\Controllers;
use App\Models\Pterodactyl\Egg;
use App\Models\Pterodactyl\Location;
use App\Models\Pterodactyl\Nest;
use App\Models\Pterodactyl\Node;
use App\Classes\Pterodactyl;
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\Notifications\ServerCreationError;
use App\Settings\UserSettings;
use App\Settings\ServerSettings;
use App\Settings\PterodactylSettings;
use App\Classes\PterodactylClient;
use App\Settings\GeneralSettings;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Client\Response;
@ -24,15 +20,8 @@ use Illuminate\Support\Facades\Request as FacadesRequest;
class ServerController extends Controller
{
private $pterodactyl;
public function __construct(PterodactylSettings $ptero_settings)
{
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
/** Display a listing of the resource. */
public function index(GeneralSettings $general_settings, PterodactylSettings $ptero_settings)
public function index()
{
$servers = Auth::user()->servers;
@ -40,7 +29,7 @@ class ServerController extends Controller
foreach ($servers as $server) {
//Get server infos from ptero
$serverAttributes = $this->pterodactyl->getServerAttributes($server->pterodactyl_id);
$serverAttributes = Pterodactyl::getServerAttributes($server->pterodactyl_id, true);
if (! $serverAttributes) {
continue;
}
@ -72,19 +61,14 @@ class ServerController extends Controller
return view('servers.index')->with([
'servers' => $servers,
'credits_display_name' => $general_settings->credits_display_name,
'pterodactyl_url' => $ptero_settings->panel_url,
'phpmyadmin_url' => $general_settings->phpmyadmin_url
]);
}
/** Show the form for creating a new resource. */
public function create(UserSettings $user_settings, ServerSettings $server_settings, GeneralSettings $general_settings)
public function create()
{
$validate_configuration = $this->validateConfigurationRules($user_settings, $server_settings);
if (!is_null($validate_configuration)) {
return $validate_configuration;
if (! is_null($this->validateConfigurationRules())) {
return $this->validateConfigurationRules();
}
$productCount = Product::query()->where('disabled', '=', false)->count();
@ -114,16 +98,13 @@ class ServerController extends Controller
'locations' => $locations,
'eggs' => $eggs,
'user' => Auth::user(),
'server_creation_enabled' => $server_settings->creation_enabled,
'min_credits_to_make_server' => $user_settings->min_credits_to_make_server,
'credits_display_name' => $general_settings->credits_display_name
]);
}
/**
* @return null|RedirectResponse
*/
private function validateConfigurationRules(UserSettings $user_settings, ServerSettings $server_settings)
private function validateConfigurationRules()
{
//limit validation
if (Auth::user()->servers()->count() >= Auth::user()->server_limit) {
@ -139,31 +120,35 @@ class ServerController extends Controller
$nodeName = $node->name;
// Check if node has enough memory and disk space
$checkResponse = $this->pterodactyl->checkNodeResources($node, $product->memory, $product->disk);
$checkResponse = Pterodactyl::checkNodeResources($node, $product->memory, $product->disk);
if ($checkResponse == false) {
return redirect()->route('servers.index')->with('error', __("The node '".$nodeName."' doesn't have the required memory or disk left to allocate this product."));
}
// Min. Credits
if (Auth::user()->credits < ($product->minimum_credits == -1
? $user_settings->min_credits_to_make_server
: $product->minimum_credits)) {
if (
Auth::user()->credits <
($product->minimum_credits == -1
? 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!');
}
}
//Required Verification for creating an server
if ($user_settings->force_email_verification && !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 (!$server_settings->creation_enabled && Auth::user()->role != 'admin') {
if (! config('SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS', 'true') && Auth::user()->role != 'admin') {
return redirect()->route('servers.index')->with('error', __('The system administrator has blocked the creation of new servers.'));
}
//Required Verification for creating an server
if ($user_settings->force_discord_verification && !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.'));
}
@ -171,15 +156,13 @@ class ServerController extends Controller
}
/** Store a newly created resource in storage. */
public function store(Request $request, UserSettings $user_settings, ServerSettings $server_settings)
public function store(Request $request)
{
/** @var Node $node */
/** @var Egg $egg */
/** @var Product $product */
$validate_configuration = $this->validateConfigurationRules($user_settings, $server_settings);
if (!is_null($validate_configuration)) {
return $validate_configuration;
if (! is_null($this->validateConfigurationRules())) {
return $this->validateConfigurationRules();
}
$request->validate([
@ -200,13 +183,13 @@ class ServerController extends Controller
]);
//get free allocation ID
$allocationId = $this->pterodactyl->getFreeAllocationId($node);
$allocationId = Pterodactyl::getFreeAllocationId($node);
if (! $allocationId) {
return $this->noAllocationsError($server);
}
//create server on pterodactyl
$response = $this->pterodactyl->createServer($server, $egg, $allocationId);
$response = Pterodactyl::createServer($server, $egg, $allocationId);
if ($response->failed()) {
return $this->serverCreationFailed($response, $server);
}
@ -218,7 +201,7 @@ class ServerController extends Controller
'identifier' => $serverAttributes['identifier'],
]);
if ($server_settings->charge_first_hour) {
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());
}
@ -269,12 +252,12 @@ class ServerController extends Controller
}
/** Show Server Settings */
public function show(Server $server, ServerSettings $server_settings, GeneralSettings $general_settings)
public function show(Server $server)
{
if ($server->user_id != Auth::user()->id) {
return back()->with('error', __('This is not your Server!'));
return back()->with('error', __('´This is not your Server!'));
}
$serverAttributes = $this->pterodactyl->getServerAttributes($server->pterodactyl_id);
$serverAttributes = Pterodactyl::getServerAttributes($server->pterodactyl_id);
$serverRelationships = $serverAttributes['relationships'];
$serverLocationAttributes = $serverRelationships['location']['attributes'];
@ -290,7 +273,7 @@ class ServerController extends Controller
$server->name = $serverAttributes['name'];
$server->egg = $serverRelationships['egg']['attributes']['name'];
$pteroNode = $this->pterodactyl->getNode($serverRelationships['node']['attributes']['id']);
$pteroNode = Pterodactyl::getNode($serverRelationships['node']['attributes']['id']);
$products = Product::orderBy('created_at')
->whereHas('nodes', function (Builder $builder) use ($serverRelationships) { //Only show products for that node
@ -309,8 +292,6 @@ class ServerController extends Controller
return view('servers.settings')->with([
'server' => $server,
'products' => $products,
'server_enable_upgrade' => $server_settings->enable_upgrade,
'credits_display_name' => $general_settings->credits_display_name
]);
}
@ -325,7 +306,7 @@ class ServerController extends Controller
$user = Auth::user();
$oldProduct = Product::where('id', $server->product->id)->first();
$newProduct = Product::where('id', $request->product_upgrade)->first();
$serverAttributes = $this->pterodactyl->getServerAttributes($server->pterodactyl_id);
$serverAttributes = Pterodactyl::getServerAttributes($server->pterodactyl_id);
$serverRelationships = $serverAttributes['relationships'];
// Get node resource allocation info
@ -336,7 +317,7 @@ class ServerController extends Controller
// Check if node has enough memory and disk space
$requireMemory = $newProduct->memory - $oldProduct->memory;
$requiredisk = $newProduct->disk - $oldProduct->disk;
$checkResponse = $this->pterodactyl->checkNodeResources($node, $requireMemory, $requiredisk);
$checkResponse = Pterodactyl::checkNodeResources($node, $requireMemory, $requiredisk);
if ($checkResponse == false) {
return redirect()->route('servers.index')->with('error', __("The node '".$nodeName."' doesn't have the required memory or disk left to upgrade the server."));
}
@ -350,12 +331,14 @@ class ServerController extends Controller
$server->product_id = $request->product_upgrade;
$server->update();
$server->allocation = $serverAttributes['allocation'];
$response = $this->pterodactyl->updateServer($server, $newProduct);
if ($response->failed()) return redirect()->route('servers.index')->with('error', __("The system was unable to update your server product. Please try again later or contact support."));
$response = Pterodactyl::updateServer($server, $newProduct);
if ($response->failed()) {
return $this->serverCreationFailed($response, $server);
}
//update user balance
$user->decrement('credits', $priceupgrade);
//restart the server
$response = $this->pterodactyl->powerAction($server, 'restart');
$response = Pterodactyl::powerAction($server, 'restart');
if ($response->failed()) {
return redirect()->route('servers.index')->with('error', $response->json()['errors'][0]['detail']);
}

View file

@ -3,31 +3,36 @@
namespace App\Http\Controllers;
use App\Models\ShopProduct;
use App\Settings\GeneralSettings;
use App\Settings\UserSettings;
use Illuminate\Support\Facades\Auth;
class StoreController extends Controller
{
/** Display a listing of the resource. */
public function index(UserSettings $user_settings, GeneralSettings $general_settings)
public function index()
{
$isStoreEnabled = $general_settings->store_enabled;
$isPaymentSetup = false;
if (
env('APP_ENV') == 'local' ||
config('SETTINGS::PAYMENTS:PAYPAL:SECRET') && config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID') ||
config('SETTINGS::PAYMENTS:STRIPE:SECRET') && config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') && config('SETTINGS::PAYMENTS:STRIPE:METHODS')
) {
$isPaymentSetup = true;
}
//Required Verification for creating an server
if ($user_settings->force_email_verification && !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 purchase credits.'));
}
//Required Verification for creating an server
if ($user_settings->force_discord_verification && !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 purchase Credits'));
}
return view('store.index')->with([
'products' => ShopProduct::where('disabled', '=', false)->orderBy('type', 'asc')->orderBy('price', 'asc')->get(),
'isStoreEnabled' => $isStoreEnabled,
'credits_display_name' => $general_settings->credits_display_name
'isPaymentSetup' => $isPaymentSetup,
]);
}
}

View file

@ -11,9 +11,6 @@ use App\Models\User;
use App\Notifications\Ticket\Admin\AdminCreateNotification;
use App\Notifications\Ticket\Admin\AdminReplyNotification;
use App\Notifications\Ticket\User\CreateNotification;
use App\Settings\LocaleSettings;
use App\Settings\PterodactylSettings;
use App\Settings\TicketSettings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Notification;
@ -21,28 +18,23 @@ use Illuminate\Support\Str;
class TicketsController extends Controller
{
public function index(LocaleSettings $locale_settings)
public function index()
{
return view('ticket.index', [
'tickets' => Ticket::where('user_id', Auth::user()->id)->paginate(10),
'ticketcategories' => TicketCategory::all(),
'locale_datatables' => $locale_settings->datatables
]);
$tickets = Ticket::where('user_id', Auth::user()->id)->paginate(10);
$ticketcategories = TicketCategory::all();
return view('ticket.index', compact('tickets', 'ticketcategories'));
}
public function store(Request $request, TicketSettings $ticket_settings)
public function store(Request $request)
{
$this->validate(
$request,
[
$this->validate($request, [
'title' => 'required',
'ticketcategory' => 'required',
'priority' => 'required',
'message' => 'required',
]
'message' => 'required',]
);
$ticket = new Ticket(
[
$ticket = new Ticket([
'title' => $request->input('title'),
'user_id' => Auth::user()->id,
'ticket_id' => strtoupper(Str::random(8)),
@ -50,28 +42,28 @@ class TicketsController extends Controller
'priority' => $request->input('priority'),
'message' => $request->input('message'),
'status' => 'Open',
'server' => $request->input('server'),
]
'server' => $request->input('server'),]
);
$ticket->save();
$user = Auth::user();
switch ($ticket_settings->notify) {
case 'all':
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
Notification::send($admin, new AdminCreateNotification($ticket, $user));
case 'admin':
$admin = User::where('role', 'admin')->get();
Notification::send($admin, new AdminCreateNotification($ticket, $user));
case 'moderator':
$admin = User::where('role', 'mod')->get();
Notification::send($admin, new AdminCreateNotification($ticket, $user));
if (config('SETTINGS::TICKET:NOTIFY') == "all") {
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
}
if (config('SETTINGS::TICKET:NOTIFY') == "admin") {
$admin = User::where('role', 'admin')->get();
}
if (config('SETTINGS::TICKET:NOTIFY') == "moderator") {
$admin = User::where('role', 'mod')->get();
}
$user->notify(new CreateNotification($ticket));
if (config('SETTINGS::TICKET:NOTIFY') != "none") {
Notification::send($admin, new AdminCreateNotification($ticket, $user));
}
return redirect()->route('ticket.index')->with('success', __('A ticket has been opened, ID: #') . $ticket->ticket_id);
}
public function show($ticket_id, PterodactylSettings $ptero_settings)
public function show($ticket_id)
{
try {
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
@ -81,9 +73,8 @@ class TicketsController extends Controller
$ticketcomments = $ticket->ticketcomments;
$ticketcategory = $ticket->ticketcategory;
$server = Server::where('id', $ticket->server)->first();
$pterodactyl_url = $ptero_settings->panel_url;
return view('ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server', 'pterodactyl_url'));
return view('ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server'));
}
public function reply(Request $request)
@ -179,10 +170,8 @@ class TicketsController extends Controller
return __($tickets->priority);
})
->editColumn('updated_at', function (Ticket $tickets) {
return [
'display' => $tickets->updated_at ? $tickets->updated_at->diffForHumans() : '',
'raw' => $tickets->updated_at ? strtotime($tickets->updated_at) : ''
];
return ['display' => $tickets->updated_at ? $tickets->updated_at->diffForHumans() : '',
'raw' => $tickets->updated_at ? strtotime($tickets->updated_at) : ''];
})
->addColumn('actions', function (Ticket $tickets) {
$statusButtonColor = ($tickets->status == "Closed") ? 'btn-success' : 'btn-warning';

View file

@ -16,6 +16,8 @@ class GlobalNames
*/
public function handle(Request $request, Closure $next)
{
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);
define('UNSUPPORTED_LANGS', $unsupported_lang_array);

View file

@ -2,7 +2,6 @@
namespace App\Http\Middleware;
use App\Settings\LocaleSettings;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
@ -10,12 +9,6 @@ use Illuminate\Support\Facades\Session;
class SetLocale
{
private $locale_settings;
public function __construct(LocaleSettings $locale_settings)
{
$this->locale_settings = $locale_settings;
}
/**
* Handle an incoming request.
*
@ -26,15 +19,15 @@ class SetLocale
public function handle($request, Closure $next)
{
if (Session::has('locale')) {
$locale = Session::get('locale', $this->locale_settings->default);
$locale = Session::get('locale', config('SETTINGS::LOCALE:DEFAULT'));
} else {
if (!$this->locale_settings->dynamic) {
$locale = $this->locale_settings->default;
if (config('SETTINGS::LOCALE:DYNAMIC') !== 'true') {
$locale = config('SETTINGS::LOCALE:DEFAULT');
} else {
$locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
if (! in_array($locale, explode(',', $this->locale_settings->available))) {
$locale = $this->locale_settings->default;
if (! in_array($locale, explode(',', config('SETTINGS::LOCALE:AVAILABLE')))) {
$locale = config('SETTINGS::LOCALE:DEFAULT');
}
}
}

View file

@ -3,27 +3,13 @@
namespace App\Listeners;
use App\Events\PaymentEvent;
use App\Settings\InvoiceSettings;
use App\Traits\Invoiceable;
class CreateInvoice
{
use Invoiceable;
private $invoice_enabled;
private $invoice_settings;
/**
* Create the event listener.
*
* @return void
*/
public function __construct(InvoiceSettings $invoice_settings)
{
$this->invoice_enabled = $invoice_settings->enabled;
$this->invoice_settings = $invoice_settings;
}
/**
* Handle the event.
*
@ -32,9 +18,9 @@ class CreateInvoice
*/
public function handle(PaymentEvent $event)
{
if ($this->invoice_enabled) {
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
// create invoice using the trait
$this->createInvoice($event->payment, $event->shopProduct, $this->invoice_settings);
$this->createInvoice($event->payment, $event->shopProduct);
}
}
}

View file

@ -4,24 +4,11 @@ namespace App\Listeners;
use App\Events\UserUpdateCreditsEvent;
use App\Models\Server;
use App\Settings\UserSettings;
use Exception;
use Illuminate\Contracts\Queue\ShouldQueue;
class UnsuspendServers implements ShouldQueue
{
private $min_credits_to_make_server;
/**
* Create the event listener.
*
* @return void
*/
public function __construct(UserSettings $user_settings)
{
$this->min_credits_to_make_server = $user_settings->min_credits_to_make_server;
}
/**
* Handle the event.
*
@ -32,7 +19,7 @@ class UnsuspendServers implements ShouldQueue
*/
public function handle(UserUpdateCreditsEvent $event)
{
if ($event->user->credits > $this->min_credits_to_make_server) {
if ($event->user->credits > config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER', 50)) {
/** @var Server $server */
foreach ($event->user->servers as $server) {
if ($server->isSuspended()) {

View file

@ -6,36 +6,11 @@ use App\Events\PaymentEvent;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use App\Models\PartnerDiscount;
use App\Settings\GeneralSettings;
use App\Settings\ReferralSettings;
use App\Settings\UserSettings;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class UserPayment
{
private $server_limit_after_irl_purchase;
private $referral_mode;
private $referral_percentage;
private $referral_always_give_commission;
private $credits_display_name;
/**
* Create the event listener.
*
* @return void
*/
public function __construct(UserSettings $user_settings, ReferralSettings $referral_settings, GeneralSettings $general_settings)
{
$this->server_limit_after_irl_purchase = $user_settings->server_limit_after_irl_purchase;
$this->referral_mode = $referral_settings->mode;
$this->referral_percentage = $referral_settings->percentage;
$this->referral_always_give_commission = $referral_settings->always_give_commission;
$this->credits_display_name = $general_settings->credits_display_name;
}
/**
* Handle the event.
*
@ -53,8 +28,8 @@ class UserPayment
}
//update server limit
if ($this->server_limit_after_irl_purchase !== 0 && $user->server_limit < $this->server_limit_after_irl_purchase) {
$user->update(['server_limit' => $this->server_limit_after_irl_purchase]);
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0 && $user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
}
//update User with bought item
@ -65,17 +40,17 @@ class UserPayment
}
//give referral commission always
if (($this->referral_mode === "commission" || $this->referral_mode === "both") && $shopProduct->type == "Credits" && $this->referral_always_give_commission) {
if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "true") {
if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
$ref_user = User::findOrFail($ref_user->referral_id);
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id, $this->referral_percentage)) / 100, 0, "", "");
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
$ref_user->increment('credits', $increment);
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained ' . $increment . ' ' . $this->credits_display_name . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
}
}
//update role give Referral-reward
@ -83,17 +58,17 @@ class UserPayment
$user->update(['role' => 'client']);
//give referral commission only on first purchase
if (($this->referral_mode === "commission" || $this->referral_mode === "both") && $shopProduct->type == "Credits" && !$this->referral_always_give_commission) {
if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "false") {
if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
$ref_user = User::findOrFail($ref_user->referral_id);
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id, $this->referral_percentage)) / 100, 0, "", "");
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
$ref_user->increment('credits', $increment);
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained ' . $increment . ' ' . $this->credits_display_name . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
}
}
}

View file

@ -2,23 +2,16 @@
namespace App\Listeners;
use App\Settings\UserSettings;
class Verified
{
private $server_limit_after_verify_email;
private $credits_reward_after_verify_email;
/**
* Create the event listener.
*
* @return void
*/
public function __construct(UserSettings $user_settings)
public function __construct()
{
$this->server_limit_after_verify_email = $user_settings->server_limit_after_verify_email;
$this->credits_reward_after_verify_email = $user_settings->credits_reward_after_verify_email;
//
}
/**
@ -29,9 +22,10 @@ class Verified
*/
public function handle($event)
{
if (! $event->user->email_verified_reward) {
$event->user->increment('server_limit', $this->server_limit_after_verify_email);
$event->user->increment('credits', $this->credits_reward_after_verify_email);
if (!$event->user->email_verified_reward) {
$event->user->increment('server_limit', config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL'));
$event->user->increment('credits', config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL'));
$event->user->update(['email_verified_reward' => true]);
}
}
}

View file

@ -1,14 +1,12 @@
<?php
namespace App\Models\Pterodactyl;
namespace App\Models;
use App\Classes\PterodactylClient;
use App\Classes\Pterodactyl;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use App\Models\Pterodactyl\Nest;
use App\Models\Product;
class Egg extends Model
{
@ -39,9 +37,9 @@ class Egg extends Model
public static function syncEggs()
{
Nest::syncNests();
$client = app(PterodactylClient::class);
Nest::all()->each(function (Nest $nest) use ($client) {
$eggs = $client->getEggs($nest);
Nest::all()->each(function (Nest $nest) {
$eggs = Pterodactyl::getEggs($nest);
foreach ($eggs as $egg) {
$array = [];

View file

@ -1,8 +1,8 @@
<?php
namespace App\Models\Pterodactyl;
namespace App\Models;
use App\Classes\PterodactylClient;
use App\Classes\Pterodactyl;
use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -33,8 +33,7 @@ class Location extends Model
*/
public static function syncLocations()
{
$client = app(PterodactylClient::class);
$locations = $client->getLocations();
$locations = Pterodactyl::getLocations();
//map response
$locations = array_map(function ($val) {

View file

@ -1,8 +1,8 @@
<?php
namespace App\Models\Pterodactyl;
namespace App\Models;
use App\Classes\PterodactylClient;
use App\Classes\Pterodactyl;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -32,8 +32,7 @@ class Nest extends Model
public static function syncNests()
{
$client = app(PterodactylClient::class);
$nests = $client->getNests();
$nests = Pterodactyl::getNests();
//map response
$nests = array_map(function ($nest) {

View file

@ -1,14 +1,13 @@
<?php
namespace App\Models\Pterodactyl;
namespace App\Models;
use App\Classes\PterodactylClient;
use App\Classes\Pterodactyl;
use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use App\Models\Product;
class Node extends Model
{
@ -33,8 +32,7 @@ class Node extends Model
public static function syncNodes()
{
Location::syncLocations();
$client = app(PterodactylClient::class);
$nodes = $client->getNodes();
$nodes = Pterodactyl::getNodes();
//map response
$nodes = array_map(function ($node) {

View file

@ -33,7 +33,7 @@ class PartnerDiscount extends Model
return 0;
}
public static function getCommission($user_id, $percentage)
public static function getCommission($user_id)
{
if ($partnerDiscount = PartnerDiscount::where('user_id', $user_id)->first()) {
if ($partnerDiscount->referral_system_commission >= 0) {
@ -41,6 +41,6 @@ class PartnerDiscount extends Model
}
}
return $percentage;
return config('SETTINGS::REFERRAL:PERCENTAGE');
}
}

View file

@ -9,8 +9,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
use App\Models\Pterodactyl\Egg;
use App\Models\Pterodactyl\Node;
class Product extends Model
{

View file

@ -2,8 +2,7 @@
namespace App\Models;
use App\Classes\PterodactylClient;
use App\Settings\PterodactylSettings;
use App\Classes\Pterodactyl;
use Exception;
use GuzzleHttp\Promise\PromiseInterface;
use Hidehalo\Nanoid\Client;
@ -22,17 +21,13 @@ class Server extends Model
{
use HasFactory;
use LogsActivity;
private PterodactylClient $pterodactyl;
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logOnlyDirty()
->logOnly(['*'])
->dontSubmitEmptyLogs();
-> logOnlyDirty()
-> logOnly(['*'])
-> dontSubmitEmptyLogs();
}
/**
* @var bool
*/
@ -67,12 +62,6 @@ class Server extends Model
'suspended' => 'datetime',
];
public function __construct()
{
$ptero_settings = new PterodactylSettings();
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
public static function boot()
{
parent::boot();
@ -84,8 +73,8 @@ class Server extends Model
});
static::deleting(function (Server $server) {
$response = $server->pterodactyl->application->delete("/application/servers/{$server->pterodactyl_id}");
if ($response->failed() && !is_null($server->pterodactyl_id)) {
$response = Pterodactyl::client()->delete("/application/servers/{$server->pterodactyl_id}");
if ($response->failed() && ! is_null($server->pterodactyl_id)) {
//only return error when it's not a 404 error
if ($response['errors'][0]['status'] != '404') {
throw new Exception($response['errors'][0]['code']);
@ -99,7 +88,7 @@ class Server extends Model
*/
public function isSuspended()
{
return !is_null($this->suspended);
return ! is_null($this->suspended);
}
/**
@ -107,7 +96,7 @@ class Server extends Model
*/
public function getPterodactylServer()
{
return $this->pterodactyl->application->get("/application/servers/{$this->pterodactyl_id}");
return Pterodactyl::client()->get("/application/servers/{$this->pterodactyl_id}");
}
/**
@ -115,7 +104,7 @@ class Server extends Model
*/
public function suspend()
{
$response = $this->pterodactyl->suspendServer($this);
$response = Pterodactyl::suspendServer($this);
if ($response->successful()) {
$this->update([
@ -131,7 +120,7 @@ class Server extends Model
*/
public function unSuspend()
{
$response = $this->pterodactyl->unSuspendServer($this);
$response = Pterodactyl::unSuspendServer($this);
if ($response->successful()) {
$this->update([

View file

@ -4,8 +4,48 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class Settings extends Model
{
use HasFactory;
protected $table = 'settings';
public const CACHE_TAG = 'setting';
public $primaryKey = 'key';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'key',
'value',
'type',
];
public static function boot()
{
parent::boot();
static::updated(function (Settings $settings) {
Cache::forget(self::CACHE_TAG.':'.$settings->key);
});
}
/**
* @param string $key
* @param $default
* @return mixed
*/
public static function getValueByKey(string $key, $default = null)
{
return Cache::rememberForever(self::CACHE_TAG.':'.$key, function () use ($default, $key) {
$settings = self::find($key);
return $settings ? $settings->value : $default;
});
}
}

View file

@ -2,12 +2,9 @@
namespace App\Models;
use App\Classes\Pterodactyl;
use App\Notifications\Auth\QueuedVerifyEmail;
use App\Notifications\WelcomeMessage;
use App\Settings\GeneralSettings;
use App\Settings\UserSettings;
use App\Classes\PterodactylClient;
use App\Settings\PterodactylSettings;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@ -26,8 +23,6 @@ class User extends Authenticatable implements MustVerifyEmail
{
use HasFactory, Notifiable, LogsActivity, CausesActivity;
private PterodactylClient $pterodactyl;
/**
* @var string[]
*/
@ -66,6 +61,7 @@ class User extends Authenticatable implements MustVerifyEmail
'avatar',
'suspended',
'referral_code',
'email_verified_reward'
];
/**
@ -88,20 +84,15 @@ class User extends Authenticatable implements MustVerifyEmail
'last_seen' => 'datetime',
'credits' => 'float',
'server_limit' => 'float',
'email_verified_reward' => 'boolean'
];
public function __construct()
{
$ptero_settings = new PterodactylSettings();
$this->pterodactyl = new PterodactylClient($ptero_settings);
}
public static function boot()
{
parent::boot();
static::created(function (User $user, GeneralSettings $general_settings, UserSettings $user_settings) {
$user->notify(new WelcomeMessage($user, $general_settings, $user_settings));
static::created(function (User $user) {
$user->notify(new WelcomeMessage($user));
});
static::deleting(function (User $user) {
@ -122,7 +113,7 @@ class User extends Authenticatable implements MustVerifyEmail
$user->discordUser()->delete();
$user->pterodactyl->application->delete("/application/users/{$user->pterodactyl_id}");
Pterodactyl::client()->delete("/application/users/{$user->pterodactyl_id}");
});
}
@ -195,6 +186,9 @@ class User extends Authenticatable implements MustVerifyEmail
return $this->suspended;
}
/**
* @throws Exception
*/
public function suspend()
{
foreach ($this->servers as $server) {
@ -208,6 +202,9 @@ class User extends Authenticatable implements MustVerifyEmail
return $this;
}
/**
* @throws Exception
*/
public function unSuspend()
{
foreach ($this->getServersWithProduct() as $server) {
@ -235,9 +232,23 @@ class User extends Authenticatable implements MustVerifyEmail
*/
public function getAvatar()
{
//TODO loading the images to confirm they exist is causing to much load time. alternative has to be found :) maybe onerror tag on the <img tags>
// if ($this->discordUser()->exists()) {
// if(@getimagesize($this->discordUser->getAvatar())) {
// $avatar = $this->discordUser->getAvatar();
// } else {
// $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
// }
// } else {
// $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
// }
return 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($this->email)));
}
/**
* @return string
*/
public function creditUsage()
{
$usage = 0;
@ -275,7 +286,7 @@ class User extends Authenticatable implements MustVerifyEmail
public function reVerifyEmail()
{
$this->forceFill([
'email_verified_at' => null,
'email_verified_at' => null
])->save();
}

View file

@ -3,8 +3,6 @@
namespace App\Notifications;
use App\Models\User;
use App\Settings\GeneralSettings;
use App\Settings\ReferralSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
@ -17,8 +15,6 @@ class ReferralNotification extends Notification
*/
private $user;
private $ref_user;
/**
* Create a new notification instance.
*
@ -47,13 +43,13 @@ class ReferralNotification extends Notification
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable, GeneralSettings $general_settings, ReferralSettings $referral_settings)
public function toArray($notifiable)
{
return [
'title' => __('Someone registered using your Code!'),
'content' => '
<p>You received '. $referral_settings->reward . ' ' . $general_settings->credits_display_name . '</p>
<p>because ' . $this->ref_user->name . ' registered with your Referral-Code!</p>
<p>You received '.config('SETTINGS::REFERRAL::REWARD').' '.config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME').'</p>
<p>because '.$this->ref_user->name.' registered with your Referral-Code!</p>
<p>Thank you very much for supporting us!.</p>
<p>'.config('app.name', 'Laravel').'</p>
',

View file

@ -3,8 +3,6 @@
namespace App\Notifications;
use App\Models\User;
use App\Settings\GeneralSettings;
use App\Settings\UserSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
@ -18,29 +16,14 @@ class WelcomeMessage extends Notification implements ShouldQueue
*/
private $user;
private $credits_display_name;
private $credits_reward_after_verify_discord;
private $credits_reward_after_verify_email;
private $server_limit_after_verify_discord;
private $server_limit_after_verify_email;
/**
* Create a new notification instance.
*
* @param User $user
*/
public function __construct(User $user, GeneralSettings $general_settings, UserSettings $user_settings)
public function __construct(User $user)
{
$this->user = $user;
$this->credits_display_name = $general_settings->credits_display_name;
$this->credits_reward_after_verify_discord = $user_settings->credits_reward_after_verify_discord;
$this->credits_reward_after_verify_email = $user_settings->credits_reward_after_verify_email;
$this->server_limit_after_verify_discord = $user_settings->server_limit_after_verify_discord;
$this->server_limit_after_verify_email = $user_settings->server_limit_after_verify_email;
}
/**
@ -57,18 +40,18 @@ class WelcomeMessage extends Notification implements ShouldQueue
public function AdditionalLines()
{
$AdditionalLine = '';
if ($this->credits_reward_after_verify_email != 0) {
$AdditionalLine .= __('Verifying your e-mail address will grant you ').$this->credits_reward_after_verify_email.' '.__('additional').' '.$this->credits_display_name.'. <br />';
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 ($this->server_limit_after_verify_email != 0) {
$AdditionalLine .= __('Verifying your e-mail will also increase your Server Limit by ').$this->server_limit_after_verify_email.'. <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 ($this->credits_reward_after_verify_discord != 0) {
$AdditionalLine .= __('You can also verify your discord account to get another ').$this->credits_reward_after_verify_discord.' '.$this->credits_display_name.'. <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 ($this->server_limit_after_verify_discord != 0) {
$AdditionalLine .= __('Verifying your Discord account will also increase your Server Limit by ').$this->server_limit_after_verify_discord.'. <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;

View file

@ -2,17 +2,16 @@
namespace App\Providers;
use App\Extensions\PaymentGateways\PayPal\PayPalSettings;
use App\Models\Settings;
use App\Models\UsefulLink;
use App\Settings\MailSettings;
use Exception;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
use Qirolab\Theme\Theme;
class AppServiceProvider extends ServiceProvider
{
@ -55,14 +54,6 @@ class AppServiceProvider extends ServiceProvider
return $ok;
});
// Force HTTPS if APP_URL is set to https
if (config('app.url') && parse_url(config('app.url'), PHP_URL_SCHEME) === 'https') {
URL::forceScheme('https');
}
// Do not run this code if no APP_KEY is set
if (config('app.key') == null) return;
try {
if (Schema::hasColumn('useful_links', 'position')) {
$useful_links = UsefulLink::where("position", "like", "%topbar%")->get()->sortby("id");
@ -72,8 +63,92 @@ class AppServiceProvider extends ServiceProvider
Log::error("Couldnt find useful_links. Probably the installation is not completet. " . $e);
}
//only run if the installer has been executed
try {
$settings = Settings::all();
// Set all configs from database
foreach ($settings as $setting) {
config([$setting->key => $setting->value]);
}
$settings = $this->app->make(MailSettings::class);
$settings->setConfig();
if (!file_exists(base_path('themes') . "/" . config("SETTINGS::SYSTEM:THEME"))) {
config(['SETTINGS::SYSTEM:THEME' => "default"]);
}
if (config('SETTINGS::SYSTEM:THEME') && config('SETTINGS::SYSTEM:THEME') !== config('theme.active')) {
Theme::set(config("SETTINGS::SYSTEM:THEME", "default"), "default");
} else {
Theme::set("default", "default");
}
// 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
// Load recaptcha package if recaptcha is enabled
if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
$this->app->register(\Biscolab\ReCaptcha\ReCaptchaServiceProvider::class);
}
//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');
}
try {
$stringfromfile = file(base_path() . '/.git/HEAD');
$firstLine = $stringfromfile[0]; //get the string from the array
$explodedstring = explode('/', $firstLine, 3); //seperate out by the "/" in the string
$branchname = $explodedstring[2]; //get the one that is always the branch name
} catch (Exception $e) {
$branchname = 'unknown';
Log::notice($e);
}
config(['BRANCHNAME' => $branchname]);
// Set Discord-API Config
config(['services.discord.client_id' => config('SETTINGS::DISCORD:CLIENT_ID')]);
config(['services.discord.client_secret' => config('SETTINGS::DISCORD:CLIENT_SECRET')]);
} catch (Exception $e) {
error_log('Settings Error: Could not load settings from database. The Installation probably is not done yet.');
error_log($e);
Log::error('Settings Error: Could not load settings from database. The Installation probably is not done yet.');
Log::error($e);
}
}
}

View file

@ -7,11 +7,12 @@ use App\Events\UserUpdateCreditsEvent;
use App\Listeners\CreateInvoice;
use App\Listeners\UnsuspendServers;
use App\Listeners\UserPayment;
use App\Listeners\Verified;
use App\Listeners\Verified as VerifiedListener;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use SocialiteProviders\Manager\SocialiteWasCalled;
use Illuminate\Auth\Events\Verified;
class EventServiceProvider extends ServiceProvider
{
@ -35,8 +36,8 @@ class EventServiceProvider extends ServiceProvider
// ... other providers
'SocialiteProviders\\Discord\\DiscordExtendSocialite@handle',
],
'Illuminate\Auth\Events\Verified' => [
Verified::class,
Verified::class => [
VerifiedListener::class,
],
];

View file

@ -1,87 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class DiscordSettings extends Settings
{
public ?string $bot_token;
public ?string $client_id;
public ?string $client_secret;
public ?string $guild_id;
public ?string $invite_url;
public ?string $role_id;
public static function group(): string
{
return 'discord';
}
public static function encrypted(): array
{
return [
'bot_token',
'client_id',
'client_secret'
];
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'bot_token' => 'nullable|string',
'client_id' => 'nullable|string',
'client_secret' => 'nullable|string',
'guild_id' => 'nullable|string',
'invite_url' => 'nullable|string|url',
'role_id' => 'nullable|string',
];
}
/**
* Summary of optionInputData array
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-user-friends',
'bot_token' => [
'label' => 'Bot Token',
'type' => 'string',
'description' => 'The bot token for your Discord bot.',
],
'client_id' => [
'label' => 'Client ID',
'type' => 'string',
'description' => 'The client ID for your Discord bot.',
],
'client_secret' => [
'label' => 'Client Secret',
'type' => 'string',
'description' => 'The client secret for your Discord bot.',
],
'guild_id' => [
'label' => 'Guild ID',
'type' => 'string',
'description' => 'The guild ID for your Discord server.',
],
'invite_url' => [
'label' => 'Invite URL',
'type' => 'string',
'description' => 'The invite URL for your Discord server.',
],
'role_id' => [
'label' => 'Role ID',
'type' => 'string',
'description' => 'The role ID for your Discord server.',
],
];
}
}

View file

@ -1,138 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class GeneralSettings extends Settings
{
public bool $store_enabled;
public string $credits_display_name;
public bool $recaptcha_enabled;
public string $recaptcha_site_key;
public string $recaptcha_secret_key;
public string $phpmyadmin_url;
public bool $alert_enabled;
public string $alert_type;
public string $alert_message;
public string $theme;
//public int $initial_user_role; wait for Roles & Permissions PR.
public static function group(): string
{
return 'general';
}
public static function encrypted(): array
{
return [
'recaptcha_site_key',
'recaptcha_secret_key'
];
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'store_enabled' => 'boolean',
'credits_display_name' => 'required|string',
'recaptcha_enabled' => 'nullable|boolean',
'recaptcha_site_key' => 'nullable|string',
'recaptcha_secret_key' => 'nullable|string',
'phpmyadmin_url' => 'nullable|string',
'alert_enabled' => 'nullable|boolean',
'alert_type' => 'required|in:primary,secondary,success,danger,warning,info',
'alert_message' => 'required|string',
'theme' => 'required|in:default,BlueInfinity' // TODO: themes should be made/loaded dynamically
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => "fas fa-cog",
'store_enabled' => [
'type' => 'boolean',
'label' => 'Enable Store',
'description' => 'Enable the store for users to purchase credits.'
],
'credits_display_name' => [
'type' => 'string',
'label' => 'Credits Display Name',
'description' => 'The name of the currency used.'
],
'initial_user_credits' => [
'type' => 'number',
'label' => 'Initial User Credits',
'description' => 'The amount of credits a user gets when they register.'
],
'initial_server_limit' => [
'type' => 'number',
'label' => 'Initial Server Limit',
'description' => 'The amount of servers a user can create when they register.'
],
'recaptcha_enabled' => [
'type' => 'boolean',
'label' => 'Enable reCAPTCHA',
'description' => 'Enable reCAPTCHA on the login page.'
],
'recaptcha_site_key' => [
'type' => 'string',
'label' => 'reCAPTCHA Site Key',
'description' => 'The site key for reCAPTCHA.'
],
'recaptcha_secret_key' => [
'type' => 'string',
'label' => 'reCAPTCHA Secret Key',
'description' => 'The secret key for reCAPTCHA.'
],
'phpmyadmin_url' => [
'type' => 'string',
'label' => 'phpMyAdmin URL',
'description' => 'The URL of your phpMyAdmin installation.'
],
'alert_enabled' => [
'type' => 'boolean',
'label' => 'Enable Alert',
'description' => 'Enable an alert to be displayed on the home page.'
],
'alert_type' => [
'type' => 'select',
'label' => 'Alert Type',
'options' => [
'primary' => 'Blue',
'secondary' => 'Grey',
'success' => 'Green',
'danger' => 'Red',
'warning' => 'Orange',
'info' => 'Cyan',
],
'description' => 'The type of alert to display.'
],
'alert_message' => [
'type' => 'string',
'label' => 'Alert Message',
'description' => 'The message to display in the alert.'
],
'theme' => [
'type' => 'select',
'label' => 'Theme',
'options' => [
'default' => 'Default',
'BlueInfinity' => 'Blue Infinity',
], // TODO: themes should be made/loaded dynamically
'description' => 'The theme to use for the site.'
],
];
}
}

View file

@ -1,92 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class InvoiceSettings extends Settings
{
public ?string $company_address;
public ?string $company_mail;
public ?string $company_name;
public ?string $company_phone;
public ?string $company_vat;
public ?string $company_website;
public bool $enabled;
public ?string $prefix;
public static function group(): string
{
return 'invoice';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'company_address' => 'nullable|string',
'company_mail' => 'nullable|string',
'company_name' => 'nullable|string',
'company_phone' => 'nullable|string',
'company_vat' => 'nullable|string',
'company_website' => 'nullable|string',
'enabled' => 'nullable|boolean',
'prefix' => 'nullable|string',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-file-invoice-dollar',
'company_address' => [
'label' => 'Company Address',
'type' => 'string',
'description' => 'The address of your company.',
],
'company_mail' => [
'label' => 'Company Mail',
'type' => 'string',
'description' => 'The mail of your company.',
],
'company_name' => [
'label' => 'Company Name',
'type' => 'string',
'description' => 'The name of your company.',
],
'company_phone' => [
'label' => 'Company Phone',
'type' => 'string',
'description' => 'The phone of your company.',
],
'company_vat' => [
'label' => 'Company VAT ID',
'type' => 'string',
'description' => 'The VAT ID of your company.',
],
'company_website' => [
'label' => 'Company Website',
'type' => 'string',
'description' => 'The website of your company.',
],
'enabled' => [
'label' => 'Enabled',
'type' => 'boolean',
'description' => 'Enable or disable invoices.',
],
'prefix' => [
'label' => 'Prefix',
'type' => 'string',
'description' => 'The prefix of your invoices.',
],
];
}
}

View file

@ -1,73 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class LocaleSettings extends Settings
{
public ?string $available;
public bool $clients_can_change;
public ?string $datatables;
public string $default;
public bool $dynamic;
public static function group(): string
{
return 'locale';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'available' => 'nullable|array',
'clients_can_change' => 'nullable|boolean',
'datatables' => 'nullable|string',
'default' => 'required|in:' . implode(',', config('app.available_locales')),
'dynamic' => 'nullable|boolean',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-globe',
'available' => [
'label' => 'Available Locales',
'type' => 'multiselect',
'description' => 'The locales that are available for the user to choose from.',
'options' => config('app.available_locales'),
],
'clients_can_change' => [
'label' => 'Clients Can Change',
'type' => 'boolean',
'description' => 'Whether clients can change their locale.',
],
'datatables' => [
'label' => 'Datatables Locale',
'type' => 'string',
'description' => 'The datatables lang-code. <br><strong>Example:</strong> en-gb, fr_fr, de_de<br>More Information: <a href="https://datatables.net/plug-ins/i18n/">https://datatables.net/plug-ins/i18n/</a>',
],
'default' => [
'label' => 'Default Locale',
'type' => 'select',
'description' => 'The default locale to use.',
'options' => config('app.available_locales'),
],
'dynamic' => [
'label' => 'Dynamic Locale',
'type' => 'boolean',
'description' => 'Whether to use the dynamic locale.',
],
];
}
}

View file

@ -1,119 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class MailSettings extends Settings
{
public ?string $mail_host;
public ?int $mail_port;
public ?string $mail_username;
public ?string $mail_password;
public ?string $mail_encryption;
public ?string $mail_from_address;
public ?string $mail_from_name;
public ?string $mail_mailer;
public bool $mail_enabled;
public static function group(): string
{
return 'mail';
}
public static function encrypted(): array
{
return [
'mail_password'
];
}
public function setConfig()
{
try {
config()->set('mail.mailers.smtp.host', $this->mail_host);
config()->set('mail.mailers.smtp.port', $this->mail_port);
config()->set('mail.mailers.smtp.encryption', $this->mail_encryption);
config()->set('mail.mailers.smtp.username', $this->mail_username);
config()->set('mail.mailers.smtp.password', $this->mail_password);
config()->set('mail.from.address', $this->mail_from_address);
config()->set('mail.from.name', $this->mail_from_name);
} catch (\Exception) {
}
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'mail_host' => 'nullable|string',
'mail_port' => 'nullable|int',
'mail_username' => 'nullable|string',
'mail_password' => 'nullable|string',
'mail_encryption' => 'nullable|string',
'mail_from_address' => 'nullable|string',
'mail_from_name' => 'nullable|string',
'mail_mailer' => 'nullable|string',
'mail_enabled' => 'nullable|boolean',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-envelope',
'mail_host' => [
'label' => 'Mail Host',
'type' => 'string',
'description' => 'The host of your mail server.',
],
'mail_port' => [
'label' => 'Mail Port',
'type' => 'number',
'description' => 'The port of your mail server.',
],
'mail_username' => [
'label' => 'Mail Username',
'type' => 'string',
'description' => 'The username of your mail server.',
],
'mail_password' => [
'label' => 'Mail Password',
'type' => 'string',
'description' => 'The password of your mail server.',
],
'mail_encryption' => [
'label' => 'Mail Encryption',
'type' => 'string',
'description' => 'The encryption of your mail server.',
],
'mail_from_address' => [
'label' => 'Mail From Address',
'type' => 'string',
'description' => 'The from address of your mail server.',
],
'mail_from_name' => [
'label' => 'Mail From Name',
'type' => 'string',
'description' => 'The from name of your mail server.',
],
'mail_mailer' => [
'label' => 'Mail Mailer',
'type' => 'string',
'description' => 'The mailer of your mail server.',
],
'mail_enabled' => [
'label' => 'Mail Enabled',
'type' => 'boolean',
],
];
}
}

View file

@ -1,82 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class PterodactylSettings extends Settings
{
public string $admin_token;
public string $user_token;
public string $panel_url;
public int $per_page_limit;
public static function group(): string
{
return 'pterodactyl';
}
public static function encrypted(): array
{
return [
'admin_token',
'user_token'
];
}
/**
* Get url with ensured ending backslash
*
* @return string
*/
public function getUrl(): string
{
return str_ends_with($this->panel_url, '/') ? $this->panel_url : $this->panel_url . '/';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'panel_url' => 'required|string|url',
'admin_token' => 'required|string',
'user_token' => 'required|string',
'per_page_limit' => 'required|integer|min:1|max:10000',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-server',
'panel_url' => [
'label' => 'Panel URL',
'type' => 'string',
'description' => 'The URL to your Pterodactyl panel.',
],
'admin_token' => [
'label' => 'Admin Token',
'type' => 'string',
'description' => 'The admin user token for your Pterodactyl panel.',
],
'user_token' => [
'label' => 'User Token',
'type' => 'string',
'description' => 'The user token for your Pterodactyl panel.',
],
'per_page_limit' => [
'label' => 'Per Page Limit',
'type' => 'number',
'description' => 'The number of servers to show per page.',
],
];
}
}

View file

@ -1,87 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class ReferralSettings extends Settings
{
public string $allowed;
public bool $always_give_commission;
public bool $enabled;
public ?float $reward;
public string $mode;
public ?int $percentage;
public static function group(): string
{
return 'referral';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'allowed' => 'required|in:Everyone,Clients',
'always_give_commission' => 'nullable|boolean',
'enabled' => 'nullable|boolean',
'reward' => 'nullable|numeric',
'mode' => 'required|in:Commission,Sign-Up,Both',
'percentage' => 'nullable|numeric',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-user-friends',
'allowed' => [
'label' => 'Allowed',
'type' => 'select',
'description' => 'Who is allowed to see their referral-URL',
'options' => [
'everyone' => 'Everyone',
'clients' => 'Clients',
],
],
'always_give_commission' => [
'label' => 'Always Give Commission',
'type' => 'boolean',
'description' => 'Always give commission to the referrer.',
],
'enabled' => [
'label' => 'Enabled',
'type' => 'boolean',
'description' => 'Enable referral system.',
],
'reward' => [
'label' => 'Reward',
'type' => 'number',
'description' => 'Reward for the referrer.',
],
'mode' => [
'label' => 'Mode',
'type' => 'select',
'description' => 'Referral mode.',
'options' => [
'commission' => 'Commission',
'sign-up' => 'Sign-Up',
'both' => 'Both',
],
],
'percentage' => [
'label' => 'Percentage',
'type' => 'number',
'description' => 'If a referred user buys credits, the referral-user will get x% of the Credits the referred user bought.',
],
];
}
}

View file

@ -1,64 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class ServerSettings extends Settings
{
public int $allocation_limit;
public bool $creation_enabled;
public bool $enable_upgrade;
public bool $charge_first_hour;
public static function group(): string
{
return 'server';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'allocation_limit' => 'required|integer|min:0',
'creation_enabled' => 'nullable|boolean',
'enable_upgrade' => 'nullable|boolean',
'charge_first_hour' => 'nullable|boolean',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-server',
'allocation_limit' => [
'label' => 'Allocation Limit',
'type' => 'number',
'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.',
],
'creation_enabled' => [
'label' => 'Creation Enabled',
'type' => 'boolean',
'description' => 'Whether or not users can create servers.',
],
'enable_upgrade' => [
'label' => 'Enable Upgrade',
'type' => 'boolean',
'description' => 'Whether or not users can upgrade their servers.',
],
'charge_first_hour' => [
'label' => 'Charge First Hour',
'type' => 'boolean',
'description' => 'Whether or not the first hour of a server is charged.',
],
];
}
}

View file

@ -1,56 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class TicketSettings extends Settings
{
public bool $enabled;
public string $notify;
public static function group(): string
{
return 'ticket';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'enabled' => 'nullable|boolean',
'notify' => 'nullable|string',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-ticket-alt',
'enabled' => [
'label' => 'Enabled',
'type' => 'boolean',
'description' => 'Enable or disable the ticket system.',
],
'notify' => [
'label' => 'Notify',
'type' => 'select',
'description' => 'Who will receive an E-Mail when a new Ticket is created.',
'options' => [
'admin' => 'Admins',
'moderator' => 'Moderators',
'all' => 'Admins and Moderators',
'none' => 'Nobody',
],
],
];
}
}

View file

@ -1,120 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class UserSettings extends Settings
{
public float $credits_reward_after_verify_discord;
public float $credits_reward_after_verify_email;
public bool $force_discord_verification;
public bool $force_email_verification;
public float $initial_credits;
public int $initial_server_limit;
public float $min_credits_to_make_server;
public int $server_limit_after_irl_purchase;
public int $server_limit_after_verify_discord;
public int $server_limit_after_verify_email;
public bool $register_ip_check;
public bool $creation_enabled;
public static function group(): string
{
return 'user';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'credits_reward_after_verify_discord' => 'required|numeric',
'credits_reward_after_verify_email' => 'required|numeric',
'force_discord_verification' => 'nullable|boolean',
'force_email_verification' => 'nullable|boolean',
'initial_credits' => 'required|numeric',
'initial_server_limit' => 'required|numeric',
'min_credits_to_make_server' => 'required|numeric',
'server_limit_after_irl_purchase' => 'required|numeric',
'server_limit_after_verify_discord' => 'required|numeric',
'server_limit_after_verify_email' => 'required|numeric',
'register_ip_check' => 'nullable|boolean',
'creation_enabled' => 'nullable|boolean',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|boolean|number|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-user',
'credits_reward_after_verify_discord' => [
'label' => 'Credits Reward After Verify Discord',
'type' => 'number',
'description' => 'The amount of credits a user gets after verifying their discord account.',
],
'credits_reward_after_verify_email' => [
'label' => 'Credits Reward After Verify Email',
'type' => 'number',
'description' => 'The amount of credits a user gets after verifying their email.',
],
'force_discord_verification' => [
'label' => 'Force Discord Verification',
'type' => 'boolean',
'description' => 'Force users to verify their discord account.',
],
'force_email_verification' => [
'label' => 'Force Email Verification',
'type' => 'boolean',
'description' => 'Force users to verify their email.',
],
'initial_credits' => [
'label' => 'Initial Credits',
'type' => 'number',
'description' => 'The amount of credits a user gets when they register.',
],
'initial_server_limit' => [
'label' => 'Initial Server Limit',
'type' => 'number',
'description' => 'The amount of servers a user can create when they register.',
],
'min_credits_to_make_server' => [
'label' => 'Min Credits To Make Server',
'type' => 'number',
'description' => 'The minimum amount of credits a user needs to create a server.',
],
'server_limit_after_irl_purchase' => [
'label' => 'Server Limit After IRL Purchase',
'type' => 'number',
'description' => 'The amount of servers a user can create after they purchase a server.',
],
'server_limit_after_verify_discord' => [
'label' => 'Server Limit After Verify Discord',
'type' => 'number',
'description' => 'The amount of servers a user can create after they verify their discord account.',
],
'server_limit_after_verify_email' => [
'label' => 'Server Limit After Verify Email',
'type' => 'number',
'description' => 'The amount of servers a user can create after they verify their email.',
],
'register_ip_check' => [
'label' => 'Register IP Check',
'type' => 'boolean',
'description' => 'Check if the IP a user is registering from is already in use.',
],
'creation_enabled' => [
'label' => 'Creation Enabled',
'type' => 'boolean',
'description' => 'Whether or not users can create servers.',
],
];
}
}

View file

@ -1,103 +0,0 @@
<?php
namespace App\Settings;
use Spatie\LaravelSettings\Settings;
class WebsiteSettings extends Settings
{
public bool $show_imprint;
public bool $show_privacy;
public bool $show_tos;
public bool $useful_links_enabled;
public bool $enable_login_logo;
public ?string $seo_title;
public ?string $seo_description;
public bool $motd_enabled;
public ?string $motd_message;
public static function group(): string
{
return 'website';
}
/**
* Summary of validations array
* @return array<string, string>
*/
public static function getValidations()
{
return [
'motd_enabled' => 'nullable|boolean',
'motd_message' => 'nullable|string',
'show_imprint' => 'nullable|boolean',
'show_privacy' => 'nullable|boolean',
'show_tos' => 'nullable|boolean',
'useful_links_enabled' => 'nullable|boolean',
'enable_login_logo' => 'nullable|boolean',
'seo_title' => 'nullable|string',
'seo_description' => 'nullable|string',
];
}
/**
* Summary of optionTypes
* Only used for the settings page
* @return array<array<'type'|'label'|'description'|'options', string|array<string, string>>>
*/
public static function getOptionInputData()
{
return [
'category_icon' => 'fas fa-globe',
'motd_enabled' => [
'label' => 'Enable MOTD',
'type' => 'boolean',
'description' => 'Enable the MOTD (Message of the day) on the dashboard.',
],
'motd_message' => [
'label' => 'MOTD Message',
'type' => 'textarea',
'description' => 'The message of the day.',
],
'show_imprint' => [
'label' => 'Show Imprint',
'type' => 'boolean',
'description' => 'Show the imprint on the website.',
],
'show_privacy' => [
'label' => 'Show Privacy',
'type' => 'boolean',
'description' => 'Show the privacy on the website.',
],
'show_tos' => [
'label' => 'Show TOS',
'type' => 'boolean',
'description' => 'Show the TOS on the website.',
],
'useful_links_enabled' => [
'label' => 'Enable Useful Links',
'type' => 'boolean',
'description' => 'Enable the useful links on the dashboard.',
],
'seo_title' => [
'label' => 'SEO Title',
'type' => 'string',
'description' => 'The title of the website.',
],
'seo_description' => [
'label' => 'SEO Description',
'type' => 'string',
'description' => 'The description of the website.',
],
'enable_login_logo' => [
'label' => 'Enable Login Logo',
'type' => 'boolean',
'description' => 'Enable the logo on the login page.',
],
];
}
}

View file

@ -5,34 +5,32 @@ namespace App\Traits;
use App\Models\PartnerDiscount;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\Invoice;
use App\Notifications\InvoiceNotification;
use App\Settings\InvoiceSettings;
use Illuminate\Support\Facades\Storage;
use LaravelDaily\Invoices\Classes\Buyer;
use LaravelDaily\Invoices\Classes\InvoiceItem;
use LaravelDaily\Invoices\Classes\Party;
use LaravelDaily\Invoices\Invoice as DailyInvoice;
use LaravelDaily\Invoices\Invoice;
use Symfony\Component\Intl\Currencies;
trait Invoiceable
{
public function createInvoice(Payment $payment, ShopProduct $shopProduct, InvoiceSettings $invoice_settings)
public function createInvoice(Payment $payment, ShopProduct $shopProduct)
{
$user = $payment->user;
//create invoice
$lastInvoiceID = Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id");
$lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id");
$newInvoiceID = $lastInvoiceID + 1;
$logoPath = storage_path('app/public/logo.png');
$seller = new Party([
'name' => $invoice_settings->company_name,
'phone' => $invoice_settings->company_phone,
'address' => $invoice_settings->company_address,
'vat' => $invoice_settings->company_vat,
'name' => config("SETTINGS::INVOICE:COMPANY_NAME"),
'phone' => config("SETTINGS::INVOICE:COMPANY_PHONE"),
'address' => config("SETTINGS::INVOICE:COMPANY_ADDRESS"),
'vat' => config("SETTINGS::INVOICE:COMPANY_VAT"),
'custom_fields' => [
'E-Mail' => $invoice_settings->company_mail,
"Web" => $invoice_settings->company_website
'E-Mail' => config("SETTINGS::INVOICE:COMPANY_MAIL"),
"Web" => config("SETTINGS::INVOICE:COMPANY_WEBSITE")
],
]);
@ -53,7 +51,7 @@ trait Invoiceable
$notes = implode("<br>", $notes);
$invoice = DailyInvoice::make()
$invoice = Invoice::make()
->template('controlpanel')
->name(__("Invoice"))
->buyer($customer)
@ -66,7 +64,7 @@ trait Invoiceable
->series(now()->format('mY'))
->delimiter("-")
->sequence($newInvoiceID)
->serialNumberFormat($invoice_settings->prefix . '{DELIMITER}{SERIES}{SEQUENCE}')
->serialNumberFormat(config("SETTINGS::INVOICE:PREFIX") . '{DELIMITER}{SERIES}{SEQUENCE}')
->currencyCode(strtoupper($payment->currency_code))
->currencySymbol(Currencies::getSymbol(strtoupper($payment->currency_code)))
->notes($notes);
@ -80,7 +78,7 @@ trait Invoiceable
$invoice->render();
Storage::disk("local")->put("invoice/" . $user->id . "/" . now()->format('Y') . "/" . $invoice->filename, $invoice->output);
Invoice::create([
\App\Models\Invoice::create([
'invoice_user' => $user->id,
'invoice_name' => $invoice->getSerialNumber(),
'payment_id' => $payment->payment_id,

View file

@ -11,38 +11,36 @@
"php": "^8.1",
"ext-intl": "*",
"biscolab/laravel-recaptcha": "^5.4",
"doctrine/dbal": "^3.5.3",
"guzzlehttp/guzzle": "^7.5",
"hidehalo/nanoid-php": "^1.1.12",
"doctrine/dbal": "^3.1",
"guzzlehttp/guzzle": "^7.2",
"hidehalo/nanoid-php": "^1.1",
"kkomelin/laravel-translatable-string-exporter": "^1.18",
"laravel/framework": "^9.50.2",
"laravel/tinker": "^2.8",
"laravel/ui": "^3.4.6",
"laraveldaily/laravel-invoices": "^3.0.2",
"league/flysystem-aws-s3-v3": "^3.12.2",
"paypal/paypal-checkout-sdk": "^1.0.2",
"paypal/rest-api-sdk-php": "^1.14.0",
"predis/predis": "*",
"qirolab/laravel-themer": "^2.0.2",
"socialiteproviders/discord": "^4.1.2",
"spatie/laravel-activitylog": "^4.7.3",
"spatie/laravel-query-builder": "^5.1.2",
"spatie/laravel-settings": "^2.7",
"spatie/laravel-validation-rules": "^3.2.2",
"stripe/stripe-php": "^7.128",
"symfony/http-client": "^6.2.6",
"symfony/intl": "^6.2.5",
"symfony/mailgun-mailer": "^6.2.5",
"yajra/laravel-datatables-oracle": "^9.21.2"
"laravel/framework": "^9.46",
"laravel/tinker": "^2.7",
"laravel/ui": "^3.3",
"laraveldaily/laravel-invoices": "^3.0",
"league/flysystem-aws-s3-v3": "^3.0",
"paypal/paypal-checkout-sdk": "^1.0",
"paypal/rest-api-sdk-php": "^1.14",
"qirolab/laravel-themer": "^2.0",
"socialiteproviders/discord": "^4.1",
"spatie/laravel-activitylog": "^4.4",
"spatie/laravel-query-builder": "^5.0",
"spatie/laravel-validation-rules": "^3.2",
"stripe/stripe-php": "^7.107",
"symfony/http-client": "^6.2",
"symfony/intl": "^6.0",
"symfony/mailgun-mailer": "^6.2",
"yajra/laravel-datatables-oracle": "^9.19"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.7",
"fakerphp/faker": "^1.21",
"laravel/sail": "^1.19",
"mockery/mockery": "^1.5.1",
"nunomaduro/collision": "^6.4",
"phpunit/phpunit": "^9.6",
"spatie/laravel-ignition": "^1.6"
"barryvdh/laravel-debugbar": "^3.6",
"fakerphp/faker": "^1.9.1",
"laravel/sail": "^1.15",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^6.3",
"phpunit/phpunit": "^9.5.10",
"spatie/laravel-ignition": "^1.4"
},
"config": {
"optimize-autoloader": true,

1493
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Facade;
return [
'version' => '0.9.4',
'version' => '0.9.8',
/*
|--------------------------------------------------------------------------
@ -17,7 +17,7 @@ return [
|
*/
'name' => env('APP_NAME', 'Controlpanel.gg'),
'name' => env('APP_NAME', 'Ctrlpanel.gg'),
/*
|--------------------------------------------------------------------------
@ -210,8 +210,9 @@ return [
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
Yajra\DataTables\DataTablesServiceProvider::class,
KKomelin\TranslatableStringExporter\Providers\ExporterServiceProvider::class,
Biscolab\ReCaptcha\ReCaptchaServiceProvider::class,
],
/*

View file

@ -93,7 +93,7 @@ return [
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'ControlPanel'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
/*

View file

@ -1,111 +0,0 @@
<?php
use App\Helpers\ExtensionHelper;
use App\Settings\GeneralSettings;
use App\Settings\DiscordSettings;
use App\Settings\InvoiceSettings;
use App\Settings\LocaleSettings;
use App\Settings\MailSettings;
use App\Settings\PterodactylSettings;
use App\Settings\ReferralSettings;
use App\Settings\ServerSettings;
use App\Settings\UserSettings;
use App\Settings\WebsiteSettings;
use App\Settings\TicketSettings;
return [
/*
* Each settings class used in your application must be registered, you can
* put them (manually) here.
*/
'settings' => [
GeneralSettings::class,
DiscordSettings::class,
InvoiceSettings::class,
LocaleSettings::class,
MailSettings::class,
PterodactylSettings::class,
ReferralSettings::class,
ServerSettings::class,
UserSettings::class,
WebsiteSettings::class,
TicketSettings::class,
],
/*
* The path where the settings classes will be created.
*/
'setting_class_path' => app_path('Settings'),
/*
* In these directories settings migrations will be stored and ran when migrating. A settings
* migration created via the make:settings-migration command will be stored in the first path or
* a custom defined path when running the command.
*/
'migrations_paths' => [
database_path('settings'),
...ExtensionHelper::getAllExtensionMigrations()
],
/*
* When no repository was set for a settings class the following repository
* will be used for loading and saving settings.
*/
'default_repository' => 'database',
/*
* Settings will be stored and loaded from these repositories.
*/
'repositories' => [
'database' => [
'type' => Spatie\LaravelSettings\SettingsRepositories\DatabaseSettingsRepository::class,
'model' => null,
'table' => null,
'connection' => null,
],
'redis' => [
'type' => Spatie\LaravelSettings\SettingsRepositories\RedisSettingsRepository::class,
'connection' => null,
'prefix' => null,
],
],
/*
* The contents of settings classes can be cached through your application,
* settings will be stored within a provided Laravel store and can have an
* additional prefix.
*/
'cache' => [
'enabled' => env('SETTINGS_CACHE_ENABLED', true),
'store' => 'redis',
'prefix' => 'setting',
'ttl' => null,
],
/*
* These global casts will be automatically used whenever a property within
* your settings class isn't a default PHP type.
*/
'global_casts' => [
DateTimeInterface::class => Spatie\LaravelSettings\SettingsCasts\DateTimeInterfaceCast::class,
DateTimeZone::class => Spatie\LaravelSettings\SettingsCasts\DateTimeZoneCast::class,
// Spatie\DataTransferObject\DataTransferObject::class => Spatie\LaravelSettings\SettingsCasts\DtoCast::class,
Spatie\LaravelData\Data::class => Spatie\LaravelSettings\SettingsCasts\DataCast::class,
],
/*
* The package will look for settings in these paths and automatically
* register them.
*/
'auto_discover_settings' => [
app()->path(),
],
/*
* Automatically discovered settings classes can be cached so they don't
* need to be searched each time the application boots up.
*/
'discovered_settings_cache_path' => storage_path('app/laravel-settings'),
];

View file

@ -14,7 +14,7 @@ return [
*/
'paths' => [
resource_path('views'),
base_path('themes'),
],
/*

View file

@ -1,45 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// rename old settings table
Schema::table('settings', function (Blueprint $table) {
$table->rename('settings_old');
});
// create new settings table
Schema::create('settings', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->json('payload')->nullable();
$table->string('group')->index();
$table->boolean('locked');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('settings');
Schema::table('settings_old', function (Blueprint $table) {
$table->rename("settings");
});
}
};

View file

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

View file

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('pterodactyl_id')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->integer('pterodactyl_id')->nullable->change();
});
}
};

View file

@ -2,8 +2,8 @@
namespace Database\Seeders;
use Database\Seeders\Seeds\SettingsSeeder;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Schema;
class DatabaseSeeder extends Seeder
{
@ -14,6 +14,8 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
// Schema::dropIfExists('settings_old');
$this->call([
SettingsSeeder::class,
]);
}
}

View file

@ -0,0 +1,656 @@
<?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:INITIAL_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!',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER',
], [
'value' => '0',
'type' => 'integer',
'description' => 'The minimum amount of credits user has to have to create a server. Can be overridden by package limits.'
]);
//credits display name
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME',
], [
'value' => 'Credits',
'type' => 'string',
'description' => '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',
'description' => 'Enables or disables the invoice feature for payments.',
]);
//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 dashboard language.',
]);
//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' => 'en',
'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' => env('PAYPAL_SECRET', ''),
'type' => 'string',
'description' => 'Your PayPal Secret-Key (https://developer.paypal.com/docs/integration/direct/rest/).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID',
], [
'value' => env('PAYPAL_CLIENT_ID', ''),
'type' => 'string',
'description' => 'Your PayPal Client_ID.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET',
], [
'value' => env('PAYPAL_SANDBOX_SECRET', ''),
'type' => 'string',
'description' => 'Your PayPal SANDBOX Secret-Key used for testing.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID',
], [
'value' => env('PAYPAL_SANDBOX_CLIENT_ID', ''),
'type' => 'string',
'description' => 'Your PayPal SANDBOX Client-ID used for testing.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:STRIPE:SECRET',
], [
'value' => env('STRIPE_SECRET', ''),
'type' => 'string',
'description' => 'Your Stripe Secret-Key (https://dashboard.stripe.com/account/apikeys).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET',
], [
'value' => env('STRIPE_ENDPOINT_SECRET', ''),
'type' => 'string',
'description' => 'Your Stripe endpoint secret-key.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:STRIPE:TEST_SECRET',
], [
'value' => env('STRIPE_TEST_SECRET', ''),
'type' => 'string',
'description' => 'Your Stripe test secret-key.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET',
], [
'value' => env('STRIPE_ENDPOINT_TEST_SECRET', ''),
'type' => 'string',
'description' => 'Your Stripe endpoint test secret-key.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::PAYMENTS:STRIPE:METHODS',
], [
'value' => env('STRIPE_METHODS', 'card,sepa_debit'),
'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' => env('DISCORD_CLIENT_ID', ''),
'type' => 'string',
'description' => 'Discord API Credentials (https://discordapp.com/developers/applications/).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::DISCORD:CLIENT_SECRET',
], [
'value' => env('DISCORD_CLIENT_SECRET', ''),
'type' => 'string',
'description' => 'Discord API Credentials (https://discordapp.com/developers/applications/).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::DISCORD:BOT_TOKEN',
], [
'value' => env('DISCORD_BOT_TOKEN', ''),
'type' => 'string',
'description' => 'Discord API Credentials (https://discordapp.com/developers/applications/).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::DISCORD:GUILD_ID',
], [
'value' => env('DISCORD_GUILD_ID', ''),
'type' => 'string',
'description' => 'Discord API Credentials (https://discordapp.com/developers/applications/).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::DISCORD:ROLE_ID',
], [
'value' => env('DISCORD_ROLE_ID', ''),
'type' => 'string',
'description' => 'Discord role that will be assigned to users when they register.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::DISCORD:INVITE_URL',
], [
'value' => env('DISCORD_INVITE_URL', ''),
'type' => 'string',
'description' => 'The invite URL to your Discord Server.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:TOKEN',
], [
'value' => env('PTERODACTYL_TOKEN', ''),
'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' => env('PTERODACTYL_URL', ''),
'type' => 'string',
'description' => 'The URL to your Pterodactyl Panel. Must not end with a / ',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT',
], [
'value' => 200,
'type' => 'integer',
'description' => 'The Pterodactyl API perPage limit. It is necessary to set it higher than your server count.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MISC:PHPMYADMIN:URL',
], [
'value' => env('PHPMYADMIN_URL', ''),
'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' => env('RECAPTCHA_SITE_KEY', '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' => env('RECAPTCHA_SECRET_KEY', '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' => env('MAIL_MAILER', 'smtp'),
'type' => 'string',
'description' => 'Selected Mailer (smtp, mailgun, sendgrid, mailtrap).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MAIL:HOST',
], [
'value' => env('MAIL_HOST', 'localhost'),
'type' => 'string',
'description' => 'Mailer Host Address.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MAIL:PORT',
], [
'value' => env('MAIL_PORT', '25'),
'type' => 'string',
'description' => 'Mailer Server Port.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MAIL:USERNAME',
], [
'value' => env('MAIL_USERNAME', ''),
'type' => 'string',
'description' => 'Mailer Username.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MAIL:PASSWORD',
], [
'value' => env('MAIL_PASSWORD', ''),
'type' => 'string',
'description' => 'Mailer Password.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MAIL:ENCRYPTION',
], [
'value' => env('MAIL_ENCRYPTION', 'tls'),
'type' => 'string',
'description' => 'Mailer Encryption (tls, ssl).',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MAIL:FROM_ADDRESS',
], [
'value' => env('MAIL_FROM_ADDRESS', ''),
'type' => 'string',
'description' => 'Mailer From Address.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::MAIL:FROM_NAME',
], [
'value' => env('APP_NAME', 'Ctrlpanel'),
'type' => 'string',
'description' => 'Mailer From Name.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::REFERRAL::ENABLED',
], [
'value' => 'false',
'type' => 'string',
'description' => 'Enable or disable the referral system.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION',
], [
'value' => 'false',
'type' => 'string',
'description' => 'Whether referrals get percentage commission only on first purchase or on every purchase',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::REFERRAL::REWARD',
], [
'value' => 100,
'type' => 'integer',
'description' => 'Credit reward a user should receive when a user registers with his referral code',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::REFERRAL::ALLOWED',
], [
'value' => 'client',
'type' => 'string',
'description' => 'Who should be allowed to to use the referral code. all/client',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::REFERRAL:MODE',
], [
'value' => 'sign-up',
'type' => 'string',
'description' => 'Whether referrals get Credits on User-Registration or if a User buys credits',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::REFERRAL:PERCENTAGE',
], [
'value' => 100,
'type' => 'integer',
'description' => 'The Percentage value a referred user gets.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN',
], [
'value' => '',
'type' => 'string',
'description' => 'The Client API Key of an Pterodactyl Admin Account.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:ENABLE_UPGRADE',
], [
'value' => 'false',
'type' => 'boolean',
'description' => 'Enables the updgrade/downgrade feature for servers.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS',
], [
'value' => 'true',
'type' => 'boolean',
'description' => 'Enable creation of new servers',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:CREATION_OF_NEW_USERS',
], [
'value' => 'true',
'type' => 'boolean',
'description' => 'Enable creation of new users',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:SHOW_IMPRINT',
], [
'value' => "false",
'type' => 'boolean',
'description' => 'Enable imprint in footer.'
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:SHOW_PRIVACY',
], [
'value' => "false",
'type' => 'boolean',
'description' => 'Enable privacy policy in footer.'
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:SHOW_TOS',
], [
'value' => 'false',
'type' => 'boolean',
'description' => 'Enable Terms of Service in footer.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:ALERT_ENABLED',
], [
'value' => 'false',
'type' => 'boolean',
'description' => 'Enable Alerts on Homepage.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:ALERT_TYPE',
], [
'value' => 'dark',
'type' => 'text',
'description' => 'Changes the Color of the Alert.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:ALERT_MESSAGE',
], [
'value' => '',
'type' => 'text',
'description' => 'Changes the Content the Alert.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:THEME',
], [
'value' => 'default',
'type' => 'text',
'description' => 'Current active theme.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:USEFULLINKS_ENABLED',
], [
'value' => 'true',
'type' => 'boolean',
'description' => 'Enable Useful Links on Homepage.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:MOTD_ENABLED',
], [
'value' => 'true',
'type' => 'boolean',
'description' => 'Enable MOTD on Homepage.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:MOTD_MESSAGE',
], [
'value' => '<h1 style="text-align: center;"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://ctrlpanel.gg/img/controlpanel.png" alt="" width="200" height="200"><span style="font-size: 36pt;">Ctrlpanel.gg</span></h1>
<p><span style="font-size: 18pt;">Thank you for using our Software</span></p>
<p><span style="font-size: 18pt;">If you have any questions, make sure to join our <a href="https://discord.com/invite/4Y6HjD2uyU" target="_blank" rel="noopener">Discord</a></span></p>
<p><span style="font-size: 10pt;">(you can change this message in the <a href="admin/settings#system">Settings</a> )</span></p>',
'type' => 'text',
'description' => 'MOTD Message.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:SEO_TITLE',
], [
'value' => 'Ctrlpanel.gg',
'type' => 'text',
'description' => 'The SEO Title.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::SYSTEM:SEO_DESCRIPTION',
], [
'value' => 'Billing software for Pterodactyl Dashboard!',
'type' => 'text',
'description' => 'SEO Description.',
]);
Settings::firstOrCreate([
'key' => 'SETTINGS::TICKET:NOTIFY',
], [
'value' => 'all',
'type' => 'text',
'description' => 'Who will get a Email Notifcation on new Tickets.',
]);
}
}

View file

@ -1,139 +0,0 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateGeneralSettings extends SettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('general.store_enabled', true);
$this->migrator->add('general.credits_display_name', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME') : 'Credits');
$this->migrator->addEncrypted('general.recaptcha_site_key', $table_exists ? $this->getOldValue("SETTINGS::RECAPTCHA:SITE_KEY") : env('RECAPTCHA_SITE_KEY', '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'));
$this->migrator->addEncrypted('general.recaptcha_secret_key', $table_exists ? $this->getOldValue("SETTINGS::RECAPTCHA:SECRET_KEY") : env('RECAPTCHA_SECRET_KEY', '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'));
$this->migrator->add('general.recaptcha_enabled', $table_exists ? $this->getOldValue("SETTINGS::RECAPTCHA:ENABLED") : true);
$this->migrator->add('general.phpmyadmin_url', $table_exists ? $this->getOldValue("SETTINGS::MISC:PHPMYADMIN:URL") : env('PHPMYADMIN_URL', ''));
$this->migrator->add('general.alert_enabled', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:ALERT_ENABLED") : false);
$this->migrator->add('general.alert_type', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:ALERT_TYPE") : 'dark');
$this->migrator->add('general.alert_message', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:ALERT_MESSAGE") : '');
$this->migrator->add('general.theme', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:THEME") : 'default');
}
public function down(): void
{
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME',
'value' => $this->getNewValue('credits_display_name'),
'type' => 'string',
'description' => 'The name of the credits on the panel.'
],
[
'key' => 'SETTINGS::SYSTEM:ALERT_ENABLED',
'value' => $this->getNewValue('alert_enabled'),
'type' => 'boolean',
'description' => 'Enable the alert at the top of the panel.'
],
[
'key' => 'SETTINGS::SYSTEM:ALERT_TYPE',
'value' => $this->getNewValue('alert_type'),
'type' => 'string',
'description' => 'The type of alert to display.'
],
[
'key' => 'SETTINGS::SYSTEM:ALERT_MESSAGE',
'value' => $this->getNewValue('alert_message'),
'type' => 'text',
'description' => 'The message to display in the alert.'
],
[
'key' => 'SETTINGS::SYSTEM:THEME',
'value' => $this->getNewValue('theme'),
'type' => 'string',
'description' => 'The theme to use for the panel.'
],
[
'key' => 'SETTINGS::RECAPTCHA:SITE_KEY',
'value' => $this->getNewValue('recaptcha_site_key'),
'type' => 'string',
'description' => 'The site key for reCAPTCHA.'
],
[
'key' => 'SETTINGS::RECAPTCHA:SECRET_KEY',
'value' => $this->getNewValue('recaptcha_secret_key'),
'type' => 'string',
'description' => 'The secret key for reCAPTCHA.'
],
[
'key' => 'SETTINGS::RECAPTCHA:ENABLED',
'value' => $this->getNewValue('recaptcha_enabled'),
'type' => 'boolean',
'description' => 'Enable reCAPTCHA on the panel.'
],
[
'key' => 'SETTINGS::MISC:PHPMYADMIN:URL',
'value' => $this->getNewValue('phpmyadmin_url'),
'type' => 'string',
'description' => 'The URL to your phpMyAdmin installation.'
],
]);
$this->migrator->delete('general.store_enabled');
$this->migrator->delete('general.credits_display_name');
$this->migrator->delete('general.recaptcha_site_key');
$this->migrator->delete('general.recaptcha_secret_key');
$this->migrator->delete('general.recaptcha_enabled');
$this->migrator->delete('general.phpmyadmin_url');
$this->migrator->delete('general.alert_enabled');
$this->migrator->delete('general.alert_type');
$this->migrator->delete('general.alert_message');
$this->migrator->delete('general.theme');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'general'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
}
// remove the quotes from the string
if (substr($new_value->payload, 0, 1) === '"' && substr($new_value->payload, -1) === '"') {
return substr($new_value->payload, 1, -1);
}
return $new_value->payload;
}
public function getOldValue(string $key)
{
// Always get the first value of the key.
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
// Handle the old values to return without it being a string in all cases.
if ($old_value->type === "string" || $old_value->type === "text") {
if (is_null($old_value->value)) {
return '';
}
// Some values have the type string, but their values are boolean.
if ($old_value->value === "false" || $old_value->value === "true") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return $old_value->value;
}
if ($old_value->type === "boolean") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return filter_var($old_value->value, FILTER_VALIDATE_INT);
}
}

View file

@ -1,98 +0,0 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use Illuminate\Support\Facades\DB;
class CreatePterodactylSettings extends SettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->addEncrypted('pterodactyl.admin_token', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:PTERODACTYL:TOKEN') : env('PTERODACTYL_TOKEN', ''));
$this->migrator->addEncrypted('pterodactyl.user_token', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN') : '');
$this->migrator->add('pterodactyl.panel_url', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:PTERODACTYL:URL') : env('PTERODACTYL_URL', ''));
$this->migrator->add('pterodactyl.per_page_limit', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT') : 200);
}
public function down(): void
{
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:TOKEN',
'value' => $this->getNewValue('admin_token'),
'type' => 'string',
'description' => 'The admin token for the Pterodactyl panel.',
],
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN',
'value' => $this->getNewValue('user_token'),
'type' => 'string',
'description' => 'The user token for the Pterodactyl panel.',
],
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:URL',
'value' => $this->getNewValue('panel_url'),
'type' => 'string',
'description' => 'The URL for the Pterodactyl panel.',
],
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT',
'value' => $this->getNewValue('per_page_limit'),
'type' => 'integer',
'description' => 'The number of servers to show per page.',
],
]);
$this->migrator->delete('pterodactyl.admin_token');
$this->migrator->delete('pterodactyl.user_token');
$this->migrator->delete('pterodactyl.panel_url');
$this->migrator->delete('pterodactyl.per_page_limit');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'pterodactyl'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
}
// remove the quotes from the string
if (substr($new_value->payload, 0, 1) === '"' && substr($new_value->payload, -1) === '"') {
return substr($new_value->payload, 1, -1);
}
return $new_value->payload;
}
public function getOldValue(string $key)
{
// Always get the first value of the key.
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
// Handle the old values to return without it being a string in all cases.
if ($old_value->type === "string" || $old_value->type === "text") {
if (is_null($old_value->value)) {
return '';
}
// Some values have the type string, but their values are boolean.
if ($old_value->value === "false" || $old_value->value === "true") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return $old_value->value;
}
if ($old_value->type === "boolean") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return filter_var($old_value->value, FILTER_VALIDATE_INT);
}
}

View file

@ -1,136 +0,0 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateMailSettings extends SettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('mail.mail_host', $table_exists ? $this->getOldValue('SETTINGS::MAIL:HOST') : env('MAIL_HOST', 'localhost'));
$this->migrator->add('mail.mail_port', $table_exists ? $this->getOldValue('SETTINGS::MAIL:PORT') : env('MAIL_PORT', 25));
$this->migrator->add('mail.mail_username', $table_exists ? $this->getOldValue('SETTINGS::MAIL:USERNAME') : env('MAIL_USERNAME', ''));
$this->migrator->addEncrypted('mail.mail_password', $table_exists ? $this->getOldValue('SETTINGS::MAIL:PASSWORD') : env('MAIL_PASSWORD', ''));
$this->migrator->add('mail.mail_encryption', $table_exists ? $this->getOldValue('SETTINGS::MAIL:ENCRYPTION') : env('MAIL_ENCRYPTION', 'tls'));
$this->migrator->add('mail.mail_from_address', $table_exists ? $this->getOldValue('SETTINGS::MAIL:FROM_ADDRESS') : env('MAIL_FROM_ADDRESS', 'example@example.com'));
$this->migrator->add('mail.mail_from_name', $table_exists ? $this->getOldValue('SETTINGS::MAIL:FROM_NAME') : env('APP_NAME', 'ControlPanel.gg'));
$this->migrator->add('mail.mail_mailer', $table_exists ? $this->getOldValue('SETTINGS::MAIL:MAILER') : env('MAIL_MAILER', 'smtp'));
$this->migrator->add('mail.mail_enabled', true);
}
public function down(): void
{
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::MAIL:HOST',
'value' => $this->getNewValue('mail_host'),
'type' => 'string',
'description' => 'The host of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:PORT',
'value' => $this->getNewValue('mail_port'),
'type' => 'integer',
'description' => 'The port of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:USERNAME',
'value' => $this->getNewValue('mail_username'),
'type' => 'string',
'description' => 'The username of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:PASSWORD',
'value' => $this->getNewValue('mail_password'),
'type' => 'string',
'description' => 'The password of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:ENCRYPTION',
'value' => $this->getNewValue('mail_encryption'),
'type' => 'string',
'description' => 'The encryption of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:FROM_ADDRESS',
'value' => $this->getNewValue('mail_from_address'),
'type' => 'string',
'description' => 'The from address of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:FROM_NAME',
'value' => $this->getNewValue('mail_from_name'),
'type' => 'string',
'description' => 'The from name of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:MAILER',
'value' => $this->getNewValue('mail_mailer'),
'type' => 'string',
'description' => 'The mailer of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:ENABLED',
'value' => $this->getNewValue('mail_enabled'),
'type' => 'boolean',
'description' => 'The enabled state of the mail server.',
],
]);
$this->migrator->delete('mail.mail_host');
$this->migrator->delete('mail.mail_port');
$this->migrator->delete('mail.mail_username');
$this->migrator->delete('mail.mail_password');
$this->migrator->delete('mail.mail_encryption');
$this->migrator->delete('mail.mail_from_address');
$this->migrator->delete('mail.mail_from_name');
$this->migrator->delete('mail.mail_mailer');
$this->migrator->delete('mail.mail_enabled');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'mail'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
}
// remove the quotes from the string
if (substr($new_value->payload, 0, 1) === '"' && substr($new_value->payload, -1) === '"') {
return substr($new_value->payload, 1, -1);
}
return $new_value->payload;
}
public function getOldValue(string $key)
{
// Always get the first value of the key.
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
// Handle the old values to return without it being a string in all cases.
if ($old_value->type === "string" || $old_value->type === "text") {
if (is_null($old_value->value)) {
return '';
}
// Some values have the type string, but their values are boolean.
if ($old_value->value === "false" || $old_value->value === "true") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return $old_value->value;
}
if ($old_value->type === "boolean") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return filter_var($old_value->value, FILTER_VALIDATE_INT);
}
}

Some files were not shown because too many files have changed in this diff Show more