hPiBot openclaw ve Opencode ilk versiyonu[B

This commit is contained in:
2026-03-04 05:17:51 +03:00
commit d49edbfba3
75 changed files with 42117 additions and 0 deletions

View File

@@ -0,0 +1,541 @@
const express = require('express');
const { body, validationResult, param, query } = require('express-validator');
const { asyncHandler, formatValidationErrors } = require('../middleware/errorHandler');
const { authenticateToken, requireAdmin } = require('../middleware/auth');
const router = express.Router();
/**
* Kullanıcının bildirimlerini getir
* GET /api/notifications
*/
router.get('/', [
authenticateToken,
query('page').optional().isInt({ min: 1 }).withMessage('Sayfa numarası pozitif bir sayı olmalı'),
query('limit').optional().isInt({ min: 1, max: 50 }).withMessage('Limit 1-50 arasında olmalı'),
query('unreadOnly').optional().isBoolean().withMessage('unreadOnly boolean değer olmalı'),
query('type').optional().isIn(['list_invite', 'item_added', 'item_removed', 'item_purchased', 'list_shared', 'system']).withMessage('Geçerli bir bildirim türü seçin')
], asyncHandler(async (req, res) => {
// Validation hatalarını kontrol et
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: 'Girilen bilgilerde hatalar var',
errors: formatValidationErrors(errors)
});
}
const {
page = 1,
limit = 20,
unreadOnly = false,
type
} = req.query;
const skip = (page - 1) * limit;
// Filtreleme koşulları
const whereCondition = {
userId: req.user.id
};
if (unreadOnly) {
whereCondition.isRead = false;
}
if (type) {
whereCondition.type = type;
}
const [notifications, totalCount, unreadCount] = await Promise.all([
req.prisma.notification.findMany({
where: whereCondition,
include: {
relatedList: {
select: {
id: true,
name: true
}
},
relatedUser: {
select: {
id: true,
username: true,
firstName: true,
lastName: true,
avatar: true
}
}
},
orderBy: {
createdAt: 'desc'
},
skip: parseInt(skip),
take: parseInt(limit)
}),
req.prisma.notification.count({ where: whereCondition }),
req.prisma.notification.count({
where: {
userId: req.user.id,
isRead: false
}
})
]);
res.json({
success: true,
data: {
notifications,
pagination: {
currentPage: parseInt(page),
totalPages: Math.ceil(totalCount / limit),
totalCount,
hasNext: skip + parseInt(limit) < totalCount,
hasPrev: page > 1
},
unreadCount
}
});
}));
/**
* Okunmamış bildirim sayısını getir
* GET /api/notifications/unread-count
*/
router.get('/unread-count', [
authenticateToken
], asyncHandler(async (req, res) => {
const unreadCount = await req.prisma.notification.count({
where: {
userId: req.user.id,
isRead: false
}
});
res.json({
success: true,
data: { unreadCount }
});
}));
/**
* Bildirimi okundu olarak işaretle
* PUT /api/notifications/:notificationId/read
*/
router.put('/:notificationId/read', [
authenticateToken,
param('notificationId').isUUID().withMessage('Geçerli bir bildirim ID\'si girin')
], asyncHandler(async (req, res) => {
const { notificationId } = req.params;
const notification = await req.prisma.notification.findUnique({
where: {
id: notificationId,
userId: req.user.id
}
});
if (!notification) {
return res.status(404).json({
success: false,
message: 'Bildirim bulunamadı'
});
}
const updatedNotification = await req.prisma.notification.update({
where: { id: notificationId },
data: {
isRead: true,
readAt: new Date()
}
});
res.json({
success: true,
message: 'Bildirim okundu olarak işaretlendi',
data: { notification: updatedNotification }
});
}));
/**
* Tüm bildirimleri okundu olarak işaretle
* PUT /api/notifications/mark-all-read
*/
router.put('/mark-all-read', [
authenticateToken
], asyncHandler(async (req, res) => {
const result = await req.prisma.notification.updateMany({
where: {
userId: req.user.id,
isRead: false
},
data: {
isRead: true,
readAt: new Date()
}
});
res.json({
success: true,
message: `${result.count} bildirim okundu olarak işaretlendi`,
data: { updatedCount: result.count }
});
}));
/**
* Bildirimi sil
* DELETE /api/notifications/:notificationId
*/
router.delete('/:notificationId', [
authenticateToken,
param('notificationId').isUUID().withMessage('Geçerli bir bildirim ID\'si girin')
], asyncHandler(async (req, res) => {
const { notificationId } = req.params;
const notification = await req.prisma.notification.findUnique({
where: {
id: notificationId,
userId: req.user.id
}
});
if (!notification) {
return res.status(404).json({
success: false,
message: 'Bildirim bulunamadı'
});
}
await req.prisma.notification.delete({
where: { id: notificationId }
});
res.json({
success: true,
message: 'Bildirim başarıyla silindi'
});
}));
/**
* Okunmuş bildirimleri temizle
* DELETE /api/notifications/clear-read
*/
router.delete('/clear-read', [
authenticateToken
], asyncHandler(async (req, res) => {
const result = await req.prisma.notification.deleteMany({
where: {
userId: req.user.id,
isRead: true
}
});
res.json({
success: true,
message: `${result.count} okunmuş bildirim temizlendi`,
data: { deletedCount: result.count }
});
}));
/**
* Tüm bildirimleri temizle
* DELETE /api/notifications/clear-all
*/
router.delete('/clear-all', [
authenticateToken
], asyncHandler(async (req, res) => {
const result = await req.prisma.notification.deleteMany({
where: {
userId: req.user.id
}
});
res.json({
success: true,
message: `${result.count} bildirim temizlendi`,
data: { deletedCount: result.count }
});
}));
/**
* Bildirim ayarlarını getir
* GET /api/notifications/settings
*/
router.get('/settings', [
authenticateToken
], asyncHandler(async (req, res) => {
const settings = await req.prisma.setting.findMany({
where: {
userId: req.user.id,
key: {
startsWith: 'notification_'
}
}
});
// Varsayılan ayarlar
const defaultSettings = {
notification_list_invite: 'true',
notification_item_added: 'true',
notification_item_removed: 'true',
notification_item_purchased: 'true',
notification_list_shared: 'true',
notification_system: 'true',
notification_push_enabled: 'true',
notification_email_enabled: 'false'
};
// Kullanıcı ayarlarını varsayılanlarla birleştir
const userSettings = {};
settings.forEach(setting => {
userSettings[setting.key] = setting.value;
});
const finalSettings = { ...defaultSettings, ...userSettings };
res.json({
success: true,
data: { settings: finalSettings }
});
}));
/**
* Bildirim ayarlarını güncelle
* PUT /api/notifications/settings
*/
router.put('/settings', [
authenticateToken,
body('settings').isObject().withMessage('Ayarlar obje formatında olmalı')
], asyncHandler(async (req, res) => {
// Validation hatalarını kontrol et
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: 'Girilen bilgilerde hatalar var',
errors: formatValidationErrors(errors)
});
}
const { settings } = req.body;
// Geçerli ayar anahtarları
const validKeys = [
'notification_list_invite',
'notification_item_added',
'notification_item_removed',
'notification_item_purchased',
'notification_list_shared',
'notification_system',
'notification_push_enabled',
'notification_email_enabled'
];
// Geçersiz anahtarları filtrele
const validSettings = {};
Object.keys(settings).forEach(key => {
if (validKeys.includes(key)) {
validSettings[key] = settings[key].toString();
}
});
if (Object.keys(validSettings).length === 0) {
return res.status(400).json({
success: false,
message: 'Geçerli ayar bulunamadı'
});
}
// Ayarları güncelle veya oluştur
const updatePromises = Object.entries(validSettings).map(([key, value]) =>
req.prisma.setting.upsert({
where: {
userId_key: {
userId: req.user.id,
key
}
},
update: { value },
create: {
userId: req.user.id,
key,
value
}
})
);
await Promise.all(updatePromises);
res.json({
success: true,
message: 'Bildirim ayarları güncellendi',
data: { settings: validSettings }
});
}));
/**
* Sistem bildirimi gönder (Admin)
* POST /api/notifications/system
*/
router.post('/system', [
authenticateToken,
requireAdmin,
body('title')
.trim()
.isLength({ min: 1, max: 100 })
.withMessage('Başlık 1-100 karakter arasında olmalı'),
body('message')
.trim()
.isLength({ min: 1, max: 500 })
.withMessage('Mesaj 1-500 karakter arasında olmalı'),
body('targetUsers')
.optional()
.isArray()
.withMessage('Hedef kullanıcılar dizi formatında olmalı'),
body('targetUsers.*')
.optional()
.isUUID()
.withMessage('Geçerli kullanıcı ID\'leri girin')
], asyncHandler(async (req, res) => {
// Validation hatalarını kontrol et
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: 'Girilen bilgilerde hatalar var',
errors: formatValidationErrors(errors)
});
}
const { title, message, targetUsers } = req.body;
let users;
if (targetUsers && targetUsers.length > 0) {
// Belirli kullanıcılara gönder
users = await req.prisma.user.findMany({
where: {
id: { in: targetUsers },
isActive: true
},
select: { id: true }
});
} else {
// Tüm aktif kullanıcılara gönder
users = await req.prisma.user.findMany({
where: { isActive: true },
select: { id: true }
});
}
if (users.length === 0) {
return res.status(400).json({
success: false,
message: 'Hedef kullanıcı bulunamadı'
});
}
// Bildirimleri oluştur
const notifications = users.map(user => ({
userId: user.id,
type: 'system',
title,
message,
data: JSON.stringify({
adminId: req.user.id,
timestamp: new Date().toISOString()
})
}));
const result = await req.prisma.notification.createMany({
data: notifications
});
// Socket.IO ile gerçek zamanlı bildirim gönder
if (req.io) {
users.forEach(user => {
req.io.to(`user_${user.id}`).emit('notification', {
type: 'system',
title,
message,
createdAt: new Date()
});
});
}
res.status(201).json({
success: true,
message: `${result.count} kullanıcıya sistem bildirimi gönderildi`,
data: { sentCount: result.count }
});
}));
/**
* Bildirim istatistikleri (Admin)
* GET /api/notifications/stats
*/
router.get('/stats/overview', [
authenticateToken,
requireAdmin
], asyncHandler(async (req, res) => {
const [
totalNotifications,
unreadNotifications,
notificationsByType,
recentNotifications
] = await Promise.all([
req.prisma.notification.count(),
req.prisma.notification.count({ where: { isRead: false } }),
req.prisma.notification.groupBy({
by: ['type'],
_count: {
id: true
},
orderBy: {
_count: {
id: 'desc'
}
}
}),
req.prisma.notification.findMany({
where: {
createdAt: {
gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // Son 24 saat
}
},
include: {
user: {
select: {
username: true,
firstName: true,
lastName: true
}
}
},
orderBy: {
createdAt: 'desc'
},
take: 10
})
]);
const typeStats = {};
notificationsByType.forEach(item => {
typeStats[item.type] = item._count.id;
});
res.json({
success: true,
data: {
overview: {
total: totalNotifications,
unread: unreadNotifications,
read: totalNotifications - unreadNotifications
},
byType: typeStats,
recent: recentNotifications
}
});
}));
module.exports = router;