mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
Merge pull request #339 from YunoHost/uniformize-actionmap-api
[enh] Uniformize actionmap api + human readable actions
This commit is contained in:
commit
c3d86065fa
35 changed files with 291 additions and 176 deletions
|
@ -5,7 +5,6 @@
|
|||
|
||||
import store from '@/store'
|
||||
import { openWebSocket, getResponseData, handleError } from './handlers'
|
||||
import { objectToParams } from '@/helpers/commons'
|
||||
|
||||
|
||||
/**
|
||||
|
@ -31,6 +30,31 @@ import { objectToParams } from '@/helpers/commons'
|
|||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Converts an object literal into an `URLSearchParams` that can be turned into a
|
||||
* query string or used as a body in a `fetch` call.
|
||||
*
|
||||
* @param {Object} obj - An object literal to convert.
|
||||
* @param {Object} options
|
||||
* @param {Boolean} [options.addLocale=false] - Option to append the locale to the query string.
|
||||
* @return {URLSearchParams}
|
||||
*/
|
||||
export function objectToParams (obj, { addLocale = false } = {}) {
|
||||
const urlParams = new URLSearchParams()
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(v => urlParams.append(key, v))
|
||||
} else {
|
||||
urlParams.append(key, value)
|
||||
}
|
||||
}
|
||||
if (addLocale) {
|
||||
urlParams.append('locale', store.getters.locale)
|
||||
}
|
||||
return urlParams
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
options: {
|
||||
credentials: 'include',
|
||||
|
@ -55,9 +79,15 @@ export default {
|
|||
* @param {Options} [options={ wait = true, websocket = true, initial = false, asFormData = false }]
|
||||
* @return {Promise<Object|Error>} Promise that resolve the api response data or an error.
|
||||
*/
|
||||
async fetch (method, uri, data = {}, { wait = true, websocket = true, initial = false, asFormData = false } = {}) {
|
||||
async fetch (
|
||||
method,
|
||||
uri,
|
||||
data = {},
|
||||
humanKey = null,
|
||||
{ wait = true, websocket = true, initial = false, asFormData = false } = {}
|
||||
) {
|
||||
// `await` because Vuex actions returns promises by default.
|
||||
const request = await store.dispatch('INIT_REQUEST', { method, uri, initial, wait, websocket })
|
||||
const request = await store.dispatch('INIT_REQUEST', { method, uri, humanKey, initial, wait, websocket })
|
||||
|
||||
if (websocket) {
|
||||
await openWebSocket(request)
|
||||
|
@ -70,6 +100,10 @@ export default {
|
|||
options = { ...options, method, body: objectToParams(data, { addLocale: true }) }
|
||||
}
|
||||
|
||||
if (['upgrade', 'postinstall', 'reboot', 'shutdown', 'diagnsosis'].some(action => uri.includes(action))) {
|
||||
store.dispatch('END_REQUEST', { request, success: true, wait })
|
||||
return
|
||||
}
|
||||
const response = await fetch('/yunohost/api/' + uri, options)
|
||||
const responseData = await getResponseData(response)
|
||||
store.dispatch('END_REQUEST', { request, success: response.ok, wait })
|
||||
|
@ -92,10 +126,10 @@ export default {
|
|||
const results = []
|
||||
if (wait) store.commit('SET_WAITING', true)
|
||||
try {
|
||||
for (const [method, uri, data, options = {}] of queries) {
|
||||
for (const [method, uri, data, humanKey, options = {}] of queries) {
|
||||
if (wait) options.wait = false
|
||||
if (initial) options.initial = true
|
||||
results.push(await this[method.toLowerCase()](uri, data, options))
|
||||
results.push(await this[method.toLowerCase()](uri, data, humanKey, options))
|
||||
}
|
||||
} finally {
|
||||
// Stop waiting even if there is an error.
|
||||
|
@ -114,10 +148,10 @@ export default {
|
|||
* @param {Options} [options={}] - options to apply to the call (default is `{ websocket: false, wait: false }`)
|
||||
* @return {Promise<Object|Error>} Promise that resolve the api response data or an error.
|
||||
*/
|
||||
get (uri, data = null, options = {}) {
|
||||
get (uri, data = null, humanKey = null, options = {}) {
|
||||
options = { websocket: false, wait: false, ...options }
|
||||
if (typeof uri === 'string') return this.fetch('GET', uri, null, options)
|
||||
return store.dispatch('GET', { ...uri, options })
|
||||
if (typeof uri === 'string') return this.fetch('GET', uri, null, humanKey, options)
|
||||
return store.dispatch('GET', { ...uri, humanKey, options })
|
||||
},
|
||||
|
||||
|
||||
|
@ -129,9 +163,9 @@ export default {
|
|||
* @param {Options} [options={}] - options to apply to the call
|
||||
* @return {Promise<Object|Error>} Promise that resolve the api response data or an error.
|
||||
*/
|
||||
post (uri, data = {}, options = {}) {
|
||||
if (typeof uri === 'string') return this.fetch('POST', uri, data, options)
|
||||
return store.dispatch('POST', { ...uri, data, options })
|
||||
post (uri, data = {}, humanKey = null, options = {}) {
|
||||
if (typeof uri === 'string') return this.fetch('POST', uri, data, humanKey, options)
|
||||
return store.dispatch('POST', { ...uri, data, humanKey, options })
|
||||
},
|
||||
|
||||
|
||||
|
@ -143,9 +177,9 @@ export default {
|
|||
* @param {Options} [options={}] - options to apply to the call
|
||||
* @return {Promise<Object|Error>} Promise that resolve the api response data or an error.
|
||||
*/
|
||||
put (uri, data = {}, options = {}) {
|
||||
if (typeof uri === 'string') return this.fetch('PUT', uri, data, options)
|
||||
return store.dispatch('PUT', { ...uri, data, options })
|
||||
put (uri, data = {}, humanKey = null, options = {}) {
|
||||
if (typeof uri === 'string') return this.fetch('PUT', uri, data, humanKey, options)
|
||||
return store.dispatch('PUT', { ...uri, data, humanKey, options })
|
||||
},
|
||||
|
||||
|
||||
|
@ -157,8 +191,8 @@ export default {
|
|||
* @param {Options} [options={}] - options to apply to the call (default is `{ websocket: false, wait: false }`)
|
||||
* @return {Promise<Object|Error>} Promise that resolve the api response data or an error.
|
||||
*/
|
||||
delete (uri, data = {}, options = {}) {
|
||||
if (typeof uri === 'string') return this.fetch('DELETE', uri, data, options)
|
||||
return store.dispatch('DELETE', { ...uri, data, options })
|
||||
delete (uri, data = {}, humanKey = null, options = {}) {
|
||||
if (typeof uri === 'string') return this.fetch('DELETE', uri, data, humanKey, options)
|
||||
return store.dispatch('DELETE', { ...uri, data, humanKey, options })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export { default } from './api'
|
||||
export { default, objectToParams } from './api'
|
||||
export { handleError, registerGlobalErrorHandlers } from './handlers'
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
|
||||
<!-- REQUEST DESCRIPTION -->
|
||||
<strong class="request-desc">
|
||||
{{ request.uri | readableUri }}
|
||||
<small>({{ $t('history.methods.' + request.method) }})</small>
|
||||
{{ request.humanRoute }}
|
||||
</strong>
|
||||
|
||||
<div v-if="request.errors || request.warnings">
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import store from '@/store'
|
||||
|
||||
|
||||
/**
|
||||
* Allow to set a timeout on a `Promise` expected response.
|
||||
* The returned Promise will be rejected if the original Promise is not resolved or
|
||||
|
@ -19,31 +16,6 @@ export function timeout (promise, delay) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts an object literal into an `URLSearchParams` that can be turned into a
|
||||
* query string or used as a body in a `fetch` call.
|
||||
*
|
||||
* @param {Object} obj - An object literal to convert.
|
||||
* @param {Object} options
|
||||
* @param {Boolean} [options.addLocale=false] - Option to append the locale to the query string.
|
||||
* @return {URLSearchParams}
|
||||
*/
|
||||
export function objectToParams (obj, { addLocale = false } = {}) {
|
||||
const urlParams = new URLSearchParams()
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(v => urlParams.append(key, v))
|
||||
} else {
|
||||
urlParams.append(key, value)
|
||||
}
|
||||
}
|
||||
if (addLocale) {
|
||||
urlParams.append('locale', store.getters.locale)
|
||||
}
|
||||
return urlParams
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if passed value is an object literal.
|
||||
*
|
||||
|
|
|
@ -91,8 +91,8 @@
|
|||
"confirm_app_default": "Are you sure you want to make this app default?",
|
||||
"confirm_change_maindomain": "Are you sure you want to change the main domain?",
|
||||
"confirm_delete": "Are you sure you want to delete {name}?",
|
||||
"confirm_firewall_open": "Are you sure you want to open port {port} (protocol: {protocol}, connection: {connection})",
|
||||
"confirm_firewall_close": "Are you sure you want to close port {port} (protocol: {protocol}, connection: {connection})",
|
||||
"confirm_firewall_allow": "Are you sure you want to open port {port} (protocol: {protocol}, connection: {connection})",
|
||||
"confirm_firewall_disallow": "Are you sure you want to close port {port} (protocol: {protocol}, connection: {connection})",
|
||||
"confirm_install_custom_app": "WARNING! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk?",
|
||||
"confirm_install_domain_root": "Are you sure you want to install this application on '/'? You will not be able to install any other app on {domain}",
|
||||
"confirm_app_install": "Are you sure you want to install this application?",
|
||||
|
@ -201,6 +201,7 @@
|
|||
"groups_and_permissions_manage": "Manage groups and permissions",
|
||||
"permissions": "Permissions",
|
||||
"history": {
|
||||
"is_empty": "Nothing in history for now.",
|
||||
"title": "History",
|
||||
"last_action": "Last action:",
|
||||
"methods": {
|
||||
|
@ -330,6 +331,82 @@
|
|||
"rerun_diagnosis": "Rerun diagnosis",
|
||||
"restore": "Restore",
|
||||
"restart": "Restart",
|
||||
"human_routes": {
|
||||
"adminpw": "Change admin password",
|
||||
"apps": {
|
||||
"change_label": "Change label of '{prevName}' for '{nextName}'",
|
||||
"change_url": "Change access url of '{name}'",
|
||||
"install": "Install app '{name}'",
|
||||
"set_default": "Redirect '{domain}' domain root to '{name}'",
|
||||
"perform_action": "Perform action '{action}' of app '{name}'",
|
||||
"uninstall": "Uninstall app '{name}'",
|
||||
"update_config": "Update app '{name}' configuration"
|
||||
},
|
||||
"backups": {
|
||||
"create": "Create a backup",
|
||||
"delete": "Delete backup '{name}'",
|
||||
"restore": "Restore backup '{name}'"
|
||||
},
|
||||
"diagnosis": {
|
||||
"ignore": {
|
||||
"error": "Ignore an error",
|
||||
"warning": "Ignore a warning"
|
||||
},
|
||||
"run": "Run the diagnosis",
|
||||
"run_specific": "Run '{description}' diagnosis",
|
||||
"unignore": {
|
||||
"error": "Unignore an error",
|
||||
"warning": "Unignore a warning"
|
||||
}
|
||||
},
|
||||
"domains": {
|
||||
"add": "Add domain '{name}'",
|
||||
"delete": "Delete domain '{name}'",
|
||||
"install_LE": "Install certificate for '{name}'",
|
||||
"manual_renew_LE": "Renew certificate for '{name}'",
|
||||
"regen_selfsigned": "Renew self-signed certificate for '{name}'",
|
||||
"revert_to_selfsigned": "Revert to self-signed certificate for '{name}'",
|
||||
"set_default": "Set '{name}' as default domain"
|
||||
},
|
||||
"firewall": {
|
||||
"ports": "{action} port {port} ({protocol}, {connection})",
|
||||
"upnp": "{action} UPnP"
|
||||
},
|
||||
"groups": {
|
||||
"create": "Create group '{name}'",
|
||||
"delete": "Delete group '{name}'",
|
||||
"add": "Add '{user}' to group '{name}'",
|
||||
"remove": "Remove '{user}' from group '{name}'"
|
||||
},
|
||||
"migrations": {
|
||||
"run": "Run migrations",
|
||||
"skip": "Skip migrations"
|
||||
},
|
||||
"permissions": {
|
||||
"add": "Allow '{name}' to access '{perm}'",
|
||||
"remove": "Remove '{name}' access to '{perm}'"
|
||||
},
|
||||
"postinstall": "Run the post-install",
|
||||
"reboot": "Reboot the server",
|
||||
"services": {
|
||||
"restart": "Restart the service '{name}'",
|
||||
"start": "Start the service '{name}'",
|
||||
"stop": "Stop the service '{name}'"
|
||||
},
|
||||
"share_logs": "Generate link for log '{name}'",
|
||||
"shutdown": "Shutdown the server",
|
||||
"update": "Check for updates",
|
||||
"upgrade": {
|
||||
"system": "Upgrade the system",
|
||||
"apps": "Upgrade all apps",
|
||||
"app": "Upgrade '{app}' app"
|
||||
},
|
||||
"users": {
|
||||
"create": "Create user '{name}'",
|
||||
"delete": "Delete user '{name}'",
|
||||
"update": "Update user '{name}'"
|
||||
}
|
||||
},
|
||||
"run": "Run",
|
||||
"running": "Running",
|
||||
"save": "Save",
|
||||
|
|
|
@ -33,12 +33,10 @@ body {
|
|||
min-height: 100vh
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
.list-group-item {
|
||||
padding: $list-group-item-padding-y 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.menu-list .list-group-item {
|
||||
padding: $list-group-item-padding-y 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,10 +112,6 @@ body {
|
|||
top: 2px;
|
||||
}
|
||||
|
||||
.list-group-item .icon {
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
|
||||
// Fork-awesome overrides
|
||||
.fa-fw {
|
||||
width: 1.25em !important;
|
||||
|
|
|
@ -90,21 +90,21 @@ export default {
|
|||
},
|
||||
|
||||
actions: {
|
||||
'GET' ({ state, commit, rootState }, { uri, param, storeKey = uri, options = {} }) {
|
||||
'GET' ({ state, commit, rootState }, { uri, param, humanKey, storeKey = uri, options = {} }) {
|
||||
const noCache = !rootState.cache || options.noCache || false
|
||||
const currentState = param ? state[storeKey][param] : state[storeKey]
|
||||
// if data has already been queried, simply return
|
||||
if (currentState !== undefined && !noCache) return currentState
|
||||
|
||||
return api.fetch('GET', param ? `${uri}/${param}` : uri, null, options).then(responseData => {
|
||||
return api.fetch('GET', param ? `${uri}/${param}` : uri, null, humanKey, options).then(responseData => {
|
||||
const data = responseData[storeKey] ? responseData[storeKey] : responseData
|
||||
commit('SET_' + storeKey.toUpperCase(), param ? [param, data] : data)
|
||||
return param ? state[storeKey][param] : state[storeKey]
|
||||
})
|
||||
},
|
||||
|
||||
'POST' ({ state, commit }, { uri, storeKey = uri, data, options }) {
|
||||
return api.fetch('POST', uri, data, options).then(responseData => {
|
||||
'POST' ({ state, commit }, { uri, storeKey = uri, data, humanKey, options }) {
|
||||
return api.fetch('POST', uri, data, humanKey, options).then(responseData => {
|
||||
// FIXME api/domains returns null
|
||||
if (responseData === null) responseData = data
|
||||
responseData = responseData[storeKey] ? responseData[storeKey] : responseData
|
||||
|
@ -113,16 +113,16 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
'PUT' ({ state, commit }, { uri, param, storeKey = uri, data, options }) {
|
||||
return api.fetch('PUT', param ? `${uri}/${param}` : uri, data, options).then(responseData => {
|
||||
'PUT' ({ state, commit }, { uri, param, storeKey = uri, data, humanKey, options }) {
|
||||
return api.fetch('PUT', param ? `${uri}/${param}` : uri, data, humanKey, options).then(responseData => {
|
||||
const data = responseData[storeKey] ? responseData[storeKey] : responseData
|
||||
commit('UPDATE_' + storeKey.toUpperCase(), param ? [param, data] : data)
|
||||
return param ? state[storeKey][param] : state[storeKey]
|
||||
})
|
||||
},
|
||||
|
||||
'DELETE' ({ commit }, { uri, param, storeKey = uri, data, options }) {
|
||||
return api.fetch('DELETE', param ? `${uri}/${param}` : uri, data, options).then(() => {
|
||||
'DELETE' ({ commit }, { uri, param, storeKey = uri, data, humanKey, options }) {
|
||||
return api.fetch('DELETE', param ? `${uri}/${param}` : uri, data, humanKey, options).then(() => {
|
||||
commit('DEL_' + storeKey.toUpperCase(), param)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import Vue from 'vue'
|
||||
import api from '@/api'
|
||||
import router from '@/router'
|
||||
import { timeout } from '@/helpers/commons'
|
||||
import i18n from '@/i18n'
|
||||
import { timeout, isObjectLiteral } from '@/helpers/commons'
|
||||
|
||||
export default {
|
||||
state: {
|
||||
|
@ -107,7 +108,7 @@ export default {
|
|||
},
|
||||
|
||||
'LOGIN' ({ dispatch }, password) {
|
||||
return api.post('login', { password }, { websocket: false }).then(() => {
|
||||
return api.post('login', { password }, null, { websocket: false }).then(() => {
|
||||
dispatch('CONNECT')
|
||||
})
|
||||
},
|
||||
|
@ -123,8 +124,12 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
'INIT_REQUEST' ({ commit }, { method, uri, initial, wait, websocket }) {
|
||||
let request = { method, uri, initial, status: 'pending' }
|
||||
'INIT_REQUEST' ({ commit }, { method, uri, humanKey, initial, wait, websocket }) {
|
||||
// Try to find a description for an API route to display in history and modals
|
||||
const { key, ...args } = isObjectLiteral(humanKey) ? humanKey : { key: humanKey }
|
||||
const humanRoute = key ? i18n.t('human_routes.' + key, args) : `[${method}] /${uri}`
|
||||
|
||||
let request = { method, uri, humanRoute, initial, status: 'pending' }
|
||||
if (websocket) {
|
||||
request = { ...request, messages: [], date: Date.now(), warnings: 0, errors: 0 }
|
||||
commit('ADD_HISTORY_ACTION', request)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
:key="item.routeName"
|
||||
:to="{ name: item.routeName }"
|
||||
>
|
||||
<icon :iname="item.icon" class="lg" />
|
||||
<icon :iname="item.icon" class="lg ml-1" />
|
||||
<h4>{{ $t(item.translation) }}</h4>
|
||||
<icon iname="chevron-right" class="lg fs-sm ml-auto" />
|
||||
</b-list-group-item>
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
class="accordion" role="tablist"
|
||||
id="history" ref="history"
|
||||
>
|
||||
<p v-if="history.length === 0" class="alert m-0 px-2 py-1">
|
||||
{{ $t('history.is_empty') }}
|
||||
</p>
|
||||
|
||||
<!-- ACTION LIST -->
|
||||
<b-card
|
||||
v-for="(action, i) in history" :key="i"
|
||||
|
|
|
@ -38,11 +38,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/api'
|
||||
import api, { objectToParams } from '@/api'
|
||||
import { validationMixin } from 'vuelidate'
|
||||
|
||||
import { formatI18nField, formatYunoHostArguments, formatFormData } from '@/helpers/yunohostArguments'
|
||||
import { objectToParams } from '@/helpers/commons'
|
||||
|
||||
export default {
|
||||
name: 'AppActions',
|
||||
|
@ -100,7 +99,11 @@ export default {
|
|||
// FIXME api expects at least one argument ?! (fake one given with { dontmindthis } )
|
||||
const args = objectToParams(action.form ? formatFormData(action.form) : { dontmindthis: undefined })
|
||||
|
||||
api.put(`apps/${this.id}/actions/${action.id}`, { args }).then(response => {
|
||||
api.put(
|
||||
`apps/${this.id}/actions/${action.id}`,
|
||||
{ args },
|
||||
{ key: 'apps.perform_action', action: action.id, name: this.id }
|
||||
).then(() => {
|
||||
this.$refs.view.fetchQueries()
|
||||
}).catch(err => {
|
||||
if (err.name !== 'APIBadRequestError') throw err
|
||||
|
|
|
@ -164,7 +164,7 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
queries: [
|
||||
['GET', 'appscatalog?full&with_categories']
|
||||
['GET', 'apps/catalog?full&with_categories']
|
||||
],
|
||||
|
||||
// Data
|
||||
|
|
|
@ -38,9 +38,8 @@
|
|||
import { validationMixin } from 'vuelidate'
|
||||
|
||||
// FIXME needs test and rework
|
||||
import api from '@/api'
|
||||
import api, { objectToParams } from '@/api'
|
||||
import { formatI18nField, formatYunoHostArguments, formatFormData } from '@/helpers/yunohostArguments'
|
||||
import { objectToParams } from '@/helpers/commons'
|
||||
|
||||
export default {
|
||||
name: 'AppConfigPanel',
|
||||
|
@ -103,7 +102,9 @@ export default {
|
|||
applyConfig (id_) {
|
||||
const args = objectToParams(formatFormData(this.forms[id_]))
|
||||
|
||||
api.post(`apps/${this.id}/config`, { args }).then(response => {
|
||||
api.put(
|
||||
`apps/${this.id}/config`, { args }, { key: 'apps.update_config', name: this.id }
|
||||
).then(response => {
|
||||
console.log('SUCCESS', response)
|
||||
}).catch(err => {
|
||||
if (err.name !== 'APIBadRequestError') throw err
|
||||
|
|
|
@ -252,7 +252,11 @@ export default {
|
|||
|
||||
changeLabel (permName, data) {
|
||||
data.show_tile = data.show_tile ? 'True' : 'False'
|
||||
api.put('users/permissions/' + permName, data).then(this.$refs.view.fetchQueries)
|
||||
api.put(
|
||||
'users/permissions/' + permName,
|
||||
data,
|
||||
{ key: 'apps.change_label', prevName: this.infos.label, nextName: data.label }
|
||||
).then(this.$refs.view.fetchQueries)
|
||||
},
|
||||
|
||||
async changeUrl () {
|
||||
|
@ -262,7 +266,8 @@ export default {
|
|||
const { domain, path } = this.form.url
|
||||
api.put(
|
||||
`apps/${this.id}/changeurl`,
|
||||
{ domain, path: '/' + path }
|
||||
{ domain, path: '/' + path },
|
||||
{ key: 'apps.change_url', name: this.infos.label }
|
||||
).then(this.$refs.view.fetchQueries)
|
||||
},
|
||||
|
||||
|
@ -270,7 +275,11 @@ export default {
|
|||
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_app_default'))
|
||||
if (!confirmed) return
|
||||
|
||||
api.put(`apps/${this.id}/default`).then(this.$refs.view.fetchQueries)
|
||||
api.put(
|
||||
`apps/${this.id}/default`,
|
||||
{},
|
||||
{ key: 'apps.set_default', name: this.infos.label, domain: this.app.domain }
|
||||
).then(this.$refs.view.fetchQueries)
|
||||
},
|
||||
|
||||
async uninstall () {
|
||||
|
@ -279,7 +288,7 @@ export default {
|
|||
)
|
||||
if (!confirmed) return
|
||||
|
||||
api.delete('apps/' + this.id).then(() => {
|
||||
api.delete('apps/' + this.id, {}, { key: 'apps.uninstall', name: this.infos.label }).then(() => {
|
||||
this.$router.push({ name: 'app-list' })
|
||||
})
|
||||
}
|
||||
|
|
|
@ -49,8 +49,7 @@
|
|||
<script>
|
||||
import { validationMixin } from 'vuelidate'
|
||||
|
||||
import api from '@/api'
|
||||
import { objectToParams } from '@/helpers/commons'
|
||||
import api, { objectToParams } from '@/api'
|
||||
import { formatYunoHostArguments, formatI18nField, formatFormData } from '@/helpers/yunohostArguments'
|
||||
|
||||
export default {
|
||||
|
@ -93,7 +92,7 @@ export default {
|
|||
},
|
||||
|
||||
getApiManifest () {
|
||||
return api.get('appscatalog?full').then(response => response.apps[this.id].manifest)
|
||||
return api.get('apps/catalog?full').then(response => response.apps[this.id].manifest)
|
||||
},
|
||||
|
||||
formatManifestData (manifest) {
|
||||
|
@ -129,7 +128,7 @@ export default {
|
|||
const { data: args, label } = formatFormData(this.form, { extract: ['label'] })
|
||||
const data = { app: this.id, label, args: objectToParams(args) }
|
||||
|
||||
api.post('apps', data).then(response => {
|
||||
api.post('apps', data, { key: 'apps.install', name: this.name }).then(() => {
|
||||
this.$router.push({ name: 'app-list' })
|
||||
}).catch(err => {
|
||||
if (err.name !== 'APIBadRequestError') throw err
|
||||
|
|
|
@ -164,7 +164,7 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
api.post('backup', data).then(response => {
|
||||
api.post('backups', data, 'backups.create').then(() => {
|
||||
this.$router.push({ name: 'backup-list', params: { id: this.id } })
|
||||
})
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
queries: [
|
||||
['GET', `backup/archives/${this.name}?with_details`]
|
||||
['GET', `backups/${this.name}?with_details`]
|
||||
],
|
||||
selected: [],
|
||||
error: '',
|
||||
|
@ -210,7 +210,9 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
api.post('backup/restore/' + this.name, data).then(response => {
|
||||
api.put(
|
||||
`backups/${this.name}/restore`, data, { key: 'backups.restore', name: this.name }
|
||||
).then(() => {
|
||||
this.isValid = null
|
||||
}).catch(err => {
|
||||
if (err.name !== 'APIBadRequestError') throw err
|
||||
|
@ -223,14 +225,16 @@ export default {
|
|||
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_delete', { name: this.name }))
|
||||
if (!confirmed) return
|
||||
|
||||
api.delete('backup/archives/' + this.name).then(() => {
|
||||
api.delete(
|
||||
'backups/' + this.name, {}, { key: 'backups.delete', name: this.name }
|
||||
).then(() => {
|
||||
this.$router.push({ name: 'backup-list', params: { id: this.id } })
|
||||
})
|
||||
},
|
||||
|
||||
downloadBackup () {
|
||||
const host = this.$store.getters.host
|
||||
window.open(`https://${host}/yunohost/api/backup/download/${this.name}`, '_blank')
|
||||
window.open(`https://${host}/yunohost/api/backups/${this.name}/download`, '_blank')
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
queries: [
|
||||
['GET', 'backup/archives?with_info']
|
||||
['GET', 'backups?with_info']
|
||||
],
|
||||
archives: undefined
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
</template>
|
||||
|
||||
<template #header-buttons>
|
||||
<b-button size="sm" :variant="report.items ? 'info' : 'success'" @click="runDiagnosis(report.id)">
|
||||
<b-button size="sm" :variant="report.items ? 'info' : 'success'" @click="runDiagnosis(report)">
|
||||
<icon iname="refresh" /> {{ $t('rerun_diagnosis') }}
|
||||
</b-button>
|
||||
</template>
|
||||
|
@ -64,13 +64,13 @@
|
|||
<div class="d-flex flex-column flex-lg-row ml-auto">
|
||||
<b-button
|
||||
v-if="item.ignored" size="sm"
|
||||
@click="toggleIgnoreIssue(false, report, item)"
|
||||
@click="toggleIgnoreIssue('unignore', report, item)"
|
||||
>
|
||||
<icon iname="bell" /> {{ $t('unignore') }}
|
||||
</b-button>
|
||||
<b-button
|
||||
v-else-if="item.issue" variant="warning" size="sm"
|
||||
@click="toggleIgnoreIssue(true, report, item)"
|
||||
@click="toggleIgnoreIssue('ignore', report, item)"
|
||||
>
|
||||
<icon iname="bell-slash" /> {{ $t('ignore') }}
|
||||
</b-button>
|
||||
|
@ -115,8 +115,8 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
queries: [
|
||||
['POST', 'diagnosis/run?except_if_never_ran_yet'],
|
||||
['GET', 'diagnosis/show?full']
|
||||
['PUT', 'diagnosis/run?except_if_never_ran_yet', {}, 'diagnosis.run'],
|
||||
['GET', 'diagnosis?full']
|
||||
],
|
||||
reports: undefined
|
||||
}
|
||||
|
@ -171,22 +171,30 @@ export default {
|
|||
this.reports = reports
|
||||
},
|
||||
|
||||
runDiagnosis (id = null) {
|
||||
runDiagnosis ({ id = null, description } = {}) {
|
||||
const param = id !== null ? '?force' : ''
|
||||
const data = id !== null ? { categories: [id] } : {}
|
||||
api.post('diagnosis/run' + param, data).then(this.$refs.view.fetchQueries)
|
||||
|
||||
api.put(
|
||||
'diagnosis/run' + param,
|
||||
data,
|
||||
{ key: 'diagnosis.run' + (id !== null ? '_specific' : ''), description }
|
||||
).then(this.$refs.view.fetchQueries)
|
||||
},
|
||||
|
||||
toggleIgnoreIssue (ignore, report, item) {
|
||||
const key = (ignore ? 'add' : 'remove') + '_filter'
|
||||
toggleIgnoreIssue (action, report, item) {
|
||||
const filterArgs = Object.entries(item.meta).reduce((filterArgs, entries) => {
|
||||
filterArgs.push(entries.join('='))
|
||||
return filterArgs
|
||||
}, [report.id])
|
||||
|
||||
api.post('diagnosis/ignore', { [key]: filterArgs }).then(() => {
|
||||
item.ignored = ignore
|
||||
if (ignore) {
|
||||
api.put(
|
||||
'diagnosis/' + action,
|
||||
{ filter: filterArgs },
|
||||
`diagnosis.${action}.${item.status.toLowerCase()}`
|
||||
).then(() => {
|
||||
item.ignored = action === 'ignore'
|
||||
if (item.ignored) {
|
||||
report[item.status.toLowerCase() + 's']--
|
||||
} else {
|
||||
report.ignoreds--
|
||||
|
|
|
@ -27,8 +27,7 @@ export default {
|
|||
onSubmit ({ domain, domainType }) {
|
||||
const uri = 'domains' + (domainType === 'dynDomain' ? '?dyndns' : '')
|
||||
api.post(
|
||||
{ uri, storeKey: 'domains' },
|
||||
{ domain }
|
||||
{ uri, storeKey: 'domains' }, { domain }, { key: 'domains.add', name: domain }
|
||||
).then(() => {
|
||||
this.$router.push({ name: 'domain-list' })
|
||||
}).catch(err => {
|
||||
|
|
|
@ -84,7 +84,7 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
queries: [
|
||||
['GET', `domains/cert-status/${this.name}?full`]
|
||||
['GET', `domains/${this.name}/cert?full`]
|
||||
],
|
||||
cert: undefined,
|
||||
actionsEnabled: undefined
|
||||
|
@ -147,13 +147,13 @@ export default {
|
|||
const confirmed = await this.$askConfirmation(this.$i18n.t(`confirm_cert_${action}`))
|
||||
if (!confirmed) return
|
||||
|
||||
let uri = 'domains/cert-install/' + this.name
|
||||
let uri = `domains/${this.name}/cert`
|
||||
if (action === 'regen_selfsigned') uri += '?self_signed'
|
||||
else if (action === 'manual_renew_LE') uri += '?force'
|
||||
else if (action === 'revert_to_selfsigned') uri += '?self_signed&force'
|
||||
// FIXME trigger loading ? while posting ? while getting ?
|
||||
// this.$refs.view.fallback_loading = true
|
||||
api.post(uri).then(this.$refs.view.fetchQueries)
|
||||
api.put(
|
||||
uri, {}, { key: 'domains.' + action, name: this.name }
|
||||
).then(this.$refs.view.fetchQueries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ export default {
|
|||
if (!confirmed) return
|
||||
|
||||
api.delete(
|
||||
{ uri: 'domains', param: this.name }
|
||||
{ uri: 'domains', param: this.name }, {}, { key: 'domains.delete', name: this.name }
|
||||
).then(() => {
|
||||
this.$router.push({ name: 'domain-list' })
|
||||
})
|
||||
|
@ -94,8 +94,9 @@ export default {
|
|||
if (!confirmed) return
|
||||
|
||||
api.put(
|
||||
{ uri: 'domains/main', storeKey: 'main_domain' },
|
||||
{ new_main_domain: this.name }
|
||||
{ uri: `domains/${this.name}/main`, storeKey: 'main_domain' },
|
||||
{},
|
||||
{ key: 'domains.set_default', name: this.name }
|
||||
).then(() => {
|
||||
// FIXME Have to commit by hand here since the response is empty (should return the given name)
|
||||
this.$store.commit('UPDATE_MAIN_DOMAIN', this.name)
|
||||
|
|
|
@ -45,7 +45,8 @@ export default {
|
|||
onSubmit () {
|
||||
api.post(
|
||||
{ uri: 'users/groups', storeKey: 'groups' },
|
||||
this.form
|
||||
this.form,
|
||||
{ key: 'groups.create', name: this.form.groupname }
|
||||
).then(() => {
|
||||
this.$router.push({ name: 'group-list' })
|
||||
}).catch(err => {
|
||||
|
|
|
@ -216,22 +216,28 @@ export default {
|
|||
},
|
||||
|
||||
onPermissionChanged ({ item, index, name, groupType, action }) {
|
||||
const uri = 'users/permissions/' + item
|
||||
const data = { [action]: name }
|
||||
// const uri = 'users/permissions/' + item
|
||||
// const data = { [action]: name }
|
||||
const from = action === 'add' ? 'availablePermissions' : 'permissions'
|
||||
const to = action === 'add' ? 'permissions' : 'availablePermissions'
|
||||
api.put(uri, data).then(() => {
|
||||
api.put(
|
||||
`users/permissions/${item}/${action}/${name}`,
|
||||
{},
|
||||
{ key: 'permissions.' + action, perm: item.replace('.main', ''), name }
|
||||
).then(() => {
|
||||
this[groupType + 'Groups'][name][from].splice(index, 1)
|
||||
this[groupType + 'Groups'][name][to].push(item)
|
||||
})
|
||||
},
|
||||
|
||||
onUserChanged ({ item, index, name, action }) {
|
||||
const uri = 'users/groups/' + name
|
||||
const data = { [action]: item }
|
||||
const from = action === 'add' ? 'availableMembers' : 'members'
|
||||
const to = action === 'add' ? 'members' : 'availableMembers'
|
||||
api.put(uri, data).then(() => {
|
||||
api.put(
|
||||
`users/groups/${name}/${action}/${item}`,
|
||||
{},
|
||||
{ key: 'groups.' + action, user: item, name }
|
||||
).then(() => {
|
||||
this.normalGroups[name][from].splice(index, 1)
|
||||
this.normalGroups[name][to].push(item)
|
||||
})
|
||||
|
@ -255,7 +261,7 @@ export default {
|
|||
if (!confirmed) return
|
||||
|
||||
api.delete(
|
||||
{ uri: 'users/groups', param: name, storeKey: 'groups' }
|
||||
{ uri: 'users/groups', param: name, storeKey: 'groups' }, {}, { key: 'groups.delete', name }
|
||||
).then(() => {
|
||||
Vue.delete(this.normalGroups, name)
|
||||
})
|
||||
|
|
|
@ -120,13 +120,11 @@ export default {
|
|||
)
|
||||
if (!confirmed) return
|
||||
|
||||
if (!['start', 'restart', 'stop'].includes(action)) return
|
||||
const method = action === 'stop' ? 'delete' : 'put'
|
||||
const uri = action === 'restart'
|
||||
? `services/${this.name}/restart`
|
||||
: 'services/' + this.name
|
||||
|
||||
api[method](uri).then(this.$refs.view.fetchQueries)
|
||||
api.put(
|
||||
`services/${this.name}/${action}`,
|
||||
{},
|
||||
{ key: 'services.' + action, name: this.name }
|
||||
).then(this.$refs.view.fetchQueries)
|
||||
},
|
||||
|
||||
shareLogs () {
|
||||
|
|
|
@ -44,8 +44,8 @@ export default {
|
|||
this.serverError = ''
|
||||
|
||||
api.fetchAll(
|
||||
[['POST', 'login', { password: currentPassword }, { websocket: false }],
|
||||
['PUT', 'admisnpw', { new_password: password }]],
|
||||
[['POST', 'login', { password: currentPassword }, null, { websocket: false }],
|
||||
['PUT', 'adminpw', { new_password: password }, 'adminpw']],
|
||||
{ wait: true }
|
||||
).then(() => {
|
||||
this.$store.dispatch('DISCONNECT')
|
||||
|
|
|
@ -115,8 +115,8 @@ export default {
|
|||
|
||||
// Ports form data
|
||||
actionChoices: [
|
||||
{ value: 'open', text: this.$i18n.t('open') },
|
||||
{ value: 'close', text: this.$i18n.t('close') }
|
||||
{ value: 'allow', text: this.$i18n.t('open') },
|
||||
{ value: 'disallow', text: this.$i18n.t('close') }
|
||||
],
|
||||
connectionChoices: [
|
||||
{ value: 'ipv4', text: this.$i18n.t('ipv4') },
|
||||
|
@ -128,7 +128,7 @@ export default {
|
|||
{ value: 'Both', text: this.$i18n.t('both') }
|
||||
],
|
||||
form: {
|
||||
action: 'open',
|
||||
action: 'allow',
|
||||
port: undefined,
|
||||
connection: 'ipv4',
|
||||
protocol: 'TCP'
|
||||
|
@ -176,27 +176,21 @@ export default {
|
|||
this.upnpEnabled = data.uPnP.enabled
|
||||
},
|
||||
|
||||
togglePort ({ action, port, protocol, connection }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.$askConfirmation(
|
||||
this.$i18n.t('confirm_firewall_' + action, { port, protocol, connection })
|
||||
).then(confirmed => {
|
||||
if (confirmed) {
|
||||
const method = action === 'open' ? 'post' : 'delete'
|
||||
api[method](
|
||||
`/firewall/port?${connection}_only`,
|
||||
{ port, protocol },
|
||||
{ wait: false }
|
||||
).then(() => {
|
||||
resolve(confirmed)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
} else {
|
||||
resolve(confirmed)
|
||||
}
|
||||
})
|
||||
})
|
||||
async togglePort ({ action, port, protocol, connection }) {
|
||||
const confirmed = await this.$askConfirmation(
|
||||
this.$i18n.t('confirm_firewall_' + action, { port, protocol, connection })
|
||||
)
|
||||
if (!confirmed) {
|
||||
return Promise.resolve(confirmed)
|
||||
}
|
||||
|
||||
const actionTrad = this.$i18n.t({ allow: 'open', disallow: 'close' }[action])
|
||||
return api.put(
|
||||
`firewall/${protocol}/${action}/${port}?${connection}_only`,
|
||||
{},
|
||||
{ key: 'firewall.ports', protocol, action: actionTrad, port, connection },
|
||||
{ wait: false }
|
||||
).then(() => confirmed)
|
||||
},
|
||||
|
||||
async toggleUpnp (value) {
|
||||
|
@ -204,7 +198,11 @@ export default {
|
|||
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_upnp_' + action))
|
||||
if (!confirmed) return
|
||||
|
||||
api.get('firewall/upnp?action=' + action, null, { websocket: true, wait: true }).then(() => {
|
||||
api.put(
|
||||
'firewall/upnp/' + action,
|
||||
{},
|
||||
{ key: 'firewall.upnp', action: this.$i18n.t(action) }
|
||||
).then(() => {
|
||||
// FIXME Couldn't test when it works.
|
||||
this.$refs.view.fetchQueries()
|
||||
}).catch(err => {
|
||||
|
@ -215,7 +213,7 @@ export default {
|
|||
|
||||
onTablePortToggling (port, protocol, connection, index, value) {
|
||||
this.$set(this.protocols[protocol][index], connection, value)
|
||||
const action = value ? 'open' : 'close'
|
||||
const action = value ? 'allow' : 'disallow'
|
||||
this.togglePort({ action, port, protocol, connection }).then(toggled => {
|
||||
// Revert change on cancel
|
||||
if (!toggled) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
:key="item.routeName"
|
||||
:to="{name: item.routeName}"
|
||||
>
|
||||
<icon :iname="item.icon" class="lg" />
|
||||
<icon :iname="item.icon" class="lg ml-1" />
|
||||
<h4>{{ $t(item.translation) }}</h4>
|
||||
<icon iname="chevron-right" class="lg fs-sm ml-auto" />
|
||||
</b-list-group-item>
|
||||
|
|
|
@ -60,8 +60,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/api'
|
||||
import { objectToParams, escapeHtml } from '@/helpers/commons'
|
||||
import api, { objectToParams } from '@/api'
|
||||
import { escapeHtml } from '@/helpers/commons'
|
||||
import { readableDate } from '@/helpers/filters/date'
|
||||
|
||||
export default {
|
||||
|
@ -127,7 +127,12 @@ export default {
|
|||
},
|
||||
|
||||
shareLogs () {
|
||||
api.get(`logs/${this.name}?share`, null, { websocket: true }).then(({ url }) => {
|
||||
api.get(
|
||||
`logs/${this.name}/share`,
|
||||
null,
|
||||
{ key: 'share_logs', name: this.name },
|
||||
{ websocket: true }
|
||||
).then(({ url }) => {
|
||||
window.open(url, '_blank')
|
||||
})
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ export default {
|
|||
}
|
||||
// Check that every migration's disclaimer has been checked.
|
||||
if (Object.values(this.checked).every(value => value === true)) {
|
||||
api.post('migrations/run?accept_disclaimer').then(() => {
|
||||
api.post('migrations/run?accept_disclaimer', 'migrations.run').then(() => {
|
||||
this.$refs.view.fetchQueries()
|
||||
})
|
||||
}
|
||||
|
@ -130,8 +130,7 @@ export default {
|
|||
async skipMigration (id) {
|
||||
const confirmed = await this.$askConfirmation(this.$i18n.t('confirm_migrations_skip'))
|
||||
if (!confirmed) return
|
||||
|
||||
api.post('/migrations/run', { skip: '', targets: id }).then(() => {
|
||||
api.post('/migrations/run', { skip: '', targets: id }, 'migration.skip').then(() => {
|
||||
this.$refs.view.fetchQueries()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ export default {
|
|||
if (!confirmed) return
|
||||
|
||||
this.action = action
|
||||
api.put(action + '?force').then(() => {
|
||||
api.put(action + '?force', {}, action).then(() => {
|
||||
// Use 'RESET_CONNECTED' and not 'DISCONNECT' else user will be redirect to login
|
||||
this.$store.dispatch('RESET_CONNECTED')
|
||||
this.inProcess = true
|
||||
|
|
|
@ -74,7 +74,7 @@ export default {
|
|||
return {
|
||||
queries: [
|
||||
['GET', 'migrations?pending'],
|
||||
['PUT', 'update']
|
||||
['PUT', 'update/all', {}, 'update']
|
||||
],
|
||||
// API data
|
||||
migrationsNotDone: undefined,
|
||||
|
@ -95,11 +95,8 @@ export default {
|
|||
const confirmed = await this.$askConfirmation(confirmMsg)
|
||||
if (!confirmed) return
|
||||
|
||||
const uri = type === 'specific_app'
|
||||
? 'upgrade/apps?app=' + id
|
||||
: 'upgrade?' + type
|
||||
|
||||
api.put(uri).then(() => {
|
||||
const uri = id !== null ? `apps/${id}/upgrade` : 'upgrade/' + type
|
||||
api.put(uri, {}, { key: 'upgrade.' + (id ? 'app' : type), app: id }).then(() => {
|
||||
this.$router.push({ name: 'tool-logs' })
|
||||
})
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ export default {
|
|||
|
||||
onSubmit () {
|
||||
const data = formatFormData(this.form, { flatten: true })
|
||||
api.post({ uri: 'users' }, data).then(() => {
|
||||
api.post({ uri: 'users' }, data, { key: 'users.create', name: this.form.username }).then(() => {
|
||||
this.$router.push({ name: 'user-list' })
|
||||
}).catch(err => {
|
||||
if (err.name !== 'APIBadRequestError') throw err
|
||||
|
|
|
@ -296,7 +296,8 @@ export default {
|
|||
|
||||
api.put(
|
||||
{ uri: 'users', param: this.name, storeKey: 'users_details' },
|
||||
data
|
||||
data,
|
||||
{ key: 'users.update', name: this.name }
|
||||
).then(() => {
|
||||
this.$router.push({ name: 'user-info', param: { name: this.name } })
|
||||
}).catch(err => {
|
||||
|
|
|
@ -108,7 +108,8 @@ export default {
|
|||
const data = this.purge ? { purge: '' } : {}
|
||||
api.delete(
|
||||
{ uri: 'users', param: this.name, storeKey: 'users_details' },
|
||||
data
|
||||
data,
|
||||
{ key: 'users.delete', name: this.name }
|
||||
).then(() => {
|
||||
this.$router.push({ name: 'user-list' })
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue