chore: basic abilities added

This commit is contained in:
cureb 2025-11-05 14:56:26 +00:00
parent d9ca95470f
commit e966158d37
12 changed files with 361 additions and 113 deletions

3
db/client.js Normal file
View File

@ -0,0 +1,3 @@
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()
module.exports = prisma

28
db/comment.db.js Normal file
View File

@ -0,0 +1,28 @@
const prisma = require("./client")
async function findComments(where, options = {}) {
return prisma.comment.findMany({
where,
include: options.include || undefined,
select: options.select || undefined,
orderBy: options.orderBy || { createdAt: "desc" },
})
}
async function createComment(data, options = {}) {
return prisma.comment.create({
data,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function deleteComment(where) {
return prisma.comment.delete({ where })
}
module.exports = {
findComments,
createComment,
deleteComment,
}

82
db/deal.db.js Normal file
View File

@ -0,0 +1,82 @@
const prisma = require("./client")
async function findDeals(where = {}, options = {}) {
return prisma.deal.findMany({
where,
include: options.include || undefined,
select: options.select || undefined,
orderBy: options.orderBy || { createdAt: "desc" },
skip: options.skip || 0,
take: options.take || undefined,
})
}
async function findDeal(where, options = {}) {
return prisma.deal.findUnique({
where,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function createDeal(data, options = {}) {
return prisma.deal.create({
data,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function updateDeal(where, data, options = {}) {
return prisma.deal.update({
where,
data,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function countDeals(where = {}) {
return prisma.deal.count({ where })
}
async function findVotes(where = {}, options = {}) {
return prisma.dealVote.findMany({
where,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function createVote(data, options = {}) {
return prisma.dealVote.create({
data,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function updateVote(where, data, options = {}) {
return prisma.dealVote.update({
where,
data,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function countVotes(where = {}) {
return prisma.dealVote.count({ where })
}
module.exports = {
findDeals,
findDeal,
createDeal,
updateDeal,
countDeals,
findVotes,
createVote,
updateVote,
countVotes,
}

24
db/user.db.js Normal file
View File

@ -0,0 +1,24 @@
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()
async function findUser(where, options = {}) {
return prisma.user.findUnique({
where,
include: options.include || undefined,
select: options.select || undefined,
})
}
async function updateUser(where, data, options = {}) {
return prisma.user.update({
where,
data,
include: options.include || undefined,
select: options.select || undefined,
})
}
module.exports = {
findUser,
updateUser,
}

View File

@ -65,7 +65,7 @@ router.get("/me", authMiddleware, async (req, res) => {
try { try {
const user = await prisma.user.findUnique({ const user = await prisma.user.findUnique({
where: { id: req.user.userId }, where: { id: req.user.userId },
select: { id: true, username: true, email: true }, select: { id: true, username: true, email: true,avatarUrl:true },
}) })
if (!user) return res.status(404).json({ error: "Kullanıcı bulunamadı" }) if (!user) return res.status(404).json({ error: "Kullanıcı bulunamadı" })

View File

@ -1,6 +1,6 @@
const express = require("express") const express = require("express")
const router = express.Router() const router = express.Router()
const { getAllDeals, getDealById, createDeal } = require("../../services/deal/dealService") const { getAllDeals, getDealById, createDeal,searchDeals } = require("../../services/deal/dealService")
const authMiddleware = require("../../middleware/authMiddleware") const authMiddleware = require("../../middleware/authMiddleware")
router.get("/", async (req, res) => { router.get("/", async (req, res) => {
@ -15,6 +15,25 @@ router.get("/", async (req, res) => {
} }
}) })
router.get("/search", async (req, res) => {
try {
const query = req.query.q || ""
const page = Number(req.query.page) || 1
const limit = 10
if (!query.trim()) {
return res.json({ results: [], total: 0, totalPages: 0, page })
}
const data = await searchDeals(query, page, limit)
res.json(data)
} catch (e) {
console.error(e)
res.status(500).json({ error: "Sunucu hatası" })
}
})
router.get("/:id", async (req, res) => { router.get("/:id", async (req, res) => {
try { try {
const deal = await getDealById(req.params.id) const deal = await getDealById(req.params.id)

63
routes/user/userRoutes.js Normal file
View File

@ -0,0 +1,63 @@
// routes/profileRoutes.js
const express = require("express")
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()
const router = express.Router()
// Belirli bir kullanıcının profil detayları
router.get("/:userName", async (req, res) => {
console.log("İstek geldi:", req.params.userName)
try {
const username = req.params.userName
const user = await prisma.user.findUnique({
where: { username: username },
select: {
id: true,
username: true,
avatarUrl: true,
createdAt: true,
},
})
if (!user) return res.status(404).json({ message: "Kullanıcı bulunamadı." })
// Kullanıcının paylaştığı fırsatlar
const deals = await prisma.deal.findMany({
where: { userId: user.id },
orderBy: { createdAt: "desc" },
select: {
id: true,
title: true,
price: true,
createdAt: true,
score: true,
images: {
orderBy: { order: "asc" }, // küçük order en önde
take: 1, // sadece ilk görsel
select: { imageUrl: true },
},
},
})
// Kullanıcının yaptığı yorumlar
const comments = await prisma.comment.findMany({
where: { userId:user.id },
orderBy: { createdAt: "desc" },
select: {
id: true,
text: true,
dealId: true,
createdAt: true,
deal: { select: { title: true } },
},
})
res.json({ user, deals, comments })
} catch (err) {
console.error(err)
res.status(500).json({ message: "Profil bilgileri alınamadı.", error: err.message })
}
})
module.exports = router

View File

@ -2,23 +2,26 @@ const express = require("express")
const cors = require("cors") const cors = require("cors")
require("dotenv").config() require("dotenv").config()
const userRoutes = require("./routes/userRoutes") const userRoutesneedRefactor = require("./routes/userRoutes")
const dealRoutes = require("./routes/deal/dealRoutes") const dealRoutes = require("./routes/deal/dealRoutes")
const authRoutes = require("./routes/authRoutes") const authRoutes = require("./routes/authRoutes")
const dealVoteRoutes = require("./routes/deal/voteRoutes") const dealVoteRoutes = require("./routes/deal/voteRoutes")
const commentRoutes = require("./routes/deal/commentRoutes") const commentRoutes = require("./routes/deal/commentRoutes")
const accountSettingsRoutes = require("./routes/account/accountSettingsRoutes") const accountSettingsRoutes = require("./routes/account/accountSettingsRoutes")
const userRoutes = require("./routes/user/userRoutes")
const app = express() const app = express()
app.use(cors()) app.use(cors())
app.use(express.json()) app.use(express.json())
app.use(express.urlencoded({ extended: true })) app.use(express.urlencoded({ extended: true }))
app.use("/api/users", userRoutes) app.use("/api/users", userRoutesneedRefactor)
app.use("/api/deals", dealRoutes) app.use("/api/deals", dealRoutes)
app.use("/api/auth", authRoutes) app.use("/api/auth", authRoutes)
app.use("/api/deal-votes", dealVoteRoutes) app.use("/api/deal-votes", dealVoteRoutes)
app.use("/api/comments", commentRoutes) app.use("/api/comments", commentRoutes)
app.use("/api/account", accountSettingsRoutes) app.use("/api/account", accountSettingsRoutes)
app.use("/api/user", userRoutes)
app.listen(3000, () => console.log("Server running on http://localhost:3000")) app.listen(3000, () => console.log("Server running on http://localhost:3000"))

View File

@ -1,6 +1,5 @@
// services/deal/commentService.js const dealDB = require("../../db/deal.db")
const { PrismaClient } = require("@prisma/client") const commentDB = require("../../db/comment.db")
const prisma = new PrismaClient()
function assertPositiveInt(v, name = "id") { function assertPositiveInt(v, name = "id") {
const n = Number(v) const n = Number(v)
@ -11,56 +10,51 @@ function assertPositiveInt(v, name = "id") {
async function getCommentsByDealId(dealId) { async function getCommentsByDealId(dealId) {
const id = assertPositiveInt(dealId, "dealId") const id = assertPositiveInt(dealId, "dealId")
// Deal mevcut mu kontrol et const deal = await dealDB.findDeal({ id })
const deal = await prisma.deal.findUnique({ where: { id } })
if (!deal) throw new Error("Deal bulunamadı.") if (!deal) throw new Error("Deal bulunamadı.")
return prisma.comment.findMany({ const include = { user: { select: { username: true, avatarUrl: true } } }
where: { dealId: id }, return commentDB.findComments({ dealId: id }, { include })
include: { user: { select: { username: true } } },
orderBy: { createdAt: "desc" },
})
} }
async function createComment({ dealId, userId, text }) { async function createComment({ dealId, userId, text }) {
// Basit doğrulamalar
const dId = assertPositiveInt(dealId, "dealId") const dId = assertPositiveInt(dealId, "dealId")
const uId = assertPositiveInt(userId, "userId") const uId = assertPositiveInt(userId, "userId")
if (!text || typeof text !== "string" || !text.trim()) if (!text || typeof text !== "string" || !text.trim())
throw new Error("Yorum boş olamaz.") throw new Error("Yorum boş olamaz.")
// Deal var mı kontrol et const deal = await dealDB.findDeal({ id: dId })
const deal = await prisma.deal.findUnique({ where: { id: dId } })
if (!deal) throw new Error("Deal bulunamadı.") if (!deal) throw new Error("Deal bulunamadı.")
// (Opsiyonel) Kullanıcı var mı kontrolü (ek güvenlik) const include = { user: { select: { username: true, avatarUrl: true } } }
const user = await prisma.user.findUnique({ where: { id: uId } }) const data = {
if (!user) throw new Error("Kullanıcı bulunamadı.") text: text.trim(),
userId: uId,
dealId: dId,
}
const comment = await prisma.comment.create({ return commentDB.createComment(data, { include })
data: {
text: text.trim(),
userId: uId,
dealId: dId,
},
include: {
user: { select: { username: true } },
},
})
return comment
} }
async function deleteComment(commentId, userId) { async function deleteComment(commentId, userId) {
const cId = assertPositiveInt(commentId, "commentId") const cId = assertPositiveInt(commentId, "commentId")
const uId = assertPositiveInt(userId, "userId") const uId = assertPositiveInt(userId, "userId")
const comment = await prisma.comment.findUnique({ where: { id: cId } }) const comments = await commentDB.findComments(
if (!comment) throw new Error("Yorum bulunamadı.") { id: cId },
if (comment.userId !== uId) throw new Error("Bu yorumu silme yetkin yok.") { select: { userId: true } }
)
await prisma.comment.delete({ where: { id: cId } }) if (!comments || comments.length === 0) throw new Error("Yorum bulunamadı.")
if (comments[0].userId !== uId) throw new Error("Bu yorumu silme yetkin yok.")
await commentDB.deleteComment({ id: cId })
return { message: "Yorum silindi." } return { message: "Yorum silindi." }
} }
module.exports = { getCommentsByDealId, createComment, deleteComment } module.exports = {
getCommentsByDealId,
createComment,
deleteComment,
}

View File

@ -1,12 +1,16 @@
const { PrismaClient } = require("@prisma/client") const dealDB = require("../../db/deal.db")
const prisma = new PrismaClient()
async function searchDeals(query, page = 1, limit = 10) {
async function getAllDeals(page = 1, limit = 10) {
const skip = (page - 1) * limit const skip = (page - 1) * limit
const where = {
OR: [
{ title: { contains: query, mode: "insensitive" } },
{ description: { contains: query, mode: "insensitive" } },
],
}
const [deals, total] = await Promise.all([ const [deals, total] = await Promise.all([
prisma.deal.findMany({ dealDB.findDeals(where, {
skip, skip,
take: limit, take: limit,
orderBy: { createdAt: "desc" }, orderBy: { createdAt: "desc" },
@ -14,12 +18,40 @@ async function getAllDeals(page = 1, limit = 10) {
user: { select: { username: true } }, user: { select: { username: true } },
images: { images: {
orderBy: { order: "asc" }, orderBy: { order: "asc" },
take: 1, // sadece kapak fotoğrafı take: 1,
select: { imageUrl: true }, select: { imageUrl: true },
}, },
}, },
}), }),
prisma.deal.count(), dealDB.countDeals(where),
])
return {
page,
total,
totalPages: Math.ceil(total / limit),
results: deals,
}
}
async function getAllDeals(page = 1, limit = 10) {
const skip = (page - 1) * limit
const [deals, total] = await Promise.all([
dealDB.findDeals({}, {
skip,
take: limit,
orderBy: { createdAt: "desc" },
include: {
user: { select: { username: true } },
images: {
orderBy: { order: "asc" },
take: 1,
select: { imageUrl: true },
},
},
}),
dealDB.countDeals(),
]) ])
return { return {
@ -31,76 +63,71 @@ async function getAllDeals(page = 1, limit = 10) {
} }
async function getDealById(id) { async function getDealById(id) {
return prisma.deal.findUnique({ return dealDB.findDeal(
where: { id: Number(id) }, { id: Number(id) },
include: { {
user: { select: { username: true } }, include: {
images: { user: { select: { username: true } },
orderBy: { order: "asc" }, // tüm fotoğrafları sırayla getir images: {
select: { id: true, imageUrl: true, order: true }, orderBy: { order: "asc" },
select: { id: true, imageUrl: true, order: true },
},
}, },
}, }
}) )
} }
async function createDeal(data, userId) { async function createDeal(data, userId) {
return prisma.deal.create({ const payload = {
data: { title: data.title,
title: data.title, description: data.description,
description: data.description, url: data.url,
url: data.url, price: data.price,
imageUrl: data.imageUrl, user: { connect: { id: userId } },
price: data.price, images: data.images?.length
user: { connect: { id: userId } }, // JWTden gelen userId burada bağlanır ? {
}, create: data.images.map((imgUrl, index) => ({
}) imageUrl: imgUrl,
order: index,
})),
}
: undefined,
}
return dealDB.createDeal(payload, { include: { images: true } })
} }
async function voteDeal(dealId, userId, voteType) { async function voteDeal(dealId, userId, voteType) {
if (!dealId || !userId || !voteType) throw new Error("Eksik veri") if (!dealId || !userId || !voteType) throw new Error("Eksik veri")
const existingVote = await prisma.dealVote.findFirst({ const existingVote = await dealDB.findVotes({ dealId, userId })
where: { dealId, userId }, const vote = existingVote[0]
})
if (existingVote) { if (vote) {
await prisma.dealVote.update({ await dealDB.updateVote({ id: vote.id }, { voteType })
where: { id: existingVote.id },
data: { voteType },
})
} else { } else {
await prisma.dealVote.create({ await dealDB.createVote({ dealId, userId, voteType })
data: { dealId, userId, voteType },
})
} }
const upvotes = await prisma.dealVote.count({ const upvotes = await dealDB.countVotes({ dealId, voteType: "UP" })
where: { dealId, voteType: "UP" }, const downvotes = await dealDB.countVotes({ dealId, voteType: "DOWN" })
})
const downvotes = await prisma.dealVote.count({
where: { dealId, voteType: "DOWN" },
})
const score = upvotes - downvotes const score = upvotes - downvotes
await prisma.deal.update({ await dealDB.updateDeal({ id: dealId }, { score })
where: { id: dealId },
data: { score },
})
return score return score
} }
async function getVotes(dealId) { async function getVotes(dealId) {
const upvotes = await prisma.dealVote.count({ const upvotes = await dealDB.countVotes({ dealId: Number(dealId), voteType: "UP" })
where: { dealId: Number(dealId), voteType: "UP" }, const downvotes = await dealDB.countVotes({ dealId: Number(dealId), voteType: "DOWN" })
})
const downvotes = await prisma.dealVote.count({
where: { dealId: Number(dealId), voteType: "DOWN" },
})
return { upvotes, downvotes, score: upvotes - downvotes } return { upvotes, downvotes, score: upvotes - downvotes }
} }
module.exports = { getAllDeals, getDealById, createDeal, voteDeal, getVotes } module.exports = {
getAllDeals,
getDealById,
createDeal,
voteDeal,
getVotes,
searchDeals,
}

View File

@ -1,25 +1,30 @@
const { PrismaClient } = require("@prisma/client") const userDb = require("../../db/user.db")
const prisma = new PrismaClient()
function assertPositiveInt(v, name = "id") {
const n = Number(v)
if (!Number.isInteger(n) || n <= 0) throw new Error(`Geçersiz ${name}.`)
return n
}
async function updateAvatarUrl(userId, url) { async function updateAvatarUrl(userId, url) {
return await prisma.user.update({ const id = assertPositiveInt(userId, "userId")
where: { id: userId }, if (!url || typeof url !== "string" || !url.trim())
data: { avatarUrl: url }, throw new Error("Geçersiz URL.")
select: { id: true, username: true, avatarUrl: true },
}) const select = { id: true, username: true, avatarUrl: true }
return userDb.updateUser({ id }, { avatarUrl: url.trim() }, { select })
} }
async function getUserProfile(userId) { async function getUserProfile(userId) {
return await prisma.user.findUnique({ const id = assertPositiveInt(userId, "userId")
where: { id: userId }, const select = {
select: { id: true,
id: true, username: true,
username: true, email: true,
email: true, avatarUrl: true,
avatarUrl: true, createdAt: true,
createdAt: true, }
}, return userDb.findUser({ id }, { select })
})
} }
module.exports = { module.exports = {

View File

@ -5,7 +5,7 @@ const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY
async function uploadProfileImage(userId, file) { async function uploadProfileImage(userId, file) {
const path = `avatars/${userId}_${Date.now()}.jpg` const path = `avatars/${userId}_${Date.now()}.jpg`
const { data, error } = await supabase.storage const { data, error } = await supabase.storage
.from("avatars") .from("deal")
.upload(path, file.data, { .upload(path, file.data, {
contentType: "image/jpeg", contentType: "image/jpeg",
upsert: true, upsert: true,