mirror of
https://github.com/pelican-dev/panel.git
synced 2026-02-24 11:20:41 +03:00
Compare commits
317 Commits
issue/68
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e85180b3d | ||
|
|
9f4a3b1c0d | ||
|
|
45db06a1bd | ||
|
|
3e26a1cf09 | ||
|
|
44111696df | ||
|
|
e04abcbcf9 | ||
|
|
ea5914f362 | ||
|
|
98c36c4cc3 | ||
|
|
6bc55b1039 | ||
|
|
11b153d23c | ||
|
|
998ad2ee31 | ||
|
|
7f0c7da37f | ||
|
|
e93d122a27 | ||
|
|
9aaf6b3798 | ||
|
|
fd6e7eb314 | ||
|
|
4e694b50ca | ||
|
|
3a24edfe1d | ||
|
|
0179ade557 | ||
|
|
05d74232af | ||
|
|
a2b2e373be | ||
|
|
0a17e78f33 | ||
|
|
c3a65aed07 | ||
|
|
d438e29154 | ||
|
|
1fdc428f3e | ||
|
|
a9e4495c91 | ||
|
|
98ddb65509 | ||
|
|
6caa741798 | ||
|
|
5512c10ee1 | ||
|
|
5331c5abfa | ||
|
|
36a38ab947 | ||
|
|
da195fd2fe | ||
|
|
82409f2fba | ||
|
|
839be53231 | ||
|
|
d79d461e7c | ||
|
|
d8e8240756 | ||
|
|
0b84b0c08c | ||
|
|
e2045e334f | ||
|
|
5e2d106bb9 | ||
|
|
40c138f086 | ||
|
|
ab543a399b | ||
|
|
0308045738 | ||
|
|
cd9cbf20ce | ||
|
|
e1308cb04d | ||
|
|
2d937229fb | ||
|
|
3d764a89f7 | ||
|
|
2f56ca5ed5 | ||
|
|
fe8e6fcfda | ||
|
|
1e7a901371 | ||
|
|
d53820bbdc | ||
|
|
d03366cf3d | ||
|
|
7d68da41f4 | ||
|
|
599d53b4f2 | ||
|
|
f0f04fd86a | ||
|
|
324fc4b7d5 | ||
|
|
5be4e22a0c | ||
|
|
75aae3e45b | ||
|
|
c1704eef3b | ||
|
|
09abec6ee6 | ||
|
|
206cc76a8b | ||
|
|
b355830db4 | ||
|
|
09375df8a7 | ||
|
|
96ec2eb3c2 | ||
|
|
b464bb4d25 | ||
|
|
c561035c75 | ||
|
|
48d1ef5d26 | ||
|
|
1f6b659546 | ||
|
|
8f47ccfbf7 | ||
|
|
35d25d216e | ||
|
|
a6963ad802 | ||
|
|
d48cf6b722 | ||
|
|
cba4cf11aa | ||
|
|
96c09acc52 | ||
|
|
7f697017a7 | ||
|
|
f8ad720f52 | ||
|
|
513117cc42 | ||
|
|
5797b790fd | ||
|
|
9ec2f6eae1 | ||
|
|
77bf70b063 | ||
|
|
b8c1b68328 | ||
|
|
431c1977e3 | ||
|
|
f8ad9a1805 | ||
|
|
635cc6a029 | ||
|
|
20125dbc6f | ||
|
|
d5b8a4c501 | ||
|
|
dde5305b3f | ||
|
|
e352754e6f | ||
|
|
7cde90a39a | ||
|
|
3202a59b07 | ||
|
|
71f3abe464 | ||
|
|
401026efa1 | ||
|
|
654143addc | ||
|
|
37f9725f27 | ||
|
|
98c915490d | ||
|
|
6fb54e32f1 | ||
|
|
fef19b9fdd | ||
|
|
6a4963200c | ||
|
|
37ba62410f | ||
|
|
262e2fd09a | ||
|
|
9e8b9cd599 | ||
|
|
3411e5e65c | ||
|
|
7e6769c96e | ||
|
|
03eaddb126 | ||
|
|
61bdf0dcd7 | ||
|
|
cbacc18e56 | ||
|
|
ad1a9cd33f | ||
|
|
02c4eb19f0 | ||
|
|
3a25d0f976 | ||
|
|
634b8dec55 | ||
|
|
43d0b78742 | ||
|
|
6b77e69e43 | ||
|
|
efbf4df2a2 | ||
|
|
4ec9171017 | ||
|
|
885e03ee06 | ||
|
|
7c6b3a03db | ||
|
|
fe43539ea7 | ||
|
|
e145fcdc56 | ||
|
|
8078f2ca4e | ||
|
|
d1007ad2fe | ||
|
|
7f3b1fd758 | ||
|
|
d088e79e5e | ||
|
|
9cfd87090f | ||
|
|
a7a7c5ba4d | ||
|
|
b14e8fd724 | ||
|
|
c93a836ad8 | ||
|
|
6fcf4173d3 | ||
|
|
7449b82f41 | ||
|
|
af4ac1db92 | ||
|
|
6707d1ccf6 | ||
|
|
b197e73173 | ||
|
|
e5418491c8 | ||
|
|
98ebc75965 | ||
|
|
121ebe6017 | ||
|
|
fc27b24783 | ||
|
|
8049ef462e | ||
|
|
17bb23b5b8 | ||
|
|
8926f9712f | ||
|
|
e4849d89d7 | ||
|
|
af11888b82 | ||
|
|
1845f2955f | ||
|
|
a2b315ba74 | ||
|
|
76c3632d14 | ||
|
|
4facaecea0 | ||
|
|
a55a2cce6e | ||
|
|
448fe41e78 | ||
|
|
7f37b3b099 | ||
|
|
ef54d52866 | ||
|
|
7bd66c3d85 | ||
|
|
74efc6e8c1 | ||
|
|
a7b767ae78 | ||
|
|
a3ecf3994b | ||
|
|
158fa24fff | ||
|
|
e5069e754d | ||
|
|
cdd46de274 | ||
|
|
ff5812e87b | ||
|
|
20ce0ca8e6 | ||
|
|
66ec86694f | ||
|
|
295134fb6c | ||
|
|
ae445840f7 | ||
|
|
77fd54fdc2 | ||
|
|
18fe4f1123 | ||
|
|
2525af8f02 | ||
|
|
7cc4358a04 | ||
|
|
168d37b996 | ||
|
|
df615f6915 | ||
|
|
17805f676e | ||
|
|
23d515c3e5 | ||
|
|
7a5dd87385 | ||
|
|
8f51502c6d | ||
|
|
9d48799c28 | ||
|
|
133c1a511f | ||
|
|
3a7ddfca5e | ||
|
|
00ae3b8b61 | ||
|
|
b5733715a6 | ||
|
|
9a859cdec3 | ||
|
|
1571e3cb24 | ||
|
|
a8680c7aed | ||
|
|
66a17879a0 | ||
|
|
f684da997c | ||
|
|
00644c2c60 | ||
|
|
02a0c5c3eb | ||
|
|
993e2c4244 | ||
|
|
7a4c4ce02a | ||
|
|
914f3dcdbd | ||
|
|
d43b99792f | ||
|
|
771eece01e | ||
|
|
026494c353 | ||
|
|
663b097d22 | ||
|
|
d09227659e | ||
|
|
eb819032bc | ||
|
|
5af507b54b | ||
|
|
bbee45592f | ||
|
|
640ff9f5b3 | ||
|
|
d6f814b7a3 | ||
|
|
8a122fa99c | ||
|
|
3ffb54503f | ||
|
|
53460b8d1b | ||
|
|
0051d9fefc | ||
|
|
ef1ae72d06 | ||
|
|
3dfdc70790 | ||
|
|
8460c52534 | ||
|
|
2bfc788e13 | ||
|
|
839ff96271 | ||
|
|
5d2b892eab | ||
|
|
c953b97009 | ||
|
|
9716b1e64d | ||
|
|
8358e410dc | ||
|
|
f6c586bf5b | ||
|
|
feadaa2caf | ||
|
|
23246eb134 | ||
|
|
6921c8b350 | ||
|
|
8cc91b0747 | ||
|
|
157fa45234 | ||
|
|
fd5016809a | ||
|
|
a0f5ef13d6 | ||
|
|
67f1e91236 | ||
|
|
cc3a7a2d0d | ||
|
|
d908fb9a9d | ||
|
|
6b96c9dbda | ||
|
|
e27f23b1b6 | ||
|
|
4ad2997566 | ||
|
|
7e7f0be7df | ||
|
|
5b3ae995e6 | ||
|
|
2a34795ab1 | ||
|
|
d3da1b0a58 | ||
|
|
5317f97870 | ||
|
|
b50acfdba2 | ||
|
|
066bdbdf78 | ||
|
|
8103ba6338 | ||
|
|
44b879215f | ||
|
|
d2a7d7708c | ||
|
|
efc37dd45a | ||
|
|
09eac71f05 | ||
|
|
6d42a15ec3 | ||
|
|
bbfdee356b | ||
|
|
994852ca00 | ||
|
|
141baeb035 | ||
|
|
bd51191da6 | ||
|
|
1337767049 | ||
|
|
918ba02075 | ||
|
|
c6977e57c8 | ||
|
|
6d1c153d09 | ||
|
|
e5433b7aab | ||
|
|
355810c549 | ||
|
|
4fd1937c54 | ||
|
|
fea1c51337 | ||
|
|
e0c6137b92 | ||
|
|
cd448cd9a7 | ||
|
|
b208835ed4 | ||
|
|
951fc73363 | ||
|
|
ad9447e974 | ||
|
|
d2d960ecf3 | ||
|
|
d555c42644 | ||
|
|
f33f91698e | ||
|
|
90afae79db | ||
|
|
54039e25a4 | ||
|
|
408897cfcf | ||
|
|
24eb52f7d6 | ||
|
|
d87d3760a1 | ||
|
|
fe4668a517 | ||
|
|
6125b07afa | ||
|
|
9717aa4b5f | ||
|
|
9491322d8c | ||
|
|
8ed6bb4d8b | ||
|
|
a787af7a06 | ||
|
|
d9016702d6 | ||
|
|
d565441b6a | ||
|
|
cb522b24ef | ||
|
|
b85b17f080 | ||
|
|
47bd7289b1 | ||
|
|
a9b76a0f51 | ||
|
|
8eebb82eba | ||
|
|
b3501be6ec | ||
|
|
ac67656d82 | ||
|
|
968239beb3 | ||
|
|
7514206186 | ||
|
|
1a8321c937 | ||
|
|
340ae8099b | ||
|
|
9d02aeb130 | ||
|
|
cf57c28c40 | ||
|
|
382dcb3868 | ||
|
|
f793b49a81 | ||
|
|
41ddae1ba0 | ||
|
|
e717e20996 | ||
|
|
b5145b016b | ||
|
|
95a8f72058 | ||
|
|
19548338ee | ||
|
|
a8356fc5d2 | ||
|
|
7a447b04d5 | ||
|
|
45699e1614 | ||
|
|
cde3546889 | ||
|
|
3f9c1dbc3c | ||
|
|
bc2df22d78 | ||
|
|
1a3dc5c743 | ||
|
|
fdd1b3798c | ||
|
|
288cbee32f | ||
|
|
a70a060350 | ||
|
|
590569a131 | ||
|
|
7acc8782bb | ||
|
|
f3de185508 | ||
|
|
291b514e24 | ||
|
|
86c369d7ce | ||
|
|
5f77deb1fd | ||
|
|
5f4429e2c3 | ||
|
|
1df3e8d5b0 | ||
|
|
ecb195b2c4 | ||
|
|
86e8a6371e | ||
|
|
d653edb22e | ||
|
|
741252e395 | ||
|
|
308601e6fe | ||
|
|
3933222d98 | ||
|
|
c53ef78d89 | ||
|
|
60792c05c2 | ||
|
|
94420d06be | ||
|
|
6655ccca6e | ||
|
|
a193b4f5ab | ||
|
|
3d5c8d14bd | ||
|
|
de002324d7 |
@@ -1,10 +1,29 @@
|
||||
**.DS_Store
|
||||
.env
|
||||
.devcontainer
|
||||
.dockerignore
|
||||
.editorconfig
|
||||
.git
|
||||
node_modules
|
||||
vendor
|
||||
.github
|
||||
**.gitignore
|
||||
.php-cs-fixer.dist.php
|
||||
.prettierrc.json
|
||||
.vscode
|
||||
Dockerfile
|
||||
bounties.md
|
||||
compose.yml
|
||||
contributing.md
|
||||
contributor_license_agreement.md
|
||||
database/database.sqlite
|
||||
docker/README.md
|
||||
node_modules
|
||||
phpstan.neon
|
||||
phpunit.xml
|
||||
readme.md
|
||||
storage/debugbar/*.json
|
||||
storage/logs/*.log
|
||||
storage/framework/cache/data/*
|
||||
storage/framework/sessions/*
|
||||
storage/framework/testing
|
||||
storage/framework/views/*.php
|
||||
storage/logs/*.log
|
||||
vendor
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
public
|
||||
node_modules
|
||||
resources/views
|
||||
babel.config.js
|
||||
tailwind.config.js
|
||||
webpack.config.js
|
||||
52
.eslintrc.js
52
.eslintrc.js
@@ -1,52 +0,0 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: './',
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
pragma: 'React',
|
||||
version: 'detect',
|
||||
},
|
||||
linkComponents: [
|
||||
{ name: 'Link', linkAttribute: 'to' },
|
||||
{ name: 'NavLink', linkAttribute: 'to' },
|
||||
],
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
},
|
||||
plugins: ['react', 'react-hooks', 'prettier', '@typescript-eslint'],
|
||||
extends: [
|
||||
// 'standard',
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:jest-dom/recommended',
|
||||
],
|
||||
rules: {
|
||||
eqeqeq: 'error',
|
||||
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
|
||||
// TypeScript can infer this significantly better than eslint ever can.
|
||||
'react/prop-types': 0,
|
||||
'react/display-name': 0,
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
// 'react/no-unknown-property': ['error', { ignore: ['css'] }],
|
||||
// This setup is required to avoid a spam of errors when running eslint about React being
|
||||
// used before it is defined.
|
||||
//
|
||||
// @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-use-before-define.md#how-to-use
|
||||
'no-use-before-define': 0,
|
||||
'@typescript-eslint/no-use-before-define': 'warn',
|
||||
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }],
|
||||
},
|
||||
};
|
||||
15
.github/CODEOWNERS
vendored
Normal file
15
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# More details are here: https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# The '*' pattern is global owners.
|
||||
|
||||
# Order is important. The last matching pattern has the most precedence.
|
||||
# The folders are ordered as follows:
|
||||
|
||||
# In each subsection folders are ordered first by depth, then alphabetically.
|
||||
# This should make it easy to add new rules without breaking existing ones.
|
||||
|
||||
# Global
|
||||
* @pelican-dev/panel
|
||||
75
.github/docker/default.conf
vendored
75
.github/docker/default.conf
vendored
@@ -1,75 +0,0 @@
|
||||
# If using Ubuntu this file should be placed in:
|
||||
# /etc/nginx/sites-available/
|
||||
#
|
||||
# If using CentOS this file should be placed in:
|
||||
# /etc/nginx/conf.d/
|
||||
#
|
||||
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Pterodactyl®
|
||||
# Copyright © Dane Everitt <dane@daneeveritt.com> and contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /app/public;
|
||||
index index.html index.htm index.php;
|
||||
charset utf-8;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location = /favicon.ico { access_log off; log_not_found off; }
|
||||
location = /robots.txt { access_log off; log_not_found off; }
|
||||
|
||||
access_log off;
|
||||
error_log /var/log/nginx/panel.app-error.log error;
|
||||
|
||||
# allow larger file uploads and longer script runtimes
|
||||
client_max_body_size 100m;
|
||||
client_body_timeout 120s;
|
||||
|
||||
sendfile off;
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
# the fastcgi_pass path needs to be changed accordingly when using CentOS
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M";
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param HTTP_PROXY "";
|
||||
fastcgi_intercept_errors off;
|
||||
fastcgi_buffer_size 16k;
|
||||
fastcgi_buffers 4 16k;
|
||||
fastcgi_connect_timeout 300;
|
||||
fastcgi_send_timeout 300;
|
||||
fastcgi_read_timeout 300;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
70
.github/docker/default_ssl.conf
vendored
70
.github/docker/default_ssl.conf
vendored
@@ -1,70 +0,0 @@
|
||||
# If using Ubuntu this file should be placed in:
|
||||
# /etc/nginx/sites-available/
|
||||
#
|
||||
server {
|
||||
listen 80;
|
||||
server_name <domain>;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name <domain>;
|
||||
|
||||
root /app/public;
|
||||
index index.php;
|
||||
|
||||
access_log /var/log/nginx/panel.app-access.log;
|
||||
error_log /var/log/nginx/panel.app-error.log error;
|
||||
|
||||
# allow larger file uploads and longer script runtimes
|
||||
client_max_body_size 100m;
|
||||
client_body_timeout 120s;
|
||||
|
||||
sendfile off;
|
||||
|
||||
# strengthen ssl security
|
||||
ssl_certificate /etc/letsencrypt/live/<domain>/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/<domain>/privkey.pem;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
|
||||
|
||||
# See the link below for more SSL information:
|
||||
# https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
|
||||
#
|
||||
# ssl_dhparam /etc/ssl/certs/dhparam.pem;
|
||||
|
||||
# Add headers to serve security related headers
|
||||
add_header Strict-Transport-Security "max-age=15768000; preload;";
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Robots-Tag none;
|
||||
add_header Content-Security-Policy "frame-ancestors 'self'";
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M";
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param HTTP_PROXY "";
|
||||
fastcgi_intercept_errors off;
|
||||
fastcgi_buffer_size 16k;
|
||||
fastcgi_buffers 4 16k;
|
||||
fastcgi_connect_timeout 300;
|
||||
fastcgi_send_timeout 300;
|
||||
fastcgi_read_timeout 300;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
16
.github/docker/www.conf
vendored
16
.github/docker/www.conf
vendored
@@ -1,16 +0,0 @@
|
||||
[www]
|
||||
|
||||
user = nginx
|
||||
group = nginx
|
||||
|
||||
listen = 127.0.0.1:9000
|
||||
listen.owner = nginx
|
||||
listen.group = nginx
|
||||
listen.mode = 0750
|
||||
|
||||
pm = ondemand
|
||||
pm.max_children = 9
|
||||
pm.process_idle_timeout = 10s
|
||||
pm.max_requests = 200
|
||||
|
||||
clear_env = no
|
||||
19
.github/workflows/build.yaml
vendored
19
.github/workflows/build.yaml
vendored
@@ -3,10 +3,8 @@ name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
ui:
|
||||
@@ -20,14 +18,25 @@ jobs:
|
||||
- name: Code Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install PHP dependencies
|
||||
run: composer install --no-interaction --no-suggest --no-progress --no-autoloader --no-scripts --no-dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: yarn build:production
|
||||
run: yarn build
|
||||
|
||||
26
.github/workflows/ci.yaml
vendored
26
.github/workflows/ci.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: [8.2, 8.3]
|
||||
php: [8.2, 8.3, 8.4]
|
||||
database: ["mysql:8"]
|
||||
services:
|
||||
database:
|
||||
@@ -66,16 +66,16 @@ jobs:
|
||||
coverage: none
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --no-suggest --prefer-dist
|
||||
run: composer install --no-interaction --no-suggest --no-progress --no-scripts
|
||||
|
||||
- name: Unit tests
|
||||
run: vendor/bin/phpunit tests/Unit
|
||||
run: vendor/bin/pest tests/Unit
|
||||
env:
|
||||
DB_HOST: UNIT_NO_DB
|
||||
SKIP_MIGRATIONS: true
|
||||
|
||||
- name: Integration tests
|
||||
run: vendor/bin/phpunit tests/Integration
|
||||
run: vendor/bin/pest tests/Integration
|
||||
env:
|
||||
DB_PORT: ${{ job.services.database.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
@@ -86,8 +86,8 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: [8.2, 8.3]
|
||||
database: ["mariadb:10.3", "mariadb:10.11", "mariadb:11.4"]
|
||||
php: [8.2, 8.3, 8.4]
|
||||
database: ["mariadb:10.6", "mariadb:10.11", "mariadb:11.4"]
|
||||
services:
|
||||
database:
|
||||
image: ${{ matrix.database }}
|
||||
@@ -139,16 +139,16 @@ jobs:
|
||||
coverage: none
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --no-suggest --prefer-dist
|
||||
run: composer install --no-interaction --no-suggest --no-progress --no-scripts
|
||||
|
||||
- name: Unit tests
|
||||
run: vendor/bin/phpunit tests/Unit
|
||||
run: vendor/bin/pest tests/Unit
|
||||
env:
|
||||
DB_HOST: UNIT_NO_DB
|
||||
SKIP_MIGRATIONS: true
|
||||
|
||||
- name: Integration tests
|
||||
run: vendor/bin/phpunit tests/Integration
|
||||
run: vendor/bin/pest tests/Integration
|
||||
env:
|
||||
DB_PORT: ${{ job.services.database.ports[3306] }}
|
||||
DB_USERNAME: root
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: [8.2, 8.3]
|
||||
php: [8.2, 8.3, 8.4]
|
||||
env:
|
||||
APP_ENV: testing
|
||||
APP_DEBUG: "false"
|
||||
@@ -200,16 +200,16 @@ jobs:
|
||||
coverage: none
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --no-suggest --prefer-dist
|
||||
run: composer install --no-interaction --no-suggest --no-progress --no-scripts
|
||||
|
||||
- name: Create SQLite file
|
||||
run: touch database/testing.sqlite
|
||||
|
||||
- name: Unit tests
|
||||
run: vendor/bin/phpunit tests/Unit
|
||||
run: vendor/bin/pest tests/Unit
|
||||
env:
|
||||
DB_HOST: UNIT_NO_DB
|
||||
SKIP_MIGRATIONS: true
|
||||
|
||||
- name: Integration tests
|
||||
run: vendor/bin/phpunit tests/Integration
|
||||
run: vendor/bin/pest tests/Integration
|
||||
|
||||
113
.github/workflows/docker-publish.yml
vendored
113
.github/workflows/docker-publish.yml
vendored
@@ -1,6 +1,5 @@
|
||||
name: Docker
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -9,24 +8,83 @@ on:
|
||||
types:
|
||||
- published
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build and Push
|
||||
runs-on: ubuntu-latest
|
||||
build-php-base:
|
||||
name: Build PHP base image on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
arch: amd64
|
||||
platform: linux/amd64
|
||||
- os: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
platform: linux/arm64
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build the base PHP image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.base
|
||||
push: false
|
||||
load: true
|
||||
platforms: ${{ matrix.platform }}
|
||||
tags: base-php:${{ matrix.arch }}
|
||||
cache-from: type=gha,scope=base-php${{ matrix.arch }}
|
||||
cache-to: type=gha,scope=base-php${{ matrix.arch }}
|
||||
|
||||
- name: Export image to file
|
||||
run: docker save -o base-php-${{ matrix.arch }}.tar base-php:${{ matrix.arch }}
|
||||
|
||||
- name: Push the docker build to the artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: base-php-${{ matrix.arch }}.tar
|
||||
path: base-php-${{ matrix.arch }}.tar
|
||||
retention-days: 7
|
||||
|
||||
|
||||
build-and-push:
|
||||
name: Build and Push ubuntu-24.04
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build-php-base
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# Start a temp local registry because workflow can not pull from localy loaded images
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
# Always run against a tag, even if the commit into the tag has [docker skip] within the commit message.
|
||||
if: "!contains(github.ref, 'main') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))"
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Docker metadata
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/pelican-dev/panel
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
@@ -34,11 +92,14 @@ jobs:
|
||||
type=ref,event=tag
|
||||
type=ref,event=branch
|
||||
|
||||
- name: Setup QEMU
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
|
||||
# We Need to start it in host mode else it can't acces the local registry on port 5000
|
||||
- name: Setup Docker buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: network=host
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -53,30 +114,52 @@ jobs:
|
||||
echo "version_tag=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
|
||||
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
# Download the base PHP image AMD64
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: base-php-amd64.tar
|
||||
|
||||
# Download the base PHP image ARM64
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: base-php-arm64.tar
|
||||
|
||||
- name: Load base images into local registry
|
||||
run: |
|
||||
docker load -i base-php-amd64.tar
|
||||
docker load -i base-php-arm64.tar
|
||||
docker tag base-php:amd64 localhost:5000/base-php:amd64
|
||||
docker tag base-php:arm64 localhost:5000/base-php:arm64
|
||||
docker push localhost:5000/base-php:amd64
|
||||
docker push localhost:5000/base-php:arm64
|
||||
rm base-php-arm64.tar base-php-amd64.tar
|
||||
|
||||
- name: Build and Push (tag)
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
if: "github.event_name == 'release' && github.event.action == 'published'"
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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 }}
|
||||
|
||||
cache-from: type=gha,scope=tagged${{ matrix.os }}
|
||||
cache-to: type=gha,scope=tagged${{ matrix.os }},mode=max
|
||||
|
||||
- name: Build and Push (main)
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
if: "github.event_name == 'push' && contains(github.ref, 'main')"
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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
|
||||
cache-from: type=gha,scope=${{ matrix.os }}
|
||||
cache-to: type=gha,scope=${{ matrix.os }},mode=max
|
||||
|
||||
25
.github/workflows/lint.yaml
vendored
25
.github/workflows/lint.yaml
vendored
@@ -25,21 +25,38 @@ jobs:
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --no-progress --prefer-dist
|
||||
run: composer install --no-interaction --no-suggest --no-progress --no-autoloader --no-scripts
|
||||
|
||||
- name: Pint
|
||||
run: vendor/bin/pint --test
|
||||
phpstan:
|
||||
name: PHPStan
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: [8.2, 8.3, 8.4]
|
||||
steps:
|
||||
- name: Code Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get cache directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-composer-${{ matrix.php }}-
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "8.3"
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
@@ -48,7 +65,7 @@ jobs:
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --no-progress --prefer-dist
|
||||
run: composer install --no-interaction --no-suggest --no-progress --no-scripts
|
||||
|
||||
- name: PHPStan
|
||||
run: vendor/bin/phpstan --memory-limit=-1
|
||||
run: vendor/bin/phpstan --memory-limit=-1
|
||||
21
.github/workflows/release.yaml
vendored
21
.github/workflows/release.yaml
vendored
@@ -11,22 +11,33 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: bcmath, curl, gd, mbstring, mysql, openssl, pdo, tokenizer, xml, zip
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install PHP dependencies
|
||||
run: composer install --no-interaction --no-suggest --no-progress --no-autoloader --no-scripts --no-dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Install JS dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: yarn build:production
|
||||
run: yarn build
|
||||
|
||||
- name: Create release branch and bump version
|
||||
env:
|
||||
@@ -44,8 +55,8 @@ jobs:
|
||||
|
||||
- name: Create release archive
|
||||
run: |
|
||||
rm -rf node_modules tests CODE_OF_CONDUCT.md CONTRIBUTING.md flake.lock flake.nix phpunit.xml shell.nix
|
||||
tar -czf panel.tar.gz * .editorconfig .env.example .eslintignore .eslintrc.js .gitignore .prettierrc.json
|
||||
rm -rf node_modules vendor tests CODE_OF_CONDUCT.md CONTRIBUTING.md phpunit.xml shell.nix
|
||||
tar -czf panel.tar.gz * .env.example
|
||||
|
||||
- name: Create checksum
|
||||
run: |
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/storage/pail
|
||||
/storage/clockwork/*
|
||||
/vendor
|
||||
*.DS_Store*
|
||||
@@ -19,10 +20,12 @@ npm-debug.log
|
||||
yarn-error.log
|
||||
/.fleet
|
||||
/.idea
|
||||
/.nova
|
||||
/.vscode
|
||||
|
||||
public/assets/manifest.json
|
||||
/database/*.sqlite
|
||||
/database/*.sqlite-journal
|
||||
filament-monaco-editor/
|
||||
_ide_helper*
|
||||
/.phpstorm.meta.php
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
use PhpCsFixer\Config;
|
||||
use PhpCsFixer\Finder;
|
||||
|
||||
$finder = (new Finder())
|
||||
->in(__DIR__)
|
||||
->exclude([
|
||||
'vendor',
|
||||
'node_modules',
|
||||
'storage',
|
||||
'bootstrap/cache',
|
||||
])
|
||||
->notName(['_ide_helper*']);
|
||||
|
||||
return (new Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setFinder($finder)
|
||||
->setRules([
|
||||
'@Symfony' => true,
|
||||
'@PSR1' => true,
|
||||
'@PSR2' => true,
|
||||
'@PSR12' => true,
|
||||
'align_multiline_comment' => ['comment_type' => 'phpdocs_like'],
|
||||
'combine_consecutive_unsets' => true,
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'heredoc_to_nowdoc' => true,
|
||||
'no_alias_functions' => true,
|
||||
'no_unreachable_default_argument_value' => true,
|
||||
'no_useless_return' => true,
|
||||
'ordered_imports' => [
|
||||
'sort_algorithm' => 'length',
|
||||
],
|
||||
'phpdoc_align' => [
|
||||
'align' => 'left',
|
||||
'tags' => [
|
||||
'param',
|
||||
'property',
|
||||
'return',
|
||||
'throws',
|
||||
'type',
|
||||
'var',
|
||||
],
|
||||
],
|
||||
'random_api_migration' => true,
|
||||
'ternary_to_null_coalescing' => true,
|
||||
'yoda_style' => [
|
||||
'equal' => false,
|
||||
'identical' => false,
|
||||
'less_and_greater' => false,
|
||||
],
|
||||
]);
|
||||
118
Dockerfile
118
Dockerfile
@@ -1,51 +1,103 @@
|
||||
# syntax=docker.io/docker/dockerfile:1.13-labs
|
||||
# Pelican Production Dockerfile
|
||||
|
||||
FROM node:20-alpine AS yarn
|
||||
#FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine AS yarn
|
||||
|
||||
# For those who want to build this Dockerfile themselves, uncomment lines 6-12 and replace "localhost:5000/base-php:$TARGETARCH" on lines 17 and 67 with "base".
|
||||
|
||||
# FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine as base
|
||||
|
||||
# ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
|
||||
|
||||
# RUN install-php-extensions bcmath gd intl zip opcache pcntl posix pdo_mysql
|
||||
|
||||
# RUN rm /usr/local/bin/install-php-extensions
|
||||
|
||||
# ================================
|
||||
# Stage 1-1: Composer Install
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH localhost:5000/base-php:$TARGETARCH AS composer
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . ./
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
# Copy bare minimum to install Composer dependencies
|
||||
COPY composer.json composer.lock ./
|
||||
|
||||
RUN composer install --no-dev --no-interaction --no-autoloader --no-scripts
|
||||
|
||||
# ================================
|
||||
# Stage 1-2: Yarn Install
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine AS yarn
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy bare minimum to install Yarn dependencies
|
||||
COPY package.json yarn.lock ./
|
||||
|
||||
RUN yarn config set network-timeout 300000 \
|
||||
&& yarn install --frozen-lockfile \
|
||||
&& yarn run build:production
|
||||
&& yarn install --frozen-lockfile
|
||||
|
||||
FROM php:8.3-fpm-alpine
|
||||
# FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
|
||||
# ================================
|
||||
# Stage 2-1: Composer Optimize
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH composer AS composerbuild
|
||||
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
# Copy full code to optimize autoload
|
||||
COPY --exclude=Caddyfile --exclude=docker/ . ./
|
||||
|
||||
RUN composer dump-autoload --optimize
|
||||
|
||||
# ================================
|
||||
# Stage 2-2: Build Frontend Assets
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH yarn AS yarnbuild
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy full code
|
||||
COPY --exclude=Caddyfile --exclude=docker/ . ./
|
||||
COPY --from=composer /build .
|
||||
|
||||
RUN yarn run build
|
||||
|
||||
# ================================
|
||||
# Stage 5: Build Final Application Image
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH localhost:5000/base-php:$TARGETARCH AS final
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Install dependencies
|
||||
# Install additional required libraries
|
||||
RUN apk update && apk add --no-cache \
|
||||
libpng-dev libjpeg-turbo-dev freetype-dev libzip-dev icu-dev \
|
||||
zip unzip curl \
|
||||
caddy ca-certificates supervisor \
|
||||
&& docker-php-ext-install bcmath gd intl zip opcache pcntl posix pdo_mysql
|
||||
caddy ca-certificates supervisor supercronic
|
||||
|
||||
# Copy the Caddyfile to the container
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
COPY --chown=root:www-data --chmod=640 --from=composerbuild /build .
|
||||
COPY --chown=root:www-data --chmod=640 --from=yarnbuild /build/public ./public
|
||||
|
||||
# Copy the application code to the container
|
||||
COPY . .
|
||||
# Set permissions
|
||||
# First ensure all files are owned by root and restrict www-data to read access
|
||||
RUN chown root:www-data ./ \
|
||||
&& chmod 750 ./ \
|
||||
# Files should not have execute set, but directories need it
|
||||
&& find ./ -type d -exec chmod 750 {} \; \
|
||||
# Symlink to env/database path, as www-data won't be able to write to webroot
|
||||
&& ln -s /pelican-data/.env ./.env \
|
||||
&& ln -s /pelican-data/database/database.sqlite ./database/database.sqlite \
|
||||
# Create necessary directories
|
||||
&& mkdir -p /pelican-data /var/run/supervisord /etc/supercronic \
|
||||
# Finally allow www-data write permissions where necessary
|
||||
&& chown -R www-data:www-data /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \
|
||||
&& chmod -R u+rwX,g+rwX,o-rwx /pelican-data ./storage ./bootstrap/cache /var/run/supervisord
|
||||
|
||||
COPY --from=yarn /build/public/assets ./public/assets
|
||||
# Configure Supervisor
|
||||
COPY docker/supervisord.conf /etc/supervisord.conf
|
||||
COPY docker/Caddyfile /etc/caddy/Caddyfile
|
||||
# Add Laravel scheduler to crontab
|
||||
COPY docker/crontab /etc/supercronic/crontab
|
||||
|
||||
RUN touch .env
|
||||
|
||||
RUN composer install --no-dev --optimize-autoloader
|
||||
|
||||
# Set file permissions
|
||||
RUN chmod -R 755 storage bootstrap/cache
|
||||
|
||||
# Add scheduler to cron
|
||||
RUN echo "* * * * * php /var/www/html/artisan schedule:run >> /dev/null 2>&1" | crontab -u www-data -
|
||||
|
||||
## supervisord config and log dir
|
||||
RUN cp .github/docker/supervisord.conf /etc/supervisord.conf && \
|
||||
mkdir /var/log/supervisord/
|
||||
COPY docker/entrypoint.sh ./docker/entrypoint.sh
|
||||
|
||||
HEALTHCHECK --interval=5m --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost/up || exit 1
|
||||
@@ -54,5 +106,7 @@ EXPOSE 80 443
|
||||
|
||||
VOLUME /pelican-data
|
||||
|
||||
ENTRYPOINT [ "/bin/ash", ".github/docker/entrypoint.sh" ]
|
||||
USER www-data
|
||||
|
||||
ENTRYPOINT [ "/bin/ash", "docker/entrypoint.sh" ]
|
||||
CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ]
|
||||
|
||||
10
Dockerfile.base
Normal file
10
Dockerfile.base
Normal file
@@ -0,0 +1,10 @@
|
||||
# ================================
|
||||
# Stage 0: Build PHP Base Image
|
||||
# ================================
|
||||
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
|
||||
|
||||
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
|
||||
|
||||
RUN install-php-extensions bcmath gd intl zip opcache pcntl posix pdo_mysql
|
||||
|
||||
RUN rm /usr/local/bin/install-php-extensions
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use App\Models\Objects\Endpoint;
|
||||
use Illuminate\Contracts\Database\Eloquent\Castable;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class EndpointCollection implements Castable
|
||||
{
|
||||
public static function castUsing(array $arguments)
|
||||
{
|
||||
return new class() implements CastsAttributes
|
||||
{
|
||||
public function get($model, $key, $value, $attributes)
|
||||
{
|
||||
if (!isset($attributes[$key])) {
|
||||
return new Collection();
|
||||
}
|
||||
|
||||
$data = json_decode($attributes[$key], true);
|
||||
|
||||
return (new Collection($data))->map(function ($value) {
|
||||
return new Endpoint($value);
|
||||
});
|
||||
}
|
||||
|
||||
public function set($model, $key, $value, $attributes)
|
||||
{
|
||||
if (!is_array($value) && !$value instanceof Collection) {
|
||||
return new Collection();
|
||||
}
|
||||
|
||||
if (!$value instanceof Collection) {
|
||||
$value = new Collection($value);
|
||||
}
|
||||
|
||||
return [
|
||||
'ports' => $value->toJson(),
|
||||
];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
58
app/Checks/CacheCheck.php
Normal file
58
app/Checks/CacheCheck.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Health\Checks\Check;
|
||||
use Spatie\Health\Checks\Result;
|
||||
|
||||
class CacheCheck extends Check
|
||||
{
|
||||
protected ?string $driver = null;
|
||||
|
||||
public function driver(string $driver): self
|
||||
{
|
||||
$this->driver = $driver;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function run(): Result
|
||||
{
|
||||
$driver = $this->driver ?? $this->defaultDriver();
|
||||
|
||||
$result = Result::make()->meta([
|
||||
'driver' => $driver,
|
||||
]);
|
||||
|
||||
try {
|
||||
return $this->canWriteValuesToCache($driver)
|
||||
? $result->ok(trans('admin/health.results.cache.ok'))
|
||||
: $result->failed(trans('admin/health.results.cache.failed_retrieve'));
|
||||
} catch (Exception $exception) {
|
||||
return $result->failed(trans('admin/health.results.cache.failed', ['error' => $exception->getMessage()]));
|
||||
}
|
||||
}
|
||||
|
||||
protected function defaultDriver(): ?string
|
||||
{
|
||||
return config('cache.default', 'file');
|
||||
}
|
||||
|
||||
protected function canWriteValuesToCache(?string $driver): bool
|
||||
{
|
||||
$expectedValue = Str::random(5);
|
||||
|
||||
$cacheName = "laravel-health:check-{$expectedValue}";
|
||||
|
||||
Cache::driver($driver)->put($cacheName, $expectedValue, 10);
|
||||
|
||||
$actualValue = Cache::driver($driver)->get($cacheName);
|
||||
|
||||
Cache::driver($driver)->forget($cacheName);
|
||||
|
||||
return $actualValue === $expectedValue;
|
||||
}
|
||||
}
|
||||
42
app/Checks/DatabaseCheck.php
Normal file
42
app/Checks/DatabaseCheck.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Spatie\Health\Checks\Check;
|
||||
use Spatie\Health\Checks\Result;
|
||||
|
||||
class DatabaseCheck extends Check
|
||||
{
|
||||
protected ?string $connectionName = null;
|
||||
|
||||
public function connectionName(string $connectionName): self
|
||||
{
|
||||
$this->connectionName = $connectionName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function run(): Result
|
||||
{
|
||||
$connectionName = $this->connectionName ?? $this->getDefaultConnectionName();
|
||||
|
||||
$result = Result::make()->meta([
|
||||
'connection_name' => $connectionName,
|
||||
]);
|
||||
|
||||
try {
|
||||
DB::connection($connectionName)->getPdo();
|
||||
|
||||
return $result->ok(trans('admin/health.results.database.ok'));
|
||||
} catch (Exception $exception) {
|
||||
return $result->failed(trans('admin/health.results.database.failed', ['error' => $exception->getMessage()]));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getDefaultConnectionName(): string
|
||||
{
|
||||
return config('database.default');
|
||||
}
|
||||
}
|
||||
44
app/Checks/DebugModeCheck.php
Normal file
44
app/Checks/DebugModeCheck.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use Spatie\Health\Checks\Check;
|
||||
use Spatie\Health\Checks\Result;
|
||||
|
||||
use function config;
|
||||
|
||||
class DebugModeCheck extends Check
|
||||
{
|
||||
protected bool $expected = false;
|
||||
|
||||
public function expectedToBe(bool $bool): self
|
||||
{
|
||||
$this->expected = $bool;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function run(): Result
|
||||
{
|
||||
$actual = config('app.debug');
|
||||
|
||||
$result = Result::make()
|
||||
->meta([
|
||||
'actual' => $actual,
|
||||
'expected' => $this->expected,
|
||||
])
|
||||
->shortSummary($this->convertToWord($actual));
|
||||
|
||||
return $this->expected === $actual
|
||||
? $result->ok()
|
||||
: $result->failed(trans('admin/health.results.debugmode.failed', [
|
||||
'actual' => $this->convertToWord($actual),
|
||||
'expected' => $this->convertToWord($this->expected),
|
||||
]));
|
||||
}
|
||||
|
||||
protected function convertToWord(bool $boolean): string
|
||||
{
|
||||
return $boolean ? 'true' : 'false';
|
||||
}
|
||||
}
|
||||
38
app/Checks/EnvironmentCheck.php
Normal file
38
app/Checks/EnvironmentCheck.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Spatie\Health\Checks\Check;
|
||||
use Spatie\Health\Checks\Result;
|
||||
|
||||
class EnvironmentCheck extends Check
|
||||
{
|
||||
protected string $expectedEnvironment = 'production';
|
||||
|
||||
public function expectEnvironment(string $expectedEnvironment): self
|
||||
{
|
||||
$this->expectedEnvironment = $expectedEnvironment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function run(): Result
|
||||
{
|
||||
$actualEnvironment = (string) App::environment();
|
||||
|
||||
$result = Result::make()
|
||||
->meta([
|
||||
'actual' => $actualEnvironment,
|
||||
'expected' => $this->expectedEnvironment,
|
||||
])
|
||||
->shortSummary($actualEnvironment);
|
||||
|
||||
return $this->expectedEnvironment === $actualEnvironment
|
||||
? $result->ok(trans('admin/health.results.environment.ok'))
|
||||
: $result->failed(trans('admin/health.results.environment.failed', [
|
||||
'actual' => $actualEnvironment,
|
||||
'expected' => $this->expectedEnvironment,
|
||||
]));
|
||||
}
|
||||
}
|
||||
45
app/Checks/NodeVersionsCheck.php
Normal file
45
app/Checks/NodeVersionsCheck.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use App\Models\Node;
|
||||
use App\Services\Helpers\SoftwareVersionService;
|
||||
use Spatie\Health\Checks\Check;
|
||||
use Spatie\Health\Checks\Result;
|
||||
use Spatie\Health\Enums\Status;
|
||||
|
||||
class NodeVersionsCheck extends Check
|
||||
{
|
||||
public function __construct(private SoftwareVersionService $versionService) {}
|
||||
|
||||
public function run(): Result
|
||||
{
|
||||
$all = Node::query()->count();
|
||||
|
||||
if ($all === 0) {
|
||||
$result = Result::make()
|
||||
->notificationMessage(trans('admin/health.results.nodeversions.no_nodes_created'))
|
||||
->shortSummary(trans('admin/health.results.nodeversions.no_nodes'));
|
||||
$result->status = Status::skipped();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$latestVersion = $this->versionService->latestWingsVersion();
|
||||
|
||||
$outdated = Node::query()->get()
|
||||
->filter(fn (Node $node) => !isset($node->systemInformation()['exception']) && $node->systemInformation()['version'] !== $latestVersion)
|
||||
->count();
|
||||
|
||||
$result = Result::make()
|
||||
->meta([
|
||||
'all' => $all,
|
||||
'outdated' => $outdated,
|
||||
])
|
||||
->shortSummary($outdated === 0 ? trans('admin/health.results.nodeversions.all_up_to_date') : trans('admin/health.results.nodeversions.outdated', ['outdated' => $outdated, 'all' => $all]));
|
||||
|
||||
return $outdated === 0
|
||||
? $result->ok(trans('admin/health.results.nodeversions.ok'))
|
||||
: $result->failed(trans('admin/health.results.nodeversions.failed', ['outdated' => $outdated, 'all' => $all]));
|
||||
}
|
||||
}
|
||||
34
app/Checks/PanelVersionCheck.php
Normal file
34
app/Checks/PanelVersionCheck.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use App\Services\Helpers\SoftwareVersionService;
|
||||
use Spatie\Health\Checks\Check;
|
||||
use Spatie\Health\Checks\Result;
|
||||
|
||||
class PanelVersionCheck extends Check
|
||||
{
|
||||
public function __construct(private SoftwareVersionService $versionService) {}
|
||||
|
||||
public function run(): Result
|
||||
{
|
||||
$isLatest = $this->versionService->isLatestPanel();
|
||||
$currentVersion = $this->versionService->currentPanelVersion();
|
||||
$latestVersion = $this->versionService->latestPanelVersion();
|
||||
|
||||
$result = Result::make()
|
||||
->meta([
|
||||
'isLatest' => $isLatest,
|
||||
'currentVersion' => $currentVersion,
|
||||
'latestVersion' => $latestVersion,
|
||||
])
|
||||
->shortSummary($isLatest ? trans('admin/health.results.panelversion.up_to_date') : trans('admin/health.results.panelversion.outdated'));
|
||||
|
||||
return $isLatest
|
||||
? $result->ok(trans('admin/health.results.panelversion.ok'))
|
||||
: $result->failed(trans('admin/health.results.panelversion.failed', [
|
||||
'currentVersion' => $currentVersion,
|
||||
'latestVersion' => $latestVersion,
|
||||
]));
|
||||
}
|
||||
}
|
||||
41
app/Checks/ScheduleCheck.php
Normal file
41
app/Checks/ScheduleCheck.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Composer\InstalledVersions;
|
||||
use Spatie\Health\Checks\Checks\ScheduleCheck as BaseCheck;
|
||||
use Spatie\Health\Checks\Result;
|
||||
|
||||
class ScheduleCheck extends BaseCheck
|
||||
{
|
||||
public function run(): Result
|
||||
{
|
||||
$result = Result::make()->ok(trans('admin/health.results.schedule.ok'));
|
||||
|
||||
$lastHeartbeatTimestamp = cache()->store($this->cacheStoreName)->get($this->cacheKey);
|
||||
|
||||
if (!$lastHeartbeatTimestamp) {
|
||||
return $result->failed(trans('admin/health.results.schedule.failed_not_ran'));
|
||||
}
|
||||
|
||||
$latestHeartbeatAt = Carbon::createFromTimestamp($lastHeartbeatTimestamp);
|
||||
|
||||
$carbonVersion = InstalledVersions::getVersion('nesbot/carbon');
|
||||
|
||||
$minutesAgo = $latestHeartbeatAt->diffInMinutes();
|
||||
|
||||
if (version_compare($carbonVersion,
|
||||
'3.0.0', '<')) {
|
||||
$minutesAgo += 1;
|
||||
}
|
||||
|
||||
if ($minutesAgo > $this->heartbeatMaxAgeInMinutes) {
|
||||
return $result->failed(trans('admin/health.results.schedule.failed_last_ran', [
|
||||
'time' => $minutesAgo,
|
||||
]));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
16
app/Checks/UsedDiskSpaceCheck.php
Normal file
16
app/Checks/UsedDiskSpaceCheck.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Checks;
|
||||
|
||||
use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck as BaseCheck;
|
||||
|
||||
class UsedDiskSpaceCheck extends BaseCheck
|
||||
{
|
||||
protected function getDiskUsagePercentage(): int
|
||||
{
|
||||
$freeSpace = disk_free_space($this->filesystemName ?? '/');
|
||||
$totalSpace = disk_total_space($this->filesystemName ?? '/');
|
||||
|
||||
return 100 - ($freeSpace * 100 / $totalSpace);
|
||||
}
|
||||
}
|
||||
@@ -16,28 +16,37 @@ class CheckEggUpdatesCommand extends Command
|
||||
$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());
|
||||
}
|
||||
$this->check($egg, $exporterService);
|
||||
} catch (Exception $exception) {
|
||||
$this->error("{$egg->name}: Error ({$exception->getMessage()})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function check(Egg $egg, EggExporterService $exporterService): void
|
||||
{
|
||||
if (is_null($egg->update_url)) {
|
||||
$this->comment("$egg->name: Skipping (no update url set)");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$currentJson = json_decode($exporterService->handle($egg->id));
|
||||
unset($currentJson->exported_at);
|
||||
|
||||
$updatedEgg = file_get_contents($egg->update_url);
|
||||
assert($updatedEgg !== false);
|
||||
$updatedJson = json_decode($updatedEgg);
|
||||
unset($updatedJson->exported_at);
|
||||
|
||||
if (md5(json_encode($currentJson, JSON_THROW_ON_ERROR)) === md5(json_encode($updatedJson, JSON_THROW_ON_ERROR))) {
|
||||
$this->info("$egg->name: Up-to-date");
|
||||
cache()->put("eggs.$egg->uuid.update", false, now()->addHour());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->warn("$egg->name: Found update");
|
||||
cache()->put("eggs.$egg->uuid.update", true, now()->addHour());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ class CacheSettingsCommand extends Command
|
||||
{--redis-pass= : Password used to connect to redis.}
|
||||
{--redis-port= : Port to connect to redis over.}';
|
||||
|
||||
protected array $variables = [];
|
||||
|
||||
/**
|
||||
* CacheSettingsCommand constructor.
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,7 @@ class DatabaseSettingsCommand extends Command
|
||||
{--username= : Username to use when connecting to the MySQL/ MariaDB server.}
|
||||
{--password= : Password to use for the MySQL/ MariaDB database.}';
|
||||
|
||||
/** @var array<array-key, mixed> */
|
||||
protected array $variables = [];
|
||||
|
||||
/**
|
||||
@@ -57,7 +58,7 @@ class DatabaseSettingsCommand extends Command
|
||||
);
|
||||
|
||||
if ($this->variables['DB_CONNECTION'] === 'mysql') {
|
||||
$this->output->note(__('commands.database_settings.DB_HOST_note'));
|
||||
$this->output->note(trans('commands.database_settings.DB_HOST_note'));
|
||||
$this->variables['DB_HOST'] = $this->option('host') ?? $this->ask(
|
||||
'Database Host',
|
||||
config('database.connections.mysql.host', '127.0.0.1')
|
||||
@@ -73,7 +74,7 @@ class DatabaseSettingsCommand extends Command
|
||||
config('database.connections.mysql.database', 'panel')
|
||||
);
|
||||
|
||||
$this->output->note(__('commands.database_settings.DB_USERNAME_note'));
|
||||
$this->output->note(trans('commands.database_settings.DB_USERNAME_note'));
|
||||
$this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask(
|
||||
'Database Username',
|
||||
config('database.connections.mysql.username', 'pelican')
|
||||
@@ -82,7 +83,7 @@ class DatabaseSettingsCommand extends Command
|
||||
$askForMySQLPassword = true;
|
||||
if (!empty(config('database.connections.mysql.password')) && $this->input->isInteractive()) {
|
||||
$this->variables['DB_PASSWORD'] = config('database.connections.mysql.password');
|
||||
$askForMySQLPassword = $this->confirm(__('commands.database_settings.DB_PASSWORD_note'));
|
||||
$askForMySQLPassword = $this->confirm(trans('commands.database_settings.DB_PASSWORD_note'));
|
||||
}
|
||||
|
||||
if ($askForMySQLPassword) {
|
||||
@@ -106,9 +107,9 @@ class DatabaseSettingsCommand extends Command
|
||||
$this->database->connection('_panel_command_test')->getPdo();
|
||||
} catch (\PDOException $exception) {
|
||||
$this->output->error(sprintf('Unable to connect to the MySQL server using the provided credentials. The error returned was "%s".', $exception->getMessage()));
|
||||
$this->output->error(__('commands.database_settings.DB_error_2'));
|
||||
$this->output->error(trans('commands.database_settings.DB_error_2'));
|
||||
|
||||
if ($this->confirm(__('commands.database_settings.go_back'))) {
|
||||
if ($this->confirm(trans('commands.database_settings.go_back'))) {
|
||||
$this->database->disconnect('_panel_command_test');
|
||||
|
||||
return $this->handle();
|
||||
@@ -117,7 +118,7 @@ class DatabaseSettingsCommand extends Command
|
||||
return 1;
|
||||
}
|
||||
} elseif ($this->variables['DB_CONNECTION'] === 'mariadb') {
|
||||
$this->output->note(__('commands.database_settings.DB_HOST_note'));
|
||||
$this->output->note(trans('commands.database_settings.DB_HOST_note'));
|
||||
$this->variables['DB_HOST'] = $this->option('host') ?? $this->ask(
|
||||
'Database Host',
|
||||
config('database.connections.mariadb.host', '127.0.0.1')
|
||||
@@ -133,7 +134,7 @@ class DatabaseSettingsCommand extends Command
|
||||
config('database.connections.mariadb.database', 'panel')
|
||||
);
|
||||
|
||||
$this->output->note(__('commands.database_settings.DB_USERNAME_note'));
|
||||
$this->output->note(trans('commands.database_settings.DB_USERNAME_note'));
|
||||
$this->variables['DB_USERNAME'] = $this->option('username') ?? $this->ask(
|
||||
'Database Username',
|
||||
config('database.connections.mariadb.username', 'pelican')
|
||||
@@ -142,7 +143,7 @@ class DatabaseSettingsCommand extends Command
|
||||
$askForMariaDBPassword = true;
|
||||
if (!empty(config('database.connections.mariadb.password')) && $this->input->isInteractive()) {
|
||||
$this->variables['DB_PASSWORD'] = config('database.connections.mariadb.password');
|
||||
$askForMariaDBPassword = $this->confirm(__('commands.database_settings.DB_PASSWORD_note'));
|
||||
$askForMariaDBPassword = $this->confirm(trans('commands.database_settings.DB_PASSWORD_note'));
|
||||
}
|
||||
|
||||
if ($askForMariaDBPassword) {
|
||||
@@ -166,9 +167,9 @@ class DatabaseSettingsCommand extends Command
|
||||
$this->database->connection('_panel_command_test')->getPdo();
|
||||
} catch (\PDOException $exception) {
|
||||
$this->output->error(sprintf('Unable to connect to the MariaDB server using the provided credentials. The error returned was "%s".', $exception->getMessage()));
|
||||
$this->output->error(__('commands.database_settings.DB_error_2'));
|
||||
$this->output->error(trans('commands.database_settings.DB_error_2'));
|
||||
|
||||
if ($this->confirm(__('commands.database_settings.go_back'))) {
|
||||
if ($this->confirm(trans('commands.database_settings.go_back'))) {
|
||||
$this->database->disconnect('_panel_command_test');
|
||||
|
||||
return $this->handle();
|
||||
@@ -179,7 +180,7 @@ class DatabaseSettingsCommand extends Command
|
||||
} elseif ($this->variables['DB_CONNECTION'] === 'sqlite') {
|
||||
$this->variables['DB_DATABASE'] = $this->option('database') ?? $this->ask(
|
||||
'Database Path',
|
||||
env('DB_DATABASE', 'database.sqlite')
|
||||
(string) env('DB_DATABASE', 'database.sqlite')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ class EmailSettingsCommand extends Command
|
||||
{--username=}
|
||||
{--password=}';
|
||||
|
||||
/** @var array<array-key, mixed> */
|
||||
protected array $variables = [];
|
||||
|
||||
/**
|
||||
@@ -91,7 +92,7 @@ class EmailSettingsCommand extends Command
|
||||
trans('command/messages.environment.mail.ask_smtp_password')
|
||||
);
|
||||
|
||||
$this->variables['MAIL_ENCRYPTION'] = $this->option('encryption') ?? $this->choice(
|
||||
$this->variables['MAIL_SCHEME'] = $this->option('encryption') ?? $this->choice(
|
||||
trans('command/messages.environment.mail.ask_encryption'),
|
||||
['tls' => 'TLS', 'ssl' => 'SSL', '' => 'None'],
|
||||
config('mail.mailers.smtp.encryption', 'tls')
|
||||
|
||||
@@ -27,8 +27,6 @@ class QueueSettingsCommand extends Command
|
||||
{--redis-pass= : Password used to connect to redis.}
|
||||
{--redis-port= : Port to connect to redis over.}';
|
||||
|
||||
protected array $variables = [];
|
||||
|
||||
/**
|
||||
* QueueSettingsCommand constructor.
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ class QueueWorkerServiceCommand extends Command
|
||||
$serviceName = $this->option('service-name') ?? $this->ask('Queue worker service name', 'pelican-queue');
|
||||
$path = '/etc/systemd/system/' . $serviceName . '.service';
|
||||
|
||||
$fileExists = file_exists($path);
|
||||
$fileExists = @file_exists($path);
|
||||
if ($fileExists && !$this->option('overwrite') && !$this->confirm('The service file already exists. Do you want to overwrite it?')) {
|
||||
$this->line('Creation of queue worker service file aborted because service file already exists.');
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ class RedisSetupCommand extends Command
|
||||
{--redis-pass= : Password used to connect to redis.}
|
||||
{--redis-port= : Port to connect to redis over.}';
|
||||
|
||||
protected array $variables = [];
|
||||
|
||||
/**
|
||||
* RedisSetupCommand constructor.
|
||||
*/
|
||||
|
||||
@@ -28,8 +28,6 @@ class SessionSettingsCommand extends Command
|
||||
{--redis-pass= : Password used to connect to redis.}
|
||||
{--redis-port= : Port to connect to redis over.}';
|
||||
|
||||
protected array $variables = [];
|
||||
|
||||
/**
|
||||
* SessionSettingsCommand constructor.
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Services\Helpers\SoftwareVersionService;
|
||||
|
||||
class InfoCommand extends Command
|
||||
{
|
||||
@@ -11,98 +10,8 @@ class InfoCommand extends Command
|
||||
|
||||
protected $signature = 'p:info';
|
||||
|
||||
/**
|
||||
* InfoCommand constructor.
|
||||
*/
|
||||
public function __construct(private SoftwareVersionService $versionService)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle execution of command.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$this->output->title('Version Information');
|
||||
$this->table([], [
|
||||
['Panel Version', $this->versionService->versionData()['version']],
|
||||
['Latest Version', $this->versionService->getPanel()],
|
||||
['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')],
|
||||
], 'compact');
|
||||
|
||||
$this->output->title('Application Configuration');
|
||||
$this->table([], [
|
||||
['Environment', config('app.env') === 'production' ? config('app.env') : $this->formatText(config('app.env'), 'bg=red')],
|
||||
['Debug Mode', config('app.debug') ? $this->formatText('Yes', 'bg=red') : 'No'],
|
||||
['Application Name', config('app.name')],
|
||||
['Application URL', config('app.url')],
|
||||
['Installation Directory', base_path()],
|
||||
['Cache Driver', config('cache.default')],
|
||||
['Queue Driver', config('queue.default') === 'sync' ? $this->formatText(config('queue.default'), 'bg=red') : config('queue.default')],
|
||||
['Session Driver', config('session.driver')],
|
||||
['Filesystem Driver', config('filesystems.default')],
|
||||
], 'compact');
|
||||
|
||||
$this->output->title('Database Configuration');
|
||||
$driver = config('database.default');
|
||||
if ($driver === 'sqlite') {
|
||||
$this->table([], [
|
||||
['Driver', $driver],
|
||||
['Database', config("database.connections.$driver.database")],
|
||||
], 'compact');
|
||||
} else {
|
||||
$this->table([], [
|
||||
['Driver', $driver],
|
||||
['Host', config("database.connections.$driver.host")],
|
||||
['Port', config("database.connections.$driver.port")],
|
||||
['Database', config("database.connections.$driver.database")],
|
||||
['Username', config("database.connections.$driver.username")],
|
||||
], 'compact');
|
||||
}
|
||||
|
||||
$this->output->title('Email Configuration');
|
||||
$driver = config('mail.default');
|
||||
if ($driver === 'smtp') {
|
||||
$this->table([], [
|
||||
['Driver', $driver],
|
||||
['Host', config("mail.mailers.$driver.host")],
|
||||
['Port', config("mail.mailers.$driver.port")],
|
||||
['Username', config("mail.mailers.$driver.username")],
|
||||
['Encryption', config("mail.mailers.$driver.encryption")],
|
||||
['From Address', config('mail.from.address')],
|
||||
['From Name', config('mail.from.name')],
|
||||
], 'compact');
|
||||
} else {
|
||||
$this->table([], [
|
||||
['Driver', $driver],
|
||||
['From Address', config('mail.from.address')],
|
||||
['From Name', config('mail.from.name')],
|
||||
], 'compact');
|
||||
}
|
||||
|
||||
$this->output->title('Backup Configuration');
|
||||
$driver = config('backups.default');
|
||||
if ($driver === 's3') {
|
||||
$this->table([], [
|
||||
['Driver', $driver],
|
||||
['Region', config("backups.disks.$driver.region")],
|
||||
['Bucket', config("backups.disks.$driver.bucket")],
|
||||
['Endpoint', config("backups.disks.$driver.endpoint")],
|
||||
['Use path style endpoint', config("backups.disks.$driver.use_path_style_endpoint") ? 'Yes' : 'No'],
|
||||
], 'compact');
|
||||
} else {
|
||||
$this->table([], [
|
||||
['Driver', $driver],
|
||||
], 'compact');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format output in a Name: Value manner.
|
||||
*/
|
||||
private function formatText(string $value, string $opts = ''): string
|
||||
{
|
||||
return sprintf('<%s>%s</>', $opts, $value);
|
||||
$this->call('about');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,31 +45,31 @@ class MakeNodeCommand extends Command
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$data['name'] = $this->option('name') ?? $this->ask(__('commands.make_node.name'));
|
||||
$data['description'] = $this->option('description') ?? $this->ask(__('commands.make_node.description'));
|
||||
$data['name'] = $this->option('name') ?? $this->ask(trans('commands.make_node.name'));
|
||||
$data['description'] = $this->option('description') ?? $this->ask(trans('commands.make_node.description'));
|
||||
$data['scheme'] = $this->option('scheme') ?? $this->anticipate(
|
||||
__('commands.make_node.scheme'),
|
||||
trans('commands.make_node.scheme'),
|
||||
['https', 'http'],
|
||||
'https'
|
||||
);
|
||||
|
||||
$data['fqdn'] = $this->option('fqdn') ?? $this->ask(__('commands.make_node.fqdn'));
|
||||
$data['public'] = $this->option('public') ?? $this->confirm(__('commands.make_node.public'), true);
|
||||
$data['behind_proxy'] = $this->option('proxy') ?? $this->confirm(__('commands.make_node.behind_proxy'));
|
||||
$data['maintenance_mode'] = $this->option('maintenance') ?? $this->confirm(__('commands.make_node.maintenance_mode'));
|
||||
$data['memory'] = $this->option('maxMemory') ?? $this->ask(__('commands.make_node.memory'), '0');
|
||||
$data['memory_overallocate'] = $this->option('overallocateMemory') ?? $this->ask(__('commands.make_node.memory_overallocate'), '-1');
|
||||
$data['disk'] = $this->option('maxDisk') ?? $this->ask(__('commands.make_node.disk'), '0');
|
||||
$data['disk_overallocate'] = $this->option('overallocateDisk') ?? $this->ask(__('commands.make_node.disk_overallocate'), '-1');
|
||||
$data['cpu'] = $this->option('maxCpu') ?? $this->ask(__('commands.make_node.cpu'), '0');
|
||||
$data['cpu_overallocate'] = $this->option('overallocateCpu') ?? $this->ask(__('commands.make_node.cpu_overallocate'), '-1');
|
||||
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(__('commands.make_node.upload_size'), '256');
|
||||
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(__('commands.make_node.daemonListen'), '8080');
|
||||
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(__('commands.make_node.daemonSFTP'), '2022');
|
||||
$data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(__('commands.make_node.daemonSFTPAlias'), '');
|
||||
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(__('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
|
||||
$data['fqdn'] = $this->option('fqdn') ?? $this->ask(trans('commands.make_node.fqdn'));
|
||||
$data['public'] = $this->option('public') ?? $this->confirm(trans('commands.make_node.public'), true);
|
||||
$data['behind_proxy'] = $this->option('proxy') ?? $this->confirm(trans('commands.make_node.behind_proxy'));
|
||||
$data['maintenance_mode'] = $this->option('maintenance') ?? $this->confirm(trans('commands.make_node.maintenance_mode'));
|
||||
$data['memory'] = $this->option('maxMemory') ?? $this->ask(trans('commands.make_node.memory'), '0');
|
||||
$data['memory_overallocate'] = $this->option('overallocateMemory') ?? $this->ask(trans('commands.make_node.memory_overallocate'), '-1');
|
||||
$data['disk'] = $this->option('maxDisk') ?? $this->ask(trans('commands.make_node.disk'), '0');
|
||||
$data['disk_overallocate'] = $this->option('overallocateDisk') ?? $this->ask(trans('commands.make_node.disk_overallocate'), '-1');
|
||||
$data['cpu'] = $this->option('maxCpu') ?? $this->ask(trans('commands.make_node.cpu'), '0');
|
||||
$data['cpu_overallocate'] = $this->option('overallocateCpu') ?? $this->ask(trans('commands.make_node.cpu_overallocate'), '-1');
|
||||
$data['upload_size'] = $this->option('uploadSize') ?? $this->ask(trans('commands.make_node.upload_size'), '256');
|
||||
$data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(trans('commands.make_node.daemonListen'), '8080');
|
||||
$data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(trans('commands.make_node.daemonSFTP'), '2022');
|
||||
$data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(trans('commands.make_node.daemonSFTPAlias'), '');
|
||||
$data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(trans('commands.make_node.daemonBase'), '/var/lib/pelican/volumes');
|
||||
|
||||
$node = $this->creationService->handle($data);
|
||||
$this->line(__('commands.make_node.success', ['name' => $data['name'], 'id' => $node->id]));
|
||||
$this->line(trans('commands.make_node.success', ['name' => $data['name'], 'id' => $node->id]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@ class NodeConfigurationCommand extends Command
|
||||
|
||||
/** @var \App\Models\Node $node */
|
||||
$node = Node::query()->where($column, $this->argument('node'))->firstOr(function () {
|
||||
$this->error(__('commands.node_config.error_not_exist'));
|
||||
$this->error(trans('commands.node_config.error_not_exist'));
|
||||
|
||||
exit(1);
|
||||
});
|
||||
|
||||
$format = $this->option('format');
|
||||
if (!in_array($format, ['yaml', 'yml', 'json'])) {
|
||||
$this->error(__('commands.node_config.error_invalid_format'));
|
||||
$this->error(trans('commands.node_config.error_invalid_format'));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ class KeyGenerateCommand extends BaseKeyGenerateCommand
|
||||
public function handle(): void
|
||||
{
|
||||
if (!empty(config('app.key')) && $this->input->isInteractive()) {
|
||||
$this->output->warning(__('commands.key_generate.error_already_exist'));
|
||||
if (!$this->confirm(__('commands.key_generate.understand'))) {
|
||||
$this->output->warning(trans('commands.key_generate.error_already_exist'));
|
||||
if (!$this->confirm(trans('commands.key_generate.understand'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->confirm(__('commands.key_generate.continue'))) {
|
||||
if (!$this->confirm(trans('commands.key_generate.continue'))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use Illuminate\Console\Command;
|
||||
use App\Models\Schedule;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Services\Schedules\ProcessScheduleService;
|
||||
use Carbon\Carbon;
|
||||
use Throwable;
|
||||
|
||||
class ProcessRunnableCommand extends Command
|
||||
{
|
||||
@@ -14,21 +14,18 @@ class ProcessRunnableCommand extends Command
|
||||
|
||||
protected $description = 'Process schedules in the database and determine which are ready to run.';
|
||||
|
||||
/**
|
||||
* Handle command execution.
|
||||
*/
|
||||
public function handle(): int
|
||||
public function handle(ProcessScheduleService $processScheduleService): int
|
||||
{
|
||||
$schedules = Schedule::query()
|
||||
->with('tasks')
|
||||
->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) {
|
||||
$this->line(__('commands.schedule.process.no_tasks'));
|
||||
$this->line(trans('commands.schedule.process.no_tasks'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -36,7 +33,7 @@ class ProcessRunnableCommand extends Command
|
||||
$bar = $this->output->createProgressBar(count($schedules));
|
||||
foreach ($schedules as $schedule) {
|
||||
$bar->clear();
|
||||
$this->processSchedule($schedule);
|
||||
$this->processSchedule($processScheduleService, $schedule);
|
||||
$bar->advance();
|
||||
$bar->display();
|
||||
}
|
||||
@@ -51,23 +48,23 @@ 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): void
|
||||
protected function processSchedule(ProcessScheduleService $processScheduleService, Schedule $schedule): void
|
||||
{
|
||||
if ($schedule->tasks->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->getLaravel()->make(ProcessScheduleService::class)->handle($schedule);
|
||||
$processScheduleService->handle($schedule);
|
||||
|
||||
$this->line(trans('command/messages.schedule.output_line', [
|
||||
'schedule' => $schedule->name,
|
||||
'id' => $schedule->id,
|
||||
]));
|
||||
} catch (\Throwable|\Exception $exception) {
|
||||
} catch (Throwable $exception) {
|
||||
logger()->error($exception, ['schedule_id' => $schedule->id]);
|
||||
|
||||
$this->error(__('commands.schedule.process.no_tasks') . " #$schedule->id: " . $exception->getMessage());
|
||||
$this->error(trans('commands.schedule.process.no_tasks') . " #$schedule->id: " . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Validation\Factory as ValidatorFactory;
|
||||
use App\Repositories\Daemon\DaemonPowerRepository;
|
||||
use App\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
use Exception;
|
||||
|
||||
class BulkPowerActionCommand extends Command
|
||||
{
|
||||
@@ -19,26 +19,13 @@ class BulkPowerActionCommand extends Command
|
||||
|
||||
protected $description = 'Perform bulk power management on large groupings of servers or nodes at once.';
|
||||
|
||||
/**
|
||||
* BulkPowerActionCommand constructor.
|
||||
*/
|
||||
public function __construct(private DaemonPowerRepository $powerRepository, private ValidatorFactory $validator)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the bulk power request.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function handle(): void
|
||||
public function handle(DaemonPowerRepository $powerRepository, ValidatorFactory $validator): void
|
||||
{
|
||||
$action = $this->argument('action');
|
||||
$nodes = empty($this->option('nodes')) ? [] : explode(',', $this->option('nodes'));
|
||||
$servers = empty($this->option('servers')) ? [] : explode(',', $this->option('servers'));
|
||||
|
||||
$validator = $this->validator->make([
|
||||
$validator = $validator->make([
|
||||
'action' => $action,
|
||||
'nodes' => $nodes,
|
||||
'servers' => $servers,
|
||||
@@ -64,13 +51,17 @@ class BulkPowerActionCommand extends Command
|
||||
}
|
||||
|
||||
$bar = $this->output->createProgressBar($count);
|
||||
$powerRepository = $this->powerRepository;
|
||||
$this->getQueryBuilder($servers, $nodes)->each(function (Server $server) use ($action, $powerRepository, &$bar) {
|
||||
|
||||
$this->getQueryBuilder($servers, $nodes)->get()->each(function ($server, int $index) use ($action, $powerRepository, &$bar): mixed {
|
||||
$bar->clear();
|
||||
|
||||
if (!$server instanceof Server) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$powerRepository->setServer($server)->send($action);
|
||||
} catch (DaemonConnectionException $exception) {
|
||||
} catch (Exception $exception) {
|
||||
$this->output->error(trans('command/messages.server.power.action_failed', [
|
||||
'name' => $server->name,
|
||||
'id' => $server->id,
|
||||
@@ -81,6 +72,8 @@ class BulkPowerActionCommand extends Command
|
||||
|
||||
$bar->advance();
|
||||
$bar->display();
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
$this->line('');
|
||||
@@ -88,6 +81,9 @@ class BulkPowerActionCommand extends Command
|
||||
|
||||
/**
|
||||
* Returns the query builder instance that will return the servers that should be affected.
|
||||
*
|
||||
* @param string[]|int[] $servers
|
||||
* @param string[]|int[] $nodes
|
||||
*/
|
||||
protected function getQueryBuilder(array $servers, array $nodes): Builder
|
||||
{
|
||||
|
||||
@@ -34,30 +34,26 @@ class UpgradeCommand extends Command
|
||||
{
|
||||
$skipDownload = $this->option('skip-download');
|
||||
if (!$skipDownload) {
|
||||
$this->output->warning(__('commands.upgrade.integrity'));
|
||||
$this->output->comment(__('commands.upgrade.source_url'));
|
||||
$this->output->warning(trans('commands.upgrade.integrity'));
|
||||
$this->output->comment(trans('commands.upgrade.source_url'));
|
||||
$this->line($this->getUrl());
|
||||
}
|
||||
|
||||
if (version_compare(PHP_VERSION, '7.4.0') < 0) {
|
||||
$this->error(__('commands.upgrade.php_version') . ' [' . PHP_VERSION . '].');
|
||||
}
|
||||
|
||||
$user = 'www-data';
|
||||
$group = 'www-data';
|
||||
if ($this->input->isInteractive()) {
|
||||
if (!$skipDownload) {
|
||||
$skipDownload = !$this->confirm(__('commands.upgrade.skipDownload'), true);
|
||||
$skipDownload = !$this->confirm(trans('commands.upgrade.skipDownload'), true);
|
||||
}
|
||||
|
||||
if (is_null($this->option('user'))) {
|
||||
$userDetails = function_exists('posix_getpwuid') ? posix_getpwuid(fileowner('public')) : [];
|
||||
$user = $userDetails['name'] ?? 'www-data';
|
||||
|
||||
$message = __('commands.upgrade.webserver_user', ['user' => $user]);
|
||||
$message = trans('commands.upgrade.webserver_user', ['user' => $user]);
|
||||
if (!$this->confirm($message, true)) {
|
||||
$user = $this->anticipate(
|
||||
__('commands.upgrade.name_webserver'),
|
||||
trans('commands.upgrade.name_webserver'),
|
||||
[
|
||||
'www-data',
|
||||
'nginx',
|
||||
@@ -71,10 +67,10 @@ class UpgradeCommand extends Command
|
||||
$groupDetails = function_exists('posix_getgrgid') ? posix_getgrgid(filegroup('public')) : [];
|
||||
$group = $groupDetails['name'] ?? 'www-data';
|
||||
|
||||
$message = __('commands.upgrade.group_webserver', ['group' => $user]);
|
||||
$message = trans('commands.upgrade.group_webserver', ['group' => $user]);
|
||||
if (!$this->confirm($message, true)) {
|
||||
$group = $this->anticipate(
|
||||
__('commands.upgrade.group_webserver_question'),
|
||||
trans('commands.upgrade.group_webserver_question'),
|
||||
[
|
||||
'www-data',
|
||||
'nginx',
|
||||
@@ -84,8 +80,8 @@ class UpgradeCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->confirm(__('commands.upgrade.are_your_sure'))) {
|
||||
$this->warn(__('commands.upgrade.terminated'));
|
||||
if (!$this->confirm(trans('commands.upgrade.are_your_sure'))) {
|
||||
$this->warn(trans('commands.upgrade.terminated'));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -175,7 +171,7 @@ class UpgradeCommand extends Command
|
||||
});
|
||||
|
||||
$this->newLine(2);
|
||||
$this->info(__('commands.upgrade.success'));
|
||||
$this->info(trans('commands.upgrade.success'));
|
||||
}
|
||||
|
||||
protected function withProgress(ProgressBar $bar, \Closure $callback): void
|
||||
|
||||
@@ -19,7 +19,7 @@ class DisableTwoFactorCommand extends Command
|
||||
public function handle(): void
|
||||
{
|
||||
if ($this->input->isInteractive()) {
|
||||
$this->output->warning(trans('command/messages.user.2fa_help_text'));
|
||||
$this->output->warning(trans('command/messages.user.2fa_help_text.0') . trans('command/messages.user.2fa_help_text.1'));
|
||||
}
|
||||
|
||||
$email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email'));
|
||||
|
||||
@@ -9,9 +9,12 @@ 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 Spatie\Health\Commands\RunHealthChecksCommand;
|
||||
use Spatie\Health\Commands\ScheduleCheckHeartbeatCommand;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
@@ -48,5 +51,12 @@ 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();
|
||||
}
|
||||
|
||||
$schedule->command(ScheduleCheckHeartbeatCommand::class)->everyMinute();
|
||||
$schedule->command(RunHealthChecksCommand::class)->everyFiveMinutes();
|
||||
}
|
||||
}
|
||||
|
||||
22
app/Contracts/Validatable.php
Normal file
22
app/Contracts/Validatable.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Contracts;
|
||||
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
interface Validatable
|
||||
{
|
||||
public function getValidator(): Validator;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function getRules(): array;
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
public static function getRulesForField(string $field): array;
|
||||
|
||||
public function validate(): void;
|
||||
}
|
||||
24
app/Eloquent/BackupQueryBuilder.php
Normal file
24
app/Eloquent/BackupQueryBuilder.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Eloquent;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* @template TModel of \Illuminate\Database\Eloquent\Model
|
||||
*
|
||||
* @extends Builder<TModel>
|
||||
*/
|
||||
class BackupQueryBuilder extends Builder
|
||||
{
|
||||
public function nonFailed(): self
|
||||
{
|
||||
$this->where(function (Builder $query) {
|
||||
$query
|
||||
->whereNull('completed_at')
|
||||
->orWhere('is_successful', true);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ContainerStatus: string
|
||||
use Filament\Support\Contracts\HasColor;
|
||||
use Filament\Support\Contracts\HasIcon;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
|
||||
{
|
||||
// Docker Based
|
||||
case Created = 'created';
|
||||
@@ -19,7 +23,7 @@ enum ContainerStatus: string
|
||||
// HTTP Based
|
||||
case Missing = 'missing';
|
||||
|
||||
public function icon(): string
|
||||
public function getIcon(): string
|
||||
{
|
||||
return match ($this) {
|
||||
|
||||
@@ -36,8 +40,17 @@ enum ContainerStatus: string
|
||||
};
|
||||
}
|
||||
|
||||
public function color(): string
|
||||
public function getColor(bool $hex = false): string
|
||||
{
|
||||
if ($hex) {
|
||||
return match ($this) {
|
||||
self::Created, self::Restarting => '#2563EB',
|
||||
self::Starting, self::Paused, self::Removing, self::Stopping => '#D97706',
|
||||
self::Running => '#22C55E',
|
||||
self::Exited, self::Missing, self::Dead, self::Offline => '#EF4444',
|
||||
};
|
||||
}
|
||||
|
||||
return match ($this) {
|
||||
self::Created => 'primary',
|
||||
self::Starting => 'warning',
|
||||
@@ -52,4 +65,50 @@ enum ContainerStatus: string
|
||||
self::Offline => 'gray',
|
||||
};
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return str($this->value)->title();
|
||||
}
|
||||
|
||||
public function isOffline(): bool
|
||||
{
|
||||
return in_array($this, [ContainerStatus::Offline, ContainerStatus::Missing]);
|
||||
}
|
||||
|
||||
public function isStartingOrRunning(): bool
|
||||
{
|
||||
return in_array($this, [ContainerStatus::Starting, ContainerStatus::Running]);
|
||||
}
|
||||
|
||||
public function isStartingOrStopping(): bool
|
||||
{
|
||||
return in_array($this, [ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting]);
|
||||
}
|
||||
|
||||
public function isStartable(): bool
|
||||
{
|
||||
return !in_array($this, [ContainerStatus::Running, ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting]);
|
||||
}
|
||||
|
||||
public function isRestartable(): bool
|
||||
{
|
||||
if ($this->isStartable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !in_array($this, [ContainerStatus::Offline]);
|
||||
}
|
||||
|
||||
public function isStoppable(): bool
|
||||
{
|
||||
return !in_array($this, [ContainerStatus::Starting, ContainerStatus::Stopping, ContainerStatus::Restarting, ContainerStatus::Exited, ContainerStatus::Offline]);
|
||||
}
|
||||
|
||||
public function isKillable(): bool
|
||||
{
|
||||
// [ContainerStatus::Restarting, ContainerStatus::Removing, ContainerStatus::Dead, ContainerStatus::Created]
|
||||
|
||||
return !in_array($this, [ContainerStatus::Offline, ContainerStatus::Running, ContainerStatus::Exited]);
|
||||
}
|
||||
}
|
||||
|
||||
141
app/Enums/EditorLanguages.php
Normal file
141
app/Enums/EditorLanguages.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?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 json = 'json';
|
||||
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';
|
||||
|
||||
public static function fromWithAlias(string $match): self
|
||||
{
|
||||
return match ($match) {
|
||||
'h' => self::c,
|
||||
|
||||
'cc', 'hpp' => self::cpp,
|
||||
|
||||
'cs' => self::csharp,
|
||||
|
||||
'class' => self::java,
|
||||
|
||||
'htm' => self::html,
|
||||
|
||||
'js', 'mjs', 'cjs' => self::javascript,
|
||||
|
||||
'kt', 'kts' => self::kotlin,
|
||||
|
||||
'md' => self::markdown,
|
||||
|
||||
'm' => self::objectivec,
|
||||
|
||||
'pl', 'pm' => self::perl,
|
||||
|
||||
'php3', 'php4', 'php5', 'phtml' => self::php,
|
||||
|
||||
'py', 'pyc', 'pyo', 'pyi' => self::python,
|
||||
|
||||
'rdata', 'rds' => self::r,
|
||||
|
||||
'rb', 'erb' => self::ruby,
|
||||
|
||||
'sc' => self::scala,
|
||||
|
||||
'sh', 'zsh' => self::shell,
|
||||
|
||||
'ts', 'tsx' => self::typescript,
|
||||
|
||||
'yml' => self::yaml,
|
||||
|
||||
default => self::tryFrom($match) ?? self::plaintext,
|
||||
};
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -13,4 +13,5 @@ enum RolePermissionModels: string
|
||||
case Role = 'role';
|
||||
case Server = 'server';
|
||||
case User = 'user';
|
||||
case Webhook = 'webhook';
|
||||
}
|
||||
|
||||
10
app/Enums/ServerResourceType.php
Normal file
10
app/Enums/ServerResourceType.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ServerResourceType
|
||||
{
|
||||
case Unit;
|
||||
case Percentage;
|
||||
case Time;
|
||||
}
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ServerState: string
|
||||
use Filament\Support\Contracts\HasColor;
|
||||
use Filament\Support\Contracts\HasIcon;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum ServerState: string implements HasColor, HasIcon, HasLabel
|
||||
{
|
||||
case Normal = 'normal';
|
||||
case Installing = 'installing';
|
||||
@@ -11,7 +15,7 @@ enum ServerState: string
|
||||
case Suspended = 'suspended';
|
||||
case RestoringBackup = 'restoring_backup';
|
||||
|
||||
public function icon(): string
|
||||
public function getIcon(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Normal => 'tabler-heart',
|
||||
@@ -23,7 +27,7 @@ enum ServerState: string
|
||||
};
|
||||
}
|
||||
|
||||
public function color(): string
|
||||
public function getColor(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Normal => 'primary',
|
||||
@@ -34,4 +38,9 @@ enum ServerState: string
|
||||
self::RestoringBackup => 'primary',
|
||||
};
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return str($this->value)->headline();
|
||||
}
|
||||
}
|
||||
|
||||
9
app/Enums/SuspendAction.php
Normal file
9
app/Enums/SuspendAction.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum SuspendAction: string
|
||||
{
|
||||
case Suspend = 'suspend';
|
||||
case Unsuspend = 'unsuspend';
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
abstract class Event
|
||||
{
|
||||
}
|
||||
abstract class Event {}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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, public bool $successful, public bool $initialInstall) {}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,15 @@
|
||||
namespace App\Events\Server;
|
||||
|
||||
use App\Events\Event;
|
||||
use App\Models\Server;
|
||||
use App\Models\Subuser;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class Saved extends Event
|
||||
class SubUserAdded extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(public Server $server)
|
||||
{
|
||||
}
|
||||
public function __construct(public Subuser $subuser) {}
|
||||
}
|
||||
@@ -4,16 +4,15 @@ namespace App\Events\Server;
|
||||
|
||||
use App\Events\Event;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class Deleting extends Event
|
||||
class SubUserRemoved extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(public Server $server)
|
||||
{
|
||||
}
|
||||
public function __construct(public Server $server, public User $user) {}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
class AccountNotFoundException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
class AutoDeploymentException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -10,9 +10,11 @@ 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;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class DisplayException extends PanelException implements HttpExceptionInterface
|
||||
{
|
||||
public const LEVEL_DEBUG = 'debug';
|
||||
@@ -41,6 +43,9 @@ class DisplayException extends PanelException implements HttpExceptionInterface
|
||||
return Response::HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return [];
|
||||
@@ -67,9 +72,6 @@ class DisplayException extends PanelException implements HttpExceptionInterface
|
||||
return response()->json(Handler::toArray($this), $this->getStatusCode(), $this->getHeaders());
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
app(AlertsMessageBag::class)->danger($this->getMessage())->flash();
|
||||
|
||||
return redirect()->back()->withInput();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\Mailer\Exception\TransportException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
@@ -45,6 +46,8 @@ class Handler extends ExceptionHandler
|
||||
/**
|
||||
* Maps exceptions to a specific response code. This handles special exception
|
||||
* types that don't have a defined response code.
|
||||
*
|
||||
* @var array<class-string, int>
|
||||
*/
|
||||
protected static array $exceptionResponseCodes = [
|
||||
AuthenticationException::class => 401,
|
||||
@@ -180,9 +183,16 @@ class Handler extends ExceptionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exception as a JSONAPI representation for use on API requests.
|
||||
* @param array<string, mixed> $override
|
||||
* @return array{errors: array{
|
||||
* code: string,
|
||||
* status: string,
|
||||
* detail: string,
|
||||
* source?: array{line: int, file: string},
|
||||
* meta?: array{trace: string[], previous: string[]}
|
||||
* }}|array{errors: array{non-empty-array<string, mixed>}}
|
||||
*/
|
||||
protected function convertExceptionToArray(\Throwable $e, array $override = []): array
|
||||
public static function exceptionToArray(Throwable $e, array $override = []): array
|
||||
{
|
||||
$match = self::$exceptionResponseCodes[get_class($e)] ?? null;
|
||||
|
||||
@@ -214,7 +224,7 @@ class Handler extends ExceptionHandler
|
||||
'trace' => Collection::make($e->getTrace())
|
||||
->map(fn ($trace) => Arr::except($trace, ['args']))
|
||||
->all(),
|
||||
'previous' => Collection::make($this->extractPrevious($e))
|
||||
'previous' => Collection::make(self::extractPrevious($e))
|
||||
->map(fn ($exception) => $exception->getTrace())
|
||||
->map(fn ($trace) => Arr::except($trace, ['args']))
|
||||
->all(),
|
||||
@@ -225,6 +235,17 @@ class Handler extends ExceptionHandler
|
||||
return ['errors' => [array_merge($error, $override)]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exception as a JSONAPI representation for use on API requests.
|
||||
*
|
||||
* @param array{detail?: mixed, source?: mixed, meta?: mixed} $override
|
||||
* @return array{errors?: array<mixed>}
|
||||
*/
|
||||
protected function convertExceptionToArray(Throwable $e, array $override = []): array
|
||||
{
|
||||
return self::exceptionToArray($e, $override);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of exceptions that should not be reported.
|
||||
*/
|
||||
@@ -244,22 +265,19 @@ class Handler extends ExceptionHandler
|
||||
return new JsonResponse($this->convertExceptionToArray($exception), JsonResponse::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return redirect()->guest('/auth/login');
|
||||
return redirect()->guest(route('filament.app.auth.login'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all the previous exceptions that lead to the one passed into this
|
||||
* function being thrown.
|
||||
*
|
||||
* @return \Throwable[]
|
||||
* @return Throwable[]
|
||||
*/
|
||||
protected function extractPrevious(\Throwable $e): array
|
||||
public static function extractPrevious(Throwable $e): array
|
||||
{
|
||||
$previous = [];
|
||||
while ($value = $e->getPrevious()) {
|
||||
if (!$value instanceof \Throwable) {
|
||||
break;
|
||||
}
|
||||
$previous[] = $value;
|
||||
$e = $value;
|
||||
}
|
||||
@@ -270,10 +288,11 @@ class Handler extends ExceptionHandler
|
||||
/**
|
||||
* Helper method to allow reaching into the handler to convert an exception
|
||||
* into the expected array response type.
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function toArray(\Throwable $e): array
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return (new self(app()))->convertExceptionToArray($e);
|
||||
return self::exceptionToArray($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace App\Exceptions\Http\Base;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class InvalidPasswordProvidedException extends DisplayException
|
||||
{
|
||||
}
|
||||
class InvalidPasswordProvidedException extends DisplayException {}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Http\Connection;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Every request to the daemon instance will return a unique X-Request-Id header
|
||||
* which allows for all errors to be efficiently tied to a specific request that
|
||||
* triggered them, and gives users a more direct method of informing hosts when
|
||||
* something goes wrong.
|
||||
*/
|
||||
private ?string $requestId;
|
||||
|
||||
/**
|
||||
* Throw a displayable exception caused by a daemon connection error.
|
||||
*/
|
||||
public function __construct(GuzzleException $previous, bool $useStatusCode = true)
|
||||
{
|
||||
/** @var \GuzzleHttp\Psr7\Response|null $response */
|
||||
$response = method_exists($previous, 'getResponse') ? $previous->getResponse() : null;
|
||||
$this->requestId = $response?->getHeaderLine('X-Request-Id');
|
||||
|
||||
Context::add('request_id', $this->requestId);
|
||||
|
||||
if ($useStatusCode) {
|
||||
$this->statusCode = is_null($response) ? $this->statusCode : $response->getStatusCode();
|
||||
// There are rare conditions where daemon encounters a panic condition and crashes the
|
||||
// request being made after content has already been sent over the wire. In these cases
|
||||
// you can end up with a "successful" response code that is actual an error.
|
||||
//
|
||||
// Handle those better here since we shouldn't ever end up in this exception state and
|
||||
// be returning a 2XX level response.
|
||||
if ($this->statusCode < 400) {
|
||||
$this->statusCode = Response::HTTP_BAD_GATEWAY;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($response)) {
|
||||
$message = 'Could not establish a connection to the machine running this server. Please try again.';
|
||||
} else {
|
||||
$message = sprintf('There was an error while communicating with the machine running this server. This error has been logged, please try again. (code: %s) (request_id: %s)', $response->getStatusCode(), $this->requestId ?? '<nil>');
|
||||
}
|
||||
|
||||
// Attempt to pull the actual error message off the response and return that if it is not
|
||||
// a 500 level error.
|
||||
if ($this->statusCode < 500 && !is_null($response)) {
|
||||
$body = json_decode($response->getBody()->__toString(), true);
|
||||
$message = sprintf('An error occurred on the remote host: %s. (request id: %s)', $body['error'] ?? $message, $this->requestId ?? '<nil>');
|
||||
}
|
||||
|
||||
$level = $this->statusCode >= 500 && $this->statusCode !== 504
|
||||
? DisplayException::LEVEL_ERROR
|
||||
: DisplayException::LEVEL_WARNING;
|
||||
|
||||
parent::__construct($message, $previous, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HTTP status code for this exception.
|
||||
*/
|
||||
public function getStatusCode(): int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Http\Server;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class FileTypeNotEditableException extends DisplayException
|
||||
{
|
||||
}
|
||||
@@ -42,6 +42,9 @@ class DataValidationException extends PanelException implements HttpExceptionInt
|
||||
return 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return [];
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
class PanelException extends \Exception
|
||||
{
|
||||
}
|
||||
class PanelException extends \Exception {}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Repository\Daemon;
|
||||
|
||||
use App\Exceptions\Repository\RepositoryException;
|
||||
|
||||
class InvalidPowerSignalException extends RepositoryException
|
||||
{
|
||||
}
|
||||
@@ -4,6 +4,4 @@ namespace App\Exceptions\Repository;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class DuplicateDatabaseNameException extends DisplayException
|
||||
{
|
||||
}
|
||||
class DuplicateDatabaseNameException extends DisplayException {}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Repository;
|
||||
|
||||
use App\Exceptions\PanelException;
|
||||
|
||||
class RepositoryException extends PanelException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Allocation;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class AutoAllocationNotEnabledException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* AutoAllocationNotEnabledException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
'Server auto-allocation is not enabled for this instance.'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Allocation;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class CidrOutOfRangeException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* CidrOutOfRangeException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.cidr_out_of_range'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Allocation;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class InvalidPortMappingException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* InvalidPortMappingException constructor.
|
||||
*/
|
||||
public function __construct(mixed $port)
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.invalid_mapping', ['port' => $port]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Allocation;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class NoAutoAllocationSpaceAvailableException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* NoAutoAllocationSpaceAvailableException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
'Cannot assign additional allocation: no more space available on node.'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Allocation;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class PortOutOfRangeException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* PortOutOfRangeException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.port_out_of_range'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Allocation;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class ServerUsingAllocationException extends DisplayException {}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Allocation;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class TooManyPortsInRangeException extends DisplayException
|
||||
{
|
||||
/**
|
||||
* TooManyPortsInRangeException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(trans('exceptions.allocations.too_many_ports'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Deployment;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class NoViableAllocationException extends DisplayException {}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Egg;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class BadJsonFormatException extends DisplayException
|
||||
{
|
||||
}
|
||||
@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Egg;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class HasChildrenException extends DisplayException
|
||||
{
|
||||
}
|
||||
class HasChildrenException extends DisplayException {}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions\Service\Egg;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class NoParentConfigurationFoundException extends DisplayException
|
||||
{
|
||||
}
|
||||
@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Egg\Variable;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class BadValidationRuleException extends DisplayException
|
||||
{
|
||||
}
|
||||
class BadValidationRuleException extends DisplayException {}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Egg\Variable;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class ReservedVariableNameException extends DisplayException
|
||||
{
|
||||
}
|
||||
class ReservedVariableNameException extends DisplayException {}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace App\Exceptions\Service;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class InvalidFileUploadException extends DisplayException
|
||||
{
|
||||
}
|
||||
class InvalidFileUploadException extends DisplayException {}
|
||||
|
||||
@@ -4,6 +4,4 @@ namespace App\Exceptions\Service\Node;
|
||||
|
||||
use App\Exceptions\DisplayException;
|
||||
|
||||
class ConfigurationNotPersistedException extends DisplayException
|
||||
{
|
||||
}
|
||||
class ConfigurationNotPersistedException extends DisplayException {}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user