From cb344f28fc974946241cbb27de6e91ee3091f62b Mon Sep 17 00:00:00 2001 From: axolotle Date: Mon, 5 Aug 2024 15:36:53 +0200 Subject: [PATCH] refactor: merge all api calls args in one object --- app/src/api/api.ts | 201 +++++++++------------- app/src/composables/useInitialQueries.ts | 6 +- app/src/views/PostInstall.vue | 6 +- app/src/views/app/AppCatalog.vue | 2 +- app/src/views/app/AppInfo.vue | 63 +++---- app/src/views/app/AppInstall.vue | 10 +- app/src/views/app/AppList.vue | 2 +- app/src/views/backup/BackupCreate.vue | 7 +- app/src/views/backup/BackupInfo.vue | 18 +- app/src/views/backup/BackupList.vue | 2 +- app/src/views/diagnosis/DiagnosisView.vue | 30 ++-- app/src/views/domain/DomainAdd.vue | 12 +- app/src/views/domain/DomainDns.vue | 16 +- app/src/views/domain/DomainInfo.vue | 43 +++-- app/src/views/domain/DomainList.vue | 2 +- app/src/views/group/GroupCreate.vue | 8 +- app/src/views/group/GroupList.vue | 60 +++---- app/src/views/service/ServiceInfo.vue | 13 +- app/src/views/service/ServiceList.vue | 2 +- app/src/views/tool/ToolFirewall.vue | 22 ++- app/src/views/tool/ToolLog.vue | 13 +- app/src/views/tool/ToolLogs.vue | 2 +- app/src/views/tool/ToolMigrations.vue | 13 +- app/src/views/tool/ToolPower.vue | 2 +- app/src/views/tool/ToolSettings.vue | 12 +- app/src/views/update/SystemUpdate.vue | 15 +- app/src/views/user/UserCreate.vue | 12 +- app/src/views/user/UserEdit.vue | 19 +- app/src/views/user/UserImport.vue | 2 +- app/src/views/user/UserInfo.vue | 16 +- app/src/views/user/UserList.vue | 11 +- 31 files changed, 316 insertions(+), 326 deletions(-) diff --git a/app/src/api/api.ts b/app/src/api/api.ts index 5fd50f09..ba6806fc 100644 --- a/app/src/api/api.ts +++ b/app/src/api/api.ts @@ -6,39 +6,29 @@ import { useSettings } from '@/composables/useSettings' import store from '@/store' import type { Obj } from '@/types/commons' +import { APIUnauthorizedError, type APIError } from './errors' import { getError, getResponseData, openWebSocket } from './handlers' export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' -type StoreUri = { - uri: string - storeKey?: string - param?: string -} -type HumanKey = { +export type HumanKey = { key: string [propName: string]: any } -type APIQueryOptions = { - // Display the waiting modal - wait?: boolean - // Open a websocket connection +export type APIQuery = { + method?: RequestMethod + uri: string + cachePath?: string + cacheParams?: Obj + data?: Obj + humanKey?: string | HumanKey + showModal?: boolean websocket?: boolean - // If an error occurs, the dismiss button will trigger a go back in history initial?: boolean - // Send the data with a body encoded as `"multipart/form-data"` instead of `"x-www-form-urlencoded"`) asFormData?: boolean } -export type APIQuery = [ - method: RequestMethod, - uri: string | StoreUri, - data?: Obj | null, - humanKey?: string | HumanKey | null, - options?: APIQueryOptions, -] - export type APIErrorData = { error: string error_key?: string @@ -54,7 +44,7 @@ export type APIRequest = { uri: string humanRouteKey: HumanKey['key'] humanRoute: string - initial: APIQueryOptions['initial'] + initial: boolean status: RequestStatus } @@ -114,39 +104,42 @@ export default { /** * Generic method to fetch the api. * - * @param method - a method in `'GET' | 'POST' | 'PUT' | 'DELETE'` * @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 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 * @throws Throw an `APIError` or subclass depending on server response */ - async fetch( - method: RequestMethod, - uri: string, - data: Obj | null | undefined = {}, - humanKey: string | HumanKey | null = null, - { - wait = true, - websocket = true, - initial = false, - asFormData = true, - }: APIQueryOptions = {}, - ): Promise { + async fetch({ + uri, + method = 'GET', + cachePath = undefined, + cacheParams = undefined, + data = undefined, + humanKey = undefined, + showModal = method !== 'GET', + websocket = method !== 'GET', + initial = false, + asFormData = true, + }: APIQuery): Promise { const { locale } = useSettings() // `await` because Vuex actions returns promises by default. - const request: APIRequest | APIRequestAction = await store.dispatch( - 'INIT_REQUEST', - { - method, - uri, - humanKey, - initial, - wait, - websocket, - }, - ) + const request: APIRequest = await store.dispatch('INIT_REQUEST', { + method, + uri, + humanKey, + initial, + wait: showModal, + websocket, + }) if (websocket) { await openWebSocket(request as APIRequestAction) @@ -173,7 +166,7 @@ export default { 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. * * @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 + * * @returns Promise that resolves an array of server responses * @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 = [] - if (wait) store.commit('SET_WAITING', true) - try { - 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() 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) + for (const query of queries) { + if (showModal) query.showModal = true + if (initial) query.initial = true + results.push(await this.fetch(query)) } return results @@ -214,86 +198,57 @@ export default { /** * Api get helper function. * - * @param uri - uri to fetch - * @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} + * @param query - a simple string for uri or complete APIQuery object {@link APIQuery} + * * @returns Promise that resolve the api response data or an error * @throws Throw an `APIError` or subclass depending on server response */ - get( - uri: string | StoreUri, - data: Obj | null = null, - humanKey: string | HumanKey | null = null, - options: APIQueryOptions = {}, - ): Promise { - 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 }) + get( + query: string | Omit, + ): Promise { + return this.fetch(typeof query === 'string' ? { uri: query } : query) }, /** * Api post helper function. * - * @param uri - uri to fetch - * @param data - data to send as body - * @param humanKey - key and eventually some data to build the query's description - * @param options - {@link APIQueryOptions} + * @param query - {@link APIQuery} + * * @returns Promise that resolve the api response data or an error * @throws Throw an `APIError` or subclass depending on server response */ - post( - uri: string | StoreUri, - data: Obj | null | undefined = {}, - humanKey: string | HumanKey | null = null, - options: APIQueryOptions = {}, - ): Promise { - if (typeof uri === 'string') - return this.fetch('POST', uri, data, humanKey, options) - return store.dispatch('POST', { ...uri, data, humanKey, options }) + post( + query: Omit, + ): Promise { + return this.fetch({ ...query, method: 'POST' }) }, /** * Api put helper function. * - * @param uri - uri to fetch - * @param data - data to send as body - * @param humanKey - key and eventually some data to build the query's description - * @param options - {@link APIQueryOptions} + * @param query - {@link APIQuery} + * * @returns Promise that resolve the api response data or an error * @throws Throw an `APIError` or subclass depending on server response */ - put( - uri: string | StoreUri, - data: Obj | null | undefined = {}, - humanKey: string | HumanKey | null = null, - options: APIQueryOptions = {}, - ): Promise { - if (typeof uri === 'string') - return this.fetch('PUT', uri, data, humanKey, options) - return store.dispatch('PUT', { ...uri, data, humanKey, options }) + put( + query: Omit, + ): Promise { + return this.fetch({ ...query, method: 'PUT' }) }, /** * Api delete helper function. * - * @param uri - uri to fetch - * @param data - data to send as body - * @param humanKey - key and eventually some data to build the query's description - * @param options - {@link APIQueryOptions} + * @param query - {@link APIQuery} + * * @returns Promise that resolve the api response data or an error * @throws Throw an `APIError` or subclass depending on server response */ - delete( - uri: string | StoreUri, - data: Obj | null | undefined = {}, - humanKey: string | HumanKey | null = null, - options: APIQueryOptions = {}, - ): Promise { - if (typeof uri === 'string') - return this.fetch('DELETE', uri, data, humanKey, options) - return store.dispatch('DELETE', { ...uri, data, humanKey, options }) + delete( + query: Omit, + ): Promise { + return this.fetch({ ...query, method: 'DELETE' }) }, /** @@ -302,6 +257,7 @@ export default { * @param attemps - Number of attemps before rejecting * @param delay - Delay between calls to the API in ms * @param initialDelay - Delay before calling the API for the first time in ms + * * @returns Promise that resolve yunohost version infos * @throws Throw an `APIError` or subclass depending on server response */ @@ -311,8 +267,8 @@ export default { store .dispatch('GET_YUNOHOST_INFOS') .then(resolve) - .catch((err) => { - if (err.name === 'APIUnauthorizedError') { + .catch((err: APIError) => { + if (err instanceof APIUnauthorizedError) { reject(err) } else if (n < 1) { reject(err) @@ -321,7 +277,6 @@ export default { } }) } - if (initialDelay > 0) setTimeout(() => reconnect(attemps), initialDelay) else reconnect(attemps) }) diff --git a/app/src/composables/useInitialQueries.ts b/app/src/composables/useInitialQueries.ts index b116a1bb..d3c21067 100644 --- a/app/src/composables/useInitialQueries.ts +++ b/app/src/composables/useInitialQueries.ts @@ -11,10 +11,10 @@ export function useInitialQueries< queries: MaybeRefOrGetter | ComputedRef, { onQueriesResponse, - wait = false, + showModal = false, }: { onQueriesResponse?: (...responses: ResponsesType) => Promise | void - wait?: boolean + showModal?: boolean } = {}, ) { const loading = ref(true) @@ -24,7 +24,7 @@ export function useInitialQueries< function call(triggerLoading = true) { if (triggerLoading) loading.value = true return api - .fetchAll(toValue(queries), { wait, initial: true }) + .fetchAll(toValue(queries), { showModal, initial: true }) .then(async (responses_) => { responses.value = responses_ as ResponsesType if (onQueriesResponse) { diff --git a/app/src/views/PostInstall.vue b/app/src/views/PostInstall.vue index 203723af..391ed66c 100644 --- a/app/src/views/PostInstall.vue +++ b/app/src/views/PostInstall.vue @@ -114,8 +114,10 @@ async function performPostInstall(force = false) { // FIXME does the api will throw an error for bad passwords ? api - .post('postinstall' + (force ? '?force_diskspace' : ''), data, { - key: 'postinstall', + .post({ + uri: 'postinstall' + (force ? '?force_diskspace' : ''), + data, + humanKey: { key: 'postinstall' }, }) .then(() => { // Display success message and allow the user to login diff --git a/app/src/views/app/AppCatalog.vue b/app/src/views/app/AppCatalog.vue index 3f709f1c..e8b48fe6 100644 --- a/app/src/views/app/AppCatalog.vue +++ b/app/src/views/app/AppCatalog.vue @@ -33,7 +33,7 @@ const router = useRouter() const route = useRoute() const modalConfirm = useAutoModal() const { loading } = useInitialQueries( - [['GET', 'apps/catalog?full&with_categories&with_antifeatures']], + [{ uri: 'apps/catalog?full&with_categories&with_antifeatures' }], { onQueriesResponse }, ) diff --git a/app/src/views/app/AppInfo.vue b/app/src/views/app/AppInfo.vue index a71ede7b..92e9ba85 100644 --- a/app/src/views/app/AppInfo.vue +++ b/app/src/views/app/AppInfo.vue @@ -54,9 +54,9 @@ const externalResults = reactive({}) const v$ = useVuelidate(rules, form, { $externalResults: externalResults }) const { loading, refetch } = useInitialQueries( [ - ['GET', `apps/${props.id}?full`], - ['GET', { uri: 'users/permissions?full', storeKey: 'permissions' }], - ['GET', { uri: 'domains' }], + { uri: `apps/${props.id}?full` }, + { uri: 'users/permissions?full', cachePath: 'permissions' }, + { uri: 'domains', cachePath: 'domains' }, ], { onQueriesResponse }, ) @@ -228,17 +228,17 @@ async function onQueriesResponse(app_: any) { const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => { api - .put( - action + .put({ + uri: action ? `apps/${props.id}/actions/${action}` : `apps/${props.id}/config/${panelId}`, - isEmptyValue(data) ? {} : { args: objectToParams(data) }, - { + data: isEmptyValue(data) ? {} : { args: objectToParams(data) }, + humanKey: { key: `apps.${action ? 'action' : 'update'}_config`, id: panelId, name: props.id, }, - ) + }) .then(() => refetch()) .catch(onError) } @@ -246,10 +246,14 @@ const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => { function changeLabel(permName, data) { data.show_tile = data.show_tile ? 'True' : 'False' api - .put('users/permissions/' + permName, data, { - key: 'apps.change_label', - prevName: app.value.label, - nextName: data.label, + .put({ + uri: 'users/permissions/' + permName, + data, + humanKey: { + key: 'apps.change_label', + prevName: app.value.label, + nextName: data.label, + }, }) .then(() => refetch(false)) } @@ -260,11 +264,11 @@ async function changeUrl() { const { domain, path } = form.url api - .put( - `apps/${props.id}/changeurl`, - { domain, path: '/' + path }, - { key: 'apps.change_url', name: app.value.label }, - ) + .put({ + uri: `apps/${props.id}/changeurl`, + data: { domain, path: '/' + path }, + humanKey: { key: 'apps.change_url', name: app.value.label }, + }) .then(() => refetch(false)) } @@ -273,34 +277,33 @@ async function setAsDefaultDomain(undo = false) { if (!confirmed) return api - .put( - `apps/${props.id}/default${undo ? '?undo' : ''}`, - {}, - { + .put({ + uri: `apps/${props.id}/default${undo ? '?undo' : ''}`, + humanKey: { key: 'apps.set_default', name: app.value.label, domain: app.value.domain, }, - ) + }) .then(() => refetch(false)) } async function dismissNotification(name: string) { api - .put( - `apps/${props.id}/dismiss_notification/${name}`, - {}, - { key: 'apps.dismiss_notification', name: app.value.label }, - ) + .put({ + uri: `apps/${props.id}/dismiss_notification/${name}`, + humanKey: { key: 'apps.dismiss_notification', name: app.value.label }, + }) .then(() => refetch(false)) } async function uninstall() { const data = purge.value === true ? { purge: 1 } : {} api - .delete('apps/' + props.id, data, { - key: 'apps.uninstall', - name: app.value.label, + .delete({ + uri: 'apps/' + props.id, + data, + humanKey: { key: 'apps.uninstall', name: app.value.label }, }) .then(() => { router.push({ name: 'app-list' }) diff --git a/app/src/views/app/AppInstall.vue b/app/src/views/app/AppInstall.vue index 5824037f..0a6974e5 100644 --- a/app/src/views/app/AppInstall.vue +++ b/app/src/views/app/AppInstall.vue @@ -37,8 +37,8 @@ const formData = shallowRef< const { loading } = useInitialQueries( [ - ['GET', 'apps/catalog?full&with_categories&with_antifeatures'], - ['GET', `apps/manifest?app=${props.id}&with_screenshot`], + { uri: 'apps/catalog?full&with_categories&with_antifeatures' }, + { uri: `apps/manifest?app=${props.id}&with_screenshot` }, ], { onQueriesResponse }, ) @@ -186,7 +186,11 @@ async function performInstall(onError: (err: APIError) => void) { } 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 }) => { const postInstall = formatAppNotifs(notifications) if (postInstall) { diff --git a/app/src/views/app/AppList.vue b/app/src/views/app/AppList.vue index 02987fe8..36792d3f 100644 --- a/app/src/views/app/AppList.vue +++ b/app/src/views/app/AppList.vue @@ -5,7 +5,7 @@ import { useInitialQueries } from '@/composables/useInitialQueries' import { useSearch } from '@/composables/useSearch' import type { Obj } from '@/types/commons' -const { loading } = useInitialQueries([['GET', 'apps?full']], { +const { loading } = useInitialQueries([{ uri: 'apps?full' }], { onQueriesResponse, }) diff --git a/app/src/views/backup/BackupCreate.vue b/app/src/views/backup/BackupCreate.vue index 993601bd..df8e8b76 100644 --- a/app/src/views/backup/BackupCreate.vue +++ b/app/src/views/backup/BackupCreate.vue @@ -13,10 +13,7 @@ const props = defineProps<{ const { t } = useI18n() const router = useRouter() const { loading } = useInitialQueries( - [ - ['GET', 'hooks/backup'], - ['GET', 'apps?with_backup'], - ], + [{ uri: 'hooks/backup' }, { uri: 'apps?with_backup' }], { 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 } }) }) } diff --git a/app/src/views/backup/BackupInfo.vue b/app/src/views/backup/BackupInfo.vue index 209b20fe..ece48c95 100644 --- a/app/src/views/backup/BackupInfo.vue +++ b/app/src/views/backup/BackupInfo.vue @@ -22,7 +22,7 @@ const router = useRouter() const store = useStore() const modalConfirm = useAutoModal() const { loading } = useInitialQueries( - [['GET', `backups/${props.name}?with_details`]], + [{ uri: `backups/${props.name}?with_details` }], { onQueriesResponse }, ) @@ -96,9 +96,10 @@ async function restoreBackup() { } api - .put(`backups/${props.name}/restore`, data, { - key: 'backups.restore', - name: props.name, + .put({ + uri: `backups/${props.name}/restore`, + data, + humanKey: { key: 'backups.restore', name: props.name }, }) .then(() => { isValid.value = null @@ -117,11 +118,10 @@ async function deleteBackup() { if (!confirmed) return api - .delete( - 'backups/' + props.name, - {}, - { key: 'backups.delete', name: props.name }, - ) + .delete({ + uri: 'backups/' + props.name, + humanKey: { key: 'backups.delete', name: props.name }, + }) .then(() => { router.push({ name: 'backup-list', params: { id: props.id } }) }) diff --git a/app/src/views/backup/BackupList.vue b/app/src/views/backup/BackupList.vue index 4242249e..fac7204b 100644 --- a/app/src/views/backup/BackupList.vue +++ b/app/src/views/backup/BackupList.vue @@ -9,7 +9,7 @@ const props = defineProps<{ id: string }>() -const { loading } = useInitialQueries([['GET', 'backups?with_info']], { +const { loading } = useInitialQueries([{ uri: 'backups?with_info' }], { onQueriesResponse, }) const archives = ref(null) diff --git a/app/src/views/diagnosis/DiagnosisView.vue b/app/src/views/diagnosis/DiagnosisView.vue index be834343..8d45541b 100644 --- a/app/src/views/diagnosis/DiagnosisView.vue +++ b/app/src/views/diagnosis/DiagnosisView.vue @@ -8,10 +8,14 @@ import { DEFAULT_STATUS_ICON } from '@/helpers/yunohostArguments' 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() @@ -62,9 +66,13 @@ function runDiagnosis({ id = null, description } = {}) { const data = id !== null ? { categories: [id] } : {} api - .put('diagnosis/run' + param, data, { - key: 'diagnosis.run' + (id !== null ? '_specific' : ''), - description, + .put({ + uri: 'diagnosis/run' + param, + data, + humanKey: { + key: 'diagnosis.run' + (id !== null ? '_specific' : ''), + description, + }, }) .then(() => refetch(false)) } @@ -75,11 +83,11 @@ function toggleIgnoreIssue(action, report, item) { ) api - .put( - 'diagnosis/' + action, - { filter: filterArgs }, - `diagnosis.${action}.${item.status.toLowerCase()}`, - ) + .put({ + uri: 'diagnosis/' + action, + data: { filter: filterArgs }, + humanKey: `diagnosis.${action}.${item.status.toLowerCase()}`, + }) .then(() => { item.ignored = action === 'ignore' if (item.ignored) { diff --git a/app/src/views/domain/DomainAdd.vue b/app/src/views/domain/DomainAdd.vue index 64cdebc7..284c806a 100644 --- a/app/src/views/domain/DomainAdd.vue +++ b/app/src/views/domain/DomainAdd.vue @@ -9,14 +9,20 @@ import { useInitialQueries } from '@/composables/useInitialQueries' import { DomainForm } from '@/views/_partials' const router = useRouter() -const store = useStore() -const { loading } = useInitialQueries([['GET', { uri: 'domains' }]]) +const { loading } = useInitialQueries([ + { uri: 'domains', cachePath: 'domains' }, +]) const serverError = ref('') function onSubmit(data) { api - .post('domains', data, { key: 'domains.add', name: data.domain }) + .post({ + uri: 'domains', + cachePath: 'domains', + data, + humanKey: { key: 'domains.add', name: data.domain }, + }) .then(() => { store.dispatch('RESET_CACHE_DATA', ['domains']) router.push({ name: 'domain-list' }) diff --git a/app/src/views/domain/DomainDns.vue b/app/src/views/domain/DomainDns.vue index 0e81744b..aaac5f30 100644 --- a/app/src/views/domain/DomainDns.vue +++ b/app/src/views/domain/DomainDns.vue @@ -14,7 +14,7 @@ const props = defineProps<{ const { t } = useI18n() const modalConfirm = useAutoModal() const { loading } = useInitialQueries( - [['GET', `domains/${props.name}/dns/suggest`]], + [{ uri: `domains/${props.name}/dns/suggest` }], { onQueriesResponse }, ) @@ -36,8 +36,9 @@ function getDnsChanges() { loading.value = true return api - .post(`domains/${props.name}/dns/push?dry_run`, {}, null, { - wait: false, + .post({ + uri: `domains/${props.name}/dns/push?dry_run`, + showModal: false, websocket: false, }) .then((dnsChanges) => { @@ -110,11 +111,10 @@ async function pushDnsChanges() { } api - .post( - `domains/${props.name}/dns/push${force.value ? '?force' : ''}`, - {}, - { key: 'domains.push_dns_changes', name: props.name }, - ) + .post({ + uri: `domains/${props.name}/dns/push${force.value ? '?force' : ''}`, + humanKey: { key: 'domains.push_dns_changes', name: props.name }, + }) .then(async (responseData) => { await getDnsChanges() if (!isEmptyValue(responseData)) { diff --git a/app/src/views/domain/DomainInfo.vue b/app/src/views/domain/DomainInfo.vue index cee75552..a569678a 100644 --- a/app/src/views/domain/DomainInfo.vue +++ b/app/src/views/domain/DomainInfo.vue @@ -29,9 +29,13 @@ const store = useStore() const modalConfirm = useAutoModal() const { loading, refetch } = useInitialQueries( [ - ['GET', { uri: 'domains', storeKey: 'domains' }], - ['GET', { uri: 'domains', storeKey: 'domains_details', param: props.name }], - ['GET', `domains/${props.name}/config?full`], + { uri: 'domains', cachePath: 'domains' }, + { + uri: `domains/${props.name}`, + cachePath: 'domains_details', + cacheParams: { domain: props.name }, + }, + { uri: `domains/${props.name}/config?full` }, ], { onQueriesResponse }, ) @@ -92,17 +96,17 @@ function onQueriesResponse( const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => { api - .put( - action + .put({ + uri: action ? `domain/${props.name}/actions/${action}` : `domains/${props.name}/config/${panelId}`, - { args: objectToParams(data) }, - { + data: { args: objectToParams(data) }, + humanKey: { key: `domains.${action ? 'action' : 'update'}_config`, id: panelId, name: props.name, }, - ) + }) .then(() => refetch()) .catch(onError) } @@ -114,9 +118,15 @@ async function deleteDomain() { : {} api - .delete({ uri: 'domains', param: props.name }, data, { - key: 'domains.delete', - name: props.name, + .delete({ + uri: 'domains', + cachePath: 'domains', + cacheParams: { domain: props.name }, + data, + humanKey: { + key: 'domains.delete', + name: props.name, + }, }) .then(() => { router.push({ name: 'domain-list' }) @@ -128,11 +138,12 @@ async function setAsDefaultDomain() { if (!confirmed) return api - .put( - { uri: `domains/${props.name}/main`, storeKey: 'main_domain' }, - {}, - { key: 'domains.set_default', name: props.name }, - ) + .put({ + uri: `domains/${props.name}/main`, + cachePath: 'main_domain', + data: {}, + humanKey: { key: 'domains.set_default', name: props.name }, + }) .then(() => { // FIXME Have to commit by hand here since the response is empty (should return the given name) store.commit('UPDATE_MAIN_DOMAIN', props.name) diff --git a/app/src/views/domain/DomainList.vue b/app/src/views/domain/DomainList.vue index 41f793dd..e183dd9a 100644 --- a/app/src/views/domain/DomainList.vue +++ b/app/src/views/domain/DomainList.vue @@ -9,7 +9,7 @@ import type { ComputedRef } from 'vue' const { mainDomain, domainsTree } = useStoreGetters() const { loading } = useInitialQueries([ - ['GET', { uri: 'domains', storeKey: 'domains' }], + { uri: 'domains', cachePath: 'domains' }, ]) const [search, filteredTree] = useSearch( diff --git a/app/src/views/group/GroupCreate.vue b/app/src/views/group/GroupCreate.vue index 8cbf7c4f..86d9b8b2 100644 --- a/app/src/views/group/GroupCreate.vue +++ b/app/src/views/group/GroupCreate.vue @@ -29,9 +29,11 @@ const { v, onSubmit } = useForm(form, fields) const onAddGroup = onSubmit((onError) => { api - .post({ uri: 'users/groups', storeKey: 'groups' }, form, { - key: 'groups.create', - name: form.value.groupname, + .post({ + uri: 'users/groups', + cachePath: 'groups', + data: form, + humanKey: { key: 'groups.create', name: form.value.groupname }, }) .then(() => { router.push({ name: 'group-list' }) diff --git a/app/src/views/group/GroupList.vue b/app/src/views/group/GroupList.vue index 9ed2c7c3..e2668785 100644 --- a/app/src/views/group/GroupList.vue +++ b/app/src/views/group/GroupList.vue @@ -17,15 +17,12 @@ const { t } = useI18n() const modalConfirm = useAutoModal() const { loading } = useInitialQueries( [ - ['GET', { uri: 'users' }], - [ - 'GET', - { - uri: 'users/groups?full&include_primary_groups', - storeKey: 'groups', - }, - ], - ['GET', { uri: 'users/permissions?full', storeKey: 'permissions' }], + { uri: 'users', cachePath: 'users' }, + { + uri: 'users/groups?full&include_primary_groups', + cachePath: 'groups', + }, + { uri: 'users/permissions?full', cachePath: 'permissions' }, ], { onQueriesResponse }, ) @@ -123,33 +120,25 @@ async function onPermissionChanged({ option, groupName, action, applyMethod }) { ) if (!confirmed) return } + // FIXME hacky way to update the store api - .put( - // FIXME hacky way to update the store - { - uri: `users/permissions/${permId}/${action}/${groupName}`, - storeKey: 'permissions', - groupName, - action, - permId, - }, - {}, - { key: 'permissions.' + action, perm: option, name: groupName }, - ) + .put({ + uri: `users/permissions/${permId}/${action}/${groupName}`, + cachePath: 'permissions', + cacheParams: { groupName, action, permId }, + humanKey: { key: 'permissions.' + action, perm: option, name: groupName }, + }) .then(() => applyMethod(option)) } function onUserChanged({ option, groupName, action, applyMethod }) { api - .put( - { - uri: `users/groups/${groupName}/${action}/${option}`, - storeKey: 'groups', - groupName, - }, - {}, - { key: 'groups.' + action, user: option, name: groupName }, - ) + .put({ + uri: `users/groups/${groupName}/${action}/${option}`, + cachePath: 'groups', + cacheParams: { groupName }, + humanKey: { key: 'groups.' + action, user: option, name: groupName }, + }) .then(() => applyMethod(option)) } @@ -165,11 +154,12 @@ async function deleteGroup(groupName) { if (!confirmed) return api - .delete( - { uri: 'users/groups', param: groupName, storeKey: 'groups' }, - {}, - { key: 'groups.delete', name: groupName }, - ) + .delete({ + uri: `users/groups/${groupName}`, + cachePath: 'groups', + cacheParams: { groupName }, + humanKey: { key: 'groups.delete', name: groupName }, + }) .then(() => { primaryGroups.value = primaryGroups.value?.filter( (group) => group.name !== groupName, diff --git a/app/src/views/service/ServiceInfo.vue b/app/src/views/service/ServiceInfo.vue index 317e08e9..a4c1da95 100644 --- a/app/src/views/service/ServiceInfo.vue +++ b/app/src/views/service/ServiceInfo.vue @@ -15,8 +15,8 @@ const { t } = useI18n() const modalConfirm = useAutoModal() const { loading, refetch } = useInitialQueries( [ - ['GET', 'services/' + props.name], - ['GET', `services/${props.name}/log?number=50`], + { uri: 'services/' + props.name }, + { uri: `services/${props.name}/log?number=50` }, ], { onQueriesResponse }, ) @@ -56,11 +56,10 @@ async function updateService(action) { if (!confirmed) return api - .put( - `services/${props.name}/${action}`, - {}, - { key: 'services.' + action, name: props.name }, - ) + .put({ + uri: `services/${props.name}/${action}`, + humanKey: { key: 'services.' + action, name: props.name }, + }) .then(() => refetch(false)) } diff --git a/app/src/views/service/ServiceList.vue b/app/src/views/service/ServiceList.vue index 0d09925f..3f1c2f32 100644 --- a/app/src/views/service/ServiceList.vue +++ b/app/src/views/service/ServiceList.vue @@ -6,7 +6,7 @@ import { useSearch } from '@/composables/useSearch' import { distanceToNow } from '@/helpers/filters/date' import type { Obj } from '@/types/commons' -const { loading } = useInitialQueries([['GET', 'services']], { +const { loading } = useInitialQueries([{ uri: 'services' }], { onQueriesResponse, }) diff --git a/app/src/views/tool/ToolFirewall.vue b/app/src/views/tool/ToolFirewall.vue index cb2e2b34..ddbdf69c 100644 --- a/app/src/views/tool/ToolFirewall.vue +++ b/app/src/views/tool/ToolFirewall.vue @@ -13,7 +13,7 @@ import type { FieldProps, FormFieldDict } from '@/types/form' const { t } = useI18n() const modalConfirm = useAutoModal() -const { loading, refetch } = useInitialQueries([['GET', '/firewall?raw']], { +const { loading, refetch } = useInitialQueries([{ uri: '/firewall?raw' }], { onQueriesResponse, }) @@ -141,18 +141,17 @@ async function togglePort({ action, port, protocol, connection }) { const actionTrad = t({ allow: 'open', disallow: 'close' }[action]) return api - .put( - `firewall/${protocol}/${action}/${port}?${connection}_only`, - {}, - { + .put({ + uri: `firewall/${protocol}/${action}/${port}?${connection}_only`, + humanKey: { key: 'firewall.ports', protocol, action: actionTrad, port, connection, }, - { wait: false }, - ) + showModal: false, + }) .then(() => confirmed) } @@ -162,11 +161,10 @@ async function toggleUpnp(value) { if (!confirmed) return api - .put( - 'firewall/upnp/' + action, - {}, - { key: 'firewall.upnp', action: t(action) }, - ) + .put({ + uri: 'firewall/upnp/' + action, + humanKey: { key: 'firewall.upnp', action: t(action) }, + }) .then(() => { // FIXME Couldn't test when it works. refetch(false) diff --git a/app/src/views/tool/ToolLog.vue b/app/src/views/tool/ToolLog.vue index 0231eea6..c286cb6d 100644 --- a/app/src/views/tool/ToolLog.vue +++ b/app/src/views/tool/ToolLog.vue @@ -18,7 +18,7 @@ const queries = computed(() => { with_suboperations: '', number: numberOfLines.value, }) - return [['GET', `logs/${props.name}?${queryString}`]] + return [{ uri: `logs/${props.name}?${queryString}` }] }) const { loading, refetch } = useInitialQueries(queries, { onQueriesResponse, @@ -66,12 +66,11 @@ function onQueriesResponse(log: any) { function shareLogs() { api - .get( - `logs/${props.name}/share`, - null, - { key: 'share_logs', name: props.name }, - { websocket: true }, - ) + .get({ + uri: `logs/${props.name}/share`, + humanKey: { key: 'share_logs', name: props.name }, + websocket: true, + }) .then(({ url }) => { window.open(url, '_blank') }) diff --git a/app/src/views/tool/ToolLogs.vue b/app/src/views/tool/ToolLogs.vue index 858380ab..6fc34eb3 100644 --- a/app/src/views/tool/ToolLogs.vue +++ b/app/src/views/tool/ToolLogs.vue @@ -7,7 +7,7 @@ import { distanceToNow, readableDate } from '@/helpers/filters/date' import type { Obj } from '@/types/commons' const { loading } = useInitialQueries( - [['GET', `logs?limit=${25}&with_details`]], + [{ uri: `logs?limit=${25}&with_details` }], { onQueriesResponse }, ) diff --git a/app/src/views/tool/ToolMigrations.vue b/app/src/views/tool/ToolMigrations.vue index 3b3dfcb7..6394a0a5 100644 --- a/app/src/views/tool/ToolMigrations.vue +++ b/app/src/views/tool/ToolMigrations.vue @@ -10,10 +10,7 @@ import { useInitialQueries } from '@/composables/useInitialQueries' const { t } = useI18n() const modalConfirm = useAutoModal() const { loading, refetch } = useInitialQueries( - [ - ['GET', 'migrations?pending'], - ['GET', 'migrations?done'], - ], + [{ uri: 'migrations?pending' }, { uri: 'migrations?done' }], { onQueriesResponse }, ) @@ -46,7 +43,7 @@ function runMigrations() { // Check that every migration's disclaimer has been checked. if (Object.values(checked).every((value) => value === true)) { api - .put('migrations?accept_disclaimer', {}, 'migrations.run') + .put({ uri: 'migrations?accept_disclaimer', humanKey: 'migrations.run' }) .then(() => refetch(false)) } } @@ -55,7 +52,11 @@ async function skipMigration(id) { const confirmed = await modalConfirm(t('confirm_migrations_skip')) if (!confirmed) return api - .put('/migrations/' + id, { skip: '', targets: id }, 'migration.skip') + .put({ + uri: '/migrations/' + id, + data: { skip: '', targets: id }, + humanKey: 'migration.skip', + }) .then(() => refetch(false)) } diff --git a/app/src/views/tool/ToolPower.vue b/app/src/views/tool/ToolPower.vue index dc807b81..f89250a0 100644 --- a/app/src/views/tool/ToolPower.vue +++ b/app/src/views/tool/ToolPower.vue @@ -13,7 +13,7 @@ async function triggerAction(action) { const confirmed = await modalConfirm(t('confirm_reboot_action_' + action)) if (!confirmed) return - api.put(action + '?force', {}, action).then(() => { + api.put({ uri: action + '?force', humanKey: action }).then(() => { const delay = action === 'reboot' ? 4000 : 10000 store.dispatch('TRY_TO_RECONNECT', { attemps: Infinity, diff --git a/app/src/views/tool/ToolSettings.vue b/app/src/views/tool/ToolSettings.vue index 4055a352..2514cdd8 100644 --- a/app/src/views/tool/ToolSettings.vue +++ b/app/src/views/tool/ToolSettings.vue @@ -13,7 +13,7 @@ import type { CoreConfigPanels } from '@/types/core/options' const props = defineProps<{ tabId?: string }>() -const { loading, refetch } = useInitialQueries([['GET', 'settings?full']], { +const { loading, refetch } = useInitialQueries([{ uri: 'settings?full' }], { onQueriesResponse, }) const config = shallowRef() @@ -29,11 +29,11 @@ function onQueriesResponse(config_: CoreConfigPanels) { const onPanelApply: OnPanelApply = ({ panelId, data }, onError) => { // FIXME no route for potential action api - .put( - `settings/${panelId}`, - { args: objectToParams(data) }, - { key: 'settings.update', panel: panelId }, - ) + .put({ + uri: `settings/${panelId}`, + data: { args: objectToParams(data) }, + humanKey: { key: 'settings.update', panel: panelId }, + }) .then(() => refetch()) .catch(onError) } diff --git a/app/src/views/update/SystemUpdate.vue b/app/src/views/update/SystemUpdate.vue index bee3501a..3c50d6c1 100644 --- a/app/src/views/update/SystemUpdate.vue +++ b/app/src/views/update/SystemUpdate.vue @@ -11,10 +11,10 @@ import { useInitialQueries } from '@/composables/useInitialQueries' const { t } = useI18n() const store = useStore() const modalConfirm = useAutoModal() -const { loading } = useInitialQueries([['PUT', 'update/all', {}, 'update']], { - wait: true, - onQueriesResponse, -}) +const { loading } = useInitialQueries( + [{ method: 'PUT', uri: 'update/all', humanKey: 'update' }], + { showModal: true, onQueriesResponse }, +) const system = ref() const apps = ref() @@ -64,7 +64,10 @@ async function performAppsUpgrade(ids) { for (const app of apps_) { 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) => { const postMessage = formatAppNotifs(response.notifications.POST_UPGRADE) const isLast = app.id === lastAppId @@ -100,7 +103,7 @@ async function performSystemUpgrade() { const confirmed = await modalConfirm(t('confirm_update_system')) 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'))) { store.dispatch('TRY_TO_RECONNECT', { attemps: 1, diff --git a/app/src/views/user/UserCreate.vue b/app/src/views/user/UserCreate.vue index 3ff1dc68..98eba9ac 100644 --- a/app/src/views/user/UserCreate.vue +++ b/app/src/views/user/UserCreate.vue @@ -23,8 +23,8 @@ const { t } = useI18n() const router = useRouter() const { loading } = useInitialQueries( [ - ['GET', { uri: 'users' }], - ['GET', { uri: 'domains' }], + { uri: 'users', cachePath: 'users' }, + { uri: 'domains', cachePath: 'domains' }, ], { onQueriesResponse }, ) @@ -117,9 +117,11 @@ function onQueriesResponse() { const onUserCreate = onSubmit(async (onError) => { const data = await formatForm(form) api - .post({ uri: 'users' }, data, { - key: 'users.create', - name: form.value.username, + .post({ + uri: 'users', + cachePath: 'users', + data, + humanKey: { key: 'users.create', name: form.value.username }, }) .then(() => { router.push({ name: 'user-list' }) diff --git a/app/src/views/user/UserEdit.vue b/app/src/views/user/UserEdit.vue index 0669a2e3..38156a0f 100644 --- a/app/src/views/user/UserEdit.vue +++ b/app/src/views/user/UserEdit.vue @@ -30,8 +30,12 @@ const { t } = useI18n() const router = useRouter() 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 }, ) @@ -207,12 +211,15 @@ const onUserEdit = onSubmit(async (onError, serverErrors) => { } api - .put({ uri: 'users', param: props.name, storeKey: 'users_details' }, data, { - key: 'users.update', - name: props.name, + .put({ + uri: `users/${props.name}`, + cachePath: 'users_details', + cacheParams: { username: props.name }, + data, + humanKey: { key: 'users.update', name: props.name }, }) .then(() => { - router.push({ name: 'user-info', param: { name: props.name } }) + router.push({ name: 'user-info', params: { name: props.name } }) }) .catch(onError) }) diff --git a/app/src/views/user/UserImport.vue b/app/src/views/user/UserImport.vue index f2b19ffe..391f28c8 100644 --- a/app/src/views/user/UserImport.vue +++ b/app/src/views/user/UserImport.vue @@ -70,7 +70,7 @@ const onUserImport = onSubmit(async (onError) => { if (!requestArgs.update) delete requestArgs.update const data = await formatForm(requestArgs) api - .post('users/import', data, null, { asFormData: true }) + .post({ uri: 'users/import', data }) .then(() => { // Reset all cached data related to users. store.dispatch('RESET_CACHE_DATA', [ diff --git a/app/src/views/user/UserInfo.vue b/app/src/views/user/UserInfo.vue index a50e8c26..fc8a596f 100644 --- a/app/src/views/user/UserInfo.vue +++ b/app/src/views/user/UserInfo.vue @@ -10,7 +10,11 @@ const props = defineProps<{ name: string }>() const router = useRouter() 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() @@ -20,11 +24,13 @@ const user = computed(() => userGetter.value(props.name)) function deleteUser() { const data = purge.value ? { purge: '' } : {} api - .delete( - { uri: 'users', param: props.name, storeKey: 'users_details' }, + .delete({ + uri: `users/${props.name}`, + cachePath: 'user_details', + cacheParams: { username: props.name }, data, - { key: 'users.delete', name: props.name }, - ) + humanKey: { key: 'users.delete', name: props.name }, + }) .then(() => { router.push({ name: 'user-list' }) }) diff --git a/app/src/views/user/UserList.vue b/app/src/views/user/UserList.vue index 122e1e01..c1ee6c49 100644 --- a/app/src/views/user/UserList.vue +++ b/app/src/views/user/UserList.vue @@ -9,13 +9,10 @@ import type { Obj } from '@/types/commons' const store = useStore() const { loading } = useInitialQueries([ - [ - 'GET', - { - uri: 'users?fields=username&fields=fullname&fields=mail&fields=mailbox-quota&fields=groups', - storeKey: 'users', - }, - ], + { + uri: 'users?fields=username&fields=fullname&fields=mail&fields=mailbox-quota&fields=groups', + cachePath: 'users', + }, ]) const { users } = useStoreGetters()