Avoid optional Fortify route import breakage #79
@@ -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 = ''"
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,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 }"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user