Compare commits

...

8 Commits

Author SHA1 Message Date
Matthew Penner
6dc85c731e chore: update SECURITY.md
ref; https://github.com/pterodactyl/panel/pull/5074 which targets the
wrong branch
2024-04-16 15:17:36 -06:00
Matthew Penner
0dad4c5a48 ui(admin): better handling of manual HTML rendering 2024-04-11 10:47:00 -06:00
Matthew Penner
b1fa3927c1 api(remote): fix oops in BackupStatusController 2024-04-11 10:42:18 -06:00
Matthew Penner
f671046947 admin: tweaks to validation and rendering 2024-04-10 18:13:25 -06:00
Matthew Penner
319ca683f8 api(remote): ensure requesting node is checked 2024-04-10 17:38:09 -06:00
Matthew Penner
1172d71d31 app: improve docker_image validation 2024-04-10 17:22:29 -06:00
Matthew Penner
2497819ca7 Update README.md 2024-03-16 14:02:07 -06:00
Matthew Penner
787bf34a59 nix: update to php 8.2 2024-03-16 14:01:51 -06:00
18 changed files with 137 additions and 222 deletions

View File

@@ -27,12 +27,14 @@ Stop settling for less. Make game servers a first class citizen on your platform
I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's development.
[Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi)
| Company | About |
|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. |
| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! |
| [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa. |
| Company | About |
|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. |
| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! |
| [**VibeGAMES**](https://vibegames.net/) | VibeGAMES is a game server provider that specializes in DDOS protection for the games we offer. We have multiple locations in the US, Brazil, France, Germany, Singapore, Australia and South Africa. |
| [**HostEZ**](https://hostez.io) | US & EU Rust & Minecraft Hosting. DDoS Protected bare metal, VPS and colocation with low latency, high uptime and maximum availability. EZ! |
| [**Blueprint**](https://blueprint.zip/?pterodactyl=true) | Create and install Pterodactyl addons and themes with the growing Blueprint framework - the package-manager for Pterodactyl. Use multiple modifications at once without worrying about conflicts and make use of the large extension ecosystem. |
### Supported Games

View File

@@ -6,7 +6,6 @@ The following versions of Pterodactyl are receiving active support and maintenan
| Panel | Daemon | Supported |
|--------|--------------|--------------------|
| 1.10.x | wings@1.7.x | :white_check_mark: |
| 1.11.x | wings@1.11.x | :white_check_mark: |
| 0.7.x | daemon@0.6.x | :x: |

View File

@@ -69,7 +69,7 @@ class EggVariableController extends Controller
{
$this->updateService->handle($variable, $request->normalize());
$this->alert->success(trans('admin/nests.variables.notices.variable_updated', [
'variable' => $variable->name,
'variable' => htmlspecialchars($variable->name),
]))->flash();
return redirect()->route('admin.nests.egg.variables', $egg->id);
@@ -82,7 +82,7 @@ class EggVariableController extends Controller
{
$this->variableRepository->delete($variable->id);
$this->alert->success(trans('admin/nests.variables.notices.variable_deleted', [
'variable' => $variable->name,
'variable' => htmlspecialchars($variable->name),
]))->flash();
return redirect()->route('admin.nests.egg.variables', $egg);

View File

@@ -56,7 +56,7 @@ class NestController extends Controller
public function store(StoreNestFormRequest $request): RedirectResponse
{
$nest = $this->nestCreationService->handle($request->normalize());
$this->alert->success(trans('admin/nests.notices.created', ['name' => $nest->name]))->flash();
$this->alert->success(trans('admin/nests.notices.created', ['name' => htmlspecialchars($nest->name)]))->flash();
return redirect()->route('admin.nests.view', $nest->id);
}

View File

@@ -131,7 +131,7 @@ class NodesController extends Controller
['ip', '=', $request->input('ip')],
]);
$this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => $request->input('ip')]))
$this->alert->success(trans('admin/node.notices.unallocated_deleted', ['ip' => htmlspecialchars($request->input('ip'))]))
->flash();
return redirect()->route('admin.nodes.view.allocation', $node);

View File

@@ -32,6 +32,10 @@ class BackupRemoteUploadController extends Controller
*/
public function __invoke(Request $request, string $backup): JsonResponse
{
// Get the node associated with the request.
/** @var \Pterodactyl\Models\Node $node */
$node = $request->attributes->get('node');
// Get the size query parameter.
$size = (int) $request->query('size');
if (empty($size)) {
@@ -39,7 +43,10 @@ class BackupRemoteUploadController extends Controller
}
/** @var \Pterodactyl\Models\Backup $backup */
$backup = Backup::query()->where('uuid', $backup)->firstOrFail();
$backup = Backup::query()
->where('node_id', $node->id)
->where('uuid', $backup)
->firstOrFail();
// Prevent backups that have already been completed from trying to
// be uploaded again.

View File

@@ -30,8 +30,15 @@ class BackupStatusController extends Controller
*/
public function index(ReportBackupCompleteRequest $request, string $backup): JsonResponse
{
// Get the node associated with the request.
/** @var \Pterodactyl\Models\Node $node */
$node = $request->attributes->get('node');
/** @var \Pterodactyl\Models\Backup $model */
$model = Backup::query()->where('uuid', $backup)->firstOrFail();
$model = Backup::query()
->where('node_id', $node->id)
->where('uuid', $backup)
->firstOrFail();
if ($model->is_successful) {
throw new BadRequestHttpException('Cannot update the status of a backup that is already marked as completed.');

View File

@@ -11,7 +11,7 @@ class EggFormRequest extends AdminFormRequest
$rules = [
'name' => 'required|string|max:191',
'description' => 'nullable|string',
'docker_images' => 'required|string',
'docker_images' => ['required', 'string', 'regex:/^[\w#\.\/\- ]*\|*[\w\.\/\-:@ ]*$/im'],
'force_outgoing_ip' => 'sometimes|boolean',
'file_denylist' => 'array',
'startup' => 'required|string',

View File

@@ -9,7 +9,7 @@ class StoreNestFormRequest extends AdminFormRequest
public function rules(): array
{
return [
'name' => 'required|string|min:1|max:191',
'name' => 'required|string|min:1|max:191|regex:/^[\w\- ]+$/',
'description' => 'string|nullable',
];
}

View File

@@ -24,7 +24,7 @@ class SetDockerImageRequest extends ClientApiRequest implements ClientPermission
Assert::isInstanceOf($server, Server::class);
return [
'docker_image' => ['required', 'string', Rule::in(array_values($server->egg->docker_images))],
'docker_image' => ['required', 'string', 'max:191', 'regex:/^[\w#\.\/\- ]*\|*[\w\.\/\-:@ ]*$/', Rule::in(array_values($server->egg->docker_images))],
];
}
}

View File

@@ -123,7 +123,7 @@ class Egg extends Model
'file_denylist' => 'array|nullable',
'file_denylist.*' => 'string',
'docker_images' => 'required|array|min:1',
'docker_images.*' => 'required|string',
'docker_images.*' => ['required', 'string', 'max:191', 'regex:/^[\w#\.\/\- ]*\|*[\w\.\/\-:@ ]*$/'],
'startup' => 'required|nullable|string',
'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id',
'config_stop' => 'required_without:config_from|nullable|string|max:191',

View File

@@ -163,7 +163,7 @@ class Server extends Model
'egg_id' => 'required|exists:eggs,id',
'startup' => 'required|string',
'skip_scripts' => 'sometimes|boolean',
'image' => 'required|string|max:191',
'image' => ['required', 'string', 'max:191', 'regex:/^[\w\.\/\-:@ ]*$/'],
'database_limit' => 'present|nullable|integer|min:0',
'allocation_limit' => 'sometimes|nullable|integer|min:0',
'backup_limit' => 'present|nullable|integer|min:0',

243
flake.lock generated
View File

@@ -1,38 +1,19 @@
{
"nodes": {
"devshell": {
"flake": false,
"locked": {
"lastModified": 1663445644,
"narHash": "sha256-+xVlcK60x7VY1vRJbNUEAHi17ZuoQxAIH4S4iUFUGBA=",
"owner": "numtide",
"repo": "devshell",
"rev": "e3dc3e21594fe07bdb24bdf1c8657acaa4cb8f66",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"dream2nix": {
"inputs": {
"devshell": "devshell",
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"nix-unit": "nix-unit",
"nixpkgs": [
"nixpkgs"
],
"pre-commit-hooks": "pre-commit-hooks"
"purescript-overlay": "purescript-overlay",
"pyproject-nix": "pyproject-nix"
},
"locked": {
"lastModified": 1695717405,
"narHash": "sha256-MvHrU3h0Bw57s2p+wCUnSZliR4wvvPi3xkW+MRWB5HU=",
"lastModified": 1710268378,
"narHash": "sha256-O61PtxUHbmwI6Ltjn9jY3QY2hRPPz7pe3BHhIYK4QkU=",
"owner": "nix-community",
"repo": "dream2nix",
"rev": "6dbd59e4a47bd916a655c4425a3e730c6aeae033",
"rev": "cd782df677aad08f7193c97376d615943c3cd4c9",
"type": "github"
},
"original": {
@@ -41,71 +22,16 @@
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"dream2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1675933616,
"narHash": "sha256-/rczJkJHtx16IFxMmAWu5nNYcSXNg1YYXTHoGjLrLUA=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "47478a4a003e745402acf63be7f9a092d51b83d7",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@@ -139,62 +65,13 @@
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"dream2nix",
"nix-unit",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix-unit": {
"inputs": {
"flake-parts": [
"dream2nix",
"flake-parts"
],
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"dream2nix",
"nixpkgs"
],
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1690289081,
"narHash": "sha256-PCXQAQt8+i2pkUym9P1JY4JGoeZJLzzxWBhprHDdItM=",
"owner": "adisbladis",
"repo": "nix-unit",
"rev": "a9d6f33e50d4dcd9cfc0c92253340437bbae282b",
"type": "github"
},
"original": {
"owner": "adisbladis",
"repo": "nix-unit",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1695644571,
"narHash": "sha256-asS9dCCdlt1lPq0DLwkVBbVoEKuEuz+Zi3DG7pR/RxA=",
"lastModified": 1709961763,
"narHash": "sha256-6H95HGJHhEZtyYA3rIQpvamMKAGoa8Yh2rFV29QnuGw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6500b4580c2a1f3d0f980d32d285739d8e156d92",
"rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34",
"type": "github"
},
"original": {
@@ -238,36 +115,75 @@
"type": "github"
}
},
"pre-commit-hooks": {
"purescript-overlay": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"dream2nix",
"nixpkgs"
]
],
"slimlock": "slimlock"
},
"locked": {
"lastModified": 1646153636,
"narHash": "sha256-AlWHMzK+xJ1mG267FdT8dCq/HvLCA6jwmx2ZUy5O8tY=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "b6bc0b21e1617e2b07d8205e7fae7224036dfa4b",
"lastModified": 1696022621,
"narHash": "sha256-eMjFmsj2G1E0Q5XiibUNgFjTiSz0GxIeSSzzVdoN730=",
"owner": "thomashoneyman",
"repo": "purescript-overlay",
"rev": "047c7933abd6da8aa239904422e22d190ce55ead",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"owner": "thomashoneyman",
"repo": "purescript-overlay",
"type": "github"
}
},
"pyproject-nix": {
"flake": false,
"locked": {
"lastModified": 1702448246,
"narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=",
"owner": "davhau",
"repo": "pyproject.nix",
"rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb",
"type": "github"
},
"original": {
"owner": "davhau",
"ref": "dream2nix",
"repo": "pyproject.nix",
"type": "github"
}
},
"root": {
"inputs": {
"dream2nix": "dream2nix",
"flake-utils": "flake-utils_2",
"flake-utils": "flake-utils",
"mk-node-package": "mk-node-package",
"nixpkgs": "nixpkgs"
}
},
"slimlock": {
"inputs": {
"nixpkgs": [
"dream2nix",
"purescript-overlay",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688610262,
"narHash": "sha256-Wg0ViDotFWGWqKIQzyYCgayeH8s4U1OZcTiWTQYdAp4=",
"owner": "thomashoneyman",
"repo": "slimlock",
"rev": "b5c6cdcaf636ebbebd0a1f32520929394493f1a6",
"type": "github"
},
"original": {
"owner": "thomashoneyman",
"repo": "slimlock",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
@@ -282,43 +198,6 @@
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"dream2nix",
"nix-unit",
"nixpkgs"
]
},
"locked": {
"lastModified": 1689620039,
"narHash": "sha256-BtNwghr05z7k5YMdq+6nbue+nEalvDepuA7qdQMAKoQ=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "719c2977f958c41fa60a928e2fbc50af14844114",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",

View File

@@ -39,7 +39,10 @@
pkgs = import nixpkgs {inherit system;};
mkNodePackage = mk-node-package.lib."${system}".mkNodePackage;
php81WithExtensions = with pkgs; (php81.buildEnv {
php = pkgs.php; # PHP 8.2
phpPackages = pkgs.phpPackages; # PHP 8.2
phpWithExtensions = php.buildEnv {
extensions = {
enabled,
all,
@@ -52,8 +55,8 @@
extraConfig = ''
xdebug.mode=debug
'';
});
composer = with pkgs; (php81Packages.composer.override {php = php81WithExtensions;});
};
composer = phpPackages.composer.override {php = phpWithExtensions;};
caCertificates = pkgs.runCommand "ca-certificates" {} ''
mkdir -p $out/etc/ssl/certs $out/etc/pki/tls/certs
@@ -201,7 +204,7 @@
};
in {
defaultPackage = panel;
devShell = import ./shell.nix {inherit composer php81WithExtensions pkgs;};
devShell = import ./shell.nix {inherit composer phpWithExtensions pkgs;};
packages = {
inherit panel;
@@ -224,7 +227,7 @@
mysql80
nodejs_18
nodePackages.yarn
php81WithExtensions
phpWithExtensions
];
pathsToLink = ["/bin" "/etc"];
};
@@ -242,7 +245,7 @@
caCertificates
caddy
configs
php81WithExtensions
phpWithExtensions
panel
];

View File

@@ -88,7 +88,7 @@ $('#pEggId').on('change', function (event) {
for (let i = 0; i < keys.length; i++) {
let opt = document.createElement('option');
opt.value = images[keys[i]];
opt.innerHTML = keys[i] + " (" + images[keys[i]] + ")";
opt.innerText = keys[i] + " (" + images[keys[i]] + ")";
$('#pDefaultContainer').append(opt);
}
@@ -109,6 +109,12 @@ $('#pEggId').on('change', function (event) {
),
});
function escapeHtml(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
const variableIds = {};
$('#appendVariablesTo').html('');
$.each(_.get(objectChain, 'variables', []), function (i, item) {
@@ -117,11 +123,11 @@ $('#pEggId').on('change', function (event) {
let isRequired = (item.required === 1) ? '<span class="label label-danger">Required</span> ' : '';
let dataAppend = ' \
<div class="form-group col-sm-6"> \
<label for="var_ref_' + item.id + '" class="control-label">' + isRequired + item.name + '</label> \
<input type="text" id="var_ref_' + item.id + '" autocomplete="off" name="environment[' + item.env_variable + ']" class="form-control" value="' + item.default_value + '" /> \
<p class="text-muted small">' + item.description + '<br /> \
<strong>Access in Startup:</strong> <code>{{' + item.env_variable + '}}</code><br /> \
<strong>Validation Rules:</strong> <code>' + item.rules + '</code></small></p> \
<label for="var_ref_' + escapeHtml(item.id) + '" class="control-label">' + isRequired + escapeHtml(item.name) + '</label> \
<input type="text" id="var_ref_' + escapeHtml(item.id) + '" autocomplete="off" name="environment[' + escapeHtml(item.env_variable) + ']" class="form-control" value="' + escapeHtml(item.default_value) + '" /> \
<p class="text-muted small">' + escapeHtml(item.description) + '<br /> \
<strong>Access in Startup:</strong> <code>{{' + escapeHtml(item.env_variable) + '}}</code><br /> \
<strong>Validation Rules:</strong> <code>' + escapeHtml(item.rules) + '</code></small></p> \
</div> \
';
$('#appendVariablesTo').append(dataAppend);

View File

@@ -145,14 +145,20 @@
@section('footer-scripts')
@parent
<script>
function escapeHtml(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
(function getInformation() {
$.ajax({
method: 'GET',
url: '/admin/nodes/view/{{ $node->id }}/system-information',
timeout: 5000,
}).done(function (data) {
$('[data-attr="info-version"]').html(data.version);
$('[data-attr="info-system"]').html(data.system.type + ' (' + data.system.arch + ') <code>' + data.system.release + '</code>');
$('[data-attr="info-version"]').html(escapeHtml(data.version));
$('[data-attr="info-system"]').html(escapeHtml(data.system.type) + ' (' + escapeHtml(data.system.arch) + ') <code>' + escapeHtml(data.system.release) + '</code>');
$('[data-attr="info-cpus"]').html(data.system.cpus);
}).fail(function (jqXHR) {

View File

@@ -107,6 +107,12 @@
@parent
{!! Theme::js('vendor/lodash/lodash.js') !!}
<script>
function escapeHtml(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
$(document).ready(function () {
$('#pEggId').select2({placeholder: 'Select a Nest Egg'}).on('change', function () {
var selectedEgg = _.isNull($(this).val()) ? $(this).find('option').first().val() : $(this).val();
@@ -119,7 +125,7 @@
for (let i = 0; i < keys.length; i++) {
let opt = document.createElement('option');
opt.value = images[keys[i]];
opt.innerHTML = keys[i] + " (" + images[keys[i]] + ")";
opt.innerText = keys[i] + " (" + images[keys[i]] + ")";
if (objectChain.id === parseInt(Pterodactyl.server.egg_id) && Pterodactyl.server.image == opt.value) {
opt.selected = true
}
@@ -149,15 +155,15 @@
<div class="col-xs-12"> \
<div class="box"> \
<div class="box-header with-border"> \
<h3 class="box-title">' + isRequired + item.name + '</h3> \
<h3 class="box-title">' + isRequired + escapeHtml(item.name) + '</h3> \
</div> \
<div class="box-body"> \
<input name="environment[' + item.env_variable + ']" class="form-control" type="text" id="egg_variable_' + item.env_variable + '" /> \
<p class="no-margin small text-muted">' + item.description + '</p> \
<input name="environment[' + escapeHtml(item.env_variable) + ']" class="form-control" type="text" id="egg_variable_' + escapeHtml(item.env_variable) + '" /> \
<p class="no-margin small text-muted">' + escapeHtml(item.description) + '</p> \
</div> \
<div class="box-footer"> \
<p class="no-margin text-muted small"><strong>Startup Command Variable:</strong> <code>' + item.env_variable + '</code></p> \
<p class="no-margin text-muted small"><strong>Input Rules:</strong> <code>' + item.rules + '</code></p> \
<p class="no-margin text-muted small"><strong>Startup Command Variable:</strong> <code>' + escapeHtml(item.env_variable) + '</code></p> \
<p class="no-margin text-muted small"><strong>Input Rules:</strong> <code>' + escapeHtml(item.rules) + '</code></p> \
</div> \
</div> \
</div>';

View File

@@ -1,6 +1,6 @@
{
composer ? null,
php81WithExtensions ? null,
phpWithExtensions ? null,
pkgs ? import <nixpkgs> {},
}:
with pkgs;
@@ -10,7 +10,7 @@ with pkgs;
composer
nodejs_18
nodePackages.yarn
php81WithExtensions
phpWithExtensions
];
shellHook = ''