mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-26 14:54:55 +00:00
376 lines
10 KiB
Lua
376 lines
10 KiB
Lua
-- dashboard/seed/scripts/db/operations.lua
|
|
-- DBAL operations for Dashboard widgets and layouts
|
|
-- @module dashboard.db.operations
|
|
|
|
local M = {}
|
|
local json = require('json')
|
|
|
|
---------------------------------------------------------------------------
|
|
-- DASHBOARD LAYOUT OPERATIONS
|
|
---------------------------------------------------------------------------
|
|
|
|
---@class DashboardLayoutParams
|
|
---@field tenantId string
|
|
---@field userId string|nil User-specific or tenant-wide
|
|
---@field name string Layout name
|
|
---@field widgets table[] Widget configurations
|
|
---@field settings table|nil Layout settings
|
|
|
|
---Save dashboard layout
|
|
---@param dbal table DBAL client instance
|
|
---@param params DashboardLayoutParams
|
|
---@return table Created/updated layout
|
|
function M.saveLayout(dbal, params)
|
|
local where = {
|
|
tenantId = params.tenantId,
|
|
name = params.name,
|
|
}
|
|
|
|
if params.userId then
|
|
where.userId = params.userId
|
|
end
|
|
|
|
local existing = dbal:findFirst('DashboardLayout', { where = where })
|
|
|
|
if existing then
|
|
return dbal:update('DashboardLayout', existing.id, {
|
|
widgets = json.encode(params.widgets),
|
|
settings = params.settings and json.encode(params.settings) or nil,
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
return dbal:create('DashboardLayout', {
|
|
tenantId = params.tenantId,
|
|
userId = params.userId,
|
|
name = params.name,
|
|
widgets = json.encode(params.widgets),
|
|
settings = params.settings and json.encode(params.settings) or '{}',
|
|
isDefault = false,
|
|
createdAt = os.time() * 1000,
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
---Get layout by name
|
|
---@param dbal table
|
|
---@param tenantId string
|
|
---@param name string
|
|
---@param userId string|nil
|
|
---@return table|nil Layout
|
|
function M.getLayout(dbal, tenantId, name, userId)
|
|
local where = { tenantId = tenantId, name = name }
|
|
|
|
if userId then
|
|
where.userId = userId
|
|
end
|
|
|
|
local layout = dbal:findFirst('DashboardLayout', { where = where })
|
|
|
|
if layout then
|
|
layout.widgets = json.decode(layout.widgets or '[]')
|
|
layout.settings = json.decode(layout.settings or '{}')
|
|
end
|
|
|
|
return layout
|
|
end
|
|
|
|
---Get user's default layout
|
|
---@param dbal table
|
|
---@param tenantId string
|
|
---@param userId string
|
|
---@return table|nil Layout
|
|
function M.getDefaultLayout(dbal, tenantId, userId)
|
|
-- First try user's default
|
|
local userDefault = dbal:findFirst('DashboardLayout', {
|
|
where = { tenantId = tenantId, userId = userId, isDefault = true },
|
|
})
|
|
|
|
if userDefault then
|
|
userDefault.widgets = json.decode(userDefault.widgets or '[]')
|
|
userDefault.settings = json.decode(userDefault.settings or '{}')
|
|
return userDefault
|
|
end
|
|
|
|
-- Fall back to tenant default
|
|
local tenantDefault = dbal:findFirst('DashboardLayout', {
|
|
where = { tenantId = tenantId, isDefault = true },
|
|
})
|
|
|
|
if tenantDefault then
|
|
tenantDefault.widgets = json.decode(tenantDefault.widgets or '[]')
|
|
tenantDefault.settings = json.decode(tenantDefault.settings or '{}')
|
|
return tenantDefault
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
---List all layouts
|
|
---@param dbal table
|
|
---@param tenantId string
|
|
---@param userId string|nil
|
|
---@return table[] Layouts
|
|
function M.listLayouts(dbal, tenantId, userId)
|
|
local where = { tenantId = tenantId }
|
|
|
|
if userId then
|
|
where.userId = userId
|
|
end
|
|
|
|
local result = dbal:list('DashboardLayout', {
|
|
where = where,
|
|
orderBy = { name = 'asc' },
|
|
take = 50,
|
|
})
|
|
|
|
return result.items or {}
|
|
end
|
|
|
|
---Set layout as default
|
|
---@param dbal table
|
|
---@param tenantId string
|
|
---@param layoutId string
|
|
---@param userId string|nil
|
|
function M.setDefault(dbal, tenantId, layoutId, userId)
|
|
-- Clear existing defaults
|
|
local layouts = M.listLayouts(dbal, tenantId, userId)
|
|
for _, layout in ipairs(layouts) do
|
|
if layout.isDefault then
|
|
dbal:update('DashboardLayout', layout.id, { isDefault = false })
|
|
end
|
|
end
|
|
|
|
return dbal:update('DashboardLayout', layoutId, {
|
|
isDefault = true,
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
---Delete layout
|
|
---@param dbal table
|
|
---@param layoutId string
|
|
---@return boolean Success
|
|
function M.deleteLayout(dbal, layoutId)
|
|
return dbal:delete('DashboardLayout', layoutId)
|
|
end
|
|
|
|
---------------------------------------------------------------------------
|
|
-- WIDGET OPERATIONS
|
|
---------------------------------------------------------------------------
|
|
|
|
---@class WidgetConfig
|
|
---@field id string Unique widget ID
|
|
---@field type string Widget type (stats, chart, list, etc.)
|
|
---@field title string Widget title
|
|
---@field x number Grid x position
|
|
---@field y number Grid y position
|
|
---@field w number Grid width
|
|
---@field h number Grid height
|
|
---@field config table Widget-specific config
|
|
|
|
---Add widget to layout
|
|
---@param dbal table
|
|
---@param layoutId string
|
|
---@param widget WidgetConfig
|
|
---@return table Updated layout
|
|
function M.addWidget(dbal, layoutId, widget)
|
|
local layout = dbal:read('DashboardLayout', layoutId)
|
|
if not layout then
|
|
error('Layout not found: ' .. layoutId)
|
|
end
|
|
|
|
local widgets = json.decode(layout.widgets or '[]')
|
|
|
|
-- Generate ID if not provided
|
|
if not widget.id then
|
|
widget.id = 'widget_' .. os.time() .. '_' .. math.random(1000, 9999)
|
|
end
|
|
|
|
table.insert(widgets, widget)
|
|
|
|
return dbal:update('DashboardLayout', layoutId, {
|
|
widgets = json.encode(widgets),
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
---Update widget in layout
|
|
---@param dbal table
|
|
---@param layoutId string
|
|
---@param widgetId string
|
|
---@param updates table
|
|
---@return table Updated layout
|
|
function M.updateWidget(dbal, layoutId, widgetId, updates)
|
|
local layout = dbal:read('DashboardLayout', layoutId)
|
|
if not layout then
|
|
error('Layout not found: ' .. layoutId)
|
|
end
|
|
|
|
local widgets = json.decode(layout.widgets or '[]')
|
|
|
|
for i, widget in ipairs(widgets) do
|
|
if widget.id == widgetId then
|
|
for key, value in pairs(updates) do
|
|
widgets[i][key] = value
|
|
end
|
|
break
|
|
end
|
|
end
|
|
|
|
return dbal:update('DashboardLayout', layoutId, {
|
|
widgets = json.encode(widgets),
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
---Remove widget from layout
|
|
---@param dbal table
|
|
---@param layoutId string
|
|
---@param widgetId string
|
|
---@return table Updated layout
|
|
function M.removeWidget(dbal, layoutId, widgetId)
|
|
local layout = dbal:read('DashboardLayout', layoutId)
|
|
if not layout then
|
|
error('Layout not found: ' .. layoutId)
|
|
end
|
|
|
|
local widgets = json.decode(layout.widgets or '[]')
|
|
local newWidgets = {}
|
|
|
|
for _, widget in ipairs(widgets) do
|
|
if widget.id ~= widgetId then
|
|
table.insert(newWidgets, widget)
|
|
end
|
|
end
|
|
|
|
return dbal:update('DashboardLayout', layoutId, {
|
|
widgets = json.encode(newWidgets),
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
---Update widget positions (after drag)
|
|
---@param dbal table
|
|
---@param layoutId string
|
|
---@param positions table[] Array of {id, x, y, w, h}
|
|
---@return table Updated layout
|
|
function M.updatePositions(dbal, layoutId, positions)
|
|
local layout = dbal:read('DashboardLayout', layoutId)
|
|
if not layout then
|
|
error('Layout not found: ' .. layoutId)
|
|
end
|
|
|
|
local widgets = json.decode(layout.widgets or '[]')
|
|
|
|
-- Create lookup map
|
|
local posMap = {}
|
|
for _, pos in ipairs(positions) do
|
|
posMap[pos.id] = pos
|
|
end
|
|
|
|
-- Update positions
|
|
for i, widget in ipairs(widgets) do
|
|
local pos = posMap[widget.id]
|
|
if pos then
|
|
widgets[i].x = pos.x
|
|
widgets[i].y = pos.y
|
|
widgets[i].w = pos.w or widgets[i].w
|
|
widgets[i].h = pos.h or widgets[i].h
|
|
end
|
|
end
|
|
|
|
return dbal:update('DashboardLayout', layoutId, {
|
|
widgets = json.encode(widgets),
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
---------------------------------------------------------------------------
|
|
-- WIDGET DATA CACHE
|
|
---------------------------------------------------------------------------
|
|
|
|
---Cache widget data
|
|
---@param dbal table
|
|
---@param tenantId string
|
|
---@param widgetType string
|
|
---@param cacheKey string
|
|
---@param data table
|
|
---@param ttlSeconds number|nil
|
|
function M.cacheWidgetData(dbal, tenantId, widgetType, cacheKey, data, ttlSeconds)
|
|
local key = widgetType .. ':' .. cacheKey
|
|
local expiresAt = ttlSeconds and ((os.time() + ttlSeconds) * 1000) or nil
|
|
|
|
local existing = dbal:findFirst('WidgetCache', {
|
|
where = { tenantId = tenantId, cacheKey = key },
|
|
})
|
|
|
|
if existing then
|
|
return dbal:update('WidgetCache', existing.id, {
|
|
data = json.encode(data),
|
|
expiresAt = expiresAt,
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
return dbal:create('WidgetCache', {
|
|
tenantId = tenantId,
|
|
cacheKey = key,
|
|
widgetType = widgetType,
|
|
data = json.encode(data),
|
|
expiresAt = expiresAt,
|
|
createdAt = os.time() * 1000,
|
|
updatedAt = os.time() * 1000,
|
|
})
|
|
end
|
|
|
|
---Get cached widget data
|
|
---@param dbal table
|
|
---@param tenantId string
|
|
---@param widgetType string
|
|
---@param cacheKey string
|
|
---@return table|nil Cached data or nil if expired/not found
|
|
function M.getCachedData(dbal, tenantId, widgetType, cacheKey)
|
|
local key = widgetType .. ':' .. cacheKey
|
|
|
|
local cached = dbal:findFirst('WidgetCache', {
|
|
where = { tenantId = tenantId, cacheKey = key },
|
|
})
|
|
|
|
if cached then
|
|
-- Check expiry
|
|
if cached.expiresAt and cached.expiresAt < (os.time() * 1000) then
|
|
dbal:delete('WidgetCache', cached.id)
|
|
return nil
|
|
end
|
|
|
|
return json.decode(cached.data)
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
---Clear expired cache entries
|
|
---@param dbal table
|
|
---@param tenantId string
|
|
---@return number Number of entries cleared
|
|
function M.clearExpiredCache(dbal, tenantId)
|
|
local now = os.time() * 1000
|
|
local result = dbal:list('WidgetCache', {
|
|
where = { tenantId = tenantId },
|
|
take = 10000,
|
|
})
|
|
|
|
local count = 0
|
|
for _, entry in ipairs(result.items or {}) do
|
|
if entry.expiresAt and entry.expiresAt < now then
|
|
dbal:delete('WidgetCache', entry.id)
|
|
count = count + 1
|
|
end
|
|
end
|
|
|
|
return count
|
|
end
|
|
|
|
return M
|