aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md2
-rw-r--r--.github/workflows/integration.yml31
-rw-r--r--.github/workflows/release.yml (renamed from .github/workflows/main.yml)0
-rw-r--r--docs/configuration.md5
-rw-r--r--docs/customservices.md59
-rw-r--r--src/App.vue6
-rw-r--r--src/assets/defaults.yml3
-rw-r--r--src/components/Service.vue3
-rw-r--r--src/components/services/AdGuardHome.vue18
-rw-r--r--src/components/services/Ping.vue14
-rw-r--r--src/mixins/service.js43
11 files changed, 136 insertions, 48 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0746863..e0063d5 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -14,5 +14,5 @@ Fixes # (issue)
14 14
15- [ ] I've read & comply with the [contributing guidelines](https://github.com/bastienwirtz/homer/blob/main/CONTRIBUTING.md) 15- [ ] I've read & comply with the [contributing guidelines](https://github.com/bastienwirtz/homer/blob/main/CONTRIBUTING.md)
16- [ ] I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers. 16- [ ] I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
17- [ ] I have made corresponding changes the documentation (README.md). 17- [ ] I have made corresponding changes to the documentation (README.md).
18- [ ] I've checked my modifications for any breaking changes, especially in the `config.yml` file 18- [ ] I've checked my modifications for any breaking changes, especially in the `config.yml` file
diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
new file mode 100644
index 0000000..196113d
--- /dev/null
+++ b/.github/workflows/integration.yml
@@ -0,0 +1,31 @@
1# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3
4name: Node.js CI
5
6on:
7 push:
8 branches: [ main ]
9 pull_request:
10 branches: [ main ]
11
12jobs:
13 build:
14
15 runs-on: ubuntu-latest
16
17 strategy:
18 matrix:
19 node-version: [16.x]
20 # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21
22 steps:
23 - uses: actions/checkout@v2
24 - name: Use Node.js ${{ matrix.node-version }}
25 uses: actions/setup-node@v2
26 with:
27 node-version: ${{ matrix.node-version }}
28 cache: 'yarn'
29 - run: yarn install
30 - run: yarn lint
31
diff --git a/.github/workflows/main.yml b/.github/workflows/release.yml
index 886556d..886556d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/release.yml
diff --git a/docs/configuration.md b/docs/configuration.md
index e1630af..50b5bd5 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -24,6 +24,11 @@ footer: '<p>Created with <span class="has-text-danger">❤️</span> with <a hre
24columns: "3" # "auto" or number (must be a factor of 12: 1, 2, 3, 4, 6, 12) 24columns: "3" # "auto" or number (must be a factor of 12: 1, 2, 3, 4, 6, 12)
25connectivityCheck: true # whether you want to display a message when the apps are not accessible anymore (VPN disconnected for example) 25connectivityCheck: true # whether you want to display a message when the apps are not accessible anymore (VPN disconnected for example)
26 26
27# Optional: Proxy / hosting option
28proxy:
29 # NOT All custom services implements this new option YET. Support will be extended very soon.
30 useCredentials: false # send cookies & authorization headers when fetching service specific data. Set to `true` if you use an authentication proxy. Can be overrided on service level.
31
27# Optional theming 32# Optional theming
28theme: default # 'default' or one of the themes available in 'src/assets/themes'. 33theme: default # 'default' or one of the themes available in 'src/assets/themes'.
29 34
diff --git a/docs/customservices.md b/docs/customservices.md
index f79428f..ef84948 100644
--- a/docs/customservices.md
+++ b/docs/customservices.md
@@ -6,6 +6,20 @@ within Homer.
6 6
7If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page. 7If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page.
8 8
9
10## Common options
11
12```yaml
13- name: "My Service"
14 logo: "assets/tools/sample.png"
15 url: "http://my-service-link"
16 endpoint: "http://my-service-endpoint" # Optional: alternative base URL used to fetch service data is necessary.
17 useCredentials: false # Optional: Override global proxy.useCredentials configuration.
18 type: "<type>"
19```
20
21⚠️🚧 `endpoint` & `useCredentials` new options are not yet supported by all custom services (but will be very soon).
22
9## PiHole 23## PiHole
10 24
11Using the PiHole service you can display info about your local PiHole instance right on your Homer dashboard. 25Using the PiHole service you can display info about your local PiHole instance right on your Homer dashboard.
@@ -13,12 +27,11 @@ Using the PiHole service you can display info about your local PiHole instance r
13The following configuration is available for the PiHole service. 27The following configuration is available for the PiHole service.
14 28
15```yaml 29```yaml
16 items: 30- name: "Pi-hole"
17 - name: "Pi-hole" 31 logo: "assets/tools/sample.png"
18 logo: "assets/tools/sample.png" 32 # subtitle: "Network-wide Ad Blocking" # optional, if no subtitle is defined, PiHole statistics will be shown
19 # subtitle: "Network-wide Ad Blocking" # optional, if no subtitle is defined, PiHole statistics will be shown 33 url: "http://192.168.0.151/admin"
20 url: "http://192.168.0.151/admin" 34 type: "PiHole"
21 type: "PiHole"
22``` 35```
23 36
24## OpenWeatherMap 37## OpenWeatherMap
@@ -27,14 +40,13 @@ Using the OpenWeatherMap service you can display weather information about a giv
27The following configuration is available for the OpenWeatherMap service: 40The following configuration is available for the OpenWeatherMap service:
28 41
29```yaml 42```yaml
30items: 43- name: "Weather"
31 - name: "Weather" 44 location: "Amsterdam" # your location.
32 location: "Amsterdam" # your location. 45 locationId: "2759794" # Optional: Specify OpenWeatherMap city ID for better accuracy
33 locationId: "2759794" # Optional: Specify OpenWeatherMap city ID for better accuracy 46 apiKey: "<---insert-api-key-here--->" # insert your own API key here. Request one from https://openweathermap.org/api.
34 apiKey: "<---insert-api-key-here--->" # insert your own API key here. Request one from https://openweathermap.org/api. 47 units: "metric" # units to display temperature. Can be one of: metric, imperial, kelvin. Defaults to kelvin.
35 units: "metric" # units to display temperature. Can be one of: metric, imperial, kelvin. Defaults to kelvin. 48 background: "square" # choose which type of background you want behind the image. Can be one of: square, cicle, none. Defaults to none.
36 background: "square" # choose which type of background you want behind the image. Can be one of: square, cicle, none. Defaults to none. 49 type: "OpenWeather"
37 type: "OpenWeather"
38``` 50```
39 51
40**Remarks:** 52**Remarks:**
@@ -46,8 +58,8 @@ This service displays News (grey), Warning (orange) or Error (red) notifications
46Two lines are needed in the config.yml : 58Two lines are needed in the config.yml :
47 59
48```yaml 60```yaml
49type: "Medusa" 61 type: "Medusa"
50apikey: "01234deb70424befb1f4ef6a23456789" 62 apikey: "01234deb70424befb1f4ef6a23456789"
51``` 63```
52 64
53The url must be the root url of Medusa application. 65The url must be the root url of Medusa application.
@@ -59,8 +71,8 @@ This service displays Activity (blue), Warning (orange) or Error (red) notificat
59Two lines are needed in the config.yml : 71Two lines are needed in the config.yml :
60 72
61```yaml 73```yaml
62type: "Radarr" or "Sonarr" 74 type: "Radarr" or "Sonarr"
63apikey: "01234deb70424befb1f4ef6a23456789" 75 apikey: "01234deb70424befb1f4ef6a23456789"
64``` 76```
65 77
66The url must be the root url of Radarr/Sonarr application. 78The url must be the root url of Radarr/Sonarr application.
@@ -75,10 +87,9 @@ For Paperless you need an API-Key which you have to store at the item in the fie
75For Ping you need to set the type to Ping and provide a url. 87For Ping you need to set the type to Ping and provide a url.
76 88
77```yaml 89```yaml
78items: 90- name: "Awesome app"
79 - name: "Awesome app" 91 type: Ping
80 type: Ping 92 logo: "assets/tools/sample.png"
81 logo: "assets/tools/sample.png" 93 subtitle: "Bookmark example" tag: "app"
82 subtitle: "Bookmark example" tag: "app" 94 url: "https://www.reddit.com/r/selfhosted/"
83 url: "https://www.reddit.com/r/selfhosted/"
84``` 95```
diff --git a/src/App.vue b/src/App.vue
index 1f4f509..c263c8a 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -74,7 +74,8 @@
74 <Service 74 <Service
75 v-for="(item, index) in group.items" 75 v-for="(item, index) in group.items"
76 :key="index" 76 :key="index"
77 v-bind:item="item" 77 :item="item"
78 :proxy="config.proxy"
78 :class="['column', `is-${12 / config.columns}`]" 79 :class="['column', `is-${12 / config.columns}`]"
79 /> 80 />
80 </template> 81 </template>
@@ -102,7 +103,8 @@
102 <Service 103 <Service
103 v-for="(item, index) in group.items" 104 v-for="(item, index) in group.items"
104 :key="index" 105 :key="index"
105 v-bind:item="item" 106 :item="item"
107 :proxy="config.proxy"
106 /> 108 />
107 </div> 109 </div>
108 </div> 110 </div>
diff --git a/src/assets/defaults.yml b/src/assets/defaults.yml
index 7d3a863..ed1fbc9 100644
--- a/src/assets/defaults.yml
+++ b/src/assets/defaults.yml
@@ -44,3 +44,6 @@ colors:
44message: ~ 44message: ~
45links: [] 45links: []
46services: [] 46services: []
47
48
49proxy: ~ \ No newline at end of file
diff --git a/src/components/Service.vue b/src/components/Service.vue
index 39a9ac4..25b86d5 100644
--- a/src/components/Service.vue
+++ b/src/components/Service.vue
@@ -1,5 +1,5 @@
1<template> 1<template>
2 <component v-bind:is="component" :item="item"></component> 2 <component v-bind:is="component" :item="item" :proxy="proxy"></component>
3</template> 3</template>
4 4
5<script> 5<script>
@@ -9,6 +9,7 @@ export default {
9 name: "Service", 9 name: "Service",
10 props: { 10 props: {
11 item: Object, 11 item: Object,
12 proxy: Object,
12 }, 13 },
13 computed: { 14 computed: {
14 component() { 15 component() {
diff --git a/src/components/services/AdGuardHome.vue b/src/components/services/AdGuardHome.vue
index 16881fa..b01f0f4 100644
--- a/src/components/services/AdGuardHome.vue
+++ b/src/components/services/AdGuardHome.vue
@@ -20,10 +20,12 @@
20</template> 20</template>
21 21
22<script> 22<script>
23import service from "@/mixins/service.js";
23import Generic from "./Generic.vue"; 24import Generic from "./Generic.vue";
24 25
25export default { 26export default {
26 name: "AdGuardHome", 27 name: "AdGuardHome",
28 mixins: [service],
27 props: { 29 props: {
28 item: Object, 30 item: Object,
29 }, 31 },
@@ -60,18 +62,14 @@ export default {
60 }, 62 },
61 methods: { 63 methods: {
62 fetchStatus: async function () { 64 fetchStatus: async function () {
63 this.status = await fetch(`${this.item.url}/control/status`, { 65 this.status = await this.fetch("/control/status").catch((e) =>
64 credentials: "include", 66 console.log(e)
65 }) 67 );
66 .then((response) => response.json())
67 .catch((e) => console.log(e));
68 }, 68 },
69 fetchStats: async function () { 69 fetchStats: async function () {
70 this.stats = await fetch(`${this.item.url}/control/stats`, { 70 this.stats = await this.fetch("/control/stats").catch((e) =>
71 credentials: "include", 71 console.log(e)
72 }) 72 );
73 .then((response) => response.json())
74 .catch((e) => console.log(e));
75 }, 73 },
76 }, 74 },
77}; 75};
diff --git a/src/components/services/Ping.vue b/src/components/services/Ping.vue
index 6fd3ec5..d1fda57 100644
--- a/src/components/services/Ping.vue
+++ b/src/components/services/Ping.vue
@@ -9,10 +9,12 @@
9</template> 9</template>
10 10
11<script> 11<script>
12import service from "@/mixins/service.js";
12import Generic from "./Generic.vue"; 13import Generic from "./Generic.vue";
13 14
14export default { 15export default {
15 name: "Ping", 16 name: "Ping",
17 mixins: [service],
16 props: { 18 props: {
17 item: Object, 19 item: Object,
18 }, 20 },
@@ -27,16 +29,8 @@ export default {
27 }, 29 },
28 methods: { 30 methods: {
29 fetchStatus: async function () { 31 fetchStatus: async function () {
30 const url = `${this.item.url}`; 32 this.fetch("/", { method: "HEAD", cache: "no-cache" }, false)
31 fetch(url, { 33 .then(() => {
32 method: "HEAD",
33 cache: "no-cache",
34 credentials: "include",
35 })
36 .then((response) => {
37 if (!response.ok) {
38 throw Error(response.statusText);
39 }
40 this.status = "online"; 34 this.status = "online";
41 }) 35 })
42 .catch(() => { 36 .catch(() => {
diff --git a/src/mixins/service.js b/src/mixins/service.js
new file mode 100644
index 0000000..17fa6fc
--- /dev/null
+++ b/src/mixins/service.js
@@ -0,0 +1,43 @@
1export default {
2 props: {
3 proxy: Object,
4 },
5 created: function () {
6 // custom service often consume info from an API using the item link (url) as a base url,
7 // but sometimes the base url is different. An optional alternative URL can be provided with the "endpoint" key.
8 this.endpoint = this.item.endpoint || this.item.url;
9
10 if (this.endpoint.endsWith("/")) {
11 this.endpoint = this.endpoint.slice(0, -1);
12 }
13 },
14 methods: {
15 fetch: function (path, init, json = true) {
16 let options = {};
17
18 if (this.proxy?.useCredentials) {
19 options.credentials = "include";
20 }
21
22 // Each item can override the credential settings
23 if (this.item.useCredentials !== undefined) {
24 options.credentials =
25 this.item.useCredentials === true ? "include" : "omit";
26 }
27
28 options = Object.assign(options, init);
29
30 if (path.startsWith("/")) {
31 path = path.slice(1);
32 }
33
34 return fetch(`${this.endpoint}/${path}`, options).then((response) => {
35 if (!response.ok) {
36 throw new Error("Not 2xx response");
37 }
38
39 return json ? response.json() : response;
40 });
41 },
42 },
43};