Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Multi-Page Support #617

Merged
merged 31 commits into from
May 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
07f6bfe
:construction: Display additional routes based on pages object
Lissy93 Apr 15, 2022
cf7587b
Merge branch 'master' of github.com:Lissy93/dashy into FEATURE/multi-…
Lissy93 Apr 18, 2022
036bc00
:sparkles: Basic multi-page support working (#584)
Lissy93 Apr 19, 2022
1bc9964
:sparkles: Updates view switcher to support multiple pages (#584)
Lissy93 Apr 20, 2022
f5a8c30
:truck: Refactored saving logic into mixin
Lissy93 Apr 24, 2022
5ab619f
:sparkles: Adds a parameter to specify which config file to edit
Lissy93 Apr 24, 2022
61761e7
:lipstick: Displays which config file is being edited
Lissy93 Apr 24, 2022
7706041
:zap: Improved props for router
Lissy93 Apr 24, 2022
dd49ad7
:zap: Adds store value to determine which view being edited
Lissy93 Apr 24, 2022
9ca11a5
Merge branch 'master' of github.com:Lissy93/dashy into FEATURE/multi-…
Lissy93 Apr 24, 2022
3347dc9
:construction: Working on `pages` form in interactive editor
Lissy93 Apr 25, 2022
bc391e2
:card_file_box: Adds pages to config docs
Lissy93 Apr 26, 2022
d5c5c4f
:memo: Starts writing multi-page docs
Lissy93 Apr 26, 2022
a0ac797
:zap: Put the initialization screen back in
Lissy93 Apr 27, 2022
dfb12ec
:memo: Updates docs for multi-page support
Lissy93 Apr 30, 2022
d765eeb
:truck: Use absolue path to loading styles
Lissy93 Apr 30, 2022
1aecf32
:speech_balloon: Adds copy for the multi-page support editor
Lissy93 Apr 30, 2022
6bf0ecb
:sparkles: Adds option for page-specific custom styling
Lissy93 Apr 30, 2022
a949639
:zap: Use page name for making nav-bar path slug
Lissy93 Apr 30, 2022
a7a7032
:card_file_box: Adds pages to schema
Lissy93 Apr 30, 2022
238c51a
Merge branch 'master' of github.com:Lissy93/dashy into FEATURE/multi-…
Lissy93 Apr 30, 2022
52a0ba5
:bento: Adds icon for multi-page editor form
Lissy93 Apr 30, 2022
eb9a5ab
:necktie: Logic work for multi-page support
Lissy93 Apr 30, 2022
854d04a
Merge branch 'master' of github.com:Lissy93/dashy into FEATURE/multi-…
Lissy93 May 1, 2022
138003c
:lipstick: Updates themes
Lissy93 May 1, 2022
f6df1e7
:memo: Updates multi-page docs
Lissy93 May 1, 2022
f107dbf
:bookmark: Bumps to 2.0.8 and updates changelog
Lissy93 May 1, 2022
7d91d51
:triangular_flag_on_post: Adds warning and note for local save
Lissy93 May 1, 2022
437ec2e
:lipstick: Applies max-width to page path when in footer
Lissy93 May 1, 2022
8c15ab4
:closed_lock_with_key: Adds local path checking
Lissy93 May 1, 2022
c87e13c
:goal_net: Catch error if URL not specificed
Lissy93 May 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## ✨ 2.0.8 Adds Multi-Page Support [PR #617](https://github.com/Lissy93/dashy/pull/617)
- Adds support for multiple pages per-dashboard
- Adds new attribute at root of main config file: `pages`
- Updates router and nav-bar to automatically create paths for both local and remote configs

## ⚡️ 2.0.7 Improves handling of Sections and Items [PR #595](https://github.com/Lissy93/dashy/pull/595)
- Adds functionality for sub-items / item-groups
- Creates an item mixin, for reusing functionality
Expand Down
11 changes: 4 additions & 7 deletions .github/LATEST_CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
## ⚡️ 2.0.7 Improves handling of Sections and Items [PR #595](https://github.com/Lissy93/dashy/pull/595)
- Adds functionality for sub-items / item-groups
- Creates an item mixin, for reusing functionality
- Item width calculated based on parent section width
- Improved mobile support, long-press for right-click
- Adds 2 new themes (`lissy` and `charry-blossom`)
- Adds 2 new widgets (`mullvad-status`, and `blacklist-check`)
## ✨ 2.0.8 Adds Multi-Page Support [PR #617](https://github.com/Lissy93/dashy/pull/617)
- Adds support for multiple pages per-dashboard
- Adds new attribute at root of main config file: `pages`
- Updates router and nav-bar to automatically create paths for both local and remote configs
45 changes: 37 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
- [⚙️ Config Editor](#config-editor-)
- [☁ Cloud Backup & Sync](#cloud-backup--sync-)
- [🌎 Language Switching](#language-switching-)
- [📃 Multi-Page Support](#multi-page-support-)
- **Community**
- [📊 System Requirements](#system-requirements-)
- [🙋‍♀️ Support](#support-)
Expand All @@ -64,18 +65,18 @@
</details>

## Features 🌈

- 📃 Support for multiple pages
- 🚦 Real-time status monitoring for each of your apps/links
- 📊 Use widgets to display info and dynamic content from self-hosted services
- 🔎 Instant search by name, domain, or tags + customizable hotkeys & keyboard shortcuts
- 🎨 Many built-in color themes, with UI color editor and support for custom CSS
- 🧸 Many icon options - Font-Awesome, homelab icons, auto-fetching Favicon, images, emojis, etc.
- 🚦 Status monitoring for each of your apps/links for basic availability and uptime checking
- 📊 Use widgets to display info and dynamic content from self-hosted services
- 💂 Optional authentication with multi-user access, configurable privileges, and SSO support
- 🌎 Multi-language support, with 10+ human-translated languages, and more on the way
- ☁ Optional, encrypted, free off-site cloud backup and restore feature available
- 💼 A workspace view, for easily switching between multiple apps simultaneously
- 🛩️ A minimal view, for use as a fast-loading browser Startpage
- 🖱️ Choose app launch method, either new tab, same tab, a pop-up modal, or in the workspace view
- 🖱️ Choose app launch methods: new tab, same tab, clipboard, pop-up modal, or open in workspace view
- 📏 Customizable layout, sizes, text, component visibility, sort order, behavior, etc.
- 🖼️ Options for a full-screen background image, custom nav-bar links, HTML footer, title, etc.
- 🚀 Easy to setup with Docker, or on bare metal, or with 1-Click cloud deployment
Expand Down Expand Up @@ -413,11 +414,11 @@ Dashy supports multiple languages and locales. When available, your language sho
- 🇸🇮 **Slovenian**: `sl` - Contributed by **[@UrekD](https://github.com/UrekD)**
- 🇸🇪 **Swedish**: `sv` - Contributed by **[@BOZG](https://github.com/BOZG)**
- 🇮🇹 **Italian**: `it` - Contributed by **[@alexdelprete](https://github.com/alexdelprete)**
- 🇵🇹 **Portuguese**: `pt` - Machine Translated *(awaiting human review)*
- 🇵🇹 **Portuguese**: `pt` - Contributed by **[@LeoColman](https://github.com/LeoColman)**
- 🇷🇺 **Russian**: `ru` - Contributed by Anon
- 🇦🇪 **Arabic**: `ar` - Contributed by Anon
- 🇮🇳 **Hindi**: `hi` - Contributed by Anon
- 🇯🇵 **Japanese**: `ja` - Contributed by Anon
- 🇦🇪 **Arabic**: `ar`
- 🇮🇳 **Hindi**: `hi`
- 🇯🇵 **Japanese**: `ja`

#### Add your Language
I would love Dashy to be available to everyone without language being a barrier to entry. If you've got a few minutes to spare, consider adding translations for your language. It's a quick task, and all text is in [a single JSON file](https://github.com/Lissy93/dashy/tree/master/src/assets/locales). Since any missing text will fall back to English, you don't need to translate it all.
Expand All @@ -426,6 +427,34 @@ I would love Dashy to be available to everyone without language being a barrier

---

## Multi-Page Support 📃

> For full multi-page documentation, see: [**Pages & Sections**](./docs/pages-and-sections.md)

Within your dashboard, you can have as many sub-pages as you require. To load additional pages, specify a name, and path to a config file under `pages`. The config file can be either local (stored in `/public`), or remote (located anywhere accessible).

```yaml
pages:
- name: Networking Services
path: 'networking.yml'
- name: Work Stuff
path: 'work.yml'
```

Or

```yaml
pages:
- name: Getting Started
path: 'https://snippet.host/tvcw/raw'
- name: Homelab
path: 'https://snippet.host/tetp/raw'
- name: Browser Startpage
path: 'https://snippet.host/zcom/raw'
```

---

## System Requirements 📊

If running on bare metal, Dashy requires [Node](https://nodejs.org/en/) V 16.0.0 or later, LTS (16.13.2) is recommended.
Expand Down
11 changes: 11 additions & 0 deletions docs/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The following file provides a reference of all supported configuration options.

- [**`pageInfo`**](#pageinfo) - Header text, footer, title, navigation, etc
- [`navLinks`](#pageinfonavlinks-optional) - Links to display in the navigation bar
- [**`pages`**](#pages-optional) - List of additional config files, for multi-page dashboards
- [**`appConfig`**](#appconfig-optional) - Main application settings
- [`webSearch`](#appconfigwebsearch-optional) - Configure web search engine options
- [`hideComponents`](#appconfighidecomponents-optional) - Show/ hide page components
Expand Down Expand Up @@ -56,6 +57,7 @@ The following file provides a reference of all supported configuration options.
**`pageInfo`** | `object` | Required | Basic meta data like title, description, nav bar links, footer text. See [`pageInfo`](#pageinfo)
**`appConfig`** | `object` | _Optional_ | Settings related to how the app functions, including API keys and global styles. See [`appConfig`](#appconfig-optional)
**`sections`** | `array` | Required | An array of sections, each containing an array of items, which will be displayed as links. See [`section`](#section)
**`pages`** | `array` | _Optional_ | An array additional config files, used for multi-page dashboards. See [`pages`](#pages-optional)

**[⬆️ Back to Top](#configuring)**

Expand All @@ -81,6 +83,15 @@ The following file provides a reference of all supported configuration options.

**[⬆️ Back to Top](#configuring)**

### `pages[]` _(optional)_

**Field** | **Type** | **Required**| **Description**
--- | --- | --- | ---
**`name`** | `string` | Required | A unique name for that page
**`path`** | `string` | Required | The path (local or remote) to the config file to use.<br>For files located within `/public`, you only need to specify filename, for externally hosted files you must include the full URL

**[⬆️ Back to Top](#configuring)**

### `appConfig` _(optional)_

**Field** | **Type** | **Required**| **Description**
Expand Down
58 changes: 58 additions & 0 deletions docs/pages-and-sections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Pages and Sections

## Multi-Page Support

You can have additional pages within your dashboard, with each having it's own config file. The config files for sub-pages can either be stored locally, or hosted separately. A link to each additional page will be displayed in the navigation bar.

You can edit additional pages using the interactive editor, exactly the same was as your primary page (so long as it's local). But please save changes to one page, before you start editing the next.

### Using Local Sub-Pages

To get started, create a new `.yml` config file for your sub-page, placing it within `/app/public`. Then within your primary `conf.yml`, choose a name, and specify the path to the new file.

For example:

```yaml
pages:
- name: Networking Services
path: 'networking.yml'
- name: Work Stuff
path: 'work.yml'
```

If you're sub-page is located within `/app/public`, then you only need to specify the filename, but if it's anywhere else, then the full path is required.

### Using Remote Sub-Pages

Config files don't need to be local, you can store them anywhere, and data will be imported as sub-pages on page load.

For example:

```yaml
pages:
- name: Getting Started
path: 'https://snippet.host/tvcw/raw'
- name: Homelab
path: 'https://snippet.host/tetp/raw'
- name: Browser Startpage
path: 'https://snippet.host/zcom/raw'
```

There are many options of how this can be used. You could store your config within a Git repository, in order to easily track and rollback changes. Or host your config on your NAS, to have it backed up with the rest of your files. Or use a hosted paste service, for example [snippet.host](https://snippet.host/), which supports never-expiring CORS-enabled pastes, which can also be edited later.

You will obviously not be able to write updates to remote configs directly through the UI editor, but you can still make and preview changes, then use the export menu to get a copy of the new config, which can then be pasted to the remote source manually.
The config file must, of course be accessible from within Dashy. If your config contains sensitive info (like API keys, credentials, secret URLs, etc), take care not to expose it to the internet.

The following example shows creating a config, publishing it as a [Gist](https://gist.github.com/), copying the URL to the raw file, and using it within your dashboard.

<p align="center">
<img width="700" alt="Public config in a gist demo"
src="https://i.ibb.co/55jm3LG/how-to-use-remote-config-sub-page.gif"
/>
</p>

### Restrictions

Only top-level fields supported by sub-pages are `pageInfo` and `sections`. The `appConfig` and `pages` will always be inherited from your main `conf.yml` file. Other than that, sub-pages behave exactly the same as your default view, and can contain sections, items, widgets and page info like nav links, title and logo.

Note that since page paths are required by the router, they are set at build-time, not run-time, and so a rebuild (happens automatically) is required for changes to page paths to take effect (this only applies to changes to the `pages` array, rebuild isn't required for editing page content).
1 change: 1 addition & 0 deletions docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [Backup & Restore](/docs/backup-restore.md) - Guide to backing up config with Dashy's cloud sync feature
- [Icons](/docs/icons.md) - Outline of all available icon types for sections and items, with examples
- [Language Switching](/docs/multi-language-support.md) - Details on how to switch language, or add a new locale
- [Pages and Sections](/docs/pages-and-sections.md) - Multi-page support, sections, items and sub-items
- [Status Indicators](/docs/status-indicators.md) - Using Dashy to monitor uptime and status of your apps
- [Searching & Shortcuts](/docs/searching.md) - Searching, launching methods + keyboard shortcuts
- [Theming](/docs/theming.md) - Complete guide to applying, writing and modifying themes + styles
Expand Down
10 changes: 10 additions & 0 deletions docs/theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ Custom CSS can be developed, tested and applied directly through the UI. Althoug

This can be done from the Config menu (spanner icon in the top-right), under the Custom Styles tab. This is then associated with `appConfig.customCss` in local storage. Styles can also be directly applied to this attribute in the config file, but this may get messy very quickly if you have a lot of CSS.

### Page-Specific Styles

If you've got multiple pages within your dashboard, you can choose to target certain styles to specific pages. The top-most element within `<body>` will have a class name specific to the current sub-page. This is usually the page's name, all lowercase, with dashes instead of spaces, but you can easily check this yourself within the dev tools.

For example, if the pages name was "CFT Toolbox", and you wanted to target `.item`s, you would do:

```css
.cft-toolbox .item { border: 4px solid yellow; }
```

### Loading External Stylesheets

The URI of a stylesheet, either local or hosted on a remote CDN can be passed into the config file. The attribute `appConfig.externalStyleSheet` accepts either a string, or an array of strings. You can also pass custom font stylesheets here, they must be in a CSS format (for example, `https://fonts.googleapis.com/css2?family=Cutive+Mono`).
Expand Down
16 changes: 16 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Refused to Connect in Web Content View](#refused-to-connect-in-modal-or-workspace-view)
- [404 On Static Hosting](#404-on-static-hosting)
- [Yarn Build or Run Error](#yarn-error)
- [Remote Config Not Loading](#remote-config-not-loading)
- [Auth Validation Error: "should be object"](#auth-validation-error-should-be-object)
- [App Not Starting After Update to 2.0.4](#app-not-starting-after-update-to-204)
- [Keycloak Redirect Error](#keycloak-redirect-error)
Expand Down Expand Up @@ -104,6 +105,21 @@ Alternatively, as a workaround, you have several options:

---

## Remote Config Not Loading

If you've got a multi-page dashboard, and are hosting the additional config files yourself, then CORS rules will apply. A CORS error will look something like:

```
Access to XMLHttpRequest at 'https://example.com/raw/my-config.yml' from origin 'http://dashy.local' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
```

The solution is to add the appropriate headers onto the target server, to allow it to accept requests from the origin where you're running Dashy.

If it is a remote service, that you do not have admin access to, then another option is to proxy the request. Either host your own, or use a publicly accessible service, like [allorigins.win](https://allorigins.win), e.g: `https://api.allorigins.win/raw?url=https://pastebin.com/raw/4tZpaJV5`. For git-based services specifically, there's [raw.githack.com](https://raw.githack.com/)

---

## Auth Validation Error: "should be object"

In V 1.6.5 an update was made that in the future will become a breaking change. You will need to update you config to reflect this before V 2.0.0 is released. In the meantime, your previous config will continue to function normally, but you will see a validation warning. The change means that the structure of the `appConfig.auth` object is now an object, which has a `users` property.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Dashy",
"version": "2.0.7",
"version": "2.0.8",
"license": "MIT",
"main": "server",
"author": "Alicia Sykes <[email protected]> (https://aliciasykes.com)",
Expand Down
5 changes: 2 additions & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
<!-- Favicon + App Icon -->
<link rel="icon" type="image/png" sizes="64x64" href="<%= BASE_URL %>/web-icons/favicon-64x64.png">
<link rel="icon" type="image/png" sizes="32x32" href="web-icons/favicon-32x32.png">
<link rel="icon" href="/favicon.ico" />
<link rel="icon" type="image/png" href="<%= BASE_URL %>favicon.ico" />
<link rel="stylesheet" type="text/css" href="<%= BASE_URL %>loading-screen.css" />
<link rel="icon" type="image/png" href="/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/loading-screen.css" />
<!-- Default Page Title -->
<title>Dashy</title>
</head>
Expand Down
2 changes: 1 addition & 1 deletion server.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const app = express()
.use(sslServer.middleware)
// Serves up static files
.use(express.static(path.join(__dirname, 'dist')))
.use(express.static(path.join(__dirname, 'public')))
.use(express.static(path.join(__dirname, 'public'), { index: 'initialization.html' }))
// Load middlewares for parsing JSON, and supporting HTML5 history routing
.use(express.json({ limit: '1mb' }))
.use(history())
Expand Down
26 changes: 16 additions & 10 deletions services/save-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
const fsPromises = require('fs').promises;

module.exports = async (newConfig, render) => {
/* Either returns nothing (if using default path), or strips navigational characters from path */
const makeSafeFileName = (configObj) => {
if (!configObj || !configObj.filename) return undefined;
return configObj.filename.replaceAll('/', '').replaceAll('..', '');
};

const usersFileName = makeSafeFileName(newConfig);

// Define constants for the config file
const settings = {
defaultLocation: './public/',
Expand All @@ -16,11 +24,11 @@ module.exports = async (newConfig, render) => {
};

// Make the full file name and path to save the backup config file
const backupFilePath = `${settings.defaultLocation}${settings.filename}-`
const backupFilePath = `${settings.defaultLocation}${usersFileName || settings.filename}-`
+ `${Math.round(new Date() / 1000)}${settings.backupDenominator}`;

// The path where the main conf.yml should be read and saved to
const defaultFilePath = settings.defaultLocation + settings.defaultFile;
const defaultFilePath = settings.defaultLocation + (usersFileName || settings.defaultFile);

// Returns a string confirming successful job
const getSuccessMessage = () => `Successfully backed up ${settings.defaultFile} to`
Expand All @@ -36,16 +44,14 @@ module.exports = async (newConfig, render) => {
});

// Makes a backup of the existing config file
await fsPromises.copyFile(defaultFilePath, backupFilePath)
.catch((error) => {
render(getRenderMessage(false, `Unable to backup conf.yml: ${error}`));
});
await fsPromises
.copyFile(defaultFilePath, backupFilePath)
.catch((error) => render(getRenderMessage(false, `Unable to backup conf.yml: ${error}`)));

// Writes the new content to the conf.yml file
await fsPromises.writeFile(defaultFilePath, newConfig.config.toString(), writeFileOptions)
.catch((error) => {
render(getRenderMessage(false, `Unable to write changes to conf.yml: ${error}`));
});
await fsPromises
.writeFile(defaultFilePath, newConfig.config.toString(), writeFileOptions)
.catch((error) => render(getRenderMessage(false, `Unable to write to conf.yml: ${error}`)));

// If successful, then render hasn't yet been called- call it
await render(getRenderMessage(true));
Expand Down
6 changes: 5 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div id="dashy" :style="topLevelStyleModifications">
<div id="dashy" :style="topLevelStyleModifications" :class="subPageClassName">
<EditModeTopBanner v-if="isEditMode" />
<LoadingScreen :isLoading="isLoading" v-if="shouldShowSplash" />
<Header :pageInfo="pageInfo" />
Expand Down Expand Up @@ -72,6 +72,10 @@ export default {
isEditMode() {
return this.$store.state.editMode;
},
subPageClassName() {
const currentSubPage = this.$store.state.currentConfigInfo;
return (currentSubPage && currentSubPage.pageId) ? currentSubPage.pageId : '';
},
topLevelStyleModifications() {
const vc = this.visibleComponents;
if (!vc.footer && !vc.pageTitle) {
Expand Down
1 change: 1 addition & 0 deletions src/assets/interface-icons/config-pages.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading