This commit is contained in:
Jan Naahs
2020-06-20 23:31:05 +02:00
parent 07123eb03a
commit 774c40bfa5
13 changed files with 287 additions and 62 deletions

View File

@ -8,6 +8,9 @@ import {BrowserRouter} from "react-router-dom";
import Logs from "./views/Logs";
import Saves from "./views/Saves";
import Layout from "./components/Layout";
import server from "../api/resources/server";
import Mods from "./views/Mods";
import UserManagement from "./views/UserManagment";
const App = () => {
@ -15,6 +18,13 @@ const App = () => {
const [serverStatus, setServerStatus] = useState(null);
const history = useHistory();
const updateServerStatus = async () => {
const status = await server.status();
if (status.success) {
setServerStatus(status)
}
}
const handleAuthenticationStatus = async () => {
const status = await user.status();
setIsAuthenticated(status.success);
@ -31,7 +41,7 @@ const App = () => {
const ProtectedRoute = useCallback(({component: Component, ...rest}) => (
<Route {...rest} render={(props) => (
isAuthenticated
? <Component serverStatus={serverStatus} {...props} />
? <Component serverStatus={serverStatus} updateServerStatus={updateServerStatus} {...props} />
: <Redirect to={{
pathname: '/login',
state: {from: props.location}
@ -44,15 +54,15 @@ const App = () => {
<Switch>
<Route path="/login" render={() => (<Login handleLogin={handleAuthenticationStatus}/>)}/>
<Layout handleLogout={handleLogout} serverStatus={serverStatus} setServerStatus={setServerStatus}>
<Layout handleLogout={handleLogout} serverStatus={serverStatus} updateServerStatus={updateServerStatus}>
<ProtectedRoute exact path="/" component={Controls}/>
<ProtectedRoute path="/saves" component={Saves}/>
<ProtectedRoute path="/mods" component={Controls}/>
<ProtectedRoute path="/mods" component={Mods}/>
<ProtectedRoute path="/server-settings" component={Controls}/>
<ProtectedRoute path="/game-settings" component={Controls}/>
<ProtectedRoute path="/console" component={Controls}/>
<ProtectedRoute path="/logs" component={Logs}/>
<ProtectedRoute path="/user-management" component={Controls}/>
<ProtectedRoute path="/user-management" component={UserManagement}/>
<ProtectedRoute path="/help" component={Controls}/>
</Layout>
</Switch>

View File

@ -3,14 +3,11 @@ import server from "../../api/resources/server";
import {NavLink} from "react-router-dom";
import Button from "../elements/Button";
const Layout = ({children, handleLogout, serverStatus, setServerStatus}) => {
const Layout = ({children, handleLogout, serverStatus, updateServerStatus}) => {
useEffect(() => {
(async () => {
const status = await server.status();
if (status.success) {
setServerStatus(status)
}
updateServerStatus()
})();
}, []);

View File

@ -1,8 +1,9 @@
import React from "react";
const Button = ({ children, type, onClick, isSubmit, className }) => {
const Button = ({ children, type, onClick, isSubmit, className, size }) => {
let color = '';
let padding = '';
switch (type) {
case 'success':
@ -15,9 +16,17 @@ const Button = ({ children, type, onClick, isSubmit, className }) => {
color = 'bg-gray-light hover:glow-orange hover:bg-orange'
}
switch (size) {
case 'sm':
padding = 'py-1 px-2';
break;
default:
padding = 'py-2 px-4'
}
return (
<button onClick={onClick} className={`${className} ${color} accentuated text-black font-bold py-2 px-4`}
type={isSubmit ? 'submit' : 'button'}>
<button onClick={onClick} className={`${className ? className: null} ${padding} ${color} inline-block accentuated text-black font-bold`}
type={isSubmit ? 'submit' : 'button'}>
{children}
</button>
);

View File

@ -1,11 +1,36 @@
import React from "react";
const ButtonLink = () => {
const ButtonLink = ({children, href, type, target, className, size}) => {
let color = '';
let padding = '';
switch (type) {
case 'success':
color = 'bg-green hover:glow-green hover:bg-green-light';
break;
case 'danger':
color = 'bg-red hover:glow-red hover:bg-red-light';
break;
default:
color = 'bg-gray-light hover:glow-orange hover:bg-orange'
}
switch (size) {
case 'sm':
padding = 'py-1 px-2';
break;
default:
padding = 'py-2 px-4'
}
return (
<button className="accentuated bg-green hover:glow-green hover:bg-green-light text-black font-bold py-2 px-4 w-full"
type="submit">
Sign In
</button>
<a
href={href}
target={target ? target : '_self'}
className={`${className ? className : null} ${color} ${padding} inline-block accentuated text-black font-bold`}>
{children}
</a>
);
}

View File

@ -1,8 +1,8 @@
import React from "react";
const Panel = ({title, content, actions}) => {
const Panel = ({title, content, actions, className}) => {
return (
<div className="accentuated rounded-sm bg-gray-dark shadow-xl pb-4">
<div className={(className ? className : null) + ' accentuated rounded-sm bg-gray-dark shadow-xl pb-4'}>
<div className="px-4 py-2 text-xl text-dirty-white font-bold">
{title}
</div>

View File

@ -0,0 +1,35 @@
import {useForm} from "react-hook-form";
import Button from "../elements/Button";
import React from "react";
import saves from "../../api/resources/saves";
const CreateSaveForm = ({onSuccess}) => {
const {register, handleSubmit, errors} = useForm();
const onSubmit = async data => {
const res = await saves.create(data.savefile);
if (res.success) {
onSuccess();
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-6">
<label className="block text-white text-sm font-bold mb-2" htmlFor="password">
Savefile Name
</label>
<input
className="shadow appearance-none w-full py-2 px-3 text-black"
ref={register({required: true})}
name="savefile"
id="savefile" type="text"/>
{errors.savefile && <span className="block text-red">Savefile Name is required</span>}
</div>
<Button type="success" isSubmit={true}>Create Save</Button>
</form>
)
}
export default CreateSaveForm;

View File

@ -0,0 +1,35 @@
import Button from "../elements/Button";
import React from "react";
import {useForm} from "react-hook-form";
import saves from "../../api/resources/saves";
const UploadSaveForm = ({onSuccess}) => {
const {register, handleSubmit, errors} = useForm();
const onSubmit = async data => {
const res = await saves.upload(data.savefile);
if (res.success) {
onSuccess();
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-6">
<label className="block text-white text-sm font-bold mb-2" htmlFor="password">
Savefile Name
</label>
<input
className="shadow appearance-none w-full py-2 px-3 text-black"
ref={register({required: true})}
name="savefile"
id="savefile" type="file"/>
{errors.savefile && <span className="block text-red">Savefile Name is required</span>}
</div>
<Button type="success">Upload Save</Button>
</form>
)
}
export default UploadSaveForm;

View File

@ -1,21 +1,26 @@
import React, {useEffect, useState} from "react";
import React, {useCallback, useEffect, useMemo, useState} from "react";
import Panel from "../elements/Panel";
import Button from "../elements/Button";
import server from "../../api/resources/server";
const Controls = ({serverStatus}) => {
const Controls = ({serverStatus, updateServerStatus}) => {
const [factorioVersion, setFactorioVersion] = useState('unknown');
const isRunning = () => {
return serverStatus.data.status === 'running';
const isRunning = serverStatus.data.status === 'running';
const startServer = async () => {
await server.start('0.0.0.0',34197,'DEATH.zip');
await updateServerStatus();
}
const startServer = () => {
const stopServer = async () => {
await server.stop();
await updateServerStatus();
}
const stopServer = () => {
const killServer = async () => {
await server.kill();
await updateServerStatus();
}
useEffect(() => {
@ -31,7 +36,7 @@ const Controls = ({serverStatus}) => {
<Panel
title="Server Status"
content={
<div className="flex" slot="content">
<div className="flex">
<table className="w-full">
<thead>
<tr className="text-left py-1">
@ -54,10 +59,10 @@ const Controls = ({serverStatus}) => {
}
actions={
<div className="flex">
{ isRunning()
{ isRunning
? <>
<Button onClick={stopServer} className="mr-2" type="default">Save & Stop Server</Button>
<Button onClick={stopServer} type="danger">Stop Server</Button>
<Button onClick={killServer} type="danger">Kill Server</Button>
</>
: <Button onClick={startServer} type="success">Start Server</Button>
}

22
ui/App/views/Mods.jsx Normal file
View File

@ -0,0 +1,22 @@
import Panel from "../elements/Panel";
import React from "react";
const Mods = () => {
return (
<>
<Panel
title="Mods"
className="mb-6"
content={<h1>Test</h1>}
/>
<Panel
title="Mod Packs"
content={<h1>Test</h1>}
/>
</>
)
}
export default Mods;

View File

@ -1,44 +1,87 @@
import React, {useEffect, useState} from "react";
import save from "../../api/resources/saves";
import saveClient from "../../api/resources/saves";
import Panel from "../elements/Panel";
import ButtonLink from "../elements/ButtonLink";
import Button from "../elements/Button";
import {useForm} from "react-hook-form";
import CreateSaveForm from "../forms/CreateSaveForm";
import UploadSaveForm from "../forms/UploadSaveForm";
const Saves = () => {
const Saves = ({serverStatus}) => {
const [saves, setSaves] = useState([]);
const {register, handleSubmit, errors} = useForm();
const updateList = async () => {
const res = await saveClient.list();
if (res.success) {
setSaves(res.data);
}
}
useEffect(() => {
(async () => {
const list = await save.list();
if (list.success) {
console.log(list)
setSaves(list.data);
}
})()
}, [])
updateList()
}, []);
const deleteSave = async (save) => {
const res = await saveClient.delete(save);
if (res.success) {
updateList()
}
}
return (
<Panel
title="Saves"
content={<table className="w-full">
<thead>
<tr className="text-left py-1">
<th>Name</th>
<th>Last Modified At</th>
<th>Size</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{saves.map(save =>
<tr className="py-1" key={save.name}>
<td className="pr-4">{save.name}</td>
<td className="pr-4">{save.last_mod}</td>
<td>{parseFloat(save.size / 1024 / 1024).toFixed(3)} MB</td>
</tr>
)}
</tbody>
</table>}
/>
<>
<div className="flex mb-6">
<Panel
title="Create Save"
className="w-1/2 mr-3"
content={
serverStatus.data.status === "running"
? <p className="text-red-light pt-4 pb-24">
Create a new Save is only possible if the Factorio server is
not running.
</p>
: <CreateSaveForm onSuccess={updateList}/>
}
/>
<Panel
title="Upload Save"
className="w-1/2 ml-3"
content={<UploadSaveForm/>}
/>
</div>
<Panel
title="Saves"
content={
<table className="w-full">
<thead>
<tr className="text-left py-1">
<th>Name</th>
<th>Last Modified At</th>
<th>Size</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{saves.map(save =>
<tr className="py-1" key={save.name}>
<td className="pr-4">{save.name}</td>
<td className="pr-4">{(new Date(save.last_mod)).toISOString().replace('T', ' ').split('.')[0]}</td>
<td>{parseFloat(save.size / 1024 / 1024).toFixed(3)} MB</td>
<td>
<ButtonLink size="sm" href={`/api/saves/dl/${save.name}`}
className="mr-2">Download</ButtonLink>
<Button size="sm" onClick={() => deleteSave(save)} type="danger">Delete</Button>
</td>
</tr>
)}
</tbody>
</table>
}
/>
</>
)
}

View File

@ -0,0 +1,20 @@
import Panel from "../elements/Panel";
import React from "react";
const UserManagement = () => {
return (
<>
<Panel
title="List of Users"
content="test"
className="mb-4"
/>
<Panel
title="Create User"
content="test"
/>
</>
)
}
export default UserManagement;

View File

@ -5,4 +5,12 @@ export default {
const response = await client.get('/api/saves/list');
return response.data;
},
delete: async (save) => {
const response = await client.get(`/api/saves/rm/${save.name}`);
return response.data;
},
create: async (name) => {
const response = await client.get(`/api/saves/create/${name}`);
return response.data;
}
}

View File

@ -9,4 +9,20 @@ export default {
const response = await client.get('/api/server/status');
return response.data;
},
stop: async () => {
const response = await client.post('/api/server/stop');
return response.data;
},
start: async (ip, port, savefile) => {
const response = await client.post('/api/server/start', {
bindip: ip,
savefile,
port
});
return response.data;
},
kill: async () => {
const response = await client.get('/api/server/kill');
return response.data;
}
}