You've already forked factorio-server-manager
mirror of
https://github.com/OpenFactorioServerManager/factorio-server-manager.git
synced 2025-07-15 01:14:28 +02:00
wip
This commit is contained in:
@ -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>
|
||||
|
@ -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()
|
||||
})();
|
||||
}, []);
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
35
ui/App/forms/CreateSaveForm.jsx
Normal file
35
ui/App/forms/CreateSaveForm.jsx
Normal 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;
|
35
ui/App/forms/UploadSaveForm.jsx
Normal file
35
ui/App/forms/UploadSaveForm.jsx
Normal 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;
|
@ -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
22
ui/App/views/Mods.jsx
Normal 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;
|
@ -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>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
20
ui/App/views/UserManagment.jsx
Normal file
20
ui/App/views/UserManagment.jsx
Normal 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;
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user