change sizing slightly and add sentry

This commit is contained in:
Zoe
2026-02-12 17:32:51 -06:00
parent d29f95bacf
commit 32a4f7f95d
14 changed files with 519 additions and 156 deletions
+85 -65
View File
@@ -1,7 +1,7 @@
import type { User, Session } from 'better-auth';
import type { Result } from '~~/types/result';
import { Ok, Err } from '~~/types/result';
import type { BetterAuthClientOptions, InferSessionFromClient, InferUserFromClient, User } from 'better-auth/client';
import { createAuthClient } from 'better-auth/vue';
import { Err, Ok, type Result } from '~~/types/result';
import { assert } from '~~/utils/assert';
export enum AuthError {
NotAuthenticated = 'NOT_AUTHENTICATED',
@@ -11,62 +11,86 @@ export enum AuthError {
NetworkError = 'NETWORK_ERROR',
}
export interface AuthState {
user: User | null;
session: Session | null;
isLoading: boolean;
}
type sessionData = {
session: InferSessionFromClient<BetterAuthClientOptions> | null;
user: InferUserFromClient<BetterAuthClientOptions> | null;
};
export const useAuth = () => {
const url = useRequestURL();
const headers = useRequestHeaders();
const client = createAuthClient({
baseURL: url.origin,
fetchOptions: {
headers
const authClient = createAuthClient({});
const session = useState<InferSessionFromClient<BetterAuthClientOptions> | null>('auth:session', () => null);
const user = useState<InferUserFromClient<BetterAuthClientOptions> | null>('auth:user', () => null);
const sessionFetching = import.meta.server ? ref(false) : useState('auth:sessionFetching', () => false);
let sessionPromise: Promise<Result<sessionData, AuthError>> | null = null;
const fetchSession = async (): Promise<Result<sessionData, AuthError>> => {
if (sessionPromise) {
console.log('already fetching session');
return sessionPromise;
}
});
const state = useState<AuthState>('auth:state', () => ({
user: null,
session: null,
isLoading: false,
}));
const isAuthenticated = computed(() => !!state.value.session);
const userId = computed(() => state.value.user?.id ?? null);
const fetchSession = async (): Promise<Result<{ session: Session | null; user: User | null }, AuthError>> => {
state.value.isLoading = true;
let finish: (value: Result<sessionData, AuthError>) => void;
sessionFetching.value = true;
sessionPromise = new Promise(async (resolve, reject) => {
finish = resolve;
});
let data: {
session: InferSessionFromClient<BetterAuthClientOptions>;
user: InferUserFromClient<BetterAuthClientOptions>;
} | null = null;
try {
const { data } = await client.getSession();
if (data) {
state.value.session = data.session;
state.value.user = data.user;
return Ok({ session: data.session, user: data.user });
if (import.meta.server) {
data =
(
await useFetch<{
session: InferSessionFromClient<BetterAuthClientOptions>;
user: InferUserFromClient<BetterAuthClientOptions>;
}>('/api/auth/get-session')
).data.value ?? null;
} else {
data = (await authClient.getSession()).data;
}
state.value.session = null;
state.value.user = null;
return Ok({ session: null, user: null });
} catch (error) {
console.error('Failed to fetch session:', error);
return Err(AuthError.NetworkError);
} finally {
state.value.isLoading = false;
sessionFetching.value = false;
finish!(Err(AuthError.NetworkError));
return sessionPromise;
}
session.value = data?.session || null;
user.value = data?.user || null;
sessionFetching.value = false;
sessionFetching.value = false;
finish!(Ok({ session: data?.session || null, user: data?.user || null }));
return sessionPromise;
};
if (import.meta.client) {
authClient.$store.listen('$sessionSignal', async (signal) => {
if (!signal) return;
await fetchSession();
if (!session.value) return;
const triplit = useTriplitClient();
if ('updateOptions' in triplit) {
triplit.updateOptions({
token: session.value.token,
});
}
});
}
const signIn = async (
email: string,
password: string
): Promise<Result<{ user: User; token: string }, { error: AuthError, data?: any }>> => {
state.value.isLoading = true;
sessionFetching.value = true;
try {
const { data, error } = await client.signIn.email({
const { data, error } = await authClient.signIn.email({
email,
password,
});
@@ -80,7 +104,7 @@ export const useAuth = () => {
return Err({ error: AuthError.SignInFailed });
}
state.value.user = data.user;
user.value = data.user;
const triplit = useTriplitClient();
if ('startSession' in triplit && data.token) {
@@ -94,7 +118,7 @@ export const useAuth = () => {
console.error('Sign in error:', err);
return Err({ error: AuthError.NetworkError });
} finally {
state.value.isLoading = false;
sessionFetching.value = false;
}
};
@@ -103,10 +127,10 @@ export const useAuth = () => {
password: string,
name: string
): Promise<Result<{ user: User; token: string }, { error: AuthError, data?: any }>> => {
state.value.isLoading = true;
sessionFetching.value = true;
try {
const { data, error } = await client.signUp.email({
const { data, error } = await authClient.signUp.email({
email,
password,
name,
@@ -121,12 +145,11 @@ export const useAuth = () => {
return Err({ error: AuthError.SignUpFailed });
}
state.value.user = data.user;
user.value = data.user;
const triplit = useTriplitClient();
if ('startSession' in triplit) {
await triplit.startSession(data.token);
}
assert('startSession' in triplit);
await triplit.startSession(data.token);
clearNuxtData();
@@ -135,28 +158,27 @@ export const useAuth = () => {
console.error('Sign up error:', err);
return Err({ error: AuthError.NetworkError });
} finally {
state.value.isLoading = false;
sessionFetching.value = false;
}
};
const signOut = async (): Promise<Result<void, AuthError>> => {
state.value.isLoading = true;
sessionFetching.value = true;
try {
const { error } = await client.signOut();
const { error } = await authClient.signOut();
if (error) {
console.error('Sign out failed:', error);
return Err(AuthError.SignOutFailed);
}
state.value.user = null;
state.value.session = null;
user.value = null;
session.value = null;
const triplit = useTriplitClient();
if ('disconnect' in triplit) {
triplit.disconnect();
}
assert('disconnect' in triplit);
triplit.disconnect();
clearNuxtData();
@@ -165,20 +187,18 @@ export const useAuth = () => {
console.error('Sign out error:', err);
return Err(AuthError.NetworkError);
} finally {
state.value.isLoading = false;
sessionFetching.value = false;
}
};
return {
client,
user: computed(() => state.value.user),
session: computed(() => state.value.session),
isLoading: computed(() => state.value.isLoading),
isAuthenticated,
userId,
fetchSession,
client: authClient,
session,
user,
loggedIn: computed(() => !!session.value),
signIn,
signUp,
signOut,
fetchSession,
};
};