mirror of
https://ceregatti.org/git/daniel/dayzdockerserver.git
synced 2025-05-07 06:41:17 +00:00
Add web UI. This has lofty goals.
Use node js module syntax. Make dev easier by not starting the web UI from docker startup.
This commit is contained in:
parent
0a73bea7f6
commit
5267ae70bb
8 changed files with 324 additions and 23 deletions
|
@ -9,6 +9,7 @@ EOF
|
||||||
# Uncomment the line below to run things manually in the container, then run:
|
# Uncomment the line below to run things manually in the container, then run:
|
||||||
# docker compose exec main bash
|
# docker compose exec main bash
|
||||||
tail -f /dev/null
|
tail -f /dev/null
|
||||||
|
exit 0
|
||||||
|
|
||||||
# Otherwise, start the server normally
|
# Otherwise, start the server normally
|
||||||
#/files/dayzserver start
|
/files/dayzserver start
|
||||||
|
|
|
@ -10,6 +10,12 @@ export PS1="${debian_chroot:+($debian_chroot)}\u@dz-main:\w\$ "
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Uncomment the lines below to run things manually in the container, then run:
|
||||||
|
# docker compose exec main bash
|
||||||
|
tail -f /dev/null
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
# Otherwise, start the server normally
|
||||||
cd /web
|
cd /web
|
||||||
npm i
|
npm i
|
||||||
node index.js
|
node index.js
|
||||||
|
|
|
@ -56,7 +56,7 @@ loadconfig(){
|
||||||
if [ ! -f "${SERVER_INSTALL_FILE}" ]
|
if [ ! -f "${SERVER_INSTALL_FILE}" ]
|
||||||
then
|
then
|
||||||
echo
|
echo
|
||||||
echo -e "The DayZ server files are not installed. Run '${green}docker-compose run --rm main dayzserver install${default}'"
|
echo -e "The DayZ server files are not installed. You need to do this first in the web UI."
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
141
web/index.js
141
web/index.js
|
@ -1,10 +1,139 @@
|
||||||
const express = require('express')
|
import express from 'express'
|
||||||
const path = require('path')
|
import path from 'path'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const port = 8000
|
|
||||||
|
|
||||||
app.use('/', express.static(path.join(__dirname, 'root')))
|
/*
|
||||||
|
The DayZ server Steam app ID. USE ONE OR THE OTHER!!
|
||||||
|
|
||||||
app.listen(port, () => {
|
Presumably once the Linux server is officially released, the binaries will come from this ID.
|
||||||
console.log(`Listening on port ${port}`)
|
Meanwhile, if we have a release-compatible binary, the base files must be installed from this id,
|
||||||
|
even if the server binary and required shared objects don't come from it. (They'd come from...elsewhere...)
|
||||||
|
*/
|
||||||
|
//const server_appid = "223350"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Without a release binary, we must use the experimental server app ID.
|
||||||
|
*/
|
||||||
|
const server_appid = "1042420"
|
||||||
|
|
||||||
|
/*
|
||||||
|
DayZ release client Steam app ID. This is for mods, as only the release client has them.
|
||||||
|
*/
|
||||||
|
const client_appid = "221100"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Base file locations
|
||||||
|
*/
|
||||||
|
const modDir = "/mods"
|
||||||
|
const serverFiles = "/serverfiles"
|
||||||
|
|
||||||
|
const d = '/'
|
||||||
|
|
||||||
|
/*
|
||||||
|
XML config files the system can handle. These are retrieved from values in templates located in /files/mods/:modId
|
||||||
|
*/
|
||||||
|
const configFiles = [
|
||||||
|
'cfgeventspawns.xml',
|
||||||
|
'cfgspawnabletypes.xml',
|
||||||
|
'events.xml',
|
||||||
|
'types.xml',
|
||||||
|
]
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
installFile: serverFiles + "/DayZServer",
|
||||||
|
modDir: modDir + "/" + client_appid,
|
||||||
|
port: 8000,
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDirSize = (dirPath) => {
|
||||||
|
let size = 0
|
||||||
|
const files = fs.readdirSync(dirPath)
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const filePath = path.join(dirPath, files[i])
|
||||||
|
const stats = fs.statSync(filePath)
|
||||||
|
if (stats.isFile()) {
|
||||||
|
size += stats.size
|
||||||
|
} else if (stats.isDirectory()) {
|
||||||
|
size += getDirSize(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCustomXML = (modId) => {
|
||||||
|
const ret = []
|
||||||
|
for(const file of configFiles) {
|
||||||
|
if (fs.existsSync(config.modDir + d + modId + d + file)) {
|
||||||
|
ret.push({name:file})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
const getModNameById = (id) => {
|
||||||
|
const files = fs.readdirSync(serverFiles, {encoding: 'utf8', withFileTypes: true})
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.isSymbolicLink()) {
|
||||||
|
const sym = fs.readlinkSync(serverFiles + d + file.name)
|
||||||
|
if(sym.indexOf(id) > -1) return file.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMods = () => {
|
||||||
|
const mods = []
|
||||||
|
fs.readdirSync(config.modDir).forEach(file => {
|
||||||
|
const name = getModNameById(file)
|
||||||
|
mods.push({name:name,id:file})
|
||||||
|
})
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(express.static('root'))
|
||||||
|
|
||||||
|
app.get('/status', (req, res) => {
|
||||||
|
// FIXME! Group these into a Promise.All()
|
||||||
|
const installed = fs.existsSync(config.installFile)
|
||||||
|
const mods = getMods()
|
||||||
|
const ret = {
|
||||||
|
"installed": installed,
|
||||||
|
"version": "1.20.bogus",
|
||||||
|
"mods": mods
|
||||||
|
}
|
||||||
|
res.send(ret)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.route('/mod/:modId')
|
||||||
|
.get((req, res) => {
|
||||||
|
// Get mod metadata by ID
|
||||||
|
const modId = req.params["modId"]
|
||||||
|
const modDir = config.modDir + d + modId
|
||||||
|
const customXML = getCustomXML(modId)
|
||||||
|
const ret = {
|
||||||
|
id: modId,
|
||||||
|
name: getModNameById(modId),
|
||||||
|
size: getDirSize(modDir),
|
||||||
|
customXML: customXML
|
||||||
|
}
|
||||||
|
res.send(ret)
|
||||||
|
})
|
||||||
|
.post((req, res) => {
|
||||||
|
// Add a mod by ID
|
||||||
|
})
|
||||||
|
.put((req, res) => {
|
||||||
|
// Update a mod by ID
|
||||||
|
})
|
||||||
|
|
||||||
|
app.route('/mod/:modId/:file')
|
||||||
|
.get((req, res) => {
|
||||||
|
const modId = req.params["modId"]
|
||||||
|
const file = req.params["file"]
|
||||||
|
const contents = fs.readFileSync(config.modDir + d + modId + d + file)
|
||||||
|
res.send(contents)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(config.port, () => {
|
||||||
|
console.log(`Listening on port ${config.port}`)
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,5 +11,6 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2"
|
"express": "^4.18.2"
|
||||||
}
|
},
|
||||||
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|
30
web/root/index.css
Normal file
30
web/root/index.css
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
body {
|
||||||
|
padding-top: 50px;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.green {
|
||||||
|
color: green;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
.yellow {
|
||||||
|
color: yellow;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
.darkgrey {
|
||||||
|
background-color: darkgray;
|
||||||
|
font-weight: bolder;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modInfo {
|
||||||
|
background-color: aliceblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simulink {
|
||||||
|
cursor: pointer;
|
||||||
|
text-underline: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding-right: 10px
|
||||||
|
}
|
|
@ -3,21 +3,19 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>DayZ Docker Server</title>
|
<title>DayZ Docker Server</title>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
<style>
|
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||||
body {
|
<link rel="stylesheet" href="/index.css">
|
||||||
padding-top: 50px;
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
|
||||||
background-color: darkgray;
|
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||||
}
|
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
<div class="container">
|
<script type="module">
|
||||||
<div class="jumbotron">
|
import app from '/index.js'
|
||||||
<h1>DayZ Docker Server</h1>
|
// eslint-disable-next-line no-undef
|
||||||
</div>
|
Vue.createApp(app).mount('#app')
|
||||||
</div>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
136
web/root/index.js
Normal file
136
web/root/index.js
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
const template = `
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row jumbotron darkgrey">
|
||||||
|
<div class="col-10">
|
||||||
|
<h1>DayZ Docker Server</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div>
|
||||||
|
Server files installed: {{ installed }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Version: {{ version }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="fetchError != ''"
|
||||||
|
class="row jumbotron text-center alert alert-danger"
|
||||||
|
>
|
||||||
|
{{ fetchError }}
|
||||||
|
</div>
|
||||||
|
<div class="row jumbotron darkgrey">
|
||||||
|
<div class="col-3">
|
||||||
|
<h2 class="text-center">Mods</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Steam Link</th>
|
||||||
|
<th>Mod Info</th>
|
||||||
|
</tr>
|
||||||
|
<template
|
||||||
|
v-for="mod in mods"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
:href="'https://steamcommunity.com/sharedfiles/filedetails/?id=' + mod.id"
|
||||||
|
>
|
||||||
|
{{ mod.id }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="simulink" @click="getModInfo(mod.id)">{{ mod.name }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-9 modInfo" v-if="modInfo != ''">
|
||||||
|
<div class="text-center col-2">
|
||||||
|
<h2>{{ modInfo.name }}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2">
|
||||||
|
<div>
|
||||||
|
ID: {{ modInfo.id }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Size: {{ modInfo.size.toLocaleString("en-US") }}
|
||||||
|
</div>
|
||||||
|
<div v-if="modInfo.customXML.length > 0">
|
||||||
|
Custom XML files:
|
||||||
|
<ul>
|
||||||
|
<li v-for="info in modInfo.customXML">
|
||||||
|
<a class="simulink" @click="getXMLInfo(modInfo.id,info.name)">{{ info.name }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-10">
|
||||||
|
<textarea cols="120" rows="15" v-if="this.XMLInfo != ''">{{ this.XMLInfo }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DazDockerServer',
|
||||||
|
template: template,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetchError: "",
|
||||||
|
installed: false,
|
||||||
|
mods: [],
|
||||||
|
modInfo: "",
|
||||||
|
version: "Unknown",
|
||||||
|
XMLInfo: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getModInfo(modId) {
|
||||||
|
fetch('/mod/' + modId)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
this.modInfo = response
|
||||||
|
this.XMLInfo = ""
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
this.fetchError = error.message
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getXMLInfo(modId, file) {
|
||||||
|
fetch('/mod/' + modId + '/' + file)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(response => {
|
||||||
|
this.XMLInfo = response
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
this.fetchError = error.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// Get the data
|
||||||
|
fetch('/status')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
this.installed = response.installed
|
||||||
|
this.version = response.version
|
||||||
|
this.mods = response.mods
|
||||||
|
if(response.error) {
|
||||||
|
this.fetchError = response.error
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
this.fetchError = error.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue