Compare commits

..

488 commits

Author SHA1 Message Date
Dennis
4358d071b3
Fix old name and dead links (#949) 2024-05-07 09:35:04 +02:00
MrWeez
e7aad7ee6c Update app.php 2024-05-07 04:59:29 +03:00
MrWeez
6623f957a1 Fix old name and dead links 2024-05-07 04:58:15 +03:00
Dennis
335951b7e6
Update app.php (#943) 2024-04-24 10:50:21 +02:00
MrWeez
9299eac552
Update app.php 2024-04-12 22:20:25 +03:00
Ferks-FK
aed5b9d16e fix: change alpine script link 2024-04-12 21:05:51 +02:00
Dennis
2d6f6f6848
GH-930 Repair docker image for PHP-FPM (#930) 2024-02-28 12:17:01 +01:00
Krzysztof Haller
36626f2d76 Fix dockerimage with docker-compose 2024-02-28 09:26:30 +01:00
Jens
9fab5451f5 Update app.php
Switch version to 0.9.6 and fix old name
2023-09-17 13:21:18 +02:00
Ferks-FK
79670657a4 Change the place where this is called. 2023-09-16 21:42:34 +02:00
Ferks-FK
441d89767d fix: 🐛 Fix credits exploit and infinite logs 2023-09-16 21:42:34 +02:00
Dennis
43ea446b2a
Update GetGithubVersion.php 2023-05-02 20:21:01 +02:00
IceToast
63692b557c Update app.php 2023-05-01 13:12:43 +02:00
Dennis
31b303b9fd
Revert "Merge branch 'development' into main" (#797) 2023-04-25 09:23:23 +02:00
1day2die
0c067e26c8 Revert "Merge branch 'development' into main"
This reverts commit 3810b487cd, reversing
changes made to d9a41840ce.
2023-04-25 09:22:30 +02:00
Dennis
3810b487cd
Merge branch 'development' into main 2023-04-25 09:15:46 +02:00
Dennis
d9a41840ce
Updated urls to new ones (#795) 2023-04-25 09:14:33 +02:00
Corwin
f52c93c925
Updated urls to new ones 2023-04-25 06:56:49 +01:00
Dennis
cfe5086b10
Update README.md 2023-04-24 13:29:04 +02:00
Dennis
87dc97c8ea
Fix MacError on MakeUser (#793) 2023-04-20 23:07:19 +02:00
1day2die
023772d929 Fix MacError on MakeUser 2023-04-20 23:07:01 +02:00
Dennis
f49137b833
Fix Install CSS (#792) 2023-04-20 11:45:31 +02:00
1day2die
d6fe858e84 Fix Installer CSS 2023-04-20 11:43:53 +02:00
Dennis
15f03c0527
Merge branch 'ControlPanel-gg:development' into development 2023-04-20 10:36:41 +02:00
Dennis
51d340d95b
[Fix] [CRITICAL] Fix new installations (#790) 2023-04-14 10:18:44 +02:00
Dennis
8cda91a6e6
fix server deletion upon changing product (#789) 2023-04-14 10:18:25 +02:00
Jens
b99d6a51a6
[FIX] Shouldn't forget caller 2023-04-14 10:09:28 +02:00
Jens
15ee059d30
[Fix] Fix new installations
This bugfix comments out the installation counter as it is currently an extremely bad practice and resulted in an issue with errors.
2023-04-14 10:01:05 +02:00
ok236449
821d85e395 fix server deletion upon changing product 2023-04-12 20:51:08 +02:00
Dennis
be1344f63e
Merge branch 'ControlPanel-gg:development' into development 2023-04-05 15:20:37 +02:00
Dennis
20bd030e78
fix: Button not clickable on step 2.5 of installation. (#786) 2023-04-05 15:20:21 +02:00
Vikas Dongre
59cb5eab67 fix: Button not clickable on step 2.5 of installation. 2023-04-05 18:45:00 +05:30
Jens
6dac81e809
Fix: WHY 2023-04-05 15:09:12 +02:00
Jens
1bb34caaa7
Fix (again): Move the .env.example check to functions.php (#785)
Fix (again): Move the .env.example check to functions.php (#784)

style: 💄 Improved UI/UX of the Installer.

Co-authored-by: Vikas Dongre
Co-authored-by: Dennis
2023-04-05 15:04:30 +02:00
Dennis
c3aff98094
Merge branch 'ControlPanel-gg:development' into development 2023-04-05 14:35:41 +02:00
Dennis
0408b391da
style: 💄 Improved UI/UX of the Installer. (#781) 2023-04-05 14:35:07 +02:00
Jens
b20a2d24e4
Merge branch 'development' into improve/installer-ui 2023-04-05 14:33:48 +02:00
Jens
3949529c32
Fix: environment file crashes installer on new installations (#783) 2023-04-05 14:17:05 +02:00
Jens
482c613ca6
Move .env.example to functions.php 2023-04-05 14:14:55 +02:00
Jens
8ff3f04c0b
Move .env.example to functions.php 2023-04-05 14:13:52 +02:00
1day2die
9ddb44c3dc Merge branch 'development' of https://github.com/1day2die/dashboard into development 2023-04-05 09:47:28 +02:00
Dennis
ca3643deef
[Bugfix] Fix several bugs (#780) 2023-04-04 20:00:51 +02:00
Vikas Dongre
b636ea0554 style: 💄 Improved UI/UX of the Installer. 2023-04-04 21:36:05 +05:30
IceToast
f68b1cc8ca
fix: 🐛 Different badge colors #703 2023-04-04 02:04:41 +02:00
IceToast
91476d2701
fix: 🐛 Show Product name instead of resource description #756 2023-04-04 01:58:23 +02:00
IceToast
44948226bb
fix: 🐛 typo 2023-04-04 01:45:29 +02:00
IceToast
5b8f12fa94
fix: 🐛 Settings update -> type check 2023-04-04 01:45:18 +02:00
IceToast
18d4962ef9
fix: 🔒️ Do not allow last admin deletion #762 2023-04-04 01:27:05 +02:00
IceToast
307f3229d2
fix: 🐛 external_id already used #769 2023-04-04 01:23:44 +02:00
IceToast
ce544019eb
refactor: 🚚 Rename pterodactyl application api accordingly 2023-04-04 01:16:39 +02:00
IceToast
1784358fc2
fix: 🐛 Potentional installation fix -> APP_KEY missing 2023-04-04 00:28:20 +02:00
IceToast
6497f6bf90 fix: 🔥 Removed default values from Settings class variables 2023-04-02 20:00:56 +02:00
Jens
b4e1b5aeca Rename controlpanel.log to installer.log 2023-04-02 20:00:56 +02:00
AGuyNamedJens
ffba7ebf1b (refactor) revert the revert, things work now. 2023-04-02 20:00:56 +02:00
AGuyNamedJens
761d044957 (refactor) actually fix the checkHttps log
(refactor) change logger output to controlpanel.log
(refactor) add and fix extension logs
2023-04-02 20:00:56 +02:00
AGuyNamedJens
307aaee082 (refactor) checkHTTPS Looks better this way, also fixes the log 2023-04-02 20:00:56 +02:00
AGuyNamedJens
b1f6528605 Forgot to remove this debug-debug log lol 2023-04-02 20:00:56 +02:00
AGuyNamedJens
388d0eafa0 (revert) Revert setenv/getenv
(refactor) Fix logging, use new logger
(refactor) Add debug logs to the installer and functions
(refactor) Remove unused dotenv
2023-04-02 20:00:56 +02:00
Jens
f7ab52fec1 (Refactor) Validation and variable modification to align with the input data 2023-04-02 20:00:56 +02:00
Jens
f63d8080a3 (Refactor) Use laravel's logging system, specified a few log levels and refactor the function calls 2023-04-02 20:00:56 +02:00
Jens
470bc6d545 (Refactor) run_console now gives more control (all optional) using a more up-to-date method proc_open
Use the PHP documentation to get more knowledge.
2023-04-02 20:00:56 +02:00
Jens
bc34bb5fa0 (Refactor) default encryption method 2023-04-02 20:00:56 +02:00
Jens
1d9ac19c16 (Refactor) rename getEnvironmentValue to getenv, rename setEnvironmentValue to setenv 2023-04-02 20:00:56 +02:00
Jens
ef1ec46bad (Refactor) Remove getEnvironmentValue, rename setEnvironmentValue to setenv to match php defaults 2023-04-02 20:00:56 +02:00
AGuyNamedJens
efd61fb376 (refactor) Refactor forms.php 2023-04-02 20:00:56 +02:00
AGuyNamedJens
299d8b637a (refactor) Refactor functions.php 2023-04-02 20:00:56 +02:00
AGuyNamedJens
086f685780 (refactor) Refactor index.php 2023-04-02 20:00:56 +02:00
AGuyNamedJens
4d97c1473b (refactor) Forgot to include autoload file 2023-04-02 20:00:56 +02:00
AGuyNamedJens
63c435c610 (refactor) More refactoring, also add missing serialize method 2023-04-02 20:00:56 +02:00
AGuyNamedJens
08e09c5d08 (feat) New encryption and decryption functions
(refactor) Modify the get and set environment functions
(refactor) Refactor the entire functions.php file.
2023-04-02 20:00:56 +02:00
AGuyNamedJens
a9b7531ab1 (refactor) Refactor encryption method 2023-04-02 20:00:56 +02:00
AGuyNamedJens
224900890b (fix/feat) Fix deprecations, add encryption, fix API Calls & Fix Database 2023-04-02 20:00:56 +02:00
AGuyNamedJens
4800a79643 (refactor) Remove main_site from database 2023-04-02 20:00:56 +02:00
AGuyNamedJens
c776da96ae This package isn't supposed to be pushed with the patch 2023-04-02 20:00:56 +02:00
AGuyNamedJens
0eb84f5f76 Update packages & docker-compose.yml 2023-04-02 20:00:56 +02:00
Dennis
bb0243df47
Merge branch 'ControlPanel-gg:development' into development 2023-03-30 14:10:55 +02:00
IceToast
3dd26b8259 style: 💄 Change checkout page to two card design 2023-03-30 14:07:59 +02:00
IceToast
fc2119ad5a fix: 🐛 Remove isPaymentSetup notification 2023-03-30 14:07:59 +02:00
IceToast
fe535c061e Revert "style: 💄 Change layout of checkout page"
This reverts commit b55c36cd5f552c46380583b9dc1772efb828bb5f.
2023-03-30 14:07:59 +02:00
IceToast
75a8db833b style: 💄 Change layout of checkout page 2023-03-30 14:07:59 +02:00
IceToast
11a12bb48b style: 💄 checkout payment method list 2023-03-30 14:07:59 +02:00
IceToast
5b44df24ab fix: 🐛 Mollie gateway route and settings 2023-03-30 14:07:07 +02:00
IceToast
c95cde5cde feat: Create Mollie payment gateway extension 2023-03-30 14:07:07 +02:00
IceToast
59ed6e51c1 fix: 🐛 Typo after reverting to path based extension loading 2023-03-30 14:03:33 +02:00
IceToast
06bfaf709c fix: 🐛 Change extensionHelper to work with filesystem not reflection 2023-03-30 14:03:33 +02:00
IceToast
5f422e1b83 refactor: ♻️ Use reflection to get all extensionsettings 2023-03-30 14:03:33 +02:00
IceToast
7701671c7e refactor: ♻️ Move Stripe Extension methods and config to Extension Class 2023-03-30 14:03:33 +02:00
IceToast
0ec3660e1a refactor: ♻️ Move PayPal Extension methods and config to Extension Class 2023-03-30 14:03:33 +02:00
IceToast
f17f5da822 refactor: ♻️ Change extensionHelper to work with the abstract class using reflection 2023-03-30 14:03:33 +02:00
IceToast
ca3e31580f feat: 🐛 add abstract extension class 2023-03-30 14:03:33 +02:00
Jens
4509b11816 Remove default, keep null 2023-03-29 16:27:28 +02:00
Jens
7fb7c23fb0 Allow custom types to be passed | add NULL case
Solves the comment about request types.
2023-03-29 16:27:28 +02:00
AGuyNamedJens
4a5a002dc1 This package isn't supposed to be pushed with the patch 2023-03-29 16:27:28 +02:00
AGuyNamedJens
3b893e7ccf Fix: Settings not saving. Fixes issue #773 2023-03-29 16:27:28 +02:00
AGuyNamedJens
1d2e2ebdc7 Allow Pterodactyl settings to be nullable 2023-03-29 16:27:28 +02:00
AGuyNamedJens
f70f82f896 Update packages & docker-compose.yml 2023-03-29 16:27:28 +02:00
IceToast
ba75d09cbf
Update app.php 2023-03-18 21:45:57 +01:00
IceToast
44dee3929b
fix: 🐛 Users table searching 2023-03-18 21:38:21 +01:00
1DayLaptop
36b7842085 installer new settings - Missing encryption 2023-03-15 10:48:10 +01:00
Dennis
ad9e33d0d7
Update web_routes.php 2023-03-09 12:43:38 +01:00
Dennis
4ae54d736f
Merge branch 'ControlPanel-gg:development' into development 2023-03-09 12:32:58 +01:00
IceToast
d7ed67a842 fix: 🐛 Added store_enabled option 2023-03-09 12:31:11 +01:00
IceToast
d7a36c61b2 feat: Migrate Extensions from old to new settings system 2023-03-09 12:31:11 +01:00
IceToast
3cba1c60f8 feat: Add Extension Settings and migrations 2023-03-09 12:31:11 +01:00
IceToast
1a8f883b63 feat: Add Extension Settings to Settingspage 2023-03-09 12:31:11 +01:00
IceToast
3ea5b4e010 feat: Added Extensionmigrations to laravel-settings config 2023-03-09 12:31:11 +01:00
IceToast
322bfed972 feat: Added PayPal Settings 2023-03-09 12:31:11 +01:00
IceToast
a5b48c4730
fix: 🐛 Removed redundant closing div tags 2023-03-02 16:00:06 +01:00
1day2die
869bd1e0ef revert 2023-03-01 14:24:59 +01:00
1day2die
a16c76940f test 2023-03-01 14:23:15 +01:00
Dennis
d382ca1613
Revert 2023-03-01 14:15:14 +01:00
Dennis
6cf1cc0fe9
Do not delete old_settings for now 2023-03-01 14:10:47 +01:00
IceToast
de911a0e3e fix: 🐛 Users table searching 2023-02-24 14:04:23 +01:00
IceToast
84f94ed564 feat: Add custom settings category icon 2023-02-24 14:02:28 +01:00
IceToast
a6a729a065 fix: 🐛 Remove quotes from strings 2023-02-24 14:02:28 +01:00
IceToast
9124b2ddbd fix: 🐛 Rollback migrations 2023-02-24 14:02:28 +01:00
IceToast
d10df843cb fix: 🔥 delete duplicated code 2023-02-24 14:02:28 +01:00
IceToast
f2a714cece fix: 🗃️ settings table migration order 2023-02-24 14:02:28 +01:00
IceToast
d6573b9bfd chore: 🗃️ Add settings migrations down method 2023-02-24 14:02:28 +01:00
Ferks-FK
415a7ed2da Insert the roolback for the old method. 2023-02-24 14:02:28 +01:00
Ferks-FK
2073aa632d Remove unnecessary import 2023-02-24 14:02:28 +01:00
Ferks-FK
825b27df57 Remove test folder xD 2023-02-24 14:02:28 +01:00
Ferks-FK
336b0c9799 Fix some options without their respective fields 2023-02-24 14:02:28 +01:00
IceToast
d48c1af824 style: 💄 Button styling 2023-02-24 14:02:28 +01:00
IceToast
8c9c70ef3f fix: 🐛 Show checkbox checked 2023-02-24 14:02:28 +01:00
IceToast
201aff6c0a feat: Set Values with correct type 2023-02-24 14:02:28 +01:00
IceToast
309cfa0514 fix: 🐛 Multiple category field send 2023-02-24 14:02:28 +01:00
IceToast
c39d6b18b2 chore: 🔥 Remove old Settings classes 2023-02-24 14:02:28 +01:00
IceToast
d9d776e5da chore: 🦺 Added Validation to settings 2023-02-24 14:02:28 +01:00
IceToast
7f8017de76 feat: Persist settings in DB on update 2023-02-24 14:02:28 +01:00
IceToast
c3420b1684 feat: Add Settings update route & Validation functionality 2023-02-24 14:02:28 +01:00
IceToast
20f0e1dfc2 refactor: ♻️ Use Switch state instead of elseif 2023-02-24 14:02:28 +01:00
IceToast
ac5fa6c25c feat: Add InputData to all settings & Render correct inputs 2023-02-24 14:02:28 +01:00
IceToast
1011e2dcc4 chore: 🔥 Remove old settings tabs blade 2023-02-24 14:02:28 +01:00
IceToast
b2d73eeda0 feat: Added Input components 2023-02-24 14:02:28 +01:00
IceToast
25b2d06572 feat: Added Labels to settings (controller) and show settings for category tab 2023-02-24 14:02:28 +01:00
IceToast
3b1b6e9b7e feat: Added validation to Settings 2023-02-24 14:02:28 +01:00
IceToast
e9fec445ff feat: Send all settings in their groups to frontend -> show tabs 2023-02-24 14:02:28 +01:00
Ferks-FK
a9619be8d0 front-end things 2023-02-24 14:02:28 +01:00
Ferks-FK
f1a3126758 Correct some types of values from the old table 2023-02-24 14:02:28 +01:00
Ferks-FK
49904b22bf Some things to be adjusted 2023-02-24 14:02:28 +01:00
Ferks-FK
fc49c6490f More Variables.. 2023-02-24 14:02:28 +01:00
Ferks-FK
f00f5addfe Change missing variables 2023-02-24 14:02:28 +01:00
Ferks-FK
9cd25034fc Another ton of modifications 2023-02-24 14:02:28 +01:00
Ferks-FK
8935dd6482 Missing locale datatables 2023-02-24 14:02:28 +01:00
Ferks-FK
da4693b310 That's better xD 2023-02-24 14:02:28 +01:00
Ferks-FK
302ef9227a Implement settings cache 2023-02-24 14:02:28 +01:00
Ferks-FK
411441e1a8 Migrate all variables in the front-end 2023-02-24 14:02:28 +01:00
Ferks-FK
cfd370eb6d Too many changes to mention. 2023-02-24 14:02:28 +01:00
Ferks-FK
1d2548a174 Update all imports 2023-02-24 14:02:28 +01:00
Ferks-FK
16ca760901 Several modifications, something already works 2023-02-24 14:02:28 +01:00
Ferks-FK
636d4e259e Register all classes in the config file 2023-02-24 14:02:28 +01:00
Ferks-FK
5305ae7803 fix: Ternary would always return 'true' 2023-02-24 14:02:28 +01:00
Ferks-FK
8e212889a4 Create all the settings files. 2023-02-24 14:02:28 +01:00
Ferks-FK
1086dd85a4 Ptero API keys are now encrypted. 2023-02-24 14:02:28 +01:00
Ferks-FK
ca0fae7bdb Add missing keys 2023-02-24 14:02:28 +01:00
Ferks-FK
2229586b58 feat: implement laravel-settings 2023-02-24 14:02:28 +01:00
Dennis
c8e82ca57b
Update app.php 2023-02-23 09:38:09 +01:00
Dennis
fc575a9554
Update app.php 2023-02-23 09:37:56 +01:00
Dennis
485aeedbf1
0.9.3 (#752) 2023-02-23 09:32:37 +01:00
IceToast
24f1319007 chore: 🔥 remove datatablessortable trait 2023-02-16 12:28:17 +01:00
IceToast
82887c013b chore: 🔥 remove datatablessortable trait 2023-02-16 12:28:17 +01:00
IceToast
aefa5ba1c8 fix: 🐛 admin/servers sort 2023-02-16 12:28:17 +01:00
IceToast
17ab473576 fix: 🐛 admin/users sort 2023-02-16 12:28:17 +01:00
IceToast
459069c5ee feat: 🐛 admin/servers sortable 2023-02-16 12:28:17 +01:00
IceToast
d905171fcb fix: 🐛 admin/store sorting 2023-02-16 12:28:17 +01:00
IceToast
a0268f1c47 fix: 🐛 Sorting on admin/users datatable 2023-02-16 12:28:17 +01:00
IceToast
b25ce96efb feat: Add Datatables Sortable trait 2023-02-16 12:28:17 +01:00
Johannes F
e78bd37da0 fix: 🚑️ Harmonize formatting and merge some try catch 2023-02-14 22:58:19 +01:00
LogischJo
371a37df7a fix: 🚑️ Use try catch to prevent 500 errors 2023-02-14 22:58:19 +01:00
Johannes F
af5d28e2a5 feat: Reopen tickets 2023-02-14 22:58:19 +01:00
Johannes F
8c2289ed74 fix: 🚑️ Wrong Store Page Name 2023-02-14 22:58:19 +01:00
2IceCube
60359a19ba Fixed Mobile View 2023-02-13 23:25:51 +01:00
2IceCube
faf9950562 Add Theme Watermark 2023-02-13 23:24:45 +01:00
Johannes F
040bcf2fa0 fix: 🚑️ Wrong ticket priority getting displayed 2023-02-11 22:23:17 +01:00
Dennis
495981cb5a
[Hotfix] Paypal payment -> blank page (#727) 2023-02-08 09:41:40 +01:00
IceToast
128015f0fa
chore: 🌐 localization 2023-02-07 14:18:22 +01:00
IceToast
d491271e7a
Merge branch 'development' of https://github.com/IceToast/controlpanel into development 2023-02-07 14:00:55 +01:00
IceToast
1e9c6c5838
Merge branch 'ControlPanel-gg:development' into development 2023-02-07 13:59:21 +01:00
IceToast
b8946d8666
fix: 🚑️ Paypal authentication error -> blank page 2023-02-07 13:59:09 +01:00
Dennis
a4c2fa2bc9
[Hotfix] v0.9.2 (#726) 2023-02-06 20:54:34 +01:00
IceToast
fbdb3f12a1 fix: 🚑️ Set Theme to default when no setting or setting null 2023-02-06 20:44:09 +01:00
IceToast
01fc633303
fix: 🚑️ Set Theme to default when no setting or setting null 2023-02-06 20:42:17 +01:00
IceToast
8da3e8cb67
Merge branch 'ControlPanel-gg:development' into development 2023-02-06 14:11:18 +01:00
Dennis
bb6d99cb74
Update versionnumber 2023-02-06 12:53:57 +01:00
IceToast
f6c8eecb87 fix: 🐛 Wrong class name 2023-02-06 12:01:29 +01:00
IceToast
f1de845554 fix: 🐛 getCsrfIgnoredRoutes map all routes just once 2023-02-06 12:01:29 +01:00
IceToast
308d1775bf fix: 🐛 Wrong class name 2023-02-06 12:00:21 +01:00
IceToast
ef5c063b09 fix: 🐛 getCsrfIgnoredRoutes map all routes just once 2023-02-06 12:00:21 +01:00
IceToast
29e9ed6e4a
fix: 🐛 Wrong class name 2023-02-06 11:57:14 +01:00
IceToast
33cc108900
fix: 🐛 getCsrfIgnoredRoutes map all routes just once 2023-02-06 11:32:19 +01:00
Dennis
0b173fb428
0.9.1 Hotfix (#717) 2023-02-06 10:53:32 +01:00
Dennis
9b193c74dd
[HOTFIX] Stripe extensions enable flag (#716) 2023-02-05 21:15:08 +01:00
IceToast
663e2370a2
fix: 🐛 Enable Stripe on local environment 2023-02-05 21:11:28 +01:00
IceToast
b7a765bd8c
fix: 🚑️ Stripe extensions enable flag 2023-02-05 21:07:13 +01:00
Dennis
f79d7d5380
sync main (#715) 2023-02-05 18:58:43 +01:00
Dennis
6b45719e79
v0.9.1 (#714) 2023-02-05 18:58:10 +01:00
1day2die
fd6e8bd51e Revert "Merge branch 'main' of https://github.com/1day2die/dashboard into development"
This reverts commit 704548343b, reversing
changes made to 4657b3419b.
2023-02-05 18:36:33 +01:00
1day2die
704548343b Merge branch 'main' of https://github.com/1day2die/dashboard into development 2023-02-05 18:35:09 +01:00
Dennis
4657b3419b
[Fix] Not all tickets deleted on user deletion (#711) 2023-02-05 17:10:48 +01:00
IceToast
3522dfe4c7
Merge branch 'ControlPanel-gg:development' into development 2023-02-05 16:37:18 +01:00
IceToast
7283f6e887
fix: 🚑️ Delete all Tickets on user deletion 2023-02-05 16:36:55 +01:00
Dennis
1d2e453fc2
[FIX] Useful_links: Add Label for Position and links on home (#705) 2023-02-02 23:45:16 +01:00
1day2die
e8efc743b2 Add Label for Position and Useful links on home 2023-02-02 23:44:13 +01:00
Dennis
a5e276d50e
[Feature] Cleanup "open" payments once a day (#704) 2023-02-02 23:33:06 +01:00
IceToast
0ffb7ccb65
refactor: 🚚 Rename Cleanup command 2023-02-02 23:21:52 +01:00
IceToast
9d383600bc
fix: 🐛 Missing primary key on payments table 2023-02-02 23:20:19 +01:00
IceToast
62358aec4e
feat: Add open payments cleanup to schedule 2023-02-02 23:19:08 +01:00
IceToast
c1a2c34a06 fix: 🐛 Wrong label name 2023-02-02 15:26:17 +01:00
IceToast
aedf7c9a22 feat: Use better issue templates 2023-02-02 15:26:17 +01:00
Dennis
58917d0b3e
[FEATURE] Add/Delete Ticket Categories (#699) 2023-02-02 14:33:26 +01:00
1day2die
9420231b58 Edit category name 2023-02-02 13:47:21 +01:00
IceToast
bd11bd2657
Revert "Merge branch 'v0.10' into development"
This reverts commit e6a545309f, reversing
changes made to 036fe6f47f.
2023-02-01 21:03:20 +01:00
IceToast
e6a545309f
Merge branch 'v0.10' into development 2023-02-01 20:55:34 +01:00
1day2die
630ebc6b88 Editable Ticket categories 2023-02-01 11:07:37 +01:00
Dennis
036fe6f47f
[FIX] Ticket tweaks || [THEME] Update theme (#698) 2023-02-01 10:12:49 +01:00
1day2die
f9735bdd35 Fix ticket message disapearing 2023-02-01 10:11:06 +01:00
1day2die
57577c95b4 Ticket Tweaks 2023-02-01 10:06:15 +01:00
1day2die
4351c8b678 Update BlueInfinity, Change AppVersion 2023-02-01 09:54:21 +01:00
Dennis
985a4e94cb
[FIX] Installation without git - wrong logs + showing branch "unknown" (#693) 2023-01-31 16:09:37 +01:00
1day2die
bcd69b4582 Change error to notice if not installed with git 2023-01-31 16:08:42 +01:00
Dennis
e180704dd1
[Fix] PartnerController Refactor | [Fix] Ticket improvements (#690) 2023-01-31 14:25:14 +01:00
1day2die
4b3ee928ff localization 2023-01-31 14:21:36 +01:00
1day2die
c8c3bff636 Update Blue-ish Theme 2023-01-31 12:37:27 +01:00
1day2die
a7ef7eb17d Refactor Partnercontroller 2023-01-31 10:39:06 +01:00
1day2die
eeed88aba8 Refactor whole PartnerController 2023-01-31 10:38:43 +01:00
1day2die
e90479e452 ,show priority on ticket 2023-01-31 10:23:18 +01:00
1day2die
826be5ce5b increase ticket ID lenght 2023-01-31 10:16:45 +01:00
Dennis
d7ca83ef97
[Refactor] Move referral_code generation to trait | [Fix] Allow free products handling (#670) 2023-01-30 22:48:42 +01:00
IceToast
4449353f3a
fix: 🐛 Do not show "Payment Methods" if product is free 2023-01-30 22:46:14 +01:00
IceToast
a077092631
feat: Allow to buy a product for free 2023-01-30 22:44:48 +01:00
IceToast
ff2d0b794e
fix: 🐛 handle free products 2023-01-30 22:34:06 +01:00
IceToast
cfdfaf2fb1
Merge branch 'ControlPanel-gg:development' into development 2023-01-30 22:23:47 +01:00
Dennis
d75fe2f428
[FIX] Themes / Paypal config || [Feature] Add blue-ish Theme for case-study (#672) 2023-01-30 22:06:46 +01:00
1day2die
8fa42c9f30 [Fix] Delete vite.config.js 2023-01-30 11:20:01 +01:00
1day2die
e1dbbb21ee [FIX] Paypal Checkout disabled even if keys are set 2023-01-30 11:19:50 +01:00
1day2die
65f777baae [Fix] Themes || [Feature] Add blue-ish theme for case study 2023-01-30 10:47:32 +01:00
1day2die
9cd65a3f02 [FIX] Default theme 2023-01-30 09:43:13 +01:00
Dennis
7cdf9c6c93
[FIX] TOS Editor (#671) 2023-01-30 09:13:20 +01:00
1day2die
360a70f3d8 Update web.php 2023-01-30 09:12:13 +01:00
1day2die
119a7fdebb edit info boxes 2023-01-29 23:31:53 +01:00
Dennis
6263f163f9
[FEATURE] wysiwyg editor for imprint/Tos/privacy (#669) 2023-01-29 23:15:35 +01:00
IceToast
5ae631ee4f
refactor: ♻️ Move referral_code generation to trait 2023-01-29 23:12:34 +01:00
1day2die
9f165580e5 back to default 2023-01-29 22:21:22 +01:00
1day2die
218ce1c785 [FEATURE] wysiwyg editor for imprint/Tos/privacy 2023-01-29 22:16:06 +01:00
Dennis
fd8dbb10a5
[FIX] User creation command referral code (#668) 2023-01-29 21:49:48 +01:00
1day2die
2c16379ec1 [FIX] User creation command referral code 2023-01-29 21:01:14 +01:00
Dennis
a4695681b5
finish usefullinks (#667) 2023-01-29 20:34:22 +01:00
1day2die
2c041bbe14 finish themes 2023-01-29 20:33:27 +01:00
Dennis
0919202599
Merge branch 'development' into development 2023-01-29 20:31:10 +01:00
Dennis
ca26da77c7
Fix the theme selector. (#666) 2023-01-29 20:29:46 +01:00
Ferks-FK
bc2d5088c5 Fix the theme selector. 2023-01-29 18:32:13 +00:00
1day2die
8b4477bc8d finish usefullinks, fix vite 2023-01-26 23:26:00 +01:00
Dennis
64a241c1f9
Merge branch 'ControlPanel-gg:main' into main 2023-01-26 16:47:21 +01:00
Dennis
fb8138a69d
Revert "[Hotfix] Enable payment gateways" (#664) 2023-01-26 16:44:20 +01:00
Dennis
e2c07edd7d
Revert "[Hotfix] Enable payment gateways" 2023-01-26 16:43:53 +01:00
Dennis
6455cb371f
Merge branch 'ControlPanel-gg:main' into main 2023-01-26 16:38:51 +01:00
IceToast
7de05a841f
[Hotfix] Enable payment gateways (#653) 2023-01-26 16:33:49 +01:00
Dennis
0cfa02b4ce
[Feature] Add useful links in top menubar (#662) 2023-01-26 16:08:57 +01:00
1day2die
de1c2e7b57 fix Themes Settings Tab 2023-01-26 15:56:54 +01:00
1day2die
d8dcad4991 better query 2023-01-26 15:29:38 +01:00
1day2die
b343134c01 using enums, adding more positions 2023-01-26 15:22:42 +01:00
1day2die
fbc9b53b6c disable "upgrade" badge on tiny 2023-01-26 14:34:32 +01:00
Dennis
93e39165c2
Update README.md (#660) 2023-01-25 22:54:58 +01:00
1day2die
f923eef868 Useful Links in Topbar 2023-01-25 22:32:59 +01:00
GeckoBoy84
0547066b30
Update README.md 2023-01-25 21:08:49 +00:00
GeckoBoy84
573106ae65
Update README.md 2023-01-25 21:07:56 +00:00
GeckoBoy84
9bbdbfecfb
Update Server Creation Image 2023-01-25 20:49:17 +00:00
GeckoBoy84
b2efd238c4
Update Ticket System Image 2023-01-25 20:48:40 +00:00
GeckoBoy84
ba3f03c7ce
Update Example server products Image 2023-01-25 20:47:59 +00:00
GeckoBoy84
ec1ce2df83
Add Voucher System Image 2023-01-25 20:46:54 +00:00
GeckoBoy84
d06f7e4933
Add Partner System Image 2023-01-25 20:45:27 +00:00
GeckoBoy84
1644ae706a
Update Ticket System Image 2023-01-25 20:44:02 +00:00
GeckoBoy84
7d65dcc7ef
Update Overview image 2023-01-25 20:43:03 +00:00
GeckoBoy84
3f02e0077c
Update Example Products Image 2023-01-25 20:42:35 +00:00
Dennis
771d013087
Update README.md 2023-01-25 21:40:13 +01:00
Dennis
843f66575f
[Fix] User creation settings (#648) 2023-01-25 21:39:02 +01:00
Dennis
56f819b905
Update README.md (#658) 2023-01-25 21:38:21 +01:00
Dennis
7ae7ad92f8
[Feature] Adding billing cycle system (#646) 2023-01-25 21:38:02 +01:00
GeckoBoy84
9f6c8dc21d
New Picture 2023-01-25 20:38:02 +00:00
Dennis
c0beaeb2b0
fix minimum credits display on create server (#659) 2023-01-25 21:36:01 +01:00
1day2die
b26ebb3150 fix minimum credits display on create server 2023-01-25 21:34:45 +01:00
GeckoBoy84
329079567e
Add more to the features list 2023-01-25 20:33:45 +00:00
Ferks-FK
0042c3bbe4 Merge branch 'development' of https://github.com/Ferks-FK/dashboard into development 2023-01-25 18:43:31 +00:00
Ferks-FK
6b848b1446 Multiple changes 2023-01-25 18:42:54 +00:00
Ferks
8eb92ef7c6
Merge branch 'ControlPanel-gg:development' into development 2023-01-25 13:30:10 -04:00
Dennis
8fe523cacf
Merge branch 'ControlPanel-gg:main' into main 2023-01-25 13:14:03 +01:00
IceToast
8745591f97
[Feature] Add error logger for register controller (#654) 2023-01-23 21:44:32 +01:00
IceToast
a3ba6257ba feat: 🔊 Add error logger on registration 2023-01-23 21:34:22 +01:00
IceToast
a9c0df015c
[Hotfix] Enable Payment Gateways (#652) 2023-01-23 17:00:31 +01:00
IceToast
f74c188d5b
Merge branch 'ControlPanel-gg:development' into development 2023-01-23 16:59:23 +01:00
IceToast
195eeb06b6
[Hotfix] Composer.lock mismatch (#651) 2023-01-23 16:58:35 +01:00
IceToast
9260b967b3 fix: 🚑️ Enable/Disable Payment Gateways TODO: Integrate into settings! 2023-01-23 16:57:28 +01:00
Dennis
ec63b6e58e
Update Composer.lock (#650) 2023-01-23 16:29:26 +01:00
1day2die
7835392d95 Update composer.lock 2023-01-23 16:27:33 +01:00
1day2die
265a35a903 Update Composer.lock 2023-01-23 16:25:35 +01:00
Dennis
e51f2aaf61
Merge branch 'ControlPanel-gg:main' into main 2023-01-23 16:01:51 +01:00
Dennis
f8fcaa6d1f
[Release] Version 0.9 🔥 (#649) 2023-01-23 15:31:20 +01:00
Ferks-FK
977d2ddd18 Simple fix of a checkbox 2023-01-22 19:58:28 +00:00
Dennis
c32bfc1c8a
[Fix/Features] CSRF Ignoring Routes support & Fix Invoice creation on webhook (#640) 2023-01-22 16:21:37 +01:00
IceToast
48b163fd6c Merge branch 'V0.10' of https://github.com/IceToast/controlpanel into V0.10 2023-01-21 15:13:51 +01:00
IceToast
5fd58514b5 Merge branch addons/billing_period into V0.10 2023-01-21 15:13:48 +01:00
IceToast
8c78a00f2a
Merge branch 'ControlPanel-gg:development' into development 2023-01-21 15:12:45 +01:00
IceToast
e5dd05e701 Merge branch addons/billing_period into V0.10 2023-01-21 15:04:12 +01:00
IceToast
d11bb52038 feat: Support csrf ignoring routes for extensions & moved extension config to own file 2023-01-21 01:04:23 +01:00
IceToast
3909202958 feat: Added several functions to the ExtensionHelper 2023-01-21 01:03:47 +01:00
IceToast
e795b65df1 refactor: ♻️ Less DB Calls & PartnerDiscount bug at invoice make() 2023-01-20 23:44:49 +01:00
IceToast
dba8e2ebb8 refactor: ♻️ So stupid function calls... 2023-01-20 23:43:23 +01:00
IceToast
6216892594 fix: 🐛 PartnerDiscount expected auth login -> not possible on webhooks 2023-01-20 23:42:53 +01:00
Dennis
b89b03d314
update language file
update language file
2023-01-20 10:16:59 +01:00
1day2die
1da57a9478 update language file 2023-01-20 10:14:40 +01:00
Dennis
5781c23f0e
Merge pull request #635 from GeckoBoy84/development
Fix language file for partner values
2023-01-20 10:05:47 +01:00
Dennis
450213ae66
Merge pull request #638 from IceToast/development
Fix composer php version
2023-01-19 18:30:37 +01:00
IceToast
8245eebabc
Merge branch 'ControlPanel-gg:development' into development 2023-01-19 18:08:59 +01:00
IceToast
519ed7e8c7 chore: Lock composer php version 2023-01-19 18:08:40 +01:00
Dennis
bd7d2770a2
Update SettingsSeeder.php 2023-01-19 16:15:53 +01:00
Dennis
29fa7744e1
Fix Invoiceevent and referral setting seed
Fix Invoiceevent and referral setting seed
2023-01-19 16:15:22 +01:00
IceToast
c3ec17e1e8 fix: 🐛 Disable Referral by defualt 2023-01-19 16:07:23 +01:00
IceToast
5289567773 fix: 🚑️ Remove queueable from Eventhandler to fix execution 2023-01-19 16:05:56 +01:00
Dennis
e978cb9914
[FEATURE] Server owner transfer
[FEATURE] Server owner transfer
2023-01-19 11:29:42 +01:00
Dennis
7a91013bd4
[FEATURE] Extension Support & Payment Gateways as Extensions 🔥
[FEATURE] Extension Support & Payment Gateways as Extensions 🔥
2023-01-19 11:29:27 +01:00
IceToast
c468e53fad Merge branch 'development' into feature/extendable_payment_gateways 2023-01-19 11:28:51 +01:00
IceToast
e146976af5 style: 💄 Styling & added infos to server edit 2023-01-19 11:23:06 +01:00
IceToast
7d81fa6247 feat: Added Server owner transfer 2023-01-19 11:18:51 +01:00
IceToast
1a75d63ba1 refactor: cleaner code closes #605 2023-01-18 23:54:37 +01:00
IceToast
3b2a851f4f fix: 🚑️ Create multiple servers when clicking submit multiple times 2023-01-18 23:50:52 +01:00
IceToast
64cd6ac98c feat: Added API-Route support for extensions 2023-01-18 18:05:32 +01:00
IceToast
30f70b35a2 fix: 🐛 Disabled submit button when no method choosen 2023-01-18 18:01:54 +01:00
GeckoBoy84
c4b12f95e9
Fix language file for partner values 2023-01-17 16:11:28 +00:00
IceToast
41f8fd9550 fix: 🐛 PayPalSuccess payment status "paid" & Require auth on payment routes 2023-01-17 15:35:58 +01:00
Dennis
3c8261f286
fix installer php version
fix installer php version
2023-01-17 14:30:31 +01:00
Dennis
ee56002e1a
Lots of small front end things
Lots of small front end things
2023-01-17 14:30:03 +01:00
1day2die
a5ec206a08 fix installer php version 2023-01-17 14:28:13 +01:00
GeckoBoy84
f0e614fa2a Show Unsuspended/Total Servers 2023-01-16 22:15:35 +00:00
GeckoBoy84
ca3b6cbb63 Capitalise Imprint inline with TOS and Privacy Policy Capitalisation 2023-01-16 21:58:10 +00:00
GeckoBoy84
d42c25bf56 -1 shows the actual value 2023-01-16 21:34:37 +00:00
GeckoBoy84
e1b4a1b9b8 Add more info to create partner page 2023-01-16 20:53:17 +00:00
GeckoBoy84
6e6102d125 Show minimum required credits 2023-01-16 20:46:07 +00:00
Dennis
c3f135f532
remove unused imports
remove unused imports
2023-01-16 18:53:53 +01:00
1day2die
a088b81366 remove unused imports 2023-01-16 18:53:09 +01:00
Dennis
fe26ba7305
Update README.md
Update README.md
2023-01-16 18:40:05 +01:00
Dennis
562aae8c92
fix user factory, fix loadingtimes of users table
fix user factory, fix loadingtimes of users table
2023-01-16 18:33:01 +01:00
1day2die
b8516c423e fix user factory, fix loadingtimes of users table 2023-01-16 18:30:25 +01:00
GeckoBoy84
47deda23e5
Update README.md 2023-01-16 07:13:13 +00:00
IceToast
3a87bf303c Merge branch 'development' into feature/extendable_payment_gateways 2023-01-15 02:25:13 +01:00
IceToast
c0ae772981 chore: ⚰️ Remove dead "use" 2023-01-15 02:24:16 +01:00
IceToast
314d4de816 fix: 🐛 TypeError 2023-01-15 02:22:03 +01:00
IceToast
11a8e50d2e fix: 🐛 Only update user credits etc. when paid 2023-01-15 01:41:42 +01:00
IceToast
a34825a692 feat: Moved Stripe functions to extension 2023-01-15 01:41:16 +01:00
IceToast
e6ef07139d feat: Moved Stripe routes to extension 2023-01-15 01:40:25 +01:00
IceToast
8516523efa style: 💄 Margin on payment methods 2023-01-15 01:39:54 +01:00
IceToast
eb122a99f2 fix: 🐛 Give Config Method the extension name 2023-01-15 01:39:38 +01:00
IceToast
45aaac4189 refactor: ♻️ Extracted Payment logic to events 2023-01-15 00:43:13 +01:00
IceToast
f00a11da0e refactor: 🔥 Remove activtiylog from payment model -> no spam 2023-01-15 00:42:20 +01:00
IceToast
b72d142a81 chore: 🔥 Remove Paypal Code from payment controller 2023-01-15 00:41:57 +01:00
IceToast
0257bf4e85 feat: Added new events to extract payment logic 2023-01-15 00:41:31 +01:00
IceToast
59f8e8c696 fix: 🐛 Payment Creation and use paymentEvent instead of manual invoice creation 2023-01-14 23:34:54 +01:00
IceToast
1f79a19943 feat: Render all available payment gateways from controller 2023-01-14 23:34:10 +01:00
IceToast
69dd7a6855 fix: 🐛 Removed PayPal Routes -> to own extension 2023-01-14 23:33:30 +01:00
Dennis
a49c7cb40c
Fix URL in Activitylog
Fix URL in Activitylog
2023-01-14 22:51:37 +01:00
1day2die
1d08c22d1d Fix URL in Activitylog 2023-01-14 22:51:19 +01:00
IceToast
8a03a7b16b feat: Create Invoiceable Trait & Fix routing Extension 2023-01-14 22:38:13 +01:00
IceToast
10f7e2688e feat: Create PaymentEvent / Listener 2023-01-14 22:37:23 +01:00
IceToast
3cb42a8b60 Merge branch 'development' into feature/extendable_payment_gateways
# Conflicts:
#	app/Http/Controllers/Admin/PaymentController.php
2023-01-14 21:51:20 +01:00
Dennis
5ed1f6d958
Ticket notify
Ticket notify
2023-01-13 19:38:12 +01:00
1day2die
ac1ca07e64 Update Misc.php 2023-01-13 19:34:37 +01:00
1day2die
a0800509cb ticket notify 2023-01-13 19:32:32 +01:00
Dennis
5e9cd13206
Merge pull request #626 from 1day2die/development
possible ticket fix
2023-01-13 19:19:40 +01:00
1day2die
e6b2afa1dc possible ticket fix 2023-01-13 19:18:50 +01:00
Dennis
c817b5d72d
SEO & Update Notification
Development
2023-01-13 18:58:06 +01:00
1day2die
bfa24facf9 Update Notification on Overview 2023-01-13 18:57:35 +01:00
1day2die
540bb95291 SEO 2023-01-13 18:41:32 +01:00
Dennis
925574e286
Update home.blade.php
Update home.blade.php
2023-01-12 16:03:21 +01:00
1day2die
4a79f3194e Update home.blade.php 2023-01-12 15:51:58 +01:00
Dennis
f41f54cc60
TinyMCE, MOTD
TinyMCE, MOTD,
2023-01-12 15:43:50 +01:00
1day2die
5d7550b43f TinyMCE, MOTD, 2023-01-12 15:43:24 +01:00
Dennis
3d20917710
Merge pull request #622 from 1day2die/development
oops. wrong route
2023-01-12 10:36:34 +01:00
1day2die
86d22b5e9a oops. wrong route 2023-01-12 10:35:57 +01:00
Dennis
91f1da67f0
Smaller fixes, basically typos
Development
2023-01-12 10:35:03 +01:00
1day2die
a74ade90ba get rid of the info label 2023-01-12 10:33:25 +01:00
1day2die
ca4309c131 close button on ticket index 2023-01-12 10:31:56 +01:00
1day2die
ae16fd6d6a change adress to address 2023-01-12 10:25:28 +01:00
1day2die
bdc99bd887 change "adress" to "address" 2023-01-12 10:25:14 +01:00
Dennis
974ef9f77e
Small Fixes
Development
2023-01-11 16:52:34 +01:00
1day2die
db2bae420f Merge branch 'development' of https://github.com/1day2die/dashboard into development 2023-01-11 16:51:25 +01:00
1day2die
c2d50c3b33 fix imprint 2023-01-11 16:51:18 +01:00
1day2die
b69cbcfe65 upgrade installer, fix TOS showing 2023-01-11 16:44:44 +01:00
Dennis
ecb99f8f66
Merge branch 'ControlPanel-gg:development' into development 2023-01-11 16:22:32 +01:00
1day2die
8bff4f857b fixes 2023-01-11 16:22:19 +01:00
1day2die
094393e7fb small fixes 2023-01-11 15:48:22 +01:00
1day2die
831a38a317 Merge branch 'development' of https://github.com/1day2die/dashboard into development 2023-01-11 15:32:03 +01:00
1day2die
5bcfd40a34 fix default theme 2023-01-11 15:31:46 +01:00
Dennis
8e5a6db764
Merge pull request #619 from 1day2die/development
Development
2023-01-11 15:28:02 +01:00
Dennis
2a600e29ae
Merge branch 'ControlPanel-gg:development' into development 2023-01-11 15:27:44 +01:00
1day2die
8e1812c1d6 admins shouldnt delete themselves 2023-01-11 11:46:29 +01:00
Dennis
409255dd5c
Adding full theme Support
Adding full theme Support
2023-01-11 11:21:13 +01:00
1day2die
a0483865bd fix parent theme 2023-01-11 11:20:23 +01:00
1day2die
3cc927c0b5 delete 1day2die testtheme 2023-01-11 11:09:54 +01:00
Dennis
4a31504593
add theme support
add theme support
2023-01-11 11:08:51 +01:00
1day2die
9b7ca02f00 add theme support 2023-01-11 11:08:22 +01:00
Dennis
ee1526fa11
Fixed backtick
Fixed backtick
2023-01-10 20:33:12 +01:00
rrm4395
8e29ffc5a6 Fixed backtick 2023-01-10 19:28:18 +00:00
Dennis
18ec685902
Update versionnumber 2023-01-10 20:07:15 +01:00
Dennis
871bd680b8
Merge pull request #616 from 1day2die/development
Development
2023-01-10 19:51:15 +01:00
Dennis
a591f86636
Merge branch 'ControlPanel-gg:development' into development 2023-01-10 19:50:08 +01:00
1day2die
f12f95bce7 language fix 2023-01-10 19:48:46 +01:00
1day2die
94fe7d245c Alert on Homepage 2023-01-10 19:45:19 +01:00
1day2die
16a48cd2aa allow login via username 2023-01-10 19:12:07 +01:00
Dennis
699a4bda95
Merge pull request #615 from 1day2die/development
Laravel 9.46
2023-01-10 19:03:59 +01:00
Dennis
9e8337efcb
Merge pull request #51 from 1day2die/shift-77419
Laravel 9.x Shift
2023-01-10 19:03:12 +01:00
Dennis
aacbd6780c
Merge branch 'development' into shift-77419 2023-01-06 00:13:50 +01:00
Dennis
eb0c77cea8
Merge branch 'ControlPanel-gg:main' into shift-77419 2023-01-06 00:10:14 +01:00
Dennis
7c717ec61c
Many Many small Fixes
adding minimum credits field in settings (it was present only in the database)
added frontend checks for settings fields (numbers)
added close ticket button for non-admin users
fixed some datatable´s sorting, adding some field, removing dupes,... (etc. Payments table, Products table, Tickets tables)
TOS, imprint and privacy policy in footer - as default they are now disabled
Made partner discounts a little bit easier to understand
little changes to delete user popup (in their profile)
removed the hamburger menu from server cards and made only a single phpmyadmin button
2023-01-06 00:09:48 +01:00
Dennis
81756c2587
Merge pull request #612 from ok236449/Partners-and-discounts
Partners and discounts - finishing it up
2023-01-06 00:09:33 +01:00
ok236449
7ca842e712 many many fixes 2023-01-06 00:01:23 +01:00
1day2die
2304b64d9a fixes 2023-01-05 19:33:46 +01:00
Shift
0e3f6c98aa
Shift cleanup 2023-01-05 17:03:38 +00:00
Shift
78980a993f
Adopt anonymous migrations 2023-01-05 17:03:31 +00:00
Shift
1317fd9e42
Use <env> tags for configuration
`<env>` tags have a lower precedence than system environment variables making it easier to overwrite PHPUnit configuration values in additional environments, such a CI.

Review this blog post for more details on configuration precedence when testing Laravel: https://jasonmccreary.me/articles/laravel-testing-configuration-precedence/
2023-01-05 17:03:02 +00:00
Shift
2987812622
Bump Laravel dependencies 2023-01-05 17:03:02 +00:00
Shift
fde21b4c98
Default config files
In an effort to make upgrading the constantly changing config files
easier, Shift defaulted them and merged your true customizations -
where ENV variables may not be used.
2023-01-05 17:03:01 +00:00
Shift
a62e04a4e4
Shift config files 2023-01-05 17:03:00 +00:00
Shift
e947abcab2
Convert deprecated $dates property to $casts 2023-01-05 17:02:42 +00:00
Shift
8f4908b1b7
Adopt class based routes 2023-01-05 17:02:41 +00:00
Shift
abb28548f1
Remove unnecessary $model property 2023-01-05 17:02:37 +00:00
Shift
acb0c08ab4
Use Faker methods
Accessing Faker properties was deprecated in Faker 1.14.
2023-01-05 17:02:35 +00:00
Shift
a9d76b1cfd
Convert optional() to nullsafe operator 2023-01-05 17:02:33 +00:00
Shift
df257ead7a
Remove web.config 2023-01-05 17:02:33 +00:00
Shift
7ee787951b
Shift core files 2023-01-05 17:02:33 +00:00
Shift
8b7632d964
Upgrade to Flysystem 3.0 2023-01-05 17:02:19 +00:00
Shift
a57c063e6b
Replace deprecated HEADER_X_FORWARDED_ALL constant 2023-01-05 17:02:13 +00:00
Shift
daff0fc566
Streamline $commands property 2023-01-05 17:02:12 +00:00
Shift
472dead0d6
Remove fruitcake/laravel-cors dependency 2023-01-05 17:02:11 +00:00
Shift
e948472475
Shift registered middleware 2023-01-05 17:02:11 +00:00
Shift
2e1af401c1
Move resources/lang folder 2023-01-05 17:02:03 +00:00
Shift
70208d2157
Apply Laravel coding style
Shift automatically applies the Laravel coding style - which uses the PSR-12 coding style as a base with some minor additions.

You may customize the code style applied by configuring [Pint](https://laravel.com/docs/pint), [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer), or [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) for your project root.

For more information on customizing the code style applied by Shift, [watch this short video](https://laravelshift.com/videos/shift-code-style).
2023-01-05 17:01:42 +00:00
Dennis
db828e7285
Recaptcha on Password reset & TOS
Recaptcha on Password reset & TOS
2023-01-05 17:55:37 +01:00
Dennis
d55a6b1944
Merge branch 'ControlPanel-gg:development' into development 2023-01-05 17:54:11 +01:00
ok236449
ed5a968d11 finishing up of Partner checkout
support for 100% discount
2023-01-04 18:59:52 +01:00
ok236449
b6e45c443c fix admin overview year conditions
sort currencies
2023-01-04 18:57:57 +01:00
1day2die
6516f7047a recaptcha on password reset 2023-01-04 12:04:44 +01:00
1day2die
81662b8e17 TOS on register site 2023-01-04 11:51:35 +01:00
Dennis
4aa2f65ec3
Merge pull request #611 from einsz/fixed-nests-description
change nest description from string to text
2023-01-04 06:48:42 +01:00
einsz
e08d173e78 change migration down to the old state 2023-01-04 06:47:37 +01:00
IceToast
23cb226196 feat: Added payment.pay route and enhanced checkOut route controller to serve gateways to view 2023-01-03 21:17:25 +01:00
einsz
4645427403 change nest description to text
fixes error 500 on long nest description
2023-01-03 19:19:07 +01:00
IceToast
16a7391372 feat: Fixed extension routes & Added PayPal Extension Routes 2023-01-03 18:31:21 +01:00
1day2die
ab26a20dde update language 2023-01-03 16:12:07 +01:00
IceToast
4041955296 feat: Added Extension routes 2023-01-03 13:50:23 +01:00
Dennis
3c210d388d
self delete user, TOS
self delete user, TOS
2023-01-02 19:27:02 +01:00
1day2die
f8ab81e27d revert 2023-01-02 15:28:59 +01:00
1day2die
70677e3a3a Update create.blade.php 2023-01-02 15:15:59 +01:00
1day2die
5053daedb5 Update create.blade.php 2023-01-02 15:14:22 +01:00
1day2die
e909feb693 Update create.blade.php 2023-01-02 15:11:04 +01:00
1day2die
27f29fc0fa fix spam create 2023-01-02 15:07:50 +01:00
1day2die
bd2eec9a28 user allowed to delete himself 2023-01-02 15:00:57 +01:00
1day2die
d02d6e78ae added TOS 2023-01-02 13:46:21 +01:00
Dennis
8b8d73c3b2
Merge pull request #575 from ok236449/Partners-and-discounts
Partners and discounts
2023-01-02 13:09:18 +01:00
Dennis
ffd2aefc26
Merge pull request #606 from IceToast/terms_of_service
Imprint and Privacy Policy Page
2022-12-27 00:02:41 +01:00
IceToast
1149497e56 docs: 📝 Add info where to edit the file (info at settings) 2022-12-22 22:42:45 +01:00
IceToast
a2743376f8 refactor: 🚚 Move imprint/privacy to own directory 2022-12-22 22:36:28 +01:00
IceToast
84cce98110 chore: Localization 2022-12-22 22:30:16 +01:00
IceToast
3edff8f6fc feat: Added Imprint and Privacy Page & Links 2022-12-22 22:30:09 +01:00
IceToast
5d18b36083 feat: added new settings 2022-12-22 22:28:52 +01:00
Dennis
2dbe7268e4
Merge branch 'ControlPanel-gg:main' into main 2022-12-13 12:02:26 -08:00
Dennis
49291de381
Merge branch 'ControlPanel-gg:main' into main 2022-11-29 00:44:40 -08:00
Dennis
89bfa9c9d8
Merge branch 'ControlPanel-gg:main' into main 2022-10-27 09:01:08 +02:00
ok236449
e4b2c2ad6b fix second single underscore 2022-10-21 22:15:07 +02:00
ok236449
34eed0cb79 fix single underscore 2022-10-21 22:10:34 +02:00
ok236449
86cf2981c1 commission from first/every purchase 2022-10-18 23:33:33 +02:00
ok236449
b69850cb21 remove discount field without partnership 2022-10-18 21:56:26 +02:00
ok236449
825198fb5a Show referral system on homepage, partnerships 2022-10-18 17:35:10 +02:00
ok236449
47afdd2f19 Hide sections if there are no data 2022-10-14 23:09:16 +02:00
ok236449
63a30a1e57 Yearly tax overview, stop caching ticket overview 2022-10-14 22:59:02 +02:00
ok236449
7537286d3a actually fixing it... 2022-10-14 21:30:07 +02:00
ok236449
8dbd4be6ce fix discount returning null and error 500 2022-10-14 21:05:19 +02:00
1day2die
afd595c965 Merge branch 'main' of https://github.com/1day2die/dashboard 2022-10-11 15:26:29 +02:00
ok236449
d41a68c63e add discount to invoices 2022-08-27 20:35:17 +02:00
ok236449
5a3259b891 this typo must have been here for long 2022-08-27 19:55:53 +02:00
ok236449
12372b5071 add commission to index view 2022-08-27 19:53:14 +02:00
ok236449
dc0737e803 fix users table sorting and add users to payments 2022-08-27 19:44:27 +02:00
ok236449
3a53ddbc59 partner discounts and their referrals 2022-08-27 18:59:07 +02:00
ok236449
748b21117a
Merge pull request #8 from ok236449/Admin-overview-fixed-counters
Stop caching nodes and counters - hotfix
2022-08-24 22:28:22 +02:00
1day2die
6d46f8a6cc Merge branch 'main' of https://github.com/1day2die/dashboard 2022-08-16 23:03:26 +02:00
Dennis
8cbbcc66be
Update dependabot.yml 2022-08-11 10:01:42 +02:00
Dennis
822b4393c3
Update dependabot.yml 2022-08-11 10:01:31 +02:00
Dennis
4c20cb8969
Create dependabot.yml 2022-08-11 09:59:16 +02:00
Dennis
417f944b4d
Create codeql-analysis.yml 2022-08-11 09:55:16 +02:00
473 changed files with 22196 additions and 31306 deletions

View file

@ -1,5 +1,5 @@
### --- App Settings --- ###
APP_NAME=Controlpanel.gg
APP_NAME=Ctrlpanel.gg
APP_ENV=production
APP_KEY=
APP_DEBUG=false

View file

@ -1,38 +0,0 @@
---
name: "\U0001F41B Bug report"
about: Create a report to help us improve
title: ''
labels: bug
assignees: RamonRobben, AVMG20
---
**Describe the bug 🐛**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View file

@ -1,31 +0,0 @@
---
name: "\U0001F680 Feature request"
about: Suggest a feature or idea for this project
title: "[feature] "
labels: enhancement
assignees: RamonRobben, AVMG20
---
# 🚀 Feature Request
### Is your proposal related to a problem?
*Provide a clear and concise description of what the problem is.
For example, "I can't change x and it makes me have to do manual work"*
(Write your answer here.)
### Describe the solution you'd like
*Provide a clear and concise description of what you want to happen.
For Example, "Automate the changing of x so I don't have to do it manually"*
(Describe your proposed solution here.)
### Additional context
*Is there anything else you can add about the proposal?
You might want to link to related issues here, if you haven't already.*
(Write your answer here.)

43
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View file

@ -0,0 +1,43 @@
name: "\U0001F41B Bug report"
description: Create a report to help us improve
title: "[Bug]: "
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: What is your request about?
description: |
Provide a clear description of what the problem is.
validations:
required: true
- type: dropdown
id: version
attributes:
label: Branch
description: What branch are you on?
multiple: false
options:
- "main"
- "development"
validations:
required: true
- type: textarea
id: solution
attributes:
label: Solution idea
description: |
Provide a clear description of how you want this bug to be fixed (Optional)
validations:
required: false
- type: textarea
id: ctrlpanel-logs
attributes:
label: Ctrlpanel Logs
description: Please copy and paste your laravel-log output. You may also provide a link to it using the following command `tail -n 100 /var/www/controlpanel/storage/logs/laravel.log | nc pteropaste.com 99`
render: Shell
- type: textarea
id: additional-info
attributes:
label: Additional Info
description: Anything else that could be used to narrow down the issue, like your config.

24
.github/ISSUE_TEMPLATE/feature.yml vendored Normal file
View file

@ -0,0 +1,24 @@
name: "\U0001F680 Feature request"
description: Suggest a feature or idea for this project
title: "[Feature] "
labels: ["feature"]
body:
- type: textarea
id: feature-description
attributes:
label: Feature Description
description: Provide a clear description of what your idea is all about, what it tries to solve and why it should be implemented.
validations:
required: true
- type: textarea
id: solution
attributes:
label: Solution Idea
description: Provide a clear description how the idea should be implemented and why.
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional Info
description: Anything else that could be used to implement your idea.

View file

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

View file

@ -3,55 +3,65 @@
- PayPal Integration
- Stripe Integration
- Referral System
- Partner System
- Ticket System
- Upgrade/Downgrade Server Ressources
- Upgrade/Downgrade Server Resources
- Store (credit system with hourly billing and invoices)
- Email Verification
- Audit Log
- Admin Dashboard
- User/Server Management
- Customizable server plans
- Customisable server plans
- Vouchers
- Alert System
- Theme Support
- and so much more!
# ControlPanel-gg
# CtrlPanel-gg
![controlpanel](https://user-images.githubusercontent.com/45005889/123518824-06b05000-d6a8-11eb-91b9-d1ed36bd2317.png)
![ctrlpanel](https://user-images.githubusercontent.com/67899387/214684708-739c1d21-06e8-4dec-a4f1-81533a46cc7e.png)
![](https://img.shields.io/endpoint?label=v0.8%20Installations&url=https%3A%2F%2Fmarket.controlpanel.gg%2Fcallhome.php%3Fgetinstalls)
![](https://img.shields.io/badge/Overall%20Installations-3500%2B-green)
![](https://img.shields.io/github/stars/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/forks/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/tag/ControlPanel-gg/dashboard) [![Crowdin](https://badges.crowdin.net/controlpanelgg/localized.svg)](https://crowdin.com/project/controlpanelgg) ![](https://img.shields.io/github/issues/ControlPanel-gg/dashboard) ![](https://img.shields.io/github/license/ControlPanel-gg/dashboard) ![](https://img.shields.io/discord/787829714483019826)
![](https://img.shields.io/endpoint?label=v0.9%20Installations&url=https%3A%2F%2Fmarket.ctrlpanel.gg%2Fcallhome.php%3Fgetinstalls)
![](https://img.shields.io/badge/Overall%20Installations-5000%2B-green)
![](https://img.shields.io/github/stars/CtrlPanel-gg/panel) ![](https://img.shields.io/github/forks/CtrlPanel-gg/panel) ![](https://img.shields.io/github/tag/CtrlPanel-gg/panel) [![Crowdin](https://badges.crowdin.net/controlpanelgg/localized.svg)](https://crowdin.com/project/controlpanelgg) ![](https://img.shields.io/github/issues/CtrlPanel-gg/panel) ![](https://img.shields.io/github/license/CtrlPanel-gg/panel) ![](https://img.shields.io/discord/787829714483019826)
## About
ControlPanel's Dashboard is a dashboard application designed to offer clients a management tool to manage their pterodactyl servers. This dashboard comes with a credit-based billing solution that credits users hourly for each server they have and suspends them if they run out of credits.
CtrlPanel's Dashboard is a dashboard application designed to offer clients a management tool to manage their pterodactyl servers. This dashboard comes with a credit-based billing solution that credits users hourly for each server they have and suspends them if they run out of credits.
This dashboard offers an easy to use and free billing solution for all starting and experienced hosting providers. This dashboard has many customization options and added discord 0auth verification to offer a solid link between your discord server and your dashboard. You can check our Demo here ([Demo](https://demo.controlpanel.gg "Demo"))
This dashboard offers an easy to use and free billing solution for all starting and experienced hosting providers. This dashboard has many customisation options and added discord Oauth verification to offer a solid link between your discord server and your dashboard. You can check our [Demo here](https://demo.ctrlpanel.gg "Demo").
### [Installation](https://controlpanel.gg/docs/intro "Installation")
### [Installation](https://ctrlpanel.gg/docs/intro "Installation")
### [Updating](https://controlpanel.gg/docs/Installation/updating "Updating")
### [Updating](https://ctrlpanel.gg/docs/Installation/updating "Updating")
### [Discord](https://discord.gg/4Y6HjD2uyU "discord")
### [Discord](https://discord.gg/4Y6HjD2uyU "Discord")
### [Contributing](https://controlpanel.gg/docs/Contributing/contributing "Contributing")
### [Contributing](https://ctrlpanel.gg/docs/Contributing/contributing "Contributing")
### [Donating](https://controlpanel.gg/docs/Contributing/donating "Donating")
### [Donating](https://ctrlpanel.gg/docs/Contributing/donating "Donating")
# Preview
### Server Creation
![image](https://user-images.githubusercontent.com/8725848/171575021-bc248f12-2aba-44e8-82aa-bdc907b1d3fc.png)
![image](https://user-images.githubusercontent.com/67899387/214687234-d1ae58c0-5667-4e99-ac39-adeaabfcc7f2.png)
### Overview
![image](https://user-images.githubusercontent.com/8725848/171575809-7620ed4f-5715-420f-8c25-8bfa1c4342f7.png)
![image](https://user-images.githubusercontent.com/67899387/214685859-03c8d9e1-c685-4a07-979f-df2e88ec3931.png)
### Example server products
![image](https://user-images.githubusercontent.com/8725848/171575987-c1398ff6-83fa-4cb8-bd1f-986cee4da565.png)
![image](https://user-images.githubusercontent.com/67899387/214686950-218e1ede-6a1f-4e53-b3f4-fe1abc371a9c.png)
### Ticket System
![image](https://user-images.githubusercontent.com/8725848/184131270-9d997ebf-8965-4910-90d2-b410ae37f201.png)
![image](https://user-images.githubusercontent.com/67899387/214687123-0a3d0f8f-b53c-4b0d-869a-4d5df45f5184.png)
### Voucher System
![image](https://user-images.githubusercontent.com/67899387/214686578-ec9f0b0f-6047-4665-835f-70594b56dfd5.png)
### Partner System
![image](https://user-images.githubusercontent.com/67899387/214686321-36ba97a3-4181-4e60-9ba3-c9b318fe66a8.png)

View file

@ -5,8 +5,9 @@ namespace App\Classes;
use App\Models\Egg;
use App\Models\Nest;
use App\Models\Node;
use App\Models\Server;
use App\Models\Product;
use App\Models\Server;
use App\Models\User;
use Exception;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
@ -22,136 +23,160 @@ class Pterodactyl
public static function client()
{
return Http::withHeaders([
'Authorization' => 'Bearer ' . config("SETTINGS::SYSTEM:PTERODACTYL:TOKEN"),
'Content-type' => 'application/json',
'Accept' => 'Application/vnd.pterodactyl.v1+json',
])->baseUrl(config("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/api');
'Authorization' => 'Bearer ' . config('SETTINGS::SYSTEM:PTERODACTYL:TOKEN'),
'Content-type' => 'application/json',
'Accept' => 'Application/vnd.pterodactyl.v1+json',
])->baseUrl(config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/api');
}
public static function clientAdmin()
{
return Http::withHeaders([
'Authorization' => 'Bearer ' . config("SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN"),
'Content-type' => 'application/json',
'Accept' => 'Application/vnd.pterodactyl.v1+json',
])->baseUrl(config("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/api');
'Authorization' => 'Bearer ' . config('SETTINGS::SYSTEM:PTERODACTYL:ADMIN_USER_TOKEN'),
'Content-type' => 'application/json',
'Accept' => 'Application/vnd.pterodactyl.v1+json',
])->baseUrl(config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/api');
}
/**
* @return Exception
*/
private static function getException(string $message = "", int $status = 0): Exception
private static function getException(string $message = '', int $status = 0): Exception
{
if ($status == 404) {
return new Exception("Ressource does not exist on pterodactyl - " . $message, 404);
return new Exception('Ressource does not exist on pterodactyl - ' . $message, 404);
}
if ($status == 403) {
return new Exception("No permission on pterodactyl, check pterodactyl token and permissions - " . $message, 403);
return new Exception('No permission on pterodactyl, check pterodactyl token and permissions - ' . $message, 403);
}
if ($status == 401) {
return new Exception("No pterodactyl token set - " . $message, 401);
return new Exception('No pterodactyl token set - ' . $message, 401);
}
if ($status == 500) {
return new Exception("Pterodactyl server error - " . $message, 500);
return new Exception('Pterodactyl server error - ' . $message, 500);
}
return new Exception('Request Failed, is pterodactyl set-up correctly? - ' . $message);
}
/**
* @param Nest $nest
* @param Nest $nest
* @return mixed
*
* @throws Exception
*/
public static function getEggs(Nest $nest)
{
try {
$response = self::client()->get("/application/nests/{$nest->id}/eggs?include=nest,variables&per_page=" . config("SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT"));
$response = self::client()->get("/application/nests/{$nest->id}/eggs?include=nest,variables&per_page=" . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to get eggs from pterodactyl - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to get eggs from pterodactyl - ', $response->status());
}
return $response->json()['data'];
}
/**
* @return mixed
*
* @throws Exception
*/
public static function getNodes()
{
try {
$response = self::client()->get('/application/nodes?per_page=' . config("SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT"));
$response = self::client()->get('/application/nodes?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to get nodes from pterodactyl - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to get nodes from pterodactyl - ', $response->status());
}
return $response->json()['data'];
}
/**
* @return mixed
*
* @throws Exception
* @description Returns the infos of a single node
*/
public static function getNode($id) {
public static function getNode($id)
{
try {
$response = self::client()->get('/application/nodes/' . $id);
} catch(Exception $e) {
throw self::getException($e->getMessage());
}
if($response->failed()) throw self::getException("Failed to get node id " . $id . " - " . $response->status());
return $response->json()['attributes'];
}
public static function getServers() {
try {
$response = self::client()->get('/application/servers?per_page=' . config("SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT"));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if($response->failed()) throw self::getException("Failed to get list of servers - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to get node id ' . $id . ' - ' . $response->status());
}
return $response->json()['attributes'];
}
public static function getServers()
{
try {
$response = self::client()->get('/application/servers?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) {
throw self::getException('Failed to get list of servers - ', $response->status());
}
return $response->json()['data'];
}
/**
* @return null
*
* @throws Exception
*/
public static function getNests()
{
try {
$response = self::client()->get('/application/nests?per_page=' . config("SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT"));
$response = self::client()->get('/application/nests?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to get nests from pterodactyl", $response->status());
if ($response->failed()) {
throw self::getException('Failed to get nests from pterodactyl', $response->status());
}
return $response->json()['data'];
}
/**
* @return mixed
*
* @throws Exception
*/
public static function getLocations()
{
try {
$response = self::client()->get('/application/locations?per_page=' . config("SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT"));
$response = self::client()->get('/application/locations?per_page=' . config('SETTINGS::SYSTEM:PTERODACTYL:PER_PAGE_LIMIT'));
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to get locations from pterodactyl - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to get locations from pterodactyl - ', $response->status());
}
return $response->json()['data'];
}
/**
* @param Node $node
* @param Node $node
* @return mixed
*
* @throws Exception
*/
public static function getFreeAllocationId(Node $node)
@ -160,8 +185,9 @@ class Pterodactyl
}
/**
* @param Node $node
* @param Node $node
* @return array|mixed|null
*
* @throws Exception
*/
public static function getFreeAllocations(Node $node)
@ -172,7 +198,9 @@ class Pterodactyl
if (isset($response['data'])) {
if (!empty($response['data'])) {
foreach ($response['data'] as $allocation) {
if (!$allocation['attributes']['assigned']) array_push($freeAllocations, $allocation);
if (!$allocation['attributes']['assigned']) {
array_push($freeAllocations, $allocation);
}
}
}
}
@ -181,8 +209,9 @@ class Pterodactyl
}
/**
* @param Node $node
* @param Node $node
* @return array|mixed
*
* @throws Exception
*/
public static function getAllocations(Node $node)
@ -193,51 +222,53 @@ class Pterodactyl
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to get allocations from pterodactyl - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to get allocations from pterodactyl - ', $response->status());
}
return $response->json();
}
/**
* @param String $route
* @param string $route
* @return string
*/
public static function url(string $route): string
{
return config("SETTINGS::SYSTEM:PTERODACTYL:URL") . $route;
return config('SETTINGS::SYSTEM:PTERODACTYL:URL') . $route;
}
/**
* @param Server $server
* @param Egg $egg
* @param int $allocationId
* @param Server $server
* @param Egg $egg
* @param int $allocationId
* @return Response
*/
public static function createServer(Server $server, Egg $egg, int $allocationId)
{
return self::client()->post("/application/servers", [
"name" => $server->name,
"external_id" => $server->id,
"user" => $server->user->pterodactyl_id,
"egg" => $egg->id,
"docker_image" => $egg->docker_image,
"startup" => $egg->startup,
"environment" => $egg->getEnvironmentVariables(),
"limits" => [
"memory" => $server->product->memory,
"swap" => $server->product->swap,
"disk" => $server->product->disk,
"io" => $server->product->io,
"cpu" => $server->product->cpu
return self::client()->post('/application/servers', [
'name' => $server->name,
'external_id' => $server->id,
'user' => $server->user->pterodactyl_id,
'egg' => $egg->id,
'docker_image' => $egg->docker_image,
'startup' => $egg->startup,
'environment' => $egg->getEnvironmentVariables(),
'limits' => [
'memory' => $server->product->memory,
'swap' => $server->product->swap,
'disk' => $server->product->disk,
'io' => $server->product->io,
'cpu' => $server->product->cpu,
],
"feature_limits" => [
"databases" => $server->product->databases,
"backups" => $server->product->backups,
"allocations" => $server->product->allocations,
'feature_limits' => [
'databases' => $server->product->databases,
'backups' => $server->product->backups,
'allocations' => $server->product->allocations,
],
'allocation' => [
'default' => $allocationId,
],
"allocation" => [
"default" => $allocationId
]
]);
}
@ -248,7 +279,9 @@ class Pterodactyl
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to suspend server from pterodactyl - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to suspend server from pterodactyl - ', $response->status());
}
return $response;
}
@ -260,14 +293,17 @@ class Pterodactyl
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to unsuspend server from pterodactyl - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to unsuspend server from pterodactyl - ', $response->status());
}
return $response;
}
/**
* Get user by pterodactyl id
* @param int $pterodactylId
*
* @param int $pterodactylId
* @return mixed
*/
public function getUser(int $pterodactylId)
@ -277,14 +313,17 @@ class Pterodactyl
} catch (Exception $e) {
throw self::getException($e->getMessage());
}
if ($response->failed()) throw self::getException("Failed to get user from pterodactyl - ", $response->status());
if ($response->failed()) {
throw self::getException('Failed to get user from pterodactyl - ', $response->status());
}
return $response->json()['attributes'];
}
/**
* Get serverAttributes by pterodactyl id
* @param int $pterodactylId
*
* @param int $pterodactylId
* @return mixed
*/
public static function getServerAttributes(int $pterodactylId, bool $deleteOn404 = false)
@ -297,51 +336,70 @@ class Pterodactyl
//print response body
if ($response->failed()){
if($deleteOn404){ //Delete the server if it does not exist (server deleted on pterodactyl)
if ($response->failed()) {
if ($deleteOn404) { //Delete the server if it does not exist (server deleted on pterodactyl)
Server::where('pterodactyl_id', $pterodactylId)->first()->delete();
return;
} else {
throw self::getException('Failed to get server attributes from pterodactyl - ', $response->status());
}
else throw self::getException("Failed to get server attributes from pterodactyl - ", $response->status());
}
return $response->json()['attributes'];
}
/**
* Update Server Resources
* @param Server $server
* @param Product $product
*
* @param Server $server
* @param Product $product
* @return Response
*/
public static function updateServer(Server $server, Product $product)
{
return self::client()->patch("/application/servers/{$server->pterodactyl_id}/build", [
"allocation" => $server->allocation,
"memory" => $product->memory,
"swap" => $product->swap,
"disk" => $product->disk,
"io" => $product->io,
"cpu" => $product->cpu,
"threads" => null,
"feature_limits" => [
"databases" => $product->databases,
"backups" => $product->backups,
"allocations" => $product->allocations,
]
'allocation' => $server->allocation,
'memory' => $product->memory,
'swap' => $product->swap,
'disk' => $product->disk,
'io' => $product->io,
'cpu' => $product->cpu,
'threads' => null,
'feature_limits' => [
'databases' => $product->databases,
'backups' => $product->backups,
'allocations' => $product->allocations,
],
]);
}
/**
* Update the owner of a server
*
* @param int $userId
* @param Server $server
* @return mixed
*/
public static function updateServerOwner(Server $server, int $userId)
{
return self::client()->patch("/application/servers/{$server->pterodactyl_id}/details", [
'name' => $server->name,
'user' => $userId,
]);
}
/**
* Power Action Specific Server
* @param Server $server
* @param string $action
*
* @param Server $server
* @param string $action
* @return Response
*/
public static function powerAction(Server $server, $action)
{
return self::clientAdmin()->post("/client/servers/{$server->identifier}/power", [
"signal" => $action
'signal' => $action,
]);
}
@ -350,16 +408,16 @@ class Pterodactyl
*/
public static function getClientUser()
{
return self::clientAdmin()->get("/client/account");
return self::clientAdmin()->get('/client/account');
}
/**
* Check if node has enough free resources to allocate the given resources
* @param Node $node
* @param int $requireMemory
* @param int $requireDisk
* @return boolean
*
* @param Node $node
* @param int $requireMemory
* @param int $requireDisk
* @return bool
*/
public static function checkNodeResources(Node $node, int $requireMemory, int $requireDisk)
{
@ -369,14 +427,15 @@ class Pterodactyl
throw self::getException($e->getMessage());
}
$node = $response['attributes'];
$freeMemory = ($node['memory']*($node['memory_overallocate']+100)/100) - $node['allocated_resources']['memory'];
$freeDisk = ($node['disk']*($node['disk_overallocate']+100)/100) - $node['allocated_resources']['disk'];
$freeMemory = ($node['memory'] * ($node['memory_overallocate'] + 100) / 100) - $node['allocated_resources']['memory'];
$freeDisk = ($node['disk'] * ($node['disk_overallocate'] + 100) / 100) - $node['allocated_resources']['disk'];
if ($freeMemory < $requireMemory) {
return false;
}
if ($freeDisk < $requireDisk) {
}
if ($freeDisk < $requireDisk) {
return false;
}
return true;
}
return true;
}
}

View file

@ -10,9 +10,8 @@ class Invoices
{
public function __construct()
{
return;
}
}
public function updateSettings(Request $request)
{
@ -22,29 +21,27 @@ class Invoices
$values = [
//SETTINGS::VALUE => REQUEST-VALUE (coming from the html-form)
"SETTINGS::INVOICE:COMPANY_NAME" => "company-name",
"SETTINGS::INVOICE:COMPANY_ADDRESS" => "company-address",
"SETTINGS::INVOICE:COMPANY_PHONE" => "company-phone",
"SETTINGS::INVOICE:COMPANY_MAIL" => "company-mail",
"SETTINGS::INVOICE:COMPANY_VAT" => "company-vat",
"SETTINGS::INVOICE:COMPANY_WEBSITE" => "company-web",
"SETTINGS::INVOICE:PREFIX" => "invoice-prefix",
"SETTINGS::INVOICE:ENABLED" => "enable-invoices",
'SETTINGS::INVOICE:COMPANY_NAME' => 'company-name',
'SETTINGS::INVOICE:COMPANY_ADDRESS' => 'company-address',
'SETTINGS::INVOICE:COMPANY_PHONE' => 'company-phone',
'SETTINGS::INVOICE:COMPANY_MAIL' => 'company-mail',
'SETTINGS::INVOICE:COMPANY_VAT' => 'company-vat',
'SETTINGS::INVOICE:COMPANY_WEBSITE' => 'company-web',
'SETTINGS::INVOICE:PREFIX' => 'invoice-prefix',
'SETTINGS::INVOICE:ENABLED' => 'enable-invoices',
];
foreach ($values as $key => $value) {
$param = $request->get($value);
Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
Cache::forget("setting" . ':' . $key);
Cache::forget('setting'.':'.$key);
}
if ($request->hasFile('logo')) {
$request->file('logo')->storeAs('public', 'logo.png');
}
return redirect(route('admin.settings.index') . '#invoices')->with('success', __('Invoice settings updated!'));
return redirect(route('admin.settings.index').'#invoices')->with('success', __('Invoice settings updated!'));
}
}

View file

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

View file

@ -7,12 +7,11 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Validator;
class Misc
{
public function __construct()
{
return;
}
public function updateSettings(Request $request)
@ -40,21 +39,23 @@ class Misc
'enable_referral' => 'nullable|string',
'referral_reward' => 'nullable|numeric',
'referral_allowed' => 'nullable|string',
'always_give_commission' => 'nullable|string',
'referral_percentage' => 'nullable|numeric',
'referral_mode' => 'nullable|string',
'ticket_enabled' => 'nullable|string',
'ticket_notify' => 'string',
]);
$validator->after(function ($validator) use ($request) {
// if enable-recaptcha is true then recaptcha-site-key and recaptcha-secret-key must be set
if ($request->get('enable-recaptcha') == 'true' && (!$request->get('recaptcha-site-key') || !$request->get('recaptcha-secret-key'))) {
if ($request->get('enable-recaptcha') == 'true' && (! $request->get('recaptcha-site-key') || ! $request->get('recaptcha-secret-key'))) {
$validator->errors()->add('recaptcha-site-key', 'The site key is required if recaptcha is enabled.');
$validator->errors()->add('recaptcha-secret-key', 'The secret key is required if recaptcha is enabled.');
}
});
if ($validator->fails()) {
return redirect(route('admin.settings.index') . '#misc')->with('error', __('Misc settings have not been updated!'))->withErrors($validator)
return redirect(route('admin.settings.index').'#misc')->with('error', __('Misc settings have not been updated!'))->withErrors($validator)
->withInput();
}
@ -66,30 +67,31 @@ class Misc
}
$values = [
"SETTINGS::DISCORD:BOT_TOKEN" => "discord-bot-token",
"SETTINGS::DISCORD:CLIENT_ID" => "discord-client-id",
"SETTINGS::DISCORD:CLIENT_SECRET" => "discord-client-secret",
"SETTINGS::DISCORD:GUILD_ID" => "discord-guild-id",
"SETTINGS::DISCORD:INVITE_URL" => "discord-invite-url",
"SETTINGS::DISCORD:ROLE_ID" => "discord-role-id",
"SETTINGS::RECAPTCHA:SITE_KEY" => "recaptcha-site-key",
"SETTINGS::RECAPTCHA:SECRET_KEY" => "recaptcha-secret-key",
"SETTINGS::RECAPTCHA:ENABLED" => "enable-recaptcha",
"SETTINGS::MAIL:MAILER" => "mailservice",
"SETTINGS::MAIL:HOST" => "mailhost",
"SETTINGS::MAIL:PORT" => "mailport",
"SETTINGS::MAIL:USERNAME" => "mailusername",
"SETTINGS::MAIL:PASSWORD" => "mailpassword",
"SETTINGS::MAIL:ENCRYPTION" => "mailencryption",
"SETTINGS::MAIL:FROM_ADDRESS" => "mailfromadress",
"SETTINGS::MAIL:FROM_NAME" => "mailfromname",
"SETTINGS::REFERRAL::ENABLED" => "enable_referral",
"SETTINGS::REFERRAL::REWARD" => "referral_reward",
"SETTINGS::REFERRAL::ALLOWED" => "referral_allowed",
"SETTINGS::REFERRAL:MODE" => "referral_mode",
"SETTINGS::REFERRAL:PERCENTAGE" => "referral_percentage",
"SETTINGS::TICKET:ENABLED" => "ticket_enabled"
'SETTINGS::DISCORD:BOT_TOKEN' => 'discord-bot-token',
'SETTINGS::DISCORD:CLIENT_ID' => 'discord-client-id',
'SETTINGS::DISCORD:CLIENT_SECRET' => 'discord-client-secret',
'SETTINGS::DISCORD:GUILD_ID' => 'discord-guild-id',
'SETTINGS::DISCORD:INVITE_URL' => 'discord-invite-url',
'SETTINGS::DISCORD:ROLE_ID' => 'discord-role-id',
'SETTINGS::RECAPTCHA:SITE_KEY' => 'recaptcha-site-key',
'SETTINGS::RECAPTCHA:SECRET_KEY' => 'recaptcha-secret-key',
'SETTINGS::RECAPTCHA:ENABLED' => 'enable-recaptcha',
'SETTINGS::MAIL:MAILER' => 'mailservice',
'SETTINGS::MAIL:HOST' => 'mailhost',
'SETTINGS::MAIL:PORT' => 'mailport',
'SETTINGS::MAIL:USERNAME' => 'mailusername',
'SETTINGS::MAIL:PASSWORD' => 'mailpassword',
'SETTINGS::MAIL:ENCRYPTION' => 'mailencryption',
'SETTINGS::MAIL:FROM_ADDRESS' => 'mailfromadress',
'SETTINGS::MAIL:FROM_NAME' => 'mailfromname',
'SETTINGS::REFERRAL::ENABLED' => 'enable_referral',
'SETTINGS::REFERRAL::REWARD' => 'referral_reward',
'SETTINGS::REFERRAL::ALLOWED' => 'referral_allowed',
'SETTINGS::REFERRAL:MODE' => 'referral_mode',
'SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION' => 'always_give_commission',
'SETTINGS::REFERRAL:PERCENTAGE' => 'referral_percentage',
'SETTINGS::TICKET:ENABLED' => 'ticket_enabled',
'SETTINGS::TICKET:NOTIFY' => 'ticket_notify',
];
@ -97,10 +99,9 @@ class Misc
$param = $request->get($value);
Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
Cache::forget("setting" . ':' . $key);
Cache::forget('setting'.':'.$key);
}
return redirect(route('admin.settings.index') . '#misc')->with('success', __('Misc settings updated!'));
return redirect(route('admin.settings.index').'#misc')->with('success', __('Misc settings updated!'));
}
}

View file

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

View file

@ -7,68 +7,85 @@ use App\Models\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Validator;
use Qirolab\Theme\Theme;
class System
{
public function __construct()
{
return;
}
public function checkPteroClientkey(){
$response = Pterodactyl::getClientUser();
public function checkPteroClientkey()
{
$response = Pterodactyl::getClientUser();
if ($response->failed()){ return redirect()->back()->with('error', __('Your Key or URL is not correct')); }
return redirect()->back()->with('success', __('Everything is good!'));
}
if ($response->failed()) {
return redirect()->back()->with('error', __('Your Key or URL is not correct'));
}
return redirect()->back()->with('success', __('Everything is good!'));
}
public function updateSettings(Request $request)
{
$validator = Validator::make($request->all(), [
"register-ip-check" => "string",
"server-create-charge-first-hour" => "string",
"credits-display-name" => "required|string",
"allocation-limit" => "required|min:0|integer",
"force-email-verification" => "string",
"force-discord-verification" => "string",
"initial-credits" => "required|min:0|integer",
"initial-server-limit" => "required|min:0|integer",
"credits-reward-amount-discord" => "required|min:0|integer",
"credits-reward-amount-email" => "required|min:0|integer",
"server-limit-discord" => "required|min:0|integer",
"server-limit-email" => "required|min:0|integer",
"server-limit-purchase" => "required|min:0|integer",
"pterodactyl-api-key" => "required|string",
"pterodactyl-url" => "required|string",
"per-page-limit" => "required|min:0|integer",
"pterodactyl-admin-api-key" => "required|string",
"enable-upgrades" => "string",
"enable-disable-servers" => "string",
'register-ip-check' => 'string',
'server-create-charge-first-hour' => 'string',
'credits-display-name' => 'required|string',
'allocation-limit' => 'required|min:0|integer',
'force-email-verification' => 'string',
'force-discord-verification' => 'string',
'initial-credits' => 'required|min:0|integer',
'initial-server-limit' => 'required|min:0|integer',
'credits-reward-amount-discord' => 'required|min:0|integer',
'credits-reward-amount-email' => 'required|min:0|integer',
'server-limit-discord' => 'required|min:0|integer',
'server-limit-email' => 'required|min:0|integer',
'server-limit-purchase' => 'required|min:0|integer',
'pterodactyl-api-key' => 'required|string',
'pterodactyl-url' => 'required|string',
'per-page-limit' => 'required|min:0|integer',
'pterodactyl-admin-api-key' => 'required|string',
'enable-upgrades' => 'string',
'enable-disable-servers' => 'string',
'enable-disable-new-users' => 'string',
'show-imprint' => 'string',
'show-privacy' => 'string',
'show-tos' => 'string',
'alert-enabled' => 'string',
'alter-type' => 'string',
'alert-message' => 'string|nullable',
'motd-enabled' => 'string',
'usefullinks-enabled' => 'string',
'motd-message' => 'string|nullable',
'seo-title' => 'string|nullable',
'seo-description' => 'string|nullable',
]);
$validator->after(function ($validator) use ($request) {
// if enable-recaptcha is true then recaptcha-site-key and recaptcha-secret-key must be set
if ($request->get('enable-upgrades') == 'true' && (!$request->get('pterodactyl-admin-api-key'))) {
if ($request->get('enable-upgrades') == 'true' && (! $request->get('pterodactyl-admin-api-key'))) {
$validator->errors()->add('pterodactyl-admin-api-key', 'The admin api key is required when upgrades are enabled.');
}
});
if ($validator->fails()) {
return redirect(route('admin.settings.index') . '#system')->with('error', __('System settings have not been updated!'))->withErrors($validator)
return redirect(route('admin.settings.index').'#system')->with('error', __('System settings have not been updated!'))->withErrors($validator)
->withInput();
}
// update Icons from request
$this->updateIcons($request);
$values = [
"SETTINGS::SYSTEM:REGISTER_IP_CHECK" => "register-ip-check",
"SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR" => "server-create-charge-first-hour",
"SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME" => "credits-display-name",
"SETTINGS::SERVER:ALLOCATION_LIMIT" => "allocation-limit",
"SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER" => "minimum-credits",
"SETTINGS::USER:FORCE_DISCORD_VERIFICATION" => "force-discord-verification",
"SETTINGS::USER:FORCE_EMAIL_VERIFICATION" => "force-email-verification",
"SETTINGS::USER:INITIAL_CREDITS" => "initial-credits",
@ -87,18 +104,34 @@ public function checkPteroClientkey(){
"SETTINGS::SYSTEM:ENABLE_UPGRADE" => "enable-upgrade",
"SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS" => "enable-disable-servers",
"SETTINGS::SYSTEM:CREATION_OF_NEW_USERS" => "enable-disable-new-users",
"SETTINGS::SYSTEM:SHOW_IMPRINT" => "show-imprint",
"SETTINGS::SYSTEM:SHOW_PRIVACY" => "show-privacy",
"SETTINGS::SYSTEM:SHOW_TOS" => "show-tos",
"SETTINGS::SYSTEM:ALERT_ENABLED" => "alert-enabled",
"SETTINGS::SYSTEM:ALERT_TYPE" => "alert-type",
"SETTINGS::SYSTEM:ALERT_MESSAGE" => "alert-message",
"SETTINGS::SYSTEM:THEME" => "theme",
"SETTINGS::SYSTEM:MOTD_ENABLED" => "motd-enabled",
"SETTINGS::SYSTEM:MOTD_MESSAGE" => "motd-message",
"SETTINGS::SYSTEM:USEFULLINKS_ENABLED" => "usefullinks-enabled",
"SETTINGS::SYSTEM:SEO_TITLE" => "seo-title",
"SETTINGS::SYSTEM:SEO_DESCRIPTION" => "seo-description",
];
foreach ($values as $key => $value) {
$param = $request->get($value);
Settings::where('key', $key)->updateOrCreate(['key' => $key], ['value' => $param]);
Cache::forget("setting" . ':' . $key);
Cache::forget('setting'.':'.$key);
}
return redirect(route('admin.settings.index') . '#system')->with('success', __('System settings updated!'));
}
//SET THEME
$theme = $request->get('theme');
Theme::set($theme);
return redirect(route('admin.settings.index').'#system')->with('success', __('System settings updated!'));
}
private function updateIcons(Request $request)
{

View file

@ -24,9 +24,9 @@ class ChargeCreditsCommand extends Command
*/
protected $description = 'Charge all users with active servers';
/**
* A list of users that have to be notified
*
* @var array
*/
protected $usersToNotify = [];
@ -56,24 +56,23 @@ class ChargeCreditsCommand extends Command
/** @var User $user */
$user = $server->user;
#charge credits / suspend server
//charge credits / suspend server
if ($user->credits >= $product->getHourlyPrice()) {
$this->line("<fg=blue>{$user->name}</> Current credits: <fg=green>{$user->credits}</> Credits to be removed: <fg=red>{$product->getHourlyPrice()}</>");
$user->decrement('credits', $product->getHourlyPrice());
} else {
try {
#suspend server
//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
//add user to notify list
if (!in_array($user, $this->usersToNotify)) {
array_push($this->usersToNotify, $user);
}
} catch (\Exception $exception) {
$this->error($exception->getMessage());
}
}
}
});
@ -94,8 +93,9 @@ class ChargeCreditsCommand extends Command
}
}
#reset array
$this->usersToNotify = array();
//reset array
$this->usersToNotify = [];
return true;
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Console\Commands;
use App\Models\Payment;
use Illuminate\Console\Command;
class CleanupOpenPayments extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'payments:open:clear';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clears all payments from the database that have state "open"';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// delete all payments that have state "open" and are older than 1 hour
try {
Payment::where('status', 'open')->where('updated_at', '<', now()->subHour())->delete();
} catch (\Exception $e) {
$this->error('Could not delete payments: ' . $e->getMessage());
return 1;
}
$this->info('Successfully deleted all open payments');
return Command::SUCCESS;
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Console\Commands;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class GetGithubVersion extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'cp:versioncheck:get';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Get the latest Version from Github';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try{
$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");
Log::error($e);
}
return Command::SUCCESS;
}
}

View file

@ -12,6 +12,7 @@ class ImportUsersFromPteroCommand extends Command
* @var string
*/
private $importFileName = 'users.json';
/**
* The name and signature of the console command.
*
@ -39,25 +40,28 @@ class ImportUsersFromPteroCommand extends Command
/**
* Execute the console command.
*
* @return boolean
* @return bool
*/
public function handle()
{
//check if json file exists
if (!Storage::disk('local')->exists('users.json')) {
$this->error('[ERROR] ' . storage_path('app') . '/' . $this->importFileName . ' is missing');
if (! Storage::disk('local')->exists('users.json')) {
$this->error('[ERROR] '.storage_path('app').'/'.$this->importFileName.' is missing');
return false;
}
//check if json file is valid
$json = json_decode(Storage::disk('local')->get('users.json'));
if (!array_key_exists(2, $json)) {
if (! array_key_exists(2, $json)) {
$this->error('[ERROR] Invalid json file');
return false;
}
if (!$json[2]->data) {
if (! $json[2]->data) {
$this->error('[ERROR] Invalid json file / No users found!');
return false;
}
@ -69,12 +73,14 @@ class ImportUsersFromPteroCommand extends Command
//cancel
if ($confirm !== 'y') {
$this->error('[ERROR] Stopped import script!');
return false;
}
//import users
$this->deleteCurrentUserBase();
$this->importUsingJsonFile($json, $initial_credits, $initial_server_limit);
return true;
}
@ -84,7 +90,9 @@ class ImportUsersFromPteroCommand extends Command
private function deleteCurrentUserBase()
{
$currentUserCount = User::count();
if ($currentUserCount == 0) return;
if ($currentUserCount == 0) {
return;
}
$this->line("Deleting ({$currentUserCount}) users..");
foreach (User::all() as $user) {
@ -104,20 +112,20 @@ class ImportUsersFromPteroCommand extends Command
$role = $user->root_admin == '0' ? 'member' : 'admin';
User::create([
"pterodactyl_id" => $user->id,
"name" => $user->name_first,
"email" => $user->email,
"password" => $user->password,
"role" => $role,
"credits" => $initial_credits,
"server_limit" => $initial_server_limit,
"created_at" => $user->created_at,
"updated_at" => $user->updated_at,
'pterodactyl_id' => $user->id,
'name' => $user->name_first,
'email' => $user->email,
'password' => $user->password,
'role' => $role,
'credits' => $initial_credits,
'server_limit' => $initial_server_limit,
'created_at' => $user->created_at,
'updated_at' => $user->updated_at,
]);
});
$this->newLine();
$this->line("Done importing, you can now login using your pterodactyl credentials.");
$this->line('Done importing, you can now login using your pterodactyl credentials.');
$this->newLine();
}
}

View file

@ -4,13 +4,16 @@ namespace App\Console\Commands;
use App\Classes\Pterodactyl;
use App\Models\User;
use App\Traits\Referral;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Str;
class MakeUserCommand extends Command
{
use Referral;
/**
* The name and signature of the console command.
*
@ -38,6 +41,7 @@ class MakeUserCommand extends Command
$this->pterodactyl = $pterodactyl;
}
/**
* Execute the console command.
*
@ -59,6 +63,7 @@ class MakeUserCommand extends Command
if ($validator->fails()) {
$this->error($validator->errors()->first());
return 0;
}
@ -66,18 +71,25 @@ class MakeUserCommand extends Command
$response = $this->pterodactyl->getUser($ptero_id);
if (isset($response['errors'])) {
if (isset($response['errors'][0]['code'])) $this->error("code: {$response['errors'][0]['code']}");
if (isset($response['errors'][0]['status'])) $this->error("status: {$response['errors'][0]['status']}");
if (isset($response['errors'][0]['detail'])) $this->error("detail: {$response['errors'][0]['detail']}");
if (isset($response['errors'][0]['code'])) {
$this->error("code: {$response['errors'][0]['code']}");
}
if (isset($response['errors'][0]['status'])) {
$this->error("status: {$response['errors'][0]['status']}");
}
if (isset($response['errors'][0]['detail'])) {
$this->error("detail: {$response['errors'][0]['detail']}");
}
return 0;
}
$user = User::create([
'name' => $response['first_name'],
'email' => $response['email'],
'role' => 'admin',
'password' => Hash::make($password),
'pterodactyl_id' => $response['id']
'referral_code' => $this->createReferralCode(),
'pterodactyl_id' => $response['id'],
]);
$this->table(['Field', 'Value'], [
@ -86,6 +98,7 @@ class MakeUserCommand extends Command
['Username', $user->name],
['Ptero-ID', $user->pterodactyl_id],
['Admin', $user->role],
['Referral code', $user->referral_code],
]);
return 1;

View file

@ -36,12 +36,13 @@ class notify extends Command
/**
* Execute the console command.
*
* @param int $id
* @param int $id
* @return int
*/
public function handle()
{
User::findOrFail($this->argument('id'))->notify(new ServerCreationError(Server::all()[0]));
return 'message send';
}
}

View file

@ -2,10 +2,10 @@
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Closure;
use Symfony\Component\Process\Process;
use Illuminate\Console\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Process\Process;
class update extends Command
{
@ -45,7 +45,7 @@ class update extends Command
$this->output->warning('This command does just pull the newest changes from the github repo. Verify the github repo before running this');
if (version_compare(PHP_VERSION, '8.0.0') < 0) {
$this->error('Cannot execute self-upgrade process. The minimum required PHP version required is 8.0.0, you have [' . PHP_VERSION . '].');
$this->error('Cannot execute self-upgrade process. The minimum required PHP version required is 8.0.0, you have ['.PHP_VERSION.'].');
}
$user = 'www-data';
@ -55,7 +55,7 @@ class update extends Command
$userDetails = posix_getpwuid(fileowner('public'));
$user = $userDetails['name'] ?? 'www-data';
if (!$this->confirm("Your webserver user has been detected as [{$user}]: is this correct?", true)) {
if (! $this->confirm("Your webserver user has been detected as [{$user}]: is this correct?", true)) {
$user = $this->anticipate(
'Please enter the name of the user running your webserver process. This varies from system to system, but is generally "www-data", "nginx", or "apache".',
[
@ -71,7 +71,7 @@ class update extends Command
$groupDetails = posix_getgrgid(filegroup('public'));
$group = $groupDetails['name'] ?? 'www-data';
if (!$this->confirm("Your webserver group has been detected as [{$group}]: is this correct?", true)) {
if (! $this->confirm("Your webserver group has been detected as [{$group}]: is this correct?", true)) {
$group = $this->anticipate(
'Please enter the name of the group running your webserver process. Normally this is the same as your user.',
[
@ -85,24 +85,21 @@ class update extends Command
ini_set('output_buffering', 0);
if (!$this->confirm('Are you sure you want to run the upgrade process for your Dashboard?')) {
if (! $this->confirm('Are you sure you want to run the upgrade process for your Dashboard?')) {
return false;
}
$bar = $this->output->createProgressBar(9);
$bar->start();
$this->withProgress($bar, function () {
$this->line("\$upgrader> git pull");
$process = Process::fromShellCommandline("git pull");
$this->line('$upgrader> git pull');
$process = Process::fromShellCommandline('git pull');
$process->run(function ($type, $buffer) {
$this->{$type === Process::ERR ? 'error' : 'line'}($buffer);
});
});
$this->withProgress($bar, function () {
$this->line('$upgrader> php artisan down');
$this->call('down');
@ -118,12 +115,12 @@ class update extends Command
$this->withProgress($bar, function () {
$command = ['composer', 'install', '--no-ansi'];
if (config('app.env') === 'production' && !config('app.debug')) {
if (config('app.env') === 'production' && ! config('app.debug')) {
$command[] = '--optimize-autoloader';
$command[] = '--no-dev';
}
$this->line('$upgrader> ' . implode(' ', $command));
$this->line('$upgrader> '.implode(' ', $command));
$process = new Process($command);
$process->setTimeout(10 * 60);
$process->run(function ($type, $buffer) {
@ -162,10 +159,9 @@ class update extends Command
$this->newLine();
$this->info('Finished running upgrade.');
};
}
}
protected function withProgress(ProgressBar $bar, Closure $callback)
{
$bar->clear();

View file

@ -8,28 +8,21 @@ use Illuminate\Support\Facades\Storage;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('credits:charge')->hourly();
$schedule->command('cp:versioncheck:get')->daily();
$schedule->command('payments:open:clear')->daily();
//log cronjob activity
$schedule->call(function () {
Storage::disk('logs')->put('cron.log' , "Last activity from cronjobs - " . now());
Storage::disk('logs')->put('cron.log', 'Last activity from cronjobs - ' . now());
})->everyMinute();
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Enums;
enum UsefulLinkLocation:String
{
/**
* Top bar
* Only visible in the dashboard view
*/
case topbar = "topbar";
/**
* Dashboard
* Only visible in the dashboard view
*/
case dashboard = "dashboard";
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Events;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class PaymentEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public User $user;
public Payment $payment;
public ShopProduct $shopProduct;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(User $user, Payment $payment, ShopProduct $shopProduct)
{
$this->user = $user;
$this->payment = $payment;
$this->shopProduct = $shopProduct;
}
}

View file

@ -3,11 +3,7 @@
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
@ -23,7 +19,7 @@ class UserUpdateCreditsEvent
/**
* Create a new event instance.
*
* @param User $user
* @param User $user
*/
public function __construct(User $user)
{

View file

@ -7,21 +7,31 @@ use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
//
];
/**
* A list of the exception types that are not reported.
*
* @var array
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,82 @@
<?php
namespace App\Helpers;
class ExtensionHelper
{
/**
* Get a config of an extension by its name
* @param string $extensionName
* @param string $configname
*/
public static function getExtensionConfig(string $extensionName, string $configname)
{
$extensions = ExtensionHelper::getAllExtensions();
// call the getConfig function of the config file of the extension like that
// call_user_func("App\\Extensions\\PaymentGateways\\Stripe" . "\\getConfig");
foreach ($extensions as $extension) {
if (!(basename($extension) == $extensionName)) {
continue;
}
$configFile = $extension . '/config.php';
if (file_exists($configFile)) {
include_once $configFile;
$config = call_user_func('App\\Extensions\\' . basename(dirname($extension)) . '\\' . basename($extension) . "\\getConfig");
}
if (isset($config[$configname])) {
return $config[$configname];
}
}
return null;
}
public static function getAllCsrfIgnoredRoutes()
{
$extensions = ExtensionHelper::getAllExtensions();
$routes = [];
foreach ($extensions as $extension) {
$configFile = $extension . '/config.php';
if (file_exists($configFile)) {
include_once $configFile;
$config = call_user_func('App\\Extensions\\' . basename(dirname($extension)) . '\\' . basename($extension) . "\\getConfig");
}
if (isset($config['RoutesIgnoreCsrf'])) {
$routes = array_merge($routes, $config['RoutesIgnoreCsrf']);
}
// map over the routes and add the extension name as prefix
$result = array_map(fn ($item) => "extensions/{$item}", $routes);
}
return $result;
}
/**
* Get all extensions
* @return array
*/
public static function getAllExtensions()
{
$extensionNamespaces = glob(app_path() . '/Extensions/*', GLOB_ONLYDIR);
$extensions = [];
foreach ($extensionNamespaces as $extensionNamespace) {
$extensions = array_merge($extensions, glob($extensionNamespace . '/*', GLOB_ONLYDIR));
}
return $extensions;
}
public static function getAllExtensionsByNamespace(string $namespace)
{
$extensions = glob(app_path() . '/Extensions/' . $namespace . '/*', GLOB_ONLYDIR);
return $extensions;
}
}

View file

@ -24,17 +24,16 @@ class ActivityLogController extends Controller
$cronLogs = Storage::disk('logs')->exists('cron.log') ? Storage::disk('logs')->get('cron.log') : null;
if ($request->input('search')) {
$query = Activity::whereHasMorph('causer' , [User::class] , function($query) use ($request) {
$query->where('name', 'like' , "%{$request->input('search')}%");
})->orderBy('created_at' , 'desc')->paginate(20);
$query = Activity::whereHasMorph('causer', [User::class], function ($query) use ($request) {
$query->where('name', 'like', "%{$request->input('search')}%");
})->orderBy('created_at', 'desc')->paginate(20);
} else {
$query = Activity::orderBy('created_at' , 'desc')->paginate(20);
$query = Activity::orderBy('created_at', 'desc')->paginate(20);
}
return view('admin.activitylogs.index')->with([
'logs' => $query,
'cronlogs' => $cronLogs
'cronlogs' => $cronLogs,
]);
}
@ -51,7 +50,7 @@ class ActivityLogController extends Controller
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @param Request $request
* @return Response
*/
public function store(Request $request)
@ -84,7 +83,7 @@ class ActivityLogController extends Controller
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param Request $request
* @param int $id
* @return Response
*/

View file

@ -12,7 +12,6 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Str;
class ApplicationApiController extends Controller
{
@ -39,17 +38,17 @@ class ApplicationApiController extends Controller
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
'memo' => 'nullable|string|max:60'
'memo' => 'nullable|string|max:60',
]);
ApplicationApi::create([
'memo' => $request->input('memo')
'memo' => $request->input('memo'),
]);
return redirect()->route('admin.api.index')->with('success', __('api key created!'));
@ -58,7 +57,7 @@ class ApplicationApiController extends Controller
/**
* Display the specified resource.
*
* @param ApplicationApi $applicationApi
* @param ApplicationApi $applicationApi
* @return Response
*/
public function show(ApplicationApi $applicationApi)
@ -69,27 +68,27 @@ class ApplicationApiController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param ApplicationApi $applicationApi
* @param ApplicationApi $applicationApi
* @return Application|Factory|View|Response
*/
public function edit(ApplicationApi $applicationApi)
{
return view('admin.api.edit' , [
'applicationApi' => $applicationApi
return view('admin.api.edit', [
'applicationApi' => $applicationApi,
]);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param ApplicationApi $applicationApi
* @param Request $request
* @param ApplicationApi $applicationApi
* @return RedirectResponse
*/
public function update(Request $request, ApplicationApi $applicationApi)
{
$request->validate([
'memo' => 'nullable|string|max:60'
'memo' => 'nullable|string|max:60',
]);
$applicationApi->update($request->all());
@ -100,18 +99,20 @@ class ApplicationApiController extends Controller
/**
* Remove the specified resource from storage.
*
* @param ApplicationApi $applicationApi
* @param ApplicationApi $applicationApi
* @return RedirectResponse
*/
public function destroy(ApplicationApi $applicationApi)
{
$applicationApi->delete();
return redirect()->back()->with('success', __('api key has been removed!'));
}
/**
* @param Request $request
* @param Request $request
* @return JsonResponse|mixed
*
* @throws Exception
*/
public function dataTable(Request $request)
@ -121,21 +122,21 @@ class ApplicationApiController extends Controller
return datatables($query)
->addColumn('actions', function (ApplicationApi $apiKey) {
return '
<a data-content="'.__("Edit").'" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.api.edit', $apiKey->token) . '" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.api.destroy', $apiKey->token) . '">
' . 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>
<a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.api.edit', $apiKey->token).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.api.destroy', $apiKey->token).'">
'.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>
</form>
';
})
->editColumn('token' , function (ApplicationApi $apiKey) {
->editColumn('token', function (ApplicationApi $apiKey) {
return "<code>{$apiKey->token}</code>";
})
->editColumn('last_used' , function (ApplicationApi $apiKey) {
->editColumn('last_used', function (ApplicationApi $apiKey) {
return $apiKey->last_used ? $apiKey->last_used->diffForHumans() : '';
})
->rawColumns(['actions' , 'token'])
->rawColumns(['actions', 'token'])
->make();
}
}

View file

@ -10,15 +10,14 @@ use ZipArchive;
class InvoiceController extends Controller
{
public function downloadAllInvoices()
{
$zip = new ZipArchive;
$zip_safe_path = storage_path('invoices.zip');
$res = $zip->open($zip_safe_path, ZipArchive::CREATE | ZipArchive::OVERWRITE);
$result = $this::rglob(storage_path('app/invoice/*'));
if ($res === TRUE) {
$zip->addFromString("1. Info.txt", __("Created at") . " " . now()->format("d.m.Y"));
if ($res === true) {
$zip->addFromString('1. Info.txt', __('Created at').' '.now()->format('d.m.Y'));
foreach ($result as $file) {
if (file_exists($file) && is_file($file)) {
$zip->addFile($file, basename($file));
@ -26,6 +25,7 @@ class InvoiceController extends Controller
}
$zip->close();
}
return response()->download($zip_safe_path);
}
@ -37,9 +37,10 @@ class InvoiceController extends Controller
public function rglob($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this::rglob($dir . '/' . basename($pattern), $flags));
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this::rglob($dir.'/'.basename($pattern), $flags));
}
return $files;
}
@ -53,16 +54,15 @@ class InvoiceController extends Controller
try {
$query = Invoice::where('payment_id', '=', $id)->firstOrFail();
} catch (Throwable $e) {
return redirect()->back()->with("error", __("Error!"));
return redirect()->back()->with('error', __('Error!'));
}
$invoice_path = storage_path('app/invoice/' . $query->invoice_user . '/' . $query->created_at->format("Y") . '/' . $query->invoice_name . '.pdf');
$invoice_path = storage_path('app/invoice/'.$query->invoice_user.'/'.$query->created_at->format('Y').'/'.$query->invoice_name.'.pdf');
if (!file_exists($invoice_path)) {
return redirect()->back()->with("error", __("Invoice does not exist on filesystem!"));
if (! file_exists($invoice_path)) {
return redirect()->back()->with('error', __('Invoice does not exist on filesystem!'));
}
return response()->download($invoice_path);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Qirolab\Theme\Theme;
class LegalController extends Controller
{
/**
* Display
*
* @return View
*/
public function index()
{
$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');
return view('admin.legal.index')->with([
"tos" => $tos,
"privacy" => $privacy,
"imprint" => $imprint,
]);
}
public function update(Request $request){
$tos = $request->tos;
$privacy = $request->privacy;
$imprint = $request->imprint;
File::put(Theme::path($path = 'views', "default") . '/information/tos-content.blade.php', $tos);
File::put(Theme::path($path = 'views', "default") . '/information/privacy-content.blade.php', $privacy);
File::put(Theme::path($path = 'views', "default") . '/information/imprint-content.blade.php', $imprint);
return back()->with("success",__("Legal pages updated"));
}
}

View file

@ -2,18 +2,17 @@
namespace App\Http\Controllers\Admin;
use App\Classes\Pterodactyl;
use App\Http\Controllers\Controller;
use App\Models\Egg;
use App\Models\Location;
use App\Models\Nest;
use App\Models\Node;
use App\Models\Payment;
use App\Models\Server;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
use App\Classes\Pterodactyl;
use App\Models\Product;
use App\Models\Server;
use App\Models\Ticket;
use App\Models\User;
use Carbon\Carbon;
class OverViewController extends Controller
@ -26,7 +25,7 @@ class OverViewController extends Controller
$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('credits', number_format(User::query()->where('role', '!=', 'admin')->sum('credits'), 2, '.', ''));
$counters->put('payments', Payment::query()->count());
$counters->put('eggs', Egg::query()->count());
$counters->put('nests', Nest::query()->count());
@ -45,55 +44,109 @@ class OverViewController extends Controller
$counters->put('payments', collect());
//Get and save payments from last 2 months for later filtering and looping
$payments = Payment::query()->where('created_at', '>=', Carbon::today()->startOfMonth()->subMonth())->where('status', 'paid')->get();
//Prepare collections and set a few variables
//Prepare collections
$counters['payments']->put('thisMonth', collect());
$counters['payments']->put('lastMonth', collect());
$counters['payments']['thisMonth']->timeStart = Carbon::today()->startOfMonth()->toDateString();
$counters['payments']['thisMonth']->timeEnd = Carbon::today()->toDateString();
$counters['payments']['lastMonth']->timeStart = Carbon::today()->startOfMonth()->subMonth()->toDateString();
$counters['payments']['lastMonth']->timeEnd = Carbon::today()->endOfMonth()->subMonth()->toDateString();
//Prepare subCollection 'taxPayments'
$counters->put('taxPayments', collect());
//Get and save taxPayments from last 2 years for later filtering and looping
$taxPayments = Payment::query()->where('created_at', '>=', Carbon::today()->startOfYear()->subYear())->where('status', 'paid')->get();
//Prepare collections
$counters['taxPayments']->put('thisYear', collect());
$counters['taxPayments']->put('lastYear', collect());
//Fill out variables for each currency separately
foreach($payments->where('created_at', '>=', Carbon::today()->startOfMonth()) as $payment){
foreach ($payments->where('created_at', '>=', Carbon::today()->startOfMonth()) as $payment) {
$paymentCurrency = $payment->currency_code;
if(!isset($counters['payments']['thisMonth'][$paymentCurrency])){
if (! isset($counters['payments']['thisMonth'][$paymentCurrency])) {
$counters['payments']['thisMonth']->put($paymentCurrency, collect());
$counters['payments']['thisMonth'][$paymentCurrency]->total = 0;
$counters['payments']['thisMonth'][$paymentCurrency]->count = 0;
}
$counters['payments']['thisMonth'][$paymentCurrency]->total += $payment->total_price;
$counters['payments']['thisMonth'][$paymentCurrency]->count ++;
$counters['payments']['thisMonth'][$paymentCurrency]->count++;
}
foreach($payments->where('created_at', '<', Carbon::today()->startOfMonth()) as $payment){
foreach ($payments->where('created_at', '<', Carbon::today()->startOfMonth()) as $payment) {
$paymentCurrency = $payment->currency_code;
if(!isset($counters['payments']['lastMonth'][$paymentCurrency])){
if (! isset($counters['payments']['lastMonth'][$paymentCurrency])) {
$counters['payments']['lastMonth']->put($paymentCurrency, collect());
$counters['payments']['lastMonth'][$paymentCurrency]->total = 0;
$counters['payments']['lastMonth'][$paymentCurrency]->count = 0;
}
$counters['payments']['lastMonth'][$paymentCurrency]->total += $payment->total_price;
$counters['payments']['lastMonth'][$paymentCurrency]->count ++;
$counters['payments']['lastMonth'][$paymentCurrency]->count++;
}
//sort currencies alphabetically and set some additional variables
$counters['payments']['thisMonth'] = $counters['payments']['thisMonth']->sortKeys();
$counters['payments']['thisMonth']->timeStart = Carbon::today()->startOfMonth()->toDateString();
$counters['payments']['thisMonth']->timeEnd = Carbon::today()->toDateString();
$counters['payments']['lastMonth'] = $counters['payments']['lastMonth']->sortKeys();
$counters['payments']['lastMonth']->timeStart = Carbon::today()->startOfMonth()->subMonth()->toDateString();
$counters['payments']['lastMonth']->timeEnd = Carbon::today()->endOfMonth()->subMonth()->toDateString();
$counters['payments']->total = Payment::query()->count();
foreach($taxPayments->where('created_at', '>=', Carbon::today()->startOfYear()) as $taxPayment){
$paymentCurrency = $taxPayment->currency_code;
if(!isset($counters['taxPayments']['thisYear'][$paymentCurrency])){
$counters['taxPayments']['thisYear']->put($paymentCurrency, collect());
$counters['taxPayments']['thisYear'][$paymentCurrency]->total = 0;
$counters['taxPayments']['thisYear'][$paymentCurrency]->count = 0;
$counters['taxPayments']['thisYear'][$paymentCurrency]->price = 0;
$counters['taxPayments']['thisYear'][$paymentCurrency]->taxes = 0;
}
$counters['taxPayments']['thisYear'][$paymentCurrency]->total += $taxPayment->total_price;
$counters['taxPayments']['thisYear'][$paymentCurrency]->count++;
$counters['taxPayments']['thisYear'][$paymentCurrency]->price += $taxPayment->price;
$counters['taxPayments']['thisYear'][$paymentCurrency]->taxes += $taxPayment->tax_value;
}
foreach($taxPayments->where('created_at', '>=', Carbon::today()->startOfYear()->subYear())->where('created_at', '<', Carbon::today()->startOfYear()) as $taxPayment){
$paymentCurrency = $taxPayment->currency_code;
if(!isset($counters['taxPayments']['lastYear'][$paymentCurrency])){
$counters['taxPayments']['lastYear']->put($paymentCurrency, collect());
$counters['taxPayments']['lastYear'][$paymentCurrency]->total = 0;
$counters['taxPayments']['lastYear'][$paymentCurrency]->count = 0;
$counters['taxPayments']['lastYear'][$paymentCurrency]->price = 0;
$counters['taxPayments']['lastYear'][$paymentCurrency]->taxes = 0;
}
$counters['taxPayments']['lastYear'][$paymentCurrency]->total += $taxPayment->total_price;
$counters['taxPayments']['lastYear'][$paymentCurrency]->count++;
$counters['taxPayments']['lastYear'][$paymentCurrency]->price += $taxPayment->price;
$counters['taxPayments']['lastYear'][$paymentCurrency]->taxes += $taxPayment->tax_value;
}
//sort currencies alphabetically and set some additional variables
$counters['taxPayments']['thisYear'] = $counters['taxPayments']['thisYear']->sortKeys();
$counters['taxPayments']['thisYear']->timeStart = Carbon::today()->startOfYear()->toDateString();
$counters['taxPayments']['thisYear']->timeEnd = Carbon::today()->toDateString();
$counters['taxPayments']['lastYear'] = $counters['taxPayments']['lastYear']->sortKeys();
$counters['taxPayments']['lastYear']->timeStart = Carbon::today()->startOfYear()->subYear()->toDateString();
$counters['taxPayments']['lastYear']->timeEnd = Carbon::today()->endOfYear()->subYear()->toDateString();
$lastEgg = Egg::query()->latest('updated_at')->first();
$syncLastUpdate = $lastEgg ? $lastEgg->updated_at->isoFormat('LLL') : __('unknown');
//Get node information and prepare collection
$pteroNodeIds = [];
foreach(Pterodactyl::getNodes() as $pteroNode){
foreach (Pterodactyl::getNodes() as $pteroNode) {
array_push($pteroNodeIds, $pteroNode['attributes']['id']);
}
$nodes = collect();
foreach($DBnodes = Node::query()->get() as $DBnode){ //gets all node information and prepares the structure
foreach ($DBnodes = Node::query()->get() as $DBnode) { //gets all node information and prepares the structure
$nodeId = $DBnode['id'];
if(!in_array($nodeId, $pteroNodeIds)) continue; //Check if node exists on pterodactyl too, if not, skip
if (! in_array($nodeId, $pteroNodeIds)) {
continue;
} //Check if node exists on pterodactyl too, if not, skip
$nodes->put($nodeId, collect());
$nodes[$nodeId]->name = $DBnode['name'];
$pteroNode = Pterodactyl::getNode($nodeId);
$nodes[$nodeId]->usagePercent = round(max($pteroNode['allocated_resources']['memory']/($pteroNode['memory']*($pteroNode['memory_overallocate']+100)/100), $pteroNode['allocated_resources']['disk']/($pteroNode['disk']*($pteroNode['disk_overallocate']+100)/100))*100, 2);
$nodes[$nodeId]->usagePercent = round(max($pteroNode['allocated_resources']['memory'] / ($pteroNode['memory'] * ($pteroNode['memory_overallocate'] + 100) / 100), $pteroNode['allocated_resources']['disk'] / ($pteroNode['disk'] * ($pteroNode['disk_overallocate'] + 100) / 100)) * 100, 2);
$counters['totalUsagePercent'] += $nodes[$nodeId]->usagePercent;
$nodes[$nodeId]->totalServers = 0;
@ -101,66 +154,61 @@ class OverViewController extends Controller
$nodes[$nodeId]->totalEarnings = 0;
$nodes[$nodeId]->activeEarnings = 0;
}
$counters['totalUsagePercent'] = ($DBnodes->count())?round($counters['totalUsagePercent']/$DBnodes->count(), 2):0;
$counters['totalUsagePercent'] = ($DBnodes->count()) ? round($counters['totalUsagePercent'] / $DBnodes->count(), 2) : 0;
foreach(Pterodactyl::getServers() as $server){ //gets all servers from Pterodactyl and calculates total of credit usage for each node separately + total
foreach (Pterodactyl::getServers() as $server) { //gets all servers from Pterodactyl and calculates total of credit usage for each node separately + total
$nodeId = $server['attributes']['node'];
if($CPServer = Server::query()->where('pterodactyl_id', $server['attributes']['id'])->first()){
if ($CPServer = Server::query()->where('pterodactyl_id', $server['attributes']['id'])->first()) {
$price = Product::query()->where('id', $CPServer->product_id)->first()->price;
if (!$CPServer->suspended){
if (! $CPServer->suspended) {
$counters['earnings']->active += $price;
$counters['servers']->active ++;
$counters['servers']->active++;
$nodes[$nodeId]->activeEarnings += $price;
$nodes[$nodeId]->activeServers ++;
$nodes[$nodeId]->activeServers++;
}
$counters['earnings']->total += $price;
$counters['servers']->total ++;
$counters['servers']->total++;
$nodes[$nodeId]->totalEarnings += $price;
$nodes[$nodeId]->totalServers ++;
$nodes[$nodeId]->totalServers++;
}
}
//Get latest tickets
$tickets = Cache::remember('tickets', self::TTL, function(){
$output = collect();
foreach(Ticket::query()->latest()->take(3)->get() as $ticket){
$output->put($ticket->ticket_id, collect());
$output[$ticket->ticket_id]->title = $ticket->title;
$user = User::query()->where('id', $ticket->user_id)->first();
$output[$ticket->ticket_id]->user_id = $user->id;
$output[$ticket->ticket_id]->user = $user->name;
$output[$ticket->ticket_id]->status = $ticket->status;
$output[$ticket->ticket_id]->last_updated = $ticket->updated_at->diffForHumans();
switch ($ticket->status) {
case 'Open':
$output[$ticket->ticket_id]->statusBadgeColor = 'badge-success';
break;
case 'Closed':
$output[$ticket->ticket_id]->statusBadgeColor = 'badge-danger';
break;
case 'Answered':
$output[$ticket->ticket_id]->statusBadgeColor = 'badge-info';
break;
default:
$output[$ticket->ticket_id]->statusBadgeColor = 'badge-warning';
break;
}
$tickets = collect();
foreach (Ticket::query()->latest()->take(5)->get() as $ticket) {
$tickets->put($ticket->ticket_id, collect());
$tickets[$ticket->ticket_id]->title = $ticket->title;
$user = User::query()->where('id', $ticket->user_id)->first();
$tickets[$ticket->ticket_id]->user_id = $user->id;
$tickets[$ticket->ticket_id]->user = $user->name;
$tickets[$ticket->ticket_id]->status = $ticket->status;
$tickets[$ticket->ticket_id]->last_updated = $ticket->updated_at->diffForHumans();
switch ($ticket->status) {
case 'Open':
$tickets[$ticket->ticket_id]->statusBadgeColor = 'badge-success';
break;
case 'Closed':
$tickets[$ticket->ticket_id]->statusBadgeColor = 'badge-danger';
break;
case 'Answered':
$tickets[$ticket->ticket_id]->statusBadgeColor = 'badge-info';
break;
default:
$tickets[$ticket->ticket_id]->statusBadgeColor = 'badge-warning';
break;
}
return $output;
});
}
return view('admin.overview.index', [
'counters' => $counters,
'nodes' => $nodes,
'counters' => $counters,
'nodes' => $nodes,
'syncLastUpdate' => $syncLastUpdate,
'deletedNodesPresent'=> ($DBnodes->count() != count($pteroNodeIds))?true:false,
'perPageLimit' => ($counters['servers']->total != Server::query()->count())?true:false,
'tickets' => $tickets
'deletedNodesPresent' => ($DBnodes->count() != count($pteroNodeIds)) ? true : false,
'perPageLimit' => ($counters['servers']->total != Server::query()->count()) ? true : false,
'tickets' => $tickets,
]);
}
}
/**
* @description Sync locations,nodes,nests,eggs with the linked pterodactyl panel

View file

@ -0,0 +1,137 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\PartnerDiscount;
use App\Models\User;
use Illuminate\Http\Request;
class PartnerController extends Controller
{
public function index()
{
return view('admin.partners.index');
}
/**
* Show the form for creating a new resource.
*
* @return Application|Factory|View
*/
public function create()
{
return view('admin.partners.create', [
'partners' => PartnerDiscount::get(),
'users' => User::orderBy('name')->get(),
]);
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
'user_id' => 'required|integer|min:0',
'partner_discount' => 'required|integer|max:100|min:0',
'registered_user_discount' => 'required|integer|max:100|min:0',
]);
if(PartnerDiscount::where("user_id",$request->user_id)->exists()){
return redirect()->route('admin.partners.index')->with('error', __('Partner already exists'));
}
PartnerDiscount::create($request->all());
return redirect()->route('admin.partners.index')->with('success', __('partner has been created!'));
}
/**
* Show the form for editing the specified resource.
*
* @param Partner $partner
* @return Application|Factory|View
*/
public function edit(PartnerDiscount $partner)
{
return view('admin.partners.edit', [
'partners' => PartnerDiscount::get(),
'partner' => $partner,
'users' => User::orderBy('name')->get(),
]);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param Partner $partner
* @return RedirectResponse
*/
public function update(Request $request, PartnerDiscount $partner)
{
//dd($request);
$request->validate([
'user_id' => 'required|integer|min:0',
'partner_discount' => 'required|integer|max:100|min:0',
'registered_user_discount' => 'required|integer|max:100|min:0',
]);
$partner->update($request->all());
return redirect()->route('admin.partners.index')->with('success', __('partner has been updated!'));
}
/**
* Remove the specified resource from storage.
*
* @param Partner $partner
* @return RedirectResponse
*/
public function destroy(PartnerDiscount $partner)
{
$partner->delete();
return redirect()->back()->with('success', __('partner has been removed!'));
}
public function dataTable()
{
$query = PartnerDiscount::query();
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>
<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>
</form>
';
})
->addColumn('user', function (PartnerDiscount $partner) {
return ($user = User::where('id', $partner->user_id)->first()) ? '<a href="'.route('admin.users.show', $partner->user_id).'">'.$user->name.'</a>' : __('Unknown user');
})
->editColumn('created_at', function (PartnerDiscount $partner) {
return $partner->created_at ? $partner->created_at->diffForHumans() : '';
})
->editColumn('partner_discount', function (PartnerDiscount $partner) {
return $partner->partner_discount ? $partner->partner_discount.'%' : '0%';
})
->editColumn('registered_user_discount', function (PartnerDiscount $partner) {
return $partner->registered_user_discount ? $partner->registered_user_discount.'%' : '0%';
})
->editColumn('referral_system_commission', function (PartnerDiscount $partner) {
return $partner->referral_system_commission >= 0 ? $partner->referral_system_commission.'%' : __('Default').' ('.config('SETTINGS::REFERRAL:PERCENTAGE').'%)';
})
->rawColumns(['user', 'actions'])
->make();
}
}

View file

@ -2,15 +2,13 @@
namespace App\Http\Controllers\Admin;
use App\Events\PaymentEvent;
use App\Events\UserUpdateCreditsEvent;
use App\Http\Controllers\Controller;
use App\Models\InvoiceSettings;
use App\Models\PartnerDiscount;
use App\Models\Payment;
use App\Models\ShopProduct;
use App\Models\Settings;
use App\Models\User;
use App\Notifications\InvoiceNotification;
use App\Notifications\ConfirmPaymentNotification;
use App\Models\ShopProduct;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -19,633 +17,125 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use LaravelDaily\Invoices\Classes\Buyer;
use LaravelDaily\Invoices\Classes\InvoiceItem;
use LaravelDaily\Invoices\Classes\Party;
use LaravelDaily\Invoices\Invoice;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalHttp\HttpException;
use Stripe\Stripe;
use Symfony\Component\Intl\Currencies;
use App\Helpers\ExtensionHelper;
class PaymentController extends Controller
{
/**
* @return Application|Factory|View
*/
public function index()
{
return view('admin.payments.index')->with([
'payments' => Payment::paginate(15)
'payments' => Payment::paginate(15),
]);
}
/**
* @param Request $request
* @param ShopProduct $shopProduct
* @param Request $request
* @param ShopProduct $shopProduct
* @return Application|Factory|View
*/
public function checkOut(Request $request, ShopProduct $shopProduct)
public function checkOut(ShopProduct $shopProduct)
{
$discount = PartnerDiscount::getDiscount();
$price = $shopProduct->price - ($shopProduct->price * $discount / 100);
$paymentGateways = [];
if ($price > 0) {
$extensions = ExtensionHelper::getAllExtensionsByNamespace('PaymentGateways');
// build a paymentgateways array that contains the routes for the payment gateways and the image path for the payment gateway which lays in public/images/Extensions/PaymentGateways with the extensionname in lowercase
foreach ($extensions as $extension) {
$extensionName = basename($extension);
if (!ExtensionHelper::getExtensionConfig($extensionName, 'enabled')) continue; // skip if not enabled
$payment = new \stdClass();
$payment->name = ExtensionHelper::getExtensionConfig($extensionName, 'name');
$payment->image = asset('images/Extensions/PaymentGateways/' . strtolower($extensionName) . '_logo.png');
$paymentGateways[] = $payment;
}
}
return view('store.checkout')->with([
'product' => $shopProduct,
'taxvalue' => $shopProduct->getTaxValue(),
'taxpercent' => $shopProduct->getTaxPercent(),
'total' => $shopProduct->getTotalPrice()
'product' => $shopProduct,
'discountpercent' => $discount,
'discountvalue' => $discount * $shopProduct->price / 100,
'discountedprice' => $shopProduct->getPriceAfterDiscount(),
'taxvalue' => $shopProduct->getTaxValue(),
'taxpercent' => $shopProduct->getTaxPercent(),
'total' => $shopProduct->getTotalPrice(),
'paymentGateways' => $paymentGateways,
'productIsFree' => $price <= 0,
]);
}
/**
* @param Request $request
* @param ShopProduct $shopProduct
* @param Request $request
* @param ShopProduct $shopProduct
* @return RedirectResponse
*/
public function PaypalPay(Request $request, ShopProduct $shopProduct)
public function handleFreeProduct(ShopProduct $shopProduct)
{
$request = new OrdersCreateRequest();
$request->prefer('return=representation');
$request->body = [
"intent" => "CAPTURE",
"purchase_units" => [
[
"reference_id" => uniqid(),
"description" => $shopProduct->description,
"amount" => [
"value" => $shopProduct->getTotalPrice(),
'currency_code' => strtoupper($shopProduct->currency_code),
'breakdown' => [
'item_total' =>
[
'currency_code' => strtoupper($shopProduct->currency_code),
'value' => $shopProduct->price,
],
'tax_total' =>
[
'currency_code' => strtoupper($shopProduct->currency_code),
'value' => $shopProduct->getTaxValue(),
]
]
]
]
],
"application_context" => [
"cancel_url" => route('payment.Cancel'),
"return_url" => route('payment.PaypalSuccess', ['product' => $shopProduct->id]),
'brand_name' => config('app.name', 'Laravel'),
'shipping_preference' => 'NO_SHIPPING'
]
];
try {
// Call API with your client and get a response for your call
$response = $this->getPayPalClient()->execute($request);
return redirect()->away($response->result->links[1]->href);
// If call returns body in response, you can get the deserialized version from the result attribute of the response
} catch (HttpException $ex) {
echo $ex->statusCode;
dd(json_decode($ex->getMessage()));
}
}
/**
* @return PayPalHttpClient
*/
protected function getPayPalClient()
{
$environment = env('APP_ENV') == 'local'
? new SandboxEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret())
: new ProductionEnvironment($this->getPaypalClientId(), $this->getPaypalClientSecret());
return new PayPalHttpClient($environment);
}
/**
* @return string
*/
protected function getPaypalClientId()
{
return env('APP_ENV') == 'local' ? config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_CLIENT_ID") : config("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID");
}
/**
* @return string
*/
protected function getPaypalClientSecret()
{
return env('APP_ENV') == 'local' ? config("SETTINGS::PAYMENTS:PAYPAL:SANDBOX_SECRET") : config("SETTINGS::PAYMENTS:PAYPAL:SECRET");
}
/**
* @param Request $laravelRequest
*/
public function PaypalSuccess(Request $laravelRequest)
{
/** @var ShopProduct $shopProduct */
$shopProduct = ShopProduct::findOrFail($laravelRequest->input('product'));
/** @var User $user */
$user = Auth::user();
$request = new OrdersCaptureRequest($laravelRequest->input('token'));
$request->prefer('return=representation');
try {
// Call API with your client and get a response for your call
$response = $this->getPayPalClient()->execute($request);
if ($response->statusCode == 201 || $response->statusCode == 200) {
//create a payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => uniqid(),
'payment_method' => 'free',
'type' => $shopProduct->type,
'status' => 'paid',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price - ($shopProduct->price * PartnerDiscount::getDiscount() / 100),
'tax_value' => $shopProduct->getTaxValue(),
'tax_percent' => $shopProduct->getTaxPercent(),
'total_price' => $shopProduct->getTotalPrice(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
//update server limit
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
}
}
event(new UserUpdateCreditsEvent($user));
event(new PaymentEvent($user, $payment, $shopProduct));
//update User with bought item
if ($shopProduct->type=="Credits") {
$user->increment('credits', $shopProduct->quantity);
}elseif ($shopProduct->type=="Server slots"){
$user->increment('server_limit', $shopProduct->quantity);
}
//not sending an invoice
//update role give Referral-reward
if ($user->role == 'member') {
$user->update(['role' => 'client']);
if((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type=="Credits"){
if($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()){
$ref_user = User::findOrFail($ref_user->referral_id);
$increment = number_format($shopProduct->quantity/100*config("SETTINGS::REFERRAL:PERCENTAGE"),0,"","");
$ref_user->increment('credits', $increment);
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained '. $increment.' '.config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME").' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
}
}
}
//store payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => $response->result->id,
'payment_method' => 'paypal',
'type' => $shopProduct->type,
'status' => 'paid',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price,
'tax_value' => $shopProduct->getTaxValue(),
'tax_percent' => $shopProduct->getTaxPercent(),
'total_price' => $shopProduct->getTotalPrice(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
event(new UserUpdateCreditsEvent($user));
//only create invoice if SETTINGS::INVOICE:ENABLED is true
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
$this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
}
//redirect back to home
return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
}
// If call returns body in response, you can get the deserialized version from the result attribute of the response
if (env('APP_ENV') == 'local') {
dd($response);
} else {
abort(500);
}
} catch (HttpException $ex) {
if (env('APP_ENV') == 'local') {
echo $ex->statusCode;
dd($ex->getMessage());
} else {
abort(422);
}
}
//redirect back to home
return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
}
public function pay(Request $request)
{
$product = ShopProduct::find($request->product_id);
$paymentGateway = $request->payment_method;
// 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);
}
return redirect()->route('payment.' . $paymentGateway . 'Pay', ['shopProduct' => $product->id]);
}
/**
* @param Request $request
* @param Request $request
*/
public function Cancel(Request $request)
{
return redirect()->route('store.index')->with('success', 'Payment was Canceled');
}
/**
* @param Request $request
* @param ShopProduct $shopProduct
* @return RedirectResponse
*/
public function StripePay(Request $request, ShopProduct $shopProduct)
{
$stripeClient = $this->getStripeClient();
$request = $stripeClient->checkout->sessions->create([
'line_items' => [
[
'price_data' => [
'currency' => $shopProduct->currency_code,
'product_data' => [
'name' => $shopProduct->display,
'description' => $shopProduct->description,
],
'unit_amount_decimal' => round($shopProduct->price * 100, 2),
],
'quantity' => 1,
],
[
'price_data' => [
'currency' => $shopProduct->currency_code,
'product_data' => [
'name' => 'Product Tax',
'description' => $shopProduct->getTaxPercent() . "%",
],
'unit_amount_decimal' => round($shopProduct->getTaxValue(), 2) * 100,
],
'quantity' => 1,
]
],
'mode' => 'payment',
"payment_method_types" => str_getcsv(config("SETTINGS::PAYMENTS:STRIPE:METHODS")),
'success_url' => route('payment.StripeSuccess', ['product' => $shopProduct->id]) . '&session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => route('payment.Cancel'),
]);
return redirect($request->url, 303);
}
/**
* @param Request $request
*/
public function StripeSuccess(Request $request)
{
/** @var ShopProduct $shopProduct */
$shopProduct = ShopProduct::findOrFail($request->input('product'));
/** @var User $user */
$user = Auth::user();
$stripeClient = $this->getStripeClient();
try {
//get stripe data
$paymentSession = $stripeClient->checkout->sessions->retrieve($request->input('session_id'));
$paymentIntent = $stripeClient->paymentIntents->retrieve($paymentSession->payment_intent);
//get DB entry of this payment ID if existing
$paymentDbEntry = Payment::where('payment_id', $paymentSession->payment_intent)->count();
// check if payment is 100% completed and payment does not exist in db already
if ($paymentSession->status == "complete" && $paymentIntent->status == "succeeded" && $paymentDbEntry == 0) {
//update server limit
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
}
}
//update User with bought item
if ($shopProduct->type=="Credits") {
$user->increment('credits', $shopProduct->quantity);
}elseif ($shopProduct->type=="Server slots"){
$user->increment('server_limit', $shopProduct->quantity);
}
//update role give Referral-reward
if ($user->role == 'member') {
$user->update(['role' => 'client']);
if((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type=="Credits"){
if($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()){
$ref_user = User::findOrFail($ref_user->referral_id);
$increment = number_format($shopProduct->quantity/100*config("SETTINGS::REFERRAL:PERCENTAGE"),0,"","");
$ref_user->increment('credits', $increment);
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained '. $increment.' '.config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME").' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
}
}
}
//store paid payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => $paymentSession->payment_intent,
'payment_method' => 'stripe',
'type' => $shopProduct->type,
'status' => 'paid',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price,
'tax_value' => $shopProduct->getTaxValue(),
'total_price' => $shopProduct->getTotalPrice(),
'tax_percent' => $shopProduct->getTaxPercent(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
//payment notification
$user->notify(new ConfirmPaymentNotification($payment));
event(new UserUpdateCreditsEvent($user));
//only create invoice if SETTINGS::INVOICE:ENABLED is true
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
$this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
}
//redirect back to home
return redirect()->route('home')->with('success', __('Your credit balance has been increased!'));
} else {
if ($paymentIntent->status == "processing") {
//store processing payment
$payment = Payment::create([
'user_id' => $user->id,
'payment_id' => $paymentSession->payment_intent,
'payment_method' => 'stripe',
'type' => $shopProduct->type,
'status' => 'processing',
'amount' => $shopProduct->quantity,
'price' => $shopProduct->price,
'tax_value' => $shopProduct->getTaxValue(),
'total_price' => $shopProduct->getTotalPrice(),
'tax_percent' => $shopProduct->getTaxPercent(),
'currency_code' => $shopProduct->currency_code,
'shop_item_product_id' => $shopProduct->id,
]);
//only create invoice if SETTINGS::INVOICE:ENABLED is true
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
$this->createInvoice($user, $payment, 'paid', $shopProduct->currency_code);
}
//redirect back to home
return redirect()->route('home')->with('success', __('Your payment is being processed!'));
}
if ($paymentDbEntry == 0 && $paymentIntent->status != "processing") {
$stripeClient->paymentIntents->cancel($paymentIntent->id);
//redirect back to home
return redirect()->route('home')->with('success', __('Your payment has been canceled!'));
} else {
abort(402);
}
}
} catch (HttpException $ex) {
if (env('APP_ENV') == 'local') {
echo $ex->statusCode;
dd($ex->getMessage());
} else {
abort(422);
}
}
}
/**
* @param Request $request
*/
protected function handleStripePaymentSuccessHook($paymentIntent)
{
try {
// Get payment db entry
$payment = Payment::where('payment_id', $paymentIntent->id)->first();
$user = User::where('id', $payment->user_id)->first();
if ($paymentIntent->status == 'succeeded' && $payment->status == 'processing') {
//update server limit
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0) {
if ($user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
}
}
//update User with bought item
if ($shopProduct->type=="Credits") {
$user->increment('credits', $shopProduct->quantity);
}elseif ($shopProduct->type=="Server slots"){
$user->increment('server_limit', $shopProduct->quantity);
}
//update role give Referral-reward
if ($user->role == 'member') {
$user->update(['role' => 'client']);
if((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both")&& $shopProduct->type=="Credits"){
if($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()){
$ref_user = User::findOrFail($ref_user->referral_id);
$increment = number_format($shopProduct->quantity/100*config("SETTINGS::REFERRAL:PERCENTAGE"),0,"","");
$ref_user->increment('credits', $increment);
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained '. $increment.' '.config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME").' for commission-referral of '.$user->name.' (ID:'.$user->id.')');
}
}
}
//update payment db entry status
$payment->update(['status' => 'paid']);
//payment notification
$user->notify(new ConfirmPaymentNotification($payment));
event(new UserUpdateCreditsEvent($user));
//only create invoice if SETTINGS::INVOICE:ENABLED is true
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
$this->createInvoice($user, $payment, 'paid', strtoupper($paymentIntent->currency));
}
}
} catch (HttpException $ex) {
abort(422);
}
}
/**
* @param Request $request
*/
public function StripeWebhooks(Request $request)
{
\Stripe\Stripe::setApiKey($this->getStripeSecret());
try {
$payload = @file_get_contents('php://input');
$sig_header = $request->header('Stripe-Signature');
$event = null;
$event = \Stripe\Webhook::constructEvent(
$payload,
$sig_header,
$this->getStripeEndpointSecret()
);
} catch (\UnexpectedValueException $e) {
// Invalid payload
abort(400);
} catch (\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
abort(400);
}
// Handle the event
switch ($event->type) {
case 'payment_intent.succeeded':
$paymentIntent = $event->data->object; // contains a \Stripe\PaymentIntent
$this->handleStripePaymentSuccessHook($paymentIntent);
break;
default:
echo 'Received unknown event type ' . $event->type;
}
}
/**
* @return \Stripe\StripeClient
*/
protected function getStripeClient()
{
return new \Stripe\StripeClient($this->getStripeSecret());
}
/**
* @return string
*/
protected function getStripeSecret()
{
return env('APP_ENV') == 'local'
? config("SETTINGS::PAYMENTS:STRIPE:TEST_SECRET")
: config("SETTINGS::PAYMENTS:STRIPE:SECRET");
}
/**
* @return string
*/
protected function getStripeEndpointSecret()
{
return env('APP_ENV') == 'local'
? config("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_TEST_SECRET")
: config("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET");
}
protected function createInvoice($user, $payment, $paymentStatus, $currencyCode)
{
$shopProduct = ShopProduct::where('id', $payment->shop_item_product_id)->first();
//create invoice
$lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id");
$newInvoiceID = $lastInvoiceID + 1;
$logoPath = storage_path('app/public/logo.png');
$seller = new Party([
'name' => config("SETTINGS::INVOICE:COMPANY_NAME"),
'phone' => config("SETTINGS::INVOICE:COMPANY_PHONE"),
'address' => config("SETTINGS::INVOICE:COMPANY_ADDRESS"),
'vat' => config("SETTINGS::INVOICE:COMPANY_VAT"),
'custom_fields' => [
'E-Mail' => config("SETTINGS::INVOICE:COMPANY_MAIL"),
"Web" => config("SETTINGS::INVOICE:COMPANY_WEBSITE")
],
]);
$customer = new Buyer([
'name' => $user->name,
'custom_fields' => [
'E-Mail' => $user->email,
'Client ID' => $user->id,
],
]);
$item = (new InvoiceItem())
->title($shopProduct->description)
->pricePerUnit($shopProduct->price);
$notes = [
__("Payment method") . ": " . $payment->payment_method,
];
$notes = implode("<br>", $notes);
$invoice = Invoice::make()
->template('controlpanel')
->name(__("Invoice"))
->buyer($customer)
->seller($seller)
->discountByPercent(0)
->taxRate(floatval($shopProduct->getTaxPercent()))
->shipping(0)
->addItem($item)
->status(__($paymentStatus))
->series(now()->format('mY'))
->delimiter("-")
->sequence($newInvoiceID)
->serialNumberFormat(config("SETTINGS::INVOICE:PREFIX") . '{DELIMITER}{SERIES}{SEQUENCE}')
->currencyCode($currencyCode)
->currencySymbol(Currencies::getSymbol($currencyCode))
->notes($notes);
if (file_exists($logoPath)) {
$invoice->logo($logoPath);
}
//Save the invoice in "storage\app\invoice\USER_ID\YEAR"
$invoice->filename = $invoice->getSerialNumber() . '.pdf';
$invoice->render();
Storage::disk("local")->put("invoice/" . $user->id . "/" . now()->format('Y') . "/" . $invoice->filename, $invoice->output);
\App\Models\Invoice::create([
'invoice_user' => $user->id,
'invoice_name' => $invoice->getSerialNumber(),
'payment_id' => $payment->payment_id,
]);
//Send Invoice per Mail
$user->notify(new InvoiceNotification($invoice, $user, $payment));
return redirect()->route('store.index')->with('info', 'Payment was Canceled');
}
/**
* @return JsonResponse|mixed
*
* @throws Exception
*/
public function dataTable()
@ -653,8 +143,9 @@ class PaymentController extends Controller
$query = Payment::with('user');
return datatables($query)
->editColumn('user', function (Payment $payment) {
return $payment->user->name;
->addColumn('user', function (Payment $payment) {
return ($payment->user) ? '<a href="' . route('admin.users.show', $payment->user->id) . '">' . $payment->user->name . '</a>' : __('Unknown user');
})
->editColumn('price', function (Payment $payment) {
return $payment->formatToCurrency($payment->price);
@ -670,12 +161,15 @@ class PaymentController extends Controller
})
->editColumn('created_at', function (Payment $payment) {
return $payment->created_at ? $payment->created_at->diffForHumans() : '';
return [
'display' => $payment->created_at ? $payment->created_at->diffForHumans() : '',
'raw' => $payment->created_at ? strtotime($payment->created_at) : ''
];
})
->addColumn('actions', function (Payment $payment) {
return '<a data-content="' . __("Download") . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.invoices.downloadSingleInvoice', "id=" . $payment->payment_id) . '" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-file-download"></i></a>';
return '<a data-content="' . __('Download') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.invoices.downloadSingleInvoice', 'id=' . $payment->payment_id) . '" class="btn btn-sm text-white btn-info mr-1"><i class="fas fa-file-download"></i></a>';
})
->rawColumns(['actions'])
->rawColumns(['actions', 'user'])
->make(true);
}
}

View file

@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
use App\Models\Location;
use App\Models\Nest;
use App\Models\Product;
use App\Models\Settings;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -52,33 +51,33 @@ class ProductController extends Controller
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
"name" => "required|max:30",
"price" => "required|numeric|max:1000000|min:0",
"memory" => "required|numeric|max:1000000|min:5",
"cpu" => "required|numeric|max:1000000|min:0",
"swap" => "required|numeric|max:1000000|min:0",
"description" => "required|string|max:191",
"disk" => "required|numeric|max:1000000|min:5",
"minimum_credits" => "required|numeric|max:1000000|min:-1",
"io" => "required|numeric|max:1000000|min:0",
"databases" => "required|numeric|max:1000000|min:0",
"backups" => "required|numeric|max:1000000|min:0",
"allocations" => "required|numeric|max:1000000|min:0",
"nodes.*" => "required|exists:nodes,id",
"eggs.*" => "required|exists:eggs,id",
"disabled" => "nullable",
'name' => 'required|max:30',
'price' => 'required|numeric|max:1000000|min:0',
'memory' => 'required|numeric|max:1000000|min:5',
'cpu' => 'required|numeric|max:1000000|min:0',
'swap' => 'required|numeric|max:1000000|min:0',
'description' => 'required|string|max:191',
'disk' => 'required|numeric|max:1000000|min:5',
'minimum_credits' => 'required|numeric|max:1000000|min:-1',
'io' => 'required|numeric|max:1000000|min:0',
'databases' => 'required|numeric|max:1000000|min:0',
'backups' => 'required|numeric|max:1000000|min:0',
'allocations' => 'required|numeric|max:1000000|min:0',
'nodes.*' => 'required|exists:nodes,id',
'eggs.*' => 'required|exists:eggs,id',
'disabled' => 'nullable',
]);
$disabled = !is_null($request->input('disabled'));
$disabled = ! is_null($request->input('disabled'));
$product = Product::create(array_merge($request->all(), ['disabled' => $disabled]));
#link nodes and eggs
//link nodes and eggs
$product->eggs()->attach($request->input('eggs'));
$product->nodes()->attach($request->input('nodes'));
@ -88,21 +87,21 @@ class ProductController extends Controller
/**
* Display the specified resource.
*
* @param Product $product
* @param Product $product
* @return Application|Factory|View
*/
public function show(Product $product)
{
return view('admin.products.show', [
'product' => $product,
'minimum_credits' => config("SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER"),
'minimum_credits' => config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER'),
]);
}
/**
* Show the form for editing the specified resource.
*
* @param Product $product
* @param Product $product
* @return Application|Factory|View
*/
public function edit(Product $product)
@ -117,34 +116,34 @@ class ProductController extends Controller
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param Product $product
* @param Request $request
* @param Product $product
* @return RedirectResponse
*/
public function update(Request $request, Product $product): RedirectResponse
{
$request->validate([
"name" => "required|max:30",
"price" => "required|numeric|max:1000000|min:0",
"memory" => "required|numeric|max:1000000|min:5",
"cpu" => "required|numeric|max:1000000|min:0",
"swap" => "required|numeric|max:1000000|min:0",
"description" => "required|string|max:191",
"disk" => "required|numeric|max:1000000|min:5",
"io" => "required|numeric|max:1000000|min:0",
"minimum_credits" => "required|numeric|max:1000000|min:-1",
"databases" => "required|numeric|max:1000000|min:0",
"backups" => "required|numeric|max:1000000|min:0",
"allocations" => "required|numeric|max:1000000|min:0",
"nodes.*" => "required|exists:nodes,id",
"eggs.*" => "required|exists:eggs,id",
"disabled" => "nullable",
'name' => 'required|max:30',
'price' => 'required|numeric|max:1000000|min:0',
'memory' => 'required|numeric|max:1000000|min:5',
'cpu' => 'required|numeric|max:1000000|min:0',
'swap' => 'required|numeric|max:1000000|min:0',
'description' => 'required|string|max:191',
'disk' => 'required|numeric|max:1000000|min:5',
'io' => 'required|numeric|max:1000000|min:0',
'minimum_credits' => 'required|numeric|max:1000000|min:-1',
'databases' => 'required|numeric|max:1000000|min:0',
'backups' => 'required|numeric|max:1000000|min:0',
'allocations' => 'required|numeric|max:1000000|min:0',
'nodes.*' => 'required|exists:nodes,id',
'eggs.*' => 'required|exists:eggs,id',
'disabled' => 'nullable',
]);
$disabled = !is_null($request->input('disabled'));
$disabled = ! is_null($request->input('disabled'));
$product->update(array_merge($request->all(), ['disabled' => $disabled]));
#link nodes and eggs
//link nodes and eggs
$product->eggs()->detach();
$product->nodes()->detach();
$product->eggs()->attach($request->input('eggs'));
@ -154,13 +153,13 @@ class ProductController extends Controller
}
/**
* @param Request $request
* @param Product $product
* @param Request $request
* @param Product $product
* @return RedirectResponse
*/
public function disable(Request $request, Product $product)
{
$product->update(['disabled' => !$product->disabled]);
$product->update(['disabled' => ! $product->disabled]);
return redirect()->route('admin.products.index')->with('success', 'Product has been updated!');
}
@ -168,7 +167,7 @@ class ProductController extends Controller
/**
* Remove the specified resource from storage.
*
* @param Product $product
* @param Product $product
* @return RedirectResponse
*/
public function destroy(Product $product)
@ -179,12 +178,13 @@ class ProductController extends Controller
}
$product->delete();
return redirect()->back()->with('success', __('Product has been removed!'));
}
/**
* @return JsonResponse|mixed
*
* @throws Exception|Exception
*/
public function dataTable()
@ -194,14 +194,14 @@ class ProductController extends Controller
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="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>
<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>
<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>
</form>
';
})
@ -216,18 +216,22 @@ class ProductController extends Controller
return $product->eggs()->count();
})
->addColumn('disabled', function (Product $product) {
$checked = $product->disabled == false ? "checked" : "";
$checked = $product->disabled == false ? 'checked' : '';
return '
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.products.disable', $product->id) . '">
' . csrf_field() . '
' . method_field("PATCH") . '
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.products.disable', $product->id).'">
'.csrf_field().'
'.method_field('PATCH').'
<div class="custom-control custom-switch">
<input ' . $checked . ' name="disabled" onchange="this.form.submit()" type="checkbox" class="custom-control-input" id="switch' . $product->id . '">
<label class="custom-control-label" for="switch' . $product->id . '"></label>
<input '.$checked.' name="disabled" onchange="this.form.submit()" type="checkbox" class="custom-control-input" id="switch'.$product->id.'">
<label class="custom-control-label" for="switch'.$product->id.'"></label>
</div>
</form>
';
})
->editColumn('minimum_credits', function (Product $product) {
return $product->minimum_credits==-1 ? config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER') : $product->minimum_credits;
})
->editColumn('created_at', function (Product $product) {
return $product->created_at ? $product->created_at->diffForHumans() : '';
})

View file

@ -3,10 +3,9 @@
namespace App\Http\Controllers\Admin;
use App\Classes\Pterodactyl;
use App\Classes\PterodactylWrapper;
use App\Http\Controllers\Controller;
use App\Models\Server;
use App\Models\Settings;
use App\Models\User;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -15,6 +14,7 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
class ServerController extends Controller
{
@ -28,66 +28,58 @@ class ServerController extends Controller
return view('admin.servers.index');
}
/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param Server $server
* @return Response
*/
public function show(Server $server)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param Server $server
* @param Server $server
* @return Response
*/
public function edit(Server $server)
{
// get all users from the database
$users = User::all();
return view('admin.servers.edit')->with([
'server' => $server
'server' => $server,
'users' => $users,
]);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param Server $server
* @return Response
* @param Request $request
* @param Server $server
*/
public function update(Request $request, Server $server)
{
$request->validate([
"identifier" => "required|string",
'identifier' => 'required|string',
'user_id' => 'required|integer',
]);
$server->update($request->all());
if ($request->get('user_id') != $server->user_id) {
// find the user
$user = User::findOrFail($request->get('user_id'));
// try to update the owner on pterodactyl
try {
$response = Pterodactyl::updateServerOwner($server, $user->pterodactyl_id);
if ($response->getStatusCode() != 200) {
return redirect()->back()->with('error', 'Failed to update server owner on pterodactyl');
}
// update the owner on the database
$server->user_id = $user->id;
} catch (Exception $e) {
return redirect()->back()->with('error', 'Internal Server Error');
}
}
// update the identifier
$server->identifier = $request->get('identifier');
$server->save();
return redirect()->route('admin.servers.index')->with('success', 'Server updated!');
}
@ -95,13 +87,14 @@ class ServerController extends Controller
/**
* Remove the specified resource from storage.
*
* @param Server $server
* @param Server $server
* @return RedirectResponse|Response
*/
public function destroy(Server $server)
{
try {
$server->delete();
return redirect()->route('admin.servers.index')->with('success', __('Server removed'));
} catch (Exception $e) {
return redirect()->route('admin.servers.index')->with('error', __('An exception has occurred while trying to remove a resource "') . $e->getMessage() . '"');
@ -109,13 +102,13 @@ class ServerController extends Controller
}
/**
* @param Server $server
* @param Server $server
* @return RedirectResponse
*/
public function toggleSuspended(Server $server)
{
try {
$server->isSuspended() ? $server->unSuspend() : $server->suspend();
$server->isSuspended() ? $server->unSuspend() : $server->suspend();
} catch (Exception $exception) {
return redirect()->back()->with('error', $exception->getMessage());
}
@ -130,20 +123,20 @@ class ServerController extends Controller
$CPIDArray = [];
$renameCount = 0;
foreach($CPServers as $CPServer)//go thru all CP servers and make array with IDs as keys. All values are false.
{
if($CPServer->pterodactyl_id) $CPIDArray[$CPServer->pterodactyl_id] = false;
foreach ($CPServers as $CPServer) { //go thru all CP servers and make array with IDs as keys. All values are false.
if ($CPServer->pterodactyl_id) {
$CPIDArray[$CPServer->pterodactyl_id] = false;
}
}
foreach($pteroServers as $server)//go thru all ptero servers, if server exists, change value to true in array.
{
if(isset($CPIDArray[$server['attributes']['id']])){
$CPIDArray[$server['attributes']['id']]=true;
foreach ($pteroServers as $server) { //go thru all ptero servers, if server exists, change value to true in array.
if (isset($CPIDArray[$server['attributes']['id']])) {
$CPIDArray[$server['attributes']['id']] = true;
if(isset($server['attributes']['name'])){//failsafe
if (isset($server['attributes']['name'])) { //failsafe
//Check if a server got renamed
$savedServer = Server::query()->where('pterodactyl_id', $server['attributes']['id'])->first();
if($savedServer->name != $server['attributes']['name']){
if ($savedServer->name != $server['attributes']['name']) {
$savedServer->name = $server['attributes']['name'];
$savedServer->save();
$renameCount++;
@ -151,27 +144,39 @@ class ServerController extends Controller
}
}
}
$filteredArray = array_filter($CPIDArray, function($v, $k) { return $v == false; }, ARRAY_FILTER_USE_BOTH); //Array of servers, that dont exist on ptero (value == false)
$filteredArray = array_filter($CPIDArray, function ($v, $k) {
return $v == false;
}, ARRAY_FILTER_USE_BOTH); //Array of servers, that dont exist on ptero (value == false)
$deleteCount = 0;
foreach($filteredArray as $key => $CPID)//delete servers that dont exist on ptero anymore
{
if(!Pterodactyl::getServerAttributes($key, true)) $deleteCount++;
foreach ($filteredArray as $key => $CPID) { //delete servers that dont exist on ptero anymore
if (!Pterodactyl::getServerAttributes($key, true)) {
$deleteCount++;
}
}
return redirect()->back()->with('success', __('Servers synced successfully' . (($renameCount)?(',\n' . __('renamed') . ' ' . $renameCount . ' ' . __('servers')):'') . ((count($filteredArray))?(',\n' . __('deleted') . ' ' . $deleteCount . '/' . count($filteredArray) . ' ' . __('old servers')):''))) . '.';
return redirect()->back()->with('success', __('Servers synced successfully' . (($renameCount) ? (',\n' . __('renamed') . ' ' . $renameCount . ' ' . __('servers')) : '') . ((count($filteredArray)) ? (',\n' . __('deleted') . ' ' . $deleteCount . '/' . count($filteredArray) . ' ' . __('old servers')) : ''))) . '.';
}
/**
* @return JsonResponse|mixed
*
* @throws Exception
*/
public function dataTable(Request $request)
{
$query = Server::with(['user', 'product']);
if ($request->has('product')) $query->where('product_id', '=', $request->input('product'));
if ($request->has('user')) $query->where('user_id', '=', $request->input('user'));
if ($request->has('product')) {
$query->where('product_id', '=', $request->input('product'));
}
if ($request->has('user')) {
$query->where('user_id', '=', $request->input('user'));
}
$query->select('servers.*');
Log::info($request->input('order'));
return datatables($query)
->addColumn('user', function (Server $server) {
@ -181,12 +186,12 @@ class ServerController extends Controller
return $server->product->description;
})
->addColumn('actions', function (Server $server) {
$suspendColor = $server->isSuspended() ? "btn-success" : "btn-warning";
$suspendIcon = $server->isSuspended() ? "fa-play-circle" : "fa-pause-circle";
$suspendText = $server->isSuspended() ? __("Unsuspend") : __("Suspend");
$suspendColor = $server->isSuspended() ? 'btn-success' : 'btn-warning';
$suspendIcon = $server->isSuspended() ? 'fa-play-circle' : 'fa-pause-circle';
$suspendText = $server->isSuspended() ? __('Unsuspend') : __('Suspend');
return '
<a data-content="' . __("Edit") . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.servers.edit', $server->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.servers.edit', $server->id) . '" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<form class="d-inline" method="post" action="' . route('admin.servers.togglesuspend', $server->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>
@ -194,14 +199,15 @@ class ServerController extends Controller
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.servers.destroy', $server->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>
' . 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>
</form>
';
})
->addColumn('status', function (Server $server) {
$labelColor = $server->isSuspended() ? 'text-danger' : 'text-success';
$labelColor = $server->suspended ? 'text-danger' : 'text-success';
return '<i class="fas ' . $labelColor . ' fa-circle mr-2"></i>';
})
->editColumn('created_at', function (Server $server) {
@ -211,7 +217,7 @@ class ServerController extends Controller
return $server->suspended ? $server->suspended->diffForHumans() : '';
})
->editColumn('name', function (Server $server) {
return '<a class="text-info" target="_blank" href="' . config("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/admin/servers/view/' . $server->pterodactyl_id . '">' . strip_tags($server->name) . '</a>';
return '<a class="text-info" target="_blank" href="' . config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/admin/servers/view/' . $server->pterodactyl_id . '">' . strip_tags($server->name) . '</a>';
})
->rawColumns(['user', 'actions', 'status', 'name'])
->make();

View file

@ -3,12 +3,11 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Settings;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Qirolab\Theme\Theme;
class SettingsController extends Controller
{
@ -19,25 +18,38 @@ class SettingsController extends Controller
*/
public function index()
{
//Get all tabs as laravel view paths
$tabs = [];
foreach (glob(resource_path('views/admin/settings/tabs/*.blade.php')) as $filename) {
$tabs[] = 'admin.settings.tabs.' . basename($filename, '.blade.php');
if(file_exists(Theme::getViewPaths()[0] . '/admin/settings/tabs/')){
$tabspath = glob(Theme::getViewPaths()[0] . '/admin/settings/tabs/*.blade.php');
}else{
$tabspath = glob(Theme::path($path = 'views', $themeName = 'default').'/admin/settings/tabs/*.blade.php');
}
foreach ($tabspath as $filename) {
$tabs[] = 'admin.settings.tabs.'.basename($filename, '.blade.php');
}
//Generate a html list item for each tab based on tabs file basename, set first tab as active
$tabListItems = [];
foreach ($tabs as $tab) {
$tabName = str_replace('admin.settings.tabs.', '', $tab);
$tabListItems[] = '<li class="nav-item">
<a class="nav-link ' . (empty($tabListItems) ? 'active' : '') . '" data-toggle="pill" href="#' . $tabName . '">
' . __(ucfirst($tabName)) . '
<a class="nav-link '.(empty($tabListItems) ? 'active' : '').'" data-toggle="pill" href="#'.$tabName.'">
'.__(ucfirst($tabName)).'
</a></li>';
}
$themes = array_diff(scandir(base_path('themes')), array('..', '.'));
return view('admin.settings.index', [
'tabs' => $tabs,
'tabListItems' => $tabListItems,
'themes' => $themes,
'active_theme' => Theme::active(),
]);
}
}

View file

@ -3,7 +3,6 @@
namespace App\Http\Controllers\Admin;
use App\Models\ShopProduct;
use App\Models\Settings;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
@ -15,6 +14,7 @@ use Illuminate\Validation\Rule;
class ShopProductController extends Controller
{
/**
* Display a listing of the resource.
*
@ -26,12 +26,14 @@ class ShopProductController extends Controller
if (
env('APP_ENV') == 'local' ||
config("SETTINGS::PAYMENTS:PAYPAL:SECRET") && config("SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID") ||
config("SETTINGS::PAYMENTS:STRIPE:SECRET") && config("SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET") && config("SETTINGS::PAYMENTS:STRIPE:METHODS")
) $isPaymentSetup = true;
config('SETTINGS::PAYMENTS:PAYPAL:SECRET') && config('SETTINGS::PAYMENTS:PAYPAL:CLIENT_ID') ||
config('SETTINGS::PAYMENTS:STRIPE:SECRET') && config('SETTINGS::PAYMENTS:STRIPE:ENDPOINT_SECRET') && config('SETTINGS::PAYMENTS:STRIPE:METHODS')
) {
$isPaymentSetup = true;
}
return view('admin.store.index', [
'isPaymentSetup' => $isPaymentSetup
'isPaymentSetup' => $isPaymentSetup,
]);
}
@ -43,26 +45,26 @@ class ShopProductController extends Controller
public function create()
{
return view('admin.store.create', [
'currencyCodes' => config('currency_codes')
'currencyCodes' => config('currency_codes'),
]);
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
"disabled" => "nullable",
"type" => "required|string",
"currency_code" => ["required", "string", "max:3", Rule::in(config('currency_codes'))],
"price" => "required|regex:/^\d+(\.\d{1,2})?$/",
"quantity" => "required|numeric",
"description" => "required|string|max:60",
"display" => "required|string|max:60",
'disabled' => 'nullable',
'type' => 'required|string',
'currency_code' => ['required', 'string', 'max:3', Rule::in(config('currency_codes'))],
'price' => "required|regex:/^\d+(\.\d{1,2})?$/",
'quantity' => 'required|numeric',
'description' => 'required|string|max:60',
'display' => 'required|string|max:60',
]);
$disabled = !is_null($request->input('disabled'));
@ -71,48 +73,37 @@ class ShopProductController extends Controller
return redirect()->route('admin.store.index')->with('success', __('Store item has been created!'));
}
/**
* Display the specified resource.
*
* @param ShopProduct $shopProduct
* @return Response
*/
public function show(ShopProduct $shopProduct)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param ShopProduct $shopProduct
* @param ShopProduct $shopProduct
* @return Application|Factory|View|Response
*/
public function edit(ShopProduct $shopProduct)
{
return view('admin.store.edit', [
'currencyCodes' => config('currency_codes'),
'shopProduct' => $shopProduct
'shopProduct' => $shopProduct,
]);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param ShopProduct $shopProduct
* @param Request $request
* @param ShopProduct $shopProduct
* @return RedirectResponse
*/
public function update(Request $request, ShopProduct $shopProduct)
{
$request->validate([
"disabled" => "nullable",
"type" => "required|string",
"currency_code" => ["required", "string", "max:3", Rule::in(config('currency_codes'))],
"price" => "required|regex:/^\d+(\.\d{1,2})?$/",
"quantity" => "required|numeric|max:100000000",
"description" => "required|string|max:60",
"display" => "required|string|max:60",
'disabled' => 'nullable',
'type' => 'required|string',
'currency_code' => ['required', 'string', 'max:3', Rule::in(config('currency_codes'))],
'price' => "required|regex:/^\d+(\.\d{1,2})?$/",
'quantity' => 'required|numeric|max:100000000',
'description' => 'required|string|max:60',
'display' => 'required|string|max:60',
]);
$disabled = !is_null($request->input('disabled'));
@ -122,8 +113,8 @@ class ShopProductController extends Controller
}
/**
* @param Request $request
* @param ShopProduct $shopProduct
* @param Request $request
* @param ShopProduct $shopProduct
* @return RedirectResponse
*/
public function disable(Request $request, ShopProduct $shopProduct)
@ -136,38 +127,40 @@ class ShopProductController extends Controller
/**
* Remove the specified resource from storage.
*
* @param ShopProduct $shopProduct
* @param ShopProduct $shopProduct
* @return RedirectResponse
*/
public function destroy(ShopProduct $shopProduct)
{
$shopProduct->delete();
return redirect()->back()->with('success', __('Store item has been removed!'));
}
public function dataTable()
public function dataTable(Request $request)
{
$query = ShopProduct::query();
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="btn btn-sm btn-info mr-1"><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>
' . 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>
</form>
';
})
->addColumn('disabled', function (ShopProduct $shopProduct) {
$checked = $shopProduct->disabled == false ? "checked" : "";
$checked = $shopProduct->disabled == false ? 'checked' : '';
return '
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.store.disable', $shopProduct->id) . '">
' . csrf_field() . '
' . method_field("PATCH") . '
' . method_field('PATCH') . '
<div class="custom-control custom-switch">
<input ' . $checked . ' name="disabled" onchange="this.form.submit()" type="checkbox" class="custom-control-input" id="switch' . $shopProduct->id . '">
<label class="custom-control-label" for="switch' . $shopProduct->id . '"></label>

View file

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Admin;
use App\Enums\UsefulLinkLocation;
use App\Http\Controllers\Controller;
use App\Models\UsefulLink;
use Illuminate\Contracts\Foundation\Application;
@ -30,33 +31,41 @@ class UsefulLinkController extends Controller
*/
public function create()
{
return view('admin.usefullinks.create');
$positions = UsefulLinkLocation::cases();
return view('admin.usefullinks.create')->with('positions', $positions);
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
'icon' => 'required|string',
'title' => 'required|string|max:60',
'link' => 'required|url|string|max:191',
'description' => 'required|string|max:2000',
'icon' => 'required|string',
'title' => 'required|string|max:60',
'link' => 'required|url|string|max:191',
'description' => 'required|string|max:2000',
]);
UsefulLink::create([
'icon' => $request->icon,
'title' => $request->title,
'link' => $request->link,
'description' => $request->description,
'position' => implode(",",$request->position),
]);
UsefulLink::create($request->all());
return redirect()->route('admin.usefullinks.index')->with('success', __('link has been created!'));
}
/**
* Display the specified resource.
*
* @param UsefulLink $usefullink
* @param UsefulLink $usefullink
* @return Response
*/
public function show(UsefulLink $usefullink)
@ -67,21 +76,23 @@ class UsefulLinkController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param UsefulLink $usefullink
* @param UsefulLink $usefullink
* @return Application|Factory|View
*/
public function edit(UsefulLink $usefullink)
{
return view('admin.usefullinks.edit' , [
'link' => $usefullink
$positions = UsefulLinkLocation::cases();
return view('admin.usefullinks.edit', [
'link' => $usefullink,
'positions' => $positions,
]);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param UsefulLink $usefullink
* @param Request $request
* @param UsefulLink $usefullink
* @return RedirectResponse
*/
public function update(Request $request, UsefulLink $usefullink)
@ -93,19 +104,27 @@ class UsefulLinkController extends Controller
'description' => 'required|string|max:2000',
]);
$usefullink->update($request->all());
$usefullink->update([
'icon' => $request->icon,
'title' => $request->title,
'link' => $request->link,
'description' => $request->description,
'position' => implode(",",$request->position),
]);
return redirect()->route('admin.usefullinks.index')->with('success', __('link has been updated!'));
}
/**
* Remove the specified resource from storage.
*
* @param UsefulLink $usefullink
* @param UsefulLink $usefullink
* @return Response
*/
public function destroy(UsefulLink $usefullink)
{
$usefullink->delete();
return redirect()->back()->with('success', __('product has been removed!'));
}
@ -116,12 +135,12 @@ class UsefulLinkController extends Controller
return datatables($query)
->addColumn('actions', function (UsefulLink $link) {
return '
<a data-content="'.__("Edit").'" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.usefullinks.edit', $link->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.usefullinks.edit', $link->id).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.usefullinks.destroy', $link->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>
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.usefullinks.destroy', $link->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>
</form>
';
})
@ -131,7 +150,7 @@ class UsefulLinkController extends Controller
->editColumn('icon', function (UsefulLink $link) {
return "<i class='{$link->icon}'></i>";
})
->rawColumns(['actions' , 'icon'])
->rawColumns(['actions', 'icon'])
->make();
}
}

View file

@ -5,11 +5,8 @@ namespace App\Http\Controllers\Admin;
use App\Classes\Pterodactyl;
use App\Events\UserUpdateCreditsEvent;
use App\Http\Controllers\Controller;
use App\Models\Settings;
use App\Models\User;
use App\Notifications\DynamicNotification;
use Illuminate\Support\Facades\DB;
use Spatie\QueryBuilder\QueryBuilder;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
@ -19,14 +16,17 @@ use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\HtmlString;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Spatie\QueryBuilder\QueryBuilder;
class UserController extends Controller
{
private Pterodactyl $pterodactyl;
public function __construct(Pterodactyl $pterodactyl)
@ -37,7 +37,7 @@ class UserController extends Controller
/**
* Display a listing of the resource.
*
* @param Request $request
* @param Request $request
* @return Application|Factory|View|Response
*/
public function index(Request $request)
@ -48,23 +48,23 @@ class UserController extends Controller
/**
* Display the specified resource.
*
* @param User $user
* @param User $user
* @return Application|Factory|View|Response
*/
public function show(User $user)
{
//QUERY ALL REFERRALS A USER HAS
//i am not proud of this at all.
$allReferals = array();
$referrals = DB::table("user_referrals")->where("referral_id","=",$user->id)->get();
foreach($referrals as $referral){
array_push($allReferals, $allReferals["id"] = User::query()->findOrFail($referral->registered_user_id));
}
array_pop($allReferals);
$allReferals = [];
$referrals = DB::table('user_referrals')->where('referral_id', '=', $user->id)->get();
foreach ($referrals as $referral) {
array_push($allReferals, $allReferals['id'] = User::query()->findOrFail($referral->registered_user_id));
}
array_pop($allReferals);
return view('admin.users.show')->with([
'user' => $user,
'referrals' => $allReferals
'referrals' => $allReferals,
]);
}
@ -92,50 +92,51 @@ class UserController extends Controller
return $item;
});
}
/**
* Show the form for editing the specified resource.
*
* @param User $user
* @param User $user
* @return Application|Factory|View|Response
*/
public function edit(User $user)
{
return view('admin.users.edit')->with([
'user' => $user
'user' => $user,
]);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param User $user
* @param Request $request
* @param User $user
* @return RedirectResponse
*
* @throws Exception
*/
public function update(Request $request, User $user)
{
$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}",
'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}",
]);
if (isset($this->pterodactyl->getUser($request->input('pterodactyl_id'))['errors'])) {
throw ValidationException::withMessages([
'pterodactyl_id' => [__("User does not exists on pterodactyl's panel")]
'pterodactyl_id' => [__("User does not exists on pterodactyl's panel")],
]);
}
if (!is_null($request->input('new_password'))) {
$request->validate([
'new_password' => 'required|string|min:8',
'new_password_confirmation' => 'required|same:new_password'
'new_password_confirmation' => 'required|same:new_password',
]);
$user->update([
@ -152,53 +153,58 @@ class UserController extends Controller
/**
* Remove the specified resource from storage.
*
* @param User $user
* @param User $user
* @return RedirectResponse
*/
public function destroy(User $user)
{
$user->delete();
return redirect()->back()->with('success', __('user has been removed!'));
}
/**
* Verifys the users email
*
* @param User $user
* @param User $user
* @return RedirectResponse
*/
public function verifyEmail(Request $request, User $user)
{
$user->verifyEmail();
return redirect()->back()->with('success', __('Email has been verified!'));
}
/**
* @param Request $request
* @param User $user
* @param Request $request
* @param User $user
* @return RedirectResponse
*/
public function loginAs(Request $request, User $user)
{
$request->session()->put('previousUser', Auth::user()->id);
Auth::login($user);
return redirect()->route('home');
}
/**
* @param Request $request
* @param Request $request
* @return RedirectResponse
*/
public function logBackIn(Request $request)
{
Auth::loginUsingId($request->session()->get('previousUser'), true);
$request->session()->remove('previousUser');
return redirect()->route('admin.users.index');
}
/**
* Show the form for seding notifications to the specified resource.
*
* @param User $user
* @param User $user
* @return Application|Factory|View|Response
*/
public function notifications(User $user)
@ -209,44 +215,46 @@ class UserController extends Controller
/**
* Notify the specified resource.
*
* @param Request $request
* @param User $user
* @param Request $request
* @param User $user
* @return RedirectResponse
*
* @throws Exception
*/
public function notify(Request $request)
{
$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",
"title" => "required|string|min:1",
"content" => "required|string|min:1"
'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',
'title' => 'required|string|min:1',
'content' => 'required|string|min:1',
]);
$mail = null;
$database = null;
if (in_array('database', $data["via"])) {
if (in_array('database', $data['via'])) {
$database = [
"title" => $data["title"],
"content" => $data["content"]
'title' => $data['title'],
'content' => $data['content'],
];
}
if (in_array('mail', $data["via"])) {
if (in_array('mail', $data['via'])) {
$mail = (new MailMessage)
->subject($data["title"])
->line(new HtmlString($data["content"]));
->subject($data['title'])
->line(new HtmlString($data['content']));
}
$all = $data["all"] ?? false;
$users = $all ? User::all() : User::whereIn("id", $data["users"])->get();
Notification::send($users, new DynamicNotification($data["via"], $database, $mail));
$all = $data['all'] ?? false;
$users = $all ? User::all() : User::whereIn('id', $data['users'])->get();
Notification::send($users, new DynamicNotification($data['via'], $database, $mail));
return redirect()->route('admin.users.notifications')->with('success', __('Notification sent!'));
}
/**
* @param User $user
* @param User $user
* @return RedirectResponse
*/
public function toggleSuspended(User $user)
@ -261,12 +269,14 @@ class UserController extends Controller
}
/**
*
* @throws Exception
*/
public function dataTable()
public function dataTable(Request $request)
{
$query = User::with(['discordUser', 'servers'])->select('users.*');
$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');
return datatables($query)
->addColumn('avatar', function (User $user) {
@ -278,36 +288,28 @@ class UserController extends Controller
->addColumn('verified', function (User $user) {
return $user->getVerifiedStatus();
})
->addColumn('servers', function (User $user) {
return $user->servers->count();
})
->addColumn('referrals', function (User $user) {
return DB::table('user_referrals')->where("referral_id","=",$user->id)->count();
})
->addColumn('discordId', function (User $user) {
return $user->discordUser ? $user->discordUser->id : '';
})
->addColumn('last_seen', function (User $user) {
return $user->last_seen ? $user->last_seen->diffForHumans() : '';
})
->addColumn('actions', function (User $user) {
$suspendColor = $user->isSuspended() ? "btn-success" : "btn-warning";
$suspendIcon = $user->isSuspended() ? "fa-play-circle" : "fa-pause-circle";
$suspendText = $user->isSuspended() ? __("Unsuspend") : __("Suspend");
$suspendColor = $user->isSuspended() ? 'btn-success' : 'btn-warning';
$suspendIcon = $user->isSuspended() ? 'fa-play-circle' : 'fa-pause-circle';
$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>
<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>
</form>
<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>
<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>
</form>
<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>
</form>
' . 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>
</form>
';
})
->editColumn('role', function (User $user) {
@ -328,13 +330,13 @@ class UserController extends Controller
return '<span class="badge ' . $badgeColor . '">' . $user->role . '</span>';
})
->editColumn('last_seen', function (User $user) {
return $user->last_seen ? $user->last_seen->diffForHumans() : __('Never');
})
->editColumn('name', function (User $user) {
return '<a class="text-info" target="_blank" href="' . config("SETTINGS::SYSTEM:PTERODACTYL:URL") . '/admin/users/view/' . $user->pterodactyl_id . '">' . strip_tags($user->name) . '</a>';
return '<a class="text-info" target="_blank" href="' . config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/admin/users/view/' . $user->pterodactyl_id . '">' . strip_tags($user->name) . '</a>';
})
->orderColumn('last_seen', function ($query) {
$query->orderBy('last_seen', "desc");
})
->rawColumns(['avatar', 'name', 'credits', 'role', 'usage', 'referrals', 'actions', 'last_seen'])
->make(true);
->rawColumns(['avatar', 'name', 'credits', 'role', 'usage', 'actions'])
->make();
}
}

View file

@ -40,16 +40,16 @@ class VoucherController extends Controller
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request)
{
$request->validate([
'memo' => 'nullable|string|max:191',
'code' => 'required|string|alpha_dash|max:36|min:4|unique:vouchers',
'uses' => 'required|numeric|max:2147483647|min:1',
'credits' => 'required|numeric|between:0,99999999',
'memo' => 'nullable|string|max:191',
'code' => 'required|string|alpha_dash|max:36|min:4|unique:vouchers',
'uses' => 'required|numeric|max:2147483647|min:1',
'credits' => 'required|numeric|between:0,99999999',
'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years',
]);
@ -61,7 +61,7 @@ class VoucherController extends Controller
/**
* Display the specified resource.
*
* @param Voucher $voucher
* @param Voucher $voucher
* @return Response
*/
public function show(Voucher $voucher)
@ -72,30 +72,30 @@ class VoucherController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param Voucher $voucher
* @param Voucher $voucher
* @return Application|Factory|View
*/
public function edit(Voucher $voucher)
{
return view('admin.vouchers.edit', [
'voucher' => $voucher
'voucher' => $voucher,
]);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param Voucher $voucher
* @param Request $request
* @param Voucher $voucher
* @return RedirectResponse
*/
public function update(Request $request, Voucher $voucher)
{
$request->validate([
'memo' => 'nullable|string|max:191',
'code' => "required|string|alpha_dash|max:36|min:4|unique:vouchers,code,{$voucher->id}",
'uses' => 'required|numeric|max:2147483647|min:1',
'credits' => 'required|numeric|between:0,99999999',
'memo' => 'nullable|string|max:191',
'code' => "required|string|alpha_dash|max:36|min:4|unique:vouchers,code,{$voucher->id}",
'uses' => 'required|numeric|max:2147483647|min:1',
'credits' => 'required|numeric|between:0,99999999',
'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years',
]);
@ -107,61 +107,71 @@ class VoucherController extends Controller
/**
* Remove the specified resource from storage.
*
* @param Voucher $voucher
* @param Voucher $voucher
* @return RedirectResponse
*/
public function destroy(Voucher $voucher)
{
$voucher->delete();
return redirect()->back()->with('success', __('voucher has been removed!'));
}
public function users(Voucher $voucher)
{
return view('admin.vouchers.users', [
'voucher' => $voucher
'voucher' => $voucher,
]);
}
/**
* @param Request $request
* @param Request $request
* @return JsonResponse
*
* @throws ValidationException
*/
public function redeem(Request $request)
{
#general validations
//general validations
$request->validate([
'code' => 'required|exists:vouchers,code'
'code' => 'required|exists:vouchers,code',
]);
#get voucher by code
//get voucher by code
$voucher = Voucher::where('code', '=', $request->input('code'))->firstOrFail();
#extra validations
if ($voucher->getStatus() == 'USES_LIMIT_REACHED') throw ValidationException::withMessages([
'code' => __('This voucher has reached the maximum amount of uses')
]);
//extra validations
if ($voucher->getStatus() == 'USES_LIMIT_REACHED') {
throw ValidationException::withMessages([
'code' => __('This voucher has reached the maximum amount of uses'),
]);
}
if ($voucher->getStatus() == 'EXPIRED') throw ValidationException::withMessages([
'code' => __('This voucher has expired')
]);
if ($voucher->getStatus() == 'EXPIRED') {
throw ValidationException::withMessages([
'code' => __('This voucher has expired'),
]);
}
if (!$request->user()->vouchers()->where('id', '=', $voucher->id)->get()->isEmpty()) throw ValidationException::withMessages([
'code' => __('You already redeemed this voucher code')
]);
if (! $request->user()->vouchers()->where('id', '=', $voucher->id)->get()->isEmpty()) {
throw ValidationException::withMessages([
'code' => __('You already redeemed this voucher code'),
]);
}
if ($request->user()->credits + $voucher->credits >= 99999999) throw ValidationException::withMessages([
'code' => "You can't redeem this voucher because you would exceed the limit of " . CREDITS_DISPLAY_NAME
]);
if ($request->user()->credits + $voucher->credits >= 99999999) {
throw ValidationException::withMessages([
'code' => "You can't redeem this voucher because you would exceed the limit of ".CREDITS_DISPLAY_NAME,
]);
}
#redeem voucher
//redeem voucher
$voucher->redeem($request->user());
event(new UserUpdateCreditsEvent($request->user()));
return response()->json([
'success' => "{$voucher->credits} " . CREDITS_DISPLAY_NAME ." ". __("have been added to your balance!")
'success' => "{$voucher->credits} ".CREDITS_DISPLAY_NAME.' '.__('have been added to your balance!'),
]);
}
@ -171,10 +181,10 @@ class VoucherController extends Controller
return datatables($users)
->editColumn('name', function (User $user) {
return '<a class="text-info" target="_blank" href="' . route('admin.users.show', $user->id) . '">' . $user->name . '</a>';
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="fas fa-coins mr-2"></i> '.$user->credits();
})
->addColumn('last_seen', function (User $user) {
return $user->last_seen ? $user->last_seen->diffForHumans() : '';
@ -182,6 +192,7 @@ class VoucherController extends Controller
->rawColumns(['name', 'credits', 'last_seen'])
->make();
}
public function dataTable()
{
$query = Voucher::query();
@ -189,20 +200,23 @@ class VoucherController extends Controller
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="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>
<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>
<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>
</form>
';
})
->addColumn('status', function (Voucher $voucher) {
$color = 'success';
if ($voucher->getStatus() != __('VALID')) $color = 'danger';
return '<span class="badge badge-' . $color . '">' . $voucher->getStatus() . '</span>';
if ($voucher->getStatus() != __('VALID')) {
$color = 'danger';
}
return '<span class="badge badge-'.$color.'">'.$voucher->getStatus().'</span>';
})
->editColumn('uses', function (Voucher $voucher) {
return "{$voucher->used} / {$voucher->uses}";
@ -211,7 +225,10 @@ class VoucherController extends Controller
return number_format($voucher->credits, 2, '.', '');
})
->editColumn('expires_at', function (Voucher $voucher) {
if (!$voucher->expires_at) return "";
if (! $voucher->expires_at) {
return '';
}
return $voucher->expires_at ? $voucher->expires_at->diffForHumans() : '';
})
->editColumn('code', function (Voucher $voucher) {

View file

@ -19,8 +19,9 @@ class NotificationController extends Controller
{
/**
* Display all notifications of an user.
* @param Request $request
* @param int $userId
*
* @param Request $request
* @param int $userId
* @return Response
*/
public function index(Request $request, int $userId)
@ -28,14 +29,14 @@ class NotificationController extends Controller
$discordUser = DiscordUser::find($userId);
$user = $discordUser ? $discordUser->user : User::findOrFail($userId);
return $user->notifications()->paginate($request->query("per_page", 50));
return $user->notifications()->paginate($request->query('per_page', 50));
}
/**
* Display a specific notification
*
* @param int $userId
* @param int $notificationId
* @param int $userId
* @param int $notificationId
* @return JsonResponse
*/
public function view(int $userId, $notificationId)
@ -43,10 +44,10 @@ class NotificationController extends Controller
$discordUser = DiscordUser::find($userId);
$user = $discordUser ? $discordUser->user : User::findOrFail($userId);
$notification = $user->notifications()->where("id", $notificationId)->get()->first();
$notification = $user->notifications()->where('id', $notificationId)->get()->first();
if (!$notification) {
return response()->json(["message" => "Notification not found."], 404);
if (! $notification) {
return response()->json(['message' => 'Notification not found.'], 404);
}
return $notification;
@ -55,42 +56,43 @@ class NotificationController extends Controller
/**
* Send a notification to an user.
*
* @param Request $request
* @param Request $request
* @return JsonResponse
*
* @throws ValidationException
*/
public function send(Request $request)
{
$data = $request->validate([
"via" => ["required", new Delimited("in:mail,database")],
"all" => "required_without:users|boolean",
"users" => ["required_without:all"],
"title" => "required|string|min:1",
"content" => "required|string|min:1"
'via' => ['required', new Delimited('in:mail,database')],
'all' => 'required_without:users|boolean',
'users' => ['required_without:all'],
'title' => 'required|string|min:1',
'content' => 'required|string|min:1',
]);
$via = explode(",", $data["via"]);
$via = explode(',', $data['via']);
$mail = null;
$database = null;
if (in_array("database", $via)) {
if (in_array('database', $via)) {
$database = [
"title" => $data["title"],
"content" => $data["content"]
'title' => $data['title'],
'content' => $data['content'],
];
}
if (in_array("mail", $via)) {
if (in_array('mail', $via)) {
$mail = (new MailMessage)
->subject($data["title"])
->line(new HtmlString($data["content"]));
->subject($data['title'])
->line(new HtmlString($data['content']));
}
$all = $data["all"] ?? false;
$all = $data['all'] ?? false;
if ($all) {
$users = User::all();
} else {
$userIds = explode(",", $data["users"]);
$userIds = explode(',', $data['users']);
$users = User::query()
->whereIn("id", $userIds)
->whereIn('id', $userIds)
->orWhereHas('discordUser', function (Builder $builder) use ($userIds) {
$builder->whereIn('id', $userIds);
})
@ -104,13 +106,14 @@ class NotificationController extends Controller
}
Notification::send($users, new DynamicNotification($via, $database, $mail));
return response()->json(["message" => "Notification successfully sent.", 'user_count' => $users->count()]);
return response()->json(['message' => 'Notification successfully sent.', 'user_count' => $users->count()]);
}
/**
* Delete all notifications from an user
*
* @param int $userId
* @param int $userId
* @return JsonResponse
*/
public function delete(int $userId)
@ -120,15 +123,14 @@ class NotificationController extends Controller
$count = $user->notifications()->delete();
return response()->json(["message" => "All notifications have been successfully deleted.", "count" => $count]);
return response()->json(['message' => 'All notifications have been successfully deleted.', 'count' => $count]);
}
/**
* Delete a specific notification
*
* @param int $userId
* @param int $notificationId
* @param int $userId
* @param int $notificationId
* @return JsonResponse
*/
public function deleteOne(int $userId, $notificationid)
@ -136,13 +138,14 @@ class NotificationController extends Controller
$discordUser = DiscordUser::find($userId);
$user = $discordUser ? $discordUser->user : User::findOrFail($userId);
$notification = $user->notifications()->where("id", $notificationid)->get()->first();
$notification = $user->notifications()->where('id', $notificationid)->get()->first();
if (!$notification) {
return response()->json(["message" => "Notification not found."], 404);
if (! $notification) {
return response()->json(['message' => 'Notification not found.'], 404);
}
$notification->delete();
return response()->json($notification);
}
}

View file

@ -15,12 +15,13 @@ use Spatie\QueryBuilder\QueryBuilder;
class ServerController extends Controller
{
public const ALLOWED_INCLUDES = ['product', 'user'];
public const ALLOWED_FILTERS = ['name', 'suspended', 'identifier', 'pterodactyl_id', 'user_id', 'product_id'];
/**
* Display a listing of the resource.
*
* @param Request $request
* @param Request $request
* @return LengthAwarePaginator
*/
public function index(Request $request)
@ -35,8 +36,7 @@ class ServerController extends Controller
/**
* Display the specified resource.
*
* @param Server $server
*
* @param Server $server
* @return Server|Collection|Model
*/
public function show(Server $server)
@ -51,19 +51,20 @@ class ServerController extends Controller
/**
* Remove the specified resource from storage.
*
* @param Server $server
* @param Server $server
* @return Server
*/
public function destroy(Server $server)
{
$server->delete();
return $server;
}
/**
* suspend server
* @param Server $server
*
* @param Server $server
* @return Server|JsonResponse
*/
public function suspend(Server $server)
@ -77,10 +78,10 @@ class ServerController extends Controller
return $server->load('product');
}
/**
* unsuspend server
* @param Server $server
*
* @param Server $server
* @return Server|JsonResponse
*/
public function unSuspend(Server $server)

View file

@ -6,9 +6,9 @@ use App\Classes\Pterodactyl;
use App\Events\UserUpdateCreditsEvent;
use App\Http\Controllers\Controller;
use App\Models\DiscordUser;
use App\Models\Settings;
use App\Models\User;
use App\Notifications\ReferralNotification;
use App\Traits\Referral;
use Carbon\Carbon;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
@ -28,13 +28,16 @@ use Spatie\QueryBuilder\QueryBuilder;
class UserController extends Controller
{
use Referral;
const ALLOWED_INCLUDES = ['servers', 'notifications', 'payments', 'vouchers', 'discordUser'];
const ALLOWED_FILTERS = ['name', 'server_limit', 'email', 'pterodactyl_id', 'role', 'suspended'];
/**
* Display a listing of the resource.
*
* @param Request $request
* @param Request $request
* @return LengthAwarePaginator
*/
public function index(Request $request)
@ -46,12 +49,10 @@ class UserController extends Controller
return $query->paginate($request->input('per_page') ?? 50);
}
/**
* Display the specified resource.
*
* @param int $id
*
* @param int $id
* @return User|Builder|Collection|Model
*/
public function show(int $id)
@ -70,12 +71,11 @@ class UserController extends Controller
return $query->firstOrFail();
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return User
*/
public function update(Request $request, int $id)
@ -84,11 +84,11 @@ class UserController extends Controller
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
$request->validate([
"name" => "sometimes|string|min:4|max:30",
"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'])],
'name' => 'sometimes|string|min:4|max:30',
'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));
@ -96,16 +96,16 @@ class UserController extends Controller
//Update Users Password on Pterodactyl
//Username,Mail,First and Lastname are required aswell
$response = Pterodactyl::client()->patch('/application/users/' . $user->pterodactyl_id, [
"username" => $request->name,
"first_name" => $request->name,
"last_name" => $request->name,
"email" => $request->email,
'username' => $request->name,
'first_name' => $request->name,
'last_name' => $request->name,
'email' => $request->email,
]);
if ($response->failed()) {
throw ValidationException::withMessages([
'pterodactyl_error_message' => $response->toException()->getMessage(),
'pterodactyl_error_status' => $response->toException()->getCode()
'pterodactyl_error_status' => $response->toException()->getCode(),
]);
}
$user->update($request->all());
@ -116,9 +116,10 @@ class UserController extends Controller
/**
* increments the users credits or/and server_limit
*
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return User
*
* @throws ValidationException
*/
public function increment(Request $request, int $id)
@ -127,22 +128,26 @@ class UserController extends Controller
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
$request->validate([
"credits" => "sometimes|numeric|min:0|max:1000000",
"server_limit" => "sometimes|numeric|min:0|max:1000000",
'credits' => 'sometimes|numeric|min:0|max:1000000',
'server_limit' => 'sometimes|numeric|min:0|max:1000000',
]);
if ($request->credits) {
if ($user->credits + $request->credits >= 99999999) throw ValidationException::withMessages([
'credits' => "You can't add this amount of credits because you would exceed the credit limit"
]);
if ($user->credits + $request->credits >= 99999999) {
throw ValidationException::withMessages([
'credits' => "You can't add this amount of credits because you would exceed the credit limit",
]);
}
event(new UserUpdateCreditsEvent($user));
$user->increment('credits', $request->credits);
}
if ($request->server_limit) {
if ($user->server_limit + $request->server_limit >= 2147483647) throw ValidationException::withMessages([
'server_limit' => "You cannot add this amount of servers because it would exceed the server limit."
]);
if ($user->server_limit + $request->server_limit >= 2147483647) {
throw ValidationException::withMessages([
'server_limit' => 'You cannot add this amount of servers because it would exceed the server limit.',
]);
}
$user->increment('server_limit', $request->server_limit);
}
@ -152,9 +157,10 @@ class UserController extends Controller
/**
* decrements the users credits or/and server_limit
*
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return User
*
* @throws ValidationException
*/
public function decrement(Request $request, int $id)
@ -163,21 +169,25 @@ class UserController extends Controller
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
$request->validate([
"credits" => "sometimes|numeric|min:0|max:1000000",
"server_limit" => "sometimes|numeric|min:0|max:1000000",
'credits' => 'sometimes|numeric|min:0|max:1000000',
'server_limit' => 'sometimes|numeric|min:0|max:1000000',
]);
if ($request->credits) {
if ($user->credits - $request->credits < 0) throw ValidationException::withMessages([
'credits' => "You can't remove this amount of credits because you would exceed the minimum credit limit"
]);
if ($user->credits - $request->credits < 0) {
throw ValidationException::withMessages([
'credits' => "You can't remove this amount of credits because you would exceed the minimum credit limit",
]);
}
$user->decrement('credits', $request->credits);
}
if ($request->server_limit) {
if ($user->server_limit - $request->server_limit < 0) throw ValidationException::withMessages([
'server_limit' => "You cannot remove this amount of servers because it would exceed the minimum server."
]);
if ($user->server_limit - $request->server_limit < 0) {
throw ValidationException::withMessages([
'server_limit' => 'You cannot remove this amount of servers because it would exceed the minimum server.',
]);
}
$user->decrement('server_limit', $request->server_limit);
}
@ -187,9 +197,10 @@ class UserController extends Controller
/**
* Suspends the user
*
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return bool
*
* @throws ValidationException
*/
public function suspend(Request $request, int $id)
@ -210,9 +221,10 @@ class UserController extends Controller
/**
* Unsuspend the user
*
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return bool
*
* @throws ValidationException
*/
public function unsuspend(Request $request, int $id)
@ -222,7 +234,7 @@ class UserController extends Controller
if (!$user->isSuspended()) {
throw ValidationException::withMessages([
'error' => "You cannot unsuspend an User who is not suspended."
'error' => 'You cannot unsuspend an User who is not suspended.',
]);
}
@ -230,17 +242,7 @@ 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
*/
@ -251,13 +253,13 @@ class UserController extends Controller
'email' => ['required', 'string', 'email', 'max:64', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'max:191'],
]);
// Prevent the creation of new users via API if this is enabled.
if (!config('SETTINGS::SYSTEM:CREATION_OF_NEW_USERS', 'true')) {
throw ValidationException::withMessages([
'error' => "The creation of new users has been blocked by the system administrator."
'error' => 'The creation of new users has been blocked by the system administrator.',
]);
}
}
$user = User::create([
'name' => $request->input('name'),
@ -269,45 +271,44 @@ class UserController extends Controller
]);
$response = Pterodactyl::client()->post('/application/users', [
"external_id" => App::environment('local') ? Str::random(16) : (string)$user->id,
"username" => $user->name,
"email" => $user->email,
"first_name" => $user->name,
"last_name" => $user->name,
"password" => $request->input('password'),
"root_admin" => false,
"language" => "en"
'external_id' => App::environment('local') ? Str::random(16) : (string) $user->id,
'username' => $user->name,
'email' => $user->email,
'first_name' => $user->name,
'last_name' => $user->name,
'password' => $request->input('password'),
'root_admin' => false,
'language' => 'en',
]);
if ($response->failed()) {
$user->delete();
throw ValidationException::withMessages([
'pterodactyl_error_message' => $response->toException()->getMessage(),
'pterodactyl_error_status' => $response->toException()->getCode()
'pterodactyl_error_status' => $response->toException()->getCode(),
]);
}
$user->update([
'pterodactyl_id' => $response->json()['attributes']['id']
'pterodactyl_id' => $response->json()['attributes']['id'],
]);
//INCREMENT REFERRAL-USER CREDITS
if(!empty($request->input("referral_code"))){
$ref_code = $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(config("SETTINGS::REFERRAL:MODE") == "register" || config("SETTINGS::REFERRAL:MODE") == "both") {
$ref_user->increment('credits', config("SETTINGS::REFERRAL::REWARD"));
if ($ref_user = User::query()->where('referral_code', '=', $ref_code)->first()) {
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
DB::table('user_referrals')->insert([
'referral_id' => $ref_user->id,
'registered_user_id' => $user->id,
'created_at' => Carbon::now(),
'updated_at' => Carbon::now()
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
]);
}
}
$user->sendEmailVerificationNotification();
@ -317,7 +318,7 @@ class UserController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @param int $id
* @return Application|Response|ResponseFactory
*/
public function destroy(int $id)
@ -326,6 +327,7 @@ class UserController extends Controller
$user = $discordUser ? $discordUser->user : User::findOrFail($id);
$user->delete();
return response($user, 200);
}
}

View file

@ -15,6 +15,7 @@ use Spatie\QueryBuilder\QueryBuilder;
class VoucherController extends Controller
{
const ALLOWED_INCLUDES = ['users'];
const ALLOWED_FILTERS = ['code', 'memo', 'credits', 'uses'];
/**
@ -44,7 +45,7 @@ class VoucherController extends Controller
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @param Request $request
* @return Response
*/
public function store(Request $request)
@ -54,7 +55,7 @@ class VoucherController extends Controller
'code' => 'required|string|alpha_dash|max:36|min:4|unique:vouchers',
'uses' => 'required|numeric|max:2147483647|min:1',
'credits' => 'required|numeric|between:0,99999999',
'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years'
'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years',
]);
return Voucher::create($request->all());
@ -63,8 +64,7 @@ class VoucherController extends Controller
/**
* Display the specified resource.
*
* @param int $id
*
* @param int $id
* @return Voucher|Collection|Model
*/
public function show(int $id)
@ -79,7 +79,7 @@ class VoucherController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @param int $id
* @return Response
*/
public function edit($id)
@ -90,8 +90,8 @@ class VoucherController extends Controller
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return Response
*/
public function update(Request $request, int $id)
@ -103,7 +103,7 @@ class VoucherController extends Controller
'code' => "required|string|alpha_dash|max:36|min:4|unique:vouchers,code,{$voucher->id}",
'uses' => 'required|numeric|max:2147483647|min:1',
'credits' => 'required|numeric|between:0,99999999',
'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years'
'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years',
]);
$voucher->update($request->all());
@ -114,21 +114,22 @@ class VoucherController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @param int $id
* @return Response
*/
public function destroy(int $id)
{
$voucher = Voucher::findOrFail($id);
$voucher->delete();
return $voucher;
}
/**
* get linked users
* @param Request $request
* @param Voucher $voucher
*
* @param Request $request
* @param Voucher $voucher
* @return LengthAwarePaginator
*/
public function users(Request $request, Voucher $voucher)
@ -138,7 +139,7 @@ class VoucherController extends Controller
'nullable',
'string',
Rule::in(['discorduser']),
]
],
]);
if ($request->input('include') == 'discorduser') {

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
class ForgotPasswordController extends Controller
{
@ -19,4 +20,27 @@ class ForgotPasswordController extends Controller
*/
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
protected function validateEmail(Request $request)
{
$this->validate($request, [
'email' => ['required', 'string', 'email', 'max:255'],
]);
if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
$this->validate($request, [
'g-recaptcha-response' => 'required|recaptcha',
]);
}
}
}

View file

@ -3,12 +3,10 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class LoginController extends Controller
{
@ -42,20 +40,30 @@ class LoginController extends Controller
$this->middleware('guest')->except('logout');
}
/**
* Get the login username to be used by the controller.
*
* @return string
*/
public function username()
{
$login = request()->input('email');
$field = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'name';
request()->merge([$field => $login]);
return $field;
}
public function login(Request $request)
{
$validationRules = [
$this->username() => 'required|string',
'password' => 'required|string',
$this->username() => 'required|string',
'password' => 'required|string',
];
if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
$validationRules['g-recaptcha-response'] = ['required', 'recaptcha'];
}
$request->validate($validationRules);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
@ -72,6 +80,7 @@ class LoginController extends Controller
$user = Auth::user();
$user->last_seen = now();
$user->save();
return $this->sendLoginResponse($request);
}

View file

@ -4,14 +4,13 @@ namespace App\Http\Controllers\Auth;
use App\Classes\Pterodactyl;
use App\Http\Controllers\Controller;
use App\Models\Settings;
use App\Models\User;
use App\Notifications\ReferralNotification;
use App\Providers\RouteServiceProvider;
use App\Traits\Referral;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
@ -32,7 +31,7 @@ class RegisterController extends Controller
|
*/
use RegistersUsers;
use RegistersUsers, Referral;
/**
* Where to redirect users after registration.
@ -54,28 +53,34 @@ class RegisterController extends Controller
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
$validationRules = [
'name' => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
'email' => ['required', 'string', 'email', 'max:64', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'name' => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
'email' => ['required', 'string', 'email', 'max:64', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
];
if (config('SETTINGS::RECAPTCHA:ENABLED') == 'true') {
$validationRules['g-recaptcha-response'] = ['required', 'recaptcha'];
}
if (config('SETTINGS::SYSTEM:SHOW_TOS') == 'true') {
$validationRules['terms'] = ['required'];
}
if (config('SETTINGS::SYSTEM:REGISTER_IP_CHECK', 'true') == 'true') {
//check if ip has already made an account
$data['ip'] = session()->get('ip') ?? request()->ip();
if (User::where('ip', '=', request()->ip())->exists()) session()->put('ip', request()->ip());
$validationRules['ip'] = ['unique:users'];
if (User::where('ip', '=', request()->ip())->exists()) {
session()->put('ip', request()->ip());
}
$validationRules['ip'] = ['unique:users'];
return Validator::make($data, $validationRules, [
'ip.unique' => "You have already made an account! Please contact support if you think this is incorrect."
'ip.unique' => 'You have already made an account! Please contact support if you think this is incorrect.',
]);
}
@ -83,82 +88,70 @@ class RegisterController extends Controller
return Validator::make($data, $validationRules);
}
/**
* 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;
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @param array $data
* @return User
*/
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'credits' => config('SETTINGS::USER:INITIAL_CREDITS', 150),
'name' => $data['name'],
'email' => $data['email'],
'credits' => config('SETTINGS::USER:INITIAL_CREDITS', 150),
'server_limit' => config('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1),
'password' => Hash::make($data['password']),
'password' => Hash::make($data['password']),
'referral_code' => $this->createReferralCode(),
]);
$response = Pterodactyl::client()->post('/application/users', [
"external_id" => App::environment('local') ? Str::random(16) : (string)$user->id,
"username" => $user->name,
"email" => $user->email,
"first_name" => $user->name,
"last_name" => $user->name,
"password" => $data['password'],
"root_admin" => false,
"language" => "en"
'external_id' => App::environment('local') ? Str::random(16) : (string) $user->id,
'username' => $user->name,
'email' => $user->email,
'first_name' => $user->name,
'last_name' => $user->name,
'password' => $data['password'],
'root_admin' => false,
'language' => 'en',
]);
if ($response->failed()) {
$user->delete();
Log::error('Pterodactyl Registration Error: ' . $response->json()['errors'][0]['detail']);
throw ValidationException::withMessages([
'ptero_registration_error' => [__('Account already exists on Pterodactyl. Please contact the Support!')],
]);
}
$user->update([
'pterodactyl_id' => $response->json()['attributes']['id']
'pterodactyl_id' => $response->json()['attributes']['id'],
]);
//INCREMENT REFERRAL-USER CREDITS
if(!empty($data['referral_code'])){
if (!empty($data['referral_code'])) {
$ref_code = $data['referral_code'];
$new_user = $user->id;
if($ref_user = User::query()->where('referral_code', '=', $ref_code)->first()) {
if(config("SETTINGS::REFERRAL:MODE") == "sign-up" || config("SETTINGS::REFERRAL:MODE") == "both") {
$ref_user->increment('credits', config("SETTINGS::REFERRAL::REWARD"));
if ($ref_user = User::query()->where('referral_code', '=', $ref_code)->first()) {
if (config('SETTINGS::REFERRAL:MODE') == 'sign-up' || config('SETTINGS::REFERRAL:MODE') == 'both') {
$ref_user->increment('credits', config('SETTINGS::REFERRAL::REWARD'));
$ref_user->notify(new ReferralNotification($ref_user->id, $new_user));
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained '. config("SETTINGS::REFERRAL::REWARD").' '.config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME").' for sign-up-referral of '.$user->name.' (ID:'.$user->id.')');
->log('gained ' . config('SETTINGS::REFERRAL::REWARD') . ' ' . config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME') . ' for sign-up-referral of ' . $user->name . ' (ID:' . $user->id . ')');
}
//INSERT INTO USER_REFERRALS TABLE
DB::table('user_referrals')->insert([
'referral_id' => $ref_user->id,
'registered_user_id' => $user->id,
'created_at' => Carbon::now(),
'updated_at' => Carbon::now()
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
]);
}
}
return $user;

View file

@ -4,10 +4,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\DiscordUser;
use App\Models\Settings;
use App\Models\User;
use App\Models\Voucher;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Laravel\Socialite\Facades\Socialite;
@ -16,7 +13,7 @@ class SocialiteController extends Controller
{
public function redirect()
{
$scopes = !empty(config("SETTINGS::DISCORD:BOT_TOKEN")) && !empty(config("SETTINGS::DISCORD:GUILD_ID")) ? ['guilds.join'] : [];
$scopes = ! empty(config('SETTINGS::DISCORD:BOT_TOKEN')) && ! empty(config('SETTINGS::DISCORD:GUILD_ID')) ? ['guilds.join'] : [];
return Socialite::driver('discord')
->scopes($scopes)
@ -32,40 +29,39 @@ class SocialiteController extends Controller
/** @var User $user */
$user = Auth::user();
$discord = Socialite::driver('discord')->user();
$botToken = config("SETTINGS::DISCORD:BOT_TOKEN");
$guildId = config("SETTINGS::DISCORD:GUILD_ID");
$roleId = config("SETTINGS::DISCORD:ROLE_ID");
$botToken = config('SETTINGS::DISCORD:BOT_TOKEN');
$guildId = config('SETTINGS::DISCORD:GUILD_ID');
$roleId = config('SETTINGS::DISCORD:ROLE_ID');
//save / update discord_users
//save / update discord_users
//check if discord account is already linked to an cpgg account
if (is_null($user->discordUser)) {
$discordLinked = DiscordUser::where('id', '=', $discord->id)->first();
if ($discordLinked !== null) {
return redirect()->route('profile.index')->with(
//check if discord account is already linked to an cpgg account
if (is_null($user->discordUser)) {
$discordLinked = DiscordUser::where('id', '=', $discord->id)->first();
if ($discordLinked !== null) {
return redirect()->route('profile.index')->with(
'error',
'Discord account already linked!'
);
}
//create discord user in db
DiscordUser::create(array_merge($discord->user, ['user_id' => Auth::user()->id]));
//update user
Auth::user()->increment('credits', config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD'));
Auth::user()->increment('server_limit', config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD'));
Auth::user()->update(['discord_verified_at' => now()]);
} else {
$user->discordUser->update($discord->user);
}
//create discord user in db
DiscordUser::create(array_merge($discord->user, ['user_id' => Auth::user()->id]));
//update user
Auth::user()->increment('credits', config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD'));
Auth::user()->increment('server_limit', config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_DISCORD'));
Auth::user()->update(['discord_verified_at' => now()]);
} else {
$user->discordUser->update($discord->user);
}
//force user into discord server
//TODO Add event on failure, to notify ppl involved
if (!empty($guildId) && !empty($botToken)) {
if (! empty($guildId) && ! empty($botToken)) {
$response = Http::withHeaders(
[
'Authorization' => 'Bot ' . $botToken,
'Authorization' => 'Bot '.$botToken,
'Content-Type' => 'application/json',
]
)->put(
@ -74,10 +70,10 @@ class SocialiteController extends Controller
);
//give user a role in the discord server
if (!empty($roleId)) {
if (! empty($roleId)) {
$response = Http::withHeaders(
[
'Authorization' => 'Bot ' . $botToken,
'Authorization' => 'Bot '.$botToken,
'Content-Type' => 'application/json',
]
)->put(

View file

@ -2,39 +2,46 @@
namespace App\Http\Controllers;
use App\Models\PartnerDiscount;
use App\Models\UsefulLink;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
class HomeController extends Controller
{
const TIME_LEFT_BG_SUCCESS = "bg-success";
const TIME_LEFT_BG_WARNING = "bg-warning";
const TIME_LEFT_BG_DANGER = "bg-danger";
const TIME_LEFT_BG_SUCCESS = 'bg-success';
const TIME_LEFT_BG_WARNING = 'bg-warning';
const TIME_LEFT_BG_DANGER = 'bg-danger';
public function __construct()
{
$this->middleware('auth');
}
public function callHome(){
if(Storage::exists("callHome")){return;}
Http::asForm()->post('https://market.controlpanel.gg/callhome.php', [
'id' => Hash::make(URL::current())
/*
* TODO: This is commented due to the fact the market is a bad dependency, will be changed later.
public function callHome()
{
if (Storage::exists('callHome')) {
return;
}
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.');
}
}*/
/**
* @description Get the Background Color for the Days-Left-Box in HomeView
*
* @param float $daysLeft
*
* @param float $daysLeft
* @return string
*/
public function getTimeLeftBoxBackground(float $daysLeft): string
@ -45,36 +52,40 @@ class HomeController extends Controller
if ($daysLeft <= 7) {
return $this::TIME_LEFT_BG_DANGER;
}
return $this::TIME_LEFT_BG_WARNING;
}
/**
* @description Set "hours", "days" or nothing behind the remaining time
*
* @param float $daysLeft
* @param float $hoursLeft
*
* @param float $daysLeft
* @param float $hoursLeft
* @return string|void
*/
public function getTimeLeftBoxUnit(float $daysLeft, float $hoursLeft)
{
if ($daysLeft > 1) return __('days');
return $hoursLeft < 1 ? null : __("hours");
if ($daysLeft > 1) {
return __('days');
}
return $hoursLeft < 1 ? null : __('hours');
}
/**
* @description Get the Text for the Days-Left-Box in HomeView
*
* @param float $daysLeft
* @param float $hoursLeft
*
* @param float $daysLeft
* @param float $hoursLeft
* @return string
*/
public function getTimeLeftBoxText(float $daysLeft, float $hoursLeft)
{
if ($daysLeft > 1) return strval(number_format($daysLeft, 0));
return ($hoursLeft < 1 ? __("You ran out of Credits") : strval($hoursLeft));
if ($daysLeft > 1) {
return strval(number_format($daysLeft, 0));
}
return $hoursLeft < 1 ? __('You ran out of Credits') : strval($hoursLeft);
}
/** Show the application dashboard. */
@ -82,9 +93,9 @@ class HomeController extends Controller
{
$usage = Auth::user()->creditUsage();
$credits = Auth::user()->Credits();
$bg = "";
$boxText = "";
$unit = "";
$bg = '';
$boxText = '';
$unit = '';
/** Build our Time-Left-Box */
if ($credits > 0.01 and $usage > 0) {
@ -93,19 +104,22 @@ class HomeController extends Controller
$bg = $this->getTimeLeftBoxBackground($daysLeft);
$boxText = $this->getTimeLeftBoxText($daysLeft, $hoursLeft);
$unit = $daysLeft < 1 ? ($hoursLeft < 1 ? null : __("hours")) : __("days");
$unit = $daysLeft < 1 ? ($hoursLeft < 1 ? null : __('hours')) : __('days');
}
$this->callhome();
//$this->callhome(); TODO: Same as the function
// RETURN ALL VALUES
return view('home')->with([
'usage' => $usage,
'credits' => $credits,
'useful_links' => UsefulLink::all()->sortBy('id'),
'useful_links_dashboard' => UsefulLink::where("position","like","%dashboard%")->get()->sortby("id"),
'bg' => $bg,
'boxText' => $boxText,
'unit' => $unit
'unit' => $unit,
'numberOfReferrals' => DB::table('user_referrals')->where('referral_id', '=', Auth::user()->id)->count(),
'partnerDiscount' => PartnerDiscount::where('user_id', Auth::user()->id)->first(),
'myDiscount' => PartnerDiscount::getDiscount(),
]);
}
}

View file

@ -0,0 +1,117 @@
<?php
namespace App\Http\Controllers\Moderation;
use App\Http\Controllers\Controller;
use App\Models\Ticket;
use App\Models\TicketCategory;
use Illuminate\Http\Request;
class TicketCategoryController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$categories = TicketCategory::all();
return view('moderator.ticket.category')->with("categories",$categories);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:191',
]);
TicketCategory::create($request->all());
return redirect(route("moderator.ticket.category.index"))->with("success",__("Category created"));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function update(Request $request)
{
$request->validate([
'category' => 'required|int',
'name' => 'required|string|max:191',
]);
$category = TicketCategory::where("id",$request->category)->firstOrFail();
$category->name = $request->name;
$category->save();
return redirect()->back()->with("success",__("Category name updated"));
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$category = TicketCategory::where("id",$id)->firstOrFail();
if($category->id == 5 ){ //cannot delete "other" category
return back()->with("error","You cannot delete that category");
}
$tickets = Ticket::where("ticketcategory_id",$category->id)->get();
foreach($tickets as $ticket){
$ticket->ticketcategory_id = "5";
$ticket->save();
}
$category->delete();
return redirect()
->route('moderator.ticket.category.index')
->with('success', __('Category removed'));
}
public function datatable()
{
$query = TicketCategory::withCount("tickets");
return datatables($query)
->addColumn('name', function ( TicketCategory $category) {
return $category->name;
})
->editColumn('tickets', function ( TicketCategory $category) {
return $category->tickets_count;
})
->addColumn('actions', function (TicketCategory $category) {
return '
<form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('moderator.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>
</form>
';
})
->editColumn('created_at', function (TicketCategory $category) {
return $category->created_at ? $category->created_at->diffForHumans() : '';
})
->rawColumns(['actions'])
->make();
}
}

View file

@ -2,63 +2,102 @@
namespace App\Http\Controllers\Moderation;
use App\Models\User;
use App\Models\Ticket;
use App\Http\Controllers\Controller;
use App\Models\Server;
use App\Models\Ticket;
use App\Models\TicketBlacklist;
use App\Models\TicketCategory;
use App\Models\TicketComment;
use App\Models\TicketBlacklist;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use App\Models\User;
use App\Notifications\Ticket\User\ReplyNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Notifications\Ticket\User\ReplyNotification;
class TicketsController extends Controller
{
public function index() {
$tickets = Ticket::orderBy('id','desc')->paginate(10);
public function index()
{
$tickets = Ticket::orderBy('id', 'desc')->paginate(10);
$ticketcategories = TicketCategory::all();
return view("moderator.ticket.index", compact("tickets", "ticketcategories"));
return view('moderator.ticket.index', compact('tickets', 'ticketcategories'));
}
public function show($ticket_id) {
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
public function show($ticket_id)
{
try {
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
} catch (Exception $e)
{
return redirect()->back()->with('warning', __('Ticket not found on the server. It potentially got deleted earlier'));
}
$ticketcomments = $ticket->ticketcomments;
$ticketcategory = $ticket->ticketcategory;
$server = Server::where('id', $ticket->server)->first();
return view("moderator.ticket.show", compact("ticket", "ticketcategory", "ticketcomments", "server"));
return view('moderator.ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server'));
}
public function close($ticket_id) {
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
$ticket->status = "Closed";
public function changeStatus($ticket_id)
{
try {
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
} catch(Exception $e)
{
return redirect()->back()->with('warning', __('Ticket not found on the server. It potentially got deleted earlier'));
}
if($ticket->status == "Closed"){
$ticket->status = "Reopened";
$ticket->save();
return redirect()->back()->with('success', __('A ticket has been reopened, ID: #') . $ticket->ticket_id);
}
$ticket->status = 'Closed';
$ticket->save();
$ticketOwner = $ticket->user;
return redirect()->back()->with('success', __('A ticket has been closed, ID: #') . $ticket->ticket_id);
return redirect()->back()->with('success', __('A ticket has been closed, ID: #').$ticket->ticket_id);
}
public function delete($ticket_id){
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
TicketComment::where("ticket_id", $ticket->id)->delete();
public function delete($ticket_id)
{
try {
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
} catch (Exception $e)
{
return redirect()->back()->with('warning', __('Ticket not found on the server. It potentially got deleted earlier'));
}
TicketComment::where('ticket_id', $ticket->id)->delete();
$ticket->delete();
return redirect()->back()->with('success', __('A ticket has been deleted, ID: #') . $ticket_id);
return redirect()->back()->with('success', __('A ticket has been deleted, ID: #').$ticket_id);
}
public function reply(Request $request) {
$this->validate($request, array("ticketcomment" => "required"));
$ticket = Ticket::where('id', $request->input("ticket_id"))->firstOrFail();
$ticket->status = "Answered";
public function reply(Request $request)
{
$this->validate($request, ['ticketcomment' => 'required']);
try {
$ticket = Ticket::where('id', $request->input('ticket_id'))->firstOrFail();
} catch (Exception $e){
return redirect()->back()->with('warning', __('Ticket not found on the server. It potentially got deleted earlier'));
}
$ticket->status = 'Answered';
$ticket->update();
TicketComment::create(array(
"ticket_id" => $request->input("ticket_id"),
"user_id" => Auth::user()->id,
"ticketcomment" => $request->input("ticketcomment"),
));
TicketComment::create([
'ticket_id' => $request->input('ticket_id'),
'user_id' => Auth::user()->id,
'ticketcomment' => $request->input('ticketcomment'),
]);
try {
$user = User::where('id', $ticket->user_id)->firstOrFail();
$newmessage = $request->input("ticketcomment");
} catch(Exception $e)
{
return redirect()->back()->with('warning', __('User not found on the server. Check on the admin database or try again later.'));
}
$newmessage = $request->input('ticketcomment');
$user->notify(new ReplyNotification($ticket, $user, $newmessage));
return redirect()->back()->with('success', __('Your comment has been submitted'));
}
@ -71,28 +110,33 @@ class TicketsController extends Controller
return $tickets->ticketcategory->name;
})
->editColumn('title', function (Ticket $tickets) {
return '<a class="text-info" href="' . route('moderator.ticket.show', ['ticket_id' => $tickets->ticket_id]) . '">' . "#" . $tickets->ticket_id . " - " . $tickets->title . '</a>';
return '<a class="text-info" href="'.route('moderator.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>';
return '<a href="'.route('admin.users.show', $tickets->user->id).'">'.$tickets->user->name.'</a>';
})
->addColumn('actions', function (Ticket $tickets) {
$statusButtonColor = ($tickets->status == "Closed") ? 'btn-success' : 'btn-warning';
$statusButtonIcon = ($tickets->status == "Closed") ? 'fa-redo' : 'fa-times';
$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.close', ['ticket_id' => $tickets->ticket_id ]) . '">
' . csrf_field() . '
' . method_field("POST") . '
<button data-content="'.__("Close").'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm text-white btn-warning mr-1"><i class="fas fa-times"></i></button>
<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]).'">
'.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>
</form>
<form class="d-inline" method="post" action="' . route('moderator.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>
<form class="d-inline" method="post" action="'.route('moderator.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>
</form>
';
})
->editColumn('status', function (Ticket $tickets) {
switch ($tickets->status) {
case 'Reopened':
case 'Open':
$badgeColor = 'badge-success';
break;
@ -107,93 +151,112 @@ class TicketsController extends Controller
break;
}
return '<span class="badge ' . $badgeColor . '">' . $tickets->status . '</span>';
return '<span class="badge '.$badgeColor.'">'.$tickets->status.'</span>';
})
->editColumn('priority', function (Ticket $tickets) {
return __($tickets->priority);
})
->editColumn('updated_at', function (Ticket $tickets) {
return $tickets->updated_at ? $tickets->updated_at->diffForHumans() : '';
return ['display' => $tickets->updated_at ? $tickets->updated_at->diffForHumans() : '',
'raw' => $tickets->updated_at ? strtotime($tickets->updated_at) : ''];
})
->rawColumns(['category', 'title', 'user_id', 'status', 'updated_at', 'actions'])
->rawColumns(['category', 'title', 'user_id', 'status', 'priority', 'updated_at', 'actions'])
->make(true);
}
public function blacklist() {
return view("moderator.ticket.blacklist");
public function blacklist()
{
return view('moderator.ticket.blacklist');
}
public function blacklistAdd(Request $request) {
$user = User::where('id', $request->user_id)->first();
public function blacklistAdd(Request $request)
{
try {
$user = User::where('id', $request->user_id)->firstOrFail();
$check = TicketBlacklist::where('user_id', $user->id)->first();
if($check){
}
catch (Exception $e){
return redirect()->back()->with('warning', __('User not found on the server. Check the admin database or try again later.'));
}
if ($check) {
$check->reason = $request->reason;
$check->status = "True";
$check->status = 'True';
$check->save();
return redirect()->back()->with('info', __('Target User already in blacklist. Reason updated'));
}
TicketBlacklist::create(array(
"user_id" => $user->id,
"status" => "True",
"reason" => $request->reason,
));
return redirect()->back()->with('success', __('Successfully add User to blacklist, User name: ' . $user->name));
TicketBlacklist::create([
'user_id' => $user->id,
'status' => 'True',
'reason' => $request->reason,
]);
return redirect()->back()->with('success', __('Successfully add User to blacklist, User name: '.$user->name));
}
public function blacklistDelete($id) {
public function blacklistDelete($id)
{
$blacklist = TicketBlacklist::where('id', $id)->first();
$blacklist->delete();
return redirect()->back()->with('success', __('Successfully remove User from blacklist, User name: ' . $blacklist->user->name));
return redirect()->back()->with('success', __('Successfully remove User from blacklist, User name: '.$blacklist->user->name));
}
public function blacklistChange($id) {
$blacklist = TicketBlacklist::where('id', $id)->first();
if($blacklist->status == "True")
{
$blacklist->status = "False";
public function blacklistChange($id)
{
try {
$blacklist = TicketBlacklist::where('id', $id)->first();
}
catch (Exception $e){
return redirect()->back()->with('warning', __('User not found on the server. Check the admin database or try again later.'));
}
if ($blacklist->status == 'True') {
$blacklist->status = 'False';
} else {
$blacklist->status = "True";
$blacklist->status = 'True';
}
$blacklist->update();
return redirect()->back()->with('success', __('Successfully change status blacklist from, User name: ' . $blacklist->user->name));
return redirect()->back()->with('success', __('Successfully change status blacklist from, User name: '.$blacklist->user->name));
}
public function dataTableBlacklist()
{
$query = TicketBlacklist::with(['user']);
$query->select('ticket_blacklists.*');
return datatables($query)
->editColumn('user', function (TicketBlacklist $blacklist) {
return '<a href="' . route('admin.users.show', $blacklist->user->id) . '">' . $blacklist->user->name . '</a>';
return '<a href="'.route('admin.users.show', $blacklist->user->id).'">'.$blacklist->user->name.'</a>';
})
->editColumn('status', function (TicketBlacklist $blacklist) {
switch ($blacklist->status) {
case 'True':
$text = "Blocked";
$text = 'Blocked';
$badgeColor = 'badge-danger';
break;
default:
$text = "Unblocked";
$text = 'Unblocked';
$badgeColor = 'badge-success';
break;
}
return '<span class="badge ' . $badgeColor . '">' . $text . '</span>';
return '<span class="badge '.$badgeColor.'">'.$text.'</span>';
})
->editColumn('reason', function (TicketBlacklist $blacklist) {
return $blacklist->reason;
})
->addColumn('actions', function (TicketBlacklist $blacklist) {
return '
<form class="d-inline" method="post" action="' . route('moderator.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>
<form class="d-inline" method="post" action="'.route('moderator.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>
</form>
<form class="d-inline" method="post" action="' . route('moderator.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>
<form class="d-inline" method="post" action="'.route('moderator.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>
</form>
';
})
@ -203,5 +266,4 @@ class TicketsController extends Controller
->rawColumns(['user', 'status', 'reason', 'created_at', 'actions'])
->make(true);
}
}

View file

@ -10,8 +10,9 @@ class NotificationController extends Controller
public function index()
{
$notifications = Auth::user()->notifications()->paginate();
return view('notifications.index')->with([
'notifications' => $notifications
'notifications' => $notifications,
]);
}
@ -21,17 +22,19 @@ class NotificationController extends Controller
$notification = Auth::user()->notifications()->findOrFail($id);
$notification->markAsRead();
return view('notifications.show')->with([
'notification' => $notification
'notification' => $notification,
]);
}
public function readAll(){
public function readAll()
{
$notifications = Auth::user()->notifications()->get();
foreach($notifications as $notification){
foreach ($notifications as $notification) {
$notification->markAsRead();
}
return redirect()->back();
return redirect()->back();
}
}

View file

@ -16,15 +16,18 @@ class ProductController extends Controller
{
/**
* @description get product locations based on selected egg
* @param Request $request
* @param Egg $egg
*
* @param Request $request
* @param Egg $egg
* @return Collection|JsonResponse
*/
public function getNodesBasedOnEgg(Request $request, Egg $egg)
{
if (is_null($egg->id)) return response()->json('Egg ID is required', '400');
if (is_null($egg->id)) {
return response()->json('Egg ID is required', '400');
}
#get products that include this egg
//get products that include this egg
$products = Product::query()
->with('nodes')
->where('disabled', '=', false)
@ -34,31 +37,33 @@ class ProductController extends Controller
$nodes = collect();
#filter unique nodes
//filter unique nodes
$products->each(function (Product $product) use ($nodes) {
$product->nodes->each(function (Node $node) use ($nodes) {
if (!$nodes->contains('id', $node->id) && !$node->disabled) {
if (! $nodes->contains('id', $node->id) && ! $node->disabled) {
$nodes->add($node);
}
});
});
return $nodes;
}
/**
* @description get product locations based on selected egg
* @param Request $request
* @param Egg $egg
*
* @param Request $request
* @param Egg $egg
* @return Collection|JsonResponse
*/
public function getLocationsBasedOnEgg(Request $request, Egg $egg)
{
$nodes = $this->getNodesBasedOnEgg($request, $egg);
foreach($nodes as $key => $node){
foreach ($nodes as $key => $node) {
$pteroNode = Pterodactyl::getNode($node->id);
if($pteroNode['allocated_resources']['memory']>=($pteroNode['memory']*($pteroNode['memory_overallocate']+100)/100)||$pteroNode['allocated_resources']['disk']>=($pteroNode['disk']*($pteroNode['disk_overallocate']+100)/100)) $nodes->forget($key);
if ($pteroNode['allocated_resources']['memory'] >= ($pteroNode['memory'] * ($pteroNode['memory_overallocate'] + 100) / 100) || $pteroNode['allocated_resources']['disk'] >= ($pteroNode['disk'] * ($pteroNode['disk_overallocate'] + 100) / 100)) {
$nodes->forget($key);
}
}
$locations = collect();
@ -67,7 +72,7 @@ class ProductController extends Controller
/** @var Location $location */
$location = $node->location;
if (!$locations->contains('id', $location->id)) {
if (! $locations->contains('id', $location->id)) {
$nodeIds = $nodes->map(function ($node) {
return $node->id;
});
@ -84,13 +89,15 @@ class ProductController extends Controller
}
/**
* @param Node $node
* @param Egg $egg
* @param Node $node
* @param Egg $egg
* @return Collection|JsonResponse
*/
public function getProductsBasedOnNode(Egg $egg, Node $node)
{
if (is_null($egg->id) || is_null($node->id)) return response()->json('node and egg id is required', '400');
if (is_null($egg->id) || is_null($node->id)) {
return response()->json('node and egg id is required', '400');
}
$products = Product::query()
->where('disabled', '=', false)
@ -103,8 +110,10 @@ class ProductController extends Controller
->get();
$pteroNode = Pterodactyl::getNode($node->id);
foreach($products as $key => $product){
if($product->memory>($pteroNode['memory']*($pteroNode['memory_overallocate']+100)/100)-$pteroNode['allocated_resources']['memory']||$product->disk>($pteroNode['disk']*($pteroNode['disk_overallocate']+100)/100)-$pteroNode['allocated_resources']['disk']) $product->doesNotFit = true;
foreach ($products as $key => $product) {
if ($product->memory > ($pteroNode['memory'] * ($pteroNode['memory_overallocate'] + 100) / 100) - $pteroNode['allocated_resources']['memory'] || $product->disk > ($pteroNode['disk'] * ($pteroNode['disk_overallocate'] + 100) / 100) - $pteroNode['allocated_resources']['disk']) {
$product->doesNotFit = true;
}
}
return $products;

View file

@ -2,7 +2,6 @@
namespace App\Http\Controllers;
use App\Classes\Pterodactyl;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
@ -30,6 +29,7 @@ class ProfileController extends Controller
$badgeColor = 'badge-secondary';
break;
}
return view('profile.index')->with([
'user' => Auth::user(),
'credits_reward_after_verify_discord' => config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_DISCORD'),
@ -39,68 +39,81 @@ class ProfileController extends Controller
]);
}
public function selfDestroyUser()
{
$user = Auth::user();
if ($user->role == "admin") return back()->with("error", "You cannot delete yourself as an admin!");
$user->delete();
return redirect('/login')->with('success', __('Account permanently deleted!'));
}
/** Update the specified resource in storage.
* @param Request $request
* @param int $id
* @param Request $request
* @param int $id
* @return RedirectResponse
*/
public function update(Request $request, int $id)
{
//prevent other users from editing a user
if ($id != Auth::user()->id) dd(401);
if ($id != Auth::user()->id) {
dd(401);
}
$user = User::findOrFail($id);
//update password if necessary
if (!is_null($request->input('new_password'))) {
if (! is_null($request->input('new_password'))) {
//validate password request
$request->validate([
'current_password' => [
'required',
function ($attribute, $value, $fail) use ($user) {
if (!Hash::check($value, $user->password)) {
$fail('The ' . $attribute . ' is invalid.');
if (! Hash::check($value, $user->password)) {
$fail('The '.$attribute.' is invalid.');
}
},
],
'new_password' => 'required|string|min:8',
'new_password_confirmation' => 'required|same:new_password'
'new_password_confirmation' => 'required|same:new_password',
]);
//Update Users Password on Pterodactyl
//Username,Mail,First and Lastname are required aswell
$response = Pterodactyl::client()->patch('/application/users/'.$user->pterodactyl_id, [
"password" => $request->input('new_password'),
"username" => $request->input('name'),
"first_name" => $request->input('name'),
"last_name" => $request->input('name'),
"email" => $request->input('email'),
'password' => $request->input('new_password'),
'username' => $request->input('name'),
'first_name' => $request->input('name'),
'last_name' => $request->input('name'),
'email' => $request->input('email'),
]);
if ($response->failed()) {
throw ValidationException::withMessages([
'pterodactyl_error_message' => $response->toException()->getMessage(),
'pterodactyl_error_status' => $response->toException()->getCode()
'pterodactyl_error_status' => $response->toException()->getCode(),
]);
}
//update password
$user->update([
'password' => Hash::make($request->input('new_password')),
]);
}
//validate request
$request->validate([
'name' => 'required|min:4|max:30|alpha_num|unique:users,name,' . $id . ',id',
'email' => 'required|email|max:64|unique:users,email,' . $id . ',id',
'avatar' => 'nullable'
'name' => 'required|min:4|max:30|alpha_num|unique:users,name,'.$id.',id',
'email' => 'required|email|max:64|unique:users,email,'.$id.',id',
'avatar' => 'nullable',
]);
//update avatar
if (!is_null($request->input('avatar'))) {
if (! is_null($request->input('avatar'))) {
$avatar = json_decode($request->input('avatar'));
if ($avatar->input->size > 3000000) abort(500);
if ($avatar->input->size > 3000000) {
abort(500);
}
$user->update([
'avatar' => $avatar->output->image,
@ -113,16 +126,16 @@ class ProfileController extends Controller
//update name and email on Pterodactyl
$response = Pterodactyl::client()->patch('/application/users/'.$user->pterodactyl_id, [
"username" => $request->input('name'),
"first_name" => $request->input('name'),
"last_name" => $request->input('name'),
"email" => $request->input('email'),
'username' => $request->input('name'),
'first_name' => $request->input('name'),
'last_name' => $request->input('name'),
'email' => $request->input('email'),
]);
if ($response->failed()) {
throw ValidationException::withMessages([
'pterodactyl_error_message' => $response->toException()->getMessage(),
'pterodactyl_error_status' => $response->toException()->getCode()
'pterodactyl_error_status' => $response->toException()->getCode(),
]);
}
@ -135,7 +148,7 @@ class ProfileController extends Controller
if ($request->input('email') != Auth::user()->email) {
$user->reVerifyEmail();
$user->sendEmailVerificationNotification();
};
}
return redirect()->route('profile.index')->with('success', __('Profile updated'));
}

View file

@ -9,7 +9,6 @@ use App\Models\Nest;
use App\Models\Node;
use App\Models\Product;
use App\Models\Server;
use App\Models\Settings;
use App\Notifications\ServerCreationError;
use Exception;
use Illuminate\Database\Eloquent\Builder;
@ -31,7 +30,9 @@ class ServerController extends Controller
//Get server infos from ptero
$serverAttributes = Pterodactyl::getServerAttributes($server->pterodactyl_id, true);
if(!$serverAttributes) continue;
if (! $serverAttributes) {
continue;
}
$serverRelationships = $serverAttributes['relationships'];
$serverLocationAttributes = $serverRelationships['location']['attributes'];
@ -47,7 +48,7 @@ class ServerController extends Controller
//Check if a server got renamed on Pterodactyl
$savedServer = Server::query()->where('id', $server->id)->first();
if($savedServer->name != $serverAttributes['name']){
if ($savedServer->name != $serverAttributes['name']) {
$savedServer->name = $serverAttributes['name'];
$server->name = $serverAttributes['name'];
$savedServer->save();
@ -59,14 +60,16 @@ class ServerController extends Controller
}
return view('servers.index')->with([
'servers' => $servers
'servers' => $servers,
]);
}
/** Show the form for creating a new resource. */
public function create()
{
if (!is_null($this->validateConfigurationRules())) return $this->validateConfigurationRules();
if (! is_null($this->validateConfigurationRules())) {
return $this->validateConfigurationRules();
}
$productCount = Product::query()->where('disabled', '=', false)->count();
$locations = Location::all();
@ -90,11 +93,11 @@ class ServerController extends Controller
return view('servers.create')->with([
'productCount' => $productCount,
'nodeCount' => $nodeCount,
'nests' => $nests,
'locations' => $locations,
'eggs' => $eggs,
'user' => Auth::user(),
'nodeCount' => $nodeCount,
'nests' => $nests,
'locations' => $locations,
'eggs' => $eggs,
'user' => Auth::user(),
]);
}
@ -109,8 +112,8 @@ class ServerController extends Controller
}
// minimum credits && Check for Allocation
if (FacadesRequest::has("product")) {
$product = Product::findOrFail(FacadesRequest::input("product"));
if (FacadesRequest::has('product')) {
$product = Product::findOrFail(FacadesRequest::input('product'));
// Get node resource allocation info
$node = $product->nodes()->findOrFail(FacadesRequest::input('node'));
@ -118,7 +121,9 @@ class ServerController extends Controller
// Check if node has enough memory and disk space
$checkResponse = Pterodactyl::checkNodeResources($node, $product->memory, $product->disk);
if ($checkResponse == False) return redirect()->route('servers.index')->with('error', __("The node '" . $nodeName . "' doesn't have the required memory or disk left to allocate this product."));
if ($checkResponse == false) {
return redirect()->route('servers.index')->with('error', __("The node '".$nodeName."' doesn't have the required memory or disk left to allocate this product."));
}
// Min. Credits
if (
@ -127,24 +132,24 @@ class ServerController extends Controller
? config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER', 50)
: $product->minimum_credits)
) {
return redirect()->route('servers.index')->with('error', "You do not have the required amount of " . CREDITS_DISPLAY_NAME . " to use this product!");
return redirect()->route('servers.index')->with('error', 'You do not have the required amount of '.CREDITS_DISPLAY_NAME.' to use this product!');
}
}
//Required Verification for creating an server
if (config('SETTINGS::USER:FORCE_EMAIL_VERIFICATION', 'false') === 'true' && !Auth::user()->hasVerifiedEmail()) {
return redirect()->route('profile.index')->with('error', __("You are required to verify your email address before you can create a server."));
}
//Required Verification for creating an server
if (!config('SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS', 'true') && Auth::user()->role != "admin") {
return redirect()->route('servers.index')->with('error', __("The system administrator has blocked the creation of new servers."));
if (config('SETTINGS::USER:FORCE_EMAIL_VERIFICATION', 'false') === 'true' && ! Auth::user()->hasVerifiedEmail()) {
return redirect()->route('profile.index')->with('error', __('You are required to verify your email address before you can create a server.'));
}
//Required Verification for creating an server
if (config('SETTINGS::USER:FORCE_DISCORD_VERIFICATION', 'false') === 'true' && !Auth::user()->discordUser) {
return redirect()->route('profile.index')->with('error', __("You are required to link your discord account before you can create a server."));
if (! config('SETTINGS::SYSTEM:CREATION_OF_NEW_SERVERS', 'true') && Auth::user()->role != 'admin') {
return redirect()->route('servers.index')->with('error', __('The system administrator has blocked the creation of new servers.'));
}
//Required Verification for creating an server
if (config('SETTINGS::USER:FORCE_DISCORD_VERIFICATION', 'false') === 'true' && ! Auth::user()->discordUser) {
return redirect()->route('profile.index')->with('error', __('You are required to link your discord account before you can create a server.'));
}
return null;
@ -156,14 +161,15 @@ class ServerController extends Controller
/** @var Node $node */
/** @var Egg $egg */
/** @var Product $product */
if (!is_null($this->validateConfigurationRules())) return $this->validateConfigurationRules();
if (! is_null($this->validateConfigurationRules())) {
return $this->validateConfigurationRules();
}
$request->validate([
"name" => "required|max:191",
"node" => "required|exists:nodes,id",
"egg" => "required|exists:eggs,id",
"product" => "required|exists:products,id"
'name' => 'required|max:191',
'node' => 'required|exists:nodes,id',
'egg' => 'required|exists:eggs,id',
'product' => 'required|exists:products,id',
]);
//get required resources
@ -172,23 +178,27 @@ class ServerController extends Controller
$node = $product->nodes()->findOrFail($request->input('node'));
$server = $request->user()->servers()->create([
'name' => $request->input('name'),
'name' => $request->input('name'),
'product_id' => $request->input('product'),
]);
//get free allocation ID
$allocationId = Pterodactyl::getFreeAllocationId($node);
if (!$allocationId) return $this->noAllocationsError($server);
if (! $allocationId) {
return $this->noAllocationsError($server);
}
//create server on pterodactyl
$response = Pterodactyl::createServer($server, $egg, $allocationId);
if ($response->failed()) return $this->serverCreationFailed($response, $server);
if ($response->failed()) {
return $this->serverCreationFailed($response, $server);
}
$serverAttributes = $response->json()['attributes'];
//update server with pterodactyl_id
$server->update([
'pterodactyl_id' => $serverAttributes['id'],
'identifier' => $serverAttributes['identifier']
'identifier' => $serverAttributes['identifier'],
]);
if (config('SETTINGS::SYSTEM:SERVER_CREATE_CHARGE_FIRST_HOUR', 'true') == 'true') {
@ -202,7 +212,8 @@ class ServerController extends Controller
/**
* return redirect with error
* @param Server $server
*
* @param Server $server
* @return RedirectResponse
*/
private function noAllocationsError(Server $server)
@ -210,13 +221,15 @@ class ServerController extends Controller
$server->delete();
Auth::user()->notify(new ServerCreationError($server));
return redirect()->route('servers.index')->with('error', __('No allocations satisfying the requirements for automatic deployment on this node were found.'));
}
/**
* return redirect with error
* @param Response $response
* @param Server $server
*
* @param Response $response
* @param Server $server
* @return RedirectResponse
*/
private function serverCreationFailed(Response $response, Server $server)
@ -231,18 +244,19 @@ class ServerController extends Controller
{
try {
$server->delete();
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().'"');
}
}
/** Show Server Settings */
public function show(Server $server)
{
if($server->user_id != Auth::user()->id){ return back()->with('error', __('´This is not your Server!'));}
if ($server->user_id != Auth::user()->id) {
return back()->with('error', __('´This is not your Server!'));
}
$serverAttributes = Pterodactyl::getServerAttributes($server->pterodactyl_id);
$serverRelationships = $serverAttributes['relationships'];
$serverLocationAttributes = $serverRelationships['location']['attributes'];
@ -261,7 +275,7 @@ class ServerController extends Controller
$pteroNode = Pterodactyl::getNode($serverRelationships['node']['attributes']['id']);
$products = Product::orderBy("created_at")
$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']);
})
@ -270,20 +284,23 @@ class ServerController extends Controller
// Set the each product eggs array to just contain the eggs name
foreach ($products as $product) {
$product->eggs = $product->eggs->pluck('name')->toArray();
if($product->memory-$currentProduct->memory>($pteroNode['memory']*($pteroNode['memory_overallocate']+100)/100)-$pteroNode['allocated_resources']['memory']||$product->disk-$currentProduct->disk>($pteroNode['disk']*($pteroNode['disk_overallocate']+100)/100)-$pteroNode['allocated_resources']['disk']) $product->doesNotFit = true;
if ($product->memory - $currentProduct->memory > ($pteroNode['memory'] * ($pteroNode['memory_overallocate'] + 100) / 100) - $pteroNode['allocated_resources']['memory'] || $product->disk - $currentProduct->disk > ($pteroNode['disk'] * ($pteroNode['disk_overallocate'] + 100) / 100) - $pteroNode['allocated_resources']['disk']) {
$product->doesNotFit = true;
}
}
return view('servers.settings')->with([
'server' => $server,
'products' => $products
'products' => $products,
]);
}
public function upgrade(Server $server, Request $request)
{
if($server->user_id != Auth::user()->id) return redirect()->route('servers.index');
if(!isset($request->product_upgrade))
{
if ($server->user_id != Auth::user()->id) {
return redirect()->route('servers.index');
}
if (! isset($request->product_upgrade)) {
return redirect()->route('servers.show', ['server' => $server->id])->with('error', __('this product is the only one'));
}
$user = Auth::user();
@ -299,32 +316,35 @@ class ServerController extends Controller
// Check if node has enough memory and disk space
$requireMemory = $newProduct->memory - $oldProduct->memory;
$requiredisk = $newProduct->disk - $oldProduct->disk;
$requiredisk = $newProduct->disk - $oldProduct->disk;
$checkResponse = Pterodactyl::checkNodeResources($node, $requireMemory, $requiredisk);
if ($checkResponse == False) return redirect()->route('servers.index')->with('error', __("The node '" . $nodeName . "' doesn't have the required memory or disk left to upgrade the server."));
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."));
}
$priceupgrade = $newProduct->getHourlyPrice();
if ($priceupgrade < $oldProduct->getHourlyPrice()) {
$priceupgrade = 0;
$priceupgrade = 0;
}
if ($user->credits >= $priceupgrade && $user->credits >= $newProduct->minimum_credits)
{
if ($user->credits >= $priceupgrade && $user->credits >= $newProduct->minimum_credits) {
$server->product_id = $request->product_upgrade;
$server->update();
$server->allocation = $serverAttributes['allocation'];
$response = Pterodactyl::updateServer($server, $newProduct);
if ($response->failed()) return $this->serverCreationFailed($response, $server);
if ($response->failed()) {
return $this->serverCreationFailed($response, $server);
}
//update user balance
$user->decrement('credits', $priceupgrade);
//restart the server
$response = Pterodactyl::powerAction($server, "restart");
if ($response->failed()) return redirect()->route('servers.index')->with('error', $response->json()['errors'][0]['detail']);
$response = Pterodactyl::powerAction($server, 'restart');
if ($response->failed()) {
return redirect()->route('servers.index')->with('error', $response->json()['errors'][0]['detail']);
}
return redirect()->route('servers.show', ['server' => $server->id])->with('success', __('Server Successfully Upgraded'));
}
else
{
} else {
return redirect()->route('servers.show', ['server' => $server->id])->with('error', __('Not Enough Balance for Upgrade'));
}
}

View file

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

View file

@ -2,113 +2,157 @@
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Notification;
use App\Models\Ticket;
use App\Models\Server;
use App\Models\TicketComment;
use App\Models\TicketCategory;
use App\Models\Ticket;
use App\Models\TicketBlacklist;
use App\Notifications\Ticket\User\CreateNotification;
use App\Models\TicketCategory;
use App\Models\TicketComment;
use App\Models\User;
use App\Notifications\Ticket\Admin\AdminCreateNotification;
use App\Notifications\Ticket\Admin\AdminReplyNotification;
use App\Notifications\Ticket\User\CreateNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;
class TicketsController extends Controller
{
public function index()
{
$tickets = Ticket::where("user_id", Auth::user()->id)->paginate(10);
$tickets = Ticket::where('user_id', Auth::user()->id)->paginate(10);
$ticketcategories = TicketCategory::all();
return view("ticket.index", compact("tickets", "ticketcategories"));
return view('ticket.index', compact('tickets', 'ticketcategories'));
}
public function create() {
#check in blacklist
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required',
'ticketcategory' => 'required',
'priority' => 'required',
'message' => 'required',]
);
$ticket = new Ticket([
'title' => $request->input('title'),
'user_id' => Auth::user()->id,
'ticket_id' => strtoupper(Str::random(8)),
'ticketcategory_id' => $request->input('ticketcategory'),
'priority' => $request->input('priority'),
'message' => $request->input('message'),
'status' => 'Open',
'server' => $request->input('server'),]
);
$ticket->save();
$user = Auth::user();
if (config('SETTINGS::TICKET:NOTIFY') == "all") {
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
}
if (config('SETTINGS::TICKET:NOTIFY') == "admin") {
$admin = User::where('role', 'admin')->get();
}
if (config('SETTINGS::TICKET:NOTIFY') == "moderator") {
$admin = User::where('role', 'mod')->get();
}
$user->notify(new CreateNotification($ticket));
if (config('SETTINGS::TICKET:NOTIFY') != "none") {
Notification::send($admin, new AdminCreateNotification($ticket, $user));
}
return redirect()->route('ticket.index')->with('success', __('A ticket has been opened, ID: #') . $ticket->ticket_id);
}
public function show($ticket_id)
{
try {
$ticket = Ticket::where('ticket_id', $ticket_id)->firstOrFail();
} catch (Exception $e) {
return redirect()->back()->with('warning', __('Ticket not found on the server. It potentially got deleted earlier'));
}
$ticketcomments = $ticket->ticketcomments;
$ticketcategory = $ticket->ticketcategory;
$server = Server::where('id', $ticket->server)->first();
return view('ticket.show', compact('ticket', 'ticketcategory', 'ticketcomments', 'server'));
}
public function reply(Request $request)
{
//check in blacklist
$check = TicketBlacklist::where('user_id', Auth::user()->id)->first();
if($check && $check->status == "True"){
if ($check && $check->status == 'True') {
return redirect()->route('ticket.index')->with('error', __("You can't reply a ticket because you're on the blacklist for a reason: '" . $check->reason . "', please contact the administrator"));
}
$this->validate($request, ['ticketcomment' => 'required']);
try {
$ticket = Ticket::where('id', $request->input('ticket_id'))->firstOrFail();
} catch (Exception $e) {
return redirect()->back()->with('warning', __('Ticket not found on the server. It potentially got deleted earlier'));
}
$ticket->status = 'Client Reply';
$ticket->update();
$ticketcomment = TicketComment::create([
'ticket_id' => $request->input('ticket_id'),
'user_id' => Auth::user()->id,
'ticketcomment' => $request->input('ticketcomment'),
'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));
return redirect()->back()->with('success', __('Your comment has been submitted'));
}
public function create()
{
//check in blacklist
$check = TicketBlacklist::where('user_id', Auth::user()->id)->first();
if ($check && $check->status == 'True') {
return redirect()->route('ticket.index')->with('error', __("You can't make a ticket because you're on the blacklist for a reason: '" . $check->reason . "', please contact the administrator"));
}
$ticketcategories = TicketCategory::all();
$servers = Auth::user()->servers;
return view("ticket.create", compact("ticketcategories", "servers"));
return view('ticket.create', compact('ticketcategories', 'servers'));
}
public function store(Request $request) {
$this->validate($request, array(
"title" => "required",
"ticketcategory" => "required",
"priority" => "required",
"message" => "required")
);
$ticket = new Ticket(array(
"title" => $request->input("title"),
"user_id" => Auth::user()->id,
"ticket_id" => strtoupper(Str::random(5)),
"ticketcategory_id" => $request->input("ticketcategory"),
"priority" => $request->input("priority"),
"message" => $request->input("message"),
"status" => "Open",
"server" => $request->input("server"))
);
$ticket->save();
$user = Auth::user();
$admin = User::where('role', 'admin')->orWhere('role', 'mod')->get();
$user->notify(new CreateNotification($ticket));
Notification::send($admin, new AdminCreateNotification($ticket, $user));
return redirect()->route('ticket.index')->with('success', __('A ticket has been opened, ID: #') . $ticket->ticket_id);
}
public function show($ticket_id) {
$ticket = Ticket::where("ticket_id", $ticket_id)->firstOrFail();
$ticketcomments = $ticket->ticketcomments;
$ticketcategory = $ticket->ticketcategory;
$server = Server::where('id', $ticket->server)->first();
return view("ticket.show", compact("ticket", "ticketcategory", "ticketcomments", "server"));
}
public function reply(Request $request) {
#check in blacklist
$check = TicketBlacklist::where('user_id', Auth::user()->id)->first();
if($check && $check->status == "True"){
return redirect()->route('ticket.index')->with('error', __("You can't reply a ticket because you're on the blacklist for a reason: '" . $check->reason . "', please contact the administrator"));
public function changeStatus($ticket_id)
{
try {
$ticket = Ticket::where('user_id', Auth::user()->id)->where("ticket_id", $ticket_id)->firstOrFail();
} catch (Exception $e) {
return redirect()->back()->with('warning', __('Ticket not found on the server. It potentially got deleted earlier'));
}
$this->validate($request, array("ticketcomment" => "required"));
$ticket = Ticket::where('id', $request->input("ticket_id"))->firstOrFail();
$ticket->status = "Client Reply";
$ticket->update();
$ticketcomment = TicketComment::create(array(
"ticket_id" => $request->input("ticket_id"),
"user_id" => Auth::user()->id,
"ticketcomment" => $request->input("ticketcomment"),
"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));
return redirect()->back()->with('success', __('Your comment has been submitted'));
if ($ticket->status == "Closed") {
$ticket->status = "Reopened";
$ticket->save();
return redirect()->back()->with('success', __('A ticket has been reopened, ID: #') . $ticket->ticket_id);
}
$ticket->status = "Closed";
$ticket->save();
return redirect()->back()->with('success', __('A ticket has been closed, ID: #') . $ticket->ticket_id);
}
public function dataTable()
{
$query = Ticket::where("user_id", Auth::user()->id)->get();
$query = Ticket::where('user_id', Auth::user()->id)->get();
return datatables($query)
->addColumn('category', function (Ticket $tickets) {
return $tickets->ticketcategory->name;
})
->editColumn('title', function (Ticket $tickets) {
return '<a class="text-info" href="' . route('ticket.show', ['ticket_id' => $tickets->ticket_id]) . '">' . "#" . $tickets->ticket_id . " - " . $tickets->title . '</a>';
return '<a class="text-info" href="' . route('ticket.show', ['ticket_id' => $tickets->ticket_id]) . '">' . '#' . $tickets->ticket_id . ' - ' . htmlspecialchars($tickets->title) . '</a>';
})
->editColumn('status', function (Ticket $tickets) {
switch ($tickets->status) {
case 'Reopened':
case 'Open':
$badgeColor = 'badge-success';
break;
break;
case 'Closed':
$badgeColor = 'badge-danger';
break;
@ -122,10 +166,30 @@ class TicketsController extends Controller
return '<span class="badge ' . $badgeColor . '">' . $tickets->status . '</span>';
})
->editColumn('updated_at', function (Ticket $tickets) {
return $tickets->updated_at ? $tickets->updated_at->diffForHumans() : '';
->editColumn('priority', function (Ticket $tickets) {
return __($tickets->priority);
})
->rawColumns(['category', 'title', 'status', 'updated_at'])
->editColumn('updated_at', function (Ticket $tickets) {
return ['display' => $tickets->updated_at ? $tickets->updated_at->diffForHumans() : '',
'raw' => $tickets->updated_at ? strtotime($tickets->updated_at) : ''];
})
->addColumn('actions', function (Ticket $tickets) {
$statusButtonColor = ($tickets->status == "Closed") ? 'btn-success' : 'btn-warning';
$statusButtonIcon = ($tickets->status == "Closed") ? 'fa-redo' : 'fa-times';
$statusButtonText = ($tickets->status == "Closed") ? __('Reopen') : __('Close');
return '
<a data-content="' . __('View') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('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('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>
</form>
</form>
';
})
->rawColumns(['category', 'title', 'status', 'updated_at', "actions"])
->make(true);
}
}

View file

@ -8,16 +8,15 @@ use Illuminate\Support\Facades\Session;
class TranslationController extends Controller
{
/**
*
* Change session locale
* @param Request $request
*
* @param Request $request
* @return Response
*/
public function changeLocale(Request $request)
{
Session::put('locale', $request->inputLocale);
return redirect()->back();
}
}

View file

@ -22,7 +22,7 @@ class Kernel extends HttpKernel
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
@ -39,7 +39,6 @@ class Kernel extends HttpKernel
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
@ -51,7 +50,7 @@ class Kernel extends HttpKernel
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
GlobalNames::class
GlobalNames::class,
],
];
@ -65,6 +64,7 @@ class Kernel extends HttpKernel
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
@ -75,6 +75,6 @@ class Kernel extends HttpKernel
'admin' => isAdmin::class,
'moderator' => isMod::class,
'api.token' => ApiAuthToken::class,
'checkSuspended' => CheckSuspended::class
'checkSuspended' => CheckSuspended::class,
];
}

View file

@ -11,18 +11,23 @@ class ApiAuthToken
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (empty($request->bearerToken())) return response()->json(['message' => 'Missing Authorization header'], 403);
if (empty($request->bearerToken())) {
return response()->json(['message' => 'Missing Authorization header'], 403);
}
$token = ApplicationApi::find($request->bearerToken());
if (is_null($token)) return response()->json(['message' => 'Invalid Authorization token'], 401);
if (is_null($token)) {
return response()->json(['message' => 'Invalid Authorization token'], 401);
}
$token->updateLastUsed();
return $next($request);
}
}

View file

@ -23,6 +23,7 @@ class CheckSuspended
return redirect()->route('login')->withMessage($message);
}
return $next($request);
}
}

View file

@ -2,8 +2,6 @@
namespace App\Http\Middleware;
use App\Models\Configuration;
use App\Models\Settings;
use Closure;
use Illuminate\Http\Request;
@ -12,15 +10,15 @@ class GlobalNames
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
define('CREDITS_DISPLAY_NAME', config('SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME', 'Credits'));
$unsupported_lang_array = explode(',', config("app.unsupported_locales"));
$unsupported_lang_array = explode(',', config('app.unsupported_locales'));
$unsupported_lang_array = array_map('strtolower', $unsupported_lang_array);
define('UNSUPPORTED_LANGS', $unsupported_lang_array);

View file

@ -3,7 +3,6 @@
namespace App\Http\Middleware;
use Closure;
use DateTime;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@ -12,17 +11,17 @@ class LastSeen
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (env('APP_ENV' , 'local') == 'local'){
if (env('APP_ENV', 'local') == 'local') {
return $next($request);
}
if (!Auth::check()) {
if (! Auth::check()) {
return $next($request);
}
@ -32,7 +31,7 @@ class LastSeen
Auth::user()->update([
'last_seen' => now(),
'ip' => $request->ip()
'ip' => $request->ip(),
]);
return $next($request);

View file

@ -2,7 +2,6 @@
namespace App\Http\Middleware;
use App\Models\Settings;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
@ -10,27 +9,25 @@ use Illuminate\Support\Facades\Session;
class SetLocale
{
/**
*
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (Session::has('locale')) {
$locale = Session::get('locale', config("SETTINGS::LOCALE:DEFAULT"));
$locale = Session::get('locale', config('SETTINGS::LOCALE:DEFAULT'));
} else {
if (config("SETTINGS::LOCALE:DYNAMIC") !== "true") {
$locale = config("SETTINGS::LOCALE:DEFAULT");
if (config('SETTINGS::LOCALE:DYNAMIC') !== 'true') {
$locale = config('SETTINGS::LOCALE:DEFAULT');
} else {
$locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
if (!in_array($locale, explode(',', config("SETTINGS::LOCALE:AVAILABLE")))) {
$locale = config("SETTINGS::LOCALE:DEFAULT");
if (! in_array($locale, explode(',', config('SETTINGS::LOCALE:AVAILABLE')))) {
$locale = config('SETTINGS::LOCALE:DEFAULT');
}
}
}

View file

@ -9,9 +9,10 @@ class TrimStrings extends Middleware
/**
* The names of the attributes that should not be trimmed.
*
* @var array
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];

View file

@ -2,7 +2,7 @@
namespace App\Http\Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
@ -10,7 +10,7 @@ class TrustProxies extends Middleware
/**
* The trusted proxies for this application.
*
* @var array|string|null
* @var array<int, string>|string|null
*/
protected $proxies;
@ -19,5 +19,10 @@ class TrustProxies extends Middleware
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [
// 'fbclid',
// 'utm_campaign',
// 'utm_content',
// 'utm_medium',
// 'utm_source',
// 'utm_term',
];
}

View file

@ -2,7 +2,10 @@
namespace App\Http\Middleware;
use App\Helpers\ExtensionHelper;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Contracts\Foundation\Application;
class VerifyCsrfToken extends Middleware
{
@ -11,7 +14,12 @@ class VerifyCsrfToken extends Middleware
*
* @var array
*/
protected $except = [
'payment/StripeWebhooks'
];
protected $except = [];
public function __construct(Application $app, Encrypter $encrypter)
{
$this->app = $app;
$this->encrypter = $encrypter;
$this->except = ExtensionHelper::getAllCsrfIgnoredRoutes();
}
}

View file

@ -12,8 +12,8 @@ class isAdmin
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)

View file

@ -12,8 +12,8 @@ class isMod
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)

View file

@ -0,0 +1,26 @@
<?php
namespace App\Listeners;
use App\Events\PaymentEvent;
use App\Traits\Invoiceable;
class CreateInvoice
{
use Invoiceable;
/**
* Handle the event.
*
* @param \App\Events\PaymentEvent $event
* @return void
*/
public function handle(PaymentEvent $event)
{
if (config('SETTINGS::INVOICE:ENABLED') == 'true') {
// create invoice using the trait
$this->createInvoice($event->payment, $event->shopProduct);
}
}
}

View file

@ -4,7 +4,6 @@ namespace App\Listeners;
use App\Events\UserUpdateCreditsEvent;
use App\Models\Server;
use App\Models\Settings;
use Exception;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -13,8 +12,9 @@ class UnsuspendServers implements ShouldQueue
/**
* Handle the event.
*
* @param UserUpdateCreditsEvent $event
* @param UserUpdateCreditsEvent $event
* @return void
*
* @throws Exception
*/
public function handle(UserUpdateCreditsEvent $event)
@ -22,7 +22,9 @@ class UnsuspendServers implements ShouldQueue
if ($event->user->credits > config('SETTINGS::USER:MINIMUM_REQUIRED_CREDITS_TO_MAKE_SERVER', 50)) {
/** @var Server $server */
foreach ($event->user->servers as $server) {
if ($server->isSuspended()) $server->unSuspend();
if ($server->isSuspended()) {
$server->unSuspend();
}
}
}
}

View file

@ -0,0 +1,82 @@
<?php
namespace App\Listeners;
use App\Events\PaymentEvent;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use App\Models\PartnerDiscount;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class UserPayment
{
/**
* Handle the event.
*
* @param \App\Events\PaymentEvent $event
* @return void
*/
public function handle(PaymentEvent $event)
{
$user = $event->user;
$shopProduct = $event->shopProduct;
// only update user if payment is paid
if ($event->payment->status != "paid") {
return;
}
//update server limit
if (config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE') !== 0 && $user->server_limit < config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')) {
$user->update(['server_limit' => config('SETTINGS::USER:SERVER_LIMIT_AFTER_IRL_PURCHASE')]);
}
//update User with bought item
if ($shopProduct->type == "Credits") {
$user->increment('credits', $shopProduct->quantity);
} elseif ($shopProduct->type == "Server slots") {
$user->increment('server_limit', $shopProduct->quantity);
}
//give referral commission always
if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "true") {
if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
$ref_user = User::findOrFail($ref_user->referral_id);
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
$ref_user->increment('credits', $increment);
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
}
}
//update role give Referral-reward
if ($user->role == 'member') {
$user->update(['role' => 'client']);
//give referral commission only on first purchase
if ((config("SETTINGS::REFERRAL:MODE") == "commission" || config("SETTINGS::REFERRAL:MODE") == "both") && $shopProduct->type == "Credits" && config("SETTINGS::REFERRAL::ALWAYS_GIVE_COMMISSION") == "false") {
if ($ref_user = DB::table("user_referrals")->where('registered_user_id', '=', $user->id)->first()) {
$ref_user = User::findOrFail($ref_user->referral_id);
$increment = number_format($shopProduct->quantity * (PartnerDiscount::getCommission($ref_user->id)) / 100, 0, "", "");
$ref_user->increment('credits', $increment);
//LOGS REFERRALS IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($ref_user)
->log('gained ' . $increment . ' ' . config("SETTINGS::SYSTEM:CREDITS_DISPLAY_NAME") . ' for commission-referral of ' . $user->name . ' (ID:' . $user->id . ')');
}
}
}
// LOGS PAYMENT IN THE ACTIVITY LOG
activity()
->performedOn($user)
->causedBy($user)
->log('bought ' . $shopProduct->quantity . ' ' . $shopProduct->type . ' for ' . $shopProduct->price . $shopProduct->currency_code);
}
}

View file

@ -17,7 +17,7 @@ class Verified
/**
* Handle the event.
*
* @param object $event
* @param object $event
* @return void
*/
public function handle($event)
@ -25,6 +25,7 @@ class Verified
if (!$event->user->email_verified_reward) {
$event->user->increment('server_limit', config('SETTINGS::USER:SERVER_LIMIT_REWARD_AFTER_VERIFY_EMAIL'));
$event->user->increment('credits', config('SETTINGS::USER:CREDITS_REWARD_AFTER_VERIFY_EMAIL'));
$event->user->update(['email_verified_reward' => true]);
}
}
}

View file

@ -10,13 +10,15 @@ class ApplicationApi extends Model
{
use HasFactory;
protected $fillable = ['token', 'memo' , 'last_used'];
protected $fillable = ['token', 'memo', 'last_used'];
protected $primaryKey = 'token';
public $incrementing = false;
protected $dates = ['last_used'];
protected $casts = [
'last_used' => 'datetime',
];
public static function boot()
{
@ -29,7 +31,8 @@ class ApplicationApi extends Model
});
}
public function updateLastUsed(){
public function updateLastUsed()
{
$this->update(['last_used' => now()]);
}
}

View file

@ -11,18 +11,18 @@ class DiscordUser extends Model
use HasFactory;
protected $fillable = [
"id",
"user_id",
"username",
"avatar",
"discriminator",
"public_flags",
"flags",
"locale",
"mfa_enabled",
"premium_type",
"email",
"verified",
'id',
'user_id',
'username',
'avatar',
'discriminator',
'public_flags',
'flags',
'locale',
'mfa_enabled',
'premium_type',
'email',
'verified',
];
public $incrementing = false;
@ -30,14 +30,16 @@ class DiscordUser extends Model
/**
* @return BelongsTo
*/
public function user(){
public function user()
{
return $this->belongsTo(User::class);
}
/**
* @return string
*/
public function getAvatar(){
return "https://cdn.discordapp.com/avatars/" . $this->id . "/" . $this->avatar . ".png";
public function getAvatar()
{
return 'https://cdn.discordapp.com/avatars/'.$this->id.'/'.$this->avatar.'.png';
}
}

View file

@ -61,8 +61,8 @@ class Egg extends Model
$array['environment'] = json_encode([$environment]);
self::query()->updateOrCreate([
'id' => $array['id']
], array_diff_key($array, array_flip(["id"]))
'id' => $array['id'],
], array_diff_key($array, array_flip(['id']))
);
}
@ -72,8 +72,9 @@ class Egg extends Model
/**
* @description remove eggs that have been deleted on pterodactyl
* @param Nest $nest
* @param array $eggs
*
* @param Nest $nest
* @param array $eggs
*/
private static function removeDeletedEggs(Nest $nest, array $eggs): void
{
@ -82,7 +83,9 @@ class Egg extends Model
}, $eggs);
$nest->eggs()->each(function (Egg $egg) use ($ids) {
if (!in_array($egg->id, $ids)) $egg->delete();
if (! in_array($egg->id, $ids)) {
$egg->delete();
}
});
}

View file

@ -12,7 +12,6 @@ class Invoice extends Model
protected $fillable = [
'invoice_name',
'invoice_user',
'payment_id'
'payment_id',
];
}

View file

@ -28,6 +28,7 @@ class Location extends Model
/**
* Sync locations with pterodactyl panel
*
* @throws Exception
*/
public static function syncLocations()
@ -36,21 +37,21 @@ class Location extends Model
//map response
$locations = array_map(function ($val) {
return array(
'id' => $val['attributes']['id'],
'name' => $val['attributes']['short'],
'description' => $val['attributes']['long']
);
return [
'id' => $val['attributes']['id'],
'name' => $val['attributes']['short'],
'description' => $val['attributes']['long'],
];
}, $locations);
//update or create
foreach ($locations as $location) {
self::query()->updateOrCreate(
[
'id' => $location['id']
'id' => $location['id'],
],
[
'name' => $location['name'],
'name' => $location['name'],
'description' => $location['name'],
]
);
@ -61,7 +62,8 @@ class Location extends Model
/**
* @description remove locations that have been deleted on pterodactyl
* @param array $locations
*
* @param array $locations
*/
private static function removeDeletedLocation(array $locations): void
{
@ -70,7 +72,9 @@ class Location extends Model
}, $locations);
self::all()->each(function (Location $location) use ($ids) {
if (!in_array($location->id, $ids)) $location->delete();
if (! in_array($location->id, $ids)) {
$location->delete();
}
});
}
@ -78,5 +82,4 @@ class Location extends Model
{
return $this->hasMany(Node::class, 'location_id', 'id');
}
}

View file

@ -36,20 +36,20 @@ class Nest extends Model
//map response
$nests = array_map(function ($nest) {
return array(
'id' => $nest['attributes']['id'],
'name' => $nest['attributes']['name'],
return [
'id' => $nest['attributes']['id'],
'name' => $nest['attributes']['name'],
'description' => $nest['attributes']['description'],
);
];
}, $nests);
foreach ($nests as $nest) {
self::query()->updateOrCreate([
'id' => $nest['id']
'id' => $nest['id'],
], [
'name' => $nest['name'],
'name' => $nest['name'],
'description' => $nest['description'],
'disabled' => false
'disabled' => false,
]);
}
@ -58,7 +58,8 @@ class Nest extends Model
/**
* @description remove nests that have been deleted on pterodactyl
* @param array $nests
*
* @param array $nests
*/
private static function removeDeletedNests(array $nests): void
{
@ -67,7 +68,9 @@ class Nest extends Model
}, $nests);
self::all()->each(function (Nest $nest) use ($ids) {
if (!in_array($nest->id, $ids)) $nest->delete();
if (! in_array($nest->id, $ids)) {
$nest->delete();
}
});
}

View file

@ -26,7 +26,6 @@ class Node extends Model
});
}
/**
* @throws Exception
*/
@ -37,25 +36,25 @@ class Node extends Model
//map response
$nodes = array_map(function ($node) {
return array(
'id' => $node['attributes']['id'],
return [
'id' => $node['attributes']['id'],
'location_id' => $node['attributes']['location_id'],
'name' => $node['attributes']['name'],
'name' => $node['attributes']['name'],
'description' => $node['attributes']['description'],
);
];
}, $nodes);
//update or create
foreach ($nodes as $node) {
self::query()->updateOrCreate(
[
'id' => $node['id']
'id' => $node['id'],
],
[
'name' => $node['name'],
'name' => $node['name'],
'description' => $node['description'],
'location_id' => $node['location_id'],
'disabled' => false
'disabled' => false,
]);
}
@ -64,7 +63,8 @@ class Node extends Model
/**
* @description remove nodes that have been deleted on pterodactyl
* @param array $nodes
*
* @param array $nodes
*/
private static function removeDeletedNodes(array $nodes): void
{
@ -73,7 +73,9 @@ class Node extends Model
}, $nodes);
self::all()->each(function (Node $node) use ($ids) {
if (!in_array($node->id, $ids)) $node->delete();
if (! in_array($node->id, $ids)) {
$node->delete();
}
});
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class PartnerDiscount extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'partner_discount',
'registered_user_discount',
'referral_system_commission',
];
public static function getDiscount(int $user_id = null)
{
if ($partnerDiscount = PartnerDiscount::where('user_id', $user_id ?? Auth::user()->id)->first()) {
return $partnerDiscount->partner_discount;
} elseif ($ref_user = DB::table('user_referrals')->where('registered_user_id', '=', $user_id ?? Auth::user()->id)->first()) {
if ($partnerDiscount = PartnerDiscount::where('user_id', $ref_user->referral_id)->first()) {
return $partnerDiscount->registered_user_discount;
}
return 0;
}
return 0;
}
public static function getCommission($user_id)
{
if ($partnerDiscount = PartnerDiscount::where('user_id', $user_id)->first()) {
if ($partnerDiscount->referral_system_commission >= 0) {
return $partnerDiscount->referral_system_commission >= 0;
}
}
return config('SETTINGS::REFERRAL:PERCENTAGE');
}
}

View file

@ -7,14 +7,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use NumberFormatter;
use Spatie\Activitylog\Traits\LogsActivity;
class Payment extends Model
{
use HasFactory;
use LogsActivity;
public $incrementing = false;
protected $primaryKey = 'id';
/**
* @var string[]
@ -55,14 +54,14 @@ class Payment extends Model
}
/**
* @param mixed $value
* @param string $locale
*
* @param mixed $value
* @param string $locale
* @return float
*/
public function formatToCurrency($value, $locale = 'en_US')
{
$formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
return $formatter->formatCurrency($value, $this->currency_code);
}
}

View file

@ -7,14 +7,20 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
class Product extends Model
{
use HasFactory;
use LogsActivity;
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
-> logOnlyDirty()
-> logOnly(['*'])
-> dontSubmitEmptyLogs();
}
public $incrementing = false;
protected $guarded = ['id'];
@ -29,7 +35,7 @@ class Product extends Model
$product->{$product->getKeyName()} = $client->generateId($size = 21);
});
static::deleting(function(Product $product) {
static::deleting(function (Product $product) {
$product->nodes()->detach();
$product->eggs()->detach();
});
@ -42,12 +48,12 @@ class Product extends Model
public function getDailyPrice()
{
return ($this->price / 30);
return $this->price / 30;
}
public function getWeeklyPrice()
{
return ($this->price / 4);
return $this->price / 4;
}
/**
@ -61,14 +67,16 @@ class Product extends Model
/**
* @return BelongsToMany
*/
public function eggs() {
public function eggs()
{
return $this->belongsToMany(Egg::class);
}
/**
* @return BelongsToMany
*/
public function nodes() {
public function nodes()
{
return $this->belongsToMany(Node::class);
}
}

View file

@ -11,17 +11,23 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Http\Client\Response;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
/**
* Class Server
* @package App\Models
*/
class Server extends Model
{
use HasFactory;
use LogsActivity;
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
-> logOnlyDirty()
-> logOnly(['*'])
-> dontSubmitEmptyLogs();
}
/**
* @var bool
*/
@ -41,24 +47,21 @@ class Server extends Model
* @var string[]
*/
protected $fillable = [
"name",
"description",
"suspended",
"identifier",
"product_id",
"pterodactyl_id",
'name',
'description',
'suspended',
'identifier',
'product_id',
'pterodactyl_id',
];
/**
* @var string[]
*/
protected $dates = [
'suspended'
protected $casts = [
'suspended' => 'datetime',
];
/**
*
*/
public static function boot()
{
parent::boot();
@ -71,7 +74,7 @@ class Server extends Model
static::deleting(function (Server $server) {
$response = Pterodactyl::client()->delete("/application/servers/{$server->pterodactyl_id}");
if ($response->failed() && !is_null($server->pterodactyl_id)) {
if ($response->failed() && ! is_null($server->pterodactyl_id)) {
//only return error when it's not a 404 error
if ($response['errors'][0]['status'] != '404') {
throw new Exception($response['errors'][0]['code']);
@ -85,10 +88,9 @@ class Server extends Model
*/
public function isSuspended()
{
return !is_null($this->suspended);
return ! is_null($this->suspended);
}
/**
* @return PromiseInterface|Response
*/
@ -98,7 +100,6 @@ class Server extends Model
}
/**
*
* @throws Exception
*/
public function suspend()
@ -107,7 +108,7 @@ class Server extends Model
if ($response->successful()) {
$this->update([
'suspended' => now()
'suspended' => now(),
]);
}
@ -123,14 +124,13 @@ class Server extends Model
if ($response->successful()) {
$this->update([
'suspended' => null
'suspended' => null,
]);
}
return $this;
}
/**
* @return HasOne
*/
@ -146,5 +146,4 @@ class Server extends Model
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}

View file

@ -31,19 +31,20 @@ class Settings extends Model
parent::boot();
static::updated(function (Settings $settings) {
Cache::forget(self::CACHE_TAG .':'. $settings->key);
Cache::forget(self::CACHE_TAG.':'.$settings->key);
});
}
/**
* @param string $key
* @param string $key
* @param $default
* @return mixed
*/
public static function getValueByKey(string $key, $default = null)
{
return Cache::rememberForever(self::CACHE_TAG .':'. $key, function () use ($default, $key) {
return Cache::rememberForever(self::CACHE_TAG.':'.$key, function () use ($default, $key) {
$settings = self::find($key);
return $settings ? $settings->value : $default;
});
}

View file

@ -5,12 +5,19 @@ namespace App\Models;
use Hidehalo\Nanoid\Client;
use Illuminate\Database\Eloquent\Model;
use NumberFormatter;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
use App\Models\Configuration;
class ShopProduct extends Model
{
use LogsActivity;
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
-> logOnlyDirty()
-> logOnly(['*'])
-> dontSubmitEmptyLogs();
}
/**
* @var bool
*/
@ -20,13 +27,13 @@ class ShopProduct extends Model
* @var string[]
*/
protected $fillable = [
"type",
"price",
"description",
"display",
"currency_code",
"quantity",
"disabled",
'type',
'price',
'description',
'display',
'currency_code',
'quantity',
'disabled',
];
public static function boot()
@ -41,14 +48,14 @@ class ShopProduct extends Model
}
/**
* @param mixed $value
* @param string $locale
*
* @param mixed $value
* @param string $locale
* @return float
*/
public function formatToCurrency($value, $locale = 'en_US')
{
$formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
return $formatter->formatCurrency($value, $this->currency_code);
}
@ -59,10 +66,16 @@ class ShopProduct extends Model
*/
public function getTaxPercent()
{
$tax = config("SETTINGS::PAYMENTS:SALES_TAX");
$tax = config('SETTINGS::PAYMENTS:SALES_TAX');
return $tax < 0 ? 0 : $tax;
}
public function getPriceAfterDiscount()
{
return number_format($this->price - ($this->price * PartnerDiscount::getDiscount() / 100), 2);
}
/**
* @description Returns the tax as Number
*
@ -70,7 +83,7 @@ class ShopProduct extends Model
*/
public function getTaxValue()
{
return number_format($this->price * $this->getTaxPercent() / 100, 2);
return number_format($this->getPriceAfterDiscount() * $this->getTaxPercent() / 100, 2);
}
/**
@ -80,6 +93,6 @@ class ShopProduct extends Model
*/
public function getTotalPrice()
{
return number_format($this->price + $this->getTaxValue(), 2);
return number_format($this->getPriceAfterDiscount() + $this->getTaxValue(), 2);
}
}

View file

@ -3,19 +3,36 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
class Ticket extends Model
{
use LogsActivity;
class Ticket extends Model {
protected $fillable = [
'user_id', 'ticketcategory_id', 'ticket_id', 'title', 'priority', 'message', 'status', 'server'
'user_id', 'ticketcategory_id', 'ticket_id', 'title', 'priority', 'message', 'status', 'server',
];
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
-> logOnlyDirty()
-> logOnly(['*'])
-> dontSubmitEmptyLogs();
}
public function ticketcategory(){
return $this->belongsTo(TicketCategory::class);}
public function ticketcategory()
{
return $this->belongsTo(TicketCategory::class);
}
public function ticketcomments(){
return $this->hasMany(TicketComment::class);}
public function ticketcomments()
{
return $this->hasMany(TicketComment::class);
}
public function user(){
return $this->belongsTo(User::class);}
}
public function user()
{
return $this->belongsTo(User::class);
}
}

View file

@ -4,14 +4,14 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TicketBlacklist extends Model {
class TicketBlacklist extends Model
{
protected $fillable = [
'user_id', 'status', 'reason'
'user_id', 'status', 'reason',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
}
}

View file

@ -4,10 +4,12 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TicketCategory extends Model {
class TicketCategory extends Model
{
protected $fillable = ['name'];
public function tickets(){
return $this->hasMany(Ticket::class);}
public function tickets()
{
return $this->hasMany(Ticket::class,'ticketcategory_id');
}
}

View file

@ -4,18 +4,24 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TicketComment extends Model {
protected $fillable = [
'ticket_id', 'user_id', 'ticketcomment'
];
class TicketComment extends Model
{
protected $fillable = [
'ticket_id', 'user_id', 'ticketcomment',
];
public function ticketcategory(){
return $this->belongsTo(TicketCategory::class);}
public function ticketcategory()
{
return $this->belongsTo(TicketCategory::class);
}
public function ticket(){
return $this->belongsTo(Ticket::class);}
public function ticket()
{
return $this->belongsTo(Ticket::class);
}
public function user(){
return $this->belongsTo(User::class);}
public function user()
{
return $this->belongsTo(User::class);
}
}

View file

@ -15,6 +15,7 @@ class UsefulLink extends Model
'icon',
'title',
'link',
'description'
'description',
'position',
];
}

View file

@ -12,12 +12,12 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\CausesActivity;
use Spatie\Activitylog\Traits\LogsActivity;
/**
* Class User
* @package App\Models
*/
class User extends Authenticatable implements MustVerifyEmail
{
@ -38,7 +38,7 @@ class User extends Authenticatable implements MustVerifyEmail
'server_limit',
'last_seen',
'ip',
'pterodactyl_id'
'pterodactyl_id',
];
/**
@ -60,7 +60,8 @@ class User extends Authenticatable implements MustVerifyEmail
'discord_verified_at',
'avatar',
'suspended',
'referral_code'
'referral_code',
'email_verified_reward'
];
/**
@ -83,11 +84,9 @@ class User extends Authenticatable implements MustVerifyEmail
'last_seen' => 'datetime',
'credits' => 'float',
'server_limit' => 'float',
'email_verified_reward' => 'boolean'
];
/**
*
*/
public static function boot()
{
parent::boot();
@ -97,29 +96,21 @@ class User extends Authenticatable implements MustVerifyEmail
});
static::deleting(function (User $user) {
$user->servers()->chunk(10, function ($servers) {
foreach ($servers as $server) {
$server->delete();
}
// delete every server the user owns without using chunks
$user->servers()->each(function ($server) {
$server->delete();
});
$user->payments()->chunk(10, function ($payments) {
foreach ($payments as $payment) {
$payment->delete();
}
});
$user->payments()->delete();
$user->tickets()->chunk(10, function ($tickets) {
foreach ($tickets as $ticket) {
$ticket->delete();
}
});
$user->tickets()->delete();
$user->ticketBlackList()->delete();
$user->vouchers()->detach();
$user->discordUser()->delete();
Pterodactyl::client()->delete("/application/users/{$user->pterodactyl_id}");
@ -174,9 +165,6 @@ class User extends Authenticatable implements MustVerifyEmail
return $this->hasOne(DiscordUser::class);
}
/**
*
*/
public function sendEmailVerificationNotification()
{
$this->notify(new QueuedVerifyEmail);
@ -199,7 +187,6 @@ class User extends Authenticatable implements MustVerifyEmail
}
/**
*
* @throws Exception
*/
public function suspend()
@ -209,7 +196,7 @@ class User extends Authenticatable implements MustVerifyEmail
}
$this->update([
'suspended' => true
'suspended' => true,
]);
return $this;
@ -227,7 +214,7 @@ class User extends Authenticatable implements MustVerifyEmail
}
$this->update([
'suspended' => false
'suspended' => false,
]);
return $this;
@ -246,18 +233,17 @@ class User extends Authenticatable implements MustVerifyEmail
public function getAvatar()
{
//TODO loading the images to confirm they exist is causing to much load time. alternative has to be found :) maybe onerror tag on the <img tags>
// if ($this->discordUser()->exists()) {
// if(@getimagesize($this->discordUser->getAvatar())) {
// $avatar = $this->discordUser->getAvatar();
// } else {
// $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
// }
// } else {
// $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
// }
return "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
// if ($this->discordUser()->exists()) {
// if(@getimagesize($this->discordUser->getAvatar())) {
// $avatar = $this->discordUser->getAvatar();
// } else {
// $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
// }
// } else {
// $avatar = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
// }
return 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($this->email)));
}
/**
@ -279,9 +265,14 @@ class User extends Authenticatable implements MustVerifyEmail
public function getVerifiedStatus()
{
$status = '';
if ($this->hasVerifiedEmail()) $status .= 'email ';
if ($this->discordUser()->exists()) $status .= 'discord';
if ($this->hasVerifiedEmail()) {
$status .= 'email ';
}
if ($this->discordUser()->exists()) {
$status .= 'discord';
}
$status = str_replace(' ', '/', $status);
return $status;
}
@ -295,7 +286,15 @@ class User extends Authenticatable implements MustVerifyEmail
public function reVerifyEmail()
{
$this->forceFill([
'email_verified_at' => null,
'email_verified_at' => null
])->save();
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logOnly(['role', 'name', 'server_limit', 'pterodactyl_id', 'email'])
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
}

View file

@ -6,16 +6,22 @@ use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
/**
* Class Voucher
* @package App\Models
*/
class Voucher extends Model
{
use HasFactory, LogsActivity;
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
-> logOnlyDirty()
-> logOnly(['*'])
-> dontSubmitEmptyLogs();
}
/**
* @var string[]
*/
@ -27,19 +33,15 @@ class Voucher extends Model
'expires_at',
];
protected $dates = [
'expires_at'
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'expires_at' => 'datetime',
'credits' => 'float',
'uses' => 'integer'
];
'uses' => 'integer', ];
protected $appends = ['used', 'status'];
@ -59,9 +61,6 @@ class Voucher extends Model
return $this->getStatus();
}
/**
*
*/
public static function boot()
{
parent::boot();
@ -84,17 +83,22 @@ class Voucher extends Model
*/
public function getStatus()
{
if ($this->users()->count() >= $this->uses) return 'USES_LIMIT_REACHED';
if (!is_null($this->expires_at)) {
if ($this->expires_at->isPast()) return __('EXPIRED');
if ($this->users()->count() >= $this->uses) {
return 'USES_LIMIT_REACHED';
}
if (! is_null($this->expires_at)) {
if ($this->expires_at->isPast()) {
return __('EXPIRED');
}
}
return __('VALID');
}
/**
* @param User $user
* @param User $user
* @return float
*
* @throws Exception
*/
public function redeem(User $user)
@ -111,7 +115,7 @@ class Voucher extends Model
}
/**
* @param User $user
* @param User $user
* @return null
*/
private function logRedeem(User $user)

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