diff options
-rw-r--r-- | .github/PULL_REQUEST_TEMPLATE.md | 2 | ||||
-rw-r--r-- | .github/workflows/integration.yml | 31 | ||||
-rw-r--r-- | .github/workflows/release.yml (renamed from .github/workflows/main.yml) | 0 | ||||
-rw-r--r-- | docs/configuration.md | 5 | ||||
-rw-r--r-- | docs/customservices.md | 59 | ||||
-rw-r--r-- | src/App.vue | 6 | ||||
-rw-r--r-- | src/assets/defaults.yml | 3 | ||||
-rw-r--r-- | src/components/Service.vue | 3 | ||||
-rw-r--r-- | src/components/services/AdGuardHome.vue | 18 | ||||
-rw-r--r-- | src/components/services/Ping.vue | 14 | ||||
-rw-r--r-- | src/mixins/service.js | 43 |
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 | |||
4 | name: Node.js CI | ||
5 | |||
6 | on: | ||
7 | push: | ||
8 | branches: [ main ] | ||
9 | pull_request: | ||
10 | branches: [ main ] | ||
11 | |||
12 | jobs: | ||
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 807fa68..c554297 100644 --- a/docs/configuration.md +++ b/docs/configuration.md | |||
@@ -26,6 +26,11 @@ footer: '<p>Created with <span class="has-text-danger">❤️</span> with <a hre | |||
26 | columns: "3" # "auto" or number (must be a factor of 12: 1, 2, 3, 4, 6, 12) | 26 | columns: "3" # "auto" or number (must be a factor of 12: 1, 2, 3, 4, 6, 12) |
27 | connectivityCheck: true # whether you want to display a message when the apps are not accessible anymore (VPN disconnected for example) | 27 | connectivityCheck: true # whether you want to display a message when the apps are not accessible anymore (VPN disconnected for example) |
28 | 28 | ||
29 | # Optional: Proxy / hosting option | ||
30 | proxy: | ||
31 | # NOT All custom services implements this new option YET. Support will be extended very soon. | ||
32 | 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. | ||
33 | |||
29 | # Optional theming | 34 | # Optional theming |
30 | theme: default # 'default' or one of the themes available in 'src/assets/themes'. | 35 | theme: default # 'default' or one of the themes available in 'src/assets/themes'. |
31 | 36 | ||
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 | ||
7 | If you experiencing any issue, please have a look to the [troubleshooting](troubleshooting.md) page. | 7 | If 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 | ||
11 | Using the PiHole service you can display info about your local PiHole instance right on your Homer dashboard. | 25 | Using 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 | |||
13 | The following configuration is available for the PiHole service. | 27 | The 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 | |||
27 | The following configuration is available for the OpenWeatherMap service: | 40 | The following configuration is available for the OpenWeatherMap service: |
28 | 41 | ||
29 | ```yaml | 42 | ```yaml |
30 | items: | 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 | |||
46 | Two lines are needed in the config.yml : | 58 | Two lines are needed in the config.yml : |
47 | 59 | ||
48 | ```yaml | 60 | ```yaml |
49 | type: "Medusa" | 61 | type: "Medusa" |
50 | apikey: "01234deb70424befb1f4ef6a23456789" | 62 | apikey: "01234deb70424befb1f4ef6a23456789" |
51 | ``` | 63 | ``` |
52 | 64 | ||
53 | The url must be the root url of Medusa application. | 65 | The url must be the root url of Medusa application. |
@@ -59,8 +71,8 @@ This service displays Activity (blue), Warning (orange) or Error (red) notificat | |||
59 | Two lines are needed in the config.yml : | 71 | Two lines are needed in the config.yml : |
60 | 72 | ||
61 | ```yaml | 73 | ```yaml |
62 | type: "Radarr" or "Sonarr" | 74 | type: "Radarr" or "Sonarr" |
63 | apikey: "01234deb70424befb1f4ef6a23456789" | 75 | apikey: "01234deb70424befb1f4ef6a23456789" |
64 | ``` | 76 | ``` |
65 | 77 | ||
66 | The url must be the root url of Radarr/Sonarr application. | 78 | The 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 | |||
75 | For Ping you need to set the type to Ping and provide a url. | 87 | For Ping you need to set the type to Ping and provide a url. |
76 | 88 | ||
77 | ```yaml | 89 | ```yaml |
78 | items: | 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 9b4803f..5e71796 100644 --- a/src/App.vue +++ b/src/App.vue | |||
@@ -75,7 +75,8 @@ | |||
75 | <Service | 75 | <Service |
76 | v-for="(item, index) in group.items" | 76 | v-for="(item, index) in group.items" |
77 | :key="index" | 77 | :key="index" |
78 | v-bind:item="item" | 78 | :item="item" |
79 | :proxy="config.proxy" | ||
79 | :class="['column', `is-${12 / config.columns}`]" | 80 | :class="['column', `is-${12 / config.columns}`]" |
80 | /> | 81 | /> |
81 | </template> | 82 | </template> |
@@ -103,7 +104,8 @@ | |||
103 | <Service | 104 | <Service |
104 | v-for="(item, index) in group.items" | 105 | v-for="(item, index) in group.items" |
105 | :key="index" | 106 | :key="index" |
106 | v-bind:item="item" | 107 | :item="item" |
108 | :proxy="config.proxy" | ||
107 | /> | 109 | /> |
108 | </div> | 110 | </div> |
109 | </div> | 111 | </div> |
diff --git a/src/assets/defaults.yml b/src/assets/defaults.yml index f011346..ae4f523 100644 --- a/src/assets/defaults.yml +++ b/src/assets/defaults.yml | |||
@@ -42,3 +42,6 @@ colors: | |||
42 | message: ~ | 42 | message: ~ |
43 | links: [] | 43 | links: [] |
44 | services: [] | 44 | services: [] |
45 | |||
46 | |||
47 | proxy: ~ \ 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> |
23 | import service from "@/mixins/service.js"; | ||
23 | import Generic from "./Generic.vue"; | 24 | import Generic from "./Generic.vue"; |
24 | 25 | ||
25 | export default { | 26 | export 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> |
12 | import service from "@/mixins/service.js"; | ||
12 | import Generic from "./Generic.vue"; | 13 | import Generic from "./Generic.vue"; |
13 | 14 | ||
14 | export default { | 15 | export 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 @@ | |||
1 | export 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 | }; | ||