refactor: merge all api calls args in one object

This commit is contained in:
axolotle 2024-08-05 15:36:53 +02:00
parent c34e05d5eb
commit cb344f28fc
31 changed files with 316 additions and 326 deletions

View file

@ -6,39 +6,29 @@
import { useSettings } from '@/composables/useSettings' import { useSettings } from '@/composables/useSettings'
import store from '@/store' import store from '@/store'
import type { Obj } from '@/types/commons' import type { Obj } from '@/types/commons'
import { APIUnauthorizedError, type APIError } from './errors'
import { getError, getResponseData, openWebSocket } from './handlers' import { getError, getResponseData, openWebSocket } from './handlers'
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type StoreUri = { export type HumanKey = {
uri: string
storeKey?: string
param?: string
}
type HumanKey = {
key: string key: string
[propName: string]: any [propName: string]: any
} }
type APIQueryOptions = { export type APIQuery = {
// Display the waiting modal method?: RequestMethod
wait?: boolean uri: string
// Open a websocket connection cachePath?: string
cacheParams?: Obj
data?: Obj
humanKey?: string | HumanKey
showModal?: boolean
websocket?: boolean websocket?: boolean
// If an error occurs, the dismiss button will trigger a go back in history
initial?: boolean initial?: boolean
// Send the data with a body encoded as `"multipart/form-data"` instead of `"x-www-form-urlencoded"`)
asFormData?: boolean asFormData?: boolean
} }
export type APIQuery = [
method: RequestMethod,
uri: string | StoreUri,
data?: Obj | null,
humanKey?: string | HumanKey | null,
options?: APIQueryOptions,
]
export type APIErrorData = { export type APIErrorData = {
error: string error: string
error_key?: string error_key?: string
@ -54,7 +44,7 @@ export type APIRequest = {
uri: string uri: string
humanRouteKey: HumanKey['key'] humanRouteKey: HumanKey['key']
humanRoute: string humanRoute: string
initial: APIQueryOptions['initial'] initial: boolean
status: RequestStatus status: RequestStatus
} }
@ -114,39 +104,42 @@ export default {
/** /**
* Generic method to fetch the api. * Generic method to fetch the api.
* *
* @param method - a method in `'GET' | 'POST' | 'PUT' | 'DELETE'`
* @param uri - URI to fetch * @param uri - URI to fetch
* @param cachePath - Cache path to get or store data
* @param cacheParams - Cache params to get or update data
* @param method - An HTTP method in `'GET' | 'POST' | 'PUT' | 'DELETE'`
* @param data - Data to send as body * @param data - Data to send as body
* @param options - {@link APIQueryOptions} * @param humanKey - Key and eventually some data to build the query's description
* @param showModal - Lock view and display the waiting modal
* @param websocket - Open a websocket connection to receive server messages
* @param initial - If an error occurs, the dismiss button will trigger a go back in history
* @param asFormData - Send the data with a body encoded as `"multipart/form-data"` instead of `"x-www-form-urlencoded"`)
*
* @returns Promise that resolve the api response data * @returns Promise that resolve the api response data
* @throws Throw an `APIError` or subclass depending on server response * @throws Throw an `APIError` or subclass depending on server response
*/ */
async fetch( async fetch<T extends any = Obj | string>({
method: RequestMethod, uri,
uri: string, method = 'GET',
data: Obj | null | undefined = {}, cachePath = undefined,
humanKey: string | HumanKey | null = null, cacheParams = undefined,
{ data = undefined,
wait = true, humanKey = undefined,
websocket = true, showModal = method !== 'GET',
initial = false, websocket = method !== 'GET',
asFormData = true, initial = false,
}: APIQueryOptions = {}, asFormData = true,
): Promise<Obj | string> { }: APIQuery): Promise<T> {
const { locale } = useSettings() const { locale } = useSettings()
// `await` because Vuex actions returns promises by default. // `await` because Vuex actions returns promises by default.
const request: APIRequest | APIRequestAction = await store.dispatch( const request: APIRequest = await store.dispatch('INIT_REQUEST', {
'INIT_REQUEST', method,
{ uri,
method, humanKey,
uri, initial,
humanKey, wait: showModal,
initial, websocket,
wait, })
websocket,
},
)
if (websocket) { if (websocket) {
await openWebSocket(request as APIRequestAction) await openWebSocket(request as APIRequestAction)
@ -173,7 +166,7 @@ export default {
throw getError(request, response, responseData as string | APIErrorData) throw getError(request, response, responseData as string | APIErrorData)
} }
return responseData return responseData as T
}, },
/** /**
@ -182,30 +175,21 @@ export default {
* Calls are synchronous since the API can't handle multiple calls. * Calls are synchronous since the API can't handle multiple calls.
* *
* @param queries - Array of {@link APIQuery} * @param queries - Array of {@link APIQuery}
* @param wait - Show the waiting modal until every queries have been resolved * @param showModal - Show the waiting modal until every queries have been resolved
* @param initial - Inform that thoses queries are required for a view to be displayed * @param initial - Inform that thoses queries are required for a view to be displayed
*
* @returns Promise that resolves an array of server responses * @returns Promise that resolves an array of server responses
* @throws Throw an `APIError` or subclass depending on server response * @throws Throw an `APIError` or subclass depending on server response
*/ */
async fetchAll(queries: APIQuery[], { wait = false, initial = false } = {}) { async fetchAll(
queries: APIQuery[],
{ showModal = false, initial = false } = {},
) {
const results: Array<Obj | string> = [] const results: Array<Obj | string> = []
if (wait) store.commit('SET_WAITING', true) for (const query of queries) {
try { if (showModal) query.showModal = true
for (const [method, uri, data, humanKey, options = {}] of queries) { if (initial) query.initial = true
if (wait) options.wait = false results.push(await this.fetch(query))
if (initial) options.initial = true
results.push(
await this[method.toLowerCase() as 'get' | 'post' | 'put' | 'delete'](
uri,
data,
humanKey,
options,
),
)
}
} finally {
// Stop waiting even if there is an error.
if (wait) store.commit('SET_WAITING', false)
} }
return results return results
@ -214,86 +198,57 @@ export default {
/** /**
* Api get helper function. * Api get helper function.
* *
* @param uri - uri to fetch * @param query - a simple string for uri or complete APIQuery object {@link APIQuery}
* @param data - for convenience in muliple calls, just pass null *
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @returns Promise that resolve the api response data or an error * @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response * @throws Throw an `APIError` or subclass depending on server response
*/ */
get( get<T extends any = Obj | string>(
uri: string | StoreUri, query: string | Omit<APIQuery, 'method' | 'data'>,
data: Obj | null = null, ): Promise<T> {
humanKey: string | HumanKey | null = null, return this.fetch(typeof query === 'string' ? { uri: query } : query)
options: APIQueryOptions = {},
): Promise<Obj | string> {
options = { websocket: false, wait: false, ...options }
if (typeof uri === 'string')
return this.fetch('GET', uri, data, humanKey, options)
return store.dispatch('GET', { ...uri, humanKey, options })
}, },
/** /**
* Api post helper function. * Api post helper function.
* *
* @param uri - uri to fetch * @param query - {@link APIQuery}
* @param data - data to send as body *
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @returns Promise that resolve the api response data or an error * @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response * @throws Throw an `APIError` or subclass depending on server response
*/ */
post( post<T extends any = Obj | string>(
uri: string | StoreUri, query: Omit<APIQuery, 'method'>,
data: Obj | null | undefined = {}, ): Promise<T> {
humanKey: string | HumanKey | null = null, return this.fetch({ ...query, method: 'POST' })
options: APIQueryOptions = {},
): Promise<Obj | string> {
if (typeof uri === 'string')
return this.fetch('POST', uri, data, humanKey, options)
return store.dispatch('POST', { ...uri, data, humanKey, options })
}, },
/** /**
* Api put helper function. * Api put helper function.
* *
* @param uri - uri to fetch * @param query - {@link APIQuery}
* @param data - data to send as body *
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @returns Promise that resolve the api response data or an error * @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response * @throws Throw an `APIError` or subclass depending on server response
*/ */
put( put<T extends any = Obj | string>(
uri: string | StoreUri, query: Omit<APIQuery, 'method'>,
data: Obj | null | undefined = {}, ): Promise<T> {
humanKey: string | HumanKey | null = null, return this.fetch({ ...query, method: 'PUT' })
options: APIQueryOptions = {},
): Promise<Obj | string> {
if (typeof uri === 'string')
return this.fetch('PUT', uri, data, humanKey, options)
return store.dispatch('PUT', { ...uri, data, humanKey, options })
}, },
/** /**
* Api delete helper function. * Api delete helper function.
* *
* @param uri - uri to fetch * @param query - {@link APIQuery}
* @param data - data to send as body *
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @returns Promise that resolve the api response data or an error * @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response * @throws Throw an `APIError` or subclass depending on server response
*/ */
delete( delete<T extends any = Obj | string>(
uri: string | StoreUri, query: Omit<APIQuery, 'method'>,
data: Obj | null | undefined = {}, ): Promise<T> {
humanKey: string | HumanKey | null = null, return this.fetch({ ...query, method: 'DELETE' })
options: APIQueryOptions = {},
): Promise<Obj | string> {
if (typeof uri === 'string')
return this.fetch('DELETE', uri, data, humanKey, options)
return store.dispatch('DELETE', { ...uri, data, humanKey, options })
}, },
/** /**
@ -302,6 +257,7 @@ export default {
* @param attemps - Number of attemps before rejecting * @param attemps - Number of attemps before rejecting
* @param delay - Delay between calls to the API in ms * @param delay - Delay between calls to the API in ms
* @param initialDelay - Delay before calling the API for the first time in ms * @param initialDelay - Delay before calling the API for the first time in ms
*
* @returns Promise that resolve yunohost version infos * @returns Promise that resolve yunohost version infos
* @throws Throw an `APIError` or subclass depending on server response * @throws Throw an `APIError` or subclass depending on server response
*/ */
@ -311,8 +267,8 @@ export default {
store store
.dispatch('GET_YUNOHOST_INFOS') .dispatch('GET_YUNOHOST_INFOS')
.then(resolve) .then(resolve)
.catch((err) => { .catch((err: APIError) => {
if (err.name === 'APIUnauthorizedError') { if (err instanceof APIUnauthorizedError) {
reject(err) reject(err)
} else if (n < 1) { } else if (n < 1) {
reject(err) reject(err)
@ -321,7 +277,6 @@ export default {
} }
}) })
} }
if (initialDelay > 0) setTimeout(() => reconnect(attemps), initialDelay) if (initialDelay > 0) setTimeout(() => reconnect(attemps), initialDelay)
else reconnect(attemps) else reconnect(attemps)
}) })

View file

@ -11,10 +11,10 @@ export function useInitialQueries<
queries: MaybeRefOrGetter<APIQuery[]> | ComputedRef<APIQuery[]>, queries: MaybeRefOrGetter<APIQuery[]> | ComputedRef<APIQuery[]>,
{ {
onQueriesResponse, onQueriesResponse,
wait = false, showModal = false,
}: { }: {
onQueriesResponse?: (...responses: ResponsesType) => Promise<void> | void onQueriesResponse?: (...responses: ResponsesType) => Promise<void> | void
wait?: boolean showModal?: boolean
} = {}, } = {},
) { ) {
const loading = ref(true) const loading = ref(true)
@ -24,7 +24,7 @@ export function useInitialQueries<
function call(triggerLoading = true) { function call(triggerLoading = true) {
if (triggerLoading) loading.value = true if (triggerLoading) loading.value = true
return api return api
.fetchAll(toValue(queries), { wait, initial: true }) .fetchAll(toValue(queries), { showModal, initial: true })
.then(async (responses_) => { .then(async (responses_) => {
responses.value = responses_ as ResponsesType responses.value = responses_ as ResponsesType
if (onQueriesResponse) { if (onQueriesResponse) {

View file

@ -114,8 +114,10 @@ async function performPostInstall(force = false) {
// FIXME does the api will throw an error for bad passwords ? // FIXME does the api will throw an error for bad passwords ?
api api
.post('postinstall' + (force ? '?force_diskspace' : ''), data, { .post({
key: 'postinstall', uri: 'postinstall' + (force ? '?force_diskspace' : ''),
data,
humanKey: { key: 'postinstall' },
}) })
.then(() => { .then(() => {
// Display success message and allow the user to login // Display success message and allow the user to login

View file

@ -33,7 +33,7 @@ const router = useRouter()
const route = useRoute() const route = useRoute()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[['GET', 'apps/catalog?full&with_categories&with_antifeatures']], [{ uri: 'apps/catalog?full&with_categories&with_antifeatures' }],
{ onQueriesResponse }, { onQueriesResponse },
) )

View file

@ -54,9 +54,9 @@ const externalResults = reactive({})
const v$ = useVuelidate(rules, form, { $externalResults: externalResults }) const v$ = useVuelidate(rules, form, { $externalResults: externalResults })
const { loading, refetch } = useInitialQueries( const { loading, refetch } = useInitialQueries(
[ [
['GET', `apps/${props.id}?full`], { uri: `apps/${props.id}?full` },
['GET', { uri: 'users/permissions?full', storeKey: 'permissions' }], { uri: 'users/permissions?full', cachePath: 'permissions' },
['GET', { uri: 'domains' }], { uri: 'domains', cachePath: 'domains' },
], ],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -228,17 +228,17 @@ async function onQueriesResponse(app_: any) {
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => { const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
api api
.put( .put({
action uri: action
? `apps/${props.id}/actions/${action}` ? `apps/${props.id}/actions/${action}`
: `apps/${props.id}/config/${panelId}`, : `apps/${props.id}/config/${panelId}`,
isEmptyValue(data) ? {} : { args: objectToParams(data) }, data: isEmptyValue(data) ? {} : { args: objectToParams(data) },
{ humanKey: {
key: `apps.${action ? 'action' : 'update'}_config`, key: `apps.${action ? 'action' : 'update'}_config`,
id: panelId, id: panelId,
name: props.id, name: props.id,
}, },
) })
.then(() => refetch()) .then(() => refetch())
.catch(onError) .catch(onError)
} }
@ -246,10 +246,14 @@ const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
function changeLabel(permName, data) { function changeLabel(permName, data) {
data.show_tile = data.show_tile ? 'True' : 'False' data.show_tile = data.show_tile ? 'True' : 'False'
api api
.put('users/permissions/' + permName, data, { .put({
key: 'apps.change_label', uri: 'users/permissions/' + permName,
prevName: app.value.label, data,
nextName: data.label, humanKey: {
key: 'apps.change_label',
prevName: app.value.label,
nextName: data.label,
},
}) })
.then(() => refetch(false)) .then(() => refetch(false))
} }
@ -260,11 +264,11 @@ async function changeUrl() {
const { domain, path } = form.url const { domain, path } = form.url
api api
.put( .put({
`apps/${props.id}/changeurl`, uri: `apps/${props.id}/changeurl`,
{ domain, path: '/' + path }, data: { domain, path: '/' + path },
{ key: 'apps.change_url', name: app.value.label }, humanKey: { key: 'apps.change_url', name: app.value.label },
) })
.then(() => refetch(false)) .then(() => refetch(false))
} }
@ -273,34 +277,33 @@ async function setAsDefaultDomain(undo = false) {
if (!confirmed) return if (!confirmed) return
api api
.put( .put({
`apps/${props.id}/default${undo ? '?undo' : ''}`, uri: `apps/${props.id}/default${undo ? '?undo' : ''}`,
{}, humanKey: {
{
key: 'apps.set_default', key: 'apps.set_default',
name: app.value.label, name: app.value.label,
domain: app.value.domain, domain: app.value.domain,
}, },
) })
.then(() => refetch(false)) .then(() => refetch(false))
} }
async function dismissNotification(name: string) { async function dismissNotification(name: string) {
api api
.put( .put({
`apps/${props.id}/dismiss_notification/${name}`, uri: `apps/${props.id}/dismiss_notification/${name}`,
{}, humanKey: { key: 'apps.dismiss_notification', name: app.value.label },
{ key: 'apps.dismiss_notification', name: app.value.label }, })
)
.then(() => refetch(false)) .then(() => refetch(false))
} }
async function uninstall() { async function uninstall() {
const data = purge.value === true ? { purge: 1 } : {} const data = purge.value === true ? { purge: 1 } : {}
api api
.delete('apps/' + props.id, data, { .delete({
key: 'apps.uninstall', uri: 'apps/' + props.id,
name: app.value.label, data,
humanKey: { key: 'apps.uninstall', name: app.value.label },
}) })
.then(() => { .then(() => {
router.push({ name: 'app-list' }) router.push({ name: 'app-list' })

View file

@ -37,8 +37,8 @@ const formData = shallowRef<
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[ [
['GET', 'apps/catalog?full&with_categories&with_antifeatures'], { uri: 'apps/catalog?full&with_categories&with_antifeatures' },
['GET', `apps/manifest?app=${props.id}&with_screenshot`], { uri: `apps/manifest?app=${props.id}&with_screenshot` },
], ],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -186,7 +186,11 @@ async function performInstall(onError: (err: APIError) => void) {
} }
api api
.post('apps', data, { key: 'apps.install', name: app.value.name }) .post({
uri: 'apps',
data,
humanKey: { key: 'apps.install', name: app.value.name },
})
.then(async ({ notifications }) => { .then(async ({ notifications }) => {
const postInstall = formatAppNotifs(notifications) const postInstall = formatAppNotifs(notifications)
if (postInstall) { if (postInstall) {

View file

@ -5,7 +5,7 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
import { useSearch } from '@/composables/useSearch' import { useSearch } from '@/composables/useSearch'
import type { Obj } from '@/types/commons' import type { Obj } from '@/types/commons'
const { loading } = useInitialQueries([['GET', 'apps?full']], { const { loading } = useInitialQueries([{ uri: 'apps?full' }], {
onQueriesResponse, onQueriesResponse,
}) })

View file

@ -13,10 +13,7 @@ const props = defineProps<{
const { t } = useI18n() const { t } = useI18n()
const router = useRouter() const router = useRouter()
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[ [{ uri: 'hooks/backup' }, { uri: 'apps?with_backup' }],
['GET', 'hooks/backup'],
['GET', 'apps?with_backup'],
],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -76,7 +73,7 @@ function createBackup() {
} }
} }
api.post('backups', data, 'backups.create').then(() => { api.post({ uri: 'backups', data, humanKey: 'backups.create' }).then(() => {
router.push({ name: 'backup-list', params: { id: props.id } }) router.push({ name: 'backup-list', params: { id: props.id } })
}) })
} }

View file

@ -22,7 +22,7 @@ const router = useRouter()
const store = useStore() const store = useStore()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[['GET', `backups/${props.name}?with_details`]], [{ uri: `backups/${props.name}?with_details` }],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -96,9 +96,10 @@ async function restoreBackup() {
} }
api api
.put(`backups/${props.name}/restore`, data, { .put({
key: 'backups.restore', uri: `backups/${props.name}/restore`,
name: props.name, data,
humanKey: { key: 'backups.restore', name: props.name },
}) })
.then(() => { .then(() => {
isValid.value = null isValid.value = null
@ -117,11 +118,10 @@ async function deleteBackup() {
if (!confirmed) return if (!confirmed) return
api api
.delete( .delete({
'backups/' + props.name, uri: 'backups/' + props.name,
{}, humanKey: { key: 'backups.delete', name: props.name },
{ key: 'backups.delete', name: props.name }, })
)
.then(() => { .then(() => {
router.push({ name: 'backup-list', params: { id: props.id } }) router.push({ name: 'backup-list', params: { id: props.id } })
}) })

View file

@ -9,7 +9,7 @@ const props = defineProps<{
id: string id: string
}>() }>()
const { loading } = useInitialQueries([['GET', 'backups?with_info']], { const { loading } = useInitialQueries([{ uri: 'backups?with_info' }], {
onQueriesResponse, onQueriesResponse,
}) })
const archives = ref<any[] | null>(null) const archives = ref<any[] | null>(null)

View file

@ -8,10 +8,14 @@ import { DEFAULT_STATUS_ICON } from '@/helpers/yunohostArguments'
const { loading, refetch } = useInitialQueries( const { loading, refetch } = useInitialQueries(
[ [
['PUT', 'diagnosis/run?except_if_never_ran_yet', {}, 'diagnosis.run'], {
['GET', 'diagnosis?full'], method: 'PUT',
uri: 'diagnosis/run?except_if_never_ran_yet',
humanKey: 'diagnosis.run',
},
{ uri: 'diagnosis?full' },
], ],
{ wait: true, onQueriesResponse }, { showModal: true, onQueriesResponse },
) )
const reports = ref() const reports = ref()
@ -62,9 +66,13 @@ function runDiagnosis({ id = null, description } = {}) {
const data = id !== null ? { categories: [id] } : {} const data = id !== null ? { categories: [id] } : {}
api api
.put('diagnosis/run' + param, data, { .put({
key: 'diagnosis.run' + (id !== null ? '_specific' : ''), uri: 'diagnosis/run' + param,
description, data,
humanKey: {
key: 'diagnosis.run' + (id !== null ? '_specific' : ''),
description,
},
}) })
.then(() => refetch(false)) .then(() => refetch(false))
} }
@ -75,11 +83,11 @@ function toggleIgnoreIssue(action, report, item) {
) )
api api
.put( .put({
'diagnosis/' + action, uri: 'diagnosis/' + action,
{ filter: filterArgs }, data: { filter: filterArgs },
`diagnosis.${action}.${item.status.toLowerCase()}`, humanKey: `diagnosis.${action}.${item.status.toLowerCase()}`,
) })
.then(() => { .then(() => {
item.ignored = action === 'ignore' item.ignored = action === 'ignore'
if (item.ignored) { if (item.ignored) {

View file

@ -9,14 +9,20 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
import { DomainForm } from '@/views/_partials' import { DomainForm } from '@/views/_partials'
const router = useRouter() const router = useRouter()
const store = useStore()
const { loading } = useInitialQueries([['GET', { uri: 'domains' }]]) const { loading } = useInitialQueries([
{ uri: 'domains', cachePath: 'domains' },
])
const serverError = ref('') const serverError = ref('')
function onSubmit(data) { function onSubmit(data) {
api api
.post('domains', data, { key: 'domains.add', name: data.domain }) .post({
uri: 'domains',
cachePath: 'domains',
data,
humanKey: { key: 'domains.add', name: data.domain },
})
.then(() => { .then(() => {
store.dispatch('RESET_CACHE_DATA', ['domains']) store.dispatch('RESET_CACHE_DATA', ['domains'])
router.push({ name: 'domain-list' }) router.push({ name: 'domain-list' })

View file

@ -14,7 +14,7 @@ const props = defineProps<{
const { t } = useI18n() const { t } = useI18n()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[['GET', `domains/${props.name}/dns/suggest`]], [{ uri: `domains/${props.name}/dns/suggest` }],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -36,8 +36,9 @@ function getDnsChanges() {
loading.value = true loading.value = true
return api return api
.post(`domains/${props.name}/dns/push?dry_run`, {}, null, { .post({
wait: false, uri: `domains/${props.name}/dns/push?dry_run`,
showModal: false,
websocket: false, websocket: false,
}) })
.then((dnsChanges) => { .then((dnsChanges) => {
@ -110,11 +111,10 @@ async function pushDnsChanges() {
} }
api api
.post( .post({
`domains/${props.name}/dns/push${force.value ? '?force' : ''}`, uri: `domains/${props.name}/dns/push${force.value ? '?force' : ''}`,
{}, humanKey: { key: 'domains.push_dns_changes', name: props.name },
{ key: 'domains.push_dns_changes', name: props.name }, })
)
.then(async (responseData) => { .then(async (responseData) => {
await getDnsChanges() await getDnsChanges()
if (!isEmptyValue(responseData)) { if (!isEmptyValue(responseData)) {

View file

@ -29,9 +29,13 @@ const store = useStore()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries( const { loading, refetch } = useInitialQueries(
[ [
['GET', { uri: 'domains', storeKey: 'domains' }], { uri: 'domains', cachePath: 'domains' },
['GET', { uri: 'domains', storeKey: 'domains_details', param: props.name }], {
['GET', `domains/${props.name}/config?full`], uri: `domains/${props.name}`,
cachePath: 'domains_details',
cacheParams: { domain: props.name },
},
{ uri: `domains/${props.name}/config?full` },
], ],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -92,17 +96,17 @@ function onQueriesResponse(
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => { const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
api api
.put( .put({
action uri: action
? `domain/${props.name}/actions/${action}` ? `domain/${props.name}/actions/${action}`
: `domains/${props.name}/config/${panelId}`, : `domains/${props.name}/config/${panelId}`,
{ args: objectToParams(data) }, data: { args: objectToParams(data) },
{ humanKey: {
key: `domains.${action ? 'action' : 'update'}_config`, key: `domains.${action ? 'action' : 'update'}_config`,
id: panelId, id: panelId,
name: props.name, name: props.name,
}, },
) })
.then(() => refetch()) .then(() => refetch())
.catch(onError) .catch(onError)
} }
@ -114,9 +118,15 @@ async function deleteDomain() {
: {} : {}
api api
.delete({ uri: 'domains', param: props.name }, data, { .delete({
key: 'domains.delete', uri: 'domains',
name: props.name, cachePath: 'domains',
cacheParams: { domain: props.name },
data,
humanKey: {
key: 'domains.delete',
name: props.name,
},
}) })
.then(() => { .then(() => {
router.push({ name: 'domain-list' }) router.push({ name: 'domain-list' })
@ -128,11 +138,12 @@ async function setAsDefaultDomain() {
if (!confirmed) return if (!confirmed) return
api api
.put( .put({
{ uri: `domains/${props.name}/main`, storeKey: 'main_domain' }, uri: `domains/${props.name}/main`,
{}, cachePath: 'main_domain',
{ key: 'domains.set_default', name: props.name }, data: {},
) humanKey: { key: 'domains.set_default', name: props.name },
})
.then(() => { .then(() => {
// FIXME Have to commit by hand here since the response is empty (should return the given name) // FIXME Have to commit by hand here since the response is empty (should return the given name)
store.commit('UPDATE_MAIN_DOMAIN', props.name) store.commit('UPDATE_MAIN_DOMAIN', props.name)

View file

@ -9,7 +9,7 @@ import type { ComputedRef } from 'vue'
const { mainDomain, domainsTree } = useStoreGetters() const { mainDomain, domainsTree } = useStoreGetters()
const { loading } = useInitialQueries([ const { loading } = useInitialQueries([
['GET', { uri: 'domains', storeKey: 'domains' }], { uri: 'domains', cachePath: 'domains' },
]) ])
const [search, filteredTree] = useSearch( const [search, filteredTree] = useSearch(

View file

@ -29,9 +29,11 @@ const { v, onSubmit } = useForm(form, fields)
const onAddGroup = onSubmit((onError) => { const onAddGroup = onSubmit((onError) => {
api api
.post({ uri: 'users/groups', storeKey: 'groups' }, form, { .post({
key: 'groups.create', uri: 'users/groups',
name: form.value.groupname, cachePath: 'groups',
data: form,
humanKey: { key: 'groups.create', name: form.value.groupname },
}) })
.then(() => { .then(() => {
router.push({ name: 'group-list' }) router.push({ name: 'group-list' })

View file

@ -17,15 +17,12 @@ const { t } = useI18n()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[ [
['GET', { uri: 'users' }], { uri: 'users', cachePath: 'users' },
[ {
'GET', uri: 'users/groups?full&include_primary_groups',
{ cachePath: 'groups',
uri: 'users/groups?full&include_primary_groups', },
storeKey: 'groups', { uri: 'users/permissions?full', cachePath: 'permissions' },
},
],
['GET', { uri: 'users/permissions?full', storeKey: 'permissions' }],
], ],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -123,33 +120,25 @@ async function onPermissionChanged({ option, groupName, action, applyMethod }) {
) )
if (!confirmed) return if (!confirmed) return
} }
// FIXME hacky way to update the store
api api
.put( .put({
// FIXME hacky way to update the store uri: `users/permissions/${permId}/${action}/${groupName}`,
{ cachePath: 'permissions',
uri: `users/permissions/${permId}/${action}/${groupName}`, cacheParams: { groupName, action, permId },
storeKey: 'permissions', humanKey: { key: 'permissions.' + action, perm: option, name: groupName },
groupName, })
action,
permId,
},
{},
{ key: 'permissions.' + action, perm: option, name: groupName },
)
.then(() => applyMethod(option)) .then(() => applyMethod(option))
} }
function onUserChanged({ option, groupName, action, applyMethod }) { function onUserChanged({ option, groupName, action, applyMethod }) {
api api
.put( .put({
{ uri: `users/groups/${groupName}/${action}/${option}`,
uri: `users/groups/${groupName}/${action}/${option}`, cachePath: 'groups',
storeKey: 'groups', cacheParams: { groupName },
groupName, humanKey: { key: 'groups.' + action, user: option, name: groupName },
}, })
{},
{ key: 'groups.' + action, user: option, name: groupName },
)
.then(() => applyMethod(option)) .then(() => applyMethod(option))
} }
@ -165,11 +154,12 @@ async function deleteGroup(groupName) {
if (!confirmed) return if (!confirmed) return
api api
.delete( .delete({
{ uri: 'users/groups', param: groupName, storeKey: 'groups' }, uri: `users/groups/${groupName}`,
{}, cachePath: 'groups',
{ key: 'groups.delete', name: groupName }, cacheParams: { groupName },
) humanKey: { key: 'groups.delete', name: groupName },
})
.then(() => { .then(() => {
primaryGroups.value = primaryGroups.value?.filter( primaryGroups.value = primaryGroups.value?.filter(
(group) => group.name !== groupName, (group) => group.name !== groupName,

View file

@ -15,8 +15,8 @@ const { t } = useI18n()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries( const { loading, refetch } = useInitialQueries(
[ [
['GET', 'services/' + props.name], { uri: 'services/' + props.name },
['GET', `services/${props.name}/log?number=50`], { uri: `services/${props.name}/log?number=50` },
], ],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -56,11 +56,10 @@ async function updateService(action) {
if (!confirmed) return if (!confirmed) return
api api
.put( .put({
`services/${props.name}/${action}`, uri: `services/${props.name}/${action}`,
{}, humanKey: { key: 'services.' + action, name: props.name },
{ key: 'services.' + action, name: props.name }, })
)
.then(() => refetch(false)) .then(() => refetch(false))
} }

View file

@ -6,7 +6,7 @@ import { useSearch } from '@/composables/useSearch'
import { distanceToNow } from '@/helpers/filters/date' import { distanceToNow } from '@/helpers/filters/date'
import type { Obj } from '@/types/commons' import type { Obj } from '@/types/commons'
const { loading } = useInitialQueries([['GET', 'services']], { const { loading } = useInitialQueries([{ uri: 'services' }], {
onQueriesResponse, onQueriesResponse,
}) })

View file

@ -13,7 +13,7 @@ import type { FieldProps, FormFieldDict } from '@/types/form'
const { t } = useI18n() const { t } = useI18n()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries([['GET', '/firewall?raw']], { const { loading, refetch } = useInitialQueries([{ uri: '/firewall?raw' }], {
onQueriesResponse, onQueriesResponse,
}) })
@ -141,18 +141,17 @@ async function togglePort({ action, port, protocol, connection }) {
const actionTrad = t({ allow: 'open', disallow: 'close' }[action]) const actionTrad = t({ allow: 'open', disallow: 'close' }[action])
return api return api
.put( .put({
`firewall/${protocol}/${action}/${port}?${connection}_only`, uri: `firewall/${protocol}/${action}/${port}?${connection}_only`,
{}, humanKey: {
{
key: 'firewall.ports', key: 'firewall.ports',
protocol, protocol,
action: actionTrad, action: actionTrad,
port, port,
connection, connection,
}, },
{ wait: false }, showModal: false,
) })
.then(() => confirmed) .then(() => confirmed)
} }
@ -162,11 +161,10 @@ async function toggleUpnp(value) {
if (!confirmed) return if (!confirmed) return
api api
.put( .put({
'firewall/upnp/' + action, uri: 'firewall/upnp/' + action,
{}, humanKey: { key: 'firewall.upnp', action: t(action) },
{ key: 'firewall.upnp', action: t(action) }, })
)
.then(() => { .then(() => {
// FIXME Couldn't test when it works. // FIXME Couldn't test when it works.
refetch(false) refetch(false)

View file

@ -18,7 +18,7 @@ const queries = computed<APIQuery[]>(() => {
with_suboperations: '', with_suboperations: '',
number: numberOfLines.value, number: numberOfLines.value,
}) })
return [['GET', `logs/${props.name}?${queryString}`]] return [{ uri: `logs/${props.name}?${queryString}` }]
}) })
const { loading, refetch } = useInitialQueries(queries, { const { loading, refetch } = useInitialQueries(queries, {
onQueriesResponse, onQueriesResponse,
@ -66,12 +66,11 @@ function onQueriesResponse(log: any) {
function shareLogs() { function shareLogs() {
api api
.get( .get({
`logs/${props.name}/share`, uri: `logs/${props.name}/share`,
null, humanKey: { key: 'share_logs', name: props.name },
{ key: 'share_logs', name: props.name }, websocket: true,
{ websocket: true }, })
)
.then(({ url }) => { .then(({ url }) => {
window.open(url, '_blank') window.open(url, '_blank')
}) })

View file

@ -7,7 +7,7 @@ import { distanceToNow, readableDate } from '@/helpers/filters/date'
import type { Obj } from '@/types/commons' import type { Obj } from '@/types/commons'
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[['GET', `logs?limit=${25}&with_details`]], [{ uri: `logs?limit=${25}&with_details` }],
{ onQueriesResponse }, { onQueriesResponse },
) )

View file

@ -10,10 +10,7 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
const { t } = useI18n() const { t } = useI18n()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries( const { loading, refetch } = useInitialQueries(
[ [{ uri: 'migrations?pending' }, { uri: 'migrations?done' }],
['GET', 'migrations?pending'],
['GET', 'migrations?done'],
],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -46,7 +43,7 @@ function runMigrations() {
// Check that every migration's disclaimer has been checked. // Check that every migration's disclaimer has been checked.
if (Object.values(checked).every((value) => value === true)) { if (Object.values(checked).every((value) => value === true)) {
api api
.put('migrations?accept_disclaimer', {}, 'migrations.run') .put({ uri: 'migrations?accept_disclaimer', humanKey: 'migrations.run' })
.then(() => refetch(false)) .then(() => refetch(false))
} }
} }
@ -55,7 +52,11 @@ async function skipMigration(id) {
const confirmed = await modalConfirm(t('confirm_migrations_skip')) const confirmed = await modalConfirm(t('confirm_migrations_skip'))
if (!confirmed) return if (!confirmed) return
api api
.put('/migrations/' + id, { skip: '', targets: id }, 'migration.skip') .put({
uri: '/migrations/' + id,
data: { skip: '', targets: id },
humanKey: 'migration.skip',
})
.then(() => refetch(false)) .then(() => refetch(false))
} }
</script> </script>

View file

@ -13,7 +13,7 @@ async function triggerAction(action) {
const confirmed = await modalConfirm(t('confirm_reboot_action_' + action)) const confirmed = await modalConfirm(t('confirm_reboot_action_' + action))
if (!confirmed) return if (!confirmed) return
api.put(action + '?force', {}, action).then(() => { api.put({ uri: action + '?force', humanKey: action }).then(() => {
const delay = action === 'reboot' ? 4000 : 10000 const delay = action === 'reboot' ? 4000 : 10000
store.dispatch('TRY_TO_RECONNECT', { store.dispatch('TRY_TO_RECONNECT', {
attemps: Infinity, attemps: Infinity,

View file

@ -13,7 +13,7 @@ import type { CoreConfigPanels } from '@/types/core/options'
const props = defineProps<{ tabId?: string }>() const props = defineProps<{ tabId?: string }>()
const { loading, refetch } = useInitialQueries([['GET', 'settings?full']], { const { loading, refetch } = useInitialQueries([{ uri: 'settings?full' }], {
onQueriesResponse, onQueriesResponse,
}) })
const config = shallowRef<ConfigPanelsProps | undefined>() const config = shallowRef<ConfigPanelsProps | undefined>()
@ -29,11 +29,11 @@ function onQueriesResponse(config_: CoreConfigPanels) {
const onPanelApply: OnPanelApply = ({ panelId, data }, onError) => { const onPanelApply: OnPanelApply = ({ panelId, data }, onError) => {
// FIXME no route for potential action // FIXME no route for potential action
api api
.put( .put({
`settings/${panelId}`, uri: `settings/${panelId}`,
{ args: objectToParams(data) }, data: { args: objectToParams(data) },
{ key: 'settings.update', panel: panelId }, humanKey: { key: 'settings.update', panel: panelId },
) })
.then(() => refetch()) .then(() => refetch())
.catch(onError) .catch(onError)
} }

View file

@ -11,10 +11,10 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
const { t } = useI18n() const { t } = useI18n()
const store = useStore() const store = useStore()
const modalConfirm = useAutoModal() const modalConfirm = useAutoModal()
const { loading } = useInitialQueries([['PUT', 'update/all', {}, 'update']], { const { loading } = useInitialQueries(
wait: true, [{ method: 'PUT', uri: 'update/all', humanKey: 'update' }],
onQueriesResponse, { showModal: true, onQueriesResponse },
}) )
const system = ref() const system = ref()
const apps = ref() const apps = ref()
@ -64,7 +64,10 @@ async function performAppsUpgrade(ids) {
for (const app of apps_) { for (const app of apps_) {
const continue_ = await api const continue_ = await api
.put(`apps/${app.id}/upgrade`, {}, { key: 'upgrade.app', app: app.name }) .put({
uri: `apps/${app.id}/upgrade`,
humanKey: { key: 'upgrade.app', app: app.name },
})
.then((response) => { .then((response) => {
const postMessage = formatAppNotifs(response.notifications.POST_UPGRADE) const postMessage = formatAppNotifs(response.notifications.POST_UPGRADE)
const isLast = app.id === lastAppId const isLast = app.id === lastAppId
@ -100,7 +103,7 @@ async function performSystemUpgrade() {
const confirmed = await modalConfirm(t('confirm_update_system')) const confirmed = await modalConfirm(t('confirm_update_system'))
if (!confirmed) return if (!confirmed) return
api.put('upgrade/system', {}, { key: 'upgrade.system' }).then(() => { api.put({ uri: 'upgrade/system', humanKey: 'upgrade.system' }).then(() => {
if (system.value.some(({ name }) => name.includes('yunohost'))) { if (system.value.some(({ name }) => name.includes('yunohost'))) {
store.dispatch('TRY_TO_RECONNECT', { store.dispatch('TRY_TO_RECONNECT', {
attemps: 1, attemps: 1,

View file

@ -23,8 +23,8 @@ const { t } = useI18n()
const router = useRouter() const router = useRouter()
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[ [
['GET', { uri: 'users' }], { uri: 'users', cachePath: 'users' },
['GET', { uri: 'domains' }], { uri: 'domains', cachePath: 'domains' },
], ],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -117,9 +117,11 @@ function onQueriesResponse() {
const onUserCreate = onSubmit(async (onError) => { const onUserCreate = onSubmit(async (onError) => {
const data = await formatForm(form) const data = await formatForm(form)
api api
.post({ uri: 'users' }, data, { .post({
key: 'users.create', uri: 'users',
name: form.value.username, cachePath: 'users',
data,
humanKey: { key: 'users.create', name: form.value.username },
}) })
.then(() => { .then(() => {
router.push({ name: 'user-list' }) router.push({ name: 'user-list' })

View file

@ -30,8 +30,12 @@ const { t } = useI18n()
const router = useRouter() const router = useRouter()
const { loading } = useInitialQueries( const { loading } = useInitialQueries(
[ [
['GET', { uri: 'users', param: props.name, storeKey: 'users_details' }], {
['GET', { uri: 'domains' }], uri: `users/${props.name}`,
cachePath: 'users_details',
cacheParams: { username: props.name },
},
{ uri: 'domains', cachePath: 'domains' },
], ],
{ onQueriesResponse }, { onQueriesResponse },
) )
@ -207,12 +211,15 @@ const onUserEdit = onSubmit(async (onError, serverErrors) => {
} }
api api
.put({ uri: 'users', param: props.name, storeKey: 'users_details' }, data, { .put({
key: 'users.update', uri: `users/${props.name}`,
name: props.name, cachePath: 'users_details',
cacheParams: { username: props.name },
data,
humanKey: { key: 'users.update', name: props.name },
}) })
.then(() => { .then(() => {
router.push({ name: 'user-info', param: { name: props.name } }) router.push({ name: 'user-info', params: { name: props.name } })
}) })
.catch(onError) .catch(onError)
}) })

View file

@ -70,7 +70,7 @@ const onUserImport = onSubmit(async (onError) => {
if (!requestArgs.update) delete requestArgs.update if (!requestArgs.update) delete requestArgs.update
const data = await formatForm(requestArgs) const data = await formatForm(requestArgs)
api api
.post('users/import', data, null, { asFormData: true }) .post({ uri: 'users/import', data })
.then(() => { .then(() => {
// Reset all cached data related to users. // Reset all cached data related to users.
store.dispatch('RESET_CACHE_DATA', [ store.dispatch('RESET_CACHE_DATA', [

View file

@ -10,7 +10,11 @@ const props = defineProps<{ name: string }>()
const router = useRouter() const router = useRouter()
const { loading } = useInitialQueries([ const { loading } = useInitialQueries([
['GET', { uri: 'users', param: props.name, storeKey: 'users_details' }], {
uri: `users/${props.name}`,
cachePath: 'users_details',
cacheParams: { username: props.name },
},
]) ])
const { user: userGetter } = useStoreGetters() const { user: userGetter } = useStoreGetters()
@ -20,11 +24,13 @@ const user = computed(() => userGetter.value(props.name))
function deleteUser() { function deleteUser() {
const data = purge.value ? { purge: '' } : {} const data = purge.value ? { purge: '' } : {}
api api
.delete( .delete({
{ uri: 'users', param: props.name, storeKey: 'users_details' }, uri: `users/${props.name}`,
cachePath: 'user_details',
cacheParams: { username: props.name },
data, data,
{ key: 'users.delete', name: props.name }, humanKey: { key: 'users.delete', name: props.name },
) })
.then(() => { .then(() => {
router.push({ name: 'user-list' }) router.push({ name: 'user-list' })
}) })

View file

@ -9,13 +9,10 @@ import type { Obj } from '@/types/commons'
const store = useStore() const store = useStore()
const { loading } = useInitialQueries([ const { loading } = useInitialQueries([
[ {
'GET', uri: 'users?fields=username&fields=fullname&fields=mail&fields=mailbox-quota&fields=groups',
{ cachePath: 'users',
uri: 'users?fields=username&fields=fullname&fields=mail&fields=mailbox-quota&fields=groups', },
storeKey: 'users',
},
],
]) ])
const { users } = useStoreGetters() const { users } = useStoreGetters()