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/ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:

View File

@ -10,7 +10,7 @@ jobs:
name: Upload Release Asset name: Upload Release Asset
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Build project - name: Build project
run: | run: |
yarn install yarn install
@ -20,9 +20,21 @@ jobs:
run: zip -r ../homer.zip ./* run: zip -r ../homer.zip ./*
- name: Create Release - name: Create Release
id: create_release id: create_release
uses: softprops/action-gh-release@v1 uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
token: ${{ secrets.GITHUB_TOKEN }} tag_name: ${{ github.ref }}
generate_release_notes: true release_name: Release ${{ github.ref }}
files: | draft: false
homer.zip 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 RUN yarn build
# production stage # production stage
FROM alpine:3.16 FROM alpine:3.15
ENV GID 1000 ENV USER darkhttpd
ENV UID 1000 ENV GROUP darkhttpd
ENV GID 911
ENV UID 911
ENV PORT 8080 ENV PORT 8080
ENV SUBFOLDER "/_"
ENV INIT_ASSETS 1
RUN addgroup -S lighttpd -g ${GID} && adduser -D -S -u ${UID} lighttpd lighttpd && \ RUN addgroup -S ${GROUP} -g ${GID} && adduser -D -S -u ${UID} ${USER} ${GROUP} && \
apk add -U --no-cache lighttpd apk add -U --no-cache su-exec darkhttpd
WORKDIR /www COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist /www/
COPY --from=build-stage --chown=${USER}:${GROUP} /app/dist/assets /www/default-assets
COPY lighttpd.conf /lighttpd.conf
COPY entrypoint.sh /entrypoint.sh 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 \ HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${PORT}/ || exit 1 CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${PORT}/ || exit 1
EXPOSE ${PORT} EXPOSE ${PORT}
VOLUME /www/assets VOLUME /www/assets
ENTRYPOINT ["/bin/sh", "/entrypoint.sh"] 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 ### Using docker
To launch container:
```sh ```sh
docker run -d \ docker run -d \
-p 8080:8080 \ -p 8080:8080 \
@ -79,19 +81,12 @@ docker run -d \
b4bz/homer:latest b4bz/homer:latest
``` ```
Environment variables: 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" [...]`).
* **`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`).
### Using docker-compose ### Using docker-compose
The `docker-compose.yml` file must be edited to match your needs. 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 ```yaml
volumes: volumes:
@ -100,13 +95,21 @@ ports:
- 8080:8080 - 8080:8080
``` ```
Then launch the container: To launch container:
```sh ```sh
cd /path/to/docker-compose.yml/ cd /path/to/docker-compose.yml
docker-compose up -d 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) ### 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. 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 - /your/local/assets/:/www/assets
ports: ports:
- 8080:8080 - 8080:8080
user: 1000:1000 # default #environment:
environment: # - UID=1000
- INIT_ASSETS=1 # default # - 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. 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) 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). 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.
# Optional: Proxy / hosting option # Optional: Proxy / hosting option
proxy: proxy:
@ -128,7 +127,6 @@ services:
# icon: "fab fa-jenkins" # icon: "fab fa-jenkins"
subtitle: "Bookmark example" subtitle: "Bookmark example"
tag: "app" tag: "app"
keywords: "self hosted reddit" # optional keyword used for searching purpose
url: "https://www.reddit.com/r/selfhosted/" url: "https://www.reddit.com/r/selfhosted/"
target: "_blank" # optional html tag target attribute target: "_blank" # optional html tag target attribute
- name: "Another one" - name: "Another one"

View File

@ -16,9 +16,7 @@ within Homer:
+ [Prometheus](#prometheus) + [Prometheus](#prometheus)
+ [AdGuard Home](#adguard-home) + [AdGuard Home](#adguard-home)
+ [Portainer](#portainer) + [Portainer](#portainer)
+ [Emby / Jellyfin](#emby--jellyfin) + [Emby](#emby)
+ [Uptime Kuma](#uptime-kuma)
+ [Tautulli](#tautulli)
If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page. 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" # - "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. 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" apikey: "MY-SUPER-SECRET-API-KEY"
libraryType: "music" #Choose which stats to show. Can be one of: music, series or movies. 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. * 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. * 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. * 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 #!/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. # Ensure compatibility with previous version (config.yml was in the root directory)
if [[ "${INIT_ASSETS}" == "1" ]] && [[ ! -f "/www/config.yml" ]]; then if [ -f "/www/config.yml" ]; then
echo "No configuration found, installing default config & assets" yes n | cp -i /www/config.yml /www/assets/ &> /dev/null
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
fi fi
echo "Starting webserver" # Install default config if no one is available.
lighttpd -D -f /lighttpd.conf 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" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^6.1.1", "@fortawesome/fontawesome-free": "^5.15.4",
"bulma": "^0.9.4", "bulma": "^0.9.3",
"core-js": "^3.22.7", "core-js": "^3.21.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",
"register-service-worker": "^1.7.2", "register-service-worker": "^1.7.2",
"vue": "^2.6.14" "vue": "^2.6.14"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "~4.5.17", "@vue/cli-plugin-babel": "~4.5.15",
"@vue/cli-plugin-eslint": "~4.5.17", "@vue/cli-plugin-eslint": "~4.5.15",
"@vue/cli-plugin-pwa": "~4.5.17", "@vue/cli-plugin-pwa": "~4.5.15",
"@vue/cli-service": "~4.5.17", "@vue/cli-service": "~4.5.15",
"@vue/eslint-config-prettier": "^6.0.0", "@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"eslint": "^6.7.2", "eslint": "^6.7.2",

View File

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

View File

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

View File

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

View File

@ -52,4 +52,5 @@ links: []
services: [] services: []
proxy: ~ proxy: ~
forwarder: ~

View File

@ -17,9 +17,6 @@ export default {
}; };
}, },
created: function () { created: function () {
if (/t=\d+/.test(window.location.href)) {
window.history.replaceState({}, document.title, window.location.pathname);
}
let that = this; let that = this;
this.checkOffline(); this.checkOffline();
@ -32,44 +29,15 @@ export default {
}, },
false false
); );
window.addEventListener(
"online",
function () {
that.checkOffline();
},
false
);
window.addEventListener(
"offline",
function () {
this.offline = true;
},
false
);
}, },
methods: { methods: {
checkOffline: function () { checkOffline: function () {
if (!navigator.onLine) {
this.offline = true;
return;
}
// extra check to make sure we're not offline
let that = this; let that = this;
const aliveCheckUrl = window.location.href + "?t=" + new Date().valueOf(); return fetch(window.location.href + "?alive", {
return fetch(aliveCheckUrl, {
method: "HEAD", method: "HEAD",
cache: "no-store", cache: "no-store",
redirect: "manual",
}) })
.then(function (response) { .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; that.offline = !response.ok;
}) })
.catch(function () { .catch(function () {

View File

@ -6,7 +6,7 @@
<p> <p>
<a <a
class="button is-primary mt-5 has-text-weight-bold" 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" target="_blank"
> >
Get started Get started

View File

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

View File

@ -62,7 +62,7 @@ export default {
this.fetch("/System/info/public") this.fetch("/System/info/public")
.then((response) => { .then((response) => {
if (response.Id) this.status = "running"; if (response.Id) this.status = "running";
else throw new Error(); else throw new Error();
}) })
.catch((e) => { .catch((e) => {
console.log(e); console.log(e);

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 { export default {
props: { props: {
proxy: Object, proxy: Object,
forwarder: Object,
}, },
created: function () { created: function () {
// custom service often consume info from an API using the item link (url) as a base url, // 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"; 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("/")) { if (path.startsWith("/")) {
path = path.slice(1); path = path.slice(1);
} }
let url = this.endpoint; let url = path ? `${this.endpoint}/${path}` : this.endpoint;
if (path) { if (this.forwarder?.url) {
url = `${this.endpoint}/${path}`; options.headers = {
...(options.headers || {}),
"X-Homer-Forwarder-Url": url,
};
url = this.forwarder.url;
} }
options = merge(options, init);
return fetch(url, options).then((response) => { return fetch(url, options).then((response) => {
if (!response.ok) { if (!response.ok) {
throw new Error("Not 2xx response"); throw new Error("Not 2xx response");

View File

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

162
yarn.lock
View File

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