Compare commits

...

304 commits

Author SHA1 Message Date
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
4d1a7e6637
Improve client API route model binding and prevent accidental route access without valid model binds 2021-08-04 22:20:43 -07: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
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
734 changed files with 31570 additions and 37919 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

@ -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

4
.github/FUNDING.yml vendored
View file

@ -1,2 +1,2 @@
github: [DaneEveritt] github: [ DaneEveritt ]
custom: ["https://paypal.me/PterodactylSoftware"] custom: [ "https://paypal.me/PterodactylSoftware" ]

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 .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

@ -1,45 +1,37 @@
name: run tests name: Run Tests
on: on:
push: push:
branches-ignore: branches:
- master - 'develop'
- "release/**" - 'v2'
pull_request: pull_request:
jobs: jobs:
tests: tests:
runs-on: ubuntu-latest runs-on: ubuntu-20.04
if: "!contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'ci skip')" if: "!contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'ci skip')"
services:
mariadb:
image: mariadb:10.2
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: panel_test
ports:
- 3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
mysql:
image: mysql:8
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: panel_test
ports:
- 3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [ 7.4, 8.0 ] php: [ 7.4, 8.0 ]
database: [ mysql, mariadb ] database: [ 'mariadb:10.2', 'mysql:8' ]
name: "php-${{ matrix.php }} (engine: ${{ matrix.database }})" services:
database:
image: ${{ matrix.database }}
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: panel_test
ports:
- 3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
name: "php-${{ matrix.php }} (${{ matrix.database }})"
steps: steps:
- name: checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: get cache directory - 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)"
- name: cache dependencies - name: Cache
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: | path: |
@ -48,35 +40,28 @@ jobs:
key: ${{ runner.os }}-cache-${{ matrix.php }}-${{ hashFiles('**.composer.lock') }} key: ${{ runner.os }}-cache-${{ matrix.php }}-${{ hashFiles('**.composer.lock') }}
restore-keys: | restore-keys: |
${{ runner.os }}-cache-${{ matrix.php }}- ${{ runner.os }}-cache-${{ matrix.php }}-
- name: setup - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
extensions: cli, openssl, gd, mysql, pdo, mbstring, tokenizer, bcmath, xml, curl, zip extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2 tools: composer:v2
coverage: none coverage: none
- name: configure - name: Configure .env
run: cp .env.ci .env run: cp .env.ci .env
- name: install dependencies - name: composer install
run: composer install --prefer-dist --no-interaction --no-progress run: composer install --prefer-dist --no-interaction --no-progress
- name: run cs-fixer - name: Run cs-fixer
run: vendor/bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff --config .php_cs.dist run: vendor/bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff --config .php-cs-fixer.dist.php
continue-on-error: true continue-on-error: true
- name: execute unit tests - name: Execute Unit Tests
run: vendor/bin/phpunit --bootstrap bootstrap/app.php tests/Unit run: php artisan test tests/Unit
if: ${{ always() }} if: ${{ always() }}
env: env:
DB_CONNECTION: testing TESTING_DB_PORT: ${{ job.services.database.ports[3306] }}
TESTING_DB_HOST: UNIT_NO_DB
- name: execute integration tests (mysql)
run: vendor/bin/phpunit tests/Integration
if: "${{ matrix.database }} == 'mysql'"
env:
TESTING_DB_PORT: ${{ job.services.mysql.ports[3306] }}
TESTING_DB_USERNAME: root TESTING_DB_USERNAME: root
- name: execute integration tests (mariadb) - name: Execute Integration Tests
run: vendor/bin/phpunit tests/Integration run: php artisan test tests/Integration
if: "${{ matrix.database }} == 'mariadb'"
env: env:
TESTING_DB_PORT: ${{ job.services.mariadb.ports[3306] }} TESTING_DB_PORT: ${{ job.services.database.ports[3306] }}
TESTING_DB_USERNAME: root TESTING_DB_USERNAME: root

104
.gitignore vendored
View file

@ -1,38 +1,82 @@
#------------------#
# 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
#-------------------#
# 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_models.php
_ide_helper.php _ide_helper.php
.phpstorm.meta.php .phpstorm.meta.php
.php_cs.cache
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

@ -21,7 +21,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',

55
.yarn/releases/yarn-berry.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View file

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

4
.yarnrc.yml Normal file
View file

@ -0,0 +1,4 @@
nodeLinker: 'node-modules'
pnpIgnorePatterns:
- './public/**/*'
yarnPath: '.yarn/releases/yarn-berry.cjs'

View file

@ -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

@ -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,7 +57,7 @@ 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],

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

@ -7,6 +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 Illuminate\Http\Response;
use Swift_TransportException; use Swift_TransportException;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -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
@ -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

@ -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;
@ -33,12 +33,9 @@ class Fractal extends SpatieFractal
// 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) {
&& $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

@ -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->trans('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 = $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,110 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Application\Eggs;
use Pterodactyl\Models\Egg;
use Pterodactyl\Models\Nest;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
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\UpdateEggRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class EggController extends ApplicationApiController
{
private EggRepositoryInterface $repository;
/**
* EggController constructor.
*/
public function __construct(EggRepositoryInterface $repository)
{
parent::__construct();
$this->repository = $repository;
}
/**
* Return an array of all eggs on a given nest.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function index(GetEggsRequest $request, Nest $nest): array
{
$perPage = $request->query('per_page', 10);
if ($perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$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.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function view(GetEggRequest $request, Egg $egg): array
{
return $this->fractal->item($egg)
->transformWith(EggTransformer::class)
->toArray();
}
/**
* Creates a new egg.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function store(StoreEggRequest $request): JsonResponse
{
$egg = Egg::query()->create($request->validated());
return $this->fractal->item($egg)
->transformWith(EggTransformer::class)
->respond(JsonResponse::HTTP_CREATED);
}
/**
* Updates an egg.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
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();
}
}

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 = $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,13 +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([ ->addMeta([
'resource' => route('api.application.locations.view', [ 'resource' => route('api.application.locations.view', [
'location' => $location->id, 'location' => $location->id,
@ -107,13 +99,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 +115,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 = $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,122 @@
namespace Pterodactyl\Http\Controllers\Api\Application\Nests; namespace Pterodactyl\Http\Controllers\Api\Application\Nests;
use Pterodactyl\Models\Nest; use Pterodactyl\Models\Nest;
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\Contracts\Repository\NestRepositoryInterface; use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
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\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 NestRepositoryInterface $repository;
* @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface protected NestCreationService $nestCreationService;
*/ protected NestDeletionService $nestDeletionService;
private $repository; protected NestUpdateService $nestUpdateService;
/** /**
* NestController constructor. * NestController constructor.
*/ */
public function __construct(NestRepositoryInterface $repository) public function __construct(
{ NestRepositoryInterface $repository,
NestCreationService $nestCreationService,
NestDeletionService $nestDeletionService,
NestUpdateService $nestUpdateService
) {
parent::__construct(); parent::__construct();
$this->repository = $repository; $this->repository = $repository;
$this->nestCreationService = $nestCreationService;
$this->nestDeletionService = $nestDeletionService;
$this->nestUpdateService = $nestUpdateService;
} }
/** /**
* 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 = $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();
}
/**
* Updates an existing nest.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
* @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,11 @@
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 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 +16,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 +34,20 @@ 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 = $request->query('per_page', 10);
if ($perPage < 1 || $perPage > 100) {
throw new QueryValueOutOfRangeHttpException('per_page', 1, 100);
}
$allocations = $node->allocations()->paginate($perPage);
return $this->fractal->collection($allocations) return $this->fractal->collection($allocations)
->transformWith($this->getTransformer(AllocationTransformer::class)) ->transformWith(AllocationTransformer::class)
->toArray(); ->toArray();
} }
@ -59,11 +60,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 +72,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,6 +3,7 @@
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;
@ -10,6 +11,7 @@ use Pterodactyl\Services\Nodes\NodeCreationService;
use Pterodactyl\Services\Nodes\NodeDeletionService; use Pterodactyl\Services\Nodes\NodeDeletionService;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; 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,34 +21,19 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class NodeController extends ApplicationApiController class NodeController extends ApplicationApiController
{ {
/** private NodeRepositoryInterface $repository;
* @var \Pterodactyl\Services\Nodes\NodeCreationService private NodeCreationService $creationService;
*/ private NodeDeletionService $deletionService;
private $creationService; private NodeUpdateService $updateService;
/**
* @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.
*/ */
public function __construct( public function __construct(
NodeRepositoryInterface $repository,
NodeCreationService $creationService, NodeCreationService $creationService,
NodeDeletionService $deletionService, NodeDeletionService $deletionService,
NodeUpdateService $updateService, NodeUpdateService $updateService
NodeRepositoryInterface $repository
) { ) {
parent::__construct(); parent::__construct();
@ -58,26 +45,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 = $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,13 +82,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([ ->addMeta([
'resource' => route('api.application.nodes.view', [ 'resource' => route('api.application.nodes.view', [
'node' => $node->id, 'node' => $node->id,
@ -111,11 +108,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 +122,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
@ -40,7 +38,7 @@ class NodeDeploymentController extends ApplicationApiController
->handle($request->query('per_page'), $request->query('page')); ->handle($request->query('per_page'), $request->query('page'));
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 = $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

@ -18,20 +18,9 @@ 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 DatabaseRepositoryInterface $repository;
private $databaseManagementService;
/**
* @var \Pterodactyl\Services\Databases\DatabasePasswordService
*/
private $databasePasswordService;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
private $repository;
/** /**
* DatabaseController constructor. * DatabaseController constructor.
@ -51,21 +40,25 @@ class DatabaseController extends ApplicationApiController
/** /**
* 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 +67,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,7 +86,7 @@ class DatabaseController extends ApplicationApiController
])); ]));
return $this->fractal->item($database) return $this->fractal->item($database)
->transformWith($this->getTransformer(ServerDatabaseTransformer::class)) ->transformWith(ServerDatabaseTransformer::class)
->addMeta([ ->addMeta([
'resource' => route('api.application.servers.databases.view', [ 'resource' => route('api.application.servers.databases.view', [
'server' => $server->id, 'server' => $server->id,
@ -106,12 +99,12 @@ class DatabaseController extends ApplicationApiController
/** /**
* 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,8 +8,8 @@ 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\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;
@ -18,48 +18,41 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class ServerController extends ApplicationApiController class ServerController extends ApplicationApiController
{ {
/** private ServerCreationService $creationService;
* @var \Pterodactyl\Services\Servers\ServerCreationService private ServerDeletionService $deletionService;
*/
private $creationService;
/**
* @var \Pterodactyl\Services\Servers\ServerDeletionService
*/
private $deletionService;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
private $repository;
/** /**
* ServerController constructor. * ServerController constructor.
*/ */
public function __construct( public function __construct(
ServerCreationService $creationService, ServerCreationService $creationService,
ServerDeletionService $deletionService, ServerDeletionService $deletionService
ServerRepositoryInterface $repository
) { ) {
parent::__construct(); parent::__construct();
$this->creationService = $creationService; $this->creationService = $creationService;
$this->deletionService = $deletionService; $this->deletionService = $deletionService;
$this->repository = $repository;
} }
/** /**
* 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 = $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,7 +62,6 @@ 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
@ -79,22 +71,27 @@ class ServerController extends ApplicationApiController
$server = $this->creationService->handle($request->validated(), $request->getDeploymentObject()); $server = $this->creationService->handle($request->validated(), $request->getDeploymentObject());
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
{ {

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

@ -11,6 +11,8 @@ use Pterodactyl\Services\Users\UserCreationService;
use Pterodactyl\Services\Users\UserDeletionService; use Pterodactyl\Services\Users\UserDeletionService;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface; 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,25 +21,10 @@ use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class UserController extends ApplicationApiController class UserController extends ApplicationApiController
{ {
/** private UserRepositoryInterface $repository;
* @var \Pterodactyl\Services\Users\UserCreationService private UserCreationService $creationService;
*/ private UserDeletionService $deletionService;
private $creationService; private UserUpdateService $updateService;
/**
* @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.
@ -50,9 +37,9 @@ class UserController extends ApplicationApiController
) { ) {
parent::__construct(); parent::__construct();
$this->repository = $repository;
$this->creationService = $creationService; $this->creationService = $creationService;
$this->deletionService = $deletionService; $this->deletionService = $deletionService;
$this->repository = $repository;
$this->updateService = $updateService; $this->updateService = $updateService;
} }
@ -60,27 +47,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 = $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 +90,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,7 +114,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([ ->addMeta([
'resource' => route('api.application.users.view', [ 'resource' => route('api.application.users.view', [
'user' => $user->id, 'user' => $user->id,
@ -133,10 +129,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,31 +12,29 @@ use Pterodactyl\Http\Requests\Api\Client\Account\UpdatePasswordRequest;
class AccountController extends ClientApiController class AccountController extends ClientApiController
{ {
/** private AuthManager $authManager;
* @var \Pterodactyl\Services\Users\UserUpdateService private UserUpdateService $updateService;
*/
private $updateService;
/**
* @var \Illuminate\Auth\SessionGuard
*/
private $sessionGuard;
/** /**
* AccountController constructor. * AccountController constructor.
*/ */
public function __construct(AuthManager $sessionGuard, UserUpdateService $updateService) public function __construct(AuthManager $authManager, UserUpdateService $updateService)
{ {
parent::__construct(); parent::__construct();
$this->authManager = $authManager;
$this->updateService = $updateService; $this->updateService = $updateService;
$this->sessionGuard = $sessionGuard;
} }
/**
* Get's information about the currently authenticated user.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
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();
} }
@ -47,11 +44,11 @@ class AccountController extends ClientApiController
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @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();
} }
/** /**
@ -61,12 +58,12 @@ class AccountController extends ClientApiController
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function updatePassword(UpdatePasswordRequest $request): JsonResponse public function updatePassword(UpdatePasswordRequest $request): Response
{ {
$this->updateService->handle($request->user(), $request->validated()); $this->updateService->handle($request->user(), $request->validated());
$this->sessionGuard->logoutOtherDevices($request->input('password')); $this->authManager->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

@ -13,10 +13,7 @@ use Pterodactyl\Http\Requests\Api\Client\GetServersRequest;
class ClientController extends ClientApiController class ClientController extends ClientApiController
{ {
/** private ServerRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/** /**
* ClientController constructor. * ClientController constructor.
@ -31,15 +28,16 @@ class ClientController extends ClientApiController
/** /**
* 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',
@ -70,15 +68,13 @@ class ClientController extends ClientApiController
$servers = $builder->paginate(min($request->query('per_page', 50), 100))->appends($request->query()); $servers = $builder->paginate(min($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

@ -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;
@ -11,8 +12,8 @@ use Pterodactyl\Models\Permission;
use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Access\AuthorizationException;
use Pterodactyl\Services\Backups\DeleteBackupService; use Pterodactyl\Services\Backups\DeleteBackupService;
use Pterodactyl\Services\Backups\DownloadLinkService; use Pterodactyl\Services\Backups\DownloadLinkService;
use Pterodactyl\Services\Backups\InitiateBackupService;
use Pterodactyl\Repositories\Eloquent\BackupRepository; use Pterodactyl\Repositories\Eloquent\BackupRepository;
use Pterodactyl\Services\Backups\InitiateBackupService;
use Pterodactyl\Repositories\Wings\DaemonBackupRepository; use Pterodactyl\Repositories\Wings\DaemonBackupRepository;
use Pterodactyl\Transformers\Api\Client\BackupTransformer; use Pterodactyl\Transformers\Api\Client\BackupTransformer;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
@ -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

@ -18,25 +18,10 @@ 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 DatabaseRepository $repository;
*/ private DatabaseManagementService $managementService;
private $deployDatabaseService; private DatabasePasswordService $passwordService;
/**
* @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.
@ -57,11 +42,13 @@ class DatabaseController extends ClientApiController
/** /**
* 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 +65,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 +73,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,7 +6,6 @@ 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 Illuminate\Contracts\Routing\ResponseFactory;
use Pterodactyl\Repositories\Wings\DaemonFileRepository; use Pterodactyl\Repositories\Wings\DaemonFileRepository;
@ -26,28 +25,17 @@ 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 ResponseFactory $responseFactory;
*/ 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(
DaemonFileRepository $fileRepository,
ResponseFactory $responseFactory, ResponseFactory $responseFactory,
NodeJWTService $jwtService, NodeJWTService $jwtService
DaemonFileRepository $fileRepository
) { ) {
parent::__construct(); parent::__construct();
@ -60,6 +48,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 +57,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 +80,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 +111,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 +124,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 +132,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 +143,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 +151,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 +161,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 +180,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
{ {
@ -215,14 +202,14 @@ class FileController extends ClientApiController
}); });
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) { $file = $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.
@ -234,15 +221,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 +241,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
{ {
$this->fileRepository->setServer($server) $server->audit(AuditLog::SERVER__FILESYSTEM_CHMOD, function (AuditLog $audit, Server $server) use ($request) {
->chmodFiles( $audit->metadata = ['directory' => $request->input('root'), 'files' => $request->input('files')];
$request->input('root'),
$request->input('files')
);
return new JsonResponse([], Response::HTTP_NO_CONTENT); $this->fileRepository->setServer($server)
->chmodFiles(
$request->input('root'),
$request->input('files'),
);
});
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 = ['directory' => $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,15 +12,8 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest;
class ResourceUtilizationController extends ClientApiController class ResourceUtilizationController extends ClientApiController
{ {
/**
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
*/
private DaemonServerRepository $repository;
/**
* @var \Illuminate\Cache\Repository
*/
private Repository $cache; private Repository $cache;
private DaemonServerRepository $repository;
/** /**
* ResourceUtilizationController constructor. * ResourceUtilizationController constructor.
@ -39,6 +32,7 @@ class ResourceUtilizationController extends ClientApiController
* a flood of unnecessary API calls. * a flood of unnecessary API calls.
* *
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function __invoke(GetServerRequest $request, Server $server): array public function __invoke(GetServerRequest $request, Server $server): array
{ {
@ -48,7 +42,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();
} }
} }

View file

@ -8,14 +8,13 @@ use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Pterodactyl\Models\Schedule; use Pterodactyl\Models\Schedule;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Helpers\Utilities; use Pterodactyl\Helpers\Utilities;
use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\Eloquent\ScheduleRepository; use Pterodactyl\Repositories\Eloquent\ScheduleRepository;
use Pterodactyl\Services\Schedules\ProcessScheduleService; use Pterodactyl\Services\Schedules\ProcessScheduleService;
use Pterodactyl\Transformers\Api\Client\ScheduleTransformer; use Pterodactyl\Transformers\Api\Client\ScheduleTransformer;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\ViewScheduleRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\ViewScheduleRequest;
use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\StoreScheduleRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\StoreScheduleRequest;
use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\DeleteScheduleRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\DeleteScheduleRequest;
@ -24,15 +23,8 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\TriggerScheduleReques
class ScheduleController extends ClientApiController class ScheduleController extends ClientApiController
{ {
/** private ScheduleRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\ScheduleRepository private ProcessScheduleService $service;
*/
private $repository;
/**
* @var \Pterodactyl\Services\Schedules\ProcessScheduleService
*/
private $service;
/** /**
* ScheduleController constructor. * ScheduleController constructor.
@ -48,27 +40,26 @@ class ScheduleController extends ClientApiController
/** /**
* Returns all of the schedules belonging to a given server. * Returns all of the schedules belonging to a given server.
* *
* @return array * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(ViewScheduleRequest $request, Server $server) public function index(ViewScheduleRequest $request, Server $server): array
{ {
$schedules = $server->schedule; $schedules = $server->schedule;
$schedules->loadMissing('tasks'); $schedules->loadMissing('tasks');
return $this->fractal->collection($schedules) return $this->fractal->collection($schedules)
->transformWith($this->getTransformer(ScheduleTransformer::class)) ->transformWith(ScheduleTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Store a new schedule for a server. * Store a new schedule for a server.
* *
* @return array
*
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function store(StoreScheduleRequest $request, Server $server) public function store(StoreScheduleRequest $request, Server $server): array
{ {
/** @var \Pterodactyl\Models\Schedule $model */ /** @var \Pterodactyl\Models\Schedule $model */
$model = $this->repository->create([ $model = $this->repository->create([
@ -85,38 +76,33 @@ class ScheduleController extends ClientApiController
]); ]);
return $this->fractal->item($model) return $this->fractal->item($model)
->transformWith($this->getTransformer(ScheduleTransformer::class)) ->transformWith(ScheduleTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Returns a specific schedule for the server. * Returns a specific schedule for the server.
* *
* @return array * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(ViewScheduleRequest $request, Server $server, Schedule $schedule) public function view(ViewScheduleRequest $request, Server $server, Schedule $schedule): array
{ {
if ($schedule->server_id !== $server->id) {
throw new NotFoundHttpException();
}
$schedule->loadMissing('tasks'); $schedule->loadMissing('tasks');
return $this->fractal->item($schedule) return $this->fractal->item($schedule)
->transformWith($this->getTransformer(ScheduleTransformer::class)) ->transformWith(ScheduleTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Updates a given schedule with the new data provided. * Updates a given schedule with the new data provided.
* *
* @return array
*
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayException
* @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(UpdateScheduleRequest $request, Server $server, Schedule $schedule) public function update(UpdateScheduleRequest $request, Server $server, Schedule $schedule): array
{ {
$active = (bool) $request->input('is_active'); $active = (bool) $request->input('is_active');
@ -143,7 +129,7 @@ class ScheduleController extends ClientApiController
$this->repository->update($schedule->id, $data); $this->repository->update($schedule->id, $data);
return $this->fractal->item($schedule->refresh()) return $this->fractal->item($schedule->refresh())
->transformWith($this->getTransformer(ScheduleTransformer::class)) ->transformWith(ScheduleTransformer::class)
->toArray(); ->toArray();
} }
@ -151,27 +137,27 @@ class ScheduleController extends ClientApiController
* Executes a given schedule immediately rather than waiting on it's normally scheduled time * Executes a given schedule immediately rather than waiting on it's normally scheduled time
* to pass. This does not care about the schedule state. * to pass. This does not care about the schedule state.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function execute(TriggerScheduleRequest $request, Server $server, Schedule $schedule) public function execute(TriggerScheduleRequest $request, Server $server, Schedule $schedule): Response
{ {
if (!$schedule->is_active) {
throw new BadRequestHttpException('Cannot trigger schedule exception for a schedule that is not currently active.');
}
$this->service->handle($schedule, true); $this->service->handle($schedule, true);
return new JsonResponse([], JsonResponse::HTTP_ACCEPTED); return $this->returnAccepted();
} }
/** /**
* Deletes a schedule and it's associated tasks. * Deletes a schedule and it's associated tasks.
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function delete(DeleteScheduleRequest $request, Server $server, Schedule $schedule) public function delete(DeleteScheduleRequest $request, Server $server, Schedule $schedule): Response
{ {
$this->repository->delete($schedule->id); $this->repository->delete($schedule->id);
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**

View file

@ -6,23 +6,19 @@ use Pterodactyl\Models\Task;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Pterodactyl\Models\Schedule; use Pterodactyl\Models\Schedule;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\Permission;
use Pterodactyl\Repositories\Eloquent\TaskRepository; use Pterodactyl\Repositories\Eloquent\TaskRepository;
use Pterodactyl\Exceptions\Http\HttpForbiddenException; use Pterodactyl\Exceptions\Http\HttpForbiddenException;
use Pterodactyl\Transformers\Api\Client\TaskTransformer; use Pterodactyl\Transformers\Api\Client\TaskTransformer;
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
use Pterodactyl\Exceptions\Service\ServiceLimitExceededException; use Pterodactyl\Exceptions\Service\ServiceLimitExceededException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\StoreTaskRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\StoreTaskRequest;
use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\DeleteScheduleRequest;
use Pterodactyl\Http\Requests\Api\Client\Servers\Schedules\UpdateScheduleRequest;
class ScheduleTaskController extends ClientApiController class ScheduleTaskController extends ClientApiController
{ {
/** private TaskRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\TaskRepository
*/
private $repository;
/** /**
* ScheduleTaskController constructor. * ScheduleTaskController constructor.
@ -37,13 +33,12 @@ class ScheduleTaskController extends ClientApiController
/** /**
* Create a new task for a given schedule and store it in the database. * Create a new task for a given schedule and store it in the database.
* *
* @return array * @throws \Pterodactyl\Exceptions\Http\HttpForbiddenException
*
* @throws \Pterodactyl\Exceptions\Model\HttpForbiddenException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Service\ServiceLimitExceededException * @throws \Pterodactyl\Exceptions\Service\ServiceLimitExceededException
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function store(StoreTaskRequest $request, Server $server, Schedule $schedule) public function store(StoreTaskRequest $request, Server $server, Schedule $schedule): array
{ {
$limit = config('pterodactyl.client_features.schedules.per_schedule_task_limit', 10); $limit = config('pterodactyl.client_features.schedules.per_schedule_task_limit', 10);
if ($schedule->tasks()->count() >= $limit) { if ($schedule->tasks()->count() >= $limit) {
@ -68,20 +63,19 @@ class ScheduleTaskController extends ClientApiController
]); ]);
return $this->fractal->item($task) return $this->fractal->item($task)
->transformWith($this->getTransformer(TaskTransformer::class)) ->transformWith(TaskTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Updates a given task for a server. * Updates a given task for a server.
* *
* @return array * @throws \Pterodactyl\Exceptions\Http\HttpForbiddenException
*
* @throws \Pterodactyl\Exceptions\Model\HttpForbiddenException
* @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(StoreTaskRequest $request, Server $server, Schedule $schedule, Task $task) public function update(StoreTaskRequest $request, Server $server, Schedule $schedule, Task $task): array
{ {
if ($schedule->id !== $task->schedule_id || $server->id !== $schedule->server_id) { if ($schedule->id !== $task->schedule_id || $server->id !== $schedule->server_id) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
@ -99,7 +93,7 @@ class ScheduleTaskController extends ClientApiController
]); ]);
return $this->fractal->item($task->refresh()) return $this->fractal->item($task->refresh())
->transformWith($this->getTransformer(TaskTransformer::class)) ->transformWith(TaskTransformer::class)
->toArray(); ->toArray();
} }
@ -107,26 +101,20 @@ class ScheduleTaskController extends ClientApiController
* Delete a given task for a schedule. If there are subsequent tasks stored in the database * Delete a given task for a schedule. If there are subsequent tasks stored in the database
* for this schedule their sequence IDs are decremented properly. * for this schedule their sequence IDs are decremented properly.
* *
* @return \Illuminate\Http\JsonResponse * This uses the UpdateScheduleRequest intentionally -- there is no permission specific
* to deleting a given task on a schedule, so we'll assume if you have permission to edit
* a schedule that you can then remove a task from said schedule.
* *
* @throws \Exception * @throws \Exception
*/ */
public function delete(ClientApiRequest $request, Server $server, Schedule $schedule, Task $task) public function delete(DeleteScheduleRequest $request, Server $server, Schedule $schedule, Task $task): Response
{ {
if ($task->schedule_id !== $schedule->id || $schedule->server_id !== $server->id) {
throw new NotFoundHttpException();
}
if (!$request->user()->can(Permission::ACTION_SCHEDULE_UPDATE, $server)) {
throw new HttpForbiddenException('You do not have permission to perform this action.');
}
$schedule->tasks()->where('sequence_id', '>', $task->sequence_id)->update([ $schedule->tasks()->where('sequence_id', '>', $task->sequence_id)->update([
'sequence_id' => $schedule->tasks()->getConnection()->raw('(sequence_id - 1)'), 'sequence_id' => $schedule->tasks()->getConnection()->raw('(sequence_id - 1)'),
]); ]);
$task->delete(); $task->delete();
return new JsonResponse(null, Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -11,15 +11,8 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\GetServerRequest;
class ServerController extends ClientApiController class ServerController extends ClientApiController
{ {
/** private SubuserRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\SubuserRepository private GetUserPermissionsService $permissionsService;
*/
private $repository;
/**
* @var \Pterodactyl\Services\Servers\GetUserPermissionsService
*/
private $permissionsService;
/** /**
* ServerController constructor. * ServerController constructor.
@ -35,11 +28,13 @@ class ServerController extends ClientApiController
/** /**
* Transform an individual server into a response that can be consumed by a * Transform an individual server into a response that can be consumed by a
* client using the API. * client using the API.
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetServerRequest $request, Server $server): array public function index(GetServerRequest $request, Server $server): array
{ {
return $this->fractal->item($server) return $this->fractal->item($server)
->transformWith($this->getTransformer(ServerTransformer::class)) ->transformWith(ServerTransformer::class)
->addMeta([ ->addMeta([
'is_server_owner' => $request->user()->id === $server->owner_id, 'is_server_owner' => $request->user()->id === $server->owner_id,
'user_permissions' => $this->permissionsService->handle($server, $request->user()), 'user_permissions' => $this->permissionsService->handle($server, $request->user()),

View file

@ -4,7 +4,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 Illuminate\Http\JsonResponse;
use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Services\Servers\ReinstallServerService; use Pterodactyl\Services\Servers\ReinstallServerService;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
@ -15,15 +14,8 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Settings\ReinstallServerRequest
class SettingsController extends ClientApiController class SettingsController extends ClientApiController
{ {
/** private ServerRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository private ReinstallServerService $reinstallServerService;
*/
private $repository;
/**
* @var \Pterodactyl\Services\Servers\ReinstallServerService
*/
private $reinstallServerService;
/** /**
* SettingsController constructor. * SettingsController constructor.
@ -41,42 +33,36 @@ class SettingsController extends ClientApiController
/** /**
* Renames a server. * Renames a server.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function rename(RenameServerRequest $request, Server $server) public function rename(RenameServerRequest $request, Server $server): Response
{ {
$this->repository->update($server->id, [ $this->repository->update($server->id, [
'name' => $request->input('name'), 'name' => $request->input('name'),
]); ]);
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**
* Reinstalls the server on the daemon. * Reinstalls the server on the daemon.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function reinstall(ReinstallServerRequest $request, Server $server) public function reinstall(ReinstallServerRequest $request, Server $server): Response
{ {
$this->reinstallServerService->handle($server); $this->reinstallServerService->handle($server);
return new JsonResponse([], Response::HTTP_ACCEPTED); return $this->returnAccepted();
} }
/** /**
* Changes the Docker image in use by the server. * Changes the Docker image in use by the server.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function dockerImage(SetDockerImageRequest $request, Server $server) public function dockerImage(SetDockerImageRequest $request, Server $server): Response
{ {
if (!in_array($server->image, $server->egg->docker_images)) { if (!in_array($server->image, $server->egg->docker_images)) {
throw new BadRequestHttpException('This server\'s Docker image has been manually set by an administrator and cannot be updated.'); throw new BadRequestHttpException('This server\'s Docker image has been manually set by an administrator and cannot be updated.');
@ -84,6 +70,6 @@ class SettingsController extends ClientApiController
$server->forceFill(['image' => $request->input('docker_image')])->saveOrFail(); $server->forceFill(['image' => $request->input('docker_image')])->saveOrFail();
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -14,20 +14,9 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Startup\UpdateStartupVariableRe
class StartupController extends ClientApiController class StartupController extends ClientApiController
{ {
/** private VariableValidatorService $service;
* @var \Pterodactyl\Services\Servers\VariableValidatorService private ServerVariableRepository $repository;
*/ private StartupCommandService $startupCommandService;
private $service;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerVariableRepository
*/
private $repository;
/**
* @var \Pterodactyl\Services\Servers\StartupCommandService
*/
private $startupCommandService;
/** /**
* StartupController constructor. * StartupController constructor.
@ -44,16 +33,16 @@ class StartupController extends ClientApiController
/** /**
* Returns the startup information for the server including all of the variables. * Returns the startup information for the server including all of the variables.
* *
* @return array * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetStartupRequest $request, Server $server) public function index(GetStartupRequest $request, Server $server): array
{ {
$startup = $this->startupCommandService->handle($server, false); $startup = $this->startupCommandService->handle($server, false);
return $this->fractal->collection( return $this->fractal->collection(
$server->variables()->where('user_viewable', true)->get() $server->variables()->where('user_viewable', true)->get()
) )
->transformWith($this->getTransformer(EggVariableTransformer::class)) ->transformWith(EggVariableTransformer::class)
->addMeta([ ->addMeta([
'startup_command' => $startup, 'startup_command' => $startup,
'docker_images' => $server->egg->docker_images, 'docker_images' => $server->egg->docker_images,
@ -65,13 +54,12 @@ class StartupController extends ClientApiController
/** /**
* Updates a single variable for a server. * Updates a single variable for a server.
* *
* @return array
*
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
* @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(UpdateStartupVariableRequest $request, Server $server) public function update(UpdateStartupVariableRequest $request, Server $server): array
{ {
/** @var \Pterodactyl\Models\EggVariable $variable */ /** @var \Pterodactyl\Models\EggVariable $variable */
$variable = $server->variables()->where('env_variable', $request->input('key'))->first(); $variable = $server->variables()->where('env_variable', $request->input('key'))->first();
@ -98,7 +86,7 @@ class StartupController extends ClientApiController
$startup = $this->startupCommandService->handle($server, false); $startup = $this->startupCommandService->handle($server, false);
return $this->fractal->item($variable) return $this->fractal->item($variable)
->transformWith($this->getTransformer(EggVariableTransformer::class)) ->transformWith(EggVariableTransformer::class)
->addMeta([ ->addMeta([
'startup_command' => $startup, 'startup_command' => $startup,
'raw_startup_command' => $server->startup, 'raw_startup_command' => $server->startup,

View file

@ -3,8 +3,9 @@
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\Server; use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse; use Pterodactyl\Models\Subuser;
use Pterodactyl\Models\Permission; use Pterodactyl\Models\Permission;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Pterodactyl\Repositories\Eloquent\SubuserRepository; use Pterodactyl\Repositories\Eloquent\SubuserRepository;
@ -20,20 +21,9 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest;
class SubuserController extends ClientApiController class SubuserController extends ClientApiController
{ {
/** private SubuserRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\SubuserRepository private SubuserCreationService $creationService;
*/ private DaemonServerRepository $serverRepository;
private $repository;
/**
* @var \Pterodactyl\Services\Subusers\SubuserCreationService
*/
private $creationService;
/**
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
*/
private $serverRepository;
/** /**
* SubuserController constructor. * SubuserController constructor.
@ -53,40 +43,36 @@ class SubuserController extends ClientApiController
/** /**
* Return the users associated with this server instance. * Return the users associated with this server instance.
* *
* @return array * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function index(GetSubuserRequest $request, Server $server) public function index(GetSubuserRequest $request, Server $server): array
{ {
return $this->fractal->collection($server->subusers) return $this->fractal->collection($server->subusers)
->transformWith($this->getTransformer(SubuserTransformer::class)) ->transformWith(SubuserTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Returns a single subuser associated with this server instance. * Returns a single subuser associated with this server instance.
* *
* @return array * @throws \Illuminate\Contracts\Container\BindingResolutionException
*/ */
public function view(GetSubuserRequest $request) public function view(GetSubuserRequest $request, Server $server, Subuser $subuser): array
{ {
$subuser = $request->attributes->get('subuser');
return $this->fractal->item($subuser) return $this->fractal->item($subuser)
->transformWith($this->getTransformer(SubuserTransformer::class)) ->transformWith(SubuserTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Create a new subuser for the given server. * Create a new subuser for the given server.
* *
* @return array
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException * @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
* @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException * @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException
* @throws \Throwable * @throws \Throwable
*/ */
public function store(StoreSubuserRequest $request, Server $server) public function store(StoreSubuserRequest $request, Server $server): array
{ {
$response = $this->creationService->handle( $response = $this->creationService->handle(
$server, $server,
@ -95,7 +81,7 @@ class SubuserController extends ClientApiController
); );
return $this->fractal->item($response) return $this->fractal->item($response)
->transformWith($this->getTransformer(SubuserTransformer::class)) ->transformWith(SubuserTransformer::class)
->toArray(); ->toArray();
} }
@ -104,12 +90,10 @@ class SubuserController 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(UpdateSubuserRequest $request, Server $server): array public function update(UpdateSubuserRequest $request, Server $server, Subuser $subuser): array
{ {
/** @var \Pterodactyl\Models\Subuser $subuser */
$subuser = $request->attributes->get('subuser');
$permissions = $this->getDefaultPermissions($request); $permissions = $this->getDefaultPermissions($request);
$current = $subuser->permissions; $current = $subuser->permissions;
@ -133,20 +117,15 @@ class SubuserController extends ClientApiController
} }
return $this->fractal->item($subuser->refresh()) return $this->fractal->item($subuser->refresh())
->transformWith($this->getTransformer(SubuserTransformer::class)) ->transformWith(SubuserTransformer::class)
->toArray(); ->toArray();
} }
/** /**
* Removes a subusers from a server's assignment. * Removes a subusers from a server's assignment.
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function delete(DeleteSubuserRequest $request, Server $server) public function delete(DeleteSubuserRequest $request, Server $server, Subuser $subuser): Response
{ {
/** @var \Pterodactyl\Models\Subuser $subuser */
$subuser = $request->attributes->get('subuser');
$this->repository->delete($subuser->id); $this->repository->delete($subuser->id);
try { try {
@ -156,7 +135,7 @@ class SubuserController extends ClientApiController
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]); Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);
} }
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
/** /**

View file

@ -5,24 +5,16 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\Permission;
use Pterodactyl\Services\Nodes\NodeJWTService; use Pterodactyl\Services\Nodes\NodeJWTService;
use Pterodactyl\Exceptions\Http\HttpForbiddenException; use Pterodactyl\Exceptions\Http\HttpForbiddenException;
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
use Pterodactyl\Services\Servers\GetUserPermissionsService; use Pterodactyl\Services\Servers\GetUserPermissionsService;
use Pterodactyl\Http\Requests\Api\Client\WebsocketTokenRequest;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
class WebsocketController extends ClientApiController class WebsocketController extends ClientApiController
{ {
/** private NodeJWTService $jwtService;
* @var \Pterodactyl\Services\Nodes\NodeJWTService private GetUserPermissionsService $permissionsService;
*/
private $jwtService;
/**
* @var \Pterodactyl\Services\Servers\GetUserPermissionsService
*/
private $permissionsService;
/** /**
* WebsocketController constructor. * WebsocketController constructor.
@ -40,19 +32,12 @@ class WebsocketController extends ClientApiController
/** /**
* Generates a one-time token that is sent along in every websocket call to the Daemon. * Generates a one-time token that is sent along in every websocket call to the Daemon.
* This is a signed JWT that the Daemon then uses the verify the user's identity, and * This is a signed JWT that the Daemon then uses the verify the user's identity, and
* allows us to continually renew this token and avoid users mainitaining sessions wrongly, * allows us to continually renew this token and avoid users maintaining sessions wrongly,
* as well as ensure that user's only perform actions they're allowed to. * as well as ensure that user's only perform actions they're allowed to.
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function __invoke(ClientApiRequest $request, Server $server) public function __invoke(WebsocketTokenRequest $request, Server $server): JsonResponse
{ {
$user = $request->user(); $permissions = $this->permissionsService->handle($server, $request->user());
if ($user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $server)) {
throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.');
}
$permissions = $this->permissionsService->handle($server, $user);
$node = $server->node; $node = $server->node;
if (!is_null($server->transfer)) { if (!is_null($server->transfer)) {
@ -68,13 +53,13 @@ class WebsocketController extends ClientApiController
} }
$token = $this->jwtService $token = $this->jwtService
->setExpiresAt(CarbonImmutable::now()->addMinutes(10)) ->setExpiresAt(CarbonImmutable::now()->addMinutes(10)->toDateTimeImmutable())
->setClaims([ ->setClaims([
'user_id' => $request->user()->id, 'user_id' => $request->user()->id,
'server_uuid' => $server->uuid, 'server_uuid' => $server->uuid,
'permissions' => $permissions, 'permissions' => $permissions,
]) ])
->handle($node, $user->id . $server->uuid); ->handle($node, $request->user()->id . $server->uuid);
$socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $node->getConnectionAddress()); $socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $node->getConnectionAddress());

View file

@ -14,20 +14,9 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class TwoFactorController extends ClientApiController class TwoFactorController extends ClientApiController
{ {
/** private ToggleTwoFactorService $toggleTwoFactorService;
* @var \Pterodactyl\Services\Users\TwoFactorSetupService private TwoFactorSetupService $setupService;
*/ private Factory $validation;
private $setupService;
/**
* @var \Illuminate\Contracts\Validation\Factory
*/
private $validation;
/**
* @var \Pterodactyl\Services\Users\ToggleTwoFactorService
*/
private $toggleTwoFactorService;
/** /**
* TwoFactorController constructor. * TwoFactorController constructor.
@ -39,9 +28,9 @@ class TwoFactorController extends ClientApiController
) { ) {
parent::__construct(); parent::__construct();
$this->toggleTwoFactorService = $toggleTwoFactorService;
$this->setupService = $setupService; $this->setupService = $setupService;
$this->validation = $validation; $this->validation = $validation;
$this->toggleTwoFactorService = $toggleTwoFactorService;
} }
/** /**
@ -49,12 +38,10 @@ class TwoFactorController extends ClientApiController
* it on their account. If two-factor is already enabled this endpoint * it on their account. If two-factor is already enabled this endpoint
* will return a 400 error. * will return a 400 error.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function index(Request $request) public function index(Request $request): JsonResponse
{ {
if ($request->user()->use_totp) { if ($request->user()->use_totp) {
throw new BadRequestHttpException('Two-factor authentication is already enabled on this account.'); throw new BadRequestHttpException('Two-factor authentication is already enabled on this account.');
@ -68,8 +55,6 @@ class TwoFactorController extends ClientApiController
/** /**
* Updates a user's account to have two-factor enabled. * Updates a user's account to have two-factor enabled.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
@ -77,7 +62,7 @@ class TwoFactorController extends ClientApiController
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
* @throws \Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid * @throws \Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid
*/ */
public function store(Request $request) public function store(Request $request): JsonResponse
{ {
$validator = $this->validation->make($request->all(), [ $validator = $this->validation->make($request->all(), [
'code' => 'required|string', 'code' => 'required|string',
@ -100,10 +85,8 @@ class TwoFactorController extends ClientApiController
/** /**
* Disables two-factor authentication on an account if the password provided * Disables two-factor authentication on an account if the password provided
* is valid. * is valid.
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function delete(Request $request) public function delete(Request $request): Response
{ {
if (!password_verify($request->input('password') ?? '', $request->user()->password)) { if (!password_verify($request->input('password') ?? '', $request->user()->password)) {
throw new BadRequestHttpException('The password provided was not valid.'); throw new BadRequestHttpException('The password provided was not valid.');
@ -117,6 +100,6 @@ class TwoFactorController extends ClientApiController
'use_totp' => false, 'use_totp' => false,
]); ]);
return new JsonResponse([], Response::HTTP_NO_CONTENT); return $this->returnNoContent();
} }
} }

View file

@ -0,0 +1,125 @@
<?php
namespace Pterodactyl\Http\Controllers\Api\Client;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\WebauthnKey;
use LaravelWebauthn\Facades\Webauthn;
use Webauthn\PublicKeyCredentialCreationOptions;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Pterodactyl\Transformers\Api\Client\WebauthnKeyTransformer;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class WebauthnController extends ClientApiController
{
private const SESSION_PUBLICKEY_CREATION = 'webauthn.publicKeyCreation';
/**
* ?
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function index(Request $request): array
{
return $this->fractal->collection(WebauthnKey::query()->where('user_id', '=', $request->user()->id)->get())
->transformWith(WebauthnKeyTransformer::class)
->toArray();
}
/**
* ?
*/
public function register(Request $request): JsonResponse
{
if (!Webauthn::canRegister($request->user())) {
return new JsonResponse([
'error' => [
'message' => trans('webauthn::errors.cannot_register_new_key'),
],
], JsonResponse::HTTP_FORBIDDEN);
}
$publicKey = Webauthn::getRegisterData($request->user());
$request->session()->put(self::SESSION_PUBLICKEY_CREATION, $publicKey);
$request->session()->save();
return new JsonResponse([
'public_key' => $publicKey,
]);
}
/**
* ?
*
* @return array|JsonResponse
*/
public function create(Request $request)
{
if (!Webauthn::canRegister($request->user())) {
return new JsonResponse([
'error' => [
'message' => trans('webauthn::errors.cannot_register_new_key'),
],
], JsonResponse::HTTP_FORBIDDEN);
}
if ($request->input('register') === null) {
throw new BadRequestHttpException('Missing register data in request body.');
}
if ($request->input('name') === null) {
throw new BadRequestHttpException('Missing name in request body.');
}
try {
$publicKey = $request->session()->pull(self::SESSION_PUBLICKEY_CREATION);
if (!$publicKey instanceof PublicKeyCredentialCreationOptions) {
throw new ModelNotFoundException(trans('webauthn::errors.create_data_not_found'));
}
$webauthnKey = Webauthn::doRegister(
$request->user(),
$publicKey,
$request->input('register'),
$request->input('name'),
);
return $this->fractal->item($webauthnKey)
->transformWith(WebauthnKeyTransformer::class)
->toArray();
} catch (Exception $e) {
return new JsonResponse([
'error' => [
'message' => $e->getMessage(),
],
], JsonResponse::HTTP_FORBIDDEN);
}
}
/**
* ?
*/
public function deleteKey(Request $request, int $webauthnKeyId): JsonResponse
{
try {
WebauthnKey::query()
->where('user_id', $request->user()->getAuthIdentifier())
->findOrFail($webauthnKeyId)
->delete();
return new JsonResponse([
'deleted' => true,
'id' => $webauthnKeyId,
]);
} catch (ModelNotFoundException $e) {
return new JsonResponse([
'error' => [
'message' => trans('webauthn::errors.object_not_found'),
],
], JsonResponse::HTTP_NOT_FOUND);
}
}
}

View file

@ -17,15 +17,8 @@ class BackupRemoteUploadController extends Controller
{ {
public const PART_SIZE = 5 * 1024 * 1024 * 1024; public const PART_SIZE = 5 * 1024 * 1024 * 1024;
/** private BackupRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\BackupRepository private BackupManager $backupManager;
*/
private $repository;
/**
* @var \Pterodactyl\Extensions\Backups\BackupManager
*/
private $backupManager;
/** /**
* BackupRemoteUploadController constructor. * BackupRemoteUploadController constructor.
@ -37,15 +30,13 @@ class BackupRemoteUploadController extends Controller
} }
/** /**
* Returns the required presigned urls to upload a backup to S3 cloud storage. * Returns the required pre-signed urls to upload a backup to S3 cloud storage.
*
* @return \Illuminate\Http\JsonResponse
* *
* @throws \Exception * @throws \Exception
* @throws \Throwable * @throws \Throwable
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/ */
public function __invoke(Request $request, string $backup) public function __invoke(Request $request, string $backup): JsonResponse
{ {
// Get the size query parameter. // Get the size query parameter.
$size = (int) $request->query('size'); $size = (int) $request->query('size');

View file

@ -4,10 +4,10 @@ namespace Pterodactyl\Http\Controllers\Api\Remote\Backups;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;
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;
use Illuminate\Http\JsonResponse;
use League\Flysystem\AwsS3v3\AwsS3Adapter; use League\Flysystem\AwsS3v3\AwsS3Adapter;
use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
@ -17,10 +17,7 @@ use Pterodactyl\Http\Requests\Api\Remote\ReportBackupCompleteRequest;
class BackupStatusController extends Controller class BackupStatusController extends Controller
{ {
/** private BackupManager $backupManager;
* @var \Pterodactyl\Extensions\Backups\BackupManager
*/
private $backupManager;
/** /**
* BackupStatusController constructor. * BackupStatusController constructor.
@ -33,11 +30,9 @@ class BackupStatusController extends Controller
/** /**
* Handles updating the state of a backup. * Handles updating the state of a backup.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function index(ReportBackupCompleteRequest $request, string $backup) public function index(ReportBackupCompleteRequest $request, string $backup): Response
{ {
/** @var \Pterodactyl\Models\Backup $model */ /** @var \Pterodactyl\Models\Backup $model */
$model = Backup::query()->where('uuid', $backup)->firstOrFail(); $model = Backup::query()->where('uuid', $backup)->firstOrFail();
@ -60,7 +55,7 @@ class BackupStatusController extends Controller
// Change the lock state to unlocked if this was a failed backup so that it can be // Change the lock state to unlocked if this was a failed backup so that it can be
// deleted easily. Also does not make sense to have a locked backup on the system // deleted easily. Also does not make sense to have a locked backup on the system
// that is failed. // that is failed.
'is_locked' => $successful ? $model->is_locked : false, 'is_locked' => $successful && $model->is_locked,
'checksum' => $successful ? ($request->input('checksum_type') . ':' . $request->input('checksum')) : null, 'checksum' => $successful ? ($request->input('checksum_type') . ':' . $request->input('checksum')) : null,
'bytes' => $successful ? $request->input('size') : 0, 'bytes' => $successful ? $request->input('size') : 0,
'completed_at' => CarbonImmutable::now(), 'completed_at' => CarbonImmutable::now(),
@ -74,7 +69,7 @@ class BackupStatusController extends Controller
} }
}); });
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return new Response('', Response::HTTP_NO_CONTENT);
} }
/** /**
@ -85,11 +80,9 @@ class BackupStatusController extends Controller
* The only thing the successful field does is update the entry value for the audit logs * The only thing the successful field does is update the entry value for the audit logs
* table tracking for this restoration. * table tracking for this restoration.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function restore(Request $request, string $backup) public function restore(Request $request, string $backup): Response
{ {
/** @var \Pterodactyl\Models\Backup $model */ /** @var \Pterodactyl\Models\Backup $model */
$model = Backup::query()->where('uuid', $backup)->firstOrFail(); $model = Backup::query()->where('uuid', $backup)->firstOrFail();
@ -105,7 +98,7 @@ class BackupStatusController extends Controller
$server->update(['status' => null]); $server->update(['status' => null]);
}); });
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return new Response('', Response::HTTP_NO_CONTENT);
} }
/** /**

View file

@ -10,15 +10,8 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
class EggInstallController extends Controller class EggInstallController extends Controller
{ {
/** private EnvironmentService $environment;
* @var \Pterodactyl\Services\Servers\EnvironmentService private ServerRepositoryInterface $repository;
*/
private $environment;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
private $repository;
/** /**
* EggInstallController constructor. * EggInstallController constructor.

View file

@ -3,13 +3,13 @@
namespace Pterodactyl\Http\Controllers\Api\Remote\Servers; namespace Pterodactyl\Http\Controllers\Api\Remote\Servers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
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 Illuminate\Http\JsonResponse;
use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Services\Eggs\EggConfigurationService; use Pterodactyl\Services\Eggs\EggConfigurationService;
use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Http\Resources\Wings\ServerConfigurationCollection; use Pterodactyl\Http\Resources\Wings\ServerConfigurationCollection;
@ -17,46 +17,30 @@ use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
class ServerDetailsController extends Controller class ServerDetailsController extends Controller
{ {
/** private ServerRepository $repository;
* @var \Pterodactyl\Services\Eggs\EggConfigurationService private ServerConfigurationStructureService $configurationStructureService;
*/ private EggConfigurationService $eggConfigurationService;
private $eggConfigurationService;
/** /**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository * ServerDetailsController constructor.
*/
private $repository;
/**
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
*/
private $configurationStructureService;
/**
* ServerConfigurationController constructor.
*/ */
public function __construct( public function __construct(
ServerRepository $repository, ServerRepository $repository,
ServerConfigurationStructureService $configurationStructureService, ServerConfigurationStructureService $configurationStructureService,
EggConfigurationService $eggConfigurationService, EggConfigurationService $eggConfigurationService
NodeRepository $nodeRepository
) { ) {
$this->eggConfigurationService = $eggConfigurationService;
$this->repository = $repository; $this->repository = $repository;
$this->configurationStructureService = $configurationStructureService; $this->configurationStructureService = $configurationStructureService;
$this->eggConfigurationService = $eggConfigurationService;
} }
/** /**
* Returns details about the server that allows Wings to self-recover and ensure * Returns details about the server that allows Wings to self-recover and ensure
* that the state of the server matches the Panel at all times. * that the state of the server matches the Panel at all times.
* *
* @param string $uuid
*
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function __invoke(Request $request, $uuid) public function __invoke(Request $request, string $uuid): JsonResponse
{ {
$server = $this->repository->getByUuid($uuid); $server = $this->repository->getByUuid($uuid);
@ -68,10 +52,8 @@ class ServerDetailsController extends Controller
/** /**
* Lists all servers with their configurations that are assigned to the requesting node. * Lists all servers with their configurations that are assigned to the requesting node.
*
* @return \Pterodactyl\Http\Resources\Wings\ServerConfigurationCollection
*/ */
public function list(Request $request) public function list(Request $request): ServerConfigurationCollection
{ {
/** @var \Pterodactyl\Models\Node $node */ /** @var \Pterodactyl\Models\Node $node */
$node = $request->attributes->get('node'); $node = $request->attributes->get('node');
@ -93,12 +75,9 @@ class ServerDetailsController extends Controller
* do not get incorrectly stuck in installing/restoring from backup states since * do not get incorrectly stuck in installing/restoring from backup states since
* a Wings reboot would completely stop those processes. * a Wings reboot would completely stop those processes.
* *
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function resetState(Request $request) public function resetState(Request $request): Response
{ {
$node = $request->attributes->get('node'); $node = $request->attributes->get('node');
@ -147,6 +126,6 @@ class ServerDetailsController extends Controller
->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP]) ->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP])
->update(['status' => null]); ->update(['status' => null]);
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); return new Response('', JsonResponse::HTTP_NO_CONTENT);
} }
} }

View file

@ -8,21 +8,14 @@ use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Http\Requests\Api\Remote\InstallationDataRequest;
use Pterodactyl\Events\Server\Installed as ServerInstalled; use Pterodactyl\Events\Server\Installed as ServerInstalled;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher; use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
use Pterodactyl\Http\Requests\Api\Remote\InstallationDataRequest;
class ServerInstallController extends Controller class ServerInstallController extends Controller
{ {
/** private ServerRepository $repository;
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository private EventDispatcher $eventDispatcher;
*/
private $repository;
/**
* @var \Illuminate\Contracts\Events\Dispatcher
*/
private $eventDispatcher;
/** /**
* ServerInstallController constructor. * ServerInstallController constructor.
@ -36,16 +29,14 @@ class ServerInstallController extends Controller
/** /**
* Returns installation information for a server. * Returns installation information for a server.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function index(Request $request, string $uuid) public function index(Request $request, string $uuid): JsonResponse
{ {
$server = $this->repository->getByUuid($uuid); $server = $this->repository->getByUuid($uuid);
$egg = $server->egg; $egg = $server->egg;
return JsonResponse::create([ return new JsonResponse([
'container_image' => $egg->copy_script_container, 'container_image' => $egg->copy_script_container,
'entrypoint' => $egg->copy_script_entry, 'entrypoint' => $egg->copy_script_entry,
'script' => $egg->copy_script_install, 'script' => $egg->copy_script_install,
@ -55,12 +46,10 @@ class ServerInstallController extends Controller
/** /**
* Updates the installation state of a server. * Updates the installation state of a server.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
*/ */
public function store(InstallationDataRequest $request, string $uuid) public function store(InstallationDataRequest $request, string $uuid): Response
{ {
$server = $this->repository->getByUuid($uuid); $server = $this->repository->getByUuid($uuid);
@ -76,6 +65,6 @@ class ServerInstallController extends Controller
$this->eventDispatcher->dispatch(new ServerInstalled($server)); $this->eventDispatcher->dispatch(new ServerInstalled($server));
} }
return new JsonResponse([], Response::HTTP_NO_CONTENT); return new Response('', Response::HTTP_NO_CONTENT);
} }
} }

View file

@ -6,7 +6,6 @@ use Carbon\CarbonImmutable;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Models\Allocation; use Pterodactyl\Models\Allocation;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Pterodactyl\Models\ServerTransfer; use Pterodactyl\Models\ServerTransfer;
@ -21,35 +20,12 @@ use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
class ServerTransferController extends Controller class ServerTransferController extends Controller
{ {
/** private ConnectionInterface $connection;
* @var \Illuminate\Database\ConnectionInterface private ServerRepository $repository;
*/ private DaemonServerRepository $daemonServerRepository;
private $connection; private DaemonTransferRepository $daemonTransferRepository;
private ServerConfigurationStructureService $configurationStructureService;
/** private NodeJWTService $jwtService;
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/**
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
*/
private $daemonServerRepository;
/**
* @var \Pterodactyl\Repositories\Wings\DaemonTransferRepository
*/
private $daemonTransferRepository;
/**
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
*/
private $configurationStructureService;
/**
* @var \Pterodactyl\Services\Nodes\NodeJWTService
*/
private $jwtService;
/** /**
* ServerTransferController constructor. * ServerTransferController constructor.
@ -73,12 +49,10 @@ class ServerTransferController extends Controller
/** /**
* The daemon notifies us about the archive status. * The daemon notifies us about the archive status.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable * @throws \Throwable
*/ */
public function archive(Request $request, string $uuid) public function archive(Request $request, string $uuid): Response
{ {
$server = $this->repository->getByUuid($uuid); $server = $this->repository->getByUuid($uuid);
@ -122,17 +96,15 @@ class ServerTransferController extends Controller
->notify($server, $data, $server->node, $token->toString()); ->notify($server, $data, $server->node, $token->toString());
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return new Response('', Response::HTTP_NO_CONTENT);
} }
/** /**
* The daemon notifies us about a transfer failure. * The daemon notifies us about a transfer failure.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function failure(string $uuid) public function failure(string $uuid): Response
{ {
$server = $this->repository->getByUuid($uuid); $server = $this->repository->getByUuid($uuid);
@ -142,11 +114,9 @@ class ServerTransferController extends Controller
/** /**
* The daemon notifies us about a transfer success. * The daemon notifies us about a transfer success.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
public function success(string $uuid) public function success(string $uuid): Response
{ {
$server = $this->repository->getByUuid($uuid); $server = $this->repository->getByUuid($uuid);
$transfer = $server->transfer; $transfer = $server->transfer;
@ -155,7 +125,9 @@ class ServerTransferController extends Controller
$server = $this->connection->transaction(function () use ($server, $transfer) { $server = $this->connection->transaction(function () use ($server, $transfer) {
$allocations = [$transfer->old_allocation]; $allocations = [$transfer->old_allocation];
if (!empty($transfer->old_additional_allocations)) { if (!empty($transfer->old_additional_allocations)) {
array_push($allocations, $transfer->old_additional_allocations); foreach ($transfer->old_additional_allocations as $allocation) {
$allocations[] = $allocation;
}
} }
// Remove the old allocations for the server and re-assign the server to the new // Remove the old allocations for the server and re-assign the server to the new
@ -173,7 +145,7 @@ class ServerTransferController extends Controller
}); });
// Delete the server from the old node making sure to point it to the old node so // Delete the server from the old node making sure to point it to the old node so
// that we do not delete it from the new node the server was transfered to. // that we do not delete it from the new node the server was transferred to.
try { try {
$this->daemonServerRepository $this->daemonServerRepository
->setServer($server) ->setServer($server)
@ -183,30 +155,30 @@ class ServerTransferController extends Controller
Log::warning($exception, ['transfer_id' => $server->transfer->id]); Log::warning($exception, ['transfer_id' => $server->transfer->id]);
} }
return new JsonResponse([], Response::HTTP_NO_CONTENT); return new Response('', Response::HTTP_NO_CONTENT);
} }
/** /**
* Release all of the reserved allocations for this transfer and mark it as failed in * Release all of the reserved allocations for this transfer and mark it as failed in
* the database. * the database.
* *
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable * @throws \Throwable
*/ */
protected function processFailedTransfer(ServerTransfer $transfer) protected function processFailedTransfer(ServerTransfer $transfer): Response
{ {
$this->connection->transaction(function () use (&$transfer) { $this->connection->transaction(function () use (&$transfer) {
$transfer->forceFill(['successful' => false])->saveOrFail(); $transfer->forceFill(['successful' => false])->saveOrFail();
$allocations = [$transfer->new_allocation]; $allocations = [$transfer->new_allocation];
if (!empty($transfer->new_additional_allocations)) { if (!empty($transfer->new_additional_allocations)) {
array_push($allocations, $transfer->new_additional_allocations); foreach ($transfer->new_additional_allocations as $allocation) {
$allocations[] = $allocation;
}
} }
Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]); Allocation::query()->whereIn('id', $allocations)->update(['server_id' => null]);
}); });
return new JsonResponse([], Response::HTTP_NO_CONTENT); return new Response('', Response::HTTP_NO_CONTENT);
} }
} }

View file

@ -19,28 +19,17 @@ class SftpAuthenticationController extends Controller
{ {
use ThrottlesLogins; use ThrottlesLogins;
/** private UserRepository $userRepository;
* @var \Pterodactyl\Repositories\Eloquent\UserRepository private ServerRepository $serverRepository;
*/ private GetUserPermissionsService $permissionsService;
private $userRepository;
/** /**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository * SftpAuthenticationController constructor.
*/
private $serverRepository;
/**
* @var \Pterodactyl\Services\Servers\GetUserPermissionsService
*/
private $permissionsService;
/**
* SftpController constructor.
*/ */
public function __construct( public function __construct(
GetUserPermissionsService $permissionsService,
UserRepository $userRepository, UserRepository $userRepository,
ServerRepository $serverRepository ServerRepository $serverRepository,
GetUserPermissionsService $permissionsService
) { ) {
$this->userRepository = $userRepository; $this->userRepository = $userRepository;
$this->serverRepository = $serverRepository; $this->serverRepository = $serverRepository;
@ -81,8 +70,14 @@ class SftpAuthenticationController extends Controller
['username', '=', $connection['username']], ['username', '=', $connection['username']],
]); ]);
if ($request->input('type') === 'publicKey') {
$verified = true;
} else {
$verified = password_verify($request->input('password'), $user->password);
}
$server = $this->serverRepository->getByUuid($connection['server'] ?? ''); $server = $this->serverRepository->getByUuid($connection['server'] ?? '');
if (!password_verify($request->input('password'), $user->password) || $server->node_id !== $node->id) { if (!$verified || $server->node_id !== $node->id) {
$this->incrementLoginAttempts($request); $this->incrementLoginAttempts($request);
throw new HttpForbiddenException('Authorization credentials were not correct, please try again.'); throw new HttpForbiddenException('Authorization credentials were not correct, please try again.');
@ -99,9 +94,8 @@ class SftpAuthenticationController extends Controller
$server->validateCurrentState(); $server->validateCurrentState();
return new JsonResponse([ return new JsonResponse([
'ssh_keys' => $user->sshKeys->pluck('public_key')->toArray(),
'server' => $server->uuid, 'server' => $server->uuid,
// Deprecated, but still needed at the moment for Wings.
'token' => '',
'permissions' => $permissions ?? ['*'], 'permissions' => $permissions ?? ['*'],
]); ]);
} }

View file

@ -89,12 +89,11 @@ abstract class AbstractLoginController extends Controller
$this->auth->guard()->login($user, true); $this->auth->guard()->login($user, true);
return JsonResponse::create([ return new JsonResponse([
'data' => [ 'complete' => true,
'complete' => true, 'methods' => [],
'intended' => $this->redirectPath(), 'intended' => $this->redirectPath(),
'user' => $user->toVueObject(), 'user' => $user->toReactObject(),
],
]); ]);
} }

View file

@ -4,62 +4,34 @@ namespace Pterodactyl\Http\Controllers\Auth;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Illuminate\Auth\AuthManager; use Illuminate\Auth\AuthManager;
use Illuminate\Http\JsonResponse;
use PragmaRX\Google2FA\Google2FA; use PragmaRX\Google2FA\Google2FA;
use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Encryption\Encrypter; use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest; use Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest;
use Illuminate\Contracts\Cache\Repository as CacheRepository; use Illuminate\Contracts\Cache\Repository as CacheRepository;
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
use Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository;
class LoginCheckpointController extends AbstractLoginController class LoginCheckpointController extends AbstractLoginController
{ {
/** private CacheRepository $cache;
* @var \Illuminate\Contracts\Cache\Repository private Encrypter $encrypter;
*/ private Google2FA $google2FA;
private $cache;
/**
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
*/
private $repository;
/**
* @var \PragmaRX\Google2FA\Google2FA
*/
private $google2FA;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
private $encrypter;
/**
* @var \Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository
*/
private $recoveryTokenRepository;
/** /**
* LoginCheckpointController constructor. * LoginCheckpointController constructor.
*/ */
public function __construct( public function __construct(
AuthManager $auth, AuthManager $auth,
Encrypter $encrypter,
Google2FA $google2FA,
Repository $config, Repository $config,
CacheRepository $cache, CacheRepository $cache,
RecoveryTokenRepository $recoveryTokenRepository, Encrypter $encrypter,
UserRepositoryInterface $repository Google2FA $google2FA
) { ) {
parent::__construct($auth, $config); parent::__construct($auth, $config);
$this->google2FA = $google2FA;
$this->cache = $cache; $this->cache = $cache;
$this->repository = $repository;
$this->encrypter = $encrypter; $this->encrypter = $encrypter;
$this->recoveryTokenRepository = $recoveryTokenRepository; $this->google2FA = $google2FA;
} }
/** /**
@ -72,13 +44,14 @@ class LoginCheckpointController extends AbstractLoginController
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException * @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException * @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException * @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
* @throws \Exception
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
*/ */
public function __invoke(LoginCheckpointRequest $request): JsonResponse public function __invoke(LoginCheckpointRequest $request)
{ {
if ($this->hasTooManyLoginAttempts($request)) { if ($this->hasTooManyLoginAttempts($request)) {
$this->sendLockoutResponse($request); $this->sendLockoutResponse($request);
return;
} }
$token = $request->input('confirmation_token'); $token = $request->input('confirmation_token');
@ -88,11 +61,13 @@ class LoginCheckpointController extends AbstractLoginController
} catch (ModelNotFoundException $exception) { } catch (ModelNotFoundException $exception) {
$this->incrementLoginAttempts($request); $this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse( $this->sendFailedLoginResponse(
$request, $request,
null, null,
'The authentication token provided has expired, please refresh the page and try again.' 'The authentication token provided has expired, please refresh the page and try again.'
); );
return;
} }
// Recovery tokens go through a slightly different pathway for usage. // Recovery tokens go through a slightly different pathway for usage.
@ -111,8 +86,7 @@ class LoginCheckpointController extends AbstractLoginController
} }
$this->incrementLoginAttempts($request); $this->incrementLoginAttempts($request);
$this->sendFailedLoginResponse($request, $user, !empty($recoveryToken) ? 'The recovery token provided is not valid.' : null);
return $this->sendFailedLoginResponse($request, $user, !empty($recoveryToken) ? 'The recovery token provided is not valid.' : null);
} }
/** /**

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