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, }