180 lines
4.9 KiB
JavaScript
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,
|
|
}
|