feat: 👽 Support Versia Server 0.9 login system
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 0s
Deploy to GitHub Pages / build (push) Failing after 0s
Deploy to GitHub Pages / deploy (push) Has been skipped
Docker / build (push) Failing after 0s
Mirror to Codeberg / Mirror (push) Failing after 0s

This commit is contained in:
Jesse Wierzbinski 2025-11-21 12:16:00 +01:00
parent cc95f043bd
commit dc32f3b3ea
No known key found for this signature in database
21 changed files with 221 additions and 567 deletions

View file

@ -2,7 +2,6 @@
import { Client } from "@versia/client";
import { AlertCircle, Loader } from "lucide-vue-next";
import { NuxtLink } from "#components";
import UserAuthForm from "~/components/oauth/login.vue";
import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert";
import { Button } from "~/components/ui/button";
import * as m from "~~/paraglide/messages.js";
@ -11,8 +10,8 @@ useHead({
title: m.fuzzy_sea_moth_absorb(),
});
const baseUrl = useRequestURL();
const client = computed(() => new Client(new URL(baseUrl)));
const baseUrl = new URL("https://versia.localhost"); //useRequestURL();
const client = computed(() => new Client(baseUrl));
const instance = useInstanceFromClient(client);
const {
error,
@ -22,8 +21,13 @@ const {
client_id,
scope,
} = useUrlSearchParams();
const hasValidUrlSearchParams =
redirect_uri && response_type && client_id && scope;
const isLoading = ref(false);
const getProviderUrl = (providerId: string) =>
new URL(`/oauth/sso/${providerId}`, baseUrl).toString();
</script>
<template>
@ -45,7 +49,7 @@ const hasValidUrlSearchParams =
}"
>
<div
class="absolute top-0 left-0 w-full h-72 bg-gradient-to-t from-transparent to-black/70"
class="absolute top-0 left-0 w-full h-72 bg-linear-to-t from-transparent to-black/70"
/>
<div class="relative z-20 flex items-center text-lg font-medium">
<img
@ -59,18 +63,6 @@ const hasValidUrlSearchParams =
/>
{{ instance?.title }}
</div>
<!-- <div class="relative z-20 mt-auto">
<blockquote class="space-y-2">
<p class="text-lg">
&ldquo;This library has saved me countless hours of work and
helped me deliver stunning designs to my clients faster than
ever before.&rdquo;
</p>
<footer class="text-sm">
Sofia Davis
</footer>
</blockquote>
</div> -->
</div>
<div class="lg:p-8 w-full max-w-xl">
<div
@ -96,9 +88,45 @@ const hasValidUrlSearchParams =
"
></p>
</div>
<template v-if="instance && hasValidUrlSearchParams">
<UserAuthForm :instance="instance" />
</template>
<div v-if="instance && hasValidUrlSearchParams" class="grid gap-6">
<div
v-if="instance.sso.providers.length > 0"
class="flex flex-col gap-2"
>
<form v-for="provider of instance.sso.providers" :key="provider.id" method="POST" :action="getProviderUrl(provider.id)">
<input type="hidden" name="redirect_uri" :value="redirect_uri" />
<input type="hidden" name="client_id" :value="client_id" />
<input v-for="(scopePart, index) of (scope as string).split(' ')" type="hidden" :name="`scope[${index}]`" :value="scopePart" />
<Button
variant="outline"
type="submit"
:disabled="isLoading"
class="w-full"
>
<Loader v-if="isLoading" class="mr-2 animate-spin" />
<img
crossorigin="anonymous"
:src="provider.icon"
:alt="`${provider.name}'s logo`"
class="size-4 mr-2"
/>
{{ provider.name }}
</Button>
</form>
</div>
<Alert v-else variant="destructive" class="mb-4">
<AlertCircle class="size-4" />
<AlertTitle>
No SSO providers are configured.
</AlertTitle>
<AlertDescription>
<p>
Please ask the administrator of
{{ instance.domain }} to set up SSO providers.
</p>
</AlertDescription>
</Alert>
</div>
<div
v-else-if="hasValidUrlSearchParams"
class="p-4 flex items-center justify-center h-48"

