Compare commits

...

465 commits

Author SHA1 Message Date
Dane Everitt
cd5c2bc5fd
Fix incompatible signatures after update 2022-02-27 10:50:25 -05:00
Dane Everitt
e02f4b8433
Raw styling needed for mass actions bar 2022-02-26 22:06:47 -05:00
Dane Everitt
308a7f3a90
Add input field 2022-02-26 21:51:40 -05:00
Dane Everitt
ae522f1585
More color adjustments 2022-02-26 21:26:53 -05:00
Dane Everitt
ff460805c0
Update colors 2022-02-26 20:59:29 -05:00
Dane Everitt
0bab962337
Add underlying design component for a dialog 2022-02-26 17:05:30 -05:00
Dane Everitt
f4119df0aa
Add basic dropdown styling using headless ui 2022-02-26 15:13:13 -05:00
Dane Everitt
eb56be8021
Merge branch 'v2' into dane/fiddle-with-new-tables 2022-02-26 12:03:49 -05:00
Dane Everitt
134ba508e2
Fix i18n 2022-02-26 12:01:05 -05:00
Dane Everitt
0dddcfca36
Continue to throw exceptions when file is not found 2022-02-26 11:54:12 -05:00
Dane Everitt
c16fa92904
Cleanup php-cs-fixer 2022-02-26 11:51:18 -05:00
Dane Everitt
0e1c3a76f4
Test cleanup 2022-02-26 11:49:59 -05:00
Dane Everitt
941cb9eda4
A little composer magic to really make this a good project 2022-02-26 11:46:31 -05:00
Dane Everitt
e236f74d1d
First pass at getting us on Laravel 9 2022-02-26 11:15:20 -05:00
Dane Everitt
cc43a6ec64
Drop remaining references to alerts package 2022-02-26 10:40:45 -05:00
Dane Everitt
a3cdfa83f2
Bump support for 8.1 minimum 2022-02-26 10:32:09 -05:00
Dane Everitt
bc59ffbf37
Initial concept at a user table listing using Tailwind JIT & Headless UI 2022-02-20 19:10:58 -05:00
Dane Everitt
4920ca41a8
Fix colors 2022-02-20 18:35:44 -05:00
Dane Everitt
06cc228f26
Move to TailwindCSS 3 and JIT support to slowly replace twin.macro/styled-components 2022-02-20 18:22:56 -05:00
Dane Everitt
cb4d4b5ce6
Make personal access tokens soft-deletable; update front-end 2022-02-20 13:07:12 -05:00
Dane Everitt
4cbdaa6699
Require php 8 2022-02-20 12:15:35 -05:00
Dane Everitt
14c9a0a031
Merge branch 'develop' into v2 2022-02-20 11:49:53 -05:00
Dane Everitt
fb1f75353d
Run cs-fixer on files 2022-02-13 19:06:53 -05:00
Dane Everitt
afd0a8f768
Update phpstan 2022-02-13 19:04:11 -05:00
Dane Everitt
b9016aa25e
Update dependencies to latest versions where relevant; drop dusk and debugbar 2022-02-13 18:50:22 -05:00
Dane Everitt
e683c0a518
Fix failing tests related to these changes 2022-02-13 18:32:02 -05:00
Dane Everitt
341ff6e178
Cleanup test framework; drop all the unused browser tests 2022-02-13 17:59:53 -05:00
Dane Everitt
a4359064ca
Cleaner login flows; hide options that aren't relevant to the user 2022-02-13 17:44:59 -05:00
Dane Everitt
fac4902ccc
Don't trigger an internal error if hitting 2fa endpoint and it isn't enabled 2022-02-13 17:33:12 -05:00
Dane Everitt
d6cd0c6230
Login checkpoint cleanup, hide prompt when leaving screen 2022-02-13 17:27:38 -05:00
Dane Everitt
d9d9b1748f
Cleanup key UI when logging in 2022-02-13 16:54:12 -05:00
Dane Everitt
9032699deb
Use SWR for security key index 2022-02-13 15:44:19 -05:00
Dane Everitt
b43e8835bb
Don't store a new key on every login 2022-02-13 15:06:08 -05:00
Dane Everitt
2d2352017d
Fix login authentication using security key 2022-02-13 14:57:45 -05:00
Dane Everitt
09497c234a
Support authenticating the provided key when loggin in 2022-02-13 14:44:50 -05:00
Dane Everitt
54c7207836
Fix authentication request creation 2022-02-13 14:23:20 -05:00
Dane Everitt
969d40d6c1
Logic cleanup after a bit of dust collection 2022-02-13 14:15:18 -05:00
Dane Everitt
8971e78ab5
Merge branch 'v2' into dane/webauthn 2022-02-13 13:46:15 -05:00
Dane Everitt
cd84663ffe
Fix missing import from merge 2022-02-13 13:17:33 -05:00
Dane Everitt
fe4d283311
Fix feature pathing 2022-02-13 13:00:52 -05:00
Dane Everitt
ca6f501c70
Merge branch 'develop' into v2 2022-02-13 12:55:02 -05:00
Matthew Penner
b0d6be802c
Merge branch 'develop' into v2 2022-01-17 20:30:46 -07:00
Matthew Penner
d1c9af8f04
Merge branch 'develop' into v2 2022-01-08 15:20:23 -07:00
Matthew Penner
6e4e0bc0a4
ui(admin): update server image when switching eggs 2021-11-17 14:28:19 -07:00
Matthew Penner
81ff03c2a3
ui(admin): add 'externalId' field for users 2021-11-17 14:26:23 -07:00
Matthew Penner
9c92d51e28
ui: organize and tweak to flash messages 2021-11-17 13:52:17 -07:00
Matthew Penner
d8da79b7fb
yarn: upgrade dependencies 2021-11-12 13:16:15 -07:00
Matthew Penner
ce0bc477c2
ui(admin): fix egg variables 2021-11-04 14:33:24 -06:00
Matthew Penner
f7c824743f
ui(editor): prevent initialContent being duplicated 2021-11-04 13:44:10 -06:00
Matthew Penner
5359ef8407
api(app): allow removing a server's startup command 2021-11-04 11:47:08 -06:00
Matthew Penner
34d20b2bf0
api: remove old debug logs 2021-11-04 11:37:33 -06:00
Matthew Penner
fad4005168
Merge branch 'develop' into v2 2021-11-04 11:34:11 -06:00
Matthew Penner
44f4cbc4c3
Merge branch 'develop' into v2 2021-11-03 15:35:22 -06:00
Matthew Penner
728adfe388
server(startup): make startup nullable; resolves #3721 2021-11-03 15:32:53 -06:00
Dane Everitt
cdd8eabcc0
Add phpstan for static analysis (#3718) 2021-10-30 13:41:38 -07:00
Matthew Penner
871d0bdd1c
ui(admin): add egg exporting 2021-10-30 14:23:29 -06:00
Matthew Penner
469c0b40a3
ui(admin): add views for settings 2021-10-30 13:12:02 -06:00
Matthew Penner
70cf5c17aa
ui(admin): basic server creation 2021-10-29 00:04:28 -06:00
Matthew Penner
cc2ed97b0f
ui: fix SearchableSelect not selecting when hitting enter 2021-10-28 23:48:07 -06:00
Matthew Penner
c48d573cc9
Merge branch 'develop' into v2 2021-10-28 22:59:12 -06:00
Matthew Penner
5e99bb8dd6
ui(admin): fix server startup variables 2021-10-24 16:05:00 -06:00
Matthew Penner
cf1cc97340
ui(admin): rough layout on new server page 2021-10-23 15:19:49 -06:00
Matthew Penner
bee7c4515c
eggs: update default script values 2021-10-23 14:31:23 -06:00
Matthew Penner
f6ac9707fa
Merge branch 'develop' into v2 2021-10-23 14:22:18 -06:00
Matthew Penner
7f7506e5a9
ui(admin): fix bad redirect on egg delete 2021-10-23 14:19:50 -06:00
Matthew Penner
0e870ab256
fix integration tests 2021-10-23 14:17:05 -06:00
Matthew Penner
2948e344d2
fix integration tests 2021-10-23 13:34:41 -06:00
Matthew Penner
b966069946
Merge branch 'develop' into v2 2021-10-23 13:26:25 -06:00
Matthew Penner
cddf2ce41c
ui(admin): new egg page 2021-10-23 13:13:25 -06:00
Matthew Penner
336923ec18
ui(admin): fix container width on server startup 2021-10-23 12:31:30 -06:00
Matthew Penner
3b5fa34d85
ui(admin): add delete confirmation for egg variables 2021-10-23 12:29:17 -06:00
Dane Everitt
a3572006cb
Merge branch 'dane/type-cleanup' into v2 2021-10-10 13:21:44 -07:00
Dane Everitt
8486c914ae
More fixup for egg handling 2021-10-10 13:21:21 -07:00
Dane Everitt
85c8f4884f
Cleanup more of the server screen typings 2021-10-10 13:13:10 -07:00
Dane Everitt
f6998018b4
Cleanup more of the server UI logic 2021-10-10 12:03:28 -07:00
Dane Everitt
e3aca937b5
Add more type cleanup and have a completed server type 2021-10-10 11:32:07 -07:00
Dane Everitt
00d0f49ede
Cleanup typing for server and expose more useful endpoint and transformer logic 2021-10-09 12:02:32 -07:00
Matthew Penner
3afd8b9f03
just work, thanks 2021-10-06 15:10:46 -06:00
Matthew Penner
fb14603e17
love me some magic numbers 2021-10-06 15:06:53 -06:00
Matthew Penner
9ab8f946ec
this should fix tests!
Pro-tip: disable function calls that don't work instead of trying
to figure out why they don't work :)
2021-10-06 15:02:30 -06:00
Matthew Penner
d945ce76f2
hopefully fix integration tests 2021-10-06 14:45:44 -06:00
Dane Everitt
7ec78d9400
Move feature limits box into own component 2021-10-03 18:38:45 -07:00
Matthew Penner
6814811a19
ui(admin): tweaks to spacing 2021-10-03 18:26:44 -06:00
Dane Everitt
817a2bf8ae
Use grid and server SWR for settings box 2021-10-03 16:24:34 -07:00
Matthew Penner
15aed3192b
ui(admin): consistency with button naming 2021-10-03 17:11:27 -06:00
Matthew Penner
b5b65e3d81
ui(admin): fix max width container 2021-10-03 17:11:26 -06:00
Dane Everitt
fcfca0730e
Use SWR to fetch and track the selected server on server detail pages 2021-10-03 16:10:06 -07:00
Dane Everitt
1405c881a8
Fix sidebar styling and remove hacky fixed positioning 2021-10-03 15:37:01 -07:00
Matthew Penner
b070efce98
ui(admin): add allocation delete button 2021-10-03 16:28:58 -06:00
Matthew Penner
6df90a12d8
ui(admin): add delete egg variable button 2021-10-03 16:07:13 -06:00
Matthew Penner
b2aa05dc07
ui(admin): add new egg variable option 2021-10-03 16:07:13 -06:00
Matthew Penner
1eed25dcc7
ui(admin): finish egg variable editing 2021-10-03 16:07:13 -06:00
Dane Everitt
749dc70e71
Unfuck lock file 2021-10-03 13:50:16 -07:00
Matthew Penner
7239f0e336
ui(admin): add egg variable elements 2021-10-01 11:25:06 -06:00
Matthew Penner
e2de673488
Merge branch 'develop' into v2 2021-09-30 16:08:11 -06:00
Matthew Penner
2dfc264bf8
ui: tweaks to Editor 2021-09-19 12:33:55 -06:00
Dane Everitt
5a4d1a668f
UI tweaking and transformer for the stored keys 2021-09-19 11:24:38 -07:00
Dane Everitt
81a6a8653f
Fix up creation of keys to fail when registering the same key again 2021-09-19 11:24:33 -07:00
Dane Everitt
1053b5d605
Get basic storage of webauthn tokens working 2021-09-19 11:24:33 -07:00
Dane Everitt
eaf12aec60
Clean out existing webauthn logic, implement base logic for base package 2021-09-19 11:24:31 -07:00
Matthew Penner
0103a0c31e
ui(admin): minor ui tweaks to server startup 2021-09-18 12:27:32 -06:00
Matthew Penner
21d717080a
ui(admin): minor ui tweaks to server settings 2021-09-18 12:26:36 -06:00
Matthew Penner
32e8406c37
ui(admin): fix node behind proxy and automatic allocation settings 2021-09-18 12:24:13 -06:00
Matthew Penner
ef47f21943
ui(admin): fix inconsistent select styling 2021-09-18 12:20:19 -06:00
Matthew Penner
b2d3aed520
ui(admin): fix egg delete redirect 2021-09-18 12:05:19 -06:00
Matthew Penner
b125830859
ui(admin): add startup and file configuration editing for eggs 2021-09-18 11:45:32 -06:00
Matthew Penner
7d1cb2971f
api(application): allow updating node description
fixes #3624
2021-09-18 11:00:31 -06:00
Matthew Penner
6abf4a302c
ui(admin): add basic egg editing 2021-09-17 19:56:44 -06:00
Matthew Penner
66443dd5d3
ui(admin): implement egg about 2021-09-17 15:40:17 -06:00
Matthew Penner
8d0dd42475
ui(admin): add egg install editing 2021-09-17 14:47:56 -06:00
Matthew Penner
e8ddadc608
ui(admin): implement basic egg importing 2021-09-17 13:48:20 -06:00
Matthew Penner
107cf72269
ui(admin): consistency tweaks, add egg pages 2021-09-17 13:06:31 -06:00
Matthew Penner
db4fb3ac53
ui(admin): don't block render until egg is loaded 2021-09-16 20:19:10 -06:00
Matthew Penner
34da772db7
ui(admin): show alias in allocation dropdown; fixes #3434 2021-09-16 16:54:02 -06:00
Matthew Penner
9b7cea5d04
ui(admin): server startup fixes
- when a new egg is selected, set the variable values to the egg's default, not just the placeholder
- when a new egg is selected, change the server's startup command to the default; fixes #3397
- remove move un-needed code
2021-09-16 16:46:41 -06:00
Matthew Penner
98da1f13a1
remove un-needed code 2021-09-16 15:07:14 -06:00
Matthew Penner
df895f4a9f
ui(admin): server edit cleanup, fix startup form 2021-09-16 15:03:51 -06:00
Matthew Penner
95f3eb54db
ui(admin): get server startup ui working 2021-09-15 21:22:15 -06:00
Matthew Penner
a6ab61adba
ui(admin): allow editing allocations for servers 2021-09-15 15:37:17 -06:00
Matthew Penner
656ac62ad2
ui: fix fields with invalid type 2021-09-15 11:41:02 -06:00
Matthew Penner
6df2368264
ui(admin): server editing improvements 2021-09-15 11:18:58 -06:00
Matthew Penner
23a160b9e1
Merge branch 'develop' into v2 2021-09-15 10:30:40 -06:00
Matthew Penner
df11129291
ui(admin): fix UserForm not allowing null role 2021-09-13 19:15:21 -06:00
Matthew Penner
24d1799322
api(application): fix 'root_admin' not being set 2021-09-13 17:24:16 -06:00
Matthew Penner
5843c34240
fix type error with WebauthnKeyTransformer 2021-09-13 02:34:01 -06:00
Matthew Penner
4da38891c7
admin: fix nest create not working 2021-09-13 01:53:10 -06:00
Matthew Penner
004a13a5f7
fix null admin role breaking user transformer 2021-09-13 00:58:39 -06:00
Matthew Penner
a92f3648b2
ui(admin): start work on egg install settings 2021-09-13 00:07:09 -06:00
Matthew Penner
a615b7fa70
ui(admin): start work on server startup settings 2021-09-12 23:50:12 -06:00
Matthew Penner
6362731d55
ui(admin): implement basic server editing 2021-09-12 22:15:45 -06:00
Matthew Penner
d0a78ec067
ui(admin): add new node page 2021-09-12 21:22:33 -06:00
Matthew Penner
3c01dbbcc5
ui(admin): add allocation table, implement allocation creator 2021-09-12 19:40:10 -06:00
Matthew Penner
6b746440fc
config(app): remove un-used alias 2021-09-12 17:48:28 -06:00
Matthew Penner
c7ab6b9e6c
ui(admin): fix validation rules on UserForm 2021-09-12 17:24:31 -06:00
Matthew Penner
38ff35873e
ui: make PowerControls buttons uppercase 2021-09-12 17:16:26 -06:00
Matthew Penner
1ead83383d
ui(admin): add 'root_admin' field to UserForm 2021-09-12 17:13:00 -06:00
Matthew Penner
fc2f8744da
ui(admin): add user editing 2021-09-12 17:00:22 -06:00
Matthew Penner
c716be263b
Merge branch 'matthewpi/fix-server-build-modification' into v2 2021-09-12 16:31:42 -06:00
Matthew Penner
0c943248bc
cleanup 2021-09-12 16:22:34 -06:00
Matthew Penner
927677e490
yarn: switch to berry 2021-09-12 16:13:57 -06:00
Matthew Penner
222300ff17
code cleanup, fix errors 2021-09-12 15:56:47 -06:00
Matthew Penner
8acce201d6
Merge branch 'develop' into v2 2021-09-12 15:47:27 -06:00
Matthew Penner
1880b83944
server: fix build modification not being persisted 2021-09-12 15:27:02 -06:00
Matthew Penner
42bfaf9a19
ui: fix security keys render error 2021-09-11 16:22:58 -06:00
Matthew Penner
e384c0d5c3
Merge branch 'develop' into v2 2021-09-11 16:13:11 -06:00
LoaiDev
9f00c353b1
Fix named exports for state (#3578) 2021-09-11 12:20:53 -07:00
Matthew Penner
ded71c9af1
ui(admin): tweaks to sidebar 2021-09-11 12:34:40 -06:00
Matthew Penner
e4fc0f5ac3
yarn and composer updates 2021-09-10 15:30:22 -06:00
Matthew Penner
d167ef1f89
Merge branch 'develop' into v2 2021-09-01 16:28:56 -06:00
Matthew Penner
4bf9143789
Merge branch 'develop' into v2 2021-08-24 11:40:40 -06:00
Matthew Penner
8f43ca66df
actually fix bad merge 2021-08-21 13:35:55 -06:00
Matthew Penner
69df0adbd9
Merge branch 'issue/3553' into v2 2021-08-21 12:57:42 -06:00
Matthew Penner
14eb482791
fix bad merge 2021-08-21 12:56:56 -06:00
Matthew Penner
b26556e201
Merge branch 'develop' into v2 2021-08-21 12:50:51 -06:00
Matthew Penner
3b72049d05
migrations: add foreign keys for mount relations 2021-08-21 12:15:39 -06:00
Dane Everitt
15b03b96fc
Be consistent with form order 2021-08-07 16:24:36 -07:00
Dane Everitt
874e7afce3
Merge branch 'dane/sanctum' into v2 2021-08-07 16:19:19 -07:00
Dane Everitt
9fe1527f10
Get tests back in working order 2021-08-07 16:19:01 -07:00
Dane Everitt
baf8a9fb2c
Skip failing application API endpoints 2021-08-07 16:17:16 -07:00
Dane Everitt
436e686037
Apply php-cs-fixer changes 2021-08-07 16:10:24 -07:00
Dane Everitt
cec96062e3
Get client API tests back into passing order 2021-08-07 16:08:29 -07:00
Dane Everitt
7169b481b1
Fix broken php-cs-fixer config 2021-08-07 16:00:14 -07:00
Dane Everitt
b19ce4df7c
Fix transformer handling of closures 2021-08-07 16:00:04 -07:00
Dane Everitt
5a1cceb9d2
Fix bindings for schedule tasks 2021-08-07 15:45:36 -07:00
Dane Everitt
3a83a2d5ac
Remove last references to removed api_key model 2021-08-07 15:31:52 -07:00
Dane Everitt
815ce0e451
Fix test coverage for creating account API keys 2021-08-07 15:20:43 -07:00
Dane Everitt
3d14974d64
Correctly set the resource name on empty transformers 2021-08-07 15:06:00 -07:00
Dane Everitt
1bc1b87422
Fix call to getIncludesForTransformer 2021-08-07 14:54:22 -07:00
Dane Everitt
9e0ec8fca8
Update access token generation to return more useful class 2021-08-07 14:52:58 -07:00
Dane Everitt
fdd90b3be7
Remove unnecessary function 2021-08-07 14:32:40 -07:00
Dane Everitt
bbf2f33c5e
Replace with corrected static calls 2021-08-07 13:25:35 -07:00
Dane Everitt
5c81f820d8
Remove old application API base transformer 2021-08-07 13:25:06 -07:00
Dane Everitt
cf500a1a54
Use a standardized transformer base; replace all client transformers to call that base 2021-08-07 13:06:45 -07:00
Dane Everitt
2203a4d87e
Normalize logic across client and application API 2021-08-07 11:55:49 -07:00
Dane Everitt
bc1db626e7
Fix up subuser controller to use better binding checks 2021-08-07 11:15:44 -07:00
Dane Everitt
74426a97f4
Simplify logic for checking for missing unbound models 2021-08-07 11:15:30 -07:00
Dane Everitt
5b33a6a434
Update .gitignore 2021-08-07 09:21:19 -07:00
Dane Everitt
74a77a0e86
Merge branch 'develop' into v2 2021-08-07 09:21:05 -07:00
Dane Everitt
4d1a7e6637
Improve client API route model binding and prevent accidental route access without valid model binds 2021-08-04 22:20:43 -07:00
Matthew Penner
56556e9660
ui(admin): implement new mount page 2021-08-04 22:41:52 -06:00
Dane Everitt
e1089e0b73
Update calls to abstract class 2021-08-04 21:36:57 -07:00
Dane Everitt
e8474271b3
Actually include sanctum post-force push 2021-08-04 21:17:56 -07:00
Matthew Penner
00b44bf3bb
ui(admin): tweaks to server management 2021-08-04 22:16:52 -06:00
Dane Everitt
47b895a98a
Update existing application API to use simplified user permission checking 2021-08-04 21:15:19 -07:00
Dane Everitt
622d292f39
Update the client API to be consistent with how validation is handled 2021-08-04 21:15:19 -07:00
Dane Everitt
b47d262ee0
Initial pass at deleting as much removed logic as possible; still need to migrate old keys and permissions over 2021-08-04 21:15:18 -07:00
Dane Everitt
dfff8ad667
Cleanup frontend to only pass the required description field 2021-08-04 21:15:18 -07:00
Dane Everitt
374910d73a
Fix support for authorization using sanctum tokens 2021-08-04 21:15:18 -07:00
Dane Everitt
1a3451fb0d
Update underlying model representation for PATs 2021-08-04 21:15:18 -07:00
Dane Everitt
d60e8a193b
Very basic working implementation of sanctum for API validation 2021-08-04 21:15:16 -07:00
Matthew Penner
4b32828423
Merge branch 'develop' into v2 2021-08-04 21:40:38 -06:00
Matthew Penner
0ab124f026
Merge branch 'fix/backup-ui' into v2 2021-08-04 17:29:57 -06:00
Matthew Penner
94198f3bfd
Merge branch 'develop' into v2 2021-08-04 17:27:14 -06:00
Matthew Penner
f44833ca9c
ui(server): fix used backup count 2021-08-04 17:22:57 -06:00
Matthew Penner
178a886d3b Merge branch 'develop' into v2 2021-08-03 21:13:13 -06:00
Matthew Penner
c6dccc568d ui(server): add file upload status 2021-08-03 20:41:28 -06:00
Matthew Penner
3d3df30903 Merge branch 'fix/upgrade-command' into v2 2021-08-03 20:40:45 -06:00
Matthew Penner
38ddcfb0d9 Merge branch 'fix/backups' into v2 2021-08-03 20:40:40 -06:00
Matthew Penner
b9ab6e2c33 editor: fix Mod-s hotkey and save button 2021-08-03 20:21:52 -06:00
Matthew Penner
07617bcd27 backups: properly query backups 2021-08-03 16:37:22 -06:00
Matthew Penner
c46131e7ad backups: default is_successful to false 2021-08-03 15:46:08 -06:00
Matthew Penner
a39802cb4e Merge branch 'develop' into v2 2021-08-03 14:41:12 -06:00
Matthew Penner
d9740fff65 cmd(upgrade): fix force and seed flags being ignored 2021-08-03 14:35:48 -06:00
Matthew Penner
174cca9e49 tests(integration): fix user controller test, actually 2021-08-02 22:26:05 -06:00
Matthew Penner
d9d79d8c95 tests(integration): fix user controller test, allegedly 2021-08-02 22:23:47 -06:00
Matthew Penner
d2864410ed user: yeet remaining name_* fields, again... 2021-08-02 22:17:11 -06:00
Matthew Penner
4d77d486ec user: yeet remaining name_* fields 2021-08-02 22:10:53 -06:00
Matthew Penner
833b47abc5 actions(tests): run on v2 branch 2021-08-02 15:25:44 -06:00
Matthew Penner
346271a9a3 ui(admin): add role delete button 2021-08-02 12:29:29 -06:00
Matthew Penner
89a0244cf2 ui: fix usePersistedState erroring when key doesn't exist 2021-08-02 12:23:38 -06:00
Matthew Penner
1d9927d7f8 ui(admin): persist sidebar collapsed state 2021-08-02 12:19:18 -06:00
Matthew Penner
98cb7370f0 ui(admin): fix null value being passed to input 2021-08-02 11:53:04 -06:00
Matthew Penner
2b8565fd1a yarn: upgrade dependencies 2021-08-02 11:47:13 -06:00
Matthew Penner
455be4bb52 composer: upgrade dependencies 2021-08-02 11:47:05 -06:00
Matthew Penner
98ed8e431b ui(admin): cleanup 2021-08-02 11:32:09 -06:00
Matthew Penner
27ed07e81f ui(admin): fix user sidebar alignment when collapsed 2021-08-02 10:26:29 -06:00
Matthew Penner
fffe3a7f8b ui(admin): fix node settings button 2021-08-02 10:23:58 -06:00
Matthew Penner
0313bdb1cb ui(admin): add role edit form 2021-08-02 10:08:52 -06:00
Matthew Penner
030bc2d8ef ui(admin): fix server table links 2021-08-02 10:01:55 -06:00
Matthew Penner
11fc88c849 ui(admin): implement user and node servers tab 2021-08-02 09:54:13 -06:00
Matthew Penner
bf9dfa87da yeet name_first and name_last from users table 2021-07-28 21:31:00 -06:00
Matthew Penner
f1be653486 ui(admin): remove api key components 2021-07-28 11:56:25 -06:00
Matthew Penner
b8b481b57b ui: fix borked styling 2021-07-25 17:14:55 -06:00
Matthew Penner
7f290c6e52 ui(admin): change other logout button 2021-07-25 16:10:12 -06:00
Matthew Penner
e56aef31bc api(application): allow updating a user's role 2021-07-25 15:59:58 -06:00
Matthew Penner
ca4046e818 ui(admin): fix new roles not mutating swr 2021-07-25 15:54:16 -06:00
Matthew Penner
25feeaa9f5 ui(admin): add role select for user management 2021-07-25 15:51:39 -06:00
Matthew Penner
58f0bbbb9b ui(files): fix error with bad URL 2021-07-25 13:43:58 -06:00
Matthew Penner
3c2a6e1136 ui(files): add pull file modal 2021-07-25 13:24:52 -06:00
Matthew Penner
01242a805d actions(tests): remove mariadb:10.5 2021-07-24 15:20:30 -06:00
Matthew Penner
6acb989965 actions(tests): use the database... 2021-07-24 15:08:13 -06:00
Matthew Penner
b25a0c4cbc actions(tests): use 'php artisan test', actually... 2021-07-24 15:05:37 -06:00
Matthew Penner
544d1f5746 actions(tests): use 'php artisan' for running tests 2021-07-24 15:04:06 -06:00
Matthew Penner
31847a8566 composer: upgrade dependencies 2021-07-24 15:01:14 -06:00
Matthew Penner
14940d666b yarn: upgrade dependencies 2021-07-24 14:41:45 -06:00
Matthew Penner
88bcb69a8a ui: small tweaks 2021-07-24 14:06:51 -06:00
Matthew Penner
631b4ae2a8 ui: fix ServerRow icon being squished 2021-07-24 12:45:03 -06:00
Matthew Penner
0828d00857 ui(admin): change logout button 2021-07-24 12:36:10 -06:00
Matthew Penner
426d955643 Merge branch 'develop' into feature/react-admin 2021-07-24 12:28:01 -06:00
Matthew Penner
27c93365e9 ui(admin): add user create and user update 2021-07-24 12:23:33 -06:00
Matthew Penner
1d290919b7 ui: fix devtools issue with missing DOCTYPE 2021-07-24 12:23:21 -06:00
Matthew Penner
7b3c71f6a7 ui(server): fix console z-index 2021-07-24 11:43:00 -06:00
Matthew Penner
26438fa034 ui(server/files): add validation for duplicate directory names 2021-07-24 11:32:31 -06:00
Matthew Penner
be011906e6 ui(admin): lower table loader height
fixes tables growing while the page is loading, this is very noticable
when latency to the API is low, which means the loader is only visible
for 1/30th to 1/20th of a second.
2021-07-22 11:21:31 -06:00
Matthew Penner
361596e051 ui: I HATE STYLED-COMPONENTS WITH TYPESCRIPT 2021-07-22 11:15:27 -06:00
Matthew Penner
23de3d68f3 ui: remove old console logs 2021-07-21 12:21:10 -06:00
Matthew Penner
3b1a0e22a7 ui: update codemirror editor 2021-07-21 12:18:53 -06:00
Matthew Penner
d2d62b7463 ui(admin): tweaks to SelectField styling 2021-07-20 15:29:22 -06:00
Matthew Penner
84b207eddb ui(admin): fix eslint errors, add CreateAllocationForm 2021-07-20 14:38:11 -06:00
Matthew Penner
d32b3a0473 Merge branch 'develop' into feature/react-admin 2021-07-20 13:01:28 -06:00
Matthew Penner
6879ca4054 ui(admin): cleanup table hooks, more... 2021-07-19 14:37:09 -06:00
Matthew Penner
7524bbe8ee ui(admin): cleanup table hooks 2021-07-19 14:34:10 -06:00
Matthew Penner
30f09a4098 ui: fix build due to wrong import path 2021-07-18 13:36:54 -06:00
Matthew Penner
08c780c388 tests: cleanup unneeded code 2021-07-18 11:29:39 -06:00
Matthew Penner
64110d84af tests(unit): add RequireTwoFactorAuthenticationTest 2021-07-18 11:28:14 -06:00
Matthew Penner
790f109e66 api(remote): update sftp auth controller 2021-07-17 17:22:47 -06:00
Matthew Penner
f9114e2de0 feat(ssh-keys): add ssh key endpoints and ui components 2021-07-17 15:45:46 -06:00
Matthew Penner
9d64c6751b app: update models 2021-07-17 15:18:05 -06:00
Matthew Penner
28bc86e23b ui(account): implement delete security key button 2021-07-17 14:45:23 -06:00
Matthew Penner
59f2ea37d8 ui(auth): add support for using a security key 2021-07-17 14:45:23 -06:00
Matthew Penner
3c21770c25 ui(account): add security key management 2021-07-17 14:45:23 -06:00
Matthew Penner
31c2ef5279 webauthn: update login flow to support other 2fa methods 2021-07-17 14:45:23 -06:00
Matthew Penner
42a3e740ba yarn: add @types/webappsec-credential-management 2021-07-17 14:45:23 -06:00
Matthew Penner
28146f5bb6 webauthn: add controllers and transformers 2021-07-17 14:45:23 -06:00
Matthew Penner
cdd07fa275 composer: require asbiin/laravel-webauthn 2021-07-17 14:45:23 -06:00
Matthew Penner
f137192113 Merge branch 'develop' into feature/react-admin 2021-07-17 14:45:12 -06:00
Matthew Penner
c09cfd4c76 ui(server): fix admin popout link 2021-07-17 14:33:03 -06:00
Matthew Penner
bc4e28578d Merge branch 'develop' into feature/react-admin 2021-07-17 11:12:02 -06:00
Matthew Penner
7ad32293f3 ui(admin): table improvements 2021-07-15 16:10:55 -06:00
Matthew Penner
11bafe42b4 composer: upgrade, again. 2021-07-15 16:10:34 -06:00
Matthew Penner
779b0eca67 ui(admin): fix tables being covered by no items message 2021-07-14 16:59:37 -06:00
Matthew Penner
c0e9f1adee ui(admin): make all tables searchable and sortable 2021-07-14 16:43:59 -06:00
Matthew Penner
8f8d66584d yarn: update codemirror/next version 2021-07-13 16:06:00 -06:00
Matthew Penner
2d412e66ac ui: fix global styles 2021-07-13 15:58:44 -06:00
Matthew Penner
ef5380e59d Merge branch 'matthewpi/yarn-upgrade' into feature/react-admin 2021-07-13 15:25:43 -06:00
Matthew Penner
e86f9a5ed9 Merge branch 'matthewpi/database-tls' into feature/react-admin 2021-07-13 15:23:07 -06:00
Matthew Penner
ea4bcf0b9c Merge branch 'develop' into feature/react-admin 2021-07-13 15:21:16 -06:00
Matthew Penner
2963b77b41 revert 'test fixes' 2021-07-13 15:16:53 -06:00
Matthew Penner
7776436d18 config: add ssl/tls options for mysql and redis 2021-07-11 15:00:30 -06:00
Matthew Penner
8d5dbbb57b attempt to fix lock error on mariadb:10.6 tests 2021-07-11 14:56:27 -06:00
Matthew Penner
63a466c79a yarn: fix hot-loader version 2021-07-11 14:01:42 -06:00
Matthew Penner
3bf34024c7 yarn: remove yarn-deduplicate, delete .yarnclean 2021-07-11 14:01:42 -06:00
Matthew Penner
f4442d0b1c yarn: fix webpack-dev-server 2021-07-11 14:01:42 -06:00
Matthew Penner
96788f9269 fix .php_cs.dist error 2021-07-11 14:01:42 -06:00
Matthew Penner
01464b6da5 actions(tests): use mariadb 10.5 instead of 10.6
10.6.2 (latest docker build as of 2021-07-09) is a RC, not a stable release.
10.6.3 is out and is a stable release, however, no docker image is built for it yet.
10.6 should be added or replace 10.5 once a stable docker image is available.
2021-07-11 14:01:42 -06:00
Matthew Penner
7dacf90718 actions(tests): only run once for pull requests 2021-07-11 14:01:42 -06:00
Matthew Penner
c49e8b1a07 actions(tests): fix bad syntax 2021-07-11 14:01:42 -06:00
Matthew Penner
98c54cba3a yarn: update dependencies, prepare for Plug'n'Play 2021-07-11 14:01:42 -06:00
Matthew Penner
3a3caee715 yarn: upgrade to yarn 2 2021-07-11 14:01:42 -06:00
Matthew Penner
556885f682 ui(admin): uncomment delete buttons 2021-06-06 14:08:39 -06:00
Matthew Penner
01c03b6b77 Merge branch 'develop' into feature/react-admin 2021-06-06 14:06:14 -06:00
Matthew Penner
a45fc525a9 deps(yarn): add 'react-select' 2021-05-22 13:41:17 -06:00
Matthew Penner
8aa9641ec2 ui(admin): too many changes, not enough commits 2021-05-20 16:00:46 -06:00
Matthew Penner
bca2338863 ui(admin): add search and sort to ServersContainer 2021-05-18 20:53:42 -06:00
Matthew Penner
ae88a01bea fix eslint and admin not loading 2021-05-18 17:20:49 -06:00
Matthew Penner
a3b59f24af Merge branch 'develop' into feature/react-admin 2021-05-18 17:07:25 -06:00
Matthew Penner
8a24c1a142 ui(admin): node changes 2021-03-23 17:47:24 -06:00
Matthew Penner
7b38f05019 ui(nests): fix broken pagination 2021-03-23 17:10:30 -06:00
Matthew Penner
984a774811 api(app): add NodeInformationController 2021-03-23 16:08:17 -06:00
Matthew Penner
c521d37ddd api(app): more consistent handling of per_page query param 2021-03-23 15:57:29 -06:00
Matthew Penner
49de31bf4c Merge branch 'develop' into feature/react-admin 2021-03-21 15:49:41 -06:00
Matthew Penner
2e046ae258 Merge branch 'develop' into feature/react-admin 2021-03-12 14:13:22 -07:00
Matthew Penner
7e8cb52d88 ui(admin): work on Node editing 2021-03-12 14:12:45 -07:00
Matthew Penner
59e5017fd8 api: fix problem with transformers 2021-03-09 08:14:48 -07:00
Matthew Penner
08546e6076 ui(admin): implement NewDatabaseContainer.tsx 2021-03-06 16:57:13 -07:00
Matthew Penner
e9546c70bd ui(admin): add CopyOnClick button to NodeConfigurationContainer 2021-03-06 16:08:42 -07:00
Matthew Penner
5d1568cf84 api(client): fix TwoFactorController 2021-03-06 15:53:45 -07:00
Matthew Penner
5fe86f164e api(client): fix AccountController 2021-03-06 15:49:44 -07:00
Matthew Penner
264c3865b2 api(application): fix api key permissions 2021-03-06 15:41:34 -07:00
Matthew Penner
7d80b5fee7 Merge branch 'develop' into feature/react-admin 2021-03-06 15:37:03 -07:00
Matthew Penner
f78aaea6a3 api: cleanup controllers 2021-03-05 10:03:12 -07:00
Matthew Penner
00c42225e8 tests(integration): fix UserControllerTest 2021-03-05 09:18:50 -07:00
Matthew Penner
350ef1aba5 tests(integration): fix database services 2021-03-05 09:15:23 -07:00
Matthew Penner
ffdf27e606 tests(integration): fix NestControllerTest, and UserControllerTest 2021-03-05 09:08:07 -07:00
Matthew Penner
f6e71e7e76 tests(integration): fix EggControllerTest 2021-03-05 09:04:32 -07:00
Matthew Penner
ee5a661e46 tests(integration): fixes EggControllerTest, NestControllerTest, and UserControllerTest 2021-03-05 09:01:29 -07:00
Matthew Penner
542fd61049 db(migrations): fix order of 'change_port_columns_on_nodes_table' migration 2021-03-05 08:52:25 -07:00
Matthew Penner
8c8de6ac62 php-cs fixes 2021-03-05 08:46:14 -07:00
Matthew Penner
d57060dad9 Merge branch 'develop' into feature/react-admin 2021-03-05 08:45:39 -07:00
Matthew Penner
ffbf7daea6 ui(websocket): bruh... 2021-02-25 15:14:12 -07:00
Matthew Penner
ff4a64bfff admin(ui): remove console.logs 2021-02-24 18:29:10 -07:00
Matthew Penner
46759ba967 admin(ui): add node configuration page 2021-02-24 18:28:24 -07:00
Matthew Penner
b8788d1af1 Merge branch 'develop' into feature/react-admin 2021-02-24 17:30:18 -07:00
Matthew Penner
c053ca7c44 admin(ui): add arrow key support to SearchableSelect 2021-02-17 15:17:37 -07:00
Matthew Penner
dc003a6ada admin(ui): SearchableSelect - differentiate between loading and no items 2021-02-16 13:23:24 -07:00
Matthew Penner
224943cc85 admin(ui): autofocus field on New Nest modal open 2021-02-16 13:05:09 -07:00
Matthew Penner
10fc7e88e0 admin(ui): autofocus field on New Role modal open 2021-02-16 13:04:10 -07:00
Matthew Penner
9b08b6b595 admin(ui): fix SearchableSelect, other tweaks 2021-02-16 13:03:14 -07:00
Matthew Penner
d43e70c97a admin(ui): autofocus field on New Location modal open 2021-02-16 13:01:55 -07:00
Matthew Penner
93be6db530 admin(ui): fix remaining problems with SearchableSelect.tsx 2021-02-15 22:41:19 -07:00
Matthew Penner
3971c4499d admin(ui): fix up SearchableSelect.tsx 2021-02-15 18:48:10 -07:00
Matthew Penner
f790404845 admin(ui): fix updateNode api request 2021-02-11 10:32:13 -07:00
Matthew Penner
3c2094890a admin(ui): use new node port columns 2021-02-11 10:21:49 -07:00
Matthew Penner
5f56ff0fed nodes: rename port columns, add public_ port columns 2021-02-11 10:21:32 -07:00
Matthew Penner
b7ee2195d7 Merge branch 'develop' into feature/react-admin 2021-02-11 09:35:21 -07:00
Matthew Penner
60b630354e ui(admin): fix shit 2021-02-07 16:41:32 -07:00
Matthew Penner
a87fef37ec Merge branch 'develop' into feature/react-admin 2021-02-07 16:16:22 -07:00
Matthew Penner
8e07bb4bc2 ui(admin): start work on DatabaseSelect.tsx 2021-02-05 09:44:31 -07:00
Matthew Penner
22807f1ff4 ui(admin): tweaks to LocationSelect.tsx 2021-02-05 09:44:16 -07:00
Matthew Penner
f3e7aab27c api(application): remove old database node endpoints 2021-02-05 09:43:48 -07:00
Matthew Penner
72be2808f3 deps(node): typescript 3.9.6 => 4.1.3 2021-02-05 09:42:57 -07:00
Matthew Penner
9184cbdf11 ui(admin): tweaks to LocationSelect.tsx 2021-02-02 14:11:54 -07:00
Matthew Penner
7bbe9e8e89 ui(admin): start work on LocationSelect.tsx 2021-01-31 16:48:49 -07:00
Matthew Penner
1c8143ad9d add missing field to Node.php comment 2021-01-30 13:55:13 -07:00
Matthew Penner
be1b05e0ec ui(admin): start work on node settings 2021-01-30 13:53:47 -07:00
Matthew Penner
2a0c99163b ui(admin): add SubNavigation 2021-01-30 13:22:16 -07:00
Matthew Penner
2b5cc99abd feat(database-hosts): allow linking of multiple nodes 2021-01-30 12:50:19 -07:00
Matthew Penner
72983e8385 ui: fix duplicate global styles 2021-01-29 09:07:15 -07:00
Matthew Penner
4e1c07d36e Merge branch 'develop' into feature/react-admin 2021-01-29 09:06:55 -07:00
Matthew Penner
b856ab17bd Merge branch 'develop' into feature/react-admin 2021-01-27 23:18:06 -07:00
Matthew Penner
31c26a2de4 ui: fix shit 2021-01-27 23:16:58 -07:00
Matthew Penner
18bdde8b81 api(application): fix requests, again 2021-01-24 15:30:58 -07:00
Matthew Penner
da3c7fa455 Merge branch 'develop' into feature/react-admin 2021-01-24 15:14:48 -07:00
Matthew Penner
eb29f6fc04 Merge branch 'develop' into feature/react-admin 2021-01-23 18:17:38 -07:00
Matthew Penner
5737b5dc5d api(application): fix requests 2021-01-23 18:17:35 -07:00
Matthew Penner
409c081275 Fix factories having wrong namespace 2021-01-23 15:07:58 -07:00
Matthew Penner
8a79589317 Fix factories and seeders having wrong directory name 2021-01-23 15:05:35 -07:00
Matthew Penner
f9bb791a7d Merge branch 'develop' into feature/react-admin 2021-01-23 15:05:19 -07:00
Matthew Penner
8feb87de7c Merge branch 'develop' into feature/react-admin 2021-01-23 14:39:23 -07:00
Matthew Penner
e01d859b53 db: add User has one AdminRole relation 2021-01-19 18:51:29 -07:00
Matthew Penner
1e61fd161c admin(roles): add has one on User -> AdminRole 2021-01-16 13:24:27 -07:00
Matthew Penner
9d005b5fd2 admin(roles): add 'permissions' column 2021-01-16 13:07:04 -07:00
Matthew Penner
9532ecf867 admin(ui-api): add 'include' parameter to all requests 2021-01-15 09:41:15 -07:00
Matthew Penner
e123367f40 db_hosts: add ability to link more than one node 2021-01-15 09:21:07 -07:00
Matthew Penner
79d80e8c22 admin(ui): add radio buttons to MountEditContainer 2021-01-14 18:13:21 -07:00
Matthew Penner
95d3f4c9e0 config: remove legacy file, fix debugbar enabled 2021-01-14 11:25:26 -07:00
Matthew Penner
b1c7b91313 cleanup: remove legacy code 2021-01-14 11:13:16 -07:00
Matthew Penner
a71392d632 api(application): add endpoints for (de)attaching eggs and nodes on mounts 2021-01-14 11:04:19 -07:00
Matthew Penner
4dab137b51 auth: fix call to renamed method 2021-01-14 10:36:05 -07:00
Matthew Penner
e2c8a2fdea routes: cleanup files 2021-01-13 10:03:27 -07:00
Matthew Penner
ba41fb5095 api(application): add includes for MountTransformer 2021-01-13 09:53:09 -07:00
Matthew Penner
d648ee5c93 admin(ui): add all fields to createServer.ts 2021-01-13 09:43:57 -07:00
Matthew Penner
c40e4bd2c0 Merge branch 'develop' into feature/react-admin 2021-01-12 11:47:49 -07:00
Matthew Penner
166221be9b ci: disable integration tests 2021-01-10 12:02:01 -07:00
Matthew Penner
6bb4f6cd01 tests(integration): remove old admin controller test 2021-01-10 11:53:08 -07:00
Matthew Penner
17f8715e41 ci: update to composer v2 2021-01-10 11:52:51 -07:00
Matthew Penner
3b0eb0d8ae ci: bye bye PHP 7.3 2021-01-10 11:49:03 -07:00
Matthew Penner
abd33c2384 admin(ui): make database and mount edit pages more responsive 2021-01-10 11:40:30 -07:00
Matthew Penner
52b2463281 admin(ui): fix descriptions overflowing vertically 2021-01-10 11:34:14 -07:00
Matthew Penner
1800800308 admin(ui): add base for mobile-responsive navigation 2021-01-09 13:07:32 -07:00
Matthew Penner
ef9bdf5cd5 admin(ui): add ability to delete mounts 2021-01-09 10:42:13 -07:00
Matthew Penner
0c7e787d44 admin(ui): add ability to delete nests 2021-01-09 10:36:40 -07:00
Matthew Penner
9ec6068f45 admin(ui): add ability to delete locations 2021-01-09 10:33:00 -07:00
Matthew Penner
a91cb578d7 admin(ui): add ability to delete database hosts 2021-01-09 10:26:30 -07:00
Matthew Penner
d323662ad5 admin(ui): implement DatabaseEditContainer.tsx 2021-01-09 09:58:55 -07:00
Matthew Penner
0759ecb1e1 api(application): fix database requests returning 404 2021-01-09 09:38:50 -07:00
Matthew Penner
c7d905fece admin(ui): implement LocationEditContainer.tsx 2021-01-09 09:27:58 -07:00
Matthew Penner
ee3b8e6432 admin(ui): fix box title on MountEditContainer.tsx 2021-01-08 15:39:20 -07:00
Matthew Penner
e70351cbad admin(ui): implement MountEditContainer.tsx, minor tweaks 2021-01-08 15:34:55 -07:00
Matthew Penner
11b7197c49 admin(ui): add mising update requests 2021-01-08 15:18:08 -07:00
Matthew Penner
52ce5e9a57 admin(ui): tweaks to OverviewContainer.tsx 2021-01-08 15:15:02 -07:00
Matthew Penner
93a333e1bb admin(ui): add context and get requests for all edit containers 2021-01-08 15:12:43 -07:00
Matthew Penner
e1b33c9346 admin(ui): implement RoleEditContainer.tsx 2021-01-08 15:09:02 -07:00
Matthew Penner
48882e6f2c admin(ui): add EggEditContainer.tsx 2021-01-08 15:07:03 -07:00
Matthew Penner
0d2fbfddc0 admin(ui): make the Nest edit page more responsive 2021-01-08 14:55:34 -07:00
Matthew Penner
bbd14c41c8 admin: delete old routes and controllers 2021-01-08 14:55:05 -07:00
Matthew Penner
a43ef62435 admin(ui): show eggs in NestEditContainer 2021-01-08 10:48:11 -07:00
Matthew Penner
cf006c9d36 admin(ui): show more infomration on NestEditContainer 2021-01-08 10:24:41 -07:00
Matthew Penner
20234b308c admin(ui): add ability to edit nests 2021-01-08 10:02:49 -07:00
Matthew Penner
0e366f69ee api(application): fix 'PATCH' nest endpoint 2021-01-08 09:44:05 -07:00
Matthew Penner
58cfa98b9c api(application): relocate egg endpoints 2021-01-08 09:26:57 -07:00
Matthew Penner
5946210e18 ui: fix ProgressBar being covered by admin sidebar 2021-01-08 08:41:39 -07:00
Matthew Penner
2352ef0369 admin(ui): display dynamic user information on sidebar 2021-01-07 10:21:09 -07:00
Matthew Penner
9eed88b430 admin(ui): actually get avatar for users 2021-01-07 09:55:04 -07:00
Matthew Penner
56c098a316 admin(ui): add document titles 2021-01-07 09:44:24 -07:00
Matthew Penner
946f907b68 admin(ui): show version information 2021-01-07 09:34:20 -07:00
Matthew Penner
12c68961db api(application): add 'version' endpoint 2021-01-07 09:32:04 -07:00
Matthew Penner
545cc3bbd2 Merge branch 'develop' into feature/react-admin 2021-01-06 22:47:06 -07:00
Matthew Penner
b45592466e admin(ui): add missing API requests 2021-01-06 16:38:39 -07:00
Matthew Penner
63daa7b14f admin(ui): add blank edit views 2021-01-06 15:50:21 -07:00
Matthew Penner
d81aef68b5 admin(ui): add blank 'create' views 2021-01-06 15:39:23 -07:00
Matthew Penner
e7021dfc39 ServersContainer: cleanup api code 2021-01-06 09:52:22 -07:00
Matthew Penner
b4ec1fb45d NodesContainer: add more fields to table 2021-01-06 09:52:07 -07:00
Matthew Penner
a038b0733d RolesContainer: make ID field CopyOnClick 2021-01-06 09:24:24 -07:00
Matthew Penner
96eb232fc0 NestsContainer: make ID field CopyOnClick 2021-01-06 09:24:00 -07:00
Matthew Penner
915666d827 MountsContainer: add source, target, readOnly, and userMountable columns 2021-01-06 09:23:35 -07:00
Matthew Penner
727cf8d753 LocationsContainer: make ID field CopyOnClick 2021-01-06 09:19:08 -07:00
Matthew Penner
231b8b40d6 DatabasesContainer: add more fields to table 2021-01-06 09:17:54 -07:00
Matthew Penner
ae6b8cba19 service(NodeJWTService): fix usage of deprecated parameters 2021-01-06 08:54:50 -07:00
Matthew Penner
935f99ab91 dependencies(composer): friendsofphp/php-cs-fixer 2.16.1 => 2.17.3 2021-01-06 08:53:41 -07:00
Matthew Penner
92840d3630 Re-add views/layouts/scripts.blade.php, bump composer.lock 2021-01-05 15:14:17 -07:00
Matthew Penner
b95f28fbd9 Remove all old admin views/assets 2021-01-05 14:58:48 -07:00
Matthew Penner
59de9576c9 Add tables for almost every admin change, update composer dependencies 2021-01-05 14:53:52 -07:00
Matthew Penner
8f1a5bf0ab Re-enable debugbar, add table to ServersContainer.tsx 2021-01-05 09:17:44 -07:00
Matthew Penner
ed73f6a020 Improvements to UsersContainer.tsx 2021-01-04 12:44:44 -07:00
Matthew Penner
95c55e7d28 Add table to admin/UsersContainer.tsx 2021-01-04 11:50:43 -07:00
Matthew Penner
0ddf806100 Merge branch 'develop' into feature/react-admin 2021-01-03 18:25:36 -07:00
Matthew Penner
5636c25838 Merge branch 'develop' into feature/react-admin 2021-01-03 18:18:06 -07:00
Matthew Penner
0511f75747 Add more application api routes for Nests 2021-01-03 16:45:07 -07:00
Matthew Penner
b1d30c1bde Fix RolesContainer, refactor NestsContainer 2021-01-03 16:25:32 -07:00
Matthew Penner
ce40194147 Make pagination tabs dynamically update 2021-01-03 12:38:48 -07:00
Matthew Penner
052a6d4ce5 Get basic concept of pagination working on NestsContainer.tsx 2021-01-03 11:34:07 -07:00
Matthew Penner
b17bb7c24b Transition RolesContainer.tsx to use table component 2021-01-01 17:03:10 -07:00
Matthew Penner
03474ccfcf Add AdminTable.tsx component 2021-01-01 16:55:06 -07:00
Matthew Penner
04799fa44b Fix checkboxes in NestsContainer.tsx 2021-01-01 16:34:47 -07:00
Matthew Penner
f72402b5fa God please forgive me for the sin I have just committed 2021-01-01 16:26:48 -07:00
Matthew Penner
88ac1ce1fd Cleanup code, add basic functionality for Nests 2021-01-01 15:55:30 -07:00
Matthew Penner
6c85be72fa Add nest endpoints and pages 2020-12-31 17:27:16 -07:00
Matthew Penner
359769244f Remove un-needed code from Admin/BaseController.php 2020-12-30 20:19:42 -07:00
Matthew Penner
193086548f Fix create role route 2020-12-30 20:18:56 -07:00
Matthew Penner
9d92053865 Merge branch 'develop' into feature/react-admin 2020-12-30 20:17:59 -07:00
Matthew Penner
9c7b49e2b9 Add proper permissions for role application routes, allow admins to access application api 2020-12-28 12:47:08 -07:00
Matthew Penner
b6abeb0994 Tweak styling of logout button 2020-12-28 12:17:11 -07:00
Matthew Penner
f67e340d33 Merge branch 'develop' into feature/react-admin 2020-12-28 12:05:19 -07:00
Matthew Penner
318c9f741b Add RoleEditContainer.tsx, fix laravel admin routes 2020-12-28 10:42:34 -07:00
Matthew Penner
0381fe1bd9 Fix 'New Role' model description 2020-12-28 10:09:55 -07:00
Matthew Penner
dc0fdee030 Add admin state store, add new role functionality 2020-12-28 10:08:08 -07:00
Matthew Penner
7369167e28 Fix problems after rebase, move RoleController to Api\Application 2020-12-27 22:02:30 -07:00
Matthew Penner
333c9312d4 Force admin get routes to be passed to the react front-end 2020-12-27 22:02:30 -07:00
Matthew Penner
108bf6e3f7 Remove PacksContainer.tsx 2020-12-27 22:02:30 -07:00
Matthew Penner
1cdf9bc18f Lazy load the AdminRouter 2020-12-27 22:02:30 -07:00
Matthew Penner
1f850fac61 Somewhat get a max-width container working 2020-12-27 22:02:30 -07:00
Matthew Penner
19176fb1b6 Get react admin sidebar working while Dane is shopping for domains 2020-12-27 22:02:30 -07:00
Matthew Penner
d2f3d8178b Fix Admin/BaseController.php showing the SSR admin area 2020-12-27 22:02:30 -07:00
Matthew Penner
1dff1f1810 Start porting over the admin area to react 2020-12-27 22:02:30 -07:00
Matthew Penner
e7aeeace26 tmp 2020-12-27 22:02:30 -07:00
Matthew Penner
df6f5c3a09 Update Mounts description, change styling of 'no items found' text on ApiKeysContainer.tsx 2020-12-27 22:02:30 -07:00
Matthew Penner
dd0cd5bcb9 Force admin get routes to be passed to the react front-end 2020-12-27 22:02:30 -07:00
Matthew Penner
3cdd348352 Remove PacksContainer.tsx 2020-12-27 22:02:30 -07:00
Matthew Penner
e5c30cb6f3 Lazy load the AdminRouter 2020-12-27 22:02:30 -07:00
Matthew Penner
434d204c49 Add 'select-none' everywhere in the admin area 2020-12-27 22:02:30 -07:00
Matthew Penner
a1115ff096 Somewhat get a max-width container working 2020-12-27 22:02:30 -07:00
Matthew Penner
6c53738070 Get react admin sidebar working while Dane is shopping for domains 2020-12-27 22:02:30 -07:00
Matthew Penner
c22ab762de Fix Admin/BaseController.php showing the SSR admin area 2020-12-27 22:02:29 -07:00
Matthew Penner
9ee3275b11 Start porting over the admin area to react 2020-12-27 22:02:29 -07:00
933 changed files with 40502 additions and 41972 deletions

View file

@ -1,11 +0,0 @@
module.exports = {
twin: {
preset: 'styled-components',
autoCssProp: true,
},
styledComponents: {
pure: true,
displayName: false,
fileName: false,
},
};

View file

@ -8,7 +8,7 @@ indent_size = 4
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
[.*yml] [*.yml]
indent_size = 2 indent_size = 2
[*.md] [*.md]

View file

@ -5,6 +5,7 @@ APP_THEME=pterodactyl
APP_TIMEZONE=America/Los_Angeles APP_TIMEZONE=America/Los_Angeles
APP_URL=http://localhost/ APP_URL=http://localhost/
DB_CONNECTION=testing
TESTING_DB_HOST=127.0.0.1 TESTING_DB_HOST=127.0.0.1
TESTING_DB_DATABASE=panel_test TESTING_DB_DATABASE=panel_test
TESTING_DB_USERNAME=root TESTING_DB_USERNAME=root

View file

@ -1,4 +1,6 @@
public public
node_modules node_modules
resources/views resources/views
babel.config.js
tailwind.config.js
webpack.config.js webpack.config.js

View file

@ -39,11 +39,14 @@ rules:
comma-dangle: comma-dangle:
- warn - warn
- always-multiline - always-multiline
spaced-comment: spaced-comment: 0
- warn
array-bracket-spacing: array-bracket-spacing:
- warn - warn
- always - always
"@typescript-eslint/no-unused-vars":
- warn
- argsIgnorePattern: '^_'
varsIgnorePattern: '^_'
# Remove errors for not having newlines between operands of ternary expressions https://eslint.org/docs/rules/multiline-ternary # Remove errors for not having newlines between operands of ternary expressions https://eslint.org/docs/rules/multiline-ternary
multiline-ternary: 0 multiline-ternary: 0
"react-hooks/rules-of-hooks": "react-hooks/rules-of-hooks":

View file

@ -8,7 +8,7 @@ on:
jobs: jobs:
push_to_registry: push_to_registry:
name: Push Image to GitHub Packages name: Push Image to GitHub Packages
runs-on: ubuntu-latest runs-on: ubuntu-20.04
# Always run against a tag, even if the commit into the tag has [docker skip] # Always run against a tag, even if the commit into the tag has [docker skip]
# within the commit message. # within the commit message.
if: "!contains(github.ref, 'develop') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))" if: "!contains(github.ref, 'develop') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))"

View file

@ -7,11 +7,13 @@ jobs:
release: release:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v2 - name: Checkout
- uses: actions/setup-node@v1 uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with: with:
node-version: '12' node-version: '14'
cache: 'yarn'
- name: Create release branch and bump version - name: Create release branch and bump version
env: env:
REF: ${{ github.ref }} REF: ${{ github.ref }}
@ -25,17 +27,14 @@ jobs:
git add config/app.php git add config/app.php
git commit -m "bump version for release" git commit -m "bump version for release"
git push git push
- name: Build assets - name: Build assets
run: | run: |
yarn install yarn install
yarn run build:production yarn run build:production
- name: Create release archive - name: Create release archive
run: | run: |
rm -rf node_modules/ test/ codecov.yml CODE_OF_CONDUCT.md CONTRIBUTING.md phpunit.dusk.xml phpunit.xml Vagrantfile rm -rf node_modules/ test/ codecov.yml CODE_OF_CONDUCT.md CONTRIBUTING.md phpunit.dusk.xml phpunit.xml
tar -czf panel.tar.gz * .env.example .eslintignore .eslintrc.yml .babel-plugin-macrosrc.js tar -czf panel.tar.gz * .env.example .eslintignore .eslintrc.yml .yarnclean
- name: Extract changelog - name: Extract changelog
id: extract_changelog id: extract_changelog
env: env:
@ -43,13 +42,11 @@ jobs:
run: | run: |
sed -n "/^## ${REF:10}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG sed -n "/^## ${REF:10}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG
echo ::set-output name=version_name::`sed -nr "s/^## (${REF:10} .*)$/\1/p" CHANGELOG.md` echo ::set-output name=version_name::`sed -nr "s/^## (${REF:10} .*)$/\1/p" CHANGELOG.md`
- name: Create checksum and add to changelog - name: Create checksum and add to changelog
run: | run: |
SUM=`sha256sum panel.tar.gz` SUM=`sha256sum panel.tar.gz`
echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG
echo $SUM > checksum.txt echo $SUM > checksum.txt
- name: Create Release - name: Create Release
id: create_release id: create_release
uses: actions/create-release@v1 uses: actions/create-release@v1
@ -61,7 +58,6 @@ jobs:
body_path: ./RELEASE_CHANGELOG body_path: ./RELEASE_CHANGELOG
draft: true draft: true
prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
- name: Upload binary - name: Upload binary
id: upload-release-archive id: upload-release-archive
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
@ -72,7 +68,6 @@ jobs:
asset_path: panel.tar.gz asset_path: panel.tar.gz
asset_name: panel.tar.gz asset_name: panel.tar.gz
asset_content_type: application/gzip asset_content_type: application/gzip
- name: Upload checksum - name: Upload checksum
id: upload-release-checksum id: upload-release-checksum
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1

View file

@ -12,8 +12,8 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [ 7.4, 8.0 ] php: [ 8.1 ]
database: [ 'mariadb:10.2', 'mysql:8' ] database: [ 'mariadb:10.6', 'mysql:8' ]
services: services:
database: database:
image: ${{ matrix.database }} image: ${{ matrix.database }}
@ -25,35 +25,40 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
name: "php-${{ matrix.php }} (${{ matrix.database }})" name: "php-${{ matrix.php }} (${{ matrix.database }})"
steps: steps:
- uses: actions/checkout@v2 - name: Checkout
- name: get cache directory uses: actions/checkout@v2
- name: Get cache directory
id: composer-cache id: composer-cache
run: | run: |
echo "::set-output name=dir::$(composer config cache-files-dir)" echo "::set-output name=dir::$(composer config cache-files-dir)"
- uses: actions/cache@v2 - name: Cache
uses: actions/cache@v2
with: with:
path: | path: |
~/.php_cs.cache ~/.php_cs.cache
${{ steps.composer-cache.outputs.dir }} ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-cache-${{ matrix.php }}-${{ hashFiles('**.composer.lock') }} key: ${{ runner.os }}-cache-${{ matrix.php }}-${{ hashFiles('composer.lock') }}
- uses: shivammathur/setup-php@v2 - name: Setup PHP
uses: shivammathur/setup-php@v2
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2 tools: composer:v2
coverage: none coverage: none
- run: cp .env.ci .env - name: Configure .env
- run: composer install --prefer-dist --no-interaction --no-progress run: cp .env.ci .env
- run: vendor/bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff --config .php-cs-fixer.dist.php - name: composer install
continue-on-error: true run: composer install --prefer-dist --no-interaction --no-progress
- name: execute unit tests - name: Run cs-fixer
run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit run: vendor/bin/php-cs-fixer fix --dry-run --diff --format=txt --config .php-cs-fixer.dist.php
if: ${{ always() }} - name: Static Analysis
if: ${{ matrix.php }} == '8.0'
run: |
php artisan ide-helper:models -N
./vendor/bin/phpstan analyse --memory-limit=2G
env: env:
DB_CONNECTION: testing DB_PORT: ${{ job.services.database.ports[3306] }}
TESTING_DB_HOST: UNIT_NO_DB - name: Execute Tests
- name: execute integration tests run: php artisan test
run: vendor/bin/phpunit tests/Integration
env: env:
TESTING_DB_PORT: ${{ job.services.database.ports[3306] }} DB_PORT: ${{ job.services.database.ports[3306] }}
TESTING_DB_USERNAME: root

109
.gitignore vendored
View file

@ -1,40 +1,85 @@
#------------------#
# IDEs and Editors #
#------------------#
# Jetbrains
/.idea
*.iml
*.ipr
*.iws
# Visual Studio Code
/.vscode
# Sublime Text
*.sublime-*
#-------------------#
# Tools #
#-------------------#
# Vagrant
/.vagrant
#-------------------#
# Languages #
#-------------------#
# Node.js
/.npm
/node_modules
npm-debug.log
.cache
.eslintcache
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*
.yarn-integrity
yarn-debug.log
yarn-error.log
# PHP
/vendor /vendor
*.DS_Store* .php-cs-fixer.cache
.phpunit.result.cache
_ide_helper.php
_ide_helper_models.php
.phpstorm.meta.php
#-------------------#
# Operating Systems #
#-------------------#
# External Drives
.Spotlight-V100
.Trashes
# Linux
*~
# Mac OS
/.DS_Store
*.DS_Store
# VIM
.*.sw[a-p]
#-------------------#
# Project Files #
#-------------------#
# Environment Files
!.env.ci !.env.ci
!.env.dusk !.env.dusk
!.env.example !.env.example
.env* .env*
.vagrant/*
.vscode/*
storage/framework/*
/.idea
/nbproject
node_modules
*.log *.log
_ide_helper.php
_ide_helper_models.php _ide_helper_models.php
_ide_helper.php
.phpstorm.meta.php .phpstorm.meta.php
.php_cs.cache
.yarn
public/assets/manifest.json
# For local development with docker
# Remove if we ever put the Dockerfile in the repo
.dockerignore
#Dockerfile
docker-compose.yml
# for image related files
misc
.phpstorm.meta.php
.php_cs.cache
coverage.xml
# Vagrant
*.log
resources/lang/locales.js
resources/assets/pterodactyl/scripts/helpers/ziggy.js
resources/assets/scripts/helpers/ziggy.js
.phpunit.result.cache

View file

@ -10,6 +10,9 @@ $finder = (new Finder())
'node_modules', 'node_modules',
'storage', 'storage',
'bootstrap/cache', 'bootstrap/cache',
'.phpstorm.meta.php',
'_ide_helper.php',
'_ide_helper_models.php',
]) ])
->notName(['_ide_helper*']); ->notName(['_ide_helper*']);
@ -29,7 +32,7 @@ return (new Config())
'no_unreachable_default_argument_value' => true, 'no_unreachable_default_argument_value' => true,
'no_useless_return' => true, 'no_useless_return' => true,
'ordered_imports' => [ 'ordered_imports' => [
'sortAlgorithm' => 'length', 'sort_algorithm' => 'length',
], ],
'phpdoc_align' => [ 'phpdoc_align' => [
'align' => 'left', 'align' => 'left',

631
.yarn/releases/yarn-3.0.2.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
@types/react-native

6
.yarnrc.yml Normal file
View file

@ -0,0 +1,6 @@
nodeLinker: node-modules
pnpIgnorePatterns:
- ./public/**/*
yarnPath: .yarn/releases/yarn-3.0.2.cjs

View file

@ -50,6 +50,11 @@ class AppSettingsCommand extends Command
*/ */
protected $config; protected $config;
/**
* @var \Illuminate\Validation\Factory
*/
protected $validator;
/** /**
* @var string * @var string
*/ */
@ -114,6 +119,7 @@ class AppSettingsCommand extends Command
foreach ($validator->errors()->all() as $error) { foreach ($validator->errors()->all() as $error) {
$this->output->error($error); $this->output->error($error);
} }
return 1; return 1;
} }

View file

@ -54,15 +54,15 @@ class InfoCommand extends Command
$this->output->title('Version Information'); $this->output->title('Version Information');
$this->table([], [ $this->table([], [
['Panel Version', $this->config->get('app.version')], ['Panel Version', $this->config->get('app.version')],
['Latest Version', $this->versionService->getPanel()], ['Latest Version', $this->versionService->getLatestPanel()],
['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')], ['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')],
['Unique Identifier', $this->config->get('pterodactyl.service.author')], ['Unique Identifier', $this->config->get('pterodactyl.service.author')],
], 'compact'); ], 'compact');
$this->output->title('Application Configuration'); $this->output->title('Application Configuration');
$this->table([], [ $this->table([], [
['Environment', $this->formatText($this->config->get('app.env'), $this->config->get('app.env') === 'production' ?: 'bg=red')], ['Environment', $this->formatText($this->config->get('app.env'), $this->config->get('app.env') === 'production' ? '' : 'bg=red')],
['Debug Mode', $this->formatText($this->config->get('app.debug') ? 'Yes' : 'No', !$this->config->get('app.debug') ?: 'bg=red')], ['Debug Mode', $this->formatText($this->config->get('app.debug') ? 'Yes' : 'No', !$this->config->get('app.debug') ? '' : 'bg=red')],
['Installation URL', $this->config->get('app.url')], ['Installation URL', $this->config->get('app.url')],
['Installation Directory', base_path()], ['Installation Directory', base_path()],
['Timezone', $this->config->get('app.timezone')], ['Timezone', $this->config->get('app.timezone')],
@ -70,7 +70,6 @@ class InfoCommand extends Command
['Queue Driver', $this->config->get('queue.default')], ['Queue Driver', $this->config->get('queue.default')],
['Session Driver', $this->config->get('session.driver')], ['Session Driver', $this->config->get('session.driver')],
['Filesystem Driver', $this->config->get('filesystems.default')], ['Filesystem Driver', $this->config->get('filesystems.default')],
['Default Theme', $this->config->get('themes.active')],
['Proxies', $this->config->get('trustedproxies.proxies')], ['Proxies', $this->config->get('trustedproxies.proxies')],
], 'compact'); ], 'compact');

View file

@ -1,11 +1,4 @@
<?php <?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Console\Commands\Location; namespace Pterodactyl\Console\Commands\Location;
@ -26,9 +19,9 @@ class DeleteLocationCommand extends Command
protected $description = 'Deletes a location from the Panel.'; protected $description = 'Deletes a location from the Panel.';
/** /**
* @var \Illuminate\Support\Collection * @var \Illuminate\Support\Collection|null
*/ */
protected $locations; protected $locations = null;
/** /**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface

View file

@ -13,14 +13,14 @@ class SeedCommand extends BaseSeedCommand
* Block someone from running this seed command if they have not completed * Block someone from running this seed command if they have not completed
* the migration process. * the migration process.
*/ */
public function handle() public function handle(): int
{ {
if (!$this->hasCompletedMigrations()) { if (!$this->hasCompletedMigrations()) {
$this->showMigrationWarning(); $this->showMigrationWarning();
return; return 1;
} }
parent::handle(); return parent::handle();
} }
} }

View file

@ -13,14 +13,14 @@ class UpCommand extends BaseUpCommand
* Block someone from running this up command if they have not completed * Block someone from running this up command if they have not completed
* the migration process. * the migration process.
*/ */
public function handle() public function handle(): int
{ {
if (!$this->hasCompletedMigrations()) { if (!$this->hasCompletedMigrations()) {
$this->showMigrationWarning(); $this->showMigrationWarning();
return; return 1;
} }
parent::handle(); return parent::handle();
} }
} }

View file

@ -24,7 +24,7 @@ class ProcessRunnableCommand extends Command
/** /**
* Handle command execution. * Handle command execution.
*/ */
public function handle() public function handle(): int
{ {
$schedules = Schedule::query()->with('tasks') $schedules = Schedule::query()->with('tasks')
->where('is_active', true) ->where('is_active', true)
@ -35,7 +35,7 @@ class ProcessRunnableCommand extends Command
if ($schedules->count() < 1) { if ($schedules->count() < 1) {
$this->line('There are no scheduled tasks for servers that need to be run.'); $this->line('There are no scheduled tasks for servers that need to be run.');
return; return 0;
} }
$bar = $this->output->createProgressBar(count($schedules)); $bar = $this->output->createProgressBar(count($schedules));
@ -47,6 +47,8 @@ class ProcessRunnableCommand extends Command
} }
$this->line(''); $this->line('');
return 0;
} }
/** /**

View file

@ -87,11 +87,12 @@ class UpgradeCommand extends Command
if (!$this->confirm('Are you sure you want to run the upgrade process for your Panel?')) { if (!$this->confirm('Are you sure you want to run the upgrade process for your Panel?')) {
$this->warn('Upgrade process terminated by user.'); $this->warn('Upgrade process terminated by user.');
return; return;
} }
} }
ini_set('output_buffering', 0); ini_set('output_buffering', '0');
$bar = $this->output->createProgressBar($skipDownload ? 9 : 10); $bar = $this->output->createProgressBar($skipDownload ? 9 : 10);
$bar->start(); $bar->start();

View file

@ -1,11 +1,4 @@
<?php <?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Console\Commands\User; namespace Pterodactyl\Console\Commands\User;
@ -47,11 +40,9 @@ class DeleteUserCommand extends Command
} }
/** /**
* @return bool
*
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
*/ */
public function handle() public function handle(): int
{ {
$search = $this->option('user') ?? $this->ask(trans('command/messages.user.search_users')); $search = $this->option('user') ?? $this->ask(trans('command/messages.user.search_users'));
Assert::notEmpty($search, 'Search term should be an email address, got: %s.'); Assert::notEmpty($search, 'Search term should be an email address, got: %s.');
@ -68,13 +59,13 @@ class DeleteUserCommand extends Command
return $this->handle(); return $this->handle();
} }
return false; return 1;
} }
if ($this->input->isInteractive()) { if ($this->input->isInteractive()) {
$tableValues = []; $tableValues = [];
foreach ($results as $user) { foreach ($results as $user) {
$tableValues[] = [$user->id, $user->email, $user->name]; $tableValues[] = [$user->id, $user->email, $user->name_first];
} }
$this->table(['User ID', 'Email', 'Name'], $tableValues); $this->table(['User ID', 'Email', 'Name'], $tableValues);
@ -85,7 +76,7 @@ class DeleteUserCommand extends Command
if (count($results) > 1) { if (count($results) > 1) {
$this->error(trans('command/messages.user.multiple_found')); $this->error(trans('command/messages.user.multiple_found'));
return false; return 1;
} }
$deleteUser = $results->first(); $deleteUser = $results->first();
@ -95,5 +86,7 @@ class DeleteUserCommand extends Command
$this->deletionService->handle($deleteUser); $this->deletionService->handle($deleteUser);
$this->info(trans('command/messages.user.deleted')); $this->info(trans('command/messages.user.deleted'));
} }
return 0;
} }
} }

View file

@ -50,8 +50,6 @@ class MakeUserCommand extends Command
$root_admin = $this->option('admin') ?? $this->confirm(trans('command/messages.user.ask_admin')); $root_admin = $this->option('admin') ?? $this->confirm(trans('command/messages.user.ask_admin'));
$email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email')); $email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email'));
$username = $this->option('username') ?? $this->ask(trans('command/messages.user.ask_username')); $username = $this->option('username') ?? $this->ask(trans('command/messages.user.ask_username'));
$name_first = $this->option('name-first') ?? $this->ask(trans('command/messages.user.ask_name_first'));
$name_last = $this->option('name-last') ?? $this->ask(trans('command/messages.user.ask_name_last'));
if (is_null($password = $this->option('password')) && !$this->option('no-password')) { if (is_null($password = $this->option('password')) && !$this->option('no-password')) {
$this->warn(trans('command/messages.user.ask_password_help')); $this->warn(trans('command/messages.user.ask_password_help'));
@ -59,12 +57,12 @@ class MakeUserCommand extends Command
$password = $this->secret(trans('command/messages.user.ask_password')); $password = $this->secret(trans('command/messages.user.ask_password'));
} }
$user = $this->creationService->handle(compact('email', 'username', 'name_first', 'name_last', 'password', 'root_admin')); $user = $this->creationService->handle(compact('email', 'username', 'password', 'root_admin'));
$this->table(['Field', 'Value'], [ $this->table(['Field', 'Value'], [
['UUID', $user->uuid], ['UUID', $user->uuid],
['Email', $user->email], ['Email', $user->email],
['Username', $user->username], ['Username', $user->username],
['Name', $user->name], ['Name', $user->name_first],
['Admin', $user->root_admin ? 'Yes' : 'No'], ['Admin', $user->root_admin ? 'Yes' : 'No'],
]); ]);
} }

View file

@ -1,13 +0,0 @@
<?php
namespace Pterodactyl\Contracts\Http;
interface ClientPermissionsRequest
{
/**
* Returns the permissions string indicating which permission should be used to
* validate that the authenticated user has permission to perform this action aganist
* the given resource (server).
*/
public function permission(): string;
}

View file

@ -1,29 +0,0 @@
<?php
namespace Pterodactyl\Contracts\Repository;
use Pterodactyl\Models\User;
use Illuminate\Support\Collection;
interface ApiKeyRepositoryInterface extends RepositoryInterface
{
/**
* Get all of the account API keys that exist for a specific user.
*/
public function getAccountKeys(User $user): Collection;
/**
* Get all of the application API keys that exist for a specific user.
*/
public function getApplicationKeys(User $user): Collection;
/**
* Delete an account API key from the panel for a specific user.
*/
public function deleteAccountKey(User $user, string $identifier): int;
/**
* Delete an application API key from the panel for a specific user.
*/
public function deleteApplicationKey(User $user, string $identifier): int;
}

View file

@ -39,10 +39,8 @@ interface DatabaseRepositoryInterface extends RepositoryInterface
/** /**
* Create a new database user on a given connection. * Create a new database user on a given connection.
*
* @param $max_connections
*/ */
public function createUser(string $username, string $remote, string $password, string $max_connections): bool; public function createUser(string $username, string $remote, string $password, int $max_connections): bool;
/** /**
* Give a specific user access to a given database. * Give a specific user access to a given database.
@ -61,8 +59,6 @@ interface DatabaseRepositoryInterface extends RepositoryInterface
/** /**
* Drop a given user on a specific connection. * Drop a given user on a specific connection.
*
* @return mixed
*/ */
public function dropUser(string $username, string $remote): bool; public function dropUser(string $username, string $remote): bool;
} }

View file

@ -20,8 +20,6 @@ interface LocationRepositoryInterface extends RepositoryInterface
/** /**
* Return all of the nodes and their respective count of servers for a location. * Return all of the nodes and their respective count of servers for a location.
* *
* @return mixed
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function getWithNodes(int $id): Location; public function getWithNodes(int $id): Location;
@ -29,8 +27,6 @@ interface LocationRepositoryInterface extends RepositoryInterface
/** /**
* Return a location and the count of nodes in that location. * Return a location and the count of nodes in that location.
* *
* @return mixed
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function getWithNodeCount(int $id): Location; public function getWithNodeCount(int $id): Location;

View file

@ -1,11 +1,4 @@
<?php <?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Contracts\Repository; namespace Pterodactyl\Contracts\Repository;
@ -16,27 +9,7 @@ interface NestRepositoryInterface extends RepositoryInterface
/** /**
* Return a nest or all nests with their associated eggs and variables. * Return a nest or all nests with their associated eggs and variables.
* *
* @param int $id
*
* @return \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Nest
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function getWithEggs(int $id = null); public function getWithEggs(int $id = null): Nest;
/**
* Return a nest or all nests and the count of eggs and servers for that nest.
*
* @return \Pterodactyl\Models\Nest|\Illuminate\Database\Eloquent\Collection
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function getWithCounts(int $id = null);
/**
* Return a nest along with its associated eggs and the servers relation on those eggs.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function getWithEggServers(int $id): Nest;
} }

View file

@ -17,8 +17,6 @@ class Installed extends Event
/** /**
* Create a new event instance. * Create a new event instance.
*
* @var \Pterodactyl\Models\Server
*/ */
public function __construct(Server $server) public function __construct(Server $server)
{ {

View file

@ -7,7 +7,6 @@ use Throwable;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Container\Container; use Illuminate\Container\Container;
use Prologue\Alerts\AlertsMessageBag;
class DisplayException extends PterodactylException class DisplayException extends PterodactylException
{ {
@ -68,8 +67,6 @@ class DisplayException extends PterodactylException
]), method_exists($this, 'getStatusCode') ? $this->getStatusCode() : Response::HTTP_BAD_REQUEST); ]), method_exists($this, 'getStatusCode') ? $this->getStatusCode() : Response::HTTP_BAD_REQUEST);
} }
Container::getInstance()->make(AlertsMessageBag::class)->danger($this->getMessage())->flash();
return redirect()->back()->withInput(); return redirect()->back()->withInput();
} }

View file

@ -7,7 +7,7 @@ use Throwable;
use PDOException; use PDOException;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Swift_TransportException; use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Container\Container; use Illuminate\Container\Container;
@ -19,6 +19,7 @@ use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Mailer\Exception\TransportException;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
@ -35,7 +36,7 @@ class Handler extends ExceptionHandler
/** /**
* A list of the exception types that should not be reported. * A list of the exception types that should not be reported.
* *
* @var array * @var array<int, class-string<Throwable>>
*/ */
protected $dontReport = [ protected $dontReport = [
AuthenticationException::class, AuthenticationException::class,
@ -50,7 +51,7 @@ class Handler extends ExceptionHandler
/** /**
* A list of the inputs that are never flashed for validation exceptions. * A list of the inputs that are never flashed for validation exceptions.
* *
* @var array * @var string[]
*/ */
protected $dontFlash = [ protected $dontFlash = [
'token', 'token',
@ -59,6 +60,16 @@ class Handler extends ExceptionHandler
'password_confirmation', 'password_confirmation',
]; ];
/**
* Maps specific internal exceptions to a valid HTTP status code.
*
* @var array
*/
protected static $statusCodeMap = [
AuthenticationException::class => Response::HTTP_UNAUTHORIZED,
ValidationException::class => Response::HTTP_UNPROCESSABLE_ENTITY,
];
/** /**
* Registers the exception handling callbacks for the application. This * Registers the exception handling callbacks for the application. This
* will capture specific exception types that we do not want to include * will capture specific exception types that we do not want to include
@ -77,7 +88,7 @@ class Handler extends ExceptionHandler
$ex = $this->generateCleanedExceptionStack($ex); $ex = $this->generateCleanedExceptionStack($ex);
}); });
$this->reportable(function (Swift_TransportException $ex) { $this->reportable(function (TransportException $ex) {
$ex = $this->generateCleanedExceptionStack($ex); $ex = $this->generateCleanedExceptionStack($ex);
}); });
} }
@ -191,7 +202,7 @@ class Handler extends ExceptionHandler
'code' => class_basename($exception), 'code' => class_basename($exception),
'status' => method_exists($exception, 'getStatusCode') 'status' => method_exists($exception, 'getStatusCode')
? strval($exception->getStatusCode()) ? strval($exception->getStatusCode())
: ($exception instanceof ValidationException ? '422' : '500'), : strval(self::$statusCodeMap[get_class($exception)] ?? 500),
'detail' => $exception instanceof HttpExceptionInterface 'detail' => $exception instanceof HttpExceptionInterface
? $exception->getMessage() ? $exception->getMessage()
: 'An unexpected error was encountered while processing this request, please try again.', : 'An unexpected error was encountered while processing this request, please try again.',
@ -212,6 +223,7 @@ class Handler extends ExceptionHandler
'file' => str_replace(Application::getInstance()->basePath(), '', $exception->getFile()), 'file' => str_replace(Application::getInstance()->basePath(), '', $exception->getFile()),
], ],
'meta' => [ 'meta' => [
'class' => get_class($exception),
'trace' => explode("\n", $exception->getTraceAsString()), 'trace' => explode("\n", $exception->getTraceAsString()),
], ],
]); ]);

View file

@ -0,0 +1,21 @@
<?php
namespace Pterodactyl\Exceptions\Http;
use Illuminate\Http\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
class QueryValueOutOfRangeHttpException extends HttpException
{
/**
* QueryValueOutOfRangeHttpException constructor.
*/
public function __construct(string $name, int $min, int $max, \Throwable $previous = null)
{
parent::__construct(
Response::HTTP_BAD_REQUEST,
'\"' . $name . '\" query parameter must be between ' . $min . ' and ' . $max,
$previous,
);
}
}

View file

@ -9,20 +9,16 @@ class RecordNotFoundException extends RepositoryException implements HttpExcepti
{ {
/** /**
* Returns the status code. * Returns the status code.
*
* @return int
*/ */
public function getStatusCode() public function getStatusCode(): int
{ {
return Response::HTTP_NOT_FOUND; return Response::HTTP_NOT_FOUND;
} }
/** /**
* Returns response headers. * Returns response headers.
*
* @return array
*/ */
public function getHeaders() public function getHeaders(): array
{ {
return []; return [];
} }

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Exceptions\Service\Egg;
use Pterodactyl\Exceptions\DisplayException;
class BadYamlFormatException extends DisplayException
{
}

View file

@ -1,9 +0,0 @@
<?php
namespace Pterodactyl\Exceptions\Transformer;
use Pterodactyl\Exceptions\PterodactylException;
class InvalidTransformerLevelException extends PterodactylException
{
}

View file

@ -1,16 +0,0 @@
<?php
namespace Pterodactyl\Extensions\Facades;
use Illuminate\Support\Facades\Facade;
class Theme extends Facade
{
/**
* @return string
*/
protected static function getFacadeAccessor()
{
return 'extensions.themes';
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Pterodactyl\Extensions\Laravel\Sanctum;
use Pterodactyl\Models\PersonalAccessToken;
use Laravel\Sanctum\NewAccessToken as SanctumAccessToken;
/**
* @property \Pterodactyl\Models\PersonalAccessToken $accessToken
*/
class NewAccessToken extends SanctumAccessToken
{
/**
* NewAccessToken constructor.
*
* @noinspection PhpMissingParentConstructorInspection
*/
public function __construct(PersonalAccessToken $accessToken, string $plainTextToken)
{
$this->accessToken = $accessToken;
$this->plainTextToken = $plainTextToken;
}
}

View file

@ -2,8 +2,8 @@
namespace Pterodactyl\Extensions\Spatie\Fractalistic; namespace Pterodactyl\Extensions\Spatie\Fractalistic;
use League\Fractal\TransformerAbstract;
use Spatie\Fractal\Fractal as SpatieFractal; use Spatie\Fractal\Fractal as SpatieFractal;
use Pterodactyl\Transformers\Api\Transformer;
use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use Pterodactyl\Extensions\League\Fractal\Serializers\PterodactylSerializer; use Pterodactyl\Extensions\League\Fractal\Serializers\PterodactylSerializer;
@ -21,24 +21,21 @@ class Fractal extends SpatieFractal
public function createData() public function createData()
{ {
// Set the serializer by default. // Set the serializer by default.
if (is_null($this->serializer)) { if (is_null($this->serializer)) { // @phpstan-ignore-line
$this->serializer = new PterodactylSerializer(); $this->serializer = new PterodactylSerializer();
} }
// Automatically set the paginator on the response object if the // Automatically set the paginator on the response object if the
// data being provided implements a paginator. // data being provided implements a paginator.
if (is_null($this->paginator) && $this->data instanceof LengthAwarePaginator) { if (is_null($this->paginator) && $this->data instanceof LengthAwarePaginator) { // @phpstan-ignore-line
$this->paginator = new IlluminatePaginatorAdapter($this->data); $this->paginator = new IlluminatePaginatorAdapter($this->data);
} }
// If the resource name is not set attempt to pull it off the transformer // If the resource name is not set attempt to pull it off the transformer
// itself and set it automatically. // itself and set it automatically.
if ( $class = is_string($this->transformer) ? new $this->transformer() : $this->transformer;
is_null($this->resourceName) if (is_null($this->resourceName) && $class instanceof Transformer) { // @phpstan-ignore-line
&& $this->transformer instanceof TransformerAbstract $this->resourceName = $class->getResourceName();
&& method_exists($this->transformer, 'getResourceName')
) {
$this->resourceName = $this->transformer->getResourceName();
} }
return parent::createData(); return parent::createData();

View file

@ -1,21 +0,0 @@
<?php
namespace Pterodactyl\Extensions\Themes;
class Theme
{
public function js($path)
{
return sprintf('<script src="%s"></script>' . PHP_EOL, $this->getUrl($path));
}
public function css($path)
{
return sprintf('<link media="all" type="text/css" rel="stylesheet" href="%s"/>' . PHP_EOL, $this->getUrl($path));
}
protected function getUrl($path)
{
return '/themes/pterodactyl/' . ltrim($path, '/');
}
}

View file

@ -17,6 +17,6 @@ final class Time
{ {
$offset = round(CarbonImmutable::now($timezone)->getTimezone()->getOffset(CarbonImmutable::now('UTC')) / 3600); $offset = round(CarbonImmutable::now($timezone)->getTimezone()->getOffset(CarbonImmutable::now('UTC')) / 3600);
return sprintf('%s%s:00', $offset > 0 ? '+' : '-', str_pad(abs($offset), 2, '0', STR_PAD_LEFT)); return sprintf('%s%s:00', $offset > 0 ? '+' : '-', str_pad((string) abs($offset), 2, '0', STR_PAD_LEFT));
} }
} }

View file

@ -1,103 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pterodactyl\Models\ApiKey;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Services\Acl\Api\AdminAcl;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Api\KeyCreationService;
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Api\StoreApplicationApiKeyRequest;
class ApiController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Pterodactyl\Services\Api\KeyCreationService
*/
private $keyCreationService;
/**
* @var \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface
*/
private $repository;
/**
* ApplicationApiController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
ApiKeyRepositoryInterface $repository,
KeyCreationService $keyCreationService
) {
$this->alert = $alert;
$this->keyCreationService = $keyCreationService;
$this->repository = $repository;
}
/**
* Render view showing all of a user's application API keys.
*/
public function index(Request $request): View
{
return view('admin.api.index', [
'keys' => $this->repository->getApplicationKeys($request->user()),
]);
}
/**
* Render view allowing an admin to create a new application API key.
*
* @throws \ReflectionException
*/
public function create(): View
{
$resources = AdminAcl::getResourceList();
sort($resources);
return view('admin.api.new', [
'resources' => $resources,
'permissions' => [
'r' => AdminAcl::READ,
'rw' => AdminAcl::READ | AdminAcl::WRITE,
'n' => AdminAcl::NONE,
],
]);
}
/**
* Store the new key and redirect the user back to the application key listing.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(StoreApplicationApiKeyRequest $request): RedirectResponse
{
$this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([
'memo' => $request->input('memo'),
'user_id' => $request->user()->id,
], $request->getKeyPermissions());
$this->alert->success('A new application API key has been generated for your account.')->flash();
return redirect()->route('admin.api.index');
}
/**
* Delete an application API key from the database.
*/
public function delete(Request $request, string $identifier): Response
{
$this->repository->deleteApplicationKey($request->user(), $identifier);
return response('', 204);
}
}

View file

@ -4,28 +4,14 @@ namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\View\View; use Illuminate\View\View;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
class BaseController extends Controller class BaseController extends Controller
{ {
/**
* @var \Pterodactyl\Services\Helpers\SoftwareVersionService
*/
private $version;
/**
* BaseController constructor.
*/
public function __construct(SoftwareVersionService $version)
{
$this->version = $version;
}
/** /**
* Return the admin index view. * Return the admin index view.
*/ */
public function index(): View public function index(): View
{ {
return view('admin.index', ['version' => $this->version]); return view('templates/base.core');
} }
} }

View file

@ -1,170 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Exception;
use PDOException;
use Illuminate\View\View;
use Pterodactyl\Models\DatabaseHost;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Databases\Hosts\HostUpdateService;
use Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest;
use Pterodactyl\Services\Databases\Hosts\HostCreationService;
use Pterodactyl\Services\Databases\Hosts\HostDeletionService;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
class DatabaseController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Pterodactyl\Services\Databases\Hosts\HostCreationService
*/
private $creationService;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
private $databaseRepository;
/**
* @var \Pterodactyl\Services\Databases\Hosts\HostDeletionService
*/
private $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
*/
private $locationRepository;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
*/
private $repository;
/**
* @var \Pterodactyl\Services\Databases\Hosts\HostUpdateService
*/
private $updateService;
/**
* DatabaseController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
DatabaseHostRepositoryInterface $repository,
DatabaseRepositoryInterface $databaseRepository,
HostCreationService $creationService,
HostDeletionService $deletionService,
HostUpdateService $updateService,
LocationRepositoryInterface $locationRepository
) {
$this->alert = $alert;
$this->creationService = $creationService;
$this->databaseRepository = $databaseRepository;
$this->deletionService = $deletionService;
$this->repository = $repository;
$this->locationRepository = $locationRepository;
$this->updateService = $updateService;
}
/**
* Display database host index.
*/
public function index(): View
{
return view('admin.databases.index', [
'locations' => $this->locationRepository->getAllWithNodes(),
'hosts' => $this->repository->getWithViewDetails(),
]);
}
/**
* Display database host to user.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function view(int $host): View
{
return view('admin.databases.view', [
'locations' => $this->locationRepository->getAllWithNodes(),
'host' => $this->repository->find($host),
'databases' => $this->databaseRepository->getDatabasesForHost($host),
]);
}
/**
* Handle request to create a new database host.
*
* @throws \Throwable
*/
public function create(DatabaseHostFormRequest $request): RedirectResponse
{
try {
$host = $this->creationService->handle($request->normalize());
} catch (Exception $exception) {
if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
return redirect()->route('admin.databases')->withInput($request->validated());
} else {
throw $exception;
}
}
$this->alert->success('Successfully created a new database host on the system.')->flash();
return redirect()->route('admin.databases.view', $host->id);
}
/**
* Handle updating database host.
*
* @throws \Throwable
*/
public function update(DatabaseHostFormRequest $request, DatabaseHost $host): RedirectResponse
{
$redirect = redirect()->route('admin.databases.view', $host->id);
try {
$this->updateService->handle($host->id, $request->normalize());
$this->alert->success('Database host was updated successfully.')->flash();
} catch (Exception $exception) {
// Catch any SQL related exceptions and display them back to the user, otherwise just
// throw the exception like normal and move on with it.
if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
return $redirect->withInput($request->normalize());
} else {
throw $exception;
}
}
return $redirect;
}
/**
* Handle request to delete a database host.
*
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
*/
public function delete(int $host): RedirectResponse
{
$this->deletionService->handle($host);
$this->alert->success('The requested database host has been deleted from the system.')->flash();
return redirect()->route('admin.databases');
}
}

View file

@ -1,148 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Admin;
use Pterodactyl\Models\Location;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Http\Requests\Admin\LocationFormRequest;
use Pterodactyl\Services\Locations\LocationUpdateService;
use Pterodactyl\Services\Locations\LocationCreationService;
use Pterodactyl\Services\Locations\LocationDeletionService;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
class LocationController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Services\Locations\LocationCreationService
*/
protected $creationService;
/**
* @var \Pterodactyl\Services\Locations\LocationDeletionService
*/
protected $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Locations\LocationUpdateService
*/
protected $updateService;
/**
* LocationController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
LocationCreationService $creationService,
LocationDeletionService $deletionService,
LocationRepositoryInterface $repository,
LocationUpdateService $updateService
) {
$this->alert = $alert;
$this->creationService = $creationService;
$this->deletionService = $deletionService;
$this->repository = $repository;
$this->updateService = $updateService;
}
/**
* Return the location overview page.
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('admin.locations.index', [
'locations' => $this->repository->getAllWithDetails(),
]);
}
/**
* Return the location view page.
*
* @param int $id
*
* @return \Illuminate\View\View
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function view($id)
{
return view('admin.locations.view', [
'location' => $this->repository->getWithNodes($id),
]);
}
/**
* Handle request to create new location.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function create(LocationFormRequest $request)
{
$location = $this->creationService->handle($request->normalize());
$this->alert->success('Location was created successfully.')->flash();
return redirect()->route('admin.locations.view', $location->id);
}
/**
* Handle request to update or delete location.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function update(LocationFormRequest $request, Location $location)
{
if ($request->input('action') === 'delete') {
return $this->delete($location);
}
$this->updateService->handle($location->id, $request->normalize());
$this->alert->success('Location was updated successfully.')->flash();
return redirect()->route('admin.locations.view', $location->id);
}
/**
* Delete a location from the system.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function delete(Location $location)
{
try {
$this->deletionService->handle($location->id);
return redirect()->route('admin.locations');
} catch (DisplayException $ex) {
$this->alert->danger($ex->getMessage())->flash();
}
return redirect()->route('admin.locations.view', $location->id);
}
}

View file

@ -1,204 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Ramsey\Uuid\Uuid;
use Illuminate\Http\Request;
use Pterodactyl\Models\Nest;
use Pterodactyl\Models\Mount;
use Pterodactyl\Models\Location;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Http\Requests\Admin\MountFormRequest;
use Pterodactyl\Repositories\Eloquent\MountRepository;
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
class MountController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface
*/
protected $nestRepository;
/**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
*/
protected $locationRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\MountRepository
*/
protected $repository;
/**
* MountController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
NestRepositoryInterface $nestRepository,
LocationRepositoryInterface $locationRepository,
MountRepository $repository
) {
$this->alert = $alert;
$this->nestRepository = $nestRepository;
$this->locationRepository = $locationRepository;
$this->repository = $repository;
}
/**
* Return the mount overview page.
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('admin.mounts.index', [
'mounts' => $this->repository->getAllWithDetails(),
]);
}
/**
* Return the mount view page.
*
* @param string $id
*
* @return \Illuminate\View\View
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function view($id)
{
$nests = Nest::query()->with('eggs')->get();
$locations = Location::query()->with('nodes')->get();
return view('admin.mounts.view', [
'mount' => $this->repository->getWithRelations($id),
'nests' => $nests,
'locations' => $locations,
]);
}
/**
* Handle request to create new mount.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function create(MountFormRequest $request)
{
$model = (new Mount())->fill($request->validated());
$model->forceFill(['uuid' => Uuid::uuid4()->toString()]);
$model->saveOrFail();
$mount = $model->fresh();
$this->alert->success('Mount was created successfully.')->flash();
return redirect()->route('admin.mounts.view', $mount->id);
}
/**
* Handle request to update or delete location.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function update(MountFormRequest $request, Mount $mount)
{
if ($request->input('action') === 'delete') {
return $this->delete($mount);
}
$mount->forceFill($request->validated())->save();
$this->alert->success('Mount was updated successfully.')->flash();
return redirect()->route('admin.mounts.view', $mount->id);
}
/**
* Delete a location from the system.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
*/
public function delete(Mount $mount)
{
$mount->delete();
return redirect()->route('admin.mounts');
}
/**
* Adds eggs to the mount's many to many relation.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function addEggs(Request $request, Mount $mount)
{
$validatedData = $request->validate([
'eggs' => 'required|exists:eggs,id',
]);
$eggs = $validatedData['eggs'] ?? [];
if (count($eggs) > 0) {
$mount->eggs()->attach($eggs);
}
$this->alert->success('Mount was updated successfully.')->flash();
return redirect()->route('admin.mounts.view', $mount->id);
}
/**
* Adds nodes to the mount's many to many relation.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function addNodes(Request $request, Mount $mount)
{
$data = $request->validate(['nodes' => 'required|exists:nodes,id']);
$nodes = $data['nodes'] ?? [];
if (count($nodes) > 0) {
$mount->nodes()->attach($nodes);
}
$this->alert->success('Mount was updated successfully.')->flash();
return redirect()->route('admin.mounts.view', $mount->id);
}
/**
* Deletes an egg from the mount's many to many relation.
*
* @return \Illuminate\Http\Response
*/
public function deleteEgg(Mount $mount, int $egg_id)
{
$mount->eggs()->detach($egg_id);
return response('', 204);
}
/**
* Deletes an node from the mount's many to many relation.
*
* @return \Illuminate\Http\Response
*/
public function deleteNode(Mount $mount, int $node_id)
{
$mount->nodes()->detach($node_id);
return response('', 204);
}
}

