Merge branch 'develop' into v2

This commit is contained in:
Matthew Penner 2022-01-17 20:30:46 -07:00
commit b0d6be802c
No known key found for this signature in database
GPG key ID: 31311906AD4CF6D6
14 changed files with 163 additions and 22 deletions

View file

@ -12,7 +12,7 @@ You can provide additional settings using a custom `.env` file or by setting the
## Setup ## Setup
Start the docker container and the required dependencies (either provide existing ones or start containers as well, see the [docker-compose.yml](docker-compose.yml) file as an example). Start the docker container and the required dependencies (either provide existing ones or start containers as well, see the [docker-compose.yml](https://github.com/pterodactyl/panel/blob/develop/docker-compose.example.yml) file as an example.
After the startup is complete you'll need to create a user. After the startup is complete you'll need to create a user.
If you are running the docker container without docker-compose, use: If you are running the docker container without docker-compose, use:

View file

@ -55,6 +55,11 @@ else
rm -rf /etc/nginx/http.d/default.conf rm -rf /etc/nginx/http.d/default.conf
fi fi
if [[ -z $DB_PORT ]]; then
echo -e "DB_PORT not specified, defaulting to 3306"
DB_PORT=3306
fi
## check for DB up before starting the panel ## check for DB up before starting the panel
echo "Checking database status." echo "Checking database status."
until nc -z -v -w30 $DB_HOST $DB_PORT until nc -z -v -w30 $DB_HOST $DB_PORT

View file

@ -5,9 +5,9 @@ Release versions of Pterodactyl will include pre-compiled, minified, and hashed
However, if you are interested in running custom themes or making modifications to the React files you'll need a build However, if you are interested in running custom themes or making modifications to the React files you'll need a build
system in place to generate these compiled assets. To get your environment setup you'll need at minimum: system in place to generate these compiled assets. To get your environment setup you'll need at minimum:
* Node.js 12 * [Node.js](https://nodejs.org/en/) v14.x.x
* [Yarn](https://classic.yarnpkg.com/lang/en/) v1 * [Yarn](https://classic.yarnpkg.com/lang/en/) v1.x.x
* [Go](https://golang.org/) 1.15. * [Go](https://golang.org/) 1.17.x
### Install Dependencies ### Install Dependencies
```bash ```bash

View file

@ -10,7 +10,8 @@
"description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.", "description": "For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community's full potential.",
"features": [ "features": [
"eula", "eula",
"java_version" "java_version",
"pid_limit"
], ],
"images": [ "images": [
"ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_8",

View file

@ -10,7 +10,8 @@
"description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.", "description": "Minecraft Forge Server. Minecraft Forge is a modding API (Application Programming Interface), which makes it easier to create mods, and also make sure mods are compatible with each other.",
"features": [ "features": [
"eula", "eula",
"java_version" "java_version",
"pid_limit"
], ],
"images": [ "images": [
"ghcr.io\/pterodactyl\/yolks:java_17", "ghcr.io\/pterodactyl\/yolks:java_17",

View file

@ -10,7 +10,8 @@
"description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.", "description": "High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.",
"features": [ "features": [
"eula", "eula",
"java_version" "java_version",
"pid_limit"
], ],
"images": [ "images": [
"ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_8",

View file

@ -10,7 +10,8 @@
"description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.", "description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.",
"features": [ "features": [
"eula", "eula",
"java_version" "java_version",
"pid_limit"
], ],
"images": [ "images": [
"ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_8",

View file

@ -10,7 +10,8 @@
"description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.", "description": "Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.",
"features": [ "features": [
"eula", "eula",
"java_version" "java_version",
"pid_limit"
], ],
"images": [ "images": [
"ghcr.io\/pterodactyl\/yolks:java_8", "ghcr.io\/pterodactyl\/yolks:java_8",

View file

@ -70,6 +70,7 @@ services:
QUEUE_DRIVER: "redis" QUEUE_DRIVER: "redis"
REDIS_HOST: "cache" REDIS_HOST: "cache"
DB_HOST: "database" DB_HOST: "database"
DB_PORT: "3306"
networks: networks:
default: default:
ipam: ipam:

View file

@ -40,11 +40,12 @@ export interface SwitchProps {
label?: string; label?: string;
description?: string; description?: string;
defaultChecked?: boolean; defaultChecked?: boolean;
readOnly?: boolean;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
children?: React.ReactNode; children?: React.ReactNode;
} }
const Switch = ({ name, label, description, defaultChecked, onChange, children }: SwitchProps) => { const Switch = ({ name, label, description, defaultChecked, readOnly, onChange, children }: SwitchProps) => {
const uuid = useMemo(() => v4(), []); const uuid = useMemo(() => v4(), []);
return ( return (
@ -57,6 +58,7 @@ const Switch = ({ name, label, description, defaultChecked, onChange, children }
type={'checkbox'} type={'checkbox'}
onChange={e => onChange && onChange(e)} onChange={e => onChange && onChange(e)}
defaultChecked={defaultChecked} defaultChecked={defaultChecked}
disabled={readOnly}
/> />
} }
<Label htmlFor={uuid}/> <Label htmlFor={uuid}/>

View file

@ -7,7 +7,7 @@ import ServerContentBlock from '@/components/elements/ServerContentBlock';
import ServerDetailsBlock from '@/components/server/ServerDetailsBlock'; import ServerDetailsBlock from '@/components/server/ServerDetailsBlock';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
import PowerControls from '@/components/server/PowerControls'; import PowerControls from '@/components/server/PowerControls';
import { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature } from 'feature/index'; import { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature, PIDLimitModalFeature } from 'feature/index';
import ErrorBoundary from '@/components/elements/ErrorBoundary'; import ErrorBoundary from '@/components/elements/ErrorBoundary';
import Spinner from '@/components/elements/Spinner'; import Spinner from '@/components/elements/Spinner';
@ -61,6 +61,7 @@ const ServerConsole = () => {
{eggFeatures.includes('eula') && <EulaModalFeature/>} {eggFeatures.includes('eula') && <EulaModalFeature/>}
{eggFeatures.includes('java_version') && <JavaVersionModalFeature/>} {eggFeatures.includes('java_version') && <JavaVersionModalFeature/>}
{eggFeatures.includes('gsl_token') && <GSLTokenModalFeature/>} {eggFeatures.includes('gsl_token') && <GSLTokenModalFeature/>}
{eggFeatures.includes('pid_limit') && <PIDLimitModalFeature/>}
</React.Suspense> </React.Suspense>
</div> </div>
</ServerContentBlock> </ServerContentBlock>

View file

@ -0,0 +1,86 @@
import React, { useEffect, useState } from 'react';
import { ServerContext } from '@/state/server';
import Modal from '@/components/elements/Modal';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
import FlashMessageRender from '@/components/FlashMessageRender';
import useFlash from '@/plugins/useFlash';
import { SocketEvent } from '@/components/server/events';
import { useStoreState } from 'easy-peasy';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
const PIDLimitModalFeature = () => {
const [ visible, setVisible ] = useState(false);
const [ loading ] = useState(false);
const status = ServerContext.useStoreState(state => state.status.value);
const { clearFlashes } = useFlash();
const { connected, instance } = ServerContext.useStoreState(state => state.socket);
const isAdmin = useStoreState(state => state.user.data!.rootAdmin);
useEffect(() => {
if (!connected || !instance || status === 'running') return;
const errors = [
'pthread_create failed',
'Exception in thread "Craft Async Scheduler Management Thread"',
'unable to create new native thread',
'unable to create native thread',
];
const listener = (line: string) => {
if (errors.some(p => line.toLowerCase().includes(p))) {
setVisible(true);
}
};
instance.addListener(SocketEvent.CONSOLE_OUTPUT, listener);
return () => {
instance.removeListener(SocketEvent.CONSOLE_OUTPUT, listener);
};
}, [ connected, instance, status ]);
useEffect(() => {
clearFlashes('feature:pidLimit');
}, []);
return (
<Modal visible={visible} onDismissed={() => setVisible(false)} closeOnBackground={false} showSpinnerOverlay={loading}>
<FlashMessageRender key={'feature:pidLimit'} css={tw`mb-4`} />
{isAdmin ?
<>
<div css={tw`mt-4 sm:flex items-center`}>
<FontAwesomeIcon css={tw`pr-4`} icon={faExclamationTriangle} color={'orange'} size={'4x'}/>
<h2 css={tw`text-2xl mb-4 text-neutral-100 `}>Memory or process limit reached...</h2>
</div>
<p css={tw`mt-4`}>This server has reached the maximum process or memory limit.</p>
<p css={tw`mt-4`}>Increasing <code css={tw`font-mono bg-neutral-900`}>container_pid_limit</code> in the wings configuration, <code css={tw`font-mono bg-neutral-900`}>config.yml</code>, might help resolve this issue.</p>
<p css={tw`mt-4`}><b>Note: Wings must be restarted for the configuration file changes to take effect</b></p>
<div css={tw`mt-8 sm:flex items-center justify-end`}>
<Button onClick={() => setVisible(false)} css={tw`w-full sm:w-auto border-transparent`}>
Close
</Button>
</div>
</>
:
<>
<div css={tw`mt-4 sm:flex items-center`}>
<FontAwesomeIcon css={tw`pr-4`} icon={faExclamationTriangle} color={'orange'} size={'4x'}/>
<h2 css={tw`text-2xl mb-4 text-neutral-100`}>Possible resource limit reached...</h2>
</div>
<p css={tw`mt-4`}>This server is attempting to use more resources than allocated. Please contact the administrator and give them the error below.</p>
<p css={tw`mt-4`}><code css={tw`font-mono bg-neutral-900`}>pthread_create failed, Possibly out of memory or process/resource limits reached</code></p>
<div css={tw`mt-8 sm:flex items-center justify-end`}>
<Button onClick={() => setVisible(false)} css={tw`w-full sm:w-auto border-transparent`}>
Close
</Button>
</div>
</>
}
</Modal>
);
};
export default PIDLimitModalFeature;

View file

@ -9,5 +9,6 @@ import { lazy } from 'react';
const EulaModalFeature = lazy(() => import(/* webpackChunkName: "feature.eula" */'feature/eula/EulaModalFeature')); const EulaModalFeature = lazy(() => import(/* webpackChunkName: "feature.eula" */'feature/eula/EulaModalFeature'));
const JavaVersionModalFeature = lazy(() => import(/* webpackChunkName: "feature.java_version" */'feature/JavaVersionModalFeature')); const JavaVersionModalFeature = lazy(() => import(/* webpackChunkName: "feature.java_version" */'feature/JavaVersionModalFeature'));
const GSLTokenModalFeature = lazy(() => import(/* webpackChunkName: "feature.gsl_token" */'feature/GSLTokenModalFeature')); const GSLTokenModalFeature = lazy(() => import(/* webpackChunkName: "feature.gsl_token" */'feature/GSLTokenModalFeature'));
const PIDLimitModalFeature = lazy(() => import(/* webpackChunkName: "feature.pid_limit" */'feature/PIDLimitModalFeature'));
export { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature }; export { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature, PIDLimitModalFeature };

View file

@ -4,12 +4,14 @@ import TitledGreyBox from '@/components/elements/TitledGreyBox';
import { usePermissions } from '@/plugins/usePermissions'; import { usePermissions } from '@/plugins/usePermissions';
import InputSpinner from '@/components/elements/InputSpinner'; import InputSpinner from '@/components/elements/InputSpinner';
import Input from '@/components/elements/Input'; import Input from '@/components/elements/Input';
import Switch from '@/components/elements/Switch';
import tw from 'twin.macro'; import tw from 'twin.macro';
import { debounce } from 'debounce'; import { debounce } from 'debounce';
import updateStartupVariable from '@/api/server/updateStartupVariable'; import updateStartupVariable from '@/api/server/updateStartupVariable';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import getServerStartup from '@/api/swr/getServerStartup'; import getServerStartup from '@/api/swr/getServerStartup';
import Select from '@/components/elements/Select';
import isEqual from 'react-fast-compare'; import isEqual from 'react-fast-compare';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
@ -43,6 +45,9 @@ const VariableBox = ({ variable }: Props) => {
.then(() => setLoading(false)); .then(() => setLoading(false));
}, 500); }, 500);
const useSwitch = variable.rules.some(v => v === 'boolean' || v === 'in:0,1');
const selectValues = variable.rules.find(v => v.startsWith('in:'))?.split(',') || [];
return ( return (
<TitledGreyBox <TitledGreyBox
title={ title={
@ -56,17 +61,52 @@ const VariableBox = ({ variable }: Props) => {
> >
<FlashMessageRender byKey={FLASH_KEY} css={tw`mb-2 md:mb-4`}/> <FlashMessageRender byKey={FLASH_KEY} css={tw`mb-2 md:mb-4`}/>
<InputSpinner visible={loading}> <InputSpinner visible={loading}>
<Input {useSwitch ?
onKeyUp={e => { <>
if (canEdit && variable.isEditable) { <Switch
setVariableValue(e.currentTarget.value); readOnly={!canEdit || !variable.isEditable}
name={variable.envVariable}
defaultChecked={variable.serverValue === '1'}
onChange={() => {
if (canEdit && variable.isEditable) {
setVariableValue(variable.serverValue === '1' ? '0' : '1');
}
}}
/>
</>
:
<>
{selectValues.length > 0 ?
<>
<Select
onChange={e => setVariableValue(e.target.value)}
name={variable.envVariable}
defaultValue={variable.serverValue}
disabled={!canEdit || !variable.isEditable}
>
{selectValues.map(selectValue => (
<option key={selectValue.replace('in:', '')} value={selectValue.replace('in:', '')}>{selectValue.replace('in:', '')}</option>
))}
</Select>
</>
:
<>
<Input
onKeyUp={e => {
if (canEdit && variable.isEditable) {
setVariableValue(e.currentTarget.value);
}
}}
readOnly={!canEdit || !variable.isEditable}
name={variable.envVariable}
defaultValue={variable.serverValue}
placeholder={variable.defaultValue}
/>
</>
} }
}} </>
readOnly={!canEdit || !variable.isEditable} }
name={variable.envVariable}
defaultValue={variable.serverValue}
placeholder={variable.defaultValue}
/>
</InputSpinner> </InputSpinner>
<p css={tw`mt-1 text-xs text-neutral-300`}> <p css={tw`mt-1 text-xs text-neutral-300`}>
{variable.description} {variable.description}