godcrm/backend/services/widget-library/registration.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

196 lines
6.4 KiB
JavaScript

/**
* Widget Library — Registration & Dashboard Integration
*
* addFromLibrary, registerWidget, registerWidgetSafe, unregisterWidget
*/
import { dbRun, dbGet } from '../../database/connection.js';
import { logger } from '../../utils/logger.js';
import { createWidget, getWidgetById } from '../WidgetService.js';
import { trackUsage } from './user-actions.js';
/**
* Add widget from library to dashboard
* @param {number} dashboardId - Target dashboard ID
* @param {number} sourceWidgetId - Source widget ID from library
* @param {string} mode - 'reference' or 'copy'
* @param {Object} position - { x, y, w, h }
* @param {number} userId - User performing the action
* @returns {Promise<Object>} { widget, mode_used }
*/
export async function addFromLibrary(dashboardId, sourceWidgetId, mode, position, userId) {
// Validate mode
if (!['reference', 'copy'].includes(mode)) {
throw new Error('Invalid mode: must be "reference" or "copy"');
}
// Check source widget exists
const sourceWidget = await getWidgetById(sourceWidgetId);
if (!sourceWidget) {
throw new Error('Source widget not found');
}
// Check dashboard exists
const dashboard = await dbGet('SELECT id FROM dashboards WHERE id = ?', [dashboardId]);
if (!dashboard) {
throw new Error('Dashboard not found');
}
let newWidget;
if (mode === 'reference') {
// Create a reference widget (linked to source)
newWidget = await createWidget({
dashboard_id: dashboardId,
source_widget_id: sourceWidgetId,
widget_type: sourceWidget.widget_type,
preset_name: sourceWidget.preset_name,
code: sourceWidget.code,
title: sourceWidget.title,
description: sourceWidget.description,
icon: sourceWidget.icon,
config: sourceWidget.config,
position,
created_by: userId
});
} else {
// Create a copy (no link to source)
newWidget = await createWidget({
dashboard_id: dashboardId,
source_widget_id: null,
widget_type: sourceWidget.widget_type,
preset_name: sourceWidget.preset_name,
code: sourceWidget.code,
title: sourceWidget.title,
description: sourceWidget.description,
icon: sourceWidget.icon,
config: sourceWidget.config,
position,
created_by: userId
});
}
// Track usage
await trackUsage(userId, sourceWidgetId);
logger.debug({ dashboardId, sourceWidgetId, mode, newWidgetId: newWidget.id }, 'Widget added from library');
return {
widget: newWidget,
mode_used: mode
};
}
/**
* Register a widget in the library
* Called from ModuleService when a module is created, or manually
* @param {number} widgetId - Widget ID to register
* @param {number} spaceId - Space ID
* @param {Object} options - { is_public, is_template, tags }
* @returns {Promise<Object>} Created library entry
*/
export async function registerWidget(widgetId, spaceId, options = {}) {
const {
is_public = false,
is_template = false,
tags = null
} = options;
// Check widget exists
const widget = await dbGet('SELECT id FROM widgets WHERE id = ?', [widgetId]);
if (!widget) {
throw new Error('Widget not found');
}
// Check space exists
const space = await dbGet('SELECT id FROM spaces WHERE id = ?', [spaceId]);
if (!space) {
throw new Error('Space not found');
}
// Check widget not already in library
const existing = await dbGet('SELECT id FROM widget_library WHERE widget_id = ?', [widgetId]);
if (existing) {
throw new Error('Widget already in library');
}
const tagsValue = tags ? `{${tags.join(',')}}` : null;
const result = await dbRun(`
INSERT INTO widget_library (widget_id, space_id, is_public, is_template, tags)
VALUES (?, ?, ?, ?, ?)
`, [widgetId, spaceId, is_public ? 1 : 0, is_template ? 1 : 0, tagsValue]);
logger.debug({ widgetId, spaceId }, 'Widget registered in library');
return await dbGet('SELECT * FROM widget_library WHERE id = ?', [result.lastInsertRowid]);
}
/**
* Safely register a widget in the library (idempotent)
* If widget is already registered, returns the existing entry
* Called from ModuleService.createModule() - ADR-073
* @param {number} widgetId - Widget ID to register
* @param {number} spaceId - Space ID
* @param {Object} options - { is_public, is_template, tags }
* @returns {Promise<Object|null>} Library entry or null if widget/space doesn't exist
*/
export async function registerWidgetSafe(widgetId, spaceId, options = {}) {
const {
is_public = false,
is_template = false,
tags = null
} = options;
// Check widget exists
const widget = await dbGet('SELECT id FROM widgets WHERE id = ?', [widgetId]);
if (!widget) {
logger.warn({ widgetId }, 'Cannot register widget in library: widget not found');
return null;
}
// Check space exists
const space = await dbGet('SELECT id FROM spaces WHERE id = ?', [spaceId]);
if (!space) {
logger.warn({ spaceId }, 'Cannot register widget in library: space not found');
return null;
}
// Check if already in library
const existing = await dbGet('SELECT * FROM widget_library WHERE widget_id = ?', [widgetId]);
if (existing) {
logger.debug({ widgetId, spaceId }, 'Widget already in library, skipping registration');
return existing;
}
// Insert into library
const tagsValue = tags ? `{${tags.join(',')}}` : null;
const result = await dbRun(`
INSERT INTO widget_library (widget_id, space_id, is_public, is_template, tags)
VALUES (?, ?, ?, ?, ?)
`, [widgetId, spaceId, is_public ? 1 : 0, is_template ? 1 : 0, tagsValue]);
logger.debug({ widgetId, spaceId }, 'Widget auto-registered in library');
return await dbGet('SELECT * FROM widget_library WHERE id = ?', [result.lastInsertRowid]);
}
/**
* Unregister a widget from the library
* @param {number} widgetId - Widget ID to unregister
* @returns {Promise<void>}
*/
export async function unregisterWidget(widgetId) {
// Check widget is in library
const existing = await dbGet('SELECT id FROM widget_library WHERE widget_id = ?', [widgetId]);
if (!existing) {
throw new Error('Widget not in library');
}
await dbRun('DELETE FROM widget_library WHERE widget_id = ?', [widgetId]);
// Also clean up related favorites and history
await dbRun('DELETE FROM user_widget_favorites WHERE widget_id = ?', [widgetId]);
await dbRun('DELETE FROM user_widget_history WHERE widget_id = ?', [widgetId]);
logger.debug({ widgetId }, 'Widget unregistered from library');
}