View file

@ -1,132 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Admin\Nests;
use Javascript;
use Illuminate\View\View;
use Pterodactyl\Models\Egg;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Eggs\EggUpdateService;
use Pterodactyl\Services\Eggs\EggCreationService;
use Pterodactyl\Services\Eggs\EggDeletionService;
use Pterodactyl\Http\Requests\Admin\Egg\EggFormRequest;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
class EggController extends Controller
{
protected $alert;
protected $creationService;
protected $deletionService;
protected $nestRepository;
protected $repository;
protected $updateService;
public function __construct(
AlertsMessageBag $alert,
EggCreationService $creationService,
EggDeletionService $deletionService,
EggRepositoryInterface $repository,
EggUpdateService $updateService,
NestRepositoryInterface $nestRepository
) {
$this->alert = $alert;
$this->creationService = $creationService;
$this->deletionService = $deletionService;
$this->nestRepository = $nestRepository;
$this->repository = $repository;
$this->updateService = $updateService;
}
/**
* Handle a request to display the Egg creation page.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function create(): View
{
$nests = $this->nestRepository->getWithEggs();
Javascript::put(['nests' => $nests->keyBy('id')]);
return view('admin.eggs.new', ['nests' => $nests]);
}
/**
* Handle request to store a new Egg.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException
*/
public function store(EggFormRequest $request): RedirectResponse
{
$data = $request->normalize();
if (!empty($data['docker_images']) && !is_array($data['docker_images'])) {
$data['docker_images'] = array_map(function ($value) {
return trim($value);
}, explode("\n", $data['docker_images']));
}
$egg = $this->creationService->handle($data);
$this->alert->success(trans('admin/nests.eggs.notices.egg_created'))->flash();
return redirect()->route('admin.nests.egg.view', $egg->id);
}
/**
* Handle request to view a single Egg.
*/
public function view(Egg $egg): View
{
return view('admin.eggs.view', ['egg' => $egg]);
}
/**
* Handle request to update an Egg.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Egg\NoParentConfigurationFoundException
*/
public function update(EggFormRequest $request, Egg $egg): RedirectResponse
{
$data = $request->normalize();
if (!empty($data['docker_images']) && !is_array($data['docker_images'])) {
$data['docker_images'] = array_map(function ($value) {
return trim($value);
}, explode("\n", $data['docker_images']));
}
$this->updateService->handle($egg, $data);
$this->alert->success(trans('admin/nests.eggs.notices.updated'))->flash();
return redirect()->route('admin.nests.egg.view', $egg->id);
}
/**
* Handle request to destroy an egg.
*
* @throws \Pterodactyl\Exceptions\Service\Egg\HasChildrenException
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
*/
public function destroy(Egg $egg): RedirectResponse
{
$this->deletionService->handle($egg->id);
$this->alert->success(trans('admin/nests.eggs.notices.deleted'))->flash();
return redirect()->route('admin.nests.view', $egg->nest_id);
}
}

View file

@ -1,81 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Nests;
use Illuminate\View\View;
use Pterodactyl\Models\Egg;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Eggs\Scripts\InstallScriptService;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Egg\EggScriptFormRequest;
class EggScriptController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Services\Eggs\Scripts\InstallScriptService
*/
protected $installScriptService;
/**
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
*/
protected $repository;
/**
* EggScriptController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
EggRepositoryInterface $repository,
InstallScriptService $installScriptService
) {
$this->alert = $alert;
$this->installScriptService = $installScriptService;
$this->repository = $repository;
}
/**
* Handle requests to render installation script for an Egg.
*/
public function index(int $egg): View
{
$egg = $this->repository->getWithCopyAttributes($egg);
$copy = $this->repository->findWhere([
['copy_script_from', '=', null],
['nest_id', '=', $egg->nest_id],
['id', '!=', $egg],
]);
$rely = $this->repository->findWhere([
['copy_script_from', '=', $egg->id],
]);
return view('admin.eggs.scripts', [
'copyFromOptions' => $copy,
'relyOnScript' => $rely,
'egg' => $egg,
]);
}
/**
* Handle a request to update the installation script for an Egg.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Egg\InvalidCopyFromException
*/
public function update(EggScriptFormRequest $request, Egg $egg): RedirectResponse
{
$this->installScriptService->handle($egg, $request->normalize());
$this->alert->success(trans('admin/nests.eggs.notices.script_updated'))->flash();
return redirect()->route('admin.nests.egg.scripts', $egg);
}
}

View file

@ -1,105 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Admin\Nests;
use Pterodactyl\Models\Egg;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Symfony\Component\HttpFoundation\Response;
use Pterodactyl\Services\Eggs\Sharing\EggExporterService;
use Pterodactyl\Services\Eggs\Sharing\EggImporterService;
use Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest;
use Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService;
class EggShareController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Services\Eggs\Sharing\EggExporterService
*/
protected $exporterService;
/**
* @var \Pterodactyl\Services\Eggs\Sharing\EggImporterService
*/
protected $importerService;
/**
* @var \Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService
*/
protected $updateImporterService;
/**
* OptionShareController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
EggExporterService $exporterService,
EggImporterService $importerService,
EggUpdateImporterService $updateImporterService
) {
$this->alert = $alert;
$this->exporterService = $exporterService;
$this->importerService = $importerService;
$this->updateImporterService = $updateImporterService;
}
/**
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function export(Egg $egg): Response
{
$filename = trim(preg_replace('/[^\w]/', '-', kebab_case($egg->name)), '-');
return response($this->exporterService->handle($egg->id), 200, [
'Content-Transfer-Encoding' => 'binary',
'Content-Description' => 'File Transfer',
'Content-Disposition' => 'attachment; filename=egg-' . $filename . '.json',
'Content-Type' => 'application/json',
]);
}
/**
* Import a new service option using an XML file.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException
* @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException
*/
public function import(EggImportFormRequest $request): RedirectResponse
{
$egg = $this->importerService->handle($request->file('import_file'), $request->input('import_to_nest'));
$this->alert->success(trans('admin/nests.eggs.notices.imported'))->flash();
return redirect()->route('admin.nests.egg.view', ['egg' => $egg->id]);
}
/**
* Update an existing Egg using a new imported file.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException
* @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException
*/
public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse
{
$this->updateImporterService->handle($egg, $request->file('import_file'));
$this->alert->success(trans('admin/nests.eggs.notices.updated_via_import'))->flash();
return redirect()->route('admin.nests.egg.view', ['egg' => $egg]);
}
}

View file

@ -1,125 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Admin\Nests;
use Illuminate\View\View;
use Pterodactyl\Models\Egg;
use Pterodactyl\Models\EggVariable;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Services\Eggs\Variables\VariableUpdateService;
use Pterodactyl\Http\Requests\Admin\Egg\EggVariableFormRequest;
use Pterodactyl\Services\Eggs\Variables\VariableCreationService;
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
class EggVariableController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Services\Eggs\Variables\VariableCreationService
*/
protected $creationService;
/**
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Eggs\Variables\VariableUpdateService
*/
protected $updateService;
/**
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
*/
protected $variableRepository;
/**
* EggVariableController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
VariableCreationService $creationService,
VariableUpdateService $updateService,
EggRepositoryInterface $repository,
EggVariableRepositoryInterface $variableRepository
) {
$this->alert = $alert;
$this->creationService = $creationService;
$this->repository = $repository;
$this->updateService = $updateService;
$this->variableRepository = $variableRepository;
}
/**
* Handle request to view the variables attached to an Egg.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function view(int $egg): View
{
$egg = $this->repository->getWithVariables($egg);
return view('admin.eggs.variables', ['egg' => $egg]);
}
/**
* Handle a request to create a new Egg variable.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException
*/
public function store(EggVariableFormRequest $request, Egg $egg): RedirectResponse
{
$this->creationService->handle($egg->id, $request->normalize());
$this->alert->success(trans('admin/nests.variables.notices.variable_created'))->flash();
return redirect()->route('admin.nests.egg.variables', $egg->id);
}
/**
* Handle a request to update an existing Egg variable.
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException
*/
public function update(EggVariableFormRequest $request, Egg $egg, EggVariable $variable): RedirectResponse
{
$this->updateService->handle($variable, $request->normalize());
$this->alert->success(trans('admin/nests.variables.notices.variable_updated', [
'variable' => $variable->name,
]))->flash();
return redirect()->route('admin.nests.egg.variables', $egg->id);
}
/**
* Handle a request to delete an existing Egg variable from the Panel.
*/
public function destroy(int $egg, EggVariable $variable): RedirectResponse
{
$this->variableRepository->delete($variable->id);
$this->alert->success(trans('admin/nests.variables.notices.variable_deleted', [
'variable' => $variable->name,
]))->flash();
return redirect()->route('admin.nests.egg.variables', $egg);
}
}

View file

@ -1,137 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Admin\Nests;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Nests\NestUpdateService;
use Pterodactyl\Services\Nests\NestCreationService;
use Pterodactyl\Services\Nests\NestDeletionService;
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Nest\StoreNestFormRequest;
class NestController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Services\Nests\NestCreationService
*/
protected $nestCreationService;
/**
* @var \Pterodactyl\Services\Nests\NestDeletionService
*/
protected $nestDeletionService;
/**
* @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Nests\NestUpdateService
*/
protected $nestUpdateService;
/**
* NestController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
NestCreationService $nestCreationService,
NestDeletionService $nestDeletionService,
NestRepositoryInterface $repository,
NestUpdateService $nestUpdateService
) {
$this->alert = $alert;
$this->nestDeletionService = $nestDeletionService;
$this->nestCreationService = $nestCreationService;
$this->nestUpdateService = $nestUpdateService;
$this->repository = $repository;
}
/**
* Render nest listing page.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function index(): View
{
return view('admin.nests.index', [
'nests' => $this->repository->getWithCounts(),
]);
}
/**
* Render nest creation page.
*/
public function create(): View
{
return view('admin.nests.new');
}
/**
* Handle the storage of a new nest.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(StoreNestFormRequest $request): RedirectResponse
{
$nest = $this->nestCreationService->handle($request->normalize());
$this->alert->success(trans('admin/nests.notices.created', ['name' => $nest->name]))->flash();
return redirect()->route('admin.nests.view', $nest->id);
}
/**
* Return details about a nest including all of the eggs and servers per egg.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function view(int $nest): View
{
return view('admin.nests.view', [
'nest' => $this->repository->getWithEggServers($nest),
]);
}
/**
* Handle request to update a nest.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(StoreNestFormRequest $request, int $nest): RedirectResponse
{
$this->nestUpdateService->handle($nest, $request->normalize());
$this->alert->success(trans('admin/nests.notices.updated'))->flash();
return redirect()->route('admin.nests.view', $nest);
}
/**
* Handle request to delete a nest.
*
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
*/
public function destroy(int $nest): RedirectResponse
{
$this->nestDeletionService->handle($nest);
$this->alert->success(trans('admin/nests.notices.deleted'))->flash();
return redirect()->route('admin.nests');
}
}

View file

@ -1,82 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\ApiKey;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Services\Api\KeyCreationService;
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
class NodeAutoDeployController extends Controller
{
/**
* @var \Pterodactyl\Services\Api\KeyCreationService
*/
private $keyCreationService;
/**
* @var \Pterodactyl\Repositories\Eloquent\ApiKeyRepository
*/
private $repository;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
private $encrypter;
/**
* NodeAutoDeployController constructor.
*/
public function __construct(
ApiKeyRepository $repository,
Encrypter $encrypter,
KeyCreationService $keyCreationService
) {
$this->keyCreationService = $keyCreationService;
$this->repository = $repository;
$this->encrypter = $encrypter;
}
/**
* Generates a new API key for the logged in user with only permission to read
* nodes, and returns that as the deployment key for a node.
*
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function __invoke(Request $request, Node $node)
{
/** @var \Pterodactyl\Models\ApiKey|null $key */
$key = $this->repository->getApplicationKeys($request->user())
->filter(function (ApiKey $key) {
foreach ($key->getAttributes() as $permission => $value) {
if ($permission === 'r_nodes' && $value === 1) {
return true;
}
}
return false;
})
->first();
// We couldn't find a key that exists for this user with only permission for
// reading nodes. Go ahead and create it now.
if (!$key) {
$key = $this->keyCreationService->setKeyType(ApiKey::TYPE_APPLICATION)->handle([
'user_id' => $request->user()->id,
'memo' => 'Automatically generated node deployment key.',
'allowed_ips' => [],
], ['r_nodes' => 1]);
}
return JsonResponse::create([
'node' => $node->id,
'token' => $key->identifier . $this->encrypter->decrypt($key->token),
]);
}
}

View file

@ -1,49 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Nodes;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Spatie\QueryBuilder\QueryBuilder;
use Illuminate\Contracts\View\Factory;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
class NodeController extends Controller
{
/**
* @var \Illuminate\Contracts\View\Factory
*/
private $view;
/**
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
*/
private $repository;
/**
* NodeController constructor.
*/
public function __construct(NodeRepository $repository, Factory $view)
{
$this->view = $view;
$this->repository = $repository;
}
/**
* Returns a listing of nodes on the system.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
{
$nodes = QueryBuilder::for(
Node::query()->with('location')->withCount('servers')
)
->allowedFilters(['uuid', 'name'])
->allowedSorts(['id'])
->paginate(25);
return $this->view->make('admin.nodes.index', ['nodes' => $nodes]);
}
}

View file

@ -1,147 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Nodes;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Illuminate\Support\Collection;
use Pterodactyl\Models\Allocation;
use Illuminate\Contracts\View\Factory;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
class NodeViewController extends Controller
{
use JavascriptInjection;
/**
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
*/
private $repository;
/**
* @var \Illuminate\Contracts\View\Factory
*/
private $view;
/**
* @var \Pterodactyl\Services\Helpers\SoftwareVersionService
*/
private $versionService;
/**
* @var \Pterodactyl\Repositories\Eloquent\LocationRepository
*/
private $locationRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\AllocationRepository
*/
private $allocationRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $serverRepository;
/**
* NodeViewController constructor.
*/
public function __construct(
AllocationRepository $allocationRepository,
LocationRepository $locationRepository,
NodeRepository $repository,
ServerRepository $serverRepository,
SoftwareVersionService $versionService,
Factory $view
) {
$this->repository = $repository;
$this->view = $view;
$this->versionService = $versionService;
$this->locationRepository = $locationRepository;
$this->allocationRepository = $allocationRepository;
$this->serverRepository = $serverRepository;
}
/**
* Returns index view for a specific node on the system.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request, Node $node)
{
$node = $this->repository->loadLocationAndServerCount($node);
return $this->view->make('admin.nodes.view.index', [
'node' => $node,
'stats' => $this->repository->getUsageStats($node),
'version' => $this->versionService,
]);
}
/**
* Returns the settings page for a specific node.
*
* @return \Illuminate\Contracts\View\View
*/
public function settings(Request $request, Node $node)
{
return $this->view->make('admin.nodes.view.settings', [
'node' => $node,
'locations' => $this->locationRepository->all(),
]);
}
/**
* Return the node configuration page for a specific node.
*
* @return \Illuminate\Contracts\View\View
*/
public function configuration(Request $request, Node $node)
{
return $this->view->make('admin.nodes.view.configuration', compact('node'));
}
/**
* Return the node allocation management page.
*
* @return \Illuminate\Contracts\View\View
*/
public function allocations(Request $request, Node $node)
{
$node = $this->repository->loadNodeAllocations($node);
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
return $this->view->make('admin.nodes.view.allocation', [
'node' => $node,
'allocations' => Allocation::query()->where('node_id', $node->id)
->groupBy('ip')
->orderByRaw('INET_ATON(ip) ASC')
->get(['ip']),
]);
}
/**
* Return a listing of servers that exist for this specific node.
*
* @return \Illuminate\Contracts\View\View
*/
public function servers(Request $request, Node $node)
{
$this->plainInject([
'node' => Collection::wrap($node->makeVisible(['daemon_token_id', 'daemon_token']))
->only(['scheme', 'fqdn', 'daemonListen', 'daemon_token_id', 'daemon_token']),
]);
return $this->view->make('admin.nodes.view.servers', [
'node' => $node,
'servers' => $this->serverRepository->loadAllServersForNode($node->id, 25),
]);
}
}

View file

@ -1,48 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Nodes;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
class SystemInformationController extends Controller
{
/**
* @var \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository
*/
private $repository;
/**
* SystemInformationController constructor.
*/
public function __construct(DaemonConfigurationRepository $repository)
{
$this->repository = $repository;
}
/**
* Returns system information from the Daemon.
*
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function __invoke(Request $request, Node $node)
{
$data = $this->repository->setNode($node)->getSystemInformation();
return JsonResponse::create([
'version' => $data['version'] ?? '',
'system' => [
'type' => Str::title($data['os'] ?? 'Unknown'),
'arch' => $data['architecture'] ?? '--',
'release' => $data['kernel_version'] ?? '--',
'cpus' => $data['cpu_count'] ?? 0,
],
]);
}
}

View file

@ -1,278 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Illuminate\Http\Response;
use Pterodactyl\Models\Allocation;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Nodes\NodeUpdateService;
use Illuminate\Cache\Repository as CacheRepository;
use Pterodactyl\Services\Nodes\NodeCreationService;
use Pterodactyl\Services\Nodes\NodeDeletionService;
use Pterodactyl\Services\Allocations\AssignmentService;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
use Pterodactyl\Http\Requests\Admin\Node\NodeFormRequest;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Node\AllocationFormRequest;
use Pterodactyl\Services\Allocations\AllocationDeletionService;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Node\AllocationAliasFormRequest;
class NodesController extends Controller
{
/**
* @var \Pterodactyl\Services\Allocations\AllocationDeletionService
*/
protected $allocationDeletionService;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
*/
protected $allocationRepository;
/**
* @var \Pterodactyl\Services\Allocations\AssignmentService
*/
protected $assignmentService;
/**
* @var \Illuminate\Cache\Repository
*/
protected $cache;
/**
* @var \Pterodactyl\Services\Nodes\NodeCreationService
*/
protected $creationService;
/**
* @var \Pterodactyl\Services\Nodes\NodeDeletionService
*/
protected $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
*/
protected $locationRepository;
/**
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
protected $serverRepository;
/**
* @var \Pterodactyl\Services\Nodes\NodeUpdateService
*/
protected $updateService;
/**
* @var \Pterodactyl\Services\Helpers\SoftwareVersionService
*/
protected $versionService;
/**
* NodesController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
AllocationDeletionService $allocationDeletionService,
AllocationRepositoryInterface $allocationRepository,
AssignmentService $assignmentService,
CacheRepository $cache,
NodeCreationService $creationService,
NodeDeletionService $deletionService,
LocationRepositoryInterface $locationRepository,
NodeRepositoryInterface $repository,
ServerRepositoryInterface $serverRepository,
NodeUpdateService $updateService,
SoftwareVersionService $versionService
) {
$this->alert = $alert;
$this->allocationDeletionService = $allocationDeletionService;
$this->allocationRepository = $allocationRepository;
$this->assignmentService = $assignmentService;
$this->cache = $cache;
$this->creationService = $creationService;
$this->deletionService = $deletionService;
$this->locationRepository = $locationRepository;
$this->repository = $repository;
$this->serverRepository = $serverRepository;
$this->updateService = $updateService;
$this->versionService = $versionService;
}
/**
* Displays create new node page.
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function create()
{
$locations = $this->locationRepository->all();
if (count($locations) < 1) {
$this->alert->warning(trans('admin/node.notices.location_required'))->flash();
return redirect()->route('admin.locations');
}
return view('admin.nodes.new', ['locations' => $locations]);
}
/**
* Post controller to create a new node on the system.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(NodeFormRequest $request)
{
$node = $this->creationService->handle($request->normalize());
$this->alert->info(trans('admin/node.notices.node_created'))->flash();
return redirect()->route('admin.nodes.view.allocation', $node->id);
}
/**
* Updates settings for a node.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function updateSettings(NodeFormRequest $request, Node $node)
{
$this->updateService->handle($node, $request->normalize(), $request->input('reset_secret') === 'on');
$this->alert->success(trans('admin/node.notices.node_updated'))->flash();
return redirect()->route('admin.nodes.view.settings', $node->id)->withInput();
}
/**
* Removes a single allocation from a node.
*
* @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException
*/
public function allocationRemoveSingle(int $node, Allocation $allocation): Response
{
$this->allocationDeletionService->handle($allocation);
return response('', 204);
}
/**
* Removes multiple individual allocations from a node.
*
* @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException
*/
public function allocationRemoveMultiple(Request $request, int $node): Response
{
$allocations = $request->input('allocations');
foreach ($allocations as $rawAllocation) {
$allocation = new Allocation();
$allocation->id = $rawAllocation['id'];
$this->allocationRemoveSingle($node, $allocation);
}
return response('', 204);
}
/**
* Remove all allocations for a specific IP at once on a node.
*
* @param int $node
*
* @return \Illuminate\Http\RedirectResponse
*/
public function allocationRemoveBlock(Request $request, $node)
{
$this->allocationRepository->deleteWhere([
['node_id', '=', $node],
['server_id', '=', null],
['ip', '=', $request->input('ip')],
]);
$this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => $request->input('ip')]))
->flash();
return redirect()->route('admin.nodes.view.allocation', $node);
}
/**
* Sets an alias for a specific allocation on a node.
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function allocationSetAlias(AllocationAliasFormRequest $request)
{
$this->allocationRepository->update($request->input('allocation_id'), [
'ip_alias' => (empty($request->input('alias'))) ? null : $request->input('alias'),
]);
return response('', 204);
}
/**
* Creates new allocations on a node.
*
* @param int|\Pterodactyl\Models\Node $node
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
* @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
*/
public function createAllocation(AllocationFormRequest $request, Node $node)
{
$this->assignmentService->handle($node, $request->normalize());
$this->alert->success(trans('admin/node.notices.allocations_added'))->flash();
return redirect()->route('admin.nodes.view.allocation', $node->id);
}
/**
* Deletes a node from the system.
*
* @param $node
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function delete($node)
{
$this->deletionService->handle($node);
$this->alert->success(trans('admin/node.notices.node_deleted'))->flash();
return redirect()->route('admin.nodes');
}
}

View file

@ -1,127 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Servers;
use JavaScript;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Eloquent\NestRepository;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Http\Requests\Admin\ServerFormRequest;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Services\Servers\ServerCreationService;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
class CreateServerController extends Controller
{
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/**
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
*/
private $nodeRepository;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Pterodactyl\Repositories\Eloquent\NestRepository
*/
private $nestRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\LocationRepository
*/
private $locationRepository;
/**
* @var \Pterodactyl\Services\Servers\ServerCreationService
*/
private $creationService;
/**
* CreateServerController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
NestRepository $nestRepository,
LocationRepository $locationRepository,
NodeRepository $nodeRepository,
ServerRepository $repository,
ServerCreationService $creationService
) {
$this->repository = $repository;
$this->nodeRepository = $nodeRepository;
$this->alert = $alert;
$this->nestRepository = $nestRepository;
$this->locationRepository = $locationRepository;
$this->creationService = $creationService;
}
/**
* Displays the create server page.
*
* @return \Illuminate\Contracts\View\Factory
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function index()
{
$nodes = $this->nodeRepository->all();
if (count($nodes) < 1) {
$this->alert->warning(trans('admin/server.alerts.node_required'))->flash();
return redirect()->route('admin.nodes');
}
$nests = $this->nestRepository->getWithEggs();
Javascript::put([
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
'nests' => $nests->map(function ($item) {
return array_merge($item->toArray(), [
'eggs' => $item->eggs->keyBy('id')->toArray(),
]);
})->keyBy('id'),
]);
return view('admin.servers.new', [
'locations' => $this->locationRepository->all(),
'nests' => $nests,
]);
}
/**
* Create a new server on the remote system.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException
* @throws \Throwable
*/
public function store(ServerFormRequest $request)
{
$data = $request->except(['_token']);
if (!empty($data['custom_image'])) {
$data['image'] = $data['custom_image'];
unset($data['custom_image']);
}
$server = $this->creationService->handle($data);
$this->alert->success(
trans('admin/server.alerts.server_created')
)->flash();
return RedirectResponse::create('/admin/servers/view/' . $server->id);
}
}

View file

@ -1,54 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Servers;
use Illuminate\Http\Request;
use Pterodactyl\Models\Server;
use Spatie\QueryBuilder\QueryBuilder;
use Illuminate\Contracts\View\Factory;
use Spatie\QueryBuilder\AllowedFilter;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Models\Filters\AdminServerFilter;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
class ServerController extends Controller
{
/**
* @var \Illuminate\Contracts\View\Factory
*/
private $view;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/**
* ServerController constructor.
*/
public function __construct(
Factory $view,
ServerRepository $repository
) {
$this->view = $view;
$this->repository = $repository;
}
/**
* Returns all of the servers that exist on the system using a paginated result set. If
* a query is passed along in the request it is also passed to the repository function.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
{
$servers = QueryBuilder::for(Server::query()->with('node', 'user', 'allocation'))
->allowedFilters([
AllowedFilter::exact('owner_id'),
AllowedFilter::custom('*', new AdminServerFilter()),
])
->paginate(config()->get('pterodactyl.paginate.admin.servers'));
return $this->view->make('admin.servers.index', ['servers' => $servers]);
}
}

View file

@ -1,150 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Servers;
use Illuminate\Http\Request;
use Pterodactyl\Models\Server;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Models\ServerTransfer;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Servers\TransferService;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
class ServerTransferController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
*/
private $allocationRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/**
* @var \Pterodactyl\Repositories\Eloquent\LocationRepository
*/
private $locationRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
*/
private $nodeRepository;
/**
* @var \Pterodactyl\Services\Servers\TransferService
*/
private $transferService;
/**
* @var \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository
*/
private $daemonConfigurationRepository;
/**
* ServerTransferController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
AllocationRepositoryInterface $allocationRepository,
ServerRepository $repository,
LocationRepository $locationRepository,
NodeRepository $nodeRepository,
TransferService $transferService,
DaemonConfigurationRepository $daemonConfigurationRepository
) {
$this->alert = $alert;
$this->allocationRepository = $allocationRepository;
$this->repository = $repository;
$this->locationRepository = $locationRepository;
$this->nodeRepository = $nodeRepository;
$this->transferService = $transferService;
$this->daemonConfigurationRepository = $daemonConfigurationRepository;
}
/**
* Starts a transfer of a server to a new node.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function transfer(Request $request, Server $server)
{
$validatedData = $request->validate([
'node_id' => 'required|exists:nodes,id',
'allocation_id' => 'required|bail|unique:servers|exists:allocations,id',
'allocation_additional' => 'nullable',
]);
$node_id = $validatedData['node_id'];
$allocation_id = intval($validatedData['allocation_id']);
$additional_allocations = array_map('intval', $validatedData['allocation_additional'] ?? []);
// Check if the node is viable for the transfer.
$node = $this->nodeRepository->getNodeWithResourceUsage($node_id);
if ($node->isViable($server->memory, $server->disk)) {
// Check if the selected daemon is online.
$this->daemonConfigurationRepository->setNode($node)->getSystemInformation();
// Create a new ServerTransfer entry.
$transfer = new ServerTransfer();
$transfer->server_id = $server->id;
$transfer->old_node = $server->node_id;
$transfer->new_node = $node_id;
$transfer->old_allocation = $server->allocation_id;
$transfer->new_allocation = $allocation_id;
$transfer->old_additional_allocations = $server->allocations->where('id', '!=', $server->allocation_id)->pluck('id');
$transfer->new_additional_allocations = $additional_allocations;
$transfer->save();
// Add the allocations to the server so they cannot be automatically assigned while the transfer is in progress.
$this->assignAllocationsToServer($server, $node_id, $allocation_id, $additional_allocations);
// Request an archive from the server's current daemon. (this also checks if the daemon is online)
$this->transferService->requestArchive($server);
$this->alert->success(trans('admin/server.alerts.transfer_started'))->flash();
} else {
$this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash();
}
return redirect()->route('admin.servers.view.manage', $server->id);
}
/**
* Assigns the specified allocations to the specified server.
*/
private function assignAllocationsToServer(Server $server, int $node_id, int $allocation_id, array $additional_allocations)
{
$allocations = $additional_allocations;
array_push($allocations, $allocation_id);
$unassigned = $this->allocationRepository->getUnassignedAllocationIds($node_id);
$updateIds = [];
foreach ($allocations as $allocation) {
if (!in_array($allocation, $unassigned)) {
continue;
}
$updateIds[] = $allocation;
}
if (!empty($updateIds)) {
$this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => $server->id]);
}
}
}

View file

@ -1,218 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Servers;
use JavaScript;
use Illuminate\Http\Request;
use Pterodactyl\Models\Nest;
use Pterodactyl\Models\Server;
use Illuminate\Contracts\View\Factory;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Servers\EnvironmentService;
use Pterodactyl\Repositories\Eloquent\NestRepository;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Eloquent\MountRepository;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Repositories\Eloquent\LocationRepository;
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
class ServerViewController extends Controller
{
use JavascriptInjection;
/**
* @var \Illuminate\Contracts\View\Factory
*/
private $view;
/**
* @var \Pterodactyl\Repositories\Eloquent\DatabaseHostRepository
*/
private $databaseHostRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/**
* @var \Pterodactyl\Repositories\Eloquent\MountRepository
*/
protected $mountRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\NestRepository
*/
private $nestRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\LocationRepository
*/
private $locationRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
*/
private $nodeRepository;
/**
* @var \Pterodactyl\Services\Servers\EnvironmentService
*/
private $environmentService;
/**
* ServerViewController constructor.
*/
public function __construct(
Factory $view,
DatabaseHostRepository $databaseHostRepository,
LocationRepository $locationRepository,
MountRepository $mountRepository,
NestRepository $nestRepository,
NodeRepository $nodeRepository,
ServerRepository $repository,
EnvironmentService $environmentService
) {
$this->view = $view;
$this->databaseHostRepository = $databaseHostRepository;
$this->locationRepository = $locationRepository;
$this->mountRepository = $mountRepository;
$this->nestRepository = $nestRepository;
$this->nodeRepository = $nodeRepository;
$this->repository = $repository;
$this->environmentService = $environmentService;
}
/**
* Returns the index view for a server.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request, Server $server)
{
return $this->view->make('admin.servers.view.index', compact('server'));
}
/**
* Returns the server details page.
*
* @return \Illuminate\Contracts\View\View
*/
public function details(Request $request, Server $server)
{
return $this->view->make('admin.servers.view.details', compact('server'));
}
/**
* Returns a view of server build settings.
*
* @return \Illuminate\Contracts\View\View
*/
public function build(Request $request, Server $server)
{
$allocations = $server->node->allocations->toBase();
return $this->view->make('admin.servers.view.build', [
'server' => $server,
'assigned' => $allocations->where('server_id', $server->id)->sortBy('port')->sortBy('ip'),
'unassigned' => $allocations->where('server_id', null)->sortBy('port')->sortBy('ip'),
]);
}
/**
* Returns the server startup management page.
*
* @return \Illuminate\Contracts\View\View
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function startup(Request $request, Server $server)
{
$nests = $this->nestRepository->getWithEggs();
$variables = $this->environmentService->handle($server);
$this->plainInject([
'server' => $server,
'server_variables' => $variables,
'nests' => $nests->map(function (Nest $item) {
return array_merge($item->toArray(), [
'eggs' => $item->eggs->keyBy('id')->toArray(),
]);
})->keyBy('id'),
]);
return $this->view->make('admin.servers.view.startup', compact('server', 'nests'));
}
/**
* Returns all of the databases that exist for the server.
*
* @return \Illuminate\Contracts\View\View
*/
public function database(Request $request, Server $server)
{
return $this->view->make('admin.servers.view.database', [
'hosts' => $this->databaseHostRepository->all(),
'server' => $server,
]);
}
/**
* Returns all of the mounts that exist for the server.
*
* @return \Illuminate\Contracts\View\View
*/
public function mounts(Request $request, Server $server)
{
$server->load('mounts');
return $this->view->make('admin.servers.view.mounts', [
'mounts' => $this->mountRepository->getMountListForServer($server),
'server' => $server,
]);
}
/**
* Returns the base server management page, or an exception if the server
* is in a state that cannot be recovered from.
*
* @return \Illuminate\Contracts\View\View
*
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function manage(Request $request, Server $server)
{
if ($server->status === Server::STATUS_INSTALL_FAILED) {
throw new DisplayException('This server is in a failed install state and cannot be recovered. Please delete and re-create the server.');
}
// Check if the panel doesn't have at least 2 nodes configured.
$nodes = $this->nodeRepository->all();
$canTransfer = false;
if (count($nodes) >= 2) {
$canTransfer = true;
}
Javascript::put([
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
]);
return $this->view->make('admin.servers.view.manage', [
'server' => $server,
'locations' => $this->locationRepository->all(),
'canTransfer' => $canTransfer,
]);
}
/**
* Returns the server deletion page.
*
* @return \Illuminate\Contracts\View\View
*/
public function delete(Request $request, Server $server)
{
return $this->view->make('admin.servers.view.delete', compact('server'));
}
}

View file

@ -1,422 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Mount;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\MountServer;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Validation\ValidationException;
use Pterodactyl\Services\Servers\SuspensionService;
use Pterodactyl\Repositories\Eloquent\MountRepository;
use Pterodactyl\Services\Servers\ServerDeletionService;
use Pterodactyl\Services\Servers\ReinstallServerService;
use Pterodactyl\Exceptions\Model\DataValidationException;
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
use Pterodactyl\Services\Servers\BuildModificationService;
use Pterodactyl\Services\Databases\DatabasePasswordService;
use Pterodactyl\Services\Servers\DetailsModificationService;
use Pterodactyl\Services\Servers\StartupModificationService;
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository;
use Pterodactyl\Services\Databases\DatabaseManagementService;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
use Pterodactyl\Http\Requests\Admin\Servers\Databases\StoreServerDatabaseRequest;
class ServersController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
*/
protected $allocationRepository;
/**
* @var \Pterodactyl\Services\Servers\BuildModificationService
*/
protected $buildModificationService;
/**
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
*/
private $daemonServerRepository;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
protected $databaseRepository;
/**
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
*/
protected $databaseManagementService;
/**
* @var \Pterodactyl\Services\Databases\DatabasePasswordService
*/
protected $databasePasswordService;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
*/
protected $databaseHostRepository;
/**
* @var \Pterodactyl\Services\Servers\ServerDeletionService
*/
protected $deletionService;
/**
* @var \Pterodactyl\Services\Servers\DetailsModificationService
*/
protected $detailsModificationService;
/**
* @var \Pterodactyl\Repositories\Eloquent\MountRepository
*/
protected $mountRepository;
/**
* @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface
*/
protected $nestRepository;
/**
* @var \Pterodactyl\Services\Servers\ReinstallServerService
*/
protected $reinstallService;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
*/
private $serverConfigurationStructureService;
/**
* @var \Pterodactyl\Services\Servers\StartupModificationService
*/
private $startupModificationService;
/**
* @var \Pterodactyl\Services\Servers\SuspensionService
*/
protected $suspensionService;
/**
* ServersController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
AllocationRepositoryInterface $allocationRepository,
BuildModificationService $buildModificationService,
ConfigRepository $config,
DaemonServerRepository $daemonServerRepository,
DatabaseManagementService $databaseManagementService,
DatabasePasswordService $databasePasswordService,
DatabaseRepositoryInterface $databaseRepository,
DatabaseHostRepository $databaseHostRepository,
ServerDeletionService $deletionService,
DetailsModificationService $detailsModificationService,
ReinstallServerService $reinstallService,
ServerRepositoryInterface $repository,
MountRepository $mountRepository,
NestRepositoryInterface $nestRepository,
ServerConfigurationStructureService $serverConfigurationStructureService,
StartupModificationService $startupModificationService,
SuspensionService $suspensionService
) {
$this->alert = $alert;
$this->allocationRepository = $allocationRepository;
$this->buildModificationService = $buildModificationService;
$this->config = $config;
$this->daemonServerRepository = $daemonServerRepository;
$this->databaseHostRepository = $databaseHostRepository;
$this->databaseManagementService = $databaseManagementService;
$this->databasePasswordService = $databasePasswordService;
$this->databaseRepository = $databaseRepository;
$this->detailsModificationService = $detailsModificationService;
$this->deletionService = $deletionService;
$this->nestRepository = $nestRepository;
$this->reinstallService = $reinstallService;
$this->repository = $repository;
$this->mountRepository = $mountRepository;
$this->serverConfigurationStructureService = $serverConfigurationStructureService;
$this->startupModificationService = $startupModificationService;
$this->suspensionService = $suspensionService;
}
/**
* Update the details for a server.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function setDetails(Request $request, Server $server)
{
$this->detailsModificationService->handle($server, $request->only([
'owner_id', 'external_id', 'name', 'description',
]));
$this->alert->success(trans('admin/server.alerts.details_updated'))->flash();
return redirect()->route('admin.servers.view.details', $server->id);
}
/**
* Toggles the install status for a server.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function toggleInstall(Server $server)
{
if ($server->status === Server::STATUS_INSTALL_FAILED) {
throw new DisplayException(trans('admin/server.exceptions.marked_as_failed'));
}
$this->repository->update($server->id, [
'status' => $server->isInstalled() ? Server::STATUS_INSTALLING : null,
], true, true);
$this->alert->success(trans('admin/server.alerts.install_toggled'))->flash();
return redirect()->route('admin.servers.view.manage', $server->id);
}
/**
* Reinstalls the server with the currently assigned service.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function reinstallServer(Server $server)
{
$this->reinstallService->handle($server);
$this->alert->success(trans('admin/server.alerts.server_reinstalled'))->flash();
return redirect()->route('admin.servers.view.manage', $server->id);
}
/**
* Manage the suspension status for a server.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function manageSuspension(Request $request, Server $server)
{
$this->suspensionService->toggle($server, $request->input('action'));
$this->alert->success(trans('admin/server.alerts.suspension_toggled', [
'status' => $request->input('action') . 'ed',
]))->flash();
return redirect()->route('admin.servers.view.manage', $server->id);
}
/**
* Update the build configuration for a server.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Illuminate\Validation\ValidationException
*/
public function updateBuild(Request $request, Server $server)
{
try {
$this->buildModificationService->handle($server, $request->only([
'allocation_id', 'add_allocations', 'remove_allocations',
'memory', 'swap', 'io', 'cpu', 'threads', 'disk',
'database_limit', 'allocation_limit', 'backup_limit', 'oom_disabled',
]));
} catch (DataValidationException $exception) {
throw new ValidationException($exception->validator);
}
$this->alert->success(trans('admin/server.alerts.build_updated'))->flash();
return redirect()->route('admin.servers.view.build', $server->id);
}
/**
* Start the server deletion process.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Throwable
*/
public function delete(Request $request, Server $server)
{
$this->deletionService->withForce($request->filled('force_delete'))->handle($server);
$this->alert->success(trans('admin/server.alerts.server_deleted'))->flash();
return redirect()->route('admin.servers');
}
/**
* Update the startup command as well as variables.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function saveStartup(Request $request, Server $server)
{
$data = $request->except('_token');
if (!empty($data['custom_docker_image'])) {
$data['docker_image'] = $data['custom_docker_image'];
unset($data['custom_docker_image']);
}
try {
$this->startupModificationService
->setUserLevel(User::USER_LEVEL_ADMIN)
->handle($server, $data);
} catch (DataValidationException $exception) {
throw new ValidationException($exception->validator);
}
$this->alert->success(trans('admin/server.alerts.startup_changed'))->flash();
return redirect()->route('admin.servers.view.startup', $server->id);
}
/**
* Creates a new database assigned to a specific server.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function newDatabase(StoreServerDatabaseRequest $request, Server $server)
{
$this->databaseManagementService->create($server, [
'database' => DatabaseManagementService::generateUniqueDatabaseName($request->input('database'), $server->id),
'remote' => $request->input('remote'),
'database_host_id' => $request->input('database_host_id'),
'max_connections' => $request->input('max_connections'),
]);
return redirect()->route('admin.servers.view.database', $server->id)->withInput();
}
/**
* Resets the database password for a specific database on this server.
*
* @param int $server
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function resetDatabasePassword(Request $request, $server)
{
$database = $this->databaseRepository->findFirstWhere([
['server_id', '=', $server],
['id', '=', $request->input('database')],
]);
$this->databasePasswordService->handle($database);
return response('', 204);
}
/**
* Deletes a database from a server.
*
* @param int $server
* @param int $database
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function deleteDatabase($server, $database)
{
$database = $this->databaseRepository->findFirstWhere([
['server_id', '=', $server],
['id', '=', $database],
]);
$this->databaseManagementService->delete($database);
return response('', 204);
}
/**
* Add a mount to a server.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Throwable
*/
public function addMount(Server $server, Mount $mount)
{
$mountServer = (new MountServer())->forceFill([
'mount_id' => $mount->id,
'server_id' => $server->id,
]);
$mountServer->saveOrFail();
$this->alert->success('Mount was added successfully.')->flash();
return redirect()->route('admin.servers.view.mounts', $server->id);
}
/**
* Remove a mount from a server.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function deleteMount(Server $server, Mount $mount)
{
MountServer::where('mount_id', $mount->id)->where('server_id', $server->id)->delete();
$this->alert->success('Mount was removed successfully.')->flash();
return redirect()->route('admin.servers.view.mounts', $server->id);
}
}

View file

@ -1,84 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Settings;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Console\Kernel;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Settings\AdvancedSettingsFormRequest;
class AdvancedController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Illuminate\Contracts\Config\Repository
*/
private $config;
/**
* @var \Illuminate\Contracts\Console\Kernel
*/
private $kernel;
/**
* @var \Pterodactyl\Contracts\Repository\SettingsRepositoryInterface
*/
private $settings;
/**
* AdvancedController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
ConfigRepository $config,
Kernel $kernel,
SettingsRepositoryInterface $settings
) {
$this->alert = $alert;
$this->config = $config;
$this->kernel = $kernel;
$this->settings = $settings;
}
/**
* Render advanced Panel settings UI.
*/
public function index(): View
{
$showRecaptchaWarning = false;
if (
$this->config->get('recaptcha._shipped_secret_key') === $this->config->get('recaptcha.secret_key')
|| $this->config->get('recaptcha._shipped_website_key') === $this->config->get('recaptcha.website_key')
) {
$showRecaptchaWarning = true;
}
return view('admin.settings.advanced', [
'showRecaptchaWarning' => $showRecaptchaWarning,
]);
}
/**
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(AdvancedSettingsFormRequest $request): RedirectResponse
{
foreach ($request->normalize() as $key => $value) {
$this->settings->set('settings::' . $key, $value);
}
$this->kernel->call('queue:restart');
$this->alert->success('Advanced settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash();
return redirect()->route('admin.settings.advanced');
}
}

View file

@ -1,82 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Settings;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Console\Kernel;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Settings\BaseSettingsFormRequest;
class IndexController extends Controller
{
use AvailableLanguages;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Illuminate\Contracts\Console\Kernel
*/
private $kernel;
/**
* @var \Pterodactyl\Contracts\Repository\SettingsRepositoryInterface
*/
private $settings;
/**
* @var \Pterodactyl\Services\Helpers\SoftwareVersionService
*/
private $versionService;
/**
* IndexController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
Kernel $kernel,
SettingsRepositoryInterface $settings,
SoftwareVersionService $versionService
) {
$this->alert = $alert;
$this->kernel = $kernel;
$this->settings = $settings;
$this->versionService = $versionService;
}
/**
* Render the UI for basic Panel settings.
*/
public function index(): View
{
return view('admin.settings.index', [
'version' => $this->versionService,
'languages' => $this->getAvailableLanguages(true),
]);
}
/**
* Handle settings update.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(BaseSettingsFormRequest $request): RedirectResponse
{
foreach ($request->normalize() as $key => $value) {
$this->settings->set('settings::' . $key, $value);
}
$this->kernel->call('queue:restart');
$this->alert->success('Panel settings have been updated successfully and the queue worker was restarted to apply these changes.')->flash();
return redirect()->route('admin.settings');
}
}

View file

@ -1,121 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin\Settings;
use Exception;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Prologue\Alerts\AlertsMessageBag;
use Illuminate\Contracts\Console\Kernel;
use Pterodactyl\Notifications\MailTested;
use Illuminate\Support\Facades\Notification;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Providers\SettingsServiceProvider;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface;
use Pterodactyl\Http\Requests\Admin\Settings\MailSettingsFormRequest;
class MailController extends Controller
{
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Illuminate\Contracts\Config\Repository
*/
private $config;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
private $encrypter;
/**
* @var \Illuminate\Contracts\Console\Kernel
*/
private $kernel;
/**
* @var \Pterodactyl\Contracts\Repository\SettingsRepositoryInterface
*/
private $settings;
/**
* MailController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
ConfigRepository $config,
Encrypter $encrypter,
Kernel $kernel,
SettingsRepositoryInterface $settings
) {
$this->alert = $alert;
$this->config = $config;
$this->encrypter = $encrypter;
$this->kernel = $kernel;
$this->settings = $settings;
}
/**
* Render UI for editing mail settings. This UI should only display if
* the server is configured to send mail using SMTP.
*/
public function index(): View
{
return view('admin.settings.mail', [
'disabled' => $this->config->get('mail.driver') !== 'smtp',
]);
}
/**
* Handle request to update SMTP mail settings.
*
* @throws DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(MailSettingsFormRequest $request): Response
{
if ($this->config->get('mail.driver') !== 'smtp') {
throw new DisplayException('This feature is only available if SMTP is the selected email driver for the Panel.');
}
$values = $request->normalize();
if (array_get($values, 'mail:password') === '!e') {
$values['mail:password'] = '';
}
foreach ($values as $key => $value) {
if (in_array($key, SettingsServiceProvider::getEncryptedKeys()) && !empty($value)) {
$value = $this->encrypter->encrypt($value);
}
$this->settings->set('settings::' . $key, $value);
}
$this->kernel->call('queue:restart');
return response('', 204);
}
/**
* Submit a request to send a test mail message.
*/
public function test(Request $request): Response
{
try {
Notification::route('mail', $request->user()->email)
->notify(new MailTested($request->user()));
} catch (Exception $exception) {
return response($exception->getMessage(), 500);
}
return response('', 204);
}
}

View file

@ -1,196 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use Prologue\Alerts\AlertsMessageBag;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Translation\Translator;
use Pterodactyl\Services\Users\UserUpdateService;
use Pterodactyl\Traits\Helpers\AvailableLanguages;
use Pterodactyl\Services\Users\UserCreationService;
use Pterodactyl\Services\Users\UserDeletionService;
use Pterodactyl\Http\Requests\Admin\UserFormRequest;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
class UserController extends Controller
{
use AvailableLanguages;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Services\Users\UserCreationService
*/
protected $creationService;
/**
* @var \Pterodactyl\Services\Users\UserDeletionService
*/
protected $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
*/
protected $repository;
/**
* @var \Illuminate\Contracts\Translation\Translator
*/
protected $translator;
/**
* @var \Pterodactyl\Services\Users\UserUpdateService
*/
protected $updateService;
/**
* UserController constructor.
*/
public function __construct(
AlertsMessageBag $alert,
UserCreationService $creationService,
UserDeletionService $deletionService,
Translator $translator,
UserUpdateService $updateService,
UserRepositoryInterface $repository
) {
$this->alert = $alert;
$this->creationService = $creationService;
$this->deletionService = $deletionService;
$this->repository = $repository;
$this->translator = $translator;
$this->updateService = $updateService;
}
/**
* Display user index page.
*
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
$users = QueryBuilder::for(
User::query()->select('users.*')
->selectRaw('COUNT(DISTINCT(subusers.id)) as subuser_of_count')
->selectRaw('COUNT(DISTINCT(servers.id)) as servers_count')
->leftJoin('subusers', 'subusers.user_id', '=', 'users.id')
->leftJoin('servers', 'servers.owner_id', '=', 'users.id')
->groupBy('users.id')
)
->allowedFilters(['username', 'email', 'uuid'])
->allowedSorts(['id', 'uuid'])
->paginate(50);
return view('admin.users.index', ['users' => $users]);
}
/**
* Display new user page.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('admin.users.new', [
'languages' => $this->getAvailableLanguages(true),
]);
}
/**
* Display user view page.
*
* @return \Illuminate\View\View
*/
public function view(User $user)
{
return view('admin.users.view', [
'user' => $user,
'languages' => $this->getAvailableLanguages(true),
]);
}
/**
* Delete a user from the system.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function delete(Request $request, User $user)
{
if ($request->user()->id === $user->id) {
throw new DisplayException($this->translator->get('admin/user.exceptions.user_has_servers'));
}
$this->deletionService->handle($user);
return redirect()->route('admin.users');
}
/**
* Create a user.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Throwable
*/
public function store(UserFormRequest $request)
{
$user = $this->creationService->handle($request->normalize());
$this->alert->success($this->translator->get('admin/user.notices.account_created'))->flash();
return redirect()->route('admin.users.view', $user->id);
}
/**
* Update a user on the system.
*
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(UserFormRequest $request, User $user)
{
$this->updateService
->setUserLevel(User::USER_LEVEL_ADMIN)
->handle($user, $request->normalize());
$this->alert->success(trans('admin/user.notices.account_updated'))->flash();
return redirect()->route('admin.users.view', $user->id);
}
/**
* Get a JSON response of users on the system.
*
* @return \Illuminate\Support\Collection|\Pterodactyl\Models\Model
*/
public function json(Request $request)
{
$users = QueryBuilder::for(User::query())->allowedFilters(['email'])->paginate(25);
// Handle single user requests.
if ($request->query('user_id')) {
$user = User::query()->findOrFail($request->input('user_id'));
$user->md5 = md5(strtolower($user->email));
return $user;
}
return $users->map(function ($item) {
$item->md5 = md5(strtolower($item->email));
return $item;
});
}
}

View file

