Add support for experimental.

Add streaming text modal for steamcmd operations that run over time with output that will do the same. So far this is just as a test, but it works.
Refactor the store to use actions to set variables that are required to be boolean.
Continue staging test components so when it comes time to do things for real, they'll be ready. Working backwards, always.
Implement Steam login. Needs polish.
Figure out styles. Our index.css is still being loaded before the theme, and its overrides only take effect when those don't set anything in the theme beforehand. Still not working.
Consolidate the config between Vue and express.
Monitor config changes in nodemon.
Everything is WIP.
This commit is contained in:
Daniel Ceregatti 2024-08-09 10:56:41 -07:00
parent 53dea0f45d
commit 9cf22998d2
17 changed files with 449 additions and 172 deletions

View file

@ -1,13 +1,15 @@
volumes:
# Only in the web container.
# For steamcmd files and resource files used by the scripts.
homedir_main:
homedir:
# Shared by all containers.
# Mods.
mods:
# Server files
# Server files for release
serverfiles:
# Server files for experimental
serverfiles_experimental:
# Upstream mission files
mpmissions:
@ -21,8 +23,9 @@ services:
user: ${USER_ID}
hostname: dayzdockerserver
volumes:
- homedir_main:/home/user
- homedir:/home/user
- serverfiles:/serverfiles
- serverfiles_experimental:/serverfiles_experimental
- mpmissions:/serverfiles/mpmissions
- mods:/serverfiles/steamapps/workshop/content
- mods:/mods

View file

@ -5,7 +5,32 @@ const store = useAppStore()
</script>
<template>
<Dialog v-model:visible="store.errorText" modal :header="$t('Error')" :style="{ width: '25rem' }">
<Dialog v-model:visible="store.alert" maximizable modal :style="{ width: '50rem' }">
<template #header>
<div>
{{ $t('Alert') }}
</div>
</template>
<template #default>
<div class="steamcmd">
<pre class="pre">{{ store.alertText }}</pre>
</div>
</template>
<template #footer>
<i v-if="store.alertLoading" 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.error" modal :header="$t('Error')" :style="{ width: '25rem' }">
{{ store.errorText }}
</Dialog>
</template>
<style scoped>
.steamcmd {
min-height: 300px;
}
.pre {
white-space: pre-wrap;
}
</style>

View file

@ -3,17 +3,33 @@ 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.installed) {
if (store.steamStatus.stableInstalled) {
which = '/updatebase'
}
const { data } = await useFetch(which).get().json()
store.errorText = data.value.message
store.setAlert(t(data.value.message))
}
</script>
<template>
<Button @click="base" v-if="store.steamStatus.installed">{{ store.steamStatus.installed ? $t('Update Server Files') : $t('Install Server Files') }}</Button>
<span v-else>{{ $t('Server files not installed') }}</span>
<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>
</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>
</div>
</div>
</div>
</template>

View file

@ -48,3 +48,16 @@ import Status from '@/components/Status.vue'
</div>
</div>
</template>
<style>
.p-tablist-tab-list {
display: flex;
justify-content: center;
}
.p-dialog-footer {
justify-content: center;
}
.p-dialog-header {
justify-content: center;
}
</style>

View file

@ -2,7 +2,7 @@
</script>
<template>
<div class="">
<div>
<h1>{{ $t('DayZ Docker Server') }}</h1>
</div>
</template>

View file

@ -3,8 +3,7 @@ import { useFetch } from "@vueuse/core"
import XmlFile from '@/components/XmlFile.vue'
import { useAppStore } from '@/store.js'
const store = useAppStore()
import { config } from '@/config'
const { data, error } = useFetch(() => config.baseUrl + `/mod/${store.modId}`, {
const { data, error } = useFetch(() => `/mod/${store.modId}`, {
immediate: false,
refetch: true
}).get().json()

View file

@ -1,27 +1,107 @@
<script setup>
import Button from 'primevue/button'
import { useAppStore } from '@/store.js'
import { useFetch } from '@vueuse/core'
const store = useAppStore()
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const test = async (type) => {
const { data, error } = await useFetch('/test?type=' + type).get().json()
if (data.value.alert) {
store.setAlert(t(data.value.alert))
} else if (data.value.error) {
store.setError(t(data.value.error))
} else if (error) {
store.setError(t(error.value))
} else {
store.setError(t('Unknown error'))
}
}
const continuous = async () => {
store.setAlert('')
store.alertLoading = true
const url = '/test?type=continuous'
const response = await fetch(url)
for await (const chunk of response.body) {
store.alertText += new TextDecoder().decode(chunk)
}
store.alertLoading = false
}
</script>
<template>
<div class="col">
<div>
{{ $t('Logged into Steam') }}:
<span v-if="store.steamStatus.loggedIn" class="pi pi-check" style="color: green"></span>
<span v-else class="pi pi-times" style="color: red"></span>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
{{ $t('Logged into Steam') }}:
<span v-if="store.steamStatus.loggedIn" class="pi pi-check" style="color: green"></span>
<span v-else class="pi pi-times" style="color: red"></span>
</div>
</div>
<div>
{{ $t('Server files installed') }}:
<span v-if="store.steamStatus.installed" class="pi pi-check" style="color: green"></span>
<span v-else class="pi pi-times" style="color: red"></span>
</div>
<div v-if="store.steamStatus.version">
{{ $t('Version') }}: <span style="color: green;">{{ store.steamStatus.version }}</span>
<span class="bold">({{ store.steamStatus.appid }})</span>
</div>
<div>
<Button @click="store.errorText = $t('This is an error message')">{{ $t('Test error') }}</Button>
</div>
<div class="grid">
<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-else class="pi pi-times" style="color: red"></span>
</div>
</div>
</div>
</div>
<div class="grid">
<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-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>
<span class="bold">({{ store.steamStatus.appid }})</span>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
<Button @click="store.alertText = $t('This is an alert message')">{{ $t('Test alert') }}</Button>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
<Button @click="store.errorText = $t('This is an error message')">{{ $t('Test error') }}</Button>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
<Button @click="test('alert')">{{ $t('Test server alert response') }}</Button>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
<Button @click="test('error')">{{ $t('Test server error response') }}</Button>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div>
<Button @click="continuous">{{ $t('Test server continuous output') }}</Button>
</div>
</div>
</div>
</template>

View file

@ -1,4 +1,5 @@
<script setup>
import { ref } from 'vue'
import { useFetch } from '@vueuse/core'
import Checkbox from 'primevue/checkbox'
import Button from 'primevue/button'
@ -6,55 +7,87 @@ import InputText from 'primevue/inputtext'
import Password from 'primevue/password'
import { useAppStore } from '@/store.js'
const store = useAppStore()
async function logOut() {
const response = await useFetch('/logout')
if (response.ok) {
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
} else if (response.err) {
store.errorText = response.err
}
}
function submit(e) {
console.log("Submit")
async function logOut() {
const { data } = await useFetch('/logout').get().json()
steamStatus(data)
}
let username = ''
let password = ''
let remember = false
let steamGuardCode = ''
async function login(e) {
loading.value = true
e.preventDefault()
const { data } = await useFetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username.value,
password: password.value,
remember: remember.value,
steamGuardCode: steamGuardCode.value
}).post().json()
})
loading.value = false
steamStatus(data)
}
let loading = ref(false)
let username = ref('')
let password = ref('')
let remember = ref(false)
let steamGuardCode = ref('')
</script>
<template>
<div v-if="store.steamStatus.loggedIn">
<div>{{ $t('Already logged in to steam') }}</div>
<div>
<Button @click="logOut">{{ $t('Log out') }}</Button>
<div>
<div v-if="store.steamStatus.loggedIn" class="grid">
<div class="col-12">{{ $t('Already logged in to steam') }}</div>
<div class="col-12">
<Button @click="logOut">{{ $t('Log out') }}</Button>
</div>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div class="flex flex-column gap-2">
{{ $t('There are no saved Steam credentials. To install the server files and mods, please login to Steam') }}
<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>
<div class="">
<div class="col-12">
</div>
<div class="flex flex-column gap-2">
<div class="col-2 col-offset-4 text-right">
<label for="username">{{ $t('Username') }}</label>
</div>
<div class="col-2 text-left">
<InputText id="username" v-model="username" />
</div>
<div class="flex flex-column gap-2">
<div class="col-2 col-offset-4 text-right">
<label for="password">{{ $t('Password') }}</label>
<Password id="username" v-model="password" :feedback="false" toggleMask />
</div>
<div class="flex flex-column gap-2">
<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">
<InputText id="steamGuardCode" v-model="steamGuardCode" />
</div>
<div class="flex flex-column gap-2">
<div class="col-2 col-offset-4 text-right">
<label for="remember">{{ $t('Remember Credentials') }}</label>
<Checkbox v-model="remember" />
</div>
<div class="flex flex-column gap-2">
<Button type="submit" onclick="submit()">{{ $t('Submit') }}</Button>
<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>
</div>

