Compare commits
339 commits
main
...
named-perm
Author | SHA1 | Date | |
---|---|---|---|
|
68d923ae98 | ||
|
159ba02c84 | ||
|
812b817968 | ||
|
6f55e306c1 | ||
|
f563dcdd74 | ||
|
8c98acb20a | ||
|
5ea4ceda6b | ||
|
14e200f88e | ||
|
cad6361e49 | ||
|
f12ab58c8e | ||
|
44a6946c44 | ||
|
9b45d76b44 | ||
|
0f138cab1d | ||
|
b2ee49f534 | ||
|
f18e8c8177 | ||
|
3656c8169d | ||
|
077ee2c7e4 | ||
|
37e505c37e | ||
|
971458a01b | ||
|
bc190dceb6 | ||
|
5c32ff2491 | ||
|
75c6dfcb79 | ||
|
d5ffe982d8 | ||
|
fa857e8a66 | ||
|
e35bbddc40 | ||
|
931f08a1d7 | ||
|
38cf81d92d | ||
|
330ea45c5b | ||
|
48cce11ebc | ||
|
7f0d934ed5 | ||
|
6a11341c0a | ||
|
391436c455 | ||
|
6f6517033e | ||
|
afe3fd23c5 | ||
|
bfb169e191 | ||
|
0e29eb171a | ||
|
9cc6e83819 | ||
|
6d61ca5cef | ||
|
3b99ae527f | ||
|
f3a341fa1b | ||
|
654932225a | ||
|
a75863909c | ||
|
03f8a7d614 | ||
|
754d4f178f | ||
|
ee5b99ce71 | ||
|
fac8668fb8 | ||
|
22413c3b30 | ||
|
04940f040b | ||
|
a471cb4021 | ||
|
7ecc29487e | ||
|
8fdac0ee46 | ||
|
f9a102509b | ||
|
90ebe1468d | ||
|
4069b5fd35 | ||
|
9ced34a51b | ||
|
df0b9f57a4 | ||
|
c4b75bf760 | ||
|
8fe6ad0d52 | ||
|
b87f3ce82e | ||
|
20772eb285 | ||
|
2ca38e5906 | ||
|
1515615507 | ||
|
d5e9f3cdaf | ||
|
0b1670ad50 | ||
|
dc232d66d4 | ||
|
3a6362402b | ||
|
74c7fc9da1 | ||
|
31bbc4f906 | ||
|
77e5b1302e | ||
|
dfd5c184af | ||
|
9a04f373f0 | ||
|
3a348ee894 | ||
|
8785325a94 | ||
|
458d6a3f26 | ||
|
57ca712254 | ||
|
fd5790c775 | ||
|
3b18b79b47 | ||
|
7795ea3d98 | ||
|
e7fd842f4b | ||
|
15a8807642 | ||
|
61d6240717 | ||
|
eae524b653 | ||
|
21e900040a | ||
|
af8eb7b1a3 | ||
|
5027986b00 | ||
|
466f903209 | ||
|
416e2f6d00 | ||
|
2c2279af56 | ||
|
d365b122db | ||
|
2161464ee3 | ||
|
48e5cae9a1 | ||
|
4873992c48 | ||
|
fc84e7d1db | ||
|
c05b4d8820 | ||
|
6d50834f9c | ||
|
4320fa9ef6 | ||
|
24ce267962 | ||
|
bad9d0ec12 | ||
|
640468acbe | ||
|
490e11572d | ||
|
8f7ad95506 | ||
|
72f3decd3f | ||
|
e03ac5dae0 | ||
|
e8a6adb4e6 | ||
|
820f47738d | ||
|
957da3dc78 | ||
|
83e153058c | ||
|
b97bc8edf7 | ||
|
da29ead3cd | ||
|
9da53bd02f | ||
|
33b402bdee | ||
|
b330e5a617 | ||
|
fec6c198d2 | ||
|
680ffefb1e | ||
|
dd962a0afb | ||
|
fd1ce85723 | ||
|
2b0321733e | ||
|
2c9b933c6d | ||
|
f37b78d750 | ||
|
67cbde2fa1 | ||
|
21529ddf65 | ||
|
0f67661aa7 | ||
|
7aa20ff30d | ||
|
b88727940c | ||
|
ce65b9860c | ||
|
1c179702d0 | ||
|
a1d302701a | ||
|
5cff1e5461 | ||
|
b58040c457 | ||
|
38db0a6ee0 | ||
|
84d8fceafc | ||
|
f53aa0e8b0 | ||
|
6f74a7379f | ||
|
166dccd3de | ||
|
259a08b3ba | ||
|
f1d5856bba | ||
|
9dbd812fc2 | ||
|
28e405a55d | ||
|
f1db71d07f | ||
|
3822c123ba | ||
|
9ed8f2bf23 | ||
|
6894e2db84 | ||
|
b4b61bcc47 | ||
|
29b5b39d17 | ||
|
78dfe881d2 | ||
|
98a894fd1e | ||
|
88ae8774e0 | ||
|
06d202f149 | ||
|
9804149beb | ||
|
fb6b2ba2ef | ||
|
34287d0014 | ||
|
ee968ff961 | ||
|
6bba6123c6 | ||
|
b1aaaeb329 | ||
|
da338aacdc | ||
|
ad5ff40761 | ||
|
04917a495e | ||
|
ae9ab59dec | ||
|
e6f02d0679 | ||
|
e965b5f5c4 | ||
|
5b95dd4d13 | ||
|
4a4f6bebdb | ||
|
6ef0b63c36 | ||
|
72fbd34a57 | ||
|
3d8a1cf53f | ||
|
b07238cbed | ||
|
d41253a1b1 | ||
|
a32d42feab | ||
|
438a3374b0 | ||
|
66039d1739 | ||
|
bc56f713ad | ||
|
8279f07156 | ||
|
a2a5426252 | ||
|
8c547414b4 | ||
|
28afc71d7f | ||
|
20f34165f6 | ||
|
f72447dfa1 | ||
|
519a13bc2d | ||
|
5e46bbbe7e | ||
|
c807476ff6 | ||
|
587e071b87 | ||
|
ea554bf288 | ||
|
0899df5945 | ||
|
d41d0a6e2d | ||
|
0ca052a2ba | ||
|
f8d7919a43 | ||
|
44db7b4847 | ||
|
59c04edf90 | ||
|
f71b6153e6 | ||
|
98566845bd | ||
|
57178da067 | ||
|
e7f19af9f9 | ||
|
dd9fc5aa9b | ||
|
7d974d3408 | ||
|
6687186fd4 | ||
|
a573454810 | ||
|
fa24a6559b | ||
|
03eef835f7 | ||
|
79432fce82 | ||
|
4c780deb02 | ||
|
88bb1395ea | ||
|
804a800d4f | ||
|
5d288cd070 | ||
|
04a4f2ab96 | ||
|
75af4c1e8f | ||
|
a4b802099c | ||
|
d51d5c66bf | ||
|
7754041532 | ||
|
a4280a6fba | ||
|
05173fd9c1 | ||
|
7d93d45197 | ||
|
eb3afbfaab | ||
|
633a3d4dfa | ||
|
e8c8d1c68d | ||
|
c9c991d164 | ||
|
fd2e65f8b3 | ||
|
83d7590b60 | ||
|
3ff60bf4ff | ||
|
490bc64f41 | ||
|
0ffceb535d | ||
|
ee96971fc6 | ||
|
02798b2c30 | ||
|
bfda91516a | ||
|
03bad4f75b | ||
|
fe2020f817 | ||
|
797d871b63 | ||
|
bcf238d6b7 | ||
|
50d0e9a37d | ||
|
0bde32bd2f | ||
|
c1165c4561 | ||
|
4f42c23a4e | ||
|
c8ce65c8e3 | ||
|
a4b56fa358 | ||
|
971226ee8e | ||
|
67d4603676 | ||
|
c44686ceda | ||
|
b4b86198e6 | ||
|
d6341d41c9 | ||
|
255671e20d | ||
|
fda40fe8bc | ||
|
27d2a48e10 | ||
|
ae787dfc4c | ||
|
94cbea528c | ||
|
56aad2a7cb | ||
|
973e77569a | ||
|
d56bc09ac5 | ||
|
cf62cdef1f | ||
|
35734579ae | ||
|
87ec49008d | ||
|
e254b2acfe | ||
|
8565faa40b | ||
|
3f4cae352a | ||
|
e4165181fe | ||
|
e310dba243 | ||
|
54e14d5f2b | ||
|
8a4273a8fb | ||
|
ecfa160450 | ||
|
2c4b3ea03e | ||
|
9ad2954f0e | ||
|
34bd88a4f6 | ||
|
759ba59988 | ||
|
7fa9bf2062 | ||
|
f8c33d43be | ||
|
642fef6864 | ||
|
994314dbe4 | ||
|
dfdba1e26e | ||
|
35e54be155 | ||
|
b1fc1f8fab | ||
|
117b75d3b7 | ||
|
907fb74734 | ||
|
2e6c03e3cc | ||
|
da0dd37559 | ||
|
5b738be6e1 | ||
|
3723b527f5 | ||
|
7e17bb62ea | ||
|
681928c3ad | ||
|
f9d5238ea2 | ||
|
7dab9d3c7e | ||
|
9777e22eab | ||
|
195cadc6a5 | ||
|
00e525a764 | ||
|
23cc70d79c | ||
|
88a8541623 | ||
|
54325fbf3e | ||
|
25db97d0a4 | ||
|
da19554a46 | ||
|
13b9d964a4 | ||
|
056e41be1a | ||
|
a7c47bff64 | ||
|
e5ae179b9d | ||
|
0f5450e01e | ||
|
bacab7bb18 | ||
|
549b2830f9 | ||
|
35479c9cfc | ||
|
7d363246b0 | ||
|
b580e47cdf | ||
|
fd21665493 | ||
|
7365cdee18 | ||
|
e3e7329d31 | ||
|
b80a8640de | ||
|
de96d6d6d3 | ||
|
1fb855a684 | ||
|
479ccdb92c | ||
|
3e4a4f32fc | ||
|
4b27665150 | ||
|
f3856c88ba | ||
|
abd8bc6b9c | ||
|
620a6b83e9 | ||
|
23a890ecba | ||
|
737bf6e8e9 | ||
|
dbbdfaa623 | ||
|
7369e8a643 | ||
|
08208cab72 | ||
|
78a6787607 | ||
|
32384044ea | ||
|
e207c0c550 | ||
|
690e0e7e12 | ||
|
16eb9eeca6 | ||
|
43119e22a5 | ||
|
dfa50141fb | ||
|
b6775f768d | ||
|
b9b860b863 | ||
|
609041a967 | ||
|
570b0b014f | ||
|
cad41ffb0a | ||
|
a26f6dd137 | ||
|
d7c0c26f35 | ||
|
6c93343200 | ||
|
fb23f43f88 | ||
|
9e8bd0a2d3 | ||
|
5c37dbcc51 | ||
|
4581b018b0 | ||
|
87bca5c52b | ||
|
b726326e99 | ||
|
d7830eeb41 | ||
|
8383fa03aa | ||
|
950278c948 | ||
|
8f38b9e023 | ||
|
3c25084ada |
220 changed files with 7599 additions and 18963 deletions
|
@ -3,9 +3,9 @@ root = true
|
|||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
|
@ -13,3 +13,24 @@ trim_trailing_whitespace = false
|
|||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.php]
|
||||
indent_size = 4
|
||||
|
||||
[*.blade.php]
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_size = 4
|
||||
|
||||
[*.jsx]
|
||||
indent_size = 2
|
||||
|
||||
[*.tsx]
|
||||
indent_size = 2
|
||||
|
||||
[*.json]
|
||||
indent_size = 4
|
||||
|
|
44
.env.example
44
.env.example
|
@ -1,28 +1,27 @@
|
|||
### --- App Settings --- ###
|
||||
APP_NAME=Controlpanel.gg
|
||||
APP_NAME=CtrlPanel.gg
|
||||
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
5
.gitattributes
vendored
|
@ -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
22
.github/CODE_OF_CONDUCT.md
vendored
Normal 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
34
.github/CONTRIBUTING.md
vendored
Normal 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! 🎉
|
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
@ -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
|
||||
|
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -5,4 +5,4 @@ contact_links:
|
|||
about: Please visit our Discord for help with your installation.
|
||||
- name: ❓ General Question
|
||||
url: https://discord.gg/4Y6HjD2uyU
|
||||
about: Please visit our Discord for general questions about the ControlPanel.
|
||||
about: Please visit our Discord for general questions about the CtrlPanel.
|
||||
|
|
37
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
37
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal 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
17
.github/SECURITY.md
vendored
Normal 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.
|
34
.gitignore
vendored
34
.gitignore
vendored
|
@ -1,28 +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
|
||||
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
|
||||
|
|
|
@ -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
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 ControlPanel.gg
|
||||
Copyright (c) 2021 CtrlPanel.gg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
106
README.md
106
README.md
|
@ -1,67 +1,87 @@
|
|||
### Features
|
||||
<div align="center">
|
||||
<img src="https://ctrlpanel.gg/img/controlpanel.png" width="128" alt="" />
|
||||
</div>
|
||||
|
||||
- PayPal Integration
|
||||
- Stripe Integration
|
||||
- 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!
|
||||
# CtrlPanel-gg
|
||||
|
||||
# ControlPanel-gg
|
||||
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.
|
||||
|
||||
![controlpanel](https://user-images.githubusercontent.com/67899387/214684708-739c1d21-06e8-4dec-a4f1-81533a46cc7e.png)
|
||||
![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)
|
||||
|
||||
![CtrlPanel](https://user-images.githubusercontent.com/67899387/214684708-739c1d21-06e8-4dec-a4f1-81533a46cc7e.png)
|
||||
|
||||
![](https://img.shields.io/endpoint?label=v0.9%20Installations&url=https%3A%2F%2Fmarket.ctrlpanel.gg%2Fcallhome.php%3Fgetinstalls)
|
||||
![](https://img.shields.io/badge/Overall%20Installations-5000%2B-green)
|
||||
![](https://img.shields.io/github/stars/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/forks/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/tag/ControlPanel-gg/dashboard) [![Crowdin](https://badges.crowdin.net/controlpanelgg/localized.svg)](https://crowdin.com/project/controlpanelgg) ![](https://img.shields.io/github/issues/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/license/ControlPanel-gg/dashboard) ![](https://img.shields.io/discord/787829714483019826)
|
||||
## About
|
||||
## ⭐ Features
|
||||
|
||||
ControlPanel's Dashboard is a dashboard application designed to offer clients a management tool to manage their pterodactyl servers. This dashboard comes with a credit-based billing solution that credits users hourly for each server they have and suspends them if they run out of credits.
|
||||
- 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!
|
||||
|
||||
This dashboard offers an easy to use and free billing solution for all starting and experienced hosting providers. This dashboard has many customisation options and added discord Oauth verification to offer a solid link between your discord server and your dashboard. You can check our [Demo here](https://demo.controlpanel.gg "Demo").
|
||||
## ⛰️ Live Demo
|
||||
|
||||
### [Installation](https://ctrlpanel.gg/docs/intro "Installation")
|
||||
Try it!
|
||||
|
||||
### [Updating](https://ctrlpanel.gg/docs/Installation/updating "Updating")
|
||||
Demo Server: [demo.CtrlPanel.gg](https://demo.CtrlPanel.gg)
|
||||
|
||||
### [Discord](https://discord.gg/4Y6HjD2uyU "Discord")
|
||||
<!-- It is a temporary live demo; all data will be deleted. -->
|
||||
|
||||
### [Contributing](https://ctrlpanel.gg/docs/Contributing/contributing "Contributing")
|
||||
## 🔧 How to Install
|
||||
|
||||
### [Donating](https://ctrlpanel.gg/docs/Contributing/donating "Donating")
|
||||
### 🐳 Docker
|
||||
|
||||
Soon...
|
||||
|
||||
<!-- ```bash
|
||||
docker run ...
|
||||
```
|
||||
|
||||
# Preview
|
||||
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)
|
||||
|
||||
### Server Creation
|
||||
![image](https://user-images.githubusercontent.com/67899387/214687234-d1ae58c0-5667-4e99-ac39-adeaabfcc7f2.png)
|
||||
more info: [Docker](docker documentation link here) -->
|
||||
|
||||
### Overview
|
||||
![image](https://user-images.githubusercontent.com/67899387/214685859-03c8d9e1-c685-4a07-979f-df2e88ec3931.png)
|
||||
### 💪🏻 Non-Docker
|
||||
|
||||
### Example server products
|
||||
![image](https://user-images.githubusercontent.com/67899387/214686950-218e1ede-6a1f-4e53-b3f4-fe1abc371a9c.png)
|
||||
Requirements:
|
||||
|
||||
### Ticket System
|
||||
![image](https://user-images.githubusercontent.com/67899387/214687123-0a3d0f8f-b53c-4b0d-869a-4d5df45f5184.png)
|
||||
- Platform
|
||||
- Major Linux distros such as Debian, Ubuntu, CentOS, Fedora, and ArchLinux etc.
|
||||
- Windows 10 (x64), Windows Server ...
|
||||
|
||||
### Voucher System
|
||||
![image](https://user-images.githubusercontent.com/67899387/214686578-ec9f0b0f-6047-4665-835f-70594b56dfd5.png)
|
||||
Follow the [documentation](https://ctrlpanel.gg/docs/intro) to know how to install.
|
||||
|
||||
### Partner System
|
||||
![image](https://user-images.githubusercontent.com/67899387/214686321-36ba97a3-4181-4e60-9ba3-c9b318fe66a8.png)
|
||||
### 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.
|
||||
|
|
8
app/Classes/AbstractExtension.php
Normal file
8
app/Classes/AbstractExtension.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Classes;
|
||||
|
||||
abstract class AbstractExtension
|
||||
{
|
||||
abstract public static function getConfig(): array;
|
||||
}
|
63
app/Classes/LegacySettingsMigration.php
Normal file
63
app/Classes/LegacySettingsMigration.php
Normal 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}");
|
||||
}
|
||||
}
|
||||
}
|
14
app/Classes/PaymentExtensions.php
Normal file
14
app/Classes/PaymentExtensions.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Classes;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Models\ShopProduct;
|
||||
|
||||
abstract class PaymentExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* Returns the redirect url of the payment gateway to redirect the user to
|
||||
*/
|
||||
abstract public static function getRedirectUrl(Payment $payment, ShopProduct $shopProduct, string $totalPriceString): string;
|
||||
}
|
|
@ -266,6 +266,7 @@ class PterodactylClient
|
|||
'docker_image' => $egg->docker_image,
|
||||
'startup' => $egg->startup,
|
||||
'environment' => $egg->getEnvironmentVariables(),
|
||||
'oom_disabled' => !$server->product->oom_killer,
|
||||
'limits' => [
|
||||
'memory' => $server->product->memory,
|
||||
'swap' => $server->product->swap,
|
||||
|
@ -378,6 +379,7 @@ class PterodactylClient
|
|||
'io' => $product->io,
|
||||
'cpu' => $product->cpu,
|
||||
'threads' => null,
|
||||
'oom_disabled' => !$server->product->oom_killer,
|
||||
'feature_limits' => [
|
||||
'databases' => $product->databases,
|
||||
'backups' => $product->backups,
|
||||
|
|
139
app/Console/Commands/ChargeServers.php
Normal file
139
app/Console/Commands/ChargeServers.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Notifications\ServersSuspendedNotification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ChargeServers extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'servers:charge';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Charge all users with severs that are due to be charged';
|
||||
|
||||
/**
|
||||
* A list of users that have to be notified
|
||||
* @var array
|
||||
*/
|
||||
protected $usersToNotify = [];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Server::whereNull('suspended')->with('user', 'product')->chunk(10, function ($servers) {
|
||||
/** @var Server $server */
|
||||
foreach ($servers as $server) {
|
||||
/** @var Product $product */
|
||||
$product = $server->product;
|
||||
/** @var User $user */
|
||||
$user = $server->user;
|
||||
|
||||
$billing_period = $product->billing_period;
|
||||
|
||||
|
||||
// check if server is due to be charged by comparing its last_billed date with the current date and the billing period
|
||||
$newBillingDate = null;
|
||||
switch ($billing_period) {
|
||||
case 'annually':
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addYear();
|
||||
break;
|
||||
case 'half-annually':
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addMonths(6);
|
||||
break;
|
||||
case 'quarterly':
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addMonths(3);
|
||||
break;
|
||||
case 'monthly':
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addMonth();
|
||||
break;
|
||||
case 'weekly':
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addWeek();
|
||||
break;
|
||||
case 'daily':
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addDay();
|
||||
break;
|
||||
case 'hourly':
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addHour();
|
||||
default:
|
||||
$newBillingDate = Carbon::parse($server->last_billed)->addHour();
|
||||
break;
|
||||
};
|
||||
|
||||
if (!($newBillingDate->isPast())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if the server is canceled or if user has enough credits to charge the server or
|
||||
if ($server->canceled || $user->credits <= $product->price) {
|
||||
try {
|
||||
// suspend server
|
||||
$this->line("<fg=yellow>{$server->name}</> from user: <fg=blue>{$user->name}</> has been <fg=red>suspended!</>");
|
||||
$server->suspend();
|
||||
|
||||
// add user to notify list
|
||||
if (!in_array($user, $this->usersToNotify)) {
|
||||
array_push($this->usersToNotify, $user);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->error($exception->getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// charge credits to user
|
||||
$this->line("<fg=blue>{$user->name}</> Current credits: <fg=green>{$user->credits}</> Credits to be removed: <fg=red>{$product->price}</>");
|
||||
$user->decrement('credits', $product->price);
|
||||
|
||||
// update server last_billed date in db
|
||||
DB::table('servers')->where('id', $server->id)->update(['last_billed' => $newBillingDate]);
|
||||
}
|
||||
|
||||
return $this->notifyUsers();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function notifyUsers()
|
||||
{
|
||||
if (!empty($this->usersToNotify)) {
|
||||
/** @var User $user */
|
||||
foreach ($this->usersToNotify as $user) {
|
||||
$this->line("<fg=yellow>Notified user:</> <fg=blue>{$user->name}</>");
|
||||
$user->notify(new ServersSuspendedNotification());
|
||||
}
|
||||
}
|
||||
|
||||
#reset array
|
||||
$this->usersToNotify = array();
|
||||
return true;
|
||||
}
|
||||
}
|
42
app/Console/Commands/DeleteExpiredCoupons.php
Normal file
42
app/Console/Commands/DeleteExpiredCoupons.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Settings\CouponSettings;
|
||||
use App\Models\Coupon;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
|
||||
class DeleteExpiredCoupons extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'coupons:delete';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Delete expired coupons from DB.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(CouponSettings $couponSettings)
|
||||
{
|
||||
if ($couponSettings->delete_coupon_on_expires) {
|
||||
$expired_coupons = Coupon::where('expires_at', '<=', Carbon::now(config('app.timezone')))->get();
|
||||
|
||||
foreach ($expired_coupons as $expired_coupon) {
|
||||
$expired_coupon->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ class GetGithubVersion extends Command
|
|||
public function handle()
|
||||
{
|
||||
try{
|
||||
$latestVersion = Http::get('https://api.github.com/repos/controlpanel-gg/dashboard/tags')->json()[0]['name'];
|
||||
$latestVersion = Http::get('https://api.github.com/repos/ctrlpanel-gg/panel/tags')->json()[0]['name'];
|
||||
Storage::disk('local')->put('latestVersion', $latestVersion);
|
||||
} catch (Exception $e) {
|
||||
Storage::disk('local')->put('latestVersion', "unknown");
|
||||
|
|
|
@ -101,6 +101,8 @@ class MakeUserCommand extends Command
|
|||
['Referral code', $user->referral_code],
|
||||
]);
|
||||
|
||||
$user->syncRoles(1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,17 @@ use Illuminate\Support\Facades\Storage;
|
|||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
Commands\ChargeCreditsCommand::class,
|
||||
Commands\ChargeServers::class,
|
||||
Commands\DeleteExpiredCoupons::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
|
@ -16,9 +27,10 @@ class Kernel extends ConsoleKernel
|
|||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->command('credits:charge')->hourly();
|
||||
$schedule->command('servers:charge')->everyMinute();
|
||||
$schedule->command('cp:versioncheck:get')->daily();
|
||||
$schedule->command('payments:open:clear')->daily();
|
||||
$schedule->command('coupons:delete')->daily();
|
||||
|
||||
//log cronjob activity
|
||||
$schedule->call(function () {
|
||||
|
|
12
app/Enums/PaymentStatus.php
Normal file
12
app/Enums/PaymentStatus.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
// Payment status are open, processing, paid and canceled
|
||||
enum PaymentStatus: String
|
||||
{
|
||||
case OPEN = "open";
|
||||
case PROCESSING = "processing";
|
||||
case PAID = "paid";
|
||||
case CANCELED = "canceled";
|
||||
}
|
28
app/Events/CouponUsedEvent.php
Normal file
28
app/Events/CouponUsedEvent.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Coupon;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CouponUsedEvent
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public Coupon $coupon;
|
||||
public string $couponCode;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $couponCode)
|
||||
{
|
||||
|
||||
$this->couponCode = $couponCode;
|
||||
$this->coupon = Coupon::where('code', $couponCode)->first();
|
||||
}
|
||||
}
|
|
@ -2,17 +2,20 @@
|
|||
|
||||
namespace App\Extensions\PaymentGateways\Mollie;
|
||||
|
||||
use App\Helpers\AbstractExtension;
|
||||
use App\Classes\AbstractExtension;
|
||||
use App\Enums\PaymentStatus;
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Models\PartnerDiscount;
|
||||
use App\Models\Payment;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use App\Models\Coupon;
|
||||
use App\Traits\Coupon as CouponTrait;
|
||||
use App\Events\CouponUsedEvent;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
@ -23,6 +26,8 @@ use Illuminate\Support\Facades\Http;
|
|||
*/
|
||||
class MollieExtension extends AbstractExtension
|
||||
{
|
||||
use CouponTrait;
|
||||
|
||||
public static function getConfig(): array
|
||||
{
|
||||
return [
|
||||
|
@ -33,31 +38,10 @@ class MollieExtension extends AbstractExtension
|
|||
];
|
||||
}
|
||||
|
||||
static function pay(Request $request): void
|
||||
public static function getRedirectUrl(Payment $payment, ShopProduct $shopProduct, string $totalPriceString): string
|
||||
{
|
||||
$url = 'https://api.mollie.com/v2/payments';
|
||||
$settings = new MollieSettings();
|
||||
|
||||
$user = Auth::user();
|
||||
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
|
||||
$discount = PartnerDiscount::getDiscount();
|
||||
|
||||
// create a new payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => null,
|
||||
'payment_method' => 'mollie',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'open',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
try {
|
||||
$response = Http::withHeaders([
|
||||
'Content-Type' => 'application/json',
|
||||
|
@ -65,7 +49,7 @@ class MollieExtension extends AbstractExtension
|
|||
])->post($url, [
|
||||
'amount' => [
|
||||
'currency' => $shopProduct->currency_code,
|
||||
'value' => number_format($shopProduct->getTotalPrice(), 2, '.', ''),
|
||||
'value' => $totalPriceString,
|
||||
],
|
||||
'description' => "Order #{$payment->id} - " . $shopProduct->name,
|
||||
'redirectUrl' => route('payment.MollieSuccess'),
|
||||
|
@ -78,31 +62,21 @@ class MollieExtension extends AbstractExtension
|
|||
|
||||
if ($response->status() != 201) {
|
||||
Log::error('Mollie Payment: ' . $response->body());
|
||||
$payment->delete();
|
||||
|
||||
Redirect::route('store.index')->with('error', __('Payment failed'))->send();
|
||||
return;
|
||||
throw new Exception('Payment failed');
|
||||
}
|
||||
|
||||
$payment->update([
|
||||
'payment_id' => $response->json()['id'],
|
||||
]);
|
||||
|
||||
Redirect::away($response->json()['_links']['checkout']['href'])->send();
|
||||
return;
|
||||
return $response->json()['_links']['checkout']['href'];
|
||||
} catch (Exception $ex) {
|
||||
Log::error('Mollie Payment: ' . $ex->getMessage());
|
||||
$payment->delete();
|
||||
|
||||
Redirect::route('store.index')->with('error', __('Payment failed'))->send();
|
||||
return;
|
||||
throw new Exception('Payment failed');
|
||||
}
|
||||
}
|
||||
|
||||
static function success(Request $request): void
|
||||
{
|
||||
$payment = Payment::findOrFail($request->input('payment'));
|
||||
$payment->status = 'pending';
|
||||
$payment->status = PaymentStatus::PROCESSING;
|
||||
$payment->save();
|
||||
|
||||
Redirect::route('home')->with('success', 'Your payment is being processed')->send();
|
||||
return;
|
||||
|
@ -123,16 +97,15 @@ class MollieExtension extends AbstractExtension
|
|||
return response()->json(['success' => false]);
|
||||
}
|
||||
|
||||
$payment = Payment::findOrFail($response->json()['metadata']['payment_id']);
|
||||
$payment->status->update([
|
||||
'status' => $response->json()['status'],
|
||||
]);
|
||||
|
||||
$payment = Payment::findOrFail($response->json()['metadata']['payment_id']);
|
||||
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
|
||||
event(new PaymentEvent($payment, $payment, $shopProduct));
|
||||
|
||||
if ($response->json()['status'] == 'paid') {
|
||||
$user = User::findOrFail($payment->user_id);
|
||||
$payment->status = PaymentStatus::PAID;
|
||||
$payment->save();
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
|
|
|
@ -15,12 +15,7 @@ class MollieSettings extends Settings
|
|||
return 'mollie';
|
||||
}
|
||||
|
||||
public static function encrypted(): array
|
||||
{
|
||||
return [
|
||||
'api_key',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public static function getOptionInputData()
|
||||
{
|
||||
|
|
|
@ -4,10 +4,6 @@ use Illuminate\Support\Facades\Route;
|
|||
use App\Extensions\PaymentGateways\Mollie\MollieExtension;
|
||||
|
||||
Route::middleware(['web', 'auth'])->group(function () {
|
||||
Route::get('payment/MolliePay/{shopProduct}', function () {
|
||||
MollieExtension::pay(request());
|
||||
})->name('payment.MolliePay');
|
||||
|
||||
Route::get(
|
||||
'payment/MollieSuccess',
|
||||
function () {
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
|
||||
namespace App\Extensions\PaymentGateways\PayPal;
|
||||
|
||||
use App\Helpers\AbstractExtension;
|
||||
use App\Events\CouponUsedEvent;
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Extensions\PaymentGateways\PayPal\PayPalSettings;
|
||||
use App\Classes\PaymentExtension;
|
||||
use App\Enums\PaymentStatus;
|
||||
use App\Models\PartnerDiscount;
|
||||
use App\Models\Payment;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use App\Models\Coupon;
|
||||
use App\Traits\Coupon as CouponTrait;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
|
@ -25,8 +29,10 @@ use PayPalHttp\HttpException;
|
|||
/**
|
||||
* Summary of PayPalExtension
|
||||
*/
|
||||
class PayPalExtension extends AbstractExtension
|
||||
class PayPalExtension extends PaymentExtension
|
||||
{
|
||||
use CouponTrait;
|
||||
|
||||
public static function getConfig(): array
|
||||
{
|
||||
return [
|
||||
|
@ -35,29 +41,8 @@ class PayPalExtension extends AbstractExtension
|
|||
];
|
||||
}
|
||||
|
||||
static function PaypalPay(Request $request): void
|
||||
public static function getRedirectUrl(Payment $payment, ShopProduct $shopProduct, string $totalPriceString): string
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
|
||||
$discount = PartnerDiscount::getDiscount();
|
||||
|
||||
// create a new payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => null,
|
||||
'payment_method' => 'paypal',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'open',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
$request = new OrdersCreateRequest();
|
||||
$request->prefer('return=representation');
|
||||
$request->body = [
|
||||
|
@ -65,18 +50,16 @@ class PayPalExtension extends AbstractExtension
|
|||
"purchase_units" => [
|
||||
[
|
||||
"reference_id" => uniqid(),
|
||||
"description" => $shopProduct->display . ($discount ? (" (" . __('Discount') . " " . $discount . '%)') : ""),
|
||||
"amount" => [
|
||||
"value" => $shopProduct->getTotalPrice(),
|
||||
"description" => $shopProduct->display,
|
||||
"amount" => [
|
||||
"value" => $totalPriceString,
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'breakdown' => [
|
||||
'item_total' =>
|
||||
[
|
||||
'item_total' => [
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'value' => $shopProduct->getPriceAfterDiscount(),
|
||||
'value' => $totalPriceString
|
||||
],
|
||||
'tax_total' =>
|
||||
[
|
||||
'tax_total' => [
|
||||
'currency_code' => strtoupper($shopProduct->currency_code),
|
||||
'value' => $shopProduct->getTaxValue(),
|
||||
]
|
||||
|
@ -87,11 +70,9 @@ class PayPalExtension extends AbstractExtension
|
|||
"application_context" => [
|
||||
"cancel_url" => route('payment.Cancel'),
|
||||
"return_url" => route('payment.PayPalSuccess', ['payment' => $payment->id]),
|
||||
'brand_name' => config('app.name', 'Controlpanel.GG'),
|
||||
'brand_name' => config('app.name', 'CtrlPanel.GG'),
|
||||
'shipping_preference' => 'NO_SHIPPING'
|
||||
]
|
||||
|
||||
|
||||
];
|
||||
|
||||
try {
|
||||
|
@ -108,17 +89,15 @@ class PayPalExtension extends AbstractExtension
|
|||
throw new \Exception('No redirect link found');
|
||||
}
|
||||
|
||||
Redirect::away($response->result->links[1]->href)->send();
|
||||
return;
|
||||
return $response->result->links[1]->href;
|
||||
} catch (HttpException $ex) {
|
||||
Log::error('PayPal Payment: ' . $ex->getMessage());
|
||||
$payment->delete();
|
||||
|
||||
Redirect::route('store.index')->with('error', __('Payment failed'))->send();
|
||||
return;
|
||||
throw new \Exception('PayPal Payment: ' . $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static function PaypalSuccess(Request $laravelRequest): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
@ -136,10 +115,11 @@ class PayPalExtension extends AbstractExtension
|
|||
if ($response->statusCode == 201 || $response->statusCode == 200) {
|
||||
//update payment
|
||||
$payment->update([
|
||||
'status' => 'paid',
|
||||
'status' => PaymentStatus::PAID,
|
||||
'payment_id' => $response->result->id,
|
||||
]);
|
||||
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
event(new PaymentEvent($user, $payment, $shopProduct));
|
||||
|
||||
|
@ -151,7 +131,7 @@ class PayPalExtension extends AbstractExtension
|
|||
dd($response);
|
||||
} else {
|
||||
$payment->update([
|
||||
'status' => 'cancelled',
|
||||
'status' => PaymentStatus::CANCELED,
|
||||
'payment_id' => $response->result->id,
|
||||
]);
|
||||
abort(500);
|
||||
|
@ -163,7 +143,7 @@ class PayPalExtension extends AbstractExtension
|
|||
dd($ex->getMessage());
|
||||
} else {
|
||||
$payment->update([
|
||||
'status' => 'cancelled',
|
||||
'status' => PaymentStatus::CANCELED,
|
||||
'payment_id' => $response->result->id,
|
||||
]);
|
||||
abort(422);
|
||||
|
@ -173,7 +153,8 @@ class PayPalExtension extends AbstractExtension
|
|||
|
||||
static function getPayPalClient(): PayPalHttpClient
|
||||
{
|
||||
$environment = env('APP_ENV') == 'local'
|
||||
|
||||
$environment = config('app.env') == 'local'
|
||||
? new SandboxEnvironment(self::getPaypalClientId(), self::getPaypalClientSecret())
|
||||
: new ProductionEnvironment(self::getPaypalClientId(), self::getPaypalClientSecret());
|
||||
return new PayPalHttpClient($environment);
|
||||
|
@ -184,7 +165,7 @@ class PayPalExtension extends AbstractExtension
|
|||
static function getPaypalClientId(): string
|
||||
{
|
||||
$settings = new PayPalSettings();
|
||||
return env('APP_ENV') == 'local' ? $settings->sandbox_client_id : $settings->client_id;
|
||||
return config('app.env') == 'local' ? $settings->sandbox_client_id : $settings->client_id;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
|
@ -192,6 +173,6 @@ class PayPalExtension extends AbstractExtension
|
|||
static function getPaypalClientSecret(): string
|
||||
{
|
||||
$settings = new PayPalSettings();
|
||||
return env('APP_ENV') == 'local' ? $settings->sandbox_client_secret : $settings->client_secret;
|
||||
return config('app.env') == 'local' ? $settings->sandbox_client_secret : $settings->client_secret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,15 +18,7 @@ class PayPalSettings extends Settings
|
|||
}
|
||||
|
||||
|
||||
public static function encrypted(): array
|
||||
{
|
||||
return [
|
||||
'client_id',
|
||||
'client_secret',
|
||||
'sandbox_client_id',
|
||||
'sandbox_client_secret'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Summary of optionInputData array
|
||||
|
|
|
@ -77,6 +77,10 @@ class CreatePayPalSettings extends SettingsMigration
|
|||
// Always get the first value of the key.
|
||||
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
|
||||
|
||||
if (is_null($old_value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
|
|
|
@ -4,10 +4,6 @@ use Illuminate\Support\Facades\Route;
|
|||
use App\Extensions\PaymentGateways\PayPal\PayPalExtension;
|
||||
|
||||
Route::middleware(['web', 'auth'])->group(function () {
|
||||
Route::get('payment/PayPalPay/{shopProduct}', function () {
|
||||
PayPalExtension::PaypalPay(request());
|
||||
})->name('payment.PayPalPay');
|
||||
|
||||
Route::get(
|
||||
'payment/PayPalSuccess',
|
||||
function () {
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
|
||||
namespace App\Extensions\PaymentGateways\Stripe;
|
||||
|
||||
use App\Helpers\AbstractExtension;
|
||||
use App\Classes\AbstractExtension;
|
||||
use App\Enums\PaymentStatus;
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\CouponUsedEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Extensions\PaymentGateways\Stripe\StripeSettings;
|
||||
use App\Models\PartnerDiscount;
|
||||
use App\Models\Payment;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use App\Models\Coupon;
|
||||
use App\Traits\Coupon as CouponTrait;
|
||||
use App\Notifications\ConfirmPaymentNotification;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
|
@ -21,6 +25,8 @@ use Stripe\StripeClient;
|
|||
|
||||
class StripeExtension extends AbstractExtension
|
||||
{
|
||||
use CouponTrait;
|
||||
|
||||
public static function getConfig(): array
|
||||
{
|
||||
return [
|
||||
|
@ -31,40 +37,14 @@ class StripeExtension extends AbstractExtension
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param ShopProduct $shopProduct
|
||||
*/
|
||||
public static function StripePay(Request $request)
|
||||
public static function getRedirectUrl(Payment $payment, ShopProduct $shopProduct, string $totalPriceString): string
|
||||
{
|
||||
$user = Auth::user();
|
||||
$shopProduct = ShopProduct::findOrFail($request->shopProduct);
|
||||
|
||||
// check if the price is valid for stripe
|
||||
if (!self::checkPriceAmount($shopProduct->getTotalPrice(), strtoupper($shopProduct->currency_code), 'stripe')) {
|
||||
Redirect::route('home')->with('error', __('The product you chose can\'t be purchased with this payment method. The total amount is too small. Please buy a bigger amount or try a different payment method.'))->send();
|
||||
return;
|
||||
// check if the total price is valid for stripe
|
||||
$totalPriceNumber = floatval($totalPriceString);
|
||||
if (!self::checkPriceAmount($totalPriceNumber, strtoupper($shopProduct->currency_code), 'stripe')) {
|
||||
throw new Exception('Invalid price amount');
|
||||
}
|
||||
|
||||
$discount = PartnerDiscount::getDiscount();
|
||||
|
||||
|
||||
// create payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => null,
|
||||
'payment_method' => 'stripe',
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'open',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $shopProduct->price - ($shopProduct->price * $discount / 100),
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
$stripeClient = self::getStripeClient();
|
||||
$request = $stripeClient->checkout->sessions->create([
|
||||
'line_items' => [
|
||||
|
@ -72,10 +52,10 @@ class StripeExtension extends AbstractExtension
|
|||
'price_data' => [
|
||||
'currency' => $shopProduct->currency_code,
|
||||
'product_data' => [
|
||||
'name' => $shopProduct->display . ($discount ? (' (' . __('Discount') . ' ' . $discount . '%)') : ''),
|
||||
'name' => $shopProduct->display,
|
||||
'description' => $shopProduct->description,
|
||||
],
|
||||
'unit_amount_decimal' => round($shopProduct->getPriceAfterDiscount() * 100, 2),
|
||||
'unit_amount_decimal' => $totalPriceString,
|
||||
],
|
||||
'quantity' => 1,
|
||||
],
|
||||
|
@ -102,7 +82,7 @@ class StripeExtension extends AbstractExtension
|
|||
],
|
||||
]);
|
||||
|
||||
Redirect::to($request->url)->send();
|
||||
return $request->url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,7 +95,6 @@ class StripeExtension extends AbstractExtension
|
|||
$payment = Payment::findOrFail($request->input('payment'));
|
||||
$shopProduct = ShopProduct::findOrFail($payment->shop_item_product_id);
|
||||
|
||||
|
||||
Redirect::route('home')->with('success', 'Please wait for success')->send();
|
||||
|
||||
$stripeClient = self::getStripeClient();
|
||||
|
@ -133,12 +112,11 @@ class StripeExtension extends AbstractExtension
|
|||
//update payment
|
||||
$payment->update([
|
||||
'payment_id' => $paymentSession->payment_intent,
|
||||
'status' => 'paid',
|
||||
'status' => PaymentStatus::PAID,
|
||||
]);
|
||||
|
||||
//payment notification
|
||||
$user->notify(new ConfirmPaymentNotification($payment));
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
event(new PaymentEvent($user, $payment, $shopProduct));
|
||||
|
||||
|
@ -150,7 +128,7 @@ class StripeExtension extends AbstractExtension
|
|||
//update payment
|
||||
$payment->update([
|
||||
'payment_id' => $paymentSession->payment_intent,
|
||||
'status' => 'processing',
|
||||
'status' => PaymentStatus::PROCESSING,
|
||||
]);
|
||||
|
||||
event(new PaymentEvent($user, $payment, $shopProduct));
|
||||
|
@ -191,7 +169,7 @@ class StripeExtension extends AbstractExtension
|
|||
//update payment db entry status
|
||||
$payment->update([
|
||||
'payment_id' => $payment->payment_id ?? $paymentIntent->id,
|
||||
'status' => 'paid'
|
||||
'status' => PaymentStatus::PAID,
|
||||
]);
|
||||
|
||||
//payment notification
|
||||
|
@ -281,7 +259,7 @@ class StripeExtension extends AbstractExtension
|
|||
* @return bool
|
||||
* @description check if the amount is higher than the minimum amount for the stripe gateway
|
||||
*/
|
||||
public static function checkPriceAmount($amount, $currencyCode, $payment_method)
|
||||
public static function checkPriceAmount(float $amount, string $currencyCode, string $payment_method)
|
||||
{
|
||||
$minimums = [
|
||||
"USD" => [
|
||||
|
|
|
@ -19,15 +19,7 @@ class StripeSettings extends Settings
|
|||
return 'stripe';
|
||||
}
|
||||
|
||||
public static function encrypted(): array
|
||||
{
|
||||
return [
|
||||
"secret_key",
|
||||
"endpoint_secret",
|
||||
"test_secret_key",
|
||||
"test_endpoint_secret"
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public static function getOptionInputData()
|
||||
{
|
||||
|
|
|
@ -10,9 +10,9 @@ class CreateStripeSettings extends SettingsMigration
|
|||
$table_exists = DB::table('settings_old')->exists();
|
||||
|
||||
$this->migrator->addEncrypted('stripe.secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:SECRET') : null);
|
||||
$this->migrator->addEncrypted('stripe.endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') : null);
|
||||
$this->migrator->add('stripe.endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') : null);
|
||||
$this->migrator->addEncrypted('stripe.test_secret_key', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:TEST_SECRET') : null);
|
||||
$this->migrator->addEncrypted('stripe.test_endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET') : null);
|
||||
$this->migrator->add('stripe.test_endpoint_secret', $table_exists ? $this->getOldValue('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET') : null);
|
||||
$this->migrator->add('stripe.enabled', false);
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,10 @@ class CreateStripeSettings extends SettingsMigration
|
|||
// Always get the first value of the key.
|
||||
$old_value = DB::table('settings_old')->where('key', '=', $key)->get(['value', 'type'])->first();
|
||||
|
||||
if (is_null($old_value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
|
|
|
@ -4,10 +4,6 @@ use Illuminate\Support\Facades\Route;
|
|||
use App\Extensions\PaymentGateways\Stripe\StripeExtension;
|
||||
|
||||
Route::middleware(['web', 'auth'])->group(function () {
|
||||
Route::get('payment/StripePay/{shopProduct}', function () {
|
||||
StripeExtension::StripePay(request());
|
||||
})->name('payment.StripePay');
|
||||
|
||||
Route::get(
|
||||
'payment/StripeSuccess',
|
||||
function () {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
// create a abstract class for the extension that will contain all the methods that will be used in the extension
|
||||
abstract class AbstractExtension
|
||||
{
|
||||
abstract public static function getConfig(): array;
|
||||
}
|
|
@ -18,8 +18,9 @@ class ExtensionHelper
|
|||
foreach ($extensionNamespaces as $extensionNamespace) {
|
||||
$extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
|
||||
}
|
||||
|
||||
// remove base path from every extension but keep app/Extensions/...
|
||||
$extensions = array_map(fn ($item) => str_replace('/', '\\', str_replace(app_path() . '/', 'App/', $item)), $extensions);
|
||||
$extensions = array_map(fn ($item) => str_replace(app_path() . '/', 'App/', $item), $extensions);
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
@ -33,7 +34,7 @@ class ExtensionHelper
|
|||
{
|
||||
$extensions = glob(app_path() . '/Extensions/' . $namespace . '/*', GLOB_ONLYDIR);
|
||||
// remove base path from every extension but keep app/Extensions/...
|
||||
$extensions = array_map(fn ($item) => str_replace('/', '\\', str_replace(app_path() . '/', 'App/', $item)), $extensions);
|
||||
$extensions = array_map(fn ($item) => str_replace(app_path() . '/', 'App/', $item), $extensions);
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
@ -60,7 +61,10 @@ class ExtensionHelper
|
|||
public static function getAllExtensionClasses()
|
||||
{
|
||||
$extensions = self::getAllExtensions();
|
||||
// add the ExtensionClass to the end of the namespace
|
||||
|
||||
// replace all slashes with backslashes
|
||||
$extensions = array_map(fn ($item) => str_replace('/', '\\', $item), $extensions);
|
||||
// add the ExtensionClass to the end of the namespace
|
||||
$extensions = array_map(fn ($item) => $item . '\\' . basename($item) . 'Extension', $extensions);
|
||||
// filter out non existing extension classes
|
||||
$extensions = array_filter($extensions, fn ($item) => class_exists($item));
|
||||
|
@ -76,6 +80,9 @@ class ExtensionHelper
|
|||
public static function getAllExtensionClassesByNamespace(string $namespace)
|
||||
{
|
||||
$extensions = self::getAllExtensionsByNamespace($namespace);
|
||||
|
||||
// replace all slashes with backslashes
|
||||
$extensions = array_map(fn ($item) => str_replace('/', '\\', $item), $extensions);
|
||||
// add the ExtensionClass to the end of the namespace
|
||||
$extensions = array_map(fn ($item) => $item . '\\' . basename($item) . 'Extension', $extensions);
|
||||
// filter out non existing extension classes
|
||||
|
@ -97,7 +104,7 @@ class ExtensionHelper
|
|||
if (!(basename($extension) == $extensionName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$extension = str_replace('/', '\\', $extension);
|
||||
$extensionClass = $extension . '\\' . $extensionName . 'Extension';
|
||||
return $extensionClass;
|
||||
}
|
||||
|
@ -177,10 +184,13 @@ class ExtensionHelper
|
|||
{
|
||||
$extensions = self::getAllExtensions();
|
||||
|
||||
|
||||
$settings = [];
|
||||
foreach ($extensions as $extension) {
|
||||
|
||||
$extensionName = basename($extension);
|
||||
|
||||
// replace all slashes with backslashes
|
||||
$extension = str_replace('/', '\\', $extension);
|
||||
$settingsClass = $extension . '\\' . $extensionName . 'Settings';
|
||||
if (class_exists($settingsClass)) {
|
||||
$settings[] = $settingsClass;
|
||||
|
@ -193,6 +203,9 @@ class ExtensionHelper
|
|||
public static function getExtensionSettings(string $extensionName)
|
||||
{
|
||||
$extension = self::getExtension($extensionName);
|
||||
// replace all slashes with backslashes
|
||||
$extension = str_replace('/', '\\', $extension);
|
||||
|
||||
$settingClass = $extension . '\\' . $extensionName . 'Settings';
|
||||
|
||||
if (class_exists($settingClass)) {
|
||||
|
@ -207,6 +220,6 @@ class ExtensionHelper
|
|||
*/
|
||||
private static function extensionNameToPath(string $extensionName)
|
||||
{
|
||||
return app_path() . '/' . str_replace('\\', '/', str_replace('App\\', '', $extensionName));
|
||||
return app_path() . '/' . str_replace('App/', '', $extensionName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use Spatie\Activitylog\Models\Activity;
|
|||
|
||||
class ActivityLogController extends Controller
|
||||
{
|
||||
const VIEW_PERMISSION = "admin.logs.read";
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -21,6 +22,9 @@ class ActivityLogController extends Controller
|
|||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->checkPermission(self::VIEW_PERMISSION);
|
||||
|
||||
|
||||
$cronLogs = Storage::disk('logs')->exists('cron.log') ? Storage::disk('logs')->get('cron.log') : null;
|
||||
|
||||
if ($request->input('search')) {
|
||||
|
|
|
@ -16,6 +16,8 @@ use Illuminate\Http\Response;
|
|||
|
||||
class ApplicationApiController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.api.read";
|
||||
const WRITE_PERMISSION = "admin.api.write";
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -23,6 +25,8 @@ class ApplicationApiController extends Controller
|
|||
*/
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.api.index', [
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
]);
|
||||
|
@ -35,6 +39,8 @@ class ApplicationApiController extends Controller
|
|||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.api.create');
|
||||
}
|
||||
|
||||
|
@ -76,6 +82,7 @@ class ApplicationApiController extends Controller
|
|||
*/
|
||||
public function edit(ApplicationApi $applicationApi)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
return view('admin.api.edit', [
|
||||
'applicationApi' => $applicationApi,
|
||||
]);
|
||||
|
@ -107,6 +114,8 @@ class ApplicationApiController extends Controller
|
|||
*/
|
||||
public function destroy(ApplicationApi $applicationApi)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$applicationApi->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('api key has been removed!'));
|
||||
|
|
242
app/Http/Controllers/Admin/CouponController.php
Normal file
242
app/Http/Controllers/Admin/CouponController.php
Normal file
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Coupon;
|
||||
use App\Settings\LocaleSettings;
|
||||
use App\Traits\Coupon as CouponTrait;
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class CouponController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.coupons.read";
|
||||
const WRITE_PERMISSION = "admin.coupons.write";
|
||||
|
||||
use CouponTrait;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.coupons.index', [
|
||||
'locale_datatables' => $localeSettings->datatables
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.coupons.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$coupon_code = $request->input('code');
|
||||
$random_codes_amount = $request->input('range_codes');
|
||||
$rules = $this->requestRules($request);
|
||||
|
||||
// If for some reason you pass both fields at once.
|
||||
if ($coupon_code && $random_codes_amount) {
|
||||
return redirect()->back()->with('error', __('Only one of the two code inputs must be provided.'))->withInput($request->all());
|
||||
}
|
||||
|
||||
if (!$coupon_code && !$random_codes_amount) {
|
||||
return redirect()->back()->with('error', __('At least one of the two code inputs must be provided.'))->withInput($request->all());
|
||||
}
|
||||
|
||||
$request->validate($rules);
|
||||
|
||||
if (array_key_exists('range_codes', $rules)) {
|
||||
$data = [];
|
||||
$coupons = Coupon::generateRandomCoupon($random_codes_amount);
|
||||
|
||||
// Scroll through all the randomly generated coupons.
|
||||
foreach ($coupons as $coupon) {
|
||||
$data[] = [
|
||||
'code' => $coupon,
|
||||
'type' => $request->input('type'),
|
||||
'value' => $request->input('value'),
|
||||
'max_uses' => $request->input('max_uses'),
|
||||
'expires_at' => $request->input('expires_at'),
|
||||
'created_at' => Carbon::now(), // Does not fill in by itself when using the 'insert' method.
|
||||
'updated_at' => Carbon::now()
|
||||
];
|
||||
}
|
||||
Coupon::insert($data);
|
||||
} else {
|
||||
Coupon::create($request->except('_token'));
|
||||
}
|
||||
|
||||
return redirect()->route('admin.coupons.index')->with('success', __("The coupon's was registered successfully."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param \App\Models\Coupon $coupon
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(Coupon $coupon)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Coupon $coupon
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Coupon $coupon)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.coupons.edit', [
|
||||
'coupon' => $coupon,
|
||||
'expired_at' => $coupon->expires_at ? Carbon::createFromTimestamp($coupon->expires_at) : null
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Models\Coupon $coupon
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, Coupon $coupon)
|
||||
{
|
||||
$coupon_code = $request->input('code');
|
||||
$random_codes_amount = $request->input('range_codes');
|
||||
$rules = $this->requestRules($request);
|
||||
|
||||
// If for some reason you pass both fields at once.
|
||||
if ($coupon_code && $random_codes_amount) {
|
||||
return redirect()->back()->with('error', __('Only one of the two code inputs must be provided.'))->withInput($request->all());
|
||||
}
|
||||
|
||||
if (!$coupon_code && !$random_codes_amount) {
|
||||
return redirect()->back()->with('error', __('At least one of the two code inputs must be provided.'))->withInput($request->all());
|
||||
}
|
||||
|
||||
$request->validate($rules);
|
||||
$coupon->update($request->except('_token'));
|
||||
|
||||
return redirect()->route('admin.coupons.index')->with('success', __('coupon has been updated!'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Coupon $coupon
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Coupon $coupon)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
$coupon->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('coupon has been removed!'));
|
||||
}
|
||||
|
||||
private function requestRules(Request $request)
|
||||
{
|
||||
$coupon_code = $request->input('code');
|
||||
$random_codes_amount = $request->input('range_codes');
|
||||
$rules = [
|
||||
"type" => "required|string|in:percentage,amount",
|
||||
"max_uses" => "required|integer|digits_between:1,100",
|
||||
"value" => "required|numeric|between:0,100",
|
||||
"expires_at" => "nullable|date|after:" . Carbon::now()->format(Coupon::formatDate())
|
||||
];
|
||||
|
||||
if ($coupon_code) {
|
||||
$rules['code'] = "required|string|min:4";
|
||||
} elseif ($random_codes_amount) {
|
||||
$rules['range_codes'] = 'required|integer|digits_between:1,100';
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function redeem(Request $request)
|
||||
{
|
||||
return $this->validateCoupon($request->user(), $request->input('couponCode'), $request->input('productId'));
|
||||
}
|
||||
|
||||
public function dataTable()
|
||||
{
|
||||
$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="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="mr-1 btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
';
|
||||
})
|
||||
->addColumn('status', function (Coupon $coupon) {
|
||||
$color = ($coupon->derived_status == 'VALID') ? 'success' : 'danger';
|
||||
$status = str_replace('_', ' ', $coupon->derived_status);
|
||||
|
||||
return '<span class="badge badge-'.$color.'">'.$status.'</span>';
|
||||
})
|
||||
->editColumn('uses', function (Coupon $coupon) {
|
||||
return "{$coupon->uses} / {$coupon->max_uses}";
|
||||
})
|
||||
->editColumn('value', function (Coupon $coupon) {
|
||||
if ($coupon->type === 'percentage') {
|
||||
return $coupon->value . "%";
|
||||
}
|
||||
|
||||
return number_format($coupon->value, 2, '.', '');
|
||||
})
|
||||
->editColumn('expires_at', function (Coupon $coupon) {
|
||||
if (!$coupon->expires_at) {
|
||||
return __('Never');
|
||||
}
|
||||
|
||||
return Carbon::createFromTimestamp($coupon->expires_at);
|
||||
})
|
||||
->editColumn('created_at', function(Coupon $coupon) {
|
||||
return Carbon::createFromTimeString($coupon->created_at);
|
||||
})
|
||||
->editColumn('code', function (Coupon $coupon) {
|
||||
return "<code>{$coupon->code}</code>";
|
||||
})
|
||||
->orderColumn('status', 'derived_status $1')
|
||||
->rawColumns(['actions', 'code', 'status'])
|
||||
->make();
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ use Qirolab\Theme\Theme;
|
|||
|
||||
class LegalController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.legal.read";
|
||||
const WRITE_PERMISSION = "admin.legal.write";
|
||||
/**
|
||||
* Display
|
||||
*
|
||||
|
@ -17,6 +19,8 @@ class LegalController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
$tos = File::get(Theme::path($path = 'views', "default") . '/information/tos-content.blade.php');
|
||||
$privacy = File::get(Theme::path($path = 'views', "default") . '/information/privacy-content.blade.php');
|
||||
$imprint = File::get(Theme::path($path = 'views', "default") . '/information/imprint-content.blade.php');
|
||||
|
@ -29,6 +33,8 @@ class LegalController extends Controller
|
|||
}
|
||||
|
||||
public function update(Request $request){
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
$tos = $request->tos;
|
||||
$privacy = $request->privacy;
|
||||
$imprint = $request->imprint;
|
||||
|
|
|
@ -19,6 +19,8 @@ use Carbon\Carbon;
|
|||
|
||||
class OverViewController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.overview.read";
|
||||
const SYNC_PERMISSION = "admin.overview.sync";
|
||||
public const TTL = 86400;
|
||||
|
||||
private $pterodactyl;
|
||||
|
@ -27,14 +29,18 @@ class OverViewController extends Controller
|
|||
{
|
||||
$this->pterodactyl = new PterodactylClient($ptero_settings);
|
||||
}
|
||||
|
||||
|
||||
public function index(GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
//Get counters
|
||||
$counters = collect();
|
||||
//Set basic variables in the collection
|
||||
$counters->put('users', User::query()->count());
|
||||
$counters->put('credits', number_format(User::query()->where('role', '!=', 'admin')->sum('credits'), 2, '.', ''));
|
||||
$counters->put('users', collect());
|
||||
$counters['users']->active = User::where("suspended", 0)->count();
|
||||
$counters['users']->total = User::query()->count();
|
||||
$counters->put('credits', number_format(User::query()->whereHas("roles", function($q){ $q->where("id", "!=", "1"); })->sum('credits'), 2, '.', ''));
|
||||
$counters->put('payments', Payment::query()->count());
|
||||
$counters->put('eggs', Egg::query()->count());
|
||||
$counters->put('nests', Nest::query()->count());
|
||||
|
@ -169,7 +175,8 @@ class OverViewController extends Controller
|
|||
$nodeId = $server['attributes']['node'];
|
||||
|
||||
if ($CPServer = Server::query()->where('pterodactyl_id', $server['attributes']['id'])->first()) {
|
||||
$price = Product::query()->where('id', $CPServer->product_id)->first()->price;
|
||||
$product = Product::query()->where('id', $CPServer->product_id)->first();
|
||||
$price = $product->getMonthlyPrice();
|
||||
if (! $CPServer->suspended) {
|
||||
$counters['earnings']->active += $price;
|
||||
$counters['servers']->active++;
|
||||
|
@ -225,6 +232,8 @@ class OverViewController extends Controller
|
|||
*/
|
||||
public function syncPterodactyl()
|
||||
{
|
||||
$this->checkPermission(self::SYNC_PERMISSION);
|
||||
|
||||
Node::syncNodes();
|
||||
Egg::syncEggs();
|
||||
|
||||
|
|
|
@ -11,8 +11,12 @@ use Illuminate\Http\Request;
|
|||
|
||||
class PartnerController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.partners.read";
|
||||
const WRITE_PERMISSION = "admin.partners.write";
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.partners.index', [
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
]);
|
||||
|
@ -25,6 +29,8 @@ class PartnerController extends Controller
|
|||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.partners.create', [
|
||||
'partners' => PartnerDiscount::get(),
|
||||
'users' => User::orderBy('name')->get(),
|
||||
|
@ -62,6 +68,8 @@ class PartnerController extends Controller
|
|||
*/
|
||||
public function edit(PartnerDiscount $partner)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.partners.edit', [
|
||||
'partners' => PartnerDiscount::get(),
|
||||
'partner' => $partner,
|
||||
|
@ -98,6 +106,8 @@ class PartnerController extends Controller
|
|||
*/
|
||||
public function destroy(PartnerDiscount $partner)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$partner->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('partner has been removed!'));
|
||||
|
@ -112,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>
|
||||
';
|
||||
})
|
||||
|
@ -135,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();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Events\CouponUsedEvent;
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
@ -9,6 +10,7 @@ use App\Models\PartnerDiscount;
|
|||
use App\Models\Payment;
|
||||
use App\Models\User;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Traits\Coupon as CouponTrait;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
|
@ -18,16 +20,26 @@ use Illuminate\Http\RedirectResponse;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Helpers\ExtensionHelper;
|
||||
use App\Settings\CouponSettings;
|
||||
use App\Settings\GeneralSettings;
|
||||
use App\Settings\LocaleSettings;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
const BUY_PERMISSION = 'user.shop.buy';
|
||||
const VIEW_PERMISSION = "admin.payments.read";
|
||||
|
||||
use CouponTrait;
|
||||
|
||||
/**
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
{
|
||||
$this->checkPermission(self::VIEW_PERMISSION);
|
||||
|
||||
|
||||
return view('admin.payments.index')->with([
|
||||
'payments' => Payment::paginate(15),
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
|
@ -39,8 +51,10 @@ class PaymentController extends Controller
|
|||
* @param ShopProduct $shopProduct
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function checkOut(ShopProduct $shopProduct, GeneralSettings $general_settings)
|
||||
public function checkOut(ShopProduct $shopProduct, GeneralSettings $general_settings, CouponSettings $coupon_settings)
|
||||
{
|
||||
$this->checkPermission(self::BUY_PERMISSION);
|
||||
|
||||
$discount = PartnerDiscount::getDiscount();
|
||||
$price = $shopProduct->price - ($shopProduct->price * $discount / 100);
|
||||
|
||||
|
@ -73,7 +87,8 @@ class PaymentController extends Controller
|
|||
'total' => $shopProduct->getTotalPrice(),
|
||||
'paymentGateways' => $paymentGateways,
|
||||
'productIsFree' => $price <= 0,
|
||||
'credits_display_name' => $general_settings->credits_display_name
|
||||
'credits_display_name' => $general_settings->credits_display_name,
|
||||
'isCouponsEnabled' => $coupon_settings->enabled,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -114,16 +129,67 @@ class PaymentController extends Controller
|
|||
|
||||
public function pay(Request $request)
|
||||
{
|
||||
$product = ShopProduct::find($request->product_id);
|
||||
$paymentGateway = $request->payment_method;
|
||||
try {
|
||||
$user = Auth::user();
|
||||
$user = User::findOrFail($user->id);
|
||||
$productId = $request->product_id;
|
||||
$shopProduct = ShopProduct::findOrFail($productId);
|
||||
$discount = PartnerDiscount::getDiscount();
|
||||
|
||||
// on free products, we don't need to use a payment gateway
|
||||
$realPrice = $product->price - ($product->price * PartnerDiscount::getDiscount() / 100);
|
||||
if ($realPrice <= 0) {
|
||||
return $this->handleFreeProduct($product);
|
||||
|
||||
$paymentGateway = $request->payment_method;
|
||||
$couponCode = $request->coupon_code;
|
||||
|
||||
$subtotal = $shopProduct->price;
|
||||
|
||||
// Apply Coupon
|
||||
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);
|
||||
}
|
||||
|
||||
// Format the total price to a readable string
|
||||
$totalPriceString = number_format($subtotal, 2, '.', '');
|
||||
|
||||
// create a new payment
|
||||
$payment = Payment::create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => null,
|
||||
'payment_method' => $paymentGateway,
|
||||
'type' => $shopProduct->type,
|
||||
'status' => 'open',
|
||||
'amount' => $shopProduct->quantity,
|
||||
'price' => $totalPriceString,
|
||||
'tax_value' => $shopProduct->getTaxValue(),
|
||||
'tax_percent' => $shopProduct->getTaxPercent(),
|
||||
'total_price' => $shopProduct->getTotalPrice(),
|
||||
'currency_code' => $shopProduct->currency_code,
|
||||
'shop_item_product_id' => $shopProduct->id,
|
||||
]);
|
||||
|
||||
$paymentGatewayExtension = ExtensionHelper::getExtensionClass($paymentGateway);
|
||||
$redirectUrl = $paymentGatewayExtension::getRedirectUrl($payment, $shopProduct, $totalPriceString);
|
||||
|
||||
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.'));
|
||||
}
|
||||
|
||||
return redirect()->route('payment.' . $paymentGateway . 'Pay', ['shopProduct' => $product->id]);
|
||||
return redirect()->away($redirectUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,10 @@ use Illuminate\Http\Request;
|
|||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.products.read";
|
||||
const WRITE_PERMISSION = "admin.products.write";
|
||||
const EDIT_PERMISSION = "admin.products.edit";
|
||||
const DELETE_PERMISSION = "admin.products.delete";
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -26,6 +30,8 @@ class ProductController extends Controller
|
|||
*/
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.products.index', [
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
]);
|
||||
|
@ -38,6 +44,7 @@ class ProductController extends Controller
|
|||
*/
|
||||
public function create(GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
return view('admin.products.create', [
|
||||
'locations' => Location::with('nodes')->get(),
|
||||
'nests' => Nest::with('eggs')->get(),
|
||||
|
@ -45,10 +52,13 @@ class ProductController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function clone(Product $product)
|
||||
public function clone(Product $product, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.products.create', [
|
||||
'product' => $product,
|
||||
'credits_display_name' => $general_settings->credits_display_name,
|
||||
'locations' => Location::with('nodes')->get(),
|
||||
'nests' => Nest::with('eggs')->get(),
|
||||
]);
|
||||
|
@ -78,10 +88,14 @@ class ProductController extends Controller
|
|||
'nodes.*' => 'required|exists:nodes,id',
|
||||
'eggs.*' => 'required|exists:eggs,id',
|
||||
'disabled' => 'nullable',
|
||||
'oom_killer' => 'nullable',
|
||||
'billing_period' => 'required|in:hourly,daily,weekly,monthly,quarterly,half-annually,annually',
|
||||
]);
|
||||
|
||||
|
||||
$disabled = ! is_null($request->input('disabled'));
|
||||
$product = Product::create(array_merge($request->all(), ['disabled' => $disabled]));
|
||||
$oomkiller = ! is_null($request->input('oom_killer'));
|
||||
$product = Product::create(array_merge($request->all(), ['disabled' => $disabled, 'oom_killer' => $oomkiller]));
|
||||
|
||||
//link nodes and eggs
|
||||
$product->eggs()->attach($request->input('eggs'));
|
||||
|
@ -98,6 +112,8 @@ class ProductController extends Controller
|
|||
*/
|
||||
public function show(Product $product, UserSettings $user_settings, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.products.show', [
|
||||
'product' => $product,
|
||||
'minimum_credits' => $user_settings->min_credits_to_make_server,
|
||||
|
@ -113,6 +129,8 @@ class ProductController extends Controller
|
|||
*/
|
||||
public function edit(Product $product, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::EDIT_PERMISSION);
|
||||
|
||||
return view('admin.products.edit', [
|
||||
'product' => $product,
|
||||
'locations' => Location::with('nodes')->get(),
|
||||
|
@ -146,10 +164,13 @@ class ProductController extends Controller
|
|||
'nodes.*' => 'required|exists:nodes,id',
|
||||
'eggs.*' => 'required|exists:eggs,id',
|
||||
'disabled' => 'nullable',
|
||||
'oom_killer' => 'nullable',
|
||||
'billing_period' => 'required|in:hourly,daily,weekly,monthly,quarterly,half-annually,annually',
|
||||
]);
|
||||
|
||||
$disabled = ! is_null($request->input('disabled'));
|
||||
$product->update(array_merge($request->all(), ['disabled' => $disabled]));
|
||||
$oomkiller = ! is_null($request->input('oom_killer'));
|
||||
$product->update(array_merge($request->all(), ['disabled' => $disabled, 'oom_killer' => $oomkiller]));
|
||||
|
||||
//link nodes and eggs
|
||||
$product->eggs()->detach();
|
||||
|
@ -167,6 +188,8 @@ class ProductController extends Controller
|
|||
*/
|
||||
public function disable(Product $product)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$product->update(['disabled' => ! $product->disabled]);
|
||||
|
||||
return redirect()->route('admin.products.index')->with('success', 'Product has been updated!');
|
||||
|
@ -180,6 +203,8 @@ class ProductController extends Controller
|
|||
*/
|
||||
public function destroy(Product $product)
|
||||
{
|
||||
$this->checkPermission(self::DELETE_PERMISSION);
|
||||
|
||||
$servers = $product->servers()->count();
|
||||
if ($servers > 0) {
|
||||
return redirect()->back()->with('error', "Product cannot be removed while it's linked to {$servers} servers");
|
||||
|
@ -198,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>
|
||||
';
|
||||
})
|
||||
|
@ -222,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 '
|
||||
|
@ -239,6 +265,9 @@ 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) {
|
||||
return $product->oom_killer ? __("enabled") : __("disabled");
|
||||
})
|
||||
->editColumn('created_at', function (Product $product) {
|
||||
return $product->created_at ? $product->created_at->diffForHumans() : '';
|
||||
})
|
||||
|
|
219
app/Http/Controllers/Admin/RoleController.php
Normal file
219
app/Http/Controllers/Admin/RoleController.php
Normal file
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Role;
|
||||
|
||||
class RoleController extends Controller
|
||||
{
|
||||
|
||||
const READ_PERMISSION = "admin.roles.read";
|
||||
const CREATE_PERMISSION = "admin.roles.create";
|
||||
const EDIT_PERMISSION = "admin.roles.edit";
|
||||
const DELETE_PERMISSION = "admin.roles.delete";
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
//datatables
|
||||
if ($request->ajax()) {
|
||||
return $this->dataTableQuery();
|
||||
}
|
||||
|
||||
$html = $this->dataTable();
|
||||
return view('admin.roles.index', compact('html'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission(self::CREATE_PERMISSION);
|
||||
|
||||
$permissions = Permission::all();
|
||||
|
||||
return view('admin.roles.edit', compact('permissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$this->checkPermission(self::CREATE_PERMISSION);
|
||||
|
||||
$role = Role::create([
|
||||
'name' => $request->name,
|
||||
'color' => $request->color,
|
||||
'power' => $request->power
|
||||
]);
|
||||
|
||||
if ($request->permissions) {
|
||||
$role->givePermissionTo($request->permissions);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('admin.roles.index')
|
||||
->with('success', __('Role saved'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
abort(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param Role $role
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function edit(Role $role)
|
||||
{
|
||||
$this->checkPermission(self::EDIT_PERMISSION);
|
||||
|
||||
if(Auth::user()->roles[0]->power < $role->power){
|
||||
return back()->with("error","You dont have enough Power to edit that Role");
|
||||
}
|
||||
|
||||
$permissions = Permission::all();
|
||||
|
||||
return view('admin.roles.edit', compact('role', 'permissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param Role $role
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function update(Request $request, Role $role)
|
||||
{
|
||||
$this->checkPermission(self::EDIT_PERMISSION);
|
||||
|
||||
if(Auth::user()->roles[0]->power < $role->power){
|
||||
return back()->with("error","You dont have enough Power to edit that Role");
|
||||
}
|
||||
|
||||
if ($request->permissions) {
|
||||
if($role->id != 1){ //disable admin permissions change
|
||||
$role->syncPermissions($request->permissions);
|
||||
}
|
||||
}
|
||||
|
||||
//if($role->id == 1 || $role->id == 3 || $role->id == 4){ //dont let the user change the names of these roles
|
||||
// $role->update([
|
||||
// 'color' => $request->color
|
||||
// ]);
|
||||
//}else{
|
||||
$role->update([
|
||||
'name' => $request->name,
|
||||
'color' => $request->color
|
||||
]);
|
||||
//}
|
||||
|
||||
//if($role->id == 1){
|
||||
// return redirect()->route('admin.roles.index')->with('success', __('Role updated. Name and Permissions of this Role cannot be changed'));
|
||||
//}elseif($role->id == 4 || $role->id == 3){
|
||||
// return redirect()->route('admin.roles.index')->with('success', __('Role updated. Name of this Role cannot be changed'));
|
||||
// }else{
|
||||
return redirect()
|
||||
->route('admin.roles.index')
|
||||
->with('success', __('Role saved'));
|
||||
//}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function destroy(Role $role)
|
||||
{
|
||||
$this->checkPermission(self::DELETE_PERMISSION);
|
||||
|
||||
if($role->id == 1 || $role->id == 3 || $role->id == 4){ //cannot delete the hard coded roles
|
||||
return back()->with("error","You cannot delete that role");
|
||||
}
|
||||
|
||||
$users = User::role($role)->get();
|
||||
|
||||
foreach($users as $user){
|
||||
//$user->syncRoles(['Member']);
|
||||
$user->syncRoles(4);
|
||||
}
|
||||
|
||||
$role->delete();
|
||||
|
||||
return redirect()
|
||||
->route('admin.roles.index')
|
||||
->with('success', __('Role removed'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dataTable()
|
||||
{
|
||||
$query = Role::query()->withCount(['users', 'permissions'])->get();
|
||||
|
||||
return datatables($query)
|
||||
->editColumn('id', function (Role $role) {
|
||||
return $role->id;
|
||||
})
|
||||
->addColumn('actions', function (Role $role) {
|
||||
return '
|
||||
<a title="Edit" href="'.route("admin.roles.edit", $role).'" class="btn btn-sm btn-info"><i
|
||||
class="fa fas fa-edit"></i></a>
|
||||
<form class="d-inline" method="post" action="'.route("admin.roles.destroy", $role).'">
|
||||
' . csrf_field() . '
|
||||
' . method_field("DELETE") . '
|
||||
<button title="Delete" type="submit" class="btn btn-sm btn-danger confirm"><i
|
||||
class="fa fas fa-trash"></i></button>
|
||||
</form>
|
||||
';
|
||||
})
|
||||
|
||||
->editColumn('name', function (Role $role) {
|
||||
return "<span style='background-color: $role->color' class='badge'>$role->name</span>";
|
||||
})
|
||||
->editColumn('users_count', function ($query) {
|
||||
return $query->users_count;
|
||||
})
|
||||
->editColumn('permissions_count', function ($query){
|
||||
return $query->permissions_count;
|
||||
})
|
||||
->editColumn('power', function (Role $role){
|
||||
return $role->power;
|
||||
})
|
||||
->rawColumns(['actions', 'name'])
|
||||
->make(true);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,13 @@ use Illuminate\Support\Facades\Log;
|
|||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
|
||||
const READ_PERMISSION = "admin.servers.read";
|
||||
const WRITE_PERMISSION = "admin.servers.write";
|
||||
const SUSPEND_PERMISSION = "admin.servers.suspend";
|
||||
const CHANGEOWNER_PERMISSION = "admin.servers.write.owner";
|
||||
const CHANGE_IDENTIFIER_PERMISSION = "admin.servers.write.identifier";
|
||||
const DELETE_PERMISSION = "admin.servers.delete";
|
||||
private $pterodactyl;
|
||||
|
||||
public function __construct(PterodactylSettings $ptero_settings)
|
||||
|
@ -34,6 +41,8 @@ class ServerController extends Controller
|
|||
*/
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.servers.index', [
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
]);
|
||||
|
@ -47,6 +56,8 @@ class ServerController extends Controller
|
|||
*/
|
||||
public function edit(Server $server)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
// get all users from the database
|
||||
$users = User::all();
|
||||
|
||||
|
@ -70,7 +81,7 @@ class ServerController extends Controller
|
|||
]);
|
||||
|
||||
|
||||
if ($request->get('user_id') != $server->user_id) {
|
||||
if ($request->get('user_id') != $server->user_id && $this->can(self::CHANGEOWNER_PERMISSION)) {
|
||||
// find the user
|
||||
$user = User::findOrFail($request->get('user_id'));
|
||||
|
||||
|
@ -89,7 +100,10 @@ class ServerController extends Controller
|
|||
}
|
||||
|
||||
// update the identifier
|
||||
$server->identifier = $request->get('identifier');
|
||||
if ($this->can(self::CHANGE_IDENTIFIER_PERMISSION)) {
|
||||
|
||||
$server->identifier = $request->get('identifier');
|
||||
}
|
||||
$server->save();
|
||||
|
||||
return redirect()->route('admin.servers.index')->with('success', 'Server updated!');
|
||||
|
@ -103,6 +117,7 @@ class ServerController extends Controller
|
|||
*/
|
||||
public function destroy(Server $server)
|
||||
{
|
||||
$this->checkPermission(self::DELETE_PERMISSION);
|
||||
try {
|
||||
$server->delete();
|
||||
|
||||
|
@ -113,11 +128,31 @@ class ServerController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
* Cancel the Server billing cycle.
|
||||
*
|
||||
* @param Server $server
|
||||
* @return RedirectResponse|Response
|
||||
*/
|
||||
public function cancel(Server $server)
|
||||
{
|
||||
try {
|
||||
$server->update([
|
||||
'canceled' => now(),
|
||||
]);
|
||||
return redirect()->route('servers.index')->with('success', __('Server canceled'));
|
||||
} catch (Exception $e) {
|
||||
return redirect()->route('servers.index')->with('error', __('An exception has occurred while trying to cancel the server"') . $e->getMessage() . '"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function toggleSuspended(Server $server)
|
||||
{
|
||||
$this->checkPermission(self::SUSPEND_PERMISSION);
|
||||
|
||||
try {
|
||||
$server->isSuspended() ? $server->unSuspend() : $server->suspend();
|
||||
} catch (Exception $exception) {
|
||||
|
|
|
@ -15,6 +15,8 @@ use Qirolab\Theme\Theme;
|
|||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -23,6 +25,7 @@ class SettingsController extends Controller
|
|||
public function index()
|
||||
{
|
||||
|
||||
|
||||
// get all other settings in app/Settings directory
|
||||
// group items by file name like $categories
|
||||
$settings = collect();
|
||||
|
@ -61,6 +64,7 @@ class SettingsController extends Controller
|
|||
'type' => $optionInputData[$key]['type'] ?? 'string',
|
||||
'description' => $optionInputData[$key]['description'] ?? '',
|
||||
'options' => $optionInputData[$key]['options'] ?? [],
|
||||
'identifier' => $optionInputData[$key]['identifier'] ?? 'option'
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -92,6 +96,9 @@ class SettingsController extends Controller
|
|||
public function update(Request $request)
|
||||
{
|
||||
$category = request()->get('category');
|
||||
|
||||
$this->checkPermission("settings." . strtolower($category) . ".write");
|
||||
|
||||
$settings_class = request()->get('settings_class');
|
||||
|
||||
if (method_exists($settings_class, 'getValidations')) {
|
||||
|
@ -113,19 +120,44 @@ class SettingsController extends Controller
|
|||
$rp = new \ReflectionProperty($settingsClass, $key);
|
||||
$rpType = $rp->getType();
|
||||
|
||||
|
||||
if ($rpType == 'bool') {
|
||||
$settingsClass->$key = $request->has($key);
|
||||
continue;
|
||||
}
|
||||
if ($rp->name == 'available') {
|
||||
$settingsClass->$key = implode(",", $request->$key);
|
||||
continue;
|
||||
}
|
||||
|
||||
$nullable = $rpType->allowsNull();
|
||||
if ($nullable) $settingsClass->$key = $request->input($key) ?? null;
|
||||
else $settingsClass->$key = $request->input($key);
|
||||
}
|
||||
|
||||
$settingsClass->save();
|
||||
|
||||
|
||||
return Redirect::to('admin/settings' . '#' . $category)->with('success', 'Settings updated successfully.');
|
||||
}
|
||||
|
||||
public function updateIcons(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'icon' => 'nullable|max:10000|mimes:jpg,png,jpeg',
|
||||
'logo' => 'nullable|max:10000|mimes:jpg,png,jpeg',
|
||||
'favicon' => 'nullable|max:10000|mimes:ico',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('icon')) {
|
||||
$request->file('icon')->storeAs('public', 'icon.png');
|
||||
}
|
||||
if ($request->hasFile('logo')) {
|
||||
$request->file('logo')->storeAs('public', 'logo.png');
|
||||
}
|
||||
if ($request->hasFile('favicon')) {
|
||||
$request->file('favicon')->storeAs('public', 'favicon.ico');
|
||||
}
|
||||
|
||||
return Redirect::to('admin/settings')->with('success', 'Icons updated successfully.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Settings\GeneralSettings;
|
||||
use App\Settings\LocaleSettings;
|
||||
|
@ -11,12 +12,15 @@ use Illuminate\Contracts\View\View;
|
|||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ShopProductController extends Controller
|
||||
{
|
||||
|
||||
const READ_PERMISSION = 'admin.store.read';
|
||||
const WRITE_PERMISSION = 'admin.store.write';
|
||||
const DISABLE_PERMISSION = 'admin.store.disable';
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -24,6 +28,8 @@ class ShopProductController extends Controller
|
|||
*/
|
||||
public function index(LocaleSettings $locale_settings, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
$isStoreEnabled = $general_settings->store_enabled;
|
||||
|
||||
|
||||
|
@ -40,6 +46,8 @@ class ShopProductController extends Controller
|
|||
*/
|
||||
public function create(GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.store.create', [
|
||||
'currencyCodes' => config('currency_codes'),
|
||||
'credits_display_name' => $general_settings->credits_display_name
|
||||
|
@ -78,6 +86,8 @@ class ShopProductController extends Controller
|
|||
*/
|
||||
public function edit(ShopProduct $shopProduct, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
return view('admin.store.edit', [
|
||||
'currencyCodes' => config('currency_codes'),
|
||||
'shopProduct' => $shopProduct,
|
||||
|
@ -117,6 +127,8 @@ class ShopProductController extends Controller
|
|||
*/
|
||||
public function disable(ShopProduct $shopProduct)
|
||||
{
|
||||
$this->checkPermission(self::DISABLE_PERMISSION);
|
||||
|
||||
$shopProduct->update(['disabled' => !$shopProduct->disabled]);
|
||||
|
||||
return redirect()->route('admin.store.index')->with('success', __('Product has been updated!'));
|
||||
|
@ -130,6 +142,7 @@ class ShopProductController extends Controller
|
|||
*/
|
||||
public function destroy(ShopProduct $shopProduct)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
$shopProduct->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('Store item has been removed!'));
|
||||
|
@ -143,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 '
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Moderation;
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
|
@ -9,15 +9,20 @@ use Illuminate\Http\Request;
|
|||
|
||||
class TicketCategoryController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.tickets.category.read";
|
||||
const WRITE_PERMISSION = "admin.tickets.category.write";
|
||||
/**
|
||||
*
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
$categories = TicketCategory::all();
|
||||
return view('moderator.ticket.category')->with("categories",$categories);
|
||||
return view('admin.ticket.category')->with("categories",$categories);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +33,8 @@ class TicketCategoryController extends Controller
|
|||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:191',
|
||||
]);
|
||||
|
@ -35,7 +42,7 @@ class TicketCategoryController extends Controller
|
|||
TicketCategory::create($request->all());
|
||||
|
||||
|
||||
return redirect(route("moderator.ticket.category.index"))->with("success",__("Category created"));
|
||||
return redirect(route("admin.ticket.category.index"))->with("success",__("Category created"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,6 +53,8 @@ class TicketCategoryController extends Controller
|
|||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$request->validate([
|
||||
'category' => 'required|int',
|
||||
'name' => 'required|string|max:191',
|
||||
|
@ -68,6 +77,8 @@ class TicketCategoryController extends Controller
|
|||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$category = TicketCategory::where("id",$id)->firstOrFail();
|
||||
|
||||
if($category->id == 5 ){ //cannot delete "other" category
|
||||
|
@ -84,7 +95,7 @@ class TicketCategoryController extends Controller
|
|||
$category->delete();
|
||||
|
||||
return redirect()
|
||||
->route('moderator.ticket.category.index')
|
||||
->route('admin.ticket.category.index')
|
||||
->with('success', __('Category removed'));
|
||||
}
|
||||
|
||||
|
@ -101,7 +112,7 @@ class TicketCategoryController extends Controller
|
|||
})
|
||||
->addColumn('actions', function (TicketCategory $category) {
|
||||
return '
|
||||
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('moderator.ticket.category.destroy', $category->id).'">
|
||||
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.ticket.category.destroy', $category->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>
|
|
@ -1,8 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Moderation;
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Moderation\Exception;
|
||||
use App\Models\Server;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketBlacklist;
|
||||
|
@ -17,9 +18,16 @@ use Illuminate\Support\Facades\Auth;
|
|||
|
||||
class TicketsController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.tickets.read";
|
||||
const WRITE_PERMISSION = "admin.tickets.write";
|
||||
|
||||
const BLACKLIST_READ_PERMISSION ='admin.ticket_blacklist.read';
|
||||
const BLACKLIST_WRITE_PERMISSION ='admin.ticket_blacklist.write';
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
{
|
||||
return view('moderator.ticket.index', [
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.ticket.index', [
|
||||
'tickets' => Ticket::orderBy('id', 'desc')->paginate(10),
|
||||
'ticketcategories' => TicketCategory::all(),
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
|
@ -28,6 +36,7 @@ class TicketsController extends Controller
|
|||
|
||||
public function show($ticket_id, PterodactylSettings $ptero_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
try {
|
||||
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
|
||||
} catch (Exception $e)
|
||||
|
@ -39,11 +48,12 @@ class TicketsController extends Controller
|
|||
$server = Server::where('id', $ticket->server)->first();
|
||||
$pterodactyl_url = $ptero_settings->panel_url;
|
||||
|
||||
return view('moderator.ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server', 'pterodactyl_url'));
|
||||
return view('admin.ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server', 'pterodactyl_url'));
|
||||
}
|
||||
|
||||
public function changeStatus($ticket_id)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
try {
|
||||
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
|
||||
} catch(Exception $e)
|
||||
|
@ -65,6 +75,7 @@ class TicketsController extends Controller
|
|||
|
||||
public function delete($ticket_id)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
try {
|
||||
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
|
||||
} catch (Exception $e)
|
||||
|
@ -80,6 +91,9 @@ class TicketsController extends Controller
|
|||
|
||||
public function reply(Request $request)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
|
||||
$this->validate($request, ['ticketcomment' => 'required']);
|
||||
try {
|
||||
$ticket = Ticket::where('id', $request->input('ticket_id'))->firstOrFail();
|
||||
|
@ -107,14 +121,15 @@ 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('moderator.ticket.show', ['ticket_id' => $tickets->ticket_id]).'">'.'#'.$tickets->ticket_id.' - '.htmlspecialchars($tickets->title).'</a>';
|
||||
return '<a class="text-info" href="'.route('admin.ticket.show', ['ticket_id' => $tickets->ticket_id]).'">'.'#'.$tickets->ticket_id.' - '.htmlspecialchars($tickets->title).'</a>';
|
||||
})
|
||||
->editColumn('user_id', function (Ticket $tickets) {
|
||||
return '<a href="'.route('admin.users.show', $tickets->user->id).'">'.$tickets->user->name.'</a>';
|
||||
|
@ -125,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('moderator.ticket.show', ['ticket_id' => $tickets->ticket_id]).'" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-eye"></i></a>
|
||||
<form class="d-inline" method="post" action="'.route('moderator.ticket.changeStatus', ['ticket_id' => $tickets->ticket_id]).'">
|
||||
<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('moderator.ticket.delete', ['ticket_id' => $tickets->ticket_id]).'">
|
||||
<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>
|
||||
';
|
||||
})
|
||||
|
@ -164,19 +179,24 @@ 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);
|
||||
}
|
||||
|
||||
public function blacklist(LocaleSettings $locale_settings)
|
||||
{
|
||||
return view('moderator.ticket.blacklist', [
|
||||
$this->checkPermission(self::BLACKLIST_READ_PERMISSION);
|
||||
|
||||
return view('admin.ticket.blacklist', [
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
]);
|
||||
}
|
||||
|
||||
public function blacklistAdd(Request $request)
|
||||
{
|
||||
$this->checkPermission(self::BLACKLIST_WRITE_PERMISSION);
|
||||
|
||||
try {
|
||||
$user = User::where('id', $request->user_id)->firstOrFail();
|
||||
$check = TicketBlacklist::where('user_id', $user->id)->first();
|
||||
|
@ -202,6 +222,8 @@ class TicketsController extends Controller
|
|||
|
||||
public function blacklistDelete($id)
|
||||
{
|
||||
$this->checkPermission(self::BLACKLIST_WRITE_PERMISSION);
|
||||
|
||||
$blacklist = TicketBlacklist::where('id', $id)->first();
|
||||
$blacklist->delete();
|
||||
|
||||
|
@ -210,6 +232,8 @@ class TicketsController extends Controller
|
|||
|
||||
public function blacklistChange($id)
|
||||
{
|
||||
$this->checkPermission(self::BLACKLIST_WRITE_PERMISSION);
|
||||
|
||||
try {
|
||||
$blacklist = TicketBlacklist::where('id', $id)->first();
|
||||
}
|
||||
|
@ -254,15 +278,15 @@ class TicketsController extends Controller
|
|||
})
|
||||
->addColumn('actions', function (TicketBlacklist $blacklist) {
|
||||
return '
|
||||
<form class="d-inline" method="post" action="'.route('moderator.ticket.blacklist.change', ['id' => $blacklist->id]).'">
|
||||
<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('moderator.ticket.blacklist.delete', ['id' => $blacklist->id]).'">
|
||||
<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>
|
||||
';
|
||||
})
|
|
@ -15,6 +15,8 @@ use Illuminate\Http\Response;
|
|||
|
||||
class UsefulLinkController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.useful_links.read";
|
||||
const WRITE_PERMISSION = "admin.useful_links.write";
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -22,6 +24,7 @@ class UsefulLinkController extends Controller
|
|||
*/
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
return view('admin.usefullinks.index', [
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
]);
|
||||
|
@ -34,6 +37,7 @@ class UsefulLinkController extends Controller
|
|||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
$positions = UsefulLinkLocation::cases();
|
||||
return view('admin.usefullinks.create')->with('positions', $positions);
|
||||
}
|
||||
|
@ -84,6 +88,8 @@ class UsefulLinkController extends Controller
|
|||
*/
|
||||
public function edit(UsefulLink $usefullink)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$positions = UsefulLinkLocation::cases();
|
||||
return view('admin.usefullinks.edit', [
|
||||
'link' => $usefullink,
|
||||
|
@ -126,6 +132,7 @@ class UsefulLinkController extends Controller
|
|||
*/
|
||||
public function destroy(UsefulLink $usefullink)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
$usefullink->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('product has been removed!'));
|
||||
|
|
|
@ -26,9 +26,24 @@ use Illuminate\Support\HtmlString;
|
|||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Spatie\QueryBuilder\QueryBuilder;
|
||||
use App\Models\Role;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.users.read";
|
||||
const WRITE_PERMISSION = "admin.users.write";
|
||||
const SUSPEND_PERMISSION = "admin.users.suspend";
|
||||
const CHANGE_EMAIL_PERMISSION = "admin.users.write.email";
|
||||
const CHANGE_CREDITS_PERMISSION = "admin.users.write.credits";
|
||||
const CHANGE_USERNAME_PERMISSION = "admin.users.write.username";
|
||||
const CHANGE_PASSWORD_PERMISSION = "admin.users.write.password";
|
||||
const CHANGE_ROLE_PERMISSION ="admin.users.write.role";
|
||||
const CHANGE_REFERAL_PERMISSION ="admin.users.write.referral";
|
||||
const CHANGE_PTERO_PERMISSION = "admin.users.write.pterodactyl";
|
||||
const DELETE_PERMISSION = "admin.users.delete";
|
||||
const NOTIFY_PERMISSION = "admin.users.notify";
|
||||
const LOGIN_PERMISSION = "admin.users.login_as";
|
||||
|
||||
private $pterodactyl;
|
||||
|
||||
public function __construct(PterodactylSettings $ptero_settings)
|
||||
|
@ -44,6 +59,8 @@ class UserController extends Controller
|
|||
*/
|
||||
public function index(LocaleSettings $locale_settings, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.users.index', [
|
||||
'locale_datatables' => $locale_settings->datatables,
|
||||
'credits_display_name' => $general_settings->credits_display_name
|
||||
|
@ -58,6 +75,8 @@ class UserController extends Controller
|
|||
*/
|
||||
public function show(User $user, LocaleSettings $locale_settings, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
//QUERY ALL REFERRALS A USER HAS
|
||||
//i am not proud of this at all.
|
||||
$allReferals = [];
|
||||
|
@ -108,9 +127,13 @@ class UserController extends Controller
|
|||
*/
|
||||
public function edit(User $user, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
|
||||
$roles = Role::all();
|
||||
return view('admin.users.edit')->with([
|
||||
'user' => $user,
|
||||
'credits_display_name' => $general_settings->credits_display_name
|
||||
'credits_display_name' => $general_settings->credits_display_name,
|
||||
'roles' => $roles
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -125,23 +148,27 @@ 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',
|
||||
'credits' => 'required|numeric|min:0|max:99999999',
|
||||
'server_limit' => 'required|numeric|min:0|max:1000000',
|
||||
'role' => Rule::in(['admin', 'moderator', 'client', 'member']),
|
||||
'referral_code' => "required|string|min:2|max:32|unique:users,referral_code,{$user->id}",
|
||||
]);
|
||||
|
||||
//update roles
|
||||
if ($request->roles && $this->can(self::CHANGE_ROLE_PERMISSION)) {
|
||||
$user->syncRoles($request->roles);
|
||||
}
|
||||
|
||||
if (isset($this->pterodactyl->getUser($request->input('pterodactyl_id'))['errors'])) {
|
||||
throw ValidationException::withMessages([
|
||||
'pterodactyl_id' => [__("User does not exists on pterodactyl's panel")],
|
||||
]);
|
||||
}
|
||||
|
||||
if (!is_null($request->input('new_password'))) {
|
||||
if (!is_null($request->input('new_password')) && $this->can(self::CHANGE_PASSWORD_PERMISSION)) {
|
||||
$request->validate([
|
||||
'new_password' => 'required|string|min:8',
|
||||
'new_password_confirmation' => 'required|same:new_password',
|
||||
|
@ -152,7 +179,24 @@ class UserController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
$user->update($request->all());
|
||||
// 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->update($data);
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
return redirect()->route('admin.users.index')->with('success', 'User updated!');
|
||||
|
@ -166,7 +210,9 @@ class UserController extends Controller
|
|||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
if ($user->role === 'admin' && User::query()->where('role', 'admin')->count() === 1) {
|
||||
$this->checkPermission(self::DELETE_PERMISSION);
|
||||
|
||||
if ($user->hasRole(1) && User::role(1)->count() === 1) {
|
||||
return redirect()->back()->with('error', __('You can not delete the last admin!'));
|
||||
}
|
||||
|
||||
|
@ -195,6 +241,8 @@ class UserController extends Controller
|
|||
*/
|
||||
public function loginAs(Request $request, User $user)
|
||||
{
|
||||
$this->checkPermission(self::LOGIN_PERMISSION);
|
||||
|
||||
$request->session()->put('previousUser', Auth::user()->id);
|
||||
Auth::login($user);
|
||||
|
||||
|
@ -207,6 +255,7 @@ class UserController extends Controller
|
|||
*/
|
||||
public function logBackIn(Request $request)
|
||||
{
|
||||
|
||||
Auth::loginUsingId($request->session()->get('previousUser'), true);
|
||||
$request->session()->remove('previousUser');
|
||||
|
||||
|
@ -221,7 +270,11 @@ class UserController extends Controller
|
|||
*/
|
||||
public function notifications()
|
||||
{
|
||||
return view('admin.users.notifications');
|
||||
$this->checkPermission(self::NOTIFY_PERMISSION);
|
||||
|
||||
$roles = Role::all();
|
||||
|
||||
return view('admin.users.notifications')->with(["roles" => $roles]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,12 +288,16 @@ class UserController extends Controller
|
|||
*/
|
||||
public function notify(Request $request)
|
||||
{
|
||||
$this->checkPermission(self::NOTIFY_PERMISSION);
|
||||
|
||||
//TODO: reimplement the required validation on all,users and roles . didnt work -- required_without:users,roles
|
||||
$data = $request->validate([
|
||||
'via' => 'required|min:1|array',
|
||||
'via.*' => 'required|string|in:mail,database',
|
||||
'all' => 'required_without:users|boolean',
|
||||
'users' => 'required_without:all|min:1|array',
|
||||
'users.*' => 'exists:users,id',
|
||||
'all' => 'boolean',
|
||||
'users' => 'min:1|array',
|
||||
'roles' => 'min:1|array',
|
||||
'roles.*' => 'required_without:all,users|exists:roles,id',
|
||||
'title' => 'required|string|min:1',
|
||||
'content' => 'required|string|min:1',
|
||||
]);
|
||||
|
@ -259,7 +316,14 @@ class UserController extends Controller
|
|||
->line(new HtmlString($data['content']));
|
||||
}
|
||||
$all = $data['all'] ?? false;
|
||||
$users = $all ? User::all() : User::whereIn('id', $data['users'])->get();
|
||||
$roles = $data['roles'] ?? false;
|
||||
if(!$roles){
|
||||
$users = $all ? User::all() : User::whereIn('id', $data['users'])->get();
|
||||
} else{
|
||||
$users = User::role($data["roles"])->get();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
Notification::send($users, new DynamicNotification($data['via'], $database, $mail));
|
||||
} catch (Exception $e) {
|
||||
|
@ -275,6 +339,12 @@ class UserController extends Controller
|
|||
*/
|
||||
public function toggleSuspended(User $user)
|
||||
{
|
||||
$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) {
|
||||
|
@ -289,17 +359,19 @@ 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) {
|
||||
return '<img width="28px" height="28px" class="rounded-circle ml-1" src="' . $user->getAvatar() . '">';
|
||||
return '<img width="28px" height="28px" class="ml-1 rounded-circle" src="' . $user->getAvatar() . '">';
|
||||
})
|
||||
->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('verified', function (User $user) {
|
||||
return $user->getVerifiedStatus();
|
||||
|
@ -313,10 +385,10 @@ class UserController extends Controller
|
|||
$suspendText = $user->isSuspended() ? __('Unsuspend') : __('Suspend');
|
||||
|
||||
return '
|
||||
<a data-content="' . __('Login as User') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.loginas', $user->id) . '" class="btn btn-sm btn-primary mr-1"><i class="fas fa-sign-in-alt"></i></a>
|
||||
<a data-content="' . __('Verify') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.verifyEmail', $user->id) . '" class="btn btn-sm btn-secondary mr-1"><i class="fas fa-envelope"></i></a>
|
||||
<a data-content="' . __('Show') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.show', $user->id) . '" class="btn btn-sm text-white btn-warning mr-1"><i class="fas fa-eye"></i></a>
|
||||
<a data-content="' . __('Edit') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.edit', $user->id) . '" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
|
||||
<a data-content="' . __('Login as User') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.loginas', $user->id) . '" class="mr-1 btn btn-sm btn-primary"><i class="fas fa-sign-in-alt"></i></a>
|
||||
<a data-content="' . __('Verify') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.verifyEmail', $user->id) . '" class="mr-1 btn btn-sm btn-secondary"><i class="fas fa-envelope"></i></a>
|
||||
<a data-content="' . __('Show') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.show', $user->id) . '" class="mr-1 text-white btn btn-sm btn-warning"><i class="fas fa-eye"></i></a>
|
||||
<a data-content="' . __('Edit') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.edit', $user->id) . '" class="mr-1 btn btn-sm btn-info"><i class="fas fa-pen"></i></a>
|
||||
<form class="d-inline" method="post" action="' . route('admin.users.togglesuspend', $user->id) . '">
|
||||
' . csrf_field() . '
|
||||
<button data-content="' . $suspendText . '" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm ' . $suspendColor . ' text-white mr-1"><i class="far ' . $suspendIcon . '"></i></button>
|
||||
|
@ -324,27 +396,18 @@ class UserController extends Controller
|
|||
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.users.destroy', $user->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>
|
||||
';
|
||||
})
|
||||
->editColumn('role', function (User $user) {
|
||||
switch ($user->role) {
|
||||
case 'admin':
|
||||
$badgeColor = 'badge-danger';
|
||||
break;
|
||||
case 'moderator':
|
||||
$badgeColor = 'badge-info';
|
||||
break;
|
||||
case 'client':
|
||||
$badgeColor = 'badge-success';
|
||||
break;
|
||||
default:
|
||||
$badgeColor = 'badge-secondary';
|
||||
break;
|
||||
$html = '';
|
||||
|
||||
foreach ($user->roles as $role) {
|
||||
$html .= "<span style='background-color: $role->color' class='badge'>$role->name</span>";
|
||||
}
|
||||
|
||||
return '<span class="badge ' . $badgeColor . '">' . $user->role . '</span>';
|
||||
return $html;
|
||||
})
|
||||
->editColumn('last_seen', function (User $user) {
|
||||
return $user->last_seen ? $user->last_seen->diffForHumans() : __('Never');
|
||||
|
@ -352,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();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ use Illuminate\Validation\ValidationException;
|
|||
|
||||
class VoucherController extends Controller
|
||||
{
|
||||
const READ_PERMISSION = "admin.voucher.read";
|
||||
const WRITE_PERMISSION = "admin.voucher.write";
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -26,6 +28,8 @@ class VoucherController extends Controller
|
|||
*/
|
||||
public function index(LocaleSettings $locale_settings, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.vouchers.index', [
|
||||
'locale_datatables' => $locale_settings->datatables,
|
||||
'credits_display_name' => $general_settings->credits_display_name
|
||||
|
@ -39,6 +43,7 @@ class VoucherController extends Controller
|
|||
*/
|
||||
public function create(GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
return view('admin.vouchers.create', [
|
||||
'credits_display_name' => $general_settings->credits_display_name
|
||||
]);
|
||||
|
@ -84,6 +89,7 @@ class VoucherController extends Controller
|
|||
*/
|
||||
public function edit(Voucher $voucher, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
return view('admin.vouchers.edit', [
|
||||
'voucher' => $voucher,
|
||||
'credits_display_name' => $general_settings->credits_display_name
|
||||
|
@ -120,6 +126,7 @@ class VoucherController extends Controller
|
|||
*/
|
||||
public function destroy(Voucher $voucher)
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
$voucher->delete();
|
||||
|
||||
return redirect()->back()->with('success', __('voucher has been removed!'));
|
||||
|
@ -127,6 +134,8 @@ class VoucherController extends Controller
|
|||
|
||||
public function users(Voucher $voucher, LocaleSettings $locale_settings, GeneralSettings $general_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
|
||||
return view('admin.vouchers.users', [
|
||||
'voucher' => $voucher,
|
||||
'locale_datatables' => $locale_settings->datatables,
|
||||
|
@ -194,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() : '';
|
||||
|
@ -205,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}";
|
||||
|
@ -236,14 +250,15 @@ class VoucherController extends Controller
|
|||
})
|
||||
->editColumn('expires_at', function (Voucher $voucher) {
|
||||
if (! $voucher->expires_at) {
|
||||
return '';
|
||||
return __("Never");
|
||||
}
|
||||
|
||||
return $voucher->expires_at ? $voucher->expires_at->diffForHumans() : '';
|
||||
return $voucher->expires_at ? $voucher->expires_at->diffForHumans() : __("Never");
|
||||
})
|
||||
->editColumn('code', function (Voucher $voucher) {
|
||||
return "<code>{$voucher->code}</code>";
|
||||
})
|
||||
->orderColumn('status', 'derived_status $1')
|
||||
->rawColumns(['actions', 'code', 'status'])
|
||||
->make();
|
||||
}
|
||||
|
|
160
app/Http/Controllers/Api/RoleController.php
Normal file
160
app/Http/Controllers/Api/RoleController.php
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Models\Role;
|
||||
use Spatie\QueryBuilder\QueryBuilder;
|
||||
|
||||
class RoleController extends Controller
|
||||
{
|
||||
const ALLOWED_INCLUDES = ['permissions', 'users'];
|
||||
|
||||
const ALLOWED_FILTERS = ['name'];
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = QueryBuilder::for(Role::class)
|
||||
->allowedIncludes(self::ALLOWED_INCLUDES)
|
||||
->allowedFilters(self::ALLOWED_FILTERS);
|
||||
|
||||
return $query->paginate($request->input('per_page') ?? 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:191',
|
||||
'color' => [
|
||||
'required',
|
||||
'regex:/^#([a-f0-9]{6}|[a-f0-9]{3})$/i'
|
||||
],
|
||||
'power' => 'required',
|
||||
]);
|
||||
|
||||
$role = Role::create([
|
||||
'name' => $request->name,
|
||||
'color' => $request->color,
|
||||
'power' => $request->power,
|
||||
]);
|
||||
|
||||
if ($request->permissions) {
|
||||
$permissions = explode(",",$request->permissions);
|
||||
foreach($permissions as $permission){
|
||||
$role->givePermissionTo($permission);
|
||||
}
|
||||
}
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return Role|Collection|Model
|
||||
*/
|
||||
public function show(int $id)
|
||||
{
|
||||
$query = QueryBuilder::for(Role::class)
|
||||
->where('id', '=', $id)
|
||||
->allowedIncludes(self::ALLOWED_INCLUDES);
|
||||
|
||||
return $query->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return Response
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param int $id
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Request $request, int $id)
|
||||
{
|
||||
$role = Role::findOrFail($id);
|
||||
|
||||
$request->validate([
|
||||
'name' => 'sometimes|string|max:191',
|
||||
'color' => [
|
||||
'sometimes',
|
||||
'regex:/^#([a-f0-9]{6}|[a-f0-9]{3})$/i'
|
||||
],
|
||||
'power' => 'sometimes',
|
||||
]);
|
||||
|
||||
if ($request->permissions) {
|
||||
$permissions = explode(",",$request->permissions);
|
||||
$role->syncPermissions($permissions);
|
||||
}
|
||||
|
||||
|
||||
$role->update($request->except('permissions'));
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return Response
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$role = Role::findOrFail($id);
|
||||
|
||||
if($role->id == 1 || $role->id == 3|| $role->id == 4){ //cannot delete admin and User role
|
||||
return response()->json([
|
||||
'error' => 'Not allowed to delete Admin, Client or Member'], 400);
|
||||
}
|
||||
|
||||
$users = User::role($role)->get();
|
||||
|
||||
foreach($users as $user){
|
||||
$user->syncRoles([4]);
|
||||
}
|
||||
$role->delete();
|
||||
|
||||
return $role;
|
||||
}
|
||||
}
|
|
@ -2,15 +2,12 @@
|
|||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Classes\Pterodactyl;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\DiscordUser;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ReferralNotification;
|
||||
use App\Traits\Referral;
|
||||
use App\Settings\PterodactylSettings;
|
||||
use App\Classes\PterodactylClient;
|
||||
use App\Settings\ReferralSettings;
|
||||
use App\Settings\UserSettings;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
|
@ -31,18 +28,9 @@ use Spatie\QueryBuilder\QueryBuilder;
|
|||
|
||||
class UserController extends Controller
|
||||
{
|
||||
use Referral;
|
||||
const ALLOWED_INCLUDES = ['servers', 'notifications', 'payments', 'vouchers', 'roles', 'discordUser'];
|
||||
|
||||
const ALLOWED_INCLUDES = ['servers', 'notifications', 'payments', 'vouchers', 'discordUser'];
|
||||
|
||||
const ALLOWED_FILTERS = ['name', 'server_limit', 'email', 'pterodactyl_id', 'role', 'suspended'];
|
||||
|
||||
private $pterodactyl;
|
||||
|
||||
public function __construct(PterodactylSettings $ptero_settings)
|
||||
{
|
||||
$this->pterodactyl = new PterodactylClient($ptero_settings);
|
||||
}
|
||||
const ALLOWED_FILTERS = ['name', 'server_limit', 'email', 'pterodactyl_id', 'suspended'];
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
|
@ -98,14 +86,13 @@ class UserController extends Controller
|
|||
'email' => 'sometimes|string|email',
|
||||
'credits' => 'sometimes|numeric|min:0|max:1000000',
|
||||
'server_limit' => 'sometimes|numeric|min:0|max:1000000',
|
||||
'role' => ['sometimes', Rule::in(['admin', 'moderator', 'client', 'member'])],
|
||||
]);
|
||||
|
||||
event(new UserUpdateCreditsEvent($user));
|
||||
|
||||
//Update Users Password on Pterodactyl
|
||||
//Username,Mail,First and Lastname are required aswell
|
||||
$response = $this->pterodactyl->application->patch('/application/users/' . $user->pterodactyl_id, [
|
||||
$response = Pterodactyl::client()->patch('/application/users/'.$user->pterodactyl_id, [
|
||||
'username' => $request->name,
|
||||
'first_name' => $request->name,
|
||||
'last_name' => $request->name,
|
||||
|
@ -118,7 +105,10 @@ class UserController extends Controller
|
|||
'pterodactyl_error_status' => $response->toException()->getCode(),
|
||||
]);
|
||||
}
|
||||
$user->update($request->all());
|
||||
if($request->has("role")){
|
||||
$user->syncRoles($request->role);
|
||||
}
|
||||
$user->update($request->except('role'));
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
@ -213,7 +203,7 @@ class UserController extends Controller
|
|||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function suspend(int $id)
|
||||
public function suspend(Request $request, int $id)
|
||||
{
|
||||
$discordUser = DiscordUser::find($id);
|
||||
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
|
||||
|
@ -237,12 +227,12 @@ class UserController extends Controller
|
|||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function unsuspend(int $id)
|
||||
public function unsuspend(Request $request, int $id)
|
||||
{
|
||||
$discordUser = DiscordUser::find($id);
|
||||
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
|
||||
|
||||
if (!$user->isSuspended()) {
|
||||
if (! $user->isSuspended()) {
|
||||
throw ValidationException::withMessages([
|
||||
'error' => 'You cannot unsuspend an User who is not suspended.',
|
||||
]);
|
||||
|
@ -253,10 +243,25 @@ class UserController extends Controller
|
|||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a unique Referral Code for User
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function createReferralCode()
|
||||
{
|
||||
$referralcode = STR::random(8);
|
||||
if (User::where('referral_code', '=', $referralcode)->exists()) {
|
||||
$this->createReferralCode();
|
||||
}
|
||||
|
||||
return $referralcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function store(Request $request, UserSettings $user_settings, ReferralSettings $referral_settings)
|
||||
public function store(Request $request, UserSettings $userSettings)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
|
||||
|
@ -265,7 +270,7 @@ class UserController extends Controller
|
|||
]);
|
||||
|
||||
// Prevent the creation of new users via API if this is enabled.
|
||||
if (!$user_settings->creation_enabled) {
|
||||
if (! $userSettings->creation_enabled) {
|
||||
throw ValidationException::withMessages([
|
||||
'error' => 'The creation of new users has been blocked by the system administrator.',
|
||||
]);
|
||||
|
@ -274,13 +279,13 @@ class UserController extends Controller
|
|||
$user = User::create([
|
||||
'name' => $request->input('name'),
|
||||
'email' => $request->input('email'),
|
||||
'credits' => $user_settings->initial_credits,
|
||||
'server_limit' => $user_settings->initial_server_limit,
|
||||
'credits' => config('SETTINGS::USER:INITIAL_CREDITS', 150),
|
||||
'server_limit' => config('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1),
|
||||
'password' => Hash::make($request->input('password')),
|
||||
'referral_code' => $this->createReferralCode(),
|
||||
]);
|
||||
|
||||
$response = $this->pterodactyl->application->post('/application/users', [
|
||||
$response = Pterodactyl::client()->post('/application/users', [
|
||||
'external_id' => App::environment('local') ? Str::random(16) : (string) $user->id,
|
||||
'username' => $user->name,
|
||||
'email' => $user->email,
|
||||
|
@ -303,12 +308,12 @@ class UserController extends Controller
|
|||
'pterodactyl_id' => $response->json()['attributes']['id'],
|
||||
]);
|
||||
//INCREMENT REFERRAL-USER CREDITS
|
||||
if (!empty($request->input('referral_code'))) {
|
||||
if (! empty($request->input('referral_code'))) {
|
||||
$ref_code = $request->input('referral_code');
|
||||
$new_user = $user->id;
|
||||
if ($ref_user = User::query()->where('referral_code', '=', $ref_code)->first()) {
|
||||
if ($referral_settings->mode === 'register' || $referral_settings->mode === 'both') {
|
||||
$ref_user->increment('credits', $referral_settings->reward);
|
||||
if (config('SETTINGS::REFERRAL:MODE') == 'register' || config('SETTINGS::REFERRAL:MODE') == 'both') {
|
||||
$ref_user->increment('credits', config('SETTINGS::REFERRAL::REWARD'));
|
||||
$ref_user->notify(new ReferralNotification($ref_user->id, $new_user));
|
||||
}
|
||||
//INSERT INTO USER_REFERRALS TABLE
|
||||
|
|
|
@ -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,8 +140,10 @@ class RegisterController extends Controller
|
|||
|
||||
]);
|
||||
|
||||
$user->syncRoles(Role::findByName('User'));
|
||||
|
||||
$response = $this->pterodactyl->application->post('/application/users', [
|
||||
'external_id' => $user->pterodactyl_id,
|
||||
'external_id' => null,
|
||||
'username' => $user->name,
|
||||
'email' => $user->email,
|
||||
'first_name' => $user->name,
|
||||
|
@ -150,6 +153,10 @@ class RegisterController extends Controller
|
|||
'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']);
|
||||
|
|
|
@ -2,12 +2,44 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
/**
|
||||
* Check if user has permissions
|
||||
* Abort 403 if the user doesn't have the required permission
|
||||
*
|
||||
* @param string $permission
|
||||
* @return void
|
||||
*/
|
||||
public function checkPermission(string $permission)
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user->can($permission)) {
|
||||
abort(403, __('User does not have the right permissions.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permissions
|
||||
*
|
||||
* @param string $permission
|
||||
* @return bool
|
||||
*/
|
||||
public function can(string $permission): bool
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
return $user->can($permission);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class HomeController extends Controller
|
|||
if (Storage::exists('callHome')) {
|
||||
return;
|
||||
}
|
||||
Http::asForm()->post('https://market.controlpanel.gg/callhome.php', [
|
||||
Http::asForm()->post('https://market.CtrlPanel.gg/callhome.php', [
|
||||
'id' => Hash::make(URL::current()),
|
||||
]);
|
||||
Storage::put('callHome', 'This is only used to count the installations of cpgg.');
|
||||
|
@ -101,7 +101,7 @@ class HomeController extends Controller
|
|||
|
||||
/** Build our Time-Left-Box */
|
||||
if ($credits > 0.01 and $usage > 0) {
|
||||
$daysLeft = number_format(($credits * 30) / $usage, 2, '.', '');
|
||||
$daysLeft = number_format($credits / ($usage / 30), 2, '.', '');
|
||||
$hoursLeft = number_format($credits / ($usage / 30 / 24), 2, '.', '');
|
||||
|
||||
$bg = $this->getTimeLeftBoxBackground($daysLeft);
|
||||
|
|
|
@ -26,38 +26,22 @@ class ProfileController extends Controller
|
|||
/** Display a listing of the resource. */
|
||||
public function index(UserSettings $user_settings, DiscordSettings $discord_settings, ReferralSettings $referral_settings)
|
||||
{
|
||||
switch (Auth::user()->role) {
|
||||
case 'admin':
|
||||
$badgeColor = 'badge-danger';
|
||||
break;
|
||||
case 'mod':
|
||||
$badgeColor = 'badge-info';
|
||||
break;
|
||||
case 'client':
|
||||
$badgeColor = 'badge-success';
|
||||
break;
|
||||
default:
|
||||
$badgeColor = 'badge-secondary';
|
||||
break;
|
||||
}
|
||||
|
||||
return view('profile.index')->with([
|
||||
'user' => Auth::user(),
|
||||
'credits_reward_after_verify_discord' => $user_settings->credits_reward_after_verify_discord,
|
||||
'force_email_verification' => $user_settings->force_email_verification,
|
||||
'force_discord_verification' => $user_settings->force_discord_verification,
|
||||
'badgeColor' => $badgeColor,
|
||||
'discord_client_id' => $discord_settings->client_id,
|
||||
'discord_client_secret' => $discord_settings->client_secret,
|
||||
'referral_enabled' => $referral_settings->enabled,
|
||||
'referral_allowed' => $referral_settings->allowed
|
||||
'referral_enabled' => $referral_settings->enabled
|
||||
]);
|
||||
}
|
||||
|
||||
public function selfDestroyUser()
|
||||
{
|
||||
$user = Auth::user();
|
||||
if ($user->role == "admin") return back()->with("error", "You cannot delete yourself as an admin!");
|
||||
if ($user->hasRole("Admin")) return back()->with("error", "You cannot delete yourself as an admin!");
|
||||
|
||||
$user->delete();
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ use App\Models\Pterodactyl\Node;
|
|||
use App\Models\Product;
|
||||
use App\Models\Server;
|
||||
use App\Notifications\ServerCreationError;
|
||||
use Carbon\Carbon;
|
||||
use App\Settings\UserSettings;
|
||||
use App\Settings\ServerSettings;
|
||||
use App\Settings\PterodactylSettings;
|
||||
|
@ -24,6 +25,9 @@ use Illuminate\Support\Facades\Request as FacadesRequest;
|
|||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
const CREATE_PERMISSION = 'user.server.create';
|
||||
const UPGRADE_PERMISSION = 'user.server.upgrade';
|
||||
|
||||
private $pterodactyl;
|
||||
|
||||
public function __construct(PterodactylSettings $ptero_settings)
|
||||
|
@ -41,7 +45,7 @@ class ServerController extends Controller
|
|||
|
||||
//Get server infos from ptero
|
||||
$serverAttributes = $this->pterodactyl->getServerAttributes($server->pterodactyl_id);
|
||||
if (! $serverAttributes) {
|
||||
if (!$serverAttributes) {
|
||||
continue;
|
||||
}
|
||||
$serverRelationships = $serverAttributes['relationships'];
|
||||
|
@ -81,7 +85,9 @@ class ServerController extends Controller
|
|||
/** Show the form for creating a new resource. */
|
||||
public function create(UserSettings $user_settings, ServerSettings $server_settings, GeneralSettings $general_settings)
|
||||
{
|
||||
$validate_configuration = $this->validateConfigurationRules($user_settings, $server_settings);
|
||||
$this->checkPermission(self::CREATE_PERMISSION);
|
||||
|
||||
$validate_configuration = $this->validateConfigurationRules($user_settings, $server_settings, $general_settings);
|
||||
|
||||
if (!is_null($validate_configuration)) {
|
||||
return $validate_configuration;
|
||||
|
@ -123,7 +129,7 @@ class ServerController extends Controller
|
|||
/**
|
||||
* @return null|RedirectResponse
|
||||
*/
|
||||
private function validateConfigurationRules(UserSettings $user_settings, ServerSettings $server_settings)
|
||||
private function validateConfigurationRules(UserSettings $user_settings, ServerSettings $server_settings, GeneralSettings $generalSettings)
|
||||
{
|
||||
//limit validation
|
||||
if (Auth::user()->servers()->count() >= Auth::user()->server_limit) {
|
||||
|
@ -141,14 +147,14 @@ class ServerController extends Controller
|
|||
// Check if node has enough memory and disk space
|
||||
$checkResponse = $this->pterodactyl->checkNodeResources($node, $product->memory, $product->disk);
|
||||
if ($checkResponse == false) {
|
||||
return redirect()->route('servers.index')->with('error', __("The node '".$nodeName."' doesn't have the required memory or disk left to allocate this product."));
|
||||
return redirect()->route('servers.index')->with('error', __("The node '" . $nodeName . "' doesn't have the required memory or disk left to allocate this product."));
|
||||
}
|
||||
|
||||
// Min. Credits
|
||||
if (Auth::user()->credits < ($product->minimum_credits == -1
|
||||
? $user_settings->min_credits_to_make_server
|
||||
: $product->minimum_credits)) {
|
||||
return redirect()->route('servers.index')->with('error', 'You do not have the required amount of '.CREDITS_DISPLAY_NAME.' to use this product!');
|
||||
return redirect()->route('servers.index')->with('error', 'You do not have the required amount of ' . $generalSettings->credits_display_name . ' to use this product!');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +164,7 @@ class ServerController extends Controller
|
|||
}
|
||||
|
||||
//Required Verification for creating an server
|
||||
if (!$server_settings->creation_enabled && Auth::user()->role != 'admin') {
|
||||
if (!$server_settings->creation_enabled && Auth::user()->cannot("admin.servers.bypass_creation_enabled")) {
|
||||
return redirect()->route('servers.index')->with('error', __('The system administrator has blocked the creation of new servers.'));
|
||||
}
|
||||
|
||||
|
@ -171,12 +177,12 @@ class ServerController extends Controller
|
|||
}
|
||||
|
||||
/** Store a newly created resource in storage. */
|
||||
public function store(Request $request, UserSettings $user_settings, ServerSettings $server_settings)
|
||||
public function store(Request $request, UserSettings $user_settings, ServerSettings $server_settings, GeneralSettings $generalSettings)
|
||||
{
|
||||
/** @var Node $node */
|
||||
/** @var Egg $egg */
|
||||
/** @var Product $product */
|
||||
$validate_configuration = $this->validateConfigurationRules($user_settings, $server_settings);
|
||||
$validate_configuration = $this->validateConfigurationRules($user_settings, $server_settings, $generalSettings);
|
||||
|
||||
if (!is_null($validate_configuration)) {
|
||||
return $validate_configuration;
|
||||
|
@ -197,11 +203,12 @@ class ServerController extends Controller
|
|||
$server = $request->user()->servers()->create([
|
||||
'name' => $request->input('name'),
|
||||
'product_id' => $request->input('product'),
|
||||
'last_billed' => Carbon::now()->toDateTimeString(),
|
||||
]);
|
||||
|
||||
//get free allocation ID
|
||||
$allocationId = $this->pterodactyl->getFreeAllocationId($node);
|
||||
if (! $allocationId) {
|
||||
if (!$allocationId) {
|
||||
return $this->noAllocationsError($server);
|
||||
}
|
||||
|
||||
|
@ -218,11 +225,8 @@ class ServerController extends Controller
|
|||
'identifier' => $serverAttributes['identifier'],
|
||||
]);
|
||||
|
||||
if ($server_settings->charge_first_hour) {
|
||||
if ($request->user()->credits >= $server->product->getHourlyPrice()) {
|
||||
$request->user()->decrement('credits', $server->product->getHourlyPrice());
|
||||
}
|
||||
}
|
||||
// Charge first billing cycle
|
||||
$request->user()->decrement('credits', $server->product->price);
|
||||
|
||||
return redirect()->route('servers.index')->with('success', __('Server created'));
|
||||
}
|
||||
|
@ -251,8 +255,6 @@ class ServerController extends Controller
|
|||
*/
|
||||
private function serverCreationFailed(Response $response, Server $server)
|
||||
{
|
||||
$server->delete();
|
||||
|
||||
return redirect()->route('servers.index')->with('error', json_encode($response->json()));
|
||||
}
|
||||
|
||||
|
@ -264,7 +266,23 @@ class ServerController extends Controller
|
|||
|
||||
return redirect()->route('servers.index')->with('success', __('Server removed'));
|
||||
} catch (Exception $e) {
|
||||
return redirect()->route('servers.index')->with('error', __('An exception has occurred while trying to remove a resource "').$e->getMessage().'"');
|
||||
return redirect()->route('servers.index')->with('error', __('An exception has occurred while trying to remove a resource"') . $e->getMessage() . '"');
|
||||
}
|
||||
}
|
||||
|
||||
/** Cancel Server */
|
||||
public function cancel(Server $server)
|
||||
{
|
||||
if ($server->user_id != Auth::user()->id) {
|
||||
return back()->with('error', __('This is not your Server!'));
|
||||
}
|
||||
try {
|
||||
$server->update([
|
||||
'canceled' => now(),
|
||||
]);
|
||||
return redirect()->route('servers.index')->with('success', __('Server canceled'));
|
||||
} catch (Exception $e) {
|
||||
return redirect()->route('servers.index')->with('error', __('An exception has occurred while trying to cancel the server"') . $e->getMessage() . '"');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,10 +311,10 @@ class ServerController extends Controller
|
|||
$pteroNode = $this->pterodactyl->getNode($serverRelationships['node']['attributes']['id']);
|
||||
|
||||
$products = Product::orderBy('created_at')
|
||||
->whereHas('nodes', function (Builder $builder) use ($serverRelationships) { //Only show products for that node
|
||||
$builder->where('id', '=', $serverRelationships['node']['attributes']['id']);
|
||||
})
|
||||
->get();
|
||||
->whereHas('nodes', function (Builder $builder) use ($serverRelationships) { //Only show products for that node
|
||||
$builder->where('id', '=', $serverRelationships['node']['attributes']['id']);
|
||||
})
|
||||
->get();
|
||||
|
||||
// Set the each product eggs array to just contain the eggs name
|
||||
foreach ($products as $product) {
|
||||
|
@ -316,10 +334,12 @@ class ServerController extends Controller
|
|||
|
||||
public function upgrade(Server $server, Request $request)
|
||||
{
|
||||
$this->checkPermission(self::UPGRADE_PERMISSION);
|
||||
|
||||
if ($server->user_id != Auth::user()->id) {
|
||||
return redirect()->route('servers.index');
|
||||
}
|
||||
if (! isset($request->product_upgrade)) {
|
||||
if (!isset($request->product_upgrade)) {
|
||||
return redirect()->route('servers.show', ['server' => $server->id])->with('error', __('this product is the only one'));
|
||||
}
|
||||
$user = Auth::user();
|
||||
|
@ -338,27 +358,54 @@ class ServerController extends Controller
|
|||
$requiredisk = $newProduct->disk - $oldProduct->disk;
|
||||
$checkResponse = $this->pterodactyl->checkNodeResources($node, $requireMemory, $requiredisk);
|
||||
if ($checkResponse == false) {
|
||||
return redirect()->route('servers.index')->with('error', __("The node '".$nodeName."' doesn't have the required memory or disk left to upgrade the server."));
|
||||
return redirect()->route('servers.index')->with('error', __("The node '" . $nodeName . "' doesn't have the required memory or disk left to upgrade the server."));
|
||||
}
|
||||
|
||||
$priceupgrade = $newProduct->getHourlyPrice();
|
||||
// calculate the amount of credits that the user overpayed for the old product when canceling the server right now
|
||||
// billing periods are hourly, daily, weekly, monthly, quarterly, half-annually, annually
|
||||
$billingPeriod = $oldProduct->billing_period;
|
||||
// seconds
|
||||
$billingPeriods = [
|
||||
'hourly' => 3600,
|
||||
'daily' => 86400,
|
||||
'weekly' => 604800,
|
||||
'monthly' => 2592000,
|
||||
'quarterly' => 7776000,
|
||||
'half-annually' => 15552000,
|
||||
'annually' => 31104000
|
||||
];
|
||||
// Get the amount of hours the user has been using the server
|
||||
$billingPeriodMultiplier = $billingPeriods[$billingPeriod];
|
||||
$timeDifference = now()->diffInSeconds($server->last_billed);
|
||||
|
||||
if ($priceupgrade < $oldProduct->getHourlyPrice()) {
|
||||
$priceupgrade = 0;
|
||||
}
|
||||
if ($user->credits >= $priceupgrade && $user->credits >= $newProduct->minimum_credits) {
|
||||
$server->product_id = $request->product_upgrade;
|
||||
$server->update();
|
||||
// Calculate the price for the time the user has been using the server
|
||||
$overpayedCredits = $oldProduct->price - $oldProduct->price * ($timeDifference / $billingPeriodMultiplier);
|
||||
|
||||
|
||||
if ($user->credits >= $newProduct->price && $user->credits >= $newProduct->minimum_credits) {
|
||||
$server->allocation = $serverAttributes['allocation'];
|
||||
$response = $this->pterodactyl->updateServer($server, $newProduct);
|
||||
if ($response->failed()) return redirect()->route('servers.index')->with('error', __("The system was unable to update your server product. Please try again later or contact support."));
|
||||
//update user balance
|
||||
$user->decrement('credits', $priceupgrade);
|
||||
//restart the server
|
||||
$response = $this->pterodactyl->powerAction($server, 'restart');
|
||||
if ($response->failed()) {
|
||||
return redirect()->route('servers.index')->with('error', $response->json()['errors'][0]['detail']);
|
||||
}
|
||||
if ($response->failed()) return redirect()->route('servers.index')->with('error', 'Upgrade Failed! Could not restart the server: ' . $response->json()['errors'][0]['detail']);
|
||||
|
||||
|
||||
// Remove the allocation property from the server object as it is not a column in the database
|
||||
unset($server->allocation);
|
||||
// Update the server on controlpanel
|
||||
$server->update([
|
||||
'product_id' => $newProduct->id,
|
||||
'updated_at' => now(),
|
||||
'last_billed' => now(),
|
||||
'canceled' => null,
|
||||
]);
|
||||
|
||||
// Refund the user the overpayed credits
|
||||
if ($overpayedCredits > 0) $user->increment('credits', $overpayedCredits);
|
||||
|
||||
// Withdraw the credits for the new product
|
||||
$user->decrement('credits', $newProduct->price);
|
||||
|
||||
return redirect()->route('servers.show', ['server' => $server->id])->with('success', __('Server Successfully Upgraded'));
|
||||
} else {
|
||||
|
|
|
@ -21,9 +21,12 @@ use Illuminate\Support\Str;
|
|||
|
||||
class TicketsController extends Controller
|
||||
{
|
||||
public function index(LocaleSettings $locale_settings)
|
||||
const READ_PERMISSION = 'user.ticket.read';
|
||||
const WRITE_PERMISSION = 'user.ticket.write';
|
||||
public function index(LocaleSettings $locale_settings, TicketSettings $ticketSettings)
|
||||
{
|
||||
return view('ticket.index', [
|
||||
'ticketsettings' => $ticketSettings,
|
||||
'tickets' => Ticket::where('user_id', Auth::user()->id)->paginate(10),
|
||||
'ticketcategories' => TicketCategory::all(),
|
||||
'locale_datatables' => $locale_settings->datatables
|
||||
|
@ -39,6 +42,7 @@ class TicketsController extends Controller
|
|||
'ticketcategory' => 'required',
|
||||
'priority' => 'required',
|
||||
'message' => 'required',
|
||||
'g-recaptcha-response' => ['required', 'recaptcha'],
|
||||
]
|
||||
);
|
||||
$ticket = new Ticket(
|
||||
|
@ -55,17 +59,13 @@ class TicketsController extends Controller
|
|||
);
|
||||
$ticket->save();
|
||||
$user = Auth::user();
|
||||
switch ($ticket_settings->notify) {
|
||||
case 'all':
|
||||
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
|
||||
Notification::send($admin, new AdminCreateNotification($ticket, $user));
|
||||
case 'admin':
|
||||
$admin = User::where('role', 'admin')->get();
|
||||
Notification::send($admin, new AdminCreateNotification($ticket, $user));
|
||||
case 'moderator':
|
||||
$admin = User::where('role', 'mod')->get();
|
||||
Notification::send($admin, new AdminCreateNotification($ticket, $user));
|
||||
|
||||
$staffNotify = User::permission('admin.tickets.get_notification')->get();
|
||||
foreach($staffNotify as $staff){
|
||||
Notification::send($staff, new AdminCreateNotification($ticket, $user));
|
||||
}
|
||||
|
||||
|
||||
$user->notify(new CreateNotification($ticket));
|
||||
|
||||
return redirect()->route('ticket.index')->with('success', __('A ticket has been opened, ID: #') . $ticket->ticket_id);
|
||||
|
@ -73,6 +73,7 @@ class TicketsController extends Controller
|
|||
|
||||
public function show($ticket_id, PterodactylSettings $ptero_settings)
|
||||
{
|
||||
$this->checkPermission(self::READ_PERMISSION);
|
||||
try {
|
||||
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
|
||||
} catch (Exception $e) {
|
||||
|
@ -108,15 +109,19 @@ class TicketsController extends Controller
|
|||
'message' => $request->input('message'),
|
||||
]);
|
||||
$user = Auth::user();
|
||||
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
|
||||
$newmessage = $request->input('ticketcomment');
|
||||
Notification::send($admin, new AdminReplyNotification($ticket, $user, $newmessage));
|
||||
|
||||
$staffNotify = User::permission('admin.tickets.get_notification')->get();
|
||||
foreach($staffNotify as $staff){
|
||||
Notification::send($staff, new AdminReplyNotification($ticket, $user, $newmessage));
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', __('Your comment has been submitted'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->checkPermission(self::WRITE_PERMISSION);
|
||||
//check in blacklist
|
||||
$check = TicketBlacklist::where('user_id', Auth::user()->id)->first();
|
||||
if ($check && $check->status == 'True') {
|
||||
|
|
|
@ -4,7 +4,6 @@ namespace App\Http;
|
|||
|
||||
use App\Http\Middleware\ApiAuthToken;
|
||||
use App\Http\Middleware\CheckSuspended;
|
||||
use App\Http\Middleware\GlobalNames;
|
||||
use App\Http\Middleware\isAdmin;
|
||||
use App\Http\Middleware\isMod;
|
||||
use App\Http\Middleware\LastSeen;
|
||||
|
@ -27,6 +26,7 @@ class Kernel extends HttpKernel
|
|||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -43,14 +43,12 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
LastSeen::class,
|
||||
GlobalNames::class,
|
||||
\App\Http\Middleware\SetLocale::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'throttle:api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
GlobalNames::class,
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -76,5 +74,9 @@ class Kernel extends HttpKernel
|
|||
'moderator' => isMod::class,
|
||||
'api.token' => ApiAuthToken::class,
|
||||
'checkSuspended' => CheckSuspended::class,
|
||||
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
|
||||
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
|
||||
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
|
||||
];
|
||||
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class GlobalNames
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$unsupported_lang_array = explode(',', config('app.unsupported_locales'));
|
||||
$unsupported_lang_array = array_map('strtolower', $unsupported_lang_array);
|
||||
define('UNSUPPORTED_LANGS', $unsupported_lang_array);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class isAdmin
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::user() && Auth::user()->role == 'admin') {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class isMod
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::user() && Auth::user()->role == 'moderator' || Auth::user() && Auth::user()->role == 'admin') {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
61
app/Listeners/CouponUsed.php
Normal file
61
app/Listeners/CouponUsed.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\CouponUsedEvent;
|
||||
use App\Settings\CouponSettings;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class CouponUsed
|
||||
{
|
||||
private $delete_coupon_on_expires;
|
||||
private $delete_coupon_on_uses_reached;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(CouponSettings $couponSettings)
|
||||
{
|
||||
$this->delete_coupon_on_expires = $couponSettings->delete_coupon_on_expires;
|
||||
$this->delete_coupon_on_uses_reached = $couponSettings->delete_coupon_on_uses_reached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \App\Events\CouponUsedEvent $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(CouponUsedEvent $event)
|
||||
{
|
||||
// Automatically increments the coupon usage.
|
||||
$this->incrementUses($event);
|
||||
|
||||
if ($this->delete_coupon_on_expires) {
|
||||
if (!is_null($event->coupon->expired_at)) {
|
||||
if ($event->coupon->expires_at <= Carbon::now()->timestamp) {
|
||||
$event->coupon->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->delete_coupon_on_uses_reached) {
|
||||
if ($event->coupon->uses >= $event->coupon->max_uses) {
|
||||
$event->coupon->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the use of a coupon.
|
||||
*
|
||||
* @param \App\Events\CouponUsedEvent $event
|
||||
*/
|
||||
private function incrementUses(CouponUsedEvent $event)
|
||||
{
|
||||
$event->coupon->increment('uses');
|
||||
$event->coupon->save();
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Enums\PaymentStatus;
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
@ -35,7 +36,7 @@ class UserPayment
|
|||
$this->referral_always_give_commission = $referral_settings->always_give_commission;
|
||||
$this->credits_display_name = $general_settings->credits_display_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
|
@ -48,7 +49,7 @@ class UserPayment
|
|||
$shopProduct = $event->shopProduct;
|
||||
|
||||
// only update user if payment is paid
|
||||
if ($event->payment->status != "paid") {
|
||||
if ($event->payment->status != PaymentStatus::PAID->value) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -79,8 +80,8 @@ class UserPayment
|
|||
}
|
||||
}
|
||||
//update role give Referral-reward
|
||||
if ($user->role == 'member') {
|
||||
$user->update(['role' => 'client']);
|
||||
if ($user->hasRole(4)) {
|
||||
$user->syncRoles(3);
|
||||
|
||||
//give referral commission only on first purchase
|
||||
if (($this->referral_mode === "commission" || $this->referral_mode === "both") && $shopProduct->type == "Credits" && !$this->referral_always_give_commission) {
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
119
app/Models/Coupon.php
Normal file
119
app/Models/Coupon.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Settings\CouponSettings;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Coupon extends Model
|
||||
{
|
||||
use HasFactory, LogsActivity;
|
||||
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->logOnlyDirty()
|
||||
->logOnly(['*'])
|
||||
->dontSubmitEmptyLogs();
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'code',
|
||||
'type',
|
||||
'value',
|
||||
'uses',
|
||||
'max_uses',
|
||||
'expires_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $casts = [
|
||||
'value' => 'float',
|
||||
'uses' => 'integer',
|
||||
'max_uses' => 'integer',
|
||||
'expires_at' => 'timestamp'
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the date format used by the coupons.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function formatDate(): string
|
||||
{
|
||||
return 'Y-MM-DD HH:mm:ss';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the coupon.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
if ($this->uses >= $this->max_uses) {
|
||||
return 'USES_LIMIT_REACHED';
|
||||
}
|
||||
|
||||
if (!is_null($this->expires_at)) {
|
||||
if ($this->expires_at <= Carbon::now(config('app.timezone'))->timestamp) {
|
||||
return __('EXPIRED');
|
||||
}
|
||||
}
|
||||
|
||||
return __('VALID');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user has already exceeded the uses of a coupon.
|
||||
*
|
||||
* @param User $user The request being made.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isMaxUsesReached($user): bool
|
||||
{
|
||||
$coupon_settings = new CouponSettings;
|
||||
$coupon_uses = $user->coupons()->where('id', $this->id)->count();
|
||||
|
||||
return $coupon_uses >= $coupon_settings->max_uses_per_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a specified quantity of coupon codes.
|
||||
*
|
||||
* @param int $amount Amount of coupons to be generated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function generateRandomCoupon(int $amount = 10): array
|
||||
{
|
||||
$coupons = [];
|
||||
|
||||
for ($i = 0; $i < $amount; $i++) {
|
||||
$random_coupon = strtoupper(bin2hex(random_bytes(3)));
|
||||
|
||||
$coupons[] = $random_coupon;
|
||||
}
|
||||
|
||||
return $coupons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'user_coupons');
|
||||
}
|
||||
}
|
22
app/Models/Permission.php
Normal file
22
app/Models/Permission.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Spatie\Permission\Models\Permission as BasePermission;
|
||||
|
||||
class Permission extends BasePermission
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'guard_name',
|
||||
'readable_name'
|
||||
];
|
||||
}
|
|
@ -45,12 +45,46 @@ class Product extends Model
|
|||
|
||||
public function getHourlyPrice()
|
||||
{
|
||||
return ($this->price / 30) / 24;
|
||||
// calculate the hourly price with the billing period
|
||||
switch($this->billing_period) {
|
||||
case 'daily':
|
||||
return $this->price / 24;
|
||||
case 'weekly':
|
||||
return $this->price / 24 / 7;
|
||||
case 'monthly':
|
||||
return $this->price / 24 / 30;
|
||||
case 'quarterly':
|
||||
return $this->price / 24 / 30 / 3;
|
||||
case 'half-annually':
|
||||
return $this->price / 24 / 30 / 6;
|
||||
case 'annually':
|
||||
return $this->price / 24 / 365;
|
||||
default:
|
||||
return $this->price;
|
||||
}
|
||||
}
|
||||
|
||||
public function getDailyPrice()
|
||||
public function getMonthlyPrice()
|
||||
{
|
||||
return $this->price / 30;
|
||||
// calculate the hourly price with the billing period
|
||||
switch($this->billing_period) {
|
||||
case 'hourly':
|
||||
return $this->price * 24 * 30;
|
||||
case 'daily':
|
||||
return $this->price * 30;
|
||||
case 'weekly':
|
||||
return $this->price * 4;
|
||||
case 'monthly':
|
||||
return $this->price;
|
||||
case 'quarterly':
|
||||
return $this->price / 3;
|
||||
case 'half-annually':
|
||||
return $this->price / 6;
|
||||
case 'annually':
|
||||
return $this->price / 12;
|
||||
default:
|
||||
return $this->price;
|
||||
}
|
||||
}
|
||||
|
||||
public function getWeeklyPrice()
|
||||
|
|
23
app/Models/Role.php
Normal file
23
app/Models/Role.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Spatie\Permission\Models\Role as BaseRole;
|
||||
|
||||
class Role extends BaseRole
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'guard_name',
|
||||
'power',
|
||||
'color'
|
||||
];
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Classes\PterodactylClient;
|
||||
use App\Settings\PterodactylSettings;
|
||||
use Exception;
|
||||
|
@ -52,12 +53,14 @@ class Server extends Model
|
|||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'suspended',
|
||||
'identifier',
|
||||
'product_id',
|
||||
'pterodactyl_id',
|
||||
"name",
|
||||
"description",
|
||||
"suspended",
|
||||
"identifier",
|
||||
"product_id",
|
||||
"pterodactyl_id",
|
||||
"last_billed",
|
||||
"canceled"
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -69,6 +72,8 @@ class Server extends Model
|
|||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$ptero_settings = new PterodactylSettings();
|
||||
$this->pterodactyl = new PterodactylClient($ptero_settings);
|
||||
}
|
||||
|
@ -136,9 +141,11 @@ class Server extends Model
|
|||
if ($response->successful()) {
|
||||
$this->update([
|
||||
'suspended' => null,
|
||||
'last_billed' => Carbon::now()->toDateTimeString(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,13 @@ class ShopProduct extends Model
|
|||
'disabled',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $casts = [
|
||||
'price' => 'float'
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
|
|
@ -4,8 +4,6 @@ namespace App\Models;
|
|||
|
||||
use App\Notifications\Auth\QueuedVerifyEmail;
|
||||
use App\Notifications\WelcomeMessage;
|
||||
use App\Settings\GeneralSettings;
|
||||
use App\Settings\UserSettings;
|
||||
use App\Classes\PterodactylClient;
|
||||
use App\Settings\PterodactylSettings;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
|
@ -15,16 +13,18 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\CausesActivity;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
/**
|
||||
* Class User
|
||||
*/
|
||||
class User extends Authenticatable implements MustVerifyEmail
|
||||
{
|
||||
use HasFactory, Notifiable, LogsActivity, CausesActivity;
|
||||
use HasFactory, Notifiable, LogsActivity, CausesActivity, HasRoles;
|
||||
|
||||
private PterodactylClient $pterodactyl;
|
||||
|
||||
|
@ -66,6 +66,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
'avatar',
|
||||
'suspended',
|
||||
'referral_code',
|
||||
'email_verified_reward',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -88,10 +89,13 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
'last_seen' => 'datetime',
|
||||
'credits' => 'float',
|
||||
'server_limit' => 'float',
|
||||
'email_verified_reward' => 'boolean'
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$ptero_settings = new PterodactylSettings();
|
||||
$this->pterodactyl = new PterodactylClient($ptero_settings);
|
||||
}
|
||||
|
@ -100,8 +104,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
{
|
||||
parent::boot();
|
||||
|
||||
static::created(function (User $user, GeneralSettings $general_settings, UserSettings $user_settings) {
|
||||
$user->notify(new WelcomeMessage($user, $general_settings, $user_settings));
|
||||
static::created(function (User $user) {
|
||||
$user->notify(new WelcomeMessage($user));
|
||||
});
|
||||
|
||||
static::deleting(function (User $user) {
|
||||
|
@ -166,6 +170,14 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
return $this->belongsToMany(Voucher::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function coupons()
|
||||
{
|
||||
return $this->belongsToMany(Coupon::class, 'user_coupons');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasOne
|
||||
*/
|
||||
|
@ -223,12 +235,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
return $this;
|
||||
}
|
||||
|
||||
private function getServersWithProduct()
|
||||
{
|
||||
return $this->servers()
|
||||
->with('product')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
|
@ -242,12 +248,21 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
{
|
||||
$usage = 0;
|
||||
foreach ($this->getServersWithProduct() as $server) {
|
||||
$usage += $server->product->price;
|
||||
$usage += $server->product->getHourlyPrice() * 24 * 30;
|
||||
}
|
||||
|
||||
return number_format($usage, 2, '.', '');
|
||||
}
|
||||
|
||||
private function getServersWithProduct()
|
||||
{
|
||||
return $this->servers()
|
||||
->whereNull('suspended')
|
||||
->whereNull('canceled')
|
||||
->with('product')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|string|string[]
|
||||
*/
|
||||
|
@ -268,17 +283,28 @@ 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();
|
||||
}
|
||||
|
||||
public function referredBy()
|
||||
{
|
||||
$referee = DB::table('user_referrals')->where("registered_user_id", $this->id)->first();
|
||||
|
||||
if ($referee) {
|
||||
$referee = User::where("id", $referee->referral_id)->firstOrFail();
|
||||
return $referee;
|
||||
}
|
||||
return Null;
|
||||
}
|
||||
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
|
|
|
@ -19,6 +19,10 @@ class ReferralNotification extends Notification
|
|||
|
||||
private $ref_user;
|
||||
|
||||
private $reward;
|
||||
|
||||
private $credits_display_name;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
|
@ -26,6 +30,11 @@ class ReferralNotification extends Notification
|
|||
*/
|
||||
public function __construct(int $user, int $ref_user)
|
||||
{
|
||||
$general_settings= new GeneralSettings();
|
||||
$referral_settings = new ReferralSettings();
|
||||
|
||||
$this->credits_display_name = $general_settings->credits_display_name;
|
||||
$this->reward = $referral_settings->reward;
|
||||
$this->user = User::findOrFail($user);
|
||||
$this->ref_user = User::findOrFail($ref_user);
|
||||
}
|
||||
|
@ -47,12 +56,12 @@ class ReferralNotification extends Notification
|
|||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable, GeneralSettings $general_settings, ReferralSettings $referral_settings)
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
'title' => __('Someone registered using your Code!'),
|
||||
'content' => '
|
||||
<p>You received '. $referral_settings->reward . ' ' . $general_settings->credits_display_name . '</p>
|
||||
<p>You received '. $this->reward . ' ' . $this->credits_display_name . '</p>
|
||||
<p>because ' . $this->ref_user->name . ' registered with your Referral-Code!</p>
|
||||
<p>Thank you very much for supporting us!.</p>
|
||||
<p>'.config('app.name', 'Laravel').'</p>
|
||||
|
|
|
@ -33,8 +33,11 @@ class WelcomeMessage extends Notification implements ShouldQueue
|
|||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user, GeneralSettings $general_settings, UserSettings $user_settings)
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$general_settings= new GeneralSettings();
|
||||
$user_settings = new UserSettings();
|
||||
|
||||
$this->user = $user;
|
||||
$this->credits_display_name = $general_settings->credits_display_name;
|
||||
$this->credits_reward_after_verify_discord = $user_settings->credits_reward_after_verify_discord;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Extensions\PaymentGateways\PayPal\PayPalSettings;
|
||||
use App\Models\UsefulLink;
|
||||
use App\Settings\GeneralSettings;
|
||||
use App\Settings\MailSettings;
|
||||
use Exception;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
|
@ -12,6 +12,7 @@ use Illuminate\Support\Facades\Schema;
|
|||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Qirolab\Theme\Theme;
|
||||
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
|
@ -60,6 +61,21 @@ class AppServiceProvider extends ServiceProvider
|
|||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
//get the Github Branch the panel is running on
|
||||
try {
|
||||
$stringfromfile = file(base_path() . '/.git/HEAD');
|
||||
|
||||
$firstLine = $stringfromfile[0]; //get the string from the array
|
||||
|
||||
$explodedstring = explode('/', $firstLine, 3); //seperate out by the "/" in the string
|
||||
|
||||
$branchname = $explodedstring[2]; //get the one that is always the branch name
|
||||
} catch (Exception $e) {
|
||||
$branchname = 'unknown';
|
||||
Log::notice($e);
|
||||
}
|
||||
config(['BRANCHNAME' => $branchname]);
|
||||
|
||||
// Do not run this code if no APP_KEY is set
|
||||
if (config('app.key') == null) return;
|
||||
|
||||
|
@ -73,7 +89,22 @@ class AppServiceProvider extends ServiceProvider
|
|||
}
|
||||
|
||||
|
||||
$settings = $this->app->make(MailSettings::class);
|
||||
$settings->setConfig();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,15 @@ namespace App\Providers;
|
|||
|
||||
use App\Events\PaymentEvent;
|
||||
use App\Events\UserUpdateCreditsEvent;
|
||||
use App\Events\CouponUsedEvent;
|
||||
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;
|
||||
|
||||
|
@ -31,12 +34,15 @@ class EventServiceProvider extends ServiceProvider
|
|||
CreateInvoice::class,
|
||||
UserPayment::class,
|
||||
],
|
||||
CouponUsedEvent::class => [
|
||||
CouponUsed::class
|
||||
],
|
||||
SocialiteWasCalled::class => [
|
||||
// ... other providers
|
||||
'SocialiteProviders\\Discord\\DiscordExtendSocialite@handle',
|
||||
],
|
||||
'Illuminate\Auth\Events\Verified' => [
|
||||
Verified::class,
|
||||
Verified::class => [
|
||||
ListenerVerified::class,
|
||||
],
|
||||
];
|
||||
|
||||
|
|
64
app/Settings/CouponSettings.php
Normal file
64
app/Settings/CouponSettings.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Settings;
|
||||
|
||||
use Spatie\LaravelSettings\Settings;
|
||||
|
||||
class CouponSettings extends Settings
|
||||
{
|
||||
public bool $enabled;
|
||||
public bool $delete_coupon_on_expires;
|
||||
public bool $delete_coupon_on_uses_reached;
|
||||
public ?int $max_uses_per_user;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
return 'coupon';
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of validations array
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function getValidations()
|
||||
{
|
||||
return [
|
||||
'enabled' => "nullable|boolean",
|
||||
'delete_coupon_on_expires' => 'nullable|boolean',
|
||||
'delete_coupon_on_uses_reached' => 'nullable|boolean',
|
||||
'max_uses_per_user' => 'nullable|integer',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of optionInputData array
|
||||
* Only used for the settings page
|
||||
* @return array<array<'type'|'label'|'description'|'options', string|bool|float|int|array<string, string>>>
|
||||
*/
|
||||
public static function getOptionInputData()
|
||||
{
|
||||
return [
|
||||
"category_icon" => "fas fa-ticket-alt",
|
||||
'enabled' => [
|
||||
'label' => 'Enable Coupons',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Enables coupons to be used in the store.'
|
||||
],
|
||||
'delete_coupon_on_expires' => [
|
||||
'label' => 'Auto Delete Expired Coupons',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Automatically deletes the coupon if it expires.'
|
||||
],
|
||||
'delete_coupon_on_uses_reached' => [
|
||||
'label' => 'Delete Coupon When Max Uses Reached',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Delete a coupon as soon as its maximum usage is reached.'
|
||||
],
|
||||
'max_uses_per_user' => [
|
||||
'label' => 'Max Uses Per User',
|
||||
'type' => 'number',
|
||||
'description' => 'Maximum number of uses that a user can make of the same coupon.'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -18,15 +18,6 @@ class DiscordSettings extends Settings
|
|||
return 'discord';
|
||||
}
|
||||
|
||||
public static function encrypted(): array
|
||||
{
|
||||
return [
|
||||
'bot_token',
|
||||
'client_id',
|
||||
'client_secret'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of validations array
|
||||
* @return array<string, string>
|
||||
|
|
|
@ -9,12 +9,12 @@ class GeneralSettings extends Settings
|
|||
public bool $store_enabled;
|
||||
public string $credits_display_name;
|
||||
public bool $recaptcha_enabled;
|
||||
public string $recaptcha_site_key;
|
||||
public string $recaptcha_secret_key;
|
||||
public string $phpmyadmin_url;
|
||||
public ?string $recaptcha_site_key;
|
||||
public ?string $recaptcha_secret_key;
|
||||
public ?string $phpmyadmin_url;
|
||||
public bool $alert_enabled;
|
||||
public string $alert_type;
|
||||
public string $alert_message;
|
||||
public ?string $alert_message;
|
||||
public string $theme;
|
||||
|
||||
//public int $initial_user_role; wait for Roles & Permissions PR.
|
||||
|
@ -24,13 +24,7 @@ class GeneralSettings extends Settings
|
|||
return 'general';
|
||||
}
|
||||
|
||||
public static function encrypted(): array
|
||||
{
|
||||
return [
|
||||
'recaptcha_site_key',
|
||||
'recaptcha_secret_key'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Summary of validations array
|
||||
|
@ -39,15 +33,15 @@ class GeneralSettings extends Settings
|
|||
public static function getValidations()
|
||||
{
|
||||
return [
|
||||
'store_enabled' => 'boolean',
|
||||
'store_enabled' => 'nullable|string',
|
||||
'credits_display_name' => 'required|string',
|
||||
'recaptcha_enabled' => 'nullable|boolean',
|
||||
'recaptcha_enabled' => 'nullable|string',
|
||||
'recaptcha_site_key' => 'nullable|string',
|
||||
'recaptcha_secret_key' => 'nullable|string',
|
||||
'phpmyadmin_url' => 'nullable|string',
|
||||
'alert_enabled' => 'nullable|boolean',
|
||||
'alert_enabled' => 'nullable|string',
|
||||
'alert_type' => 'required|in:primary,secondary,success,danger,warning,info',
|
||||
'alert_message' => 'required|string',
|
||||
'alert_message' => 'nullable|string',
|
||||
'theme' => 'required|in:default,BlueInfinity' // TODO: themes should be made/loaded dynamically
|
||||
];
|
||||
}
|
||||
|
@ -120,7 +114,7 @@ class GeneralSettings extends Settings
|
|||
'description' => 'The type of alert to display.'
|
||||
],
|
||||
'alert_message' => [
|
||||
'type' => 'string',
|
||||
'type' => 'textarea',
|
||||
'label' => 'Alert Message',
|
||||
'description' => 'The message to display in the alert.'
|
||||
],
|
||||
|
|
|
@ -6,13 +6,13 @@ use Spatie\LaravelSettings\Settings;
|
|||
|
||||
class InvoiceSettings extends Settings
|
||||
{
|
||||
public bool $enabled;
|
||||
public ?string $company_address;
|
||||
public ?string $company_mail;
|
||||
public ?string $company_name;
|
||||
public ?string $company_phone;
|
||||
public ?string $company_vat;
|
||||
public ?string $company_website;
|
||||
public bool $enabled;
|
||||
public ?string $prefix;
|
||||
|
||||
public static function group(): string
|
||||
|
@ -33,7 +33,7 @@ class InvoiceSettings extends Settings
|
|||
'company_phone' => 'nullable|string',
|
||||
'company_vat' => 'nullable|string',
|
||||
'company_website' => 'nullable|string',
|
||||
'enabled' => 'nullable|boolean',
|
||||
'enabled' => 'nullable|string',
|
||||
'prefix' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ class LocaleSettings extends Settings
|
|||
public static function getValidations()
|
||||
{
|
||||
return [
|
||||
'available' => 'nullable|array',
|
||||
'clients_can_change' => 'nullable|boolean',
|
||||
'available' => 'array|required',
|
||||
'clients_can_change' => 'nullable|string',
|
||||
'datatables' => 'nullable|string',
|
||||
'default' => 'required|in:' . implode(',', config('app.available_locales')),
|
||||
'dynamic' => 'nullable|boolean',
|
||||
'dynamic' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,7 @@ class LocaleSettings extends Settings
|
|||
'type' => 'select',
|
||||
'description' => 'The default locale to use.',
|
||||
'options' => config('app.available_locales'),
|
||||
'identifier' => 'display'
|
||||
],
|
||||
'dynamic' => [
|
||||
'label' => 'Dynamic Locale',
|
||||
|
|
|
@ -14,7 +14,6 @@ class MailSettings extends Settings
|
|||
public ?string $mail_from_address;
|
||||
public ?string $mail_from_name;
|
||||
public ?string $mail_mailer;
|
||||
public bool $mail_enabled;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
|
@ -24,7 +23,7 @@ class MailSettings extends Settings
|
|||
public static function encrypted(): array
|
||||
{
|
||||
return [
|
||||
'mail_password'
|
||||
'mail_password',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -57,7 +56,6 @@ class MailSettings extends Settings
|
|||
'mail_from_address' => 'nullable|string',
|
||||
'mail_from_name' => 'nullable|string',
|
||||
'mail_mailer' => 'nullable|string',
|
||||
'mail_enabled' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -87,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' => [
|
||||
|
@ -110,10 +113,6 @@ class MailSettings extends Settings
|
|||
'type' => 'string',
|
||||
'description' => 'The mailer of your mail server.',
|
||||
],
|
||||
'mail_enabled' => [
|
||||
'label' => 'Mail Enabled',
|
||||
'type' => 'boolean',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ class PterodactylSettings extends Settings
|
|||
{
|
||||
return [
|
||||
'admin_token',
|
||||
'user_token'
|
||||
'user_token',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ use Spatie\LaravelSettings\Settings;
|
|||
|
||||
class ReferralSettings extends Settings
|
||||
{
|
||||
public string $allowed;
|
||||
public bool $always_give_commission;
|
||||
public bool $enabled;
|
||||
public ?float $reward;
|
||||
|
@ -25,11 +24,10 @@ class ReferralSettings extends Settings
|
|||
public static function getValidations()
|
||||
{
|
||||
return [
|
||||
'allowed' => 'required|in:Everyone,Clients',
|
||||
'always_give_commission' => 'nullable|boolean',
|
||||
'enabled' => 'nullable|boolean',
|
||||
'always_give_commission' => 'nullable|string',
|
||||
'enabled' => 'nullable|string',
|
||||
'reward' => 'nullable|numeric',
|
||||
'mode' => 'required|in:Commission,Sign-Up,Both',
|
||||
'mode' => 'required|in:commission,sign-up,both',
|
||||
'percentage' => 'nullable|numeric',
|
||||
];
|
||||
}
|
||||
|
@ -43,19 +41,10 @@ class ReferralSettings extends Settings
|
|||
{
|
||||
return [
|
||||
'category_icon' => 'fas fa-user-friends',
|
||||
'allowed' => [
|
||||
'label' => 'Allowed',
|
||||
'type' => 'select',
|
||||
'description' => 'Who is allowed to see their referral-URL',
|
||||
'options' => [
|
||||
'everyone' => 'Everyone',
|
||||
'clients' => 'Clients',
|
||||
],
|
||||
],
|
||||
'always_give_commission' => [
|
||||
'label' => 'Always Give Commission',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Always give commission to the referrer.',
|
||||
'description' => 'Always give commission to the referrer or only on the first Purchase.',
|
||||
],
|
||||
'enabled' => [
|
||||
'label' => 'Enabled',
|
||||
|
|
|
@ -9,7 +9,6 @@ class ServerSettings extends Settings
|
|||
public int $allocation_limit;
|
||||
public bool $creation_enabled;
|
||||
public bool $enable_upgrade;
|
||||
public bool $charge_first_hour;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
|
@ -24,9 +23,8 @@ class ServerSettings extends Settings
|
|||
{
|
||||
return [
|
||||
'allocation_limit' => 'required|integer|min:0',
|
||||
'creation_enabled' => 'nullable|boolean',
|
||||
'enable_upgrade' => 'nullable|boolean',
|
||||
'charge_first_hour' => 'nullable|boolean',
|
||||
'creation_enabled' => 'nullable|string',
|
||||
'enable_upgrade' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -47,17 +45,12 @@ class ServerSettings extends Settings
|
|||
'creation_enabled' => [
|
||||
'label' => 'Creation Enabled',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Whether or not users can create servers.',
|
||||
'description' => 'Enable the user server creation.',
|
||||
],
|
||||
'enable_upgrade' => [
|
||||
'label' => 'Enable Upgrade',
|
||||
'label' => 'Server Upgrade Enabled',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Whether or not users can upgrade their servers.',
|
||||
],
|
||||
'charge_first_hour' => [
|
||||
'label' => 'Charge First Hour',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Whether or not the first hour of a server is charged.',
|
||||
'description' => 'Enable the server upgrade feature.',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use Spatie\LaravelSettings\Settings;
|
|||
class TicketSettings extends Settings
|
||||
{
|
||||
public bool $enabled;
|
||||
public string $notify;
|
||||
public ?string $information;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
|
@ -21,8 +21,8 @@ class TicketSettings extends Settings
|
|||
public static function getValidations()
|
||||
{
|
||||
return [
|
||||
'enabled' => 'nullable|boolean',
|
||||
'notify' => 'nullable|string',
|
||||
'enabled' => 'nullable|string',
|
||||
'information' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -40,16 +40,10 @@ class TicketSettings extends Settings
|
|||
'type' => 'boolean',
|
||||
'description' => 'Enable or disable the ticket system.',
|
||||
],
|
||||
'notify' => [
|
||||
'label' => 'Notify',
|
||||
'type' => 'select',
|
||||
'description' => 'Who will receive an E-Mail when a new Ticket is created.',
|
||||
'options' => [
|
||||
'admin' => 'Admins',
|
||||
'moderator' => 'Moderators',
|
||||
'all' => 'Admins and Moderators',
|
||||
'none' => 'Nobody',
|
||||
],
|
||||
'information' => [
|
||||
'label' => 'Ticket Information',
|
||||
'type' => 'textarea',
|
||||
'description' => 'Message shown on the right side when users create a new ticket.',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ use Spatie\LaravelSettings\Settings;
|
|||
|
||||
class UserSettings extends Settings
|
||||
{
|
||||
public bool $register_ip_check;
|
||||
public bool $creation_enabled;
|
||||
public float $credits_reward_after_verify_discord;
|
||||
public float $credits_reward_after_verify_email;
|
||||
public bool $force_discord_verification;
|
||||
|
@ -16,8 +18,6 @@ class UserSettings extends Settings
|
|||
public int $server_limit_after_irl_purchase;
|
||||
public int $server_limit_after_verify_discord;
|
||||
public int $server_limit_after_verify_email;
|
||||
public bool $register_ip_check;
|
||||
public bool $creation_enabled;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
|
@ -33,16 +33,16 @@ class UserSettings extends Settings
|
|||
return [
|
||||
'credits_reward_after_verify_discord' => 'required|numeric',
|
||||
'credits_reward_after_verify_email' => 'required|numeric',
|
||||
'force_discord_verification' => 'nullable|boolean',
|
||||
'force_email_verification' => 'nullable|boolean',
|
||||
'force_discord_verification' => 'nullable|string',
|
||||
'force_email_verification' => 'nullable|string',
|
||||
'initial_credits' => 'required|numeric',
|
||||
'initial_server_limit' => 'required|numeric',
|
||||
'min_credits_to_make_server' => 'required|numeric',
|
||||
'server_limit_after_irl_purchase' => 'required|numeric',
|
||||
'server_limit_after_verify_discord' => 'required|numeric',
|
||||
'server_limit_after_verify_email' => 'required|numeric',
|
||||
'register_ip_check' => 'nullable|boolean',
|
||||
'creation_enabled' => 'nullable|boolean',
|
||||
'register_ip_check' => 'nullable|string',
|
||||
'creation_enabled' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -91,9 +91,9 @@ class UserSettings extends Settings
|
|||
'description' => 'The minimum amount of credits a user needs to create a server.',
|
||||
],
|
||||
'server_limit_after_irl_purchase' => [
|
||||
'label' => 'Server Limit After IRL Purchase',
|
||||
'label' => 'Server Limit After first purchase',
|
||||
'type' => 'number',
|
||||
'description' => 'The amount of servers a user can create after they purchase a server.',
|
||||
'description' => 'The amount of servers a user can create after they make their first purchase.',
|
||||
],
|
||||
'server_limit_after_verify_discord' => [
|
||||
'label' => 'Server Limit After Verify Discord',
|
||||
|
@ -106,14 +106,14 @@ class UserSettings extends Settings
|
|||
'description' => 'The amount of servers a user can create after they verify their email.',
|
||||
],
|
||||
'register_ip_check' => [
|
||||
'label' => 'Register IP Check',
|
||||
'label' => 'Register IP Check Enabled',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Check if the IP a user is registering from is already in use.',
|
||||
],
|
||||
'creation_enabled' => [
|
||||
'label' => 'Creation Enabled',
|
||||
'type' => 'boolean',
|
||||
'description' => 'Whether or not users can create servers.',
|
||||
'description' => 'Enable the user registration.',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
|
@ -31,13 +31,13 @@ class WebsiteSettings extends Settings
|
|||
public static function getValidations()
|
||||
{
|
||||
return [
|
||||
'motd_enabled' => 'nullable|boolean',
|
||||
'motd_enabled' => 'nullable|string',
|
||||
'motd_message' => 'nullable|string',
|
||||
'show_imprint' => 'nullable|boolean',
|
||||
'show_privacy' => 'nullable|boolean',
|
||||
'show_tos' => 'nullable|boolean',
|
||||
'useful_links_enabled' => 'nullable|boolean',
|
||||
'enable_login_logo' => 'nullable|boolean',
|
||||
'show_imprint' => 'nullable|string',
|
||||
'show_privacy' => 'nullable|string',
|
||||
'show_tos' => 'nullable|string',
|
||||
'useful_links_enabled' => 'nullable|string',
|
||||
'enable_login_logo' => 'nullable|string',
|
||||
'seo_title' => 'nullable|string',
|
||||
'seo_description' => 'nullable|string',
|
||||
];
|
||||
|
|
115
app/Traits/Coupon.php
Normal file
115
app/Traits/Coupon.php
Normal file
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Settings\CouponSettings;
|
||||
use App\Models\Coupon as CouponModel;
|
||||
use App\Models\ShopProduct;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use stdClass;
|
||||
|
||||
trait Coupon
|
||||
{
|
||||
public function validateCoupon($requestUser, $couponCode, $productId): JsonResponse
|
||||
{
|
||||
$coupon = CouponModel::where('code', $couponCode)->first();
|
||||
$shopProduct = ShopProduct::findOrFail($productId);
|
||||
$coupon_settings = new CouponSettings;
|
||||
$response = response()->json([
|
||||
'isValid' => false,
|
||||
'error' => __('This coupon does not exist.')
|
||||
], 404);
|
||||
|
||||
if (!is_null($coupon)) {
|
||||
if ($coupon->getStatus() == 'USES_LIMIT_REACHED') {
|
||||
$response = response()->json([
|
||||
'isValid' => false,
|
||||
'error' => __('This coupon has reached the maximum amount of uses.')
|
||||
], 422);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($coupon->getStatus() == 'EXPIRED') {
|
||||
$response = response()->json([
|
||||
'isValid' => false,
|
||||
'error' => __('This coupon has expired.')
|
||||
], 422);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($coupon->isMaxUsesReached($requestUser, $coupon_settings)) {
|
||||
$response = response()->json([
|
||||
'isValid' => false,
|
||||
'error' => __('You have reached the maximum uses of this coupon.')
|
||||
], 422);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($coupon->type === 'amount' && $coupon->value >= $shopProduct->price) {
|
||||
$response = response()->json([
|
||||
'isValid' => false,
|
||||
'error' => __('The coupon you are trying to use would give you 100% off, so it cannot be used for this product, sorry.')
|
||||
], 422);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response = response()->json([
|
||||
'isValid' => true,
|
||||
'couponCode' => $coupon->code,
|
||||
'couponType' => $coupon->type,
|
||||
'couponValue' => $coupon->value
|
||||
]);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function isCouponValid(string $couponCode, User $user, string $productId): bool
|
||||
{
|
||||
if (is_null($couponCode)) return false;
|
||||
|
||||
$coupon = CouponModel::where('code', $couponCode)->first();
|
||||
$shopProduct = ShopProduct::findOrFail($productId);
|
||||
|
||||
if ($coupon->getStatus() == 'USES_LIMIT_REACHED') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($coupon->getStatus() == 'EXPIRED') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($coupon->isMaxUsesReached($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($coupon->type === 'amount' && $coupon->value >= $shopProduct->price) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function applyCoupon(string $couponCode, float $price)
|
||||
{
|
||||
$coupon = CouponModel::where('code', $couponCode)->first();
|
||||
|
||||
if ($coupon->type === 'percentage') {
|
||||
return $price - ($price * $coupon->value / 100);
|
||||
}
|
||||
|
||||
if ($coupon->type === 'amount') {
|
||||
// There is no discount if the value of the coupon is greater than or equal to the value of the product.
|
||||
if ($coupon->value >= $price) {
|
||||
return $price;
|
||||
}
|
||||
}
|
||||
|
||||
return $price - $coupon->value;
|
||||
}
|
||||
}
|
0
bootstrap/cache/.gitignore
vendored
Normal file → Executable file
0
bootstrap/cache/.gitignore
vendored
Normal file → Executable file
|
@ -26,6 +26,7 @@
|
|||
"qirolab/laravel-themer": "^2.0.2",
|
||||
"socialiteproviders/discord": "^4.1.2",
|
||||
"spatie/laravel-activitylog": "^4.7.3",
|
||||
"spatie/laravel-permission": "^5.10",
|
||||
"spatie/laravel-query-builder": "^5.1.2",
|
||||
"spatie/laravel-settings": "^2.7",
|
||||
"spatie/laravel-validation-rules": "^3.2.2",
|
||||
|
|
612
composer.lock
generated
612
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Facade;
|
|||
|
||||
return [
|
||||
|
||||
'version' => '0.9.4',
|
||||
'version' => '0.10',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -17,7 +17,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Controlpanel.gg'),
|
||||
'name' => env('APP_NAME', 'CtrlPanel.gg'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
@ -93,7 +93,7 @@ return [
|
|||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'ControlPanel'),
|
||||
'name' => env('MAIL_FROM_NAME', 'CtrlPanel'),
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
161
config/permission.php
Normal file
161
config/permission.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'models' => [
|
||||
|
||||
/*
|
||||
* When using the "HasPermissions" trait from this package, we need to know which
|
||||
* Eloquent model should be used to retrieve your permissions. Of course, it
|
||||
* is often just the "Permission" model but you may use whatever you like.
|
||||
*
|
||||
* The model you want to use as a Permission model needs to implement the
|
||||
* `Spatie\Permission\Contracts\Permission` contract.
|
||||
*/
|
||||
|
||||
'permission' => App\Models\Permission::class,
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* Eloquent model should be used to retrieve your roles. Of course, it
|
||||
* is often just the "Role" model but you may use whatever you like.
|
||||
*
|
||||
* The model you want to use as a Role model needs to implement the
|
||||
* `Spatie\Permission\Contracts\Role` contract.
|
||||
*/
|
||||
|
||||
'role' => App\Models\Role::class,
|
||||
|
||||
],
|
||||
|
||||
'table_names' => [
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* table should be used to retrieve your roles. We have chosen a basic
|
||||
* default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'roles' => 'roles',
|
||||
|
||||
/*
|
||||
* When using the "HasPermissions" trait from this package, we need to know which
|
||||
* table should be used to retrieve your permissions. We have chosen a basic
|
||||
* default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'permissions' => 'permissions',
|
||||
|
||||
/*
|
||||
* When using the "HasPermissions" trait from this package, we need to know which
|
||||
* table should be used to retrieve your models permissions. We have chosen a
|
||||
* basic default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'model_has_permissions' => 'model_has_permissions',
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* table should be used to retrieve your models roles. We have chosen a
|
||||
* basic default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'model_has_roles' => 'model_has_roles',
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* table should be used to retrieve your roles permissions. We have chosen a
|
||||
* basic default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'role_has_permissions' => 'role_has_permissions',
|
||||
],
|
||||
|
||||
'column_names' => [
|
||||
/*
|
||||
* Change this if you want to name the related pivots other than defaults
|
||||
*/
|
||||
'role_pivot_key' => null, //default 'role_id',
|
||||
'permission_pivot_key' => null, //default 'permission_id',
|
||||
|
||||
/*
|
||||
* Change this if you want to name the related model primary key other than
|
||||
* `model_id`.
|
||||
*
|
||||
* For example, this would be nice if your primary keys are all UUIDs. In
|
||||
* that case, name this `model_uuid`.
|
||||
*/
|
||||
|
||||
'model_morph_key' => 'model_id',
|
||||
|
||||
/*
|
||||
* Change this if you want to use the teams feature and your related model's
|
||||
* foreign key is other than `team_id`.
|
||||
*/
|
||||
|
||||
'team_foreign_key' => 'team_id',
|
||||
],
|
||||
|
||||
/*
|
||||
* When set to true, the method for checking permissions will be registered on the gate.
|
||||
* Set this to false, if you want to implement custom logic for checking permissions.
|
||||
*/
|
||||
|
||||
'register_permission_check_method' => true,
|
||||
|
||||
/*
|
||||
* When set to true the package implements teams using the 'team_foreign_key'. If you want
|
||||
* the migrations to register the 'team_foreign_key', you must set this to true
|
||||
* before doing the migration. If you already did the migration then you must make a new
|
||||
* migration to also add 'team_foreign_key' to 'roles', 'model_has_roles', and
|
||||
* 'model_has_permissions'(view the latest version of package's migration file)
|
||||
*/
|
||||
|
||||
'teams' => false,
|
||||
|
||||
/*
|
||||
* When set to true, the required permission names are added to the exception
|
||||
* message. This could be considered an information leak in some contexts, so
|
||||
* the default setting is false here for optimum safety.
|
||||
*/
|
||||
|
||||
'display_permission_in_exception' => false,
|
||||
|
||||
/*
|
||||
* When set to true, the required role names are added to the exception
|
||||
* message. This could be considered an information leak in some contexts, so
|
||||
* the default setting is false here for optimum safety.
|
||||
*/
|
||||
|
||||
'display_role_in_exception' => false,
|
||||
|
||||
/*
|
||||
* By default wildcard permission lookups are disabled.
|
||||
*/
|
||||
|
||||
'enable_wildcard_permission' => true,
|
||||
|
||||
'cache' => [
|
||||
|
||||
/*
|
||||
* By default all permissions are cached for 24 hours to speed up performance.
|
||||
* When permissions or roles are updated the cache is flushed automatically.
|
||||
*/
|
||||
|
||||
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
|
||||
|
||||
/*
|
||||
* The cache key used to store all permissions.
|
||||
*/
|
||||
|
||||
'key' => 'spatie.permission.cache',
|
||||
|
||||
/*
|
||||
* You may optionally indicate a specific cache driver to use for permission and
|
||||
* role caching using any of the `store` drivers listed in the cache.php config
|
||||
* file. Using 'default' here means to use the `default` set in cache.php.
|
||||
*/
|
||||
|
||||
'store' => 'default',
|
||||
],
|
||||
];
|
138
config/permissions_web.php
Normal file
138
config/permissions_web.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* Permissions for admin
|
||||
*/
|
||||
|
||||
'All Permissions' => '*',
|
||||
|
||||
'View Roles' => 'admin.roles.read',
|
||||
'Create Role' => 'admin.roles.create',
|
||||
'Edit Role' => 'admin.roles.edit',
|
||||
'Delete Role' => 'admin.roles.delete',
|
||||
|
||||
|
||||
'View Tickets' => 'admin.ticket.read',
|
||||
'Manage Ticket' => 'admin.tickets.write',
|
||||
'Receive Ticket Notifications' => 'admin.tickets.get_notification',
|
||||
|
||||
'Create Ticket Category' => 'admin.tickets.category.read',
|
||||
'Manage Ticket Category' => 'admin.tickets.category.write',
|
||||
|
||||
'View Blacklist Tickets' => 'admin.ticket_blacklist.read',
|
||||
'Manage Blacklist Tickets' => 'admin.ticket_blacklist.write',
|
||||
|
||||
'View Overview' => 'admin.overview.read',
|
||||
'Overview Sync' => 'admin.overview.sync',
|
||||
|
||||
'View Api Keys' => 'admin.api.read',
|
||||
'Manage Api Keys' => 'admin.api.write',
|
||||
|
||||
'View Users' => 'admin.users.read',
|
||||
'Manage Users' => 'admin.users.write',
|
||||
'Suspend Users' => 'admin.users.suspend',
|
||||
'Manage User Credits' => 'admin.users.write.credits',
|
||||
'Manage User Name' => 'admin.users.write.username',
|
||||
'Manage User Email' => 'admin.users.write.email',
|
||||
'Manage User Password' => 'admin.users.write.password',
|
||||
'Manage User Role' => 'admin.users.write.role',
|
||||
'Manage User Referral' => 'admin.users.write.referral',
|
||||
'Manage User Pterodactyl' => 'admin.users.write.pterodactyl',
|
||||
|
||||
'Notify Users' => 'admin.users.notify',
|
||||
'Login As User' => 'admin.users.login_as',
|
||||
'Delete User' => 'admin.users.delete',
|
||||
|
||||
'View Servers' => 'admin.servers.read',
|
||||
'Manage Servers' => 'admin.servers.write',
|
||||
'Suspend Server' => 'admin.servers.suspend',
|
||||
'Change Server Owner' => 'admin.servers.write.owner',
|
||||
'Manage Server Identifier' => 'admin.servers.write.identifier',
|
||||
'Create Server' => 'admin.servers.bypass_creation_enabled',
|
||||
'Delete Server' => 'admin.servers.delete',
|
||||
|
||||
'View Products' => 'admin.products.read',
|
||||
'Create Product' => 'admin.products.create',
|
||||
'Edit Product' => 'admin.products.edit',
|
||||
'Delete Product' => 'admin.products.delete',
|
||||
|
||||
'View Store' => 'admin.store.read',
|
||||
'Manage Store' => 'admin.store.write',
|
||||
'Disable Store' => 'admin.store.disable',
|
||||
|
||||
'View Vouchers' => 'admin.voucher.read',
|
||||
'Manage Voucher' => 'admin.voucher.write',
|
||||
|
||||
'View Useful Links' => 'admin.useful_links.read',
|
||||
'Manage Useful Links' => 'admin.useful_links.write',
|
||||
|
||||
'View Legal' => 'admin.legal.read',
|
||||
'Manage Legal' => 'admin.legal.write',
|
||||
|
||||
'View Payments' => 'admin.payments.read',
|
||||
|
||||
'View Partners' => 'admin.partners.read',
|
||||
'Manage Partners' => 'admin.partners.write',
|
||||
|
||||
'View Coupons' => 'admin.coupons.read',
|
||||
'Manage Coupons' => 'admin.coupons.write',
|
||||
|
||||
'View Logs' => 'admin.logs.read',
|
||||
|
||||
/*
|
||||
* Settings Permissions
|
||||
*/
|
||||
'View Discord Settings' => 'settings.discord.read',
|
||||
'Manage Discord Settings' => 'settings.discord.write',
|
||||
|
||||
'View General Settings' => 'settings.general.read',
|
||||
'Manage General Settings' => 'settings.general.write',
|
||||
|
||||
'View Invoice Settings' => 'settings.invoice.read',
|
||||
'Manage Invoice Settings' => 'settings.invoice.write',
|
||||
|
||||
'View Locale Settings' => 'settings.locale.read',
|
||||
'Manage Locale Settings' => 'settings.locale.write',
|
||||
|
||||
'View Mail Settings' => 'settings.mail.read',
|
||||
'Manage Mail Settings' => 'settings.mail.write',
|
||||
|
||||
'View Pterodactyl Settings' => 'settings.pterodactyl.read',
|
||||
'Manage Pterodactyl Settings' => 'settings.pterodactyl.write',
|
||||
|
||||
'View Referral Settings' => 'settings.referral.read',
|
||||
'Manage Referral Settings' => 'settings.referral.write',
|
||||
|
||||
'View Server Settings' => 'settings.server.read',
|
||||
'Manage Server Settings' => 'settings.server.write',
|
||||
|
||||
'View Ticket Settings' => 'settings.ticket.read',
|
||||
'Manage Ticket Settings' => 'settings.ticket.write',
|
||||
|
||||
'View User Settings' => 'settings.user.read',
|
||||
'Manage User Settings' => 'settings.user.write',
|
||||
|
||||
'View Website Settings' => 'settings.website.read',
|
||||
'Manage Website Settings' => 'settings.website.write',
|
||||
|
||||
'View Paypal Settings' => 'settings.paypal.read',
|
||||
'Manage Paypal Settings' => 'settings.paypal.write',
|
||||
|
||||
'View Stripe Settings' => 'settings.stripe.read',
|
||||
'Manage Stripe Settings' => 'settings.stripe.write',
|
||||
|
||||
'View Mollie Settings' => 'settings.mollie.read',
|
||||
'Manage Mollie Settings' => 'settings.mollie.write',
|
||||
|
||||
|
||||
/*
|
||||
* Permissions for users
|
||||
*/
|
||||
'User Create Server' => 'user.server.create',
|
||||
'User Upgrade Server' => 'user.server.upgrade',
|
||||
'User Shop Buy' => 'user.shop.buy',
|
||||
'User View Tickets' => 'user.ticket.read',
|
||||
'User Manage Ticket' => 'user.ticket.write',
|
||||
'User View Referral' => 'user.referral',
|
||||
];
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue