Implement much missing stuff:

Login. Still WIP, but works.
Install server files. Once logged in, this works.
Add support for experimental in both web and scripts. WIP.
Continuous streaming text web interface for long-running server processes.
Large refactor of the config file. Use arrays instead of delimited strings.
Large refactor of the express server to support the above.
Add Steam API key to env for when we do mod searches.
Customize the bash shell of the web container should we exec into it.
Ignore the env file.
This commit is contained in:
Daniel Ceregatti 2024-08-26 09:13:30 -07:00
parent 20700cdc26
commit 569e45c93c
13 changed files with 269 additions and 199 deletions

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
.idea
*.iml
.env*
env.json
node_modules/
web/client/bin
web/client/obj

View file

@ -12,6 +12,7 @@ volumes:
serverfiles_experimental:
# Upstream mission files
mpmissions:
mpmissions_experimental:
services:
@ -27,6 +28,7 @@ services:
- serverfiles:/serverfiles
- serverfiles_experimental:/serverfiles_experimental
- mpmissions:/serverfiles/mpmissions
- mpmissions_experimental:/serverfiles_experimental/mpmissions
- mods:/serverfiles/steamapps/workshop/content
- mods:/mods
- ./files:/files

View file

@ -36,7 +36,7 @@ export cyan="\e[36m"
export release_server_appid=223350
# Leaving the experimental server appid here to allow for the use of the experimental server.
#export release_server_appid=1042420
export experimental_server_appid=1042420
# DayZ release client SteamID. This is for mods, as only the release client has them.
export release_client_appid=221100
@ -47,6 +47,7 @@ export SERVER_PROFILE="/profiles"
# Common container base directories
export FILES="/files"
export SERVER_FILES="/serverfiles"
export SERVER_FILES_EXPERIMENTAL="/serverfiles_experimental"
# Used to check if dayZ is installed
export SERVER_INSTALL_FILE="${SERVER_FILES}/DayZServer"

View file

@ -131,8 +131,10 @@ ARG USER_ID
RUN groupadd -g ${USER_ID} user && \
useradd -l -u ${USER_ID} -m -g user user && \
mkdir -p /home/user /serverfiles/mpmissions /serverfiles/steamapps/workshop/content /web && \
chown -R user:user /home/user /serverfiles /web
mkdir -p /home/user \
/serverfiles/mpmissions /serverfiles/steamapps/workshop/content \
/serverfiles_experimental/mpmissions /serverfiles_experimental/steamapps/workshop/content /web && \
chown -R user:user /home/user /serverfiles /serverfiles_experimental /web
# Use our non-privileged user
USER user

View file

