mirror of
https://ceregatti.org/git/daniel/dayzdockerserver.git
synced 2025-05-07 14:51:17 +00:00
Merge pull request 'release-server' (#2) from release-server into main
Reviewed-on: https://ceregatti.org/git/daniel/dayzdockerserver/pulls/2
This commit is contained in:
commit
35c0f0a3ba
20 changed files with 193 additions and 143 deletions
|
@ -34,7 +34,7 @@ services:
|
||||||
- "8000:8000/tcp"
|
- "8000:8000/tcp"
|
||||||
restart: no
|
restart: no
|
||||||
environment:
|
environment:
|
||||||
# The use of the Steam API requires a key. Get yours here: https://steamcommunity.com/dev/apikey
|
# The use of the Steam API in the (very incomplete) web app requires a key. Get yours here: https://steamcommunity.com/dev/apikey
|
||||||
- STEAMAPIKEY=YOUR_STEAM_API_KEY_HERE
|
- STEAMAPIKEY=YOUR_STEAM_API_KEY_HERE
|
||||||
|
|
||||||
server:
|
server:
|
||||||
|
@ -47,7 +47,6 @@ services:
|
||||||
- mpmissions:/serverfiles/mpmissions
|
- mpmissions:/serverfiles/mpmissions
|
||||||
- profiles:/profiles
|
- profiles:/profiles
|
||||||
- ./files:/files
|
- ./files:/files
|
||||||
- ./server:/server
|
|
||||||
- ./server/bin/dz:/usr/local/bin/dz
|
- ./server/bin/dz:/usr/local/bin/dz
|
||||||
# To have the server show up in the LAN tab of the DayZ launcher,
|
# To have the server show up in the LAN tab of the DayZ launcher,
|
||||||
# it must run under host mode.
|
# it must run under host mode.
|
||||||
|
@ -59,7 +58,7 @@ services:
|
||||||
# # Game port
|
# # Game port
|
||||||
# - 2302:2302/udp
|
# - 2302:2302/udp
|
||||||
# # RCON port
|
# # RCON port
|
||||||
# - 2302:2302/udp
|
# - 2303:2303/udp
|
||||||
# # Steam port
|
# # Steam port
|
||||||
# - 27016:27016/udp
|
# - 27016:27016/udp
|
||||||
# Always restart, unless stopped
|
# Always restart, unless stopped
|
||||||
|
|
|
@ -25,9 +25,9 @@ cyan="\e[36m"
|
||||||
# Presumably once the Linux server is released, the binaries will come from this ID.
|
# Presumably once the Linux server is released, the binaries will come from this ID.
|
||||||
# But more importantly, if we have a release-compatible binary, the base files must be installed from this id,
|
# But more importantly, if we have a release-compatible binary, the base files must be installed from this id,
|
||||||
# even if the server binary and accompanying shared object don't come from it.
|
# even if the server binary and accompanying shared object don't come from it.
|
||||||
#release_server_appid=223350
|
release_server_appid=223350
|
||||||
# Without a release binary, we must use the experimental server app id for everything.
|
# Without a release binary, we must use the experimental server app id for everything.
|
||||||
release_server_appid=1042420
|
#release_server_appid=1042420
|
||||||
|
|
||||||
# DayZ release client SteamID. This is for mods, as only the release client has them.
|
# DayZ release client SteamID. This is for mods, as only the release client has them.
|
||||||
release_client_appid=221100
|
release_client_appid=221100
|
||||||
|
@ -72,7 +72,7 @@ list(){
|
||||||
for dir in $(ls -tr ${WORKSHOP_DIR})
|
for dir in $(ls -tr ${WORKSHOP_DIR})
|
||||||
do
|
do
|
||||||
ID=${dir}
|
ID=${dir}
|
||||||
NAME=$(grep name "${WORKSHOP_DIR}/${dir}/meta.cpp" | cut -d '"' -f2 | sed -r 's/\s+//g')
|
NAME=$(grep name "${WORKSHOP_DIR}/${dir}/meta.cpp" | cut -d '"' -f2 | tr -cd [:alnum:])
|
||||||
SIZE=$(du -sh "${WORKSHOP_DIR}/${dir}" | awk '{print $1}')
|
SIZE=$(du -sh "${WORKSHOP_DIR}/${dir}" | awk '{print $1}')
|
||||||
printf "${C}%.3d %s %.30s %s https://steamcommunity.com/sharedfiles/filedetails/?id=%s %s${default}\n" ${X} ${ID} "${NAME}" "${spaces:${#NAME}+1}" ${ID} ${SIZE}
|
printf "${C}%.3d %s %.30s %s https://steamcommunity.com/sharedfiles/filedetails/?id=%s %s${default}\n" ${X} ${ID} "${NAME}" "${spaces:${#NAME}+1}" ${ID} ${SIZE}
|
||||||
X=$((X+1))
|
X=$((X+1))
|
||||||
|
|
BIN
files/tmp/libsteam_api.so
Executable file
BIN
files/tmp/libsteam_api.so
Executable file
Binary file not shown.
BIN
files/tmp/steamclient.so
Executable file
BIN
files/tmp/steamclient.so
Executable file
Binary file not shown.
|
@ -12,7 +12,6 @@ RUN echo 'deb http://deb.debian.org/debian bullseye-backports main non-free' >>
|
||||||
RUN apt-get update && apt-get -y upgrade && apt-get -y install --no-install-recommends \
|
RUN apt-get update && apt-get -y upgrade && apt-get -y install --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
gdb \
|
|
||||||
git \
|
git \
|
||||||
jq \
|
jq \
|
||||||
libsdl2-2.0-0 \
|
libsdl2-2.0-0 \
|
||||||
|
@ -21,14 +20,9 @@ RUN apt-get update && apt-get -y upgrade && apt-get -y install --no-install-reco
|
||||||
locales \
|
locales \
|
||||||
nano \
|
nano \
|
||||||
procps \
|
procps \
|
||||||
python3-pip \
|
|
||||||
strace \
|
|
||||||
wget \
|
wget \
|
||||||
xmlstarlet
|
xmlstarlet
|
||||||
|
|
||||||
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1
|
|
||||||
RUN update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
|
|
||||||
|
|
||||||
# Set the locale
|
# Set the locale
|
||||||
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
|
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
|
||||||
ENV LANG en_US.UTF-8
|
ENV LANG en_US.UTF-8
|
||||||
|
@ -47,6 +41,9 @@ RUN groupadd user && \
|
||||||
mkdir -p /home/user /serverfiles/mpmissions /mods /mpmissions /profiles && \
|
mkdir -p /home/user /serverfiles/mpmissions /mods /mpmissions /profiles && \
|
||||||
chown -R user:user /home/user /serverfiles /mods /mpmissions /profiles
|
chown -R user:user /home/user /serverfiles /mods /mpmissions /profiles
|
||||||
|
|
||||||
|
# Add our startup script, as this rarely changes.
|
||||||
|
COPY --chown=user:user start.sh /usr/local/bin/start.sh
|
||||||
|
|
||||||
# Use our non-privileged user
|
# Use our non-privileged user
|
||||||
USER user
|
USER user
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ parameters="-config=${SERVER_CFG_DST} -port=${port} -freezecheck -BEpath=${SERVE
|
||||||
|
|
||||||
# Where mods are installed.
|
# Where mods are installed.
|
||||||
WORKSHOP_DIR="/mods/${release_client_appid}"
|
WORKSHOP_DIR="/mods/${release_client_appid}"
|
||||||
mod_command_line=""
|
|
||||||
|
|
||||||
# Backups
|
# Backups
|
||||||
BACKUP_DIR="${HOME}/backup"
|
BACKUP_DIR="${HOME}/backup"
|
||||||
|
@ -27,6 +26,8 @@ then
|
||||||
mkdir -p "${BACKUP_DIR}"
|
mkdir -p "${BACKUP_DIR}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mod_command_line=""
|
||||||
|
|
||||||
# Functions
|
# Functions
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
@ -144,7 +145,7 @@ report() {
|
||||||
mergexml(){
|
mergexml(){
|
||||||
# First copy the pristine files from upstream
|
# First copy the pristine files from upstream
|
||||||
echo "Copying pristine versions of cfgeconomycore.xml and cfgeventspawns.xml..."
|
echo "Copying pristine versions of cfgeconomycore.xml and cfgeventspawns.xml..."
|
||||||
find /mpmissions -name cfgeconomycore.xml -exec cp {} ${SERVER_FILES}{} \;
|
`` find /mpmissions -name cfgeconomycore.xml -exec cp -v {} ${SERVER_FILES}{} \;
|
||||||
# find /mpmissions -name cfgeventspawns.xml -exec cp {} ${SERVER_FILES}{} \;
|
# find /mpmissions -name cfgeventspawns.xml -exec cp {} ${SERVER_FILES}{} \;
|
||||||
|
|
||||||
# Follow https://community.bistudio.com/wiki/DayZ:Central_Economy_mission_files_modding and make a single XML
|
# Follow https://community.bistudio.com/wiki/DayZ:Central_Economy_mission_files_modding and make a single XML
|
||||||
|
@ -263,7 +264,7 @@ config(){
|
||||||
get_mod_id_by_index(){
|
get_mod_id_by_index(){
|
||||||
X=1
|
X=1
|
||||||
# Loop over mods
|
# Loop over mods
|
||||||
for link in $(ls -tdr ${SERVER_PROFILE}/@* 2> /dev/null)
|
for link in $(ls -tdr ${SERVER_FILES}/@* 2> /dev/null)
|
||||||
do
|
do
|
||||||
ID=$(readlink ${link} | awk -F/ '{print $NF}')
|
ID=$(readlink ${link} | awk -F/ '{print $NF}')
|
||||||
if [[ ${X} = ${1} ]]
|
if [[ ${X} = ${1} ]]
|
||||||
|
@ -283,7 +284,7 @@ get_mod_name(){
|
||||||
echo "Mod ID ${1} doesn't exist" >&2
|
echo "Mod ID ${1} doesn't exist" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
NAME=$(grep name ${WORKSHOP_DIR}/${ID}/meta.cpp | cut -d '"' -f2 | sed -r 's/\s+//g')
|
NAME=$(grep name ${WORKSHOP_DIR}/${ID}/meta.cpp | cut -d '"' -f2 | tr -cd [:alnum:])
|
||||||
echo -n ${NAME}
|
echo -n ${NAME}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +300,7 @@ activate(){
|
||||||
UU="un"
|
UU="un"
|
||||||
COLOR="${red}"
|
COLOR="${red}"
|
||||||
fi
|
fi
|
||||||
ID=$(get_mod_id_by_index2 ${1})
|
ID=$(get_mod_id_by_index ${1})
|
||||||
MODNAME=$(get_mod_name ${ID})
|
MODNAME=$(get_mod_name ${ID})
|
||||||
# Toggle state or report nothing burger
|
# Toggle state or report nothing burger
|
||||||
pushd "${SERVER_PROFILE}" > /dev/null
|
pushd "${SERVER_PROFILE}" > /dev/null
|
||||||
|
|
|
@ -22,8 +22,6 @@ RUN apt-get update && apt-get -y upgrade && apt-get -y install --no-install-reco
|
||||||
lib32gcc-s1 \
|
lib32gcc-s1 \
|
||||||
lib32stdc++6 \
|
lib32stdc++6 \
|
||||||
libcurl4:i386 \
|
libcurl4:i386 \
|
||||||
libsdl2-2.0-0:i386 \
|
|
||||||
libsdl2-2.0-0 \
|
|
||||||
libcap2 \
|
libcap2 \
|
||||||
libxml2-utils \
|
libxml2-utils \
|
||||||
locales \
|
locales \
|
||||||
|
@ -73,6 +71,9 @@ RUN groupadd user && \
|
||||||
mkdir -p /home/user /serverfiles/mpmissions /serverfiles/steamapps/workshop/content /web && \
|
mkdir -p /home/user /serverfiles/mpmissions /serverfiles/steamapps/workshop/content /web && \
|
||||||
chown -R user:user /home/user /serverfiles /web
|
chown -R user:user /home/user /serverfiles /web
|
||||||
|
|
||||||
|
# Add our startup script, as this rarely changes.
|
||||||
|
COPY --chown=user:user start.sh /usr/local/bin/start.sh
|
||||||
|
|
||||||
# Use our non-privileged user
|
# Use our non-privileged user
|
||||||
USER user
|
USER user
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,10 @@ source dz-common
|
||||||
|
|
||||||
# An array to store Workshop items. Each element contains the mod's ID, name, and state (active or not).
|
# An array to store Workshop items. Each element contains the mod's ID, name, and state (active or not).
|
||||||
WORKSHOP_DIR="/mods/${release_client_appid}"
|
WORKSHOP_DIR="/mods/${release_client_appid}"
|
||||||
|
if [ ! -d ${WORKSHOP_DIR} ]
|
||||||
|
then
|
||||||
|
mkdir -p ${WORKSHOP_DIR}
|
||||||
|
fi
|
||||||
|
|
||||||
workshoplist=""
|
workshoplist=""
|
||||||
|
|
||||||
|
@ -59,13 +63,13 @@ get_mod_name(){
|
||||||
echo "Mod ID ${1} doesn't exist" >&2
|
echo "Mod ID ${1} doesn't exist" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
NAME=$(grep name ${WORKSHOP_DIR}/${ID}/meta.cpp | cut -d '"' -f2 | sed -r 's/\s+//g')
|
NAME=$(grep name ${WORKSHOP_DIR}/${ID}/meta.cpp | cut -d '"' -f2 | tr -cd [:alnum:])
|
||||||
echo -n ${NAME}
|
echo -n ${NAME}
|
||||||
}
|
}
|
||||||
|
|
||||||
# "Manage" XML files.
|
# "Manage" XML files.
|
||||||
xml(){
|
xml(){
|
||||||
/files/mods/xml.sh ${1}
|
/files/bin/xml.sh ${1}
|
||||||
mergexml ${1}
|
mergexml ${1}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import Mods from '@/components/Mods.vue'
|
import Mods from '@/components/Mods.vue'
|
||||||
import ModInfo from '@/components/Modinfo.vue'
|
|
||||||
import SearchResults from "@/components/SearchResults.vue";
|
import SearchResults from "@/components/SearchResults.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="row flex-grow-1">
|
<SearchResults />
|
||||||
<Mods />
|
<Mods />
|
||||||
<ModInfo />
|
|
||||||
<SearchResults />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,28 +1,39 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import Search from '@/components/Search.vue'
|
import Search from '@/components/Search.vue'
|
||||||
import Status from '@/components/Status.vue'
|
import Status from '@/components/Status.vue'
|
||||||
|
import Servers from '@/components/Servers.vue'
|
||||||
import { useFetch } from '@vueuse/core'
|
import { useFetch } from '@vueuse/core'
|
||||||
const { error, data: status } = await useFetch('http://bubba:8000/status').get().json()
|
import { useAppStore } from '@/stores/app.js'
|
||||||
|
const store = useAppStore()
|
||||||
|
const { error, data } = await useFetch('http://bubba:8000/status').get().json()
|
||||||
|
const set = (w, e) => {
|
||||||
|
store.section = w
|
||||||
|
const active = Array.from(document.getElementsByClassName('active'))
|
||||||
|
active.forEach((a) => a.classList.remove('active'))
|
||||||
|
e.target.classList.add('active')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="status" class="row">
|
<div v-if="data" class="row">
|
||||||
<div class="col-3 text-center">
|
<div class="col-3 text-center">
|
||||||
<h1>DayZ Docker Server</h1>
|
<h1>DayZ Docker Server</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
<button
|
<button
|
||||||
@click="installbase"
|
@click="installbase"
|
||||||
:class="'btn btn-sm ' + (status.installed ? 'btn-danger' : 'btn-success')"
|
:class="'btn btn-sm ' + (data.installed ? 'btn-danger' : 'btn-success')"
|
||||||
>
|
>
|
||||||
Install Server Files
|
Install Server Files
|
||||||
</button>
|
</button>
|
||||||
<button @click="updatebase" class="btn btn-sm btn-success">Update Server Files</button>
|
<button @click="updatebase" class="btn btn-sm btn-outline-success">Update Server Files</button>
|
||||||
<button @click="updatemods" class="btn btn-sm btn-success">Update Mods</button>
|
<button @click="updatemods" class="btn btn-sm btn-outline-success">Update Mods</button>
|
||||||
<button @click="servers" class="btn btn-sm btn-primary">Servers</button>
|
<button type="button" @click="set('servers', $event)" class="btn btn-sm btn-outline-primary">Servers</button>
|
||||||
<button @click="listmods" class="btn btn-sm btn-primary">Mods</button>
|
<button type="button" @click="set('mods', $event)" class="btn btn-sm btn-outline-primary active" data-bs-toggle="button">Mods</button>
|
||||||
|
<button type="button" @click="set('search', $event)" class="btn btn-sm btn-outline-primary">Search</button>
|
||||||
</div>
|
</div>
|
||||||
<Search />
|
<Search />
|
||||||
<Status :status="status" />
|
<Status :status="data" />
|
||||||
|
<Servers />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,37 +1,50 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useFetch} from '@vueuse/core'
|
import { config } from '@/config'
|
||||||
|
import { useFetch } from '@vueuse/core'
|
||||||
import { useAppStore } from '@/stores/app.js'
|
import { useAppStore } from '@/stores/app.js'
|
||||||
|
import ModInfo from '@/components/Modinfo.vue'
|
||||||
const store = useAppStore()
|
const store = useAppStore()
|
||||||
const { data, error } = await useFetch('http://bubba:8000/mods').get().json()
|
const { data, error } = useFetch(config.baseUrl + '/mods', {
|
||||||
|
afterFetch(ctx) {
|
||||||
|
store.mods = ctx.data.mods
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
}).get().json()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="col-md-3 border">
|
<div class="row flex-grow-1" v-if="store.section === 'mods'">
|
||||||
<div v-if="data">
|
<div v-if="error" class="row text-danger">
|
||||||
<h4 class="text-center">Installed Mods</h4>
|
{{ error }}
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Steam Link</th>
|
|
||||||
<th>Mod Name</th>
|
|
||||||
</tr>
|
|
||||||
<template
|
|
||||||
v-for="mod in data.mods"
|
|
||||||
>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
:href="steamUrl + mod.id"
|
|
||||||
>
|
|
||||||
{{ mod.id }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a class="simulink" @click="store.modId=parseInt(mod.id)">{{ mod.name }}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-3 border" v-if="data">
|
||||||
|
<div>
|
||||||
|
<h4 class="text-center">Installed Mods</h4>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Steam Link</th>
|
||||||
|
<th>Mod Name</th>
|
||||||
|
</tr>
|
||||||
|
<template
|
||||||
|
v-for="mod in data.mods"
|
||||||
|
>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
:href="config.steamUrl + mod.id"
|
||||||
|
>
|
||||||
|
{{ mod.id }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="simulink" @click="store.modId=parseInt(mod.id)">{{ mod.name }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ModInfo />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,7 +5,7 @@ const store = useAppStore()
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="col form-control-lg text-center">
|
<div class="col form-control-lg text-center">
|
||||||
<form @submit.prevent="(e) => store.searchText=e.target.search.value">
|
<form @submit.prevent="(e) => {store.searchText=e.target.search.value; store.section='search'}">
|
||||||
<input name="search" placeholder="Search mods..." autofocus>
|
<input name="search" placeholder="Search mods..." autofocus>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,79 +1,77 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { config } from '@/config'
|
||||||
|
import { BKMG } from '@/util'
|
||||||
import { useFetch} from '@vueuse/core'
|
import { useFetch} from '@vueuse/core'
|
||||||
import { useAppStore } from '@/stores/app.js'
|
import { useAppStore } from '@/stores/app.js'
|
||||||
const store = useAppStore()
|
const store = useAppStore()
|
||||||
const { data: searchResults, error } = useFetch(() => `http://bubba:8000/search/${store.searchText}`, {
|
const { data: searchResults, error, isFetching } = useFetch(() => `http://bubba:8000/search/${store.searchText}`, {
|
||||||
immediate: false,
|
immediate: false,
|
||||||
refetch: true
|
refetch: true,
|
||||||
|
afterFetch(response) {
|
||||||
|
// const sortField = "time_updated"
|
||||||
|
const sortField = "lifetime_subscriptions"
|
||||||
|
response.data.response.publishedfiledetails.sort((a, b) =>
|
||||||
|
a[sortField] < b[sortField] ? 1 : -1
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
}
|
||||||
}).get().json()
|
}).get().json()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "SearchResults",
|
|
||||||
methods: {
|
|
||||||
handleSubmit(e) {
|
|
||||||
e.preventDefault()
|
|
||||||
fetch(this.apihost + '/search/' + e.target.search.value)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(response => {
|
|
||||||
this.modInfo = ""
|
|
||||||
this.XMLInfo = ""
|
|
||||||
// const sortField = "time_updated"
|
|
||||||
const sortField = "lifetime_subscriptions"
|
|
||||||
response.response.publishedfiledetails.sort((a, b) =>
|
|
||||||
a[sortField] < b[sortField] ? 1 : -1
|
|
||||||
)
|
|
||||||
this.searchResults = response.response.publishedfiledetails
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// Enable all tooltips
|
|
||||||
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
|
||||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|
||||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
|
||||||
})
|
|
||||||
// Enable all alerts
|
|
||||||
// $('.alert').alert()
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
this.fetchError = error.message
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="searchResults" class="d-flex">
|
<div v-if="store.section === 'search'">
|
||||||
<table>
|
<div v-if="error" class="row text-danger">
|
||||||
<tr>
|
{{ error }}
|
||||||
<th>Steam Link</th>
|
</div>
|
||||||
<th>Title</th>
|
<div v-if="store.searchText === ''">
|
||||||
<th>Size</th>
|
<div class="row justify-content-center">
|
||||||
<th>Last Updated</th>
|
<div class="col-4">
|
||||||
<th>Subscriptions</th>
|
<h2>Search for something...</h2>
|
||||||
<th></th>
|
</div>
|
||||||
</tr>
|
</div>
|
||||||
<tr v-for="result in searchResults">
|
</div>
|
||||||
<td>
|
<div v-if="isFetching" class="row justify-content-center">
|
||||||
<a
|
<div class="col-1 text-end">
|
||||||
target="_blank"
|
<div class="spinner-border" role="status"></div>
|
||||||
:href="steamURL + result.publishedfileid"
|
</div>
|
||||||
>
|
<div class="col-4">
|
||||||
<img :alt="result.short_description" data-bs-toggle="tooltip" data-bs-placement="left" :title="result.short_description" width="160" height="90" :src="result.preview_url">
|
<h2>Searching for <strong>"{{ store.searchText }}"...</strong></h2>
|
||||||
</a>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td>{{ result.title }}</td>
|
<template v-if="searchResults && ! isFetching">
|
||||||
<td>{{ BKMG(result.file_size) }}</td>
|
<div class="text-center">
|
||||||
<td>{{ new Date(result.time_updated * 1000).toLocaleDateString("en-us") }}</td>
|
<h2>{{ searchResults.response.total }} results for <strong>"{{ store.searchText }}"</strong></h2>
|
||||||
<td>{{ result.lifetime_subscriptions }}</td>
|
</div>
|
||||||
<td>
|
<div class="d-flex">
|
||||||
<button v-if="mods.find(o => o.id === result.publishedfileid)" @click="removeMod(result.publishedfileid)" type="button" class="btn btn-danger">Remove</button>
|
<table>
|
||||||
<button v-else @click="installMod(result.publishedfileid)" type="button" class="btn btn-success">Install</button>
|
<tr>
|
||||||
</td>
|
<th>Steam Link</th>
|
||||||
</tr>
|
<th>Title</th>
|
||||||
</table>
|
<th>Size</th>
|
||||||
|
<th>Last Updated</th>
|
||||||
|
<th>Subscriptions</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="result in searchResults.response.publishedfiledetails">
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
:href="config.steamUrl + result.publishedfileid"
|
||||||
|
>
|
||||||
|
<img :alt="result.short_description" data-bs-toggle="tooltip" data-bs-placement="left" :title="result.short_description" width="160" height="90" :src="result.preview_url">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ result.title }}</td>
|
||||||
|
<td>{{ BKMG(result.file_size) }}</td>
|
||||||
|
<td>{{ new Date(result.time_updated * 1000).toLocaleDateString("en-us") }}</td>
|
||||||
|
<td>{{ result.lifetime_subscriptions }}</td>
|
||||||
|
<td>
|
||||||
|
<button v-if="store.mods.find(o => o.id === result.publishedfileid)" @click="removeMod(result.publishedfileid)" type="button" class="btn btn-danger">Remove</button>
|
||||||
|
<button v-else @click="installMod(result.publishedfileid)" type="button" class="btn btn-success">Install</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
20
web/docroot/src/components/Servers.vue
Normal file
20
web/docroot/src/components/Servers.vue
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<script setup>
|
||||||
|
import { useAppStore } from '@/stores/app.js'
|
||||||
|
const store = useAppStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="row" v-if="store.section === 'servers'">
|
||||||
|
<div class="col text-center">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h2>Servers</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row flex-grow-1">
|
||||||
|
<div class="col-6 justify-content-end">Running</div>
|
||||||
|
<div class="col-6">Stopped</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
6
web/docroot/src/config.js
Normal file
6
web/docroot/src/config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const config = {
|
||||||
|
baseUrl: window.location.protocol + '//' + window.location.hostname + ':8000',
|
||||||
|
steamUrl: 'https://steamcommunity.com/sharedfiles/filedetails/?id='
|
||||||
|
}
|
||||||
|
|
||||||
|
export { config }
|
|
@ -7,10 +7,6 @@ th, td {
|
||||||
padding-right: 10px
|
padding-right: 10px
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected {
|
|
||||||
background-color: cyan;
|
|
||||||
}
|
|
||||||
|
|
||||||
.simulink {
|
.simulink {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-underline: blue;
|
text-underline: blue;
|
||||||
|
|
|
@ -14,12 +14,6 @@ const app = createApp(App)
|
||||||
// Add the store
|
// Add the store
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
|
|
||||||
// Global properties
|
|
||||||
// The back end URL
|
|
||||||
app.config.globalProperties.baseUrl = window.location.protocol + '//' + window.location.hostname + ':8000'
|
|
||||||
// The steam workshop URL
|
|
||||||
app.config.globalProperties.steamUrl = 'https://steamcommunity.com/sharedfiles/filedetails/?id='
|
|
||||||
|
|
||||||
// A global error handler
|
// A global error handler
|
||||||
app.config.errorHandler = (err, instance, info) => {
|
app.config.errorHandler = (err, instance, info) => {
|
||||||
const store = useAppStore()
|
const store = useAppStore()
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const useAppStore = defineStore('app', {
|
||||||
errorText: '',
|
errorText: '',
|
||||||
modId: 0,
|
modId: 0,
|
||||||
modFile: '',
|
modFile: '',
|
||||||
searchText: ''
|
mods: [],
|
||||||
|
searchText: '',
|
||||||
|
section: 'mods',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
11
web/docroot/src/util.js
Normal file
11
web/docroot/src/util.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
||||||
|
|
||||||
|
const BKMG = (val) => {
|
||||||
|
let l = 0, n = parseInt(val, 10) || 0
|
||||||
|
while(n >= 1024 && ++l){
|
||||||
|
n = n/1024
|
||||||
|
}
|
||||||
|
return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l])
|
||||||
|
}
|
||||||
|
|
||||||
|
export { BKMG }
|
|
@ -16,4 +16,5 @@ export DEBUG='express:*'
|
||||||
npx nodemon web.js &
|
npx nodemon web.js &
|
||||||
|
|
||||||
cd docroot
|
cd docroot
|
||||||
|
npm i
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
Loading…
Add table
Reference in a new issue