tokenRefresh, crear y editar productos con img funcionando

This commit is contained in:
2025-08-21 14:57:55 -04:00
parent 6a28e141a9
commit c45307d47d
22 changed files with 301 additions and 193 deletions

View File

@@ -24,7 +24,7 @@ export class JwtRefreshGuard implements CanActivate {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request); const token = this.extractTokenFromHeader(request);
if (!token) { if (!token) {
throw new UnauthorizedException(); throw new UnauthorizedException('No Refresh Token?');
} }
try { try {
request.user = await this.jwtService.verifyAsync(token, { request.user = await this.jwtService.verifyAsync(token, {
@@ -43,8 +43,8 @@ export class JwtRefreshGuard implements CanActivate {
} }
private extractTokenFromHeader(request: Request): string | undefined { private extractTokenFromHeader(request: Request): string | undefined {
const token = request.body.token const token = request.body.refresh_token
console.log(token); // console.log(token);
if (token) { if (token) {
return token; return token;

View File

@@ -57,13 +57,20 @@ export class AuthController {
@Patch('refresh') @Patch('refresh')
//@RequirePermissions('auth:refresh-token') //@RequirePermissions('auth:refresh-token')
async refreshToken(@Req() req: Request,@Body() refreshTokenDto: RefreshTokenDto) { async refreshToken(@Req() req: Request,@Body() refreshTokenDto: RefreshTokenDto) {
console.log("Pepe");
console.log(req['user']); // console.log("Pepeeeee");
//console.log(refreshTokenDto); // console.log(req['user']);
// console.log("refreshTokenDto",refreshTokenDto);
return null // console.log(typeof refreshTokenDto);
// return await this.authService.refreshToken(refreshTokenDto); const data = await this.authService.refreshToken(refreshTokenDto,req['user'].sub);
// console.log("data",data);
if (!data) {
return null;
}
return {tokens: data}
} }
// @Public() // @Public()

View File

@@ -261,8 +261,9 @@ export class AuthService {
} }
//Refresh User Access Token //Refresh User Access Token
async refreshToken(dto: RefreshTokenDto): Promise<RefreshTokenInterface> { async refreshToken(dto: RefreshTokenDto,user_id:number): Promise<RefreshTokenInterface> {
const { user_id } = dto; // const { user_id } = dto;
// const user_id = 1;
const session = await this.drizzle const session = await this.drizzle
.select() .select()
@@ -274,16 +275,22 @@ export class AuthService {
), ),
); );
// console.log(session.length);
if (session.length === 0) throw new NotFoundException('session not found'); if (session.length === 0) throw new NotFoundException('session not found');
const user = await this.findUserById(dto.user_id); const user = await this.findUserById(user_id);
if (!user) throw new NotFoundException('User not found'); if (!user) throw new NotFoundException('User not found');
// Genera token
const tokens = await this.generateTokens(user); const tokens = await this.generateTokens(user);
const decodeAccess = this.decodeToken(tokens.access_token); const decodeAccess = this.decodeToken(tokens.access_token);
const decodeRefresh = this.decodeToken(tokens.refresh_token); const decodeRefresh = this.decodeToken(tokens.refresh_token);
// Actualiza session
await this.drizzle await this.drizzle
.update(sessions) .update(sessions)
.set({ sessionToken: tokens.refresh_token, expiresAt: decodeRefresh.exp }) .set({ sessionToken: tokens.refresh_token, expiresAt: decodeRefresh.exp })
.where(eq(sessions.userId, dto.user_id)); .where(eq(sessions.userId, user_id));
return { return {
access_token: tokens.access_token, access_token: tokens.access_token,

View File

@@ -39,15 +39,10 @@ export class CreateProductDto {
status: string; status: string;
@ApiProperty() @ApiProperty()
@IsInt({ @IsOptional()
message: 'userID must be a number',
})
// @IsOptional()
userId: number; userId: number;
@ApiProperty() @ApiProperty()
@IsString({ @IsOptional()
message: 'urlImg must be a string',
})
urlImg: string; urlImg: string;
} }

View File

@@ -52,43 +52,34 @@ export class UsersController {
} }
@Post() @Post()
// @Roles('admin')
@ApiOperation({ summary: 'Create a new product' }) @ApiOperation({ summary: 'Create a new product' })
@ApiResponse({ status: 201, description: 'Product created successfully.' }) @ApiResponse({ status: 201, description: 'Product created successfully.' })
@ApiResponse({ status: 500, description: 'Internal server error.' })
@UseInterceptors(FilesInterceptor('urlImg'))
async create( async create(
@Req() req: Request,
@Body() createUserDto: CreateProductDto, @Body() createUserDto: CreateProductDto,
@UploadedFiles() files: Express.Multer.File[],
@Query('roleId') roleId?: string, @Query('roleId') roleId?: string,
) { ) {
const data = await this.inventoryService.create(createUserDto) const id = Number(req['user'].id);
const data = await this.inventoryService.create(files,createUserDto,id)
return { message: 'User created successfully', data }; return { message: 'User created successfully', data };
} }
@Patch('/id/:id')
// @Roles('admin')
@ApiOperation({ summary: 'Update a product' })
@ApiResponse({ status: 200, description: 'Product updated successfully.' })
@ApiResponse({ status: 404, description: 'Product not found.' })
async update(@Param('id') id: string, @Body() UpdateProductDto: UpdateProductDto) {
const data = await this.inventoryService.update(id, UpdateProductDto);
return { message: 'User updated successfully', data };
}
@Patch('/upload') @Patch('/upload')
@ApiOperation({ summary: 'Update a product' }) @ApiOperation({ summary: 'Update a product' })
@ApiResponse({ status: 200, description: 'Product uploaded successfully.'}) @ApiResponse({ status: 200, description: 'Product uploaded successfully.'})
@ApiResponse({ status: 404, description: 'Product not found.' }) @ApiResponse({ status: 404, description: 'Product not found.' })
@ApiResponse({ status: 500, description: 'Internal server error.' }) @ApiResponse({ status: 500, description: 'Internal server error.' })
@UseInterceptors(FilesInterceptor('urlImg')) @UseInterceptors(FilesInterceptor('urlImg'))
async uploadFile(@Req() req: Request, @UploadedFiles() files: Express.Multer.File[], @Body() body: any) { async uploadFile(
// Aquí puedes acceder a los campos del formulario @Req() req: Request,
// console.log('Archivos:', files); @UploadedFiles() files: Express.Multer.File[],
@Body() body: any
) {
const id = Number(req['user'].id); const id = Number(req['user'].id);
// console.log(req['user'].id)
// console.log('Otros campos del formulario:', body);
const result = await this.inventoryService.saveImages(files,body,id); const result = await this.inventoryService.saveImages(files,body,id);
// const result = ['result']
return { data: result }; return { data: result };
} }

View File

@@ -187,63 +187,92 @@ export class InventoryService {
// Rest of the service remains the same // Rest of the service remains the same
async create( async create(
createProductDto: CreateProductDto file: Express.Multer.File[],
createProductDto: CreateProductDto,
userId: number,
): Promise<any> { ): Promise<any> {
let gallery: string[] = [];
await Promise.all(file.map(async (f, index) => {
const fileName = `${index + 1}-${f.originalname}`;
gallery.push(fileName);
}));
console.log(gallery);
// Start a transaction // Start a transaction
return await this.drizzle.transaction(async (tx) => { return await this.drizzle.transaction(async (tx) => {
const productValue = {
title: createProductDto.title,
description: createProductDto.description,
price: createProductDto.price,
address: createProductDto.address,
status: createProductDto.status,
urlImg: gallery[0],
stock: createProductDto.stock,
userId: userId,
gallery: gallery
}
console.log(productValue);
const [newProduct] = await tx const [newProduct] = await tx
.insert(products) .insert(products)
.values({ .values(productValue)
title: createProductDto.title,
description: createProductDto.description,
price: createProductDto.price,
address: createProductDto.address,
urlImg: createProductDto.urlImg,
stock: createProductDto.stock,
status: createProductDto.status,
userId: createProductDto.userId
})
.returning(); .returning();
const productId = newProduct.id;
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory',userId.toString() , productId.toString());
// Crea el directorio si no existe
await mkdir(picturesPath, { recursive: true });
await Promise.all(file.map(async (f, index) => {
const fileName = `${index + 1}-${f.originalname}`;
const filePath = join(picturesPath, fileName);
await writeFile(filePath, f.buffer);
}));
return newProduct return newProduct
}); })
} }
async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> { // async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> {
const productId = parseInt(id); // const productId = parseInt(id);
// console.log(updateProductDto); // // console.log(updateProductDto);
// Check if exists // // Check if exists
await this.findOne(id); // await this.findOne(id);
// Prepare update data // // Prepare update data
const updateData: any = {}; // const updateData: any = {};
if (updateProductDto.title) updateData.title = updateProductDto.title; // if (updateProductDto.title) updateData.title = updateProductDto.title;
if (updateProductDto.description) updateData.description = updateProductDto.description; // if (updateProductDto.description) updateData.description = updateProductDto.description;
if (updateProductDto.price) updateData.price = updateProductDto.price; // if (updateProductDto.price) updateData.price = updateProductDto.price;
if (updateProductDto.address) updateData.address = updateProductDto.address; // if (updateProductDto.address) updateData.address = updateProductDto.address;
if (updateProductDto.status) updateData.status = updateProductDto.status; // if (updateProductDto.status) updateData.status = updateProductDto.status;
if (updateProductDto.stock) updateData.stock = updateProductDto.stock; // if (updateProductDto.stock) updateData.stock = updateProductDto.stock;
if (updateProductDto.urlImg) updateData.urlImg = updateProductDto.urlImg; // if (updateProductDto.urlImg) updateData.urlImg = updateProductDto.urlImg;
const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning(); // const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning();
return updatedProduct // return updatedProduct
// Return updated user // // Return updated user
// return this.findOne(id); // // return this.findOne(id);
} // }
/** /**
* Guarda una imagen en el directorio de imágenes. * Guarda una imagen en el directorio de imágenes.
* @param file - El archivo de imagen a guardar. * @param file - El archivo de imagen a guardar.
* @returns La ruta de la imagen guardada. * @returns La ruta de la imagen guardada.
*/ */
async saveImages(file: Express.Multer.File[], updateProductDto: UpdateProductDto, id: number): Promise<Product> { async saveImages(file: Express.Multer.File[], updateProductDto: UpdateProductDto, userId: number): Promise<Product> {
const productId = parseInt(id.toString()); const productId = parseInt(updateProductDto.id);
// Construye la ruta al directorio de imágenes. // Construye la ruta al directorio de imágenes.
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory', id.toString()); const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory', userId.toString() , productId.toString());
// --- NUEVA LÓGICA: Borrar el directorio anterior --- // --- NUEVA LÓGICA: Borrar el directorio anterior ---
try { try {
@@ -278,6 +307,7 @@ export class InventoryService {
if (updateProductDto.status) updateData.status = updateProductDto.status; if (updateProductDto.status) updateData.status = updateProductDto.status;
if (updateProductDto.stock) updateData.stock = updateProductDto.stock; if (updateProductDto.stock) updateData.stock = updateProductDto.stock;
if (file && file.length > 0) updateData.gallery = gallery; if (file && file.length > 0) updateData.gallery = gallery;
if (file && file.length > 0) updateData.urlImg = gallery[0];
const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning(); const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning();
return updatedProduct; return updatedProduct;

View File

@@ -10,8 +10,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from '@repo/shadcn/card'; } from '@repo/shadcn/card';
import { Edit } from 'lucide-react'; import {ProductList} from '@/feactures/inventory/components/products/see-product'
export default async function SurveyResponsePage({ export default async function SurveyResponsePage({
params, params,
@@ -39,56 +38,57 @@ export default async function SurveyResponsePage({
// console.log(data.data); // console.log(data.data);
return ( return (
<ProductList product={product} />
// <PageContainer> // <PageContainer>
<main className='px-4 lg:px-6 flex flex-col md:flex-row gap-3 lg:gap-4 md:relative'> // <main className='px-4 lg:px-6 flex flex-col md:flex-row gap-3 lg:gap-4 md:relative'>
<div className='w-full flex justify-between flex-col'> // <div className='w-full flex justify-between flex-col'>
<img // <img
className="border-2 object-contain w-full f-full min-h-[400px] md:h-[70vh] aspect-square rounded-2xl" // className="border-2 object-contain w-full f-full min-h-[400px] md:h-[70vh] aspect-square rounded-2xl"
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.urlImg}`} // src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.urlImg}`}
alt="" // alt=""
/> // />
<section className=''> // <section className=''>
<img // <img
className="border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl" // className="border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl"
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.urlImg}`} // src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.urlImg}`}
alt="" // alt=""
/> // />
</section> // </section>
</div> // </div>
<Card className="flex flex-col md:w-[400px] lg:w-[500px] min-h-[400px] md:h-[85vh] md:overflow-auto md:sticky top-0 right-0"> // <Card className="flex flex-col md:w-[400px] lg:w-[500px] min-h-[400px] md:h-[85vh] md:overflow-auto md:sticky top-0 right-0">
<CardHeader className='py-2 px-2 md:px-4 lg:px-6'> // <CardHeader className='py-2 px-2 md:px-4 lg:px-6'>
<CardTitle className="font-bold text-2xl"> // <CardTitle className="font-bold text-2xl">
{product.title.charAt(0).toUpperCase() + product.title.slice(1)} // {product.title.charAt(0).toUpperCase() + product.title.slice(1)}
</CardTitle> // </CardTitle>
<p className='font-semibold'>$ {product.price} </p> // <p className='font-semibold'>$ {product.price} </p>
{product.status === 'AGOTADO' ? ( // {product.status === 'AGOTADO' ? (
<p className="font-semibold text-lg text-red-900">AGOTADO</p> // <p className="font-semibold text-lg text-red-900">AGOTADO</p>
) : ('')} // ) : ('')}
</CardHeader> // </CardHeader>
<CardContent className="py-0 px-2 md:px-4 lg:px-6 flex-col justify-between flex-grow md:overflow-auto"> // <CardContent className="py-0 px-2 md:px-4 lg:px-6 flex-col justify-between flex-grow md:overflow-auto">
<div> // <div>
<p className='font-semibold text-lg border-t border-b'> Descripción</p> // <p className='font-semibold text-lg border-t border-b'> Descripción</p>
<p className='p-1'>{product.description}</p> // <p className='p-1'>{product.description}</p>
{/* <p className='p-1'>{lorem+lorem+lorem+lorem}</p> */} // {/* <p className='p-1'>{lorem+lorem+lorem+lorem}</p> */}
</div> // </div>
<div className='mt-2'> // <div className='mt-2'>
<p className='font-semibold text-lg border-t border-b'> Dirección</p> // <p className='font-semibold text-lg border-t border-b'> Dirección</p>
<p className='p-1'>{product.address}</p> // <p className='p-1'>{product.address}</p>
</div> // </div>
</CardContent> // </CardContent>
<CardFooter className="px-2 md:px-4 lg:px-6"> // <CardFooter className="px-2 md:px-4 lg:px-6">
<div> // <div>
<p className='font-semibold text-lg border-t border-b mt-4'>Información del vendedor</p> // <p className='font-semibold text-lg border-t border-b mt-4'>Información del vendedor</p>
<p>{product.fullname}</p> // <p>{product.fullname}</p>
<p>{product.phone}</p> // <p>{product.phone}</p>
<p>{product.email}</p> // <p>{product.email}</p>
</div> // </div>
</CardFooter> // </CardFooter>
</Card> // </Card>
</main> // </main>
); );
} }

View File

@@ -7,14 +7,9 @@ import {
export const resfreshTokenAction = async (refreshToken: RefreshTokenValue) => { export const resfreshTokenAction = async (refreshToken: RefreshTokenValue) => {
try { try {
const body = { const response = await refreshApi.patch('/auth/refresh', {refresh_token: refreshToken.token});
token: refreshToken.token,
}
// Usa la nueva instancia `refreshApi` const parsed = RefreshTokenResponseSchema.safeParse(response.data);
const response = await refreshApi.patch('/auth/refresh', body);
const parsed = RefreshTokenResponseSchema.safeParse(response.data);
if (!parsed.success) { if (!parsed.success) {
console.error('Error de validación en la respuesta de refresh token:', { console.error('Error de validación en la respuesta de refresh token:', {

View File

@@ -118,15 +118,15 @@ export const getProductById = async (id: number) => {
}; };
export const createProductAction = async (payload: FormData) => { export const createProductAction = async (payload: FormData) => {
const session = await auth() // const session = await auth()
const userId = session?.user?.id // const userId = session?.user?.id
if (userId) { // if (userId) {
payload.append('userId', String(userId)); // payload.append('userId', String(userId));
} // }
const [error, data] = await safeFetchApi( const [error, data] = await safeFetchApi(
productMutate, test,
'/products', '/products',
'POST', 'POST',
payload, payload,

View File

@@ -18,7 +18,7 @@ import {
} from '@repo/shadcn/select'; } from '@repo/shadcn/select';
import { Input } from '@repo/shadcn/input'; import { Input } from '@repo/shadcn/input';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useCreateUser } from "@/feactures/inventory/hooks/use-mutation"; import { useCreateProduct } from "@/feactures/inventory/hooks/use-mutation";
import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory'; import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory';
import { Textarea } from '@repo/shadcn/textarea'; import { Textarea } from '@repo/shadcn/textarea';
import { STATUS } from '@/constants/status' import { STATUS } from '@/constants/status'
@@ -37,7 +37,7 @@ export function CreateForm({
const { const {
mutate: saveProduct, mutate: saveProduct,
isPending: isSaving, isPending: isSaving,
} = useCreateUser(); } = useCreateProduct();
const [sizeFile, setSizeFile] = useState('0 bytes'); const [sizeFile, setSizeFile] = useState('0 bytes');
const [previewUrls, setPreviewUrls] = useState<string[]>([]); const [previewUrls, setPreviewUrls] = useState<string[]>([]);

View File

@@ -14,7 +14,7 @@ export const columns: ColumnDef<InventoryTable>[] = [
header: 'img', header: 'img',
cell: ({ row }) => { cell: ({ row }) => {
return ( return (
<img src={`http://localhost:3000/uploads/inventory/${row.original.userId}/${row.original.urlImg}`} alt="" width={64} height={64} className="rounded"/> <img src={`http://localhost:3000/uploads/inventory/${row.original.userId}/${row.original.id}/${row.original.urlImg}`} alt="" width={64} height={64} className="rounded"/>
) )
}, },
}, },

View File

@@ -18,7 +18,7 @@ import {
} from '@repo/shadcn/select'; } from '@repo/shadcn/select';
import { Input } from '@repo/shadcn/input'; import { Input } from '@repo/shadcn/input';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useUpdateUser } from "@/feactures/inventory/hooks/use-mutation"; import { useUpdateProduct } from "@/feactures/inventory/hooks/use-mutation";
import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory'; // Renombrado EditInventory para claridad import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory'; // Renombrado EditInventory para claridad
import { Textarea } from '@repo/shadcn/components/ui/textarea'; import { Textarea } from '@repo/shadcn/components/ui/textarea';
import {STATUS} from '@/constants/status' import {STATUS} from '@/constants/status'
@@ -55,7 +55,7 @@ export function UpdateForm({
mutate: saveAccountingAccounts, mutate: saveAccountingAccounts,
isPending: isSaving, isPending: isSaving,
isError, isError,
} = useUpdateUser(); } = useUpdateProduct();
const [sizeFile, setSizeFile] = useState('0 bytes'); const [sizeFile, setSizeFile] = useState('0 bytes');
const [previewUrls, setPreviewUrls] = useState<string[]>([]); const [previewUrls, setPreviewUrls] = useState<string[]>([]);
@@ -106,10 +106,10 @@ export function UpdateForm({
} }
} }
// --- IMPORTANTE: Tu hook `useUpdateUser` DEBE ser capaz de aceptar FormData --- // --- IMPORTANTE: Tu hook `useUpdateProduct` DEBE ser capaz de aceptar FormData ---
// Si `useUpdateUser` llama a `safeFetchApi`, entonces `safeFetchApi` ya está preparado // Si `useUpdateProduct` llama a `safeFetchApi`, entonces `safeFetchApi` ya está preparado
// para recibir `FormData`. // para recibir `FormData`.
saveAccountingAccounts(formData as any, { // Forzamos el tipo a 'any' si `useUpdateUser` no espera FormData en su tipo saveAccountingAccounts(formData as any, { // Forzamos el tipo a 'any' si `useUpdateProduct` no espera FormData en su tipo
onSuccess: () => { onSuccess: () => {
form.reset(); form.reset();
onSuccess?.(); onSuccess?.();

View File

@@ -44,7 +44,7 @@ export function ProductList() {
<CardContent className="p-0 flex-grow"> <CardContent className="p-0 flex-grow">
<img <img
className="object-cover w-full h-full aspect-square border" className="object-cover w-full h-full aspect-square border"
src={`http://localhost:3000/uploads/inventory/${data.userId}/${data.urlImg}`} src={`http://localhost:3000/uploads/inventory/${data.userId}/${data.id}/${data.urlImg}`}
alt="" alt=""
/> />
</CardContent> </CardContent>

View File

@@ -0,0 +1,64 @@
import { allProducts, } from "../../schemas/inventory";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from '@repo/shadcn/card';
export function ProductList({product}: {product: allProducts}) {
return (
// <PageContainer>
<main className='px-4 lg:px-6 flex flex-col md:flex-row gap-3 lg:gap-4 md:relative'>
<div className='w-full flex justify-between flex-col'>
<img
className="border-2 object-contain w-full f-full min-h-[400px] md:h-[70vh] aspect-square rounded-2xl"
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.id}/${product.urlImg}`}
alt=""
/>
<section className=''>
<img
className="border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl"
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.id}/${product.urlImg}`}
alt=""
/>
</section>
</div>
<Card className="flex flex-col md:w-[400px] lg:w-[500px] min-h-[400px] md:h-[85vh] md:overflow-auto md:sticky top-0 right-0">
<CardHeader className='py-2 px-2 md:px-4 lg:px-6'>
<CardTitle className="font-bold text-2xl">
{product.title.charAt(0).toUpperCase() + product.title.slice(1)}
</CardTitle>
<p className='font-semibold'>$ {product.price} </p>
{product.status === 'AGOTADO' ? (
<p className="font-semibold text-lg text-red-900">AGOTADO</p>
) : ('')}
</CardHeader>
<CardContent className="py-0 px-2 md:px-4 lg:px-6 flex-col justify-between flex-grow md:overflow-auto">
<div>
<p className='font-semibold text-lg border-t border-b'> Descripción</p>
<p className='p-1'>{product.description}</p>
{/* <p className='p-1'>{lorem+lorem+lorem+lorem}</p> */}
</div>
<div className='mt-2'>
<p className='font-semibold text-lg border-t border-b'> Dirección</p>
<p className='p-1'>{product.address}</p>
</div>
</CardContent>
<CardFooter className="px-2 md:px-4 lg:px-6">
<div>
<p className='font-semibold text-lg border-t border-b mt-4'>Información del vendedor</p>
<p>{product.fullname}</p>
<p>{product.phone}</p>
<p>{product.email}</p>
</div>
</CardFooter>
</Card>
</main>
);
}

View File

@@ -3,7 +3,7 @@ import { EditInventory } from "../schemas/inventory";
import { updateUserAction, createProductAction, updateUserAction2 } from "../actions/actions"; import { updateUserAction, createProductAction, updateUserAction2 } from "../actions/actions";
// Create mutation // Create mutation
export function useCreateUser() { export function useCreateProduct() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const mutation = useMutation({ const mutation = useMutation({
mutationFn: (data: any) => createProductAction(data), mutationFn: (data: any) => createProductAction(data),
@@ -13,7 +13,7 @@ export function useCreateUser() {
} }
// Update mutation // Update mutation
export function useUpdateUser() { export function useUpdateProduct() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const mutation = useMutation({ const mutation = useMutation({
// mutationFn: (data: EditInventory) => updateUserAction(data), // mutationFn: (data: EditInventory) => updateUserAction(data),

View File

@@ -1,5 +1,6 @@
// import { user } from '@/feactures/auth/schemas/register'; // import { user } from '@/feactures/auth/schemas/register';
// import { all } from 'axios'; // import { all } from 'axios';
import { url } from 'inspector';
import { z } from 'zod'; import { z } from 'zod';
export type InventoryTable = z.infer<typeof product>; export type InventoryTable = z.infer<typeof product>;
@@ -26,7 +27,11 @@ export const product = z.object({
userId: z.number().optional() userId: z.number().optional()
}) })
export const productDetails = product.extend({ export const seeProduct = product.extend({
urlImg: z.string(),
})
export const productDetails = seeProduct.extend({
fullname: z.string(), fullname: z.string(),
phone: z.string().nullable(), phone: z.string().nullable(),
email: z.string().email().nullable() email: z.string().email().nullable()

View File

@@ -100,65 +100,77 @@ const authConfig: NextAuthConfig = {
signIn: '/', //sigin page signIn: '/', //sigin page
}, },
callbacks: { callbacks: {
async jwt({ async jwt({ token, user }:{
token, user: User
user, token: any
}: {
token: any;
user: User;
}) { }) {
// 1. Manejar el inicio de sesión inicial
// El `user` solo se proporciona en el primer inicio de sesión.
if (user) {
return {
id: user.id,
username: user.username,
fullname: user.fullname,
email: user.email,
role: user.role,
access_token: user.access_token,
access_expire_in: user.access_expire_in,
refresh_token: user.refresh_token,
refresh_expire_in: user.refresh_expire_in
}
// return token;
}
// 2. Si no es un nuevo login, verificar la expiración del token
const now = Math.floor(Date.now() / 1000); // Usar Math.floor para un número entero
// Verificar si el token de acceso aún es válido
if (now < (token.access_expire_in as number)) {
return token; // Si no ha expirado, no hacer nada y devolver el token actual
}
console.log("Now Access Expire:",token.access_expire_in);
// 3. Si el token de acceso ha expirado, verificar el refresh token
console.log("Access token ha expirado. Verificando refresh token...");
if (now > (token.refresh_expire_in as number)) {
console.log("Refresh token ha expirado. Forzando logout.");
return null; // Forzar el logout al devolver null
}
console.log("token:", token.refresh_token);
// 4. Si el token de acceso ha expirado pero el refresh token es válido, renovar
console.log("Renovando token de acceso...");
try { try {
// Si es un nuevo login, asignamos los datos const res = await resfreshTokenAction({ token: token.refresh_token as string });
if (user) {
token.id = user.id; if (!res || !res.tokens) {
token.username = user.username; throw new Error('Fallo en la respuesta de la API de refresco.');
token.fullname = user.fullname;
token.email = user.email;
token.role = user.role;
token.access_token = user.access_token;
token.access_expire_in = user.access_expire_in;
token.refresh_token = user.refresh_token;
token.refresh_expire_in = user.refresh_expire_in;
return token; // IMPORTANTE: retornamos el token aquí para evitar que entre en el siguiente 'if'
} }
// Verificar si el access_token ha expirado console.log("Old Access Expire:", token.access_expire_in);
const now = Date.now() / 1000; console.log("New Access Expire:", res.tokens.access_expire_in);
if (now < (token.access_expire_in as number)) {
return token; // Si el token no ha expirado, lo retornamos sin cambios
}
// Si el access_token ha expirado, verificar el refresh_token console.log("token:", token.refresh_token);
if (now > (token.refresh_expire_in as number)) {
console.log("Refresh token ha expirado. Forzando logout.");
return null; // Forzar logout
}
// Si el access_token ha expirado pero el refresh_token es válido, renovar
console.log("Renovando token de acceso...");
const res = await resfreshTokenAction({
token: token.refresh_token as string,
});
console.log("Pepe");
if (!res) throw new Error('Fallo al renovar el token'); // Actualizar el token directamente con los nuevos valores
token.access_token = res.tokens.access_token;
token.access_expire_in = res.tokens.access_expire_in;
token.refresh_token = res.tokens.refresh_token;
token.refresh_expire_in = res.tokens.refresh_expire_in;
return token;
// Actualizar el token con la nueva información
return {
...token,
access_token: res.tokens.access_token,
access_expire_in: res.tokens.access_expire_in,
refresh_token: res.tokens.refresh_token,
refresh_expire_in: res.tokens.refresh_expire_in,
};
} catch (error) { } catch (error) {
console.error("Error al renovar el token: ", error); console.error("Error al renovar el token: ", error);
return null; // Fallo al renovar, forzar logout return null; // Fallo al renovar, forzar logout
} }
}, },
async session({ session, token }: { session: Session; token: DefaultJWT }) { async session({ session, token }: { session: Session; token: any }) {
session.access_token = token.access_token as string; session.access_token = token.access_token as string;
session.access_expire_in = token.access_expire_in as number; session.access_expire_in = token.access_expire_in as number;
session.refresh_token = token.refresh_token as string; session.refresh_token = token.refresh_token as string;

View File

@@ -12,6 +12,8 @@ const fetchApi = axios.create({
// ESTE INTERCEPTOR ESTÁ BIEN PARA EL RESTO DE LAS PETICIONES AUTENTICADAS // ESTE INTERCEPTOR ESTÁ BIEN PARA EL RESTO DE LAS PETICIONES AUTENTICADAS
fetchApi.interceptors.request.use(async (config: any) => { fetchApi.interceptors.request.use(async (config: any) => {
try { try {
console.log("Solicitando autenticación...");
const { auth } = await import('@/lib/auth'); // Importación dinámica const { auth } = await import('@/lib/auth'); // Importación dinámica
const session = await auth(); const session = await auth();
const token = session?.access_token; const token = session?.access_token;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 786 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB