diff --git a/packages/audit_log/seed/scripts/db/operations.lua b/packages/audit_log/seed/scripts/db/operations.lua new file mode 100644 index 000000000..0c627ffbf --- /dev/null +++ b/packages/audit_log/seed/scripts/db/operations.lua @@ -0,0 +1,238 @@ +-- audit_log/seed/scripts/db/operations.lua +-- DBAL operations for AuditLog entity +-- @module audit_log.db.operations + +local M = {} + +---@class AuditLogCreateParams +---@field tenantId string +---@field userId string|nil +---@field username string|nil +---@field action string +---@field entity string +---@field entityId string|nil +---@field oldValue table|nil +---@field newValue table|nil +---@field ipAddress string|nil +---@field userAgent string|nil +---@field details table|nil + +---Create a new audit log entry +---@param dbal table DBAL client instance +---@param params AuditLogCreateParams +---@return table Created audit log record +function M.create(dbal, params) + local json = require('json') + + return dbal:create('AuditLog', { + tenantId = params.tenantId, + userId = params.userId, + username = params.username, + action = params.action, + entity = params.entity, + entityId = params.entityId, + oldValue = params.oldValue and json.encode(params.oldValue) or nil, + newValue = params.newValue and json.encode(params.newValue) or nil, + ipAddress = params.ipAddress, + userAgent = params.userAgent, + details = params.details and json.encode(params.details) or nil, + timestamp = os.time() * 1000, + }) +end + +---@class AuditLogListParams +---@field tenantId string +---@field userId string|nil +---@field action string|nil +---@field entity string|nil +---@field entityId string|nil +---@field startTime number|nil Unix timestamp +---@field endTime number|nil Unix timestamp +---@field take number|nil +---@field skip number|nil + +---List audit logs with filters +---@param dbal table DBAL client instance +---@param params AuditLogListParams +---@return table[] List of audit log records +function M.list(dbal, params) + local where = { tenantId = params.tenantId } + + if params.userId then + where.userId = params.userId + end + + if params.action then + where.action = params.action + end + + if params.entity then + where.entity = params.entity + end + + if params.entityId then + where.entityId = params.entityId + end + + -- Time range filtering would need DBAL support for comparison operators + -- For now, return all and filter in Lua + local result = dbal:list('AuditLog', { + where = where, + orderBy = { timestamp = 'desc' }, + take = params.take or 50, + skip = params.skip or 0, + }) + + -- Filter by time range if specified + if params.startTime or params.endTime then + local filtered = {} + for _, log in ipairs(result.items or {}) do + local ts = log.timestamp + local inRange = true + + if params.startTime and ts < (params.startTime * 1000) then + inRange = false + end + + if params.endTime and ts > (params.endTime * 1000) then + inRange = false + end + + if inRange then + table.insert(filtered, log) + end + end + result.items = filtered + end + + return result +end + +---Get audit logs for a specific entity +---@param dbal table DBAL client instance +---@param tenantId string +---@param entity string Entity type +---@param entityId string Entity ID +---@return table[] Audit history for entity +function M.getEntityHistory(dbal, tenantId, entity, entityId) + return M.list(dbal, { + tenantId = tenantId, + entity = entity, + entityId = entityId, + take = 100, + }) +end + +---Get audit logs for a specific user +---@param dbal table DBAL client instance +---@param tenantId string +---@param userId string +---@param take number|nil +---@return table[] User's audit history +function M.getUserHistory(dbal, tenantId, userId, take) + return M.list(dbal, { + tenantId = tenantId, + userId = userId, + take = take or 50, + }) +end + +---Log a create action +---@param dbal table DBAL client instance +---@param tenantId string +---@param userId string +---@param entity string +---@param entityId string +---@param newValue table Created record +---@param context table|nil Additional context +function M.logCreate(dbal, tenantId, userId, entity, entityId, newValue, context) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + action = 'create', + entity = entity, + entityId = entityId, + newValue = newValue, + details = context, + }) +end + +---Log an update action +---@param dbal table DBAL client instance +---@param tenantId string +---@param userId string +---@param entity string +---@param entityId string +---@param oldValue table Previous state +---@param newValue table New state +---@param context table|nil Additional context +function M.logUpdate(dbal, tenantId, userId, entity, entityId, oldValue, newValue, context) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + action = 'update', + entity = entity, + entityId = entityId, + oldValue = oldValue, + newValue = newValue, + details = context, + }) +end + +---Log a delete action +---@param dbal table DBAL client instance +---@param tenantId string +---@param userId string +---@param entity string +---@param entityId string +---@param oldValue table Deleted record +---@param context table|nil Additional context +function M.logDelete(dbal, tenantId, userId, entity, entityId, oldValue, context) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + action = 'delete', + entity = entity, + entityId = entityId, + oldValue = oldValue, + details = context, + }) +end + +---Log a login action +---@param dbal table DBAL client instance +---@param tenantId string +---@param userId string +---@param username string +---@param ipAddress string|nil +---@param userAgent string|nil +function M.logLogin(dbal, tenantId, userId, username, ipAddress, userAgent) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + username = username, + action = 'login', + entity = 'User', + entityId = userId, + ipAddress = ipAddress, + userAgent = userAgent, + }) +end + +---Log a logout action +---@param dbal table DBAL client instance +---@param tenantId string +---@param userId string +---@param username string +function M.logLogout(dbal, tenantId, userId, username) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + username = username, + action = 'logout', + entity = 'User', + entityId = userId, + }) +end + +return M diff --git a/packages/notification_center/seed/scripts/db/operations.lua b/packages/notification_center/seed/scripts/db/operations.lua new file mode 100644 index 000000000..e24947318 --- /dev/null +++ b/packages/notification_center/seed/scripts/db/operations.lua @@ -0,0 +1,241 @@ +-- notification_center/seed/scripts/db/operations.lua +-- DBAL operations for Notification entity +-- @module notification_center.db.operations + +local M = {} + +---@class NotificationCreateParams +---@field tenantId string +---@field userId string +---@field type string info|warning|success|error|mention|reply|follow|like|system +---@field title string +---@field message string +---@field icon string|nil +---@field data table|nil +---@field expiresAt number|nil Unix timestamp + +---Create a new notification +---@param dbal table DBAL client instance +---@param params NotificationCreateParams +---@return table Created notification +function M.create(dbal, params) + local json = require('json') + + return dbal:create('Notification', { + tenantId = params.tenantId, + userId = params.userId, + type = params.type, + title = params.title, + message = params.message, + icon = params.icon, + read = false, + data = params.data and json.encode(params.data) or nil, + createdAt = os.time() * 1000, + expiresAt = params.expiresAt and (params.expiresAt * 1000) or nil, + }) +end + +---List notifications for a user +---@param dbal table DBAL client instance +---@param userId string +---@param unreadOnly boolean|nil Only return unread notifications +---@param take number|nil +---@param skip number|nil +---@return table List result with items and total +function M.list(dbal, userId, unreadOnly, take, skip) + local where = { userId = userId } + + if unreadOnly then + where.read = false + end + + return dbal:list('Notification', { + where = where, + orderBy = { createdAt = 'desc' }, + take = take or 20, + skip = skip or 0, + }) +end + +---Get unread notification count +---@param dbal table DBAL client instance +---@param userId string +---@return number Unread count +function M.getUnreadCount(dbal, userId) + local result = M.list(dbal, userId, true, 1000, 0) + return result.total or #(result.items or {}) +end + +---Mark a notification as read +---@param dbal table DBAL client instance +---@param notificationId string +---@return table Updated notification +function M.markAsRead(dbal, notificationId) + return dbal:update('Notification', notificationId, { + read = true, + }) +end + +---Mark all notifications as read for a user +---@param dbal table DBAL client instance +---@param userId string +---@return number Number of notifications updated +function M.markAllAsRead(dbal, userId) + local unread = M.list(dbal, userId, true, 1000, 0) + local count = 0 + + for _, notification in ipairs(unread.items or {}) do + dbal:update('Notification', notification.id, { read = true }) + count = count + 1 + end + + return count +end + +---Delete a notification +---@param dbal table DBAL client instance +---@param notificationId string +---@return boolean Success +function M.delete(dbal, notificationId) + return dbal:delete('Notification', notificationId) +end + +---Delete all read notifications for a user +---@param dbal table DBAL client instance +---@param userId string +---@return number Number deleted +function M.deleteRead(dbal, userId) + local result = dbal:list('Notification', { + where = { userId = userId, read = true }, + take = 1000, + }) + + local count = 0 + for _, notification in ipairs(result.items or {}) do + dbal:delete('Notification', notification.id) + count = count + 1 + end + + return count +end + +---Delete expired notifications +---@param dbal table DBAL client instance +---@param tenantId string +---@return number Number deleted +function M.deleteExpired(dbal, tenantId) + local now = os.time() * 1000 + local result = dbal:list('Notification', { + where = { tenantId = tenantId }, + take = 1000, + }) + + local count = 0 + for _, notification in ipairs(result.items or {}) do + if notification.expiresAt and notification.expiresAt < now then + dbal:delete('Notification', notification.id) + count = count + 1 + end + end + + return count +end + +-- Convenience functions for creating typed notifications + +---Send an info notification +---@param dbal table +---@param tenantId string +---@param userId string +---@param title string +---@param message string +---@param data table|nil +function M.sendInfo(dbal, tenantId, userId, title, message, data) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + type = 'info', + title = title, + message = message, + icon = 'Info', + data = data, + }) +end + +---Send a success notification +---@param dbal table +---@param tenantId string +---@param userId string +---@param title string +---@param message string +---@param data table|nil +function M.sendSuccess(dbal, tenantId, userId, title, message, data) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + type = 'success', + title = title, + message = message, + icon = 'CheckCircle', + data = data, + }) +end + +---Send a warning notification +---@param dbal table +---@param tenantId string +---@param userId string +---@param title string +---@param message string +---@param data table|nil +function M.sendWarning(dbal, tenantId, userId, title, message, data) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + type = 'warning', + title = title, + message = message, + icon = 'Warning', + data = data, + }) +end + +---Send an error notification +---@param dbal table +---@param tenantId string +---@param userId string +---@param title string +---@param message string +---@param data table|nil +function M.sendError(dbal, tenantId, userId, title, message, data) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + type = 'error', + title = title, + message = message, + icon = 'Error', + data = data, + }) +end + +---Send a mention notification +---@param dbal table +---@param tenantId string +---@param userId string +---@param mentionedBy string Username who mentioned +---@param context string Where they were mentioned +---@param link string|nil Link to the mention +function M.sendMention(dbal, tenantId, userId, mentionedBy, context, link) + return M.create(dbal, { + tenantId = tenantId, + userId = userId, + type = 'mention', + title = 'You were mentioned', + message = mentionedBy .. ' mentioned you in ' .. context, + icon = 'AlternateEmail', + data = { mentionedBy = mentionedBy, link = link }, + }) +end + +return M