Merge branch 'development' into named-permissions

This commit is contained in:
Dennis 2024-05-18 01:14:12 +02:00 committed by GitHub
commit 68d923ae98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 1092 additions and 1232 deletions

View file

@ -4,25 +4,24 @@ APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://localhost
# List with timezones https://www.php.net/manual/en/timezones.php
APP_TIMEZONE=UTC
APP_TIMEZONE=UTC # List with timezones https://www.php.net/manual/en/timezones.php
### --- App Settings End --- ###
### --- DB Settings (required) --- ###
### --- Database Settings (required) --- ###
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=dashboard
DB_USERNAME=dashboarduser
DB_PASSWORD=
### --- DB Settings End --- ###
### --- Database Settings End --- ###
# Google Recaptcha API Credentials - https://www.google.com/recaptcha/admin - reCaptcha V2 (not v3)
### --- Google Recaptcha Settings --- ###
RECAPTCHA_SITE_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
RECAPTCHA_SECRET_KEY=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
### --- Google Recaptcha Settings End --- ###
# Mail Server Settings - (HOST -> SMTP Server)
### --- Mail Server Settings --- ###
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
@ -31,25 +30,22 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
### --- Mail Server Settings End --- ###
# Laravel Logging Settings - https://laravel.com/docs/5.7/logging - Not needed to be changed
### --- Logging Settings --- ###
LOG_CHANNEL=stack
LOG_LEVEL=debug
### --- Logging Settings End --- ###
# Do not change anything below this line
BROADCAST_DRIVER=log
### --- Cache and Queue Settings --- ###
CACHE_DRIVER=file
QUEUE_CONNECTION=database
SESSION_DRIVER=file
SESSION_LIFETIME=120
SETTINGS_CACHE_ENABLED=true
### --- Cache and Queue Settings End --- ###
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
### --- External Services Credentials --- ###
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
@ -59,9 +55,15 @@ PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
### --- External Services Credentials End --- ###
### --- Additional Configuration --- ###
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Settings Cache
SETTINGS_CACHE_ENABLED=true
### --- Additional Configuration End --- ###

5
.gitattributes vendored
View file

@ -1,5 +1,10 @@
# Automatically detect text files
* text=auto
# Vendored files for specific languages
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
# Ignore CHANGELOG.md when exporting
CHANGELOG.md export-ignore

22
.github/CODE_OF_CONDUCT.md vendored Normal file
View file

@ -0,0 +1,22 @@
## Code of Conduct
### 🤝 Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
### 🌟 Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting

34
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,34 @@
# Contributing Guidelines
Thank you for considering contributing to this repository! Before making a contribution, please take a moment to review the following guidelines.
## 🕵️‍♂️ Finding Tasks
Check the open issues to see if there's something you can contribute to. If you have an idea or encounter a bug that's not already listed, feel free to create a new issue and wait for feedback from the development team.
## 🤝 Code of Conduct
Please adhere to our [Code of Conduct](https://github.com/Ctrlpanel-gg/panel/blob/main/.github/CODE_OF_CONDUCT.md) in all your interactions with the project.
## 🌍 Localization
If you add any strings that are displayed on the frontend, please localize them using the following format:
```
"New String" -> {{ __('New String') }}
```
After adding localized strings, run the following command to generate localization files:
```cmd
php artisan translatable:export en
```
## 🚀 Pull Request Process
1. Give your pull request (PR) a clear and descriptive title that summarizes the changes.
2. The development team will review your code and provide feedback or approve/merge it when appropriate.
3. Ensure that your PR follows our Code of Conduct and coding style guidelines.
### 💻 Coding Style
We follow the PSR12 code standard for PHP.
Thank you for your contributions! 🎉

View file

@ -1,6 +1,6 @@
name: "\U0001F41B Bug report"
description: Create a report to help us improve
title: "[Bug]: "
title: "[Bug] "
labels: ["bug"]
body:
- type: textarea

37
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,37 @@
✨ Thank you for your contribution to our project! Before you submit your pull request, please take a moment to review and complete the following
⚠️ Please modify this template below and if not already done, read our pull request rules, Thanks!
Ensure that your pull request meets the following criteria:
- The code follows the style guidelines of this project
- You have performed a self-review of your own code and tested it
- You have commented your code, particularly in hard-to-understand areas
- Your changes generate no new warnings
Delete the above text and the following sections before submitting your pull request.
---
💡 **Description**
Briefly describe the purpose of your pull request, including any relevant issue numbers it addresses.
---
🛠️ **Type of Change**
Please select the appropriate type of change:
- Bug fix (non-breaking change which fixes an issue)
- User interface (UI) improvement
- New feature (non-breaking change which adds functionality)
- Breaking change (a fix or feature that would cause existing functionality to not work as expected)
- Other
- This change requires a documentation update
---
🖼️ **Screenshots (if applicable)**
If your pull request includes any visual changes, please provide screenshots here, do not use any external link.

17
.github/SECURITY.md vendored Normal file
View file

@ -0,0 +1,17 @@
# Security Policy
## Reporting a Vulnerability
🛡️ If you discover a security vulnerability, please report it to us via GitHub Advisories.
⚠️ Please refrain from using the public issue tracker or discussing the vulnerability in public channels, as it may exacerbate the issue.
## Acceptance of Bug Bounty Platforms
At this time, we only accept vulnerability reports through GitHub Advisories. We kindly ask that you do not submit reports via other third-party bug bounty platforms, as they will be disregarded.
## Supported Versions
### ControlPanel Versions
We strongly recommend using or upgrading to the latest version of ControlPanel to ensure you have access to the latest security fixes and enhancements.

35
.gitignore vendored
View file

@ -1,29 +1,34 @@
# Ignore dependencies and cache
/node_modules
/vendor
/storage/*.key
# Ignore public assets
/public/hot
/public/storage
/storage/*.key
/vendor
/storage/credit_deduction_log
storage/debugbar
/storage/app/public/logo.png
# Ignore environment files and configuration
.env
.env.testing
.env.backup
.idea
.env.dev
# Ignore testing and debug logs
.phpunit.result.cache
.editorconfig
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
yarn.lock
# Ignore Docker and Homestead configuration
docker-compose.override.yml
Homestead.json
Homestead.yaml
# Ignore gitignore itself
.gitignore
.env.dev
.env.testing
storage/invoices.zip
storage/app/public/logo.png
*vscode
- Kopie.env
# Ignore installation logs and locks
public/install/logs.txt
install.lock
public/install/logs/installer.log

View file

@ -1,43 +0,0 @@
# Contributing
When contributing to this repository, please go through the open issues to see if you can contribute to something. If you want to contribute something that is not in the issues you can make an issue and wait for response from the dev team.
Please note we have a code of conduct, please follow it in all your interactions with the project.
If you added any Strings which are displayed at the frontend please localize them (e.g. "New String" -> {{ __('New String') }}) and run the localization string generation:
```cmd
php artisan translatable:export en
```
## Pull request process
1. Give your PR a good descriptive title, so we can view immediately what the PR is about.
2. The dev team will look at your code and approve / merge when possible.
3. Make sure your PR follows our code of conduct and coding style.
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
### Coding Style
We are following the PSR12 code standard for PHP.
### Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting

106
README.md
View file

@ -1,67 +1,87 @@
### Features
- PayPal, Stripe and Mollie Integration
- Hourly, Weekely, Monthly, Quarterly and Annual billing Cycles
- Referral System
- Partner System
- Ticket System
- Upgrade/Downgrade Server Resources
- Store (credit system with hourly billing and invoices)
- Email Verification
- Audit Log
- Admin Dashboard
- User/Server Management
- Customisable server plans
- Vouchers
- Alert System
- Theme Support
- and so much more!
<div align="center">
<img src="https://ctrlpanel.gg/img/controlpanel.png" width="128" alt="" />
</div>
# CtrlPanel-gg
![ctrlpanel](https://user-images.githubusercontent.com/67899387/214684708-739c1d21-06e8-4dec-a4f1-81533a46cc7e.png)
CtrlPanel offers an easy-to-use and free billing solution for all starting and experienced hosting providers that seamlessly integrates with the Pterodactyl panel. It facilitates account creation, server ordering, and management, while offering addons, multiple payment methods, and customizable themes for a comprehensive solution.
![GitHub tag](https://img.shields.io/github/tag/ControlPanel-gg/dashboard)
![Overall Installations](https://img.shields.io/badge/Overall%20Installations-5000%2B-green)
![GitHub stars](https://img.shields.io/github/stars/ControlPanel-gg/dashboard)
[![Crowdin](https://badges.crowdin.net/controlpanelgg/localized.svg)](https://crowdin.com/project/controlpanelgg)
![License](https://img.shields.io/github/license/ControlPanel-gg/dashboard)
![Discord](https://img.shields.io/discord/787829714483019826)
![](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/ctrlpanel-gg/dashboard) ![](https://img.shields.io/github/forks/ctrlpanel-gg/panel) ![](https://img.shields.io/github/tag/ctrlpanel-gg/panel) [![Crowdin](https://badges.crowdin.com/project/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
![CtrlPanel](https://user-images.githubusercontent.com/67899387/214684708-739c1d21-06e8-4dec-a4f1-81533a46cc7e.png)
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 charges users depending on the billing cycle you chose for each server they have and suspends them if they run out of credits.
## ⭐ Features
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").
- Store (credit system with hourly billing and invoices)
- Many Popular Payment Methods
- Referral
- Partner
- Vouchers
- Ticket
- Account Management
- Admin Dashboard and Tools
- Addon Support
- and more!
### [Installation](https://ctrlpanel.gg/docs/intro "Installation")
## ⛰️ Live Demo
### [Updating](https://ctrlpanel.gg/docs/Installation/updating "Updating")
Try it!
### [Discord](https://discord.gg/4Y6HjD2uyU "Discord")
Demo Server: [demo.CtrlPanel.gg](https://demo.CtrlPanel.gg)
### [Contributing](https://ctrlpanel.gg/docs/Contributing/contributing "Contributing")
<!-- It is a temporary live demo; all data will be deleted. -->
### [Donating](https://ctrlpanel.gg/docs/Contributing/donating "Donating")
## 🔧 How to Install
### 🐳 Docker
Soon...
# Preview
<!-- ```bash
docker run ...
```
### Server Creation
![image](https://user-images.githubusercontent.com/67899387/214687234-d1ae58c0-5667-4e99-ac39-adeaabfcc7f2.png)
CtrlPanel is now running on [0.0.0.0:3001](http://0.0.0.0:3001). Don't forget to configure the database and Pterodactyl. [Documentation](documentation link here)
### Overview
![image](https://user-images.githubusercontent.com/67899387/214685859-03c8d9e1-c685-4a07-979f-df2e88ec3931.png)
more info: [Docker](docker documentation link here) -->
### Example server products
![image](https://user-images.githubusercontent.com/67899387/214686950-218e1ede-6a1f-4e53-b3f4-fe1abc371a9c.png)
### 💪🏻 Non-Docker
### Ticket System
![image](https://user-images.githubusercontent.com/67899387/214687123-0a3d0f8f-b53c-4b0d-869a-4d5df45f5184.png)
Requirements:
### Voucher System
![image](https://user-images.githubusercontent.com/67899387/214686578-ec9f0b0f-6047-4665-835f-70594b56dfd5.png)
- Platform
- Major Linux distros such as Debian, Ubuntu, CentOS, Fedora, and ArchLinux etc.
- Windows 10 (x64), Windows Server ...
### Partner System
![image](https://user-images.githubusercontent.com/67899387/214686321-36ba97a3-4181-4e60-9ba3-c9b318fe66a8.png)
Follow the [documentation](https://ctrlpanel.gg/docs/intro) to know how to install.
### MarketPlace
If you need more functionality, check out [Marketplace](https://market.ctrlpanel.gg/resources/).
## 🆙 How to Update
Please read: [Update Instructions](https://ctrlpanel.gg/docs/Installation/updating)
## 🆕 What's Next?
Roadmap: [CtrlPanel Roadmap](https://github.com/orgs/Ctrlpanel-gg/projects/1)
## 🗣️ Discussion / Ask for Help
For any general or technical questions, join CtrlPanel Discord for finding answers to your question. If you cannot find the information you need, feel free to ask.
## 🤝 Contributing
Please read [CONTRIBUTING.md](https://github.com/Ctrlpanel-gg/panel/blob/main/.github/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
Thanks to all contributors and supporters!
## ♥️ Donations
If you like what we do, please consider [supporting](https://ctrlpanel.gg/docs/Contributing/donating) us.

View file

@ -0,0 +1,63 @@
<?php
namespace App\Classes;
use Exception;
use Illuminate\Support\Facades\DB;
use Spatie\LaravelSettings\Migrations\SettingsMigration;
abstract class LegacySettingsMigration extends SettingsMigration
{
public function getNewValue(string $name, string $group)
{
$new_value = DB::table('settings')->where([['group', '=', $group], ['name', '=', $name]])->get(['payload'])->first();
if (is_null($new_value) || is_null($new_value->payload)) {
return null;
}
// 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;
}
/**
* Get the old value from the settings_old table.
* @param string $key The key to get the value from table.
* @param int|string|bool|null $default The default value to return if the value is null. If value is not nullable, a default must be provided.
*/
public function getOldValue(string $key, int|string|bool|null $default = null)
{
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
if (is_null($old_value) || is_null($old_value->value)) {
return $default;
}
switch ($old_value->type) {
case 'string':
case 'text':
// Edgecase: The value is a boolean, but it's stored as a string.
if ($old_value->value === "false" || $old_value->value === "true") {
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
}
return $old_value->value;
case 'boolean':
return filter_var($old_value->value, FILTER_VALIDATE_BOOL);
case 'integer':
return filter_var($old_value->value, FILTER_VALIDATE_INT);
default:
throw new Exception("Unknown type: {$old_value->type}");
}
}
}

View file

@ -6,7 +6,7 @@ class CreateMollieSettings extends SettingsMigration
{
public function up(): void
{
$this->migrator->add('mollie.api_key', null);
$this->migrator->addEncrypted('mollie.api_key', null);
$this->migrator->add('mollie.enabled', false);
}

View file

@ -11,10 +11,10 @@ class CreatePayPalSettings extends SettingsMigration
$table_exists = DB::table('settings_old')->exists();
$this->migrator->add('paypal.client_id', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID') : null);
$this->migrator->add('paypal.client_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:SECRET') : null);
$this->migrator->add('paypal.sandbox_client_id', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID') : null);
$this->migrator->add('paypal.sandbox_client_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET') : null);
$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);
}

View file

@ -9,9 +9,9 @@ class CreateStripeSettings extends SettingsMigration
{
$table_exists = DB::table('settings_old')->exists();
$this->migrator->add('stripe.secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:SECRET') : null);
$this->migrator->addEncrypted('stripe.secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:SECRET') : null);
$this->migrator->add('stripe.endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') : null);
$this->migrator->add('stripe.test_secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') : null);
$this->migrator->addEncrypted('stripe.test_secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') : null);
$this->migrator->add('stripe.test_endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET') : null);
$this->migrator->add('stripe.enabled', false);
}

View file

