feat: show validation errors from joi on the frontend

This commit is contained in:
Zephyrrus 2021-06-17 16:06:53 +03:00
parent 6fe5055e9d
commit 0cae7e9eda
7 changed files with 67 additions and 26 deletions

View File

@ -1,19 +1,36 @@
const Joi = require('joi');
const Route = require('../../structures/Route');
const Util = require('../../utils/Util');
const { schema } = require('../../structures/Setting');
const joiOptions = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true // remove unknown props
};
class configGET extends Route {
constructor() {
super('/service/config', 'post', { adminOnly: true });
}
run(req, res) {
async run(req, res) {
const { settings } = req.body;
const validationRes = schema.validate(settings, { abortEarly: false });
console.log(JSON.stringify(validationRes));
const { error, value } = schema.validate(settings, joiOptions);
if (error) {
return res.status(400).json({
errors: error.details.reduce((acc, v) => {
for (const p of v.path) {
acc[p] = (acc[p] || []).concat(v.message);
}
return acc;
}, {})
});
}
await Util.writeConfigToDb(value);
return res.status(200).json({ value });
}
}

View File

@ -116,9 +116,9 @@ const schema = Joi.object({
.description('Allows people to create new accounts'),
// Social and sharing
metaThemeColor: Joi.string().hex().min(3)
.max(6)
.default('20222b')
metaThemeColor: Joi.string().pattern(/^#([0-9a-f]{6}|[0-9a-f]{3})$/i).min(4)
.max(7)
.default('#20222b')
.meta({
section: Sections.SOCIAL_AND_SHARING
})

View File

@ -64,7 +64,7 @@ class Util {
static async writeConfigToDb(config) {
// TODO: Check that the config passes the joi schema validation
if (!config || !config.key || !config.key) return;
if (!config || !config.key) return;
try {
config.value = JSON.stringify(config.value);
await db.table('settings').insert(config);

View File

@ -3,7 +3,8 @@
<div v-for="[key, field] in Object.entries(settings)" :key="key">
<b-field
:label="field.flags.label"
:message="field.flags.description"
:message="getErrorMessage(key) || field.flags.description"
:type="getValidationType(key)"
class="field"
horizontal>
<b-input
@ -43,7 +44,6 @@
</b-field>
<!--
TODO: Add asterisk to required fields
TODO: Implement showing errors returned by backend/joi
-->
</div>
</div>
@ -108,6 +108,14 @@ export default {
return [...acc, ...item.allow];
}, []);
},
getValidationType(fieldName) {
if (Array.isArray(this.errors[fieldName])) return 'is-danger';
return null;
},
getErrorMessage(fieldName) {
if (Array.isArray(this.errors[fieldName])) return this.errors[fieldName].join('\n');
return null;
},
getValues() {
return this.values;
}
@ -119,6 +127,10 @@ export default {
.field {
margin-bottom: 1em;
::v-deep .help.is-danger {
white-space: pre-line;
}
}
.taginp {

View File

@ -15,7 +15,7 @@
<h5 class="title is-5 has-text-grey-lighter">
{{ sectionName }}
</h5>
<JoiObject ref="jois" :settings="fields" />
<JoiObject ref="jois" :settings="fields" :errors="validationErrors" />
</div>
<div class="mb2 mt2 text-center">
@ -42,6 +42,11 @@ export default {
JoiObject
},
middleware: ['auth', 'admin'],
data() {
return {
validationErrors: {}
};
},
computed: {
...mapState({
settings: state => state.admin.settings,
@ -74,16 +79,24 @@ export default {
onConfirm: () => this.saveSettings()
});
},
saveSettings() {
async saveSettings() {
// handle refs
let settings = {};
for (const joiComponent of this.$refs.jois) {
settings = { ...settings, ...joiComponent.getValues() };
}
this.$handler.executeAction('admin/saveSettings', settings);
// restart service
// this.$handler.executeAction('admin/restartService');
try {
await this.$store.dispatch('admin/saveSettings', settings);
this.$set(this, 'validationErrors', {});
await this.$store.dispatch('config/fetchSettings');
this.$handler.executeAction('admin/restartService');
} catch (e) {
if (e.response?.data?.errors) {
this.$set(this, 'validationErrors', e.response.data.errors);
}
}
}
},
head() {

View File

@ -28,7 +28,6 @@ export const actions = {
},
async saveSettings({ commit }, settings) {
const response = await this.$axios.$post('service/config', { settings });
commit('setSettings', response);
return response;
},

View File

@ -10,6 +10,15 @@ export const state = () => ({
userAccounts: false
});
export const actions = {
async fetchSettings({ commit }) {
const response = await this.$axios.$get('service/config');
commit('setSettings', response);
return response;
}
};
export const mutations = {
setSettings(state, { config }) {
state.version = `v${config.version}`;
@ -22,12 +31,3 @@ export const mutations = {
state.userAccounts = config.userAccounts;
}
};
export const actions = {
async fetchSettings({ commit }) {
const response = await this.$axios.$get('service/config');
commit('setSettings', response);
return response;
}
};