Compare commits

...

1000 Commits

Author SHA1 Message Date
Lance Pioch
4eeefab6c6 Delete allocation exceptions 2024-10-20 15:50:40 -04:00
Lance Pioch
a2108c3d91 Various improvements 2024-10-20 15:23:46 -04:00
Lance Pioch
4f5e9a6c30 Merge branch 'main' into issue/68
# Conflicts:
#	app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
#	app/Filament/Resources/ServerResource/Pages/EditServer.php
#	app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
#	app/Filament/Resources/UserResource/Pages/EditProfile.php
#	app/Models/Node.php
#	app/Models/Objects/DeploymentObject.php
#	app/Services/Allocations/AssignmentService.php
#	app/Services/Servers/ServerCreationService.php
#	app/Services/Servers/TransferServerService.php
#	pint.json
2024-10-20 15:14:08 -04:00
Lance Pioch
bcbacb47cd Fix #606 - Prevent database hosts bulk selection if host has any databases (#640)
* Prevent hosts with databases from being selected for bulk actions

* Add icons

* Update input to select

* Update app/Filament/Resources/DatabaseHostResource/Pages/ListDatabaseHosts.php

* Add placeholder
2024-10-20 14:20:32 -04:00
Lance Pioch
e9f6fbadd4 Merge pull request #638 from pelican-dev/lance/pint-fixes
Reenable Disabled Pint Rules
2024-10-20 11:59:20 -04:00
Lance Pioch
c621d2dad5 Remove superfluous doc block 2024-10-20 11:55:47 -04:00
Lance Pioch
64943aa50c Merge branch 'main' into lance/pint-fixes 2024-10-20 11:53:10 -04:00
Lance Pioch
020e41cbbc Merge pull request #639 from pelican-dev/lance/phpstan-return-types
Enforce return and parameter types
2024-10-20 11:50:48 -04:00
Lance Pioch
e162374e15 Add return types 2024-10-20 11:41:46 -04:00
Lance Pioch
81c75f7966 Merge branch 'main' into lance/phpstan-return-types 2024-10-20 11:39:04 -04:00
Lance Pioch
2be8168468 Merge pull request #637 from pelican-dev/lance/enforce-di
Enforce Dependency Injection
2024-10-20 10:03:14 -04:00
Lance Pioch
465a372000 Merge pull request #641 from pelican-dev/lance/fix-node-create-redirect
Redirect to configuration file tab after creating the node
2024-10-20 10:00:53 -04:00
Lance Pioch
f0c536c045 Merge pull request #642 from pelican-dev/lance/fix-installer-redirect
Redirect to Admin Panel Dashboard after installer finishes
2024-10-20 10:00:37 -04:00
Lance Pioch
6a8e630444 Redirect to Admin Panel Dashboard after installer finishes 2024-10-19 22:16:55 -04:00
Lance Pioch
71aed151d9 Redirect to configuration file tab after creating the node 2024-10-19 22:11:24 -04:00
Lance Pioch
bb5955cff4 Have to make this match the trait 2024-10-19 21:19:59 -04:00
Lance Pioch
38be89a71e Pint 2024-10-19 21:16:33 -04:00
Lance Pioch
deb6603840 Revert "Add concat_space rule"
This reverts commit 96acd268be.
2024-10-19 21:14:41 -04:00
Lance Pioch
c7a307af6e Enforce return and parameter types 2024-10-19 21:02:49 -04:00
notCharles
8740f0f645 Change MaxWidth 2024-10-19 18:52:08 -04:00
Lance Pioch
466f9f7edc Add phpdoc_separation rule 2024-10-19 18:46:05 -04:00
Lance Pioch
d21740d458 Add phpdoc_align rule 2024-10-19 18:42:23 -04:00
Lance Pioch
1bf6a880fb Add nullable_type_declaration_for_default_null_value rule 2024-10-19 18:41:08 -04:00
Lance Pioch
96acd268be Add concat_space rule 2024-10-19 18:30:34 -04:00
Lance Pioch
c0a41acf1f Add class_attributes_separation 2024-10-19 18:29:44 -04:00
Lance Pioch
75e89b2d4c Prevent double ci checks 2024-10-19 17:25:02 -04:00
Lance Pioch
54ea55d426 Enforce DI 2024-10-19 17:22:03 -04:00
Lance Pioch
1aa51d15f0 Resolve this for now 2024-10-18 22:47:25 -04:00
Lance Pioch
3d8847a508 Resolve phpstan issues 2024-10-18 22:46:12 -04:00
Lance Pioch
4fa49ae915 This is now an array 2024-10-18 22:46:04 -04:00
Lance Pioch
a42cb193a3 Thank you merge conflict 2024-10-18 22:45:54 -04:00
Lance Pioch
50ffaf4d01 Fix tests 2024-10-18 22:38:06 -04:00
Lance Pioch
804ff64a71 Force register the validation rule 2024-10-18 22:27:18 -04:00
Lance Pioch
c2f6842f64 Smarter check 2024-10-18 22:17:15 -04:00
Lance Pioch
455d0543f1 Fix double test runners 2024-10-18 22:17:06 -04:00
Lance Pioch
97a4601150 Update laravel framework 2024-10-18 21:40:17 -04:00
Lance Pioch
2cc4a42905 This is now an array 2024-10-18 21:37:03 -04:00
Lance Pioch
5353d38302 Merge branch 'main' into issue/68
# Conflicts:
#	app/Filament/Resources/NodeResource/Pages/CreateNode.php
#	app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
#	app/Filament/Resources/NodeResource/RelationManagers/NodesRelationManager.php
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
#	app/Filament/Resources/ServerResource/Pages/EditServer.php
#	app/Filament/Resources/ServerResource/Pages/ListServers.php
#	app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
#	app/Filament/Resources/UserResource/RelationManagers/ServersRelationManager.php
#	app/Transformers/Api/Client/ServerTransformer.php
#	composer.lock
#	config/panel.php
2024-10-18 21:18:48 -04:00
Boy132
207d875df8 Fix default value for dns check on EditNode (#635) 2024-10-18 08:24:49 +02:00
Boy132
ff0215afed Add permission check to delete button on EditServer (#633) 2024-10-18 08:24:14 +02:00
Boy132
f357c9501f Auto-check eggs for update (#620)
* add command to check eggs for update

* add "update" button to ListEggs

* fix "unset"

* rename class

* add confirmation modal to update button
2024-10-15 22:54:06 +02:00
Boy132
71116e81ba Cleanup .env.example and configs (#624)
* add back some configs to add some defaults

* cleanup .env.example
2024-10-15 22:37:05 +02:00
Boy132
f2063d7506 Follow up installer fixes (#621)
* enable installer on docker first run

* add SESSION_COOKIE to compose file

* `APP_ENVIRONMENT_ONLY` is long gone

* session env vars no longer needed after #624

* set defaults to null if sqlite is selected
2024-10-15 22:36:35 +02:00
Boy132
c5c05150d8 Remove no longer needed View::share (#625)
* remove no longer needed `View::share`

* hardcode values so the old admin area doesn't break

* add todo comment
2024-10-15 22:35:59 +02:00
Michael (Parker) Parker
214eb5874f Merge pull request #626 from RMartinOscar/patch-1
Fix docker build workflow
2024-10-14 18:49:01 -04:00
MartinOscar
b14f6e1645 Update Dockerfile 2024-10-14 23:05:47 +02:00
RMartinOscar
04b251d125 Disable Caddy admin endpoint 2024-10-14 20:52:05 +00:00
MartinOscar
5f9ee09ebd Add yarn timeout & cleanup 2024-10-14 22:13:01 +02:00
Michael (Parker) Parker
2fb85f8236 Merge pull request #598 from Freddo3000/feature/docker-workflow
Add Docker build/publish action
2024-10-14 09:38:19 -04:00
Fredrik Falk
4eba5b3f7a Update .github/workflows/docker-publish.yml
Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-10-13 15:10:39 +02:00
Charles
f95ba6447c Add Filament Optimize (#619) 2024-10-12 10:58:18 -04:00
Boy132
c0eedc16e0 Update web installer (#614)
* update web installer

* make sure we have a user

* save SESSION_SECURE_COOKIE as text so it's written correctly to the .env

* set `SESSION_COOKIE` so session doesn't expire when changing the app name

* Allow enter to go to next step

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-10-12 10:19:52 -04:00
Boy132
3c5da1cd70 Replace all number_format with Number::format (#617) 2024-10-12 16:12:56 +02:00
Lance Pioch
8638e53f2b Merge pull request #601 from RMartinOscar/issue/600
Add can check to fix #600
2024-10-08 17:58:54 -04:00
Boy132
3ec90264bd Update API for roles (#611)
* remove `guard_name` from api and add id to transformer

* disallow update/ delete for root admin role via api

* disallow assigning root admin via api

* add api to remove user roles

* fix assignRoles & removeRoles
2024-10-08 23:46:28 +02:00
Boy132
e23a4a667a Fix escaping for EnvironmentWriterTrait (#610)
* fix escaping for EnvironmentWriterTrait

* remove alphaNum from app name field

* add test for `'` escaping
2024-10-08 23:46:06 +02:00
Boy132
a946669dc8 Add warning about database data to database settings command (#612) 2024-10-08 23:45:50 +02:00
MartinOscar
6a8ff1a186 Update app/Filament/Resources/ServerResource/Pages/ListServers.php
Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-10-07 18:50:57 +02:00
Boy132
b003404aea Collection of small admin area changes (#604)
* enable tags for nodes

* update icon for cpu column

* disable inline for "force outgoing ip" label

* change label for database hosts resource

* add custom empty state for database hosts & api keys

* add icons to egg tabs

* fix typo

* rename node "Automatic Allocation" to avoid confusion

* run code cleanup

* remove regex for node name

* only check count for application api keys

* replace "New" with "Create"

* change sidebar width to fit "Database Hosts"
2024-10-04 01:15:08 +02:00
MartinOscar
45b73debc2 Switch to authorize
Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-10-02 08:03:48 +00:00
RMartinOscar
329a3993c1 Add can check 2024-10-02 05:13:23 +00:00
Fredrik Falk
da7cba3203 Add docker-publish.yml workflow 2024-10-01 17:03:48 +02:00
Charles
6c205a744d Enable spa (#594) 2024-10-01 04:58:45 -04:00
Charles
e78f7bc054 Just some houseKeeping (#593)
* Just some houseKeeping

* ... pint
2024-10-01 04:37:21 -04:00
Boy132
12a189f585 Remove old queue worker args in docker supervisord (#596) 2024-09-30 14:43:35 +02:00
Boy132
af4cba341a Add config option to disable server descriptions for users (#581)
* add config option to disable server descriptions

* only disable server descriptions for users but not for admins

* Add ,

* invert

* unset description in server transformer if disabled

* remove testing leftover

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-09-29 00:35:57 +02:00
Lance Pioch
aafe17174f Do not show installer by default 2024-09-27 18:44:56 -04:00
Lance Pioch
a067419d6e Merge pull request #313 from pelican-dev/issue/311
Docker
2024-09-27 18:01:00 -04:00
Michael (Parker) Parker
6117282909 update to use supervisord
Update the dockerfile to use supervisord
Update supervisord config to use start caddy unless configured not to.
Updated entrypoint to handle caddy skip for supervisord.
2024-09-27 17:36:45 -04:00
Lance Pioch
967d02612d Add cron and queue 2024-09-27 16:50:34 -04:00
Lance Pioch
0cd20eb444 We don’t do this yet 2024-09-27 16:50:18 -04:00
Lance Pioch
4dba73163b Switch this back 2024-09-27 16:50:10 -04:00
Boy132
aab3817244 Fix role permissions model name (#591) 2024-09-27 22:49:01 +02:00
Lance Pioch
1785883c55 Don’t need this anymore 2024-09-27 15:54:00 -04:00
Lance Pioch
4c19144640 Don’t need separate file 2024-09-27 15:53:44 -04:00
Lance Pioch
a8a2668754 Revert the composer lock 2024-09-27 15:43:04 -04:00
Boy132
6734fe3be6 Replace fonts.googleapis.com with fonts.bunny.net (#586) 2024-09-27 21:38:30 +02:00
Charles
ff0cde5152 Auto Login After Install (#585)
* Auto Login After Install

* pint
2024-09-27 15:34:26 -04:00
Lance Pioch
b098d20afb Make this work 2024-09-27 15:34:22 -04:00
Lance Pioch
3ca77765e6 Small installer updates 2024-09-27 15:33:51 -04:00
Lance Pioch
476eccca53 Add mysql 2024-09-27 15:33:18 -04:00
Lance Pioch
f686eda718 Allow absolute path in database file 2024-09-27 15:32:41 -04:00
Lance Pioch
0f58643cf2 Fix order of params 2024-09-27 15:32:31 -04:00
Lance Pioch
83ba05d7fb Update installer 2024-09-27 15:32:22 -04:00
Lance Pioch
66841f5fab Merge pull request #590 from pelican-dev/filament-update
Update Filament to 115
2024-09-27 14:29:20 -04:00
notCharles
c03ef43767 Fix server deletion with databases 2024-09-27 14:28:32 -04:00
notCharles
805461aaf0 Update Filament to 115 2024-09-27 14:04:20 -04:00
Michael Parker
6f15537d77 add ability to skip starting caddy
dockerfile
  cmd updated to just start php-fpm

entrypoint
  now starts caddy unless SKIP_CADDY has been set.

compose file
  updated ports to work properly.
  updated networks to use the correct network.
  added commented port and variable to disable caddy
  added further notes.
2024-09-26 18:29:46 -04:00
Lance Pioch
4fc8d98a0f Revert "wip"
This reverts commit 649e82d0c06f068f08b024a1f8fc4837b488cb3d.
2024-09-26 18:29:46 -04:00
Lance Pioch
9779365432 wip 2024-09-26 18:29:46 -04:00
Lance Pioch
6e998498e3 Update composer 2024-09-26 18:29:45 -04:00
Lance Pioch
7d0b9af21a Add logs volume 2024-09-26 18:29:05 -04:00
Lance Pioch
116175ba60 Store caddy config and certs in a volume 2024-09-26 18:29:05 -04:00
Lance Pioch
1e841ac40d Update variables 2024-09-26 18:29:05 -04:00
Lance Pioch
3401703ccd Use this one primarily 2024-09-26 18:29:05 -04:00
Lance Pioch
f7cb42e008 Remove old one 2024-09-26 18:29:05 -04:00
Lance Pioch
b6e55795c1 Docker 2024-09-26 18:29:05 -04:00
Lance Pioch
17c0041bfd Already have defaults 2024-09-26 18:29:05 -04:00
Lance Pioch
478948c81b Use variables 2024-09-26 18:28:11 -04:00
Lance Pioch
6b706de23d Don’t include this 2024-09-26 18:28:11 -04:00
Lance Pioch
508e1c9645 Add some docker 2024-09-26 18:28:11 -04:00
Boy132
3e7c29d264 Add artisan command for setting up redis (#580) 2024-09-26 21:53:34 +02:00
Lance Pioch
df88d33af4 Update pint 2024-09-21 15:45:22 -04:00
Boy132
fc643f57f9 Admin Roles (#502)
* add spatie/permissions

* add policies

* add role resource

* add root admin role handling

* replace some "root_admin" with function

* add model specific permissions

* make permission selection nicer

* fix user creation

* fix tests

* add back subuser checks in server policy

* add custom model for role

* assign new users to role if root_admin is set

* add api for roles

* fix phpstan

* add permissions for settings page

* remove "restore" and "forceDelete" permissions

* add user count to list

* prevent deletion if role has users

* update user list

* fix server policy

* remove old `root_admin` column

* small refactor

* fix tests

* forgot can checks here

* forgot use

* disable editing own roles & disable assigning root admin

* don't allow to rename root admin role

* remove php bombing exception handler

* fix role assignment when creating a user

* fix disableOptionWhen

* fix missing `root_admin` attribute on react frontend

* add permission check for bulk delete

* rename viewAny to viewList

* improve canAccessPanel check

* fix admin not displaying for non-root admins

* make sure non root admins can't edit root admins

* fix import

* fix settings page permission check

* fix server permissions for non-subusers

* fix settings page permission check v2

* small cleanup

* cleanup config file

* move consts from resouce into enum & model

* Update database/migrations/2024_08_01_114538_remove_root_admin_column.php

Co-authored-by: Lance Pioch <lancepioch@gmail.com>

* fix config

* fix phpstan

* fix phpstan 2.0

---------

Co-authored-by: Lance Pioch <lancepioch@gmail.com>
2024-09-21 12:27:41 +02:00
Lance Pioch
906d4a7d28 Use new migration logic 2024-09-19 13:19:47 -04:00
Lance Pioch
9ba8c1df9b Update Laravel framework 2024-09-18 22:22:08 -04:00
Lance Pioch
0a6b846230 Merge branch 'main' into issue/68
# Conflicts:
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
#	app/Filament/Resources/ServerResource/Pages/EditServer.php
#	app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
#	app/Services/Allocations/AssignmentService.php
#	database/Seeders/eggs/minecraft/egg-bungeecord.json
#	database/Seeders/eggs/minecraft/egg-forge-minecraft.json
#	database/Seeders/eggs/minecraft/egg-paper.json
#	database/Seeders/eggs/minecraft/egg-sponge-sponge-vanilla.json
#	database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json
#	database/Seeders/eggs/rust/egg-rust.json
#	database/Seeders/eggs/source-engine/egg-counter-strike-global-offensive.json
#	database/Seeders/eggs/source-engine/egg-custom-source-engine-game.json
#	database/Seeders/eggs/source-engine/egg-garrys-mod.json
#	database/Seeders/eggs/source-engine/egg-insurgency.json
#	database/Seeders/eggs/source-engine/egg-team-fortress2.json
#	database/Seeders/eggs/voice-servers/egg-mumble-server.json
#	database/Seeders/eggs/voice-servers/egg-teamspeak3-server.json
2024-09-18 22:21:55 -04:00
ash
68a0cbbf10 Update placeholders & panel error command (#576) 2024-09-16 10:16:25 -04:00
Charles
8497e8b009 Update egg-bungeecord.json (#571) 2024-09-07 12:45:25 -04:00
notCharles
8c64a4ad55 Make MySQL Happy
MySQL complains when we try to change the rules column to json before we change the data... If we change the data, then change the column its happy. :)
2024-09-07 09:47:39 -04:00
notCharles
49e93c1379 Fix Migration
Fix rule migration reversal.
2024-09-06 15:50:42 -04:00
Boy132
d7b5966e1b Remove required from smtp username (#565) 2024-09-01 17:42:15 +02:00
notCharles
e152efc5f9 Add toggle for starting server after install 2024-08-24 21:05:43 -04:00
notCharles
58307c15a3 App Name AlphaNum
Closes https://github.com/pelican-dev/panel/issues/562
2024-08-24 19:16:33 -04:00
Boy132
40810877e0 Add redis connection check to installer (#556) 2024-08-22 22:20:11 +02:00
Boy132
818781ca66 Fix isViable for Nodes with "unlimited" resources (#559) 2024-08-22 22:19:56 +02:00
Boy132
05477c711f Create missing server variables on EditServer page (#560)
* create missing server variables on editserver page

* remove count check
2024-08-22 22:19:38 +02:00
Boy132
20b06b7b39 Fix variables on CreateServer page (#558) 2024-08-20 22:45:41 +02:00
Boy132
c2b1a98d29 Convert variable "rules" to array (#507)
* convert variable "rules" to array

* allow importing eggs with string rules

* fix tests

* update stock eggs to rules array
2024-08-19 08:33:53 +02:00
notCharles
0ff429215d Revert "Test Runners"
This reverts commit d1ca21de9f.
2024-08-18 11:54:12 -04:00
notCharles
d1ca21de9f Test Runners 2024-08-18 11:41:36 -04:00
Boy132
d0c89b0729 ix installer cache (#554) 2024-08-18 17:23:02 +02:00
Boy132
ffadf9ac16 Clear cache before running migrations (#553) 2024-08-18 16:43:40 +02:00
Boy132
bf23389dba Fix default value for mailgun secret (#552) 2024-08-18 16:11:40 +02:00
MartinOscar
68e24896ae Patch for node 18 (#539) 2024-08-16 16:50:09 -04:00
Boy132
1864fff04f Update default image for new eggs (#540) 2024-08-16 22:44:12 +02:00
Boy132
155f2d6476 Add migration to fix allocations server_id foreign key (#542)
* add migration to fix allocations server_id foreign key

* fix the fix...
2024-08-13 19:43:16 +02:00
notCharles
bad5409d9c Fix saving SMTP without encryption 2024-08-10 19:39:41 -04:00
notCharles
3158bdfef8 Fix Single Egg Import 2024-08-10 18:20:21 -04:00
Boy132
1fba700096 Improve error handling for Installer (#532)
* make sure migrations ran

* add loading indicator to finish button

* make error notification persistent

* fix migration checker

* cleanup traits
2024-08-09 08:23:03 +02:00
MartinOscar
7f8fb3f650 Patch Env CLI (#528)
* Remove unused option

* Add redis user

* Adapt lang

* Change default redis username

* Cleanup

* Update app/Traits/Commands/RequestRedisSettingsTrait.php

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>

---------

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-08-08 17:59:28 -04:00
MartinOscar
d6e0421aaf Update StoreNodeRequest.php (#531) 2024-08-08 17:59:16 -04:00
MartinOscar
e8e1958969 Make default favicon path absolute to avoid 404 on admin (#529) 2024-08-06 06:31:52 -04:00
Boy132
2e094605e9 Round memory, swap and disk limits for wings (#523) 2024-08-04 22:21:23 +02:00
Boy132
953ee940aa Installer followup (#519)
* remove queue worker service creation from installer

* auto check redis
2024-08-04 18:53:54 +02:00
Boy132
496eaaaf83 Web Installer (#504)
* simplify setup command

* add installer page

* add route for installer

* adjust gitignore

* set colors globally

* add "unsaved data changes" alert

* add helper method to check if panel is installed

* make nicer

* redis username isn't required

* bring back db settings command

* store current date in "installed" file

* only redirect if install was successfull

* remove fpm requirement

* change "installed" marker to env variable

* improve requirements step

* add commands to change cache, queue or session drivers respectively

* removed `grouped` for better mobile view
2024-08-03 21:13:17 +02:00
MartinOscar
18cf6e9338 Update SetupTOTPDialog.tsx (#518) 2024-07-31 15:10:58 -04:00
Charles
525a106e81 Change TextArea -> Textarea...
Makes no sense as we have TextInput, TagsInput and KeyValue... But TextArea is an issue...
2024-07-30 14:12:29 -04:00
Charles
d22f975684 More Mobile UI
Closes https://github.com/pelican-dev/panel/issues/512
2024-07-30 12:58:16 -04:00
Charles
c4864feaa5 Whoops 2024-07-30 10:45:12 -04:00
Charles
b7b72d7336 Merge branch 'main' of https://github.com/pelican-dev/panel 2024-07-30 10:43:30 -04:00
Charles
686c4375bc Layout fix for mobile 2024-07-30 10:43:24 -04:00
Boy132
3f40256f8b Settings page followup (#514)
* remove group for toggle buttons

* fix default for APP_DEBUG

* correctly handle bool values

* fix pint

* small cleanup for example .env
2024-07-30 16:07:20 +02:00
Boy132
a58e159478 Settings page (#486)
* remove old settings stuff

* add basic settings page

* add some settings

* add "test mail" button

* fix mail fields not updating

* fix phpstan

* fix default for "top navigation"

* force toggle buttons to be bool

* force toggle to be bool

* add class to view to allow customization

* add mailgun settings

* add notification settings

* add timeout settings

* organize tabs into sub-functions

* add more settings

* add backup settings

* add sections to mail settings

* add setting for trusted_proxies

* fix unsaved data alert not showing

* fix clear action

* Fix clear action v2

TagsInput expects an array, not a string, fails on saving when using `''`

* Add App favicon

* Remove defaults, collapse misc sections

* Move Save btn, Add API rate limit

* small cleanup

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-07-29 12:14:24 +02:00
Boy132
d89af243a8 Fix user search on "create server" (#508) 2024-07-29 12:13:29 +02:00
Boy132
bddd6af8af Fix user deletion in no interactive mode (#506) 2024-07-29 12:13:08 +02:00
MartinOscar
e1bdf95971 Update SetupTOTPDialog.tsx (#476) 2024-07-29 05:58:20 -04:00
Lance Pioch
465a03bf0e Update readme.md 2024-07-24 20:10:45 -04:00
Boy132
2c2e52b18a fix phpstan (#503) 2024-07-23 11:32:32 +02:00
notCharles
fcef8d69ae Remove breadcrumbs 2024-07-20 19:15:01 -04:00
notCharles
8662806dfd Fix 500 if update url is blank 2024-07-20 18:51:38 -04:00
MartinOscar
acf43f2826 Ability to create allocations on EditServer page (#494)
* Ability to create allocation on edit page + Ability to assign allocation to server on creation

* Disable dehydrate for readonly

* set these to false

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-07-20 11:38:34 -04:00
Boy132
dfba8e3993 Command to cleanup docker images (#495)
* add command to cleanup docker images

* automatically cleanup images daily

* fix request

* fix empty check

* run pint
2024-07-20 17:23:03 +02:00
Boy132
56484a2282 Increase guzzle timeout when running tests (#485)
* increase guzzle timeout when running tests

* catch correct exception
2024-07-20 17:18:45 +02:00
MartinOscar
56b4938dc2 Fix #489 (#490)
* Fix #489

* Update app/Filament/Resources/NodeResource/Pages/EditNode.php

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>

* Update app/Filament/Resources/NodeResource/Pages/EditNode.php

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>

* Update app/Filament/Resources/NodeResource/Pages/EditNode.php

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>

---------

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-07-17 16:22:12 -04:00
Boy132
10806d6d6b Fix SQLite foreign keys (#478)
* start migration to fix sqlite foreign keys

* add remaining foreign keys

* add ".sqlite.backup" files to gitignore
2024-07-17 14:43:04 +02:00
Boy132
a04937d698 Fix PORT_FLOOR check and CIDR_MAX_BITS in AssignmentService (#491)
* fix max cidr

* fix port floor
2024-07-17 13:01:13 +02:00
Boy132
8a3d67ada0 Fix update egg from url (#492) 2024-07-17 13:00:54 +02:00
Charles
833ae30e59 Add timeouts (#483)
* Add timeouts

Add Timeouts to github call.

* use config value
2024-07-15 19:09:52 -04:00
Charles
1fdff43ae7 Add Node CPU/Memory Graphs (#459)
* Update Node Stats

Soon TM

* Update

* Make these smaller

* Change graphs

* Remove this.

Didn't work anyways.

* Update Graphs

* Use User TZ and config var

* Fix math

* Change to per thread.
2024-07-14 16:48:14 -04:00
Boy132
bb7c0e0e66 Add "Delete files" task (#470)
* started "delete files" task

* add logic to DeleteFilesService

* add frontend

* make nicer

* move description to right place
2024-07-10 09:25:15 +02:00
Boy132
447e889a4f Fix default timestamp for activity logs (#468)
* fix default timestamp for activity logs

* fix phpstan
2024-07-10 08:36:24 +02:00
Exotical
1c1c8c0cc6 Fix client Activity tab issues; fixes #465 (#466)
* Remove deploy.locations from validator

* Change location data to optional for backwards compat

* Better styling

* Add back comma to follow coding style

* Remove EventServiceProvider from providers file

Fixes duplicated auth messages in the client Activity tab.

* Add null check on $model->actor

Prevents the client Activity tab page from breaking when an authentication attempt has failed.

* Proper type checking on $model->actor

Chose instanceof as it seems to be the best in terms of type safety.

Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com>

* Revert removal of EventServiceProvider

* Remove subscription of AuthenticationListener

* Remove subscriptions for auth events

* Remove unused import Dispatcher

* Remove unused import AuthenticationListener

---------

Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-07-09 21:30:12 -07:00
notCharles
7dad2d0e42 Fix #464 2024-07-07 19:33:25 -04:00
notCharles
212c93c2ba Fix #462 2024-07-05 18:24:07 -04:00
Boy132
7557dc1c8d Restart queue worker when changing email settings (#457) 2024-07-05 16:17:35 +02:00
Boy132
07735464c7 Add contributing guide (#460) 2024-07-05 01:15:45 +02:00
Lance Pioch
aff9f4ea37 Don’t need this anymore 2024-07-04 18:56:28 -04:00
Lance Pioch
f2754c3cb1 Fix mappings 2024-07-04 13:50:48 -04:00
Lance Pioch
8b86707150 Wip 2024-07-04 13:11:13 -04:00
Lance Pioch
233fd50b2b Don’t include this by default 2024-07-04 10:24:46 -04:00
Lance Pioch
3a76fb1c79 Revert "Add custom component"
This reverts commit 0f798e5edb.
2024-07-03 22:23:18 -04:00
notCharles
8ba15538a9 Fix ToolTip 2024-07-03 16:33:32 -04:00
Lance Pioch
e76630b7f3 Merge branch 'issue/68' of github.com:pelican-dev/panel into issue/68 2024-07-03 12:14:09 -04:00
Lance Pioch
0f798e5edb Add custom component 2024-07-03 12:13:46 -04:00
Lance Pioch
a9c7eeddde Add the any ip address 2024-07-03 12:13:25 -04:00
Charles
c115c6ddf5 Add Update URL to stock eggs 2024-07-03 10:36:34 -04:00
Charles
160ea1ed50 Enable Update URL
Since importing an egg via url was added, we can enable this.
2024-07-03 10:27:57 -04:00
Charles
11feef4f8c Update Eggs 2024-07-03 06:32:30 -04:00
notCharles
bebc410eda Update these 2024-07-02 19:36:53 -04:00
notCharles
ec0fa3c913 Revert "Also nuke this"
This reverts commit 4574821ed8.
2024-07-02 18:32:32 -04:00
notCharles
4574821ed8 Also nuke this 2024-07-02 16:50:39 -04:00
Lance Pioch
d71b1a4710 Merge branch 'issue/68' of github.com:pelican-dev/panel into issue/68 2024-07-02 14:39:57 -04:00
Charles
d9922e86f2 This has annoyed me... 2024-07-02 14:30:11 -04:00
Lance Pioch
9d9e4adbbd Don’t need this anymore 2024-07-02 14:27:19 -04:00
Lance Pioch
6b104e3331 Allow the server port to be selected if it also may only exist in the config 2024-07-02 14:03:05 -04:00
MartinOscar
7164951085 Update EditServer.php (#455) 2024-07-02 13:31:35 -04:00
Lance Pioch
f2eca17480 Use constants 2024-07-02 11:50:46 -04:00
Charles
40721a2cb8 Fix #452
Prob not the best solution, but it works

Closes: https://github.com/pelican-dev/panel/issues/452
2024-07-02 08:01:17 -04:00
MartinOscar
c464b321dd Update EditProfile.php (#454) 2024-07-02 07:05:00 -04:00
Lance Pioch
4c41e659b5 Add main server port and default 2024-07-01 15:13:20 -04:00
Lance Pioch
6238d6dd08 Switch to port rule 2024-07-01 15:12:33 -04:00
Lance Pioch
c45e4edcf6 Allow port rule to be optional 2024-07-01 15:12:03 -04:00
Lance Pioch
4273880126 Better helper text 2024-07-01 15:11:56 -04:00
Lance Pioch
d86843977b Run pint 2024-07-01 14:55:17 -04:00
Lance Pioch
5b468c21ae Merge branch 'main' into issue/68
# Conflicts:
#	app/Filament/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php
#	app/Filament/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php
#	app/Filament/Resources/EggResource/RelationManagers/ServersRelationManager.php
#	app/Filament/Resources/NodeResource.php
#	app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
#	app/Filament/Resources/NodeResource/RelationManagers/NodesRelationManager.php
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
2024-07-01 14:54:12 -04:00
MartinOscar
0f8c27a297 Update ContainerStatus add Starting|Stopping|Default (#449)
* Update ContainerStatus add Starting

* Update ContainerStatus add Stopping

* Update ContainerStatus add Default

* Update Icons, PHPStan

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-06-30 10:13:08 -04:00
notCharles
40819cf171 Use correct action 2024-06-29 18:02:53 -04:00
Boy132
133b94ab08 Add missing user timezone stuff (#446) 2024-06-29 23:42:46 +02:00
Charles
82c0568129 Reduce Reuse (#443)
* Reduce Reuse

Reduce the repetitiveness of \Form\Component\Blah along with all the others...

* PHPStan Fix
2024-06-29 17:38:18 -04:00
MartinOscar
75d35e6ee8 Add ability to create User on Create Server page (#442)
* Add ability to create a User on Create Server page

* pint

* Fix creating user without password

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-06-29 17:18:42 -04:00
Boy132
2a740b43e6 Fix schedules running every minute (second try) (#444) 2024-06-29 23:16:28 +02:00
Charles
818a8a42ad Closes #439 (#440)
* Update readme.md

* Update readme.md

---------

Co-authored-by: Lance Pioch <git@lance.sh>
2024-06-27 19:09:50 -04:00
Lance Pioch
67dbf772d5 Separate these out in the navigation (#434) 2024-06-27 05:54:21 -04:00
Lance Pioch
68ef0a1d0a Fix these 2024-06-26 22:03:26 -04:00
Lance Pioch
f6122f919a Rename this because order matters 2024-06-26 22:03:23 -04:00
Lance Pioch
4f10ec2c20 Fix name 2024-06-26 21:56:01 -04:00
Lance Pioch
45fcc2a09a Merge branch 'main' into issue/68
# Conflicts:
#	app/Filament/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php
#	app/Filament/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
#	app/Filament/Resources/ServerResource/Pages/EditServer.php
#	app/Filament/Resources/ServerResource/Pages/ListServers.php
#	app/Http/Requests/Admin/Node/AllocationFormRequest.php
#	app/Http/Requests/Api/Application/Allocations/StoreAllocationRequest.php
#	app/Models/AuditLog.php
#	app/Models/Server.php
2024-06-26 21:52:05 -04:00
Lance Pioch
19c7b4d044 Clean up 2024-06-26 21:44:06 -04:00
Lance Pioch
7c8b204d13 Remove network tab 2024-06-26 21:42:57 -04:00
Lance Pioch
343a5b81bc Almost done 2024-06-26 21:38:18 -04:00
Lance Pioch
efb834c8f7 Combine Server states and statuses and resolve #362 (#417)
* Simplify states and statuses and resolve #362

# Conflicts:
#	app/Models/Server.php

* Move Random button...

Moves button to the info tab

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-06-25 20:30:06 -04:00
Lance Pioch
cf37994c3b Allow user to switch time zones (#332)
* Description not required

* Overwrite to use user’s time zone

* Allow users to change time zones

* Update app/Filament/Resources/UserResource/Pages/EditProfile.php

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>

* Pint fix

---------

Co-authored-by: Charles <charles@pelican.dev>
Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-06-25 20:17:08 -04:00
Boy132
fc92a87993 Add connection test to database hosts (#410)
* add connection test to database hosts

* fix phpstan
2024-06-25 18:42:55 -04:00
Lance Pioch
f459987458 Allow manual (force) updates to eggs (#427)
* Update labels

* Add force imports

* Not multiple

* pint + changes

---------

Co-authored-by: Charles <charles@pelican.dev>
2024-06-25 15:37:12 -04:00
MartinOscar
5290b8f8bb Update ListUsers Prevent bulkdelete of yourself/last admin (#425)
* Update ListUsers.php

* Update ListUsers.php

* Update app/Filament/Resources/UserResource/Pages/ListUsers.php

---------

Co-authored-by: Lance Pioch <lancepioch@gmail.com>
2024-06-24 21:41:42 -04:00
MartinOscar
e08cbdecd4 Update EnvironmentWriterTrait to allow empty string in CLI (#421) 2024-06-24 13:52:52 -04:00
Lance Pioch
755632f9d5 Wip 2024-06-23 22:32:37 -04:00
Lance Pioch
e7ee86a914 Fix whoopsie 2024-06-23 22:31:41 -04:00
Boy132
70c31eef8f Refactor UserTransformers (#423)
* remove AccountTransformer and update UserTransformer (client api) to match UserTransformer (application api)

* rename "toVueObject"

* fix tests

* forgot to rename this

* backwards compat

* fix tests
2024-06-23 16:33:18 +02:00
Boy132
5409532ca1 Fix the fix (#424) 2024-06-23 16:23:11 +02:00
Lance Pioch
eb0bad82e6 Wip 2024-06-22 11:30:08 -04:00
Lance Pioch
a1190c12e0 Add required to editing server variables and fix #413 (#415)
* Add required to editing server variables

* Misplaced
2024-06-21 16:17:44 -04:00
notCharles
42ca4e7fba This never happened... 2024-06-20 16:15:56 -04:00
notCharles
d6b71885ec Add env. to egg upgrader 2024-06-20 16:00:17 -04:00
Boy132
7b0a15e746 Remove hard coded queue name for RunTaskJob (#420) 2024-06-19 16:04:00 +02:00
Boy132
7813b6060c Make oauth nullable & remove middleware from oauth callback (#418)
* make oauth nullable

* fix oauth callback middleware
2024-06-18 22:05:08 +02:00
Lance Pioch
48fd3cc84e Add restore to simplify form 2024-06-18 10:59:56 -04:00
Lance Pioch
d4484f5254 Handle nulls 2024-06-18 10:59:50 -04:00
Lance Pioch
958e8fac8a Simplify states and statuses and resolve #362 2024-06-17 18:18:48 -04:00
Lance Pioch
7986505b99 Don’t report status anymore 2024-06-17 18:15:56 -04:00
Lance Pioch
ba5b81cf2d Show localhost 2024-06-17 18:15:45 -04:00
Lance Pioch
32018399b6 Add server io weight default 2024-06-17 18:15:35 -04:00
Charles
c431775b7e [Create Server] Fix 500 when changing egg
When changing from one egg to nothing, a 500 is displayed due to it expecting startup to have a value
2024-06-17 12:24:52 -04:00
Lance Pioch
6692942f6f Group servers (#412)
* Group servers

# Conflicts:
#	app/Filament/Resources/ServerResource/Pages/ListServers.php

* Can be null apparently

* pint

---------

Co-authored-by: Charles <charles@pelican.dev>
2024-06-17 12:12:56 -04:00
Lance Pioch
48f4c35d0b Can be null apparently 2024-06-17 11:55:32 -04:00
Lance Pioch
f699fd5459 Make ports into badges 2024-06-17 11:46:58 -04:00
Lance Pioch
05573f64dd Group servers 2024-06-17 11:46:40 -04:00
Lance Pioch
3fa714c7e3 Refactor 2024-06-17 10:48:05 -04:00
Lance Pioch
69acc48b5e Use constants 2024-06-17 10:47:55 -04:00
Lance Pioch
9d9720a5a2 Start servers by default 2024-06-17 10:47:43 -04:00
MartinOscar
276b51f477 Remove locationId in MakeNodeCommand (#405)
* Concat + Default

* Concat + Default + Enforce scheme

* fix typo

---------

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-06-17 08:51:04 -04:00
MartinOscar
d4eecdd53d Update OAuth migration (#409) 2024-06-17 07:03:36 -04:00
Senna
d7316c4dfe Dashboard update. Update section (#390)
* Created command

* Pint Fixes

* Removed old upgrade command translations

* Update to Dashboard and linting Dashboard view

* Pint Fixes

* A few small improvements

* Delete modifications to upgrade command

* Revert "Removed old upgrade command translations"

This reverts commit 31315a0d9e.

* Pint Fixes

* Boy132's Suggestions
2024-06-16 17:54:49 -04:00
notCharles
011579451d Merge branch 'main' of https://github.com/pelican-dev/panel 2024-06-16 14:21:33 -04:00
notCharles
6b5b480902 Update database section on EditServer 2024-06-16 14:21:25 -04:00
MartinOscar
87dc8066c9 Update required (#401) 2024-06-16 14:01:27 -04:00
MartinOscar
aa08e774a1 Fix varchar(191) by replacing with 255 #135 (#376)
* Add Nullable

* Edit filament & AppServiceProvider

* Pint

* Patch tests

* Actually patching tests

* Actually patching tests

* Remove length

* Remove defaultStringLength

* Let’s see the differences

---------

Co-authored-by: Lance Pioch <git@lance.sh>
2024-06-16 13:56:18 -04:00
notCharles
482e8ed6b2 Add Databases to Edit Server 2024-06-16 13:50:28 -04:00
Lance Pioch
ff261f9c99 Realism 2024-06-16 13:07:12 -04:00
Lance Pioch
c7bea4f024 This was wrong but somehow worked in sqlite, wut 2024-06-16 12:30:42 -04:00
Lance Pioch
738707b251 Add validation rule 2024-06-16 12:30:28 -04:00
Lance Pioch
a8699704de Fix return 2024-06-16 11:56:21 -04:00
Lance Pioch
d9dc932e07 Remove unused import 2024-06-16 11:55:54 -04:00
Lance Pioch
f57232bc23 Fix tests 2024-06-16 11:50:08 -04:00
Boy132
59bbb63739 Fix queue worker file when using redis (#399) 2024-06-15 23:20:08 +02:00
Charles
f4c3c89c17 Also add that here
Prevent 500's on server create
2024-06-15 12:36:15 -04:00
Charles
fe4e6271fb Set minValue
Closes #397
2024-06-15 12:33:33 -04:00
Boy132
8ee5d6aabd Add missing "search" translations (#393) 2024-06-15 14:46:10 +02:00
Lance Pioch
b24ff8bb26 Unwrap transaction 2024-06-15 07:27:17 -04:00
Lance Pioch
eff8e509ef Wrap in transaction 2024-06-15 07:19:01 -04:00
Lance Pioch
6976fa8989 Try again 2024-06-15 06:57:11 -04:00
Lance Pioch
2b58160da9 Drop it 2024-06-15 06:04:20 -04:00
Lance Pioch
44e0dd3e09 Remove final refs 2024-06-15 06:01:40 -04:00
Lance Pioch
8ea57bc46b Remove unused audit logs 2024-06-15 05:53:29 -04:00
Lance Pioch
459d90e8d1 Revert this 2024-06-15 05:44:53 -04:00
Lance Pioch
a97341f6f2 Cast to int 2024-06-15 05:40:25 -04:00
Lance Pioch
375a64a38e Remove these relationships 2024-06-15 05:40:21 -04:00
Lance Pioch
7c25fc2a9d Revert these doc blocks 2024-06-15 05:39:53 -04:00
Lance Pioch
1a26f5ce9e This goes first 2024-06-15 05:34:56 -04:00
Lance Pioch
b47f40bd13 Remove debug 2024-06-15 05:24:57 -04:00
Lance Pioch
bcb7240ed2 Pint fixes 2024-06-15 05:24:19 -04:00
Lance Pioch
405aa857b1 Reset these for now 2024-06-15 05:23:33 -04:00
Lance Pioch
0bd2935885 Merge branch 'main' into issue/68
# Conflicts:
#	app/Filament/Resources/EggResource/RelationManagers/ServersRelationManager.php
#	app/Filament/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
#	app/Filament/Resources/NodeResource/RelationManagers/NodesRelationManager.php
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
#	app/Filament/Resources/ServerResource/Pages/ListServers.php
#	app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
#	app/Filament/Resources/UserResource/RelationManagers/ServersRelationManager.php
#	app/Models/Allocation.php
#	app/Models/ApiKey.php
#	app/Models/Server.php
#	app/Models/User.php
2024-06-15 05:21:58 -04:00
Lance Pioch
4cba1540ac Add endpoints 2024-06-15 05:14:54 -04:00
Lance Pioch
30051ab0d7 Add tooltips for versions 2024-06-15 05:14:30 -04:00
Boy132
42ecd2951d Update p:info command (#389) 2024-06-14 17:17:49 +02:00
Boy132
7a6edab79a Remove unnecessary json_encode in oauthcontrollers (#391) 2024-06-14 17:17:34 +02:00
Lance Pioch
e15d515f71 Wip 2024-06-14 07:54:07 -04:00
MartinOscar
4f43e9171a Rename OauthController.php to OAuthController.php (#388)
Fixes Class App\Http\Controllers\Base\OAuthController located in ./app/Http/Controllers/Base/OauthController.php does not comply with psr-4 autoloading standard (rule: App\ => ./app). Skipping.
2024-06-13 23:52:53 -04:00
Boy132
5a3c606627 Add OAuth backend (#386)
* add socialite backend

* fix redirect url

* small cleanup

* fix "oauth" type

* changes from review
2024-06-13 21:06:31 +02:00
Lance Pioch
36e2fa8e2b Wip 2024-06-13 11:31:58 -04:00
Boy132
6916b89638 Fill startup & image from egg if missing in request (#384) 2024-06-13 15:48:36 +02:00
Boy132
0da184c56e Get latest Panel & Wings version from github api (#379)
* get latest panel % wings version from github api

* fix tests

* remove unused CdnVersionFetchingException
2024-06-13 08:23:45 +02:00
Boy132
ce1163d387 Hide task delay on frontend if queue driver is set to sync (#375) 2024-06-13 08:23:24 +02:00
Boy132
cd4fc1a95d Add config variable to change between MiB and MB (#374) 2024-06-13 08:22:29 +02:00
Boy132
0c0b468525 Change allowed_ips to non-nullable (#373)
* change `allowed_ips` to non nullable

* fix default value

* show "allowed_ips" input
2024-06-13 08:21:56 +02:00
notCharles
12518bc5d6 Allow more searchable
Allow more columns to be searchable.
2024-06-12 19:30:02 -04:00
Boy132
7c829fb9cf Fix egg export (#380) 2024-06-12 18:18:24 +02:00
Boy132
61f3e965ba Refactor egg services (#358)
* combine importer and updateimport

* integrate egg parser into importer

* remove EggCreationService and EggUpdateService

* run pint

* revert change to composer.json

* use egg exporter directly instead of old admin route
2024-06-11 21:01:31 +02:00
Boy132
10796f8916 Dedicated MariaDB driver (#365)
* dedicated tests for mariadb

* fix migrations

* update database config

* update database setup command
2024-06-11 21:01:14 +02:00
Djordy Koert
1d66d4c320 PHPStan workflow + PHPStan fixes (#339)
* add PHPStan to workflow

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
Co-authored-by: Djordy <djordy@lap-0394-djordy-koert.local>
Co-authored-by: Lance Pioch <lancepioch@gmail.com>
2024-06-11 15:00:51 -04:00
Lance Pioch
e95cd0cd98 Update bounties.md 2024-06-11 14:58:42 -04:00
MartinOscar
46a24a087b Update CreateServer.php (#364) 2024-06-11 14:36:22 -04:00
MartinOscar
f216376265 Update EditProfile (#368)
* Update EditProfile.php
2024-06-11 14:32:55 -04:00
Lance Pioch
6d6b50c27d Create bounties.md 2024-06-11 12:48:04 -04:00
MartinOscar
58bfa12280 Cleanup unused vars (#366)
* Update app/Exceptions/Handler.php

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-06-11 12:22:37 -04:00
MartinOscar
8e5660a1b9 Add default for Automatic Allocation in CreateNode (#367)
* Update CreateNode.php
* Update app/Filament/Resources/NodeResource/Pages/CreateNode.php

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-06-11 12:21:51 -04:00
MartinOscar
beac4cd3f6 Update AllocationsRelationManager.php (#369)
* Update AllocationsRelationManager.php

* Update app/Filament/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php

---------

Co-authored-by: Lance Pioch <lancepioch@gmail.com>
2024-06-11 12:21:13 -04:00
Lance Pioch
510ae3c0df Swap this for now 2024-06-10 21:38:28 -04:00
Lance Pioch
f5edb34873 Some filament changes for ports 2024-06-10 21:32:46 -04:00
Lance Pioch
0895bd2be5 Remove relation managers 2024-06-10 20:35:36 -04:00
Lance Pioch
17bc3de0d0 Rename translation keys 2024-06-10 20:34:46 -04:00
kubi
9184441763 Update release workflow for new parameter format (#363)
action-gh-release combined their artifact uploader action with their create release action.  Combine these into a single step.
2024-06-10 15:08:18 -07:00
kubi
3ac23d1514 Update version of upload release actions in release workflow 2024-06-10 14:55:14 -07:00
kubi
6295ea34de Update tests to use mariadb 10.3
Update version of mariadb used by tests since this is our minimum required version.
2024-06-10 14:45:32 -07:00
kubi
3cadbbc60c Run build on commits 2024-06-10 14:41:02 -07:00
kubi
60c5f826d6 Run build and tests on commits 2024-06-10 14:32:36 -07:00
Boy132
1047e8f948 Make egg list searchable and sortable (#359)
* make egg list searchable and sortable

* run pint
2024-06-10 09:11:12 -04:00
Lance Pioch
4319f24f51 Merge branch 'main' into issue/68
# Conflicts:
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
#	app/Models/Node.php
2024-06-09 15:42:49 -04:00
Lance Pioch
f3501d8b14 Merge pull request #343 from Boy132/phpstan-fixes
Fix remaining phpstan issues for #339
2024-06-09 15:13:44 -04:00
Charles
9114685680 Use Wizard for server/node create pages (#352)
* Update create server flow

* Update create node & buttons

* Remove duplicate

* Composer Update

Update some of the packages <3

* Small adjustments

* pint

---------

Co-authored-by: Lance Pioch <git@lance.sh>
2024-06-09 15:07:33 -04:00
Lance Pioch
9ad113bc61 Move these 2024-06-09 14:18:19 -04:00
Lance Pioch
beadce96f6 Wip 2024-06-09 08:20:31 -04:00
notCharles
8080435eca It's Late... 2024-06-07 22:28:53 -04:00
notCharles
c5824ff26c Whoops.... Fix env replacement...
Somehow this got copy pasta'd and yeh.... its not right...
2024-06-07 22:18:12 -04:00
Charles
dd7a01aa04 Merge pull request #345 from Boy132/show-git-version
Show update info on dashboard & show git commit (when using git)
2024-06-07 20:00:04 -04:00
Boy132
55badb5644 update colors 2024-06-08 00:43:25 +02:00
Boy132
93f059025c show update info on dashboard, show git commit (when using git) 2024-06-08 00:38:46 +02:00
Charles
7be0cd6928 Merge pull request #323 from Boy132/feature/node-sftp-alias
Add alias for node sftp address
2024-06-07 18:04:44 -04:00
Boy132
0156456919 Merge branch 'pelican-dev:main' into feature/node-sftp-alias 2024-06-07 23:49:38 +02:00
Charles
b9d1ce4438 Merge pull request #334 from pelican-dev/issue/297
Better exception handling
2024-06-07 17:46:33 -04:00
Charles
9ce262bf56 Merge pull request #316 from pelican-dev/issue/node-update
Fix Node Updating
2024-06-07 17:44:10 -04:00
notCharles
7ee52affb2 Update token rotation 2024-06-07 17:38:58 -04:00
Charles
93bfe925b9 Merge pull request #333 from pelican-dev/issue/2
Remove unused parameters
2024-06-07 17:32:40 -04:00
Boy132
cc1ac1eba1 Allow importing eggs via url (#344)
* allow importing eggs via url

* refactor

* run pint

* turn back into one button

* fix empty check

* small cleanup

* removed container for tabs

* Update URL function

* Use sys temp

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-06-07 17:31:34 -04:00
Charles
02d24b8a36 Fix the egg variable disaster... (#331)
* Migrations to update existing eggs in db

* Update stock eggs

* Update Eggs on import

* Also update updated versions of eggs that are uploaded

* Redo this..

Tests passed locally.

* Pint & Update replace

* Squash Migrations, simplify logic

* Maybe this way...

* Swap them over to single call

---------

Co-authored-by: Lance Pioch <git@lance.sh>
2024-06-07 16:23:25 -04:00
Charles
16fac3b5c6 Merge pull request #337 from Boy132/fix/schedules-run-every-minute
Fix schedules running every minute
2024-06-07 05:43:56 -04:00
Boy132
6b249b9545 fix tests 2024-06-07 09:17:10 +02:00
Boy132
70fc84309f revert some changes in EditProfile 2024-06-07 09:11:40 +02:00
Boy132
f43fb985a2 fix phpstan in Node and EditProfile 2024-06-07 08:59:00 +02:00
Lance Pioch
b1d7d210fc Allow startup command to change with server variable 2024-06-07 01:00:00 -04:00
Lance Pioch
eb99f53d87 Reset this for now 2024-06-07 00:08:41 -04:00
Lance Pioch
643e4168b9 Add required rule separately 2024-06-06 19:39:46 -04:00
Lance Pioch
51cd7a8e81 Remove unused route files 2024-06-06 16:15:35 -04:00
Lance Pioch
32e96dc0a6 Wip 2024-06-06 15:49:36 -04:00
Lance Pioch
b16a11c365 No longer reserved 2024-06-06 15:49:33 -04:00
Lance Pioch
81f218ddc9 Skip port variables down below 2024-06-06 15:49:23 -04:00
Boy132
91bf38b63d fix schedules running every minute 2024-06-06 15:53:29 +02:00
Charles
e3699f34d8 Merge pull request #336 from Boy132/fix/default-database-path
Use env value instead of config value for database path
2024-06-06 06:09:51 -04:00
Charles
dc3da2dc98 Merge pull request #335 from Boy132/add/mounts-helper-text
Add helper text to mounts on EditServer page
2024-06-06 06:05:27 -04:00
Boy132
d245751c97 use env value instead of config value 2024-06-06 11:59:24 +02:00
Boy132
e0d7a094ab add helper text to mounts 2024-06-06 10:18:05 +02:00
Lance Pioch
3010e3d61e Better default 2024-06-05 23:37:12 -04:00
Lance Pioch
d68e7218a8 Reformat as table 2024-06-05 23:37:09 -04:00
Lance Pioch
a4435a7454 Pint fix 2024-06-05 22:12:53 -04:00
Lance Pioch
df26c4f9f5 Better exception handling 2024-06-05 21:49:09 -04:00
Lance Pioch
6f1de67523 Remove extraneous parameters 2024-06-05 16:03:04 -04:00
Charles
6f009ee126 Remove cli from php
Every workflow run hangs at attempting to add the cli package and adds ~1 min to the workflow.
2024-06-05 14:15:33 -04:00
Boy132
328e159c6b Merge branch 'pelican-dev:main' into feature/node-sftp-alias 2024-06-05 08:47:20 +02:00
Boy132
f9fd426aca change column type to string
Co-authored-by: Lance Pioch <lancepioch@gmail.com>
2024-06-05 08:47:11 +02:00
Lance Pioch
6166fac929 Merge pull request #322 from Boy132/fix/make-user-db-test
Replace DB check in MakeUserCommand
2024-06-04 17:33:47 -04:00
Lance Pioch
4bd1070025 Merge pull request #324 from Boy132/patch-1
Remove maxLength from `variable_value` input
2024-06-04 17:30:16 -04:00
Lance Pioch
2d6e30b646 Merge pull request #326 from Boy132/fix/artisan-queue-again
Another call fix in AppSettingsCommand
2024-06-04 17:29:48 -04:00
Lance Pioch
f61c6b9dc2 Merge pull request #327 from Boy132/patch-2
Fix default sqlite database path in setup command
2024-06-04 17:28:45 -04:00
Lance Pioch
be6f79521e Wip 2024-06-04 16:34:54 -04:00
Lance Pioch
5e29737dc5 Merge pull request #328 from Boy132/fix/pelicanignore
Replace `panelignore` with `pelicanignore`
2024-06-04 15:43:09 -04:00
Lance Pioch
551175862e Better kebab names 2024-06-04 15:38:45 -04:00
Boy132
d996019204 fix eslint 2024-06-04 17:49:04 +02:00
Boy132
91d8dbd084 replace panelignore with pelicanignore 2024-06-04 17:48:02 +02:00
Lance Pioch
dbad5ae9c7 WIP 2024-06-04 11:40:19 -04:00
Boy132
bb03ddda50 listen on all queues 2024-06-04 17:26:19 +02:00
Boy132
1c66681c0e make default sqlite database path relative 2024-06-04 13:26:05 +02:00
Boy132
0728266826 restart queue service if service already exists 2024-06-04 13:14:54 +02:00
Boy132
d81c9faac6 improve prompts 2024-06-04 13:01:52 +02:00
Boy132
cff54f1969 show output when running p:environment:queue-service 2024-06-04 13:01:24 +02:00
Boy132
201563a13b remove maxLength from variable_value input 2024-06-04 11:20:40 +02:00
Boy132
8f2261f6cd add alias for node sftp address 2024-06-04 09:17:36 +02:00
Boy132
29cc92f0dc replace db check in MakeUserCommand 2024-06-04 08:33:54 +02:00
Lance Pioch
33f10cbcb9 Merge pull request #312 from RMartinOscar/patch-1
Update EditUser.php
2024-06-03 10:35:31 -04:00
Lance Pioch
b538532e34 Merge pull request #314 from RMartinOscar/patch-2
Update EditDatabaseHost.php
2024-06-03 10:35:07 -04:00
Lance Pioch
a892821b4f Merge pull request #319 from RMartinOscar/patch-3
Update AllocationsRelationManager to allow big endian
2024-06-03 10:34:27 -04:00
Lance Pioch
5a3b50b31f Apply suggestions from code review 2024-06-03 10:34:08 -04:00
Lance Pioch
51b217571b Merge pull request #320 from Boy132/fix/artisan-call
Fix artisan call in AppSettingsCommand
2024-06-03 10:33:00 -04:00
Boy132
6e75c76c60 cleanup 2024-06-03 13:46:48 +02:00
Boy132
e22c5c3e0a fix artisan call in app settings command 2024-06-03 13:43:11 +02:00
MartinOscar
f3171939a4 Update AllocationsRelationManager.php
Remove useless range order
2024-06-03 07:11:09 +02:00
MartinOscar
189d564f87 Update AllocationsRelationManager.php 2024-06-03 06:30:05 +02:00
MartinOscar
7926f97c8e Update EditDatabaseHost.php 2024-06-03 04:09:36 +02:00
MartinOscar
f4d39c1c68 Update EditDatabaseHost.php 2024-06-03 04:02:31 +02:00
Lance Pioch
6c2d0a2d50 Remove shenanigans 2024-06-02 21:59:12 -04:00
Lance Pioch
768a45bbb8 More updates 2024-06-02 21:55:13 -04:00
MartinOscar
f6899301fd Update EditDatabaseHost.php 2024-06-03 03:54:33 +02:00
MartinOscar
cbb4ef1da2 Update EditUser.php 2024-06-03 03:52:39 +02:00
notCharles
f6ef76d98e Disable delete for own user. 2024-06-02 21:00:11 -04:00
notCharles
65a697d8f7 add variables to .env 2024-06-02 18:04:02 -04:00
Charles
9515a82a75 Merge pull request #280 from pelican-dev/charles/rework-server
Rework Server Pages
2024-06-02 17:54:00 -04:00
Lance Pioch
44f5ea567f Merge branch 'main' into charles/rework-server
# Conflicts:
#	app/Filament/Resources/ServerResource/Pages/EditServer.php
2024-06-02 17:46:45 -04:00
Charles
88f910f3e7 Merge pull request #307 from pelican-dev/issue/287
Allow Servers to have Mounts
2024-06-02 17:42:13 -04:00
Lance Pioch
020f028008 Add new component 2024-06-02 17:38:07 -04:00
Lance Pioch
0cb7f737b0 Set the node 2024-06-02 17:38:00 -04:00
notCharles
53aa52f519 Add migration to update stock egg uuids 2024-06-02 17:06:42 -04:00
Lance Pioch
bbe09ced1d Add some new options for ports 2024-06-02 16:58:32 -04:00
Lance Pioch
2e7c534a3b Disable this if there’s no egg 2024-06-02 16:58:21 -04:00
notCharles
e884eda5a7 Update stock eggs to have UUIDs 2024-06-02 16:50:55 -04:00
notCharles
58d1fd3917 Add Mounts + Icons 2024-06-02 15:05:45 -04:00
notCharles
0b0952650e Remove labels/mounts if empty. 2024-06-02 13:43:25 -04:00
notCharles
aa55a7ed83 Update .gitignore, again... 2024-06-02 10:51:48 -04:00
notCharles
c7fa7a1bad Add to .gitignore 2024-06-02 10:48:45 -04:00
Lance Pioch
71684dc517 Pint fix 2024-06-02 01:38:42 -04:00
Lance Pioch
4dbb55059d Remove auto generated properties 2024-06-02 01:38:30 -04:00
Lance Pioch
f480a271b3 Simplify logic 2024-06-02 01:27:04 -04:00
Lance Pioch
9c81c0ce18 Use public member 2024-06-02 01:25:48 -04:00
Lance Pioch
b220c582cc Rearrange these 2024-06-02 01:22:33 -04:00
Lance Pioch
29f8ac625a This is for the api 2024-06-02 01:22:21 -04:00
Lance Pioch
5b6c462943 This isn’t typically used for a brand new server 2024-06-02 01:22:12 -04:00
Lance Pioch
4a3bdd78ef Update readme 2024-06-02 00:38:54 -04:00
Lance Pioch
a1067fd4aa Allow mounts to be added to servers 2024-06-02 00:34:35 -04:00
Lance Pioch
110cc1248b Fix relationship 2024-06-02 00:33:58 -04:00
Lance Pioch
04a1ccc97e Merge branch 'main' of github.com:pelican-dev/panel 2024-06-01 19:26:25 -04:00
Lance Pioch
5e7f5c2a4c Allow adding new allocations to existing servers 2024-06-01 19:26:23 -04:00
kubi
b804878d7b Fix labels position in server config 2024-06-01 20:42:44 +00:00
notCharles
118977c8c5 Merge branch 'main' into charles/rework-server 2024-06-01 15:54:03 -04:00
notCharles
c31b7b8c6a Correctly save labels on create 2024-06-01 15:52:13 -04:00
Charles
eefe59b153 Merge pull request #300 from pelican-dev/issue/286
2FA Profile
2024-06-01 13:07:32 -04:00
Lance Pioch
cd4b7cbf9e Refresh this 2024-06-01 13:03:46 -04:00
Lance Pioch
67cb3d4816 Show backup tokens better 2024-06-01 12:49:36 -04:00
Lance Pioch
7762e68a6c Make qr code visible on light mode 2024-06-01 12:49:19 -04:00
Lance Pioch
7a327ea378 Remove clockwork 2024-06-01 12:36:11 -04:00
Lance Pioch
b3ca7b7ac9 Merge pull request #284 from Boy132/replace/encrypt-decrypt
Replace encrypt/decrypt with `encrypted` casting to resolve #4
2024-05-31 19:50:07 -04:00
Charles
abc99cd928 Merge pull request #303 from pelican-dev/issue/290
Allow updating of existing eggs via upload
2024-05-31 18:52:20 -04:00
Charles
cb638369cf Merge pull request #302 from pelican-dev/issue/294
Only show application api keys
2024-05-31 18:49:35 -04:00
notCharles
9174de2d8c Add Labels 2024-05-31 17:24:03 -04:00
Boy132
7cda358b66 add missing import 2024-05-31 23:07:50 +02:00
Boy132
33f6551b21 run pint 2024-05-31 23:06:46 +02:00
Boy132
b1928e89b4 Merge branch 'pelican-dev:main' into replace/encrypt-decrypt 2024-05-31 23:04:43 +02:00
Lance Pioch
c956cd0106 Update old keys 2024-05-31 17:03:14 -04:00
notCharles
5081cc3f63 Fix badge, update table 2024-05-31 16:39:23 -04:00
Lance Pioch
8eb2c23420 Fix creating new node 2024-05-31 16:03:12 -04:00
notCharles
cfe385f53a Add uuid to egg exproter
Add UUID to egg exporter.
2024-05-31 16:01:15 -04:00
Lance Pioch
264d3498a6 Allow mailgun to work #257 2024-05-31 15:44:21 -04:00
Lance Pioch
065f3f2468 Activity log fix #257 2024-05-31 15:18:04 -04:00
Lance Pioch
957638d4ac Fill previously existing egg 2024-05-31 15:14:22 -04:00
Lance Pioch
7d0ce1627b Remove unused imports 2024-05-31 02:00:38 -04:00
Lance Pioch
8cec7368ab Only show application api keys 2024-05-31 01:59:33 -04:00
Lance Pioch
5519931ee5 Pint 2024-05-31 01:42:02 -04:00
Charles
97ac0fe54b Add Reset Daemon Key Button (#298) closes #292 2024-05-31 01:41:21 -04:00
Lance Pioch
7657364208 Cache per user and show backup tokens temporarily 2024-05-31 01:38:32 -04:00
Lance Pioch
ef1a208b95 Add 2fa setup 2024-05-31 01:26:28 -04:00
Lance Pioch
aa82c6dd04 Update this 2024-05-31 01:20:25 -04:00
Lance Pioch
8ecabef6b5 Add customization 2024-05-29 19:24:02 -04:00
notCharles
a6d07ede5a Soon-TM 2024-05-29 19:18:09 -04:00
Boy132
f6325c07c4 Fix overallocation -1 and close #268 (#283) 2024-05-29 18:57:30 -04:00
Exotical
7674ee0e2b Make deploy.locations optional in the api (#295) 2024-05-29 18:54:07 -04:00
Senna
5760e72b8f Added 2 badges (#296) 2024-05-29 18:51:45 -04:00
Boy132
b6e46f758d Remove hashids (#282) and close #269 2024-05-29 18:41:44 -04:00
notCharles
e980877bbc Fix Node Creation
Add missing defaults
2024-05-29 18:28:21 -04:00
notCharles
dd223b47c0 WIP Server Transfer 2024-05-29 18:27:54 -04:00
Boy132
639fa3399d run pint 2024-05-28 15:27:33 +02:00
Boy132
82fd547484 replace encrypt/ decrypt with encrypted casting 2024-05-28 15:24:20 +02:00
notCharles
d461242f08 Improve Logic on buttons
If a server is suspended, disable transfer/toggle/reinstall as they will unsuspend the server due to the status change.

Also properly updates server state and container status.
2024-05-27 21:51:24 -04:00
notCharles
dec1cf8e74 Rework Edit Server Page
a WIP, Also functional
2024-05-27 20:02:13 -04:00
notCharles
15caac51fb fix auth redirect
closes https://github.com/pelican-dev/panel/issues/278
2024-05-27 13:41:46 -04:00
notCharles
183c274a0d Correct Tags to KeyVal 2024-05-27 13:37:59 -04:00
Lance Pioch
a8b2fb440f Merge branch 'main' of github.com:pelican-dev/panel 2024-05-25 20:59:54 -04:00
Lance Pioch
f8e4514998 Update filename 2024-05-25 20:51:52 -04:00
Lance Pioch
deeebf73d3 Update gitignores 2024-05-25 20:51:41 -04:00
Boy132
422fc102c9 Improve "no interaction" mode for queue worker service command (#270) 2024-05-25 20:48:02 -04:00
Boy132
e715e92f9d correctly transform eggs that use inheritance (application api) (#217) 2024-05-25 20:47:27 -04:00
Lance Pioch
73babfa2b3 Merge pull request #274 from pelican-dev/issue/267 and fix #267 2024-05-24 21:15:14 -04:00
Lance Pioch
e0a92d733b I swear I already did this 2024-05-24 20:58:19 -04:00
Jordan Adams
1e67cd9944 Fix mumble host to allow IPv6 (#264) 2024-05-24 19:36:18 -04:00
Lance Pioch
3946116dff Merge pull request #265 from pelican-dev/issue/222
Simplify node deployment service, add filtering with tags instead of locations
2024-05-24 19:35:06 -04:00
Lance Pioch
b77fd3d653 Fix #267 2024-05-24 19:34:23 -04:00
Lance Pioch
f4672c6cb1 Pint fix 2024-05-22 03:15:29 -04:00
Lance Pioch
5b9e4b1729 Always limit by nodes, was like this before anyways 2024-05-22 03:10:33 -04:00
Lance Pioch
48f715ae69 Fix directory 2024-05-22 02:52:49 -04:00
Lance Pioch
51460782cc Merge branch 'main' into issue/222
# Conflicts:
#	app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php
#	app/Http/Requests/Api/Application/Nodes/GetDeployableNodesRequest.php
#	app/Services/Deployment/FindViableNodesService.php
#	app/Services/Servers/ServerCreationService.php
#	tests/Integration/Services/Deployment/FindViableNodesServiceTest.php
2024-05-22 02:47:37 -04:00
Lance Pioch
b007e63937 Pint fixes 2024-05-22 02:35:20 -04:00
Boy132
4dd833562b Add CPU limit to node (#239) to resolve #233
* add node cpu limit to backend

* update makenodecommand

* add node cpu limit to frontend

* add migration and update mysql schema

* run pint

* fix typo in mysql schema

* forgot this assert

* forgot to setCpu here

* run pint

* adjust migration

* Fix db migration

* make cpu optional

* set default value for cpu in node deployment

* update mysql schema

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-05-22 02:34:43 -04:00
Lance Pioch
b579f14f3f Backwards compatibility 2024-05-21 22:04:12 -04:00
Lance Pioch
eadaec1b30 Simplify now that keys are fixed 2024-05-21 21:48:16 -04:00
Lance Pioch
a9e58bb493 Fix database path for sqlite 2024-05-21 21:48:04 -04:00
Lance Pioch
5c33c7495a Ignore this for now 2024-05-21 21:45:26 -04:00
Lance Pioch
f9aa8cf218 Simplify viable nodes service 2024-05-21 21:44:49 -04:00
Lance Pioch
da698a3666 Remove exception 2024-05-21 21:02:11 -04:00
Lance Pioch
2808a3dd35 Simplify buttons 2024-05-20 14:38:48 -04:00
Lance Pioch
7ea365e8de Pint 2024-05-19 23:25:40 -04:00
Lance Pioch
ae399f9bad Add port validation rule for #68 2024-05-19 23:25:12 -04:00
Lance Pioch
53a5ff6e6d Update api docs 2024-05-19 23:24:21 -04:00
Lance Pioch
54ae4b3dc1 Merge pull request #261 from pelican-dev/charles/docker-tags
Add docker container labels
2024-05-19 21:36:26 -04:00
notCharles
859a721e17 mysql vs sqlite... 2024-05-19 21:30:25 -04:00
notCharles
03cbdd5bdd update edit/create pages 2024-05-19 21:15:43 -04:00
notCharles
4c43fd1683 Add docker_labels 2024-05-19 20:55:37 -04:00
notCharles
0c61a63191 Add id to allocation table 2024-05-19 20:23:59 -04:00
Boy132
b1f99ca8a3 Add api for mounts (#160)
* add application api endpoints for mounts

* run pint

* add mounts resource to api key

* add includes to mount transformer

* forgot delete route for mount itself

* add migration for "r_mounts" column

* add mounts to testcase api key
2024-05-19 08:50:15 -07:00
notCharles
0a5810358a Update jdbc string
should also update the password here.
2024-05-18 18:17:20 -04:00
notCharles
1bae239971 Fix db password rotation
updates the password textbox when password is rotated.
2024-05-18 18:13:06 -04:00
notCharles
597f74f105 reload form data after save
closes https://github.com/pelican-dev/panel/issues/251
2024-05-18 18:08:55 -04:00
notCharles
5344d99a40 Update Mobile View 2024-05-18 17:47:33 -04:00
Boy132
1db1a1a3e0 set default db username to "pelican" to match docs (#254) 2024-05-18 12:38:11 -07:00
Boy132
712b6a285b Add artisan command to create queue worker service (#253)
* add command to create queue worker service file

* remove comments from service file that are no longer needed

* only create queue worker service file if queue driver is not "sync"

* make "database" the recommended queue driver, again
2024-05-18 10:31:02 -07:00
notCharles
38b92ae21d Fix user->admin
closes https://github.com/pelican-dev/panel/issues/197
2024-05-17 22:58:17 -04:00
notCharles
c18d291f8f database page updates 2024-05-17 20:56:12 -04:00
Charles
45c0cfe4d8 Merge pull request #240 from Boy132/backport/ptero-backup-node-check
backups: ensure requesting node is checked
2024-05-17 20:21:57 -04:00
Charles
89d555f308 Merge pull request #244 from pelican-dev/charles/fix-newuser
fix: Creating User
2024-05-17 20:20:10 -04:00
notCharles
9b2e00ead2 pint 2024-05-17 20:10:53 -04:00
notCharles
d0997dca1a Remove hints 2024-05-17 19:59:41 -04:00
notCharles
cca723d21e Update unique, remove dehydrates 2024-05-17 19:59:04 -04:00
Lance Pioch
68dbd6e329 Remove ref 2024-05-17 19:50:34 -04:00
Lance Pioch
544364f061 Merge branch 'main' of github.com:pelican-dev/panel 2024-05-17 19:49:36 -04:00
Lance Pioch
649f319776 Swap route key 2024-05-17 19:49:33 -04:00
notCharles
6ffe800e0d Forgot to delete these. 2024-05-17 19:14:29 -04:00
notCharles
12af8fe51e This is better... 2024-05-16 20:34:37 -04:00
notCharles
9f5d27896a Pint 2024-05-16 18:44:59 -04:00
notCharles
15969da41e Fix New User
Removes the required password field for making new accounts, triggers email for user to make their own password.
2024-05-16 18:43:05 -04:00
Charles
628a6e54e3 Merge pull request #243 from pelican-dev/charles/remove-log
Remove Log Viewer
2024-05-16 18:04:46 -04:00
notCharles
edcd406b82 Remove Log Viewer 2024-05-16 17:18:27 -04:00
Boy132
1742061807 Merge branch 'pelican-dev:main' into backport/ptero-backup-node-check 2024-05-16 17:32:19 +02:00
Charles
289eda64ad Merge pull request #238 from Boy132/fix/236
Add default for disk/ memory and overallocation
2024-05-16 11:31:25 -04:00
Boy132
cc46c463ac run pint 2024-05-16 17:28:49 +02:00
Boy132
e6dded61a4 backups: ensure requesting node is checked
Co-authored-by: matthewpi <matthew@pterodactyl.io>
2024-05-16 11:53:18 +02:00
Boy132
921b76f1e1 add default for disk/ memory and overallocation 2024-05-16 08:43:09 +02:00
Lance Pioch
91a3bb969e Better scrambling to fix #235 2024-05-16 00:23:29 -04:00
Lance Pioch
8b62df6c53 Composer update 2024-05-16 00:15:35 -04:00
Lance Pioch
3c14e1ffa4 Update edit images to match create and fix #229 2024-05-16 00:04:13 -04:00
Lance Pioch
095fff7ad6 Merge branch 'main' of github.com:pelican-dev/panel 2024-05-15 22:33:59 -04:00
notCharles
0cd03e95f4 Update database view 2024-05-15 21:21:57 -04:00
notCharles
00dda7dbe4 update composer file 2024-05-15 20:26:44 -04:00
notCharles
eee1260fcc upgrade 2024-05-15 19:58:17 -04:00
notCharles
71c91e086f Update sidebar width 2024-05-15 19:51:12 -04:00
Lance Pioch
94bbf46659 Small adjustments for now 2024-05-15 15:14:17 -04:00
Lance Pioch
558b8b2ea7 Update funding 2024-05-15 14:16:30 -04:00
Lance Pioch
4bb16887c4 Add top navigation config 2024-05-15 14:14:31 -04:00
Lance Pioch
0c72833af7 Show blank if only 0 2024-05-15 14:11:09 -04:00
Lance Pioch
ee3f8cd3ec No longer need this 2024-05-15 14:10:56 -04:00
Lance Pioch
b513366f35 Merge branch 'main' of github.com:pelican-dev/panel 2024-05-15 13:24:44 -04:00
Lance Pioch
b1d056a301 Merge pull request #232 from Poseidon281/Navigation-Sidebar
Update: Navigation sidebar
2024-05-15 13:24:25 -04:00
Poseidon281
c72d3f1338 Added topNavigation 2024-05-15 16:57:30 +02:00
Poseidon281
7e6231e6de Run Pint 2024-05-15 16:48:32 +02:00
Poseidon281
4ad837ff14 Added count badge to navigation sidebar 2024-05-15 16:45:35 +02:00
Poseidon281
d7ebde5f5f Composer update because of error 2024-05-15 16:40:48 +02:00
Boy132
6bdd1b3ccb Add api for server transfers (#153)
* add application api endpoint to start server transfer

* add basics for "cancel transfer" endpoint

* wire up wings cancel

* lint
2024-05-15 07:37:56 -07:00
Boy132
afd9f2eb0e Add api for database hosts (#159)
* add application api endpoints for database hosts

* run pint

* forgot to lint this one

* Update app/Http/Controllers/Api/Application/DatabaseHosts/DatabaseHostController.php

Co-authored-by: Devonte W <devnote.dev75@gmail.com>

* Update routes/api-application.php

Co-authored-by: Devonte W <devnote.dev75@gmail.com>

* rename all "databaseHost" to "database_host"

---------

Co-authored-by: Devonte W <devnote.dev75@gmail.com>
2024-05-15 07:37:38 -07:00
notCharles
8a617d416e Add Log Viewer
If you `ln /var/log/pelican/wings.log /var/www/pelican/storage/logs/wings.log` &  `chmod 755 /var/log/pelican/wings.log` They will show in the viewer <3
2024-05-14 22:08:26 -04:00
Charles
7f4cc64d11 Update readme.md 2024-05-14 21:19:50 -04:00
Charles
38e117e304 Update readme.md
Does an SVG work?
2024-05-14 21:16:39 -04:00
notCharles
4d272a1234 WIP: show databases on database host edit screen 2024-05-14 20:10:27 -04:00
notCharles
e9dc6cd32c Update database hosts 2024-05-14 19:30:07 -04:00
notCharles
98ba2c1b8b Update node pages 2024-05-14 19:22:14 -04:00
Charles
2d643ec79f Merge pull request #225 from Boy132/update/gb-to-gib
Update GB to GiB + minValues for resources + other stuff
2024-05-14 18:59:33 -04:00
Charles
2b4625e9b9 Merge pull request #228 from Boy132/patch-1
Add back config variable for redis client
2024-05-14 18:47:02 -04:00
Boy132
9f40ed2f84 add back config variable for redis client 2024-05-14 19:05:52 +02:00
Boy132
af797b3018 run pint 2024-05-14 11:40:01 +02:00
Boy132
e5146e0dbb add helper to cpu input 2024-05-14 11:21:58 +02:00
Boy132
586d1b413c set default overallocation to 0 2024-05-14 10:53:28 +02:00
Boy132
7d239de7f6 add min values for resources 2024-05-14 10:53:04 +02:00
Boy132
5f13c15c70 convert chart data from bytes to GiB 2024-05-14 10:50:26 +02:00
Boy132
4e3f919d8e change all GB/ MB to GiB/ MiB 2024-05-14 10:41:06 +02:00
Lance Pioch
7efa04f1ef Merge pull request #215 from Boy132/remove-brandName
Remove hardcoded brandName
2024-05-14 00:55:51 -04:00
notCharles
35ac1f863a Move save buttons to the top. 2024-05-13 19:58:01 -04:00
notCharles
68195ab0b7 Remove ports, >1024 | <65535 2024-05-13 19:57:42 -04:00
notCharles
f79dac2d13 Show associated servers on edit egg page 2024-05-13 19:39:28 -04:00
notCharles
4f176c47d2 Fix saving servers when egg variable is blank
closes https://github.com/pelican-dev/panel/issues/201
2024-05-13 19:18:04 -04:00
Charles
f7e7864dfe Merge pull request #216 from Boy132/update/storeschedulerequest
Add missing fields to StoreScheduleRequest
2024-05-13 19:14:51 -04:00
Charles
2f8a15facd Merge pull request #210 from Boy132/patch-1
Use correct variable name for mail driver
2024-05-13 19:14:27 -04:00
Charles
7bf175190b Merge pull request #206 from Boy132/update-default-drivers
Update default drivers
2024-05-13 19:14:02 -04:00
Charles
8866ca3d96 Merge pull request #207 from Boy132/rename/oom_disabled
Convert `oom_disabled` to `oom_killer`
2024-05-13 19:12:59 -04:00
Boy132
703e5480ff add missing fields to StoreScheduleRequest 2024-05-13 18:55:59 +02:00
Boy132
45a6c37594 add back env variable for APP_NAME 2024-05-13 18:24:26 +02:00
Boy132
340085cc9b remove hardcoded brandName 2024-05-13 18:23:17 +02:00
Boy132
ef9d1ab614 use env value as default for driver choice 2024-05-13 08:57:06 +02:00
Boy132
55a0bfdf7e remove "MAIL_DRIVER" from phpunit.xml 2024-05-13 08:53:14 +02:00
Boy132
d6f3934f80 use correct variable name for mail driver
closes #209
2024-05-13 08:51:12 +02:00
notCharles
f9e8adad30 Update EditServer egg variables to match CreateServer
validation is still broken on `nullable|<##>` rules
2024-05-12 22:37:40 -04:00
notCharles
08dc5753d4 Merge branch 'main' of https://github.com/pelican-dev/panel 2024-05-12 22:34:53 -04:00
notCharles
0c851ea075 Fix database host saving of password
Closes https://github.com/pelican-dev/panel/issues/203
2024-05-12 22:34:41 -04:00
kubi
cf3ea38e65 Update FUNDING.yml 2024-05-12 15:42:02 -07:00
Boy132
b813de0467 run pint 2024-05-12 22:24:37 +02:00
Boy132
399bed7576 fix typo 2024-05-12 22:21:22 +02:00
Boy132
5aa7128b9c backwards compatibility 2024-05-12 22:21:22 +02:00
Boy132
92d167eb10 add migration to db schemas 2024-05-12 22:21:22 +02:00
Boy132
f02eb5bfba rename "oom_disabled" to "oom_killer" and invert logic 2024-05-12 22:21:22 +02:00
Boy132
f348ac9f0a update example .env file 2024-05-12 22:19:30 +02:00
Boy132
893b2dca89 update drivers in setup command 2024-05-12 22:18:22 +02:00
notCharles
1f5217a9d9 Add Egg ID to page 2024-05-12 16:16:13 -04:00
notCharles
e84b47410a move some stuff 2024-05-12 14:30:35 -04:00
notCharles
76cf4391ae Add advanced node setting tab 2024-05-12 14:11:17 -04:00
notCharles
7688006574 Pint 2024-05-12 14:11:00 -04:00
notCharles
ed36041a7e Allow alias to be edited on edit server page 2024-05-12 12:44:44 -04:00
notCharles
bb52485606 Update Allocation Relationship
Allow editing of alias, and ip after allocation is created.
2024-05-12 12:35:08 -04:00
notCharles
615b70f9a2 Fix rule validation? 2024-05-12 00:55:44 -04:00
Lance Pioch
3609873c4c These are emails 2024-05-11 23:44:59 -04:00
Lance Pioch
7f6e4a18c1 Validate these 2024-05-11 23:44:12 -04:00
Lance Pioch
c5b441e54a Merge branch 'main' of github.com:pelican-dev/panel 2024-05-11 22:08:30 -04:00
Lance Pioch
30452890f5 Fix mounts 2024-05-11 22:08:23 -04:00
notCharles
af18a3704f Fix Creating Eggs 2024-05-11 22:02:57 -04:00
Lance Pioch
2c98693bd2 Replace hard coded url 2024-05-11 21:56:12 -04:00
Lance Pioch
259599b441 Merge branch 'main' of github.com:pelican-dev/panel 2024-05-11 21:50:04 -04:00
Lance Pioch
2da058be49 Force reset allocations 2024-05-11 21:50:00 -04:00
Lance Pioch
8d33e20f6c Create these one by one 2024-05-11 21:49:12 -04:00
Lance Pioch
b4e8f0586a Limit to valid ports 2024-05-11 21:48:46 -04:00
Lance Pioch
6f8f5e2746 Don’t handle livewire 2024-05-11 21:48:36 -04:00
notCharles
37cc3ae20d Oopsie Woopsie 2024-05-11 21:39:00 -04:00
Lance Pioch
9fc46b9ae5 Add default in 2024-05-11 21:01:15 -04:00
Lance Pioch
d409ff037c Fix route 2024-05-11 21:01:07 -04:00
Lance Pioch
000363cd17 Pint 2024-05-11 21:01:01 -04:00
Lance Pioch
2beb12c04f Swap over routes 2024-05-11 20:50:10 -04:00
Lance Pioch
373ede8548 Make sure transaction is finished 2024-05-11 20:19:56 -04:00
notCharles
e286100197 Fix migrations to work with SQLite without needing SQLite CLI 2024-05-11 20:09:22 -04:00
notCharles
082163389a update gitignore 2024-05-11 18:56:55 -04:00
notCharles
f9247c9318 Update Nav Bar, '/new-admin' > '/panel'
We cannot replace `/admin` as some functions still call the old admin area, Exporting Eggs
2024-05-11 18:54:47 -04:00
notCharles
5a00b8690d update name 2024-05-11 18:43:48 -04:00
notCharles
c0ca189536 Update CreateEgg to match EditEgg
Creating Eggs still broken.
2024-05-11 18:42:24 -04:00
kubi
84a3ceeae3 Update release.yaml 2024-05-11 14:44:58 -07:00
Lance Pioch
1c499d84cf Merge branch 'main' of github.com:pelican-dev/panel 2024-05-11 17:39:01 -04:00
Lance Pioch
871e93a38c Add scramble api docs 2024-05-11 17:38:44 -04:00
notCharles
1481338eb9 Fix Egg delete button logic 2024-05-11 17:21:06 -04:00
kubi
1e4fadda24 Update release.yaml (#196) 2024-05-11 12:31:15 -07:00
Lance Pioch
585fe8d1a1 Add basic widgets for now 2024-05-11 15:25:37 -04:00
Lance Pioch
f58c697d28 Add this for the future 2024-05-11 14:10:06 -04:00
Lance Pioch
b32f8966e1 Revert "Remove yarn"
This reverts commit 602c1ed9a6.
2024-05-11 00:55:35 -04:00
Lance Pioch
b18ebeefdc Merge branch 'main' of github.com:pelican-dev/panel 2024-05-10 22:20:20 -04:00
Lance Pioch
5fd7e419d9 Run pint 2024-05-10 22:20:14 -04:00
Lance Pioch
602c1ed9a6 Remove yarn 2024-05-10 22:19:51 -04:00
Lance Pioch
39bc87c2e2 Swap suspend over 2024-05-10 22:19:17 -04:00
notCharles
44cc5a8132 Sort server vars for front end 2024-05-10 21:31:32 -04:00
Lance Pioch
0c7ae26313 Fix faker 2024-05-10 20:24:01 -04:00
Lance Pioch
102955bf6a Merge branch 'main' of github.com:pelican-dev/panel 2024-05-10 19:59:57 -04:00
Lance Pioch
72eb3ce467 Generate a new key if it doesn’t exist 2024-05-10 19:59:33 -04:00
notCharles
d656f21cd9 Add view link on server listing 2024-05-10 19:33:53 -04:00
notCharles
8da5afb35e disable delete if node has servers 2024-05-10 17:57:42 -04:00
notCharles
44e9da93b6 Add variable permission to edit/create egg + export 2024-05-10 17:53:16 -04:00
notCharles
29e1bd4757 Color Export link 2024-05-10 17:01:39 -04:00
notCharles
f1493c5139 Fix creating api keys 2024-05-10 16:42:05 -04:00
notCharles
30a668c84a Fix making databases when using sqlite for panel 2024-05-10 16:26:29 -04:00
notCharles
9f4bf8777e encrypt the database hosts password 2024-05-10 16:15:10 -04:00
notCharles
dfe2e9d629 Pint & Add application features to create page 2024-05-09 19:43:46 -04:00
notCharles
607e186082 Remove author variable from cli help 2024-05-08 20:20:02 -04:00
notCharles
e60c86a87e un-break ui building wf 2024-05-08 20:17:13 -04:00
Lance Pioch
577479edaf Merge pull request #188 from QuintenQVD0/main
Some simple updates
2024-05-08 10:28:23 -04:00
Quinten
4423baa1e7 7z was already their. 2024-05-08 12:14:44 +02:00
Quinten
d967681227 Swap all docker images and add java 21 to all minecraft eggs 2024-05-08 12:05:14 +02:00
Quinten
f391edda27 Fix minecraft Eula link 2024-05-08 11:44:59 +02:00
Quinten
255030136f allow decompressing .7z files 2024-05-08 11:43:47 +02:00
Lance Pioch
f79d304586 Remove facade usage 2024-05-08 00:04:57 -04:00
Lance Pioch
60772b1775 Add new cluster 2024-05-07 23:54:53 -04:00
Lance Pioch
bf3ef435ae Update config 2024-05-07 22:17:28 -04:00
Lance Pioch
30411ccd13 Don’t need this anymore 2024-05-07 22:14:04 -04:00
Lance Pioch
bd8ca0abcf Don’t run this for the merfolk 2024-05-07 22:13:54 -04:00
Lance Pioch
6621ece3a1 Don’t ask for the author 2024-05-07 22:13:46 -04:00
Lance Pioch
8f4b68617a Use the current user as the new author 2024-05-07 22:00:40 -04:00
Lance Pioch
1b3017222e Time zone should always be UTC
Will implement display time zones on a per user basis
2024-05-07 22:00:24 -04:00
Lance Pioch
a050fbd2d3 Copy example env over for user 2024-05-07 21:52:51 -04:00
notCharles
fe3bf88ea4 Fix editing server resources 2024-05-07 21:44:44 -04:00
notCharles
94f583fef0 Pint 2024-05-07 20:55:02 -04:00
Lance Pioch
8da0017eaf Make things work
Co-authored-by: notCharles <charles@pelican.dev>
2024-05-07 20:47:58 -04:00
Lance Pioch
4e838201c6 Merge branch 'main' of github.com:pelican-dev/panel 2024-05-07 20:00:08 -04:00
Lance Pioch
111b8b3cda Add specific logic for CF 2024-05-07 20:00:02 -04:00
Lance Pioch
d0d388534b Add http status code enum 2024-05-07 19:59:52 -04:00
Lance Pioch
939b7354e4 Return basic response 2024-05-07 19:59:41 -04:00
Lance Pioch
7077693da2 Comment this for now 2024-05-07 19:59:27 -04:00
notCharles
9c047d0a45 revert new buttons on server create 2024-05-07 18:19:51 -04:00
notCharles
f13dbfa766 Remove live on toggles 2024-05-07 17:01:55 -04:00
notCharles
d4dd1349da Add defaults to create server. 2024-05-07 16:52:34 -04:00
Boy132
1db8c209fb fix schedule process command (#187) 2024-05-06 23:55:05 -07:00
Lance Pioch
852d2b7431 Merge branch 'main' of github.com:pelican-dev/panel 2024-05-07 00:11:02 -04:00
Lance Pioch
6468f85cb0 Fix #164 and bypass the casting issue
https://github.com/filamentphp/filament/issues/12116#issuecomment-2097408424
2024-05-07 00:10:46 -04:00
kubi
5c61865dfb Update cla.yaml (#185)
Try using a new personal access token
2024-05-06 21:00:43 -07:00
kubi
3feb9d2304 Update cla.yaml (#184)
Update to the latest release and remove unnecessary permission when using a remote repository.
2024-05-06 20:50:15 -07:00
notCharles
f17ac6ffac Add new resource view to create server 2024-05-06 22:05:07 -04:00
notCharles
04675a73fd Make pretty 2024-05-06 22:05:03 -04:00
notCharles
e2353af0d8 Fix editing egg-variables 2024-05-06 22:05:00 -04:00
Lance Pioch
8eaf64b5fd Merge pull request #100 from Poseidon281/Command-Translations
Translation file for commands & tiny cleanup
2024-05-06 22:04:55 -04:00
Lance Pioch
e5c5bc40d9 Remove these casts for now 2024-05-06 22:03:59 -04:00
Lance Pioch
5286f446dc Merge pull request #139 from Boy132/database/sqlite
Support SQLite
2024-05-05 15:17:08 -04:00
Lance Pioch
abbf2038a7 Fix pint issue 2024-05-05 15:14:04 -04:00
Lance Pioch
52026ca9e4 Update egg creation process 2024-05-04 18:45:26 -04:00
Lance Pioch
0b0c4bb434 Switch to select 2024-05-04 15:26:08 -04:00
Lance Pioch
22aa56d306 Rearrange these 2024-05-04 15:25:52 -04:00
Lance Pioch
3b935a1eea No need to mess with this for now 2024-05-04 15:20:51 -04:00
Lance Pioch
146421ee52 Add suggestions for egg features 2024-05-04 15:20:10 -04:00
Lance Pioch
9875942191 Rearrange these 2024-05-04 15:19:57 -04:00
Lance Pioch
3d2b18140a Hide this 2024-05-04 15:19:40 -04:00
Lance Pioch
ec0882cd14 Redo how docker images work 2024-05-04 15:05:15 -04:00
Lance Pioch
48c97ee1cc Generate name automatically for server if egg is selected 2024-05-04 13:39:57 -04:00
Lance Pioch
17787fee18 Forbid built in webserver due to unresolvable issues 2024-05-04 13:12:33 -04:00
Boy132
f2a59002bc simplify AllocationSelectionService 2024-05-02 09:58:28 +02:00
Boy132
98419bc625 update sql schemas 2024-05-02 09:58:28 +02:00
Boy132
fb596fa4f9 update IntegrationTestCase 2024-05-02 09:58:28 +02:00
Boy132
7a4289cee1 add default value for "ignored_files" in backup factory 2024-05-02 09:58:27 +02:00
Boy132
488acce564 update database settings command 2024-05-02 09:58:27 +02:00
Boy132
3f3b500a14 update ci tests 2024-05-02 09:58:27 +02:00
Boy132
29803bbaf2 change default db driver in example .env to "sqlite" 2024-05-02 09:58:27 +02:00
Boy132
e07eabc579 set default value for "tags" 2024-05-02 09:58:27 +02:00
Boy132
e82a3b838c update AllocationSelectionService 2024-05-02 09:58:27 +02:00
Boy132
9761c3762d add config for sqlite 2024-05-02 09:58:27 +02:00
Lance Pioch
1ecfcc611f Pint fixes 2024-04-30 19:49:18 -04:00
Lance Pioch
ad60b437ce Merge pull request #165 from parkervcp/issue/fix_discord_button
make discord button blurple
2024-04-30 19:48:37 -04:00
Scai
cc5208cc6b fix prettier linting issue 2024-04-30 22:59:30 +03:00
Michael (Parker) Parker
a9212d9e7d remove extra comma 2024-04-30 08:16:06 -04:00
Michael (Parker) Parker
efcf22d837 make discord button blurple
changes the discord button in the admin page to discords official 'blurple' color.
2024-04-29 22:55:53 -04:00
Lance Pioch
d4a02336aa Merge branch 'main' of github.com:pelican-dev/panel 2024-04-29 22:42:21 -04:00
Lance Pioch
386eba28e6 Allow searching by tags 2024-04-29 22:42:15 -04:00
Lance Pioch
651b887a0e Allow this to be changed 2024-04-29 22:42:04 -04:00
Lance Pioch
2982757649 Don’t need to show this 2024-04-29 22:41:58 -04:00
Lance Pioch
04a8999e0b Default this to true 2024-04-29 22:41:46 -04:00
Lance Pioch
41ee9e563c Fix saving tags 2024-04-29 22:41:39 -04:00
notCharles
16a16dc390 Add console button 2024-04-29 22:13:54 -04:00
notCharles
80155a17e5 remove add allocation btn 2024-04-29 21:42:02 -04:00
notCharles
26da0c5e74 Add server url to allocations relationship manager 2024-04-29 21:35:16 -04:00
notCharles
27059e7b99 Fix edit server docker image selection 2024-04-29 21:34:49 -04:00
notCharles
fdc51e03ac Change colors 2024-04-29 21:34:27 -04:00
Lance Pioch
549ab12048 Pint 2024-04-29 20:17:14 -04:00
Lance Pioch
fb95a3d923 Replace with relation manager 2024-04-29 20:15:59 -04:00
Lance Pioch
70da7c0f51 Add allocations to node pages 2024-04-29 20:06:34 -04:00
Lance Pioch
d7051bb7ed Add icon to tab 2024-04-29 20:06:18 -04:00
Lance Pioch
dd8f28b864 Increase cache 2024-04-29 15:02:12 -04:00
Lance Pioch
8b060e8834 Style fixes 2024-04-29 14:29:33 -04:00
Lance Pioch
7b66e1ce33 Merge branch 'main' of github.com:pelican-dev/panel 2024-04-29 14:29:10 -04:00
Lance Pioch
5d2248ab1f Implement container statuses from wings 2024-04-29 14:29:04 -04:00
Lance Pioch
7df5f12c75 Show select box for specific egg variables 2024-04-28 17:43:26 -04:00
Lance Pioch
b0dadc60f2 Make this form look nicer 2024-04-28 17:43:15 -04:00
Lance Pioch
36b7998714 Remove dd 2024-04-28 17:42:40 -04:00
notCharles
d42fc88535 Update Server Resources, remove defaults 2024-04-28 14:25:40 -04:00
Lance Pioch
4ebc67aab0 Fix updating server variables and some small cleanup 2024-04-28 12:57:56 -04:00
Lance Pioch
5bef99611b Remove unused imports 2024-04-27 23:08:49 -04:00
Lance Pioch
191f1456ee Update passed in hidden data to include actual changed server variable values 2024-04-27 23:07:21 -04:00
Lance Pioch
a839078c7d Merge branch 'main' of github.com:pelican-dev/panel 2024-04-27 22:49:36 -04:00
Lance Pioch
2d7804311d Populate these fields for server variables 2024-04-27 22:49:27 -04:00
Lance Pioch
6b0c1d136b Prevent duplicated server variables 2024-04-27 22:49:18 -04:00
notCharles
383845ca62 fix saving/editing server egg vars 2024-04-27 22:35:10 -04:00
Lance Pioch
a9b755ae2d Merge branch 'main' of github.com:pelican-dev/panel 2024-04-27 22:18:34 -04:00
Lance Pioch
6ed85cfdc6 Better icon 2024-04-27 22:18:25 -04:00
notCharles
137b6040ab Fix Backups 2024-04-27 22:08:51 -04:00
notCharles
da08b60f20 hide checkbox if servers > 0 2024-04-27 21:39:37 -04:00
iamkubi
267952d750 Merge pull request #162 from pelican-dev/backup_fix
Fix backups
2024-04-27 02:02:17 -07:00
kubi
38aea0edbe Fix backups 2024-04-27 08:52:27 +00:00
Charles
51bb60c3b1 Add emptyState 2024-04-26 06:49:11 -04:00
Lance Pioch
8f2413dc7e Add front end translations
Co-authored-by: Miniontoby <tobias.gaarenstroom@gmail.com>
2024-04-25 23:33:08 -04:00
Lance Pioch
1bdef318f0 Start adding translations to backend
Co-authored-by: Miniontoby <tobias.gaarenstroom@gmail.com>
2024-04-25 23:29:19 -04:00
Lance Pioch
2efb807f0b Merge branch 'main' of github.com:pelican-dev/panel 2024-04-25 22:44:45 -04:00
Lance Pioch
be35692125 Add basic relationship manager for allocations 2024-04-25 22:36:59 -04:00
Lance Pioch
076125485d Don’t need to specify this param 2024-04-25 22:36:36 -04:00
Lance Pioch
1800f105d7 Simplify logic greatly 2024-04-25 20:31:46 -04:00
Lance Pioch
933e693897 Put something here 2024-04-25 20:31:38 -04:00
Lance Pioch
97ff693e5c Don’t update status/state 2024-04-25 19:48:46 -04:00
Lance Pioch
7853cdc9ed Return default 2024-04-25 19:15:45 -04:00
notCharles
cba00d822c minor fixes for random stuff 2024-04-25 18:57:21 -04:00
Lance Pioch
00502f6d4d Allow suspension of servers 2024-04-25 17:45:49 -04:00
Lance Pioch
7ffd7019a2 Always default this 2024-04-25 17:35:35 -04:00
Lance Pioch
ea146f4715 Resource changes 2024-04-25 17:35:27 -04:00
notCharles
85b250d016 Fix deleting servers 2024-04-23 20:33:54 -04:00
notCharles
7bbbba37f5 Add force delete for servers 2024-04-23 20:03:57 -04:00
notCharles
07244c38eb refactor resources 2024-04-23 19:45:11 -04:00
notCharles
50f9dde280 Render egg variables by sort 2024-04-23 17:57:20 -04:00
notCharles
76a3197022 Remove un-needed code 2024-04-23 17:55:43 -04:00
Charles
f26628a546 remove unused imports / options 2024-04-23 06:40:12 -04:00
Charles
ceb365b95c Split create/edit server pages 2024-04-23 06:28:23 -04:00
notCharles
b0a2bae0b5 Don't need to display the mount id 2024-04-22 19:04:39 -04:00
notCharles
426b82754d remove search bar 2024-04-22 18:48:09 -04:00
notCharles
2f82229048 Fix Container/Server Status + Mobile Styling 2024-04-22 18:03:34 -04:00
notCharles
65bfda1034 Add Server list to node page 2024-04-22 17:47:16 -04:00
notCharles
2328f07473 fix egg variables on server edit page 2024-04-22 17:02:47 -04:00
Scai
ad2e48cfc1 Merge pull request #138 from Boy132/update/webpack
Update Webpack & change to node LTS version for Dockerfile
2024-04-22 19:37:43 +03:00
Boy132
cca5e4a4c0 add php 8.3 to composer.json 2024-04-22 09:10:01 +02:00
Boy132
17ec5c7acf use node lts version for dockerfile 2024-04-22 08:58:05 +02:00
Boy132
4708105104 set min node version to 18 2024-04-22 08:57:08 +02:00
Boy132
19d2066a1a update webpack to 4.47.0 2024-04-22 08:55:49 +02:00
Lance Pioch
ae3a355a99 Create security.md 2024-04-21 21:32:42 -04:00
Lance Pioch
1996ffe724 Update license 2024-04-21 21:25:13 -04:00
Lance Pioch
edf9bc6f4d Merge pull request #137 from pelican-dev/feature/72
Allow Egg Variables to be orderable/sortable
2024-04-21 21:21:35 -04:00
notCharles
c31eafaf4f Pint 2024-04-21 21:16:05 -04:00
notCharles
1a884c0cdf Save sort order 2024-04-21 21:11:18 -04:00
notCharles
e343de00c0 Merge branch 'feature/72' of https://github.com/pelican-dev/panel into feature/72 2024-04-21 21:09:36 -04:00
Lance Pioch
0f360fcdd1 Merge branch 'main' into feature/72
# Conflicts:
#	app/Filament/Resources/EggResource.php
2024-04-21 21:08:57 -04:00
Lance Pioch
a9a18464dd Wip 2024-04-21 21:06:21 -04:00
Lance Pioch
e47beb59d2 Merge branch 'main' of github.com:pelican-dev/panel 2024-04-21 20:53:22 -04:00
Lance Pioch
d4ff502e08 Make error message more helpful 2024-04-21 20:52:24 -04:00
Lance Pioch
8c9c2c080a Fix creating egg variables 2024-04-21 20:52:18 -04:00
Lance Pioch
aaf7429298 New Crowdin updates (#125) 2024-04-21 17:33:35 -04:00
notCharles
329268697b Merge branch 'main' into feature/72 2024-04-21 16:22:48 -04:00
Lance Pioch
0b950832c2 Merge branch 'main' of github.com:pelican-dev/panel 2024-04-21 16:18:37 -04:00
Lance Pioch
788056d55d Small updates 2024-04-21 16:18:13 -04:00
Lance Pioch
dfaff50ca1 Fix egg variable saving 2024-04-21 15:57:15 -04:00
notCharles
145568237c Rename columns 2024-04-21 15:50:46 -04:00
notCharles
9baaff53cd Update node allocation page 2024-04-21 15:27:05 -04:00
Lance Pioch
3ad622dd69 Add new completed language 2024-04-21 15:16:03 -04:00
Lance Pioch
906a1d7f3e Merge pull request #66 from pelican-dev/issue/fix-3
Change columns
2024-04-21 14:21:16 -04:00
Lance Pioch
ba7a5d5126 Be explicit about this being a string 2024-04-21 14:17:32 -04:00
notCharles
693c65995d Merge branch 'issue/fix-3' of https://github.com/pelican-dev/panel into issue/fix-3 2024-04-21 14:09:35 -04:00
notCharles
85f7bf30b9 fix daemon_base 2024-04-21 14:08:40 -04:00
notCharles
53ad87a349 update egg configuration tab 2024-04-21 13:30:55 -04:00
Scai
7a034c1abf Merge pull request #103 from Boy132/update/dockerfile 2024-04-21 17:33:17 +03:00
notCharles
eeee5779ba Merge branch 'main' into feature/72 2024-04-21 10:14:21 -04:00
Charles
6ff9568760 Merge pull request #115 from pelican-dev/feature/mult-egg-upload
Allow importing of multiple eggs at once
2024-04-21 10:06:21 -04:00
notCharles
7de4cf1417 Allow desc to be null
Desc's are allowed to be null as they're not a required field.
2024-04-21 10:04:16 -04:00
notCharles
db67c64da0 We already know we're importing eggs... 2024-04-21 09:52:08 -04:00
Charles
8476f89f19 Merge branch 'main' into feature/mult-egg-upload 2024-04-21 09:47:46 -04:00
Boy132
01f89d7855 add nodejs 21 to build workflow 2024-04-21 15:14:57 +02:00
Boy132
fa46f78fd5 update dockerfile to php 8.3
add php intl extension
change to official nodejs image and to nodejs 21
2024-04-21 15:14:57 +02:00
Lance Pioch
3e239f9caa Fix tabs 2024-04-21 01:00:48 -04:00
Lance Pioch
204734914d Rename to normal 2024-04-20 22:55:21 -04:00
Lance Pioch
67edf4f472 Adding and deleting api keys 2024-04-20 21:01:41 -04:00
Lance Pioch
7693106a44 Order in reverse chronological order 2024-04-20 20:22:40 -04:00
Lance Pioch
d8f5e1506c Persist this for now 2024-04-20 20:22:17 -04:00
Lance Pioch
330b3bb496 Update languages 2024-04-20 20:21:45 -04:00
notCharles
ac3a36e489 Updates 2024-04-20 18:08:05 -04:00
notCharles
22c03c8075 add sort column 2024-04-20 17:57:14 -04:00
Lance Pioch
bc972da982 Merge branch 'main' of github.com:pelican-dev/panel 2024-04-19 22:33:01 -04:00
Lance Pioch
d9738949c1 Some progress 2024-04-19 22:32:57 -04:00
Lance Pioch
1c0e91a301 Show missing if we can’t connect 2024-04-19 22:32:52 -04:00
Lance Pioch
3db6593b0e Cast the enum to a string 2024-04-19 22:32:30 -04:00
Lance Pioch
2bb1caf308 Better color 2024-04-19 22:32:20 -04:00
Lance Pioch
807a6f02fd New Crowdin updates (#110) 2024-04-19 22:31:46 -04:00
Lance Pioch
367b9bd154 Fix this up 2024-04-19 21:59:11 -04:00
Lance Pioch
d30accbc71 Switch this back to datalist 2024-04-19 21:58:45 -04:00
Lance Pioch
716d298b75 Styling fixes 2024-04-19 18:08:17 -04:00
Charles
05c4610654 Allow uploading multiple eggs
I'm sure there is a cleaner way to do it, but this works :)

Also ran pint...
2024-04-19 12:59:29 -04:00
Charles
82c294ab63 Make allocations mobile friendly 2024-04-19 10:18:51 -04:00
Lance Pioch
ee142a26b0 Small refactor 2024-04-19 01:33:14 -04:00
Lance Pioch
dea310e9ab Do some fancy ports 2024-04-19 01:33:06 -04:00
Lance Pioch
daf2cb0ebc Specify split keys 2024-04-19 00:36:22 -04:00
Lance Pioch
2812129d00 Pull ip addresses into selector 2024-04-19 00:36:02 -04:00
Lance Pioch
89b6f70cde Merge pull request #108 from NeonSpectrum/fix/daemon-http-params
Fix missed adjustments on http parameters
2024-04-19 00:34:11 -04:00
Lance Pioch
ad372a754e Update app/Repositories/Daemon/DaemonFileRepository.php 2024-04-19 00:33:42 -04:00
NeonSpectrum
c0b1345e90 Remove withQueryParameters on get request 2024-04-19 11:37:02 +08:00
NeonSpectrum
11e6430d42 Fix missed adjustments on http parameters 2024-04-19 11:29:35 +08:00
Lance Pioch
1e10f250b4 Merge branch 'main' of github.com:pelican-dev/panel 2024-04-18 23:15:18 -04:00
Lance Pioch
4b23703f99 Cache the statuses for a bit 2024-04-18 23:07:15 -04:00
Lance Pioch
bab4315bb7 Don’t allow eggs to be selected for deletion 2024-04-18 23:06:57 -04:00
Lance Pioch
c4839708ce New Crowdin updates (#106) 2024-04-18 22:13:30 -04:00
Lance Pioch
fa379be99b Single quotes 2024-04-18 21:56:19 -04:00
iamkubi
12eee6f6a2 Merge pull request #107 from Boy132/fix/tests
Fix tests
2024-04-18 15:37:29 -07:00
Boy132
50240933a0 fix "uuid must be a string" 2024-04-18 23:58:55 +02:00
Lance Pioch
69b70bf649 Merge branch 'master' into issue/fix-3 2024-04-18 17:20:23 -04:00
notCharles
3e01e483fb Allow variables to be sorted
todo: save order in db in a new column 'sort'
2024-04-18 17:13:17 -04:00
Lance Pioch
ae189748f1 Fix styling 2024-04-18 17:08:10 -04:00
Lance Pioch
679c72d70e Merge branch '3.x'
# Conflicts:
#	composer.lock
#	resources/scripts/components/auth/LoginFormContainer.tsx
2024-04-18 16:50:39 -04:00
Lance Pioch
49e02a2574 Merge branch 'feature/filament' of github.com:pelican-dev/panel into feature/filament 2024-04-18 16:46:18 -04:00
Lance Pioch
ee735c9b77 Add skeleton for adding new allocations 2024-04-18 16:46:08 -04:00
Lance Pioch
e25ca5dfc1 Add separated repositories for eggs 2024-04-18 16:21:40 -04:00
Lance Pioch
422fc1a6b2 Add github for funding 2024-04-18 16:21:22 -04:00
Lance Pioch
0949362da5 Merge pull request #89 from pelican-dev/issue/fix-mobile
Improve Mobile Experience
2024-04-18 16:14:27 -04:00
Lance Pioch
65f59c446e Merge branch 'feature/filament' of github.com:pelican-dev/panel into feature/filament
# Conflicts:
#	composer.lock
2024-04-18 04:02:42 -04:00
Lance Pioch
556ab76fc5 Basic server status implementation 2024-04-18 03:53:28 -04:00
Lance Pioch
4c5072b5c0 When editing set the custom image selector correctly 2024-04-18 03:52:55 -04:00
Lance Pioch
25177d1685 Wip 2fa 2024-04-18 03:51:25 -04:00
Lance Pioch
5469dce6ca Remove unused variables 2024-04-18 03:50:45 -04:00
Lance Pioch
d659bf4349 Allow primary allocation to be changed 2024-04-18 03:50:33 -04:00
Lance Pioch
c5008a43e7 Use new enum 2024-04-18 03:50:20 -04:00
Lance Pioch
256a961e1b Add enums 2024-04-18 03:48:30 -04:00
Lance Pioch
d642987df4 Better phrasing 2024-04-17 01:32:47 -04:00
Lance Pioch
7d0fc80a80 Switch to components 2024-04-17 01:30:05 -04:00
Lance Pioch
fa0bc96611 Merge pull request #88 from pelican-dev/feature/code-editor
Add Monaco
2024-04-17 01:28:56 -04:00
Lance Pioch
c492fa285f Update languages 2024-04-17 00:23:35 -04:00
notCharles
11494bbad6 Should enable this, so it ya know.. works <3 2024-04-16 20:56:47 -04:00
notCharles
c83cc073f0 Major Mobile Improvements <3 2024-04-16 18:47:12 -04:00
notCharles
b7c0829af9 Add Monaco
Known issues...
Changing themes does not reload editor, F5 required.
Editing a variable/docker images clears code box render, not sure how to fix this? reload on view?
2024-04-15 19:00:49 -04:00
Lance Pioch
e899acbdbe Add docker container status enum 2024-04-15 02:15:06 -04:00
Lance Pioch
1f95430507 Update Laravel to latest version 2024-04-15 02:14:54 -04:00
Lance Pioch
b70ab0e6cc Adjust these 2024-04-15 01:39:59 -04:00
Lance Pioch
8ec4dc1b6e Cool status bars 2024-04-15 01:34:27 -04:00
Lance Pioch
9b9875a31b Whoops 2024-04-15 01:34:17 -04:00
Lance Pioch
56e2cac85f Always just start automatically for now 2024-04-15 01:34:07 -04:00
Lance Pioch
65359f87d0 Nobody needs to see my mistakes 2024-04-15 01:15:09 -04:00
Lance Pioch
058371ba7d Merge branch 'feature/filament' of github.com:pelican-dev/panel into feature/filament
# Conflicts:
#	app/Filament/Resources/ServerResource.php
2024-04-14 22:24:30 -04:00
notCharles
dc8d7aa3da Fix mariadb? 2024-04-14 20:53:50 -04:00
Scai
e1c6545507 styled database hosts layout 2024-04-15 01:55:44 +03:00
Scai
5c8097d9b7 cleanup server boilerplate 2024-04-15 01:34:55 +03:00
notCharles
6118ed91fa oops, readd this 2024-04-14 15:23:15 -04:00
notCharles
0814b82b7e pint 2024-04-14 15:23:03 -04:00
Lance Pioch
288d3a2cff Nobody look 2024-04-14 02:48:21 -04:00
Lance Pioch
e2399eb4e2 Hide this again 2024-04-14 02:41:56 -04:00
Lance Pioch
bfe8fc66ce Merge branch 'feature/filament' of github.com:pelican-dev/panel into feature/filament
# Conflicts:
#	app/Filament/Resources/ServerResource.php
2024-04-14 02:38:50 -04:00
Lance Pioch
dd1b25604a Do the additional allocations 2024-04-14 02:36:32 -04:00
Lance Pioch
b488253c76 Unused 2024-04-14 02:31:30 -04:00
Lance Pioch
56f96348f4 Allow disabling block io balancing 2024-04-14 02:31:19 -04:00
Lance Pioch
18cbaf7458 Add deletion button 2024-04-14 02:31:06 -04:00
Lance Pioch
9add408b6b Filter out null entries 2024-04-14 02:30:57 -04:00
notCharles
54eaf8ab0f Change colums
Closes https://github.com/pelican-dev/panel/issues/3
2024-04-13 21:51:22 -04:00
Scai
32a3b8dd9b add servers relationship to users 2024-04-14 01:50:43 +03:00
Lance Pioch
06c773c3b1 Prevent deleting nodes if there are active servers 2024-04-13 16:49:53 -04:00
Lance Pioch
18e5c17ebe Add languages to profile edit 2024-04-13 16:21:44 -04:00
Lance Pioch
287c657e60 Merge branch 'feature/filament' of github.com:pelican-dev/panel into feature/filament
# Conflicts:
#	app/Filament/Resources/ServerResource.php
#	app/Filament/Resources/ServerResource/Pages/CreateServer.php
2024-04-13 15:55:29 -04:00
Lance Pioch
f69d0823f4 Small updates 2024-04-13 15:52:54 -04:00
Scai
f26373dfd5 linting 2024-04-13 16:30:20 +03:00
Scai
c54217f236 Merge pull request #56 from pelican-dev/feature/mounts
Mounts: Relationships for Eggs & Nodes
2024-04-13 16:25:31 +03:00
Scai
25c1b251a3 added eggs, nodes relationship, structured the layout 2024-04-13 16:23:05 +03:00
Scai
9fafe2f42c remove timestamp columns 2024-04-13 14:52:22 +03:00
Lance Pioch
f25bd33f06 Add data lists 2024-04-12 14:59:54 -04:00
Scai
de02e8853d add mounts icons columns 2024-04-12 20:36:30 +03:00
Scai
4c09905503 fix error on no node created 2024-04-12 20:15:57 +03:00
Lance Pioch
a9a39ae502 Set defaults 2024-04-12 13:05:12 -04:00
Lance Pioch
99693367d3 Simplify and restrict api keys 2024-04-12 13:05:04 -04:00
Lance Pioch
05e9f12dc4 Switch default path 2024-04-12 12:52:00 -04:00
Lance Pioch
0b1712f653 Merge branch 'feature/filament' of github.com:pelican-dev/panel into feature/filament 2024-04-12 12:19:25 -04:00
Lance Pioch
8575f1b036 More icons and a couple tweaks 2024-04-12 12:18:41 -04:00
Scai
4c2278a7f2 fix route missing on database hosts 2024-04-12 19:09:01 +03:00
Scai
af4d1d1fee lower the size of logo on login 2024-04-12 18:19:39 +03:00
Scai
d52cb4c7d7 round the avatar 2024-04-12 18:01:24 +03:00
Lance Pioch
65acb3fd94 Add more icons 2024-04-12 02:31:31 -04:00
Lance Pioch
ecf54a3025 Double down on this to fix validation 2024-04-12 02:31:25 -04:00
Lance Pioch
180bfc30a8 Add server name generator 2024-04-12 02:29:39 -04:00
Lance Pioch
cbc255ddf8 Add server variables 2024-04-12 02:04:22 -04:00
Lance Pioch
15971aaa94 Auto expand the startup command 2024-04-12 02:04:03 -04:00
Lance Pioch
695ebd35a4 No another create 2024-04-12 02:03:36 -04:00
Lance Pioch
21247e91c7 Do the icons 2024-04-12 02:03:20 -04:00
Lance Pioch
d30471ae6a Fix this around 2024-04-11 21:03:06 -04:00
Lance Pioch
03292bb02d logo and favicon changes 2024-04-11 20:59:35 -04:00
Lance Pioch
297e292e06 Switch favicon 2024-04-11 20:59:00 -04:00
Lance Pioch
f32aa8609d Add toggle for custom image 2024-04-11 11:41:29 -04:00
Lance Pioch
f8550334dd Helper text for creating new allocations 2024-04-11 11:41:03 -04:00
Lance Pioch
da1bf320dd Auto select latest node 2024-04-11 11:31:51 -04:00
Lance Pioch
27f05b5f95 Expand text area to make it look nicer 2024-04-11 11:31:38 -04:00
Lance Pioch
203289fd38 This isn’t required 2024-04-11 11:31:25 -04:00
Lance Pioch
f9b93f284c Servers 2024-04-11 03:11:51 -04:00
Lance Pioch
24f9a8aeb1 Use default 2024-04-11 03:11:27 -04:00
Lance Pioch
c6eb6dc054 Return all allocation ids back 2024-04-11 03:11:19 -04:00
Lance Pioch
a16ef9743b Add missing relationship 2024-04-11 03:09:19 -04:00
Lance Pioch
6c8816c289 Liven this up 2024-04-11 03:08:56 -04:00
Lance Pioch
d4f325e6c5 Force usage of global search 2024-04-11 00:55:30 -04:00
Lance Pioch
93ec3bdfc4 Rename 2024-04-11 00:51:36 -04:00
Lance Pioch
a4fb9eea40 Get docker images working 2024-04-11 00:45:58 -04:00
Lance Pioch
f201a5eaf6 Easier to understand 2024-04-11 00:45:33 -04:00
Lance Pioch
6689e796a7 Better description 2024-04-11 00:45:26 -04:00
Lance Pioch
b2e5b4862d Add nice new intro page 2024-04-11 00:45:20 -04:00
Lance Pioch
546dc5c449 Fix from demo 2024-04-11 00:45:12 -04:00
Lance Pioch
f660611ed3 Add skeleton new page 2024-04-10 17:10:25 -04:00
Lance Pioch
89a507de69 Better email 2024-04-10 17:10:13 -04:00
Lance Pioch
3cc29ba7b6 Small adjustments 2024-04-10 16:11:27 -04:00
Lance Pioch
1660af94a5 New revolution 2024-04-09 21:07:14 -04:00
Lance Pioch
c51687246e Better icon 2024-04-09 21:02:02 -04:00
Lance Pioch
bc72d6103c We are stars 2024-04-09 21:01:57 -04:00
Lance Pioch
cf44e46490 Rename to owner 2024-04-09 21:01:51 -04:00
Lance Pioch
8311669e6c Show valid ip address 2024-04-09 21:01:46 -04:00
Lance Pioch
195557373c Clarify the resource name 2024-04-09 21:01:30 -04:00
Lance Pioch
e7055242e1 More small adjustments 2024-04-09 18:45:51 -04:00
Lance Pioch
6020b8d6a8 Small adjustments 2024-04-09 18:45:43 -04:00
Lance Pioch
7c14a2edff Remove the top bar 2024-04-09 18:45:31 -04:00
Lance Pioch
6307919546 Add database hosts 2024-04-09 18:45:01 -04:00
Lance Pioch
2dd53eee27 Clean up 2024-04-08 01:20:07 -04:00
Lance Pioch
4e0aaedc86 Add api keys 2024-04-08 00:59:55 -04:00
Lance Pioch
970d2b0f0f Add better profile page 2024-04-08 00:33:00 -04:00
Lance Pioch
b1cc4ef45c Squish them 2024-04-08 00:10:19 -04:00
Lance Pioch
17c20d6b91 Shorten nav sidebar 2024-04-07 20:13:39 -04:00
Lance Pioch
d05332662b Add some of these 2024-04-07 01:35:30 -04:00
Lance Pioch
42728fd9b9 Small tweaks 2024-04-07 01:35:20 -04:00
Lance Pioch
732db9a5b4 User resource adjustments 2024-04-06 23:58:10 -04:00
Lance Pioch
a19c8e72b3 Fix password saving 2024-04-06 22:56:57 -04:00
Lance Pioch
b0067c4e4b Top right user menu 2024-04-06 10:11:10 -04:00
Lance Pioch
a133503256 Simplify 2024-04-04 21:45:57 -04:00
Lance Pioch
3465d2fc64 Revert "Not used"
This reverts commit 1728cbf28b.
2024-04-04 21:45:21 -04:00
Lance Pioch
05ae2b2ecf Add home url 2024-04-04 21:43:31 -04:00
Lance Pioch
e00d9ed273 Add some allocations 2024-04-04 21:43:18 -04:00
Lance Pioch
bf8f90f479 More specific 2024-04-04 21:26:18 -04:00
Lance Pioch
978a4ac0a2 Adjust the mounts 2024-04-04 21:26:10 -04:00
Lance Pioch
aac522232f Update database form 2024-04-04 21:25:59 -04:00
Lance Pioch
95570724e6 Use tabs instead 2024-04-04 20:16:14 -04:00
Lance Pioch
a96c53f407 Make this hidden by default 2024-04-04 20:15:56 -04:00
Lance Pioch
f79ec37f5e Move this over 2024-04-04 20:15:41 -04:00
Lance Pioch
5d38f2ece6 Adjustments 2024-04-04 18:59:42 -04:00
Lance Pioch
cf13bfb1e4 Add dns checking field 2024-04-04 18:59:34 -04:00
Lance Pioch
051b5d6bea Move this up 2024-04-04 18:59:12 -04:00
Lance Pioch
a8532d1cd2 Fix colspan 2024-04-04 18:58:40 -04:00
Lance Pioch
a7d8f3b79f Better account for ip addresses 2024-04-04 18:58:19 -04:00
Lance Pioch
e634dd81b1 Take these out 2024-04-04 18:58:01 -04:00
Lance Pioch
c6f4ee3d57 Increase this slightly 2024-04-01 12:33:22 -04:00
Lance Pioch
f47b420785 Better display of tablular data 2024-04-01 12:33:16 -04:00
Lance Pioch
1add3ca605 Allow this to be swappable from here in the future 2024-03-31 02:20:23 -04:00
Lance Pioch
2b172e6d8b User adjustments 2024-03-31 02:09:23 -04:00
Lance Pioch
1728cbf28b Not used 2024-03-31 01:57:59 -04:00
Lance Pioch
766c6c08f4 Easy to copy and paste 2024-03-31 01:39:24 -04:00
Lance Pioch
400e4d783b Redirect them directly to the config 2024-03-31 01:07:03 -04:00
Lance Pioch
a8b3e2bfa4 Just prevent the user altogether 2024-03-31 01:06:49 -04:00
Lance Pioch
57649d1c08 Show warning to user 2024-03-31 00:14:48 -04:00
Lance Pioch
dc794c64ce This is supposed to be sent, but isn’t because it’s disabled, even though it’s dehydrated 2024-03-31 00:14:15 -04:00
Lance Pioch
eda3959748 Too slow for now 2024-03-31 00:13:17 -04:00
Lance Pioch
f5a0a0f8ba Better helper text 2024-03-31 00:12:25 -04:00
Lance Pioch
6e4f3f7191 Set better default 2024-03-31 00:11:56 -04:00
Lance Pioch
e276a07f1b Hide this for now 2024-03-30 20:32:48 -04:00
Lance Pioch
db9b3e9b67 Set the name to the first part of the domain 2024-03-30 20:32:20 -04:00
Lance Pioch
d32d0692f8 Icons 2024-03-30 20:31:36 -04:00
Lance Pioch
ebdca47fbc Go nuts with icons 2024-03-30 02:30:29 -04:00
Lance Pioch
957a335817 Prevent root from getting rid of last root 2024-03-30 02:29:57 -04:00
Lance Pioch
71a27862bd This isn’t used 2024-03-30 02:16:02 -04:00
Lance Pioch
a2b03895d7 Switch icon set 2024-03-30 02:15:56 -04:00
Lance Pioch
a93ebfd7bf Add licensing 2024-03-29 00:58:13 -04:00
Lance Pioch
3c43f3aa18 Don’t need this anymore 2024-03-28 17:01:09 -04:00
Lance Pioch
eb5c304f69 Remove these mount usages 2024-03-28 13:29:24 -04:00
Lance Pioch
0925e141b4 These don’t work 2024-03-28 13:27:14 -04:00
Lance Pioch
ad8d087fd9 Lazy load statuses 2024-03-27 00:07:21 -04:00
Lance Pioch
e50e3509bd More adjustments 2024-03-26 22:36:32 -04:00
Lance Pioch
090b2e6f1b Move this over for the import 2024-03-26 22:34:42 -04:00
Lance Pioch
0af0bea90d Update views 2024-03-26 22:00:39 -04:00
Lance Pioch
f452280cdb Favicon adjustments 2024-03-26 21:59:45 -04:00
Lance Pioch
8c892ac05d Adjustments 2024-03-26 20:52:56 -04:00
Lance Pioch
65384250d6 Add attribute 2024-03-26 20:52:32 -04:00
Lance Pioch
3e7bff2446 Consolidate these 2024-03-25 10:26:57 -04:00
Lance Pioch
b6d39c66d1 Allow adjusting timeout 2024-03-24 14:42:54 -04:00
Lance Pioch
03e1733b7d Update users 2024-03-24 14:42:45 -04:00
Lance Pioch
039ac40cf7 Update nodes 2024-03-24 14:42:36 -04:00
Lance Pioch
2664ba0774 Baseline 2024-03-24 01:48:03 -04:00
Lance Pioch
90efb4e827 Simplify the parsing 2024-03-24 01:47:30 -04:00
Lance Pioch
946d597a13 Switch route key over 2024-03-24 01:37:24 -04:00
Lance Pioch
0961d6314c Eggs should not be manually created 2024-03-24 01:36:32 -04:00
Lance Pioch
0ef015bb0e Allow admins to access filament 2024-03-23 20:17:48 -04:00
Lance Pioch
36ca708850 Add filament provider 2024-03-23 20:17:33 -04:00
Lance Pioch
1c539ff50c Add filament css and js 2024-03-23 20:17:21 -04:00
Lance Pioch
49d0865010 Add filament dependencies 2024-03-23 20:17:14 -04:00
1166 changed files with 51734 additions and 19031 deletions

10
.dockerignore Normal file
View File

@@ -0,0 +1,10 @@
.git
node_modules
vendor
database/database.sqlite
storage/debugbar/*.json
storage/logs/*.log
storage/framework/cache/data/*
storage/framework/sessions/*
storage/framework/testing
storage/framework/views/*.php

View File

@@ -1,44 +1,7 @@
APP_ENV=production
APP_DEBUG=false
APP_KEY=
APP_TIMEZONE=UTC
APP_URL=http://panel.test
APP_INSTALLED=false
APP_TIMEZONE=UTC
APP_LOCALE=en
APP_ENVIRONMENT_ONLY=true
LOG_CHANNEL=daily
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=panel
DB_USERNAME=panel
DB_PASSWORD=
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_STORE=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
HASHIDS_SALT=
HASHIDS_LENGTH=8
MAIL_MAILER=log
MAIL_HOST=smtp.example.com
MAIL_PORT=25
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="Pelican Admin"
# Set this to your domain to prevent it defaulting to 'localhost', causing mail servers such as Gmail to reject your mail
# MAIL_EHLO_DOMAIN=panel.example.com
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

View File

@@ -39,6 +39,7 @@ module.exports = {
'react/display-name': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-non-null-assertion': 0,
// 'react/no-unknown-property': ['error', { ignore: ['css'] }],
// This setup is required to avoid a spam of errors when running eslint about React being
// used before it is defined.
//

3
.github/FUNDING.yml vendored
View File

@@ -1 +1,2 @@
custom: [https://buy.stripe.com/14kdU99SI4UT7ni9AB, https://buy.stripe.com/14kaHXc0Q9b9372eUU]
github: pelican-dev
custom: [https://hub.pelican.dev/donors]

View File

@@ -33,7 +33,6 @@ body:
attributes:
label: Panel Version
description: Version number of your Panel (latest is not a version)
placeholder: 1.4.0
validations:
required: true
@@ -42,7 +41,6 @@ body:
attributes:
label: Wings Version
description: Version number of your Wings (latest is not a version)
placeholder: 1.4.2
validations:
required: true
@@ -68,7 +66,7 @@ body:
Run the following command to collect logs on your system.
Wings: `sudo wings diagnostics`
Panel: `tail -n 150 /var/www/pelican/storage/logs/laravel-$(date +%F).log | nc pelipaste.com 99`
Panel: `tail -n 150 /var/www/pelican/storage/logs/laravel-$(date +%F).log | curl -X POST -F 'c=@-' paste.pelistuff.com`
placeholder: "https://pelipaste.com/a1h6z"
render: bash
validations:

View File

@@ -4,6 +4,30 @@
# If using CentOS this file should be placed in:
# /etc/nginx/conf.d/
#
# The MIT License (MIT)
#
# Pterodactyl®
# Copyright © Dane Everitt <dane@daneeveritt.com> and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
server {
listen 80;
server_name _;

View File

@@ -1,81 +1,64 @@
#!/bin/ash -e
cd /app
mkdir -p /var/log/panel/logs/ /var/log/supervisord/ /var/log/nginx/ /var/log/php7/ \
&& chmod 777 /var/log/panel/logs/ \
&& ln -s /app/storage/logs/ /var/log/panel/
#mkdir -p /var/log/supervisord/ /var/log/php8/ \
## check for .env file and generate app keys if missing
if [ -f /app/var/.env ]; then
if [ -f /pelican-data/.env ]; then
echo "external vars exist."
rm -rf /app/.env
ln -s /app/var/.env /app/
rm -rf /var/www/html/.env
else
echo "external vars don't exist."
rm -rf /app/.env
touch /app/var/.env
rm -rf /var/www/html/.env
touch /pelican-data/.env
## manually generate a key because key generate --force fails
if [ -z $APP_KEY ]; then
echo -e "Generating key."
APP_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
echo -e "Generated app key: $APP_KEY"
echo -e "APP_KEY=$APP_KEY" > /app/var/.env
echo -e "APP_KEY=$APP_KEY" > /pelican-data/.env
else
echo -e "APP_KEY exists in environment, using that."
echo -e "APP_KEY=$APP_KEY" > /app/var/.env
echo -e "APP_KEY=$APP_KEY" > /pelican-data/.env
fi
ln -s /app/var/.env /app/
## enable installer
echo -e "APP_INSTALLED=false" >> /pelican-data/.env
fi
echo "Checking if https is required."
if [ -f /etc/nginx/http.d/panel.conf ]; then
echo "Using nginx config already in place."
if [ $LE_EMAIL ]; then
echo "Checking for cert update"
certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n
else
echo "No letsencrypt email is set"
fi
mkdir /pelican-data/database
ln -s /pelican-data/.env /var/www/html/
ln -s /pelican-data/database/database.sqlite /var/www/html/database/
if ! grep -q "APP_KEY=" .env || grep -q "APP_KEY=$" .env; then
echo "Generating APP_KEY..."
php artisan key:generate --force
else
echo "Checking if letsencrypt email is set."
if [ -z $LE_EMAIL ]; then
echo "No letsencrypt email is set using http config."
cp .github/docker/default.conf /etc/nginx/http.d/panel.conf
else
echo "writing ssl config"
cp .github/docker/default_ssl.conf /etc/nginx/http.d/panel.conf
echo "updating ssl config for domain"
sed -i "s|<domain>|$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/http.d/panel.conf
echo "generating certs"
certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n
fi
echo "Removing the default nginx config"
rm -rf /etc/nginx/http.d/default.conf
echo "APP_KEY is already set."
fi
if [[ -z $DB_PORT ]]; then
echo -e "DB_PORT not specified, defaulting to 3306"
DB_PORT=3306
fi
## check for DB up before starting the panel
echo "Checking database status."
until nc -z -v -w30 $DB_HOST $DB_PORT
do
echo "Waiting for database connection..."
# wait for 1 seconds before check again
sleep 1
done
## make sure the db is set up
echo -e "Migrating and Seeding D.B"
php artisan migrate --seed --force
echo -e "Migrating Database"
php artisan migrate --force
echo -e "Optimizing Filament"
php artisan filament:optimize
## start cronjobs for the queue
echo -e "Starting cron jobs."
crond -L /var/log/crond -l 5
echo -e "Starting supervisord."
export SUPERVISORD_CADDY=false
## disable caddy if SKIP_CADDY is set
if [[ -z $SKIP_CADDY ]]; then
echo "Starting PHP-FPM and Caddy"
export SUPERVISORD_CADDY=true
else
echo "Starting PHP-FPM only"
fi
chown -R www-data:www-data . /pelican-data/.env /pelican-data/database
echo "Starting Supervisord"
exec "$@"

View File

@@ -25,15 +25,15 @@ autostart=true
autorestart=true
[program:queue-worker]
command=/usr/local/bin/php /app/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
user=nginx
command=/usr/local/bin/php /var/www/html/artisan queue:work --tries=3
user=www-data
autostart=true
autorestart=true
[program:nginx]
command=/usr/sbin/nginx -g 'daemon off;'
autostart=true
autorestart=true
[program:caddy]
command=caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
autostart=%(ENV_SUPERVISORD_CADDY)s
autorestart=%(ENV_SUPERVISORD_CADDY)s
priority=10
stdout_events_enabled=true
stderr_events_enabled=true
stderr_events_enabled=true

View File

@@ -1,6 +1,9 @@
name: Build
on:
push:
branches:
- '**'
pull_request:
branches:
- '**'
@@ -12,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [18]
node-version: [18, 20]
steps:
- name: Code Checkout
uses: actions/checkout@v4

View File

@@ -1,19 +1,20 @@
name: Tests
on:
pull_request:
push:
branches:
- '**'
- main
pull_request:
jobs:
tests:
name: Tests
mysql:
name: MySQL
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: [8.2, 8.3]
database: ["mariadb:10.2", "mysql:8"]
database: ["mysql:8"]
services:
database:
image: ${{ matrix.database }}
@@ -23,6 +24,22 @@ jobs:
ports:
- 3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
env:
APP_ENV: testing
APP_DEBUG: "false"
APP_KEY: ThisIsARandomStringForTests12345
APP_TIMEZONE: UTC
APP_URL: http://localhost/
CACHE_DRIVER: array
MAIL_MAILER: array
SESSION_DRIVER: array
QUEUE_CONNECTION: sync
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
GUZZLE_TIMEOUT: 60
GUZZLE_CONNECT_TIMEOUT: 60
steps:
- name: Code Checkout
uses: actions/checkout@v4
@@ -44,22 +61,15 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: bcmath, cli, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: Setup .env
run: cp .env.example .env
- name: Install dependencies
run: composer install --no-interaction --no-suggest --prefer-dist
- name: Generate App Key
run: php artisan key:generate
- name: Unit tests
run: vendor/bin/phpunit tests/Unit
if: ${{ always() }}
env:
DB_HOST: UNIT_NO_DB
SKIP_MIGRATIONS: true
@@ -69,3 +79,137 @@ jobs:
env:
DB_PORT: ${{ job.services.database.ports[3306] }}
DB_USERNAME: root
mariadb:
name: MariaDB
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: [8.2, 8.3]
database: ["mariadb:10.3", "mariadb:10.11", "mariadb:11.4"]
services:
database:
image: ${{ matrix.database }}
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: testing
ports:
- 3306
options: --health-cmd="mariadb-admin ping || mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
env:
APP_ENV: testing
APP_DEBUG: "false"
APP_KEY: ThisIsARandomStringForTests12345
APP_TIMEZONE: UTC
APP_URL: http://localhost/
CACHE_DRIVER: array
MAIL_MAILER: array
SESSION_DRIVER: array
QUEUE_CONNECTION: sync
DB_CONNECTION: mariadb
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
GUZZLE_TIMEOUT: 60
GUZZLE_CONNECT_TIMEOUT: 60
steps:
- name: Code Checkout
uses: actions/checkout@v4
- name: Get cache directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-${{ matrix.php }}-
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: Install dependencies
run: composer install --no-interaction --no-suggest --prefer-dist
- name: Unit tests
run: vendor/bin/phpunit tests/Unit
env:
DB_HOST: UNIT_NO_DB
SKIP_MIGRATIONS: true
- name: Integration tests
run: vendor/bin/phpunit tests/Integration
env:
DB_PORT: ${{ job.services.database.ports[3306] }}
DB_USERNAME: root
sqlite:
name: SQLite
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: [8.2, 8.3]
env:
APP_ENV: testing
APP_DEBUG: "false"
APP_KEY: ThisIsARandomStringForTests12345
APP_TIMEZONE: UTC
APP_URL: http://localhost/
CACHE_DRIVER: array
MAIL_MAILER: array
SESSION_DRIVER: array
QUEUE_CONNECTION: sync
DB_CONNECTION: sqlite
DB_DATABASE: testing.sqlite
GUZZLE_TIMEOUT: 60
GUZZLE_CONNECT_TIMEOUT: 60
steps:
- name: Code Checkout
uses: actions/checkout@v4
- name: Get cache directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-${{ matrix.php }}-
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: Install dependencies
run: composer install --no-interaction --no-suggest --prefer-dist
- name: Create SQLite file
run: touch database/testing.sqlite
- name: Unit tests
run: vendor/bin/phpunit tests/Unit
env:
DB_HOST: UNIT_NO_DB
SKIP_MIGRATIONS: true
- name: Integration tests
run: vendor/bin/phpunit tests/Integration

View File

@@ -7,7 +7,7 @@ on:
permissions:
actions: write
contents: write
contents: read
pull-requests: write
statuses: write
@@ -17,13 +17,13 @@ jobs:
steps:
- name: "CLA Assistant"
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
uses: contributor-assistant/github-action@v2.3.0
uses: contributor-assistant/github-action@v2.4.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_PERSONAL_ACCESS_TOKEN }}
with:
path-to-signatures: 'version1/cla.json'
path-to-document: 'https://github.com/pelican-dev/panel/blob/3.x/contributor_license_agreement.md'
path-to-document: 'https://github.com/pelican-dev/panel/blob/main/contributor_license_agreement.md'
branch: 'main'
allowlist: dependabot[bot]
remote-organization-name: pelican-dev

82
.github/workflows/docker-publish.yml vendored Normal file
View File

@@ -0,0 +1,82 @@
name: Docker
on:
push:
branches:
- main
release:
types:
- published
jobs:
build-and-push:
name: Build and Push
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# Always run against a tag, even if the commit into the tag has [docker skip] within the commit message.
if: "!contains(github.ref, 'main') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))"
steps:
- name: Code checkout
uses: actions/checkout@v4
- name: Docker metadata
id: docker_meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/pelican-dev/panel
flavor: |
latest=false
tags: |
type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.action == 'published' && github.event.release.prerelease == false }}
type=ref,event=tag
type=ref,event=branch
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get Build Information
id: build_info
run: |
echo "version_tag=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Build and Push (tag)
uses: docker/build-push-action@v5
if: "github.event_name == 'release' && github.event.action == 'published'"
with:
context: .
file: ./Dockerfile
push: true
platforms: linux/amd64,linux/arm64
build-args: |
VERSION=${{ steps.build_info.outputs.version_tag }}
labels: ${{ steps.docker_meta.outputs.labels }}
tags: ${{ steps.docker_meta.outputs.tags }}
- name: Build and Push (main)
uses: docker/build-push-action@v5
if: "github.event_name == 'push' && contains(github.ref, 'main')"
with:
context: .
file: ./Dockerfile
push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64,linux/arm64
build-args: |
VERSION=dev-${{ steps.build_info.outputs.short_sha }}
labels: ${{ steps.docker_meta.outputs.labels }}
tags: ${{ steps.docker_meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@@ -6,8 +6,8 @@ on:
- '**'
jobs:
lint:
name: Lint
pint:
name: Pint
runs-on: ubuntu-latest
steps:
- name: Code Checkout
@@ -16,7 +16,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.2"
php-version: "8.3"
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
@@ -29,3 +29,26 @@ jobs:
- name: Pint
run: vendor/bin/pint --test
phpstan:
name: PHPStan
runs-on: ubuntu-latest
steps:
- name: Code Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
tools: composer:v2
coverage: none
- name: Setup .env
run: cp .env.example .env
- name: Install dependencies
run: composer install --no-interaction --no-progress --prefer-dist
- name: PHPStan
run: vendor/bin/phpstan --memory-limit=-1

View File

@@ -8,15 +8,18 @@ on:
jobs:
release:
name: Release
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
permissions:
contents: write
steps:
- name: Code checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 20
cache: "yarn"
- name: Install dependencies
@@ -30,8 +33,8 @@ jobs:
REF: ${{ github.ref }}
run: |
BRANCH=release/${REF:10}
git config --local user.email "ci@pelican.dev"
git config --local user.name "Pelican CI"
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git checkout -b $BRANCH
git push -u origin $BRANCH
sed -i "s/ 'version' => 'canary',/ 'version' => '${REF:11}',/" config/app.php
@@ -44,46 +47,19 @@ jobs:
rm -rf node_modules tests CODE_OF_CONDUCT.md CONTRIBUTING.md flake.lock flake.nix phpunit.xml shell.nix
tar -czf panel.tar.gz * .editorconfig .env.example .eslintignore .eslintrc.js .gitignore .prettierrc.json
- name: Extract changelog
env:
REF: ${{ github.ref }}
run: |
sed -n "/^## ${REF:10}/,/^## /{/^## /b;p}" CHANGELOG.md > ./RELEASE_CHANGELOG
- name: Create checksum and add to changelog
- name: Create checksum
run: |
SUM=`sha256sum panel.tar.gz`
echo -e "\n#### SHA256 Checksum\n\n\`\`\`\n$SUM\n\`\`\`\n" >> ./RELEASE_CHANGELOG
echo $SUM > checksum.txt
- name: Create release
id: create_release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
draft: true
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
body_path: ./RELEASE_CHANGELOG
- name: Upload release archive
id: upload-release-archive
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: panel.tar.gz
asset_name: panel.tar.gz
asset_content_type: application/gzip
- name: Upload release checksum
id: upload-release-checksum
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./checksum.txt
asset_name: checksum.txt
asset_content_type: text/plain
files: |
panel.tar.gz
checksum.txt

60
.gitignore vendored
View File

@@ -1,36 +1,28 @@
/vendor
*.DS_Store*
!.env.ci
!.env.example
.env*
.vagrant/*
.vscode/*
storage/framework/*
/.idea
/nbproject
/.direnv
node_modules
*.log
_ide_helper.php
_ide_helper_models.php
.phpstorm.meta.php
.yarn
public/assets/manifest.json
# For local development with docker
# Remove if we ever put the Dockerfile in the repo
.dockerignore
docker-compose.yml
# for image related files
misc
.php-cs-fixer.cache
coverage.xml
resources/lang/locales.js
.phpunit.result.cache
/.phpunit.cache
/node_modules
/public/build
/public/hot
result
docker-compose.yaml
/public/storage
/storage/*.key
/storage/clockwork/*
/vendor
*.DS_Store*
.env
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode
public/assets/manifest.json
/database/*.sqlite
filament-monaco-editor/
_ide_helper*
/.phpstorm.meta.php

12
Caddyfile Normal file
View File

@@ -0,0 +1,12 @@
{
admin off
email {$ADMIN_EMAIL}
}
{$APP_URL} {
root * /var/www/html/public
encode gzip
php_fastcgi 127.0.0.1:9000
file_server
}

View File

@@ -1,41 +1,58 @@
# Stage 0:
# Build the assets that are needed for the frontend. This build stage is then discarded
# since we won't need NodeJS anymore in the future. This Docker image ships a final production
# level distribution
FROM --platform=$TARGETOS/$TARGETARCH mhart/alpine-node:14
WORKDIR /app
# Pelican Production Dockerfile
FROM node:20-alpine AS yarn
#FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine AS yarn
WORKDIR /build
COPY . ./
RUN yarn install --frozen-lockfile \
RUN yarn config set network-timeout 300000 \
&& yarn install --frozen-lockfile \
&& yarn run build:production
# Stage 1:
# Build the actual container with all of the needed PHP dependencies that will run the application.
FROM --platform=$TARGETOS/$TARGETARCH php:8.1-fpm-alpine
WORKDIR /app
COPY . ./
COPY --from=0 /app/public/assets ./public/assets
RUN apk add --no-cache --update ca-certificates dcron curl git supervisor tar unzip nginx libpng-dev libxml2-dev libzip-dev certbot certbot-nginx \
&& docker-php-ext-configure zip \
&& docker-php-ext-install bcmath gd pdo_mysql zip \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& cp .env.example .env \
&& mkdir -p bootstrap/cache/ storage/logs storage/framework/sessions storage/framework/views storage/framework/cache \
&& chmod 777 -R bootstrap storage \
&& composer install --no-dev --optimize-autoloader \
&& rm -rf .env bootstrap/cache/*.php \
&& mkdir -p /app/storage/logs/ \
&& chown -R nginx:nginx .
FROM php:8.3-fpm-alpine
# FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
RUN rm /usr/local/etc/php-fpm.conf \
&& echo "* * * * * /usr/local/bin/php /app/artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root \
&& echo "0 23 * * * certbot renew --nginx --quiet" >> /var/spool/cron/crontabs/root \
&& sed -i s/ssl_session_cache/#ssl_session_cache/g /etc/nginx/nginx.conf \
&& mkdir -p /var/run/php /var/run/nginx
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
COPY .github/docker/default.conf /etc/nginx/http.d/default.conf
COPY .github/docker/www.conf /usr/local/etc/php-fpm.conf
COPY .github/docker/supervisord.conf /etc/supervisord.conf
WORKDIR /var/www/html
# Install dependencies
RUN apk update && apk add --no-cache \
libpng-dev libjpeg-turbo-dev freetype-dev libzip-dev icu-dev \
zip unzip curl \
caddy ca-certificates supervisor \
&& docker-php-ext-install bcmath gd intl zip opcache pcntl posix pdo_mysql
# Copy the Caddyfile to the container
COPY Caddyfile /etc/caddy/Caddyfile
# Copy the application code to the container
COPY . .
COPY --from=yarn /build/public/assets ./public/assets
RUN touch .env
RUN composer install --no-dev --optimize-autoloader
# Set file permissions
RUN chmod -R 755 storage bootstrap/cache
# Add scheduler to cron
RUN echo "* * * * * php /var/www/html/artisan schedule:run >> /dev/null 2>&1" | crontab -u www-data -
## supervisord config and log dir
RUN cp .github/docker/supervisord.conf /etc/supervisord.conf && \
mkdir /var/log/supervisord/
HEALTHCHECK --interval=5m --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost/up || exit 1
EXPOSE 80 443
VOLUME /pelican-data
ENTRYPOINT [ "/bin/ash", ".github/docker/entrypoint.sh" ]
CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ]

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Casts;
use App\Models\Objects\Endpoint;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Collection;
class EndpointCollection implements Castable
{
public static function castUsing(array $arguments)
{
return new class() implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
if (!isset($attributes[$key])) {
return new Collection();
}
$data = json_decode($attributes[$key], true);
return (new Collection($data))->map(function ($value) {
return new Endpoint($value);
});
}
public function set($model, $key, $value, $attributes)
{
if (!is_array($value) && !$value instanceof Collection) {
return new Collection();
}
if (!$value instanceof Collection) {
$value = new Collection($value);
}
return [
'ports' => $value->toJson(),
];
}
};
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Console\Commands\Egg;
use App\Models\Egg;
use App\Services\Eggs\Sharing\EggExporterService;
use Exception;
use Illuminate\Console\Command;
class CheckEggUpdatesCommand extends Command
{
protected $signature = 'p:egg:check-updates';
public function handle(EggExporterService $exporterService): void
{
$eggs = Egg::all();
foreach ($eggs as $egg) {
try {
if (is_null($egg->update_url)) {
$this->comment("{$egg->name}: Skipping (no update url set)");
continue;
}
$currentJson = json_decode($exporterService->handle($egg->id));
unset($currentJson->exported_at);
$updatedJson = json_decode(file_get_contents($egg->update_url));
unset($updatedJson->exported_at);
if (md5(json_encode($currentJson)) === md5(json_encode($updatedJson))) {
$this->info("{$egg->name}: Up-to-date");
cache()->put("eggs.{$egg->uuid}.update", false, now()->addHour());
} else {
$this->warn("{$egg->name}: Found update");
cache()->put("eggs.{$egg->uuid}.update", true, now()->addHour());
}
} catch (Exception $exception) {
$this->error("{$egg->name}: Error ({$exception->getMessage()})");
}
}
}
}

View File

@@ -3,174 +3,27 @@
namespace App\Console\Commands\Environment;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
use App\Traits\Commands\EnvironmentWriterTrait;
use Illuminate\Support\Facades\Artisan;
class AppSettingsCommand extends Command
{
use EnvironmentWriterTrait;
public const CACHE_DRIVERS = [
'redis' => 'Redis',
'memcached' => 'Memcached',
'file' => 'Filesystem (recommended)',
];
public const SESSION_DRIVERS = [
'redis' => 'Redis',
'memcached' => 'Memcached',
'database' => 'MySQL Database',
'file' => 'Filesystem (recommended)',
'cookie' => 'Cookie',
];
public const QUEUE_DRIVERS = [
'redis' => 'Redis',
'database' => 'MySQL Database',
'sync' => 'Sync (recommended)',
];
protected $description = 'Configure basic environment settings for the Panel.';
protected $signature = 'p:environment:setup
{--new-salt : Whether or not to generate a new salt for Hashids.}
{--author= : The email that services created on this instance should be linked to.}
{--url= : The URL that this Panel is running on.}
{--timezone= : The timezone to use for Panel times.}
{--cache= : The cache driver backend to use.}
{--session= : The session driver backend to use.}
{--queue= : The queue driver backend to use.}
{--redis-host= : Redis host to use for connections.}
{--redis-pass= : Password used to connect to redis.}
{--redis-port= : Port to connect to redis over.}
{--settings-ui= : Enable or disable the settings UI.}';
protected $signature = 'p:environment:setup';
protected array $variables = [];
/**
* AppSettingsCommand constructor.
*/
public function __construct(private Kernel $console)
public function handle(): void
{
parent::__construct();
}
/**
* Handle command execution.
*
* @throws \App\Exceptions\PanelException
*/
public function handle(): int
{
if (empty(config('hashids.salt')) || $this->option('new-salt')) {
$this->variables['HASHIDS_SALT'] = str_random(20);
$path = base_path('.env');
if (!file_exists($path)) {
$this->comment('Copying example .env file');
copy($path . '.example', $path);
}
$this->output->comment('Provide the email address that eggs exported by this Panel should be from. This should be a valid email address.');
$this->variables['APP_SERVICE_AUTHOR'] = $this->option('author') ?? $this->ask(
'Egg Author Email',
config('panel.service.author', 'unknown@unknown.com')
);
if (!filter_var($this->variables['APP_SERVICE_AUTHOR'], FILTER_VALIDATE_EMAIL)) {
$this->output->error('The service author email provided is invalid.');
return 1;
if (!config('app.key')) {
$this->comment('Generating app key');
Artisan::call('key:generate');
}
$this->output->comment('The application URL MUST begin with https:// or http:// depending on if you are using SSL or not. If you do not include the scheme your emails and other content will link to the wrong location.');
$this->variables['APP_URL'] = $this->option('url') ?? $this->ask(
'Application URL',
config('app.url', 'https://example.com')
);
$this->output->comment('The timezone should match one of PHP\'s supported timezones. If you are unsure, please reference https://php.net/manual/en/timezones.php.');
$this->variables['APP_TIMEZONE'] = $this->option('timezone') ?? $this->anticipate(
'Application Timezone',
\DateTimeZone::listIdentifiers(),
config('app.timezone')
);
$selected = config('cache.default', 'file');
$this->variables['CACHE_STORE'] = $this->option('cache') ?? $this->choice(
'Cache Driver',
self::CACHE_DRIVERS,
array_key_exists($selected, self::CACHE_DRIVERS) ? $selected : null
);
$selected = config('session.driver', 'file');
$this->variables['SESSION_DRIVER'] = $this->option('session') ?? $this->choice(
'Session Driver',
self::SESSION_DRIVERS,
array_key_exists($selected, self::SESSION_DRIVERS) ? $selected : null
);
$selected = config('queue.default', 'sync');
$this->variables['QUEUE_CONNECTION'] = $this->option('queue') ?? $this->choice(
'Queue Driver',
self::QUEUE_DRIVERS,
array_key_exists($selected, self::QUEUE_DRIVERS) ? $selected : null
);
if (!is_null($this->option('settings-ui'))) {
$this->variables['APP_ENVIRONMENT_ONLY'] = $this->option('settings-ui') == 'true' ? 'false' : 'true';
} else {
$this->variables['APP_ENVIRONMENT_ONLY'] = $this->confirm('Enable UI based settings editor?', true) ? 'false' : 'true';
}
// Make sure session cookies are set as "secure" when using HTTPS
if (str_starts_with($this->variables['APP_URL'], 'https://')) {
$this->variables['SESSION_SECURE_COOKIE'] = 'true';
}
$this->checkForRedis();
$this->writeToEnvironment($this->variables);
$this->info($this->console->output());
return 0;
}
/**
* Check if redis is selected, if so, request connection details and verify them.
*/
private function checkForRedis()
{
$items = collect($this->variables)->filter(function ($item) {
return $item === 'redis';
});
// Redis was not selected, no need to continue.
if (count($items) === 0) {
return;
}
$this->output->note('You\'ve selected the Redis driver for one or more options, please provide valid connection information below. In most cases you can use the defaults provided unless you have modified your setup.');
$this->variables['REDIS_HOST'] = $this->option('redis-host') ?? $this->ask(
'Redis Host',
config('database.redis.default.host')
);
$askForRedisPassword = true;
if (!empty(config('database.redis.default.password'))) {
$this->variables['REDIS_PASSWORD'] = config('database.redis.default.password');
$askForRedisPassword = $this->confirm('It seems a password is already defined for Redis, would you like to change it?');
}
if ($askForRedisPassword) {
$this->output->comment('By default a Redis server instance has no password as it is running locally and inaccessible to the outside world. If this is the case, simply hit enter without entering a value.');
$this->variables['REDIS_PASSWORD'] = $this->option('redis-pass') ?? $this->output->askHidden(
'Redis Password'
);
}
if (empty($this->variables['REDIS_PASSWORD'])) {
$this->variables['REDIS_PASSWORD'] = 'null';
}
$this->variables['REDIS_PORT'] = $this->option('redis-port') ?? $this->ask(
'Redis Port',
config('database.redis.default.port')
);
Artisan::call('filament:optimize');
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Console\Commands\Environment;
use App\Traits\Commands\RequestRedisSettingsTrait;
use App\Traits\EnvironmentWriterTrait;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
class CacheSettingsCommand extends Command
{
use EnvironmentWriterTrait;
use RequestRedisSettingsTrait;
public const CACHE_DRIVERS = [
'file' => 'Filesystem (default)',
'database' => 'Database',
'redis' => 'Redis',
];
protected $description = 'Configure cache settings for the Panel.';
protected $signature = 'p:environment:cache
{--driver= : The cache driver backend to use.}
{--redis-host= : Redis host to use for connections.}
{--redis-user= : User used to connect to redis.}
{--redis-pass= : Password used to connect to redis.}
{--redis-port= : Port to connect to redis over.}';
protected array $variables = [];
/**
* CacheSettingsCommand constructor.
*/
public function __construct(private Kernel $console)
{
parent::__construct();
}
/**
* Handle command execution.
*/
public function handle(): int
{
$selected = config('cache.default', 'file');
$this->variables['CACHE_STORE'] = $this->option('driver') ?? $this->choice(
'Cache Driver',
self::CACHE_DRIVERS,
array_key_exists($selected, self::CACHE_DRIVERS) ? $selected : null
);
if ($this->variables['CACHE_STORE'] === 'redis') {
$this->requestRedisSettings();
if (config('queue.default') !== 'sync') {
$this->call('p:environment:queue-service', [
'--overwrite' => true,
]);
}
}
$this->writeToEnvironment($this->variables);
$this->info($this->console->output());
return 0;
}
}

View File

@@ -2,23 +2,30 @@
namespace App\Console\Commands\Environment;
use App\Traits\EnvironmentWriterTrait;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Database\DatabaseManager;
use App\Traits\Commands\EnvironmentWriterTrait;
class DatabaseSettingsCommand extends Command
{
use EnvironmentWriterTrait;
public const DATABASE_DRIVERS = [
'sqlite' => 'SQLite (recommended)',
'mariadb' => 'MariaDB',
'mysql' => 'MySQL',
];
protected $description = 'Configure database settings for the Panel.';
protected $signature = 'p:environment:database
{--host= : The connection address for the MySQL server.}
{--port= : The connection port for the MySQL server.}
{--driver= : The database driver backend to use.}
{--database= : The database to use.}
{--username= : Username to use when connecting.}
{--password= : Password to use for this database.}';
{--host= : The connection address for the MySQL/ MariaDB server.}
{--port= : The connection port for the MySQL/ MariaDB server.}
{--username= : Username to use when connecting to the MySQL/ MariaDB server.}
{--password= : Password to use for the MySQL/ MariaDB database.}';
protected array $variables = [];
@@ -35,51 +42,145 @@ class DatabaseSettingsCommand extends Command
*/
public function handle(): int
{
$this->output->note('It is highly recommended to not use "localhost" as your database host as we have seen frequent socket connection issues. If you want to use a local connection you should be using "127.0.0.1".');
$this->variables['DB_HOST'] = $this->option('host') ?? $this->ask(
'Database Host',
config('database.connections.mysql.host', '127.0.0.1')
);
$this->variables['DB_PORT'] = $this->option('port') ?? $this->ask(
'Database Port',
config('database.connections.mysql.port', 3306)
);
$this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask(
'Database Name',
config('database.connections.mysql.database', 'panel')
);
$this->output->note('Using the "root" account for MySQL connections is not only highly frowned upon, it is also not allowed by this application. You\'ll need to have created a MySQL user for this software.');
$this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask(
'Database Username',
config('database.connections.mysql.username', 'panel')
);
$askForMySQLPassword = true;
if (!empty(config('database.connections.mysql.password')) && $this->input->isInteractive()) {
$this->variables['DB_PASSWORD'] = config('database.connections.mysql.password');
$askForMySQLPassword = $this->confirm('It appears you already have a MySQL connection password defined, would you like to change it?');
$this->error('Changing the database driver will NOT move any database data!');
$this->error('Please make sure you made a database backup first!');
$this->error('After changing the driver you will have to manually move the old data to the new database.');
if (!$this->confirm('Do you want to continue?')) {
return 1;
}
if ($askForMySQLPassword) {
$this->variables['DB_PASSWORD'] = $this->option('password') ?? $this->secret('Database Password');
}
$selected = config('database.default', 'sqlite');
$this->variables['DB_CONNECTION'] = $this->option('driver') ?? $this->choice(
'Database Driver',
self::DATABASE_DRIVERS,
array_key_exists($selected, self::DATABASE_DRIVERS) ? $selected : null
);
try {
$this->testMySQLConnection();
} catch (\PDOException $exception) {
$this->output->error(sprintf('Unable to connect to the MySQL server using the provided credentials. The error returned was "%s".', $exception->getMessage()));
$this->output->error('Your connection credentials have NOT been saved. You will need to provide valid connection information before proceeding.');
if ($this->variables['DB_CONNECTION'] === 'mysql') {
$this->output->note(__('commands.database_settings.DB_HOST_note'));
$this->variables['DB_HOST'] = $this->option('host') ?? $this->ask(
'Database Host',
config('database.connections.mysql.host', '127.0.0.1')
);
if ($this->confirm('Go back and try again?')) {
$this->database->disconnect('_panel_command_test');
$this->variables['DB_PORT'] = $this->option('port') ?? $this->ask(
'Database Port',
config('database.connections.mysql.port', 3306)
);
return $this->handle();
$this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask(
'Database Name',
config('database.connections.mysql.database', 'panel')
);
$this->output->note(__('commands.database_settings.DB_USERNAME_note'));
$this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask(
'Database Username',
config('database.connections.mysql.username', 'pelican')
);
$askForMySQLPassword = true;
if (!empty(config('database.connections.mysql.password')) && $this->input->isInteractive()) {
$this->variables['DB_PASSWORD'] = config('database.connections.mysql.password');
$askForMySQLPassword = $this->confirm(__('commands.database_settings.DB_PASSWORD_note'));
}
return 1;
if ($askForMySQLPassword) {
$this->variables['DB_PASSWORD'] = $this->option('password') ?? $this->secret('Database Password');
}
try {
// Test connection
config()->set('database.connections._panel_command_test', [
'driver' => 'mysql',
'host' => $this->variables['DB_HOST'],
'port' => $this->variables['DB_PORT'],
'database' => $this->variables['DB_DATABASE'],
'username' => $this->variables['DB_USERNAME'],
'password' => $this->variables['DB_PASSWORD'],
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'strict' => true,
]);
$this->database->connection('_panel_command_test')->getPdo();
} catch (\PDOException $exception) {
$this->output->error(sprintf('Unable to connect to the MySQL server using the provided credentials. The error returned was "%s".', $exception->getMessage()));
$this->output->error(__('commands.database_settings.DB_error_2'));
if ($this->confirm(__('commands.database_settings.go_back'))) {
$this->database->disconnect('_panel_command_test');
return $this->handle();
}
return 1;
}
} elseif ($this->variables['DB_CONNECTION'] === 'mariadb') {
$this->output->note(__('commands.database_settings.DB_HOST_note'));
$this->variables['DB_HOST'] = $this->option('host') ?? $this->ask(
'Database Host',
config('database.connections.mariadb.host', '127.0.0.1')
);
$this->variables['DB_PORT'] = $this->option('port') ?? $this->ask(
'Database Port',
config('database.connections.mariadb.port', 3306)
);
$this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask(
'Database Name',
config('database.connections.mariadb.database', 'panel')
);
$this->output->note(__('commands.database_settings.DB_USERNAME_note'));
$this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask(
'Database Username',
config('database.connections.mariadb.username', 'pelican')
);
$askForMariaDBPassword = true;
if (!empty(config('database.connections.mariadb.password')) && $this->input->isInteractive()) {
$this->variables['DB_PASSWORD'] = config('database.connections.mariadb.password');
$askForMariaDBPassword = $this->confirm(__('commands.database_settings.DB_PASSWORD_note'));
}
if ($askForMariaDBPassword) {
$this->variables['DB_PASSWORD'] = $this->option('password') ?? $this->secret('Database Password');
}
try {
// Test connection
config()->set('database.connections._panel_command_test', [
'driver' => 'mariadb',
'host' => $this->variables['DB_HOST'],
'port' => $this->variables['DB_PORT'],
'database' => $this->variables['DB_DATABASE'],
'username' => $this->variables['DB_USERNAME'],
'password' => $this->variables['DB_PASSWORD'],
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'strict' => true,
]);
$this->database->connection('_panel_command_test')->getPdo();
} catch (\PDOException $exception) {
$this->output->error(sprintf('Unable to connect to the MariaDB server using the provided credentials. The error returned was "%s".', $exception->getMessage()));
$this->output->error(__('commands.database_settings.DB_error_2'));
if ($this->confirm(__('commands.database_settings.go_back'))) {
$this->database->disconnect('_panel_command_test');
return $this->handle();
}
return 1;
}
} elseif ($this->variables['DB_CONNECTION'] === 'sqlite') {
$this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask(
'Database Path',
env('DB_DATABASE', 'database.sqlite')
);
}
$this->writeToEnvironment($this->variables);
@@ -88,24 +189,4 @@ class DatabaseSettingsCommand extends Command
return 0;
}
/**
* Test that we can connect to the provided MySQL instance and perform a selection.
*/
private function testMySQLConnection()
{
config()->set('database.connections._panel_command_test', [
'driver' => 'mysql',
'host' => $this->variables['DB_HOST'],
'port' => $this->variables['DB_PORT'],
'database' => $this->variables['DB_DATABASE'],
'username' => $this->variables['DB_USERNAME'],
'password' => $this->variables['DB_PASSWORD'],
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'strict' => true,
]);
$this->database->connection('_panel_command_test')->getPdo();
}
}

View File

@@ -2,8 +2,8 @@
namespace App\Console\Commands\Environment;
use App\Traits\EnvironmentWriterTrait;
use Illuminate\Console\Command;
use App\Traits\Commands\EnvironmentWriterTrait;
class EmailSettingsCommand extends Command
{
@@ -31,19 +31,20 @@ class EmailSettingsCommand extends Command
*/
public function handle(): void
{
$this->variables['MAIL_DRIVER'] = $this->option('driver') ?? $this->choice(
$this->variables['MAIL_MAILER'] = $this->option('driver') ?? $this->choice(
trans('command/messages.environment.mail.ask_driver'),
[
'log' => 'Log',
'smtp' => 'SMTP Server',
'sendmail' => 'sendmail Binary',
'mailgun' => 'Mailgun Transactional Email',
'mandrill' => 'Mandrill Transactional Email',
'postmark' => 'Postmark Transactional Email',
'mailgun' => 'Mailgun',
'mandrill' => 'Mandrill',
'postmark' => 'Postmark',
],
config('mail.default', 'smtp')
env('MAIL_MAILER', env('MAIL_DRIVER', 'smtp')),
);
$method = 'setup' . studly_case($this->variables['MAIL_DRIVER']) . 'DriverVariables';
$method = 'setup' . studly_case($this->variables['MAIL_MAILER']) . 'DriverVariables';
if (method_exists($this, $method)) {
$this->{$method}();
}
@@ -60,6 +61,8 @@ class EmailSettingsCommand extends Command
$this->writeToEnvironment($this->variables);
$this->call('queue:restart');
$this->line('Updating stored environment configuration file.');
$this->line('');
}
@@ -67,7 +70,7 @@ class EmailSettingsCommand extends Command
/**
* Handle variables for SMTP driver.
*/
private function setupSmtpDriverVariables()
private function setupSmtpDriverVariables(): void
{
$this->variables['MAIL_HOST'] = $this->option('host') ?? $this->ask(
trans('command/messages.environment.mail.ask_smtp_host'),
@@ -98,7 +101,7 @@ class EmailSettingsCommand extends Command
/**
* Handle variables for mailgun driver.
*/
private function setupMailgunDriverVariables()
private function setupMailgunDriverVariables(): void
{
$this->variables['MAILGUN_DOMAIN'] = $this->option('host') ?? $this->ask(
trans('command/messages.environment.mail.ask_mailgun_domain'),
@@ -119,7 +122,7 @@ class EmailSettingsCommand extends Command
/**
* Handle variables for mandrill driver.
*/
private function setupMandrillDriverVariables()
private function setupMandrillDriverVariables(): void
{
$this->variables['MANDRILL_SECRET'] = $this->option('password') ?? $this->ask(
trans('command/messages.environment.mail.ask_mandrill_secret'),
@@ -130,7 +133,7 @@ class EmailSettingsCommand extends Command
/**
* Handle variables for postmark driver.
*/
private function setupPostmarkDriverVariables()
private function setupPostmarkDriverVariables(): void
{
$this->variables['MAIL_DRIVER'] = 'smtp';
$this->variables['MAIL_HOST'] = 'smtp.postmarkapp.com';

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Console\Commands\Environment;
use App\Traits\Commands\RequestRedisSettingsTrait;
use App\Traits\EnvironmentWriterTrait;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
class QueueSettingsCommand extends Command
{
use EnvironmentWriterTrait;
use RequestRedisSettingsTrait;
public const QUEUE_DRIVERS = [
'database' => 'Database (default)',
'redis' => 'Redis',
'sync' => 'Synchronous',
];
protected $description = 'Configure queue settings for the Panel.';
protected $signature = 'p:environment:queue
{--driver= : The queue driver backend to use.}
{--redis-host= : Redis host to use for connections.}
{--redis-user= : User used to connect to redis.}
{--redis-pass= : Password used to connect to redis.}
{--redis-port= : Port to connect to redis over.}';
protected array $variables = [];
/**
* QueueSettingsCommand constructor.
*/
public function __construct(private Kernel $console)
{
parent::__construct();
}
/**
* Handle command execution.
*/
public function handle(): int
{
$selected = config('queue.default', 'database');
$this->variables['QUEUE_CONNECTION'] = $this->option('driver') ?? $this->choice(
'Queue Driver',
self::QUEUE_DRIVERS,
array_key_exists($selected, self::QUEUE_DRIVERS) ? $selected : null
);
if ($this->variables['QUEUE_CONNECTION'] === 'redis') {
$this->requestRedisSettings();
$this->call('p:environment:queue-service', [
'--overwrite' => true,
]);
}
$this->writeToEnvironment($this->variables);
$this->info($this->console->output());
return 0;
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Console\Commands\Environment;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process;
class QueueWorkerServiceCommand extends Command
{
protected $description = 'Create the service for the queue worker.';
protected $signature = 'p:environment:queue-service
{--service-name= : Name of the queue worker service.}
{--user= : The user that PHP runs under.}
{--group= : The group that PHP runs under.}
{--overwrite : Force overwrite if the service file already exists.}';
public function handle(): void
{
$serviceName = $this->option('service-name') ?? $this->ask('Queue worker service name', 'pelican-queue');
$path = '/etc/systemd/system/' . $serviceName . '.service';
$fileExists = file_exists($path);
if ($fileExists && !$this->option('overwrite') && !$this->confirm('The service file already exists. Do you want to overwrite it?')) {
$this->line('Creation of queue worker service file aborted because service file already exists.');
return;
}
$user = $this->option('user') ?? $this->ask('Webserver User', 'www-data');
$group = $this->option('group') ?? $this->ask('Webserver Group', 'www-data');
$redisUsed = config('queue.default') === 'redis' || config('session.driver') === 'redis' || config('cache.default') === 'redis';
$afterRedis = $redisUsed ? '
After=redis-server.service' : '';
$basePath = base_path();
$success = File::put($path, "# Pelican Queue File
# ----------------------------------
[Unit]
Description=Pelican Queue Service$afterRedis
[Service]
User=$user
Group=$group
Restart=always
ExecStart=/usr/bin/php $basePath/artisan queue:work --tries=3
StartLimitInterval=180
StartLimitBurst=30
RestartSec=5s
[Install]
WantedBy=multi-user.target
");
if (!$success) {
$this->error('Error creating service file');
return;
}
if ($fileExists) {
$result = Process::run("systemctl restart $serviceName.service");
if ($result->failed()) {
$this->error('Error restarting service: ' . $result->errorOutput());
return;
}
$this->line('Queue worker service file updated successfully.');
} else {
$result = Process::run("systemctl enable --now $serviceName.service");
if ($result->failed()) {
$this->error('Error enabling service: ' . $result->errorOutput());
return;
}
$this->line('Queue worker service file created successfully.');
}
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Console\Commands\Environment;
use App\Traits\Commands\RequestRedisSettingsTrait;
use App\Traits\EnvironmentWriterTrait;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
class RedisSetupCommand extends Command
{
use EnvironmentWriterTrait;
use RequestRedisSettingsTrait;
protected $description = 'Configure the Panel to use Redis as cache, queue and session driver.';
protected $signature = 'p:redis:setup
{--redis-host= : Redis host to use for connections.}
{--redis-user= : User used to connect to redis.}
{--redis-pass= : Password used to connect to redis.}
{--redis-port= : Port to connect to redis over.}';
protected array $variables = [];
/**
* RedisSetupCommand constructor.
*/
public function __construct(private Kernel $console)
{
parent::__construct();
}
/**
* Handle command execution.
*/
public function handle(): int
{
$this->variables['CACHE_STORE'] = 'redis';
$this->variables['QUEUE_CONNECTION'] = 'redis';
$this->variables['SESSION_DRIVERS'] = 'redis';
$this->requestRedisSettings();
$this->call('p:environment:queue-service', [
'--overwrite' => true,
]);
$this->writeToEnvironment($this->variables);
$this->info($this->console->output());
return 0;
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Console\Commands\Environment;
use App\Traits\Commands\RequestRedisSettingsTrait;
use App\Traits\EnvironmentWriterTrait;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
class SessionSettingsCommand extends Command
{
use EnvironmentWriterTrait;
use RequestRedisSettingsTrait;
public const SESSION_DRIVERS = [
'file' => 'Filesystem (default)',
'redis' => 'Redis',
'database' => 'Database',
'cookie' => 'Cookie',
];
protected $description = 'Configure session settings for the Panel.';
protected $signature = 'p:environment:session
{--driver= : The session driver backend to use.}
{--redis-host= : Redis host to use for connections.}
{--redis-user= : User used to connect to redis.}
{--redis-pass= : Password used to connect to redis.}
{--redis-port= : Port to connect to redis over.}';
protected array $variables = [];
/**
* SessionSettingsCommand constructor.
*/
public function __construct(private Kernel $console)
{
parent::__construct();
}
/**
* Handle command execution.
*/
public function handle(): int
{
$selected = config('session.driver', 'file');
$this->variables['SESSION_DRIVER'] = $this->option('driver') ?? $this->choice(
'Session Driver',
self::SESSION_DRIVERS,
array_key_exists($selected, self::SESSION_DRIVERS) ? $selected : null
);
if ($this->variables['SESSION_DRIVER'] === 'redis') {
$this->requestRedisSettings();
if (config('queue.default') !== 'sync') {
$this->call('p:environment:queue-service', [
'--overwrite' => true,
]);
}
}
$this->writeToEnvironment($this->variables);
$this->info($this->console->output());
return 0;
}
}

View File

@@ -7,12 +7,12 @@ use App\Services\Helpers\SoftwareVersionService;
class InfoCommand extends Command
{
protected $description = 'Displays the application, database, and email configurations along with the panel version.';
protected $description = 'Displays the application, database, email and backup configurations along with the panel version.';
protected $signature = 'p:info';
/**
* VersionCommand constructor.
* InfoCommand constructor.
*/
public function __construct(private SoftwareVersionService $versionService)
{
@@ -26,48 +26,76 @@ class InfoCommand extends Command
{
$this->output->title('Version Information');
$this->table([], [
['Panel Version', config('app.version')],
['Panel Version', $this->versionService->versionData()['version']],
['Latest Version', $this->versionService->getPanel()],
['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')],
['Unique Identifier', config('panel.service.author')],
], 'compact');
$this->output->title('Application Configuration');
$this->table([], [
['Environment', $this->formatText(config('app.env'), config('app.env') === 'production' ?: 'bg=red')],
['Debug Mode', $this->formatText(config('app.debug') ? 'Yes' : 'No', !config('app.debug') ?: 'bg=red')],
['Installation URL', config('app.url')],
['Environment', config('app.env') === 'production' ? config('app.env') : $this->formatText(config('app.env'), 'bg=red')],
['Debug Mode', config('app.debug') ? $this->formatText('Yes', 'bg=red') : 'No'],
['Application Name', config('app.name')],
['Application URL', config('app.url')],
['Installation Directory', base_path()],
['Timezone', config('app.timezone')],
['Cache Driver', config('cache.default')],
['Queue Driver', config('queue.default')],
['Queue Driver', config('queue.default') === 'sync' ? $this->formatText(config('queue.default'), 'bg=red') : config('queue.default')],
['Session Driver', config('session.driver')],
['Filesystem Driver', config('filesystems.default')],
['Default Theme', config('themes.active')],
['Proxies', config('trustedproxies.proxies')],
], 'compact');
$this->output->title('Database Configuration');
$driver = config('database.default');
$this->table([], [
['Driver', $driver],
['Host', config("database.connections.$driver.host")],
['Port', config("database.connections.$driver.port")],
['Database', config("database.connections.$driver.database")],
['Username', config("database.connections.$driver.username")],
], 'compact');
if ($driver === 'sqlite') {
$this->table([], [
['Driver', $driver],
['Database', config("database.connections.$driver.database")],
], 'compact');
} else {
$this->table([], [
['Driver', $driver],
['Host', config("database.connections.$driver.host")],
['Port', config("database.connections.$driver.port")],
['Database', config("database.connections.$driver.database")],
['Username', config("database.connections.$driver.username")],
], 'compact');
}
// TODO: Update this to handle other mail drivers
$this->output->title('Email Configuration');
$this->table([], [
['Driver', config('mail.default')],
['Host', config('mail.mailers.smtp.host')],
['Port', config('mail.mailers.smtp.port')],
['Username', config('mail.mailers.smtp.username')],
['From Address', config('mail.from.address')],
['From Name', config('mail.from.name')],
['Encryption', config('mail.mailers.smtp.encryption')],
], 'compact');
$driver = config('mail.default');
if ($driver === 'smtp') {
$this->table([], [
['Driver', $driver],
['Host', config("mail.mailers.$driver.host")],
['Port', config("mail.mailers.$driver.port")],
['Username', config("mail.mailers.$driver.username")],
['Encryption', config("mail.mailers.$driver.encryption")],
['From Address', config('mail.from.address')],
['From Name', config('mail.from.name')],
], 'compact');
} else {
$this->table([], [
['Driver', $driver],
['From Address', config('mail.from.address')],
['From Name', config('mail.from.name')],
], 'compact');
}
$this->output->title('Backup Configuration');
$driver = config('backups.default');
if ($driver === 's3') {
$this->table([], [
['Driver', $driver],
['Region', config("backups.disks.$driver.region")],
['Bucket', config("backups.disks.$driver.bucket")],
['Endpoint', config("backups.disks.$driver.endpoint")],
['Use path style endpoint', config("backups.disks.$driver.use_path_style_endpoint") ? 'Yes' : 'No'],
], 'compact');
} else {
$this->table([], [
['Driver', $driver],
], 'compact');
}
}
/**

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Console\Commands\Maintenance;
use App\Models\Node;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class PruneImagesCommand extends Command
{
protected $signature = 'p:maintenance:prune-images {node?}';
protected $description = 'Clean up all dangling docker images to clear up disk space.';
public function handle(): void
{
$node = $this->argument('node');
if (empty($node)) {
$nodes = Node::all();
/** @var Node $node */
foreach ($nodes as $node) {
$this->cleanupImages($node);
}
} else {
$this->cleanupImages((int) $node);
}
}
private function cleanupImages(int|Node $node): void
{
if (!$node instanceof Node) {
$node = Node::query()->findOrFail($node);
}
try {
$response = Http::daemon($node)
->connectTimeout(5)
->timeout(30)
->delete('/api/system/docker/image/prune')
->json() ?? [];
if (empty($response) || $response['ImagesDeleted'] === null) {
$this->warn("Node {$node->id}: No images to clean up.");
return;
}
$count = count($response['ImagesDeleted']);
$useBinaryPrefix = config('panel.use_binary_prefix');
$space = round($useBinaryPrefix ? $response['SpaceReclaimed'] / 1024 / 1024 : $response['SpaceReclaimed'] / 1000 / 1000, 2) . ($useBinaryPrefix ? ' MiB' : ' MB');
$this->info("Node {$node->id}: Cleaned up {$count} dangling docker images. ({$space})");
} catch (Exception $exception) {
$this->error($exception->getMessage());
}
}
}

View File

@@ -20,9 +20,12 @@ class MakeNodeCommand extends Command
{--overallocateMemory= : Enter the amount of ram to overallocate (% or -1 to overallocate the maximum).}
{--maxDisk= : Set the max disk amount.}
{--overallocateDisk= : Enter the amount of disk to overallocate (% or -1 to overallocate the maximum).}
{--maxCpu= : Set the max cpu amount.}
{--overallocateCpu= : Enter the amount of cpu to overallocate (% or -1 to overallocate the maximum).}
{--uploadSize= : Enter the maximum upload filesize.}
{--daemonListeningPort= : Enter the daemon listening port.}
{--daemonSFTPPort= : Enter the daemon SFTP listening port.}
{--daemonSFTPAlias= : Enter the daemon SFTP alias.}
{--daemonBase= : Enter the base folder.}';
protected $description = 'Creates a new node on the system via the CLI.';
@@ -42,27 +45,31 @@ class MakeNodeCommand extends Command
*/
public function handle(): void
{
$data['name'] = $this->option('name') ?? $this->ask('Enter a short identifier used to distinguish this node from others');
$data['description'] = $this->option('description') ?? $this->ask('Enter a description to identify the node');
$data['name'] = $this->option('name') ?? $this->ask(__('commands.make_node.name'));
$data['description'] = $this->option('description') ?? $this->ask(__('commands.make_node.description'));
$data['scheme'] = $this->option('scheme') ?? $this->anticipate(
'Please either enter https for SSL or http for a non-ssl connection',
__('commands.make_node.scheme'),
['https', 'http'],
'https'
);
$data['fqdn'] = $this->option('fqdn') ?? $this->ask('Enter a domain name (e.g node.example.com) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node');
$data['public'] = $this->option('public') ?? $this->confirm('Should this node be public? As a note, setting a node to private you will be denying the ability to auto-deploy to this node.', true);
$data['behind_proxy'] = $this->option('proxy') ?? $this->confirm('Is your FQDN behind a proxy?');
$data['maintenance_mode'] = $this->option('maintenance') ?? $this->confirm('Should maintenance mode be enabled?');
$data['memory'] = $this->option('maxMemory') ?? $this->ask('Enter the maximum amount of memory');
$data['memory_overallocate'] = $this->option('overallocateMemory') ?? $this->ask('Enter the amount of memory to over allocate by, -1 will disable checking and 0 will prevent creating new servers');
$data['disk'] = $this->option('maxDisk') ?? $this->ask('Enter the maximum amount of disk space');
$data['disk_overallocate'] = $this->option('overallocateDisk') ?? $this->ask('Enter the amount of memory to over allocate by, -1 will disable checking and 0 will prevent creating new server');
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask('Enter the maximum filesize upload', '100');
$data['daemonListen'] = $this->option('daemonListeningPort') ?? $this->ask('Enter the daemon listening port', '8080');
$data['daemonSFTP'] = $this->option('daemonSFTPPort') ?? $this->ask('Enter the daemon SFTP listening port', '2022');
$data['daemonBase'] = $this->option('daemonBase') ?? $this->ask('Enter the base folder', '/var/lib/panel/volumes');
$data['fqdn'] = $this->option('fqdn') ?? $this->ask(__('commands.make_node.fqdn'));
$data['public'] = $this->option('public') ?? $this->confirm(__('commands.make_node.public'), true);
$data['behind_proxy'] = $this->option('proxy') ?? $this->confirm(__('commands.make_node.behind_proxy'));
$data['maintenance_mode'] = $this->option('maintenance') ?? $this->confirm(__('commands.make_node.maintenance_mode'));
$data['memory'] = $this->option('maxMemory') ?? $this->ask(__('commands.make_node.memory'), '0');
$data['memory_overallocate'] = $this->option('overallocateMemory') ?? $this->ask(__('commands.make_node.memory_overallocate'), '-1');
$data['disk'] = $this->option('maxDisk') ?? $this->ask(__('commands.make_node.disk'), '0');
$data['disk_overallocate'] = $this->option('overallocateDisk') ?? $this->ask(__('commands.make_node.disk_overallocate'), '-1');
$data['cpu'] = $this->option('maxCpu') ?? $this->ask(__('commands.make_node.cpu'), '0');
$data['cpu_overallocate'] = $this->option('overallocateCpu') ?? $this->ask(__('commands.make_node.cpu_overallocate'), '-1');
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(__('commands.make_node.upload_size'), '256');
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(__('commands.make_node.daemonListen'), '8080');
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(__('commands.make_node.daemonSFTP'), '2022');
$data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(__('commands.make_node.daemonSFTPAlias'), '');
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(__('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
$node = $this->creationService->handle($data);
$this->line('Successfully created a new node with the name ' . $data['name'] . ' and has an id of ' . $node->id . '.');
$this->line(__('commands.make_node.success', ['name' => $data['name'], 'id' => $node->id]));
}
}

View File

@@ -19,14 +19,14 @@ class NodeConfigurationCommand extends Command
/** @var \App\Models\Node $node */
$node = Node::query()->where($column, $this->argument('node'))->firstOr(function () {
$this->error('The selected node does not exist.');
$this->error(__('commands.node_config.error_not_exist'));
exit(1);
});
$format = $this->option('format');
if (!in_array($format, ['yaml', 'yml', 'json'])) {
$this->error('Invalid format specified. Valid options are "yaml" and "json".');
$this->error(__('commands.node_config.error_invalid_format'));
return 1;
}

View File

@@ -13,12 +13,12 @@ class KeyGenerateCommand extends BaseKeyGenerateCommand
public function handle(): void
{
if (!empty(config('app.key')) && $this->input->isInteractive()) {
$this->output->warning('It appears you have already configured an application encryption key. Continuing with this process with overwrite that key and cause data corruption for any existing encrypted data. DO NOT CONTINUE UNLESS YOU KNOW WHAT YOU ARE DOING.');
if (!$this->confirm('I understand the consequences of performing this command and accept all responsibility for the loss of encrypted data.')) {
$this->output->warning(__('commands.key_generate.error_already_exist'));
if (!$this->confirm(__('commands.key_generate.understand'))) {
return;
}
if (!$this->confirm('Are you sure you wish to continue? Changing the application encryption key WILL CAUSE DATA LOSS.')) {
if (!$this->confirm(__('commands.key_generate.continue'))) {
return;
}
}

View File

@@ -2,7 +2,7 @@
namespace App\Console\Commands\Overrides;
use App\Console\RequiresDatabaseMigrations;
use App\Traits\Commands\RequiresDatabaseMigrations;
use Illuminate\Database\Console\Seeds\SeedCommand as BaseSeedCommand;
class SeedCommand extends BaseSeedCommand

View File

@@ -2,7 +2,7 @@
namespace App\Console\Commands\Overrides;
use App\Console\RequiresDatabaseMigrations;
use App\Traits\Commands\RequiresDatabaseMigrations;
use Illuminate\Foundation\Console\UpCommand as BaseUpCommand;
class UpCommand extends BaseUpCommand

View File

@@ -6,6 +6,7 @@ use Illuminate\Console\Command;
use App\Models\Schedule;
use Illuminate\Database\Eloquent\Builder;
use App\Services\Schedules\ProcessScheduleService;
use Carbon\Carbon;
class ProcessRunnableCommand extends Command
{
@@ -23,11 +24,11 @@ class ProcessRunnableCommand extends Command
->whereRelation('server', fn (Builder $builder) => $builder->whereNull('status'))
->where('is_active', true)
->where('is_processing', false)
->whereRaw('next_run_at <= NOW()')
->where('next_run_at', '<=', Carbon::now()->toDateTimeString())
->get();
if ($schedules->count() < 1) {
$this->line('There are no scheduled tasks for servers that need to be run.');
$this->line(__('commands.schedule.process.no_tasks'));
return 0;
}
@@ -50,7 +51,7 @@ class ProcessRunnableCommand extends Command
* never throw an exception out, otherwise you'll end up killing the entire run group causing
* any other schedules to not process correctly.
*/
protected function processSchedule(Schedule $schedule)
protected function processSchedule(Schedule $schedule): void
{
if ($schedule->tasks->isEmpty()) {
return;
@@ -61,12 +62,12 @@ class ProcessRunnableCommand extends Command
$this->line(trans('command/messages.schedule.output_line', [
'schedule' => $schedule->name,
'hash' => $schedule->hashid,
'id' => $schedule->id,
]));
} catch (\Throwable|\Exception $exception) {
logger()->error($exception, ['schedule_id' => $schedule->id]);
$this->error("An error was encountered while processing Schedule #$schedule->id: " . $exception->getMessage());
$this->error(__('commands.schedule.process.no_tasks') . " #$schedule->id: " . $exception->getMessage());
}
}
}

View File

@@ -34,29 +34,30 @@ class UpgradeCommand extends Command
{
$skipDownload = $this->option('skip-download');
if (!$skipDownload) {
$this->output->warning('This command does not verify the integrity of downloaded assets. Please ensure that you trust the download source before continuing. If you do not wish to download an archive, please indicate that using the --skip-download flag, or answering "no" to the question below.');
$this->output->comment('Download Source (set with --url=):');
$this->output->warning(__('commands.upgrade.integrity'));
$this->output->comment(__('commands.upgrade.source_url'));
$this->line($this->getUrl());
}
if (version_compare(PHP_VERSION, '7.4.0') < 0) {
$this->error('Cannot execute self-upgrade process. The minimum required PHP version required is 7.4.0, you have [' . PHP_VERSION . '].');
$this->error(__('commands.upgrade.php_version') . ' [' . PHP_VERSION . '].');
}
$user = 'www-data';
$group = 'www-data';
if ($this->input->isInteractive()) {
if (!$skipDownload) {
$skipDownload = !$this->confirm('Would you like to download and unpack the archive files for the latest version?', true);
$skipDownload = !$this->confirm(__('commands.upgrade.skipDownload'), true);
}
if (is_null($this->option('user'))) {
$userDetails = function_exists('posix_getpwuid') ? posix_getpwuid(fileowner('public')) : [];
$user = $userDetails['name'] ?? 'www-data';
if (!$this->confirm("Your webserver user has been detected as <fg=blue>[{$user}]:</> is this correct?", true)) {
$message = __('commands.upgrade.webserver_user', ['user' => $user]);
if (!$this->confirm($message, true)) {
$user = $this->anticipate(
'Please enter the name of the user running your webserver process. This varies from system to system, but is generally "www-data", "nginx", or "apache".',
__('commands.upgrade.name_webserver'),
[
'www-data',
'nginx',
@@ -70,9 +71,10 @@ class UpgradeCommand extends Command
$groupDetails = function_exists('posix_getgrgid') ? posix_getgrgid(filegroup('public')) : [];
$group = $groupDetails['name'] ?? 'www-data';
if (!$this->confirm("Your webserver group has been detected as <fg=blue>[{$group}]:</> is this correct?", true)) {
$message = __('commands.upgrade.group_webserver', ['group' => $user]);
if (!$this->confirm($message, true)) {
$group = $this->anticipate(
'Please enter the name of the group running your webserver process. Normally this is the same as your user.',
__('commands.upgrade.group_webserver_question'),
[
'www-data',
'nginx',
@@ -82,8 +84,8 @@ class UpgradeCommand extends Command
}
}
if (!$this->confirm('Are you sure you want to run the upgrade process for your Panel?')) {
$this->warn('Upgrade process terminated by user.');
if (!$this->confirm(__('commands.upgrade.are_your_sure'))) {
$this->warn(__('commands.upgrade.terminated'));
return;
}
@@ -173,10 +175,10 @@ class UpgradeCommand extends Command
});
$this->newLine(2);
$this->info('Panel has been successfully upgraded. Please ensure you also update any Daemon instances');
$this->info(__('commands.upgrade.success'));
}
protected function withProgress(ProgressBar $bar, \Closure $callback)
protected function withProgress(ProgressBar $bar, \Closure $callback): void
{
$bar->clear();
$callback();

View File

@@ -15,7 +15,7 @@ class DeleteUserCommand extends Command
public function handle(): int
{
$search = $this->option('user') ?? $this->ask(trans('command/messages.user.search_users'));
Assert::notEmpty($search, 'Search term should be an email address, got: %s.');
Assert::notEmpty($search, 'Search term should not be empty.');
$results = User::query()
->where('id', 'LIKE', "$search%")
@@ -42,6 +42,8 @@ class DeleteUserCommand extends Command
if (!$deleteUser = $this->ask(trans('command/messages.user.select_search_user'))) {
return $this->handle();
}
$deleteUser = User::query()->findOrFail($deleteUser);
} else {
if (count($results) > 1) {
$this->error(trans('command/messages.user.multiple_found'));
@@ -53,8 +55,7 @@ class DeleteUserCommand extends Command
}
if ($this->confirm(trans('command/messages.user.confirm_delete')) || !$this->input->isInteractive()) {
$user = User::query()->findOrFail($deleteUser);
$user->delete();
$deleteUser->delete();
$this->info(trans('command/messages.user.deleted'));
}

View File

@@ -11,7 +11,7 @@ class MakeUserCommand extends Command
{
protected $description = 'Creates a user on the system via the CLI.';
protected $signature = 'p:user:make {--email=} {--username=} {--name-first=} {--name-last=} {--password=} {--admin=} {--no-password}';
protected $signature = 'p:user:make {--email=} {--username=} {--password=} {--admin=} {--no-password}';
/**
* MakeUserCommand constructor.
@@ -30,7 +30,7 @@ class MakeUserCommand extends Command
public function handle(): int
{
try {
DB::select('select 1 where 1');
DB::connection()->getPdo();
} catch (Exception $exception) {
$this->error($exception->getMessage());
@@ -40,8 +40,6 @@ class MakeUserCommand extends Command
$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'));
$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')) {
$this->warn(trans('command/messages.user.ask_password_help'));
@@ -49,13 +47,12 @@ class MakeUserCommand extends Command
$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'], [
['UUID', $user->uuid],
['Email', $user->email],
['Username', $user->username],
['Name', $user->name],
['Admin', $user->root_admin ? 'Yes' : 'No'],
['Admin', $user->isRootAdmin() ? 'Yes' : 'No'],
]);
return 0;

View File

@@ -2,13 +2,16 @@
namespace App\Console;
use App\Console\Commands\Egg\CheckEggUpdatesCommand;
use App\Console\Commands\Maintenance\CleanServiceBackupFilesCommand;
use App\Console\Commands\Maintenance\PruneImagesCommand;
use App\Console\Commands\Maintenance\PruneOrphanedBackupsCommand;
use App\Console\Commands\Schedule\ProcessRunnableCommand;
use App\Jobs\NodeStatistics;
use App\Models\ActivityLog;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Database\Console\PruneCommand;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\Console\Commands\Schedule\ProcessRunnableCommand;
use App\Console\Commands\Maintenance\PruneOrphanedBackupsCommand;
use App\Console\Commands\Maintenance\CleanServiceBackupFilesCommand;
class Kernel extends ConsoleKernel
{
@@ -30,7 +33,12 @@ class Kernel extends ConsoleKernel
// Execute scheduled commands for servers every minute, as if there was a normal cron running.
$schedule->command(ProcessRunnableCommand::class)->everyMinute()->withoutOverlapping();
$schedule->command(CleanServiceBackupFilesCommand::class)->daily();
$schedule->command(PruneImagesCommand::class)->daily();
$schedule->command(CheckEggUpdatesCommand::class)->hourly();
$schedule->job(new NodeStatistics())->everyFiveSeconds()->withoutOverlapping();
if (config('backups.prune_age')) {
// Every 30 minutes, run the backup pruning command so that any abandoned backups can be deleted.

View File

@@ -1,15 +0,0 @@
<?php
namespace App\Contracts\Extensions;
use Hashids\HashidsInterface as VendorHashidsInterface;
interface HashidsInterface extends VendorHashidsInterface
{
/**
* Decode an encoded hashid and return the first result.
*
* @throws \InvalidArgumentException
*/
public function decodeFirst(string $encoded, string $default = null): mixed;
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Enums;
enum ContainerStatus: string
{
// Docker Based
case Created = 'created';
case Starting = 'starting';
case Running = 'running';
case Restarting = 'restarting';
case Exited = 'exited';
case Paused = 'paused';
case Dead = 'dead';
case Removing = 'removing';
case Stopping = 'stopping';
case Offline = 'offline';
// HTTP Based
case Missing = 'missing';
public function icon(): string
{
return match ($this) {
self::Created => 'tabler-heart-plus',
self::Starting => 'tabler-heart-up',
self::Running => 'tabler-heartbeat',
self::Restarting => 'tabler-heart-bolt',
self::Exited => 'tabler-heart-exclamation',
self::Paused => 'tabler-heart-pause',
self::Dead, self::Offline => 'tabler-heart-x',
self::Removing => 'tabler-heart-down',
self::Missing => 'tabler-heart-search',
self::Stopping => 'tabler-heart-minus',
};
}
public function color(): string
{
return match ($this) {
self::Created => 'primary',
self::Starting => 'warning',
self::Running => 'success',
self::Restarting => 'info',
self::Exited => 'danger',
self::Paused => 'warning',
self::Dead => 'danger',
self::Removing => 'warning',
self::Missing => 'danger',
self::Stopping => 'warning',
self::Offline => 'gray',
};
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Enums;
enum HttpStatusCode: int
{
// Client
case BadRequest = 400;
case Unauthorized = 401;
case Forbidden = 403;
case NotFound = 404;
case MethodNotAllowed = 405;
case NotAcceptable = 406;
case ProxyAuthenticationRequired = 407;
case RequestTimeout = 408;
case Conflict = 409;
case Gone = 410;
case LengthRequired = 411;
case PreconditionFailed = 412;
case PayloadTooLarge = 413;
case UriTooLong = 414;
case UnsupportedMediaType = 415;
// Server
case InternalServerError = 500;
case NotImplemented = 501;
case BadGateway = 502;
case ServiceUnavailable = 503;
case GatewayTimeout = 504;
case HTTPVersionNotSupported = 505;
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Enums;
enum RolePermissionModels: string
{
case ApiKey = 'apiKey';
case DatabaseHost = 'databaseHost';
case Database = 'database';
case Egg = 'egg';
case Mount = 'mount';
case Node = 'node';
case Role = 'role';
case Server = 'server';
case User = 'user';
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Enums;
enum RolePermissionPrefixes: string
{
case ViewAny = 'viewList';
case View = 'view';
case Create = 'create';
case Update = 'update';
case Delete = 'delete';
}

37
app/Enums/ServerState.php Normal file
View File

@@ -0,0 +1,37 @@
<?php
namespace App\Enums;
enum ServerState: string
{
case Normal = 'normal';
case Installing = 'installing';
case InstallFailed = 'install_failed';
case ReinstallFailed = 'reinstall_failed';
case Suspended = 'suspended';
case RestoringBackup = 'restoring_backup';
public function icon(): string
{
return match ($this) {
self::Normal => 'tabler-heart',
self::Installing => 'tabler-heart-bolt',
self::InstallFailed => 'tabler-heart-x',
self::ReinstallFailed => 'tabler-heart-x',
self::Suspended => 'tabler-heart-cancel',
self::RestoringBackup => 'tabler-heart-up',
};
}
public function color(): string
{
return match ($this) {
self::Normal => 'primary',
self::Installing => 'primary',
self::InstallFailed => 'danger',
self::ReinstallFailed => 'danger',
self::Suspended => 'warning',
self::RestoringBackup => 'primary',
};
}
}

View File

@@ -3,20 +3,24 @@
namespace App\Exceptions;
use Exception;
use Filament\Notifications\Notification;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Psr\Log\LoggerInterface;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Container\Container;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class DisplayException extends PanelException implements HttpExceptionInterface
{
public const LEVEL_DEBUG = 'debug';
public const LEVEL_INFO = 'info';
public const LEVEL_WARNING = 'warning';
public const LEVEL_ERROR = 'error';
/**
@@ -47,12 +51,23 @@ class DisplayException extends PanelException implements HttpExceptionInterface
* and then redirecting them back to the page that they came from. If the
* request originated from an API hit, return the error in JSONAPI spec format.
*/
public function render(Request $request): JsonResponse|RedirectResponse
public function render(Request $request): bool|RedirectResponse|JsonResponse
{
if ($request->is('livewire/update')) {
Notification::make()
->title(static::class)
->body($this->getMessage())
->danger()
->send();
return false;
}
if ($request->expectsJson()) {
return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders());
}
// @phpstan-ignore-next-line
app(AlertsMessageBag::class)->danger($this->getMessage())->flash();
return redirect()->back()->withInput();
@@ -64,10 +79,10 @@ class DisplayException extends PanelException implements HttpExceptionInterface
*
* @throws \Throwable
*/
public function report()
public function report(): void
{
if (!$this->getPrevious() instanceof \Exception || !Handler::isReportable($this->getPrevious())) {
return null;
return;
}
try {
@@ -76,6 +91,6 @@ class DisplayException extends PanelException implements HttpExceptionInterface
throw $this->getPrevious();
}
return $logger->{$this->getErrorLevel()}($this->getPrevious());
$logger->{$this->getErrorLevel()}($this->getPrevious());
}
}

View File

@@ -114,7 +114,7 @@ class Handler extends ExceptionHandler
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Request $request
*
* @throws \Throwable
*/
@@ -140,7 +140,7 @@ class Handler extends ExceptionHandler
* Transform a validation exception into a consistent format to be returned for
* calls to the API.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Request $request
*/
public function invalidJson($request, ValidationException $exception): JsonResponse
{
@@ -215,7 +215,7 @@ class Handler extends ExceptionHandler
->map(fn ($trace) => Arr::except($trace, ['args']))
->all(),
'previous' => Collection::make($this->extractPrevious($e))
->map(fn ($exception) => $e->getTrace())
->map(fn ($exception) => $exception->getTrace())
->map(fn ($trace) => Arr::except($trace, ['args']))
->all(),
],
@@ -236,7 +236,7 @@ class Handler extends ExceptionHandler
/**
* Convert an authentication exception into an unauthenticated response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Request $request
*/
protected function unauthenticated($request, AuthenticationException $exception): JsonResponse|RedirectResponse
{
@@ -273,6 +273,7 @@ class Handler extends ExceptionHandler
*/
public static function toArray(\Throwable $e): array
{
// @phpstan-ignore-next-line
return (new self(app()))->convertExceptionToArray($e);
}
}

View File

@@ -10,7 +10,7 @@ class HttpForbiddenException extends HttpException
/**
* HttpForbiddenException constructor.
*/
public function __construct(string $message = null, \Throwable $previous = null)
public function __construct(?string $message = null, ?\Throwable $previous = null)
{
parent::__construct(Response::HTTP_FORBIDDEN, $message, $previous);
}

View File

@@ -2,6 +2,7 @@
namespace App\Exceptions\Http\Server;
use App\Enums\ServerState;
use App\Models\Server;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
@@ -11,7 +12,7 @@ class ServerStateConflictException extends ConflictHttpException
* Exception thrown when the server is in an unsupported state for API access or
* certain operations within the codebase.
*/
public function __construct(Server $server, \Throwable $previous = null)
public function __construct(Server $server, ?\Throwable $previous = null)
{
$message = 'This server is currently in an unsupported state, please try again later.';
if ($server->isSuspended()) {
@@ -20,7 +21,7 @@ class ServerStateConflictException extends ConflictHttpException
$message = 'The node of this server is currently under maintenance and the functionality requested is unavailable.';
} elseif (!$server->isInstalled()) {
$message = 'This server has not yet completed its installation process, please try again later.';
} elseif ($server->status === Server::STATUS_RESTORING_BACKUP) {
} elseif ($server->status === ServerState::RestoringBackup) {
$message = 'This server is currently restoring from a backup, please try again later.';
} elseif (!is_null($server->transfer)) {
$message = 'This server is currently being transferred to a new machine, please try again later.';

View File

@@ -11,7 +11,7 @@ class TwoFactorAuthRequiredException extends HttpException implements HttpExcept
/**
* TwoFactorAuthRequiredException constructor.
*/
public function __construct(\Throwable $previous = null)
public function __construct(?\Throwable $previous = null)
{
parent::__construct(Response::HTTP_BAD_REQUEST, 'Two-factor authentication is required on this account in order to access this endpoint.', $previous);
}

View File

@@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\PanelException;
class AllocationDoesNotBelongToServerException extends PanelException
{
}

View File

@@ -1,18 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class AutoAllocationNotEnabledException extends DisplayException
{
/**
* AutoAllocationNotEnabledException constructor.
*/
public function __construct()
{
parent::__construct(
'Server auto-allocation is not enabled for this instance.'
);
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class CidrOutOfRangeException extends DisplayException
{
/**
* CidrOutOfRangeException constructor.
*/
public function __construct()
{
parent::__construct(trans('exceptions.allocations.cidr_out_of_range'));
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class InvalidPortMappingException extends DisplayException
{
/**
* InvalidPortMappingException constructor.
*/
public function __construct(mixed $port)
{
parent::__construct(trans('exceptions.allocations.invalid_mapping', ['port' => $port]));
}
}

View File

@@ -1,18 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class NoAutoAllocationSpaceAvailableException extends DisplayException
{
/**
* NoAutoAllocationSpaceAvailableException constructor.
*/
public function __construct()
{
parent::__construct(
'Cannot assign additional allocation: no more space available on node.'
);
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class PortOutOfRangeException extends DisplayException
{
/**
* PortOutOfRangeException constructor.
*/
public function __construct()
{
parent::__construct(trans('exceptions.allocations.port_out_of_range'));
}
}

View File

@@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class ServerUsingAllocationException extends DisplayException
{
}

View File

@@ -1,16 +0,0 @@
<?php
namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class TooManyPortsInRangeException extends DisplayException
{
/**
* TooManyPortsInRangeException constructor.
*/
public function __construct()
{
parent::__construct(trans('exceptions.allocations.too_many_ports'));
}
}

View File

@@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Deployment;
use App\Exceptions\DisplayException;
class NoViableAllocationException extends DisplayException
{
}

View File

@@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Service\Deployment;
use App\Exceptions\DisplayException;
class NoViableNodeException extends DisplayException
{
}

View File

@@ -1,7 +0,0 @@
<?php
namespace App\Exceptions\Service\Helper;
class CdnVersionFetchingException extends \Exception
{
}

View File

@@ -10,7 +10,7 @@ class ServiceLimitExceededException extends DisplayException
* Exception thrown when something goes over a defined limit, such as allocated
* ports, tasks, databases, etc.
*/
public function __construct(string $message, \Throwable $previous = null)
public function __construct(string $message, ?\Throwable $previous = null)
{
parent::__construct($message, $previous, self::LEVEL_WARNING);
}

View File

@@ -6,9 +6,10 @@ use App\Exceptions\DisplayException;
class TwoFactorAuthenticationTokenInvalid extends DisplayException
{
/**
* TwoFactorAuthenticationTokenInvalid constructor.
*/
public string $title = 'Invalid 2FA Code';
public string $icon = 'tabler-2fa';
public function __construct()
{
parent::__construct('The provided two-factor authentication token was not valid.');

View File

@@ -34,7 +34,7 @@ class BackupManager
/**
* Returns a backup adapter instance.
*/
public function adapter(string $name = null): FilesystemAdapter
public function adapter(?string $name = null): FilesystemAdapter
{
return $this->get($name ?: $this->getDefaultAdapter());
}
@@ -97,7 +97,7 @@ class BackupManager
/**
* Creates a new daemon adapter.
*/
public function createDaemonAdapter(array $config): FilesystemAdapter
public function createWingsAdapter(array $config): FilesystemAdapter
{
return new InMemoryFilesystemAdapter();
}
@@ -145,7 +145,7 @@ class BackupManager
/**
* Unset the given adapter instances.
*
* @param string|string[] $adapter
* @param string|string[] $adapter
*/
public function forget(array|string $adapter): self
{

View File

@@ -7,7 +7,9 @@ use App\Models\DatabaseHost;
class DynamicDatabaseConnection
{
public const DB_CHARSET = 'utf8';
public const DB_COLLATION = 'utf8_unicode_ci';
public const DB_DRIVER = 'mysql';
/**
@@ -25,7 +27,7 @@ class DynamicDatabaseConnection
'port' => $host->port,
'database' => $database,
'username' => $host->username,
'password' => decrypt($host->password),
'password' => $host->password,
'charset' => self::DB_CHARSET,
'collation' => self::DB_COLLATION,
]);

View File

@@ -1,5 +1,28 @@
<?php
/* The MIT License (MIT)
Pterodactyl®
Copyright © Dane Everitt <dane@daneeveritt.com> and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */
namespace App\Extensions\Filesystem;
use Aws\S3\S3ClientInterface;

View File

@@ -1,22 +0,0 @@
<?php
namespace App\Extensions;
use Hashids\Hashids as VendorHashids;
use App\Contracts\Extensions\HashidsInterface;
class Hashids extends VendorHashids implements HashidsInterface
{
/**
* {@inheritdoc}
*/
public function decodeFirst(string $encoded, string $default = null): mixed
{
$result = $this->decode($encoded);
if (!is_array($result)) {
return $default;
}
return array_first($result, null, $default);
}
}

View File

@@ -4,17 +4,17 @@ namespace App\Extensions\Themes;
class Theme
{
public function js($path): string
public function js(string $path): string
{
return sprintf('<script src="%s"></script>' . PHP_EOL, $this->getUrl($path));
}
public function css($path): string
public function css(string $path): string
{
return sprintf('<link media="all" type="text/css" rel="stylesheet" href="%s"/>' . PHP_EOL, $this->getUrl($path));
}
protected function getUrl($path): string
protected function getUrl(string $path): string
{
return '/themes/panel/' . ltrim($path, '/');
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Filament\Pages;
use App\Filament\Resources\NodeResource\Pages\ListNodes;
use App\Models\Egg;
use App\Models\Node;
use App\Models\Server;
use App\Models\User;
use App\Services\Helpers\SoftwareVersionService;
use Filament\Actions\CreateAction;
use Filament\Pages\Page;
class Dashboard extends Page
{
protected static ?string $navigationIcon = 'tabler-layout-dashboard';
protected static string $view = 'filament.pages.dashboard';
protected ?string $heading = '';
public function getTitle(): string
{
return trans('strings.dashboard');
}
protected static ?string $slug = '/';
public string $activeTab = 'nodes';
private SoftwareVersionService $softwareVersionService;
public function mount(SoftwareVersionService $softwareVersionService): void
{
$this->softwareVersionService = $softwareVersionService;
}
public function getViewData(): array
{
return [
'inDevelopment' => config('app.version') === 'canary',
'version' => $this->softwareVersionService->versionData()['version'],
'latestVersion' => $this->softwareVersionService->getPanel(),
'isLatest' => $this->softwareVersionService->isLatestPanel(),
'eggsCount' => Egg::query()->count(),
'nodesList' => ListNodes::getUrl(),
'nodesCount' => Node::query()->count(),
'serversCount' => Server::query()->count(),
'usersCount' => User::query()->count(),
'devActions' => [
CreateAction::make()
->label('Bugs & Features')
->icon('tabler-brand-github')
->url('https://github.com/pelican-dev/panel/discussions', true),
],
'updateActions' => [
CreateAction::make()
->label('Read Documentation')
->icon('tabler-clipboard-text')
->url('https://pelican.dev/docs/panel/update', true)
->color('warning'),
],
'nodeActions' => [
CreateAction::make()
->label(trans('dashboard/index.sections.intro-first-node.button_label'))
->icon('tabler-server-2')
->url(route('filament.admin.resources.nodes.create')),
],
'supportActions' => [
CreateAction::make()
->label(trans('dashboard/index.sections.intro-support.button_donate'))
->icon('tabler-cash')
->url($this->softwareVersionService->getDonations(), true)
->color('success'),
],
'helpActions' => [
CreateAction::make()
->label(trans('dashboard/index.sections.intro-help.button_docs'))
->icon('tabler-speedboat')
->url('https://pelican.dev/docs', true),
],
];
}
}

View File

@@ -0,0 +1,182 @@
<?php
namespace App\Filament\Pages\Installer;
use App\Filament\Pages\Dashboard;
use App\Filament\Pages\Installer\Steps\AdminUserStep;
use App\Filament\Pages\Installer\Steps\CompletedStep;
use App\Filament\Pages\Installer\Steps\DatabaseStep;
use App\Filament\Pages\Installer\Steps\EnvironmentStep;
use App\Filament\Pages\Installer\Steps\RedisStep;
use App\Filament\Pages\Installer\Steps\RequirementsStep;
use App\Models\User;
use App\Services\Users\UserCreationService;
use App\Traits\CheckMigrationsTrait;
use App\Traits\EnvironmentWriterTrait;
use Exception;
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\Wizard;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Form;
use Filament\Forms\Get;
use Filament\Notifications\Notification;
use Filament\Pages\SimplePage;
use Filament\Support\Enums\MaxWidth;
use Filament\Support\Exceptions\Halt;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\HtmlString;
/**
* @property Form $form
*/
class PanelInstaller extends SimplePage implements HasForms
{
use CheckMigrationsTrait;
use EnvironmentWriterTrait;
use InteractsWithForms;
public array $data = [];
protected static string $view = 'filament.pages.installer';
private User $user;
public function getMaxWidth(): MaxWidth|string
{
return MaxWidth::SevenExtraLarge;
}
public static function isInstalled(): bool
{
// This defaults to true so existing panels count as "installed"
return env('APP_INSTALLED', true);
}
public function mount(): void
{
abort_if(self::isInstalled(), 404);
$this->form->fill();
}
protected function getFormSchema(): array
{
return [
Wizard::make([
RequirementsStep::make(),
EnvironmentStep::make($this),
DatabaseStep::make($this),
RedisStep::make($this)
->hidden(fn (Get $get) => $get('env_general.SESSION_DRIVER') != 'redis' && $get('env_general.QUEUE_CONNECTION') != 'redis' && $get('env_general.CACHE_STORE') != 'redis'),
AdminUserStep::make($this),
CompletedStep::make(),
])
->persistStepInQueryString()
->nextAction(fn (Action $action) => $action->keyBindings('enter'))
->submitAction(new HtmlString(Blade::render(<<<'BLADE'
<x-filament::button
type="submit"
size="sm"
wire:loading.attr="disabled"
>
Finish
<span wire:loading><x-filament::loading-indicator class="h-4 w-4" /></span>
</x-filament::button>
BLADE))),
];
}
protected function getFormStatePath(): ?string
{
return 'data';
}
public function submit(): RedirectResponse
{
// Disable installer
$this->writeToEnvironment(['APP_INSTALLED' => 'true']);
// Login user
$this->user ??= User::all()->filter(fn ($user) => $user->isRootAdmin())->first();
auth()->guard()->login($this->user, true);
// Redirect to admin panel
return redirect(Dashboard::getUrl());
}
public function writeToEnv(string $key): void
{
try {
$variables = array_get($this->data, $key);
$this->writeToEnvironment($variables);
} catch (Exception $exception) {
report($exception);
Notification::make()
->title('Could not write to .env file')
->body($exception->getMessage())
->danger()
->persistent()
->send();
throw new Halt('Error while writing .env file');
}
Artisan::call('config:clear');
}
public function runMigrations(string $driver): void
{
try {
Artisan::call('migrate', [
'--force' => true,
'--seed' => true,
'--database' => $driver,
]);
} catch (Exception $exception) {
report($exception);
Notification::make()
->title('Migrations failed')
->body($exception->getMessage())
->danger()
->persistent()
->send();
throw new Halt('Error while running migrations');
}
if (!$this->hasCompletedMigrations()) {
Notification::make()
->title('Migrations failed')
->danger()
->persistent()
->send();
throw new Halt('Migrations failed');
}
}
public function createAdminUser(UserCreationService $userCreationService): void
{
try {
$userData = array_get($this->data, 'user');
$userData['root_admin'] = true;
$this->user = $userCreationService->handle($userData);
} catch (Exception $exception) {
report($exception);
Notification::make()
->title('Could not create admin user')
->body($exception->getMessage())
->danger()
->persistent()
->send();
throw new Halt('Error while creating admin user');
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Filament\Pages\Installer\Steps;
use App\Filament\Pages\Installer\PanelInstaller;
use App\Services\Users\UserCreationService;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Wizard\Step;
class AdminUserStep
{
public static function make(PanelInstaller $installer): Step
{
return Step::make('user')
->label('Admin User')
->schema([
TextInput::make('user.email')
->label('Admin E-Mail')
->required()
->email()
->placeholder('admin@example.com'),
TextInput::make('user.username')
->label('Admin Username')
->required()
->placeholder('admin'),
TextInput::make('user.password')
->label('Admin Password')
->required()
->password()
->revealable(),
])
->afterValidation(fn (UserCreationService $service) => $installer->createAdminUser($service));
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Filament\Pages\Installer\Steps;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Wizard\Step;
use Illuminate\Support\HtmlString;
use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction;
class CompletedStep
{
public static function make(): Step
{
return Step::make('complete')
->label('Setup complete')
->schema([
Placeholder::make('')
->content(new HtmlString('The setup is nearly complete!<br>As last step you need to create a new cronjob that runs every minute to process specific tasks, such as session cleanup and scheduled tasks, and also create a queue worker.')),
TextInput::make('crontab')
->label(new HtmlString('Run the following command to setup your crontab. Note that <code>www-data</code> is your webserver user. On some systems this username might be different!'))
->disabled()
->hintAction(CopyAction::make())
->default('(crontab -l -u www-data 2>/dev/null; echo "* * * * * php ' . base_path() . '/artisan schedule:run >> /dev/null 2>&1") | crontab -u www-data -'),
TextInput::make('queueService')
->label(new HtmlString('To setup the queue worker service you simply have to run the following command.'))
->disabled()
->hintAction(CopyAction::make())
->default('sudo php ' . base_path() . '/artisan p:environment:queue-service'),
Placeholder::make('')
->content('After you finished these two last tasks you can click on "Finish" and use your new panel! Have fun!'),
]);
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace App\Filament\Pages\Installer\Steps;
use App\Filament\Pages\Installer\PanelInstaller;
use Exception;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Wizard\Step;
use Filament\Forms\Get;
use Filament\Notifications\Notification;
use Filament\Support\Exceptions\Halt;
use Illuminate\Support\Facades\DB;
class DatabaseStep
{
public static function make(PanelInstaller $installer): Step
{
return Step::make('database')
->label('Database')
->columns()
->schema([
TextInput::make('env_database.DB_DATABASE')
->label(fn (Get $get) => $get('env_general.DB_CONNECTION') === 'sqlite' ? 'Database Path' : 'Database Name')
->columnSpanFull()
->hintIcon('tabler-question-mark')
->hintIconTooltip(fn (Get $get) => $get('env_general.DB_CONNECTION') === 'sqlite' ? 'The path of your .sqlite file relative to the database folder.' : 'The name of the panel database.')
->required()
->default(fn (Get $get) => env('DB_DATABASE', $get('env_general.DB_CONNECTION') === 'sqlite' ? 'database.sqlite' : 'panel')),
TextInput::make('env_database.DB_HOST')
->label('Database Host')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The host of your database. Make sure it is reachable.')
->required(fn (Get $get) => $get('env_general.DB_CONNECTION') !== 'sqlite')
->default(fn (Get $get) => $get('env_general.DB_CONNECTION') !== 'sqlite' ? env('DB_HOST', '127.0.0.1') : null)
->hidden(fn (Get $get) => $get('env_general.DB_CONNECTION') === 'sqlite'),
TextInput::make('env_database.DB_PORT')
->label('Database Port')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The port of your database.')
->required(fn (Get $get) => $get('env_general.DB_CONNECTION') !== 'sqlite')
->numeric()
->minValue(1)
->maxValue(65535)
->default(fn (Get $get) => $get('env_general.DB_CONNECTION') !== 'sqlite' ? env('DB_PORT', 3306) : null)
->hidden(fn (Get $get) => $get('env_general.DB_CONNECTION') === 'sqlite'),
TextInput::make('env_database.DB_USERNAME')
->label('Database Username')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The name of your database user.')
->required(fn (Get $get) => $get('env_general.DB_CONNECTION') !== 'sqlite')
->default(fn (Get $get) => $get('env_general.DB_CONNECTION') !== 'sqlite' ? env('DB_USERNAME', 'pelican') : null)
->hidden(fn (Get $get) => $get('env_general.DB_CONNECTION') === 'sqlite'),
TextInput::make('env_database.DB_PASSWORD')
->label('Database Password')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The password of your database user. Can be empty.')
->password()
->revealable()
->default(fn (Get $get) => $get('env_general.DB_CONNECTION') !== 'sqlite' ? env('DB_PASSWORD') : null)
->hidden(fn (Get $get) => $get('env_general.DB_CONNECTION') === 'sqlite'),
])
->afterValidation(function (Get $get) use ($installer) {
$driver = $get('env_general.DB_CONNECTION');
if (!self::testConnection($driver, $get('env_database.DB_HOST'), $get('env_database.DB_PORT'), $get('env_database.DB_DATABASE'), $get('env_database.DB_USERNAME'), $get('env_database.DB_PASSWORD'))) {
throw new Halt('Database connection failed');
}
$installer->writeToEnv('env_database');
$installer->runMigrations($driver);
});
}
private static function testConnection(string $driver, string $host, string $port, string $database, string $username, string $password): bool
{
if ($driver === 'sqlite') {
return true;
}
try {
config()->set('database.connections._panel_install_test', [
'driver' => $driver,
'host' => $host,
'port' => $port,
'database' => $database,
'username' => $username,
'password' => $password,
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'strict' => true,
]);
DB::connection('_panel_install_test')->getPdo();
} catch (Exception $exception) {
DB::disconnect('_panel_install_test');
Notification::make()
->title('Database connection failed')
->body($exception->getMessage())
->danger()
->send();
return false;
}
return true;
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace App\Filament\Pages\Installer\Steps;
use App\Filament\Pages\Installer\PanelInstaller;
use App\Traits\EnvironmentWriterTrait;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Components\Wizard\Step;
use Filament\Forms\Set;
class EnvironmentStep
{
use EnvironmentWriterTrait;
public const CACHE_DRIVERS = [
'file' => 'Filesystem',
'redis' => 'Redis',
];
public const SESSION_DRIVERS = [
'file' => 'Filesystem',
'database' => 'Database',
'cookie' => 'Cookie',
'redis' => 'Redis',
];
public const QUEUE_DRIVERS = [
'database' => 'Database',
'sync' => 'Sync',
'redis' => 'Redis',
];
public const DATABASE_DRIVERS = [
'sqlite' => 'SQLite',
'mariadb' => 'MariaDB',
'mysql' => 'MySQL',
];
public static function make(PanelInstaller $installer): Step
{
return Step::make('environment')
->label('Environment')
->columns()
->schema([
TextInput::make('env_general.APP_NAME')
->label('App Name')
->hintIcon('tabler-question-mark')
->hintIconTooltip('This will be the Name of your Panel.')
->required()
->default(config('app.name')),
TextInput::make('env_general.APP_URL')
->label('App URL')
->hintIcon('tabler-question-mark')
->hintIconTooltip('This will be the URL you access your Panel from.')
->required()
->default(url(''))
->live()
->afterStateUpdated(fn ($state, Set $set) => $set('env_general.SESSION_SECURE_COOKIE', str_starts_with($state, 'https://') ? 'true' : 'false')),
TextInput::make('env_general.SESSION_SECURE_COOKIE')
->hidden()
->default(str_starts_with(url(''), 'https://') ? 'true' : 'false'),
ToggleButtons::make('env_general.CACHE_STORE')
->label('Cache Driver')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The driver used for caching. We recommend "Filesystem".')
->required()
->inline()
->options(self::CACHE_DRIVERS)
->default(config('cache.default', 'file')),
ToggleButtons::make('env_general.SESSION_DRIVER')
->label('Session Driver')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The driver used for storing sessions. We recommend "Filesystem" or "Database".')
->required()
->inline()
->options(self::SESSION_DRIVERS)
->default(config('session.driver', 'file')),
ToggleButtons::make('env_general.QUEUE_CONNECTION')
->label('Queue Driver')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The driver used for handling queues. We recommend "Database".')
->required()
->inline()
->options(self::QUEUE_DRIVERS)
->default(config('queue.default', 'database')),
ToggleButtons::make('env_general.DB_CONNECTION')
->label('Database Driver')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The driver used for the panel database. We recommend "SQLite".')
->required()
->inline()
->options(self::DATABASE_DRIVERS)
->default(config('database.default', 'sqlite')),
])
->afterValidation(fn () => $installer->writeToEnv('env_general'));
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Filament\Pages\Installer\Steps;
use App\Filament\Pages\Installer\PanelInstaller;
use App\Traits\EnvironmentWriterTrait;
use Exception;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Wizard\Step;
use Filament\Forms\Get;
use Filament\Notifications\Notification;
use Filament\Support\Exceptions\Halt;
use Illuminate\Support\Facades\Redis;
class RedisStep
{
use EnvironmentWriterTrait;
public static function make(PanelInstaller $installer): Step
{
return Step::make('redis')
->label('Redis')
->columns()
->schema([
TextInput::make('env_redis.REDIS_HOST')
->label('Redis Host')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The host of your redis server. Make sure it is reachable.')
->required()
->default(config('database.redis.default.host')),
TextInput::make('env_redis.REDIS_PORT')
->label('Redis Port')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The port of your redis server.')
->required()
->default(config('database.redis.default.port')),
TextInput::make('env_redis.REDIS_USERNAME')
->label('Redis Username')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The name of your redis user. Can be empty')
->default(config('database.redis.default.username')),
TextInput::make('env_redis.REDIS_PASSWORD')
->label('Redis Password')
->hintIcon('tabler-question-mark')
->hintIconTooltip('The password for your redis user. Can be empty.')
->password()
->revealable()
->default(config('database.redis.default.password')),
])
->afterValidation(function (Get $get) use ($installer) {
if (!self::testConnection($get('env_redis.REDIS_HOST'), $get('env_redis.REDIS_PORT'), $get('env_redis.REDIS_USERNAME'), $get('env_redis.REDIS_PASSWORD'))) {
throw new Halt('Redis connection failed');
}
$installer->writeToEnv('env_redis');
});
}
private static function testConnection(string $host, string $port, string $username, string $password): bool
{
try {
config()->set('database.redis._panel_install_test', [
'host' => $host,
'port' => $port,
'username' => $username,
'password' => $password,
]);
Redis::connection('_panel_install_test')->command('ping');
} catch (Exception $exception) {
Notification::make()
->title('Redis connection failed')
->body($exception->getMessage())
->danger()
->send();
return false;
}
return true;
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace App\Filament\Pages\Installer\Steps;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Wizard\Step;
use Filament\Notifications\Notification;
use Filament\Support\Exceptions\Halt;
class RequirementsStep
{
public const MIN_PHP_VERSION = '8.2';
public static function make(): Step
{
$correctPhpVersion = version_compare(PHP_VERSION, self::MIN_PHP_VERSION) >= 0;
$fields = [
Section::make('PHP Version')
->description(self::MIN_PHP_VERSION . ' or newer')
->icon($correctPhpVersion ? 'tabler-check' : 'tabler-x')
->iconColor($correctPhpVersion ? 'success' : 'danger')
->schema([
Placeholder::make('')
->content('Your PHP Version is ' . PHP_VERSION . '.'),
]),
];
$phpExtensions = [
'BCMath' => extension_loaded('bcmath'),
'cURL' => extension_loaded('curl'),
'GD' => extension_loaded('gd'),
'intl' => extension_loaded('intl'),
'mbstring' => extension_loaded('mbstring'),
'MySQL' => extension_loaded('pdo_mysql'),
'SQLite3' => extension_loaded('pdo_sqlite'),
'XML' => extension_loaded('xml'),
'Zip' => extension_loaded('zip'),
];
$allExtensionsInstalled = !in_array(false, $phpExtensions);
$fields[] = Section::make('PHP Extensions')
->description(implode(', ', array_keys($phpExtensions)))
->icon($allExtensionsInstalled ? 'tabler-check' : 'tabler-x')
->iconColor($allExtensionsInstalled ? 'success' : 'danger')
->schema([
Placeholder::make('')
->content('All needed PHP Extensions are installed.')
->visible($allExtensionsInstalled),
Placeholder::make('')
->content('The following PHP Extensions are missing: ' . implode(', ', array_keys($phpExtensions, false)))
->visible(!$allExtensionsInstalled),
]);
$folderPermissions = [
'Storage' => substr(sprintf('%o', fileperms(base_path('storage/'))), -4) >= 755,
'Cache' => substr(sprintf('%o', fileperms(base_path('bootstrap/cache/'))), -4) >= 755,
];
$correctFolderPermissions = !in_array(false, $folderPermissions);
$fields[] = Section::make('Folder Permissions')
->description(implode(', ', array_keys($folderPermissions)))
->icon($correctFolderPermissions ? 'tabler-check' : 'tabler-x')
->iconColor($correctFolderPermissions ? 'success' : 'danger')
->schema([
Placeholder::make('')
->content('All Folders have the correct permissions.')
->visible($correctFolderPermissions),
Placeholder::make('')
->content('The following Folders have wrong permissions: ' . implode(', ', array_keys($folderPermissions, false)))
->visible(!$correctFolderPermissions),
]);
return Step::make('requirements')
->label('Server Requirements')
->schema($fields)
->afterValidation(function () use ($correctPhpVersion, $allExtensionsInstalled, $correctFolderPermissions) {
if (!$correctPhpVersion || !$allExtensionsInstalled || !$correctFolderPermissions) {
Notification::make()
->title('Some requirements are missing!')
->danger()
->send();
throw new Halt('Some requirements are missing');
}
});
}
}

View File

@@ -0,0 +1,597 @@
<?php
namespace App\Filament\Pages;
use App\Models\Backup;
use App\Notifications\MailTested;
use App\Traits\EnvironmentWriterTrait;
use Exception;
use Filament\Actions\Action;
use Filament\Forms\Components\Actions\Action as FormAction;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Form;
use Filament\Forms\Get;
use Filament\Forms\Set;
use Filament\Notifications\Notification;
use Filament\Pages\Concerns\HasUnsavedDataChangesAlert;
use Filament\Pages\Concerns\InteractsWithHeaderActions;
use Filament\Pages\Page;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Notification as MailNotification;
/**
* @property Form $form
*/
class Settings extends Page implements HasForms
{
use EnvironmentWriterTrait;
use HasUnsavedDataChangesAlert;
use InteractsWithForms;
use InteractsWithHeaderActions;
protected static ?string $navigationIcon = 'tabler-settings';
protected static ?string $navigationGroup = 'Advanced';
protected static string $view = 'filament.pages.settings';
public ?array $data = [];
public function mount(): void
{
$this->form->fill();
}
public static function canAccess(): bool
{
return auth()->user()->can('view settings');
}
protected function getFormSchema(): array
{
return [
Tabs::make('Tabs')
->columns()
->persistTabInQueryString()
->disabled(fn () => !auth()->user()->can('update settings'))
->tabs([
Tab::make('general')
->label('General')
->icon('tabler-home')
->schema($this->generalSettings()),
Tab::make('recaptcha')
->label('reCAPTCHA')
->icon('tabler-shield')
->schema($this->recaptchaSettings()),
Tab::make('mail')
->label('Mail')
->icon('tabler-mail')
->schema($this->mailSettings()),
Tab::make('backup')
->label('Backup')
->icon('tabler-box')
->schema($this->backupSettings()),
Tab::make('misc')
->label('Misc')
->icon('tabler-tool')
->schema($this->miscSettings()),
]),
];
}
private function generalSettings(): array
{
return [
TextInput::make('APP_NAME')
->label('App Name')
->required()
->default(env('APP_NAME', 'Pelican')),
TextInput::make('APP_FAVICON')
->label('App Favicon')
->hintIcon('tabler-question-mark')
->hintIconTooltip('Favicons should be placed in the public folder, located in the root panel directory.')
->required()
->default(env('APP_FAVICON', '/pelican.ico')),
Toggle::make('APP_DEBUG')
->label('Enable Debug Mode?')
->inline(false)
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('APP_DEBUG', (bool) $state))
->default(env('APP_DEBUG', config('app.debug'))),
ToggleButtons::make('FILAMENT_TOP_NAVIGATION')
->label('Navigation')
->inline()
->options([
false => 'Sidebar',
true => 'Topbar',
])
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('FILAMENT_TOP_NAVIGATION', (bool) $state))
->default(env('FILAMENT_TOP_NAVIGATION', config('panel.filament.top-navigation'))),
ToggleButtons::make('PANEL_USE_BINARY_PREFIX')
->label('Unit prefix')
->inline()
->options([
false => 'Decimal Prefix (MB/ GB)',
true => 'Binary Prefix (MiB/ GiB)',
])
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_USE_BINARY_PREFIX', (bool) $state))
->default(env('PANEL_USE_BINARY_PREFIX', config('panel.use_binary_prefix'))),
ToggleButtons::make('APP_2FA_REQUIRED')
->label('2FA Requirement')
->inline()
->options([
0 => 'Not required',
1 => 'Required for only Admins',
2 => 'Required for all Users',
])
->formatStateUsing(fn ($state): int => (int) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('APP_2FA_REQUIRED', (int) $state))
->default(env('APP_2FA_REQUIRED', config('panel.auth.2fa_required'))),
TagsInput::make('TRUSTED_PROXIES')
->label('Trusted Proxies')
->separator()
->splitKeys(['Tab', ' '])
->placeholder('New IP or IP Range')
->default(env('TRUSTED_PROXIES', config('trustedproxy.proxies')))
->hintActions([
FormAction::make('clear')
->label('Clear')
->color('danger')
->icon('tabler-trash')
->requiresConfirmation()
->authorize(fn () => auth()->user()->can('update settings'))
->action(fn (Set $set) => $set('TRUSTED_PROXIES', [])),
FormAction::make('cloudflare')
->label('Set to Cloudflare IPs')
->icon('tabler-brand-cloudflare')
->authorize(fn () => auth()->user()->can('update settings'))
->action(fn (Set $set) => $set('TRUSTED_PROXIES', [
'173.245.48.0/20',
'103.21.244.0/22',
'103.22.200.0/22',
'103.31.4.0/22',
'141.101.64.0/18',
'108.162.192.0/18',
'190.93.240.0/20',
'188.114.96.0/20',
'197.234.240.0/22',
'198.41.128.0/17',
'162.158.0.0/15',
'104.16.0.0/13',
'104.24.0.0/14',
'172.64.0.0/13',
'131.0.72.0/22',
])),
]),
];
}
private function recaptchaSettings(): array
{
return [
Toggle::make('RECAPTCHA_ENABLED')
->label('Enable reCAPTCHA?')
->inline(false)
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->live()
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('RECAPTCHA_ENABLED', (bool) $state))
->default(env('RECAPTCHA_ENABLED', config('recaptcha.enabled'))),
TextInput::make('RECAPTCHA_DOMAIN')
->label('Domain')
->required()
->visible(fn (Get $get) => $get('RECAPTCHA_ENABLED'))
->default(env('RECAPTCHA_DOMAIN', config('recaptcha.domain'))),
TextInput::make('RECAPTCHA_WEBSITE_KEY')
->label('Website Key')
->required()
->visible(fn (Get $get) => $get('RECAPTCHA_ENABLED'))
->default(env('RECAPTCHA_WEBSITE_KEY', config('recaptcha.website_key'))),
TextInput::make('RECAPTCHA_SECRET_KEY')
->label('Secret Key')
->required()
->visible(fn (Get $get) => $get('RECAPTCHA_ENABLED'))
->default(env('RECAPTCHA_SECRET_KEY', config('recaptcha.secret_key'))),
];
}
private function mailSettings(): array
{
return [
ToggleButtons::make('MAIL_MAILER')
->label('Mail Driver')
->columnSpanFull()
->inline()
->options([
'log' => 'Print mails to Log',
'smtp' => 'SMTP Server',
'sendmail' => 'sendmail Binary',
'mailgun' => 'Mailgun',
'mandrill' => 'Mandrill',
'postmark' => 'Postmark',
])
->live()
->default(env('MAIL_MAILER', config('mail.default')))
->hintAction(
FormAction::make('test')
->label('Send Test Mail')
->icon('tabler-send')
->hidden(fn (Get $get) => $get('MAIL_MAILER') === 'log')
->authorize(fn () => auth()->user()->can('update settings'))
->action(function () {
try {
MailNotification::route('mail', auth()->user()->email)
->notify(new MailTested(auth()->user()));
Notification::make()
->title('Test Mail sent')
->success()
->send();
} catch (Exception $exception) {
Notification::make()
->title('Test Mail failed')
->body($exception->getMessage())
->danger()
->send();
}
})
),
Section::make('"From" Settings')
->description('Set the Address and Name used as "From" in mails.')
->columns()
->schema([
TextInput::make('MAIL_FROM_ADDRESS')
->label('From Address')
->required()
->email()
->default(env('MAIL_FROM_ADDRESS', config('mail.from.address'))),
TextInput::make('MAIL_FROM_NAME')
->label('From Name')
->required()
->default(env('MAIL_FROM_NAME', config('mail.from.name'))),
]),
Section::make('SMTP Configuration')
->columns()
->visible(fn (Get $get) => $get('MAIL_MAILER') === 'smtp')
->schema([
TextInput::make('MAIL_HOST')
->label('Host')
->required()
->default(env('MAIL_HOST', config('mail.mailers.smtp.host'))),
TextInput::make('MAIL_PORT')
->label('Port')
->required()
->numeric()
->minValue(1)
->maxValue(65535)
->default(env('MAIL_PORT', config('mail.mailers.smtp.port'))),
TextInput::make('MAIL_USERNAME')
->label('Username')
->default(env('MAIL_USERNAME', config('mail.mailers.smtp.username'))),
TextInput::make('MAIL_PASSWORD')
->label('Password')
->password()
->revealable()
->default(env('MAIL_PASSWORD')),
ToggleButtons::make('MAIL_ENCRYPTION')
->label('Encryption')
->inline()
->options(['tls' => 'TLS', 'ssl' => 'SSL', '' => 'None'])
->default(env('MAIL_ENCRYPTION', config('mail.mailers.smtp.encryption', 'tls'))),
]),
Section::make('Mailgun Configuration')
->columns()
->visible(fn (Get $get) => $get('MAIL_MAILER') === 'mailgun')
->schema([
TextInput::make('MAILGUN_DOMAIN')
->label('Domain')
->required()
->default(env('MAILGUN_DOMAIN', config('services.mailgun.domain'))),
TextInput::make('MAILGUN_SECRET')
->label('Secret')
->required()
->default(env('MAILGUN_SECRET', config('services.mailgun.secret'))),
TextInput::make('MAILGUN_ENDPOINT')
->label('Endpoint')
->required()
->default(env('MAILGUN_ENDPOINT', config('services.mailgun.endpoint'))),
]),
];
}
private function backupSettings(): array
{
return [
ToggleButtons::make('APP_BACKUP_DRIVER')
->label('Backup Driver')
->columnSpanFull()
->inline()
->options([
Backup::ADAPTER_DAEMON => 'Wings',
Backup::ADAPTER_AWS_S3 => 'S3',
])
->live()
->default(env('APP_BACKUP_DRIVER', config('backups.default'))),
Section::make('Throttles')
->description('Configure how many backups can be created in a period. Set period to 0 to disable this throttle.')
->columns()
->schema([
TextInput::make('BACKUP_THROTTLE_LIMIT')
->label('Limit')
->required()
->numeric()
->minValue(1)
->default(config('backups.throttles.limit')),
TextInput::make('BACKUP_THROTTLE_PERIOD')
->label('Period')
->required()
->numeric()
->minValue(0)
->suffix('Seconds')
->default(config('backups.throttles.period')),
]),
Section::make('S3 Configuration')
->columns()
->visible(fn (Get $get) => $get('APP_BACKUP_DRIVER') === Backup::ADAPTER_AWS_S3)
->schema([
TextInput::make('AWS_DEFAULT_REGION')
->label('Default Region')
->required()
->default(config('backups.disks.s3.region')),
TextInput::make('AWS_ACCESS_KEY_ID')
->label('Access Key ID')
->required()
->default(config('backups.disks.s3.key')),
TextInput::make('AWS_SECRET_ACCESS_KEY')
->label('Secret Access Key')
->required()
->default(config('backups.disks.s3.secret')),
TextInput::make('AWS_BACKUPS_BUCKET')
->label('Bucket')
->required()
->default(config('backups.disks.s3.bucket')),
TextInput::make('AWS_ENDPOINT')
->label('Endpoint')
->required()
->default(config('backups.disks.s3.endpoint')),
Toggle::make('AWS_USE_PATH_STYLE_ENDPOINT')
->label('Use path style endpoint?')
->inline(false)
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->live()
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('AWS_USE_PATH_STYLE_ENDPOINT', (bool) $state))
->default(env('AWS_USE_PATH_STYLE_ENDPOINT', config('backups.disks.s3.use_path_style_endpoint'))),
]),
];
}
private function miscSettings(): array
{
return [
Section::make('Automatic Allocation Creation')
->description('Toggle if Users can create allocations via the client area.')
->columns()
->collapsible()
->collapsed()
->schema([
Toggle::make('PANEL_CLIENT_ALLOCATIONS_ENABLED')
->label('Allow Users to create allocations?')
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->live()
->columnSpanFull()
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_CLIENT_ALLOCATIONS_ENABLED', (bool) $state))
->default(env('PANEL_CLIENT_ALLOCATIONS_ENABLED', config('panel.client_features.allocations.enabled'))),
TextInput::make('PANEL_CLIENT_ALLOCATIONS_RANGE_START')
->label('Starting Port')
->required()
->numeric()
->minValue(1024)
->maxValue(65535)
->visible(fn (Get $get) => $get('PANEL_CLIENT_ALLOCATIONS_ENABLED'))
->default(env('PANEL_CLIENT_ALLOCATIONS_RANGE_START')),
TextInput::make('PANEL_CLIENT_ALLOCATIONS_RANGE_END')
->label('Ending Port')
->required()
->numeric()
->minValue(1024)
->maxValue(65535)
->visible(fn (Get $get) => $get('PANEL_CLIENT_ALLOCATIONS_ENABLED'))
->default(env('PANEL_CLIENT_ALLOCATIONS_RANGE_END')),
]),
Section::make('Mail Notifications')
->description('Toggle which mail notifications should be sent to Users.')
->columns()
->collapsible()
->collapsed()
->schema([
Toggle::make('PANEL_SEND_INSTALL_NOTIFICATION')
->label('Server Installed')
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->live()
->columnSpanFull()
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_INSTALL_NOTIFICATION', (bool) $state))
->default(env('PANEL_SEND_INSTALL_NOTIFICATION', config('panel.email.send_install_notification'))),
Toggle::make('PANEL_SEND_REINSTALL_NOTIFICATION')
->label('Server Reinstalled')
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->live()
->columnSpanFull()
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_SEND_REINSTALL_NOTIFICATION', (bool) $state))
->default(env('PANEL_SEND_REINSTALL_NOTIFICATION', config('panel.email.send_reinstall_notification'))),
]),
Section::make('Connections')
->description('Timeouts used when making requests.')
->columns()
->collapsible()
->collapsed()
->schema([
TextInput::make('GUZZLE_TIMEOUT')
->label('Request Timeout')
->required()
->numeric()
->minValue(15)
->maxValue(60)
->suffix('Seconds')
->default(env('GUZZLE_TIMEOUT', config('panel.guzzle.timeout'))),
TextInput::make('GUZZLE_CONNECT_TIMEOUT')
->label('Connect Timeout')
->required()
->numeric()
->minValue(5)
->maxValue(60)
->suffix('Seconds')
->default(env('GUZZLE_CONNECT_TIMEOUT', config('panel.guzzle.connect_timeout'))),
]),
Section::make('Activity Logs')
->description('Configure how often old activity logs should be pruned and whether admin activities should be logged.')
->columns()
->collapsible()
->collapsed()
->schema([
TextInput::make('APP_ACTIVITY_PRUNE_DAYS')
->label('Prune age')
->required()
->numeric()
->minValue(1)
->maxValue(365)
->suffix('Days')
->default(env('APP_ACTIVITY_PRUNE_DAYS', config('activity.prune_days'))),
Toggle::make('APP_ACTIVITY_HIDE_ADMIN')
->label('Hide admin activities?')
->inline(false)
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->live()
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('APP_ACTIVITY_HIDE_ADMIN', (bool) $state))
->default(env('APP_ACTIVITY_HIDE_ADMIN', config('activity.hide_admin_activity'))),
]),
Section::make('API')
->description('Defines the rate limit for the number of requests per minute that can be executed.')
->columns()
->collapsible()
->collapsed()
->schema([
TextInput::make('APP_API_CLIENT_RATELIMIT')
->label('Client API Rate Limit')
->required()
->numeric()
->minValue(1)
->suffix('Requests Per Minute')
->default(env('APP_API_CLIENT_RATELIMIT', config('http.rate_limit.client'))),
TextInput::make('APP_API_APPLICATION_RATELIMIT')
->label('Application API Rate Limit')
->required()
->numeric()
->minValue(1)
->suffix('Requests Per Minute')
->default(env('APP_API_APPLICATION_RATELIMIT', config('http.rate_limit.application'))),
]),
Section::make('Server')
->description('Settings for Servers.')
->columns()
->collapsible()
->collapsed()
->schema([
Toggle::make('PANEL_EDITABLE_SERVER_DESCRIPTIONS')
->label('Allow Users to edit Server Descriptions?')
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->live()
->columnSpanFull()
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_EDITABLE_SERVER_DESCRIPTIONS', (bool) $state))
->default(env('PANEL_EDITABLE_SERVER_DESCRIPTIONS', config('panel.editable_server_descriptions'))),
]),
];
}
protected function getFormStatePath(): ?string
{
return 'data';
}
protected function hasUnsavedDataChangesAlert(): bool
{
return true;
}
public function save(): void
{
try {
$data = $this->form->getState();
// Convert bools to a string, so they are correctly written to the .env file
$data = array_map(fn ($value) => is_bool($value) ? ($value ? 'true' : 'false') : $value, $data);
$this->writeToEnvironment($data);
Artisan::call('config:clear');
Artisan::call('queue:restart');
$this->rememberData();
$this->redirect($this->getUrl());
Notification::make()
->title('Settings saved')
->success()
->send();
} catch (Exception $exception) {
Notification::make()
->title('Save failed')
->body($exception->getMessage())
->danger()
->send();
}
}
protected function getHeaderActions(): array
{
return [
Action::make('save')
->action('save')
->authorize(fn () => auth()->user()->can('update settings'))
->keyBindings(['mod+s']),
];
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\ApiKeyResource\Pages;
use App\Models\ApiKey;
use Filament\Resources\Resource;
use Illuminate\Database\Eloquent\Model;
class ApiKeyResource extends Resource
{
protected static ?string $model = ApiKey::class;
protected static ?string $label = 'API Key';
protected static ?string $navigationIcon = 'tabler-key';
protected static ?string $navigationGroup = 'Advanced';
public static function getNavigationBadge(): ?string
{
return static::getModel()::where('key_type', '2')->count() ?: null;
}
public static function canEdit(Model $record): bool
{
return false;
}
public static function getPages(): array
{
return [
'index' => Pages\ListApiKeys::route('/'),
'create' => Pages\CreateApiKey::route('/create'),
];
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace App\Filament\Resources\ApiKeyResource\Pages;
use App\Filament\Resources\ApiKeyResource;
use App\Models\ApiKey;
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Form;
use Filament\Resources\Pages\CreateRecord;
class CreateApiKey extends CreateRecord
{
protected static string $resource = ApiKeyResource::class;
protected ?string $heading = 'Create Application API Key';
public function form(Form $form): Form
{
return $form
->schema([
Hidden::make('identifier')->default(ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION)),
Hidden::make('token')->default(str_random(ApiKey::KEY_LENGTH)),
Hidden::make('user_id')
->default(auth()->user()->id)
->required(),
Hidden::make('key_type')
->inlineLabel()
->default(ApiKey::TYPE_APPLICATION)
->required(),
Fieldset::make('Permissions')
->columns([
'default' => 1,
'sm' => 1,
'md' => 2,
])
->schema(
collect(ApiKey::RESOURCES)->map(fn ($resource) => ToggleButtons::make("r_$resource")
->label(str($resource)->replace('_', ' ')->title())->inline()
->options([
0 => 'None',
1 => 'Read',
// 2 => 'Write',
3 => 'Read & Write',
])
->icons([
0 => 'tabler-book-off',
1 => 'tabler-book',
2 => 'tabler-writing',
3 => 'tabler-writing',
])
->colors([
0 => 'success',
1 => 'warning',
2 => 'danger',
3 => 'danger',
])
->required()
->columnSpan([
'default' => 1,
'sm' => 1,
'md' => 1,
])
->default(0),
)->all(),
),
TagsInput::make('allowed_ips')
->placeholder('Example: 127.0.0.1 or 192.168.1.1')
->label('Whitelisted IPv4 Addresses')
->helperText('Press enter to add a new IP address or leave blank to allow any IP address')
->columnSpanFull(),
Textarea::make('memo')
->required()
->label('Description')
->helperText('
Once you have assigned permissions and created this set of credentials you will be unable to come back and edit it.
If you need to make changes down the road you will need to create a new set of credentials.
')
->columnSpanFull(),
]);
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace App\Filament\Resources\ApiKeyResource\Pages;
use App\Filament\Resources\ApiKeyResource;
use App\Models\ApiKey;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Actions\CreateAction;
use Filament\Tables\Actions\DeleteAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ListApiKeys extends ListRecords
{
protected static string $resource = ApiKeyResource::class;
public function table(Table $table): Table
{
return $table
->searchable(false)
->modifyQueryUsing(fn ($query) => $query->where('key_type', ApiKey::TYPE_APPLICATION))
->columns([
TextColumn::make('key')
->copyable()
->icon('tabler-clipboard-text')
->state(fn (ApiKey $key) => $key->identifier . $key->token),
TextColumn::make('memo')
->label('Description')
->wrap()
->limit(50),
TextColumn::make('identifier')
->hidden()
->searchable(),
TextColumn::make('last_used_at')
->label('Last Used')
->placeholder('Not Used')
->dateTime()
->sortable(),
TextColumn::make('created_at')
->label('Created')
->dateTime()
->sortable(),
TextColumn::make('user.username')
->label('Created By')
->url(fn (ApiKey $apiKey): string => route('filament.admin.resources.users.edit', ['record' => $apiKey->user])),
])
->actions([
DeleteAction::make(),
])
->emptyStateIcon('tabler-key')
->emptyStateDescription('')
->emptyStateHeading('No API Keys')
->emptyStateActions([
CreateAction::make('create')
->label('Create API Key')
->button(),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make()
->label('Create API Key')
->hidden(fn () => ApiKey::where('key_type', ApiKey::TYPE_APPLICATION)->count() <= 0),
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\DatabaseHostResource\Pages;
use App\Models\DatabaseHost;
use Filament\Resources\Resource;
class DatabaseHostResource extends Resource
{
protected static ?string $model = DatabaseHost::class;
protected static ?string $label = 'Database Host';
protected static ?string $navigationIcon = 'tabler-database';
protected static ?string $navigationGroup = 'Advanced';
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;
}
public static function getPages(): array
{
return [
'index' => Pages\ListDatabaseHosts::route('/'),
'create' => Pages\CreateDatabaseHost::route('/create'),
'edit' => Pages\EditDatabaseHost::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\Pages;
use App\Filament\Resources\DatabaseHostResource;
use App\Models\Objects\Endpoint;
use App\Services\Databases\Hosts\HostCreationService;
use Closure;
use Exception;
use Filament\Forms;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Database\Eloquent\Model;
use PDOException;
class CreateDatabaseHost extends CreateRecord
{
private HostCreationService $service;
protected static string $resource = DatabaseHostResource::class;
protected ?string $heading = 'Database Hosts';
protected static bool $canCreateAnother = false;
protected ?string $subheading = '(database servers that can have individual databases)';
public function boot(HostCreationService $service): void
{
$this->service = $service;
}
public function form(Form $form): Form
{
return $form
->schema([
Section::make()
->columns([
'default' => 2,
'sm' => 3,
'md' => 3,
'lg' => 4,
])
->schema([
TextInput::make('host')
->columnSpan(2)
->helperText('The IP address or Domain name that should be used when attempting to connect to this MySQL host from this Panel to create new databases.')
->required()
->live(onBlur: true)
->afterStateUpdated(fn ($state, Forms\Set $set) => $set('name', $state))
->maxLength(255),
TextInput::make('port')
->columnSpan(1)
->helperText('The port that MySQL is running on for this host.')
->required()
->numeric()
->default(3306)
->minValue(0)
->maxValue(Endpoint::PORT_CEIL),
TextInput::make('max_databases')
->label('Max databases')
->helpertext('Blank is unlimited.')
->numeric(),
TextInput::make('name')
->label('Display Name')
->helperText('A short identifier used to distinguish this location from others. Must be between 1 and 60 characters, for example, us.nyc.lvl3.')
->required()
->maxLength(60),
TextInput::make('username')
->helperText('The username of an account that has enough permissions to create new users and databases on the system.')
->required()
->maxLength(255),
TextInput::make('password')
->helperText('The password for the database user.')
->password()
->revealable()
->maxLength(255)
->required(),
Select::make('node_id')
->searchable()
->preload()
->helperText('This setting only defaults to this database host when adding a database to a server on the selected node.')
->label('Linked Node')
->relationship('node', 'name'),
]),
]);
}
protected function getHeaderActions(): array
{
return [
$this->getCreateFormAction()->formId('form'),
];
}
protected function getFormActions(): array
{
return [];
}
protected function handleRecordCreation(array $data): Model
{
return $this->service->handle($data);
}
public function exception(Exception $e, Closure $stopPropagation): void
{
if ($e instanceof PDOException) {
Notification::make()
->title('Error connecting to database host')
->body($e->getMessage())
->color('danger')
->icon('tabler-database')
->danger()
->send();
$stopPropagation();
}
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\Pages;
use App\Filament\Resources\DatabaseHostResource;
use App\Filament\Resources\DatabaseHostResource\RelationManagers\DatabasesRelationManager;
use App\Models\DatabaseHost;
use App\Models\Objects\Endpoint;
use App\Services\Databases\Hosts\HostUpdateService;
use Closure;
use Exception;
use Filament\Actions;
use Filament\Forms;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Database\Eloquent\Model;
use PDOException;
class EditDatabaseHost extends EditRecord
{
protected static string $resource = DatabaseHostResource::class;
private HostUpdateService $hostUpdateService;
public function boot(HostUpdateService $hostUpdateService): void
{
$this->hostUpdateService = $hostUpdateService;
}
public function form(Form $form): Form
{
return $form
->schema([
Section::make()
->columns([
'default' => 2,
'sm' => 3,
'md' => 3,
'lg' => 4,
])
->schema([
TextInput::make('host')
->columnSpan(2)
->helperText('The IP address or Domain name that should be used when attempting to connect to this MySQL host from this Panel to create new databases.')
->required()
->live(onBlur: true)
->afterStateUpdated(fn ($state, Forms\Set $set) => $set('name', $state))
->maxLength(255),
TextInput::make('port')
->columnSpan(1)
->helperText('The port that MySQL is running on for this host.')
->required()
->numeric()
->minValue(0)
->maxValue(Endpoint::PORT_CEIL),
TextInput::make('max_databases')
->label('Max databases')
->helpertext('Blank is unlimited.')
->numeric(),
TextInput::make('name')
->label('Display Name')
->helperText('A short identifier used to distinguish this location from others. Must be between 1 and 60 characters, for example, us.nyc.lvl3.')
->required()
->maxLength(60),
TextInput::make('username')
->helperText('The username of an account that has enough permissions to create new users and databases on the system.')
->required()
->maxLength(255),
TextInput::make('password')
->helperText('The password for the database user.')
->password()
->revealable()
->maxLength(255),
Select::make('node_id')
->searchable()
->preload()
->helperText('This setting only defaults to this database host when adding a database to a server on the selected node.')
->label('Linked Node')
->relationship('node', 'name'),
]),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make()
->label(fn (DatabaseHost $databaseHost) => $databaseHost->databases()->count() > 0 ? 'Database Host Has Databases' : 'Delete')
->disabled(fn (DatabaseHost $databaseHost) => $databaseHost->databases()->count() > 0),
$this->getSaveFormAction()->formId('form'),
];
}
protected function getFormActions(): array
{
return [];
}
public function getRelationManagers(): array
{
return [
DatabasesRelationManager::class,
];
}
protected function handleRecordUpdate(Model $record, array $data): Model
{
if (!$record instanceof DatabaseHost) {
return $record;
}
return $this->hostUpdateService->handle($record, $data);
}
public function exception(Exception $e, Closure $stopPropagation): void
{
if ($e instanceof PDOException) {
Notification::make()
->title('Error connecting to database host')
->body($e->getMessage())
->color('danger')
->icon('tabler-database')
->danger()
->send();
$stopPropagation();
}
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\Pages;
use App\Filament\Resources\DatabaseHostResource;
use App\Models\DatabaseHost;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Actions\BulkActionGroup;
use Filament\Tables\Actions\CreateAction;
use Filament\Tables\Actions\DeleteBulkAction;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ListDatabaseHosts extends ListRecords
{
protected static string $resource = DatabaseHostResource::class;
protected ?string $heading = 'Database Hosts';
public function table(Table $table): Table
{
return $table
->searchable(false)
->columns([
TextColumn::make('name')
->searchable(),
TextColumn::make('host')
->searchable(),
TextColumn::make('port')
->sortable(),
TextColumn::make('username')
->searchable(),
TextColumn::make('databases_count')
->counts('databases')
->icon('tabler-database')
->label('Databases'),
TextColumn::make('node.name')
->icon('tabler-server-2')
->placeholder('No Nodes')
->sortable(),
])
->checkIfRecordIsSelectableUsing(fn (DatabaseHost $databaseHost) => !$databaseHost->databases_count)
->actions([
EditAction::make(),
])
->bulkActions([
BulkActionGroup::make([
DeleteBulkAction::make()
->authorize(fn () => auth()->user()->can('delete databasehost')),
]),
])
->emptyStateIcon('tabler-database')
->emptyStateDescription('')
->emptyStateHeading('No Database Hosts')
->emptyStateActions([
CreateAction::make('create')
->label('Create Database Host')
->button(),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make('create')
->label('Create Database Host')
->hidden(fn () => DatabaseHost::count() <= 0),
];
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\RelationManagers;
use App\Models\Database;
use App\Services\Databases\DatabasePasswordService;
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Forms\Get;
use Filament\Forms\Set;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables\Actions\DeleteAction;
use Filament\Tables\Actions\ViewAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class DatabasesRelationManager extends RelationManager
{
protected static string $relationship = 'databases';
public function form(Form $form): Form
{
return $form
->schema([
TextInput::make('database')->columnSpanFull(),
TextInput::make('username'),
TextInput::make('password')
->hintAction(
Action::make('rotate')
->icon('tabler-refresh')
->requiresConfirmation()
->action(fn (DatabasePasswordService $service, Database $database, $set, $get) => $this->rotatePassword($service, $database, $set, $get))
)
->formatStateUsing(fn (Database $database) => $database->password),
TextInput::make('remote')->label('Connections From'),
TextInput::make('max_connections'),
TextInput::make('JDBC')
->label('JDBC Connection String')
->columnSpanFull()
->formatStateUsing(fn (Get $get, Database $database) => 'jdbc:mysql://' . $get('username') . ':' . urlencode($database->password) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database')),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('servers')
->columns([
TextColumn::make('database')->icon('tabler-database'),
TextColumn::make('username')->icon('tabler-user'),
TextColumn::make('remote'),
TextColumn::make('server.name')
->icon('tabler-brand-docker')
->url(fn (Database $database) => route('filament.admin.resources.servers.edit', ['record' => $database->server_id])),
TextColumn::make('max_connections'),
TextColumn::make('created_at')->dateTime(),
])
->actions([
DeleteAction::make(),
ViewAction::make()->color('primary'),
]);
}
protected function rotatePassword(DatabasePasswordService $service, Database $database, Set $set, Get $get): void
{
$newPassword = $service->handle($database);
$jdbcString = 'jdbc:mysql://' . $get('username') . ':' . urlencode($newPassword) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database');
$set('password', $newPassword);
$set('JDBC', $jdbcString);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\DatabaseResource\Pages;
use App\Models\Database;
use Filament\Resources\Resource;
class DatabaseResource extends Resource
{
protected static ?string $model = Database::class;
protected static ?string $navigationIcon = 'tabler-database';
protected static bool $shouldRegisterNavigation = false;
protected static ?string $navigationGroup = 'Advanced';
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;
}
public static function getPages(): array
{
return [
'index' => Pages\ListDatabases::route('/'),
'create' => Pages\CreateDatabase::route('/create'),
'edit' => Pages\EditDatabase::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Filament\Resources\DatabaseResource\Pages;
use App\Filament\Resources\DatabaseResource;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\Pages\CreateRecord;
class CreateDatabase extends CreateRecord
{
protected static string $resource = DatabaseResource::class;
public function form(Form $form): Form
{
return $form
->schema([
Select::make('server_id')
->relationship('server', 'name')
->searchable()
->preload()
->required(),
Select::make('database_host_id')
->relationship('host', 'name')
->searchable()
->selectablePlaceholder(false)
->preload()
->required(),
TextInput::make('database')
->required()
->maxLength(255),
TextInput::make('remote')
->required()
->maxLength(255)
->default('%'),
TextInput::make('username')
->required()
->maxLength(255),
TextInput::make('password')
->password()
->revealable()
->required(),
TextInput::make('max_connections')
->numeric()
->minValue(0)
->default(0),
]);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Filament\Resources\DatabaseResource\Pages;
use App\Filament\Resources\DatabaseResource;
use Filament\Actions;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\Pages\EditRecord;
class EditDatabase extends EditRecord
{
protected static string $resource = DatabaseResource::class;
public function form(Form $form): Form
{
return $form
->schema([
Select::make('server_id')
->relationship('server', 'name')
->searchable()
->preload()
->required(),
TextInput::make('database_host_id')
->required()
->numeric(),
TextInput::make('database')
->required()
->maxLength(255),
TextInput::make('remote')
->required()
->maxLength(255)
->default('%'),
TextInput::make('username')
->required()
->maxLength(255),
TextInput::make('password')
->password()
->revealable()
->required(),
TextInput::make('max_connections')
->numeric()
->minValue(0)
->default(0),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Filament\Resources\DatabaseResource\Pages;
use App\Filament\Resources\DatabaseResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Actions\BulkActionGroup;
use Filament\Tables\Actions\DeleteBulkAction;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ListDatabases extends ListRecords
{
protected static string $resource = DatabaseResource::class;
public function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('server.name')
->numeric()
->sortable(),
TextColumn::make('database_host_id')
->numeric()
->sortable(),
TextColumn::make('database')
->searchable(),
TextColumn::make('username')
->searchable(),
TextColumn::make('remote')
->searchable(),
TextColumn::make('max_connections')
->numeric()
->sortable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->actions([
EditAction::make(),
])
->bulkActions([
BulkActionGroup::make([
DeleteBulkAction::make()
->authorize(fn () => auth()->user()->can('delete database')),
]),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\EggResource\Pages;
use App\Models\Egg;
use Filament\Resources\Resource;
class EggResource extends Resource
{
protected static ?string $model = Egg::class;
protected static ?string $navigationIcon = 'tabler-eggs';
protected static ?string $recordTitleAttribute = 'name';
protected static ?string $recordRouteKeyName = 'id';
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;
}
public static function getGloballySearchableAttributes(): array
{
return ['name', 'tags', 'uuid', 'id'];
}
public static function getPages(): array
{
return [
'index' => Pages\ListEggs::route('/'),
'create' => Pages\CreateEgg::route('/create'),
'edit' => Pages\EditEgg::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,250 @@
<?php
namespace App\Filament\Resources\EggResource\Pages;
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
use App\Filament\Resources\EggResource;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Form;
use Filament\Forms\Set;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class CreateEgg extends CreateRecord
{
protected static string $resource = EggResource::class;
protected static bool $canCreateAnother = false;
public function form(Form $form): Form
{
return $form
->schema([
Tabs::make()->tabs([
Tab::make('Configuration')
->columns(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 4])
->schema([
TextInput::make('name')
->required()
->maxLength(255)
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2])
->helperText('A simple, human-readable name to use as an identifier for this Egg.'),
TextInput::make('author')
->maxLength(255)
->required()
->email()
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2])
->helperText('The author of this version of the Egg.'),
Textarea::make('description')
->rows(3)
->columnSpanFull()
->helperText('A description of this Egg that will be displayed throughout the Panel as needed.'),
Textarea::make('startup')
->rows(3)
->columnSpanFull()
->required()
->placeholder(implode("\n", [
'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}',
]))
->helperText('The default startup command that should be used for new servers using this Egg.'),
TagsInput::make('features')
->placeholder('Add Feature')
->helperText('')
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]),
Toggle::make('force_outgoing_ip')
->hintIcon('tabler-question-mark')
->hintIconTooltip("Forces all outgoing network traffic to have its Source IP NATed to the IP of the server's primary endpoint.
Required for certain games to work properly when the Node has multiple public IP addresses.
Enabling this option will disable internal networking for any servers using this egg, causing them to be unable to internally access other servers on the same node."),
Hidden::make('script_is_privileged')
->default(1),
TagsInput::make('tags')
->placeholder('Add Tags')
->helperText('')
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]),
TextInput::make('update_url')
->hintIcon('tabler-question-mark')
->hintIconTooltip('URLs must point directly to the raw .json file.')
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2])
->url(),
KeyValue::make('docker_images')
->live()
->columnSpanFull()
->required()
->addActionLabel('Add Image')
->keyLabel('Name')
->keyPlaceholder('Java 21')
->valueLabel('Image URI')
->valuePlaceholder('ghcr.io/parkervcp/yolks:java_21')
->helperText('The docker images available to servers using this egg.'),
]),
Tab::make('Process Management')
->columns()
->schema([
Hidden::make('config_from')
->default(null)
->label('Copy Settings From')
// ->placeholder('None')
// ->relationship('configFrom', 'name', ignoreRecord: true)
->helperText('If you would like to default to settings from another Egg select it from the menu above.'),
TextInput::make('config_stop')
->required()
->maxLength(255)
->label('Stop Command')
->helperText('The command that should be sent to server processes to stop them gracefully. If you need to send a SIGINT you should enter ^C here.'),
Textarea::make('config_startup')->rows(10)->json()
->label('Start Configuration')
->default('{}')
->helperText('List of values the daemon should be looking for when booting a server to determine completion.'),
Textarea::make('config_files')->rows(10)->json()
->label('Configuration Files')
->default('{}')
->helperText('This should be a JSON representation of configuration files to modify and what parts should be changed.'),
Textarea::make('config_logs')->rows(10)->json()
->label('Log Configuration')
->default('{}')
->helperText('This should be a JSON representation of where log files are stored, and whether or not the daemon should be creating custom logs.'),
]),
Tab::make('Egg Variables')
->columnSpanFull()
->schema([
Repeater::make('variables')
->label('')
->addActionLabel('Add New Egg Variable')
->grid()
->relationship('variables')
->name('name')
->reorderable()->orderColumn()
->collapsible()->collapsed()
->columnSpan(2)
->defaultItems(0)
->itemLabel(fn (array $state) => $state['name'])
->mutateRelationshipDataBeforeCreateUsing(function (array $data): array {
$data['default_value'] ??= '';
$data['description'] ??= '';
$data['rules'] ??= [];
$data['user_viewable'] ??= '';
$data['user_editable'] ??= '';
return $data;
})
->mutateRelationshipDataBeforeSaveUsing(function (array $data): array {
$data['default_value'] ??= '';
$data['description'] ??= '';
$data['rules'] ??= [];
$data['user_viewable'] ??= '';
$data['user_editable'] ??= '';
return $data;
})
->schema([
TextInput::make('name')
->live()
->debounce(750)
->maxLength(255)
->columnSpanFull()
->afterStateUpdated(fn (Set $set, $state) => $set('env_variable', str($state)->trim()->snake()->upper()->toString())
)
->required(),
Textarea::make('description')->columnSpanFull(),
TextInput::make('env_variable')
->label('Environment Variable')
->maxLength(255)
->prefix('{{')
->suffix('}}')
->hintIcon('tabler-code')
->hintIconTooltip(fn ($state) => "{{{$state}}}")
->required(),
TextInput::make('default_value')->maxLength(255),
Fieldset::make('User Permissions')
->schema([
Checkbox::make('user_viewable')->label('Viewable'),
Checkbox::make('user_editable')->label('Editable'),
]),
TagsInput::make('rules')
->columnSpanFull()
->placeholder('Add Rule')
->reorderable()
->suggestions([
'required',
'nullable',
'string',
'integer',
'numeric',
'boolean',
'alpha',
'alpha_dash',
'alpha_num',
'url',
'email',
'regex:',
'min:',
'max:',
'between:',
'between:1024,65535',
'in:',
'in:true,false',
]),
]),
]),
Tab::make('Install Script')
->columns(3)
->schema([
Hidden::make('copy_script_from'),
//->placeholder('None')
//->relationship('scriptFrom', 'name', ignoreRecord: true),
TextInput::make('script_container')
->required()
->maxLength(255)
->default('ghcr.io/pelican-eggs/installers:debian'),
Select::make('script_entry')
->selectablePlaceholder(false)
->default('bash')
->options(['bash', 'ash', '/bin/bash'])
->required(),
MonacoEditor::make('script_install')
->columnSpanFull()
->fontSize('16px')
->language('shell')
->lazy()
->view('filament.plugins.monaco-editor'),
]),
])->columnSpanFull()->persistTabInQueryString(),
]);
}
protected function handleRecordCreation(array $data): Model
{
$data['uuid'] ??= Str::uuid()->toString();
if (is_array($data['config_startup'])) {
$data['config_startup'] = json_encode($data['config_startup']);
}
if (is_array($data['config_logs'])) {
$data['config_logs'] = json_encode($data['config_logs']);
}
logger()->info('new egg', $data);
return parent::handleRecordCreation($data);
}
}

View File

@@ -0,0 +1,341 @@
<?php
namespace App\Filament\Resources\EggResource\Pages;
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
use App\Filament\Resources\EggResource;
use App\Filament\Resources\EggResource\RelationManagers\ServersRelationManager;
use App\Models\Egg;
use App\Services\Eggs\Sharing\EggExporterService;
use App\Services\Eggs\Sharing\EggImporterService;
use Exception;
use Filament\Actions;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Form;
use Filament\Forms\Set;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
class EditEgg extends EditRecord
{
protected static string $resource = EggResource::class;
public function form(Form $form): Form
{
return $form
->schema([
Tabs::make()->tabs([
Tab::make('Configuration')
->columns(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 4])
->icon('tabler-egg')
->schema([
TextInput::make('name')
->required()
->maxLength(255)
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 1])
->helperText('A simple, human-readable name to use as an identifier for this Egg.'),
TextInput::make('uuid')
->label('Egg UUID')
->disabled()
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2])
->helperText('This is the globally unique identifier for this Egg which Wings uses as an identifier.'),
TextInput::make('id')
->label('Egg ID')
->disabled(),
Textarea::make('description')
->rows(3)
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2])
->helperText('A description of this Egg that will be displayed throughout the Panel as needed.'),
TextInput::make('author')
->required()
->maxLength(255)
->email()
->disabled()
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2])
->helperText('The author of this version of the Egg. Uploading a new Egg configuration from a different author will change this.'),
Textarea::make('startup')
->rows(2)
->columnSpanFull()
->required()
->helperText('The default startup command that should be used for new servers using this Egg.'),
TagsInput::make('file_denylist')
->hidden() // latest wings breaks it.
->placeholder('denied-file.txt')
->helperText('A list of files that the end user is not allowed to edit.')
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]),
TagsInput::make('features')
->placeholder('Add Feature')
->helperText('')
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]),
Toggle::make('force_outgoing_ip')
->inline(false)
->hintIcon('tabler-question-mark')
->hintIconTooltip("Forces all outgoing network traffic to have its Source IP NATed to the IP of the server's endpoint.
Required for certain games to work properly when the Node has multiple public IP addresses.
Enabling this option will disable internal networking for any servers using this egg, causing them to be unable to internally access other servers on the same node."),
Hidden::make('script_is_privileged')
->helperText('The docker images available to servers using this egg.'),
TagsInput::make('tags')
->placeholder('Add Tags')
->helperText('')
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]),
TextInput::make('update_url')
->label('Update URL')
->url()
->hintIcon('tabler-question-mark')
->hintIconTooltip('URLs must point directly to the raw .json file.')
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]),
KeyValue::make('docker_images')
->live()
->columnSpanFull()
->required()
->addActionLabel('Add Image')
->keyLabel('Name')
->valueLabel('Image URI')
->helperText('The docker images available to servers using this egg.'),
]),
Tab::make('Process Management')
->columns()
->icon('tabler-server-cog')
->schema([
Select::make('config_from')
->label('Copy Settings From')
->placeholder('None')
->relationship('configFrom', 'name', ignoreRecord: true)
->helperText('If you would like to default to settings from another Egg select it from the menu above.'),
TextInput::make('config_stop')
->maxLength(255)
->label('Stop Command')
->helperText('The command that should be sent to server processes to stop them gracefully. If you need to send a SIGINT you should enter ^C here.'),
Textarea::make('config_startup')->rows(10)->json()
->label('Start Configuration')
->helperText('List of values the daemon should be looking for when booting a server to determine completion.'),
Textarea::make('config_files')->rows(10)->json()
->label('Configuration Files')
->helperText('This should be a JSON representation of configuration files to modify and what parts should be changed.'),
Textarea::make('config_logs')->rows(10)->json()
->label('Log Configuration')
->helperText('This should be a JSON representation of where log files are stored, and whether or not the daemon should be creating custom logs.'),
]),
Tab::make('Egg Variables')
->columnSpanFull()
->icon('tabler-variable')
->schema([
Repeater::make('variables')
->label('')
->grid()
->relationship('variables')
->name('name')
->reorderable()
->collapsible()->collapsed()
->orderColumn()
->addActionLabel('New Variable')
->itemLabel(fn (array $state) => $state['name'])
->mutateRelationshipDataBeforeCreateUsing(function (array $data): array {
$data['default_value'] ??= '';
$data['description'] ??= '';
$data['rules'] ??= [];
$data['user_viewable'] ??= '';
$data['user_editable'] ??= '';
return $data;
})
->mutateRelationshipDataBeforeSaveUsing(function (array $data): array {
$data['default_value'] ??= '';
$data['description'] ??= '';
$data['rules'] ??= [];
$data['user_viewable'] ??= '';
$data['user_editable'] ??= '';
return $data;
})
->schema([
TextInput::make('name')
->live()
->debounce(750)
->maxLength(255)
->columnSpanFull()
->afterStateUpdated(fn (Set $set, $state) => $set('env_variable', str($state)->trim()->snake()->upper()->toString())
)
->required(),
Textarea::make('description')->columnSpanFull(),
TextInput::make('env_variable')
->label('Environment Variable')
->maxLength(255)
->prefix('{{')
->suffix('}}')
->hintIcon('tabler-code')
->hintIconTooltip(fn ($state) => "{{{$state}}}")
->required(),
TextInput::make('default_value')->maxLength(255),
Fieldset::make('User Permissions')
->schema([
Checkbox::make('user_viewable')->label('Viewable'),
Checkbox::make('user_editable')->label('Editable'),
]),
TagsInput::make('rules')
->columnSpanFull()
->placeholder('Add Rule')
->reorderable()
->suggestions([
'required',
'nullable',
'string',
'integer',
'numeric',
'boolean',
'alpha',
'alpha_dash',
'alpha_num',
'url',
'email',
'regex:',
'min:',
'max:',
'between:',
'between:1024,65535',
'in:',
'in:true,false',
]),
]),
]),
Tab::make('Install Script')
->columns(3)
->icon('tabler-file-download')
->schema([
Select::make('copy_script_from')
->placeholder('None')
->relationship('scriptFrom', 'name', ignoreRecord: true),
TextInput::make('script_container')
->required()
->maxLength(255)
->default('alpine:3.4'),
TextInput::make('script_entry')
->required()
->maxLength(255)
->default('ash'),
MonacoEditor::make('script_install')
->label('Install Script')
->columnSpanFull()
->fontSize('16px')
->language('shell')
->view('filament.plugins.monaco-editor'),
]),
])->columnSpanFull()->persistTabInQueryString(),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make('deleteEgg')
->disabled(fn (Egg $egg): bool => $egg->servers()->count() > 0)
->label(fn (Egg $egg): string => $egg->servers()->count() <= 0 ? 'Delete' : 'In Use'),
Actions\Action::make('exportEgg')
->label('Export')
->color('primary')
->action(fn (EggExporterService $service, Egg $egg) => response()->streamDownload(function () use ($service, $egg) {
echo $service->handle($egg->id);
}, 'egg-' . $egg->getKebabName() . '.json'))
->authorize(fn () => auth()->user()->can('export egg')),
Actions\Action::make('importEgg')
->label('Import')
->form([
Placeholder::make('warning')
->label('This will overwrite the current egg to the one you upload.'),
Tabs::make('Tabs')
->tabs([
Tab::make('From File')
->icon('tabler-file-upload')
->schema([
FileUpload::make('egg')
->label('Egg')
->hint('eg. minecraft.json')
->acceptedFileTypes(['application/json'])
->storeFiles(false),
]),
Tab::make('From URL')
->icon('tabler-world-upload')
->schema([
TextInput::make('url')
->label('URL')
->default(fn (Egg $egg): ?string => $egg->update_url)
->hint('Link to the egg file (eg. minecraft.json)')
->url(),
]),
])
->contained(false),
])
->action(function (array $data, Egg $egg, EggImporterService $eggImportService): void {
if (!empty($data['egg'])) {
try {
$eggImportService->fromFile($data['egg'], $egg);
} catch (Exception $exception) {
Notification::make()
->title('Import Failed')
->body($exception->getMessage())
->danger() // Will Robinson
->send();
report($exception);
return;
}
} elseif (!empty($data['url'])) {
try {
$eggImportService->fromUrl($data['url'], $egg);
} catch (Exception $exception) {
Notification::make()
->title('Import Failed')
->body($exception->getMessage())
->danger()
->send();
report($exception);
return;
}
}
$this->refreshForm();
Notification::make()
->title('Import Success')
->success()
->send();
})
->authorize(fn () => auth()->user()->can('import egg')),
$this->getSaveFormAction()->formId('form'),
];
}
public function refreshForm(): void
{
$this->fillForm();
}
protected function getFormActions(): array
{
return [];
}
public function getRelationManagers(): array
{
return [
ServersRelationManager::class,
];
}
}

View File

@@ -0,0 +1,178 @@
<?php
namespace App\Filament\Resources\EggResource\Pages;
use App\Filament\Resources\EggResource;
use App\Models\Egg;
use App\Services\Eggs\Sharing\EggExporterService;
use App\Services\Eggs\Sharing\EggImporterService;
use Exception;
use Filament\Actions;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Actions\Action;
use Filament\Tables\Actions\BulkActionGroup;
use Filament\Tables\Actions\DeleteBulkAction;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
class ListEggs extends ListRecords
{
protected static string $resource = EggResource::class;
public function table(Table $table): Table
{
return $table
->searchable(true)
->defaultPaginationPageOption(25)
->checkIfRecordIsSelectableUsing(fn (Egg $egg) => $egg->servers_count <= 0)
->columns([
TextColumn::make('id')
->label('Id')
->hidden(),
TextColumn::make('name')
->icon('tabler-egg')
->description(fn ($record): ?string => (strlen($record->description) > 120) ? substr($record->description, 0, 120).'...' : $record->description)
->wrap()
->searchable()
->sortable(),
TextColumn::make('servers_count')
->counts('servers')
->icon('tabler-server')
->label('Servers'),
])
->actions([
EditAction::make(),
Action::make('export')
->icon('tabler-download')
->label('Export')
->color('primary')
->action(fn (EggExporterService $service, Egg $egg) => response()->streamDownload(function () use ($service, $egg) {
echo $service->handle($egg->id);
}, 'egg-' . $egg->getKebabName() . '.json'))
->authorize(fn () => auth()->user()->can('export egg')),
Action::make('update')
->icon('tabler-cloud-download')
->label('Update')
->color('success')
->requiresConfirmation()
->modalHeading('Are you sure you want to update this egg?')
->modalDescription('If you made any changes to the egg they will be overwritten!')
->modalIconColor('danger')
->modalSubmitAction(fn (Actions\StaticAction $action) => $action->color('danger'))
->action(function (Egg $egg, EggImporterService $eggImporterService) {
try {
$eggImporterService->fromUrl($egg->update_url, $egg);
cache()->forget("eggs.{$egg->uuid}.update");
} catch (Exception $exception) {
Notification::make()
->title('Egg Update failed')
->body($exception->getMessage())
->danger()
->send();
report($exception);
return;
}
Notification::make()
->title('Egg updated')
->success()
->send();
})
->authorize(fn () => auth()->user()->can('import egg'))
->visible(fn (Egg $egg) => cache()->get("eggs.{$egg->uuid}.update", false)),
])
->bulkActions([
BulkActionGroup::make([
DeleteBulkAction::make()
->authorize(fn () => auth()->user()->can('delete egg')),
]),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make('create')->label('Create Egg'),
Actions\Action::make('import')
->label('Import')
->form([
Tabs::make('Tabs')
->tabs([
Tab::make('From File')
->icon('tabler-file-upload')
->schema([
FileUpload::make('egg')
->label('Egg')
->hint('This should be the json file ( egg-minecraft.json )')
->acceptedFileTypes(['application/json'])
->storeFiles(false)
->multiple(),
]),
Tab::make('From URL')
->icon('tabler-world-upload')
->schema([
TextInput::make('url')
->label('URL')
->hint('This URL should point to a single json file')
->url(),
]),
])
->contained(false),
])
->action(function (array $data, EggImporterService $eggImportService): void {
if (!empty($data['egg'])) {
/** @var TemporaryUploadedFile[] $eggFile */
$eggFile = $data['egg'];
foreach ($eggFile as $file) {
try {
$eggImportService->fromFile($file);
} catch (Exception $exception) {
Notification::make()
->title('Import Failed')
->danger()
->send();
report($exception);
return;
}
}
}
if (!empty($data['url'])) {
try {
$eggImportService->fromUrl($data['url']);
} catch (Exception $exception) {
Notification::make()
->title('Import Failed')
->danger()
->send();
report($exception);
return;
}
}
Notification::make()
->title('Import Success')
->success()
->send();
})
->authorize(fn () => auth()->user()->can('import egg')),
];
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Filament\Resources\EggResource\RelationManagers;
use App\Models\Server;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ServersRelationManager extends RelationManager
{
protected static string $relationship = 'servers';
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('servers')
->emptyStateDescription('No Servers')->emptyStateHeading('No servers are assigned to this Egg.')
->searchable(false)
->columns([
TextColumn::make('user.username')
->label('Owner')
->icon('tabler-user')
->url(fn (Server $server): string => route('filament.admin.resources.users.edit', ['record' => $server->user]))
->sortable(),
TextColumn::make('name')
->icon('tabler-brand-docker')
->url(fn (Server $server): string => route('filament.admin.resources.servers.edit', ['record' => $server]))
->sortable(),
TextColumn::make('node.name')
->icon('tabler-server-2')
->url(fn (Server $server): string => route('filament.admin.resources.nodes.edit', ['record' => $server->node])),
TextColumn::make('image')
->label('Docker Image'),
]);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\MountResource\Pages;
use App\Models\Mount;
use Filament\Resources\Resource;
class MountResource extends Resource
{
protected static ?string $model = Mount::class;
protected static ?string $navigationIcon = 'tabler-layers-linked';
protected static ?string $navigationGroup = 'Advanced';
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;
}
public static function getPages(): array
{
return [
'index' => Pages\ListMounts::route('/'),
'create' => Pages\CreateMount::route('/create'),
'edit' => Pages\EditMount::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace App\Filament\Resources\MountResource\Pages;
use App\Filament\Resources\MountResource;
use Filament\Forms\Components\Group;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Form;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class CreateMount extends CreateRecord
{
protected static string $resource = MountResource::class;
protected static bool $canCreateAnother = false;
public function form(Form $form): Form
{
return $form
->schema([
Section::make()->schema([
TextInput::make('name')
->required()
->helperText('Unique name used to separate this mount from another.')
->maxLength(64),
ToggleButtons::make('read_only')
->label('Read only?')
->helperText('Is the mount read only inside the container?')
->options([
false => 'Writeable',
true => 'Read only',
])
->icons([
false => 'tabler-writing',
true => 'tabler-writing-off',
])
->colors([
false => 'warning',
true => 'success',
])
->inline()
->default(false)
->required(),
TextInput::make('source')
->required()
->helperText('File path on the host system to mount to a container.')
->maxLength(255),
TextInput::make('target')
->required()
->helperText('Where the mount will be accessible inside a container.')
->maxLength(255),
ToggleButtons::make('user_mountable')
->hidden()
->label('User mountable?')
->options([
false => 'No',
true => 'Yes',
])
->icons([
false => 'tabler-user-cancel',
true => 'tabler-user-bolt',
])
->colors([
false => 'success',
true => 'warning',
])
->default(false)
->inline()
->required(),
Textarea::make('description')
->helperText('A longer description for this mount.')
->columnSpanFull(),
Hidden::make('user_mountable')->default(1),
])->columnSpan(1)->columns([
'default' => 1,
'lg' => 2,
]),
Group::make()->schema([
Section::make()->schema([
Select::make('eggs')->multiple()
->relationship('eggs', 'name')
->preload(),
Select::make('nodes')->multiple()
->relationship('nodes', 'name')
->searchable(['name', 'fqdn'])
->preload(),
]),
])->columns([
'default' => 1,
'lg' => 2,
]),
])->columns([
'default' => 1,
'lg' => 2,
]);
}
protected function handleRecordCreation(array $data): Model
{
$data['uuid'] ??= Str::uuid()->toString();
return parent::handleRecordCreation($data);
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace App\Filament\Resources\MountResource\Pages;
use App\Filament\Resources\MountResource;
use Filament\Actions;
use Filament\Forms\Components\Group;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Form;
use Filament\Resources\Pages\EditRecord;
class EditMount extends EditRecord
{
protected static string $resource = MountResource::class;
public function form(Form $form): Form
{
return $form
->schema([
Section::make()->schema([
TextInput::make('name')
->required()
->helperText('Unique name used to separate this mount from another.')
->maxLength(64),
ToggleButtons::make('read_only')
->label('Read only?')
->helperText('Is the mount read only inside the container?')
->options([
false => 'Writeable',
true => 'Read only',
])
->icons([
false => 'tabler-writing',
true => 'tabler-writing-off',
])
->colors([
false => 'warning',
true => 'success',
])
->inline()
->default(false)
->required(),
TextInput::make('source')
->required()
->helperText('File path on the host system to mount to a container.')
->maxLength(255),
TextInput::make('target')
->required()
->helperText('Where the mount will be accessible inside a container.')
->maxLength(255),
ToggleButtons::make('user_mountable')
->hidden()
->label('User mountable?')
->options([
false => 'No',
true => 'Yes',
])
->icons([
false => 'tabler-user-cancel',
true => 'tabler-user-bolt',
])
->colors([
false => 'success',
true => 'warning',
])
->default(false)
->inline()
->required(),
Textarea::make('description')
->helperText('A longer description for this mount.')
->columnSpanFull(),
])->columnSpan(1)->columns([
'default' => 1,
'lg' => 2,
]),
Group::make()->schema([
Section::make()->schema([
Select::make('eggs')->multiple()
->relationship('eggs', 'name')
->preload(),
Select::make('nodes')->multiple()
->relationship('nodes', 'name')
->searchable(['name', 'fqdn'])
->preload(),
]),
])->columns([
'default' => 1,
'lg' => 2,
]),
])->columns([
'default' => 1,
'lg' => 2,
]);
}
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
$this->getSaveFormAction()->formId('form'),
];
}
protected function getFormActions(): array
{
return [];
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Filament\Resources\MountResource\Pages;
use App\Filament\Resources\MountResource;
use App\Models\Mount;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Actions\BulkActionGroup;
use Filament\Tables\Actions\CreateAction;
use Filament\Tables\Actions\DeleteBulkAction;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ListMounts extends ListRecords
{
protected static string $resource = MountResource::class;
public function table(Table $table): Table
{
return $table
->searchable(false)
->columns([
TextColumn::make('name')
->searchable(),
TextColumn::make('source')
->searchable(),
TextColumn::make('target')
->searchable(),
IconColumn::make('read_only')
->icon(fn (bool $state) => $state ? 'tabler-circle-check-filled' : 'tabler-circle-x-filled')
->color(fn (bool $state) => $state ? 'success' : 'danger')
->sortable(),
IconColumn::make('user_mountable')
->hidden()
->icon(fn (bool $state) => $state ? 'tabler-circle-check-filled' : 'tabler-circle-x-filled')
->color(fn (bool $state) => $state ? 'success' : 'danger')
->sortable(),
])
->actions([
EditAction::make(),
])
->bulkActions([
BulkActionGroup::make([
DeleteBulkAction::make()
->authorize(fn () => auth()->user()->can('delete mount')),
]),
])
->emptyStateIcon('tabler-layers-linked')
->emptyStateDescription('')
->emptyStateHeading('No Mounts')
->emptyStateActions([
CreateAction::make('create')
->label('Create Mount')
->button(),
]);
}
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make()
->label('Create Mount')
->hidden(fn () => Mount::count() <= 0),
];
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\NodeResource\Pages;
use App\Filament\Resources\NodeResource\RelationManagers\NodesRelationManager;
use App\Models\Node;
use Filament\Resources\Resource;
class NodeResource extends Resource
{
protected static ?string $model = Node::class;
protected static ?string $navigationIcon = 'tabler-server-2';
protected static ?string $recordTitleAttribute = 'name';
public static function getNavigationBadge(): ?string
{
return static::getModel()::count() ?: null;
}
public static function getRelations(): array
{
return [
NodesRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListNodes::route('/'),
'create' => Pages\CreateNode::route('/create'),
'edit' => Pages\EditNode::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,410 @@
<?php
namespace App\Filament\Resources\NodeResource\Pages;
use App\Filament\Resources\NodeResource;
use App\Models\Objects\Endpoint;
use Filament\Forms;
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Components\Wizard;
use Filament\Forms\Components\Wizard\Step;
use Filament\Forms\Get;
use Filament\Forms\Set;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\HtmlString;
class CreateNode extends CreateRecord
{
protected static string $resource = NodeResource::class;
protected static bool $canCreateAnother = false;
protected ?string $subheading = 'which is a machine that runs your Servers';
public function form(Forms\Form $form): Forms\Form
{
return $form
->schema([
Wizard::make([
Step::make('basic')
->label('Basic Settings')
->icon('tabler-server')
->columnSpanFull()
->columns([
'default' => 2,
'sm' => 3,
'md' => 3,
'lg' => 4,
])
->schema([
TextInput::make('fqdn')
->columnSpan(2)
->required()
->autofocus()
->live(debounce: 1500)
->rule('prohibited', fn ($state) => is_ip($state) && request()->isSecure())
->label(fn ($state) => is_ip($state) ? 'IP Address' : 'Domain Name')
->placeholder(fn ($state) => is_ip($state) ? '192.168.1.1' : 'node.example.com')
->helperText(function ($state) {
if (is_ip($state)) {
if (request()->isSecure()) {
return '
Your panel is currently secured via an SSL certificate and that means your nodes require one too.
You must use a domain name, because you cannot get SSL certificates for IP Addresses
';
}
return '';
}
return "
This is the domain name that points to your node's IP Address.
If you've already set up this, you can verify it by checking the next field!
";
})
->hintColor('danger')
->hint(function ($state) {
if (is_ip($state) && request()->isSecure()) {
return 'You cannot connect to an IP Address over SSL';
}
return '';
})
->afterStateUpdated(function (Set $set, ?string $state) {
$set('dns', null);
$set('ip', null);
[$subdomain] = str($state)->explode('.', 2);
if (!is_numeric($subdomain)) {
$set('name', $subdomain);
}
if (!$state || is_ip($state)) {
$set('dns', null);
return;
}
$validRecords = gethostbynamel($state);
if ($validRecords) {
$set('dns', true);
$set('ip', collect($validRecords)->first());
return;
}
$set('dns', false);
})
->maxLength(255),
TextInput::make('ip')
->disabled()
->hidden(),
ToggleButtons::make('dns')
->label('DNS Record Check')
->helperText('This lets you know if your DNS record correctly points to an IP Address.')
->disabled()
->inline()
->default(null)
->hint(fn (Get $get) => $get('ip'))
->hintColor('success')
->options([
true => 'Valid',
false => 'Invalid',
])
->colors([
true => 'success',
false => 'danger',
])
->columnSpan([
'default' => 1,
'sm' => 1,
'md' => 1,
'lg' => 1,
]),
TextInput::make('daemon_listen')
->columnSpan([
'default' => 1,
'sm' => 1,
'md' => 1,
'lg' => 1,
])
->label(trans('strings.port'))
->helperText('If you are running the daemon behind Cloudflare you should set the daemon port to 8443 to allow websocket proxying over SSL.')
->minValue(1)
->maxValue(Endpoint::PORT_CEIL)
->default(8080)
->required()
->integer(),
TextInput::make('name')
->label('Display Name')
->columnSpan([
'default' => 1,
'sm' => 1,
'md' => 1,
'lg' => 2,
])
->required()
->helperText('This name is for display only and can be changed later.')
->maxLength(100),
ToggleButtons::make('scheme')
->label('Communicate over SSL')
->columnSpan([
'default' => 1,
'sm' => 1,
'md' => 1,
'lg' => 1,
])
->inline()
->helperText(function (Get $get) {
if (request()->isSecure()) {
return new HtmlString('Your Panel is using a secure SSL connection,<br>so your Daemon must too.');
}
if (is_ip($get('fqdn'))) {
return 'An IP address cannot use SSL.';
}
return '';
})
->disableOptionWhen(fn (string $value): bool => $value === 'http' && request()->isSecure())
->options([
'http' => 'HTTP',
'https' => 'HTTPS (SSL)',
])
->colors([
'http' => 'warning',
'https' => 'success',
])
->icons([
'http' => 'tabler-lock-open-off',
'https' => 'tabler-lock',
])
->default(fn () => request()->isSecure() ? 'https' : 'http'),
]),
Step::make('advanced')
->label('Advanced Settings')
->icon('tabler-server-cog')
->columnSpanFull()
->columns([
'default' => 2,
'sm' => 3,
'md' => 3,
'lg' => 4,
])
->schema([
ToggleButtons::make('maintenance_mode')
->label('Maintenance Mode')->inline()
->columnSpan(1)
->default(false)
->hinticon('tabler-question-mark')
->hintIconTooltip("If the node is marked 'Under Maintenance' users won't be able to access servers that are on this node.")
->options([
true => 'Enable',
false => 'Disable',
])
->colors([
true => 'danger',
false => 'success',
]),
ToggleButtons::make('public')
->default(true)
->columnSpan(1)
->label('Use Node for deployment?')->inline()
->options([
true => 'Yes',
false => 'No',
])
->colors([
true => 'success',
false => 'danger',
]),
TagsInput::make('tags')
->placeholder('Add Tags')
->columnSpan(2),
TextInput::make('upload_size')
->label('Upload Limit')
->helperText('Enter the maximum size of files that can be uploaded through the web-based file manager.')
->columnSpan(1)
->numeric()->required()
->default(256)
->minValue(1)
->maxValue(1024)
->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB'),
TextInput::make('daemon_sftp')
->columnSpan(1)
->label('SFTP Port')
->minValue(1)
->maxValue(Endpoint::PORT_CEIL)
->default(2022)
->required()
->integer(),
TextInput::make('daemon_sftp_alias')
->columnSpan(2)
->label('SFTP Alias')
->helperText('Display alias for the SFTP address. Leave empty to use the Node FQDN.'),
Grid::make()
->columns(6)
->columnSpanFull()
->schema([
ToggleButtons::make('unlimited_mem')
->label('Memory')->inlineLabel()->inline()
->afterStateUpdated(fn (Set $set) => $set('memory', 0))
->afterStateUpdated(fn (Set $set) => $set('memory_overallocate', 0))
->formatStateUsing(fn (Get $get) => $get('memory') == 0)
->live()
->options([
true => 'Unlimited',
false => 'Limited',
])
->colors([
true => 'primary',
false => 'warning',
])
->columnSpan(2),
TextInput::make('memory')
->dehydratedWhenHidden()
->hidden(fn (Get $get) => $get('unlimited_mem'))
->label('Memory Limit')->inlineLabel()
->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB')
->columnSpan(2)
->numeric()
->minValue(0)
->default(0)
->required(),
TextInput::make('memory_overallocate')
->dehydratedWhenHidden()
->label('Overallocate')->inlineLabel()
->hidden(fn (Get $get) => $get('unlimited_mem'))
->hintIcon('tabler-question-mark')
->hintIconTooltip('The % allowable to go over the set limit.')
->columnSpan(2)
->numeric()
->minValue(-1)
->maxValue(100)
->default(0)
->suffix('%')
->required(),
]),
Grid::make()
->columns(6)
->columnSpanFull()
->schema([
ToggleButtons::make('unlimited_disk')
->label('Disk')->inlineLabel()->inline()
->live()
->afterStateUpdated(fn (Set $set) => $set('disk', 0))
->afterStateUpdated(fn (Set $set) => $set('disk_overallocate', 0))
->formatStateUsing(fn (Get $get) => $get('disk') == 0)
->options([
true => 'Unlimited',
false => 'Limited',
])
->colors([
true => 'primary',
false => 'warning',
])
->columnSpan(2),
TextInput::make('disk')
->dehydratedWhenHidden()
->hidden(fn (Get $get) => $get('unlimited_disk'))
->label('Disk Limit')->inlineLabel()
->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB')
->columnSpan(2)
->numeric()
->minValue(0)
->default(0)
->required(),
TextInput::make('disk_overallocate')
->dehydratedWhenHidden()
->hidden(fn (Get $get) => $get('unlimited_disk'))
->label('Overallocate')->inlineLabel()
->hintIcon('tabler-question-mark')
->hintIconTooltip('The % allowable to go over the set limit.')
->columnSpan(2)
->numeric()
->minValue(-1)
->maxValue(100)
->default(0)
->suffix('%')
->required(),
]),
Grid::make()
->columns(6)
->columnSpanFull()
->schema([
ToggleButtons::make('unlimited_cpu')
->label('CPU')->inlineLabel()->inline()
->live()
->afterStateUpdated(fn (Set $set) => $set('cpu', 0))
->afterStateUpdated(fn (Set $set) => $set('cpu_overallocate', 0))
->formatStateUsing(fn (Get $get) => $get('cpu') == 0)
->options([
true => 'Unlimited',
false => 'Limited',
])
->colors([
true => 'primary',
false => 'warning',
])
->columnSpan(2),
TextInput::make('cpu')
->dehydratedWhenHidden()
->hidden(fn (Get $get) => $get('unlimited_cpu'))
->label('CPU Limit')->inlineLabel()
->suffix('%')
->columnSpan(2)
->numeric()
->default(0)
->minValue(0)
->required(),
TextInput::make('cpu_overallocate')
->dehydratedWhenHidden()
->hidden(fn (Get $get) => $get('unlimited_cpu'))
->label('Overallocate')->inlineLabel()
->hintIcon('tabler-question-mark')
->hintIconTooltip('The % allowable to go over the set limit.')
->columnSpan(2)
->numeric()
->default(0)
->minValue(-1)
->maxValue(100)
->suffix('%')
->required(),
]),
]),
])->columnSpanFull()
->nextAction(fn (Action $action) => $action->label('Next Step'))
->submitAction(new HtmlString(Blade::render(<<<'BLADE'
<x-filament::button
type="submit"
size="sm"
>
Create Node
</x-filament::button>
BLADE))),
]);
}
protected function getRedirectUrlParameters(): array
{
return [
'tab' => '-configuration-file-tab',
];
}
protected function getFormActions(): array
{
return [];
}
}

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