@ -185,29 +185,32 @@ class CouponController extends Controller
public function dataTable()
{
$query = Coupon::query();
$query = Coupon::selectRaw('
coupons.*,
CASE
WHEN coupons.uses >= coupons.max_uses THEN "USES_LIMIT_REACHED"
WHEN coupons.expires_at IS NOT NULL AND coupons.expires_at < NOW() THEN "EXPIRED"
ELSE "VALID"
END as derived_status
');
return datatables($query)
->addColumn('actions', function(Coupon $coupon) {
return '
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.coupons.edit', $coupon->id).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.coupons.edit', $coupon->id).'" class="mr-1 btn btn-sm btn-info"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.coupons.destroy', $coupon->id).'">
'.csrf_field().'
'.method_field('DELETE').'
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
';
})
->addColumn('status', function(Coupon $coupon) {
$color = 'success';
$status = $coupon->getStatus();
->addColumn('status', function (Coupon $coupon) {
$color = ($coupon->derived_status == 'VALID') ? 'success' : 'danger';
$status = str_replace('_', ' ', $coupon->derived_status);
if ($status != __('VALID')) {
$color = 'danger';
}
return '<span class="badge badge-'.$color.'">'.str_replace('_', ' ', $status).'</span>';
return '<span class="badge badge-'.$color.'">'.$status.'</span>';
})
->editColumn('uses', function (Coupon $coupon) {
return "{$coupon->uses} / {$coupon->max_uses}";
@ -232,6 +235,7 @@ class CouponController extends Controller
->editColumn('code', function (Coupon $coupon) {
return "<code>{$coupon->code}</code>";
})
->orderColumn('status', 'derived_status $1')
->rawColumns(['actions', 'code', 'status'])
->make();
}

View file

@ -122,11 +122,11 @@ class PartnerController extends Controller
return datatables($query)
->addColumn('actions', function (PartnerDiscount $partner) {
return '
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.partners.edit', $partner->id).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.partners.edit', $partner->id).'" class="mr-1 btn btn-sm btn-info"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.partners.destroy', $partner->id).'">
'.csrf_field().'
'.method_field('DELETE').'
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
';
})
@ -145,6 +145,7 @@ class PartnerController extends Controller
->editColumn('referral_system_commission', function (PartnerDiscount $partner, ReferralSettings $referral_settings) {
return $partner->referral_system_commission >= 0 ? $partner->referral_system_commission . '%' : __('Default') . ' ('.$referral_settings->percentage . '%)';
})
->orderColumn('user', 'user_id $1')
->rawColumns(['user', 'actions'])
->make();
}

View file

