feat: refactor user authentication and place management functions

This commit is contained in:
Jrodenas 2025-11-20 18:11:46 +01:00
parent b754ee35cc
commit 66c60829ab
7 changed files with 120 additions and 63 deletions

View File

@ -2,7 +2,7 @@ import { useMutation } from "@tanstack/react-query"
import { useNavigate } from "@tanstack/react-router" import { useNavigate } from "@tanstack/react-router"
import { toast } from "sonner" import { toast } from "sonner"
import type z from "zod" import type z from "zod"
import { loginUser } from "@/lib/server/user" import { user } from "@/lib/server/user"
import { loginFormSchema } from "@/lib/validation/user" import { loginFormSchema } from "@/lib/validation/user"
import { useValidation } from "../useValidation" import { useValidation } from "../useValidation"
@ -16,9 +16,7 @@ export const useLogin = () => {
const loginMutation = useMutation({ const loginMutation = useMutation({
mutationKey: ["login"], mutationKey: ["login"],
mutationFn: async (data: TLoginForm) => { mutationFn: async (data: TLoginForm) => {
const response = await loginUser({ const response = await user.login({ data })
data
})
if (response.error) { if (response.error) {
throw new Error(response.message) throw new Error(response.message)

View File

@ -2,7 +2,7 @@ import { useMutation } from "@tanstack/react-query"
import { useNavigate } from "@tanstack/react-router" import { useNavigate } from "@tanstack/react-router"
import { toast } from "sonner" import { toast } from "sonner"
import type z from "zod" import type z from "zod"
import { signupUser } from "@/lib/server/user" import { user } from "@/lib/server/user"
import { signupFormSchema } from "@/lib/validation/user" import { signupFormSchema } from "@/lib/validation/user"
import { useValidation } from "../useValidation" import { useValidation } from "../useValidation"
@ -15,7 +15,7 @@ export const useSignup = () => {
}) })
const signup = useMutation({ const signup = useMutation({
mutationKey: ["signup"], mutationKey: ["signup"],
mutationFn: async (data: TSignupForm) => signupUser({ data }), mutationFn: async (data: TSignupForm) => user.signup({ data }),
onSuccess: () => { onSuccess: () => {
toast.success("Signup successful! Redirecting to login...", { toast.success("Signup successful! Redirecting to login...", {
id: "signup" id: "signup"

View File

@ -10,5 +10,8 @@ export const insertUserPlace = createServerFn({
}) })
export const getUserPlaces = createServerFn().handler(async () => { export const getUserPlaces = createServerFn().handler(async () => {
return await db.select().from(places).where(eq(places.id_user, "e6472b9d-01a9-4e2e-8bdc-0ddaa9baf5d8")) // No haría falta el where puedo filtrar mediante RSL que solo pueda ver sus places solo los datos que el ha insertado return await db
}) .select()
.from(places)
.where(eq(places.id_user, "e6472b9d-01a9-4e2e-8bdc-0ddaa9baf5d8")) // No haría falta el where puedo filtrar mediante RSL que solo pueda ver sus places solo los datos que el ha insertado
})

View File

@ -1,9 +1,79 @@
import { redirect } from "@tanstack/react-router" import { redirect } from "@tanstack/react-router"
import { createServerFn } from "@tanstack/react-start" import { createServerFn } from "@tanstack/react-start"
import { getSupabaseServerClient } from "@/integrations/supabase/supabase" import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
import { loginFormSchema, signupFormSchema } from "../validation/user" import {
loginFormSchema,
signupFormSchema,
userListParamsSchema
} from "../validation/user"
export const getUser = createServerFn().handler(async () => { const login = createServerFn({ method: "POST" })
.inputValidator(loginFormSchema)
.handler(async ({ data }) => {
const supabase = getSupabaseServerClient()
const login = await supabase.auth.signInWithPassword({
email: data.email,
password: data.password
})
if (login.error) {
return {
error: true,
message: login.error.message
}
}
return {
error: false
}
})
const logout = createServerFn().handler(async () => {
const supabase = getSupabaseServerClient()
const { error } = await supabase.auth.signOut()
if (error) {
return {
error: true,
message: error.message
}
}
throw redirect({
to: "/",
viewTransition: true,
replace: true
})
})
const signup = createServerFn({ method: "POST" })
.inputValidator(signupFormSchema)
.handler(async ({ data }) => {
const supabase = getSupabaseServerClient()
const { error } = await supabase.auth.signUp({
email: data.email,
password: data.password,
options: {
data: {
name: data.name,
location: data.location
}
}
})
if (error) {
return {
error: true,
message: error.message
}
}
throw redirect({
href: data.redirectUrl || "/"
})
})
const userData = createServerFn().handler(async () => {
const supabase = getSupabaseServerClient() const supabase = getSupabaseServerClient()
const { data, error } = await supabase.auth.getUser() const { data, error } = await supabase.auth.getUser()
if (error || !data.user) { if (error || !data.user) {
@ -24,20 +94,16 @@ export const getUser = createServerFn().handler(async () => {
} }
}) })
export const loginUser = createServerFn({ const resendConfirmationEmail = createServerFn({ method: "POST" })
method: "POST" .inputValidator(signupFormSchema.pick({ email: true }))
})
.inputValidator(loginFormSchema)
.handler(async ({ data }) => { .handler(async ({ data }) => {
const supabase = getSupabaseServerClient() const supabase = getSupabaseServerClient()
const login = await supabase.auth.signInWithPassword({ const { error } = await supabase.auth.resetPasswordForEmail(data.email)
email: data.email,
password: data.password if (error) {
})
if (login.error) {
return { return {
error: true, error: true,
message: login.error.message message: error.message
} }
} }
@ -46,49 +112,32 @@ export const loginUser = createServerFn({
} }
}) })
export const logoutUser = createServerFn().handler(async () => { const userList = createServerFn()
const supabase = getSupabaseServerClient() .inputValidator(userListParamsSchema)
const { error } = await supabase.auth.signOut()
if (error) {
return {
error: true,
message: error.message
}
}
throw redirect({
to: "/",
viewTransition: true,
replace: true
})
})
export const signupUser = createServerFn({ method: "POST" })
.inputValidator(signupFormSchema)
.handler(async ({ data }) => { .handler(async ({ data }) => {
const supabase = getSupabaseServerClient() const supabase = getSupabaseServerClient()
const { error } = await supabase.auth.signUp({ const users = await supabase.auth.admin.listUsers({
email: data.email, page: data.page,
password: data.password, perPage: data.limit
options: {
// Estos son los metadatos
data: {
name: data.name,
location: data.location
}
}
}) })
if (error) {
if (users.error) {
return { return {
error: true, error: true,
message: error.message message: users.error.message
} }
} }
return {
throw redirect({ users: users.data,
href: data.redirectUrl || "/" error: false
}) }
}) })
// TODO: Add resend confirmation email function export const user = {
login,
logout,
signup,
userData,
resendConfirmationEmail,
userList
}

View File

@ -18,3 +18,8 @@ export const profileFormSchema = z.object({
firstName: z.string().min(1, "First name is required"), firstName: z.string().min(1, "First name is required"),
lastName: z.string().min(1, "Last name is required") lastName: z.string().min(1, "Last name is required")
}) })
export const userListParamsSchema = z.object({
page: z.number().min(1).default(1),
limit: z.number().min(1).max(100).default(10)
})

View File

@ -8,7 +8,7 @@ import {
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools" import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"
import { HeroUIProvider } from "@/integrations/heroui/provider" import { HeroUIProvider } from "@/integrations/heroui/provider"
import { SonnerProvider } from "@/integrations/sonner/provider" import { SonnerProvider } from "@/integrations/sonner/provider"
import { getUser } from "@/lib/server/user" import { user } from "@/lib/server/user"
interface MyRouterContext { interface MyRouterContext {
queryClient: QueryClient queryClient: QueryClient
@ -17,9 +17,9 @@ interface MyRouterContext {
export const Route = createRootRouteWithContext<MyRouterContext>()({ export const Route = createRootRouteWithContext<MyRouterContext>()({
beforeLoad: async () => { beforeLoad: async () => {
const user = await getUser() const userData = await user.userData()
return { return {
...user ...userData
} }
}, },
head: () => ({ head: () => ({

View File

@ -1,7 +1,9 @@
import { createFileRoute } from "@tanstack/react-router" import { createFileRoute } from "@tanstack/react-router"
import { logoutUser } from "@/lib/server/user" import { user } from "@/lib/server/user"
export const Route = createFileRoute("/logout")({ export const Route = createFileRoute("/logout")({
preload: false, beforeLoad: async () => {
loader: () => logoutUser() await user.logout()
},
preload: false
}) })