mirror of https://github.com/lissy93/dashy
Merge f77c192e66
into 382f8f3ec0
This commit is contained in:
commit
869828bc0b
2
.env
2
.env
|
@ -52,7 +52,7 @@
|
|||
# VUE_APP_VERSION=2.0.0
|
||||
|
||||
# Directory for conf.yml backups
|
||||
# BACKUP_DIR=./user-data/
|
||||
# BACKUP_DIR=./user-data/config-backups
|
||||
|
||||
# Setup any other user defined vars by prepending VUE_APP_ to the var name
|
||||
# VUE_APP_pihole_ip=http://your.pihole.ip
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
name: ⭐ Hello non-Stargazers
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
jobs:
|
||||
check-user:
|
||||
if: >
|
||||
${{
|
||||
! contains( github.event.issue.labels.*.name, '📌 Keep Open') &&
|
||||
! contains( github.event.issue.labels.*.name, '🌈 Feedback') &&
|
||||
! contains( github.event.issue.labels.*.name, '💯 Showcase') &&
|
||||
github.event.comment.author_association != 'CONTRIBUTOR'
|
||||
}}
|
||||
runs-on: ubuntu-latest
|
||||
name: Add comment to issues opened by non-stargazers
|
||||
steps:
|
||||
- name: comment
|
||||
uses: qxip/please-star-light@v4
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
autoclose: false
|
||||
message: "If you're enjoying Dashy, consider dropping us a ⭐<br>_<sub>🤖 I'm a bot, and this message was automated</sub>_"
|
|
@ -42,7 +42,7 @@ RUN apk add --no-cache tzdata
|
|||
COPY --from=BUILD_IMAGE /app ./
|
||||
|
||||
# Finally, run start command to serve up the built application
|
||||
CMD [ "yarn", "build-and-start" ]
|
||||
CMD [ "yarn", "start" ]
|
||||
|
||||
# Expose the port
|
||||
EXPOSE ${PORT}
|
||||
|
|
|
@ -8,6 +8,14 @@
|
|||
<b><a href="./docs/showcase.md">User Showcase</a></b> | <b><a href="https://demo.dashy.to">Live Demo</a></b> | <b><a href="./docs/quick-start.md">Getting Started</a></b> | <b><a href="https://dashy.to/docs">Documentation</a></b> | <b><a href="https://github.com/Lissy93/dashy">GitHub</a></b>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<br>
|
||||
<sup>Dashy is kindly sponsored by <a href="https://umbrel.com?ref=dashy">Umbrel</a> - the personal home cloud and OS for self-hosting</sup><br>
|
||||
<a href="https://umbrel.com?ref=dashy">
|
||||
<img width="460" src="https://github.com/Lissy93/dashy/blob/WEBSITE/docs-site-source/static/umbrel-banner-github.jpg?raw=true" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
> [!NOTE]
|
||||
> Version [3.0.0](https://github.com/Lissy93/dashy/releases/tag/3.0.0) has been released, and requires some changes to your setup, see [#1529](https://github.com/Lissy93/dashy/discussions/1529) for details.
|
||||
|
||||
|
|
|
@ -32,7 +32,32 @@ Your dashboard should now be up and running at `http://localhost:8080` (or your
|
|||
|
||||
---
|
||||
|
||||
## 3. Configure
|
||||
## 3. User Data Directory
|
||||
|
||||
Your config file should be placed inside `user-data/` (in Docker, that's `/app/user-data/`).
|
||||
|
||||
This directory can also contain some optional assets you wish to use within your dashboard, like icons, fonts, styles, scripts, etc.
|
||||
|
||||
Any files placed here will be served up to the root of the domain, and override the contents of `public/`.
|
||||
For example, if you had `user-data/favicon.ico` this would be accessible at `http://my-dashy-instance.local/favicon.ico`
|
||||
|
||||
Example Files in `user-data`:
|
||||
- `conf.yml` - This is the only file that is compulsary, it's your main Dashy config
|
||||
- `**.yml` - Include more config files, if you'd like to have multiple pages, see [Multi-page support](/docs/pages-and-sections.md#multi-page-support) for docs
|
||||
- `favicon.ico` - The default favicon, shown in the browser's tab title
|
||||
- `initialization.html` - Static HTML page displayed before the app has finished compiling, see [`public/initialization.html`](https://github.com/Lissy93/dashy/blob/master/public/initialization.html)
|
||||
- `robots.txt` - Search engine crawl rules, override this if you want your dashboard to be indexable
|
||||
- `manifest.json` - PWA configuration file, for installing Dashy on mobile devices
|
||||
- `index.html` - The main index page which initializes the client-side app, copy it from [`/public/index.html`](https://github.com/Lissy93/dashy/blob/master/public/index.html)
|
||||
- `**.html` - Write your own HTML pages, and access them at `http://my-dashy-instance.local/my-page.html`
|
||||
- `fonts/` - Custom fonts (be sure to include the ones already in [`public/fonts`](https://github.com/Lissy93/dashy/tree/master/public/fonts)
|
||||
- `item-icons/` - To use your own icons for items on your dashboard, see [Icons --> Local Icons](/docs/icons.md#local-icons)
|
||||
- `web-icons/` - Override Dashy logo
|
||||
- `widget-resources/` - Fonts, icons and assets for custom widgets
|
||||
|
||||
---
|
||||
|
||||
## 4. Configure
|
||||
|
||||
Now that you've got Dashy running, you are going to want to set it up with your own content.
|
||||
Config is written in [YAML Format](https://yaml.org/), and saved in [`/user-data/conf.yml`](https://github.com/Lissy93/dashy/blob/master/user-data/conf.yml).
|
||||
|
@ -41,6 +66,7 @@ The format on the config file is pretty straight forward. There are three root a
|
|||
- [`pageInfo`](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md#pageinfo) - Dashboard meta data, like title, description, nav bar links and footer text
|
||||
- [`appConfig`](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md#appconfig-optional) - Dashboard settings, like themes, authentication, language and customization
|
||||
- [`sections`](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md#section) - An array of sections, each including an array of items
|
||||
- [`pages`](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md#pages-optional) - Have multiples pages in your dashboard
|
||||
|
||||
You can view a full list of all available config options in the [Configuring Docs](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md).
|
||||
|
||||
|
@ -76,11 +102,11 @@ Notes:
|
|||
- It's also possible to edit your config directly through the UI, and changes will be saved in this file
|
||||
- Check your config against Dashy's schema, with `docker exec -it [container-id] yarn validate-config`
|
||||
- You might find it helpful to look at some examples, a collection of which can be [found here](https://gist.github.com/Lissy93/000f712a5ce98f212817d20bc16bab10)
|
||||
- After editing your config, the app will rebuild in the background, which may take a minute
|
||||
- It's also possible to load a remote config, e.g. from a GitHub Gist
|
||||
|
||||
---
|
||||
|
||||
## 4. Further Customisation
|
||||
## 5. Further Customisation
|
||||
|
||||
Once you've got Dashy setup, you'll want to ensure the container is properly healthy, secured, backed up and kept up-to-date. All this is covered in the [Management Docs](https://github.com/Lissy93/dashy/blob/master/docs/management.md).
|
||||
|
||||
|
@ -97,7 +123,7 @@ You might also want to check out the docs for specific features you'd like to us
|
|||
|
||||
---
|
||||
|
||||
## 5. Final Note
|
||||
## 6. Final Note
|
||||
|
||||
If you need any help or support in getting Dashy running, head over to the [Discussions](https://github.com/Lissy93/dashy/discussions) page. If you think you've found a bug, please do [raise it](https://github.com/Lissy93/dashy/issues/new/choose) so it can be fixed. For contact options, see the [Support Page](https://github.com/Lissy93/dashy/blob/master/.github/SUPPORT.md).
|
||||
|
||||
|
@ -118,7 +144,7 @@ yarn build # Build the app
|
|||
yarn start # Start the app
|
||||
```
|
||||
|
||||
Then edit `./user-data/conf.yml` and rebuild the app with `yarn build`
|
||||
Then edit `./user-data/conf.yml`
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dashy",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"license": "MIT",
|
||||
"main": "server",
|
||||
"author": "Alicia Sykes <alicia@omg.lol> (https://aliciasykes.com)",
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
Before Width: | Height: | Size: 827 B |
|
@ -14,18 +14,23 @@ module.exports = async (newConfig, render) => {
|
|||
return configObj.filename.replaceAll('/', '').replaceAll('..', '');
|
||||
};
|
||||
|
||||
// Path to config file (with navigational characters stripped)
|
||||
const usersFileName = makeSafeFileName(newConfig);
|
||||
|
||||
// Path to user data directory
|
||||
const userDataDirectory = process.env.USER_DATA_DIR || './user-data/';
|
||||
|
||||
// Define constants for the config file
|
||||
const settings = {
|
||||
defaultLocation: process.env.USER_DATA_DIR || './user-data/',
|
||||
defaultLocation: userDataDirectory,
|
||||
backupLocation: process.env.BACKUP_DIR || path.join(userDataDirectory, 'config-backups'),
|
||||
defaultFile: 'conf.yml',
|
||||
filename: 'conf',
|
||||
backupDenominator: '.backup.yml',
|
||||
};
|
||||
|
||||
// Make the full file name and path to save the backup config file
|
||||
const backupFilePath = `${path.normalize(process.env.BACKUP_DIR || settings.defaultLocation)
|
||||
const backupFilePath = `${path.normalize(settings.backupLocation)
|
||||
}/${usersFileName || settings.filename}-`
|
||||
+ `${Math.round(new Date() / 1000)}${settings.backupDenominator}`;
|
||||
|
||||
|
@ -45,15 +50,20 @@ module.exports = async (newConfig, render) => {
|
|||
message: !success ? errorMsg : getSuccessMessage(),
|
||||
});
|
||||
|
||||
// Makes a backup of the existing config file
|
||||
// Create a backup of current config, and if backup dir doesn't yet exist, create it
|
||||
await fsPromises
|
||||
.copyFile(defaultFilePath, backupFilePath)
|
||||
.catch((error) => render(getRenderMessage(false, `Unable to backup ${settings.defaultFile}: ${error}`)));
|
||||
.mkdir(settings.backupLocation, { recursive: true })
|
||||
.then(() => fsPromises.copyFile(defaultFilePath, backupFilePath))
|
||||
.catch((error) => render(
|
||||
getRenderMessage(false, `Unable to backup ${settings.defaultFile}: ${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 to ${settings.defaultFile}: ${error}`)));
|
||||
.catch((error) => render(
|
||||
getRenderMessage(false, `Unable to write to ${settings.defaultFile}: ${error}`),
|
||||
));
|
||||
|
||||
// If successful, then render hasn't yet been called- call it
|
||||
await render(getRenderMessage(true));
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<LoadingScreen :isLoading="isLoading" v-if="shouldShowSplash" />
|
||||
<Header :pageInfo="pageInfo" />
|
||||
<router-view v-if="!isFetching" />
|
||||
<CriticalError v-if="hasCriticalError" />
|
||||
<Footer :text="footerText" v-if="visibleComponents.footer && !isFetching" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -12,6 +13,7 @@
|
|||
import Header from '@/components/PageStrcture/Header.vue';
|
||||
import Footer from '@/components/PageStrcture/Footer.vue';
|
||||
import EditModeTopBanner from '@/components/InteractiveEditor/EditModeTopBanner.vue';
|
||||
import CriticalError from '@/components/PageStrcture/CriticalError.vue';
|
||||
import LoadingScreen from '@/components/PageStrcture/LoadingScreen.vue';
|
||||
import { welcomeMsg } from '@/utils/CoolConsole';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
|
@ -29,6 +31,7 @@ export default {
|
|||
Footer,
|
||||
LoadingScreen,
|
||||
EditModeTopBanner,
|
||||
CriticalError,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -72,6 +75,9 @@ export default {
|
|||
isEditMode() {
|
||||
return this.$store.state.editMode;
|
||||
},
|
||||
hasCriticalError() {
|
||||
return this.$store.state.criticalError;
|
||||
},
|
||||
subPageClassName() {
|
||||
const currentSubPage = this.$store.state.currentConfigInfo;
|
||||
return (currentSubPage && currentSubPage.pageId) ? currentSubPage.pageId : '';
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
<template>
|
||||
<div class="critical-error-wrap" v-if="shouldShow">
|
||||
<button class="close" title="Close Warning" @click="close">🗙</button>
|
||||
<h3>Configuration Load Error</h3>
|
||||
<p>
|
||||
Dashy has failed to load correctly due to a configuration error.
|
||||
</p>
|
||||
<h4>Ensure that</h4>
|
||||
<ul>
|
||||
<li>The configuration file can be found at the specified location</li>
|
||||
<li>There are no CORS rules preventing client-side access</li>
|
||||
<li>The YAML is valid, parsable and matches the schema</li>
|
||||
</ul>
|
||||
<h4>Error Details</h4>
|
||||
<pre>{{ this.$store.state.criticalError }}</pre>
|
||||
<h4>Next Steps</h4>
|
||||
<ul>
|
||||
<li>Check the browser console for more details
|
||||
(<a href="https://github.com/Lissy93/dashy/blob/master/docs/troubleshooting.md#how-to-open-browser-console">see how</a>)
|
||||
</li>
|
||||
<li>View the
|
||||
<a href="https://github.com/Lissy93/dashy/blob/master/docs/troubleshooting.md">Troubleshooting Guide</a>
|
||||
and <a href="https://dashy.to/docs/">Docs</a>
|
||||
</li>
|
||||
<li>
|
||||
If you've verified the config is present, accessible and valid, and cannot find the solution
|
||||
in the troubleshooting, docs or GitHub issues,
|
||||
then <a href="https://github.com/Lissy93/dashy/issues/new/choose">open a ticket on GitHub</a>
|
||||
</li>
|
||||
<li>Click 'Ignore Critical Errors' below to not show this warning again</li>
|
||||
</ul>
|
||||
<button class="user-doesnt-care" @click="ignoreWarning">Ignore Critical Errors</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { localStorageKeys } from '@/utils/defaults';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
|
||||
export default {
|
||||
name: 'CriticalError',
|
||||
computed: {
|
||||
/* Determines if we should show this component.
|
||||
* If error present AND user hasn't disabled */
|
||||
shouldShow() {
|
||||
return this.$store.state.criticalError
|
||||
&& !localStorage[localStorageKeys.DISABLE_CRITICAL_WARNING];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Ignore all future errors, by putting a key in local storage */
|
||||
ignoreWarning() {
|
||||
localStorage.setItem(localStorageKeys.DISABLE_CRITICAL_WARNING, true);
|
||||
this.close();
|
||||
},
|
||||
/* Close this dialog, by removing this error from the local store */
|
||||
close() {
|
||||
this.$store.commit(Keys.CRITICAL_ERROR_MSG, null);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/media-queries.scss';
|
||||
.critical-error-wrap {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 3;
|
||||
background: var(--background-darker);
|
||||
padding: 1rem;
|
||||
border-radius: var(--curve-factor);
|
||||
color: var(--danger);
|
||||
border: 2px solid var(--danger);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
transition: all 0.2s ease-in-out;
|
||||
@include tablet-down {
|
||||
top: 50%;
|
||||
width: 85vw;
|
||||
}
|
||||
p, ul, h4, a {
|
||||
margin: 0;
|
||||
color: var(--white);
|
||||
}
|
||||
pre {
|
||||
color: var(--warning);
|
||||
font-size: 0.8rem;
|
||||
overflow: auto;
|
||||
background: var(--transparent-white-10);
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--curve-factor);
|
||||
}
|
||||
h4 {
|
||||
margin: 0.5rem 0 0 0;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
h3 {
|
||||
font-size: 2.2rem;
|
||||
text-align: center;
|
||||
background: var(--danger);
|
||||
color: var(--white);
|
||||
margin: -1rem -1rem 1rem -1rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
ul {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.user-doesnt-care {
|
||||
background: var(--background-darker);
|
||||
color: var(--white);
|
||||
border-radius: var(--curve-factor);
|
||||
border: none;
|
||||
text-decoration: underline;
|
||||
padding: 0.25rem 0.5rem;
|
||||
cursor: pointer;
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
transition: all 0.2s ease-in-out;
|
||||
&:hover {
|
||||
background: var(--danger);
|
||||
color: var(--background-darker);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1rem;
|
||||
background: var(--background);
|
||||
color: var(--primary);
|
||||
border: none;
|
||||
border-radius: var(--curve-factor);
|
||||
transition: all 0.2s ease-in-out;
|
||||
&:hover {
|
||||
background: var(--primary);
|
||||
color: var(--background);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,9 +1,15 @@
|
|||
<template>
|
||||
<div class="readme-stats">
|
||||
<img class="stats-card" v-if="!hideProfileCard" :src="profileCard" alt="Profile Card" />
|
||||
<img class="stats-card" v-if="!hideLanguagesCard" :src="topLanguagesCard" alt="Languages" />
|
||||
<a v-if="!hideProfileCard" :href="profileCardLink" target="_blank">
|
||||
<img class="stats-card" :src="profileCard" alt="Profile Card" />
|
||||
</a>
|
||||
<a v-if="!hideLanguagesCard" :href="profileCardLink" target="_blank">
|
||||
<img class="stats-card" :src="topLanguagesCard" alt="Languages" />
|
||||
</a>
|
||||
<template v-if="repos">
|
||||
<img class="stats-card" v-for="(repo, i) in repoCards" :key="i" :src="repo" :alt="repo" />
|
||||
<a v-for="(repo, i) in repoCards" :key="i" :href="repo.cardHref" target="_blank">
|
||||
<img class="stats-card" :src="repo.cardSrc" :alt="repo" />
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -61,6 +67,9 @@ export default {
|
|||
profileCard() {
|
||||
return `${widgetApiEndpoints.readMeStats}?username=${this.username}${this.cardConfig}`;
|
||||
},
|
||||
profileCardLink() {
|
||||
return `https://github.com/${this.username}`;
|
||||
},
|
||||
topLanguagesCard() {
|
||||
return `${widgetApiEndpoints.readMeStats}/top-langs/?username=${this.username}`
|
||||
+ `${this.cardConfig}&langs_count=12`;
|
||||
|
@ -70,8 +79,11 @@ export default {
|
|||
this.repos.forEach((repo) => {
|
||||
const username = repo.split('/')[0];
|
||||
const repoName = repo.split('/')[1];
|
||||
cards.push(`${widgetApiEndpoints.readMeStats}/pin/?username=${username}&repo=${repoName}`
|
||||
+ `${this.cardConfig}&show_owner=true`);
|
||||
cards.push({
|
||||
cardSrc: `${widgetApiEndpoints.readMeStats}/pin/?username=${username}`
|
||||
+ `&repo=${repoName}${this.cardConfig}&show_owner=true`,
|
||||
cardHref: `https://github.com/${username}/${repoName}`,
|
||||
});
|
||||
});
|
||||
return cards;
|
||||
},
|
||||
|
|
|
@ -34,13 +34,18 @@ const HomeMixin = {
|
|||
data: () => ({
|
||||
searchValue: '',
|
||||
}),
|
||||
async mounted() {
|
||||
// await this.getConfigForRoute();
|
||||
},
|
||||
watch: {
|
||||
async $route() {
|
||||
this.loadUpConfig();
|
||||
},
|
||||
pageInfo: {
|
||||
handler(newPageInfo) {
|
||||
if (newPageInfo && newPageInfo.title) {
|
||||
document.title = newPageInfo.title;
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.loadUpConfig();
|
||||
|
|
55
src/store.js
55
src/store.js
|
@ -41,6 +41,7 @@ const {
|
|||
INSERT_ITEM,
|
||||
UPDATE_CUSTOM_CSS,
|
||||
CONF_MENU_INDEX,
|
||||
CRITICAL_ERROR_MSG,
|
||||
} = Keys;
|
||||
|
||||
const store = new Vuex.Store({
|
||||
|
@ -51,6 +52,7 @@ const store = new Vuex.Store({
|
|||
modalOpen: false, // KB shortcut functionality will be disabled when modal is open
|
||||
currentConfigInfo: {}, // For multi-page support, will store info about config file
|
||||
isUsingLocalConfig: false, // If true, will use local config instead of fetched
|
||||
criticalError: null, // Will store a message, if a critical error occurs
|
||||
navigateConfToTab: undefined, // Used to switch active tab in config modal
|
||||
},
|
||||
getters: {
|
||||
|
@ -174,6 +176,10 @@ const store = new Vuex.Store({
|
|||
state.editMode = editMode;
|
||||
}
|
||||
},
|
||||
[CRITICAL_ERROR_MSG](state, message) {
|
||||
if (message) ErrorHandler(message);
|
||||
state.criticalError = message;
|
||||
},
|
||||
[UPDATE_ITEM](state, payload) {
|
||||
const { itemId, newItem } = payload;
|
||||
const newConfig = { ...state.config };
|
||||
|
@ -320,16 +326,39 @@ const store = new Vuex.Store({
|
|||
actions: {
|
||||
/* Fetches the root config file, only ever called by INITIALIZE_CONFIG */
|
||||
async [INITIALIZE_ROOT_CONFIG]({ commit }) {
|
||||
// Load and parse config from root config file
|
||||
const configFilePath = process.env.VUE_APP_CONFIG_PATH || '/conf.yml';
|
||||
const data = await yaml.load((await axios.get(configFilePath)).data);
|
||||
// Replace missing root properties with empty objects
|
||||
if (!data.appConfig) data.appConfig = {};
|
||||
if (!data.pageInfo) data.pageInfo = {};
|
||||
if (!data.sections) data.sections = [];
|
||||
// Set the state, and return data
|
||||
commit(SET_ROOT_CONFIG, data);
|
||||
return data;
|
||||
try {
|
||||
// Attempt to fetch the YAML file
|
||||
const response = await axios.get(configFilePath);
|
||||
let data;
|
||||
try {
|
||||
data = yaml.load(response.data);
|
||||
} catch (parseError) {
|
||||
commit(CRITICAL_ERROR_MSG, `Failed to parse YAML: ${parseError.message}`);
|
||||
return {};
|
||||
}
|
||||
// Replace missing root properties with empty objects
|
||||
if (!data.appConfig) data.appConfig = {};
|
||||
if (!data.pageInfo) data.pageInfo = {};
|
||||
if (!data.sections) data.sections = [];
|
||||
// Set the state, and return data
|
||||
commit(SET_ROOT_CONFIG, data);
|
||||
commit(CRITICAL_ERROR_MSG, null);
|
||||
return data;
|
||||
} catch (fetchError) {
|
||||
if (fetchError.response) {
|
||||
commit(
|
||||
CRITICAL_ERROR_MSG,
|
||||
'Failed to fetch configuration: Server responded with status '
|
||||
+ `${fetchError.response?.status || 'mystery status'}`,
|
||||
);
|
||||
} else if (fetchError.request) {
|
||||
commit(CRITICAL_ERROR_MSG, 'Failed to fetch configuration: No response from server');
|
||||
} else {
|
||||
commit(CRITICAL_ERROR_MSG, `Failed to fetch configuration: ${fetchError.message}`);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Fetches config and updates state
|
||||
|
@ -351,7 +380,7 @@ const store = new Vuex.Store({
|
|||
const json = JSON.parse(localSectionsRaw);
|
||||
if (json.length >= 1) localSections = json;
|
||||
} catch (e) {
|
||||
ErrorHandler('Malformed section data in local storage');
|
||||
commit(CRITICAL_ERROR_MSG, 'Malformed section data in local storage');
|
||||
}
|
||||
}
|
||||
if (localSections.length > 0) {
|
||||
|
@ -366,7 +395,7 @@ const store = new Vuex.Store({
|
|||
)?.path);
|
||||
|
||||
if (!subConfigPath) {
|
||||
ErrorHandler(`Unable to find config for '${subConfigId}'`);
|
||||
commit(CRITICAL_ERROR_MSG, `Unable to find config for '${subConfigId}'`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -389,14 +418,14 @@ const store = new Vuex.Store({
|
|||
commit(SET_IS_USING_LOCAL_CONFIG, true);
|
||||
}
|
||||
} catch (e) {
|
||||
ErrorHandler('Malformed section data in local storage for sub-config');
|
||||
commit(CRITICAL_ERROR_MSG, 'Malformed section data in local storage for sub-config');
|
||||
}
|
||||
}
|
||||
// Set the config
|
||||
commit(SET_CONFIG, configContent);
|
||||
commit(SET_CURRENT_CONFIG_INFO, { confPath: subConfigPath, confId: subConfigId });
|
||||
}).catch((err) => {
|
||||
ErrorHandler(`Unable to load config from '${subConfigPath}'`, err);
|
||||
commit(CRITICAL_ERROR_MSG, `Unable to load config from '${subConfigPath}'`, err);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
--transparent-white-70: #ffffffb3;
|
||||
--transparent-white-50: #ffffff80;
|
||||
--transparent-white-30: #ffffff4d;
|
||||
--transparent-white-10: #ffffff0f;
|
||||
|
||||
/* Color variables for specific components
|
||||
* all variables are optional, since they inherit initial values from above*
|
||||
|
|
|
@ -22,6 +22,11 @@ html {
|
|||
}
|
||||
}
|
||||
|
||||
#dashy {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Hide text, and show 'Loading...' while Vue is initializing tags */
|
||||
[v-cloak] > * { display:none }
|
||||
[v-cloak]::before { content: "loading…" }
|
||||
|
|
|
@ -29,6 +29,7 @@ const KEY_NAMES = [
|
|||
'INSERT_ITEM',
|
||||
'UPDATE_CUSTOM_CSS',
|
||||
'CONF_MENU_INDEX',
|
||||
'CRITICAL_ERROR_MSG',
|
||||
];
|
||||
|
||||
// Convert array of key names into an object, and export
|
||||
|
|
|
@ -135,6 +135,7 @@ module.exports = {
|
|||
MOST_USED: 'mostUsed',
|
||||
LAST_USED: 'lastUsed',
|
||||
KEYCLOAK_INFO: 'keycloakInfo',
|
||||
DISABLE_CRITICAL_WARNING: 'disableCriticalWarning',
|
||||
},
|
||||
/* Key names for cookie identifiers */
|
||||
cookieKeys: {
|
||||
|
|
Loading…
Reference in New Issue