View file

@ -1,10 +1,9 @@
<script setup>
import { useFetch } from '@vueuse/core'
import { config } from '@/config'
import { useAppStore } from '@/store.js'
import XmlTree from '@/components/XmlTree.vue'
const store = useAppStore()
const { data, error } = await useFetch(() => config.baseUrl + `/mod/${store.modId}/${store.modFile}`, {
const { data, error } = await useFetch(() => `/mod/${store.modId}/${store.modFile}`, {
immediate: false,
refetch: true,
afterFetch(response) {

View file

@ -1,6 +1,36 @@
/*
* The stable DayZ server Steam app ID.
*/
const stable_server_appid = 223350
/*
* The experimental DayZ server Steam app ID.
*/
const experimental_server_appid = 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 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 config = {
baseUrl: window.location.protocol + '//' + window.location.hostname + ':8000',
steamUrl: 'https://steamcommunity.com/sharedfiles/filedetails/?id='
client_appid: client_appid,
experimental_server_appid: experimental_server_appid,
installFile: serverFiles + "/DayZServer",
loginFile: homeDir + "/steamlogin",
modDir: "/mods/" + client_appid,
port: 8000,
searchUrl: searchUrl,
serverFiles: serverFiles,
stable_server_appid: stable_server_appid,
steamUrl: 'https://steamcommunity.com/sharedfiles/filedetails/?id=',
}
export { config }

View file

@ -1,8 +1,3 @@
.p-tablist-tab-list {
display: flex;
justify-content: center;
}
.active {
background-color: cyan;
}

View file

@ -1,4 +1,5 @@
export const en = {
'Alert': 'Alert',
'Already logged in to steam': 'Already logged in to steam',
'DayZ': 'DayZ',
'DayZ Docker Server': 'DayZ Docker Server',
@ -8,7 +9,8 @@ export const en = {
'Home': 'Home',
'Install': 'Install',
'Installed mods': 'Installed mods',
'Install Server Files': 'Install Server Files',
'Install Stable Server Files': 'Install Stable Server Files',
'Install Experimental Server Files': 'Install Experimental Server Files',
'Log out': 'Log out',
'Logged into Steam': 'Logged into Steam',
'Mods': 'Mods',
@ -21,18 +23,27 @@ export const en = {
'Remember Credentials': 'Remember Credentials',
'Search': 'Search',
'Server files installed': 'Server files installed',
'Server files not installed': 'Server files not installed',
'Server files are installed': 'Server files are installed',
'Server files were successfully installed': 'Server files were successfully installed',
'Servers': 'Servers',
'Stable': 'Stable',
'Status': 'Status',
'Steam': 'Steam',
'Steam Guard Code': 'Steam Guard Code',
'Submit': 'Submit',
'Test alert': 'Test alert',
'Test error': 'Test error',
'Test server alert response': 'Test server alert response',
'Test server continuous output': 'Test server continuous output',
'Test server error response': 'Test server error response',
'There are no saved Steam credentials. To install the server files and mods, please login to Steam': 'There are no saved Steam credentials. To install the server files and mods, please login to Steam',
'This is an alert message': 'This is an alert message',
'This is an error message': 'This is an error message',
'This is a test server alert': 'This is a test server alert',
'This is a test server error': 'This is a test server error',
'Total servers': 'Total servers',
'Total workshop items': 'Total workshop items',
'Unknown error': 'Unknown error',
'Up to date': 'Up to date',
'Update': 'Update',
'Update Server Files': 'Update Server Files',

View file

@ -1,4 +1,5 @@
export const pt = {
'Alert': 'Alerta',
'Already logged in to steam': 'Já registrado com Steam',
'DayZ': 'DayZ',
'DayZ Docker Server': 'Servidor DayZ de Docker',
@ -8,31 +9,41 @@ export const pt = {
'Home': 'Início',
'Install': 'Instalar',
'Installed mods': 'Mods instalados',
'Install Server Files': 'Instalar Arquivos de Servidor',
'Install Stable Server Files': 'Instalar Arquivos de Servidor Estável',
'Install Experimental Server Files': 'Instalar Arquivos de Servidor Experimental',
'Log out': 'Encerrar sessão',
'Logged into Steam': 'Registrado com Steam',
'Mods': 'Mods',
'Name': 'Nome',
'No mods are installed': 'Nenhum mod está installado',
'No mods are installed': 'Nenhum mod está instalado',
'No servers have been created': 'Nenhum servidor foi criado',
'Not Installed': 'Não Instalado',
'Password': 'Senha',
'Release': 'Lançado',
'Remember Credentials': 'Lembrar Credenciais',
'Search': 'Procurar',
'Server files installed': 'Arquivos de servidor instalado',
'Server files not installed': 'Arquivos de servidor não instalados',
'Server files installed': 'Arquivos de servidor instalados',
'Server files are installed': 'Arquivos de servidor estão instalados',
'Server files were successfully installed': 'Arquivos de servidor foram instalados com sucesso',
'Servers': 'Servidores',
'Stable': 'Estável',
'Status': 'Estado',
'Steam': 'Steam',
'Steam Guard Code': 'Còdigo Steam Guard',
'Submit': 'Enviar',
'Test alert': 'Testar alerta',
'Test error': 'Testar erro',
'There are no saved Steam credentials. To install the server files and mods, please login to Steam': 'Não existe credencias salvados. Para instalar o servidor, por favor registre-se no Steam',
'Test server alert response': 'Testar resposta de alerta de servidor',
'Test server continuous output': 'Testar saída contínua de servidor',
'Test server error response': 'Testar resposta de erro de servidor',
'There are no saved Steam credentials. To install the server files and mods, please login to Steam': 'Não existem credencias salvados. Para instalar o servidor, por favor registre-se no Steam',
'This is a test server alert': 'Este é um teste de alerta de servidor',
'This is a test server error': 'Este é um teste de erro de servidor',
'This is an alert message': 'Esta é uma mensagem de alerta',
'This is an error message': 'Esta é uma mensagem de erro',
'Total servers': 'Total de servidores',
'Total workshop items': 'Total de itens de oficina',
'Unknown error': 'Erro desconhecido',
'Up to date': 'Atualizado',
'Update': 'Atualizar',
'Update Server Files': 'Atualizer Arquivos de Servidor',

View file

@ -2,13 +2,65 @@ import { defineStore } from 'pinia'
export const useAppStore = defineStore('app', {
state: () => ({
errorText: false,
alert: false,
alertLoading: false,
alertText: '',
error: false,
errorText: '',
loading: false,
modId: 0,
modFile: false,
messageText: false,
mods: [],
searchText: false,
servers: [],
steamStatus: {appid: 0, installed: false, loggedIn: false, version: ''},
})
steamStatus: {
appid: 0,
experimentalInstalled: false,
loggedIn: false,
stableInstalled: false,
version: ''
},
}),
actions: {
setAlert(alertText, loading = false) {
this.alert = true
this.setAlertLoading(loading)
if (loading) {
this.alertText += alertText
} else {
this.alertText = alertText
}
},
setAlertLoading(alertLoading) {
this.alertLoading = alertLoading
},
setError(error) {
this.error = error
},
setLoading(loading) {
this.loading = loading
},
setModId(modId) {
this.modId = modId
},
setModFile(modFile) {
this.modFile = modFile
},
setMessageText(messageText) {
this.messageText = messageText
},
setMods(mods) {
this.mods = mods
},
setSearchText(searchText) {
this.searchText = searchText
},
setServers(servers) {
this.servers = servers
},
setSteamStatus(steamStatus) {
this.steamStatus = steamStatus
},
},
})

View file

@ -12,41 +12,7 @@ import path from 'path'
import fs from 'fs'
import https from 'https'
import { spawn } from 'child_process'
/*
The DayZ server Steam app ID. USE ONE OR THE OTHER!!
Presumably once the Linux server is officially released, the binaries will come from this ID.
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"
/*
Denote if it's release or experimental
*/
const versions = {
"1042420": "Experimental",
"223350": "Release"
}
const appid_version = versions[server_appid]
/*
Base file locations
*/
const modDir = "/mods"
const serverFiles = "/serverfiles"
const homeDir = "/home/user"
import { config } from "./docroot/src/config.js";
/*
File path delimiter
@ -106,14 +72,6 @@ const allConfigFiles = {
]
}
const config = {
installFile: serverFiles + "/DayZServer",
loginFile: homeDir + "/steamlogin",
modDir: modDir + "/" + client_appid,
port: 8000,
steamAPIKey: process.env["STEAMAPIKEY"]
}
const getVersion = () => {
return "1.25.bogus"
}
@ -146,7 +104,7 @@ const getCustomXML = (modId) => {
}
const getModNameById = (id) => {
const files = fs.readdirSync(serverFiles, {encoding: 'utf8', withFileTypes: true})
const files = fs.readdirSync(config.serverFiles, {encoding: 'utf8', withFileTypes: true})
for (const file of files) {
if (file.isSymbolicLink()) {
const sym = fs.readlinkSync(serverFiles + d + file.name)
@ -166,28 +124,41 @@ const getMods = () => {
return mods
}
const steamcmd = async (args) => {
let out = ''
let err = ''
const command = 'steamcmd +force_install_dir ' + serverFiles + ' ' + args + ' +quit'
// console.log(command)
const proc = spawn(command, {shell: true})
proc.stdout.on('data', (data) => {
// console.log("[OUT] " + data)
out += data + "\n"
})
proc.stderr.on('data', (data) => {
// console.log("[ERROR] " + data)
err += data + "\n"
})
proc.on('error', (error) => {
// console.log("[ERROR] " + error)
err += error + "\n"
})
proc.on('close', (error) => {
if(error) err += error
// console.log("Return")
return { out: out, err: err }
const sendError = (res, message) => {
res.send({"errorCode": 42, "error": message})
}
const sendAlert = (res, message) => {
res.send({"alert": message})
}
const steamcmd = async (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})
proc.stdout.on('data', (data) => {
const out = "[OUT] " + data + "\n"
console.log(out)
res.write(out)
stdout += out
})
proc.stderr.on('data', (data) => {
const err = "[ERROR] " + data + "\n"
console.log(err)
stderr += err
})
proc.on('error', (data) => {
const err = "[ERROR] " + data + "\n"
console.log(err)
stderr += err
})
proc.on('close', (errorCode) => {
console.log("Close")
resolve({ out: stdout, err: stderr, errorCode: errorCode })
})
})
}
@ -213,41 +184,56 @@ app.get(('/install/:modId'), (req, res) => {
})
// Install base files
app.get('/installbase', (req, res) => {
const ret = {
"message": "Base files were installed"
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")
}
res.send(ret)
let args = `+login "${username}" +app_update "${which}" validate`
const result = await steamcmd(args, res)
if (result.errorCode === 0) {
}
res.send(result)
})
// Login to Steam
app.post(('/login'), async (req, res) => {
const username = req.body?.username;
const password = req.body?.password;
const guard = req.body?.guard;
const steamGuardCode = req.body?.steamGuardCode;
const remember = req.body?.remember;
let args = `+login "${username}" "${password}"`
if (guard) args += ` "${guard}"`
if (steamGuardCode) args += ` "${steamGuardCode}"`
const result = await steamcmd(args)
if (remember) {
fs.writeFileSync(config.loginFile, username)
}
if (result) {
fs.writeFileSync(config.loginFile, username)
res.send({"ok": 1})
} else {
res.send({"ok": 0})
if (result.errorCode === 0) {
if (remember) {
console.log("Writing login file")
fs.writeFileSync(config.loginFile, username)
}
}
res.append('Content-Type', 'application/json')
res.send(result)
})
// Logout from Steam
app.get(('/logout'), async (req, res) => {
fs.unlink(config.loginFile, (err) => {
if (err) {
return res.send({"ok": 0, "error:": err})
}
res.send({"ok": 1})
})
let result = {"status": 304}
if (fs.existsSync(config.loginFile)) {
fs.unlinkSync(config.loginFile, (err) => {
if (err) {
result.status = 500
result.error = err
}
result.status = 200
})
}
res.append('Content-Type', 'application/json')
res.send(result)
})
// Get mod metadata by ID
@ -296,7 +282,8 @@ app.get(('/remove/:modId'), (req, res) => {
// Search for a mod
app.get(('/search/:searchString'), (req, res) => {
const searchString = req.params["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.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
https.get(url, resp => {
let data = '';
resp.on('data', chunk => {
@ -318,7 +305,7 @@ app.get('/status', (_, res) => {
const installed = fs.existsSync(config.installFile)
const loggedIn = fs.existsSync(config.loginFile)
const ret = {
"appid": appid_version,
"appid": config.appid_version,
"installed": installed,
"loggedIn": loggedIn,
}
@ -328,6 +315,35 @@ app.get('/status', (_, res) => {
res.send(ret)
})
app.get('/test', async (req, res) => {
const type = req.query.type
if (type === "error") {
const ret = {
"errorCode": 42,
"alert": "This is a test server error",
}
res.send(ret)
} else if (type === "alert") {
const ret = {
"errorCode": 0,
"alert": "This is a test server alert",
}
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, 1000));
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.end()
} else {
res.send("Unknown test type")
}
})
// Update base files
app.get('/updatebase', (req, res) => {
res.send("Base files were updated")
@ -341,9 +357,3 @@ app.get('/updatemods', (req, res) => {
ViteExpress.listen(app, config.port, () =>
console.log(`Server is listening on port ${config.port}`)
)
// const server = app.listen(config.port, "0.0.0.0", () =>
// console.log(`Server is listening on port ${config.port}`)
// )
//
// ViteExpress.bind(app, server)

View file

@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"dev": "nodemon index.js -w index.js",
"dev": "nodemon index.js -w index.js -w docroot/src/config.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],

View file

@ -6,7 +6,7 @@ export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': '/web/docroot/src'
'@': './web/docroot/src/'
}
},
server: {