@ -180,7 +180,8 @@ login(){
dologin(){
if [ -f "${STEAM_LOGIN}" ]
then
source "${STEAM_LOGIN}"
echo "Logging in to Steam"
steamlogin=$(cat ${STEAM_LOGIN})
else
echo "No cached Steam credentials. Please configure this now: "
login
@ -189,9 +190,16 @@ dologin(){
# Perform the installation of the server files.
install(){
if [ ! -f "${SERVER_INSTALL_FILE}" ] || [[ ${1} = "force" ]]
WHICH=${1}
if [[ ${WHICH} = "experimental" ]]
then
printf "[ ${yellow}DayZ${default} ] Downloading DayZ Server-Files!\n"
SERVER_FILES=${SERVER_FILES_EXPERIMENTAL}
SERVER_INSTALL_FILE="${SERVER_FILES}/DayZServer"
release_server_appid=${experimental_server_appid}
fi
if [ ! -f "${SERVER_INSTALL_FILE}" ] || [[ ${2} = "force" ]]
then
printf "[ ${yellow}DayZ${default} ] Downloading ${WHICH} DayZ Server-Files!\n"
dologin
${STEAMCMD} +force_install_dir ${SERVER_FILES} +login "${steamlogin}" +app_update "${release_server_appid}" validate +quit
# This installs the mpmissions for charnarusplus and enoch (AKA Livonia) from github. The game once allowed the full server
@ -199,7 +207,7 @@ install(){
echo "Installing mpmissions for ChernarusPlus and Livonia from github..."
map default
else
printf "[ ${lightblue}DayZ${default} ] The server is already installed.\n"
printf "[ ${lightblue}DayZ${default} ] The ${WHICH} server is already installed.\n"
fi
}
@ -341,8 +349,6 @@ xml(){
installxml ${1}
}
# Capture the first argument and shift it off so we can pass $@ to every function
C=${1}
shift || {
usage
}

View file

@ -7,16 +7,13 @@ trap '
' SIGINT SIGTERM
# Set PS1 so we know we're in the container
if ! grep -q "dz-web" .bashrc
then
echo "Adding PS1 to .bashrc..."
cat >> .bashrc <<EOF
echo "Adding PS1 to .bashrc..."
cat > .bashrc <<EOF
alias ls='ls --color'
export PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
export PS1='${debian_chroot:+($debian_chroot)}\[\033[01;35m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
unset DEVELOPMENT
export PATH=${PATH}:/usr/local/dotnet
EOF
fi
# Shut steamcmd up
if ! [ -d ${HOME}/.steam ]
@ -25,6 +22,16 @@ then
fi
cd /web
if [ -n "${STEAMAPIKEY}" ]
then
cat > env.json <<EOF
{
"STEAMAPIKEY": "${STEAMAPIKEY}"
}
EOF
else
echo "{}" > env.json
fi
#export DEBUG=express:*
npm run dev &
wait $!

View file

@ -5,23 +5,24 @@ const store = useAppStore()
</script>
<template>
<Dialog v-model:visible="store.stream" maximizable modal :style="{ width: '50rem' }">
<template #header>
<div class="grid">
<div class="col align-content-center justify-content-center"><i class="pi pi-exclamation-circle" style="color: green;"></i></div>
<div class="col align-content-center justify-content-center" style="font-size: 1.5em;">
<i v-if="store.streamLoading" class="pi pi-spin pi-cog" style="color: orange;"></i>
<i v-else class="pi pi-exclamation-circle" style="color: green;"></i>
</div>
<div class="col align-content-center justify-content-center white-space-nowrap">{{ $t('Server Output') }}</div>
</div>
</template>
<template #default>
<div class="steamcmd">
<pre class="pre">{{ store.streamText }}</pre>
<div class="container">
<div class="autoscroll">{{ store.streamText }}</div>
</div>
</template>
<template #footer>
<i v-if="store.streamLoading" class="pi pi-spin pi-cog" style="color: red;"></i>
<i v-else class="pi pi-check" style="color: green;"></i>
</template>
</Dialog>
<Dialog v-model:visible="store.alert" modal :header="$t('Alert')" :style="{ width: '25rem' }">
<template #header>
<div class="grid">
@ -31,6 +32,7 @@ const store = useAppStore()
</template>
{{ store.alertText }}
</Dialog>
<Dialog v-model:visible="store.error" modal :header="$t('Error')" :style="{ width: '25rem' }">
<template #header>
<div class="grid">
@ -40,13 +42,21 @@ const store = useAppStore()
</template>
{{ store.errorText }}
</Dialog>
</template>
<style scoped>
.steamcmd {
.container {
height: 300px;
min-height: 300px;
}
.pre {
.autoscroll {
overflow-y: scroll;
scroll-behavior: smooth;
max-height: 100%;
display: flex;
flex-direction: column-reverse;
white-space: pre-wrap;
font-family: monospace;
}
</style>

View file

@ -1,34 +1,46 @@
<script setup>
import Button from 'primevue/button'
import { useFetch } from '@vueuse/core'
import { useAppStore } from '@/store.js'
const store = useAppStore()
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
async function base() {
let which = '/installbase'
if (store.steamStatus.stableInstalled) {
which = '/updatebase'
async function install(which) {
const url = '/install/server/' + which
store.setStreamLoading(true)
const response = await fetch(url)
for await (const chunk of response.body) {
store.setStream(new TextDecoder().decode(chunk), true)
}
store.setStreamLoading(false)
const data = JSON.parse(store.streamText.match(/({.*})/)[1])
if (data.errorCode === 0) {
store.setAlert(t('Successfully installed server files'))
store.steamStatus.installed[which] = true
} else if (data.errorMessage) {
store.setError(t(data.errorMessage))
} else {
store.setError(t('Unknown error'))
}
const { data } = await useFetch(which).get().json()
store.setAlert(t(data.value.message))
}
</script>
<template>
<div v-if="! store.steamStatus.loggedIn" class="grid">
<div class="col-6 col-offset-3">
{{ $t('Please log in to Steam to install server files') }}
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
<Button @click="base('stable')" v-if="! store.steamStatus.stableInstalled">{{ store.steamStatus.stableInstalled ? $t('Update Stable Server Files') : $t('Install Stable Server Files') }}</Button>
<span v-else>{{ $t('Stable Server files are installed') }}</span>
<Button v-if="! store.steamStatus.installed['stable']" @click="install('stable')" :disabled="! store.steamStatus.loggedIn">{{ $t('Install Stable Server Files') }}</Button>
<Button v-else severity="warn" @click="install('stable')" :disabled="! store.steamStatus.loggedIn">{{ $t('Stable Server files are installed') + ' - ' + $t('Update Stable Server Files') }}</Button>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
<Button @click="base('experimental')" v-if="! store.steamStatus.experimentalInstalled">{{ store.steamStatus.experimentalInstalled ? $t('Update Experimental Server Files') : $t('Install Experimental Server Files') }}</Button>
<span v-else>{{ $t('Experimental Server files are installed') }}</span>
<Button v-if="! store.steamStatus.installed['experimental']" @click="install('experimental')" :disabled="! store.steamStatus.loggedIn">{{ store.steamStatus.installed_experimental ? $t('Update Experimental Server Files') : $t('Install Experimental Server Files') }}</Button>
<Button v-else severity="warn" @click="install('experimental')" :disabled="! store.steamStatus.loggedIn">{{ $t('Experimental Server files are installed') + ' - ' + $t('Update Experimental Server Files') }}</Button>
</div>
</div>
</div>

View file

@ -23,11 +23,11 @@ const test = async (type) => {
const continuous = async () => {
const url = '/test?type=continuous'
const response = await fetch(url)
store.setStream('')
store.setStreamLoading(true)
for await (const chunk of response.body) {
store.setStream(new TextDecoder().decode(chunk), true)
}
store.setSteamLoading(false)
store.setStreamLoading(false)
}
</script>
@ -46,7 +46,7 @@ const continuous = async () => {
<div class="col-6 col-offset-3">
<div>
{{ $t('Stable Server files installed') }}:
<span v-if="store.steamStatus.stableInstalled" class="pi pi-check" style="color: green"></span>
<span v-if="store.steamStatus.installed['stable']" class="pi pi-check" style="color: green"></span>
<span v-else class="pi pi-times" style="color: red"></span>
</div>
</div>
@ -55,15 +55,15 @@ const continuous = async () => {
<div class="col-6 col-offset-3">
<div>
{{ $t('Experimental Server files installed') }}:
<span v-if="store.steamStatus.experimentalInstalled" class="pi pi-check" style="color: green"></span>
<span v-if="store.steamStatus.installed['experimental']" class="pi pi-check" style="color: green"></span>
<span v-else class="pi pi-times" style="color: red"></span>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div v-if="store.steamStatus.version">
{{ $t('Version') }}: <span style="color: green;">{{ store.steamStatus.version }}</span>
<div v-if="store.steamStatus.version_stable">
{{ $t('Version') }}: <span style="color: green;">{{ store.steamStatus.version_stable }}</span>
<span class="bold">({{ store.steamStatus.appid }})</span>
</div>
</div>

View file

@ -9,24 +9,20 @@ import { useAppStore } from '@/store.js'
const store = useAppStore()
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
function steamStatus(data) {
console.log(data)
if (data.errorCode === 0) {
store.setAlert(t('Successfully logged in to Steam'))
store.steamStatus.loggedIn = true
} else {
store.setError(t(data.errorMessage))
store.steamStatus.loggedIn = false
}
}
async function logOut() {
const { data } = await useFetch('/logout').get().json()
steamStatus(data)
if (data.value.errorCode === 0) {
store.setAlert(t('Successfully logged out of Steam'))
store.steamStatus.loggedIn = false
} else if (data.errorMessage) {
store.setError(t(data.errorMessage))
} else {
store.setError(t('Unknown error'))
}
}
async function login(e) {
loading.value = true
e.preventDefault()
const { data } = await useFetch('/login', {
async function login() {
const url = '/login'
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -36,12 +32,23 @@ async function login(e) {
password: password.value,
remember: remember.value,
steamGuardCode: steamGuardCode.value
}).post().json()
})
loading.value = false
steamStatus(data)
})
store.setStreamLoading(true)
for await (const chunk of response.body) {
store.setStream(new TextDecoder().decode(chunk), true)
}
store.setStreamLoading(false)
const data = JSON.parse(store.streamText.match(/({.*})/)[1])
if (data.errorCode === 0) {
store.setAlert(t('Successfully logged in to Steam'))
store.steamStatus.loggedIn = true
} else if (data.errorMessage) {
store.setError(t(data.errorMessage))
} else {
store.setError(t('Unknown error'))
}
}
let loading = ref(false)
let username = ref('')
let password = ref('')
let remember = ref(false)
@ -49,46 +56,42 @@ let steamGuardCode = ref('')
</script>
<template>
<div>
<div v-if="store.steamStatus.loggedIn" class="grid">
<div class="col-12">{{ $t('Already logged in to steam') }}</div>
<div class="col-4 col-offset-4 text-center">{{ $t('Already logged in to steam') }}</div>
<div class="col-12">
<Button @click="logOut">{{ $t('Log out') }}</Button>
<Button @click="logOut" severity="danger">{{ $t('Log out') }}</Button>
</div>
</div>
<div v-else class="grid">
<div class="col-12">
<h2>{{ $t('There are no saved Steam credentials. To install the server files and mods, please login to Steam') }}</h2>
<div class="col-4 col-offset-4 text-left">
{{ $t('There are no saved Steam credentials. To install the server files and mods, please login to Steam') }}
</div>
<div class="col-12">
</div>
<div class="col-2 col-offset-4 text-right">
<div class="col-4 col-offset-4 text-right">
<div class="col-6 text-left p-0">
<label for="username">{{ $t('Username') }}</label>
<InputText id="username" v-model="username" autofocus />
</div>
<div class="col-2 text-left">
<InputText id="username" v-model="username" />
</div>
<div class="col-2 col-offset-4 text-right">
<div class="col-4 col-offset-4 text-right">
<div class="col-6 text-left p-0">
<label for="password">{{ $t('Password') }}</label>
</div>
<div class="col-2 text-left">
<Password id="password" v-model="password" :feedback="false" toggleMask />
</div>
<div class="col-2 col-offset-4 text-right">
<label for="steamGuardCode">{{ $t('Steam Guard Code') }}</label>
</div>
<div class="col-2 text-left">
<div class="col-4 col-offset-4 text-right">
<div class="col-6 text-left p-0">
<label for="steamGuardCode">{{ $t('Steam Guard Code') }}</label>
<InputText id="steamGuardCode" v-model="steamGuardCode" />
</div>
<div class="col-2 col-offset-4 text-right">
</div>
<div class="col-4 col-offset-4 text-right">
<div class="col-6 text-left p-0">
<label for="remember">{{ $t('Remember Credentials') }}</label>
<Checkbox inputId="remember" v-model="remember" binary />
</div>
<div class="col-2 text-left">
<Checkbox id="remember" v-model="remember" binary />
</div>
<div class="col-12 text-center">
<Button @click="login" :icon="loading ? 'pi pi-spin pi-spinner' : ''">{{ $t('Submit') }}</Button>
</div>
<div class="col-4 col-offset-4 text-left">
<Button type="button" @click="login" :loading="store.streamLoading" icon="pi pi-user" :label="$t('Submit')"></Button>
</div>
</div>
</template>

View file

@ -1,36 +1,51 @@
/*
* The stable DayZ server Steam app ID.
*/
const stable_server_appid = 223350
// The STEAMAPIKEY is added to this file from the value in the .env file.
import env from '/web/env.json' assert { type: "json" }
/*
* The experimental DayZ server Steam app ID.
*/
const experimental_server_appid = 1042420
// The stable DayZ server Steam app ID.
const server_appid_stable = 223350
/*
* DayZ release client Steam app ID. This is for mods, as only the release client has them.
*/
// The experimental DayZ server Steam app ID.
const server_appid_experimental = 1042420
// DayZ release client Steam app ID. This is for mods, as only the release client has them.
const client_appid = 221100
const serverFiles = "/serverfiles"
const homeDir = "/home/user"
const server_files_stable = "/serverfiles"
const server_files_experimental = "/serverfiles_experimental"
const home_dir = "/home/user"
// const steamAPIKey = process?.env["STEAMAPIKEY"] || ""
//
// const searchUrl = "https://api.steampowered.com/IPublishedFileService/QueryFiles/v1/?numperpage=1000&appid=221100&return_short_description=true&strip_description_bbcode=true&key=" + steamAPIKey + "&search_text="
const search_url = `https://api.steampowered.com/IPublishedFileService/QueryFiles/v1/?numperpage=1000&appid=221100&return_short_description=true&strip_description_bbcode=true&key=${env.STEAMAPIKEY}&search_text=`
const config = {
appid: {
client: client_appid,
experimental: server_appid_experimental,
stable: server_appid_stable,
},
client_appid: client_appid,
experimental_server_appid: experimental_server_appid,
installFile: serverFiles + "/DayZServer",
loginFile: homeDir + "/steamlogin",
modDir: "/mods/" + client_appid,
install_file: {
experimental: server_files_experimental + "/DayZServer",
stable: server_files_stable + "/DayZServer",
},
login_file: home_dir + "/steamlogin",
mod_dir: "/mods/" + client_appid,
port: 8000,
// searchUrl: searchUrl,
serverFiles: serverFiles,
stable_server_appid: stable_server_appid,
search_url: search_url,
server_appid: {
experimental: server_appid_experimental,
stable: server_appid_stable,
},
server_files: {
experimental: server_files_experimental,
stable: server_files_stable,
},
steamUrl: 'https://steamcommunity.com/sharedfiles/filedetails/?id=',
version: {
experimental: "1.26.56789",
stable: "1.26.123456",
},
version_experimental: "1.26.56789",
version_stable: "1.26.123456",
}
export { config }

View file

@ -9,14 +9,19 @@ export const useAppStore = defineStore('app', {
modId: 0,
modFile: false,
mods: [],
searchText: false,
search: false,
searchText: '',
servers: [],
steamStatus: {
appid: 0,
experimentalInstalled: false,
installed: {
experimental: false,
stable: false,
},
loggedIn: false,
stableInstalled: false,
version: ''
version: {
stable: '',
experimental: '',
}
},
stream: false,
streamLoading: false,
@ -28,15 +33,14 @@ export const useAppStore = defineStore('app', {
this.alert = true
},
setStream(streamText) {
this.stream = true
if (streamText) {
this.streamText += streamText
} else {
this.streamText = ''
}
},
setStreamLoading(streamLoading) {
this.stream = true
this.streamLoading = streamLoading
if (streamLoading) {
this.streamText = ''
}
},
setError(error) {
this.errorText = error

View file

@ -12,7 +12,7 @@ import path from 'path'
import fs from 'fs'
import https from 'https'
import { spawn } from 'child_process'
import { config } from "./docroot/src/config.js";
import { config } from './docroot/src/config.js'
/*
File path delimiter
@ -94,9 +94,9 @@ const getDirSize = (dirPath) => {
const getCustomXML = (modId) => {
const ret = []
if (! fs.existsSync(config.modDir)) return ret
if (! fs.existsSync(config.mod_dir)) return ret
for(const file of configFiles) {
if (fs.existsSync(config.modDir + d + modId + d + file)) {
if (fs.existsSync(config.mod_dir + d + modId + d + file)) {
ret.push({name:file})
}
}
@ -104,7 +104,7 @@ const getCustomXML = (modId) => {
}
const getModNameById = (id) => {
const files = fs.readdirSync(config.serverFiles, {encoding: 'utf8', withFileTypes: true})
const files = fs.readdirSync(config.server_files, {encoding: 'utf8', withFileTypes: true})
for (const file of files) {
if (file.isSymbolicLink()) {
const sym = fs.readlinkSync(serverFiles + d + file.name)
@ -116,8 +116,8 @@ const getModNameById = (id) => {
const getMods = () => {
const mods = []
if (! fs.existsSync(config.modDir)) return mods
fs.readdirSync(config.modDir).forEach(file => {
if (! fs.existsSync(config.mod_dir)) return mods
fs.readdirSync(config.mod_dir).forEach(file => {
const name = getModNameById(file)
mods.push({name:name,id:file})
})
@ -132,26 +132,27 @@ const sendAlert = (res, message) => {
res.send({"alert": message})
}
const steamcmd = async (args, res) => {
const cmd = async (command, args, res) => {
return new Promise((resolve) => {
let stdout = ''
let stderr = ''
const command = 'steamcmd +force_install_dir ' + config.serverFiles + ' ' + args + ' +quit'
console.log(command)
const proc = spawn(command, {shell: true})
const re = /(\u001b\[.*?m)/g
console.log(command, args)
const proc = spawn(command, args)
proc.stdout.on('data', (data) => {
const out = "[OUT] " + data + "\n"
const out = "[OUT] " + data.toString().replace(re,'') + "\n"
console.log(out)
res.write(out)
stdout += out
})
proc.stderr.on('data', (data) => {
const err = "[ERROR] " + data + "\n"
const err = "[ERROR] " + data.toString().replace(re,'') + "\n"
res.write(err)
console.log(err)
stderr += err
})
proc.on('error', (data) => {
const err = "[ERROR] " + data + "\n"
const err = "[ERROR] " + data.toString().replace(re,'') + "\n"
console.log(err)
stderr += err
})
@ -162,6 +163,14 @@ const steamcmd = async (args, res) => {
})
}
const steamcmd = async (args, which, res) => {
return await cmd('steamcmd', [ '+force_install_dir', config.server_files[which] ].concat(args).concat("+quit"), res)
}
const dz = async (args, res) => {
return cmd('dz', args, res)
}
const app = express()
app.use(express.json())
@ -170,35 +179,29 @@ app.use(express.urlencoded({extended: true}))
app.disable('etag')
app.use((req, res, next) => {
res.append('Access-Control-Allow-Origin', ['*'])
res.append('Access-Control-Allow-Origin', '*')
res.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
res.append('Access-Control-Allow-Headers', 'Content-Type')
next()
})
// Install a mod
app.get(('/install/:modId'), (req, res) => {
app.get(('/install/mod/:modId'), async (req, res) => {
const modId = req.params["modId"]
// Shell out to steamcmd, monitor the process, and display the output as it runs
res.send(modId + " was installed")
await dz('a', [ modId ], res)
res.end()
})
// Install base files
app.get('/installbase', async (req, res) => {
let which = req.query?.which
const username = fs.readFileSync(config.loginFile, 'utf8')
if (which === "experimental") {
which = config.experimental_server_appid
} else if (which === "stable") {
which = config.stable_server_appid
} else {
sendError(res, "Invalid base file type")
}
let args = `+login "${username}" +app_update "${which}" validate`
const result = await steamcmd(args, res)
if (result.errorCode === 0) {
}
res.send(result)
app.get('/install/server/:which', async (req, res) => {
const which = req.params["which"]
const appid = config.appid[which]
const username = fs.readFileSync(config.login_file, 'utf8')
let args = ['+login', username, '+app_update', appid, 'validate']
const result = await steamcmd(args, which, res)
res.write(JSON.stringify(result))
res.end()
})
// Login to Steam
@ -207,39 +210,40 @@ app.post(('/login'), async (req, res) => {
const password = req.body?.password;
const steamGuardCode = req.body?.steamGuardCode;
const remember = req.body?.remember;
let args = `+login "${username}" "${password}"`
if (steamGuardCode) args += ` "${steamGuardCode}"`
const result = await steamcmd(args)
let args = ['+login', username, password ]
if (steamGuardCode) args.push(steamGuardCode)
const result = await steamcmd(args, 'stable', res)
if (result.errorCode === 0) {
if (remember) {
console.log("Writing login file")
fs.writeFileSync(config.loginFile, username)
fs.writeFileSync(config.login_file, username)
} else {
console.log("Not writing login file")
}
}
res.append('Content-Type', 'application/json')
res.send(result)
res.write(JSON.stringify(result))
res.end()
})
// Logout from Steam
app.get(('/logout'), async (req, res) => {
let result = {"status": 304}
if (fs.existsSync(config.loginFile)) {
fs.unlinkSync(config.loginFile, (err) => {
let result = {"errorCode": 0}
if (fs.existsSync(config.login_file)) {
fs.rmSync('/home/user/.local/share/Steam/userdata', { recursive: true, force: true })
fs.unlinkSync(config.login_file, (err) => {
if (err) {
result.status = 500
result.errorCoder = 1
result.error = err
}
result.status = 200
})
}
res.append('Content-Type', 'application/json')
res.send(result)
})
// Get mod metadata by ID
app.get('/mod/:modId', (req, res) => {
const modId = req.params["modId"]
const modDir = config.modDir + d + modId
const modDir = config.mod_dir + d + modId
const customXML = getCustomXML(modId)
const ret = {
id: modId,
@ -254,8 +258,8 @@ app.get('/mod/:modId', (req, res) => {
app.get('/mod/:modId/:file', (req, res) => {
const modId = req.params["modId"]
const file = req.params["file"]
if (fs.existsSync(config.modDir + d + modId + d + file)) {
const contents = fs.readFileSync(config.modDir + d + modId + d + file)
if (fs.existsSync(config.mod_dir + d + modId + d + file)) {
const contents = fs.readFileSync(config.mod_dir + d + modId + d + file)
res.set('Content-type', 'application/xml')
res.send(contents)
}
@ -282,8 +286,7 @@ app.get(('/remove/:modId'), (req, res) => {
// Search for a mod
app.get(('/search/:searchString'), (req, res) => {
const searchString = req.params["searchString"]
const url = config.searchUrl + searchString
// const url = "https://api.steampowered.com/IPublishedFileService/QueryFiles/v1/?numperpage=1000&appid=221100&return_short_description=true&strip_description_bbcode=true&key=" + config.steamAPIKey + "&search_text=" + searchString
const url = config.search_url + searchString
https.get(url, resp => {
let data = '';
resp.on('data', chunk => {
@ -302,15 +305,19 @@ app.get(('/search/:searchString'), (req, res) => {
If the base files are installed, the version of the server, the appid (If release or experimental)
*/
app.get('/status', (_, res) => {
const installed = fs.existsSync(config.installFile)
const loggedIn = fs.existsSync(config.loginFile)
const ret = {
"appid": config.appid_version,
"installed": installed,
"loggedIn": loggedIn,
"appid": config.version['stable'],
"installed": {
"experimental": fs.existsSync(config.install_file['experimental']),
"stable": fs.existsSync(config.install_file['stable']),
},
"loggedIn": fs.existsSync(config.login_file),
}
if (ret.installed.stable) {
ret.version = {
stable: getVersion('stable'),
experimental: getVersion('experimental')
}
if (installed) {
ret.version = getVersion()
}
res.send(ret)
})
@ -331,29 +338,29 @@ app.get('/test', async (req, res) => {
res.send(ret)
} else if (type === "continuous") {
res.set('Content-Type', 'text/plain')
res.write("data: This is a test server continuous output 1\n")
await new Promise(resolve => setTimeout(resolve, 1000));
res.write("data: This is a test server continuous output 2\n")
await new Promise(resolve => setTimeout(resolve, 2000));
res.write("data: This is a test server continuous output 3 but it's a very long line intended to force wrapping of text because the length is so long and the girth is so gorth\n")
await new Promise(resolve => setTimeout(resolve, 1000));
res.write("data: This is a test server continuous output 4\nDone!")
res.write("This is a test server continuous output 1\n")
await new Promise(resolve => setTimeout(resolve, 1000))
res.write("This is a test server continuous output 2\n")
await new Promise(resolve => setTimeout(resolve, 1000))
res.write("This is a test server continuous output 3 but it's a very long line intended to force wrapping of text because the length is so long and the girth is so gorth\n")
await new Promise(resolve => setTimeout(resolve, 1000))
res.write("This is a test server continuous output 4\n")
await new Promise(resolve => setTimeout(resolve, 1000))
res.write("This is a test server continuous output 5 with a whole SHIT TON of text! Lorem ipsum ain't got nothing on this! Hell yeah! Let's add a lot\nof\n\nnewlines and ellipses and other garbage...\nthis and that...and the other!\nLet's keep pushing this down...WAY DOWN...\nDOWN\nDOWN\nDOWN...\n\n...")
await new Promise(resolve => setTimeout(resolve, 1000))
res.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean lacinia tristique porta. Integer luctus dui non augue egestas, vitae faucibus massa placerat. In ornare sodales risus quis faucibus. Cras viverra mauris vel neque sollicitudin pretium. Integer quis consectetur purus. Nulla sed accumsan tortor. Nulla felis eros, egestas quis eros ut, hendrerit aliquam ante. Nulla sagittis tortor nulla, eu consectetur tellus tempus eget. In hac habitasse platea dictumst. Mauris interdum cursus massa ac vestibulum. Morbi sodales justo sed feugiat consequat. Nunc purus nibh, faucibus id porttitor eget, dignissim eu purus. Duis efficitur varius libero vitae tristique. Mauris libero dolor, tempor at sagittis in, malesuada auctor sapien. Nulla eu accumsan odio. Phasellus dapibus dictum nulla ac feugiat.\n")
await new Promise(resolve => setTimeout(resolve, 500))
res.write("Pellentesque at massa vel eros auctor fringilla. Vestibulum at molestie augue. Proin dictum, tortor quis efficitur finibus, tortor nisi viverra libero, eget placerat lectus dolor a felis. Pellentesque vitae felis vulputate enim feugiat rutrum. Pellentesque auctor tempor eros sed consectetur. Integer id pellentesque massa, quis suscipit nisi. Fusce tempor cursus nulla nec imperdiet. Phasellus sodales iaculis eros, sed auctor lacus elementum vitae. Sed efficitur condimentum risus. Cras varius risus at quam condimentum, vitae cursus leo facilisis. Suspendisse pellentesque erat leo, a cursus augue blandit sed. Aliquam quis nibh vel sapien pulvinar feugiat quis eu diam. Pellentesque ullamcorper vestibulum leo non imperdiet.\n")
await new Promise(resolve => setTimeout(resolve, 500))
res.write("In et nulla risus. Fusce luctus ligula vitae velit lacinia egestas. Nullam semper, nisl vel ultrices semper, magna sem vestibulum ipsum, in pulvinar elit diam ac odio. Etiam id laoreet odio, a vehicula est. Sed luctus lobortis sollicitudin. Morbi hendrerit erat vel lacus pellentesque, eget pretium nisi faucibus. Nunc a orci sed mauris commodo cursus. Morbi at ipsum fermentum, placerat felis at, porta felis. Pellentesque sit amet sollicitudin est, aliquet consequat tortor. Cras efficitur egestas pulvinar. Morbi ultrices, ligula ac luctus ullamcorper, risus metus hendrerit eros, et ultricies diam justo eget lorem. Duis varius pulvinar nulla a luctus. Curabitur sed quam cursus risus pellentesque dignissim id vel arcu.\n")
await new Promise(resolve => setTimeout(resolve, 500))
res.write("This is a test server continuous output 5 with a whole SHIT TON of text! Lorem ipsum ain't got nothing on this! Hell yeah! Let's add a lot\nof\n\nnewlines and ellipses and other garbage...\nthis and that...and the other!\nLet's keep pushing this down...WAY DOWN...\nDOWN\nDOWN\nDOWN...\n\n...\n\nDone!")
res.end()
} else {
res.send("Unknown test type")
}
})
// Update base files
app.get('/updatebase', (req, res) => {
res.send("Base files were updated")
})
// Update mods
app.get('/updatemods', (req, res) => {
res.send("Mod files were updated")
})
ViteExpress.listen(app, config.port, () =>
console.log(`Server is listening on port ${config.port}`)
)