View file

@ -1,187 +0,0 @@
<template>
<Card v-if="params.success" class="w-full max-w-md *:w-full">
<CardHeader>
<CardTitle>{{ m.late_mean_capybara_fade() }}</CardTitle>
<CardDescription>
{{ m.brave_acidic_lobster_fetch() }}
</CardDescription>
</CardHeader>
<CardFooter class="grid">
<Button :as="NuxtLink" href="/" variant="default">
{{ m.every_tangy_koala_persist() }}
</Button>
</CardFooter>
</Card>
<Card v-else class="w-full max-w-md">
<form
method="POST"
action="/api/auth/reset"
@submit="form.submitForm"
>
<CardHeader>
<Alert
v-if="params.login_reset"
variant="default"
class="mb-4"
>
<AlertCircle class="size-4" />
<AlertTitle>{{
m.east_loud_lobster_explore()
}}</AlertTitle>
<AlertDescription>
{{ m.good_plane_gazelle_glow() }}
</AlertDescription>
</Alert>
<Alert
v-if="params.error"
variant="destructive"
class="mb-4"
>
<AlertCircle class="size-4" />
<AlertTitle>{{ params.error }}</AlertTitle>
<AlertDescription>
{{ params.error_description }}
</AlertDescription>
</Alert>
<CardTitle as="h1">{{
m.tired_green_sloth_evoke()
}}</CardTitle>
<CardDescription>
{{ m.solid_slow_platypus_talk() }}
</CardDescription>
</CardHeader>
<CardContent class="grid gap-6">
<input type="hidden" name="token" :value="params.token" />
<FormField v-slot="{ componentField }" name="password">
<FormItem>
<FormLabel>
{{ m.true_male_gadfly_stab() }}
</FormLabel>
<FormControl>
<Input
placeholder="hunter2"
type="password"
auto-capitalize="none"
auto-correct="off"
v-bind="componentField"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ componentField }"
name="password-confirm"
>
<FormItem>
<FormLabel>
{{ m.awful_cozy_jannes_rise() }}
</FormLabel>
<FormControl>
<Input
placeholder="hunter2"
type="password"
auto-capitalize="none"
auto-correct="off"
v-bind="componentField"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</CardContent>
<CardFooter class="grid gap-2 mt-4">
<Button variant="default" type="submit">{{
m.noisy_round_skate_yell()
}}</Button>
</CardFooter>
</form>
</Card>
</template>
<script setup lang="ts">
import { toTypedSchema } from "@vee-validate/zod";
import { AlertCircle } from "lucide-vue-next";
import { useForm } from "vee-validate";
import { z } from "zod";
import { NuxtLink } from "#components";
import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert";
import { Button } from "~/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "~/components/ui/form";
import { Input } from "~/components/ui/input";
import * as m from "~~/paraglide/messages.js";
useHead({
title: m.arable_arable_herring_lead(),
});
definePageMeta({
layout: "auth",
});
const authStore = useAuthStore();
authStore.setActiveIdentity(null);
const formSchema = toTypedSchema(
z
.object({
token: z.string(),
password: z
.string()
.min(3, {
message: m.smart_bold_macaw_aid({
count: 3,
}),
})
.max(100, {
message: m.dry_smug_goldfish_promise({
count: 100,
}),
}),
"password-confirm": z
.string()
.min(3, {
message: m.smart_bold_macaw_aid({
count: 3,
}),
})
.max(100, {
message: m.dry_smug_goldfish_promise({
count: 100,
}),
}),
})
.superRefine((data, ctx) => {
if (data.password !== data["password-confirm"]) {
ctx.addIssue({
path: [...ctx.path, "password-confirm"],
code: "custom",
message: m.candid_fancy_leopard_prosper(),
});
}
return {};
}),
);
const params = useUrlSearchParams();
const form = useForm({
validationSchema: formSchema,
initialValues: {
token: (params.token as string) ?? undefined,
},
});
</script>