Merge branch 'develop' into feature/react-admin

This commit is contained in:
Matthew Penner 2021-01-03 18:18:06 -07:00
commit 5636c25838
14 changed files with 90 additions and 108 deletions

View file

@ -57,7 +57,7 @@ class SendPasswordReset extends Notification implements ShouldQueue
return (new MailMessage) return (new MailMessage)
->subject('Reset Password') ->subject('Reset Password')
->line('You are receiving this email because we received a password reset request for your account.') ->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url('/auth/password/reset/' . $this->token . '?email=' . $notifiable->email)) ->action('Reset Password', url('/auth/password/reset/' . $this->token . '?email=' . urlencode($notifiable->email)))
->line('If you did not request a password reset, no further action is required.'); ->line('If you did not request a password reset, no further action is required.');
} }
} }

View file

@ -8,7 +8,7 @@
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"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": ["eula"], "features": ["eula"],
"image": "quay.io\/pterodactyl\/core:java", "images": ["quay.io\/pterodactyl\/core:java", "quay.io\/pterodactyl\/core:java-11"],
"startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}",
"config": { "config": {
"files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_enabled\": true,\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}", "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_enabled\": true,\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}",

View file

@ -8,7 +8,7 @@
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"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": ["eula"], "features": ["eula"],
"image": "quay.io\/pterodactyl\/core:java", "images": ["quay.io\/pterodactyl\/core:java", "quay.io\/pterodactyl\/core:java-11"],
"startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}",
"config": { "config": {
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",

View file

@ -8,7 +8,7 @@
"author": "parker@pterodactyl.io", "author": "parker@pterodactyl.io",
"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": ["eula"], "features": ["eula"],
"image": "quay.io\/pterodactyl\/core:java-11", "images": ["quay.io\/pterodactyl\/core:java-11", "quay.io\/pterodactyl\/core:java"],
"startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}", "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}",
"config": { "config": {
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",

View file

@ -8,7 +8,7 @@
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.", "description": "SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.",
"features": ["eula"], "features": ["eula"],
"image": "quay.io\/pterodactyl\/core:java-glibc", "images": ["quay.io\/pterodactyl\/core:java-glibc"],
"startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}",
"config": { "config": {
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",

View file

@ -8,7 +8,7 @@
"author": "support@pterodactyl.io", "author": "support@pterodactyl.io",
"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": ["eula"], "features": ["eula"],
"image": "quay.io\/pterodactyl\/core:java", "images": ["quay.io\/pterodactyl\/core:java", "quay.io\/pterodactyl\/core:java-11"],
"startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}", "startup": "java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}",
"config": { "config": {
"files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"enable-query\": \"true\",\r\n \"server-port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}",

View file

@ -1,10 +1,9 @@
import React, { useRef } from 'react'; import React from 'react';
import { Route } from 'react-router'; import { Route } from 'react-router';
import { SwitchTransition } from 'react-transition-group'; import { SwitchTransition } from 'react-transition-group';
import Fade from '@/components/elements/Fade'; import Fade from '@/components/elements/Fade';
import styled from 'styled-components/macro'; import styled from 'styled-components/macro';
import tw from 'twin.macro'; import tw from 'twin.macro';
import v4 from 'uuid/v4';
const StyledSwitchTransition = styled(SwitchTransition)` const StyledSwitchTransition = styled(SwitchTransition)`
${tw`relative`}; ${tw`relative`};
@ -15,13 +14,11 @@ const StyledSwitchTransition = styled(SwitchTransition)`
`; `;
const TransitionRouter: React.FC = ({ children }) => { const TransitionRouter: React.FC = ({ children }) => {
const uuid = useRef(v4()).current;
return ( return (
<Route <Route
render={({ location }) => ( render={({ location }) => (
<StyledSwitchTransition> <StyledSwitchTransition>
<Fade timeout={150} key={location.key || uuid} in appear unmountOnExit> <Fade timeout={150} key={location.pathname + location.search} in appear unmountOnExit>
<section> <section>
{children} {children}
</section> </section>

View file

@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import getServerSchedules from '@/api/server/schedules/getServerSchedules'; import getServerSchedules from '@/api/server/schedules/getServerSchedules';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import Spinner from '@/components/elements/Spinner'; import Spinner from '@/components/elements/Spinner';
import { RouteComponentProps } from 'react-router-dom'; import { useHistory, useRouteMatch } from 'react-router-dom';
import FlashMessageRender from '@/components/FlashMessageRender'; import FlashMessageRender from '@/components/FlashMessageRender';
import ScheduleRow from '@/components/server/schedules/ScheduleRow'; import ScheduleRow from '@/components/server/schedules/ScheduleRow';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
@ -14,7 +14,10 @@ import GreyRowBox from '@/components/elements/GreyRowBox';
import Button from '@/components/elements/Button'; import Button from '@/components/elements/Button';
import ServerContentBlock from '@/components/elements/ServerContentBlock'; import ServerContentBlock from '@/components/elements/ServerContentBlock';
export default ({ match, history }: RouteComponentProps) => { export default () => {
const match = useRouteMatch();
const history = useHistory();
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
const { clearFlashes, addError } = useFlash(); const { clearFlashes, addError } = useFlash();
const [ loading, setLoading ] = useState(true); const [ loading, setLoading ] = useState(true);

View file

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom'; import { useHistory, useLocation, useParams } from 'react-router-dom';
import { Schedule } from '@/api/server/schedules/getServerSchedules'; import { Schedule } from '@/api/server/schedules/getServerSchedules';
import getServerSchedule from '@/api/server/schedules/getServerSchedule'; import getServerSchedule from '@/api/server/schedules/getServerSchedule';
import Spinner from '@/components/elements/Spinner'; import Spinner from '@/components/elements/Spinner';
@ -45,7 +45,11 @@ const ActivePill = ({ active }: { active: boolean }) => (
</span> </span>
); );
export default ({ match, history, location: { state } }: RouteComponentProps<Params, Record<string, unknown>, State>) => { export default () => {
const params = useParams() as Params;
const history = useHistory();
const state: State = useLocation().state;
const id = ServerContext.useStoreState(state => state.server.data!.id); const id = ServerContext.useStoreState(state => state.server.data!.id);
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
@ -57,20 +61,20 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule); const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
useEffect(() => { useEffect(() => {
if (schedule?.id === Number(match.params.id)) { if (schedule?.id === Number(params.id)) {
setIsLoading(false); setIsLoading(false);
return; return;
} }
clearFlashes('schedules'); clearFlashes('schedules');
getServerSchedule(uuid, Number(match.params.id)) getServerSchedule(uuid, Number(params.id))
.then(schedule => appendSchedule(schedule)) .then(schedule => appendSchedule(schedule))
.catch(error => { .catch(error => {
console.error(error); console.error(error);
clearAndAddHttpError({ error, key: 'schedules' }); clearAndAddHttpError({ error, key: 'schedules' });
}) })
.then(() => setIsLoading(false)); .then(() => setIsLoading(false));
}, [ match ]); }, [ params ]);
const toggleEditModal = useCallback(() => { const toggleEditModal = useCallback(() => {
setShowEditModal(s => !s); setShowEditModal(s => !s);

View file

@ -48,7 +48,7 @@ export default () => {
} }
return ( return (
<ServerContentBlock title={'Subusers'}> <ServerContentBlock title={'Users'}>
<FlashMessageRender byKey={'users'} css={tw`mb-4`}/> <FlashMessageRender byKey={'users'} css={tw`mb-4`}/>
{!subusers.length ? {!subusers.length ?
<p css={tw`text-center text-sm text-neutral-300`}> <p css={tw`text-center text-sm text-neutral-300`}>

View file

@ -0,0 +1,25 @@
import React from 'react';
import Can from '@/components/elements/Can';
import ScreenBlock from '@/components/screens/ScreenBlock';
export interface RequireServerPermissionProps {
permissions: string | string[]
}
const RequireServerPermission: React.FC<RequireServerPermissionProps> = ({ children, permissions }) => {
return (
<Can
action={permissions}
renderOnError={
<ScreenBlock
image={'/assets/svgs/server_error.svg'}
title={'Access Denied'}
message={'You do not have permission to access this page.'}
/>
}
>
{children}
</Can>
);
};
export default RequireServerPermission;

View file

@ -1,31 +0,0 @@
import React from 'react';
import Can from '@/components/elements/Can';
import ScreenBlock from '@/components/screens/ScreenBlock';
import isEqual from 'react-fast-compare';
const requireServerPermission = (Component: React.ComponentType<any>, permissions: string | string[]) => {
return class extends React.Component<any, any> {
shouldComponentUpdate (nextProps: Readonly<any>) {
return !isEqual(nextProps, this.props);
}
render () {
return (
<Can
action={permissions}
renderOnError={
<ScreenBlock
image={'/assets/svgs/server_error.svg'}
title={'Access Denied'}
message={'You do not have permission to access this page.'}
/>
}
>
<Component {...this.props}/>
</Can>
);
}
};
};
export default requireServerPermission;

View file

@ -27,10 +27,10 @@ import SubNavigation from '@/components/elements/SubNavigation';
import NetworkContainer from '@/components/server/network/NetworkContainer'; import NetworkContainer from '@/components/server/network/NetworkContainer';
import InstallListener from '@/components/server/InstallListener'; import InstallListener from '@/components/server/InstallListener';
import StartupContainer from '@/components/server/startup/StartupContainer'; import StartupContainer from '@/components/server/startup/StartupContainer';
import requireServerPermission from '@/hoc/requireServerPermission';
import ErrorBoundary from '@/components/elements/ErrorBoundary'; import ErrorBoundary from '@/components/elements/ErrorBoundary';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
import RequireServerPermission from '@/hoc/RequireServerPermission';
const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => { const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => {
const rootAdmin = useStoreState(state => state.user.data!.rootAdmin); const rootAdmin = useStoreState(state => state.user.data!.rootAdmin);
@ -142,50 +142,44 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
<TransitionRouter> <TransitionRouter>
<Switch location={location}> <Switch location={location}>
<Route path={`${match.path}`} component={ServerConsole} exact/> <Route path={`${match.path}`} component={ServerConsole} exact/>
<Route <Route path={`${match.path}/files`} exact>
path={`${match.path}/files`} <RequireServerPermission permissions={'file.*'}>
component={requireServerPermission(FileManagerContainer, 'file.*')} <FileManagerContainer />
exact </RequireServerPermission>
/> </Route>
<Route <Route path={`${match.path}/files/:action(edit|new)`} exact>
path={`${match.path}/files/:action(edit|new)`}
render={props => (
<SuspenseSpinner> <SuspenseSpinner>
<FileEditContainer {...props as any}/> <FileEditContainer />
</SuspenseSpinner> </SuspenseSpinner>
)} </Route>
exact <Route path={`${match.path}/databases`} exact>
/> <RequireServerPermission permissions={'database.*'}>
<Route <DatabasesContainer />
path={`${match.path}/databases`} </RequireServerPermission>
component={requireServerPermission(DatabasesContainer, 'database.*')} </Route>
exact <Route path={`${match.path}/schedules`} exact>
/> <RequireServerPermission permissions={'schedule.*'}>
<Route <ScheduleContainer />
path={`${match.path}/schedules`} </RequireServerPermission>
component={requireServerPermission(ScheduleContainer, 'schedule.*')} </Route>
exact <Route path={`${match.path}/schedules/:id`} exact>
/> <ScheduleEditContainer/>
<Route </Route>
path={`${match.path}/schedules/:id`} <Route path={`${match.path}/users`} exact>
component={ScheduleEditContainer} <RequireServerPermission permissions={'user.*'}>
exact <UsersContainer />
/> </RequireServerPermission>
<Route </Route>
path={`${match.path}/users`} <Route path={`${match.path}/backups`} exact>
component={requireServerPermission(UsersContainer, 'user.*')} <RequireServerPermission permissions={'backup.*'}>
exact <BackupContainer />
/> </RequireServerPermission>
<Route </Route>
path={`${match.path}/backups`} <Route path={`${match.path}/network`} exact>
component={requireServerPermission(BackupContainer, 'backup.*')} <RequireServerPermission permissions={'allocation.*'}>
exact <NetworkContainer />
/> </RequireServerPermission>
<Route </Route>
path={`${match.path}/network`}
component={requireServerPermission(NetworkContainer, 'allocation.*')}
exact
/>
<Route path={`${match.path}/startup`} component={StartupContainer} exact/> <Route path={`${match.path}/startup`} component={StartupContainer} exact/>
<Route path={`${match.path}/settings`} component={SettingsContainer} exact/> <Route path={`${match.path}/settings`} component={SettingsContainer} exact/>
<Route path={'*'} component={NotFound}/> <Route path={'*'} component={NotFound}/>

View file

@ -2166,19 +2166,9 @@ camelize@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
caniuse-lite@^1.0.30001088: caniuse-lite@^1.0.30001088, caniuse-lite@^1.0.30001135, caniuse-lite@^1.0.30001165:
version "1.0.30001093"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001093.tgz#833e80f64b1a0455cbceed2a4a3baf19e4abd312"
integrity sha512-0+ODNoOjtWD5eS9aaIpf4K0gQqZfILNY4WSNuYzeT1sXni+lMrrVjc0odEobJt6wrODofDZUX8XYi/5y7+xl8g==
caniuse-lite@^1.0.30001135:
version "1.0.30001148"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz#dc97c7ed918ab33bf8706ddd5e387287e015d637"
integrity sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==
caniuse-lite@^1.0.30001165:
version "1.0.30001170" version "1.0.30001170"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz#0088bfecc6a14694969e391cc29d7eb6362ca6a7" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz"
integrity sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA== integrity sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA==
chalk@^2.0, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: chalk@^2.0, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: