Avoid optional Fortify route import breakage #79

Closed
Koda wants to merge 1 commits from koda/fortify-optional-route-import-fix into main
14 changed files with 76 additions and 32 deletions
@@ -12,7 +12,7 @@ import {
CardTitle, CardTitle,
} from '@/components/ui/card'; } from '@/components/ui/card';
import { useTwoFactorAuth } from '@/composables/useTwoFactorAuth'; import { useTwoFactorAuth } from '@/composables/useTwoFactorAuth';
import { regenerateRecoveryCodes } from '@/routes/two-factor'; import { fortifyRoutes } from '@/lib/fortify';
const { recoveryCodesList, fetchRecoveryCodes, errors } = useTwoFactorAuth(); const { recoveryCodesList, fetchRecoveryCodes, errors } = useTwoFactorAuth();
const isRecoveryCodesVisible = ref<boolean>(false); const isRecoveryCodesVisible = ref<boolean>(false);
@@ -64,7 +64,7 @@ onMounted(async () => {
<Form <Form
v-if="isRecoveryCodesVisible && recoveryCodesList.length" v-if="isRecoveryCodesVisible && recoveryCodesList.length"
v-bind="regenerateRecoveryCodes.form()" :action="fortifyRoutes.twoFactor.recoveryCodes"
method="post" method="post"
:options="{ preserveScroll: true }" :options="{ preserveScroll: true }"
@success="fetchRecoveryCodes" @success="fetchRecoveryCodes"
@@ -21,7 +21,7 @@ import {
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { useAppearance } from '@/composables/useAppearance'; import { useAppearance } from '@/composables/useAppearance';
import { useTwoFactorAuth } from '@/composables/useTwoFactorAuth'; import { useTwoFactorAuth } from '@/composables/useTwoFactorAuth';
import { confirm } from '@/routes/two-factor'; import { fortifyRoutes } from '@/lib/fortify';
import type { TwoFactorConfigContent } from '@/types'; import type { TwoFactorConfigContent } from '@/types';
type Props = { type Props = {
@@ -238,7 +238,8 @@ watch(
<template v-else> <template v-else>
<Form <Form
v-bind="confirm.form()" :action="fortifyRoutes.twoFactor.confirm"
method="post"
error-bag="confirmTwoFactorAuthentication" error-bag="confirmTwoFactorAuthentication"
reset-on-error reset-on-error
@finish="code = ''" @finish="code = ''"
+4 -4
View File
@@ -1,6 +1,6 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { qrCode, recoveryCodes, secretKey } from '@/routes/two-factor'; import { fortifyRoutes } from '@/lib/fortify';
export type UseTwoFactorAuthReturn = { export type UseTwoFactorAuthReturn = {
qrCodeSvg: Ref<string | null>; qrCodeSvg: Ref<string | null>;
@@ -42,7 +42,7 @@ export const useTwoFactorAuth = (): UseTwoFactorAuthReturn => {
const fetchQrCode = async (): Promise<void> => { const fetchQrCode = async (): Promise<void> => {
try { try {
const { svg } = await fetchJson<{ svg: string; url: string }>( const { svg } = await fetchJson<{ svg: string; url: string }>(
qrCode.url(), fortifyRoutes.twoFactor.qrCode,
); );
qrCodeSvg.value = svg; qrCodeSvg.value = svg;
@@ -55,7 +55,7 @@ export const useTwoFactorAuth = (): UseTwoFactorAuthReturn => {
const fetchSetupKey = async (): Promise<void> => { const fetchSetupKey = async (): Promise<void> => {
try { try {
const { secretKey: key } = await fetchJson<{ secretKey: string }>( const { secretKey: key } = await fetchJson<{ secretKey: string }>(
secretKey.url(), fortifyRoutes.twoFactor.secretKey,
); );
manualSetupKey.value = key; manualSetupKey.value = key;
@@ -85,7 +85,7 @@ export const useTwoFactorAuth = (): UseTwoFactorAuthReturn => {
try { try {
clearErrors(); clearErrors();
recoveryCodesList.value = await fetchJson<string[]>( recoveryCodesList.value = await fetchJson<string[]>(
recoveryCodes.url(), fortifyRoutes.twoFactor.recoveryCodes,
); );
} catch { } catch {
errors.value.push('Failed to fetch recovery codes'); errors.value.push('Failed to fetch recovery codes');
+25
View File
@@ -0,0 +1,25 @@
export const fortifyRoutes = {
register: {
create: '/register',
store: '/register',
},
password: {
request: '/forgot-password',
email: '/forgot-password',
update: '/reset-password',
confirm: '/user/confirm-password',
reset: (token: string): string => `/reset-password/${encodeURIComponent(token)}`,
},
verification: {
send: '/email/verification-notification',
},
twoFactor: {
challenge: '/two-factor-challenge',
enable: '/user/two-factor-authentication',
disable: '/user/two-factor-authentication',
confirm: '/user/confirmed-two-factor-authentication',
qrCode: '/user/two-factor-qr-code',
recoveryCodes: '/user/two-factor-recovery-codes',
secretKey: '/user/two-factor-secret-key',
},
} as const;
+3 -2
View File
@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { Head, Link } from '@inertiajs/vue3'; import { Head, Link } from '@inertiajs/vue3';
import { dashboard, login, register } from '@/routes/index'; import { fortifyRoutes } from '@/lib/fortify';
import { dashboard, login } from '@/routes/index';
withDefaults( withDefaults(
defineProps<{ defineProps<{
@@ -40,7 +41,7 @@ withDefaults(
</Link> </Link>
<Link <Link
v-if="canRegister" v-if="canRegister"
:href="register()" :href="fortifyRoutes.register.create"
class="inline-block rounded-sm border border-[#19140035] px-5 py-1.5 text-sm leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b]" class="inline-block rounded-sm border border-[#19140035] px-5 py-1.5 text-sm leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b]"
> >
Register Register
+3 -2
View File
@@ -5,8 +5,8 @@ import PasswordInput from '@/components/PasswordInput.vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { fortifyRoutes } from '@/lib/fortify';
import AuthLayout from '@/layouts/AuthLayout.vue'; import AuthLayout from '@/layouts/AuthLayout.vue';
import { store } from '@/routes/password/confirm';
</script> </script>
<template> <template>
@@ -17,7 +17,8 @@ import { store } from '@/routes/password/confirm';
<Head title="Confirm password" /> <Head title="Confirm password" />
<Form <Form
v-bind="store.form()" :action="fortifyRoutes.password.confirm"
method="post"
reset-on-success reset-on-success
v-slot="{ errors, processing }" v-slot="{ errors, processing }"
> >
+6 -2
View File
@@ -6,9 +6,9 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { fortifyRoutes } from '@/lib/fortify';
import AuthLayout from '@/layouts/AuthLayout.vue'; import AuthLayout from '@/layouts/AuthLayout.vue';
import { login } from '@/routes/index'; import { login } from '@/routes/index';
import { email } from '@/routes/password';
defineProps<{ defineProps<{
status?: string; status?: string;
@@ -30,7 +30,11 @@ defineProps<{
</div> </div>
<div class="space-y-6"> <div class="space-y-6">
<Form v-bind="email.form()" v-slot="{ errors, processing }"> <Form
:action="fortifyRoutes.password.email"
method="post"
v-slot="{ errors, processing }"
>
<div class="grid gap-2"> <div class="grid gap-2">
<Label for="email">Email address</Label> <Label for="email">Email address</Label>
<Input <Input
+5 -4
View File
@@ -8,10 +8,9 @@ import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { fortifyRoutes } from '@/lib/fortify';
import AuthBase from '@/layouts/AuthLayout.vue'; import AuthBase from '@/layouts/AuthLayout.vue';
import { register } from '@/routes/index';
import { store } from '@/routes/login'; import { store } from '@/routes/login';
import { request } from '@/routes/password';
defineProps<{ defineProps<{
status?: string; status?: string;
@@ -61,7 +60,7 @@ defineProps<{
<Label for="password">Password</Label> <Label for="password">Password</Label>
<TextLink <TextLink
v-if="canResetPassword" v-if="canResetPassword"
:href="request()" :href="fortifyRoutes.password.request"
class="text-sm" class="text-sm"
:tabindex="5" :tabindex="5"
> >
@@ -103,7 +102,9 @@ defineProps<{
v-if="canRegister" v-if="canRegister"
> >
Don't have an account? Don't have an account?
<TextLink :href="register()" :tabindex="5">Sign up</TextLink> <TextLink :href="fortifyRoutes.register.create" :tabindex="5">
Sign up
</TextLink>
</div> </div>
</Form> </Form>
</AuthBase> </AuthBase>
+3 -2
View File
@@ -7,9 +7,9 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { fortifyRoutes } from '@/lib/fortify';
import AuthBase from '@/layouts/AuthLayout.vue'; import AuthBase from '@/layouts/AuthLayout.vue';
import { login } from '@/routes/index'; import { login } from '@/routes/index';
import { store } from '@/routes/register';
</script> </script>
<template> <template>
@@ -20,7 +20,8 @@ import { store } from '@/routes/register';
<Head title="Register" /> <Head title="Register" />
<Form <Form
v-bind="store.form()" :action="fortifyRoutes.register.store"
method="post"
:reset-on-success="['password', 'password_confirmation']" :reset-on-success="['password', 'password_confirmation']"
v-slot="{ errors, processing }" v-slot="{ errors, processing }"
class="flex flex-col gap-6" class="flex flex-col gap-6"
+3 -2
View File
@@ -7,8 +7,8 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { fortifyRoutes } from '@/lib/fortify';
import AuthLayout from '@/layouts/AuthLayout.vue'; import AuthLayout from '@/layouts/AuthLayout.vue';
import { update } from '@/routes/password';
const props = defineProps<{ const props = defineProps<{
token: string; token: string;
@@ -26,7 +26,8 @@ const inputEmail = ref(props.email);
<Head title="Reset password" /> <Head title="Reset password" />
<Form <Form
v-bind="update.form()" :action="fortifyRoutes.password.update"
method="post"
:transform="(data) => ({ ...data, token, email })" :transform="(data) => ({ ...data, token, email })"
:reset-on-success="['password', 'password_confirmation']" :reset-on-success="['password', 'password_confirmation']"
v-slot="{ errors, processing }" v-slot="{ errors, processing }"
@@ -9,8 +9,8 @@ import {
InputOTPGroup, InputOTPGroup,
InputOTPSlot, InputOTPSlot,
} from '@/components/ui/input-otp'; } from '@/components/ui/input-otp';
import { fortifyRoutes } from '@/lib/fortify';
import AuthLayout from '@/layouts/AuthLayout.vue'; import AuthLayout from '@/layouts/AuthLayout.vue';
import { store } from '@/routes/two-factor/login';
import type { TwoFactorConfigContent } from '@/types'; import type { TwoFactorConfigContent } from '@/types';
const authConfigContent = computed<TwoFactorConfigContent>(() => { const authConfigContent = computed<TwoFactorConfigContent>(() => {
@@ -52,7 +52,8 @@ const code = ref<string>('');
<div class="space-y-6"> <div class="space-y-6">
<template v-if="!showRecoveryInput"> <template v-if="!showRecoveryInput">
<Form <Form
v-bind="store.form()" :action="fortifyRoutes.twoFactor.challenge"
method="post"
class="space-y-4" class="space-y-4"
reset-on-error reset-on-error
@error="code = ''" @error="code = ''"
@@ -99,7 +100,8 @@ const code = ref<string>('');
<template v-else> <template v-else>
<Form <Form
v-bind="store.form()" :action="fortifyRoutes.twoFactor.challenge"
method="post"
class="space-y-4" class="space-y-4"
reset-on-error reset-on-error
#default="{ errors, processing, clearErrors }" #default="{ errors, processing, clearErrors }"
+3 -2
View File
@@ -3,9 +3,9 @@ import { Form, Head } from '@inertiajs/vue3';
import TextLink from '@/components/TextLink.vue'; import TextLink from '@/components/TextLink.vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { fortifyRoutes } from '@/lib/fortify';
import AuthLayout from '@/layouts/AuthLayout.vue'; import AuthLayout from '@/layouts/AuthLayout.vue';
import { logout } from '@/routes/index'; import { logout } from '@/routes/index';
import { send } from '@/routes/verification';
defineProps<{ defineProps<{
status?: string; status?: string;
@@ -28,7 +28,8 @@ defineProps<{
</div> </div>
<Form <Form
v-bind="send.form()" :action="fortifyRoutes.verification.send"
method="post"
class="space-y-6 text-center" class="space-y-6 text-center"
v-slot="{ processing }" v-slot="{ processing }"
> >
+3 -2
View File
@@ -8,10 +8,10 @@ import InputError from '@/components/InputError.vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { fortifyRoutes } from '@/lib/fortify';
import AppLayout from '@/layouts/AppLayout.vue'; import AppLayout from '@/layouts/AppLayout.vue';
import SettingsLayout from '@/layouts/settings/Layout.vue'; import SettingsLayout from '@/layouts/settings/Layout.vue';
import { edit } from '@/routes/profile/index'; import { edit } from '@/routes/profile/index';
import { send } from '@/routes/verification';
import type { BreadcrumbItem } from '@/types'; import type { BreadcrumbItem } from '@/types';
type Props = { type Props = {
@@ -84,7 +84,8 @@ const user = computed(() => page.props.auth.user);
<p class="text-muted-foreground -mt-4 text-sm"> <p class="text-muted-foreground -mt-4 text-sm">
Your email address is unverified. Your email address is unverified.
<Link <Link
:href="send()" :href="fortifyRoutes.verification.send"
method="post"
as="button" as="button"
class="text-foreground hover:decoration-current! underline decoration-neutral-300 underline-offset-4 transition-colors duration-300 ease-out dark:decoration-neutral-500" class="text-foreground hover:decoration-current! underline decoration-neutral-300 underline-offset-4 transition-colors duration-300 ease-out dark:decoration-neutral-500"
> >
+8 -3
View File
@@ -11,10 +11,10 @@ import TwoFactorSetupModal from '@/components/TwoFactorSetupModal.vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { useTwoFactorAuth } from '@/composables/useTwoFactorAuth'; import { useTwoFactorAuth } from '@/composables/useTwoFactorAuth';
import { fortifyRoutes } from '@/lib/fortify';
import AppLayout from '@/layouts/AppLayout.vue'; import AppLayout from '@/layouts/AppLayout.vue';
import SettingsLayout from '@/layouts/settings/Layout.vue'; import SettingsLayout from '@/layouts/settings/Layout.vue';
import { edit } from '@/routes/security/index'; import { edit } from '@/routes/security/index';
import { disable, enable } from '@/routes/two-factor';
import type { BreadcrumbItem } from '@/types'; import type { BreadcrumbItem } from '@/types';
type Props = { type Props = {
@@ -175,7 +175,8 @@ onUnmounted(() => clearTwoFactorAuthData());
</Button> </Button>
<Form <Form
v-else v-else
v-bind="enable.form()" :action="fortifyRoutes.twoFactor.enable"
method="post"
@success="showSetupModal = true" @success="showSetupModal = true"
#default="{ processing }" #default="{ processing }"
> >
@@ -197,7 +198,11 @@ onUnmounted(() => clearTwoFactorAuthData());
</p> </p>
<div class="relative inline"> <div class="relative inline">
<Form v-bind="disable.form()" #default="{ processing }"> <Form
:action="fortifyRoutes.twoFactor.disable"
method="delete"
#default="{ processing }"
>
<Button <Button
variant="destructive" variant="destructive"
type="submit" type="submit"