HotTRDealsBackend/services/profile.service.js
2026-02-09 21:47:55 +00:00

180 lines
4.9 KiB
JavaScript

const bcrypt = require("bcryptjs")
const userDb = require("../db/user.db")
const notificationDb = require("../db/notification.db")
const refreshTokenDb = require("../db/refreshToken.db")
const { queueNotificationReadAll } = require("./redis/dbSync.service")
const { normalizeMediaPath } = require("../utils/mediaPath")
function assertPositiveInt(v, name = "id") {
const n = Number(v)
if (!Number.isInteger(n) || n <= 0) throw new Error(`Gecersiz ${name}.`)
return n
}
async function updateAvatarUrl(userId, url) {
const id = assertPositiveInt(userId, "userId")
if (!url || typeof url !== "string" || !url.trim()) {
throw new Error("Gecersiz URL.")
}
const normalizedAvatarUrl = normalizeMediaPath(url)
if (!normalizedAvatarUrl) throw new Error("Gecersiz URL.")
const select = { id: true, username: true, avatarUrl: true }
return userDb.updateUser({ id }, { avatarUrl: normalizedAvatarUrl }, { select })
}
async function getUserProfile(userId) {
const id = assertPositiveInt(userId, "userId")
const select = {
id: true,
username: true,
avatarUrl: true,
createdAt: true,
userBadges: {
orderBy: { earnedAt: "desc" },
select: {
earnedAt: true,
badge: { select: { id: true, name: true, iconUrl: true, description: true } },
},
},
notifications: {
orderBy: { createdAt: "desc" },
take: 3,
select: {
id: true,
message: true,
type: true,
extras: true,
createdAt: true,
readAt: true,
},
},
}
const user = await userDb.findUser({ id }, { select })
if (!user) return user
const formatDate = (value) => (value instanceof Date ? value.toISOString() : value ?? null)
const notifications = Array.isArray(user.notifications)
? user.notifications.map((n) => ({
...n,
extras: n.extras ?? null,
createdAt: formatDate(n.createdAt),
readAt: formatDate(n.readAt),
unread: n.readAt == null,
}))
: []
const badges = Array.isArray(user.userBadges)
? user.userBadges.map((item) => ({
badge: item.badge
? {
id: item.badge.id,
name: item.badge.name,
iconUrl: normalizeMediaPath(item.badge.iconUrl) ?? null,
description: item.badge.description ?? null,
}
: null,
earnedAt: formatDate(item.earnedAt),
}))
: []
return {
id: user.id,
username: user.username,
avatarUrl: normalizeMediaPath(user.avatarUrl) ?? null,
createdAt: formatDate(user.createdAt),
notifications,
badges,
}
}
async function markAllNotificationsRead(userId) {
const id = assertPositiveInt(userId, "userId")
const readAt = new Date().toISOString()
await queueNotificationReadAll({ userId: id, readAt })
return { queued: true, readAt }
}
async function getUserNotificationsPage(userId, page = 1, limit = 10) {
const id = assertPositiveInt(userId, "userId")
const pageNumber = assertPositiveInt(page, "page")
const take = assertPositiveInt(limit, "limit")
const skip = (pageNumber - 1) * take
const [total, notifications] = await Promise.all([
notificationDb.countNotifications({ userId: id }),
notificationDb.findNotifications(
{ userId: id },
{
orderBy: { createdAt: "desc" },
skip,
take,
select: {
id: true,
message: true,
type: true,
extras: true,
createdAt: true,
readAt: true,
},
}
),
])
const formatDate = (value) => (value instanceof Date ? value.toISOString() : value ?? null)
const results = Array.isArray(notifications)
? notifications.map((n) => ({
...n,
extras: n.extras ?? null,
createdAt: formatDate(n.createdAt),
readAt: formatDate(n.readAt),
unread: n.readAt == null,
}))
: []
const totalPages = Math.ceil(total / take)
return {
page: pageNumber,
total,
totalPages,
results,
}
}
async function changePassword(userId, { currentPassword, newPassword }) {
const id = assertPositiveInt(userId, "userId")
if (!currentPassword || typeof currentPassword !== "string") {
throw new Error("Mevcut sifre gerekli.")
}
if (!newPassword || typeof newPassword !== "string") {
throw new Error("Yeni sifre gerekli.")
}
const user = await userDb.findUser(
{ id },
{ select: { id: true, passwordHash: true } }
)
if (!user) throw new Error("Kullanici bulunamadi.")
const isMatch = await bcrypt.compare(currentPassword, user.passwordHash)
if (!isMatch) throw new Error("Mevcut sifre hatali.")
const passwordHash = await bcrypt.hash(newPassword, 10)
await userDb.updateUser({ id }, { passwordHash })
await refreshTokenDb.revokeAllUserRefreshTokens(id)
return { message: "Sifre guncellendi." }
}
module.exports = {
updateAvatarUrl,
getUserProfile,
markAllNotificationsRead,
getUserNotificationsPage,
changePassword,
}