@ -3,25 +3,17 @@
namespace Pterodactyl\Http\Controllers\Api\Application; namespace Pterodactyl\Http\Controllers\Api\Application;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Webmozart\Assert\Assert;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Container\Container; use Illuminate\Container\Container;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Extensions\Spatie\Fractalistic\Fractal; use Pterodactyl\Extensions\Spatie\Fractalistic\Fractal;
use Pterodactyl\Transformers\Api\Application\BaseTransformer;
abstract class ApplicationApiController extends Controller abstract class ApplicationApiController extends Controller
{ {
/** protected Request $request;
* @var \Illuminate\Http\Request
*/
protected $request;
/** protected Fractal $fractal;
* @var \Pterodactyl\Extensions\Spatie\Fractalistic\Fractal
*/
protected $fractal;
/** /**
* ApplicationApiController constructor. * ApplicationApiController constructor.
@ -53,19 +45,11 @@ abstract class ApplicationApiController extends Controller
} }
/** /**
* Return an instance of an application transformer. * Return a HTTP/201 response for the API.
*
* @return \Pterodactyl\Transformers\Api\Application\BaseTransformer
*/ */
public function getTransformer(string $abstract) protected function returnAccepted(): Response
{ {
/** @var \Pterodactyl\Transformers\Api\Application\BaseTransformer $transformer */ return new Response('', Response::HTTP_ACCEPTED);
$transformer = Container::getInstance()->make($abstract);
$transformer->setKey($this->request->attributes->get('api_key'));
Assert::isInstanceOf($transformer, BaseTransformer::class);
return $transformer;
} }
/** /**

View file

@ -0,0 +1,109 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Databases;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\DatabaseHost;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Databases\Hosts\HostUpdateService;
use Pterodactyl\Services\Databases\Hosts\HostCreationService;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Transformers\Api\Application\DatabaseHostTransformer;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Databases\GetDatabaseRequest;
use Pterodactyl\Http\Requests\Api\Application\Databases\GetDatabasesRequest;
use Pterodactyl\Http\Requests\Api\Application\Databases\StoreDatabaseRequest;
use Pterodactyl\Http\Requests\Api\Application\Databases\DeleteDatabaseRequest;
use Pterodactyl\Http\Requests\Api\Application\Databases\UpdateDatabaseRequest;
class DatabaseController extends ApplicationApiController
{
private HostCreationService $creationService;
private HostUpdateService $updateService;
/**
* DatabaseController constructor.
*/
public function __construct(HostCreationService $creationService, HostUpdateService $updateService)
{
parent::__construct();
$this->creationService = $creationService;
$this->updateService = $updateService;
}
/**
* Returns an array of all database hosts.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function index(GetDatabasesRequest $request): array
{
$perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$databases = QueryBuilder::for(DatabaseHost::query())
->allowedFilters(['name', 'host'])
->allowedSorts(['id', 'name', 'host'])
->paginate($perPage);
return $this->fractal->collection($databases)
->transformWith(DatabaseHostTransformer::class)
->toArray();
}
/**
* Returns a single database host.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function view(GetDatabaseRequest $request, DatabaseHost $databaseHost): array
{
return $this->fractal->item($databaseHost)
->transformWith(DatabaseHostTransformer::class)
->toArray();
}
/**
* Creates a new database host.
*
* @throws \Throwable
*/
public function store(StoreDatabaseRequest $request): JsonResponse
{
$databaseHost = $this->creationService->handle($request->validated());
return $this->fractal->item($databaseHost)
->transformWith(DatabaseHostTransformer::class)
->respond(JsonResponse::HTTP_CREATED);
}
/**
* Updates a database host.
*
* @throws \Throwable
*/
public function update(UpdateDatabaseRequest $request, DatabaseHost $databaseHost): array
{
$databaseHost = $this->updateService->handle($databaseHost->id, $request->validated());
return $this->fractal->item($databaseHost)
->transformWith(DatabaseHostTransformer::class)
->toArray();
}
/**
* Deletes a database host.
*
* @throws \Exception
*/
public function delete(DeleteDatabaseRequest $request, DatabaseHost $databaseHost): Response
{
$databaseHost->delete();
return $this->returnNoContent();
}
}

View file

@ -0,0 +1,119 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Eggs;
use Ramsey\Uuid\Uuid;
use Pterodactyl\Models\Egg;
use Pterodactyl\Models\Nest;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Eggs\Sharing\EggExporterService;
use Pterodactyl\Transformers\Api\Application\EggTransformer;
use Pterodactyl\Http\Requests\Api\Application\Eggs\GetEggRequest;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Http\Requests\Api\Application\Eggs\GetEggsRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\StoreEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\DeleteEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\ExportEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\UpdateEggRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class EggController extends ApplicationApiController
{
private EggExporterService $eggExporterService;
public function __construct(EggExporterService $eggExporterService)
{
parent::__construct();
$this->eggExporterService = $eggExporterService;
}
/**
* Return an array of all eggs on a given nest.
*/
public function index(GetEggsRequest $request, Nest $nest): array
{
$perPage = (int) $request->query('per_page', '10');
if ($perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
// @phpstan-ignore-next-line
$eggs = QueryBuilder::for(Egg::query())
->where('nest_id', '=', $nest->id)
->allowedFilters(['id', 'name', 'author'])
->allowedSorts(['id', 'name', 'author']);
if ($perPage > 0) {
$eggs = $eggs->paginate($perPage);
}
return $this->fractal->collection($eggs)
->transformWith(EggTransformer::class)
->toArray();
}
/**
* Returns a single egg.
*/
public function view(GetEggRequest $request, Egg $egg): array
{
return $this->fractal->item($egg)
->transformWith(EggTransformer::class)
->toArray();
}
/**
* Creates a new egg.
*/
public function store(StoreEggRequest $request): JsonResponse
{
$validated = $request->validated();
$merged = array_merge($validated, [
'uuid' => Uuid::uuid4()->toString(),
// TODO: allow this to be set in the request, and default to config value if null or not present.
'author' => config('pterodactyl.service.author'),
]);
$egg = Egg::query()->create($merged);
return $this->fractal->item($egg)
->transformWith(EggTransformer::class)
->respond(Response::HTTP_CREATED);
}
/**
* Updates an egg.
*/
public function update(UpdateEggRequest $request, Egg $egg): array
{
$egg->update($request->validated());
return $this->fractal->item($egg)
->transformWith(EggTransformer::class)
->toArray();
}
/**
* Deletes an egg.
*
* @throws \Exception
*/
public function delete(DeleteEggRequest $request, Egg $egg): Response
{
$egg->delete();
return $this->returnNoContent();
}
/**
* Exports an egg.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function export(ExportEggRequest $request, int $eggId): JsonResponse
{
return new JsonResponse($this->eggExporterService->handle($eggId));
}
}

View file

@ -0,0 +1,81 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Eggs;
use Pterodactyl\Models\Egg;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pterodactyl\Models\EggVariable;
use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Services\Eggs\Variables\VariableUpdateService;
use Pterodactyl\Services\Eggs\Variables\VariableCreationService;
use Pterodactyl\Transformers\Api\Application\EggVariableTransformer;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Eggs\Variables\StoreEggVariableRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\Variables\UpdateEggVariablesRequest;
class EggVariableController extends ApplicationApiController
{
private ConnectionInterface $connection;
private VariableCreationService $variableCreationService;
private VariableUpdateService $variableUpdateService;
public function __construct(ConnectionInterface $connection, VariableCreationService $variableCreationService, VariableUpdateService $variableUpdateService)
{
parent::__construct();
$this->connection = $connection;
$this->variableCreationService = $variableCreationService;
$this->variableUpdateService = $variableUpdateService;
}
/**
* Creates a new egg variable.
*
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\BadValidationRuleException
* @throws \Pterodactyl\Exceptions\Service\Egg\Variable\ReservedVariableNameException
*/
public function store(StoreEggVariableRequest $request, Egg $egg): array
{
$variable = $this->variableCreationService->handle($egg->id, $request->validated());
return $this->fractal->item($variable)
->transformWith(EggVariableTransformer::class)
->toArray();
}
/**
* Updates multiple egg variables.
*
* @throws \Throwable
*/
public function update(UpdateEggVariablesRequest $request, Egg $egg): array
{
$validated = $request->validated();
$this->connection->transaction(function () use ($egg, $validated) {
foreach ($validated as $data) {
$this->variableUpdateService->handle($egg, $data);
}
});
return $this->fractal->collection($egg->refresh()->variables)
->transformWith(EggVariableTransformer::class)
->toArray();
}
/**
* Deletes a single egg variable.
*/
public function delete(Request $request, Egg $egg, EggVariable $eggVariable): Response
{
EggVariable::query()
->where('id', $eggVariable->id)
->where('egg_id', $egg->id)
->delete();
return $this->returnNoContent();
}
}

View file

@ -9,8 +9,8 @@ use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Locations\LocationUpdateService; use Pterodactyl\Services\Locations\LocationUpdateService;
use Pterodactyl\Services\Locations\LocationCreationService; use Pterodactyl\Services\Locations\LocationCreationService;
use Pterodactyl\Services\Locations\LocationDeletionService; use Pterodactyl\Services\Locations\LocationDeletionService;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
use Pterodactyl\Transformers\Api\Application\LocationTransformer; use Pterodactyl\Transformers\Api\Application\LocationTransformer;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationRequest; use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationRequest;
use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationsRequest; use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationsRequest;
@ -20,25 +20,9 @@ use Pterodactyl\Http\Requests\Api\Application\Locations\UpdateLocationRequest;
class LocationController extends ApplicationApiController class LocationController extends ApplicationApiController
{ {
/** private LocationCreationService $creationService;
* @var \Pterodactyl\Services\Locations\LocationCreationService private LocationDeletionService $deletionService;
*/ private LocationUpdateService $updateService;
private $creationService;
/**
* @var \Pterodactyl\Services\Locations\LocationDeletionService
*/
private $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
*/
private $repository;
/**
* @var \Pterodactyl\Services\Locations\LocationUpdateService
*/
private $updateService;
/** /**
* LocationController constructor. * LocationController constructor.
@ -46,39 +30,46 @@ class LocationController extends ApplicationApiController
public function __construct( public function __construct(
LocationCreationService $creationService, LocationCreationService $creationService,
LocationDeletionService $deletionService, LocationDeletionService $deletionService,
LocationRepositoryInterface $repository,
LocationUpdateService $updateService LocationUpdateService $updateService
) { ) {
parent::__construct(); parent::__construct();
$this->creationService = $creationService; $this->creationService = $creationService;
$this->deletionService = $deletionService; $this->deletionService = $deletionService;
$this->repository = $repository;
$this->updateService = $updateService; $this->updateService = $updateService;
} }
/** /**
* Return all of the locations currently registered on the Panel. * Return all of the locations currently registered on the Panel.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetLocationsRequest $request): array public function index(GetLocationsRequest $request): array
{ {
$perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$locations = QueryBuilder::for(Location::query()) $locations = QueryBuilder::for(Location::query())
->allowedFilters(['short', 'long']) ->allowedFilters(['short', 'long'])
->allowedSorts(['id']) ->allowedSorts(['id', 'short', 'long'])
->paginate($request->query('per_page') ?? 50); ->paginate($perPage);
return $this->fractal->collection($locations) return $this->fractal->collection($locations)
->transformWith($this->getTransformer(LocationTransformer::class)) ->transformWith(LocationTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Return a single location. * Return a single location.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(GetLocationRequest $request): array public function view(GetLocationRequest $request, Location $location): array
{ {
return $this->fractal->item($request->getModel(Location::class)) return $this->fractal->item($location)
->transformWith($this->getTransformer(LocationTransformer::class)) ->transformWith(LocationTransformer::class)
->toArray(); ->toArray();
} }
@ -87,18 +78,14 @@ class LocationController extends ApplicationApiController
* new location attached. * new location attached.
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function store(StoreLocationRequest $request): JsonResponse public function store(StoreLocationRequest $request): JsonResponse
{ {
$location = $this->creationService->handle($request->validated()); $location = $this->creationService->handle($request->validated());
return $this->fractal->item($location) return $this->fractal->item($location)
->transformWith($this->getTransformer(LocationTransformer::class)) ->transformWith(LocationTransformer::class)
->addMeta([
'resource' => route('api.application.locations.view', [
'location' => $location->id,
]),
])
->respond(201); ->respond(201);
} }
@ -107,13 +94,14 @@ class LocationController extends ApplicationApiController
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function update(UpdateLocationRequest $request): array public function update(UpdateLocationRequest $request, Location $location): array
{ {
$location = $this->updateService->handle($request->getModel(Location::class), $request->validated()); $location = $this->updateService->handle($location, $request->validated());
return $this->fractal->item($location) return $this->fractal->item($location)
->transformWith($this->getTransformer(LocationTransformer::class)) ->transformWith(LocationTransformer::class)
->toArray(); ->toArray();
} }
@ -122,10 +110,10 @@ class LocationController extends ApplicationApiController
* *
* @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException * @throws \Pterodactyl\Exceptions\Service\Location\HasActiveNodesException
*/ */
public function delete(DeleteLocationRequest $request): Response public function delete(DeleteLocationRequest $request, Location $location): Response
{ {
$this->deletionService->handle($request->getModel(Location::class)); $this->deletionService->handle($location);
return response('', 204); return $this->returnNoContent();
} }
} }

View file

@ -0,0 +1,179 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Mounts;
use Illuminate\Http\Response;
use Pterodactyl\Models\Mount;
use Illuminate\Http\JsonResponse;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Transformers\Api\Application\MountTransformer;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Http\Requests\Api\Application\Mounts\GetMountRequest;
use Pterodactyl\Http\Requests\Api\Application\Mounts\GetMountsRequest;
use Pterodactyl\Http\Requests\Api\Application\Mounts\MountEggsRequest;
use Pterodactyl\Http\Requests\Api\Application\Mounts\MountNodesRequest;
use Pterodactyl\Http\Requests\Api\Application\Mounts\StoreMountRequest;
use Pterodactyl\Http\Requests\Api\Application\Mounts\DeleteMountRequest;
use Pterodactyl\Http\Requests\Api\Application\Mounts\UpdateMountRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class MountController extends ApplicationApiController
{
/**
* MountController constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* Returns an array of all mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function index(GetMountsRequest $request): array
{
$perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$mounts = QueryBuilder::for(Mount::query())
->allowedFilters(['id', 'name', 'source', 'target'])
->allowedSorts(['id', 'name', 'source', 'target'])
->paginate($perPage);
return $this->fractal->collection($mounts)
->transformWith(MountTransformer::class)
->toArray();
}
/**
* Returns a single mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function view(GetMountRequest $request, Mount $mount): array
{
return $this->fractal->item($mount)
->transformWith(MountTransformer::class)
->toArray();
}
/**
* Creates a new mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function store(StoreMountRequest $request): JsonResponse
{
$mount = Mount::query()->create($request->validated());
return $this->fractal->item($mount)
->transformWith(MountTransformer::class)
->respond(JsonResponse::HTTP_CREATED);
}
/**
* Updates a mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function update(UpdateMountRequest $request, Mount $mount): array
{
$mount->update($request->validated());
return $this->fractal->item($mount)
->transformWith(MountTransformer::class)
->toArray();
}
/**
* Deletes a mount.
*
* @throws \Exception
*/
public function delete(DeleteMountRequest $request, Mount $mount): Response
{
$mount->delete();
return $this->returnNoContent();
}
/**
* Attaches eggs to a mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function addEggs(MountEggsRequest $request, Mount $mount): array
{
$data = $request->validated();
$eggs = $data['eggs'] ?? [];
if (count($eggs) > 0) {
$mount->eggs()->syncWithoutDetaching($eggs);
}
return $this->fractal->item($mount)
->transformWith(MountTransformer::class)
->toArray();
}
/**
* Attaches nodes to a mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function addNodes(MountNodesRequest $request, Mount $mount): array
{
$data = $request->validated();
$nodes = $data['nodes'] ?? [];
if (count($nodes) > 0) {
$mount->nodes()->syncWithoutDetaching($nodes);
}
return $this->fractal->item($mount)
->transformWith(MountTransformer::class)
->toArray();
}
/**
* Detaches eggs from a mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function deleteEggs(MountEggsRequest $request, Mount $mount): array
{
$data = $request->validated();
$eggs = $data['eggs'] ?? [];
if (count($eggs) > 0) {
$mount->eggs()->detach($eggs);
}
return $this->fractal->item($mount)
->transformWith(MountTransformer::class)
->toArray();
}
/**
* Detaches nodes from a mount.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function deleteNodes(MountNodesRequest $request, Mount $mount): array
{
$data = $request->validated();
$nodes = $data['nodes'] ?? [];
if (count($nodes) > 0) {
$mount->nodes()->detach($nodes);
}
return $this->fractal->item($mount)
->transformWith(MountTransformer::class)
->toArray();
}
}

View file

@ -1,53 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Nests;
use Pterodactyl\Models\Egg;
use Pterodactyl\Models\Nest;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
use Pterodactyl\Transformers\Api\Application\EggTransformer;
use Pterodactyl\Http\Requests\Api\Application\Nests\Eggs\GetEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Nests\Eggs\GetEggsRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class EggController extends ApplicationApiController
{
/**
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
*/
private $repository;
/**
* EggController constructor.
*/
public function __construct(EggRepositoryInterface $repository)
{
parent::__construct();
$this->repository = $repository;
}
/**
* Return all eggs that exist for a given nest.
*/
public function index(GetEggsRequest $request): array
{
$eggs = $this->repository->findWhere([
['nest_id', '=', $request->getModel(Nest::class)->id],
]);
return $this->fractal->collection($eggs)
->transformWith($this->getTransformer(EggTransformer::class))
->toArray();
}
/**
* Return a single egg that exists on the specified nest.
*/
public function view(GetEggRequest $request): array
{
return $this->fractal->item($request->getModel(Egg::class))
->transformWith($this->getTransformer(EggTransformer::class))
->toArray();
}
}

View file

@ -3,47 +3,138 @@
namespace Pterodactyl\Http\Controllers\Api\Application\Nests; namespace Pterodactyl\Http\Controllers\Api\Application\Nests;
use Pterodactyl\Models\Nest; use Pterodactyl\Models\Nest;
use Pterodactyl\Contracts\Repository\NestRepositoryInterface; use Illuminate\Http\Response;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Nests\NestUpdateService;
use Pterodactyl\Services\Nests\NestCreationService;
use Pterodactyl\Services\Nests\NestDeletionService;
use Pterodactyl\Services\Eggs\Sharing\EggImporterService;
use Pterodactyl\Transformers\Api\Application\EggTransformer;
use Pterodactyl\Transformers\Api\Application\NestTransformer; use Pterodactyl\Transformers\Api\Application\NestTransformer;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Http\Requests\Api\Application\Nests\GetNestRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\ImportEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Nests\GetNestsRequest; use Pterodactyl\Http\Requests\Api\Application\Nests\GetNestsRequest;
use Pterodactyl\Http\Requests\Api\Application\Nests\StoreNestRequest;
use Pterodactyl\Http\Requests\Api\Application\Nests\DeleteNestRequest;
use Pterodactyl\Http\Requests\Api\Application\Nests\UpdateNestRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class NestController extends ApplicationApiController class NestController extends ApplicationApiController
{ {
/** private NestCreationService $nestCreationService;
* @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface private NestDeletionService $nestDeletionService;
*/ private NestUpdateService $nestUpdateService;
private $repository; private EggImporterService $eggImporterService;
/** /**
* NestController constructor. * NestController constructor.
*/ */
public function __construct(NestRepositoryInterface $repository) public function __construct(
{ NestCreationService $nestCreationService,
NestDeletionService $nestDeletionService,
NestUpdateService $nestUpdateService,
EggImporterService $eggImporterService
) {
parent::__construct(); parent::__construct();
$this->repository = $repository; $this->nestCreationService = $nestCreationService;
$this->nestDeletionService = $nestDeletionService;
$this->nestUpdateService = $nestUpdateService;
$this->eggImporterService = $eggImporterService;
} }
/** /**
* Return all Nests that exist on the Panel. * Return all Nests that exist on the Panel.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetNestsRequest $request): array public function index(GetNestsRequest $request): array
{ {
$nests = $this->repository->paginated($request->query('per_page') ?? 50); $perPage = (int) $request->query('per_page', '10');
if ($perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$nests = QueryBuilder::for(Nest::query())
->allowedFilters(['id', 'name', 'author'])
->allowedSorts(['id', 'name', 'author']);
if ($perPage > 0) {
$nests = $nests->paginate($perPage);
}
return $this->fractal->collection($nests) return $this->fractal->collection($nests)
->transformWith($this->getTransformer(NestTransformer::class)) ->transformWith(NestTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Return information about a single Nest model. * Return information about a single Nest model.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(GetNestsRequest $request): array public function view(GetNestRequest $request, Nest $nest): array
{ {
return $this->fractal->item($request->getModel(Nest::class)) return $this->fractal->item($nest)
->transformWith($this->getTransformer(NestTransformer::class)) ->transformWith(NestTransformer::class)
->toArray(); ->toArray();
} }
/**
* Creates a new nest.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/
public function store(StoreNestRequest $request): array
{
$nest = $this->nestCreationService->handle($request->validated());
return $this->fractal->item($nest)
->transformWith(NestTransformer::class)
->toArray();
}
/**
* Imports an egg.
*/
public function import(ImportEggRequest $request, Nest $nest): array
{
$egg = $this->eggImporterService->handleContent(
$nest->id,
$request->getContent(),
$request->headers->get('Content-Type'),
);
return $this->fractal->item($egg)
->transformWith(EggTransformer::class)
->toArray();
}
/**
* Updates an existing nest.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(UpdateNestRequest $request, Nest $nest): array
{
$this->nestUpdateService->handle($nest->id, $request->validated());
return $this->fractal->item($nest)
->transformWith(NestTransformer::class)
->toArray();
}
/**
* Deletes an existing nest.
*
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
*/
public function delete(DeleteNestRequest $request, Nest $nest): Response
{
$this->nestDeletionService->handle($nest->id);
return $this->returnNoContent();
}
} }

View file

@ -3,10 +3,14 @@
namespace Pterodactyl\Http\Controllers\Api\Application\Nodes; namespace Pterodactyl\Http\Controllers\Api\Application\Nodes;
use Pterodactyl\Models\Node; use Pterodactyl\Models\Node;
use Illuminate\Http\JsonResponse; use Illuminate\Http\Response;
use Pterodactyl\Models\Allocation; use Pterodactyl\Models\Allocation;
use Spatie\QueryBuilder\QueryBuilder;
use Spatie\QueryBuilder\AllowedFilter;
use Illuminate\Database\Eloquent\Builder;
use Pterodactyl\Services\Allocations\AssignmentService; use Pterodactyl\Services\Allocations\AssignmentService;
use Pterodactyl\Services\Allocations\AllocationDeletionService; use Pterodactyl\Services\Allocations\AllocationDeletionService;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Transformers\Api\Application\AllocationTransformer; use Pterodactyl\Transformers\Api\Application\AllocationTransformer;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Allocations\GetAllocationsRequest; use Pterodactyl\Http\Requests\Api\Application\Allocations\GetAllocationsRequest;
@ -15,15 +19,8 @@ use Pterodactyl\Http\Requests\Api\Application\Allocations\DeleteAllocationReques
class AllocationController extends ApplicationApiController class AllocationController extends ApplicationApiController
{ {
/** private AssignmentService $assignmentService;
* @var \Pterodactyl\Services\Allocations\AssignmentService private AllocationDeletionService $deletionService;
*/
private $assignmentService;
/**
* @var \Pterodactyl\Services\Allocations\AllocationDeletionService
*/
private $deletionService;
/** /**
* AllocationController constructor. * AllocationController constructor.
@ -40,13 +37,32 @@ class AllocationController extends ApplicationApiController
/** /**
* Return all of the allocations that exist for a given node. * Return all of the allocations that exist for a given node.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetAllocationsRequest $request, Node $node): array public function index(GetAllocationsRequest $request, Node $node): array
{ {
$allocations = $node->allocations()->paginate($request->query('per_page') ?? 50); $perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$allocations = QueryBuilder::for(Allocation::query()->where('node_id', '=', $node->id))
->allowedFilters([
'id', 'ip', 'port', 'alias',
AllowedFilter::callback('server_id', function (Builder $query, $value) {
if ($value === '0') {
$query->whereNull('server_id');
} else {
$query->where('server_id', '=', $value);
}
}),
])
->allowedSorts(['id', 'ip', 'port', 'server_id'])
->paginate($perPage);
return $this->fractal->collection($allocations) return $this->fractal->collection($allocations)
->transformWith($this->getTransformer(AllocationTransformer::class)) ->transformWith(AllocationTransformer::class)
->toArray(); ->toArray();
} }
@ -59,11 +75,11 @@ class AllocationController extends ApplicationApiController
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException * @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException * @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
*/ */
public function store(StoreAllocationRequest $request, Node $node): JsonResponse public function store(StoreAllocationRequest $request, Node $node): Response
{ {
$this->assignmentService->handle($node, $request->validated()); $this->assignmentService->handle($node, $request->validated());
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
@ -71,10 +87,10 @@ class AllocationController extends ApplicationApiController
* *
* @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException * @throws \Pterodactyl\Exceptions\Service\Allocation\ServerUsingAllocationException
*/ */
public function delete(DeleteAllocationRequest $request, Node $node, Allocation $allocation): JsonResponse public function delete(DeleteAllocationRequest $request, Node $node, Allocation $allocation): Response
{ {
$this->deletionService->handle($allocation); $this->deletionService->handle($allocation);
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -14,10 +14,14 @@ class NodeConfigurationController extends ApplicationApiController
* to remote machines so long as an API key is provided to the machine to make the request * to remote machines so long as an API key is provided to the machine to make the request
* with, and the node is known. * with, and the node is known.
* *
* @return \Illuminate\Http\JsonResponse * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function __invoke(GetNodeRequest $request, Node $node) public function __invoke(GetNodeRequest $request, Node $node): string
{ {
return JsonResponse::create($node->getConfiguration()); if ($request->query('format') === 'yaml') {
return $node->getYamlConfiguration();
}
return new JsonResponse($node->getConfiguration());
} }
} }

View file

@ -3,13 +3,14 @@
namespace Pterodactyl\Http\Controllers\Api\Application\Nodes; namespace Pterodactyl\Http\Controllers\Api\Application\Nodes;
use Pterodactyl\Models\Node; use Pterodactyl\Models\Node;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Nodes\NodeUpdateService; use Pterodactyl\Services\Nodes\NodeUpdateService;
use Pterodactyl\Services\Nodes\NodeCreationService; use Pterodactyl\Services\Nodes\NodeCreationService;
use Pterodactyl\Services\Nodes\NodeDeletionService; use Pterodactyl\Services\Nodes\NodeDeletionService;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Transformers\Api\Application\NodeTransformer; use Pterodactyl\Transformers\Api\Application\NodeTransformer;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest; use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodeRequest;
use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodesRequest; use Pterodactyl\Http\Requests\Api\Application\Nodes\GetNodesRequest;
use Pterodactyl\Http\Requests\Api\Application\Nodes\StoreNodeRequest; use Pterodactyl\Http\Requests\Api\Application\Nodes\StoreNodeRequest;
@ -19,25 +20,9 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class NodeController extends ApplicationApiController class NodeController extends ApplicationApiController
{ {
/** private NodeCreationService $creationService;
* @var \Pterodactyl\Services\Nodes\NodeCreationService private NodeDeletionService $deletionService;
*/ private NodeUpdateService $updateService;
private $creationService;
/**
* @var \Pterodactyl\Services\Nodes\NodeDeletionService
*/
private $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
*/
private $repository;
/**
* @var \Pterodactyl\Services\Nodes\NodeUpdateService
*/
private $updateService;
/** /**
* NodeController constructor. * NodeController constructor.
@ -45,12 +30,10 @@ class NodeController extends ApplicationApiController
public function __construct( public function __construct(
NodeCreationService $creationService, NodeCreationService $creationService,
NodeDeletionService $deletionService, NodeDeletionService $deletionService,
NodeUpdateService $updateService, NodeUpdateService $updateService
NodeRepositoryInterface $repository
) { ) {
parent::__construct(); parent::__construct();
$this->repository = $repository;
$this->creationService = $creationService; $this->creationService = $creationService;
$this->deletionService = $deletionService; $this->deletionService = $deletionService;
$this->updateService = $updateService; $this->updateService = $updateService;
@ -58,26 +41,35 @@ class NodeController extends ApplicationApiController
/** /**
* Return all of the nodes currently available on the Panel. * Return all of the nodes currently available on the Panel.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetNodesRequest $request): array public function index(GetNodesRequest $request): array
{ {
$perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$nodes = QueryBuilder::for(Node::query()) $nodes = QueryBuilder::for(Node::query())
->allowedFilters(['uuid', 'name', 'fqdn', 'daemon_token_id']) ->allowedFilters(['id', 'uuid', 'name', 'fqdn', 'daemon_token_id'])
->allowedSorts(['id', 'uuid', 'memory', 'disk']) ->allowedSorts(['id', 'uuid', 'name', 'location_id', 'fqdn', 'memory', 'disk'])
->paginate($request->query('per_page') ?? 50); ->paginate($perPage);
return $this->fractal->collection($nodes) return $this->fractal->collection($nodes)
->transformWith($this->getTransformer(NodeTransformer::class)) ->transformWith(NodeTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Return data for a single instance of a node. * Return data for a single instance of a node.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(GetNodeRequest $request, Node $node): array public function view(GetNodeRequest $request, Node $node): array
{ {
return $this->fractal->item($node) return $this->fractal->item($node)
->transformWith($this->getTransformer(NodeTransformer::class)) ->transformWith(NodeTransformer::class)
->toArray(); ->toArray();
} }
@ -86,18 +78,14 @@ class NodeController extends ApplicationApiController
* status response on success. * status response on success.
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function store(StoreNodeRequest $request): JsonResponse public function store(StoreNodeRequest $request): JsonResponse
{ {
$node = $this->creationService->handle($request->validated()); $node = $this->creationService->handle($request->validated());
return $this->fractal->item($node) return $this->fractal->item($node)
->transformWith($this->getTransformer(NodeTransformer::class)) ->transformWith(NodeTransformer::class)
->addMeta([
'resource' => route('api.application.nodes.view', [
'node' => $node->id,
]),
])
->respond(201); ->respond(201);
} }
@ -111,11 +99,11 @@ class NodeController extends ApplicationApiController
$node = $this->updateService->handle( $node = $this->updateService->handle(
$node, $node,
$request->validated(), $request->validated(),
$request->input('reset_secret') === true false,
); );
return $this->fractal->item($node) return $this->fractal->item($node)
->transformWith($this->getTransformer(NodeTransformer::class)) ->transformWith(NodeTransformer::class)
->toArray(); ->toArray();
} }
@ -125,10 +113,10 @@ class NodeController extends ApplicationApiController
* *
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
*/ */
public function delete(DeleteNodeRequest $request, Node $node): JsonResponse public function delete(DeleteNodeRequest $request, Node $node): Response
{ {
$this->deletionService->handle($node); $this->deletionService->handle($node);
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -9,10 +9,7 @@ use Pterodactyl\Http\Requests\Api\Application\Nodes\GetDeployableNodesRequest;
class NodeDeploymentController extends ApplicationApiController class NodeDeploymentController extends ApplicationApiController
{ {
/** private FindViableNodesService $viableNodesService;
* @var \Pterodactyl\Services\Deployment\FindViableNodesService
*/
private $viableNodesService;
/** /**
* NodeDeploymentController constructor. * NodeDeploymentController constructor.
@ -29,6 +26,7 @@ class NodeDeploymentController extends ApplicationApiController
* similarly to the server creation process, but allows you to pass the deployment object * similarly to the server creation process, but allows you to pass the deployment object
* to this endpoint and get back a list of all Nodes satisfying the requirements. * to this endpoint and get back a list of all Nodes satisfying the requirements.
* *
* @throws \Illuminate\Contracts\Container\BindingResolutionException
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException
*/ */
public function __invoke(GetDeployableNodesRequest $request): array public function __invoke(GetDeployableNodesRequest $request): array
@ -37,10 +35,10 @@ class NodeDeploymentController extends ApplicationApiController
$nodes = $this->viableNodesService->setLocations($data['location_ids'] ?? []) $nodes = $this->viableNodesService->setLocations($data['location_ids'] ?? [])
->setMemory($data['memory']) ->setMemory($data['memory'])
->setDisk($data['disk']) ->setDisk($data['disk'])
->handle($request->query('per_page'), $request->query('page')); ->handle($request->query('per_page'), $request->query('page')); // @phpstan-ignore-line
return $this->fractal->collection($nodes) return $this->fractal->collection($nodes)
->transformWith($this->getTransformer(NodeTransformer::class)) ->transformWith(NodeTransformer::class)
->toArray(); ->toArray();
} }
} }

View file

@ -0,0 +1,52 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Nodes;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Illuminate\Http\JsonResponse;
use Illuminate\Cache\Repository as Cache;
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class NodeInformationController extends ApplicationApiController
{
private Cache $cache;
private DaemonConfigurationRepository $repository;
public function __construct(Cache $cache, DaemonConfigurationRepository $repository)
{
parent::__construct();
$this->cache = $cache;
$this->repository = $repository;
}
/**
* Returns system information from the node.
*
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function __invoke(Request $request, Node $node)
{
$data = $this->cache
->tags(['nodes'])
->remember($node->uuid, Carbon::now()->addSeconds(30), function () use ($node) {
return $this->repository->setNode($node)->getSystemInformation();
});
return new JsonResponse([
'version' => $data['version'] ?? null,
'system' => [
'type' => Str::title($data['os'] ?? 'Unknown'),
'arch' => $data['architecture'] ?? null,
'release' => $data['kernel_version'] ?? null,
'cpus' => $data['cpu_count'] ?? null,
],
]);
}
}

View file

@ -0,0 +1,104 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Roles;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\AdminRole;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Transformers\Api\Application\AdminRoleTransformer;
use Pterodactyl\Http\Requests\Api\Application\Roles\GetRoleRequest;
use Pterodactyl\Http\Requests\Api\Application\Roles\GetRolesRequest;
use Pterodactyl\Http\Requests\Api\Application\Roles\StoreRoleRequest;
use Pterodactyl\Http\Requests\Api\Application\Roles\DeleteRoleRequest;
use Pterodactyl\Http\Requests\Api\Application\Roles\UpdateRoleRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class RoleController extends ApplicationApiController
{
/**
* RoleController constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* Returns an array of all roles.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function index(GetRolesRequest $request): array
{
$perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$roles = QueryBuilder::for(AdminRole::query())
->allowedFilters(['id', 'name'])
->allowedSorts(['id', 'name'])
->paginate($perPage);
return $this->fractal->collection($roles)
->transformWith(AdminRoleTransformer::class)
->toArray();
}
/**
* Returns a single role.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function view(GetRoleRequest $request, AdminRole $role): array
{
return $this->fractal->item($role)
->transformWith(AdminRoleTransformer::class)
->toArray();
}
/**
* Creates a new role.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function store(StoreRoleRequest $request): JsonResponse
{
$data = array_merge($request->validated(), [
'sort_id' => 99,
]);
$role = AdminRole::query()->create($data);
return $this->fractal->item($role)
->transformWith(AdminRoleTransformer::class)
->respond(JsonResponse::HTTP_CREATED);
}
/**
* Updates a role.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function update(UpdateRoleRequest $request, AdminRole $role): array
{
$role->update($request->validated());
return $this->fractal->item($role)
->transformWith(AdminRoleTransformer::class)
->toArray();
}
/**
* Deletes a role.
*
* @throws \Exception
*/
public function delete(DeleteRoleRequest $request, AdminRole $role): Response
{
$role->delete();
return $this->returnNoContent();
}
}

View file

@ -8,7 +8,6 @@ use Pterodactyl\Models\Database;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Pterodactyl\Services\Databases\DatabasePasswordService; use Pterodactyl\Services\Databases\DatabasePasswordService;
use Pterodactyl\Services\Databases\DatabaseManagementService; use Pterodactyl\Services\Databases\DatabaseManagementService;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Transformers\Api\Application\ServerDatabaseTransformer; use Pterodactyl\Transformers\Api\Application\ServerDatabaseTransformer;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabaseRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\GetServerDatabaseRequest;
@ -18,54 +17,44 @@ use Pterodactyl\Http\Requests\Api\Application\Servers\Databases\StoreServerDatab
class DatabaseController extends ApplicationApiController class DatabaseController extends ApplicationApiController
{ {
/** private DatabaseManagementService $databaseManagementService;
* @var \Pterodactyl\Services\Databases\DatabaseManagementService private DatabasePasswordService $databasePasswordService;
*/
private $databaseManagementService;
/**
* @var \Pterodactyl\Services\Databases\DatabasePasswordService
*/
private $databasePasswordService;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
private $repository;
/** /**
* DatabaseController constructor. * DatabaseController constructor.
*/ */
public function __construct( public function __construct(
DatabaseManagementService $databaseManagementService, DatabaseManagementService $databaseManagementService,
DatabasePasswordService $databasePasswordService, DatabasePasswordService $databasePasswordService
DatabaseRepositoryInterface $repository
) { ) {
parent::__construct(); parent::__construct();
$this->databaseManagementService = $databaseManagementService; $this->databaseManagementService = $databaseManagementService;
$this->databasePasswordService = $databasePasswordService; $this->databasePasswordService = $databasePasswordService;
$this->repository = $repository;
} }
/** /**
* Return a listing of all databases currently available to a single * Return a listing of all databases currently available to a single
* server. * server.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetServerDatabasesRequest $request, Server $server): array public function index(GetServerDatabasesRequest $request, Server $server): array
{ {
return $this->fractal->collection($server->databases) return $this->fractal->collection($server->databases)
->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) ->transformWith(ServerDatabaseTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Return a single server database. * Return a single server database.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(GetServerDatabaseRequest $request, Server $server, Database $database): array public function view(GetServerDatabaseRequest $request, Server $server, Database $database): array
{ {
return $this->fractal->item($database) return $this->fractal->item($database)
->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) ->transformWith(ServerDatabaseTransformer::class)
->toArray(); ->toArray();
} }
@ -74,11 +63,11 @@ class DatabaseController extends ApplicationApiController
* *
* @throws \Throwable * @throws \Throwable
*/ */
public function resetPassword(ServerDatabaseWriteRequest $request, Server $server, Database $database): JsonResponse public function resetPassword(ServerDatabaseWriteRequest $request, Server $server, Database $database): Response
{ {
$this->databasePasswordService->handle($database); $this->databasePasswordService->handle($database);
return JsonResponse::create([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
@ -93,25 +82,19 @@ class DatabaseController extends ApplicationApiController
])); ]));
return $this->fractal->item($database) return $this->fractal->item($database)
->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) ->transformWith(ServerDatabaseTransformer::class)
->addMeta([
'resource' => route('api.application.servers.databases.view', [
'server' => $server->id,
'database' => $database->id,
]),
])
->respond(Response::HTTP_CREATED); ->respond(Response::HTTP_CREATED);
} }
/** /**
* Handle a request to delete a specific server database from the Panel. * Handle a request to delete a specific server database from the Panel.
* *
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Exception
*/ */
public function delete(ServerDatabaseWriteRequest $request): Response public function delete(ServerDatabaseWriteRequest $request, Database $database): Response
{ {
$this->databaseManagementService->delete($request->getModel(Database::class)); $this->databaseManagementService->delete($database);
return response('', 204); return $this->returnNoContent();
} }
} }

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Http\Controllers\Api\Application\Servers; namespace Pterodactyl\Http\Controllers\Api\Application\Servers;
use Pterodactyl\Models\Server;
use Pterodactyl\Transformers\Api\Application\ServerTransformer; use Pterodactyl\Transformers\Api\Application\ServerTransformer;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Servers\GetExternalServerRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\GetExternalServerRequest;
@ -10,11 +11,15 @@ class ExternalServerController extends ApplicationApiController
{ {
/** /**
* Retrieve a specific server from the database using its external ID. * Retrieve a specific server from the database using its external ID.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetExternalServerRequest $request): array public function index(GetExternalServerRequest $request, string $external_id): array
{ {
return $this->fractal->item($request->getServerModel()) $server = Server::query()->where('external_id', $external_id)->firstOrFail();
->transformWith($this->getTransformer(ServerTransformer::class))
return $this->fractal->item($server)
->transformWith(ServerTransformer::class)
->toArray(); ->toArray();
} }
} }

View file

@ -8,30 +8,24 @@ use Illuminate\Http\JsonResponse;
use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Servers\ServerCreationService; use Pterodactyl\Services\Servers\ServerCreationService;
use Pterodactyl\Services\Servers\ServerDeletionService; use Pterodactyl\Services\Servers\ServerDeletionService;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Services\Servers\BuildModificationService;
use Pterodactyl\Services\Servers\DetailsModificationService;
use Pterodactyl\Transformers\Api\Application\ServerTransformer; use Pterodactyl\Transformers\Api\Application\ServerTransformer;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Http\Requests\Api\Application\Servers\GetServerRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\GetServerRequest;
use Pterodactyl\Http\Requests\Api\Application\Servers\GetServersRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\GetServersRequest;
use Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\ServerWriteRequest;
use Pterodactyl\Http\Requests\Api\Application\Servers\StoreServerRequest; use Pterodactyl\Http\Requests\Api\Application\Servers\StoreServerRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerRequest;
class ServerController extends ApplicationApiController class ServerController extends ApplicationApiController
{ {
/** private ServerCreationService $creationService;
* @var \Pterodactyl\Services\Servers\ServerCreationService private ServerDeletionService $deletionService;
*/
private $creationService;
/** private BuildModificationService $buildModificationService;
* @var \Pterodactyl\Services\Servers\ServerDeletionService private DetailsModificationService $detailsModificationService;
*/
private $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
private $repository;
/** /**
* ServerController constructor. * ServerController constructor.
@ -39,27 +33,37 @@ class ServerController extends ApplicationApiController
public function __construct( public function __construct(
ServerCreationService $creationService, ServerCreationService $creationService,
ServerDeletionService $deletionService, ServerDeletionService $deletionService,
ServerRepositoryInterface $repository BuildModificationService $buildModificationService,
DetailsModificationService $detailsModificationService
) { ) {
parent::__construct(); parent::__construct();
$this->creationService = $creationService; $this->creationService = $creationService;
$this->deletionService = $deletionService; $this->deletionService = $deletionService;
$this->repository = $repository;
$this->buildModificationService = $buildModificationService;
$this->detailsModificationService = $detailsModificationService;
} }
/** /**
* Return all of the servers that currently exist on the Panel. * Return all of the servers that currently exist on the Panel.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetServersRequest $request): array public function index(GetServersRequest $request): array
{ {
$perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$servers = QueryBuilder::for(Server::query()) $servers = QueryBuilder::for(Server::query())
->allowedFilters(['uuid', 'uuidShort', 'name', 'image', 'external_id']) ->allowedFilters(['id', 'uuid', 'uuidShort', 'name', 'owner_id', 'node_id', 'external_id'])
->allowedSorts(['id', 'uuid']) ->allowedSorts(['id', 'uuid', 'uuidShort', 'name', 'owner_id', 'node_id', 'status'])
->paginate($request->query('per_page') ?? 50); ->paginate($perPage);
return $this->fractal->collection($servers) return $this->fractal->collection($servers)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith(ServerTransformer::class)
->toArray(); ->toArray();
} }
@ -69,32 +73,36 @@ class ServerController extends ApplicationApiController
* @throws \Throwable * @throws \Throwable
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException
*/ */
public function store(StoreServerRequest $request): JsonResponse public function store(StoreServerRequest $request): JsonResponse
{ {
$server = $this->creationService->handle($request->validated(), $request->getDeploymentObject()); $server = $this->creationService->handle($request->validated());
return $this->fractal->item($server) return $this->fractal->item($server)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith(ServerTransformer::class)
->respond(201); ->respond(Response::HTTP_CREATED);
} }
/** /**
* Show a single server transformed for the application API. * Show a single server transformed for the application API.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(GetServerRequest $request): array public function view(GetServerRequest $request, Server $server): array
{ {
return $this->fractal->item($request->getModel(Server::class)) return $this->fractal->item($server)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith(ServerTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Deletes a server.
*
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Throwable
*/ */
public function delete(ServerWriteRequest $request, Server $server, string $force = ''): Response public function delete(ServerWriteRequest $request, Server $server, string $force = ''): Response
{ {
@ -102,4 +110,24 @@ class ServerController extends ApplicationApiController
return $this->returnNoContent(); return $this->returnNoContent();
} }
/**
* Update a server.
*
* @throws \Throwable
* @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException
*/
public function update(UpdateServerRequest $request, Server $server): array
{
$server = $this->buildModificationService->handle($server, $request->validated());
$server = $this->detailsModificationService->returnUpdatedModel()->handle($server, $request->validated());
return $this->fractal->item($server)
->transformWith(ServerTransformer::class)
->toArray();
}
} }

View file

@ -12,15 +12,8 @@ use Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerBuildConfigura
class ServerDetailsController extends ApplicationApiController class ServerDetailsController extends ApplicationApiController
{ {
/** private BuildModificationService $buildModificationService;
* @var \Pterodactyl\Services\Servers\BuildModificationService private DetailsModificationService $detailsModificationService;
*/
private $buildModificationService;
/**
* @var \Pterodactyl\Services\Servers\DetailsModificationService
*/
private $detailsModificationService;
/** /**
* ServerDetailsController constructor. * ServerDetailsController constructor.
@ -38,35 +31,31 @@ class ServerDetailsController extends ApplicationApiController
/** /**
* Update the details for a specific server. * Update the details for a specific server.
* *
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function details(UpdateServerDetailsRequest $request): array public function details(UpdateServerDetailsRequest $request, Server $server): array
{ {
$server = $this->detailsModificationService->returnUpdatedModel()->handle( $server = $this->detailsModificationService->returnUpdatedModel()->handle(
$request->getModel(Server::class), $server,
$request->validated() $request->validated()
); );
return $this->fractal->item($server) return $this->fractal->item($server)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith(ServerTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Update the build details for a specific server. * Update the build details for a specific server.
* *
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function build(UpdateServerBuildConfigurationRequest $request, Server $server): array public function build(UpdateServerBuildConfigurationRequest $request, Server $server): array
{ {
$server = $this->buildModificationService->handle($server, $request->validated()); $server = $this->buildModificationService->handle($server, $request->validated());
return $this->fractal->item($server) return $this->fractal->item($server)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith(ServerTransformer::class)
->toArray(); ->toArray();
} }
} }

View file

@ -11,15 +11,8 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class ServerManagementController extends ApplicationApiController class ServerManagementController extends ApplicationApiController
{ {
/** private ReinstallServerService $reinstallServerService;
* @var \Pterodactyl\Services\Servers\ReinstallServerService private SuspensionService $suspensionService;
*/
private $reinstallServerService;
/**
* @var \Pterodactyl\Services\Servers\SuspensionService
*/
private $suspensionService;
/** /**
* SuspensionController constructor. * SuspensionController constructor.
@ -61,9 +54,7 @@ class ServerManagementController extends ApplicationApiController
/** /**
* Mark a server as needing to be reinstalled. * Mark a server as needing to be reinstalled.
* *
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function reinstall(ServerWriteRequest $request, Server $server): Response public function reinstall(ServerWriteRequest $request, Server $server): Response
{ {

View file

@ -11,10 +11,7 @@ use Pterodactyl\Http\Requests\Api\Application\Servers\UpdateServerStartupRequest
class StartupController extends ApplicationApiController class StartupController extends ApplicationApiController
{ {
/** private StartupModificationService $modificationService;
* @var \Pterodactyl\Services\Servers\StartupModificationService
*/
private $modificationService;
/** /**
* StartupController constructor. * StartupController constructor.
@ -29,19 +26,16 @@ class StartupController extends ApplicationApiController
/** /**
* Update the startup and environment settings for a specific server. * Update the startup and environment settings for a specific server.
* *
* @throws \Illuminate\Validation\ValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function index(UpdateServerStartupRequest $request): array public function index(UpdateServerStartupRequest $request, Server $server): array
{ {
$server = $this->modificationService $server = $this->modificationService
->setUserLevel(User::USER_LEVEL_ADMIN) ->setUserLevel(User::USER_LEVEL_ADMIN)
->handle($request->getModel(Server::class), $request->validated()); ->handle($server, $request->validated());
return $this->fractal->item($server) return $this->fractal->item($server)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith(ServerTransformer::class)
->toArray(); ->toArray();
} }
} }

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Http\Controllers\Api\Application\Users; namespace Pterodactyl\Http\Controllers\Api\Application\Users;
use Pterodactyl\Models\User;
use Pterodactyl\Transformers\Api\Application\UserTransformer; use Pterodactyl\Transformers\Api\Application\UserTransformer;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
use Pterodactyl\Http\Requests\Api\Application\Users\GetExternalUserRequest; use Pterodactyl\Http\Requests\Api\Application\Users\GetExternalUserRequest;
@ -10,11 +11,15 @@ class ExternalUserController extends ApplicationApiController
{ {
/** /**
* Retrieve a specific user from the database using their external ID. * Retrieve a specific user from the database using their external ID.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetExternalUserRequest $request): array public function index(GetExternalUserRequest $request, string $external_id): array
{ {
return $this->fractal->item($request->getUserModel()) $user = User::query()->where('external_id', $external_id)->firstOrFail();
->transformWith($this->getTransformer(UserTransformer::class))
return $this->fractal->item($user)
->transformWith(UserTransformer::class)
->toArray(); ->toArray();
} }
} }

View file

@ -9,8 +9,9 @@ use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Users\UserUpdateService; use Pterodactyl\Services\Users\UserUpdateService;
use Pterodactyl\Services\Users\UserCreationService; use Pterodactyl\Services\Users\UserCreationService;
use Pterodactyl\Services\Users\UserDeletionService; use Pterodactyl\Services\Users\UserDeletionService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Transformers\Api\Application\UserTransformer; use Pterodactyl\Transformers\Api\Application\UserTransformer;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
use Pterodactyl\Http\Requests\Api\Application\Users\GetUserRequest;
use Pterodactyl\Http\Requests\Api\Application\Users\GetUsersRequest; use Pterodactyl\Http\Requests\Api\Application\Users\GetUsersRequest;
use Pterodactyl\Http\Requests\Api\Application\Users\StoreUserRequest; use Pterodactyl\Http\Requests\Api\Application\Users\StoreUserRequest;
use Pterodactyl\Http\Requests\Api\Application\Users\DeleteUserRequest; use Pterodactyl\Http\Requests\Api\Application\Users\DeleteUserRequest;
@ -19,31 +20,14 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class UserController extends ApplicationApiController class UserController extends ApplicationApiController
{ {
/** private UserCreationService $creationService;
* @var \Pterodactyl\Services\Users\UserCreationService private UserDeletionService $deletionService;
*/ private UserUpdateService $updateService;
private $creationService;
/**
* @var \Pterodactyl\Services\Users\UserDeletionService
*/
private $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
*/
private $repository;
/**
* @var \Pterodactyl\Services\Users\UserUpdateService
*/
private $updateService;
/** /**
* UserController constructor. * UserController constructor.
*/ */
public function __construct( public function __construct(
UserRepositoryInterface $repository,
UserCreationService $creationService, UserCreationService $creationService,
UserDeletionService $deletionService, UserDeletionService $deletionService,
UserUpdateService $updateService UserUpdateService $updateService
@ -52,7 +36,6 @@ class UserController extends ApplicationApiController
$this->creationService = $creationService; $this->creationService = $creationService;
$this->deletionService = $deletionService; $this->deletionService = $deletionService;
$this->repository = $repository;
$this->updateService = $updateService; $this->updateService = $updateService;
} }
@ -60,27 +43,36 @@ class UserController extends ApplicationApiController
* Handle request to list all users on the panel. Returns a JSON-API representation * Handle request to list all users on the panel. Returns a JSON-API representation
* of a collection of users including any defined relations passed in * of a collection of users including any defined relations passed in
* the request. * the request.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetUsersRequest $request): array public function index(GetUsersRequest $request): array
{ {
$perPage = (int) $request->query('per_page', '10');
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$users = QueryBuilder::for(User::query()) $users = QueryBuilder::for(User::query())
->allowedFilters(['email', 'uuid', 'username', 'external_id']) ->allowedFilters(['id', 'uuid', 'username', 'email', 'external_id'])
->allowedSorts(['id', 'uuid']) ->allowedSorts(['id', 'uuid', 'username', 'email', 'admin_role_id'])
->paginate($request->query('per_page') ?? 50); ->paginate($perPage);
return $this->fractal->collection($users) return $this->fractal->collection($users)
->transformWith($this->getTransformer(UserTransformer::class)) ->transformWith(UserTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Handle a request to view a single user. Includes any relations that * Handle a request to view a single user. Includes any relations that
* were defined in the request. * were defined in the request.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(GetUsersRequest $request, User $user): array public function view(GetUserRequest $request, User $user): array
{ {
return $this->fractal->item($user) return $this->fractal->item($user)
->transformWith($this->getTransformer(UserTransformer::class)) ->transformWith(UserTransformer::class)
->toArray(); ->toArray();
} }
@ -94,16 +86,16 @@ class UserController extends ApplicationApiController
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function update(UpdateUserRequest $request, User $user): array public function update(UpdateUserRequest $request, User $user): array
{ {
$this->updateService->setUserLevel(User::USER_LEVEL_ADMIN); $this->updateService->setUserLevel(User::USER_LEVEL_ADMIN);
$user = $this->updateService->handle($user, $request->validated()); $user = $this->updateService->handle($user, $request->validated());
$response = $this->fractal->item($user) return $this->fractal->item($user)
->transformWith($this->getTransformer(UserTransformer::class)); ->transformWith(UserTransformer::class)
->toArray();
return $response->toArray();
} }
/** /**
@ -118,12 +110,7 @@ class UserController extends ApplicationApiController
$user = $this->creationService->handle($request->validated()); $user = $this->creationService->handle($request->validated());
return $this->fractal->item($user) return $this->fractal->item($user)
->transformWith($this->getTransformer(UserTransformer::class)) ->transformWith(UserTransformer::class)
->addMeta([
'resource' => route('api.application.users.view', [
'user' => $user->id,
]),
])
->respond(201); ->respond(201);
} }
@ -133,10 +120,10 @@ class UserController extends ApplicationApiController
* *
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
*/ */
public function delete(DeleteUserRequest $request, User $user): JsonResponse public function delete(DeleteUserRequest $request, User $user): Response
{ {
$this->deletionService->handle($user); $this->deletionService->handle($user);
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -0,0 +1,29 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Services\Helpers\SoftwareVersionService;
class VersionController extends ApplicationApiController
{
private SoftwareVersionService $softwareVersionService;
/**
* VersionController constructor.
*/
public function __construct(SoftwareVersionService $softwareVersionService)
{
parent::__construct();
$this->softwareVersionService = $softwareVersionService;
}
/**
* Returns version information.
*/
public function __invoke(): JsonResponse
{
return new JsonResponse($this->softwareVersionService->getVersionData());
}
}

View file

@ -5,7 +5,6 @@ namespace Pterodactyl\Http\Controllers\Api\Client;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Auth\AuthManager; use Illuminate\Auth\AuthManager;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Services\Users\UserUpdateService; use Pterodactyl\Services\Users\UserUpdateService;
use Pterodactyl\Transformers\Api\Client\AccountTransformer; use Pterodactyl\Transformers\Api\Client\AccountTransformer;
use Pterodactyl\Http\Requests\Api\Client\Account\UpdateEmailRequest; use Pterodactyl\Http\Requests\Api\Client\Account\UpdateEmailRequest;
@ -13,20 +12,17 @@ use Pterodactyl\Http\Requests\Api\Client\Account\UpdatePasswordRequest;
class AccountController extends ClientApiController class AccountController extends ClientApiController
{ {
/** private UserUpdateService $updateService;
* @var \Pterodactyl\Services\Users\UserUpdateService
*/
private $updateService;
/** /**
* @var \Illuminate\Auth\SessionGuard * @var \Illuminate\Auth\AuthManager
*/ */
private $sessionGuard; private $sessionGuard;
/** /**
* AccountController constructor. * AccountController constructor.
*/ */
public function __construct(AuthManager $sessionGuard, UserUpdateService $updateService) public function __construct(UserUpdateService $updateService, AuthManager $sessionGuard)
{ {
parent::__construct(); parent::__construct();
@ -34,24 +30,24 @@ class AccountController extends ClientApiController
$this->sessionGuard = $sessionGuard; $this->sessionGuard = $sessionGuard;
} }
/**
* Gets information about the currently authenticated user.
*/
public function index(Request $request): array public function index(Request $request): array
{ {
return $this->fractal->item($request->user()) return $this->fractal->item($request->user())
->transformWith($this->getTransformer(AccountTransformer::class)) ->transformWith(AccountTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Update the authenticated user's email address. * Update the authenticated user's email address.
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function updateEmail(UpdateEmailRequest $request): JsonResponse public function updateEmail(UpdateEmailRequest $request): Response
{ {
$this->updateService->handle($request->user(), $request->validated()); $this->updateService->handle($request->user(), $request->validated());
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
@ -60,7 +56,7 @@ class AccountController extends ClientApiController
* *
* @throws \Throwable * @throws \Throwable
*/ */
public function updatePassword(UpdatePasswordRequest $request): JsonResponse public function updatePassword(UpdatePasswordRequest $request): Response
{ {
$user = $this->updateService->handle($request->user(), $request->validated()); $user = $this->updateService->handle($request->user(), $request->validated());
@ -68,10 +64,13 @@ class AccountController extends ClientApiController
// cached copy of the user that does not include the updated password. Do this // cached copy of the user that does not include the updated password. Do this
// to correctly store the new user details in the guard and allow the logout // to correctly store the new user details in the guard and allow the logout
// other devices functionality to work. // other devices functionality to work.
if (method_exists($this->sessionGuard, 'setUser')) {
$this->sessionGuard->setUser($user); $this->sessionGuard->setUser($user);
}
$this->sessionGuard->logoutOtherDevices($request->input('password')); // TODO: Find another way to do this, function doesn't exist due to API changes.
//$this->sessionGuard->logoutOtherDevices($request->input('password'));
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -2,106 +2,57 @@
namespace Pterodactyl\Http\Controllers\Api\Client; namespace Pterodactyl\Http\Controllers\Api\Client;
use Pterodactyl\Models\ApiKey; use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayException;
use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Http\Requests\Api\Client\AccountApiRequest;
use Pterodactyl\Services\Api\KeyCreationService;
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
use Pterodactyl\Transformers\Api\Client\ApiKeyTransformer;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Pterodactyl\Http\Requests\Api\Client\Account\StoreApiKeyRequest; use Pterodactyl\Http\Requests\Api\Client\Account\StoreApiKeyRequest;
use Pterodactyl\Transformers\Api\Client\PersonalAccessTokenTransformer;
class ApiKeyController extends ClientApiController class ApiKeyController extends ClientApiController
{ {
/**
* @var \Pterodactyl\Services\Api\KeyCreationService
*/
private $keyCreationService;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
private $encrypter;
/**
* @var \Pterodactyl\Repositories\Eloquent\ApiKeyRepository
*/
private $repository;
/**
* ApiKeyController constructor.
*/
public function __construct(
Encrypter $encrypter,
KeyCreationService $keyCreationService,
ApiKeyRepository $repository
) {
parent::__construct();
$this->encrypter = $encrypter;
$this->keyCreationService = $keyCreationService;
$this->repository = $repository;
}
/** /**
* Returns all of the API keys that exist for the given client. * Returns all of the API keys that exist for the given client.
* *
* @return array * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(ClientApiRequest $request) public function index(AccountApiRequest $request): array
{ {
return $this->fractal->collection($request->user()->apiKeys) return $this->fractal->collection($request->user()->tokens)
->transformWith($this->getTransformer(ApiKeyTransformer::class)) ->transformWith(PersonalAccessTokenTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Store a new API key for a user's account. * Store a new API key for a user's account.
* *
* @return array
*
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function store(StoreApiKeyRequest $request) public function store(StoreApiKeyRequest $request): array
{ {
if ($request->user()->apiKeys->count() >= 5) { if ($request->user()->tokens->count() >= 10) {
throw new DisplayException('You have reached the account limit for number of API keys.'); throw new DisplayException('You have reached the account limit for number of API keys.');
} }
$key = $this->keyCreationService->setKeyType(ApiKey::TYPE_ACCOUNT)->handle([ // TODO: this should accept an array of different scopes to apply as permissions
'user_id' => $request->user()->id, // for the token. Right now it allows any account level permission.
'memo' => $request->input('description'), $token = $request->user()->createToken($request->input('description'));
'allowed_ips' => $request->input('allowed_ips') ?? [],
]);
return $this->fractal->item($key) return $this->fractal->item($token->accessToken)
->transformWith($this->getTransformer(ApiKeyTransformer::class)) ->transformWith(PersonalAccessTokenTransformer::class)
->addMeta([ ->addMeta([
'secret_token' => $this->encrypter->decrypt($key->token), 'secret_token' => $token->plainTextToken,
]) ])
->toArray(); ->toArray();
} }
/** /**
* Deletes a given API key. * Deletes a given API key.
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function delete(ClientApiRequest $request, string $identifier) public function delete(AccountApiRequest $request, string $id): Response
{ {
$response = $this->repository->deleteWhere([ $request->user()->tokens()->where('token_id', $id)->delete();
'key_type' => ApiKey::TYPE_ACCOUNT,
'user_id' => $request->user()->id,
'identifier' => $identifier,
]);
if (!$response) { return $this->returnNoContent();
throw new NotFoundHttpException();
}
return JsonResponse::create([], JsonResponse::HTTP_NO_CONTENT);
} }
} }

View file

@ -2,10 +2,7 @@
namespace Pterodactyl\Http\Controllers\Api\Client; namespace Pterodactyl\Http\Controllers\Api\Client;
use Webmozart\Assert\Assert; use Pterodactyl\Transformers\Api\Transformer;
use Illuminate\Container\Container;
use Pterodactyl\Transformers\Daemon\BaseDaemonTransformer;
use Pterodactyl\Transformers\Api\Client\BaseClientTransformer;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController; use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
abstract class ClientApiController extends ApplicationApiController abstract class ClientApiController extends ApplicationApiController
@ -15,7 +12,7 @@ abstract class ClientApiController extends ApplicationApiController
* *
* @return string[] * @return string[]
*/ */
protected function getIncludesForTransformer(BaseClientTransformer $transformer, array $merge = []) protected function getIncludesForTransformer(Transformer $transformer, array $merge = []): array
{ {
$filtered = array_filter($this->parseIncludes(), function ($datum) use ($transformer) { $filtered = array_filter($this->parseIncludes(), function ($datum) use ($transformer) {
return in_array($datum, $transformer->getAvailableIncludes()); return in_array($datum, $transformer->getAvailableIncludes());
@ -29,7 +26,7 @@ abstract class ClientApiController extends ApplicationApiController
* *
* @return string[] * @return string[]
*/ */
protected function parseIncludes() protected function parseIncludes(): array
{ {
$includes = $this->request->query('include') ?? []; $includes = $this->request->query('include') ?? [];
@ -41,26 +38,4 @@ abstract class ClientApiController extends ApplicationApiController
return trim($item); return trim($item);
}, explode(',', $includes)); }, explode(',', $includes));
} }
/**
* Return an instance of an application transformer.
*
* @return \Pterodactyl\Transformers\Api\Client\BaseClientTransformer
*/
public function getTransformer(string $abstract)
{
/** @var \Pterodactyl\Transformers\Api\Client\BaseClientTransformer $transformer */
$transformer = Container::getInstance()->make($abstract);
Assert::isInstanceOfAny($transformer, [
BaseClientTransformer::class,
BaseDaemonTransformer::class,
]);
if ($transformer instanceof BaseClientTransformer) {
$transformer->setKey($this->request->attributes->get('api_key'));
$transformer->setUser($this->request->user());
}
return $transformer;
}
} }

View file

@ -7,39 +7,24 @@ use Pterodactyl\Models\Permission;
use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\QueryBuilder;
use Spatie\QueryBuilder\AllowedFilter; use Spatie\QueryBuilder\AllowedFilter;
use Pterodactyl\Models\Filters\MultiFieldServerFilter; use Pterodactyl\Models\Filters\MultiFieldServerFilter;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Transformers\Api\Client\ServerTransformer; use Pterodactyl\Transformers\Api\Client\ServerTransformer;
use Pterodactyl\Http\Requests\Api\Client\GetServersRequest; use Pterodactyl\Http\Requests\Api\Client\GetServersRequest;
class ClientController extends ClientApiController class ClientController extends ClientApiController
{ {
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/**
* ClientController constructor.
*/
public function __construct(ServerRepository $repository)
{
parent::__construct();
$this->repository = $repository;
}
/** /**
* Return all of the servers available to the client making the API * Return all of the servers available to the client making the API
* request, including servers the user has access to as a subuser. * request, including servers the user has access to as a subuser.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetServersRequest $request): array public function index(GetServersRequest $request): array
{ {
$user = $request->user(); $user = $request->user();
$transformer = $this->getTransformer(ServerTransformer::class);
// Start the query builder and ensure we eager load any requested relationships from the request. // Start the query builder and ensure we eager load any requested relationships from the request.
$builder = QueryBuilder::for( $builder = QueryBuilder::for(
Server::query()->with($this->getIncludesForTransformer($transformer, ['node'])) Server::query()->with($this->getIncludesForTransformer(new ServerTransformer(), ['node']))
)->allowedFilters([ )->allowedFilters([
'uuid', 'uuid',
'name', 'name',
@ -68,17 +53,15 @@ class ClientController extends ClientApiController
$builder = $builder->whereIn('servers.id', $user->accessibleServers()->pluck('id')->all()); $builder = $builder->whereIn('servers.id', $user->accessibleServers()->pluck('id')->all());
} }
$servers = $builder->paginate(min($request->query('per_page', 50), 100))->appends($request->query()); $servers = $builder->paginate(min((int) $request->query('per_page', '50'), 100))->appends($request->query());
return $this->fractal->transformWith($transformer)->collection($servers)->toArray(); return $this->fractal->transformWith(new ServerTransformer())->collection($servers)->toArray();
} }
/** /**
* Returns all of the subuser permissions available on the system. * Returns all of the subuser permissions available on the system.
*
* @return array
*/ */
public function permissions() public function permissions(): array
{ {
return [ return [
'object' => 'system_permissions', 'object' => 'system_permissions',

View file

@ -0,0 +1,57 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Client;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\UserSSHKey;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Transformers\Api\Client\UserSSHKeyTransformer;
use Pterodactyl\Http\Requests\Api\Client\Account\StoreSSHKeyRequest;
class SSHKeyController extends ClientApiController
{
/**
* ?
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function index(Request $request): \Pterodactyl\Extensions\Spatie\Fractalistic\Fractal
{
return $this->fractal->collection(UserSSHKey::query()->where('user_id', '=', $request->user()->id)->get())
->transformWith(UserSSHKeyTransformer::class);
}
/**
* ?
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
* @throws \Pterodactyl\Exceptions\DisplayException
*/
public function store(StoreSSHKeyRequest $request): JsonResponse
{
if ($request->user()->sshKeys->count() >= 5) {
throw new DisplayException('You have reached the account limit for number of SSH keys.');
}
$data = array_merge($request->validated(), [
'user_id' => $request->user()->id,
]);
$key = UserSSHKey::query()->create($data);
return $this->fractal->item($key)
->transformWith(UserSSHKeyTransformer::class)
->respond(JsonResponse::HTTP_CREATED);
}
/**
* ?
*/
public function delete(Request $request, UserSSHKey $sshKey): Response
{
$sshKey->delete();
return new Response('', Response::HTTP_NO_CONTENT);
}
}

View file

@ -0,0 +1,116 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Client;
use Carbon\CarbonImmutable;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\SecurityKey;
use Illuminate\Contracts\Cache\Repository;
use Pterodactyl\Exceptions\DisplayException;
use Webauthn\PublicKeyCredentialCreationOptions;
use Pterodactyl\Transformers\Api\Client\SecurityKeyTransformer;
use Pterodactyl\Repositories\SecurityKeys\WebauthnServerRepository;
use Pterodactyl\Services\Users\SecurityKeys\StoreSecurityKeyService;
use Pterodactyl\Http\Requests\Api\Client\Account\RegisterWebauthnTokenRequest;
use Pterodactyl\Services\Users\SecurityKeys\CreatePublicKeyCredentialsService;
class SecurityKeyController extends ClientApiController
{
protected CreatePublicKeyCredentialsService $createPublicKeyCredentials;
protected Repository $cache;
protected WebauthnServerRepository $webauthnServerRepository;
protected StoreSecurityKeyService $storeSecurityKeyService;
public function __construct(
Repository $cache,
WebauthnServerRepository $webauthnServerRepository,
StoreSecurityKeyService $storeSecurityKeyService,
CreatePublicKeyCredentialsService $createPublicKeyCredentials
) {
parent::__construct();
$this->cache = $cache;
$this->webauthnServerRepository = $webauthnServerRepository;
$this->createPublicKeyCredentials = $createPublicKeyCredentials;
$this->storeSecurityKeyService = $storeSecurityKeyService;
}
/**
* Returns all of the hardware security keys (WebAuthn) that exists for a user.
*/
public function index(Request $request): array
{
return $this->fractal->collection($request->user()->securityKeys)
->transformWith(SecurityKeyTransformer::class)
->toArray();
}
/**
* Returns the data necessary for creating a new hardware security key for the
* user.
*/
public function create(Request $request): JsonResponse
{
$tokenId = Str::random(64);
$credentials = $this->createPublicKeyCredentials->handle($request->user());
// TODO: session
$this->cache->put(
"register-security-key:$tokenId",
serialize($credentials),
CarbonImmutable::now()->addMinutes(10)
);
return new JsonResponse([
'data' => [
'token_id' => $tokenId,
'credentials' => $credentials->jsonSerialize(),
],
]);
}
/**
* Stores a new key for a user account.
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Throwable
*/
public function store(RegisterWebauthnTokenRequest $request): array
{
$credentials = unserialize(
$this->cache->pull("register-security-key:{$request->input('token_id')}", serialize(null))
);
if (
!is_object($credentials) ||
!$credentials instanceof PublicKeyCredentialCreationOptions ||
$credentials->getUser()->getId() !== $request->user()->uuid
) {
throw new DisplayException('Could not register security key: invalid data present in session, please try again.');
}
$key = $this->storeSecurityKeyService
->setRequest(SecurityKey::getPsrRequestFactory($request))
->setKeyName($request->input('name'))
->handle($request->user(), $request->input('registration'), $credentials);
return $this->fractal->item($key)
->transformWith(SecurityKeyTransformer::class)
->toArray();
}
/**
* Removes a WebAuthn key from a user's account.
*/
public function delete(Request $request, string $securityKey): JsonResponse
{
$request->user()->securityKeys()->where('uuid', $securityKey)->delete();
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
}
}

View file

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Controllers\Api\Client\Servers; namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pterodactyl\Models\Backup; use Pterodactyl\Models\Backup;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Pterodactyl\Models\AuditLog; use Pterodactyl\Models\AuditLog;
@ -51,6 +52,7 @@ class BackupController extends ClientApiController
* result set. * result set.
* *
* @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(Request $request, Server $server): array public function index(Request $request, Server $server): array
{ {
@ -61,7 +63,7 @@ class BackupController extends ClientApiController
$limit = min($request->query('per_page') ?? 20, 50); $limit = min($request->query('per_page') ?? 20, 50);
return $this->fractal->collection($server->backups()->paginate($limit)) return $this->fractal->collection($server->backups()->paginate($limit))
->transformWith($this->getTransformer(BackupTransformer::class)) ->transformWith(BackupTransformer::class)
->addMeta([ ->addMeta([
'backup_count' => $this->repository->getNonFailedBackups($server)->count(), 'backup_count' => $this->repository->getNonFailedBackups($server)->count(),
]) ])
@ -98,7 +100,7 @@ class BackupController extends ClientApiController
}); });
return $this->fractal->item($backup) return $this->fractal->item($backup)
->transformWith($this->getTransformer(BackupTransformer::class)) ->transformWith(BackupTransformer::class)
->toArray(); ->toArray();
} }
@ -124,7 +126,7 @@ class BackupController extends ClientApiController
$backup->refresh(); $backup->refresh();
return $this->fractal->item($backup) return $this->fractal->item($backup)
->transformWith($this->getTransformer(BackupTransformer::class)) ->transformWith(BackupTransformer::class)
->toArray(); ->toArray();
} }
@ -140,7 +142,7 @@ class BackupController extends ClientApiController
} }
return $this->fractal->item($backup) return $this->fractal->item($backup)
->transformWith($this->getTransformer(BackupTransformer::class)) ->transformWith(BackupTransformer::class)
->toArray(); ->toArray();
} }
@ -150,7 +152,7 @@ class BackupController extends ClientApiController
* *
* @throws \Throwable * @throws \Throwable
*/ */
public function delete(Request $request, Server $server, Backup $backup): JsonResponse public function delete(Request $request, Server $server, Backup $backup): Response
{ {
if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { if (!$request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) {
throw new AuthorizationException(); throw new AuthorizationException();
@ -162,7 +164,7 @@ class BackupController extends ClientApiController
$this->deleteBackupService->handle($backup); $this->deleteBackupService->handle($backup);
}); });
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
@ -205,7 +207,7 @@ class BackupController extends ClientApiController
* *
* @throws \Throwable * @throws \Throwable
*/ */
public function restore(Request $request, Server $server, Backup $backup): JsonResponse public function restore(Request $request, Server $server, Backup $backup): Response
{ {
if (!$request->user()->can(Permission::ACTION_BACKUP_RESTORE, $server)) { if (!$request->user()->can(Permission::ACTION_BACKUP_RESTORE, $server)) {
throw new AuthorizationException(); throw new AuthorizationException();
@ -237,6 +239,6 @@ class BackupController extends ClientApiController
$this->daemonRepository->setServer($server)->restore($backup, $url ?? null, $request->input('truncate')); $this->daemonRepository->setServer($server)->restore($backup, $url ?? null, $request->input('truncate'));
}); });
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -14,10 +14,7 @@ use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
class CommandController extends ClientApiController class CommandController extends ClientApiController
{ {
/** private DaemonCommandRepository $repository;
* @var \Pterodactyl\Repositories\Wings\DaemonCommandRepository
*/
private $repository;
/** /**
* CommandController constructor. * CommandController constructor.

View file

@ -5,7 +5,6 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Pterodactyl\Models\Database; use Pterodactyl\Models\Database;
use Pterodactyl\Repositories\Eloquent\DatabaseRepository;
use Pterodactyl\Services\Databases\DatabasePasswordService; use Pterodactyl\Services\Databases\DatabasePasswordService;
use Pterodactyl\Transformers\Api\Client\DatabaseTransformer; use Pterodactyl\Transformers\Api\Client\DatabaseTransformer;
use Pterodactyl\Services\Databases\DatabaseManagementService; use Pterodactyl\Services\Databases\DatabaseManagementService;
@ -18,25 +17,9 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Databases\RotatePasswordRequest
class DatabaseController extends ClientApiController class DatabaseController extends ClientApiController
{ {
/** private DeployServerDatabaseService $deployDatabaseService;
* @var \Pterodactyl\Services\Databases\DeployServerDatabaseService private DatabaseManagementService $managementService;
*/ private DatabasePasswordService $passwordService;
private $deployDatabaseService;
/**
* @var \Pterodactyl\Repositories\Eloquent\DatabaseRepository
*/
private $repository;
/**
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
*/
private $managementService;
/**
* @var \Pterodactyl\Services\Databases\DatabasePasswordService
*/
private $passwordService;
/** /**
* DatabaseController constructor. * DatabaseController constructor.
@ -44,24 +27,24 @@ class DatabaseController extends ClientApiController
public function __construct( public function __construct(
DatabaseManagementService $managementService, DatabaseManagementService $managementService,
DatabasePasswordService $passwordService, DatabasePasswordService $passwordService,
DatabaseRepository $repository,
DeployServerDatabaseService $deployDatabaseService DeployServerDatabaseService $deployDatabaseService
) { ) {
parent::__construct(); parent::__construct();
$this->deployDatabaseService = $deployDatabaseService; $this->deployDatabaseService = $deployDatabaseService;
$this->repository = $repository;
$this->managementService = $managementService; $this->managementService = $managementService;
$this->passwordService = $passwordService; $this->passwordService = $passwordService;
} }
/** /**
* Return all of the databases that belong to the given server. * Return all of the databases that belong to the given server.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetDatabasesRequest $request, Server $server): array public function index(GetDatabasesRequest $request, Server $server): array
{ {
return $this->fractal->collection($server->databases) return $this->fractal->collection($server->databases)
->transformWith($this->getTransformer(DatabaseTransformer::class)) ->transformWith(DatabaseTransformer::class)
->toArray(); ->toArray();
} }
@ -78,7 +61,7 @@ class DatabaseController extends ClientApiController
return $this->fractal->item($database) return $this->fractal->item($database)
->parseIncludes(['password']) ->parseIncludes(['password'])
->transformWith($this->getTransformer(DatabaseTransformer::class)) ->transformWith(DatabaseTransformer::class)
->toArray(); ->toArray();
} }
@ -86,30 +69,28 @@ class DatabaseController extends ClientApiController
* Rotates the password for the given server model and returns a fresh instance to * Rotates the password for the given server model and returns a fresh instance to
* the caller. * the caller.
* *
* @return array
*
* @throws \Throwable * @throws \Throwable
*/ */
public function rotatePassword(RotatePasswordRequest $request, Server $server, Database $database) public function rotatePassword(RotatePasswordRequest $request, Server $server, Database $database): array
{ {
$this->passwordService->handle($database); $this->passwordService->handle($database);
$database->refresh(); $database->refresh();
return $this->fractal->item($database) return $this->fractal->item($database)
->parseIncludes(['password']) ->parseIncludes(['password'])
->transformWith($this->getTransformer(DatabaseTransformer::class)) ->transformWith(DatabaseTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Removes a database from the server. * Removes a database from the server.
* *
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Exception
*/ */
public function delete(DeleteDatabaseRequest $request, Server $server, Database $database): Response public function delete(DeleteDatabaseRequest $request, Server $server, Database $database): Response
{ {
$this->managementService->delete($database); $this->managementService->delete($database);
return Response::create('', Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -6,9 +6,7 @@ use Carbon\CarbonImmutable;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Pterodactyl\Models\AuditLog; use Pterodactyl\Models\AuditLog;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Services\Nodes\NodeJWTService; use Pterodactyl\Services\Nodes\NodeJWTService;
use Illuminate\Contracts\Routing\ResponseFactory;
use Pterodactyl\Repositories\Wings\DaemonFileRepository; use Pterodactyl\Repositories\Wings\DaemonFileRepository;
use Pterodactyl\Transformers\Daemon\FileObjectTransformer; use Pterodactyl\Transformers\Daemon\FileObjectTransformer;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
@ -26,33 +24,19 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Files\WriteFileContentRequest;
class FileController extends ClientApiController class FileController extends ClientApiController
{ {
/** private DaemonFileRepository $fileRepository;
* @var \Pterodactyl\Repositories\Wings\DaemonFileRepository private NodeJWTService $jwtService;
*/
private $fileRepository;
/**
* @var \Illuminate\Contracts\Routing\ResponseFactory
*/
private $responseFactory;
/**
* @var \Pterodactyl\Services\Nodes\NodeJWTService
*/
private $jwtService;
/** /**
* FileController constructor. * FileController constructor.
*/ */
public function __construct( public function __construct(
ResponseFactory $responseFactory, DaemonFileRepository $fileRepository,
NodeJWTService $jwtService, NodeJWTService $jwtService
DaemonFileRepository $fileRepository
) { ) {
parent::__construct(); parent::__construct();
$this->fileRepository = $fileRepository; $this->fileRepository = $fileRepository;
$this->responseFactory = $responseFactory;
$this->jwtService = $jwtService; $this->jwtService = $jwtService;
} }
@ -60,6 +44,7 @@ class FileController extends ClientApiController
* Returns a listing of files in a given directory. * Returns a listing of files in a given directory.
* *
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function directory(ListFilesRequest $request, Server $server): array public function directory(ListFilesRequest $request, Server $server): array
{ {
@ -68,7 +53,7 @@ class FileController extends ClientApiController
->getDirectory($request->get('directory') ?? '/'); ->getDirectory($request->get('directory') ?? '/');
return $this->fractal->collection($contents) return $this->fractal->collection($contents)
->transformWith($this->getTransformer(FileObjectTransformer::class)) ->transformWith(FileObjectTransformer::class)
->toArray(); ->toArray();
} }
@ -91,11 +76,9 @@ class FileController extends ClientApiController
* Generates a one-time token with a link that the user can use to * Generates a one-time token with a link that the user can use to
* download a given file. * download a given file.
* *
* @return array
*
* @throws \Throwable * @throws \Throwable
*/ */
public function download(GetFileContentsRequest $request, Server $server) public function download(GetFileContentsRequest $request, Server $server): array
{ {
$token = $server->audit(AuditLog::SERVER__FILESYSTEM_DOWNLOAD, function (AuditLog $audit, Server $server) use ($request) { $token = $server->audit(AuditLog::SERVER__FILESYSTEM_DOWNLOAD, function (AuditLog $audit, Server $server) use ($request) {
$audit->metadata = ['file' => $request->get('file')]; $audit->metadata = ['file' => $request->get('file')];
@ -124,9 +107,9 @@ class FileController extends ClientApiController
/** /**
* Writes the contents of the specified file to the server. * Writes the contents of the specified file to the server.
* *
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Throwable
*/ */
public function write(WriteFileContentRequest $request, Server $server): JsonResponse public function write(WriteFileContentRequest $request, Server $server): Response
{ {
$server->audit(AuditLog::SERVER__FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) { $server->audit(AuditLog::SERVER__FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) {
$audit->subaction = 'write_content'; $audit->subaction = 'write_content';
@ -137,7 +120,7 @@ class FileController extends ClientApiController
->putContent($request->get('file'), $request->getContent()); ->putContent($request->get('file'), $request->getContent());
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
@ -145,7 +128,7 @@ class FileController extends ClientApiController
* *
* @throws \Throwable * @throws \Throwable
*/ */
public function create(CreateFolderRequest $request, Server $server): JsonResponse public function create(CreateFolderRequest $request, Server $server): Response
{ {
$server->audit(AuditLog::SERVER__FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) { $server->audit(AuditLog::SERVER__FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) {
$audit->subaction = 'create_folder'; $audit->subaction = 'create_folder';
@ -156,7 +139,7 @@ class FileController extends ClientApiController
->createDirectory($request->input('name'), $request->input('root', '/')); ->createDirectory($request->input('name'), $request->input('root', '/'));
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
@ -164,7 +147,7 @@ class FileController extends ClientApiController
* *
* @throws \Throwable * @throws \Throwable
*/ */
public function rename(RenameFileRequest $request, Server $server): JsonResponse public function rename(RenameFileRequest $request, Server $server): Response
{ {
$server->audit(AuditLog::SERVER__FILESYSTEM_RENAME, function (AuditLog $audit, Server $server) use ($request) { $server->audit(AuditLog::SERVER__FILESYSTEM_RENAME, function (AuditLog $audit, Server $server) use ($request) {
$audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')]; $audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')];
@ -174,15 +157,15 @@ class FileController extends ClientApiController
->renameFiles($request->input('root'), $request->input('files')); ->renameFiles($request->input('root'), $request->input('files'));
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
* Copies a file on the server. * Copies a file on the server.
* *
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Throwable
*/ */
public function copy(CopyFileRequest $request, Server $server): JsonResponse public function copy(CopyFileRequest $request, Server $server): Response
{ {
$server->audit(AuditLog::SERVER__FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) { $server->audit(AuditLog::SERVER__FILESYSTEM_WRITE, function (AuditLog $audit, Server $server) use ($request) {
$audit->subaction = 'copy_file'; $audit->subaction = 'copy_file';
@ -193,11 +176,11 @@ class FileController extends ClientApiController
->copyFile($request->input('location')); ->copyFile($request->input('location'));
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Throwable
*/ */
public function compress(CompressFilesRequest $request, Server $server): array public function compress(CompressFilesRequest $request, Server $server): array
{ {
@ -210,21 +193,21 @@ class FileController extends ClientApiController
return $this->fileRepository->setServer($server) return $this->fileRepository->setServer($server)
->compressFiles( ->compressFiles(
$request->input('root'), $request->input('root'),
$request->input('files') $request->input('files'),
); );
}); });
return $this->fractal->item($file) return $this->fractal->item($file)
->transformWith($this->getTransformer(FileObjectTransformer::class)) ->transformWith(FileObjectTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Throwable
*/ */
public function decompress(DecompressFilesRequest $request, Server $server): JsonResponse public function decompress(DecompressFilesRequest $request, Server $server): Response
{ {
$file = $server->audit(AuditLog::SERVER__FILESYSTEM_DECOMPRESS, function (AuditLog $audit, Server $server) use ($request) { $server->audit(AuditLog::SERVER__FILESYSTEM_DECOMPRESS, function (AuditLog $audit, Server $server) use ($request) {
// Allow up to five minutes for this request to process before timing out. // Allow up to five minutes for this request to process before timing out.
set_time_limit(300); set_time_limit(300);
@ -234,15 +217,15 @@ class FileController extends ClientApiController
->decompressFile($request->input('root'), $request->input('file')); ->decompressFile($request->input('root'), $request->input('file'));
}); });
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
* Deletes files or folders for the server in the given root directory. * Deletes files or folders for the server in the given root directory.
* *
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Throwable
*/ */
public function delete(DeleteFileRequest $request, Server $server): JsonResponse public function delete(DeleteFileRequest $request, Server $server): Response
{ {
$server->audit(AuditLog::SERVER__FILESYSTEM_DELETE, function (AuditLog $audit, Server $server) use ($request) { $server->audit(AuditLog::SERVER__FILESYSTEM_DELETE, function (AuditLog $audit, Server $server) use ($request) {
$audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')]; $audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')];
@ -254,40 +237,46 @@ class FileController extends ClientApiController
); );
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
* Updates file permissions for file(s) in the given root directory. * Updates file permissions for file(s) in the given root directory.
* *
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Throwable
*/ */
public function chmod(ChmodFilesRequest $request, Server $server): JsonResponse public function chmod(ChmodFilesRequest $request, Server $server): Response
{ {
$server->audit(AuditLog::SERVER__FILESYSTEM_CHMOD, function (AuditLog $audit, Server $server) use ($request) {
$audit->metadata = ['root' => $request->input('root'), 'files' => $request->input('files')];
$this->fileRepository->setServer($server) $this->fileRepository->setServer($server)
->chmodFiles( ->chmodFiles(
$request->input('root'), $request->input('root'),
$request->input('files') $request->input('files'),
); );
});
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
* Requests that a file be downloaded from a remote location by Wings. * Requests that a file be downloaded from a remote location by Wings.
* *
* @param $request
*
* @throws \Throwable * @throws \Throwable
*/ */
public function pull(PullFileRequest $request, Server $server): JsonResponse public function pull(PullFileRequest $request, Server $server): Response
{ {
$server->audit(AuditLog::SERVER__FILESYSTEM_PULL, function (AuditLog $audit, Server $server) use ($request) { $server->audit(AuditLog::SERVER__FILESYSTEM_PULL, function (AuditLog $audit, Server $server) use ($request) {
$audit->metadata = ['directory' => $request->input('directory'), 'url' => $request->input('url')]; $audit->metadata = ['root' => $request->input('root'), 'url' => $request->input('url')];
$this->fileRepository->setServer($server)->pull($request->input('url'), $request->input('directory')); $this->fileRepository->setServer($server)
->pull(
$request->input('root'),
$request->input('url'),
);
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -12,17 +12,13 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Files\UploadFileRequest;
class FileUploadController extends ClientApiController class FileUploadController extends ClientApiController
{ {
/** private NodeJWTService $jwtService;
* @var \Pterodactyl\Services\Nodes\NodeJWTService
*/
private $jwtService;
/** /**
* FileUploadController constructor. * FileUploadController constructor.
*/ */
public function __construct( public function __construct(NodeJWTService $jwtService)
NodeJWTService $jwtService {
) {
parent::__construct(); parent::__construct();
$this->jwtService = $jwtService; $this->jwtService = $jwtService;
@ -30,10 +26,8 @@ class FileUploadController extends ClientApiController
/** /**
* Returns a url where files can be uploaded to. * Returns a url where files can be uploaded to.
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function __invoke(UploadFileRequest $request, Server $server) public function __invoke(UploadFileRequest $request, Server $server): JsonResponse
{ {
return new JsonResponse([ return new JsonResponse([
'object' => 'signed_url', 'object' => 'signed_url',
@ -45,13 +39,11 @@ class FileUploadController extends ClientApiController
/** /**
* Returns a url where files can be uploaded to. * Returns a url where files can be uploaded to.
*
* @return string
*/ */
protected function getUploadUrl(Server $server, User $user) protected function getUploadUrl(Server $server, User $user): string
{ {
$token = $this->jwtService $token = $this->jwtService
->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)->toDateTimeImmutable())
->setClaims([ ->setClaims([
'server_uuid' => $server->uuid, 'server_uuid' => $server->uuid,
]) ])

View file

@ -2,8 +2,8 @@
namespace Pterodactyl\Http\Controllers\Api\Client\Servers; namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Illuminate\Http\Response;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\Allocation; use Pterodactyl\Models\Allocation;
use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository;
@ -19,20 +19,9 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Network\SetPrimaryAllocationReq
class NetworkAllocationController extends ClientApiController class NetworkAllocationController extends ClientApiController
{ {
/** private AllocationRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\AllocationRepository private ServerRepository $serverRepository;
*/ private FindAssignableAllocationService $assignableAllocationService;
private $repository;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $serverRepository;
/**
* @var \Pterodactyl\Services\Allocations\FindAssignableAllocationService
*/
private $assignableAllocationService;
/** /**
* NetworkController constructor. * NetworkController constructor.
@ -50,13 +39,15 @@ class NetworkAllocationController extends ClientApiController
} }
/** /**
* Lists all of the allocations available to a server and wether or * Lists all of the allocations available to a server and whether or
* not they are currently assigned as the primary for this server. * not they are currently assigned as the primary for this server.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetNetworkRequest $request, Server $server): array public function index(GetNetworkRequest $request, Server $server): array
{ {
return $this->fractal->collection($server->allocations) return $this->fractal->collection($server->allocations)
->transformWith($this->getTransformer(AllocationTransformer::class)) ->transformWith(AllocationTransformer::class)
->toArray(); ->toArray();
} }
@ -65,6 +56,7 @@ class NetworkAllocationController extends ClientApiController
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function update(UpdateAllocationRequest $request, Server $server, Allocation $allocation): array public function update(UpdateAllocationRequest $request, Server $server, Allocation $allocation): array
{ {
@ -73,7 +65,7 @@ class NetworkAllocationController extends ClientApiController
]); ]);
return $this->fractal->item($allocation) return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class)) ->transformWith(AllocationTransformer::class)
->toArray(); ->toArray();
} }
@ -82,21 +74,22 @@ class NetworkAllocationController extends ClientApiController
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function setPrimary(SetPrimaryAllocationRequest $request, Server $server, Allocation $allocation): array public function setPrimary(SetPrimaryAllocationRequest $request, Server $server, Allocation $allocation): array
{ {
$this->serverRepository->update($server->id, ['allocation_id' => $allocation->id]); $this->serverRepository->update($server->id, ['allocation_id' => $allocation->id]);
return $this->fractal->item($allocation) return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class)) ->transformWith(AllocationTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Set the notes for the allocation for a server. * Set the notes for the allocation for a server.
*s.
* *
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function store(NewAllocationRequest $request, Server $server): array public function store(NewAllocationRequest $request, Server $server): array
{ {
@ -107,18 +100,16 @@ class NetworkAllocationController extends ClientApiController
$allocation = $this->assignableAllocationService->handle($server); $allocation = $this->assignableAllocationService->handle($server);
return $this->fractal->item($allocation) return $this->fractal->item($allocation)
->transformWith($this->getTransformer(AllocationTransformer::class)) ->transformWith(AllocationTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Delete an allocation from a server. * Delete an allocation from a server.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
*/ */
public function delete(DeleteAllocationRequest $request, Server $server, Allocation $allocation) public function delete(DeleteAllocationRequest $request, Server $server, Allocation $allocation): Response
{ {
if ($allocation->id === $server->allocation_id) { if ($allocation->id === $server->allocation_id) {
throw new DisplayException('You cannot delete the primary allocation for this server.'); throw new DisplayException('You cannot delete the primary allocation for this server.');
@ -129,6 +120,6 @@ class NetworkAllocationController extends ClientApiController
'server_id' => null, 'server_id' => null,
]); ]);
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -10,10 +10,7 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\SendPowerRequest;
class PowerController extends ClientApiController class PowerController extends ClientApiController
{ {
/** private DaemonPowerRepository $repository;
* @var \Pterodactyl\Repositories\Wings\DaemonPowerRepository
*/
private $repository;
/** /**
* PowerController constructor. * PowerController constructor.

View file

@ -12,9 +12,8 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest;
class ResourceUtilizationController extends ClientApiController class ResourceUtilizationController extends ClientApiController
{ {
private DaemonServerRepository $repository;
private Repository $cache; private Repository $cache;
private DaemonServerRepository $repository;
/** /**
* ResourceUtilizationController constructor. * ResourceUtilizationController constructor.
@ -42,7 +41,7 @@ class ResourceUtilizationController extends ClientApiController
}); });
return $this->fractal->item($stats) return $this->fractal->item($stats)
->transformWith($this->getTransformer(StatsTransformer::class)) ->transformWith(StatsTransformer::class)
->toArray(); ->toArray();
} }
} }

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