2024-12-02 17:20:27 +01:00
|
|
|
<script setup lang="ts">
|
2025-06-26 22:39:02 +02:00
|
|
|
import { toTypedSchema } from "@vee-validate/zod";
|
|
|
|
|
import type { Instance } from "@versia/client/schemas";
|
|
|
|
|
import { Loader } from "lucide-vue-next";
|
|
|
|
|
import { useForm } from "vee-validate";
|
|
|
|
|
import * as z from "zod";
|
2024-12-02 17:20:27 +01:00
|
|
|
import {
|
|
|
|
|
FormControl,
|
|
|
|
|
FormField,
|
|
|
|
|
FormItem,
|
|
|
|
|
FormLabel,
|
|
|
|
|
FormMessage,
|
|
|
|
|
} from "@/components/ui/form";
|
|
|
|
|
import { Button } from "~/components/ui/button";
|
|
|
|
|
import { Input } from "~/components/ui/input";
|
2024-12-07 20:24:09 +01:00
|
|
|
import * as m from "~/paraglide/messages.js";
|
2024-12-02 17:20:27 +01:00
|
|
|
|
|
|
|
|
const { instance } = defineProps<{
|
2025-05-26 11:19:15 +02:00
|
|
|
instance: z.infer<typeof Instance>;
|
2024-12-02 17:20:27 +01:00
|
|
|
}>();
|
|
|
|
|
|
|
|
|
|
const isLoading = ref(false);
|
|
|
|
|
const ssoConfig = computed(() => instance.sso);
|
|
|
|
|
|
|
|
|
|
const formSchema = toTypedSchema(
|
|
|
|
|
z.object({
|
|
|
|
|
identifier: z
|
|
|
|
|
.string()
|
|
|
|
|
.min(3, {
|
2024-12-07 20:24:09 +01:00
|
|
|
message: m.aware_house_dolphin_win(),
|
2024-12-02 17:20:27 +01:00
|
|
|
})
|
|
|
|
|
.or(
|
|
|
|
|
z.string().email({
|
2024-12-07 20:24:09 +01:00
|
|
|
message: m.weary_fresh_dragonfly_bless(),
|
2024-12-02 17:20:27 +01:00
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
password: z.string().min(3, {
|
2024-12-07 20:24:09 +01:00
|
|
|
message: m.aware_house_dolphin_win(),
|
2024-12-02 17:20:27 +01:00
|
|
|
}),
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const form = useForm({
|
|
|
|
|
validationSchema: formSchema,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const redirectUrl = new URL("/api/auth/login", `https://${instance.domain}`);
|
|
|
|
|
|
|
|
|
|
const params = useUrlSearchParams();
|
|
|
|
|
|
|
|
|
|
for (const name of [
|
|
|
|
|
"redirect_uri",
|
|
|
|
|
"response_type",
|
|
|
|
|
"client_id",
|
|
|
|
|
"scope",
|
|
|
|
|
"state",
|
|
|
|
|
]) {
|
|
|
|
|
if (params[name]) {
|
|
|
|
|
redirectUrl.searchParams.set(name, params[name] as string);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const issuerRedirectUrl = (issuerId: string) => {
|
2025-02-09 20:14:27 +01:00
|
|
|
const url = new URL("/oauth/sso", client.value.url);
|
2024-12-02 17:20:27 +01:00
|
|
|
|
|
|
|
|
for (const name of [
|
|
|
|
|
"redirect_uri",
|
|
|
|
|
"response_type",
|
|
|
|
|
"client_id",
|
|
|
|
|
"scope",
|
|
|
|
|
"state",
|
|
|
|
|
]) {
|
|
|
|
|
if (params[name]) {
|
|
|
|
|
url.searchParams.set(name, params[name] as string);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
url.searchParams.set("issuer", issuerId);
|
|
|
|
|
return url.toString();
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div class="grid gap-6">
|
2025-03-28 01:16:24 +01:00
|
|
|
<form
|
|
|
|
|
@submit="form.submitForm"
|
|
|
|
|
method="post"
|
|
|
|
|
:action="redirectUrl.toString()"
|
|
|
|
|
>
|
2024-12-02 17:20:27 +01:00
|
|
|
<div class="grid gap-6">
|
|
|
|
|
<FormField v-slot="{ componentField }" name="identifier">
|
|
|
|
|
<FormItem>
|
|
|
|
|
<FormLabel>
|
2024-12-07 20:24:09 +01:00
|
|
|
{{ m.fluffy_soft_wolf_cook() }}
|
2024-12-02 17:20:27 +01:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormControl>
|
2025-03-28 01:16:24 +01:00
|
|
|
<Input
|
|
|
|
|
placeholder="petergriffin"
|
|
|
|
|
type="text"
|
|
|
|
|
auto-capitalize="none"
|
|
|
|
|
auto-complete="idenfifier"
|
|
|
|
|
auto-correct="off"
|
|
|
|
|
:disabled="isLoading"
|
|
|
|
|
v-bind="componentField"
|
|
|
|
|
/>
|
2024-12-02 17:20:27 +01:00
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
</FormField>
|
|
|
|
|
<FormField v-slot="{ componentField }" name="password">
|
|
|
|
|
<FormItem>
|
|
|
|
|
<FormLabel>
|
2024-12-07 20:24:09 +01:00
|
|
|
{{ m.livid_bright_wallaby_quiz() }}
|
2024-12-02 17:20:27 +01:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormControl>
|
2025-03-28 01:16:24 +01:00
|
|
|
<Input
|
|
|
|
|
placeholder="hunter2"
|
|
|
|
|
type="password"
|
|
|
|
|
auto-capitalize="none"
|
|
|
|
|
auto-complete="password"
|
|
|
|
|
auto-correct="off"
|
|
|
|
|
:disabled="isLoading"
|
|
|
|
|
v-bind="componentField"
|
|
|
|
|
/>
|
2024-12-02 17:20:27 +01:00
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
</FormField>
|
|
|
|
|
<Button :disabled="isLoading" type="submit">
|
2025-02-09 19:39:05 +01:00
|
|
|
<Loader v-if="isLoading" class="mr-2 size-4 animate-spin" />
|
2024-12-07 20:24:09 +01:00
|
|
|
{{ m.fuzzy_sea_moth_absorb() }}
|
2024-12-02 17:20:27 +01:00
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
2025-03-28 01:16:24 +01:00
|
|
|
<div
|
|
|
|
|
v-if="ssoConfig && ssoConfig.providers.length > 0"
|
|
|
|
|
class="relative"
|
|
|
|
|
>
|
2024-12-02 17:20:27 +01:00
|
|
|
<div class="absolute inset-0 flex items-center">
|
|
|
|
|
<span class="w-full border-t" />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="relative flex justify-center text-xs uppercase">
|
|
|
|
|
<span class="bg-background px-2 text-muted-foreground">
|
2024-12-07 20:24:09 +01:00
|
|
|
{{ m.tidy_tidy_cow_cut() }}
|
2024-12-02 17:20:27 +01:00
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-03-28 01:16:24 +01:00
|
|
|
<div
|
|
|
|
|
v-if="ssoConfig && ssoConfig.providers.length > 0"
|
|
|
|
|
class="flex flex-col gap-2"
|
|
|
|
|
>
|
|
|
|
|
<Button
|
|
|
|
|
as="a"
|
|
|
|
|
:href="issuerRedirectUrl(provider.id)"
|
|
|
|
|
variant="outline"
|
|
|
|
|
type="button"
|
|
|
|
|
:disabled="isLoading"
|
|
|
|
|
v-for="provider of ssoConfig.providers"
|
|
|
|
|
>
|
2024-12-02 17:20:27 +01:00
|
|
|
<Loader v-if="isLoading" class="mr-2 animate-spin" />
|
2025-03-28 01:16:24 +01:00
|
|
|
<img
|
|
|
|
|
crossorigin="anonymous"
|
|
|
|
|
:src="provider.icon"
|
|
|
|
|
:alt="`${provider.name}'s logo`"
|
|
|
|
|
class="size-4 mr-2"
|
|
|
|
|
/>
|
2024-12-02 17:20:27 +01:00
|
|
|
{{ provider.name }}
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-02-09 19:39:05 +01:00
|
|
|
</template>
|