Files
metabuilder/packages/user_manager/seed/scripts/db/operations.lua
2025-12-30 21:59:30 +00:00

339 lines
8.8 KiB
Lua

-- user_manager/seed/scripts/db/operations.lua
-- DBAL operations for User management
-- Uses existing User entity from Prisma schema
-- @module user_manager.db.operations
local M = {}
local json = require('json')
---------------------------------------------------------------------------
-- USER CRUD OPERATIONS
---------------------------------------------------------------------------
---@class UserCreateParams
---@field tenantId string
---@field email string
---@field username string
---@field password string Hashed password
---@field displayName string|nil
---@field avatar string|nil
---@field level number 0-6 permission level
---@field status string active|inactive|banned
---Create a new user
---@param dbal table DBAL client instance
---@param params UserCreateParams
---@return table Created user
function M.createUser(dbal, params)
return dbal:create('User', {
tenantId = params.tenantId,
email = params.email,
username = params.username,
password = params.password,
displayName = params.displayName or params.username,
avatar = params.avatar,
level = params.level or 1,
status = params.status or 'active',
createdAt = os.time() * 1000,
updatedAt = os.time() * 1000,
})
end
---Get user by ID
---@param dbal table
---@param userId string
---@return table|nil User
function M.getUser(dbal, userId)
return dbal:read('User', userId)
end
---Get user by email
---@param dbal table
---@param tenantId string
---@param email string
---@return table|nil User
function M.getUserByEmail(dbal, tenantId, email)
return dbal:findFirst('User', {
where = { tenantId = tenantId, email = email },
})
end
---Get user by username
---@param dbal table
---@param tenantId string
---@param username string
---@return table|nil User
function M.getUserByUsername(dbal, tenantId, username)
return dbal:findFirst('User', {
where = { tenantId = tenantId, username = username },
})
end
---List users
---@param dbal table
---@param tenantId string
---@param status string|nil Filter by status
---@param minLevel number|nil Minimum permission level
---@param take number|nil
---@param skip number|nil
---@return table List result
function M.listUsers(dbal, tenantId, status, minLevel, take, skip)
local where = { tenantId = tenantId }
if status then
where.status = status
end
local result = dbal:list('User', {
where = where,
orderBy = { createdAt = 'desc' },
take = take or 50,
skip = skip or 0,
})
-- Filter by minLevel if specified
if minLevel and result.items then
local filtered = {}
for _, user in ipairs(result.items) do
if (user.level or 0) >= minLevel then
table.insert(filtered, user)
end
end
result.items = filtered
end
return result
end
---Update user
---@param dbal table
---@param userId string
---@param updates table
---@return table Updated user
function M.updateUser(dbal, userId, updates)
updates.updatedAt = os.time() * 1000
return dbal:update('User', userId, updates)
end
---Update user profile
---@param dbal table
---@param userId string
---@param displayName string|nil
---@param avatar string|nil
---@param bio string|nil
---@return table Updated user
function M.updateProfile(dbal, userId, displayName, avatar, bio)
local updates = { updatedAt = os.time() * 1000 }
if displayName ~= nil then
updates.displayName = displayName
end
if avatar ~= nil then
updates.avatar = avatar
end
if bio ~= nil then
updates.bio = bio
end
return dbal:update('User', userId, updates)
end
---Change user password
---@param dbal table
---@param userId string
---@param hashedPassword string
---@return table Updated user
function M.changePassword(dbal, userId, hashedPassword)
return M.updateUser(dbal, userId, {
password = hashedPassword,
})
end
---Set user status
---@param dbal table
---@param userId string
---@param status string active|inactive|banned
---@return table Updated user
function M.setStatus(dbal, userId, status)
return M.updateUser(dbal, userId, { status = status })
end
---Activate user
---@param dbal table
---@param userId string
function M.activateUser(dbal, userId)
return M.setStatus(dbal, userId, 'active')
end
---Deactivate user
---@param dbal table
---@param userId string
function M.deactivateUser(dbal, userId)
return M.setStatus(dbal, userId, 'inactive')
end
---Ban user
---@param dbal table
---@param userId string
---@param reason string|nil
function M.banUser(dbal, userId, reason)
return M.updateUser(dbal, userId, {
status = 'banned',
banReason = reason,
bannedAt = os.time() * 1000,
})
end
---Unban user
---@param dbal table
---@param userId string
function M.unbanUser(dbal, userId)
return M.updateUser(dbal, userId, {
status = 'active',
banReason = nil,
bannedAt = nil,
})
end
---------------------------------------------------------------------------
-- PERMISSION OPERATIONS
---------------------------------------------------------------------------
---Set user permission level
---@param dbal table
---@param userId string
---@param level number 0-6
---@return table Updated user
function M.setLevel(dbal, userId, level)
if level < 0 or level > 6 then
error('Invalid permission level: ' .. tostring(level))
end
return M.updateUser(dbal, userId, { level = level })
end
---Promote user by one level
---@param dbal table
---@param userId string
---@param maxLevel number|nil Maximum level to promote to
---@return table Updated user
function M.promoteUser(dbal, userId, maxLevel)
local user = M.getUser(dbal, userId)
if not user then
error('User not found: ' .. userId)
end
local newLevel = math.min((user.level or 0) + 1, maxLevel or 6)
return M.setLevel(dbal, userId, newLevel)
end
---Demote user by one level
---@param dbal table
---@param userId string
---@return table Updated user
function M.demoteUser(dbal, userId)
local user = M.getUser(dbal, userId)
if not user then
error('User not found: ' .. userId)
end
local newLevel = math.max((user.level or 0) - 1, 0)
return M.setLevel(dbal, userId, newLevel)
end
---Check if user has minimum permission level
---@param dbal table
---@param userId string
---@param requiredLevel number
---@return boolean
function M.hasPermission(dbal, userId, requiredLevel)
local user = M.getUser(dbal, userId)
if not user then
return false
end
return (user.level or 0) >= requiredLevel
end
---------------------------------------------------------------------------
-- BULK OPERATIONS
---------------------------------------------------------------------------
---List admins (level >= 3)
---@param dbal table
---@param tenantId string
---@return table[] Admin users
function M.listAdmins(dbal, tenantId)
local result = M.listUsers(dbal, tenantId, 'active', 3, 100, 0)
return result.items or {}
end
---List moderators (level >= 2)
---@param dbal table
---@param tenantId string
---@return table[] Moderator users
function M.listModerators(dbal, tenantId)
local result = M.listUsers(dbal, tenantId, 'active', 2, 100, 0)
return result.items or {}
end
---Count users by status
---@param dbal table
---@param tenantId string
---@return table Counts by status
function M.countByStatus(dbal, tenantId)
local all = M.listUsers(dbal, tenantId, nil, nil, 10000, 0)
local counts = {
active = 0,
inactive = 0,
banned = 0,
total = 0,
}
for _, user in ipairs(all.items or {}) do
counts.total = counts.total + 1
local status = user.status or 'active'
counts[status] = (counts[status] or 0) + 1
end
return counts
end
---Search users by username or email
---@param dbal table
---@param tenantId string
---@param query string
---@param take number|nil
---@return table[] Matching users
function M.searchUsers(dbal, tenantId, query, take)
local all = M.listUsers(dbal, tenantId, nil, nil, 1000, 0)
local matches = {}
local lowerQuery = query:lower()
for _, user in ipairs(all.items or {}) do
local username = (user.username or ''):lower()
local email = (user.email or ''):lower()
local displayName = (user.displayName or ''):lower()
if username:find(lowerQuery, 1, true) or
email:find(lowerQuery, 1, true) or
displayName:find(lowerQuery, 1, true) then
table.insert(matches, user)
if #matches >= (take or 20) then
break
end
end
end
return matches
end
---Delete user
---@param dbal table
---@param userId string
---@return boolean Success
function M.deleteUser(dbal, userId)
return dbal:delete('User', userId)
end
return M