Compare commits

..

5 Commits

30 changed files with 295 additions and 578 deletions

8
.github/release.yml vendored
View File

@ -1,8 +0,0 @@
changelog:
exclude:
authors:
- dependabot
categories:
- title: Main changes
labels:
- "*"

View File

@ -1,44 +0,0 @@
# Build & publish docker images
name: Dockerhub
on:
push:
tags: [v*]
branches: [ main ]
jobs:
dockerhub:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Set tag name
run: |
if [[ ${{ github.ref_type }} == "tag" ]]; then
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
else
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
fi
-
name: Build and push
uses: docker/build-push-action@v3
with:
push: true
tags: b4bz/homer:${{env.IMAGE_TAG}}
platforms: linux/amd64,linux/arm/v7,linux/arm64

View File

@ -20,7 +20,7 @@ jobs:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:

View File

@ -10,7 +10,7 @@ jobs:
name: Upload Release Asset
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Build project
run: |
yarn install
@ -20,9 +20,21 @@ jobs:
run: zip -r ../homer.zip ./*
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: true
files: |
homer.zip
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./homer.zip
asset_name: homer.zip
asset_content_type: application/zip

View File

@ -10,30 +10,24 @@ COPY . .
RUN yarn build
# production stage
FROM alpine:3.16
FROM alpine:3.15
ENV GID 1000
ENV UID 1000
ENV USER darkhttpd
ENV GROUP darkhttpd
ENV GID 911
ENV UID 911
ENV PORT 8080
ENV SUBFOLDER "/_"
ENV INIT_ASSETS 1
RUN addgroup -S lighttpd -g ${GID} && adduser -D -S -u ${UID} lighttpd lighttpd && \
apk add -U --no-cache lighttpd
RUN addgroup -S ${GROUP} -g ${GID} && adduser -D -S -u ${UID} ${USER} ${GROUP} && \
apk add -U --no-cache su-exec darkhttpd
WORKDIR /www
COPY lighttpd.conf /lighttpd.conf
COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist /www/
COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist/assets /www/default-assets
COPY entrypoint.sh /entrypoint.sh
COPY --from=build-stage --chown=${UID}:${GID} /app/dist /www/
COPY --from=build-stage --chown=${UID}:${GID} /app/dist/assets /www/default-assets
USER ${UID}:${GID}
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${PORT}/ || exit 1
EXPOSE ${PORT}
VOLUME /www/assets
ENTRYPOINT ["/bin/sh", "/entrypoint.sh"]

43
Dockerfile.arm32v7 Normal file
View File

@ -0,0 +1,43 @@
# build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build
# Multi arch build support
FROM alpine as qemu
ARG QEMU_VERSION="v4.2.0-7"
RUN wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-arm-static && chmod +x qemu-arm-static
# production stage
FROM arm32v7/alpine:3.11
COPY --from=qemu qemu-arm-static /usr/bin/
ENV USER darkhttpd
ENV GROUP darkhttpd
ENV GID 911
ENV UID 911
ENV PORT 8080
RUN addgroup -S ${GROUP} -g ${GID} && adduser -D -S -u ${UID} ${USER} ${GROUP} && \
apk add -U --no-cache darkhttpd su-exec && \
rm /usr/bin/qemu-arm-static
COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist /www/
COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist/assets /www/default-assets
COPY entrypoint.sh /entrypoint.sh
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${PORT}/ || exit 1
EXPOSE ${PORT}
VOLUME /www/assets
ENTRYPOINT ["/bin/sh", "/entrypoint.sh"]

43
Dockerfile.arm64v8 Normal file
View File

@ -0,0 +1,43 @@
# build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build
# Multi arch build support
FROM alpine as qemu
ARG QEMU_VERSION="v4.2.0-7"
RUN wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-aarch64-static && chmod +x qemu-aarch64-static
# production stage
FROM arm64v8/alpine:3.11
COPY --from=qemu qemu-aarch64-static /usr/bin/
ENV USER darkhttpd
ENV GROUP darkhttpd
ENV GID 911
ENV UID 911
ENV PORT 8080
RUN addgroup -S ${GROUP} -g ${GID} && adduser -D -S -u ${UID} ${USER} ${GROUP} && \
apk add -U --no-cache darkhttpd su-exec && \
rm /usr/bin/qemu-aarch64-static
COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist /www/
COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist/assets /www/default-assets
COPY entrypoint.sh /entrypoint.sh
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${PORT}/ || exit 1
EXPOSE ${PORT}
VOLUME /www/assets
ENTRYPOINT ["/bin/sh", "/entrypoint.sh"]

View File

@ -71,6 +71,8 @@ See [documentation](docs/configuration.md) for information about the configurati
### Using docker
To launch container:
```sh
docker run -d \
-p 8080:8080 \
@ -79,19 +81,12 @@ docker run -d \
b4bz/homer:latest
```
Environment variables:
* **`INIT_ASSETS`** (default: `1`)
Install example configuration file & assets (favicons, ...) to help you get started.
* **`SUBFOLDER`** (default: `null`)
If you would like to host Homer in a subfolder, (ex: *http://my-domain/**homer***), set this to the subfolder path (ex `/homer`).
Default assets will be automatically installed in the `/www/assets` directory. Use `UID` and/or `GID` env var to change the assets owner (`docker run -e "UID=1000" -e "GID=1000" [...]`).
### Using docker-compose
The `docker-compose.yml` file must be edited to match your needs.
You probably want to set the port mapping and volume binding (equivalent to `-p` and `-v` arguments):
Set the port and volume (equivalent to `-p` and `-v` arguments):
```yaml
volumes:
@ -100,13 +95,21 @@ ports:
- 8080:8080
```
Then launch the container:
To launch container:
```sh
cd /path/to/docker-compose.yml/
cd /path/to/docker-compose.yml
docker-compose up -d
```
Default assets will be automatically installed in the `/www/assets` directory. Use `UID` and/or `GID` env var to change the assets owner, also in `docker-compose.yml`:
```yaml
environment:
- UID=1000
- GID=1000
```
### Using the release tarball (prebuilt, ready to use)
Download and extract the latest release (`homer.zip`) from the [release page](https://github.com/bastienwirtz/homer/releases), rename the `assets/config.yml.dist` file to `assets/config.yml`, and put it behind a web server.

View File

@ -10,6 +10,7 @@ services:
- /your/local/assets/:/www/assets
ports:
- 8080:8080
user: 1000:1000 # default
environment:
- INIT_ASSETS=1 # default
#environment:
# - UID=1000
# - GID=1000
restart: unless-stopped

View File

@ -25,8 +25,7 @@ header: true # Set to false to hide the header
footer: '<p>Created with <span class="has-text-danger">❤️</span> with <a href="https://bulma.io/">bulma</a>, <a href="https://vuejs.org/">vuejs</a> & <a href="https://fontawesome.com/">font awesome</a> // Fork me on <a href="https://github.com/bastienwirtz/homer"><i class="fab fa-github-alt"></i></a></p>' # set false if you want to hide it.
columns: "3" # "auto" or number (must be a factor of 12: 1, 2, 3, 4, 6, 12)
connectivityCheck: true # whether you want to display a message when the apps are not accessible anymore (VPN disconnected for example).
# You should set it to true when using an authentication proxy, it also reloads the page when a redirection is detected when checking connectivity.
connectivityCheck: true # whether you want to display a message when the apps are not accessible anymore (VPN disconnected for example)
# Optional: Proxy / hosting option
proxy:
@ -128,7 +127,6 @@ services:
# icon: "fab fa-jenkins"
subtitle: "Bookmark example"
tag: "app"
keywords: "self hosted reddit" # optional keyword used for searching purpose
url: "https://www.reddit.com/r/selfhosted/"
target: "_blank" # optional html tag target attribute
- name: "Another one"

View File

@ -16,9 +16,7 @@ within Homer:
+ [Prometheus](#prometheus)
+ [AdGuard Home](#adguard-home)
+ [Portainer](#portainer)
+ [Emby / Jellyfin](#emby--jellyfin)
+ [Uptime Kuma](#uptime-kuma)
+ [Tautulli](#tautulli)
+ [Emby](#emby)
If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page.
@ -169,7 +167,7 @@ See https://docs.portainer.io/v/ce-2.11/user/account-settings#access-tokens
# - "local"
```
## Emby / Jellyfin
## Emby
You need to set the type to Emby, provide an api key and choose which stats to show if the subtitle is disabled.
@ -181,45 +179,3 @@ You need to set the type to Emby, provide an api key and choose which stats to s
apikey: "MY-SUPER-SECRET-API-KEY"
libraryType: "music" #Choose which stats to show. Can be one of: music, series or movies.
```
## Uptime Kuma
Using the Uptime Kuma service you can display info about your instance uptime right on your Homer dashboard.
The following configuration is available for the UptimeKuma service. Needs v1.13.1 or later because of the change in APIs due to [multiple status pages support](https://github.com/louislam/uptime-kuma/releases/tag/1.13.1).
```yaml
- name: "Uptime Kuma"
logo: "assets/tools/sample.png"
# subtitle: "A fancy self-hosted monitoring tool" # optional, if no subtitle is defined, Uptime Kuma incidents, if any, will be shown
url: "http://192.168.0.151:3001"
slug: "myCustomDashboard" # Defaults to "default" if not provided.
type: "UptimeKuma"
```
## Tautulli
The Tautulli service can allow you to show the number of currently active
streams on you Plex instance. An API key is required, and can be obtained from
the "Web Interface" section of settings on the Tautulli web UI.
```yaml
- name: "Tautulli"
logo: "assets/tools/sample.png"
url: "http://192.168.0.151:8181"
type: "Tautulli"
apikey: "MY-SUPER-SECRET-API-KEY"
```
Because the service type and link don't necessarily have to match, you could
even make the service type Tautulli on your Plex card and provide a separate
endpoint pointing to Tautulli!
```yaml
- name: "Plex"
logo: "assets/tools/sample.png"
url: "http://192.168.0.151:32400/web" # Plex
endpoint: "http://192.168.0.151:8181" # Tautulli
type: "Tautulli"
apikey: "MY-SUPER-SECRET-API-KEY"
```

View File

@ -17,9 +17,3 @@ To resolve this, you can either:
* Host all your target service under the same domain & port.
* Modify the target server configuration so that the response of the server included following header- `Access-Control-Allow-Origin: *` (<https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests>). It might be an option in the targeted service, otherwise depending on how the service is hosted, the proxy or web server can seamlessly add it.
* Use a cors proxy server like [`cors-container`](https://github.com/imjacobclark/cors-container), [`cors-anywhere`](https://github.com/Rob--W/cors-anywhere) or many others.
## I am using an authentication proxy and homer says I am offline
This should be a configuration issue.
* Make sure the option `connectivityCheck` is set to `true` in configuration.
* Check your proxy configuration, the expected behavior is to redirect user using a 302 to the login page when user is not authenticated.

View File

@ -1,18 +1,15 @@
#!/bin/sh
PERMISSION_ERROR="Check assets directory permissions & docker user or skip default assets install by setting the INIT_ASSETS env var to 0"
# Ensure default assets are present.
while true; do echo n; done | cp -Ri /www/default-assets/* /www/assets/ &> /dev/null
# Default assets & exemple configuration installation if possible.
if [[ "${INIT_ASSETS}" == "1" ]] && [[ ! -f "/www/config.yml" ]]; then
echo "No configuration found, installing default config & assets"
if [[ ! -w "/www/assets/" ]]; then echo "Assets directory not writable. $PERMISSION_ERROR" && exit 1; fi
while true; do echo n; done | cp -Ri /www/default-assets/* /www/assets/ &> /dev/null
if [[ $? -ne 0 ]]; then echo "Fail to copy default assets. $PERMISSION_ERROR" && exit 1; fi
yes n | cp -i /www/default-assets/config.yml.dist /www/assets/config.yml &> /dev/null
if [[ $? -ne 0 ]]; then echo "Fail to copy default config file. $PERMISSION_ERROR" && exit 1; fi
# Ensure compatibility with previous version (config.yml was in the root directory)
if [ -f "/www/config.yml" ]; then
yes n | cp -i /www/config.yml /www/assets/ &> /dev/null
fi
echo "Starting webserver"
lighttpd -D -f /lighttpd.conf
# Install default config if no one is available.
yes n | cp -i /www/default-assets/config.yml.dist /www/assets/config.yml &> /dev/null
chown -R $UID:$GID /www/assets
exec su-exec $UID:$GID darkhttpd /www/ --no-listing --port "$PORT"

8
hooks/post_push Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
IFS='-' read -r TAG string <<< "$DOCKER_TAG"
docker manifest create b4bz/homer:$TAG b4bz/homer:$TAG-amd64 b4bz/homer:$TAG-arm32v7 b4bz/homer:$TAG-arm64v8
docker manifest annotate b4bz/homer:$TAG b4bz/homer:$TAG-arm32v7 --os linux --arch arm
docker manifest annotate b4bz/homer:$TAG b4bz/homer:$TAG-arm64v8 --os linux --arch arm64 --variant v8
docker manifest push --purge b4bz/homer:$TAG

8
hooks/pre_build Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
# Update to docker-ee 18.x for manifests
apt-get -y update
apt-get -y --only-upgrade install docker-ee
# Register qemu-*-static for all supported processors except the
# current one, but also remove all registered binfmt_misc before
docker run --rm --privileged multiarch/qemu-user-static:register --reset

View File

@ -1,10 +0,0 @@
include "/etc/lighttpd/mime-types.conf"
server.port = env.PORT
server.modules = ( "mod_alias" )
server.username = "lighttpd"
server.groupname = "lighttpd"
server.document-root = "/www"
alias.url = ( env.SUBFOLDER => "/www" )
server.indexfiles = ("index.html")
server.follow-symlink = "enable"

View File

@ -7,19 +7,19 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.1.1",
"bulma": "^0.9.4",
"core-js": "^3.22.7",
"@fortawesome/fontawesome-free": "^5.15.4",
"bulma": "^0.9.3",
"core-js": "^3.21.1",
"js-yaml": "^4.1.0",
"lodash.merge": "^4.6.2",
"register-service-worker": "^1.7.2",
"vue": "^2.6.14"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.17",
"@vue/cli-plugin-eslint": "~4.5.17",
"@vue/cli-plugin-pwa": "~4.5.17",
"@vue/cli-service": "~4.5.17",
"@vue/cli-plugin-babel": "~4.5.15",
"@vue/cli-plugin-eslint": "~4.5.15",
"@vue/cli-plugin-pwa": "~4.5.15",
"@vue/cli-service": "~4.5.15",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",

View File

@ -75,7 +75,6 @@ services:
logo: "assets/tools/sample.png"
subtitle: "Bookmark example"
tag: "app"
keywords: "self hosted reddit"
url: "https://www.reddit.com/r/selfhosted/"
target: "_blank" # optional html a tag target attribute
- name: "Another one"

View File

@ -18,10 +18,7 @@
</a>
<i v-if="config.icon" :class="config.icon"></i>
</div>
<div
class="dashboard-title"
:class="{ 'no-logo': !config.icon || !config.logo }"
>
<div class="dashboard-title">
<span class="headline">{{ config.subtitle }}</span>
<h1>{{ config.title }}</h1>
</div>
@ -64,7 +61,7 @@
@network-status-update="offline = $event"
/>
<GetStarted v-if="configurationNeeded" />
<GetStarted v-if="loaded && !services" />
<div v-if="!offline">
<!-- Optional messages -->
@ -72,12 +69,8 @@
<!-- Horizontal layout -->
<div v-if="!vlayout || filter" class="columns is-multiline">
<template v-for="(group, groupIndex) in services">
<h2
v-if="group.name"
class="column is-full group-title"
:key="`header-${groupIndex}`"
>
<template v-for="group in services">
<h2 v-if="group.name" class="column is-full group-title">
<i v-if="group.icon" :class="['fa-fw', group.icon]"></i>
<div v-else-if="group.logo" class="group-logo media-left">
<figure class="image is-48x48">
@ -88,9 +81,10 @@
</h2>
<Service
v-for="(item, index) in group.items"
:key="`service-${groupIndex}-${index}`"
:key="index"
:item="item"
:proxy="config.proxy"
:forwarder="config.forwarder"
:class="['column', `is-${12 / config.columns}`]"
/>
</template>
@ -103,8 +97,8 @@
>
<div
:class="['column', `is-${12 / config.columns}`]"
v-for="(group, groupIndex) in services"
:key="groupIndex"
v-for="group in services"
:key="group.name"
>
<h2 v-if="group.name" class="group-title">
<i v-if="group.icon" :class="['fa-fw', group.icon]"></i>
@ -120,6 +114,7 @@
:key="index"
:item="item"
:proxy="config.proxy"
:forwarder="config.forwarder"
/>
</div>
</div>
@ -171,7 +166,6 @@ export default {
data: function () {
return {
loaded: false,
configNotFound: false,
config: null,
services: null,
offline: false,
@ -181,11 +175,6 @@ export default {
showMenu: false,
};
},
computed: {
configurationNeeded: function () {
return (this.loaded && !this.services) || this.configNotFound;
},
},
created: async function () {
this.buildDashboard();
window.onhashchange = this.buildDashboard;
@ -231,9 +220,10 @@ export default {
},
getConfig: function (path = "assets/config.yml") {
return fetch(path).then((response) => {
if (response.status == 404 || response.redirected) {
this.configNotFound = true;
return {};
if (response.redirected) {
// This allows to work with authentication proxies.
window.location.href = response.url;
return;
}
if (!response.ok) {
@ -258,8 +248,7 @@ export default {
return (
item.name.toLowerCase().includes(this.filter) ||
(item.subtitle && item.subtitle.toLowerCase().includes(this.filter)) ||
(item.tag && item.tag.toLowerCase().includes(this.filter)) ||
(item.keywords && item.keywords.toLowerCase().includes(this.filter))
(item.tag && item.tag.toLowerCase().includes(this.filter))
);
},
navigateToFirstService: function (target) {

View File

@ -104,10 +104,6 @@ body {
.dashboard-title {
padding: 6px 0 0 80px;
&.no-logo {
padding-left: 0;
}
}
.first-line {
@ -167,7 +163,8 @@ body {
}
#main-section {
padding: 0 0 2.5rem 0;
margin-bottom: 2rem;
padding: 0;
h2 {
padding-bottom: 0px;
@ -289,7 +286,7 @@ body {
.no-footer {
#main-section {
padding-bottom: 0;
margin-bottom: 0;
}
.footer {

View File

@ -53,3 +53,4 @@ services: []
proxy: ~
forwarder: ~

View File

@ -17,9 +17,6 @@ export default {
};
},
created: function () {
if (/t=\d+/.test(window.location.href)) {
window.history.replaceState({}, document.title, window.location.pathname);
}
let that = this;
this.checkOffline();
@ -32,44 +29,15 @@ export default {
},
false
);
window.addEventListener(
"online",
function () {
that.checkOffline();
},
false
);
window.addEventListener(
"offline",
function () {
this.offline = true;
},
false
);
},
methods: {
checkOffline: function () {
if (!navigator.onLine) {
this.offline = true;
return;
}
// extra check to make sure we're not offline
let that = this;
const aliveCheckUrl = window.location.href + "?t=" + new Date().valueOf();
return fetch(aliveCheckUrl, {
return fetch(window.location.href + "?alive", {
method: "HEAD",
cache: "no-store",
redirect: "manual",
})
.then(function (response) {
// opaqueredirect means request has been redirected, to auth provider probably
if (
(response.type === "opaqueredirect" && !response.ok) ||
[401, 403].indexOf(response.status) != -1
) {
window.location.href = aliveCheckUrl;
}
that.offline = !response.ok;
})
.catch(function () {

View File

@ -6,7 +6,7 @@
<p>
<a
class="button is-primary mt-5 has-text-weight-bold"
href="https://github.com/bastienwirtz/homer/blob/main/docs/configuration.md#configuration"
href="https://github.com/bastienwirtz/homer/blob/main/README.md#getting-started"
target="_blank"
>
Get started

View File

@ -1,5 +1,10 @@
<template>
<component v-bind:is="component" :item="item" :proxy="proxy"></component>
<component
v-bind:is="component"
:item="item"
:proxy="proxy"
:forwarder="forwarder"
></component>
</template>
<script>
@ -10,6 +15,7 @@ export default {
props: {
item: Object,
proxy: Object,
forwarder: Object,
},
computed: {
component() {

View File

@ -1,94 +0,0 @@
<template>
<Generic :item="item">
<template #indicator>
<div class="notifs">
<strong
v-if="streams > 0"
class="notif playing"
:title="`${streams} active stream${streams > 1 ? 's' : ''}`"
>
{{ streams }}
</strong>
<i
v-if="error"
class="notif error fa-solid fa-triangle-exclamation"
title="Unable to fetch current status"
></i>
</div>
</template>
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Tautulli",
mixins: [service],
props: {
item: Object,
},
components: {
Generic,
},
data: () => ({
stats: null,
error: false,
}),
computed: {
streams: function () {
if (!this.stats) {
return "";
}
return this.stats.stream_count;
},
},
created() {
this.fetchStatus();
},
methods: {
fetchStatus: async function () {
try {
const response = await this.fetch(
`/api/v2?apikey=${this.item.apikey}&cmd=get_activity`
);
this.error = false;
this.stats = response.response.data;
} catch (e) {
this.error = true;
console.error(e);
}
},
},
};
</script>
<style scoped lang="scss">
.notifs {
position: absolute;
color: white;
font-family: sans-serif;
top: 0.3em;
right: 0.5em;
.notif {
display: inline-block;
padding: 0.2em 0.35em;
border-radius: 0.25em;
position: relative;
margin-left: 0.3em;
font-size: 0.8em;
&.playing {
background-color: #28a9a3;
}
&.error {
border-radius: 50%;
aspect-ratio: 1;
background-color: #e51111;
}
}
}
</style>

View File

@ -1,154 +0,0 @@
<template>
<Generic :item="item">
<template #content>
<p class="title is-4">{{ item.name }}</p>
<p class="subtitle is-6">
<template v-if="item.subtitle">
{{ item.subtitle }}
</template>
<template v-else-if="status">
{{ statusMessage }}
</template>
</p>
</template>
<template #indicator>
<div v-if="status" class="status" :class="status">
{{ uptime }}&percnt;
</div>
</template>
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "UptimeKuma",
mixins: [service],
props: {
item: Object,
},
components: {
Generic,
},
data: () => ({
incident: null,
heartbeat: null,
}),
computed: {
dashboard: function () {
return this.item.slug ? this.item.slug : "default";
},
status: function () {
if (!this.incident) {
return "";
}
return this.incident.incident == null ? this.pageStatus : "bad";
},
lastHeartBeatList: function () {
let result = {};
for (let id in this.heartbeat.heartbeatList) {
let index = this.heartbeat.heartbeatList[id].length - 1;
result[id] = this.heartbeat.heartbeatList[id][index];
}
return result;
},
pageStatus: function () {
if (!this.heartbeat) {
return "";
}
if (Object.keys(this.heartbeat.heartbeatList).length === 0) {
return "";
}
let result = "good";
let hasUp = false;
for (let id in this.lastHeartBeatList) {
let beat = this.lastHeartBeatList[id];
if (beat.status == 1) {
hasUp = true;
} else {
result = "warn";
}
}
if (!hasUp) {
result = "bad";
}
return result;
},
statusMessage: function () {
if (!this.incident) {
return "";
}
if (this.incident.incident) {
return this.incident.incident.title;
}
return this.pageStatus == "warn"
? "Partially Degraded Service"
: "All Systems Operational";
},
uptime: function () {
if (!this.heartbeat) {
return 0;
}
const data = Object.values(this.heartbeat.uptimeList);
const percent = data.reduce((a, b) => a + b, 0) / data.length || 0;
return (percent * 100).toFixed(1);
},
},
created() {
this.item.url = `${this.item.url}/status/${this.dashboard}`;
this.fetchStatus();
},
methods: {
fetchStatus: function () {
this.fetch(`/api/status-page/${this.dashboard}?cachebust=${Date.now()}`)
.catch((e) => console.error(e))
.then((resp) => (this.incident = resp));
this.fetch(
`/api/status-page/heartbeat/${this.dashboard}?cachebust=${Date.now()}`
)
.catch((e) => console.error(e))
.then((resp) => (this.heartbeat = resp));
},
},
};
</script>
<style scoped lang="scss">
.status {
font-size: 0.8rem;
color: var(--text-title);
&.good:before {
background-color: #94e185;
border-color: #78d965;
box-shadow: 0 0 5px 1px #94e185;
}
&.warn:before {
background-color: #f8a306;
border-color: #e1b35e;
box-shadow: 0 0 5px 1px #f8a306;
}
&.bad:before {
background-color: #c9404d;
border-color: #c42c3b;
box-shadow: 0 0 5px 1px #c9404d;
}
&:before {
content: " ";
display: inline-block;
width: 7px;
height: 7px;
margin-right: 10px;
border: 1px solid #000;
border-radius: 7px;
}
}
</style>

View File

@ -1,6 +1,9 @@
const merge = require("lodash.merge");
export default {
props: {
proxy: Object,
forwarder: Object,
},
created: function () {
// custom service often consume info from an API using the item link (url) as a base url,
@ -25,18 +28,28 @@ export default {
this.item.useCredentials === true ? "include" : "omit";
}
options = Object.assign(options, init);
if (this.forwarder?.apikey) {
options.headers = {
"X-Homer-Forwarder-Api-Key": this.forwarder.apikey,
};
}
if (path.startsWith("/")) {
path = path.slice(1);
}
let url = this.endpoint;
let url = path ? `${this.endpoint}/${path}` : this.endpoint;
if (path) {
url = `${this.endpoint}/${path}`;
if (this.forwarder?.url) {
options.headers = {
...(options.headers || {}),
"X-Homer-Forwarder-Url": url,
};
url = this.forwarder.url;
}
options = merge(options, init);
return fetch(url, options).then((response) => {
if (!response.ok) {
throw new Error("Not 2xx response");

View File

@ -26,7 +26,4 @@ module.exports = {
msTileImage: "assets/icons/icon-any.png",
},
},
devServer: {
disableHostCheck: true,
},
};

162
yarn.lock
View File

@ -2,15 +2,6 @@
# yarn lockfile v1
"@achrinza/node-ipc@9.2.2":
version "9.2.2"
resolved "https://registry.yarnpkg.com/@achrinza/node-ipc/-/node-ipc-9.2.2.tgz#ae1b5d3d6a9362034eea60c8d946b93893c2e4ec"
integrity sha512-b90U39dx0cU6emsOvy5hxU4ApNXnE3+Tuo8XQZfiKTGelDwpMwBVgBP7QX6dGTcJgu/miyJuNJ/2naFBliNWEw==
dependencies:
"@node-ipc/js-queue" "2.0.3"
event-pubsub "4.3.0"
js-message "1.0.7"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
@ -912,10 +903,10 @@
"@babel/helper-validator-identifier" "^7.14.5"
to-fast-properties "^2.0.0"
"@fortawesome/fontawesome-free@^6.1.1":
version "6.1.1"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz#bf5d45611ab74890be386712a0e5d998c65ee2a1"
integrity sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg==
"@fortawesome/fontawesome-free@^5.15.4":
version "5.15.4"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5"
integrity sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==
"@hapi/address@2.x.x":
version "2.1.4"
@ -966,13 +957,6 @@
call-me-maybe "^1.0.1"
glob-to-regexp "^0.3.0"
"@node-ipc/js-queue@2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz#ac7fe33d766fa53e233ef8fedaf3443a01c5a4cd"
integrity sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==
dependencies:
easy-stack "1.0.1"
"@nodelib/fs.stat@^1.1.2":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
@ -1189,10 +1173,10 @@
lodash.kebabcase "^4.1.1"
svg-tags "^1.0.0"
"@vue/babel-preset-app@^4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-4.5.17.tgz#09c64eedfe868bfa3121fc12a59138518f830bde"
integrity sha512-iFv9J3F5VKUPcbx+TqW5qhGmAVyXQxPRpKpPOuTLFIVTzg+iwJnrqVbL4kJU5ECGDxPESW2oCVgxv9bTlDPu7w==
"@vue/babel-preset-app@^4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-4.5.15.tgz#f6bc08f8f674e98a260004234cde18b966d72eb0"
integrity sha512-J+YttzvwRfV1BPczf8r3qCevznYk+jh531agVF+5EYlHF4Sgh/cGXTz9qkkiux3LQgvhEGXgmCteg1n38WuuKg==
dependencies:
"@babel/core" "^7.11.0"
"@babel/helper-compilation-targets" "^7.9.6"
@ -1274,61 +1258,61 @@
"@vue/babel-plugin-transform-vue-jsx" "^1.2.1"
camelcase "^5.0.0"
"@vue/cli-overlay@^4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-4.5.17.tgz#4e0e24b7c3b71ff86de86f532821fd3abb48d10c"
integrity sha512-QKKp66VbMg+X8Qh0wgXSwgxLfxY7EIkZkV6bZ6nFqBx8xtaJQVDbTL+4zcUPPA6nygbIcQ6gvTinNEqIqX6FUQ==
"@vue/cli-overlay@^4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-4.5.15.tgz#0700fd6bad39336d4189ba3ff7d25e638e818c9c"
integrity sha512-0zI0kANAVmjFO2LWGUIzdGPMeE3+9k+KeRDXsUqB30YfRF7abjfiiRPq5BU9pOzlJbVdpRkisschBrvdJqDuDg==
"@vue/cli-plugin-babel@~4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.17.tgz#8c468e32ef6546f843201770a294bf599689e004"
integrity sha512-6kZuc3PdoUvGAnndUq6+GqjIXn3bqdTR8lOcAb1BH2b4N7IKGlmzcipALGS23HLVMAvDgNuUS7vf0unin9j2cg==
"@vue/cli-plugin-babel@~4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.15.tgz#ae4fb2ed54255fe3d84df381dab68509641179ed"
integrity sha512-hBLrwYfFkHldEe34op/YNgPhpOWI5n5DB2Qt9I/1Epeif4M4iFaayrgjvOR9AVM6WbD3Yx7WCFszYpWrQZpBzQ==
dependencies:
"@babel/core" "^7.11.0"
"@vue/babel-preset-app" "^4.5.17"
"@vue/cli-shared-utils" "^4.5.17"
"@vue/babel-preset-app" "^4.5.15"
"@vue/cli-shared-utils" "^4.5.15"
babel-loader "^8.1.0"
cache-loader "^4.1.0"
thread-loader "^2.1.3"
webpack "^4.0.0"
"@vue/cli-plugin-eslint@~4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.17.tgz#7667bf87bdfdb39faeb3baed58657622354a17bc"
integrity sha512-bVNDP+SuWcuJrBMc+JLaKvlxx25XKIlZBa+zzFnxhHZlwPZ7CeBD3e2wnsygJyPoKgDZcZwDgmEz1BZzMEjsNw==
"@vue/cli-plugin-eslint@~4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.15.tgz#5781824a941f34c26336a67b1f6584a06c6a24ff"
integrity sha512-/2Fl6wY/5bz3HD035oSnFRMsKNxDxU396KqBdpCQdwdvqk4mm6JAbXqihpcBRTNPeTO6w+LwGe6FE56PVbJdbg==
dependencies:
"@vue/cli-shared-utils" "^4.5.17"
"@vue/cli-shared-utils" "^4.5.15"
eslint-loader "^2.2.1"
globby "^9.2.0"
inquirer "^7.1.0"
webpack "^4.0.0"
yorkie "^2.0.0"
"@vue/cli-plugin-pwa@~4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-pwa/-/cli-plugin-pwa-4.5.17.tgz#73b2f9dd1203de46761a9843e972966e2717fe87"
integrity sha512-IaODWmj5eQjv97ne0CTOgPZA8QmVS7zYX64C+SivWPw0uevJAhNUdDHgyrUODP7fEfyufKliStLMQJTowohGNQ==
"@vue/cli-plugin-pwa@~4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-pwa/-/cli-plugin-pwa-4.5.15.tgz#eb800c418d96b496deec9d063a1798fe6e9c2db8"
integrity sha512-yQzsspaIkjeQyN6btF8ATgbJFU023q1HC8uUpmiBa4QE9EyBlR8fSrKFhcJ0EmT6KnU7PMwlnOJ/OqjguFnufA==
dependencies:
"@vue/cli-shared-utils" "^4.5.17"
"@vue/cli-shared-utils" "^4.5.15"
webpack "^4.0.0"
workbox-webpack-plugin "^4.3.1"
"@vue/cli-plugin-router@^4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-router/-/cli-plugin-router-4.5.17.tgz#9de189a7a8740817cde2a4e57aade14552ff68c3"
integrity sha512-9r9CSwqv2+39XHQPDZJ0uaTtTP7oe0Gx17m7kBhHG3FA7R7AOSk2aVzhHZmDRhzlOxjx9kQSvrOSMfUG0kV4dQ==
"@vue/cli-plugin-router@^4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-router/-/cli-plugin-router-4.5.15.tgz#1e75c8c89df42c694f143b9f1028de3cf5d61e1e"
integrity sha512-q7Y6kP9b3k55Ca2j59xJ7XPA6x+iSRB+N4ac0ZbcL1TbInVQ4j5wCzyE+uqid40hLy4fUdlpl4X9fHJEwuVxPA==
dependencies:
"@vue/cli-shared-utils" "^4.5.17"
"@vue/cli-shared-utils" "^4.5.15"
"@vue/cli-plugin-vuex@^4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.17.tgz#eb6f597c775f3c847bf5a638ad65a0d03c11dcbf"
integrity sha512-ck/ju2T2dmPKLWK/5QctNJs9SCb+eSZbbmr8neFkMc7GlbXw6qLWw5v3Vpd4KevdQA8QuQOA1pjUmzpCiU/mYQ==
"@vue/cli-plugin-vuex@^4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.15.tgz#466c1f02777d02fef53a9bb49a36cc3a3bcfec4e"
integrity sha512-fqap+4HN+w+InDxlA3hZTOGE0tzBTgXhKLoDydhywqgmhQ1D9JA6Feh94ze6tG8DsWX58/ujYUqA8jAz17FJtg==
"@vue/cli-service@~4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-4.5.17.tgz#6f796056363b70b69065d95815ac170b7772d0c6"
integrity sha512-MqfkRYIcIUACe3nYlzNrYstJTWRXHlIqh6JCkbWbdnXWN+IfaVdlG8zw5Q0DVcSdGvkevUW7zB4UhtZB4uyAcA==
"@vue/cli-service@~4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-4.5.15.tgz#0e9a186d51550027d0e68e95042077eb4d115b45"
integrity sha512-sFWnLYVCn4zRfu45IcsIE9eXM0YpDV3S11vlM2/DVbIPAGoYo5ySpSof6aHcIvkeGsIsrHFpPHzNvDZ/efs7jA==
dependencies:
"@intervolga/optimize-cssnano-plugin" "^1.0.5"
"@soda/friendly-errors-webpack-plugin" "^1.7.1"
@ -1336,10 +1320,10 @@
"@types/minimist" "^1.2.0"
"@types/webpack" "^4.0.0"
"@types/webpack-dev-server" "^3.11.0"
"@vue/cli-overlay" "^4.5.17"
"@vue/cli-plugin-router" "^4.5.17"
"@vue/cli-plugin-vuex" "^4.5.17"
"@vue/cli-shared-utils" "^4.5.17"
"@vue/cli-overlay" "^4.5.15"
"@vue/cli-plugin-router" "^4.5.15"
"@vue/cli-plugin-vuex" "^4.5.15"
"@vue/cli-shared-utils" "^4.5.15"
"@vue/component-compiler-utils" "^3.1.2"
"@vue/preload-webpack-plugin" "^1.1.0"
"@vue/web-component-wrapper" "^1.2.0"
@ -1388,17 +1372,17 @@
optionalDependencies:
vue-loader-v16 "npm:vue-loader@^16.1.0"
"@vue/cli-shared-utils@^4.5.17":
version "4.5.17"
resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-4.5.17.tgz#bb4aac8b816036cf5c0adf3af3cc1cb9c425501e"
integrity sha512-VoFNdxvTW4vZu3ne+j1Mf7mU99J2SAoRVn9XPrsouTUUJablglM8DASk7Ixhsh6ymyL/W9EADQFR6Pgj8Ujjuw==
"@vue/cli-shared-utils@^4.5.15":
version "4.5.15"
resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-4.5.15.tgz#dba3858165dbe3465755f256a4890e69084532d6"
integrity sha512-SKaej9hHzzjKSOw1NlFmc6BSE0vcqUQMQiv1cxQ2DhVyy4QxZXBmzmiLBUBe+hYZZs1neXW7n//udeN9bCAY+Q==
dependencies:
"@achrinza/node-ipc" "9.2.2"
"@hapi/joi" "^15.0.1"
chalk "^2.4.2"
execa "^1.0.0"
launch-editor "^2.2.1"
lru-cache "^5.1.1"
node-ipc "^9.1.1"
open "^6.3.0"
ora "^3.4.0"
read-pkg "^5.1.1"
@ -1839,9 +1823,9 @@ async-limiter@~1.0.0:
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async@^2.6.2:
version "2.6.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
dependencies:
lodash "^4.17.14"
@ -2226,10 +2210,10 @@ builtin-status-codes@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
bulma@^0.9.4:
version "0.9.4"
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.9.4.tgz#0ca8aeb1847a34264768dba26a064c8be72674a1"
integrity sha512-86FlT5+1GrsgKbPLRRY7cGDg8fsJiP/jzTqXXVqiUZZ2aZT8uemEOHlU1CDU+TxklPEZ11HZNNWclRBBecP4CQ==
bulma@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.9.3.tgz#ddccb7436ebe3e21bf47afe01d3c43a296b70243"
integrity sha512-0d7GNW1PY4ud8TWxdNcP6Cc8Bu7MxcntD/RRLGWuiw/s0a9P+XlH/6QoOIrmbj6o8WWJzJYhytiu9nFjTszk1g==
bytes@3.0.0:
version "3.0.0"
@ -2789,10 +2773,10 @@ core-js@^2.4.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-js@^3.22.7:
version "3.22.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.7.tgz#8d6c37f630f6139b8732d10f2c114c3f1d00024f"
integrity sha512-Jt8SReuDKVNZnZEzyEQT5eK6T2RRCXkfTq7Lo09kpm+fHjgGewSbNjV+Wt4yZMhPDdzz2x1ulI5z/w4nxpBseg==
core-js@^3.21.1:
version "3.21.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94"
integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==
core-js@^3.6.5:
version "3.15.2"
@ -3355,7 +3339,7 @@ duplexify@^3.4.2, duplexify@^3.6.0:
readable-stream "^2.0.0"
stream-shift "^1.0.0"
easy-stack@1.0.1:
easy-stack@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/easy-stack/-/easy-stack-1.0.1.tgz#8afe4264626988cabb11f3c704ccd0c835411066"
integrity sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==
@ -3678,9 +3662,9 @@ events@^3.0.0:
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
eventsource@^1.0.7:
version "1.1.1"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.1.tgz#4544a35a57d7120fba4fa4c86cb4023b2c09df2f"
integrity sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA==
version "1.1.0"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf"
integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==
dependencies:
original "^1.0.0"
@ -5082,6 +5066,13 @@ js-message@1.0.7:
resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47"
integrity sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==
js-queue@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/js-queue/-/js-queue-2.0.2.tgz#0be590338f903b36c73d33c31883a821412cd482"
integrity sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==
dependencies:
easy-stack "^1.0.1"
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -5754,6 +5745,15 @@ node-forge@^0.10.0:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
node-ipc@^9.1.1:
version "9.2.1"
resolved "https://registry.yarnpkg.com/node-ipc/-/node-ipc-9.2.1.tgz#b32f66115f9d6ce841dc4ec2009d6a733f98bb6b"
integrity sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==
dependencies:
event-pubsub "4.3.0"
js-message "1.0.7"
js-queue "2.0.2"
node-libs-browser@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"