Compare commits

...

169 Commits

Author SHA1 Message Date
Lance Pioch
2bfc788e13 Allow searching for port when associating allocations (#801) 2024-12-08 16:24:00 -05:00
Lance Pioch
839ff96271 Fix power buttons (#799) 2024-12-08 16:19:15 -05:00
Lance Pioch
5d2b892eab Better IP addresses (#800)
* Unique ip addresses

* Only ipv4 addresses for now

* Switch to selects
2024-12-08 16:19:04 -05:00
MartinOscar
c953b97009 Force width (#798) 2024-12-08 20:27:16 +01:00
MartinOscar
9716b1e64d Only allow one * (#797) 2024-12-08 20:23:37 +01:00
Boy132
8358e410dc Move installer to correct namespace (#795) 2024-12-08 19:57:00 +01:00
Boy132
f6c586bf5b Add persistFiltersInSession to server list (#796) 2024-12-08 19:14:56 +01:00
Charles
feadaa2caf Add Kill button to console (#791)
* Add Kill button to console

* Add confirm, and warning
2024-12-08 12:01:44 -05:00
Charles
23246eb134 Fix #784 (#790)
* Remove +1

* Update app/Filament/Server/Pages/Settings.php

---------

Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-12-08 11:47:01 -05:00
Charles
6921c8b350 Fix power actions (#789) 2024-12-08 17:34:47 +01:00
Charles
8cc91b0747 Small updates (#787)
* increase action size on console

* fix layout on create database
2024-12-08 11:19:35 -05:00
Charles
157fa45234 Fix forever expanding code editor (#782)
* Update CSS

* Update Placeholder
2024-12-07 22:44:13 -05:00
Charles
fd5016809a Enable Global Search (#783) 2024-12-07 22:43:44 -05:00
Lance Pioch
a0f5ef13d6 Show login failure message (#781)
* Show login failure message

* Update resources/scripts/components/auth/LoginContainer.tsx

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

---------

Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-12-07 22:03:56 -05:00
Lance Pioch
67f1e91236 Fix Issue 763 (#780)
* Fix path to moved component

* Fix actual issue #763
2024-12-07 20:44:41 -05:00
Charles
cc3a7a2d0d Update Server Console, Again... (#776)
* More console changes

* Update Console Page

* Edit console input

* oops

* Remove failed attempt of clearing input when server offline

* Update File Editor to match console

* a touch more style

* Show not-allowed on read-only input

* round bottom corners of command input

* Move custom css to file
2024-12-07 19:22:18 -05:00
Charles
d908fb9a9d Remove unsaved changes alert (#778)
* Remove unsaved changes alert

* Remove this also

* Remove this also...
2024-12-07 11:39:00 -05:00
Boy132
6b96c9dbda Fix admin discover (#777) 2024-12-07 17:06:57 +01:00
Boy132
e27f23b1b6 Move admin pages & resources into own namespace (#741)
* move admin pages & resources into own namespace

* fix imports for resource pages
2024-12-07 15:51:27 +01:00
Charles
4ad2997566 Update database creation (#775)
Updates database creation flow to account for new database host to many nodes change.
2024-12-06 22:46:36 -05:00
pelican-vehikl
7e7f0be7df Allow Database Hosts to have multiple Nodes (#767)
* WIP

* Update laravel and migrations

* WIP

* fix tests

* Update composer

* Fix transformer

* Fix filament pages

* WIP

* Update DatabaseHostTransformer

* fix: tests

* pint this files pls

* resolve merge better

* Update migration

* Update Migration, Again

* Update down migration

---------

Co-authored-by: Vehikl <go@vehikl.com>
2024-12-06 20:24:30 -05:00
Boy132
5b3ae995e6 Show full client api key after creation (#771)
* show notification when api key is created

* remove hardcoded redirect url
2024-12-06 16:31:58 -05:00
Charles
2a34795ab1 More console changes (#774) 2024-12-06 16:21:05 -05:00
Charles
d3da1b0a58 Update Server Console, Address Overflows (#764)
* Update Console

Updates console to be more better <3.

Light Mode still needs some love, haven't figured that out with filaments light/dark options yet as it does not use the "bright<color>" colors...

* Add overflow to... Everything?

* Oops, Add Name label back

* Actually handle Transfer Status & remove useless switch

* Use switch case

* Readonly command input if server can't receive one

* lint

* Update app/Filament/Server/Widgets/ServerConsole.php

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

* Use filament::icon instead of raw svg

* Update resources/views/filament/components/server-console.blade.php

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

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
2024-12-06 09:46:10 -05:00
Boy132
5317f97870 Display roles as badge in user list (#772) 2024-12-06 13:02:37 +01:00
Boy132
b50acfdba2 Add config value for display width to other pages (#770) 2024-12-06 09:45:06 +01:00
Charles
066bdbdf78 Server Listing tweaks. (#760)
* Server Listing tweaks.

* Use filament::icon instead of raw svg & add hover title

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-12-05 08:34:47 -05:00
MartinOscar
8103ba6338 Remove unique (#765) 2024-12-05 08:55:04 +01:00
Boy132
44b879215f Add filters to server list (#762)
* add server owner filter

* add egg filter

* replace SelectFilter with TernaryFilter
2024-12-05 08:31:34 +01:00
Charles
d2a7d7708c Add Display Width setting (#759)
Allow users to pick how "wide" the panel is.
2024-12-04 09:41:47 -05:00
Boy132
efc37dd45a Hide sidebar on server list (#761) 2024-12-04 09:50:49 +01:00
MartinOscar
09eac71f05 Delete subuser on owner change (#748)
* Delete subuser on owner change

* Move logic to Model
2024-12-03 23:55:02 +01:00
Boy132
6d42a15ec3 Handle token expiring and token expired websocket events (#755)
* handle `token expiring` and `token expired` events

* fix "getToken"

* Move logic to Widget instead of blade & add user check

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-12-03 23:54:40 +01:00
Boy132
bbfdee356b Fix timezone for file timestamps (#757) 2024-12-03 14:27:06 +01:00
Charles
994852ca00 Tweak EditNode layout for mobile (#752)
* Tweak EditNode layout for mobile

* Replace hidden with toggle
2024-12-03 05:30:04 -05:00
MartinOscar
141baeb035 Empty array if user->oauth is null (#754) 2024-12-03 08:48:47 +01:00
Boy132
bd51191da6 Add role permissions for webhooks (#742) 2024-12-02 23:53:35 +01:00
Boy132
1337767049 Small changes for new client area (#751)
* add placeholder to allocation notes

* add button to open server in admin area

* use new client area for "console" button on EditServer

* hide schedule presets on view

* use arrow functions for auth checks

* add placeholder to schedules last run

* change icon of "open in admin"

* fix parentheses
2024-12-02 22:27:35 +01:00
Boy132
918ba02075 Remove exception methods because of memory bombing (#750)
* remove exception methods

* throw Halt instead of return

* manually throw Halt to make phpstan happy
2024-12-02 22:27:25 +01:00
Charles
c6977e57c8 Fix Subuser issues. (#747)
* Better Error handling

* Remove unique, make email lowercase in request

* Remove 'kill' option, not used.

* Prevent users from editing them selves

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-12-01 20:17:27 -05:00
MartinOscar
6d1c153d09 Add config panel.editable_server_descriptions check (#734)
* Add config panel.editable_server_descriptions check

* Hide the field rather then disabling it
2024-12-01 23:27:07 +01:00
Boy132
e5433b7aab Auto update resources on server list (#737)
* auto update resources on server list

* use Arr::get helper
2024-12-01 18:12:58 +01:00
Boy132
355810c549 Combine status & uptime, add address on ServerOverview (#739) 2024-12-01 18:12:28 +01:00
Boy132
4fd1937c54 Hide global search button for now (#738) 2024-12-01 18:04:24 +01:00
Lance Pioch
fea1c51337 feat: Client UI translate to Filament (from React) (#416)
* Add new panel

* Add some basic resource pages

* Wip

* Wip terminal

* Wip

* Add new panel

* Add some basic resource pages

* Wip

* [Sub-Users] Add Invite

TODO: The logic with permissions

* [Sub-Users] Fix Creation

* [Cron] Add basics

* Add basic auth and messages

* Add basic buttons

* WIP on issue/353

* WIP on issue/353

* Add Database page

* Update Database Page

* Start of Backup Page

* Composer Update

* Changes

* Send input

* Remove this includes

* Better offline handling

* Consolidate top nav config

* Update Backups Page

* Update Backups

* Change name

* Add Assign All, Layout Fixes.

* conflict

* update schedule pages

* fix phpstan

* update pint.json

* add cron presets to schedule

* fix tests

* fix task creation

* schedules: disable task creation if limit is reached & disable backup action if backup limit is 0

* update activity pages

* update resources

* Update Edit User

TODO: actually save permissions when they're changed.
TODO: Figure out why Control does not update it's state... but the rest do...

* .... Sure it works.

TODO: Update permissions when you save editing a sub user.

* user: update canAccessPanel & canAccessTenant

* add helper to convert bytes into readable format

* very basic file explorer

* files: fix some stuff & remove dummy data

* files: better error handling

* files: basic file editor

* files: add some actions

* File manager updates

* files: fix paths

* Revery Composer Upgrade, Fixes SQLite

* fix: Pint (#517)

feat: MenuItems to and from admin

* Update File Editing

Updated File Editing to its own page,
Added Permission checks for file manager.

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

* add enum for editor langs

* files: add upload & pull actions

* fix build

* files: handle images

* Update to Filament v3.2.98

* files: add remaining actions

* use `authorize` instead of `hidden`

* fix canAccessTenant

* update date columns

* files: testing & fixes

* Fix File Names

Co-authored-by: lancepioch <git@lance.sh>

* Combine Pull/Upload

* Fix BulkDelete

* Uncontained tabs

* Hide Lang Selection, Move Actions

* Update Monaco, more custom

* Add livewire config

livewire limits uploads to 12MB... who knows why...
Fixed uploading a single files failing

* files: fix record url

* basic setup for settings & startup page

* make abstract class for simple app pages

* Basic Startup Page

* Update nav sort

* small cleanup

* startup: fix shouldHideComponent & getSelectOptionsFromRules

* startup: fix non editable fields & set default value

* startup: add todo for save button

* Save Variables after update & off click

Variables update when the user clicks off the input.

* Notifications are cool

* Add rule validation

* Sort variables by sortid

* pint

* Settings Page + Startup Changes

* settings: cleanup

* refactor: use server model for ServerFormPage (formerly known as SimplePage)

* Use Repeater for variables

* Add Network, Remove breadcrumbs

* Add paginated to file explorer

* Fix updating variables

* Add link to go to new client area

* fix after merge

* Add graphs to console page

Graphs still need to get the data from the web socket.

* fix pint & phpstan

* fix authorizeAccess for EditFiles and Startup page

* Fix rules on startup page

* Update console size

* Fix node name

* add "global search" to files list

requires https://github.com/pelican-dev/wings/pull/44

* remove debug dummy data

* update view action on ListServers

* enable SPA mode for app panel

* remove colors from app panel

they are defined globally in AppServiceProvider

* update global search ui a bit

(to be replaced with a custom page that is similar to the list files table)

* add own page for global search

untested - and route needs cleanup (if possible)

* fix File getRows

* remove "path" from SearchFiles (for now)

* fix caching for searched files

* add title and breadcrumbs to global search page

* make cpu & memory charts on console page working

* fix phpstan

* add missing import

* cleanup console views & widgets

* add overview stats to console

* don't be so lazy, console!

* make history working

* decode data to get array

* add missing On

* fix json_decode

* change polling to 1 sec

* hide "0" cpu/ memory

* add data to network chart

* Remove data labels

* fix data on network chart

* fix data on network chart (2nd try)

* WIP Network Stats

* Remove test

* Change MaxWidth

* run pint

* fix phpstan

* Fix storeStats cast

* make $data a string

this time for real

* update visible check for "admin" menu item

* remove account widget

* rebrand "Dashboard" to "Server List"

WIP - doesn't look good but is somewhat working

* fix canAccessPanel

* separate server list into own panel

* change path to avoid conflicts with old client area (and remove sidebar width)

* display correct icon and color on server list entries

* show total memory if server is offline

* replace custom server list page with ListRecords page

* fix tests

* fix namespace

* remove "open" button and make whole column clickable

* Update EditProfile

* run pint

* fix access to server list

* add new login page to panels

* fix next_run_at for new schedules

* use new DateTimeColumn

* add own column for file bytes

* return to server list when clicking title

* fix console loading

* handle server with "conflict state"

* add banner if server is in "conflict state"

* fix phpstan

* update docker image select

* fix permission checks on Settings & Startup pages

* fix query for activity log page

* fix activity log not being logged

* adjust ListActivities

* fix phpstan

* fix pint

* fix profile menu item link on server panel

* add ip tooltip to activity logs (and role permission)

* change backup icon

* update navigation sort

* general code cleanup

* more cleanup

* Disable Restart/Stop if server is offline

* Change rename notification

* Remove negation on abort_unless

* Add notification on save

* Single disabled closure & comment unused import

* Add required to Server Name & Nullable to description

* mutateFormDataBeforeSave doesn't work since we use forceFill

* Fix web socket connection not existing.

* Fix some subuser permissions

* add permission checks to resources

* do not allow self-deletion

* Update editing file permissions

* Fix of the previous fix

* add service for subuser updating

* Only allow save if they have file_update

* Remove unused import

* Update backup delete button

* Add Delete, remove bulks

* Update Database page

* Use Allocation Permissions

* add canAccess check to startup

* Add Permission checks to Settings page

* add service for subuser deletion

* Remove Kill permission

* Updates

* fix move files

* add redirects

* fix phpstan

* activity: remove properties from tans for now

* If alias, use that, else ip

---------

Co-authored-by: notCharles <charles@pelican.dev>
Co-authored-by: Boy132 <mail@boy132.de>
Co-authored-by: Senna <62171904+Poseidon281@users.noreply.github.com>
Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-12-01 04:13:45 +01:00
Boy132
e0c6137b92 Installer: Shows errors on submit & move runMigrations (#722)
* catch Halt exception to make sure error notifications are displayed

* run migrations on submit to make sure the correct data is used
2024-12-01 04:04:40 +01:00
Charles
cd448cd9a7 Add Create Database btn on admin side (#721)
* Add Create Database btn on admin side

* Remove unused function

* readd function

* replace refreshform function

* add authorize, remove database limit check

* add random words, use proper name function, catch exceptions on creation

* add validation, match old client area more

* Add more authorize to Database tab

* Add confirmation to delete

* make password hidden / revealable

* better clarification

* Set default and remove placeholder.

* Remove server import, add database model to auth

* Make same changes for the database host page

* Update app/Filament/Resources/ServerResource/Pages/EditServer.php

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

* Update app/Filament/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php

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

* Update app/Filament/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php

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

* Remove each hidden

* Return nothing if user has no perms

* This is the way... Im done messing with it...

* Fix view permission for relationship manager

* Update app/Filament/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php

* Pint

---------

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-11-30 22:04:10 -05:00
Boy132
b208835ed4 Add Oauth frontend and backend improvements (#718)
* better oauth provider loading

* add auth frontend

* add configs for all default providers

* add more default providers

* add env variables to enable oauth providers

* small refactor to link/ unlink routes

* add oauth tab to (admin) profile

* use redirects instead of exceptions

* add notification if no oauth user is found

* use import in config

* remove whmcs provider

* replace hardcoded links with `route`

* redirect to account page on unlink

* remove unnecessary controller and handle linking/ unlinking in action

* only show oauth tab if at least one oauth provider is enabled
2024-11-30 17:38:38 +01:00
MartinOscar
951fc73363 Add min length check (#730) 2024-11-27 09:02:41 +01:00
Boy132
ad9447e974 Add back force https (#726) 2024-11-26 23:27:58 +01:00
Boy132
d2d960ecf3 Update egg jsons (#725)
* re-import eggs

* re-import eggs (again)
2024-11-23 23:29:37 +01:00
Boy132
d555c42644 Update all dependencies (#712)
* update composer.lock

* run pint

* fix phpstan

* update migrations (sqlite `dropForeign`)

* fix migrations

* Reset these back for now

* Alphabetize the rules

* run `php artisan filament:upgrade`

---------

Co-authored-by: Lance Pioch <git@lance.sh>
2024-11-22 09:27:57 +01:00
MartinOscar
f33f91698e Add exit admin to Menu (#723) 2024-11-21 17:49:19 +01:00
Boy132
90afae79db Fix permission check if user is subuser and admin (#720) 2024-11-20 08:41:37 +01:00
Boy132
54039e25a4 Make sure UTC is always used internally (#713)
* force app timezone to be UTC

* remove asDateTime overwrite

* add custom column to display dates in user timezone

* use `APP_TIMEZONE` as default timezone for new users

* revert accidental pinting
2024-11-15 20:41:33 +01:00
Boy132
408897cfcf Allow username on filament login page + make case insensitive (#714)
* allow login with username

* make login case insensitive

* fix tests
2024-11-15 20:39:06 +01:00
MartinOscar
24eb52f7d6 Merge pull request #709 from pelican-dev/charles/fixversio
Fix Panel Version Cache
2024-11-14 01:12:09 +01:00
notCharles
d87d3760a1 Fix Panel Version Cache 2024-11-13 19:08:46 -05:00
Boy132
fe4668a517 Update web installer (again) (#705)
* update web installer (again)

* set default values for mysql/ mariadb and redis

* add own step for queue setup

* create admin user in submit

* disable redis for queue if cache isn't redis

* remove separate user step and make session own step

* use `request()->isSecure()`
2024-11-13 18:15:48 -05:00
Lance Pioch
6125b07afa Remove old admin area (#648)
* Remove old admin

* Remove controller test

* Remove unused exceptions

* Remove unused files

* More small tweaks

* Fix doc block

* Remove unused service

* Restore these

* Add back autoDeploy

* Revert "Add back autoDeploy"

This reverts commit 630c1e08ac.

* Add these back

* Add back exception

* Remove ApiController again

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
Co-authored-by: Boy132 <mail@boy132.de>
Co-authored-by: notCharles <charles@pelican.dev>
2024-11-13 17:05:48 -05:00
Boy132
9717aa4b5f Cleanup SoftwareVersionService (#704)
* cleanup SoftwareVersionService

* fix old admin area

* show latest wings version on EditNode page

* even more cleanup
2024-11-13 16:26:10 -05:00
MartinOscar
9491322d8c Merge pull request #708 from pelican-dev/charles/fixbulk
Prevent Select All on Allocations
2024-11-13 22:25:21 +01:00
notCharles
8ed6bb4d8b pint 2024-11-13 16:22:12 -05:00
notCharles
a787af7a06 Prevent Select All
Prevent Select all on allocations, prevent people from trying to delete 30,000 ports at once ....
2024-11-13 16:21:27 -05:00
MartinOscar
d9016702d6 Merge pull request #707 from pelican-dev/charles/fixnode
Change 'exception'
2024-11-13 22:07:45 +01:00
notCharles
d565441b6a Change 'exception'
Remove the exception and just report the whole error.
2024-11-13 15:58:20 -05:00
Michael (Parker) Parker
cb522b24ef Merge pull request #706 from parkervcp/update/egg_version
use correct case for import
2024-11-09 13:59:38 -05:00
Michael (Parker) Parker
b85b17f080 use correct case for import
use lower case `v` instead of upper case `V`
2024-11-09 13:53:50 -05:00
Lance Pioch
47bd7289b1 Clear webhook cache when webhooks are deleted (#695)
* Clear webhook cache when webhooks are deleted

* fix: type casts

---------

Co-authored-by: Vehikl <go@vehikl.com>
2024-11-07 17:26:47 -05:00
Boy132
a9b76a0f51 Improve egg import error handling (#703)
* make sure read & write are successful

* show exception message in notification
2024-11-07 17:15:47 -05:00
MartinOscar
8eebb82eba Fix AutoDeploy & KeyCreationService (#701)
* Fix AutoDeploy & KeyCreationService

* Get rid of 2nd param & unset perm
2024-11-07 17:15:41 -05:00
Boy132
b3501be6ec Refactor api key permissions (#361)
* use RESOURCE_NAME for requests

* use RESOURCE_NAME for transformers

* add permissions field to api key

* add migration for new permissions field

* update tests

* remove debug log

* set column type to "json"

* remove default attribute to fix tests

* fix default value for permissions

* fix after merge

* fix after merge

* allow to "register" custom permissions

* add "role" to default resource names

* fix after merge

* fix phpstan

* fix migrations
2024-11-06 09:09:10 +01:00
Michael (Parker) Parker
ac67656d82 Merge pull request #700 from BlockyBlockling/skip-caddy-fix
Fixing Docker Environment variable only getting checked for existence instead of value
2024-11-04 11:51:05 -05:00
BlockyBlockling
968239beb3 Update entrypoint.sh
Fixed Syntax after last change
2024-11-04 13:07:57 +01:00
BlockyBlockling
7514206186 Update entrypoint.sh
Adding :- Syntax which ensures that, if SKIP_CADDY is unset, it will be treated as an empty string, which will not match "true". This avoids potential issues with unbound variables in some shell configurations where set -u (treating unset variables as an error) is enabled.

(ChatGPT)
2024-11-04 13:07:20 +01:00
BlockyBlockling
1a8321c937 Update entrypoint.sh
Fixing that its only checking for the existence of the environment variable „SKIP_CADDY“ instead of checking for its value
2024-11-04 12:43:40 +01:00
MartinOscar
340ae8099b Fix trusted proxies settings & Move ips to config & Add ipv6 (#692)
* Fix blank proxy & Move hardcoded cloudflare ips

* Add cloudflare's ipv6

* Pull from url innstead of hardcoded

* Remove Service
2024-11-01 18:16:59 -04:00
Boy132
9d02aeb130 Replace reCAPTCHA with Turnstile (#589)
* add laravel turnstile

* add config & settings for turnstile

* publish view to center captcha

* completely replace reCAPTCHA

* update FailedCaptcha event

* add back config for domain verification

* don't set language so browser lang is used
2024-11-01 18:15:04 -04:00
Charles
cf57c28c40 Update Webhooks to match other resources (#686)
* Move these

Move List/Create to their own pages to follow the flow of the other resources.

* Move EditPage aswell

* Move Save

* Labels

* Change Edit/Delete

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-11-01 18:14:20 -04:00
Boy132
382dcb3868 Fix redis connection check (#698) 2024-11-01 18:10:36 +01:00
Boy132
f793b49a81 Add egg filter to server mounts list (#697) 2024-11-01 18:10:24 +01:00
Lance Pioch
41ddae1ba0 Update ci.yaml (#643) 2024-10-31 05:39:42 -04:00
MartinOscar
e717e20996 Merge pull request #687 from RMartinOscar/fix/HealthVersion
Fix Node Health not refreshing live & Add tooltip
2024-10-30 01:58:37 +01:00
Lance Pioch
b5145b016b Update app/Models/Node.php 2024-10-29 19:53:12 -04:00
Lance Pioch
95a8f72058 Update app/Models/Node.php 2024-10-29 19:52:51 -04:00
Lance Pioch
19548338ee Update app/Models/Node.php 2024-10-29 19:52:32 -04:00
RMartinOscar
a8356fc5d2 Polishing & throw curl error 2024-10-29 20:36:44 +00:00
Boy132
7a447b04d5 Make sure roles always use web guard name (#690) 2024-10-29 18:29:25 +01:00
RMartinOscar
45699e1614 Set refresh rate 10s & Add tooltip for unreachable node 2024-10-29 15:01:30 +00:00
RMartinOscar
cde3546889 Add poll & tooltip 2024-10-29 03:28:51 +00:00
MartinOscar
3f9c1dbc3c Add prune & event blacklist (#682)
* Add prune & event blacklist

* Pinted 3times with --dirty bruh

* Add to Settings

* Fix prune & description

* Prune Logs not Configuration
2024-10-28 18:44:32 -04:00
Charles
bc2df22d78 Add unique (#685)
Usernames have to be unique, trying to make a new user with an existing username results in a 500, this fixes it.
2024-10-28 18:23:29 -04:00
Michael (Parker) Parker
1a3dc5c743 Update Egg Export Version to PLCN_V1 (#676)
* Update Egg Export Version to PLCN_V1

resolves #675

* correct version tag

* remove trailing space
2024-10-27 18:04:21 -04:00
Charles
fdd1b3798c add whereNull (#680)
Add where null to not include allocations already assigned to a server.
2024-10-27 18:01:09 -04:00
Charles
288cbee32f Fix Docker image selection (#674)
* Fix Docker image selection

Should address issue 672

Closes #672

* Fix Docker image selection in CreateServer page

---------

Co-authored-by: MartinOscar <40749467+RMartinOscar@users.noreply.github.com>
2024-10-27 11:22:12 -04:00
MartinOscar
a70a060350 Add Soft Deletes to webhooks config table (#670) 2024-10-27 00:42:08 -04:00
MartinOscar
590569a131 Remove duplicated spa in AdminPanelProvider (#668) 2024-10-26 23:25:21 -04:00
Charles
7acc8782bb Make description required. (#667) 2024-10-26 22:06:34 -04:00
MartinOscar
f3de185508 Add back auto deploy (#627)
* Add Docker, Refactor, Fix Notification

Co-authored-by: notCharles <charles@pelican.dev>

* Pint

* Required adjustments

* Remove deprecated

* Third time's the charm

---------

Co-authored-by: notCharles <charles@pelican.dev>
2024-10-26 20:43:19 -04:00
Charles
291b514e24 Webhook updates (#666) 2024-10-26 20:40:19 -04:00
Colin DeCarlo
86c369d7ce Implement Webhooks (#548)
* feat: First Webhook PoC draft

* feat: Dispatch Webhooks PoC

* fix: typo in webhook configuration scope

* Update 2024_04_21_162552_create_webhooks_table.php

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

* Update 2024_04_21_162552_create_webhooks_table.php

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

* Update 2024_04_21_162544_create_webhook_configurations_table.php

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

* Update 2024_04_21_162544_create_webhook_configurations_table.php

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

* Update DispatchWebhooks.php

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

* Update DispatchWebhooksJob.php

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

* Update DispatchWebhookForConfiguration.php

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

* Update DispatchWebhookForConfiguration.php

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

* Update DispatchWebhookForConfiguration.php

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

* Update DispatchWebhooksJob.php

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

* Update DispatchWebhooksJob.php

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

* Update DispatchWebhooksJob.php

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

* chore: Implement Webhook Event Discovery

* we got a test working for webhooks

* WIP

* Something is working!

* More tests

* clean up the tests now that they are passing

* WIP

* Don't use model specific events

* WIP

* WIP

* WIP

* WIP

* WIP

* Do it sync

* Reset these

* Don't need restored event type

* Deleted some unused jobs

* Find custom Events

* Remove observers

* Add custom event test

* Run Pint

* Add caching

* Don't cache every single event

* Fix tests

* Run Pint

* Phpstan fixes

* Pint fix

* Test fixes

* Middleware unit test fix

* Pint fixes

* Remove index not working for older dbs

* Use facade instead

---------

Co-authored-by: Pascale Beier <mail@pascalebeier.de>
Co-authored-by: Lance Pioch <lancepioch@gmail.com>
Co-authored-by: Vehikl <go@vehikl.com>
2024-10-26 20:35:25 -04:00
Quinten
5f77deb1fd Panel: Fix wings stoplogic (#407)
* Panel: FIx wings stoplogic

* do not make an expetion for `^C` let wings handle this

* remove withspaces
2024-10-26 19:21:14 -04:00
Charles
5f4429e2c3 Remove Bulk Delete from Nodes (#665)
* Remove Bulk Delete from Nodes

Removes bulk delete option from nodes.

* pint
2024-10-26 18:59:06 -04:00
Lance Pioch
1df3e8d5b0 Don't allow NodeStatisticsJob to be in the queue multiple times (#664)
* Make job unique

* Pint fix
2024-10-26 18:53:32 -04:00
Michael (Parker) Parker
ecb195b2c4 Merge pull request #662 from BlockyBlockling/docker-workflow-rework
Adding fix for forks to use a variable for Docker image reference instead of hard String
2024-10-26 18:45:59 -04:00
BlockyBlockling
86e8a6371e Update docker-publish.yml
Adding fix for forks to use a variable for Docker image reference

Source of information: https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images
2024-10-24 22:05:46 +02:00
Michael (Parker) Parker
d653edb22e Merge pull request #660 from BlockyBlockling/main
Fixing Critical error on Webserver on Pelican Panel Docker Image
2024-10-24 15:21:48 -04:00
BlockyBlockling
741252e395 Update supervisord.conf
Adding username and password dummy to get rid of critical error message
2024-10-24 21:15:03 +02:00
Michael (Parker) Parker
308601e6fe Merge pull request #659 from pelican-dev/issue/629
Make sure the .env can be accessed by the webserver when running Docker
2024-10-24 08:59:34 -04:00
Lance Pioch
3933222d98 Make sure the .env can be accessed 2024-10-23 21:36:48 -04:00
Boy132
c53ef78d89 Make sure schedules run with UTC (#657)
* make sure schedules use UTC for `next_run_at`

* use function from Utilities
2024-10-23 21:59:13 +02:00
Boy132
60792c05c2 Fix required for pinned threads input (#656) 2024-10-23 12:50:09 +02:00
Boy132
94420d06be Add UI for cpu pinning (#652)
* add ui for cpu pinning

* create "advanced" section
2024-10-22 23:34:46 +02:00
Fredrik Falk
6655ccca6e Speed up docker start (#647)
Starting the docker container is hampered due to setting `chown -R www-data:www-data /var/www/html/` on every start, causing it to traverse the entire directory which in our use case is very slow. This PR instead changes it to set permissions as part of the build process.

Sidenote: Is `LE_EMAIL` supposed to be used in addition to `ADMIN_EMAIL`?
2024-10-21 12:46:42 -04:00
Boy132
a193b4f5ab Installer: fix argument types for testConnection & return type for submit (#650)
* fix argument types for `testConnection`

* fix return type of submit
2024-10-21 18:43:16 +02:00
Boy132
3d5c8d14bd Add back trustedproxy config (#651) 2024-10-21 18:43:05 +02:00
Lance Pioch
de002324d7 Deselect all table records when switching primary allocation (#645) 2024-10-21 12:27:23 -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
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
686 changed files with 11327 additions and 25309 deletions

View File

@@ -1,32 +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
LOG_CHANNEL=daily
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
CACHE_STORE=file
QUEUE_CONNECTION=database
SESSION_DRIVER=file
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

@@ -21,10 +21,14 @@ else
echo -e "APP_KEY exists in environment, using that."
echo -e "APP_KEY=$APP_KEY" > /pelican-data/.env
fi
## enable installer
echo -e "APP_INSTALLED=false" >> /pelican-data/.env
fi
mkdir /pelican-data/database
ln -s /pelican-data/.env /var/www/html/
chown -h www-data:www-data /var/www/html/.env
ln -s /pelican-data/database/database.sqlite /var/www/html/database/
if ! grep -q "APP_KEY=" .env || grep -q "APP_KEY=$" .env; then
@@ -38,6 +42,9 @@ fi
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
@@ -45,14 +52,14 @@ crond -L /var/log/crond -l 5
export SUPERVISORD_CADDY=false
## disable caddy if SKIP_CADDY is set
if [[ -z $SKIP_CADDY ]]; then
if [[ "${SKIP_CADDY:-}" == "true" ]]; then
echo "Starting PHP-FPM only"
else
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
chown -R www-data:www-data /pelican-data/.env /pelican-data/database
echo "Starting Supervisord"
exec "$@"

View File

@@ -1,5 +1,7 @@
[unix_http_server]
file=/tmp/supervisor.sock ; path to your socket file
username=dummy
password=dummy
[supervisord]
logfile=/var/log/supervisord/supervisord.log ; supervisord log file
@@ -18,6 +20,8 @@ supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
username=dummy
password=dummy
[program:php-fpm]
command=/usr/local/sbin/php-fpm -F
@@ -25,7 +29,7 @@ autostart=true
autorestart=true
[program:queue-worker]
command=/usr/local/bin/php /var/www/html/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
command=/usr/local/bin/php /var/www/html/artisan queue:work --tries=3
user=www-data
autostart=true
autorestart=true
@@ -36,4 +40,4 @@ 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

@@ -3,10 +3,8 @@ name: Tests
on:
push:
branches:
- '**'
- main
pull_request:
branches:
- '**'
jobs:
mysql:
@@ -89,7 +87,7 @@ jobs:
fail-fast: false
matrix:
php: [8.2, 8.3]
database: ["mariadb:10.3", "mariadb:10.11", "mariadb:11.4"]
database: ["mariadb:10.6", "mariadb:10.11", "mariadb:11.4"]
services:
database:
image: ${{ matrix.database }}

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

@@ -0,0 +1,86 @@
name: Docker
on:
push:
branches:
- main
release:
types:
- published
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
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: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
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

@@ -1,4 +1,5 @@
{
admin off
email {$ADMIN_EMAIL}
}

View File

@@ -7,7 +7,9 @@ WORKDIR /build
COPY . ./
RUN yarn install --frozen-lockfile && yarn run build:production
RUN yarn config set network-timeout 300000 \
&& yarn install --frozen-lockfile \
&& yarn run build:production
FROM php:8.3-fpm-alpine
# FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
@@ -36,8 +38,8 @@ RUN touch .env
RUN composer install --no-dev --optimize-autoloader
# Set file permissions
RUN chmod -R 755 /var/www/html/storage \
&& chmod -R 755 /var/www/html/bootstrap/cache
RUN chmod -R 755 storage bootstrap/cache \
&& chown -R www-data:www-data ./
# Add scheduler to cron
RUN echo "* * * * * php /var/www/html/artisan schedule:run >> /dev/null 2>&1" | crontab -u www-data -
@@ -49,8 +51,7 @@ RUN cp .github/docker/supervisord.conf /etc/supervisord.conf && \
HEALTHCHECK --interval=5m --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost/up || exit 1
EXPOSE 80:2019
EXPOSE 443
EXPOSE 80 443
VOLUME /pelican-data

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

@@ -2,20 +2,14 @@
namespace App\Console\Commands\Environment;
use App\Traits\EnvironmentWriterTrait;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
class AppSettingsCommand extends Command
{
use EnvironmentWriterTrait;
protected $description = 'Configure basic environment settings for the Panel.';
protected $signature = 'p:environment:setup
{--url= : The URL that this Panel is running on.}';
protected array $variables = [];
protected $signature = 'p:environment:setup';
public function handle(): void
{
@@ -30,21 +24,6 @@ class AppSettingsCommand extends Command
Artisan::call('key:generate');
}
$this->variables['APP_TIMEZONE'] = 'UTC';
$this->variables['APP_URL'] = $this->option('url') ?? $this->ask(
'Application URL',
config('app.url', 'https://example.com')
);
// 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->comment('Writing variables to .env file');
$this->writeToEnvironment($this->variables);
$this->info("Setup complete. Vist {$this->variables['APP_URL']}/installer to complete the installation");
Artisan::call('filament:optimize');
}
}

View File

@@ -42,6 +42,13 @@ class DatabaseSettingsCommand extends Command
*/
public function handle(): int
{
$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;
}
$selected = config('database.default', 'sqlite');
$this->variables['DB_CONNECTION'] = $this->option('driver') ?? $this->choice(
'Database Driver',

View File

@@ -70,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'),
@@ -101,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'),
@@ -122,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'),
@@ -133,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

@@ -26,8 +26,8 @@ class InfoCommand extends Command
{
$this->output->title('Version Information');
$this->table([], [
['Panel Version', $this->versionService->versionData()['version']],
['Latest Version', $this->versionService->getPanel()],
['Panel Version', $this->versionService->currentPanelVersion()],
['Latest Version', $this->versionService->latestPanelVersion()],
['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')],
], 'compact');

View File

@@ -6,7 +6,6 @@ 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
{
@@ -24,7 +23,7 @@ class ProcessRunnableCommand extends Command
->whereRelation('server', fn (Builder $builder) => $builder->whereNull('status'))
->where('is_active', true)
->where('is_processing', false)
->where('next_run_at', '<=', Carbon::now()->toDateTimeString())
->where('next_run_at', '<=', now('UTC')->toDateTimeString())
->get();
if ($schedules->count() < 1) {
@@ -51,7 +50,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;

View File

@@ -65,6 +65,7 @@ class BulkPowerActionCommand extends Command
$bar = $this->output->createProgressBar($count);
$powerRepository = $this->powerRepository;
// @phpstan-ignore-next-line
$this->getQueryBuilder($servers, $nodes)->each(function (Server $server) use ($action, $powerRepository, &$bar) {
$bar->clear();

View File

@@ -178,7 +178,7 @@ class UpgradeCommand extends Command
$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

@@ -2,15 +2,17 @@
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 App\Models\Webhook;
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;
use App\Console\Commands\Maintenance\PruneImagesCommand;
class Kernel extends ConsoleKernel
{
@@ -35,6 +37,7 @@ class Kernel extends ConsoleKernel
$schedule->command(CleanServiceBackupFilesCommand::class)->daily();
$schedule->command(PruneImagesCommand::class)->daily();
$schedule->command(CheckEggUpdatesCommand::class)->hourly();
$schedule->job(new NodeStatistics())->everyFiveSeconds()->withoutOverlapping();
@@ -46,5 +49,9 @@ class Kernel extends ConsoleKernel
if (config('activity.prune_days')) {
$schedule->command(PruneCommand::class, ['--model' => [ActivityLog::class]])->daily();
}
if (config('panel.webhook.prune_days')) {
$schedule->command(PruneCommand::class, ['--model' => [Webhook::class]])->daily();
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace App\Enums;
use Filament\Support\Contracts\HasLabel;
enum EditorLanguages: string implements HasLabel
{
case plaintext = 'plaintext';
case abap = 'abap';
case apex = 'apex';
case azcali = 'azcali';
case bat = 'bat';
case bicep = 'bicep';
case cameligo = 'cameligo';
case coljure = 'coljure';
case coffeescript = 'coffeescript';
case c = 'c';
case cpp = 'cpp';
case csharp = 'csharp';
case csp = 'csp';
case css = 'css';
case cypher = 'cypher';
case dart = 'dart';
case dockerfile = 'dockerfile';
case ecl = 'ecl';
case elixir = 'elixir';
case flow9 = 'flow9';
case fsharp = 'fsharp';
case go = 'go';
case graphql = 'graphql';
case handlebars = 'handlebars';
case hcl = 'hcl';
case html = 'html';
case ini = 'ini';
case java = 'java';
case javascript = 'javascript';
case julia = 'julia';
case kotlin = 'kotlin';
case less = 'less';
case lexon = 'lexon';
case lua = 'lua';
case liquid = 'liquid';
case m3 = 'm3';
case markdown = 'markdown';
case mdx = 'mdx';
case mips = 'mips';
case msdax = 'msdax';
case mysql = 'mysql';
case objectivec = 'objective-c';
case pascal = 'pascal';
case pascaligo = 'pascaligo';
case perl = 'perl';
case pgsql = 'pgsql';
case php = 'php';
case pla = 'pla';
case postiats = 'postiats';
case powerquery = 'powerquery';
case powershell = 'powershell';
case proto = 'proto';
case pug = 'pug';
case python = 'python';
case qsharp = 'qsharp';
case r = 'r';
case razor = 'razor';
case redis = 'redis';
case redshift = 'redshift';
case restructuredtext = 'restructuredtext';
case ruby = 'ruby';
case rust = 'rust';
case sb = 'sb';
case scala = 'scala';
case scheme = 'scheme';
case scss = 'scss';
case shell = 'shell';
case sol = 'sol';
case aes = 'aes';
case sparql = 'sparql';
case sql = 'sql';
case st = 'st';
case swift = 'swift';
case systemverilog = 'systemverilog';
case verilog = 'verilog';
case tcl = 'tcl';
case twig = 'twig';
case typescript = 'typescript';
case typespec = 'typespec';
case vb = 'vb';
case wgsl = 'wgsl';
case xml = 'xml';
case yaml = 'yaml';
case json = 'json';
public function getLabel(): ?string
{
return $this->name;
}
}

View File

@@ -13,4 +13,5 @@ enum RolePermissionModels: string
case Role = 'role';
case Server = 'server';
case User = 'user';
case Webhook = 'webhook';
}

View File

@@ -8,9 +8,7 @@ use Illuminate\Database\Eloquent\Model;
class ActivityLogged extends Event
{
public function __construct(public ActivityLog $model)
{
}
public function __construct(public ActivityLog $model) {}
public function is(string $event): bool
{

View File

@@ -7,7 +7,5 @@ use App\Events\Event;
class DirectLogin extends Event
{
public function __construct(public User $user, public bool $remember)
{
}
public function __construct(public User $user, public bool $remember) {}
}

View File

@@ -12,7 +12,5 @@ class FailedCaptcha extends Event
/**
* Create a new event instance.
*/
public function __construct(public string $ip, public string $domain)
{
}
public function __construct(public string $ip, public ?string $message) {}
}

View File

@@ -12,7 +12,5 @@ class FailedPasswordReset extends Event
/**
* Create a new event instance.
*/
public function __construct(public string $ip, public string $email)
{
}
public function __construct(public string $ip, public string $email) {}
}

View File

@@ -7,7 +7,5 @@ use App\Events\Event;
class ProvidedAuthenticationToken extends Event
{
public function __construct(public User $user, public bool $recovery = false)
{
}
public function __construct(public User $user, public bool $recovery = false) {}
}

View File

@@ -2,6 +2,4 @@
namespace App\Events;
abstract class Event
{
}
abstract class Event {}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Created extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Creating extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Deleted extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Deleting extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -13,7 +13,5 @@ class Installed extends Event
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Saved extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Saving extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Updated extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Server;
use App\Events\Event;
use App\Models\Server;
use Illuminate\Queue\SerializesModels;
class Updating extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Server $server)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Subuser;
use App\Events\Event;
use App\Models\Subuser;
use Illuminate\Queue\SerializesModels;
class Created extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Subuser $subuser)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Subuser;
use App\Events\Event;
use App\Models\Subuser;
use Illuminate\Queue\SerializesModels;
class Creating extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Subuser $subuser)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Subuser;
use App\Events\Event;
use App\Models\Subuser;
use Illuminate\Queue\SerializesModels;
class Deleted extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Subuser $subuser)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\Subuser;
use App\Events\Event;
use App\Models\Subuser;
use Illuminate\Queue\SerializesModels;
class Deleting extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Subuser $subuser)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\User;
use App\Models\User;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class Created extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public User $user)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\User;
use App\Models\User;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class Creating extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public User $user)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\User;
use App\Models\User;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class Deleted extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public User $user)
{
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace App\Events\User;
use App\Models\User;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
class Deleting extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public User $user)
{
}
}

View File

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

View File

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

View File

@@ -4,18 +4,22 @@ 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\Container\Container;
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';
/**
@@ -46,7 +50,7 @@ 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)
public function render(Request $request): bool|RedirectResponse|JsonResponse
{
if ($request->is('livewire/update')) {
Notification::make()
@@ -55,15 +59,13 @@ class DisplayException extends PanelException implements HttpExceptionInterface
->danger()
->send();
return;
return false;
}
if ($request->expectsJson()) {
return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders());
}
app(AlertsMessageBag::class)->danger($this->getMessage())->flash();
return redirect()->back()->withInput();
}
@@ -73,10 +75,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 {
@@ -85,6 +87,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
{
@@ -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

@@ -4,6 +4,4 @@ namespace App\Exceptions\Http\Base;
use App\Exceptions\DisplayException;
class InvalidPasswordProvidedException extends DisplayException
{
}
class InvalidPasswordProvidedException extends DisplayException {}

View File

@@ -7,9 +7,6 @@ use GuzzleHttp\Exception\GuzzleException;
use App\Exceptions\DisplayException;
use Illuminate\Support\Facades\Context;
/**
* @method \GuzzleHttp\Exception\GuzzleException getPrevious()
*/
class DaemonConnectionException extends DisplayException
{
private int $statusCode = Response::HTTP_GATEWAY_TIMEOUT;

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

@@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Http\Server;
use App\Exceptions\DisplayException;
class FileTypeNotEditableException extends DisplayException
{
}

View File

@@ -12,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()) {

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

@@ -2,6 +2,4 @@
namespace App\Exceptions;
class PanelException extends \Exception
{
}
class PanelException extends \Exception {}

View File

@@ -1,9 +0,0 @@
<?php
namespace App\Exceptions\Repository\Daemon;
use App\Exceptions\Repository\RepositoryException;
class InvalidPowerSignalException extends RepositoryException
{
}

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Repository;
use App\Exceptions\DisplayException;
class DuplicateDatabaseNameException extends DisplayException
{
}
class DuplicateDatabaseNameException extends DisplayException {}

View File

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

View File

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

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Allocation;
use App\Exceptions\DisplayException;
class ServerUsingAllocationException extends DisplayException
{
}
class ServerUsingAllocationException extends DisplayException {}

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Deployment;
use App\Exceptions\DisplayException;
class NoViableAllocationException extends DisplayException
{
}
class NoViableAllocationException extends DisplayException {}

View File

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

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Egg;
use App\Exceptions\DisplayException;
class HasChildrenException extends DisplayException
{
}
class HasChildrenException extends DisplayException {}

View File

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

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Egg\Variable;
use App\Exceptions\DisplayException;
class BadValidationRuleException extends DisplayException
{
}
class BadValidationRuleException extends DisplayException {}

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Egg\Variable;
use App\Exceptions\DisplayException;
class ReservedVariableNameException extends DisplayException
{
}
class ReservedVariableNameException extends DisplayException {}

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service;
use App\Exceptions\DisplayException;
class InvalidFileUploadException extends DisplayException
{
}
class InvalidFileUploadException extends DisplayException {}

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Node;
use App\Exceptions\DisplayException;
class ConfigurationNotPersistedException extends DisplayException
{
}
class ConfigurationNotPersistedException extends DisplayException {}

View File

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

View File

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

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

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Subuser;
use App\Exceptions\DisplayException;
class ServerSubuserExistsException extends DisplayException
{
}
class ServerSubuserExistsException extends DisplayException {}

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Subuser;
use App\Exceptions\DisplayException;
class UserIsServerOwnerException extends DisplayException
{
}
class UserIsServerOwnerException extends DisplayException {}

View File

@@ -7,6 +7,7 @@ use App\Exceptions\DisplayException;
class TwoFactorAuthenticationTokenInvalid extends DisplayException
{
public string $title = 'Invalid 2FA Code';
public string $icon = 'tabler-2fa';
public function __construct()

View File

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

View File

@@ -27,14 +27,12 @@ class BackupManager
/**
* BackupManager constructor.
*/
public function __construct(protected Application $app)
{
}
public function __construct(protected Application $app) {}
/**
* 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());
}
@@ -145,7 +143,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';
/**

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
use App\Services\Activity\ActivityLogBatchService;
class LogBatch extends Facade
{
protected static function getFacadeAccessor(): string
{
return ActivityLogBatchService::class;
}
}

View File

@@ -1,8 +1,9 @@
<?php
namespace App\Filament\Pages;
namespace App\Filament\Admin\Pages;
use App\Filament\Resources\NodeResource\Pages\ListNodes;
use App\Filament\Admin\Resources\NodeResource\Pages\CreateNode;
use App\Filament\Admin\Resources\NodeResource\Pages\ListNodes;
use App\Models\Egg;
use App\Models\Node;
use App\Models\Server;
@@ -28,16 +29,20 @@ class Dashboard extends Page
public string $activeTab = 'nodes';
private SoftwareVersionService $softwareVersionService;
public function mount(SoftwareVersionService $softwareVersionService): void
{
$this->softwareVersionService = $softwareVersionService;
}
public function getViewData(): array
{
/** @var SoftwareVersionService $softwareVersionService */
$softwareVersionService = app(SoftwareVersionService::class);
return [
'inDevelopment' => config('app.version') === 'canary',
'version' => $softwareVersionService->versionData()['version'],
'latestVersion' => $softwareVersionService->getPanel(),
'isLatest' => $softwareVersionService->isLatestPanel(),
'version' => $this->softwareVersionService->currentPanelVersion(),
'latestVersion' => $this->softwareVersionService->latestPanelVersion(),
'isLatest' => $this->softwareVersionService->isLatestPanel(),
'eggsCount' => Egg::query()->count(),
'nodesList' => ListNodes::getUrl(),
'nodesCount' => Node::query()->count(),
@@ -61,13 +66,13 @@ class Dashboard extends Page
CreateAction::make()
->label(trans('dashboard/index.sections.intro-first-node.button_label'))
->icon('tabler-server-2')
->url(route('filament.admin.resources.nodes.create')),
->url(CreateNode::getUrl()),
],
'supportActions' => [
CreateAction::make()
->label(trans('dashboard/index.sections.intro-support.button_donate'))
->icon('tabler-cash')
->url($softwareVersionService->getDonations(), true)
->url('https://pelican.dev/donate', true)
->color('success'),
],
'helpActions' => [

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Filament\Pages;
namespace App\Filament\Admin\Pages;
use App\Models\Backup;
use App\Notifications\MailTested;
@@ -8,7 +8,9 @@ use App\Traits\EnvironmentWriterTrait;
use Exception;
use Filament\Actions\Action;
use Filament\Forms\Components\Actions\Action as FormAction;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TagsInput;
@@ -21,11 +23,14 @@ 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 Filament\Support\Enums\MaxWidth;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Notification as MailNotification;
use Illuminate\Support\HtmlString;
/**
* @property Form $form
@@ -33,11 +38,11 @@ use Illuminate\Support\Facades\Notification as MailNotification;
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';
@@ -66,10 +71,11 @@ class Settings extends Page implements HasForms
->label('General')
->icon('tabler-home')
->schema($this->generalSettings()),
Tab::make('recaptcha')
->label('reCAPTCHA')
Tab::make('captcha')
->label('Captcha')
->icon('tabler-shield')
->schema($this->recaptchaSettings()),
->schema($this->captchaSettings())
->columns(3),
Tab::make('mail')
->label('Mail')
->icon('tabler-mail')
@@ -92,7 +98,6 @@ class Settings extends Page implements HasForms
TextInput::make('APP_NAME')
->label('App Name')
->required()
->alphaNum()
->default(env('APP_NAME', 'Pelican')),
TextInput::make('APP_FAVICON')
->label('App Favicon')
@@ -146,7 +151,7 @@ class Settings extends Page implements HasForms
->separator()
->splitKeys(['Tab', ' '])
->placeholder('New IP or IP Range')
->default(env('TRUSTED_PROXIES', config('trustedproxy.proxies')))
->default(env('TRUSTED_PROXIES', implode(',', config('trustedproxy.proxies'))))
->hintActions([
FormAction::make('clear')
->label('Clear')
@@ -159,56 +164,76 @@ class Settings extends Page implements HasForms
->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',
])),
->action(function (Client $client, Set $set) {
$ips = collect();
try {
$response = $client->request(
'GET',
'https://api.cloudflare.com/client/v4/ips',
config('panel.guzzle')
);
if ($response->getStatusCode() === 200) {
$result = json_decode($response->getBody(), true)['result'];
foreach (['ipv4_cidrs', 'ipv6_cidrs'] as $value) {
$ips->push(...data_get($result, $value));
}
$ips->unique();
}
} catch (GuzzleException $e) {
}
$set('TRUSTED_PROXIES', $ips->values()->all());
}),
]),
Select::make('FILAMENT_WIDTH')
->label('Display Width')
->native(false)
->options(MaxWidth::class)
->default(env('FILAMENT_WIDTH', config('panel.filament.display-width'))),
];
}
private function recaptchaSettings(): array
private function captchaSettings(): array
{
return [
Toggle::make('RECAPTCHA_ENABLED')
->label('Enable reCAPTCHA?')
Toggle::make('TURNSTILE_ENABLED')
->label('Enable Turnstile Captcha?')
->inline(false)
->columnSpan(1)
->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')
->afterStateUpdated(fn ($state, Set $set) => $set('TURNSTILE_ENABLED', (bool) $state))
->default(env('TURNSTILE_ENABLED', config('turnstile.turnstile_enabled'))),
Placeholder::make('info')
->columnSpan(2)
->content(new HtmlString('<p>You can generate the keys on your <u><a href="https://developers.cloudflare.com/turnstile/get-started/#get-a-sitekey-and-secret-key" target="_blank">Cloudflare Dashboard</a></u>. A Cloudflare account is required.</p>')),
TextInput::make('TURNSTILE_SITE_KEY')
->label('Site Key')
->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')
->visible(fn (Get $get) => $get('TURNSTILE_ENABLED'))
->default(env('TURNSTILE_SITE_KEY', config('turnstile.turnstile_site_key')))
->placeholder('1x00000000000000000000AA'),
TextInput::make('TURNSTILE_SECRET_KEY')
->label('Secret Key')
->required()
->visible(fn (Get $get) => $get('RECAPTCHA_ENABLED'))
->default(env('RECAPTCHA_SECRET_KEY', config('recaptcha.secret_key'))),
->visible(fn (Get $get) => $get('TURNSTILE_ENABLED'))
->default(env('TURNSTILE_SECRET_KEY', config('turnstile.secret_key')))
->placeholder('1x0000000000000000000000000000000AA'),
Toggle::make('TURNSTILE_VERIFY_DOMAIN')
->label('Verify domain?')
->inline(false)
->onIcon('tabler-check')
->offIcon('tabler-x')
->onColor('success')
->offColor('danger')
->visible(fn (Get $get) => $get('TURNSTILE_ENABLED'))
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('TURNSTILE_VERIFY_DOMAIN', (bool) $state))
->default(env('TURNSTILE_VERIFY_DOMAIN', config('turnstile.turnstile_verify_domain'))),
];
}
@@ -522,6 +547,39 @@ class Settings extends Page implements HasForms
->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'))),
]),
Section::make('Webhook')
->description('Configure how often old webhook logs should be pruned.')
->columns()
->collapsible()
->collapsed()
->schema([
TextInput::make('APP_WEBHOOK_PRUNE_DAYS')
->label('Prune age')
->required()
->numeric()
->minValue(1)
->maxValue(365)
->suffix('Days')
->default(env('APP_WEBHOOK_PRUNE_DAYS', config('panel.webhook.prune_days'))),
]),
];
}
@@ -530,11 +588,6 @@ class Settings extends Page implements HasForms
return 'data';
}
protected function hasUnsavedDataChangesAlert(): bool
{
return true;
}
public function save(): void
{
try {
@@ -548,8 +601,6 @@ class Settings extends Page implements HasForms
Artisan::call('config:clear');
Artisan::call('queue:restart');
$this->rememberData();
$this->redirect($this->getUrl());
Notification::make()

View File

@@ -1,35 +1,32 @@
<?php
namespace App\Filament\Resources;
namespace App\Filament\Admin\Resources;
use App\Filament\Resources\ApiKeyResource\Pages;
use App\Filament\Admin\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;
return static::getModel()::where('key_type', ApiKey::TYPE_APPLICATION)->count() ?: null;
}
public static function canEdit($record): bool
public static function canEdit(Model $record): bool
{
return false;
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources\ApiKeyResource\Pages;
namespace App\Filament\Admin\Resources\ApiKeyResource\Pages;
use App\Filament\Resources\ApiKeyResource;
use App\Filament\Admin\Resources\ApiKeyResource;
use App\Models\ApiKey;
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\Hidden;
@@ -11,6 +11,7 @@ use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\ToggleButtons;
use Filament\Forms\Form;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Database\Eloquent\Model;
class CreateApiKey extends CreateRecord
{
@@ -41,7 +42,7 @@ class CreateApiKey extends CreateRecord
'md' => 2,
])
->schema(
collect(ApiKey::RESOURCES)->map(fn ($resource) => ToggleButtons::make("r_$resource")
collect(ApiKey::getPermissionList())->map(fn ($resource) => ToggleButtons::make('permissions_' . $resource)
->label(str($resource)->replace('_', ' ')->title())->inline()
->options([
0 => 'None',
@@ -87,4 +88,20 @@ class CreateApiKey extends CreateRecord
->columnSpanFull(),
]);
}
protected function handleRecordCreation(array $data): Model
{
$permissions = [];
foreach (ApiKey::getPermissionList() as $permission) {
if (isset($data['permissions_' . $permission])) {
$permissions[$permission] = intval($data['permissions_' . $permission]);
unset($data['permissions_' . $permission]);
}
}
$data['permissions'] = $permissions;
return parent::handleRecordCreation($data);
}
}

View File

@@ -1,11 +1,13 @@
<?php
namespace App\Filament\Resources\ApiKeyResource\Pages;
namespace App\Filament\Admin\Resources\ApiKeyResource\Pages;
use App\Filament\Resources\ApiKeyResource;
use App\Filament\Admin\Resources\ApiKeyResource;
use App\Models\ApiKey;
use App\Tables\Columns\DateTimeColumn;
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;
@@ -34,15 +36,13 @@ class ListApiKeys extends ListRecords
->hidden()
->searchable(),
TextColumn::make('last_used_at')
DateTimeColumn::make('last_used_at')
->label('Last Used')
->placeholder('Not Used')
->dateTime()
->sortable(),
TextColumn::make('created_at')
DateTimeColumn::make('created_at')
->label('Created')
->dateTime()
->sortable(),
TextColumn::make('user.username')
@@ -51,13 +51,23 @@ class ListApiKeys extends ListRecords
])
->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(),
Actions\CreateAction::make()
->label('Create API Key')
->hidden(fn () => ApiKey::where('key_type', ApiKey::TYPE_APPLICATION)->count() <= 0),
];
}
}

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources;
namespace App\Filament\Admin\Resources;
use App\Filament\Resources\DatabaseHostResource\Pages;
use App\Filament\Admin\Resources\DatabaseHostResource\Pages;
use App\Models\DatabaseHost;
use Filament\Resources\Resource;
@@ -10,9 +10,10 @@ class DatabaseHostResource extends Resource
{
protected static ?string $model = DatabaseHost::class;
protected static ?string $label = 'Databases';
protected static ?string $label = 'Database Host';
protected static ?string $navigationIcon = 'tabler-database';
protected static ?string $navigationGroup = 'Advanced';
public static function getNavigationBadge(): ?string
@@ -20,13 +21,6 @@ class DatabaseHostResource extends Resource
return static::getModel()::count() ?: null;
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [

View File

@@ -1,21 +1,24 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\Pages;
namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages;
use App\Filament\Resources\DatabaseHostResource;
use App\Filament\Admin\Resources\DatabaseHostResource;
use App\Services\Databases\Hosts\HostCreationService;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Pages\CreateRecord;
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 Filament\Support\Exceptions\Halt;
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';
@@ -24,6 +27,11 @@ class CreateDatabaseHost extends CreateRecord
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
@@ -70,12 +78,13 @@ class CreateDatabaseHost extends CreateRecord
->revealable()
->maxLength(255)
->required(),
Select::make('node_id')
Select::make('node_ids')
->multiple()
->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'),
->label('Linked Nodes')
->relationship('nodes', 'name'),
]),
]);
}
@@ -94,21 +103,18 @@ class CreateDatabaseHost extends CreateRecord
protected function handleRecordCreation(array $data): Model
{
return resolve(HostCreationService::class)->handle($data);
}
public function exception($e, $stopPropagation): void
{
if ($e instanceof PDOException) {
try {
return $this->service->handle($data);
} catch (PDOException $exception) {
Notification::make()
->title('Error connecting to database host')
->body($e->getMessage())
->body($exception->getMessage())
->color('danger')
->icon('tabler-database')
->danger()
->send();
$stopPropagation();
throw new Halt();
}
}
}

View File

@@ -1,19 +1,20 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\Pages;
namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages;
use App\Filament\Resources\DatabaseHostResource;
use App\Filament\Resources\DatabaseHostResource\RelationManagers\DatabasesRelationManager;
use App\Filament\Admin\Resources\DatabaseHostResource;
use App\Filament\Admin\Resources\DatabaseHostResource\RelationManagers\DatabasesRelationManager;
use App\Models\DatabaseHost;
use App\Services\Databases\Hosts\HostUpdateService;
use Filament\Actions;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Pages\EditRecord;
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 Filament\Support\Exceptions\Halt;
use Illuminate\Database\Eloquent\Model;
use PDOException;
@@ -21,6 +22,13 @@ 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
@@ -65,12 +73,13 @@ class EditDatabaseHost extends EditRecord
->password()
->revealable()
->maxLength(255),
Select::make('node_id')
Select::make('nodes')
->multiple()
->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'),
->label('Linked Nodes')
->relationship('nodes', 'name'),
]),
]);
}
@@ -92,28 +101,33 @@ class EditDatabaseHost extends EditRecord
public function getRelationManagers(): array
{
return [
DatabasesRelationManager::class,
];
if (DatabasesRelationManager::canViewForRecord($this->getRecord(), static::class)) {
return [
DatabasesRelationManager::class,
];
}
return [];
}
protected function handleRecordUpdate($record, array $data): Model
protected function handleRecordUpdate(Model $record, array $data): Model
{
return resolve(HostUpdateService::class)->handle($record->id, $data);
}
if (!$record instanceof DatabaseHost) {
return $record;
}
public function exception($e, $stopPropagation): void
{
if ($e instanceof PDOException) {
try {
return $this->hostUpdateService->handle($record, $data);
} catch (PDOException $exception) {
Notification::make()
->title('Error connecting to database host')
->body($e->getMessage())
->body($exception->getMessage())
->color('danger')
->icon('tabler-database')
->danger()
->send();
$stopPropagation();
throw new Halt();
}
}
}

View File

@@ -1,11 +1,13 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\Pages;
namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages;
use App\Filament\Resources\DatabaseHostResource;
use App\Filament\Admin\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;
@@ -30,13 +32,17 @@ class ListDatabaseHosts extends ListRecords
->sortable(),
TextColumn::make('username')
->searchable(),
TextColumn::make('max_databases')
->numeric()
->sortable(),
TextColumn::make('node.name')
->numeric()
TextColumn::make('databases_count')
->counts('databases')
->icon('tabler-database')
->label('Databases'),
TextColumn::make('nodes.name')
->icon('tabler-server-2')
->badge()
->placeholder('No Nodes')
->sortable(),
])
->checkIfRecordIsSelectableUsing(fn (DatabaseHost $databaseHost) => !$databaseHost->databases_count)
->actions([
EditAction::make(),
])
@@ -45,13 +51,23 @@ class ListDatabaseHosts extends ListRecords
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('New Database Host'),
Actions\CreateAction::make('create')
->label('Create Database Host')
->hidden(fn () => DatabaseHost::count() <= 0),
];
}
}

View File

@@ -1,13 +1,15 @@
<?php
namespace App\Filament\Resources\DatabaseHostResource\RelationManagers;
namespace App\Filament\Admin\Resources\DatabaseHostResource\RelationManagers;
use App\Models\Database;
use App\Services\Databases\DatabasePasswordService;
use App\Tables\Columns\DateTimeColumn;
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;
@@ -22,45 +24,62 @@ class DatabasesRelationManager extends RelationManager
{
return $form
->schema([
TextInput::make('database')->columnSpanFull(),
TextInput::make('database')
->columnSpanFull(),
TextInput::make('username'),
TextInput::make('password')
->password()
->revealable()
->hintAction(
Action::make('rotate')
->icon('tabler-refresh')
->requiresConfirmation()
->action(fn (DatabasePasswordService $service, Database $database, $set, $get) => $this->rotatePassword($service, $database, $set, $get))
->authorize(fn (Database $database) => auth()->user()->can('update database', $database))
)
->formatStateUsing(fn (Database $database) => $database->password),
TextInput::make('remote')->label('Connections From'),
TextInput::make('max_connections'),
TextInput::make('remote')
->label('Connections From')
->formatStateUsing(fn ($record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->remote),
TextInput::make('max_connections')
->formatStateUsing(fn ($record) => $record->max_connections === 0 ? 'Unlimited' : $record->max_connections),
TextInput::make('JDBC')
->label('JDBC Connection String')
->columnSpanFull()
->password()
->revealable()
->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('database')
->icon('tabler-database'),
TextColumn::make('username')
->icon('tabler-user'),
TextColumn::make('remote')
->formatStateUsing(fn ($record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->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(),
TextColumn::make('max_connections')
->formatStateUsing(fn ($record) => $record->max_connections === 0 ? 'Unlimited' : $record->max_connections),
DateTimeColumn::make('created_at'),
])
->actions([
DeleteAction::make(),
ViewAction::make()->color('primary'),
DeleteAction::make()
->authorize(fn (Database $database) => auth()->user()->can('delete database', $database)),
ViewAction::make()
->color('primary')
->hidden(fn () => !auth()->user()->can('viewList database')),
]);
}
protected function rotatePassword(DatabasePasswordService $service, Database $database, $set, $get): void
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');

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources;
namespace App\Filament\Admin\Resources;
use App\Filament\Resources\DatabaseResource\Pages;
use App\Filament\Admin\Resources\DatabaseResource\Pages;
use App\Models\Database;
use Filament\Resources\Resource;
@@ -13,6 +13,7 @@ class DatabaseResource extends Resource
protected static ?string $navigationIcon = 'tabler-database';
protected static bool $shouldRegisterNavigation = false;
protected static ?string $navigationGroup = 'Advanced';
public static function getNavigationBadge(): ?string
@@ -20,13 +21,6 @@ class DatabaseResource extends Resource
return static::getModel()::count() ?: null;
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources\DatabaseResource\Pages;
namespace App\Filament\Admin\Resources\DatabaseResource\Pages;
use App\Filament\Resources\DatabaseResource;
use App\Filament\Admin\Resources\DatabaseResource;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
@@ -21,9 +21,12 @@ class CreateDatabase extends CreateRecord
->searchable()
->preload()
->required(),
TextInput::make('database_host_id')
->required()
->numeric(),
Select::make('database_host_id')
->relationship('host', 'name')
->searchable()
->selectablePlaceholder(false)
->preload()
->required(),
TextInput::make('database')
->required()
->maxLength(255),

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources\DatabaseResource\Pages;
namespace App\Filament\Admin\Resources\DatabaseResource\Pages;
use App\Filament\Resources\DatabaseResource;
use App\Filament\Admin\Resources\DatabaseResource;
use Filament\Actions;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;

View File

@@ -1,8 +1,9 @@
<?php
namespace App\Filament\Resources\DatabaseResource\Pages;
namespace App\Filament\Admin\Resources\DatabaseResource\Pages;
use App\Filament\Resources\DatabaseResource;
use App\Filament\Admin\Resources\DatabaseResource;
use App\Tables\Columns\DateTimeColumn;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Actions\BulkActionGroup;
@@ -34,12 +35,10 @@ class ListDatabases extends ListRecords
TextColumn::make('max_connections')
->numeric()
->sortable(),
TextColumn::make('created_at')
->dateTime()
DateTimeColumn::make('created_at')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
DateTimeColumn::make('updated_at')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources;
namespace App\Filament\Admin\Resources;
use App\Filament\Resources\EggResource\Pages;
use App\Filament\Admin\Resources\EggResource\Pages;
use App\Models\Egg;
use Filament\Resources\Resource;
@@ -21,13 +21,6 @@ class EggResource extends Resource
return static::getModel()::count() ?: null;
}
public static function getRelations(): array
{
return [
//
];
}
public static function getGloballySearchableAttributes(): array
{
return ['name', 'tags', 'uuid', 'id'];

View File

@@ -1,8 +1,9 @@
<?php
namespace App\Filament\Resources\EggResource\Pages;
namespace App\Filament\Admin\Resources\EggResource\Pages;
use App\Filament\Resources\EggResource;
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
use App\Filament\Admin\Resources\EggResource;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\Hidden;
@@ -15,10 +16,9 @@ use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Pages\CreateRecord;
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Forms\Set;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
@@ -27,6 +27,7 @@ class CreateEgg extends CreateRecord
protected static string $resource = EggResource::class;
protected static bool $canCreateAnother = false;
public function form(Form $form): Form
{
return $form
@@ -155,7 +156,7 @@ class CreateEgg extends CreateRecord
->debounce(750)
->maxLength(255)
->columnSpanFull()
->afterStateUpdated(fn (Forms\Set $set, $state) => $set('env_variable', str($state)->trim()->snake()->upper()->toString())
->afterStateUpdated(fn (Set $set, $state) => $set('env_variable', str($state)->trim()->snake()->upper()->toString())
)
->required(),
Textarea::make('description')->columnSpanFull(),

View File

@@ -1,16 +1,15 @@
<?php
namespace App\Filament\Resources\EggResource\Pages;
namespace App\Filament\Admin\Resources\EggResource\Pages;
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
use App\Filament\Resources\EggResource;
use App\Filament\Resources\EggResource\RelationManagers\ServersRelationManager;
use App\Filament\Admin\Resources\EggResource;
use App\Filament\Admin\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;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\FileUpload;
@@ -26,6 +25,7 @@ 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;
@@ -40,6 +40,7 @@ class EditEgg extends EditRecord
Tabs::make()->tabs([
Tab::make('Configuration')
->columns(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 4])
->icon('tabler-egg')
->schema([
TextInput::make('name')
->required()
@@ -80,6 +81,7 @@ class EditEgg extends EditRecord
->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 primary allocation IP.
Required for certain games to work properly when the Node has multiple public IP addresses.
@@ -105,9 +107,9 @@ class EditEgg extends EditRecord
->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')
@@ -130,6 +132,7 @@ class EditEgg extends EditRecord
]),
Tab::make('Egg Variables')
->columnSpanFull()
->icon('tabler-variable')
->schema([
Repeater::make('variables')
->label('')
@@ -165,7 +168,7 @@ class EditEgg extends EditRecord
->debounce(750)
->maxLength(255)
->columnSpanFull()
->afterStateUpdated(fn (Forms\Set $set, $state) => $set('env_variable', str($state)->trim()->snake()->upper()->toString())
->afterStateUpdated(fn (Set $set, $state) => $set('env_variable', str($state)->trim()->snake()->upper()->toString())
)
->required(),
Textarea::make('description')->columnSpanFull(),
@@ -211,30 +214,27 @@ class EditEgg extends EditRecord
]),
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')
->placeholderText('')
->columnSpanFull()
->fontSize('16px')
->language('shell')
->view('filament.plugins.monaco-editor'),
]),
])->columnSpanFull()->persistTabInQueryString(),
]);
}
@@ -281,10 +281,7 @@ class EditEgg extends EditRecord
->contained(false),
])
->action(function (array $data, Egg $egg): void {
/** @var EggImporterService $eggImportService */
$eggImportService = resolve(EggImporterService::class);
->action(function (array $data, Egg $egg, EggImporterService $eggImportService): void {
if (!empty($data['egg'])) {
try {
$eggImportService->fromFile($data['egg'], $egg);

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources\EggResource\Pages;
namespace App\Filament\Admin\Resources\EggResource\Pages;
use App\Filament\Resources\EggResource;
use App\Filament\Admin\Resources\EggResource;
use App\Models\Egg;
use App\Services\Eggs\Sharing\EggExporterService;
use App\Services\Eggs\Sharing\EggImporterService;
@@ -14,7 +14,7 @@ use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;
use Filament\Tables;
use Filament\Tables\Actions\Action;
use Filament\Tables\Actions\BulkActionGroup;
use Filament\Tables\Actions\DeleteBulkAction;
use Filament\Tables\Actions\EditAction;
@@ -49,7 +49,7 @@ class ListEggs extends ListRecords
])
->actions([
EditAction::make(),
Tables\Actions\Action::make('export')
Action::make('export')
->icon('tabler-download')
->label('Export')
->color('primary')
@@ -57,6 +57,39 @@ class ListEggs extends ListRecords
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([
@@ -65,6 +98,7 @@ class ListEggs extends ListRecords
]),
]);
}
protected function getHeaderActions(): array
{
return [
@@ -97,10 +131,7 @@ class ListEggs extends ListRecords
->contained(false),
])
->action(function (array $data): void {
/** @var EggImporterService $eggImportService */
$eggImportService = resolve(EggImporterService::class);
->action(function (array $data, EggImporterService $eggImportService): void {
if (!empty($data['egg'])) {
/** @var TemporaryUploadedFile[] $eggFile */
$eggFile = $data['egg'];
@@ -111,6 +142,7 @@ class ListEggs extends ListRecords
} catch (Exception $exception) {
Notification::make()
->title('Import Failed')
->body($exception->getMessage())
->danger()
->send();
@@ -127,6 +159,7 @@ class ListEggs extends ListRecords
} catch (Exception $exception) {
Notification::make()
->title('Import Failed')
->body($exception->getMessage())
->danger()
->send();

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Filament\Resources\EggResource\RelationManagers;
namespace App\Filament\Admin\Resources\EggResource\RelationManagers;
use App\Models\Server;
use Filament\Resources\RelationManagers\RelationManager;
@@ -16,7 +16,7 @@ class ServersRelationManager extends RelationManager
{
return $table
->recordTitleAttribute('servers')
->emptyStateDescription('No Servers')->emptyStateHeading('No servers are assigned this egg.')
->emptyStateDescription('No Servers')->emptyStateHeading('No servers are assigned to this Egg.')
->searchable(false)
->columns([
TextColumn::make('user.username')

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources;
namespace App\Filament\Admin\Resources;
use App\Filament\Resources\MountResource\Pages;
use App\Filament\Admin\Resources\MountResource\Pages;
use App\Models\Mount;
use Filament\Resources\Resource;
@@ -11,6 +11,7 @@ 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
@@ -18,13 +19,6 @@ class MountResource extends Resource
return static::getModel()::count() ?: null;
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources\MountResource\Pages;
namespace App\Filament\Admin\Resources\MountResource\Pages;
use App\Filament\Resources\MountResource;
use App\Filament\Admin\Resources\MountResource;
use Filament\Forms\Components\Group;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\Section;

View File

@@ -1,17 +1,17 @@
<?php
namespace App\Filament\Resources\MountResource\Pages;
namespace App\Filament\Admin\Resources\MountResource\Pages;
use App\Filament\Resources\MountResource;
use App\Filament\Admin\Resources\MountResource;
use Filament\Actions;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\ToggleButtons;
use Filament\Resources\Pages\EditRecord;
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
{
@@ -96,6 +96,7 @@ class EditMount extends EditRecord
'lg' => 2,
]);
}
protected function getHeaderActions(): array
{
return [

View File

@@ -1,8 +1,8 @@
<?php
namespace App\Filament\Resources\MountResource\Pages;
namespace App\Filament\Admin\Resources\MountResource\Pages;
use App\Filament\Resources\MountResource;
use App\Filament\Admin\Resources\MountResource;
use App\Models\Mount;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
@@ -17,6 +17,7 @@ use Filament\Tables\Table;
class ListMounts extends ListRecords
{
protected static string $resource = MountResource::class;
public function table(Table $table): Table
{
return $table
@@ -56,6 +57,7 @@ class ListMounts extends ListRecords
->button(),
]);
}
protected function getHeaderActions(): array
{
return [

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