From d4bf6b7e3b3dd3a617e1f7a1c2723a25e5fc3383 Mon Sep 17 00:00:00 2001 From: Bernd Storath Date: Wed, 3 Jun 2026 10:05:56 +0200 Subject: [PATCH] rework useSubmit --- .../components/ClientCard/OneTimeLinkBtn.vue | 9 ++-- src/app/components/ClientCard/Switch.vue | 18 +++---- src/app/components/Clients/CreateDialog.vue | 9 ++-- src/app/components/Ui/UserMenu.vue | 9 ++-- src/app/composables/useSubmit.ts | 47 +++++-------------- src/app/pages/admin/config.vue | 9 ++-- src/app/pages/admin/general.vue | 9 ++-- src/app/pages/admin/hooks.vue | 9 ++-- src/app/pages/admin/interface.vue | 27 ++++++----- src/app/pages/clients/[id].vue | 18 +++---- src/app/pages/login.vue | 9 ++-- src/app/pages/me.vue | 45 ++++++++++-------- src/app/pages/setup/2.vue | 9 ++-- src/app/pages/setup/4.vue | 9 ++-- src/app/pages/setup/migrate.vue | 9 ++-- src/server/api/session.post.ts | 6 +-- 16 files changed, 124 insertions(+), 127 deletions(-) diff --git a/src/app/components/ClientCard/OneTimeLinkBtn.vue b/src/app/components/ClientCard/OneTimeLinkBtn.vue index e3ff7e3f..ed50495e 100644 --- a/src/app/components/ClientCard/OneTimeLinkBtn.vue +++ b/src/app/components/ClientCard/OneTimeLinkBtn.vue @@ -14,10 +14,11 @@ const props = defineProps<{ client: LocalClient }>(); const clientsStore = useClientsStore(); const _showOneTimeLink = useSubmit( - `/api/client/${props.client.id}/generateOneTimeLink`, - { - method: 'post', - }, + (data) => + $fetch(`/api/client/${props.client.id}/generateOneTimeLink`, { + method: 'post', + body: data, + }), { revert: async () => { await clientsStore.refresh(); diff --git a/src/app/components/ClientCard/Switch.vue b/src/app/components/ClientCard/Switch.vue index 60e7b68a..4b505266 100644 --- a/src/app/components/ClientCard/Switch.vue +++ b/src/app/components/ClientCard/Switch.vue @@ -18,10 +18,11 @@ const enabled = ref(props.client.enabled); const clientsStore = useClientsStore(); const _disableClient = useSubmit( - `/api/client/${props.client.id}/disable`, - { - method: 'post', - }, + (data) => + $fetch(`/api/client/${props.client.id}/disable`, { + method: 'post', + body: data, + }), { revert: async () => { await clientsStore.refresh(); @@ -31,10 +32,11 @@ const _disableClient = useSubmit( ); const _enableClient = useSubmit( - `/api/client/${props.client.id}/enable`, - { - method: 'post', - }, + (data) => + $fetch(`/api/client/${props.client.id}/enable`, { + method: 'post', + body: data, + }), { revert: async () => { await clientsStore.refresh(); diff --git a/src/app/components/Clients/CreateDialog.vue b/src/app/components/Clients/CreateDialog.vue index be5aa27f..f097f20b 100644 --- a/src/app/components/Clients/CreateDialog.vue +++ b/src/app/components/Clients/CreateDialog.vue @@ -43,10 +43,11 @@ function createClient() { } const _createClient = useSubmit( - '/api/client', - { - method: 'post', - }, + (data) => + $fetch('/api/client', { + method: 'post', + body: data, + }), { revert: () => clientsStore.refresh(), successMsg: t('client.created'), diff --git a/src/app/components/Ui/UserMenu.vue b/src/app/components/Ui/UserMenu.vue index 6d32a756..65785f06 100644 --- a/src/app/components/Ui/UserMenu.vue +++ b/src/app/components/Ui/UserMenu.vue @@ -70,10 +70,11 @@ const authStore = useAuthStore(); const toggleState = ref(false); const _submit = useSubmit( - '/api/session', - { - method: 'delete', - }, + (data) => + $fetch('/api/session', { + method: 'delete', + body: data, + }), { revert: async () => { await navigateTo('/login'); diff --git a/src/app/composables/useSubmit.ts b/src/app/composables/useSubmit.ts index 805c2b66..7477137f 100644 --- a/src/app/composables/useSubmit.ts +++ b/src/app/composables/useSubmit.ts @@ -1,49 +1,24 @@ -import type { - NitroFetchRequest, - NitroFetchOptions, - TypedInternalResponse, - ExtractedRouteMethod, -} from 'nitropack/types'; import { FetchError } from 'ofetch'; -type RevertFn< - R extends NitroFetchRequest, - T = unknown, - O extends NitroFetchOptions = NitroFetchOptions, -> = ( - success: boolean, - data: - | TypedInternalResponse< - R, - T, - NitroFetchOptions extends O ? 'get' : ExtractedRouteMethod - > - | undefined -) => Promise; +type RevertFn = (success: boolean, data: T | undefined) => Promise; -type SubmitOpts< - R extends NitroFetchRequest, - T = unknown, - O extends NitroFetchOptions = NitroFetchOptions, -> = { - revert: RevertFn; +type SubmitOpts = { + revert: RevertFn; successMsg?: string; noSuccessToast?: boolean; }; -export function useSubmit< - R extends NitroFetchRequest, - O extends NitroFetchOptions & { body?: never }, - T = unknown, ->(url: R, options: O, opts: SubmitOpts) { +type Body = Record | null | undefined; + +export function useSubmit( + fetcher: (data: Body) => Promise, + opts: SubmitOpts +) { const toast = useToast(); - return async (data: unknown) => { + return async (data: Body) => { try { - const res = await $fetch(url, { - ...options, - body: data, - }); + const res = await fetcher(data); if (!opts.noSuccessToast) { toast.showToast({ diff --git a/src/app/pages/admin/config.vue b/src/app/pages/admin/config.vue index 40146ae2..8f6f5767 100644 --- a/src/app/pages/admin/config.vue +++ b/src/app/pages/admin/config.vue @@ -121,10 +121,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/userconfig`, { const data = toRef(_data.value); const _submit = useSubmit( - `/api/admin/userconfig`, - { - method: 'post', - }, + (data) => + $fetch(`/api/admin/userconfig`, { + method: 'post', + body: data, + }), { revert } ); diff --git a/src/app/pages/admin/general.vue b/src/app/pages/admin/general.vue index 9515e456..5950776f 100644 --- a/src/app/pages/admin/general.vue +++ b/src/app/pages/admin/general.vue @@ -46,10 +46,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/general`, { const data = toRef(_data.value); const _submit = useSubmit( - `/api/admin/general`, - { - method: 'post', - }, + (data) => + $fetch(`/api/admin/general`, { + method: 'post', + body: data, + }), { revert } ); diff --git a/src/app/pages/admin/hooks.vue b/src/app/pages/admin/hooks.vue index c992b721..047ac0b0 100644 --- a/src/app/pages/admin/hooks.vue +++ b/src/app/pages/admin/hooks.vue @@ -40,10 +40,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/hooks`, { const data = toRef(_data.value); const _submit = useSubmit( - `/api/admin/hooks`, - { - method: 'post', - }, + (data) => + $fetch(`/api/admin/hooks`, { + method: 'post', + body: data, + }), { revert } ); diff --git a/src/app/pages/admin/interface.vue b/src/app/pages/admin/interface.vue index 0df031df..e75f164a 100644 --- a/src/app/pages/admin/interface.vue +++ b/src/app/pages/admin/interface.vue @@ -176,10 +176,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/interface`, { const data = toRef(_data.value); const _submit = useSubmit( - `/api/admin/interface`, - { - method: 'post', - }, + (data) => + $fetch(`/api/admin/interface`, { + method: 'post', + body: data, + }), { revert: async (success) => { await revert(); @@ -201,10 +202,11 @@ async function revert() { } const _changeCidr = useSubmit( - `/api/admin/interface/cidr`, - { - method: 'post', - }, + (data) => + $fetch(`/api/admin/interface/cidr`, { + method: 'post', + body: data, + }), { revert, successMsg: t('admin.interface.cidrSuccess'), @@ -216,10 +218,11 @@ async function changeCidr(ipv4Cidr: string, ipv6Cidr: string) { } const _restartInterface = useSubmit( - `/api/admin/interface/restart`, - { - method: 'post', - }, + (data) => + $fetch(`/api/admin/interface/restart`, { + method: 'post', + body: data, + }), { revert, successMsg: t('admin.interface.restartSuccess'), diff --git a/src/app/pages/clients/[id].vue b/src/app/pages/clients/[id].vue index f4714ea9..0b875282 100644 --- a/src/app/pages/clients/[id].vue +++ b/src/app/pages/clients/[id].vue @@ -225,10 +225,11 @@ const { data: _data, refresh } = await useFetch(`/api/client/${id}`, { const data = toRef(_data.value); const _submit = useSubmit( - `/api/client/${id}`, - { - method: 'post', - }, + (data) => + $fetch(`/api/client/${id}`, { + method: 'post', + body: data, + }), { revert: async (success) => { if (success) { @@ -250,10 +251,11 @@ async function revert() { } const _deleteClient = useSubmit( - `/api/client/${id}`, - { - method: 'delete', - }, + (data) => + $fetch(`/api/client/${id}`, { + method: 'delete', + body: data, + }), { revert: async () => { await navigateTo('/'); diff --git a/src/app/pages/login.vue b/src/app/pages/login.vue index 1d716fb4..5ec66df3 100644 --- a/src/app/pages/login.vue +++ b/src/app/pages/login.vue @@ -78,10 +78,11 @@ const totpRequired = ref(false); const totp = ref(''); const _submit = useSubmit( - '/api/session', - { - method: 'post', - }, + (data) => + $fetch('/api/session', { + method: 'post', + body: data, + }), { revert: async (success, data) => { if (success) { diff --git a/src/app/pages/me.vue b/src/app/pages/me.vue index 65c68ae9..b0bb54a7 100644 --- a/src/app/pages/me.vue +++ b/src/app/pages/me.vue @@ -127,10 +127,11 @@ const name = ref(authStore.userData?.name); const email = ref(authStore.userData?.email); const _submit = useSubmit( - `/api/me`, - { - method: 'post', - }, + (data) => + $fetch(`/api/me`, { + method: 'post', + body: data, + }), { revert: () => { return authStore.update(); @@ -147,10 +148,11 @@ const newPassword = ref(''); const confirmPassword = ref(''); const _updatePassword = useSubmit( - `/api/me/password`, - { - method: 'post', - }, + (data) => + $fetch(`/api/me/password`, { + method: 'post', + body: data, + }), { revert: async () => { currentPassword.value = ''; @@ -171,10 +173,11 @@ function updatePassword() { const twofa = ref<{ key: string; qrcode: string } | null>(null); const _setup2fa = useSubmit( - `/api/me/totp`, - { - method: 'post', - }, + (data) => + $fetch(`/api/me/totp`, { + method: 'post', + body: data, + }), { revert: async (success, data) => { if (success && data?.type === 'setup') { @@ -199,10 +202,11 @@ async function setup2fa() { const code = ref(''); const _enable2fa = useSubmit( - `/api/me/totp`, - { - method: 'post', - }, + (data) => + $fetch(`/api/me/totp`, { + method: 'post', + body: data, + }), { revert: async (success, data) => { if (success && data?.type === 'created') { @@ -224,10 +228,11 @@ async function enable2fa() { const disable2faPassword = ref(''); const _disable2fa = useSubmit( - `/api/me/totp`, - { - method: 'post', - }, + (data) => + $fetch(`/api/me/totp`, { + method: 'post', + body: data, + }), { revert: async (success, data) => { if (success && data?.type === 'deleted') { diff --git a/src/app/pages/setup/2.vue b/src/app/pages/setup/2.vue index 7c32e89c..bba23947 100644 --- a/src/app/pages/setup/2.vue +++ b/src/app/pages/setup/2.vue @@ -50,10 +50,11 @@ const password = ref(''); const confirmPassword = ref(''); const _submit = useSubmit( - '/api/setup/2', - { - method: 'post', - }, + (data) => + $fetch('/api/setup/2', { + method: 'post', + body: data, + }), { revert: async (success) => { if (success) { diff --git a/src/app/pages/setup/4.vue b/src/app/pages/setup/4.vue index 887fc968..f9210ace 100644 --- a/src/app/pages/setup/4.vue +++ b/src/app/pages/setup/4.vue @@ -43,10 +43,11 @@ const host = ref(null); const port = ref(51820); const _submit = useSubmit( - '/api/setup/4', - { - method: 'post', - }, + (data) => + $fetch('/api/setup/4', { + method: 'post', + body: data, + }), { revert: async (success) => { if (success) { diff --git a/src/app/pages/setup/migrate.vue b/src/app/pages/setup/migrate.vue index f706b84c..a5e5ae25 100644 --- a/src/app/pages/setup/migrate.vue +++ b/src/app/pages/setup/migrate.vue @@ -36,10 +36,11 @@ function onChangeFile(evt: Event) { } const _submit = useSubmit( - '/api/setup/migrate', - { - method: 'post', - }, + (data) => + $fetch('/api/setup/migrate', { + method: 'post', + body: data, + }), { revert: async (success) => { if (success) { diff --git a/src/server/api/session.post.ts b/src/server/api/session.post.ts index a11f14f4..07a46021 100644 --- a/src/server/api/session.post.ts +++ b/src/server/api/session.post.ts @@ -18,9 +18,9 @@ export default defineEventHandler(async (event) => { statusMessage: 'Invalid username or password', }); case 'TOTP_REQUIRED': - return { status: 'TOTP_REQUIRED' }; + return { status: 'TOTP_REQUIRED' as const }; case 'INVALID_TOTP_CODE': - return { status: 'INVALID_TOTP_CODE' }; + return { status: 'INVALID_TOTP_CODE' as const }; case 'USER_DISABLED': throw createError({ statusCode: 401, @@ -47,5 +47,5 @@ export default defineEventHandler(async (event) => { SERVER_DEBUG(`New Session: ${data.id} for ${user.id} (${user.username})`); - return { status: 'success' }; + return { status: 'success' as const }; });