@ -143,14 +143,19 @@ class PaymentController extends Controller
$subtotal = $shopProduct->price;
// Apply Coupon
$isCouponValid = $this->isCouponValid($couponCode, $user, $shopProduct->id);
if ($isCouponValid) {
$subtotal = $this->applyCoupon($couponCode, $subtotal);
if ($couponCode) {
if ($this->isCouponValid($couponCode, $user, $shopProduct->id)) {
$subtotal = $this->applyCoupon($couponCode, $subtotal);
}
}
// Apply Partner Discount
$subtotal = $subtotal - ($subtotal * $discount / 100);
if ($subtotal <= 0) {
if ($couponCode) {
event(new CouponUsedEvent($couponCode));
}
return $this->handleFreeProduct($shopProduct);
}
@ -175,7 +180,10 @@ class PaymentController extends Controller
$paymentGatewayExtension = ExtensionHelper::getExtensionClass($paymentGateway);
$redirectUrl = $paymentGatewayExtension::getRedirectUrl($payment, $shopProduct, $totalPriceString);
event(new CouponUsedEvent($couponCode));
if ($couponCode) {
event(new CouponUsedEvent($couponCode));
}
} catch (Exception $e) {
Log::error($e->getMessage());
return redirect()->route('store.index')->with('error', __('Oops, something went wrong! Please try again later.'));

View file

@ -223,17 +223,18 @@ class ProductController extends Controller
public function dataTable()
{
$query = Product::with(['servers']);
return datatables($query)
->addColumn('actions', function (Product $product) {
return '
<a data-content="'.__('Show').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.products.show', $product->id).'" class="btn btn-sm text-white btn-warning mr-1"><i class="fas fa-eye"></i></a>
<a data-content="'.__('Clone').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.products.clone', $product->id).'" class="btn btn-sm text-white btn-primary mr-1"><i class="fas fa-clone"></i></a>
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.products.edit', $product->id).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<a data-content="'.__('Show').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.products.show', $product->id).'" class="mr-1 text-white btn btn-sm btn-warning"><i class="fas fa-eye"></i></a>
<a data-content="'.__('Clone').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.products.clone', $product->id).'" class="mr-1 text-white btn btn-sm btn-primary"><i class="fas fa-clone"></i></a>
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.products.edit', $product->id).'" class="mr-1 btn btn-sm btn-info"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.products.destroy', $product->id).'">
'.csrf_field().'
'.method_field('DELETE').'
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
';
})
@ -247,7 +248,7 @@ class ProductController extends Controller
->addColumn('eggs', function (Product $product) {
return $product->eggs()->count();
})
->addColumn('disabled', function (Product $product) {
->editColumn('disabled', function (Product $product) {
$checked = $product->disabled == false ? 'checked' : '';
return '
@ -264,7 +265,7 @@ class ProductController extends Controller
->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('oom_killer', function (Product $product, UserSettings $user_settings) {
->editColumn('oom_killer', function (Product $product) {
return $product->oom_killer ? __("enabled") : __("disabled");
})
->editColumn('created_at', function (Product $product) {

View file

@ -156,16 +156,16 @@ class ShopProductController extends Controller
return datatables($query)
->addColumn('actions', function (ShopProduct $shopProduct) {
return '
<a data-content="' . __('Edit') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.store.edit', $shopProduct->id) . '" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<a data-content="' . __('Edit') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.store.edit', $shopProduct->id) . '" class="mr-1 btn btn-sm btn-info"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.store.destroy', $shopProduct->id) . '">
' . csrf_field() . '
' . method_field('DELETE') . '
<button data-content="' . __('Delete') . '" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
<button data-content="' . __('Delete') . '" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
';
})
->addColumn('disabled', function (ShopProduct $shopProduct) {
->editColumn('disabled', function (ShopProduct $shopProduct) {
$checked = $shopProduct->disabled == false ? 'checked' : '';
return '

View file

@ -121,11 +121,12 @@ class TicketsController extends Controller
public function dataTable()
{
$query = Ticket::query();
$query = Ticket::leftJoin('ticket_categories', 'tickets.ticketcategory_id', '=', 'ticket_categories.id')
->select(['tickets.*', 'ticket_categories.name as category_name']);
return datatables($query)
->addColumn('category', function (Ticket $tickets) {
return $tickets->ticketcategory->name;
->addColumn('category', function (Ticket $ticket) {
return $ticket->category_name;
})
->editColumn('title', function (Ticket $tickets) {
return '<a class="text-info" href="'.route('admin.ticket.show', ['ticket_id' => $tickets->ticket_id]).'">'.'#'.$tickets->ticket_id.' - '.htmlspecialchars($tickets->title).'</a>';
@ -139,16 +140,16 @@ class TicketsController extends Controller
$statusButtonText = ($tickets->status == "Closed") ? __('Reopen') : __('Close');
return '
<a data-content="'.__('View').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.ticket.show', ['ticket_id' => $tickets->ticket_id]).'" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-eye"></i></a>
<a data-content="'.__('View').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.ticket.show', ['ticket_id' => $tickets->ticket_id]).'" class="mr-1 text-white btn btn-sm btn-info"><i class="fas fa-eye"></i></a>
<form class="d-inline" method="post" action="'.route('admin.ticket.changeStatus', ['ticket_id' => $tickets->ticket_id]).'">
'.csrf_field().'
'.method_field('POST').'
<button data-content="'.__($statusButtonText).'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm text-white '.$statusButtonColor.' mr-1"><i class="fas '.$statusButtonIcon.'"></i></button>
<button data-content="'.__($statusButtonText).'" data-toggle="popover" data-trigger="hover" data-placement="top" class="text-white btn btn-sm '.$statusButtonColor.' mr-1"><i class="fas '.$statusButtonIcon.'"></i></button>
</form>
<form class="d-inline" method="post" action="'.route('admin.ticket.delete', ['ticket_id' => $tickets->ticket_id]).'">
'.csrf_field().'
'.method_field('POST').'
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm text-white btn-danger mr-1"><i class="fas fa-trash"></i></button>
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 text-white btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
';
})
@ -178,7 +179,8 @@ class TicketsController extends Controller
return ['display' => $tickets->updated_at ? $tickets->updated_at->diffForHumans() : '',
'raw' => $tickets->updated_at ? strtotime($tickets->updated_at) : ''];
})
->rawColumns(['category', 'title', 'user_id', 'status', 'priority', 'updated_at', 'actions'])
->orderColumn('category', 'category_name $1')
->rawColumns(['title', 'user_id', 'status', 'priority', 'updated_at', 'actions'])
->make(true);
}
@ -279,12 +281,12 @@ class TicketsController extends Controller
<form class="d-inline" method="post" action="'.route('admin.ticket.blacklist.change', ['id' => $blacklist->id]).'">
'.csrf_field().'
'.method_field('POST').'
<button data-content="'.__('Change Status').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm text-white btn-warning mr-1"><i class="fas fa-sync-alt"></i></button>
<button data-content="'.__('Change Status').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 text-white btn btn-sm btn-warning"><i class="fas fa-sync-alt"></i></button>
</form>
<form class="d-inline" method="post" action="'.route('admin.ticket.blacklist.delete', ['id' => $blacklist->id]).'">
'.csrf_field().'
'.method_field('POST').'
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm text-white btn-danger mr-1"><i class="fas fa-trash"></i></button>
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 text-white btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
';
})

View file

@ -148,7 +148,7 @@ class UserController extends Controller
*/
public function update(Request $request, User $user)
{
$request->validate([
$data = $request->validate([
'name' => 'required|string|min:4|max:30',
'pterodactyl_id' => "required|numeric|unique:users,pterodactyl_id,{$user->id}",
'email' => 'required|string|email',
@ -179,23 +179,23 @@ class UserController extends Controller
]);
}
if($this->can(self::CHANGE_USERNAME_PERMISSION)){
$user->name = $request->name;
}
if($this->can(self::CHANGE_CREDITS_PERMISSION)){
$user->credits = $request->credits;
}
if($this->can(self::CHANGE_PTERO_PERMISSION)){
$user->pterodactyl_id = $request->pterodactyl_id;
}
if($this->can(self::CHANGE_REFERAL_PERMISSION)){
$user->referral_code = $request->referral_code;
}
if($this->can(self::CHANGE_EMAIL_PERMISSION)){
$user->email = $request->email;
}
// if($this->can(self::CHANGE_USERNAME_PERMISSION)){
// $user->name = $request->name;
// }
// if($this->can(self::CHANGE_CREDITS_PERMISSION)){
// $user->credits = $request->credits;
// }
// if($this->can(self::CHANGE_PTERO_PERMISSION)){
// $user->pterodactyl_id = $request->pterodactyl_id;
// }
// if($this->can(self::CHANGE_REFERAL_PERMISSION)){
// $user->referral_code = $request->referral_code;
// }
// if($this->can(self::CHANGE_EMAIL_PERMISSION)){
// $user->email = $request->email;
// }
$user->save();
$user->update($data);
event(new UserUpdateCreditsEvent($user));
@ -316,7 +316,8 @@ class UserController extends Controller
->line(new HtmlString($data['content']));
}
$all = $data['all'] ?? false;
if(!$data["roles"]){
$roles = $data['roles'] ?? false;
if(!$roles){
$users = $all ? User::all() : User::whereIn('id', $data['users'])->get();
} else{
$users = User::role($data["roles"])->get();
@ -340,6 +341,10 @@ class UserController extends Controller
{
$this->checkPermission(self::SUSPEND_PERMISSION);
if (Auth::user()->id === $user->id) {
return redirect()->back()->with('error', __('You can not suspend yourself!'));
}
try {
!$user->isSuspended() ? $user->suspend() : $user->unSuspend();
} catch (Exception $exception) {
@ -354,10 +359,12 @@ class UserController extends Controller
*/
public function dataTable(Request $request)
{
$query = User::with('discordUser')->withCount('servers');
// manually count referrals in user_referrals table
$query->selectRaw('users.*, (SELECT COUNT(*) FROM user_referrals WHERE user_referrals.referral_id = users.id) as referrals_count');
$query = User::query()
->withCount('servers')
->leftJoin('model_has_roles', 'users.id', '=', 'model_has_roles.model_id')
->leftJoin('roles', 'model_has_roles.role_id', '=', 'roles.id')
->selectRaw('users.*, roles.name as role_name, (SELECT COUNT(*) FROM user_referrals WHERE user_referrals.referral_id = users.id) as referrals_count')
->where('model_has_roles.model_type', User::class);
return datatables($query)
->addColumn('avatar', function (User $user) {
@ -408,6 +415,7 @@ class UserController extends Controller
->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>';
})
->orderColumn('role', 'role_name $1')
->rawColumns(['avatar', 'name', 'credits', 'role', 'usage', 'actions'])
->make();
}

View file

@ -203,7 +203,7 @@ class VoucherController extends Controller
return '<a class="text-info" target="_blank" href="'.route('admin.users.show', $user->id).'">'.$user->name.'</a>';
})
->addColumn('credits', function (User $user) {
return '<i class="fas fa-coins mr-2"></i> '.$user->credits();
return '<i class="mr-2 fas fa-coins"></i> '.$user->credits();
})
->addColumn('last_seen', function (User $user) {
return $user->last_seen ? $user->last_seen->diffForHumans() : '';
@ -214,28 +214,33 @@ class VoucherController extends Controller
public function dataTable()
{
$query = Voucher::query();
$query = Voucher::selectRaw('
vouchers.*,
CASE
WHEN (SELECT COUNT(*) FROM user_voucher WHERE user_voucher.voucher_id = vouchers.id) >= vouchers.uses THEN "USES_LIMIT_REACHED"
WHEN vouchers.expires_at IS NOT NULL AND vouchers.expires_at < NOW() THEN "EXPIRED"
ELSE "VALID"
END as derived_status
');
return datatables($query)
->addColumn('actions', function (Voucher $voucher) {
return '
<a data-content="'.__('Users').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.vouchers.users', $voucher->id).'" class="btn btn-sm btn-primary mr-1"><i class="fas fa-users"></i></a>
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.vouchers.edit', $voucher->id).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<a data-content="'.__('Users').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.vouchers.users', $voucher->id).'" class="mr-1 btn btn-sm btn-primary"><i class="fas fa-users"></i></a>
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.vouchers.edit', $voucher->id).'" class="mr-1 btn btn-sm btn-info"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.vouchers.destroy', $voucher->id).'">
'.csrf_field().'
'.method_field('DELETE').'
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
<button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="mr-1 btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
';
})
->addColumn('status', function (Voucher $voucher) {
$color = 'success';
if ($voucher->getStatus() != __('VALID')) {
$color = 'danger';
}
$color = ($voucher->derived_status == 'VALID') ? 'success' : 'danger';
$status = str_replace('_', ' ', $voucher->derived_status);
return '<span class="badge badge-'.$color.'">'.$voucher->getStatus().'</span>';
return '<span class="badge badge-'.$color.'">'.$status.'</span>';
})
->editColumn('uses', function (Voucher $voucher) {
return "{$voucher->used} / {$voucher->uses}";
@ -253,6 +258,7 @@ class VoucherController extends Controller
->editColumn('code', function (Voucher $voucher) {
return "<code>{$voucher->code}</code>";
})
->orderColumn('status', 'derived_status $1')
->rawColumns(['actions', 'code', 'status'])
->make();
}

View file

@ -22,6 +22,7 @@ use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Spatie\Permission\Models\Role;
class RegisterController extends Controller
{
@ -139,7 +140,7 @@ class RegisterController extends Controller
]);
$user->syncRoles(4);
$user->syncRoles(Role::findByName('User'));
$response = $this->pterodactyl->application->post('/application/users', [
'external_id' => null,
@ -151,15 +152,11 @@ class RegisterController extends Controller
'root_admin' => false,
'language' => 'en',
]);
$user->update([
'pterodactyl_id' => $response->json()['attributes']['id'],
]);
if ($response->failed()) {
$user->delete();
Log::error('Pterodactyl Registration Error: ' . $response->json()['errors'][0]['detail']);

View file

@ -8,8 +8,6 @@ use App\Models\Pterodactyl\Nest;
use App\Models\Pterodactyl\Node;
use App\Models\Product;
use App\Models\Server;
use App\Models\User;
use App\Models\Settings;
use App\Notifications\ServerCreationError;
use Carbon\Carbon;
use App\Settings\UserSettings;
@ -18,7 +16,6 @@ use App\Settings\PterodactylSettings;
use App\Classes\PterodactylClient;
use App\Settings\GeneralSettings;
use Exception;
use GuzzleHttp\Promise\Create;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Client\Response;
use Illuminate\Http\RedirectResponse;

View file

@ -49,7 +49,7 @@ class UserPayment
$shopProduct = $event->shopProduct;
// only update user if payment is paid
if ($event->payment->status != PaymentStatus::PAID) {
if ($event->payment->status != PaymentStatus::PAID->value) {
return;
}

View file

@ -7,7 +7,6 @@ use App\Settings\UserSettings;
class Verified
{
private $server_limit_after_verify_email;
private $credits_reward_after_verify_email;
/**
@ -29,9 +28,10 @@ class Verified
*/
public function handle($event)
{
if (! $event->user->email_verified_reward) {
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);
$event->user->update(['email_verified_reward' => true]);
}
}
}

View file

@ -66,6 +66,7 @@ class User extends Authenticatable implements MustVerifyEmail
'avatar',
'suspended',
'referral_code',
'email_verified_reward',
];
/**
@ -88,6 +89,7 @@ class User extends Authenticatable implements MustVerifyEmail
'last_seen' => 'datetime',
'credits' => 'float',
'server_limit' => 'float',
'email_verified_reward' => 'boolean'
];
public function __construct()
@ -280,16 +282,15 @@ class User extends Authenticatable implements MustVerifyEmail
public function verifyEmail()
{
$this->forceFill([
'email_verified_at' => now(),
'email_verified_at' => now()
])->save();
}
public function reVerifyEmail()
{
$this->forceFill([
'email_verified_at' => null,
'email_verified_at' => null
])->save();
}

View file

@ -2,7 +2,6 @@
namespace App\Providers;
use App\Extensions\PaymentGateways\PayPal\PayPalSettings;
use App\Models\UsefulLink;
use App\Settings\GeneralSettings;
use App\Settings\MailSettings;
@ -89,20 +88,23 @@ class AppServiceProvider extends ServiceProvider
Log::error("Couldnt find useful_links. Probably the installation is not completet. " . $e);
}
$generalSettings = $this->app->make(GeneralSettings::class);
if (!file_exists(base_path('themes') . "/" . $generalSettings->theme)) {
$generalSettings->theme = "default";
try {
$generalSettings = $this->app->make(GeneralSettings::class);
if (!file_exists(base_path('themes') . "/" . $generalSettings->theme)) {
$generalSettings->theme = "default";
}
if ($generalSettings->theme && $generalSettings->theme !== config('theme.active')) {
Theme::set($generalSettings->theme, "default");
} else {
Theme::set("default", "default");
}
$settings = $this->app->make(MailSettings::class);
$settings->setConfig();
} catch (Exception $e) {
Log::error("Couldnt load Settings. Probably the installation is not completet. " . $e);
}
if ($generalSettings->theme && $generalSettings->theme !== config('theme.active')) {
Theme::set($generalSettings->theme, "default");
} else {
Theme::set("default", "default");
}
$settings = $this->app->make(MailSettings::class);
$settings->setConfig();
}
}

View file

@ -9,9 +9,10 @@ use App\Listeners\CouponUsed;
use App\Listeners\CreateInvoice;
use App\Listeners\UnsuspendServers;
use App\Listeners\UserPayment;
use App\Listeners\Verified;
use App\Listeners\Verified as ListenerVerified;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use SocialiteProviders\Manager\SocialiteWasCalled;
@ -40,8 +41,8 @@ class EventServiceProvider extends ServiceProvider
// ... other providers
'SocialiteProviders\\Discord\\DiscordExtendSocialite@handle',
],
'Illuminate\Auth\Events\Verified' => [
Verified::class,
Verified::class => [
ListenerVerified::class,
],
];

View file

@ -18,8 +18,6 @@ class DiscordSettings extends Settings
return 'discord';
}
/**
* Summary of validations array
* @return array<string, string>

View file

@ -20,7 +20,12 @@ class MailSettings extends Settings
return 'mail';
}
public static function encrypted(): array
{
return [
'mail_password',
];
}
public function setConfig()
{
@ -80,12 +85,17 @@ class MailSettings extends Settings
],
'mail_password' => [
'label' => 'Mail Password',
'type' => 'string',
'type' => 'password',
'description' => 'The password of your mail server.',
],
'mail_encryption' => [
'label' => 'Mail Encryption',
'type' => 'string',
'type' => 'select',
'options' => [
'null' => 'None',
'tls' => 'TLS',
'ssl' => 'SSL'
],
'description' => 'The encryption of your mail server.',
],
'mail_from_address' => [

View file

@ -16,7 +16,13 @@ class PterodactylSettings extends Settings
return 'pterodactyl';
}
public static function encrypted(): array
{
return [
'admin_token',
'user_token',
];
}
/**
* Get url with ensured ending backslash

View file

@ -95,27 +95,6 @@ trait Coupon
return true;
}
public function calcDiscount($productPrice, stdClass $data)
{
if ($data->isValid) {
if ($data->couponType === 'percentage') {
return $productPrice - ($productPrice * $data->couponValue / 100);
}
if ($data->couponType === 'amount') {
// There is no discount if the value of the coupon is greater than or equal to the value of the product.
if ($data->couponValue >= $productPrice) {
return $productPrice;
}
}
return $productPrice - $data->couponValue;
}
return $productPrice;
}
public function applyCoupon(string $couponCode, float $price)
{
$coupon = CouponModel::where('code', $couponCode)->first();

View file

View file

@ -32,7 +32,7 @@ return [
UserSettings::class,
WebsiteSettings::class,
TicketSettings::class,
CouponSettings::class,
CouponSettings::class,
],
/*

View file

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

View file

@ -26,7 +26,7 @@ class UpdateUserCreditsDatatype extends Migration
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->decimal('price', ['11', '2'])->change();
$table->decimal('credits', ['11', '2'])->change();
});
}
}

View file

@ -1,9 +1,9 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateGeneralSettings extends SettingsMigration
class CreateGeneralSettings extends LegacySettingsMigration
{
public function up(): void
{
@ -11,15 +11,15 @@ class CreateGeneralSettings extends SettingsMigration
// 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->add('general.credits_display_name', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME', 'Credits') : 'Credits');
$this->migrator->add('general.recaptcha_site_key', $table_exists ? $this->getOldValue("SETTINGS::RECAPTCHA:SITE_KEY") : env('RECAPTCHA_SITE_KEY', '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'));
$this->migrator->add('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');
$this->migrator->add('general.recaptcha_enabled', $table_exists ? $this->getOldValue("SETTINGS::RECAPTCHA:ENABLED", false) : false);
$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) : false);
$this->migrator->add('general.alert_type', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:ALERT_TYPE", 'dark') : 'dark');
$this->migrator->add('general.alert_message', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:ALERT_MESSAGE") : null);
$this->migrator->add('general.theme', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:THEME", 'default') : 'default');
}
public function down(): void
@ -27,113 +27,73 @@ class CreateGeneralSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME',
'value' => $this->getNewValue('credits_display_name'),
'value' => $this->getNewValue('credits_display_name', 'general'),
'type' => 'string',
'description' => 'The name of the credits on the panel.'
],
[
'key' => 'SETTINGS::SYSTEM:ALERT_ENABLED',
'value' => $this->getNewValue('alert_enabled'),
'value' => $this->getNewValue('alert_enabled', 'general'),
'type' => 'boolean',
'description' => 'Enable the alert at the top of the panel.'
],
[
'key' => 'SETTINGS::SYSTEM:ALERT_TYPE',
'value' => $this->getNewValue('alert_type'),
'value' => $this->getNewValue('alert_type', 'general'),
'type' => 'string',
'description' => 'The type of alert to display.'
],
[
'key' => 'SETTINGS::SYSTEM:ALERT_MESSAGE',
'value' => $this->getNewValue('alert_message'),
'value' => $this->getNewValue('alert_message', 'general'),
'type' => 'text',
'description' => 'The message to display in the alert.'
],
[
'key' => 'SETTINGS::SYSTEM:THEME',
'value' => $this->getNewValue('theme'),
'value' => $this->getNewValue('theme', 'general'),
'type' => 'string',
'description' => 'The theme to use for the panel.'
],
[
'key' => 'SETTINGS::RECAPTCHA:SITE_KEY',
'value' => $this->getNewValue('recaptcha_site_key'),
'value' => $this->getNewValue('recaptcha_site_key', 'general'),
'type' => 'string',
'description' => 'The site key for reCAPTCHA.'
],
[
'key' => 'SETTINGS::RECAPTCHA:SECRET_KEY',
'value' => $this->getNewValue('recaptcha_secret_key'),
'value' => $this->getNewValue('recaptcha_secret_key', 'general'),
'type' => 'string',
'description' => 'The secret key for reCAPTCHA.'
],
[
'key' => 'SETTINGS::RECAPTCHA:ENABLED',
'value' => $this->getNewValue('recaptcha_enabled'),
'value' => $this->getNewValue('recaptcha_enabled', 'general'),
'type' => 'boolean',
'description' => 'Enable reCAPTCHA on the panel.'
],
[
'key' => 'SETTINGS::MISC:PHPMYADMIN:URL',
'value' => $this->getNewValue('phpmyadmin_url'),
'value' => $this->getNewValue('phpmyadmin_url', 'general'),
'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;
try {
$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');
} catch (Exception $e) {
// Do nothing
}
// 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,19 +1,18 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreatePterodactylSettings extends SettingsMigration
class CreatePterodactylSettings extends LegacySettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('pterodactyl.admin_token', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:PTERODACTYL:TOKEN') : env('PTERODACTYL_TOKEN', ''));
$this->migrator->add('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);
$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) : 200);
}
public function down(): void
@ -23,76 +22,37 @@ class CreatePterodactylSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:TOKEN',
'value' => $this->getNewValue('admin_token'),
'value' => $this->getNewValue('admin_token', 'pterodactyl'),
'type' => 'string',
'description' => 'The admin token for the Pterodactyl panel.',
],
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN',
'value' => $this->getNewValue('user_token'),
'value' => $this->getNewValue('user_token', 'pterodactyl'),
'type' => 'string',
'description' => 'The user token for the Pterodactyl panel.',
],
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:URL',
'value' => $this->getNewValue('panel_url'),
'value' => $this->getNewValue('panel_url', 'pterodactyl'),
'type' => 'string',
'description' => 'The URL for the Pterodactyl panel.',
],
[
'key' => 'SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT',
'value' => $this->getNewValue('per_page_limit'),
'value' => $this->getNewValue('per_page_limit', 'pterodactyl'),
'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;
try {
$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');
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// 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,9 +1,9 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateMailSettings extends SettingsMigration
class CreateMailSettings extends LegacySettingsMigration
{
public function up(): void
{
@ -13,7 +13,7 @@ class CreateMailSettings extends SettingsMigration
$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->add('mail.mail_password', $table_exists ? $this->getOldValue('SETTINGS::MAIL:PASSWORD') : env('MAIL_PASSWORD', ''));
$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', 'CtrlPanel.gg'));
@ -25,105 +25,66 @@ class CreateMailSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::MAIL:HOST',
'value' => $this->getNewValue('mail_host'),
'value' => $this->getNewValue('mail_host', 'mail'),
'type' => 'string',
'description' => 'The host of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:PORT',
'value' => $this->getNewValue('mail_port'),
'value' => $this->getNewValue('mail_port', 'mail'),
'type' => 'integer',
'description' => 'The port of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:USERNAME',
'value' => $this->getNewValue('mail_username'),
'value' => $this->getNewValue('mail_username', 'mail'),
'type' => 'string',
'description' => 'The username of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:PASSWORD',
'value' => $this->getNewValue('mail_password'),
'value' => $this->getNewValue('mail_password', 'mail'),
'type' => 'string',
'description' => 'The password of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:ENCRYPTION',
'value' => $this->getNewValue('mail_encryption'),
'value' => $this->getNewValue('mail_encryption', 'mail'),
'type' => 'string',
'description' => 'The encryption of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:FROM_ADDRESS',
'value' => $this->getNewValue('mail_from_address'),
'value' => $this->getNewValue('mail_from_address', 'mail'),
'type' => 'string',
'description' => 'The from address of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:FROM_NAME',
'value' => $this->getNewValue('mail_from_name'),
'value' => $this->getNewValue('mail_from_name', 'mail'),
'type' => 'string',
'description' => 'The from name of the mail server.',
],
[
'key' => 'SETTINGS::MAIL:MAILER',
'value' => $this->getNewValue('mail_mailer'),
'value' => $this->getNewValue('mail_mailer', 'mail'),
'type' => 'string',
'description' => 'The mailer 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');
}
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;
try {
$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');
} catch (Exception $e) {
//
}
// 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,27 +1,27 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateUserSettings extends SettingsMigration
class CreateUserSettings extends LegacySettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('user.credits_reward_after_verify_discord', $table_exists ? $this->getOldValue('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD') : 250);
$this->migrator->add('user.credits_reward_after_verify_email', $table_exists ? $this->getOldValue('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL') : 250);
$this->migrator->add('user.force_discord_verification', $table_exists ? $this->getOldValue('SETTINGS::USER:FORCE_DISCORD_VERIFICATION') : false);
$this->migrator->add('user.force_email_verification', $table_exists ? $this->getOldValue('SETTINGS::USER:FORCE_EMAIL_VERIFICATION') : false);
$this->migrator->add('user.initial_credits', $table_exists ? $this->getOldValue('SETTINGS::USER:INITIAL_CREDITS') : 250);
$this->migrator->add('user.initial_server_limit', $table_exists ? $this->getOldValue('SETTINGS::USER:INITIAL_SERVER_LIMIT') : 1);
$this->migrator->add('user.min_credits_to_make_server', $table_exists ? $this->getOldValue('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER') : 50);
$this->migrator->add('user.server_limit_after_irl_purchase', $table_exists ? $this->getOldValue('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') : 10);
$this->migrator->add('user.server_limit_after_verify_discord', $table_exists ? $this->getOldValue('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD') : 2);
$this->migrator->add('user.server_limit_after_verify_email', $table_exists ? $this->getOldValue('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL') : 2);
$this->migrator->add('user.register_ip_check', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:REGISTER_IP_CHECK") : true);
$this->migrator->add('user.creation_enabled', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:CREATION_OF_NEW_USERS") : true);
$this->migrator->add('user.credits_reward_after_verify_discord', $table_exists ? $this->getOldValue('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD', 250) : 250);
$this->migrator->add('user.credits_reward_after_verify_email', $table_exists ? $this->getOldValue('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL', 250) : 250);
$this->migrator->add('user.force_discord_verification', $table_exists ? $this->getOldValue('SETTINGS::USER:FORCE_DISCORD_VERIFICATION', false) : false);
$this->migrator->add('user.force_email_verification', $table_exists ? $this->getOldValue('SETTINGS::USER:FORCE_EMAIL_VERIFICATION', false) : false);
$this->migrator->add('user.initial_credits', $table_exists ? $this->getOldValue('SETTINGS::USER:INITIAL_CREDITS', 250) : 250);
$this->migrator->add('user.initial_server_limit', $table_exists ? $this->getOldValue('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1) : 1);
$this->migrator->add('user.min_credits_to_make_server', $table_exists ? $this->getOldValue('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER', 50) : 50);
$this->migrator->add('user.server_limit_after_irl_purchase', $table_exists ? $this->getOldValue('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE', 10) : 10);
$this->migrator->add('user.server_limit_after_verify_discord', $table_exists ? $this->getOldValue('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD', 2) : 2);
$this->migrator->add('user.server_limit_after_verify_email', $table_exists ? $this->getOldValue('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL', 2) : 2);
$this->migrator->add('user.register_ip_check', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:REGISTER_IP_CHECK", true) : true);
$this->migrator->add('user.creation_enabled', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:CREATION_OF_NEW_USERS", true) : true);
}
public function down(): void
@ -29,135 +29,96 @@ class CreateUserSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD',
'value' => $this->getNewValue('credits_reward_after_verify_discord'),
'value' => $this->getNewValue('credits_reward_after_verify_discord', 'user'),
'type' => 'integer',
'description' => 'The amount of credits that the user will receive after verifying their Discord account.',
],
[
'key' => 'SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL',
'value' => $this->getNewValue('credits_reward_after_verify_email'),
'value' => $this->getNewValue('credits_reward_after_verify_email', 'user'),
'type' => 'integer',
'description' => 'The amount of credits that the user will receive after verifying their email.',
],
[
'key' => 'SETTINGS::USER:FORCE_DISCORD_VERIFICATION',
'value' => $this->getNewValue('force_discord_verification'),
'value' => $this->getNewValue('force_discord_verification', 'user'),
'type' => 'boolean',
'description' => 'If the user must verify their Discord account to use the panel.',
],
[
'key' => 'SETTINGS::USER:FORCE_EMAIL_VERIFICATION',
'value' => $this->getNewValue('force_email_verification'),
'value' => $this->getNewValue('force_email_verification', 'user'),
'type' => 'boolean',
'description' => 'If the user must verify their email to use the panel.',
],
[
'key' => 'SETTINGS::USER:INITIAL_CREDITS',
'value' => $this->getNewValue('initial_credits'),
'value' => $this->getNewValue('initial_credits', 'user'),
'type' => 'integer',
'description' => 'The amount of credits that the user will receive when they register.',
],
[
'key' => 'SETTINGS::USER:INITIAL_SERVER_LIMIT',
'value' => $this->getNewValue('initial_server_limit'),
'value' => $this->getNewValue('initial_server_limit', 'user'),
'type' => 'integer',
'description' => 'The amount of servers that the user will be able to create when they register.',
],
[
'key' => 'SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER',
'value' => $this->getNewValue('min_credits_to_make_server'),
'value' => $this->getNewValue('min_credits_to_make_server', 'user'),
'type' => 'integer',
'description' => 'The minimum amount of credits that the user must have to create a server.',
],
[
'key' => 'SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE',
'value' => $this->getNewValue('server_limit_after_irl_purchase'),
'value' => $this->getNewValue('server_limit_after_irl_purchase', 'user'),
'type' => 'integer',
'description' => 'The amount of servers that the user will be able to create after making a real purchase.',
],
[
'key' => 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD',
'value' => $this->getNewValue('server_limit_after_verify_discord'),
'value' => $this->getNewValue('server_limit_after_verify_discord', 'user'),
'type' => 'integer',
'description' => 'The amount of servers that the user will be able to create after verifying their Discord account.',
],
[
'key' => 'SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL',
'value' => $this->getNewValue('server_limit_after_verify_email'),
'value' => $this->getNewValue('server_limit_after_verify_email', 'user'),
'type' => 'integer',
'description' => 'The amount of servers that the user will be able to create after verifying their email.',
],
[
'key' => 'SETTINGS::SYSTEM:REGISTER_IP_CHECK',
'value' => $this->getNewValue('register_ip_check'),
'value' => $this->getNewValue('register_ip_check', 'user'),
'type' => 'boolean',
'description' => 'If the user must verify their IP address to register.',
],
[
'key' => 'SETTINGS::SYSTEM:CREATION_OF_NEW_USERS',
'value' => $this->getNewValue('creation_enabled'),
'value' => $this->getNewValue('creation_enabled', 'user'),
'type' => 'boolean',
'description' => 'If the user can register.',
],
]);
$this->migrator->delete('user.credits_reward_after_verify_discord');
$this->migrator->delete('user.credits_reward_after_verify_email');
$this->migrator->delete('user.force_discord_verification');
$this->migrator->delete('user.force_email_verification');
$this->migrator->delete('user.initial_credits');
$this->migrator->delete('user.initial_server_limit');
$this->migrator->delete('user.min_credits_to_make_server');
$this->migrator->delete('user.server_limit_after_irl_purchase');
$this->migrator->delete('user.server_limit_after_verify_discord');
$this->migrator->delete('user.server_limit_after_verify_email');
$this->migrator->delete('user.register_ip_check');
$this->migrator->delete('user.creation_enabled');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'user'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('user.credits_reward_after_verify_discord');
$this->migrator->delete('user.credits_reward_after_verify_email');
$this->migrator->delete('user.force_discord_verification');
$this->migrator->delete('user.force_email_verification');
$this->migrator->delete('user.initial_credits');
$this->migrator->delete('user.initial_server_limit');
$this->migrator->delete('user.min_credits_to_make_server');
$this->migrator->delete('user.server_limit_after_irl_purchase');
$this->migrator->delete('user.server_limit_after_verify_discord');
$this->migrator->delete('user.server_limit_after_verify_email');
$this->migrator->delete('user.register_ip_check');
$this->migrator->delete('user.creation_enabled');
} catch (Exception $e) {
// Do nothing
}
// 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,18 +1,18 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateServerSettings extends SettingsMigration
class CreateServerSettings extends LegacySettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('server.allocation_limit', $table_exists ? $this->getOldValue('SETTINGS::SERVER:ALLOCATION_LIMIT') : 200);
$this->migrator->add('server.creation_enabled', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS') : true);
$this->migrator->add('server.enable_upgrade', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:ENABLE_UPGRADE') : false);
$this->migrator->add('server.allocation_limit', $table_exists ? $this->getOldValue('SETTINGS::SERVER:ALLOCATION_LIMIT', 200) : 200);
$this->migrator->add('server.creation_enabled', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS', true) : true);
$this->migrator->add('server.enable_upgrade', $table_exists ? $this->getOldValue('SETTINGS::SYSTEM:ENABLE_UPGRADE', false) : false);
}
public function down(): void
@ -20,69 +20,30 @@ class CreateServerSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::SERVER:ALLOCATION_LIMIT',
'value' => $this->getNewValue('allocation_limit'),
'value' => $this->getNewValue('allocation_limit', 'server'),
'type' => 'integer',
'description' => 'The number of servers to show per page.',
],
[
'key' => 'SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS',
'value' => $this->getNewValue('creation_enabled'),
'value' => $this->getNewValue('creation_enabled', 'server'),
'type' => 'boolean',
'description' => 'Whether or not users can create new servers.',
],
[
'key' => 'SETTINGS::SYSTEM:ENABLE_UPGRADE',
'value' => $this->getNewValue('enable_upgrade'),
'value' => $this->getNewValue('enable_upgrade', 'server'),
'type' => 'boolean',
'description' => 'Whether or not users can upgrade their servers.',
],
]);
$this->migrator->delete('server.allocation_limit');
$this->migrator->delete('server.creation_enabled');
$this->migrator->delete('server.enable_upgrade');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'server'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('server.allocation_limit');
$this->migrator->delete('server.creation_enabled');
$this->migrator->delete('server.enable_upgrade');
} catch (Exception $e) {
// Do nothing
}
// 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,23 +1,23 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateInvoiceSettings extends SettingsMigration
class CreateInvoiceSettings extends LegacySettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('invoice.company_address', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_ADDRESS') : '');
$this->migrator->add('invoice.company_mail', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_MAIL') : '');
$this->migrator->add('invoice.company_name', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_NAME') : '');
$this->migrator->add('invoice.company_phone', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_PHONE') : '');
$this->migrator->add('invoice.company_vat', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_VAT') : '');
$this->migrator->add('invoice.company_website', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_WEBSITE') : '');
$this->migrator->add('invoice.enabled', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:ENABLED') : false);
$this->migrator->add('invoice.prefix', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:PREFIX') : 'INV');
$this->migrator->add('invoice.company_address', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_ADDRESS') : null);
$this->migrator->add('invoice.company_mail', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_MAIL') : null);
$this->migrator->add('invoice.company_name', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_NAME') : null);
$this->migrator->add('invoice.company_phone', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_PHONE') : null);
$this->migrator->add('invoice.company_vat', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_VAT') : null);
$this->migrator->add('invoice.company_website', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:COMPANY_WEBSITE') : null);
$this->migrator->add('invoice.enabled', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:ENABLED', false) : false);
$this->migrator->add('invoice.prefix', $table_exists ? $this->getOldValue('SETTINGS::INVOICE:PREFIX') : null);
}
public function down(): void
@ -25,104 +25,65 @@ class CreateInvoiceSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::INVOICE:COMPANY_ADDRESS',
'value' => $this->getNewValue('company_address'),
'value' => $this->getNewValue('company_address', 'invoice'),
'type' => 'string',
'description' => 'The address of the company.',
],
[
'key' => 'SETTINGS::INVOICE:COMPANY_MAIL',
'value' => $this->getNewValue('company_mail'),
'value' => $this->getNewValue('company_mail', 'invoice'),
'type' => 'string',
'description' => 'The email address of the company.',
],
[
'key' => 'SETTINGS::INVOICE:COMPANY_NAME',
'value' => $this->getNewValue('company_name'),
'value' => $this->getNewValue('company_name', 'invoice'),
'type' => 'string',
'description' => 'The name of the company.',
],
[
'key' => 'SETTINGS::INVOICE:COMPANY_PHONE',
'value' => $this->getNewValue('company_phone'),
'value' => $this->getNewValue('company_phone', 'invoice'),
'type' => 'string',
'description' => 'The phone number of the company.',
],
[
'key' => 'SETTINGS::INVOICE:COMPANY_VAT',
'value' => $this->getNewValue('company_vat'),
'value' => $this->getNewValue('company_vat', 'invoice'),
'type' => 'string',
'description' => 'The VAT number of the company.',
],
[
'key' => 'SETTINGS::INVOICE:COMPANY_WEBSITE',
'value' => $this->getNewValue('company_website'),
'value' => $this->getNewValue('company_website', 'invoice'),
'type' => 'string',
'description' => 'The website of the company.',
],
[
'key' => 'SETTINGS::INVOICE:ENABLED',
'value' => $this->getNewValue('enabled'),
'value' => $this->getNewValue('enabled', 'invoice'),
'type' => 'boolean',
'description' => 'Enable or disable the invoice system.',
],
[
'key' => 'SETTINGS::INVOICE:PREFIX',
'value' => $this->getNewValue('prefix'),
'value' => $this->getNewValue('prefix', 'invoice'),
'type' => 'string',
'description' => 'The prefix of the invoice.',
],
]);
$this->migrator->delete('invoice.company_address');
$this->migrator->delete('invoice.company_mail');
$this->migrator->delete('invoice.company_name');
$this->migrator->delete('invoice.company_phone');
$this->migrator->delete('invoice.company_vat');
$this->migrator->delete('invoice.company_website');
$this->migrator->delete('invoice.enabled');
$this->migrator->delete('invoice.prefix');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'invoice'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('invoice.company_address');
$this->migrator->delete('invoice.company_mail');
$this->migrator->delete('invoice.company_name');
$this->migrator->delete('invoice.company_phone');
$this->migrator->delete('invoice.company_vat');
$this->migrator->delete('invoice.company_website');
$this->migrator->delete('invoice.enabled');
$this->migrator->delete('invoice.prefix');
} catch (Exception $e) {
// Do nothing
}
// 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,21 +1,21 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateDiscordSettings extends SettingsMigration
class CreateDiscordSettings extends LegacySettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('discord.bot_token', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:BOT_TOKEN') : '');
$this->migrator->add('discord.client_id', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:CLIENT_ID') : '');
$this->migrator->add('discord.client_secret', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:CLIENT_SECRET') : '');
$this->migrator->add('discord.guild_id', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:GUILD_ID') : '');
$this->migrator->add('discord.invite_url', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:INVITE_URL') : '');
$this->migrator->add('discord.role_id', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:ROLE_ID') : '');
$this->migrator->add('discord.bot_token', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:BOT_TOKEN') : null);
$this->migrator->add('discord.client_id', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:CLIENT_ID') : null);
$this->migrator->add('discord.client_secret', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:CLIENT_SECRET') : null);
$this->migrator->add('discord.guild_id', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:GUILD_ID') : null);
$this->migrator->add('discord.invite_url', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:INVITE_URL') : null);
$this->migrator->add('discord.role_id', $table_exists ? $this->getOldValue('SETTINGS::DISCORD:ROLE_ID') : null);
}
public function down(): void
@ -23,91 +23,52 @@ class CreateDiscordSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::DISCORD:BOT_TOKEN',
'value' => $this->getNewValue('bot_token'),
'value' => $this->getNewValue('bot_token', 'discord'),
'type' => 'string',
'description' => 'The bot token for the Discord bot.',
],
[
'key' => 'SETTINGS::DISCORD:CLIENT_ID',
'value' => $this->getNewValue('client_id'),
'value' => $this->getNewValue('client_id', 'discord'),
'type' => 'string',
'description' => 'The client ID for the Discord bot.',
],
[
'key' => 'SETTINGS::DISCORD:CLIENT_SECRET',
'value' => $this->getNewValue('client_secret'),
'value' => $this->getNewValue('client_secret', 'discord'),
'type' => 'string',
'description' => 'The client secret for the Discord bot.',
],
[
'key' => 'SETTINGS::DISCORD:GUILD_ID',
'value' => $this->getNewValue('guild_id'),
'value' => $this->getNewValue('guild_id', 'discord'),
'type' => 'string',
'description' => 'The guild ID for the Discord bot.',
],
[
'key' => 'SETTINGS::DISCORD:INVITE_URL',
'value' => $this->getNewValue('invite_url'),
'value' => $this->getNewValue('invite_url', 'discord'),
'type' => 'string',
'description' => 'The invite URL for the Discord bot.',
],
[
'key' => 'SETTINGS::DISCORD:ROLE_ID',
'value' => $this->getNewValue('role_id'),
'value' => $this->getNewValue('role_id', 'discord'),
'type' => 'string',
'description' => 'The role ID for the Discord bot.',
]
]);
$this->migrator->delete('discord.bot_token');
$this->migrator->delete('discord.client_id');
$this->migrator->delete('discord.client_secret');
$this->migrator->delete('discord.guild_id');
$this->migrator->delete('discord.invite_url');
$this->migrator->delete('discord.role_id');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'discord'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('discord.bot_token');
$this->migrator->delete('discord.client_id');
$this->migrator->delete('discord.client_secret');
$this->migrator->delete('discord.guild_id');
$this->migrator->delete('discord.invite_url');
$this->migrator->delete('discord.role_id');
} catch (Exception $e) {
// Do nothing.
}
// 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,9 +1,9 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateLocaleSettings extends SettingsMigration
class CreateLocaleSettings extends LegacySettingsMigration
{
public function up(): void
{
@ -11,10 +11,10 @@ class CreateLocaleSettings extends SettingsMigration
// Get the user-set configuration values from the old table.
$this->migrator->add('locale.available', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:AVAILABLE') : '');
$this->migrator->add('locale.clients_can_change', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:CLIENTS_CAN_CHANGE') : true);
$this->migrator->add('locale.clients_can_change', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:CLIENTS_CAN_CHANGE', true) : true);
$this->migrator->add('locale.datatables', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:DATATABLES') : 'en-gb');
$this->migrator->add('locale.default', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:DEFAULT') : 'en');
$this->migrator->add('locale.dynamic', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:DYNAMIC') : false);
$this->migrator->add('locale.default', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:DEFAULT', 'en') : 'en');
$this->migrator->add('locale.dynamic', $table_exists ? $this->getOldValue('SETTINGS::LOCALE:DYNAMIC', false) : false);
}
public function down(): void
@ -22,83 +22,44 @@ class CreateLocaleSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::LOCALE:AVAILABLE',
'value' => $this->getNewValue('available'),
'value' => $this->getNewValue('available', 'locale'),
'type' => 'string',
'description' => 'The available locales.',
],
[
'key' => 'SETTINGS::LOCALE:CLIENTS_CAN_CHANGE',
'value' => $this->getNewValue('clients_can_change'),
'value' => $this->getNewValue('clients_can_change', 'locale'),
'type' => 'boolean',
'description' => 'If clients can change their locale.',
],
[
'key' => 'SETTINGS::LOCALE:DATATABLES',
'value' => $this->getNewValue('datatables'),
'value' => $this->getNewValue('datatables', 'locale'),
'type' => 'string',
'description' => 'The locale for datatables.',
],
[
'key' => 'SETTINGS::LOCALE:DEFAULT',
'value' => $this->getNewValue('default'),
'value' => $this->getNewValue('default', 'locale'),
'type' => 'string',
'description' => 'The default locale.',
],
[
'key' => 'SETTINGS::LOCALE:DYNAMIC',
'value' => $this->getNewValue('dynamic'),
'value' => $this->getNewValue('dynamic', 'locale'),
'type' => 'boolean',
'description' => 'If the locale should be dynamic.',
],
]);
$this->migrator->delete('locale.available');
$this->migrator->delete('locale.clients_can_change');
$this->migrator->delete('locale.datatables');
$this->migrator->delete('locale.default');
$this->migrator->delete('locale.dynamic');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'locale'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('locale.available');
$this->migrator->delete('locale.clients_can_change');
$this->migrator->delete('locale.datatables');
$this->migrator->delete('locale.default');
$this->migrator->delete('locale.dynamic');
} catch (Exception $e) {
// Do nothing
}
// 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,20 +1,20 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateReferralSettings extends SettingsMigration
class CreateReferralSettings extends LegacySettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('referral.always_give_commission', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION') : false);
$this->migrator->add('referral.enabled', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL::ENABLED') : false);
$this->migrator->add('referral.always_give_commission', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION', false) : false);
$this->migrator->add('referral.enabled', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL::ENABLED', false) : false);
$this->migrator->add('referral.reward', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL::REWARD') : 100);
$this->migrator->add('referral.mode', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL:MODE') : 'sign-up');
$this->migrator->add('referral.percentage', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL:PERCENTAGE') : 100);
$this->migrator->add('referral.mode', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL:MODE', 'sign-up') : 'sign-up');
$this->migrator->add('referral.percentage', $table_exists ? $this->getOldValue('SETTINGS::REFERRAL:PERCENTAGE', 100) : 100);
}
public function down(): void
@ -22,90 +22,51 @@ class CreateReferralSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::REFERRAL::ALLOWED',
'value' => $this->getNewValue('allowed'),
'value' => $this->getNewValue('allowed', 'referral'),
'type' => 'string',
'description' => 'The allowed referral types.',
],
[
'key' => 'SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION',
'value' => $this->getNewValue('always_give_commission'),
'value' => $this->getNewValue('always_give_commission', 'referral'),
'type' => 'boolean',
'description' => 'Whether to always give commission to the referrer.',
],
[
'key' => 'SETTINGS::REFERRAL::ENABLED',
'value' => $this->getNewValue('enabled'),
'value' => $this->getNewValue('enabled', 'referral'),
'type' => 'boolean',
'description' => 'Whether to enable the referral system.',
],
[
'key' => 'SETTINGS::REFERRAL::REWARD',
'value' => $this->getNewValue('reward'),
'value' => $this->getNewValue('reward', 'referral'),
'type' => 'integer',
'description' => 'The reward for the referral.',
],
[
'key' => 'SETTINGS::REFERRAL:MODE',
'value' => $this->getNewValue('mode'),
'value' => $this->getNewValue('mode', 'referral'),
'type' => 'string',
'description' => 'The referral mode.',
],
[
'key' => 'SETTINGS::REFERRAL:PERCENTAGE',
'value' => $this->getNewValue('percentage'),
'value' => $this->getNewValue('percentage', 'referral'),
'type' => 'integer',
'description' => 'The referral percentage.',
],
]);
$this->migrator->delete('referral.allowed');
$this->migrator->delete('referral.always_give_commission');
$this->migrator->delete('referral.enabled');
$this->migrator->delete('referral.reward');
$this->migrator->delete('referral.mode');
$this->migrator->delete('referral.percentage');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'referral'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('referral.allowed');
$this->migrator->delete('referral.always_give_commission');
$this->migrator->delete('referral.enabled');
$this->migrator->delete('referral.reward');
$this->migrator->delete('referral.mode');
$this->migrator->delete('referral.percentage');
} catch (Exception $e) {
//
}
// 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,27 +1,28 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateWebsiteSettings extends SettingsMigration
class CreateWebsiteSettings extends LegacySettingsMigration
{
public function up(): void
{
$table_exists = DB::table('settings_old')->exists();
// Get the user-set configuration values from the old table.
$this->migrator->add('website.motd_enabled', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:MOTD_ENABLED") : true);
$this->migrator->add('website.motd_enabled', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:MOTD_ENABLED", true) : true);
$this->migrator->add(
'website.motd_message',
$table_exists ? $this->getOldValue("SETTINGS::SYSTEM:MOTD_MESSAGE") :
"<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>");
$this->migrator->add('website.show_imprint', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SHOW_IMPRINT") : false);
$this->migrator->add('website.show_privacy', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SHOW_PRIVACY") : false);
$this->migrator->add('website.show_tos', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SHOW_TOS") : false);
$this->migrator->add('website.useful_links_enabled', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:USEFULLINKS_ENABLED") : true);
<p><span style='font-size: 10pt;'>(you can change this message in the <a href='admin/settings#system'>Settings</a> )</span></p>"
);
$this->migrator->add('website.show_imprint', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SHOW_IMPRINT", false) : false);
$this->migrator->add('website.show_privacy', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SHOW_PRIVACY", false) : false);
$this->migrator->add('website.show_tos', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SHOW_TOS", false) : false);
$this->migrator->add('website.useful_links_enabled', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:USEFULLINKS_ENABLED", true) : true);
$this->migrator->add('website.seo_title', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SEO_TITLE") : 'CtrlPanel.gg');
$this->migrator->add('website.seo_description', $table_exists ? $this->getOldValue("SETTINGS::SYSTEM:SEO_DESCRIPTION") : 'Billing software for Pterodactyl Panel.');
$this->migrator->add('website.enable_login_logo', true);
@ -32,111 +33,72 @@ class CreateWebsiteSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::SYSTEM:MOTD_ENABLED',
'value' => $this->getNewValue('motd_enabled'),
'value' => $this->getNewValue('motd_enabled', 'website'),
'type' => 'boolean',
'description' => 'Enable or disable the MOTD.',
],
[
'key' => 'SETTINGS::SYSTEM:MOTD_MESSAGE',
'value' => $this->getNewValue('motd_message'),
'value' => $this->getNewValue('motd_message', 'website'),
'type' => 'text',
'description' => 'The message that will be displayed in the MOTD.',
],
[
'key' => 'SETTINGS::SYSTEM:SHOW_IMPRINT',
'value' => $this->getNewValue('show_imprint'),
'value' => $this->getNewValue('show_imprint', 'website'),
'type' => 'boolean',
'description' => 'Enable or disable the imprint.',
],
[
'key' => 'SETTINGS::SYSTEM:SHOW_PRIVACY',
'value' => $this->getNewValue('show_privacy'),
'value' => $this->getNewValue('show_privacy', 'website'),
'type' => 'boolean',
'description' => 'Enable or disable the privacy policy.',
],
[
'key' => 'SETTINGS::SYSTEM:SHOW_TOS',
'value' => $this->getNewValue('show_tos'),
'value' => $this->getNewValue('show_tos', 'website'),
'type' => 'boolean',
'description' => 'Enable or disable the terms of service.',
],
[
'key' => 'SETTINGS::SYSTEM:USEFULLINKS_ENABLED',
'value' => $this->getNewValue('useful_links_enabled'),
'value' => $this->getNewValue('useful_links_enabled', 'website'),
'type' => 'boolean',
'description' => 'Enable or disable the useful links.',
],
[
'key' => 'SETTINGS::SYSTEM:SEO_TITLE',
'value' => $this->getNewValue('seo_title'),
'value' => $this->getNewValue('seo_title', 'website'),
'type' => 'string',
'description' => 'The title of the website.',
],
[
'key' => 'SETTINGS::SYSTEM:SEO_DESCRIPTION',
'value' => $this->getNewValue('seo_description'),
'value' => $this->getNewValue('seo_description', 'website'),
'type' => 'string',
'description' => 'The description of the website.',
],
[
'key' => 'SETTINGS::SYSTEM:ENABLE_LOGIN_LOGO',
'value' => $this->getNewValue('enable_login_logo'),
'value' => $this->getNewValue('enable_login_logo', 'website'),
'type' => 'boolean',
'description' => 'Enable or disable the login logo.',
]
]);
$this->migrator->delete('website.motd_enabled');
$this->migrator->delete('website.motd_message');
$this->migrator->delete('website.show_imprint');
$this->migrator->delete('website.show_privacy');
$this->migrator->delete('website.show_tos');
$this->migrator->delete('website.useful_links_enabled');
$this->migrator->delete('website.seo_title');
$this->migrator->delete('website.seo_description');
$this->migrator->delete('website.enable_login_logo');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'website'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('website.motd_enabled');
$this->migrator->delete('website.motd_message');
$this->migrator->delete('website.show_imprint');
$this->migrator->delete('website.show_privacy');
$this->migrator->delete('website.show_tos');
$this->migrator->delete('website.useful_links_enabled');
$this->migrator->delete('website.seo_title');
$this->migrator->delete('website.seo_description');
$this->migrator->delete('website.enable_login_logo');
} catch (Exception $e) {
// Do nothing
}
// 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,9 +1,9 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
use App\Classes\LegacySettingsMigration;
use Illuminate\Support\Facades\DB;
class CreateTicketSettings extends SettingsMigration
class CreateTicketSettings extends LegacySettingsMigration
{
public function up(): void
{
@ -19,67 +19,23 @@ class CreateTicketSettings extends SettingsMigration
DB::table('settings_old')->insert([
[
'key' => 'SETTINGS::TICKET:NOTIFY',
'value' => $this->getNewValue('notify'),
'value' => $this->getNewValue('notify', 'ticket'),
'type' => 'string',
'description' => 'The notification type for tickets.',
],
[
'key' => 'SETTINGS::TICKET:ENABLED',
'value' => $this->getNewValue('enabled'),
'value' => $this->getNewValue('enabled', 'ticket'),
'type' => 'boolean',
'description' => 'Enable or disable the ticket system.',
]
]);
$this->migrator->delete('ticket.enabled');
$this->migrator->delete('ticket.notify');
}
public function getNewValue(string $name)
{
$new_value = DB::table('settings')->where([['group', '=', 'ticket'], ['name', '=', $name]])->get(['payload'])->first();
// Some keys returns '""' as a value.
if ($new_value->payload === '""') {
return null;
try {
$this->migrator->delete('ticket.enabled');
$this->migrator->delete('ticket.notify');
} catch (Exception $e) {
// Do nothing.
}
// 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 (is_null($old_value)) {
return '';
}
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

@ -9,4 +9,10 @@ return new class extends SettingsMigration
$this->migrator->delete('ticket.notify');
$this->migrator->add('ticket.information', "Can't start your server? Need an additional port? Do you have any other questions? Let us know by opening a ticket.");
}
public function down(): void
{
$this->migrator->add('ticket.notify', 'all');
$this->migrator->delete('ticket.information');
}
};

View file

@ -12,7 +12,7 @@ services:
ports:
- 80:80
volumes:
- "../:/var/www/html:delegated"
- "../:/var/www/html"
depends_on:
- php
- mysql
@ -42,7 +42,7 @@ services:
dockerfile: docker/php/Dockerfile
container_name: controlpanel_php
volumes:
- "../:/var/www/html:delegated"
- "../:/var/www/html"
networks:
- laravel
@ -61,19 +61,5 @@ services:
networks:
- laravel
redis:
image: "redis:alpine"
command: redis-server --requirepass sOmE_sEcUrE_pAsS
ports:
- "6379:6379"
volumes:
- $PWD/redis-data:/var/lib/redis
- $PWD/redis.conf:/usr/local/etc/redis/redis.conf
environment:
- REDIS_REPLICATION_MODE=master
networks:
- laravel
volumes:
mysql:
mysql:

View file

@ -1,16 +1,14 @@
FROM php:8.0-fpm-alpine3.13
FROM php:8.1-fpm-buster
RUN apt-get update \
&& apt-get install -y build-essential zlib1g-dev default-mysql-client curl gnupg procps vim git unzip libzip-dev libpq-dev libicu-dev libonig-dev libpng-dev libjpeg-dev libfreetype6-dev
RUN apk update && apk upgrade
RUN apk add --no-cache --repository https://alpine.global.ssl.fastly.net/alpine/edge/community/
RUN apk add --no-cache curl-dev icu-dev libzip-dev
RUN docker-php-ext-install mysqli pdo pdo_mysql intl zip
RUN docker-php-ext-install mysqli pdo pdo_mysql intl zip gd bcmath
ADD ./docker/php/www.conf /usr/local/etc/php-fpm.d/
RUN mkdir -p /var/www/html
RUN addgroup -g 1000 laravel && adduser -G laravel -g laravel -s /bin/sh -D laravel
RUN addgroup --gid 1000 laravel && adduser --ingroup laravel --uid 1000 --shell /bin/sh --disabled-password --gecos "" laravel
RUN chown laravel:laravel /var/www/html
WORKDIR /var/www/html
@ -18,4 +16,3 @@ WORKDIR /var/www/html
USER laravel
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

View file

@ -29,8 +29,7 @@ if (isset($_POST['checkDB'])) {
try {
$db = new mysqli($_POST['databasehost'], $_POST['databaseuser'], $_POST['databaseuserpass'], $_POST['database'], $_POST['databaseport']);
}
catch (mysqli_sql_exception $e) {
} catch (mysqli_sql_exception $e) {
wh_log($e->getMessage(), 'error');
header('LOCATION: index.php?step=2&message=' . $e->getMessage());
exit();
@ -69,26 +68,26 @@ if (isset($_POST['feedDB'])) {
wh_log('Feeding the Database', 'debug');
$logs = '';
//$logs .= run_console(setenv('COMPOSER_HOME', dirname(__FILE__, 3) . '/vendor/bin/composer'));
//$logs .= run_console('composer install --no-dev --optimize-autoloader');
if (!str_contains(getenv('APP_KEY'), 'base64')) {
$logs .= run_console('php artisan key:generate --force');
} else {
$logs .= "Key already exists. Skipping\n";
}
$logs .= run_console('php artisan storage:link');
$logs .= run_console('php artisan migrate --seed --force');
$logs .= run_console('php artisan db:seed --class=ExampleItemsSeeder --force');
$logs .= run_console('php artisan db:seed --class=PermissionsSeeder --force');
try {
//$logs .= run_console(setenv('COMPOSER_HOME', dirname(__FILE__, 3) . '/vendor/bin/composer'));
//$logs .= run_console('composer install --no-dev --optimize-autoloader');
if (!str_contains(getenv('APP_KEY'), 'base64')) {
$logs .= run_console('php artisan key:generate --force');
} else {
$logs .= "Key already exists. Skipping\n";
}
$logs .= run_console('php artisan storage:link');
$logs .= run_console('php artisan migrate --seed --force');
$logs .= run_console('php artisan db:seed --class=ExampleItemsSeeder --force');
$logs .= run_console('php artisan db:seed --class=PermissionsSeeder --force');
wh_log($logs, 'debug');
wh_log($logs, 'debug');
if (str_contains(getenv('APP_KEY'), 'base64')) {
wh_log('Feeding the Database successful', 'debug');
header('LOCATION: index.php?step=3');
} else {
wh_log('Feeding the Database failed', 'debug');
header('LOCATION: index.php?step=2.5&message=There was an error. Please check the installer.log file in /var/www/controlpanel/storage/logs !');
} catch (\Throwable $th) {
wh_log('Feeding the Database failed', 'error');
header("LOCATION: index.php?step=2.5&message=" . $th->getMessage() . " <br>Please check the installer.log file in /var/www/controlpanel/storage/logs !");
}
}

View file

@ -10,7 +10,7 @@ use Monolog\Handler\StreamHandler;
use Monolog\Logger;
if (!file_exists('../../.env')) {
echo run_console('cp .env.example .env');
echo run_console('cp .env.example .env');
}
(new DotEnv(dirname(__FILE__, 3) . '/.env'))->load();
@ -19,7 +19,7 @@ $required_extensions = ['openssl', 'gd', 'mysql', 'PDO', 'mbstring', 'tokenizer'
$requirements = [
'minPhp' => '8.1',
'maxPhp' => '8.3', // This version is not supported
'maxPhp' => '8.4', // This version is not supported
'mysql' => '5.7.22',
];
@ -150,7 +150,8 @@ function checkExtensions(): array
return $not_ok;
}
function removeQuotes($string){
function removeQuotes($string)
{
return str_replace('"', "", $string);
}
@ -162,7 +163,7 @@ function removeQuotes($string){
*/
function setenv($envKey, $envValue)
{
$envFile = dirname(__FILE__, 3).'/.env';
$envFile = dirname(__FILE__, 3) . '/.env';
$str = file_get_contents($envFile);
$str .= "\n"; // In case the searched variable is in the last line without \n
@ -236,9 +237,16 @@ function run_console(string $command, array $descriptors = null, string $cwd = n
$path = dirname(__FILE__, 3);
$descriptors = $descriptors ?? [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", $descriptors, $pipes, $cwd, null, $options);
$output = stream_get_contents($pipes[1]);
$exit_code = proc_close($handle);
wh_log('command result: ' . stream_get_contents($pipes[1]), 'debug');
return stream_get_contents($pipes[1]);
if ($exit_code > 0) {
wh_log('command result: ' . $output, 'error');
throw new Exception("There was an error after running command `$command`", $exit_code);
return $output;
} else {
return $output;
}
}
/**
@ -259,7 +267,7 @@ function wh_log(string $message, string $level = 'info', array $context = []): v
switch (strtolower($level)) {
case 'debug': // Only log debug messages if APP_DEBUG is true
if(getenv('APP_DEBUG') === false) return;
if (getenv('APP_DEBUG') === false) return;
$log->debug($message, $context);
break;
case 'info':

View file

@ -34,6 +34,7 @@ use App\Http\Controllers\TicketsController;
use App\Http\Controllers\TranslationController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
/*
@ -77,11 +78,13 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
Route::patch('/servers/cancel/{server}', [ServerController::class, 'cancel'])->name('servers.cancel');
Route::resource('servers', ServerController::class);
if (config('app.key')) {
try {
$serverSettings = app(App\Settings\ServerSettings::class);
if ($serverSettings->enable_upgrade) {
if ($serverSettings->creation_enabled) {
Route::post('servers/{server}/upgrade', [ServerController::class, 'upgrade'])->name('servers.upgrade');
}
} catch (Exception $e) {
Log::error("ServerSettings not found, skipping server upgrade route");
}
Route::post('profile/selfdestruct', [ProfileController::class, 'selfDestroyUser'])->name('profile.selfDestroyUser');

BIN
storage/app/public/logo.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View file

@ -1,12 +0,0 @@
[
{
"PASSWORD": "",
"USERNAME": "",
"AUTO_UPDATE": "0",
"BOT_PY_FILE": "bot.py",
"PY_PACKAGES": "",
"USER_UPLOAD": "0",
"INSTALL_REPO": "",
"INSTALL_BRANCH": ""
}
]

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{__('Coupons')}}</h1>
</div>
@ -31,7 +31,7 @@
{{__('Coupons')}}
</h5>
<a href="{{route('admin.coupons.create')}}" class="btn btn-sm btn-primary">
<i class="fas fa-plus mr-1"></i>
<i class="mr-1 fas fa-plus"></i>
{{__('Create new')}}
</a>
</div>
@ -82,7 +82,7 @@
{data: 'status'},
{data: 'code'},
{data: 'value'},
{data: 'uses'},
{data: 'uses', sortable: false},
{data: 'expires_at'},
{data: 'created_at'},
{data: 'actions', sortable: false},

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{__('Admin Overview')}}</h1>
</div>
@ -33,22 +33,22 @@
<section class="content">
<div class="container-fluid">
<div class="row mb-3">
<div class="mb-3 row">
<div class="col-md-3">
<a href="https://discord.gg/4Y6HjD2uyU" class="btn btn-dark btn-block px-3"><i
class="fab fa-discord mr-2"></i> {{__('Support server')}}</a>
<a href="https://discord.gg/4Y6HjD2uyU" class="px-3 btn btn-dark btn-block"><i
class="mr-2 fab fa-discord"></i> {{__('Support server')}}</a>
</div>
<div class="col-md-3">
<a href="https://CtrlPanel.gg/docs/intro" class="btn btn-dark btn-block px-3"><i
class="fas fa-link mr-2"></i> {{__('Documentation')}}</a>
<a href="https://CtrlPanel.gg/docs/intro" class="px-3 btn btn-dark btn-block"><i
class="mr-2 fas fa-link"></i> {{__('Documentation')}}</a>
</div>
<div class="col-md-3">
<a href="https://github.com/ControlPanel-gg/dashboard" class="btn btn-dark btn-block px-3"><i
class="fab fa-github mr-2"></i> {{__('Github')}}</a>
<a href="https://github.com/Ctrlpanel-gg/panel" class="px-3 btn btn-dark btn-block"><i
class="mr-2 fab fa-github"></i> {{__('Github')}}</a>
</div>
<div class="col-md-3">
<a href="https://CtrlPanel.gg/docs/Contributing/donating" class="btn btn-dark btn-block px-3"><i
class="fas fa-money-bill mr-2"></i> {{__('Support CtrlPanel')}}</a>
<a href="https://CtrlPanel.gg/docs/Contributing/donating" class="px-3 btn btn-dark btn-block"><i
class="mr-2 fas fa-money-bill"></i> {{__('Support CtrlPanel')}}</a>
</div>
</div>
@ -59,7 +59,7 @@
<div class="info-box-content">
<span class="info-box-text">{{__('Servers')}}
<i class="fas fa-info-circle mr-4" data-toggle="popover"
<i class="mr-4 fas fa-info-circle" data-toggle="popover"
data-trigger="hover" data-placement="top"
data-html="true"
data-content="{{ __("This shows the total active servers and the total servers. Total active servers are all servers which are not suspended") }}"></i>
@ -77,7 +77,7 @@
<div class="info-box-content">
<span class="info-box-text">{{__('Users')}}
<i class="fas fa-info-circle mr-4" data-toggle="popover"
<i class="mr-4 fas fa-info-circle" data-toggle="popover"
data-trigger="hover" data-placement="top"
data-html="true"
data-content="{{ __("This shows the total active Users and the total Users. Total active Users are all Users which are not suspended") }}"></i>
@ -92,7 +92,7 @@
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<span class="info-box-icon bg-warning elevation-1"><i
class="fas fa-coins text-white"></i></span>
class="text-white fas fa-coins"></i></span>
<div class="info-box-content">
<span class="info-box-text">{{__('Total')}} {{ $credits_display_name }}</span>
@ -123,15 +123,15 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="card-title ">
<span><i class="fas fa-kiwi-bird mr-2"></i>{{__('Pterodactyl')}}</span>
<span><i class="mr-2 fas fa-kiwi-bird"></i>{{__('Pterodactyl')}}</span>
</div>
<a href="{{route('admin.overview.sync')}}" class="btn btn-primary btn-sm"><i
class="fas fa-sync mr-2"></i>{{__('Sync')}}</a>
class="mr-2 fas fa-sync"></i>{{__('Sync')}}</a>
</div>
</div>
<div class="card-body py-1">
<div class="py-1 card-body">
@if ($deletedNodesPresent)
<div class="alert alert-danger m-2">
<div class="m-2 alert alert-danger">
<h5><i class="icon fas fa-exclamation-circle"></i>{{ __('Warning!') }}</h5>
<p class="mb-2">
{{ __('Some nodes got deleted on pterodactyl only. Please click the sync button above.') }}
@ -166,42 +166,44 @@
</table>
</div>
<div class="card-footer">
<span><i class="fas fa-sync mr-2"></i>{{__('Last updated :date', ['date' => $syncLastUpdate])}}</span>
<span><i class="mr-2 fas fa-sync"></i>{{__('Last updated :date', ['date' => $syncLastUpdate])}}</span>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="card-title ">
<span><i class="fas fa-ticket-alt mr-2"></i>{{__('Latest tickets')}}</span>
<span><i class="mr-2 fas fa-ticket-alt"></i>{{__('Latest tickets')}}</span>
</div>
</div>
</div>
<div class="card-body py-1">
<div class="py-1 card-body">
@if(!$tickets->count())<span style="font-size: 16px; font-weight:700">{{__('There are no tickets')}}.</span>
@else
<table class="table">
<thead>
<tr>
<th>{{__('Title')}}</th>
<th>{{__('User')}}</th>
<th>{{__('Status')}}</th>
<th>{{__('Last updated')}}</th>
</tr>
</thead>
<tbody>
<div class="overflow-auto">
<table class="table">
<thead>
<tr class="text-nowrap">
<th>{{__('Title')}}</th>
<th>{{__('User')}}</th>
<th>{{__('Status')}}</th>
<th>{{__('Last updated')}}</th>
</tr>
</thead>
<tbody>
@foreach($tickets as $ticket_id => $ticket)
<tr>
<td><a class="text-info" href="{{route('admin.ticket.show', ['ticket_id' => $ticket_id])}}">#{{$ticket_id}} - {{$ticket->title}}</td>
<td><a href="{{route('admin.users.show', $ticket->user_id)}}">{{$ticket->user}}</a></td>
<td><span class="badge {{$ticket->statusBadgeColor}}">{{$ticket->status}}</span></td>
<td>{{$ticket->last_updated}}</td>
</tr>
@endforeach
@foreach($tickets as $ticket_id => $ticket)
<tr class="text-nowrap">
<td><a class="text-info" href="{{route('admin.ticket.show', ['ticket_id' => $ticket_id])}}">#{{$ticket_id}} - {{$ticket->title}}</td>
<td><a href="{{route('admin.users.show', $ticket->user_id)}}">{{$ticket->user}}</a></td>
<td><span class="badge {{$ticket->statusBadgeColor}}">{{$ticket->status}}</span></td>
<td>{{$ticket->last_updated}}</td>
</tr>
@endforeach
</tbody>
</table>
</tbody>
</table>
</div>
@endif
</div>
</div>
@ -209,14 +211,14 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="card-title ">
<span><i class="fas fa-server mr-2"></i>{{__('CtrlPanel.gg')}}</span>
<span><i class="mr-2 fas fa-server"></i>{{__('CtrlPanel.gg')}}</span>
</div>
</div>
<div class="card-body py-1">
<div class="py-1 card-body">
</div>
<div class="card-footer">
<span><i class="fas fa-info mr-2"></i>{{__("Version")}} {{config("app.version")}} - {{config("BRANCHNAME")}}</span>
<span><i class="mr-2 fas fa-info"></i>{{__("Version")}} {{config("app.version")}} - {{config("BRANCHNAME")}}</span>
</div>
</div>
</div>
@ -226,13 +228,13 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="card-title ">
<span><i class="fas fa-server mr-2"></i>{{__('Individual nodes')}}</span>
<span><i class="mr-2 fas fa-server"></i>{{__('Individual nodes')}}</span>
</div>
</div>
</div>
<div class="card-body py-1">
<div class="py-1 card-body">
@if ($perPageLimit)
<div class="alert alert-danger m-2">
<div class="m-2 alert alert-danger">
<h5><i class="icon fas fa-exclamation-circle"></i>{{ __('Error!') }}</h5>
<p class="mb-2">
{{ __('You reached the Pterodactyl perPage limit. Please make sure to set it higher than your server count.') }}<br>
@ -240,39 +242,41 @@
{{ __('Note') }}: {{ __('If this error persists even after changing the limit, it might mean a server was deleted on Pterodactyl, but not on CtrlPanel. Try clicking the button below.') }}
</p>
<a href="{{route('admin.servers.sync')}}" class="btn btn-primary btn-md"><i
class="fas fa-sync mr-2"></i>{{__('Sync servers')}}</a>
class="mr-2 fas fa-sync"></i>{{__('Sync servers')}}</a>
</div>
@endif
<table class="table">
<thead>
<tr>
<th>{{__('ID')}}</th>
<th>{{__('Node')}}</th>
<th>{{__('Server count')}}</th>
<th>{{__('Resource usage')}}</th>
<th>{{ $credits_display_name . ' ' . __('Usage') ." (".__('per month').")"}}</th>
</tr>
</thead>
<tbody>
@foreach($nodes as $nodeID => $node)
<tr>
<td>{{$nodeID}}</td>
<td>{{$node->name}}</td>
<td>{{$node->activeServers}}/{{$node->totalServers}}</td>
<td>{{$node->usagePercent}}%</td>
<td>{{$node->activeEarnings}}/{{$node->totalEarnings}}</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<td colspan="2"><span style="float: right; font-weight: 700">{{__('Total')}} ({{__('active')}}/{{__('total')}}):</span></td>
<td>{{$counters['servers']->active}}/{{$counters['servers']->total}}</td>
<td>{{$counters['totalUsagePercent']}}%</td>
<td>{{$counters['earnings']->active}}/{{$counters['earnings']->total}}</td>
<div class="overflow-auto">
<table class="table">
<thead>
<tr class="text-nowrap">
<th>{{__('ID')}}</th>
<th>{{__('Node')}}</th>
<th>{{__('Server count')}}</th>
<th>{{__('Resource usage')}}</th>
<th>{{ $credits_display_name . ' ' . __('Usage') ." (".__('per month').")"}}</th>
</tr>
</tfoot>
</table>
</thead>
<tbody>
@foreach($nodes as $nodeID => $node)
<tr>
<td>{{$nodeID}}</td>
<td>{{$node->name}}</td>
<td>{{$node->activeServers}}/{{$node->totalServers}}</td>
<td>{{$node->usagePercent}}%</td>
<td>{{$node->activeEarnings}}/{{$node->totalEarnings}}</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<td class="text-nowrap" colspan="2"><span style="float: right; font-weight: 700">{{__('Total')}} ({{__('active')}}/{{__('total')}}):</span></td>
<td>{{$counters['servers']->active}}/{{$counters['servers']->total}}</td>
<td>{{$counters['totalUsagePercent']}}%</td>
<td>{{$counters['earnings']->active}}/{{$counters['earnings']->total}}</td>
</tr>
</tfoot>
</table>
</div>
<hr style="width: 100%; height:2px; border-width:0; background-color:#6c757d; margin-top: 0px;">
</div>
</div>
@ -280,11 +284,11 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="card-title ">
<span><i class="fas fa-file-invoice-dollar mr-2"></i>{{__('Latest payments')}}</span>
<span><i class="mr-2 fas fa-file-invoice-dollar"></i>{{__('Latest payments')}}</span>
</div>
</div>
</div>
<div class="card-body py-1">
<div class="py-1 card-body">
<div class="row">
@if($counters['payments']['lastMonth']->count())
<div class="col-md-6" style="border-right:1px solid #6c757d">
@ -293,25 +297,26 @@
data-content="{{ __('Payments in this time window') }}:<br>{{$counters['payments']['lastMonth']->timeStart}} - {{$counters['payments']['lastMonth']->timeEnd}}"
class="fas fa-info-circle"></i>
</span>
<table class="table">
<thead>
<tr>
<th><b>{{__('Currency')}}</b></th>
<th>{{__('Number of payments')}}</th>
<th>{{__('Total amount')}}</th>
</tr>
</thead>
<tbody>
@foreach($counters['payments']['lastMonth'] as $currency => $income)
<tr>
<td>{{$currency}}</td>
<td>{{$income->count}}</td>
<td>{{$income->total}}</td>
</tr>
@endforeach
</tbody>
</table>
<hr style="width: 100%; height:1px; border-width:0; background-color:#6c757d; margin-top: -16px">
<div class="overflow-auto">
<table class="table">
<thead>
<tr class="text-nowrap">
<th><b>{{__('Currency')}}</b></th>
<th>{{__('Number of payments')}}</th>
<th>{{__('Total amount')}}</th>
</tr>
</thead>
<tbody>
@foreach($counters['payments']['lastMonth'] as $currency => $income)
<tr>
<td>{{$currency}}</td>
<td>{{$income->count}}</td>
<td>{{$income->total}}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@if($counters['payments']['lastMonth']->count()) <div class="col-md-6">
@ -321,25 +326,26 @@
data-content="{{ __('Payments in this time window') }}:<br>{{$counters['payments']['thisMonth']->timeStart}} - {{$counters['payments']['thisMonth']->timeEnd}}"
class="fas fa-info-circle"></i>
</span>
<table class="table">
<thead>
<tr>
<th><b>{{__('Currency')}}</b></th>
<th>{{__('Number of payments')}}</th>
<th>{{__('Total amount')}}</th>
</tr>
</thead>
<tbody>
@foreach($counters['payments']['thisMonth'] as $currency => $income)
<tr>
<td>{{$currency}}</td>
<td>{{$income->count}}</td>
<td>{{$income->total}}</td>
</tr>
@endforeach
</tbody>
</table>
<hr style="width: 100%; height:1px; border-width:0; background-color:#6c757d; margin-top: -16px">
<div class="overflow-auto">
<table class="table">
<thead>
<tr class="text-nowrap">
<th><b>{{__('Currency')}}</b></th>
<th>{{__('Number of payments')}}</th>
<th>{{__('Total amount')}}</th>
</tr>
</thead>
<tbody>
@foreach($counters['payments']['thisMonth'] as $currency => $income)
<tr>
<td>{{$currency}}</td>
<td>{{$income->count}}</td>
<td>{{$income->total}}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@ -349,20 +355,52 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="card-title ">
<span><i class="fas fa-hand-holding-usd mr-2"></i>{{__('Tax overview')}}</span>
<span><i class="mr-2 fas fa-hand-holding-usd"></i>{{__('Tax overview')}}</span>
</div>
</div>
</div>
<div class="card-body py-1">
<div class="py-1 card-body">
@if($counters['taxPayments']['lastYear']->count())
<span style="margin:auto; display:table; font-size: 18px; font-weight:700">{{__('Last year')}}:
<i data-toggle="popover" data-trigger="hover" data-html="true"
data-content="{{ __('Payments in this time window') }}:<br>{{$counters['taxPayments']['lastYear']->timeStart}} - {{$counters['taxPayments']['lastYear']->timeEnd}}"
class="fas fa-info-circle"></i>
</span>
<div class="overflow-auto">
<table class="table">
<thead>
<tr class="text-nowrap">
<th><b>{{__('Currency')}}</b></th>
<th>{{__('Number of payments')}}</th>
<th><b>{{__('Base amount')}}</b></th>
<th><b>{{__('Total taxes')}}</b></th>
<th>{{__('Total amount')}}</th>
</tr>
</thead>
<tbody>
@foreach($counters['taxPayments']['lastYear'] as $currency => $income)
<tr>
<td>{{$currency}}</td>
<td>{{$income->count}}</td>
<td>{{$income->price}}</td>
<td>{{$income->taxes}}</td>
<td>{{$income->total}}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<hr style="width: 100%; height:2px; border-width:0; background-color:#6c757d; margin-top: 0px; margin-bottom: 8px">
@endif
<span style="margin:auto; display:table; font-size: 18px; font-weight:700">{{__('This year')}}:
<i data-toggle="popover" data-trigger="hover" data-html="true"
data-content="{{ __('Payments in this time window') }}:<br>{{$counters['taxPayments']['thisYear']->timeStart}} - {{$counters['taxPayments']['thisYear']->timeEnd}}"
class="fas fa-info-circle"></i>
</span>
<div class="overflow-auto">
<table class="table">
<thead>
<tr>
<tr class="text-nowrap">
<th><b>{{__('Currency')}}</b></th>
<th>{{__('Number of payments')}}</th>
<th><b>{{__('Base amount')}}</b></th>
@ -371,7 +409,7 @@
</tr>
</thead>
<tbody>
@foreach($counters['taxPayments']['lastYear'] as $currency => $income)
@foreach($counters['taxPayments']['thisYear'] as $currency => $income)
<tr>
<td>{{$currency}}</td>
<td>{{$income->count}}</td>
@ -382,35 +420,7 @@
@endforeach
</tbody>
</table>
<hr style="width: 100%; height:2px; border-width:0; background-color:#6c757d; margin-top: 0px; margin-bottom: 8px">
@endif
<span style="margin:auto; display:table; font-size: 18px; font-weight:700">{{__('This year')}}:
<i data-toggle="popover" data-trigger="hover" data-html="true"
data-content="{{ __('Payments in this time window') }}:<br>{{$counters['taxPayments']['thisYear']->timeStart}} - {{$counters['taxPayments']['thisYear']->timeEnd}}"
class="fas fa-info-circle"></i>
</span>
<table class="table">
<thead>
<tr>
<th><b>{{__('Currency')}}</b></th>
<th>{{__('Number of payments')}}</th>
<th><b>{{__('Base amount')}}</b></th>
<th><b>{{__('Total taxes')}}</b></th>
<th>{{__('Total amount')}}</th>
</tr>
</thead>
<tbody>
@foreach($counters['taxPayments']['thisYear'] as $currency => $income)
<tr>
<td>{{$currency}}</td>
<td>{{$income->count}}</td>
<td>{{$income->price}}</td>
<td>{{$income->taxes}}</td>
<td>{{$income->total}}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<hr style="width: 100%; height:2px; border-width:0; background-color:#6c757d; margin-top: 0px;">
</div>
</div>

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{__('Products')}}</h1>
</div>
@ -30,9 +30,9 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-sliders-h mr-2"></i>{{__('Products')}}</h5>
<h5 class="card-title"><i class="mr-2 fas fa-sliders-h"></i>{{__('Products')}}</h5>
<a href="{{route('admin.products.create')}}" class="btn btn-sm btn-primary"><i
class="fas fa-plus mr-1"></i>{{__('Create new')}}</a>
class="mr-1 fas fa-plus"></i>{{__('Create new')}}</a>
</div>
</div>

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{__('Products')}}</h1>
</div>
@ -28,17 +28,17 @@
<div class="card">
<div class="card-header d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-sliders-h mr-2"></i>{{__('Product')}}</h5>
<h5 class="card-title"><i class="mr-2 fas fa-sliders-h"></i>{{__('Product')}}</h5>
<div class="ml-auto">
<a data-content="Edit" data-trigger="hover" data-toggle="tooltip"
href="{{ route('admin.products.edit', $product->id) }}" class="btn btn-sm btn-info mr-1"><i
href="{{ route('admin.products.edit', $product->id) }}" class="mr-1 btn btn-sm btn-info"><i
class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post"
action="{{ route('admin.products.destroy', $product->id) }}">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button data-content="Delete" data-trigger="hover" data-toggle="tooltip"
class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
class="mr-1 btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
</form>
</div>
</div>
@ -78,7 +78,7 @@
</div>
<div class="col-lg-8">
<span style="max-width: 250px;" class="d-inline-block text-truncate">
<i class="fas fa-coins mr-1"></i>{{ $product->price }}
<i class="mr-1 fas fa-coins"></i>{{ $product->price }}
</span>
</div>
</div>
@ -92,9 +92,9 @@
<div class="col-lg-8">
<span style="max-width: 250px;" class="d-inline-block text-truncate">
@if ($product->minimum_credits == -1)
<i class="fas fa-coins mr-1"></i>{{ $minimum_credits }}
<i class="mr-1 fas fa-coins"></i>{{ $minimum_credits }}
@else
<i class="fas fa-coins mr-1"></i>{{ $product->minimum_credits }}
<i class="mr-1 fas fa-coins"></i>{{ $product->minimum_credits }}
@endif
</span>
</div>
@ -213,7 +213,7 @@
<label>{{__('Description')}}</label>
</div>
<div class="col-lg-8">
<span class="d-inline-block text-truncate">
<span class="d-block text-truncate">
{{ $product->description }}
</span>
</div>
@ -240,7 +240,7 @@
<div class="card">
<div class="card-header">
<h5 class="card-title"><i class="fas fa-server mr-2"></i>{{__('Servers')}}</h5>
<h5 class="card-title"><i class="mr-2 fas fa-server"></i>{{__('Servers')}}</h5>
</div>
<div class="card-body table-responsive">

View file

@ -18,6 +18,18 @@
</div>
</section>
<div class="py-4 main">
@can('admin.roles.write')
<div class="my-3 d-flex justify-content-end">
<a href="{{route('admin.roles.create')}}" class="btn btn-primary"><i
class="fa fas fa-shield-alt pe-2"></i>{{__('Create role')}}</a>
</div>
@endcan
<div class="border-0 shadow card card-body table-wrapper table-responsive">
<h2 class="mb-4 h5">{{ __('Roles') }}</h2>
<section class="content">
<div class="container-fluid">
<div class="card">
@ -86,6 +98,5 @@
$('[data-toggle="popover"]').popover();
}
});
});
</script>

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{ __('Servers') }}</h1>
</div>
@ -28,10 +28,10 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="card-title ">
<span><i class="fas fa-server mr-2"></i>{{ __('Servers') }}</span>
<span><i class="mr-2 fas fa-server"></i>{{ __('Servers') }}</span>
</div>
<a href="{{ route('admin.servers.sync') }}" class="btn btn-primary btn-sm"><i
class="fas fa-sync mr-2"></i>{{ __('Sync') }}</a>
class="mr-2 fas fa-sync"></i>{{ __('Sync') }}</a>
</div>
</div>
<div class="card-body table-responsive">
@ -93,7 +93,6 @@
},
{
data: 'product.name',
sortable: false
},
{
data: 'suspended'

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{ __('Settings') }}</h1>
</div>
@ -37,13 +37,13 @@
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-tools mr-2"></i>{{ __('Settings') }}</h5>
<h5 class="card-title"><i class="mr-2 fas fa-tools"></i>{{ __('Settings') }}</h5>
</div>
</div>
<div class="card-body">
<!-- Sidebar Menu -->
<div class="d-flex w-100">
<div class="col-2 p-0">
<div class="p-0 col-2">
<nav class="mt-1">
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="tablist"
data-accordion="false">
@ -109,17 +109,17 @@
</div>
<!-- /.sidebar-menu -->
<!-- Content in $settings -->
<div class="col-10 p-0">
<div class="tab-content ml-3" style="width: 100%;">
<div container class="tab-pane fade container" id="icons" role="tabpanel">
<div class="p-0 col-10">
<div class="ml-3 tab-content" style="width: 100%;">
<div container class="container tab-pane fade" id="icons" role="tabpanel">
<form method="POST" enctype="multipart/form-data" class="mb-3"
action="{{ route('admin.settings.updateIcons') }}">
@csrf
@method('POST')
<div class="row">
<div class="card ml-5" style="width: 18rem;">
<span class="h3 text-center">{{ __('FavIcon') }} </span>
<div class="ml-5 card" style="width: 18rem;">
<span class="text-center h3">{{ __('FavIcon') }} </span>
<img src="{{ Storage::disk('public')->exists('favicon.ico') ? asset('storage/favicon.ico') : asset('images/controlpanel_logo.png') }}"
style="width:5vw;display: block; margin-left: auto;margin-right: auto"
class="card-img-top" alt="...">
@ -130,8 +130,8 @@
name="favicon" id="favicon">
</div>
<div class="card ml-5" style="width: 18rem;">
<span class="h3 text-center">{{ __('Icon') }} </span>
<div class="ml-5 card" style="width: 18rem;">
<span class="text-center h3">{{ __('Icon') }} </span>
<img src="{{ Storage::disk('public')->exists('icon.png') ? asset('storage/icon.png') : asset('images/controlpanel_logo.png') }}"
style="width:5vw;display: block; margin-left: auto;margin-right: auto"
class="card-img-top" alt="...">
@ -142,8 +142,8 @@
class="form-control" name="icon" id="icon">
</div>
<div class="card ml-5" style="width: 18rem;">
<span class="h3 text-center">{{ __('Login-page Logo') }} </span>
<div class="ml-5 card" style="width: 18rem;">
<span class="text-center h3">{{ __('Login-page Logo') }} </span>
<img src="{{ Storage::disk('public')->exists('logo.png') ? asset('storage/logo.png') : asset('images/controlpanel_logo.png') }}"
style="width:5vw;display: block; margin-left: auto;margin-right: auto"
class="card-img-top" alt="...">
@ -155,7 +155,7 @@
</div>
</div>
<div class="row">
<button class="btn btn-primary ml-3 mt-3">{{ __('Save') }}</button>
<button class="mt-3 ml-3 btn btn-primary">{{ __('Save') }}</button>
</div>
</form>
</div>
@ -182,14 +182,14 @@
</div>
<div class="col-8">
<div class="custom-control mb-3 d-flex align-items-center">
<div class="mb-3 custom-control d-flex align-items-center">
@if ($value['description'])
<i class="fas fa-info-circle mr-4" data-toggle="popover"
<i class="mr-4 fas fa-info-circle" data-toggle="popover"
data-trigger="hover" data-placement="top"
data-html="true"
data-content="{{ $value['description'] }}"></i>
@else
<i class="fas fa-info-circle mr-4 invisible"></i>
<i class="invisible mr-4 fas fa-info-circle"></i>
@endif
<div class="w-100">
@ -200,6 +200,12 @@
value="{{ $value['value'] }}">
@break
@case($value['type'] == 'password')
<input type="password" class="form-control"
name="{{ $key }}"
value="{{ $value['value'] }}">
@break
@case($value['type'] == 'boolean')
<input type="checkbox" name="{{ $key }}"
value="{{ $value['value'] }}"
@ -277,7 +283,7 @@
<div class="col-8">
<div class="w-100">
<div class="input-group mb-3">
<div class="mb-3 input-group">
{!! htmlScriptTagJsApi() !!}
{!! htmlFormSnippet() !!}
@error('g-recaptcha-response')
@ -294,10 +300,10 @@
<div class="row">
<div class="col-12 d-flex align-items-center justify-content-end">
<button type="submit" class="btn btn-primary float-right ">Save
<button type="submit" class="float-right btn btn-primary ">Save
</button>
<button type="reset"
class="btn btn-secondary float-right ml-2">Reset
class="float-right ml-2 btn btn-secondary">Reset
</button>
</div>
</div>

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{ __('Ticket Blacklist') }}</h1>
</div>
@ -29,7 +29,7 @@
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fas fa-users mr-2"></i>{{__('Blacklist List')}}</h5>
<h5 class="card-title"><i class="mr-2 fas fa-users"></i>{{__('Blacklist List')}}</h5>
</div>
</div>
<div class="card-body table-responsive">
@ -62,7 +62,7 @@
<div class="card-body">
<form action="{{route('admin.ticket.blacklist.add')}}" method="POST" class="ticket-form">
@csrf
<div class="custom-control mb-3 p-0">
<div class="p-0 mb-3 custom-control">
<label for="user_id">{{ __('User') }}:
<i data-toggle="popover" data-trigger="hover"
data-content="{{ __('Please note, the blacklist will make the user unable to make a ticket/reply again') }}" class="fas fa-info-circle"></i>
@ -100,7 +100,7 @@
{data: 'user' , name : 'user.name'},
{data: 'status'},
{data: 'reason'},
{data: 'created_at', sortable: false},
{data: 'created_at'},
{data: 'actions', sortable: false},
],
fnDrawCallback: function( oSettings ) {

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{__('Vouchers')}}</h1>
</div>
@ -28,9 +28,9 @@
<div class="card-header">
<div class="d-flex justify-content-between">
<h5 class="card-title"><i class="fas fa-money-check-alt mr-2"></i>{{__('Vouchers')}}</h5>
<h5 class="card-title"><i class="mr-2 fas fa-money-check-alt"></i>{{__('Vouchers')}}</h5>
<a href="{{route('admin.vouchers.create')}}" class="btn btn-sm btn-primary"><i
class="fas fa-plus mr-1"></i>{{__('Create new')}}</a>
class="mr-1 fas fa-plus"></i>{{__('Create new')}}</a>
</div>
</div>
@ -81,7 +81,7 @@
{data: 'code'},
{data: 'memo'},
{data: 'credits'},
{data: 'uses'},
{data: 'uses', sortable: false},
{data: 'expires_at'},
{data: 'actions', sortable: false},
],

View file

@ -49,8 +49,8 @@
Service or from the Service infrastructure itself (for example, the duration of a page visit).</p>
</li>
<li>
<p><strong>Website</strong> refers to CtrlPanel, accessible from <a href="controlpanel"
rel="external nofollow noopener" target="_blank">controlpanel</a></p>
<p><strong>Website</strong> refers to CtrlPanel, accessible from <a href="ctrlpanel"
rel="external nofollow noopener" target="_blank">ctrlpanel</a></p>
</li>
<li>
<p><strong>You</strong> means the individual accessing or using the Service, or the company, or other legal

View file

@ -146,6 +146,10 @@
<p>- PayPal</p>
<p>- Stripe</p>
<p>- Mollie</p>
<p></p>
<p>You agree to provide current, complete, and accurate purchase and account information for all purchases made via the Site. You further agree to promptly update account and payment information, including email address, payment method, and payment card expiration date, so that we can complete your transactions and contact you as needed. Sales tax will be added to the price of purchases as deemed required by us. We may change prices at any time. All payments shall beinU.S. dollars.</p>

View file

@ -4,7 +4,7 @@
<!-- CONTENT HEADER -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="mb-2 row">
<div class="col-sm-6">
<h1>{{ __('Profile') }}</h1>
</div>
@ -26,9 +26,9 @@
<div class="container-fluid">
<div class="row">
<div class="col-lg-12 px-0">
@if (!Auth::user()->hasVerifiedEmail() && strtolower($force_email_verification) == 'true')
<div class="alert alert-warning p-2 m-2">
<div class="px-0 col-lg-12">
@if (!Auth::user()->hasVerifiedEmail() && $force_email_verification)
<div class="p-2 m-2 alert alert-warning">
<h5><i class="icon fas fa-exclamation-circle"></i>{{ __('Required Email verification!') }}
</h5>
{{ __('You have not yet verified your email address') }}
@ -40,9 +40,9 @@
</div>
@endif
@if (is_null(Auth::user()->discordUser) && strtolower($force_discord_verification) == 'true')
@if (is_null(Auth::user()->discordUser) && $force_discord_verification)
@if (!empty($discord_client_id) && !empty($discord_client_secret))
<div class="alert alert-warning p-2 m-2">
<div class="p-2 m-2 alert alert-warning">
<h5>
<i class="icon fas fa-exclamation-circle"></i>{{ __('Required Discord verification!') }}
</h5>
@ -52,7 +52,7 @@
{{ __('Please contact support If you face any issues.') }}
</div>
@else
<div class="alert alert-danger p-2 m-2">
<div class="p-2 m-2 alert alert-danger">
<h5>
<i class="icon fas fa-exclamation-circle"></i>{{ __('Required Discord verification!') }}
</h5>
@ -72,8 +72,8 @@
<div class="card-body">
<div class="e-profile">
<div class="row">
<div class="col-12 col-sm-auto mb-4">
<div class="slim rounded-circle border-secondary border text-gray-dark"
<div class="mb-4 col-12 col-sm-auto">
<div class="border slim rounded-circle border-secondary text-gray-dark"
data-label="Change your avatar" data-max-file-size="3"
data-save-initial-image="true"
style="width: 140px;height:140px; cursor: pointer"
@ -81,9 +81,9 @@
<img src="{{ $user->getAvatar() }}" alt="avatar">
</div>
</div>
<div class="col d-flex flex-column flex-sm-row justify-content-between mb-3">
<div class="text-center text-sm-left mb-2 mb-sm-0">
<h4 class="pt-sm-2 pb-1 mb-0 text-nowrap">{{ $user->name }}</h4>
<div class="mb-3 col d-flex flex-column flex-sm-row justify-content-between">
<div class="mb-2 text-center text-sm-left mb-sm-0">
<h4 class="pb-1 mb-0 pt-sm-2 text-nowrap">{{ $user->name }}</h4>
<p class="mb-0">{{ $user->email }}
@if ($user->hasVerifiedEmail())
<i data-toggle="popover" data-trigger="hover" data-content="Verified"
@ -97,21 +97,21 @@
</p>
<div class="mt-1">
<span class="badge badge-primary"><i
class="fa fa-coins mr-2"></i>{{ $user->Credits() }}</span>
class="mr-2 fa fa-coins"></i>{{ $user->Credits() }}</span>
</div>
@if($referral_enabled)
@can("user.referral")
<div class="mt-1">
<span class="badge badge-success"><i
class="fa fa-user-check mr-2"></i>
class="mr-2 fa fa-user-check"></i>
{{__("Referral URL")}} :
<span onclick="onClickCopy()" id="RefLink" style="cursor: pointer;">
{{route("register")}}?ref={{$user->referral_code}}</span>
</span>
@else
<span class="badge badge-warning"><i
class="fa fa-user-check mr-2"></i>
class="mr-2 fa fa-user-check"></i>
{{__("You can not see your Referral Code")}}</span>
@endcan
</div>
@ -138,7 +138,7 @@
class="active nav-link">{{ __('Settings') }}</a>
</li>
</ul>
<div class="tab-content pt-3">
<div class="pt-3 tab-content">
<div class="tab-pane active">
<div class="row">
<div class="col">
@ -189,7 +189,7 @@
</div>
</div>
<div class="row">
<div class="col-12 col-sm-6 mb-3">
<div class="mb-3 col-12 col-sm-6">
<div class="mb-3"><b>{{ __('Change Password') }}</b></div>
<div class="row">
<div class="col">
@ -242,7 +242,7 @@
</div>
</div>
@if (!empty($discord_client_id) && !empty($discord_client_secret))
<div class="col-12 col-sm-5 offset-sm-1 mb-3">
<div class="mb-3 col-12 col-sm-5 offset-sm-1">
@if (is_null(Auth::user()->discordUser))
<b>{{ __('Link your discord account!') }}</b>
<div class="verify-discord">
@ -255,7 +255,7 @@
</div>
<a class="btn btn-light" href="{{ route('auth.redirect') }}">
<i class="fab fa-discord mr-2"></i>{{ __('Login with Discord') }}
<i class="mr-2 fab fa-discord"></i>{{ __('Login with Discord') }}
</a>
@else
<div class="verified-discord">
@ -263,7 +263,7 @@
<p>{{ __('You are verified!') }}</p>
</div>
</div>
<div class="row pl-2">
<div class="pl-2 row">
<div class="small-box bg-dark">
<div class="d-flex justify-content-between">
<div class="p-3">
@ -282,7 +282,7 @@
<div class="small-box-footer">
<a href="{{ route('auth.redirect') }}">
<i
class="fab fa-discord mr-1"></i>{{ __('Re-Sync Discord') }}
class="mr-1 fab fa-discord"></i>{{ __('Re-Sync Discord') }}
</a>
</div>
</div>

95
transferusers.php Normal file
View file

@ -0,0 +1,95 @@
<?php
/*
* ---------CONFIG----------
*
* FILL IN THE DATABASE INFORMATION
*/
function generateRandomString($length = 8) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
echo "ENTER YOUR PTERODACTYL DATABASE HOST: ";
$PTERODACTYL_HOST = trim(fgets(STDIN));
echo "ENTER YOUR PTERODACTYL DATABASE USER: ";
$PTERODACTYL_USER = trim(fgets(STDIN));
echo "ENTER YOUR PTERODACTYL DATABASE PASSWORD: ";
$PTERODACTYL_PASSWORD = trim(fgets(STDIN));
echo "ENTER YOUR PTERODACTYL DATABASE DATABASE NAME: ";
$PTERODACTYL_DATABASE = trim(fgets(STDIN));
$pterodb = new mysqli($PTERODACTYL_HOST, $PTERODACTYL_USER, $PTERODACTYL_PASSWORD, $PTERODACTYL_DATABASE);
if (!$pterodb) {
die('Connect Error (' . mysqli_connect_errno() . ') '
. mysqli_connect_error());
}
echo "ENTER YOUR CPGG DATABASE HOST: ";
$CPGG_HOST = trim(fgets(STDIN));
echo "ENTER YOUR CPGG DATABASE USER: ";
$CPGG_USER = trim(fgets(STDIN));
echo "ENTER YOUR CPGG DATABASE PASSWORD: ";
$CPPPG_PASSWORD = trim(fgets(STDIN));
echo "ENTER YOUR CPGG DATABASE DATABASE NAME: ";
$CPGG_DATABASE = trim(fgets(STDIN));
$cpggdb = new mysqli($CPGG_HOST, $CPGG_USER, $CPPPG_PASSWORD, $CPGG_DATABASE);
if (!$cpggdb) {
die('Connect Error (' . mysqli_connect_errno() . ') '
. mysqli_connect_error());
}
echo "ENTER THE AMOUNT OF CREDITS A USER SHOULD START WITH (default: 250)";
$init_credits = trim(fgets(STDIN));
if (empty($init_credits)) {
$init_credits = 250;
}
echo "ENTER THE AMOUNT OF SERVERS A USER SHOULD START WITH (default: 2)";
$serverlimit = trim(fgets(STDIN));
if (empty($serverlimit)) {
$serverlimit = 2;
}
$userSQL = "SELECT * FROM `users`";
$pteroUserResult = mysqli_query($pterodb, $userSQL);
$cpggUserResult = mysqli_query($cpggdb, $userSQL);
while ($pterouser = $pteroUserResult->fetch_assoc()) {
$id = $pterouser["id"];
$username = $pterouser["username"];
$email = $pterouser['email'];
$password = $pterouser['password'];
$now = date("Y-m-d H:i:s");
$role = "member";
$referral_code = generateRandomString();
try {
if ($pterouser["root_admin"]) {
$role = "admin";
}
$checkusersql = mysqli_query($cpggdb, "SELECT * FROM `users` WHERE `email` = '$email'");
if (mysqli_num_rows($checkusersql) > 0) {
echo "User ".$email." exists. Skipping! \n";
} else {
$sql = "INSERT INTO `users` (`id`, `name`, `role`, `credits`, `server_limit`, `pterodactyl_id`, `avatar`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `ip`, `last_seen`, `discord_verified_at`, `suspended`, `referral_code`) VALUES (NULL, '$username', '$role', '$init_credits', '$serverlimit', '$id', NULL, '$email', NULL, '$password', NULL, '$now', NULL, NULL, NULL, NULL, '0', '$referral_code')";
$res = mysqli_query($cpggdb, $sql);
echo "User ".$email." created \n";
}
} catch (Exception $e) {
echo "Fail: " . $e;
}
}
?>