mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
update: packages,operations,lua (3 files)
This commit is contained in:
383
packages/role_editor/seed/scripts/db/operations.lua
Normal file
383
packages/role_editor/seed/scripts/db/operations.lua
Normal file
@@ -0,0 +1,383 @@
|
||||
-- role_editor/seed/scripts/db/operations.lua
|
||||
-- DBAL operations for Role management
|
||||
-- Uses Permission entity from Prisma schema
|
||||
-- @module role_editor.db.operations
|
||||
|
||||
local M = {}
|
||||
local json = require('json')
|
||||
|
||||
-- Permission levels
|
||||
M.LEVELS = {
|
||||
PUBLIC = 0,
|
||||
USER = 1,
|
||||
MODERATOR = 2,
|
||||
ADMIN = 3,
|
||||
GOD = 4,
|
||||
SUPERGOD = 5,
|
||||
SYSTEM = 6,
|
||||
}
|
||||
|
||||
M.LEVEL_NAMES = {
|
||||
[0] = 'Public',
|
||||
[1] = 'User',
|
||||
[2] = 'Moderator',
|
||||
[3] = 'Admin',
|
||||
[4] = 'God',
|
||||
[5] = 'Supergod',
|
||||
[6] = 'System',
|
||||
}
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- ROLE/PERMISSION OPERATIONS
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
---@class RoleCreateParams
|
||||
---@field tenantId string
|
||||
---@field name string
|
||||
---@field level number 0-6
|
||||
---@field description string|nil
|
||||
---@field permissions table|nil Array of permission strings
|
||||
---@field color string|nil Display color
|
||||
|
||||
---Create a new role
|
||||
---@param dbal table DBAL client instance
|
||||
---@param params RoleCreateParams
|
||||
---@return table Created role
|
||||
function M.createRole(dbal, params)
|
||||
return dbal:create('Role', {
|
||||
tenantId = params.tenantId,
|
||||
name = params.name,
|
||||
level = params.level or 1,
|
||||
description = params.description,
|
||||
permissions = params.permissions and json.encode(params.permissions) or '[]',
|
||||
color = params.color or '#808080',
|
||||
isDefault = false,
|
||||
createdAt = os.time() * 1000,
|
||||
updatedAt = os.time() * 1000,
|
||||
})
|
||||
end
|
||||
|
||||
---Get role by ID
|
||||
---@param dbal table
|
||||
---@param roleId string
|
||||
---@return table|nil Role
|
||||
function M.getRole(dbal, roleId)
|
||||
local role = dbal:read('Role', roleId)
|
||||
if role and role.permissions then
|
||||
role.permissions = json.decode(role.permissions)
|
||||
end
|
||||
return role
|
||||
end
|
||||
|
||||
---Get role by name
|
||||
---@param dbal table
|
||||
---@param tenantId string
|
||||
---@param name string
|
||||
---@return table|nil Role
|
||||
function M.getRoleByName(dbal, tenantId, name)
|
||||
local role = dbal:findFirst('Role', {
|
||||
where = { tenantId = tenantId, name = name },
|
||||
})
|
||||
if role and role.permissions then
|
||||
role.permissions = json.decode(role.permissions)
|
||||
end
|
||||
return role
|
||||
end
|
||||
|
||||
---List all roles for a tenant
|
||||
---@param dbal table
|
||||
---@param tenantId string
|
||||
---@return table[] Roles sorted by level
|
||||
function M.listRoles(dbal, tenantId)
|
||||
local result = dbal:list('Role', {
|
||||
where = { tenantId = tenantId },
|
||||
orderBy = { level = 'asc' },
|
||||
take = 100,
|
||||
})
|
||||
|
||||
local roles = result.items or {}
|
||||
|
||||
-- Parse permissions JSON
|
||||
for _, role in ipairs(roles) do
|
||||
if role.permissions and type(role.permissions) == 'string' then
|
||||
role.permissions = json.decode(role.permissions)
|
||||
end
|
||||
end
|
||||
|
||||
return roles
|
||||
end
|
||||
|
||||
---Update role
|
||||
---@param dbal table
|
||||
---@param roleId string
|
||||
---@param updates table
|
||||
---@return table Updated role
|
||||
function M.updateRole(dbal, roleId, updates)
|
||||
if updates.permissions and type(updates.permissions) == 'table' then
|
||||
updates.permissions = json.encode(updates.permissions)
|
||||
end
|
||||
updates.updatedAt = os.time() * 1000
|
||||
return dbal:update('Role', roleId, updates)
|
||||
end
|
||||
|
||||
---Add permission to role
|
||||
---@param dbal table
|
||||
---@param roleId string
|
||||
---@param permission string
|
||||
---@return table Updated role
|
||||
function M.addPermission(dbal, roleId, permission)
|
||||
local role = M.getRole(dbal, roleId)
|
||||
if not role then
|
||||
error('Role not found: ' .. roleId)
|
||||
end
|
||||
|
||||
local perms = role.permissions or {}
|
||||
|
||||
-- Check if already has permission
|
||||
for _, p in ipairs(perms) do
|
||||
if p == permission then
|
||||
return role
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(perms, permission)
|
||||
return M.updateRole(dbal, roleId, { permissions = perms })
|
||||
end
|
||||
|
||||
---Remove permission from role
|
||||
---@param dbal table
|
||||
---@param roleId string
|
||||
---@param permission string
|
||||
---@return table Updated role
|
||||
function M.removePermission(dbal, roleId, permission)
|
||||
local role = M.getRole(dbal, roleId)
|
||||
if not role then
|
||||
error('Role not found: ' .. roleId)
|
||||
end
|
||||
|
||||
local perms = role.permissions or {}
|
||||
local newPerms = {}
|
||||
|
||||
for _, p in ipairs(perms) do
|
||||
if p ~= permission then
|
||||
table.insert(newPerms, p)
|
||||
end
|
||||
end
|
||||
|
||||
return M.updateRole(dbal, roleId, { permissions = newPerms })
|
||||
end
|
||||
|
||||
---Check if role has permission
|
||||
---@param dbal table
|
||||
---@param roleId string
|
||||
---@param permission string
|
||||
---@return boolean
|
||||
function M.hasPermission(dbal, roleId, permission)
|
||||
local role = M.getRole(dbal, roleId)
|
||||
if not role then
|
||||
return false
|
||||
end
|
||||
|
||||
for _, p in ipairs(role.permissions or {}) do
|
||||
if p == permission or p == '*' then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---Delete role
|
||||
---@param dbal table
|
||||
---@param roleId string
|
||||
---@return boolean Success
|
||||
function M.deleteRole(dbal, roleId)
|
||||
return dbal:delete('Role', roleId)
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- USER ROLE ASSIGNMENT
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
---Assign role to user
|
||||
---@param dbal table
|
||||
---@param userId string
|
||||
---@param roleId string
|
||||
---@return table Created assignment
|
||||
function M.assignRoleToUser(dbal, userId, roleId)
|
||||
-- Check if already assigned
|
||||
local existing = dbal:findFirst('UserRole', {
|
||||
where = { userId = userId, roleId = roleId },
|
||||
})
|
||||
|
||||
if existing then
|
||||
return existing
|
||||
end
|
||||
|
||||
return dbal:create('UserRole', {
|
||||
userId = userId,
|
||||
roleId = roleId,
|
||||
createdAt = os.time() * 1000,
|
||||
})
|
||||
end
|
||||
|
||||
---Remove role from user
|
||||
---@param dbal table
|
||||
---@param userId string
|
||||
---@param roleId string
|
||||
---@return boolean Success
|
||||
function M.removeRoleFromUser(dbal, userId, roleId)
|
||||
local assignment = dbal:findFirst('UserRole', {
|
||||
where = { userId = userId, roleId = roleId },
|
||||
})
|
||||
|
||||
if assignment then
|
||||
return dbal:delete('UserRole', assignment.id)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---Get user's roles
|
||||
---@param dbal table
|
||||
---@param userId string
|
||||
---@return table[] Roles
|
||||
function M.getUserRoles(dbal, userId)
|
||||
local assignments = dbal:list('UserRole', {
|
||||
where = { userId = userId },
|
||||
take = 100,
|
||||
})
|
||||
|
||||
local roles = {}
|
||||
for _, assignment in ipairs(assignments.items or {}) do
|
||||
local role = M.getRole(dbal, assignment.roleId)
|
||||
if role then
|
||||
table.insert(roles, role)
|
||||
end
|
||||
end
|
||||
|
||||
return roles
|
||||
end
|
||||
|
||||
---Get effective permission level for user
|
||||
---@param dbal table
|
||||
---@param userId string
|
||||
---@return number Maximum level from all roles
|
||||
function M.getEffectiveLevel(dbal, userId)
|
||||
local roles = M.getUserRoles(dbal, userId)
|
||||
local maxLevel = 0
|
||||
|
||||
for _, role in ipairs(roles) do
|
||||
if (role.level or 0) > maxLevel then
|
||||
maxLevel = role.level
|
||||
end
|
||||
end
|
||||
|
||||
return maxLevel
|
||||
end
|
||||
|
||||
---Get all permissions for user
|
||||
---@param dbal table
|
||||
---@param userId string
|
||||
---@return table Unique permissions
|
||||
function M.getAllUserPermissions(dbal, userId)
|
||||
local roles = M.getUserRoles(dbal, userId)
|
||||
local permSet = {}
|
||||
|
||||
for _, role in ipairs(roles) do
|
||||
for _, perm in ipairs(role.permissions or {}) do
|
||||
permSet[perm] = true
|
||||
end
|
||||
end
|
||||
|
||||
local perms = {}
|
||||
for perm, _ in pairs(permSet) do
|
||||
table.insert(perms, perm)
|
||||
end
|
||||
|
||||
table.sort(perms)
|
||||
return perms
|
||||
end
|
||||
|
||||
---Check if user has specific permission
|
||||
---@param dbal table
|
||||
---@param userId string
|
||||
---@param permission string
|
||||
---@return boolean
|
||||
function M.userHasPermission(dbal, userId, permission)
|
||||
local perms = M.getAllUserPermissions(dbal, userId)
|
||||
|
||||
for _, p in ipairs(perms) do
|
||||
if p == permission or p == '*' then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- DEFAULT ROLES
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
---Create default roles for a tenant
|
||||
---@param dbal table
|
||||
---@param tenantId string
|
||||
---@return table[] Created roles
|
||||
function M.createDefaultRoles(dbal, tenantId)
|
||||
local roles = {}
|
||||
|
||||
-- Public role
|
||||
table.insert(roles, M.createRole(dbal, {
|
||||
tenantId = tenantId,
|
||||
name = 'Guest',
|
||||
level = 0,
|
||||
description = 'Unauthenticated visitor',
|
||||
permissions = {'view:public'},
|
||||
color = '#9E9E9E',
|
||||
}))
|
||||
|
||||
-- User role
|
||||
table.insert(roles, M.createRole(dbal, {
|
||||
tenantId = tenantId,
|
||||
name = 'User',
|
||||
level = 1,
|
||||
description = 'Registered user',
|
||||
permissions = {'view:public', 'view:private', 'edit:own'},
|
||||
color = '#2196F3',
|
||||
}))
|
||||
|
||||
-- Moderator role
|
||||
table.insert(roles, M.createRole(dbal, {
|
||||
tenantId = tenantId,
|
||||
name = 'Moderator',
|
||||
level = 2,
|
||||
description = 'Content moderator',
|
||||
permissions = {'view:public', 'view:private', 'edit:own', 'moderate:content'},
|
||||
color = '#4CAF50',
|
||||
}))
|
||||
|
||||
-- Admin role
|
||||
table.insert(roles, M.createRole(dbal, {
|
||||
tenantId = tenantId,
|
||||
name = 'Admin',
|
||||
level = 3,
|
||||
description = 'Administrator',
|
||||
permissions = {'view:*', 'edit:*', 'manage:users', 'manage:content'},
|
||||
color = '#FF9800',
|
||||
}))
|
||||
|
||||
-- God role
|
||||
table.insert(roles, M.createRole(dbal, {
|
||||
tenantId = tenantId,
|
||||
name = 'God',
|
||||
level = 4,
|
||||
description = 'Full access',
|
||||
permissions = {'*'},
|
||||
color = '#F44336',
|
||||
}))
|
||||
|
||||
return roles
|
||||
end
|
||||
|
||||
return M
|
||||
338
packages/user_manager/seed/scripts/db/operations.lua
Normal file
338
packages/user_manager/seed/scripts/db/operations.lua
Normal file
@@ -0,0 +1,338 @@
|
||||
-- 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
|
||||
361
packages/workflow_editor/seed/scripts/db/operations.lua
Normal file
361
packages/workflow_editor/seed/scripts/db/operations.lua
Normal file
@@ -0,0 +1,361 @@
|
||||
-- workflow_editor/seed/scripts/db/operations.lua
|
||||
-- DBAL operations for Workflow entities
|
||||
-- @module workflow_editor.db.operations
|
||||
|
||||
local M = {}
|
||||
local json = require('json')
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- WORKFLOW OPERATIONS
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
---@class WorkflowCreateParams
|
||||
---@field tenantId string
|
||||
---@field name string
|
||||
---@field description string|nil
|
||||
---@field trigger table Trigger configuration
|
||||
---@field nodes table[] Workflow nodes
|
||||
---@field edges table[] Node connections
|
||||
---@field createdBy string
|
||||
|
||||
---Create a new workflow
|
||||
---@param dbal table DBAL client instance
|
||||
---@param params WorkflowCreateParams
|
||||
---@return table Created workflow
|
||||
function M.createWorkflow(dbal, params)
|
||||
return dbal:create('Workflow', {
|
||||
tenantId = params.tenantId,
|
||||
name = params.name,
|
||||
description = params.description,
|
||||
status = 'draft',
|
||||
version = 1,
|
||||
trigger = json.encode(params.trigger or {}),
|
||||
nodes = json.encode(params.nodes or {}),
|
||||
edges = json.encode(params.edges or {}),
|
||||
createdBy = params.createdBy,
|
||||
createdAt = os.time() * 1000,
|
||||
updatedAt = os.time() * 1000,
|
||||
})
|
||||
end
|
||||
|
||||
---Get workflow by ID
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
---@return table|nil Workflow with parsed JSON
|
||||
function M.getWorkflow(dbal, workflowId)
|
||||
local workflow = dbal:read('Workflow', workflowId)
|
||||
if workflow then
|
||||
workflow.trigger = json.decode(workflow.trigger or '{}')
|
||||
workflow.nodes = json.decode(workflow.nodes or '[]')
|
||||
workflow.edges = json.decode(workflow.edges or '[]')
|
||||
end
|
||||
return workflow
|
||||
end
|
||||
|
||||
---List workflows
|
||||
---@param dbal table
|
||||
---@param tenantId string
|
||||
---@param status string|nil draft|active|paused|archived
|
||||
---@param take number|nil
|
||||
---@param skip number|nil
|
||||
---@return table List result
|
||||
function M.listWorkflows(dbal, tenantId, status, take, skip)
|
||||
local where = { tenantId = tenantId }
|
||||
|
||||
if status then
|
||||
where.status = status
|
||||
end
|
||||
|
||||
local result = dbal:list('Workflow', {
|
||||
where = where,
|
||||
orderBy = { updatedAt = 'desc' },
|
||||
take = take or 20,
|
||||
skip = skip or 0,
|
||||
})
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
---Update workflow
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
---@param updates table
|
||||
---@return table Updated workflow
|
||||
function M.updateWorkflow(dbal, workflowId, updates)
|
||||
if updates.trigger and type(updates.trigger) == 'table' then
|
||||
updates.trigger = json.encode(updates.trigger)
|
||||
end
|
||||
if updates.nodes and type(updates.nodes) == 'table' then
|
||||
updates.nodes = json.encode(updates.nodes)
|
||||
end
|
||||
if updates.edges and type(updates.edges) == 'table' then
|
||||
updates.edges = json.encode(updates.edges)
|
||||
end
|
||||
|
||||
updates.updatedAt = os.time() * 1000
|
||||
return dbal:update('Workflow', workflowId, updates)
|
||||
end
|
||||
|
||||
---Save workflow nodes and edges
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
---@param nodes table[]
|
||||
---@param edges table[]
|
||||
---@return table Updated workflow
|
||||
function M.saveWorkflowGraph(dbal, workflowId, nodes, edges)
|
||||
return M.updateWorkflow(dbal, workflowId, {
|
||||
nodes = nodes,
|
||||
edges = edges,
|
||||
})
|
||||
end
|
||||
|
||||
---Publish workflow (activate)
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
---@return table Updated workflow
|
||||
function M.publishWorkflow(dbal, workflowId)
|
||||
local workflow = M.getWorkflow(dbal, workflowId)
|
||||
if not workflow then
|
||||
error('Workflow not found: ' .. workflowId)
|
||||
end
|
||||
|
||||
return M.updateWorkflow(dbal, workflowId, {
|
||||
status = 'active',
|
||||
version = (workflow.version or 0) + 1,
|
||||
publishedAt = os.time() * 1000,
|
||||
})
|
||||
end
|
||||
|
||||
---Pause workflow
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
function M.pauseWorkflow(dbal, workflowId)
|
||||
return M.updateWorkflow(dbal, workflowId, { status = 'paused' })
|
||||
end
|
||||
|
||||
---Resume workflow
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
function M.resumeWorkflow(dbal, workflowId)
|
||||
return M.updateWorkflow(dbal, workflowId, { status = 'active' })
|
||||
end
|
||||
|
||||
---Archive workflow
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
function M.archiveWorkflow(dbal, workflowId)
|
||||
return M.updateWorkflow(dbal, workflowId, { status = 'archived' })
|
||||
end
|
||||
|
||||
---Delete workflow
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
---@return boolean Success
|
||||
function M.deleteWorkflow(dbal, workflowId)
|
||||
return dbal:delete('Workflow', workflowId)
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- WORKFLOW EXECUTION OPERATIONS
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
---@class WorkflowExecutionParams
|
||||
---@field tenantId string
|
||||
---@field workflowId string
|
||||
---@field triggeredBy string|nil User or system
|
||||
---@field input table|nil Input data
|
||||
|
||||
---Start a workflow execution
|
||||
---@param dbal table
|
||||
---@param params WorkflowExecutionParams
|
||||
---@return table Created execution
|
||||
function M.startExecution(dbal, params)
|
||||
return dbal:create('WorkflowExecution', {
|
||||
tenantId = params.tenantId,
|
||||
workflowId = params.workflowId,
|
||||
status = 'running',
|
||||
triggeredBy = params.triggeredBy or 'system',
|
||||
input = params.input and json.encode(params.input) or nil,
|
||||
startedAt = os.time() * 1000,
|
||||
})
|
||||
end
|
||||
|
||||
---Get execution by ID
|
||||
---@param dbal table
|
||||
---@param executionId string
|
||||
---@return table|nil Execution
|
||||
function M.getExecution(dbal, executionId)
|
||||
local execution = dbal:read('WorkflowExecution', executionId)
|
||||
if execution then
|
||||
if execution.input then
|
||||
execution.input = json.decode(execution.input)
|
||||
end
|
||||
if execution.output then
|
||||
execution.output = json.decode(execution.output)
|
||||
end
|
||||
end
|
||||
return execution
|
||||
end
|
||||
|
||||
---List executions for a workflow
|
||||
---@param dbal table
|
||||
---@param workflowId string
|
||||
---@param status string|nil running|completed|failed|cancelled
|
||||
---@param take number|nil
|
||||
---@return table List result
|
||||
function M.listExecutions(dbal, workflowId, status, take)
|
||||
local where = { workflowId = workflowId }
|
||||
|
||||
if status then
|
||||
where.status = status
|
||||
end
|
||||
|
||||
return dbal:list('WorkflowExecution', {
|
||||
where = where,
|
||||
orderBy = { startedAt = 'desc' },
|
||||
take = take or 20,
|
||||
})
|
||||
end
|
||||
|
||||
---Update execution status
|
||||
---@param dbal table
|
||||
---@param executionId string
|
||||
---@param status string
|
||||
---@param output table|nil
|
||||
---@param error string|nil
|
||||
function M.updateExecution(dbal, executionId, status, output, error)
|
||||
local updates = { status = status }
|
||||
|
||||
if status == 'completed' or status == 'failed' or status == 'cancelled' then
|
||||
updates.completedAt = os.time() * 1000
|
||||
end
|
||||
|
||||
if output then
|
||||
updates.output = json.encode(output)
|
||||
end
|
||||
|
||||
if error then
|
||||
updates.error = error
|
||||
end
|
||||
|
||||
return dbal:update('WorkflowExecution', executionId, updates)
|
||||
end
|
||||
|
||||
---Complete execution
|
||||
---@param dbal table
|
||||
---@param executionId string
|
||||
---@param output table|nil
|
||||
function M.completeExecution(dbal, executionId, output)
|
||||
return M.updateExecution(dbal, executionId, 'completed', output, nil)
|
||||
end
|
||||
|
||||
---Fail execution
|
||||
---@param dbal table
|
||||
---@param executionId string
|
||||
---@param error string
|
||||
function M.failExecution(dbal, executionId, error)
|
||||
return M.updateExecution(dbal, executionId, 'failed', nil, error)
|
||||
end
|
||||
|
||||
---Cancel execution
|
||||
---@param dbal table
|
||||
---@param executionId string
|
||||
function M.cancelExecution(dbal, executionId)
|
||||
return M.updateExecution(dbal, executionId, 'cancelled', nil, nil)
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- NODE STEP LOGGING
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
---Log a node step
|
||||
---@param dbal table
|
||||
---@param executionId string
|
||||
---@param nodeId string
|
||||
---@param status string pending|running|completed|failed|skipped
|
||||
---@param input table|nil
|
||||
---@param output table|nil
|
||||
---@param error string|nil
|
||||
function M.logNodeStep(dbal, executionId, nodeId, status, input, output, error)
|
||||
return dbal:create('WorkflowStep', {
|
||||
executionId = executionId,
|
||||
nodeId = nodeId,
|
||||
status = status,
|
||||
input = input and json.encode(input) or nil,
|
||||
output = output and json.encode(output) or nil,
|
||||
error = error,
|
||||
timestamp = os.time() * 1000,
|
||||
})
|
||||
end
|
||||
|
||||
---Get steps for an execution
|
||||
---@param dbal table
|
||||
---@param executionId string
|
||||
---@return table[] Steps in order
|
||||
function M.getExecutionSteps(dbal, executionId)
|
||||
local result = dbal:list('WorkflowStep', {
|
||||
where = { executionId = executionId },
|
||||
orderBy = { timestamp = 'asc' },
|
||||
take = 1000,
|
||||
})
|
||||
return result.items or {}
|
||||
end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- WORKFLOW TEMPLATES
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
---List workflow templates
|
||||
---@param dbal table
|
||||
---@param category string|nil
|
||||
---@return table[] Templates
|
||||
function M.listTemplates(dbal, category)
|
||||
local where = { isTemplate = true }
|
||||
|
||||
local result = dbal:list('Workflow', {
|
||||
where = where,
|
||||
orderBy = { name = 'asc' },
|
||||
take = 50,
|
||||
})
|
||||
|
||||
local templates = result.items or {}
|
||||
|
||||
-- Filter by category if specified
|
||||
if category then
|
||||
local filtered = {}
|
||||
for _, t in ipairs(templates) do
|
||||
if t.category == category then
|
||||
table.insert(filtered, t)
|
||||
end
|
||||
end
|
||||
templates = filtered
|
||||
end
|
||||
|
||||
return templates
|
||||
end
|
||||
|
||||
---Create workflow from template
|
||||
---@param dbal table
|
||||
---@param templateId string
|
||||
---@param tenantId string
|
||||
---@param name string
|
||||
---@param createdBy string
|
||||
---@return table Created workflow
|
||||
function M.createFromTemplate(dbal, templateId, tenantId, name, createdBy)
|
||||
local template = M.getWorkflow(dbal, templateId)
|
||||
if not template then
|
||||
error('Template not found: ' .. templateId)
|
||||
end
|
||||
|
||||
return M.createWorkflow(dbal, {
|
||||
tenantId = tenantId,
|
||||
name = name,
|
||||
description = 'Created from template: ' .. template.name,
|
||||
trigger = template.trigger,
|
||||
nodes = template.nodes,
|
||||
edges = template.edges,
|
||||
createdBy = createdBy,
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user