style: clean up code formatting and remove trailing whitespace
refactor: reorganize imports and fix type imports
This commit is contained in:
1 parent
d3704602fd
commit
47c04de6fe
232 files changed
+7140
-4798
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getListingById, updateListing } from "@/lib/prisma/queries/listings";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/:id/activate
|
||||
@@ -9,7 +9,7 @@ import { getListingById, updateListing } from "@/lib/prisma/queries/listings";
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
// Autenticação obrigatória
|
||||
@@ -24,13 +24,13 @@ export async function POST(
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Anúncio não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Você não tem permissão para reativar este anúncio",
|
||||
},
|
||||
{ status: 403 }
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Apenas anúncios pausados podem ser reativados",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao reativar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function POST(
|
||||
listing: updated,
|
||||
message: "Anúncio reativado com sucesso",
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao reativar anúncio", {
|
||||
@@ -105,8 +105,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao reativar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { placeBid } from "@/lib/prisma/queries/listings";
|
||||
import { createNotification } from "@/lib/prisma/queries/notifications";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/[id]/bid
|
||||
@@ -10,7 +10,7 @@ import { createNotification } from "@/lib/prisma/queries/notifications";
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const authResult = await requireAuth(request);
|
||||
@@ -19,10 +19,10 @@ export async function POST(
|
||||
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de anúncio inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,34 +31,42 @@ export async function POST(
|
||||
|
||||
if (!amount || typeof amount !== "number" || amount <= 0) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Valor do lance é obrigatório e deve ser maior que zero" },
|
||||
{ status: 400 }
|
||||
{
|
||||
success: false,
|
||||
error: "Valor do lance é obrigatório e deve ser maior que zero",
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
if (!payment_method || !["pix", "tc", "gp"].includes(payment_method)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Método de pagamento inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Colocar lance
|
||||
const result = await placeBid(listingId, auth.userId, amount, payment_method);
|
||||
const result = await placeBid(
|
||||
listingId,
|
||||
auth.userId,
|
||||
amount,
|
||||
payment_method,
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: result.error },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Criar notificação para o vendedor
|
||||
await createNotification({
|
||||
user_id: result.listing!.seller_id,
|
||||
user_id: result.listing?.seller_id,
|
||||
notification_type: "bid",
|
||||
title: "Novo lance!",
|
||||
message: `Novo lance de R$ ${amount.toFixed(2)} no item "${result.listing!.item_name}"`,
|
||||
message: `Novo lance de R$ ${amount.toFixed(2)} no item "${result.listing?.item_name}"`,
|
||||
related_id: listingId,
|
||||
send_whatsapp: false,
|
||||
});
|
||||
@@ -69,7 +77,7 @@ export async function POST(
|
||||
user_id: result.previousBidderId,
|
||||
notification_type: "bid",
|
||||
title: "Lance superado",
|
||||
message: `Seu lance foi superado no item "${result.listing!.item_name}"`,
|
||||
message: `Seu lance foi superado no item "${result.listing?.item_name}"`,
|
||||
related_id: listingId,
|
||||
send_whatsapp: false,
|
||||
});
|
||||
@@ -84,8 +92,12 @@ export async function POST(
|
||||
metadata: {
|
||||
bid_amount: amount,
|
||||
payment_method: payment_method,
|
||||
previous_bid: result.listing!.current_bid_amount ? Number(result.listing!.current_bid_amount) : null,
|
||||
min_increment: result.listing!.min_bid_increment ? Number(result.listing!.min_bid_increment) : null,
|
||||
previous_bid: result.listing?.current_bid_amount
|
||||
? Number(result.listing?.current_bid_amount)
|
||||
: null,
|
||||
min_increment: result.listing?.min_bid_increment
|
||||
? Number(result.listing?.min_bid_increment)
|
||||
: null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -102,7 +114,7 @@ export async function POST(
|
||||
listing: result.listing,
|
||||
message: "Lance realizado com sucesso",
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao processar lance", {
|
||||
@@ -112,8 +124,7 @@ export async function POST(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao processar lance" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { prisma } from "@/lib/prisma/app";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { trackEvent } from "@/lib/prisma/queries/telemetry";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* GET /api/listings/[id]/bids
|
||||
@@ -11,7 +11,7 @@ import { trackEvent } from "@/lib/prisma/queries/telemetry";
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const authResult = await requireAuth(request);
|
||||
@@ -19,10 +19,10 @@ export async function GET(
|
||||
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de anúncio inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ export async function GET(
|
||||
if (!listing) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não encontrado" },
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
if (!listing.is_auction) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Este anúncio não é um leilão" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ export async function GET(
|
||||
bids: formattedBids,
|
||||
total: formattedBids.length,
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao buscar histórico de lances", {
|
||||
@@ -102,8 +102,7 @@ export async function GET(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao buscar histórico de lances" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { isFavorite, toggleFavorite } from "@/lib/prisma/queries/favorites";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/:id/favorite
|
||||
@@ -10,7 +10,7 @@ import { isFavorite, toggleFavorite } from "@/lib/prisma/queries/favorites";
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
// Autenticação obrigatória
|
||||
@@ -25,13 +25,13 @@ export async function POST(
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,17 +44,20 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Anúncio não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
// Toggle favorite
|
||||
const favorited = await toggleFavorite(auth.userId, listingId);
|
||||
|
||||
secureLogger.info(`✅ Anúncio ${favorited ? "favoritado" : "desfavoritado"}`, {
|
||||
listingId,
|
||||
userId: auth.userId,
|
||||
});
|
||||
secureLogger.info(
|
||||
`✅ Anúncio ${favorited ? "favoritado" : "desfavoritado"}`,
|
||||
{
|
||||
listingId,
|
||||
userId: auth.userId,
|
||||
},
|
||||
);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
@@ -62,7 +65,7 @@ export async function POST(
|
||||
favorited,
|
||||
message: favorited ? "Anúncio favoritado" : "Anúncio desfavoritado",
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao favoritar/desfavoritar anúncio", {
|
||||
@@ -75,7 +78,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao favoritar/desfavoritar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -86,7 +89,7 @@ export async function POST(
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
// Autenticação obrigatória
|
||||
@@ -101,13 +104,13 @@ export async function GET(
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,7 +121,7 @@ export async function GET(
|
||||
success: true,
|
||||
favorited,
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao verificar favorito", {
|
||||
@@ -130,8 +133,7 @@ export async function GET(
|
||||
success: false,
|
||||
error: "Erro ao verificar favorito",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { connectMongoDB } from "@/lib/mongodb/connection";
|
||||
import { Chat } from "@/lib/mongodb/models/Chat";
|
||||
import { Message } from "@/lib/mongodb/models/Message";
|
||||
import { getMainCharacter } from "@/lib/prisma/queries/characters";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { createNotification } from "@/lib/prisma/queries/notifications";
|
||||
import {
|
||||
getPurchaseIntentionById,
|
||||
updatePurchaseIntentionStatus,
|
||||
} from "@/lib/prisma/queries/purchase-intentions";
|
||||
import { createNotification } from "@/lib/prisma/queries/notifications";
|
||||
import { connectMongoDB } from "@/lib/mongodb/connection";
|
||||
import { Chat } from "@/lib/mongodb/models/Chat";
|
||||
import { Message } from "@/lib/mongodb/models/Message";
|
||||
import { getUserById } from "@/lib/prisma/queries/users";
|
||||
import { getMainCharacter } from "@/lib/prisma/queries/characters";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* GET /api/listings/[id]/intentions/[intentionId]
|
||||
@@ -19,7 +19,7 @@ import { getMainCharacter } from "@/lib/prisma/queries/characters";
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string; intentionId: string }> }
|
||||
{ params }: { params: Promise<{ id: string; intentionId: string }> },
|
||||
) {
|
||||
try {
|
||||
const authResult = await requireAuth(request);
|
||||
@@ -27,10 +27,10 @@ export async function GET(
|
||||
|
||||
const { intentionId: intentionIdParam } = await params;
|
||||
const intentionId = parseInt(intentionIdParam, 10);
|
||||
if (isNaN(intentionId)) {
|
||||
if (Number.isNaN(intentionId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de intenção inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,15 +38,21 @@ export async function GET(
|
||||
if (!intention) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Intenção não encontrada" },
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar permissões: vendedor ou comprador
|
||||
if (intention.listing?.seller_id !== authResult.userId && intention.buyer_id !== authResult.userId) {
|
||||
if (
|
||||
intention.listing?.seller_id !== authResult.userId &&
|
||||
intention.buyer_id !== authResult.userId
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Você não tem permissão para ver esta intenção" },
|
||||
{ status: 403 }
|
||||
{
|
||||
success: false,
|
||||
error: "Você não tem permissão para ver esta intenção",
|
||||
},
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,7 +61,7 @@ export async function GET(
|
||||
success: true,
|
||||
intention,
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao buscar intenção", {
|
||||
@@ -65,7 +71,7 @@ export async function GET(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao buscar intenção" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +82,7 @@ export async function GET(
|
||||
*/
|
||||
export async function PATCH(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string; intentionId: string }> }
|
||||
{ params }: { params: Promise<{ id: string; intentionId: string }> },
|
||||
) {
|
||||
try {
|
||||
const authResult = await requireAuth(request);
|
||||
@@ -84,10 +90,10 @@ export async function PATCH(
|
||||
|
||||
const { intentionId: intentionIdParam } = await params;
|
||||
const intentionId = parseInt(intentionIdParam, 10);
|
||||
if (isNaN(intentionId)) {
|
||||
if (Number.isNaN(intentionId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de intenção inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -100,8 +106,11 @@ export async function PATCH(
|
||||
|
||||
if (!status || !["accepted", "rejected"].includes(status)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Status inválido. Use 'accepted' ou 'rejected'" },
|
||||
{ status: 400 }
|
||||
{
|
||||
success: false,
|
||||
error: "Status inválido. Use 'accepted' ou 'rejected'",
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,15 +119,18 @@ export async function PATCH(
|
||||
if (!intention) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Intenção não encontrada" },
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar se o usuário é o vendedor
|
||||
if (intention.listing?.seller_id !== authResult.userId) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Apenas o vendedor pode aceitar ou rejeitar intenções" },
|
||||
{ status: 403 }
|
||||
{
|
||||
success: false,
|
||||
error: "Apenas o vendedor pode aceitar ou rejeitar intenções",
|
||||
},
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,16 +138,20 @@ export async function PATCH(
|
||||
if (intention.status !== "pending") {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: `Esta intenção já foi ${intention.status}` },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar se o anúncio ainda está disponível (ativo ou em escrow)
|
||||
const listing = await getListingById(intention.listing_id);
|
||||
if (!listing || (listing.listing_status !== "active" && listing.listing_status !== "in_escrow")) {
|
||||
if (
|
||||
!listing ||
|
||||
(listing.listing_status !== "active" &&
|
||||
listing.listing_status !== "in_escrow")
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não está mais disponível" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -143,7 +159,7 @@ export async function PATCH(
|
||||
const updatedIntention = await updatePurchaseIntentionStatus(
|
||||
intentionId,
|
||||
status,
|
||||
sellerResponse
|
||||
sellerResponse,
|
||||
);
|
||||
|
||||
await sendSellerResponseToChat({
|
||||
@@ -159,19 +175,23 @@ export async function PATCH(
|
||||
user_id: intention.buyer_id,
|
||||
notification_type: status === "accepted" ? "purchase" : "system",
|
||||
title: status === "accepted" ? "Intenção aceita!" : "Intenção rejeitada",
|
||||
message: status === "accepted"
|
||||
? `O vendedor aceitou sua intenção de compra do item "${intention.listing?.item_name}"`
|
||||
: `O vendedor rejeitou sua intenção de compra do item "${intention.listing?.item_name}"`,
|
||||
message:
|
||||
status === "accepted"
|
||||
? `O vendedor aceitou sua intenção de compra do item "${intention.listing?.item_name}"`
|
||||
: `O vendedor rejeitou sua intenção de compra do item "${intention.listing?.item_name}"`,
|
||||
related_id: intention.listing_id,
|
||||
send_whatsapp: false,
|
||||
});
|
||||
|
||||
secureLogger.info(`✅ Intenção ${status === "accepted" ? "aceita" : "rejeitada"}`, {
|
||||
userId: authResult.userId,
|
||||
intentionId,
|
||||
listingId: intention.listing_id,
|
||||
buyerId: intention.buyer_id,
|
||||
});
|
||||
secureLogger.info(
|
||||
`✅ Intenção ${status === "accepted" ? "aceita" : "rejeitada"}`,
|
||||
{
|
||||
userId: authResult.userId,
|
||||
intentionId,
|
||||
listingId: intention.listing_id,
|
||||
buyerId: intention.buyer_id,
|
||||
},
|
||||
);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
@@ -179,7 +199,7 @@ export async function PATCH(
|
||||
intention: updatedIntention,
|
||||
message: `Intenção ${status === "accepted" ? "aceita" : "rejeitada"} com sucesso`,
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao atualizar intenção", {
|
||||
@@ -189,7 +209,7 @@ export async function PATCH(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao atualizar intenção" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -202,7 +222,10 @@ async function sendSellerResponseToChat({
|
||||
sellerId,
|
||||
}: {
|
||||
listing: Awaited<ReturnType<typeof getListingById>>;
|
||||
intention: Exclude<Awaited<ReturnType<typeof getPurchaseIntentionById>>, null>;
|
||||
intention: Exclude<
|
||||
Awaited<ReturnType<typeof getPurchaseIntentionById>>,
|
||||
null
|
||||
>;
|
||||
status: "accepted" | "rejected";
|
||||
sellerResponse?: string;
|
||||
sellerId: number;
|
||||
@@ -225,12 +248,13 @@ async function sendSellerResponseToChat({
|
||||
let chat = await Chat.findOne({ chat_id: chatId });
|
||||
|
||||
if (!chat) {
|
||||
const [buyer, seller, buyerCharacter, sellerCharacter] = await Promise.all([
|
||||
getUserById(intention.buyer_id),
|
||||
getUserById(listing.seller_id),
|
||||
getMainCharacter(intention.buyer_id),
|
||||
getMainCharacter(listing.seller_id),
|
||||
]);
|
||||
const [buyer, seller, buyerCharacter, sellerCharacter] =
|
||||
await Promise.all([
|
||||
getUserById(intention.buyer_id),
|
||||
getUserById(listing.seller_id),
|
||||
getMainCharacter(intention.buyer_id),
|
||||
getMainCharacter(listing.seller_id),
|
||||
]);
|
||||
|
||||
chat = new Chat({
|
||||
chat_id: chatId,
|
||||
@@ -284,4 +308,3 @@ async function sendSellerResponseToChat({
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { createNotification } from "@/lib/prisma/queries/notifications";
|
||||
import {
|
||||
createPurchaseIntention,
|
||||
getPurchaseIntentionsByListing,
|
||||
hasPendingIntention,
|
||||
getPendingIntentionByBuyerAndListing,
|
||||
getPurchaseIntentionsByListing,
|
||||
} from "@/lib/prisma/queries/purchase-intentions";
|
||||
import { createNotification } from "@/lib/prisma/queries/notifications";
|
||||
import { trackEvent } from "@/lib/prisma/queries/telemetry";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/[id]/intentions
|
||||
@@ -17,7 +16,7 @@ import { trackEvent } from "@/lib/prisma/queries/telemetry";
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const authResult = await requireAuth(request);
|
||||
@@ -26,10 +25,10 @@ export async function POST(
|
||||
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de anúncio inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,10 +40,13 @@ export async function POST(
|
||||
custom_offer_currency,
|
||||
} = body;
|
||||
|
||||
if (!payment_method || !["pix", "tibia_coins", "gold_coins", "trade"].includes(payment_method)) {
|
||||
if (
|
||||
!payment_method ||
|
||||
!["pix", "tibia_coins", "gold_coins", "trade"].includes(payment_method)
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Método de pagamento inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,15 +55,19 @@ export async function POST(
|
||||
if (!listing) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não encontrado" },
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
// Validar se não é o vendedor
|
||||
if (listing.seller_id === auth.userId) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Você não pode criar uma intenção de compra para seu próprio anúncio" },
|
||||
{ status: 403 }
|
||||
{
|
||||
success: false,
|
||||
error:
|
||||
"Você não pode criar uma intenção de compra para seu próprio anúncio",
|
||||
},
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,15 +75,18 @@ export async function POST(
|
||||
if (listing.listing_status !== "active") {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não está disponível para compra" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Validar se é leilão (leilões não podem ter intenções, apenas lances)
|
||||
if (listing.is_auction) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Este item está em leilão. Use a funcionalidade de lance." },
|
||||
{ status: 400 }
|
||||
{
|
||||
success: false,
|
||||
error: "Este item está em leilão. Use a funcionalidade de lance.",
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,7 +104,7 @@ export async function POST(
|
||||
error: `Método de pagamento "${payment_method}" não está disponível para este anúncio`,
|
||||
available_methods: availableMethods,
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -107,9 +116,10 @@ export async function POST(
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Ofertas personalizadas só são permitidas para métodos monetários (PIX, Tibia Coins ou Gold Coins).",
|
||||
error:
|
||||
"Ofertas personalizadas só são permitidas para métodos monetários (PIX, Tibia Coins ou Gold Coins).",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,14 +128,16 @@ export async function POST(
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Valor da oferta personalizada deve ser numérico e maior que zero.",
|
||||
error:
|
||||
"Valor da oferta personalizada deve ser numérico e maior que zero.",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
normalizedOfferCurrency =
|
||||
custom_offer_currency && ["pix", "tibia_coins", "gold_coins"].includes(custom_offer_currency)
|
||||
custom_offer_currency &&
|
||||
["pix", "tibia_coins", "gold_coins"].includes(custom_offer_currency)
|
||||
? custom_offer_currency
|
||||
: payment_method;
|
||||
|
||||
@@ -136,7 +148,10 @@ export async function POST(
|
||||
}
|
||||
|
||||
// Verificar se já existe uma intenção pendente
|
||||
const existingIntention = await getPendingIntentionByBuyerAndListing(listingId, auth.userId);
|
||||
const existingIntention = await getPendingIntentionByBuyerAndListing(
|
||||
listingId,
|
||||
auth.userId,
|
||||
);
|
||||
if (existingIntention) {
|
||||
// Se já existe e ainda pode trocar o método
|
||||
if (existingIntention.payment_method_change_count < 1) {
|
||||
@@ -144,23 +159,27 @@ export async function POST(
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Você já possui uma intenção de compra pendente para este anúncio",
|
||||
error:
|
||||
"Você já possui uma intenção de compra pendente para este anúncio",
|
||||
existing_intention_id: existingIntention.intention_id,
|
||||
can_change_payment_method: true,
|
||||
payment_method_change_count: existingIntention.payment_method_change_count,
|
||||
message: "Use o endpoint PATCH /api/purchase-intentions/[intentionId]/payment-method para trocar o método de pagamento",
|
||||
payment_method_change_count:
|
||||
existingIntention.payment_method_change_count,
|
||||
message:
|
||||
"Use o endpoint PATCH /api/purchase-intentions/[intentionId]/payment-method para trocar o método de pagamento",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
} else {
|
||||
// Já atingiu o limite de trocas
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Você já possui uma intenção de compra pendente para este anúncio e atingiu o limite de trocas de método de pagamento",
|
||||
error:
|
||||
"Você já possui uma intenção de compra pendente para este anúncio e atingiu o limite de trocas de método de pagamento",
|
||||
existing_intention_id: existingIntention.intention_id,
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -197,7 +216,9 @@ export async function POST(
|
||||
payment_method,
|
||||
listing_price_pix: listing.price_pix ? Number(listing.price_pix) : null,
|
||||
listing_price_tc: listing.price_tibia_coins || null,
|
||||
listing_price_gp: listing.price_gold_coins ? Number(listing.price_gold_coins) : null,
|
||||
listing_price_gp: listing.price_gold_coins
|
||||
? Number(listing.price_gold_coins)
|
||||
: null,
|
||||
custom_offer_amount: normalizedOfferValue,
|
||||
custom_offer_currency: normalizedOfferCurrency,
|
||||
},
|
||||
@@ -214,9 +235,10 @@ export async function POST(
|
||||
{
|
||||
success: true,
|
||||
intention,
|
||||
message: "Intenção de compra criada com sucesso. O vendedor será notificado.",
|
||||
message:
|
||||
"Intenção de compra criada com sucesso. O vendedor será notificado.",
|
||||
},
|
||||
{ status: 201 }
|
||||
{ status: 201 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao criar intenção de compra", {
|
||||
@@ -226,7 +248,7 @@ export async function POST(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao criar intenção de compra" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -237,19 +259,19 @@ export async function POST(
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const authResult = await requireAuth(request);
|
||||
if (authResult instanceof NextResponse) return authResult;
|
||||
const auth = authResult;
|
||||
|
||||
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de anúncio inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -258,15 +280,18 @@ export async function GET(
|
||||
if (!listing) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não encontrado" },
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
// Apenas o vendedor pode ver as intenções
|
||||
if (listing.seller_id !== auth.userId) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Você não tem permissão para ver as intenções deste anúncio" },
|
||||
{ status: 403 }
|
||||
{
|
||||
success: false,
|
||||
error: "Você não tem permissão para ver as intenções deste anúncio",
|
||||
},
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -297,7 +322,7 @@ export async function GET(
|
||||
intentions,
|
||||
count: intentions.length,
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao buscar intenções de compra", {
|
||||
@@ -307,8 +332,7 @@ export async function GET(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao buscar intenções de compra" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getListingById, updateListing } from "@/lib/prisma/queries/listings";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/:id/mark-sold
|
||||
@@ -9,7 +9,7 @@ import { getListingById, updateListing } from "@/lib/prisma/queries/listings";
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
// Autenticação obrigatória
|
||||
@@ -24,13 +24,13 @@ export async function POST(
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Anúncio não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,18 +53,22 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Você não tem permissão para marcar este anúncio como vendido",
|
||||
},
|
||||
{ status: 403 }
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar se o anúncio pode ser marcado como vendido
|
||||
if (listing.listing_status !== "active" && listing.listing_status !== "in_escrow") {
|
||||
if (
|
||||
listing.listing_status !== "active" &&
|
||||
listing.listing_status !== "in_escrow"
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Apenas anúncios ativos ou em escrow podem ser marcados como vendidos",
|
||||
error:
|
||||
"Apenas anúncios ativos ou em escrow podem ser marcados como vendidos",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,12 +94,24 @@ export async function POST(
|
||||
// Converter campos para formato esperado
|
||||
const updated = {
|
||||
...updatedListing,
|
||||
price_pix: updatedListing.price_pix ? Number(updatedListing.price_pix) : null,
|
||||
current_bid_amount: updatedListing.current_bid_amount ? Number(updatedListing.current_bid_amount) : null,
|
||||
min_bid_increment: updatedListing.min_bid_increment ? Number(updatedListing.min_bid_increment) : null,
|
||||
initial_price_pix: updatedListing.initial_price_pix ? Number(updatedListing.initial_price_pix) : null,
|
||||
price_gold_coins: updatedListing.price_gold_coins ? Number(updatedListing.price_gold_coins) : null,
|
||||
initial_price_gold_coins: updatedListing.initial_price_gold_coins ? Number(updatedListing.initial_price_gold_coins) : null,
|
||||
price_pix: updatedListing.price_pix
|
||||
? Number(updatedListing.price_pix)
|
||||
: null,
|
||||
current_bid_amount: updatedListing.current_bid_amount
|
||||
? Number(updatedListing.current_bid_amount)
|
||||
: null,
|
||||
min_bid_increment: updatedListing.min_bid_increment
|
||||
? Number(updatedListing.min_bid_increment)
|
||||
: null,
|
||||
initial_price_pix: updatedListing.initial_price_pix
|
||||
? Number(updatedListing.initial_price_pix)
|
||||
: null,
|
||||
price_gold_coins: updatedListing.price_gold_coins
|
||||
? Number(updatedListing.price_gold_coins)
|
||||
: null,
|
||||
initial_price_gold_coins: updatedListing.initial_price_gold_coins
|
||||
? Number(updatedListing.initial_price_gold_coins)
|
||||
: null,
|
||||
seller_character_name: character?.character_name || null,
|
||||
};
|
||||
|
||||
@@ -105,7 +121,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao marcar anúncio como vendido",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,7 +136,7 @@ export async function POST(
|
||||
listing: updated,
|
||||
message: "Anúncio marcado como vendido com sucesso",
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao marcar anúncio como vendido", {
|
||||
@@ -132,8 +148,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao marcar anúncio como vendido",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getListingById, updateListing } from "@/lib/prisma/queries/listings";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/:id/pause
|
||||
@@ -9,7 +9,7 @@ import { getListingById, updateListing } from "@/lib/prisma/queries/listings";
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
// Autenticação obrigatória
|
||||
@@ -24,13 +24,13 @@ export async function POST(
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Anúncio não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Você não tem permissão para pausar este anúncio",
|
||||
},
|
||||
{ status: 403 }
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Apenas anúncios ativos podem ser pausados",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao pausar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export async function POST(
|
||||
listing: updated,
|
||||
message: "Anúncio pausado com sucesso",
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao pausar anúncio", {
|
||||
@@ -106,8 +106,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao pausar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { createNotification } from "@/lib/prisma/queries/notifications";
|
||||
import {
|
||||
createPurchaseIntention,
|
||||
hasPendingIntention,
|
||||
getPendingIntentionByBuyerAndListing,
|
||||
} from "@/lib/prisma/queries/purchase-intentions";
|
||||
import { trackEvent } from "@/lib/prisma/queries/telemetry";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/[id]/purchase
|
||||
@@ -16,7 +15,7 @@ import { trackEvent } from "@/lib/prisma/queries/telemetry";
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const authResult = await requireAuth(request);
|
||||
@@ -25,10 +24,10 @@ export async function POST(
|
||||
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de anúncio inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ export async function POST(
|
||||
if (!payment_method) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Método de pagamento é obrigatório" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +51,7 @@ export async function POST(
|
||||
if (!listing) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não encontrado" },
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ export async function POST(
|
||||
if (listing.seller_id === auth.userId) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Você não pode comprar seu próprio anúncio" },
|
||||
{ status: 403 }
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,15 +67,18 @@ export async function POST(
|
||||
if (listing.listing_status !== "active") {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não está disponível para compra" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Validar se é leilão (leilões não podem ser comprados diretamente, apenas com lances)
|
||||
if (listing.is_auction) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Este item está em leilão. Use a funcionalidade de lance." },
|
||||
{ status: 400 }
|
||||
{
|
||||
success: false,
|
||||
error: "Este item está em leilão. Use a funcionalidade de lance.",
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,7 +101,7 @@ export async function POST(
|
||||
error: `Método de pagamento "${payment_method}" não está disponível para este anúncio`,
|
||||
available_methods: availableMethods,
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,9 +113,10 @@ export async function POST(
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Ofertas personalizadas só são permitidas para métodos monetários (PIX, Tibia Coins ou Gold Coins).",
|
||||
error:
|
||||
"Ofertas personalizadas só são permitidas para métodos monetários (PIX, Tibia Coins ou Gold Coins).",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,14 +125,16 @@ export async function POST(
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Valor da oferta personalizada deve ser numérico e maior que zero.",
|
||||
error:
|
||||
"Valor da oferta personalizada deve ser numérico e maior que zero.",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
normalizedOfferCurrency =
|
||||
custom_offer_currency && ["pix", "tibia_coins", "gold_coins"].includes(custom_offer_currency)
|
||||
custom_offer_currency &&
|
||||
["pix", "tibia_coins", "gold_coins"].includes(custom_offer_currency)
|
||||
? custom_offer_currency
|
||||
: normalizedMethod;
|
||||
|
||||
@@ -140,7 +145,10 @@ export async function POST(
|
||||
}
|
||||
|
||||
// Verificar se já existe uma intenção pendente
|
||||
const existingIntention = await getPendingIntentionByBuyerAndListing(listingId, auth.userId);
|
||||
const existingIntention = await getPendingIntentionByBuyerAndListing(
|
||||
listingId,
|
||||
auth.userId,
|
||||
);
|
||||
if (existingIntention) {
|
||||
// Se já existe e ainda pode trocar o método
|
||||
if (existingIntention.payment_method_change_count < 1) {
|
||||
@@ -148,23 +156,27 @@ export async function POST(
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Você já possui uma intenção de compra pendente para este anúncio",
|
||||
error:
|
||||
"Você já possui uma intenção de compra pendente para este anúncio",
|
||||
existing_intention_id: existingIntention.intention_id,
|
||||
can_change_payment_method: true,
|
||||
payment_method_change_count: existingIntention.payment_method_change_count,
|
||||
message: "Use o endpoint PATCH /api/purchase-intentions/[intentionId]/payment-method para trocar o método de pagamento",
|
||||
payment_method_change_count:
|
||||
existingIntention.payment_method_change_count,
|
||||
message:
|
||||
"Use o endpoint PATCH /api/purchase-intentions/[intentionId]/payment-method para trocar o método de pagamento",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
} else {
|
||||
// Já atingiu o limite de trocas
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Você já possui uma intenção de compra pendente para este anúncio e atingiu o limite de trocas de método de pagamento",
|
||||
error:
|
||||
"Você já possui uma intenção de compra pendente para este anúncio e atingiu o limite de trocas de método de pagamento",
|
||||
existing_intention_id: existingIntention.intention_id,
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -201,7 +213,9 @@ export async function POST(
|
||||
payment_method: normalizedMethod,
|
||||
listing_price_pix: listing.price_pix ? Number(listing.price_pix) : null,
|
||||
listing_price_tc: listing.price_tibia_coins || null,
|
||||
listing_price_gp: listing.price_gold_coins ? Number(listing.price_gold_coins) : null,
|
||||
listing_price_gp: listing.price_gold_coins
|
||||
? Number(listing.price_gold_coins)
|
||||
: null,
|
||||
custom_offer_amount: normalizedOfferValue,
|
||||
custom_offer_currency: normalizedOfferCurrency,
|
||||
},
|
||||
@@ -218,9 +232,10 @@ export async function POST(
|
||||
{
|
||||
success: true,
|
||||
intention,
|
||||
message: "Intenção de compra criada com sucesso. O vendedor será notificado.",
|
||||
message:
|
||||
"Intenção de compra criada com sucesso. O vendedor será notificado.",
|
||||
},
|
||||
{ status: 201 }
|
||||
{ status: 201 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao criar intenção de compra", {
|
||||
@@ -230,8 +245,7 @@ export async function POST(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao criar intenção de compra" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { requireAuth, optionalAuth } from "@/lib/auth/middleware";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { optionalAuth, requireAuth } from "@/lib/auth/middleware";
|
||||
import {
|
||||
deleteListing,
|
||||
getListingById,
|
||||
updateListing,
|
||||
deleteListing,
|
||||
} from "@/lib/prisma/queries/listings";
|
||||
import { hasPendingIntention } from "@/lib/prisma/queries/purchase-intentions";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { updateListingSchema } from "@/lib/validation-schemas";
|
||||
|
||||
/**
|
||||
@@ -15,20 +15,20 @@ import { updateListingSchema } from "@/lib/validation-schemas";
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const auth = await optionalAuth(request);
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,14 +40,13 @@ export async function GET(
|
||||
success: false,
|
||||
error: "Anúncio não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
const hasPendingForUser =
|
||||
auth && auth.userId
|
||||
? await hasPendingIntention(listingId, auth.userId)
|
||||
: false;
|
||||
const hasPendingForUser = auth?.userId
|
||||
? await hasPendingIntention(listingId, auth.userId)
|
||||
: false;
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
@@ -57,7 +56,7 @@ export async function GET(
|
||||
has_pending_intention_for_current_user: hasPendingForUser,
|
||||
},
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao buscar anúncio", {
|
||||
@@ -70,7 +69,7 @@ export async function GET(
|
||||
success: false,
|
||||
error: "Erro ao buscar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -81,7 +80,7 @@ export async function GET(
|
||||
*/
|
||||
export async function PATCH(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
// Autenticação obrigatória
|
||||
@@ -96,13 +95,13 @@ export async function PATCH(
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,7 +114,7 @@ export async function PATCH(
|
||||
success: false,
|
||||
error: "Anúncio não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -125,7 +124,7 @@ export async function PATCH(
|
||||
success: false,
|
||||
error: "Você não tem permissão para editar este anúncio",
|
||||
},
|
||||
{ status: 403 }
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -146,7 +145,7 @@ export async function PATCH(
|
||||
error: "Dados de entrada inválidos",
|
||||
details: errors,
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,16 +158,37 @@ export async function PATCH(
|
||||
price_tibia_coins?: number;
|
||||
price_gold_coins?: number;
|
||||
description?: string;
|
||||
listing_status?: "active" | "sold" | "cancelled" | "in_escrow" | "expired";
|
||||
listing_status?:
|
||||
| "active"
|
||||
| "sold"
|
||||
| "cancelled"
|
||||
| "in_escrow"
|
||||
| "expired";
|
||||
current_bid_amount?: number;
|
||||
current_bidder_id?: number;
|
||||
} = {
|
||||
...(validatedData.quantity !== undefined && { quantity: validatedData.quantity }),
|
||||
...(validatedData.price_pix !== null && validatedData.price_pix !== undefined && { price_pix: validatedData.price_pix }),
|
||||
...(validatedData.price_tibia_coins !== null && validatedData.price_tibia_coins !== undefined && { price_tibia_coins: validatedData.price_tibia_coins }),
|
||||
...(validatedData.price_gold_coins !== null && validatedData.price_gold_coins !== undefined && { price_gold_coins: validatedData.price_gold_coins }),
|
||||
...(validatedData.description !== null && validatedData.description !== undefined && { description: validatedData.description }),
|
||||
...(validatedData.listing_status !== undefined && { listing_status: validatedData.listing_status }),
|
||||
...(validatedData.quantity !== undefined && {
|
||||
quantity: validatedData.quantity,
|
||||
}),
|
||||
...(validatedData.price_pix !== null &&
|
||||
validatedData.price_pix !== undefined && {
|
||||
price_pix: validatedData.price_pix,
|
||||
}),
|
||||
...(validatedData.price_tibia_coins !== null &&
|
||||
validatedData.price_tibia_coins !== undefined && {
|
||||
price_tibia_coins: validatedData.price_tibia_coins,
|
||||
}),
|
||||
...(validatedData.price_gold_coins !== null &&
|
||||
validatedData.price_gold_coins !== undefined && {
|
||||
price_gold_coins: validatedData.price_gold_coins,
|
||||
}),
|
||||
...(validatedData.description !== null &&
|
||||
validatedData.description !== undefined && {
|
||||
description: validatedData.description,
|
||||
}),
|
||||
...(validatedData.listing_status !== undefined && {
|
||||
listing_status: validatedData.listing_status,
|
||||
}),
|
||||
};
|
||||
|
||||
// Atualizar anúncio
|
||||
@@ -180,7 +200,7 @@ export async function PATCH(
|
||||
success: false,
|
||||
error: "Erro ao atualizar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,7 +215,7 @@ export async function PATCH(
|
||||
success: true,
|
||||
listing: updatedListing,
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao atualizar anúncio", {
|
||||
@@ -208,7 +228,7 @@ export async function PATCH(
|
||||
success: false,
|
||||
error: "Erro ao atualizar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -219,7 +239,7 @@ export async function PATCH(
|
||||
*/
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
// Autenticação obrigatória
|
||||
@@ -234,13 +254,13 @@ export async function DELETE(
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -253,7 +273,7 @@ export async function DELETE(
|
||||
success: false,
|
||||
error: "Anúncio não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -263,7 +283,7 @@ export async function DELETE(
|
||||
success: false,
|
||||
error: "Você não tem permissão para cancelar este anúncio",
|
||||
},
|
||||
{ status: 403 }
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -276,7 +296,7 @@ export async function DELETE(
|
||||
success: false,
|
||||
error: "Erro ao cancelar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -291,7 +311,7 @@ export async function DELETE(
|
||||
success: true,
|
||||
message: "Anúncio cancelado com sucesso",
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao cancelar anúncio", {
|
||||
@@ -304,8 +324,7 @@ export async function DELETE(
|
||||
success: false,
|
||||
error: "Erro ao cancelar anúncio",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { getMainCharacterByUserId } from "@/lib/prisma/queries/characters";
|
||||
import { getListingById } from "@/lib/prisma/queries/listings";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* GET /api/listings/[id]/seller
|
||||
* Retorna informações do vendedor (nome do personagem)
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "ID de anúncio inválido" },
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,12 +25,15 @@ export async function GET(
|
||||
if (!listing) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Anúncio não encontrado" },
|
||||
{ status: 404 }
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
// Buscar personagem principal do vendedor no servidor do listing
|
||||
const character = await getMainCharacterByUserId(listing.seller_id, listing.server_name);
|
||||
const character = await getMainCharacterByUserId(
|
||||
listing.seller_id,
|
||||
listing.server_name,
|
||||
);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
@@ -41,7 +44,7 @@ export async function GET(
|
||||
server_name: listing.server_name,
|
||||
},
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao buscar informações do vendedor", {
|
||||
@@ -51,8 +54,7 @@ export async function GET(
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: "Erro ao buscar informações do vendedor" },
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { incrementViews } from "@/lib/prisma/queries/listings";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
|
||||
/**
|
||||
* POST /api/listings/[id]/views
|
||||
@@ -8,20 +8,20 @@ import { incrementViews } from "@/lib/prisma/queries/listings";
|
||||
* Não requer autenticação (público)
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const listingId = parseInt(id, 10);
|
||||
|
||||
if (isNaN(listingId)) {
|
||||
if (Number.isNaN(listingId)) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "ID de anúncio inválido",
|
||||
},
|
||||
{ status: 400 }
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function POST(
|
||||
success: true,
|
||||
message: "View incrementada",
|
||||
},
|
||||
{ status: 200 }
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao incrementar views", {
|
||||
@@ -46,8 +46,7 @@ export async function POST(
|
||||
success: false,
|
||||
error: "Erro ao incrementar views",
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user