Add support for minecraft server status widgets

This commit is contained in:
Taylor Jones 2024-10-15 10:35:32 -06:00
parent 146055541f
commit 8e4e95597a
No known key found for this signature in database
4 changed files with 303 additions and 0 deletions

View File

@ -40,6 +40,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
- [Mvg Connection](#mvg-connection)
- [Custom search](#custom-search)
- [Rescuetime overview](#rescuetime-overview)
- [Minecraft Server](#minecraft-server)
- **[Self-Hosted Services Widgets](#self-hosted-services-widgets)**
- [System Info](#system-info)
- [Cron Monitoring](#cron-monitoring-health-checks)
@ -1378,6 +1379,45 @@ Show an overview of how you have spent your time for the current day.
---
### Minecraft Server
Show minecraft server status
<p align="center"><img width="380" src="https://i.ibb.co/hcmd4Wf/minecraft-widget.png" /></p>
#### Options
**Field** | **Type** | **Required** | **Description**
--- | --- | --- | ---
**`title`** | `string` | _Optional_ | Display title for server uses server address if not set.
**`server`** | `string` | Required | Server hostname or ip(:port) will use srv records.
**`bedrock`** | `boolean` | _Optional_ | If server is a bedrock edition server. (default false)
**`showMods`** | `boolean` | _Optional_ | Display mod list if available
**`showPlayers`** | `boolean` | _Optional_ | Display player list if available
**`showPlugins`** | `boolean` | _Optional_ | Display plugin list if available
#### Example
```yaml
- type: minecraft-status
options:
title: Venity Network
server: play.venitymc.com
bedrock: true
showMods: true
showPlayers: true
showPlugins: true
```
#### Info
- **CORS**: 🟢 Enabled
- **Auth**: 🟢 Not Required
- **Price**: 🟢 Free
- **Host**: [Minecraft Server Status](https://mcsrvstat.us/)
- **Privacy**: _See [Minecraft Server Status FAQ](https://mcsrvstat.us/faq)_
---
## Self-Hosted Services Widgets

View File

@ -0,0 +1,258 @@
<template>
<div class="minecraft-wrapper">
<a class="minecraft-link" :href="serverLinkEndpoint">
<h3 class="minecraft-title">{{ title }}</h3>
</a>
<div class="minecraft-icon-wrapper">
<img :src="icon" alt="server-icon" class="minecraft-icon" />
</div>
<div class="minecraft-content-wrapper">
<StatusIndicator class="status-indicator" :statusSuccess="status ? online : undefined"
:statusText="status ? statusTooltip : undefined" />
<span v-if="title != server" class="minecraft-server">{{ server }}<br /></span>
<span v-if="!online" class="minecraft-status">Server Offline</span>
<span v-if="online" class="minecraft-version">
{{ software || (bedrock ? "Bedrock" : "Minecraft") }} {{ version }}
</span>
<ul v-if="online" class="minecraft-motd">
<li v-for="(line, idx) in motd" :key="idx">{{ line }}</li>
</ul>
<div v-if="showPlayers" class="player-list">
<span>{{ onlinePlayers }}/{{ maxPlayers }} Players</span>
<ul>
<li v-for="{ name, uuid } in players" :key="uuid">
<a :href="playerLinkEndpoint(uuid)">
<img :src="playerIconEndpoint(uuid)" :alt="`${name}'s Head'`"/>{{ name }}
</a>
</li>
</ul>
</div>
<div v-if="showMods" class="mod-list">
<span>{{ mods.length }} Mods</span>
<ul>
<li v-for="{ name, version } in mods" :key="name">
{{ name }}={{ version }}
</li>
</ul>
</div>
<div v-if="showPlugins" class="plugin-list">
<span>{{ plugins.length }} Plugins</span>
<ul>
<li v-for="{ name, version } in plugins" :key="name">
{{ name }}={{ version }}
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import WidgetMixin from '@/mixins/WidgetMixin';
import { widgetApiEndpoints } from '@/utils/defaults';
import StatusIndicator from '@/components/LinkItems/StatusIndicator.vue';
export default {
mixins: [WidgetMixin],
components: {
StatusIndicator
},
data() {
return {
status: false,
online: false,
ip: '',
port: 0,
hostname: '',
iconData: null,
motd: null,
version: null,
software: null,
gamemode: null,
mods: [],
plugins: [],
players: [],
onlinePlayers: null,
maxPlayers: null,
};
},
computed: {
title() {
return this.options.title || this.options.server || this.error("options.server not set");
},
alt() {
return this.title
},
icon() {
return `https://api.mcsrvstat.us/icon/${this.server}`
},
server() {
return this.options.server || this.error("options.server not set");
},
bedrock() {
return this.options.bedrock === true;
},
statusTooltip() {
if (!this.status) {
return "Loading...";
}
if (!this.online) {
return `${this.server} Offline`;
}
return `${this.onlinePlayers}/${this.maxPlayers} Online`;
},
showPlayers() {
return this.options.showPlayers && this.players.length > 0;
},
showMods() {
return this.options.showMods && this.mods.length > 0;
},
showPlugins() {
return this.options.showPlugins && this.plugins.length > 0;
},
endpoint() {
return `${widgetApiEndpoints.minecraftStatus}${this.bedrock?'bedrock/':''}3/${this.server}`;
},
serverLinkEndpoint() {
return `${widgetApiEndpoints.minecraftServerLink}${this.server}`;
}
},
methods: {
playerIconEndpoint(uuid) {
return `${widgetApiEndpoints.minecraftPlayerIcon}${uuid}/32.png`;
},
playerLinkEndpoint(uuid) {
return `${widgetApiEndpoints.minecraftPlayerLink}${uuid}`;
},
/* Make GET request to McSrvStat.US API endpoint */
fetchData() {
this.startLoading()
fetch(this.endpoint)
.then(response => {
if (!response.ok) {
this.error('Network response was not ok');
}
return response.json();
})
.then(data => {
this.processData(data);
})
.catch(dataFetchError => {
this.error('Unable to fetch data', dataFetchError);
})
.finally(() => {
this.finishLoading();
});
},
/* Assign data variables to the returned data */
processData(data) {
this.online = data.online
this.ip = data.ip
this.port = data.port
this.hostname = data.hostname
if (this.online) {
this.version = data.version
this.iconData = data.icon
this.software = data.software
this.gamemode = data.gamemode
this.motd = data.motd.clean || []
this.players = data.players.list || []
this.onlinePlayers = data.players.online
this.maxPlayers = data.players.max
this.mods = data.mods || []
this.plugins = data.plugins || []
}
this.status = true
},
update() {
this.fetchData()
},
},
};
</script>
<style scoped lang="scss">
.minecraft-wrapper {
margin-top: -1em;
display: grid;
justify-content: center;
grid-template-columns: 64px 1fr;
color: var(--widget-text-color);
padding-top: 0.5rem;
.minecraft-link {
grid-column: 1 / span 2;
text-decoration: none;
.minecraft-title {
font-size: 1.2rem;
margin: 0.25rem auto;
border-bottom: 1px solid var(--widget-text-color);
color: var(--widget-text-color);
}
}
.minecraft-icon {
display: flex;
width: 100%;
max-width: 64px;
margin: 0.25rem auto 0;
padding-top: 0.5rem;
border-radius: var(--curve-factor);
}
.minecraft-content-wrapper {
position: relative;
padding: 0.5rem;
ul.minecraft-motd {
border-top: 1px dashed var(--widget-text-color);
list-style-type: none;
padding: 0.5rem 0;
margin: 0.5rem 0 0 0;
}
.player-list,
.mod-list,
.plugin-list {
span {
font-size: 1.2rem;
border-top: 1px dashed var(--widget-text-color);
padding: 0.25rem 0;
display: block;
}
ul {
list-style-type: '- ';
padding: 0;
margin: 0;
li {
padding: 1rem 0;
margin: 0;
}
}
}
.player-list {
ul {
li {
padding: 0;
list-style-type: none;
img {
width: 1rem;
height: 1rem;
float: left;
position: relative;
margin-right: 1em;
}
}
}
}
}
}
</style>
<style lang="scss">
.minecraft-alt-tt {
min-width: 20rem;
}
</style>

View File

@ -86,6 +86,7 @@ const COMPAT = {
iframe: 'IframeWidget',
image: 'ImageWidget',
joke: 'Jokes',
'minecraft-status': 'MinecraftStatus',
'mullvad-status': 'MullvadStatus',
mvg: 'Mvg',
linkding: 'Linkding',

View File

@ -240,6 +240,10 @@ module.exports = {
holidays: 'https://kayaposoft.com/enrico/json/v2.0/?action=getHolidaysForDateRange',
jokes: 'https://v2.jokeapi.dev/joke/',
news: 'https://api.currentsapi.services/v1/latest-news',
minecraftPlayerIcon: 'https://mc-heads.net/avatar/',
minecraftPlayerLink: 'https://minecraftuuid.com/?search=',
minecraftServerLink: 'https://mcsrvstat.us/server/',
minecraftStatus: 'https://api.mcsrvstat.us/',
mullvad: 'https://am.i.mullvad.net/json',
mvg: 'https://www.mvg.de/api/fib/v2/',
publicIp: 'https://ipapi.co/json',