@timmeck/marketing-brain 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +9 -0
- package/README.md +342 -0
- package/dashboard.html +666 -0
- package/dist/api/server.d.ts +15 -0
- package/dist/api/server.js +73 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli/colors.d.ts +43 -0
- package/dist/cli/colors.js +54 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/commands/campaign.d.ts +2 -0
- package/dist/cli/commands/campaign.js +62 -0
- package/dist/cli/commands/campaign.js.map +1 -0
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/config.js +164 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +2 -0
- package/dist/cli/commands/dashboard.js +147 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.js +111 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +2 -0
- package/dist/cli/commands/export.js +37 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/import.d.ts +2 -0
- package/dist/cli/commands/import.js +76 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/insights.d.ts +2 -0
- package/dist/cli/commands/insights.js +41 -0
- package/dist/cli/commands/insights.js.map +1 -0
- package/dist/cli/commands/learn.d.ts +2 -0
- package/dist/cli/commands/learn.js +22 -0
- package/dist/cli/commands/learn.js.map +1 -0
- package/dist/cli/commands/network.d.ts +2 -0
- package/dist/cli/commands/network.js +66 -0
- package/dist/cli/commands/network.js.map +1 -0
- package/dist/cli/commands/post.d.ts +2 -0
- package/dist/cli/commands/post.js +45 -0
- package/dist/cli/commands/post.js.map +1 -0
- package/dist/cli/commands/query.d.ts +2 -0
- package/dist/cli/commands/query.js +96 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/rules.d.ts +2 -0
- package/dist/cli/commands/rules.js +25 -0
- package/dist/cli/commands/rules.js.map +1 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.js +91 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +63 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +2 -0
- package/dist/cli/commands/stop.js +34 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/suggest.d.ts +2 -0
- package/dist/cli/commands/suggest.js +57 -0
- package/dist/cli/commands/suggest.js.map +1 -0
- package/dist/cli/ipc-helper.d.ts +2 -0
- package/dist/cli/ipc-helper.js +26 -0
- package/dist/cli/ipc-helper.js.map +1 -0
- package/dist/cli/update-check.d.ts +2 -0
- package/dist/cli/update-check.js +58 -0
- package/dist/cli/update-check.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +111 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard/renderer.d.ts +11 -0
- package/dist/dashboard/renderer.js +112 -0
- package/dist/dashboard/renderer.js.map +1 -0
- package/dist/dashboard/server.d.ts +15 -0
- package/dist/dashboard/server.js +122 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.js +19 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrations/001_core_schema.d.ts +2 -0
- package/dist/db/migrations/001_core_schema.js +62 -0
- package/dist/db/migrations/001_core_schema.js.map +1 -0
- package/dist/db/migrations/002_learning_schema.d.ts +2 -0
- package/dist/db/migrations/002_learning_schema.js +45 -0
- package/dist/db/migrations/002_learning_schema.js.map +1 -0
- package/dist/db/migrations/003_synapse_schema.d.ts +2 -0
- package/dist/db/migrations/003_synapse_schema.js +26 -0
- package/dist/db/migrations/003_synapse_schema.js.map +1 -0
- package/dist/db/migrations/004_insights_schema.d.ts +2 -0
- package/dist/db/migrations/004_insights_schema.js +37 -0
- package/dist/db/migrations/004_insights_schema.js.map +1 -0
- package/dist/db/migrations/005_fts_indexes.d.ts +2 -0
- package/dist/db/migrations/005_fts_indexes.js +76 -0
- package/dist/db/migrations/005_fts_indexes.js.map +1 -0
- package/dist/db/migrations/index.d.ts +2 -0
- package/dist/db/migrations/index.js +47 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/repositories/audience.repository.d.ts +18 -0
- package/dist/db/repositories/audience.repository.js +45 -0
- package/dist/db/repositories/audience.repository.js.map +1 -0
- package/dist/db/repositories/campaign.repository.d.ts +15 -0
- package/dist/db/repositories/campaign.repository.js +58 -0
- package/dist/db/repositories/campaign.repository.js.map +1 -0
- package/dist/db/repositories/engagement.repository.d.ts +26 -0
- package/dist/db/repositories/engagement.repository.js +83 -0
- package/dist/db/repositories/engagement.repository.js.map +1 -0
- package/dist/db/repositories/insight.repository.d.ts +18 -0
- package/dist/db/repositories/insight.repository.js +87 -0
- package/dist/db/repositories/insight.repository.js.map +1 -0
- package/dist/db/repositories/post.repository.d.ts +21 -0
- package/dist/db/repositories/post.repository.js +105 -0
- package/dist/db/repositories/post.repository.js.map +1 -0
- package/dist/db/repositories/rule.repository.d.ts +16 -0
- package/dist/db/repositories/rule.repository.js +71 -0
- package/dist/db/repositories/rule.repository.js.map +1 -0
- package/dist/db/repositories/strategy.repository.d.ts +16 -0
- package/dist/db/repositories/strategy.repository.js +69 -0
- package/dist/db/repositories/strategy.repository.js.map +1 -0
- package/dist/db/repositories/synapse.repository.d.ts +25 -0
- package/dist/db/repositories/synapse.repository.js +115 -0
- package/dist/db/repositories/synapse.repository.js.map +1 -0
- package/dist/db/repositories/template.repository.d.ts +16 -0
- package/dist/db/repositories/template.repository.js +61 -0
- package/dist/db/repositories/template.repository.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/client.d.ts +13 -0
- package/dist/ipc/client.js +93 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/protocol.d.ts +8 -0
- package/dist/ipc/protocol.js +29 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/router.d.ts +30 -0
- package/dist/ipc/router.js +88 -0
- package/dist/ipc/router.js.map +1 -0
- package/dist/ipc/server.d.ts +14 -0
- package/dist/ipc/server.js +130 -0
- package/dist/ipc/server.js.map +1 -0
- package/dist/learning/confidence-scorer.d.ts +17 -0
- package/dist/learning/confidence-scorer.js +26 -0
- package/dist/learning/confidence-scorer.js.map +1 -0
- package/dist/learning/learning-engine.d.ts +33 -0
- package/dist/learning/learning-engine.js +211 -0
- package/dist/learning/learning-engine.js.map +1 -0
- package/dist/marketing-core.d.ts +17 -0
- package/dist/marketing-core.js +233 -0
- package/dist/marketing-core.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +67 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +3 -0
- package/dist/mcp/tools.js +138 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/research/research-engine.d.ts +28 -0
- package/dist/research/research-engine.js +211 -0
- package/dist/research/research-engine.js.map +1 -0
- package/dist/services/analytics.service.d.ts +116 -0
- package/dist/services/analytics.service.js +69 -0
- package/dist/services/analytics.service.js.map +1 -0
- package/dist/services/audience.service.d.ts +20 -0
- package/dist/services/audience.service.js +30 -0
- package/dist/services/audience.service.js.map +1 -0
- package/dist/services/campaign.service.d.ts +27 -0
- package/dist/services/campaign.service.js +65 -0
- package/dist/services/campaign.service.js.map +1 -0
- package/dist/services/insight.service.d.ts +18 -0
- package/dist/services/insight.service.js +40 -0
- package/dist/services/insight.service.js.map +1 -0
- package/dist/services/post.service.d.ts +48 -0
- package/dist/services/post.service.js +93 -0
- package/dist/services/post.service.js.map +1 -0
- package/dist/services/rule.service.d.ts +29 -0
- package/dist/services/rule.service.js +67 -0
- package/dist/services/rule.service.js.map +1 -0
- package/dist/services/strategy.service.d.ts +17 -0
- package/dist/services/strategy.service.js +39 -0
- package/dist/services/strategy.service.js.map +1 -0
- package/dist/services/synapse.service.d.ts +22 -0
- package/dist/services/synapse.service.js +22 -0
- package/dist/services/synapse.service.js.map +1 -0
- package/dist/services/template.service.d.ts +17 -0
- package/dist/services/template.service.js +37 -0
- package/dist/services/template.service.js.map +1 -0
- package/dist/synapses/activation.d.ts +13 -0
- package/dist/synapses/activation.js +50 -0
- package/dist/synapses/activation.js.map +1 -0
- package/dist/synapses/decay.d.ts +11 -0
- package/dist/synapses/decay.js +27 -0
- package/dist/synapses/decay.js.map +1 -0
- package/dist/synapses/hebbian.d.ts +13 -0
- package/dist/synapses/hebbian.js +35 -0
- package/dist/synapses/hebbian.js.map +1 -0
- package/dist/synapses/pathfinder.d.ts +14 -0
- package/dist/synapses/pathfinder.js +50 -0
- package/dist/synapses/pathfinder.js.map +1 -0
- package/dist/synapses/synapse-manager.d.ts +32 -0
- package/dist/synapses/synapse-manager.js +76 -0
- package/dist/synapses/synapse-manager.js.map +1 -0
- package/dist/types/config.types.d.ts +69 -0
- package/dist/types/config.types.js +2 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/ipc.types.d.ts +11 -0
- package/dist/types/ipc.types.js +2 -0
- package/dist/types/ipc.types.js.map +1 -0
- package/dist/types/post.types.d.ts +141 -0
- package/dist/types/post.types.js +2 -0
- package/dist/types/post.types.js.map +1 -0
- package/dist/types/synapse.types.d.ts +23 -0
- package/dist/types/synapse.types.js +2 -0
- package/dist/types/synapse.types.js.map +1 -0
- package/dist/utils/events.d.ts +57 -0
- package/dist/utils/events.js +23 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +5 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +3 -0
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +40 -0
- package/seed-data.json +78 -0
- package/src/api/server.ts +86 -0
- package/src/cli/colors.ts +59 -0
- package/src/cli/commands/campaign.ts +66 -0
- package/src/cli/commands/config.ts +168 -0
- package/src/cli/commands/dashboard.ts +165 -0
- package/src/cli/commands/doctor.ts +110 -0
- package/src/cli/commands/export.ts +40 -0
- package/src/cli/commands/import.ts +84 -0
- package/src/cli/commands/insights.ts +44 -0
- package/src/cli/commands/learn.ts +24 -0
- package/src/cli/commands/network.ts +71 -0
- package/src/cli/commands/post.ts +47 -0
- package/src/cli/commands/query.ts +108 -0
- package/src/cli/commands/rules.ts +27 -0
- package/src/cli/commands/start.ts +100 -0
- package/src/cli/commands/status.ts +73 -0
- package/src/cli/commands/stop.ts +33 -0
- package/src/cli/commands/suggest.ts +64 -0
- package/src/cli/ipc-helper.ts +22 -0
- package/src/cli/update-check.ts +63 -0
- package/src/config.ts +110 -0
- package/src/dashboard/renderer.ts +136 -0
- package/src/dashboard/server.ts +140 -0
- package/src/db/connection.ts +22 -0
- package/src/db/migrations/001_core_schema.ts +63 -0
- package/src/db/migrations/002_learning_schema.ts +46 -0
- package/src/db/migrations/003_synapse_schema.ts +27 -0
- package/src/db/migrations/004_insights_schema.ts +38 -0
- package/src/db/migrations/005_fts_indexes.ts +77 -0
- package/src/db/migrations/index.ts +62 -0
- package/src/db/repositories/audience.repository.ts +53 -0
- package/src/db/repositories/campaign.repository.ts +72 -0
- package/src/db/repositories/engagement.repository.ts +108 -0
- package/src/db/repositories/insight.repository.ts +100 -0
- package/src/db/repositories/post.repository.ts +123 -0
- package/src/db/repositories/rule.repository.ts +87 -0
- package/src/db/repositories/strategy.repository.ts +82 -0
- package/src/db/repositories/synapse.repository.ts +148 -0
- package/src/db/repositories/template.repository.ts +76 -0
- package/src/index.ts +69 -0
- package/src/ipc/client.ts +110 -0
- package/src/ipc/protocol.ts +35 -0
- package/src/ipc/router.ts +126 -0
- package/src/ipc/server.ts +140 -0
- package/src/learning/confidence-scorer.ts +36 -0
- package/src/learning/learning-engine.ts +254 -0
- package/src/marketing-core.ts +285 -0
- package/src/mcp/server.ts +72 -0
- package/src/mcp/tools.ts +216 -0
- package/src/research/research-engine.ts +226 -0
- package/src/services/analytics.service.ts +73 -0
- package/src/services/audience.service.ts +40 -0
- package/src/services/campaign.service.ts +80 -0
- package/src/services/insight.service.ts +54 -0
- package/src/services/post.service.ts +116 -0
- package/src/services/rule.service.ts +90 -0
- package/src/services/strategy.service.ts +53 -0
- package/src/services/synapse.service.ts +32 -0
- package/src/services/template.service.ts +50 -0
- package/src/synapses/activation.ts +80 -0
- package/src/synapses/decay.ts +38 -0
- package/src/synapses/hebbian.ts +68 -0
- package/src/synapses/pathfinder.ts +81 -0
- package/src/synapses/synapse-manager.ts +115 -0
- package/src/types/config.types.ts +79 -0
- package/src/types/ipc.types.ts +8 -0
- package/src/types/post.types.ts +156 -0
- package/src/types/synapse.types.ts +43 -0
- package/src/utils/events.ts +44 -0
- package/src/utils/hash.ts +5 -0
- package/src/utils/logger.ts +48 -0
- package/src/utils/paths.ts +19 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
function escapeHtml(str) {
|
|
2
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
3
|
+
}
|
|
4
|
+
export function renderDashboard(template, services) {
|
|
5
|
+
let html = template;
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
const summary = services.analytics.getSummary();
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
const dashData = services.analytics.getDashboardData();
|
|
10
|
+
const insights = services.insight.listActive(200);
|
|
11
|
+
const rules = services.rule.listRules();
|
|
12
|
+
const strongest = services.synapse.getStrongest(50);
|
|
13
|
+
const s = summary;
|
|
14
|
+
// Stats
|
|
15
|
+
html = html.replace('{{POSTS}}', String(s.posts?.total ?? 0));
|
|
16
|
+
html = html.replace('{{CAMPAIGNS}}', String(s.campaigns?.total ?? 0));
|
|
17
|
+
html = html.replace('{{STRATEGIES}}', String(s.strategies?.total ?? 0));
|
|
18
|
+
html = html.replace('{{RULES}}', String(s.rules?.total ?? 0));
|
|
19
|
+
html = html.replace('{{TEMPLATES}}', String(s.templates?.total ?? 0));
|
|
20
|
+
html = html.replace('{{SYNAPSES}}', String(s.network?.synapses ?? 0));
|
|
21
|
+
// Activity score
|
|
22
|
+
const activity = Math.min(100, Math.round(((s.posts?.total ?? 0) * 5 +
|
|
23
|
+
(s.campaigns?.total ?? 0) * 10 +
|
|
24
|
+
(s.strategies?.total ?? 0) * 3 +
|
|
25
|
+
(s.rules?.active ?? 0) * 15 +
|
|
26
|
+
(s.insights?.active ?? 0) * 5) / 2));
|
|
27
|
+
html = html.replace(/\{\{ACTIVITY\}\}/g, String(activity));
|
|
28
|
+
// Version
|
|
29
|
+
html = html.replace('{{VERSION}}', '0.1.0');
|
|
30
|
+
// Platform chart
|
|
31
|
+
const platforms = s.posts?.byPlatform ?? {};
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
const maxCount = Math.max(1, ...Object.values(platforms));
|
|
34
|
+
let platformHtml = '';
|
|
35
|
+
for (const [platform, count] of Object.entries(platforms)) {
|
|
36
|
+
const width = Math.round((count / maxCount) * 100);
|
|
37
|
+
const barClass = `${platform}-bar`;
|
|
38
|
+
platformHtml += `<div class="platform-row"><span class="platform-name">${escapeHtml(platform)}</span><div class="platform-bar-bg"><div class="platform-bar ${barClass}" data-width="${width}"></div></div><span class="platform-count">${count}</span></div>\n`;
|
|
39
|
+
}
|
|
40
|
+
if (!platformHtml)
|
|
41
|
+
platformHtml = '<p class="empty">No posts tracked yet.</p>';
|
|
42
|
+
html = html.replace('{{PLATFORM_CHART}}', platformHtml);
|
|
43
|
+
// Top posts
|
|
44
|
+
const topPosts = dashData.topPerformers?.topPosts ?? [];
|
|
45
|
+
let postsHtml = '';
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
for (const post of topPosts.slice(0, 10)) {
|
|
48
|
+
const score = (post.likes ?? 0) + (post.comments ?? 0) * 3 + (post.shares ?? 0) * 5 + (post.clicks ?? 0) * 2;
|
|
49
|
+
const preview = escapeHtml((post.content ?? '').slice(0, 140));
|
|
50
|
+
postsHtml += `<div class="post-card ${post.platform}"><div class="post-meta"><span class="post-platform ${post.platform}">${escapeHtml(post.platform)}</span><strong>${escapeHtml(post.format ?? 'text')}</strong></div><p>${preview}</p><div class="post-engagement"><span>❤ ${post.likes ?? 0}</span><span>💬 ${post.comments ?? 0}</span><span>🔁 ${post.shares ?? 0}</span><span>Score: ${score}</span></div></div>\n`;
|
|
51
|
+
}
|
|
52
|
+
if (!postsHtml)
|
|
53
|
+
postsHtml = '<p class="empty">No posts with engagement data yet.</p>';
|
|
54
|
+
html = html.replace('{{TOP_POSTS}}', postsHtml);
|
|
55
|
+
// Rules
|
|
56
|
+
const rulesList = Array.isArray(rules) ? rules : [];
|
|
57
|
+
let rulesHtml = '';
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
for (const rule of rulesList) {
|
|
60
|
+
const conf = Math.round((rule.confidence ?? 0) * 100);
|
|
61
|
+
rulesHtml += `<div class="rule-card"><div class="rule-pattern">${escapeHtml(rule.pattern ?? '')}</div><div class="rule-recommendation">${escapeHtml(rule.recommendation ?? '')}</div><div class="rule-confidence"><span>Confidence:</span><div class="confidence-bar"><div class="confidence-fill" data-width="${conf}"></div></div><span>${conf}%</span></div></div>\n`;
|
|
62
|
+
}
|
|
63
|
+
if (!rulesHtml)
|
|
64
|
+
rulesHtml = '<p class="empty">No rules learned yet. Post more content to discover patterns.</p>';
|
|
65
|
+
html = html.replace('{{RULES_LIST}}', rulesHtml);
|
|
66
|
+
// Insights by type
|
|
67
|
+
const allInsights = Array.isArray(insights) ? insights : [];
|
|
68
|
+
const insightsByType = {
|
|
69
|
+
trend: [], gap: [], synergy: [], template: [], optimization: [],
|
|
70
|
+
};
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
for (const ins of allInsights) {
|
|
73
|
+
const type = ins.type ?? 'optimization';
|
|
74
|
+
if (insightsByType[type])
|
|
75
|
+
insightsByType[type].push(ins);
|
|
76
|
+
else
|
|
77
|
+
insightsByType.optimization.push(ins);
|
|
78
|
+
}
|
|
79
|
+
const typeColors = {
|
|
80
|
+
trend: 'cyan', gap: 'orange', synergy: 'green', template: 'purple', optimization: 'blue',
|
|
81
|
+
};
|
|
82
|
+
const pluralMap = {
|
|
83
|
+
trend: 'TRENDS', gap: 'GAPS', synergy: 'SYNERGIES',
|
|
84
|
+
template: 'TEMPLATES', optimization: 'OPTIMIZATIONS',
|
|
85
|
+
};
|
|
86
|
+
for (const [type, items] of Object.entries(insightsByType)) {
|
|
87
|
+
const plural = pluralMap[type] ?? `${type.toUpperCase()}S`;
|
|
88
|
+
html = html.replace(`{{${plural}_COUNT}}`, String(items.length));
|
|
89
|
+
let insHtml = '';
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
for (const ins of items) {
|
|
92
|
+
const prio = ins.priority >= 70 ? 'high' : ins.priority >= 40 ? 'medium' : 'low';
|
|
93
|
+
insHtml += `<div class="insight-card ${typeColors[type] ?? 'blue'}"><div class="insight-header"><span class="prio prio-${prio}">${ins.priority ?? 0}</span><strong>${escapeHtml(ins.title ?? '')}</strong></div><p>${escapeHtml(ins.description ?? '')}</p></div>\n`;
|
|
94
|
+
}
|
|
95
|
+
if (!insHtml)
|
|
96
|
+
insHtml = '<p class="empty">No insights in this category yet.</p>';
|
|
97
|
+
const placeholder = type === 'template' ? '{{TEMPLATES_INSIGHTS}}' : `{{${plural}}}`;
|
|
98
|
+
html = html.replace(placeholder, insHtml);
|
|
99
|
+
}
|
|
100
|
+
// Graph edges
|
|
101
|
+
const edges = Array.isArray(strongest) ? strongest : [];
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
+
const graphEdges = edges.map((e) => ({
|
|
104
|
+
s: `${e.source_type}:${e.source_id}`,
|
|
105
|
+
t: `${e.target_type}:${e.target_id}`,
|
|
106
|
+
type: e.synapse_type ?? 'related',
|
|
107
|
+
w: e.weight ?? 0.5,
|
|
108
|
+
}));
|
|
109
|
+
html = html.replace('{{GRAPH_EDGES}}', JSON.stringify(graphEdges));
|
|
110
|
+
return html;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/dashboard/renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface DashboardServerOptions {
|
|
2
|
+
port: number;
|
|
3
|
+
getDashboardHtml: () => string;
|
|
4
|
+
getStats: () => unknown;
|
|
5
|
+
}
|
|
6
|
+
export declare class DashboardServer {
|
|
7
|
+
private options;
|
|
8
|
+
private server;
|
|
9
|
+
private clients;
|
|
10
|
+
private logger;
|
|
11
|
+
constructor(options: DashboardServerOptions);
|
|
12
|
+
start(): void;
|
|
13
|
+
stop(): void;
|
|
14
|
+
private broadcast;
|
|
15
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import { getEventBus } from '../utils/events.js';
|
|
3
|
+
import { getLogger } from '../utils/logger.js';
|
|
4
|
+
export class DashboardServer {
|
|
5
|
+
options;
|
|
6
|
+
server = null;
|
|
7
|
+
clients = new Set();
|
|
8
|
+
logger = getLogger();
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
start() {
|
|
13
|
+
const { port, getDashboardHtml, getStats } = this.options;
|
|
14
|
+
const bus = getEventBus();
|
|
15
|
+
this.server = http.createServer((req, res) => {
|
|
16
|
+
const url = new URL(req.url ?? '/', `http://localhost:${port}`);
|
|
17
|
+
// CORS
|
|
18
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
19
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
20
|
+
if (req.method === 'OPTIONS') {
|
|
21
|
+
res.writeHead(204);
|
|
22
|
+
res.end();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (url.pathname === '/events') {
|
|
26
|
+
// SSE endpoint
|
|
27
|
+
res.writeHead(200, {
|
|
28
|
+
'Content-Type': 'text/event-stream',
|
|
29
|
+
'Cache-Control': 'no-cache',
|
|
30
|
+
'Connection': 'keep-alive',
|
|
31
|
+
});
|
|
32
|
+
res.write('data: {"type":"connected"}\n\n');
|
|
33
|
+
this.clients.add(res);
|
|
34
|
+
req.on('close', () => this.clients.delete(res));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (url.pathname === '/api/stats') {
|
|
38
|
+
const stats = getStats();
|
|
39
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
40
|
+
res.end(JSON.stringify(stats));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (url.pathname === '/' || url.pathname === '/dashboard') {
|
|
44
|
+
const html = getDashboardHtml();
|
|
45
|
+
// Inject SSE script into the dashboard
|
|
46
|
+
const sseScript = `
|
|
47
|
+
<script>
|
|
48
|
+
(function(){
|
|
49
|
+
const evtSource = new EventSource('/events');
|
|
50
|
+
evtSource.onmessage = function(e) {
|
|
51
|
+
try {
|
|
52
|
+
const data = JSON.parse(e.data);
|
|
53
|
+
if (data.type === 'stats_update') {
|
|
54
|
+
document.querySelectorAll('.stat-number').forEach(el => {
|
|
55
|
+
const key = el.parentElement?.querySelector('.stat-label')?.textContent?.toLowerCase();
|
|
56
|
+
if (key && data.stats[key] !== undefined) {
|
|
57
|
+
el.textContent = Number(data.stats[key]).toLocaleString();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (data.type === 'event') {
|
|
62
|
+
const dot = document.querySelector('.activity-dot');
|
|
63
|
+
if (dot) { dot.style.background = '#ff5577'; setTimeout(() => dot.style.background = '', 500); }
|
|
64
|
+
}
|
|
65
|
+
} catch {}
|
|
66
|
+
};
|
|
67
|
+
evtSource.onerror = function() { setTimeout(() => location.reload(), 5000); };
|
|
68
|
+
})();
|
|
69
|
+
</script>`;
|
|
70
|
+
const liveHtml = html.replace('</body>', sseScript + '</body>');
|
|
71
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
72
|
+
res.end(liveHtml);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
76
|
+
res.end('Not Found');
|
|
77
|
+
});
|
|
78
|
+
// Forward Marketing Brain events to SSE clients
|
|
79
|
+
const eventNames = [
|
|
80
|
+
'post:created', 'post:published', 'engagement:updated',
|
|
81
|
+
'strategy:reported', 'rule:learned', 'rule:triggered',
|
|
82
|
+
'template:created', 'campaign:created',
|
|
83
|
+
'insight:created', 'synapse:created', 'synapse:strengthened',
|
|
84
|
+
];
|
|
85
|
+
for (const eventName of eventNames) {
|
|
86
|
+
bus.on(eventName, (data) => {
|
|
87
|
+
this.broadcast({ type: 'event', event: eventName, data });
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Periodic stats broadcast (every 30s)
|
|
91
|
+
setInterval(() => {
|
|
92
|
+
if (this.clients.size > 0) {
|
|
93
|
+
const stats = getStats();
|
|
94
|
+
this.broadcast({ type: 'stats_update', stats });
|
|
95
|
+
}
|
|
96
|
+
}, 30_000);
|
|
97
|
+
this.server.listen(port, () => {
|
|
98
|
+
this.logger.info(`Dashboard server started on http://localhost:${port}`);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
stop() {
|
|
102
|
+
for (const client of this.clients) {
|
|
103
|
+
client.end();
|
|
104
|
+
}
|
|
105
|
+
this.clients.clear();
|
|
106
|
+
this.server?.close();
|
|
107
|
+
this.server = null;
|
|
108
|
+
this.logger.info('Dashboard server stopped');
|
|
109
|
+
}
|
|
110
|
+
broadcast(data) {
|
|
111
|
+
const msg = `data: ${JSON.stringify(data)}\n\n`;
|
|
112
|
+
for (const client of this.clients) {
|
|
113
|
+
try {
|
|
114
|
+
client.write(msg);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
this.clients.delete(client);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getLogger } from '../utils/logger.js';
|
|
5
|
+
export function createConnection(dbPath) {
|
|
6
|
+
const logger = getLogger();
|
|
7
|
+
const dir = path.dirname(dbPath);
|
|
8
|
+
if (!fs.existsSync(dir)) {
|
|
9
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
logger.info(`Opening database at ${dbPath}`);
|
|
12
|
+
const db = new Database(dbPath);
|
|
13
|
+
db.pragma('journal_mode = WAL');
|
|
14
|
+
db.pragma('synchronous = NORMAL');
|
|
15
|
+
db.pragma('cache_size = 10000');
|
|
16
|
+
db.pragma('foreign_keys = ON');
|
|
17
|
+
return db;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,uBAAuB,MAAM,EAAE,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEhC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function up(db) {
|
|
2
|
+
db.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS campaigns (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
name TEXT NOT NULL UNIQUE,
|
|
6
|
+
brand TEXT,
|
|
7
|
+
goal TEXT,
|
|
8
|
+
platform TEXT,
|
|
9
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
10
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
11
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS posts (
|
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
|
+
campaign_id INTEGER,
|
|
17
|
+
platform TEXT NOT NULL,
|
|
18
|
+
content TEXT NOT NULL,
|
|
19
|
+
format TEXT NOT NULL DEFAULT 'text',
|
|
20
|
+
hashtags TEXT,
|
|
21
|
+
url TEXT,
|
|
22
|
+
published_at TEXT,
|
|
23
|
+
fingerprint TEXT NOT NULL,
|
|
24
|
+
status TEXT NOT NULL DEFAULT 'draft',
|
|
25
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
26
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
27
|
+
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE SET NULL
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE TABLE IF NOT EXISTS engagement (
|
|
31
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
32
|
+
post_id INTEGER NOT NULL,
|
|
33
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
|
34
|
+
likes INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
comments INTEGER NOT NULL DEFAULT 0,
|
|
36
|
+
shares INTEGER NOT NULL DEFAULT 0,
|
|
37
|
+
impressions INTEGER NOT NULL DEFAULT 0,
|
|
38
|
+
clicks INTEGER NOT NULL DEFAULT 0,
|
|
39
|
+
saves INTEGER NOT NULL DEFAULT 0,
|
|
40
|
+
reach INTEGER NOT NULL DEFAULT 0,
|
|
41
|
+
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS audiences (
|
|
45
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
|
+
name TEXT NOT NULL UNIQUE,
|
|
47
|
+
platform TEXT,
|
|
48
|
+
demographics TEXT,
|
|
49
|
+
interests TEXT,
|
|
50
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_posts_campaign ON posts(campaign_id);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_posts_platform ON posts(platform);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_posts_fingerprint ON posts(fingerprint);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_posts_status ON posts(status);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_posts_published ON posts(published_at);
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_engagement_post ON engagement(post_id);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_engagement_timestamp ON engagement(timestamp);
|
|
60
|
+
`);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=001_core_schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"001_core_schema.js","sourceRoot":"","sources":["../../../src/db/migrations/001_core_schema.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,EAAE,CAAC,EAAqB;IACtC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function up(db) {
|
|
2
|
+
db.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS strategies (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
post_id INTEGER,
|
|
6
|
+
description TEXT NOT NULL,
|
|
7
|
+
approach TEXT,
|
|
8
|
+
outcome TEXT,
|
|
9
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
10
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
11
|
+
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE SET NULL
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS marketing_rules (
|
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
|
+
pattern TEXT NOT NULL,
|
|
17
|
+
recommendation TEXT NOT NULL,
|
|
18
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
19
|
+
trigger_count INTEGER NOT NULL DEFAULT 0,
|
|
20
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
21
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
22
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
23
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CREATE TABLE IF NOT EXISTS content_templates (
|
|
27
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
28
|
+
name TEXT NOT NULL,
|
|
29
|
+
structure TEXT NOT NULL,
|
|
30
|
+
example TEXT,
|
|
31
|
+
platform TEXT,
|
|
32
|
+
avg_engagement REAL NOT NULL DEFAULT 0,
|
|
33
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
34
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_strategies_post ON strategies(post_id);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_strategies_confidence ON strategies(confidence);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_rules_active ON marketing_rules(active);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_rules_confidence ON marketing_rules(confidence);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_templates_platform ON content_templates(platform);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_templates_engagement ON content_templates(avg_engagement);
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=002_learning_schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"002_learning_schema.js","sourceRoot":"","sources":["../../../src/db/migrations/002_learning_schema.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,EAAE,CAAC,EAAqB;IACtC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function up(db) {
|
|
2
|
+
db.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS synapses (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
source_type TEXT NOT NULL,
|
|
6
|
+
source_id INTEGER NOT NULL,
|
|
7
|
+
target_type TEXT NOT NULL,
|
|
8
|
+
target_id INTEGER NOT NULL,
|
|
9
|
+
synapse_type TEXT NOT NULL,
|
|
10
|
+
weight REAL NOT NULL DEFAULT 0.5,
|
|
11
|
+
activation_count INTEGER NOT NULL DEFAULT 1,
|
|
12
|
+
last_activated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
13
|
+
metadata TEXT,
|
|
14
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
15
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
16
|
+
UNIQUE(source_type, source_id, target_type, target_id, synapse_type)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_synapses_source ON synapses(source_type, source_id);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_synapses_target ON synapses(target_type, target_id);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_synapses_type ON synapses(synapse_type);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_synapses_weight ON synapses(weight);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_synapses_last_activated ON synapses(last_activated_at);
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=003_synapse_schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"003_synapse_schema.js","sourceRoot":"","sources":["../../../src/db/migrations/003_synapse_schema.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,EAAE,CAAC,EAAqB;IACtC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;GAsBP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function up(db) {
|
|
2
|
+
db.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS insights (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
type TEXT NOT NULL,
|
|
6
|
+
title TEXT NOT NULL,
|
|
7
|
+
description TEXT NOT NULL,
|
|
8
|
+
confidence REAL NOT NULL DEFAULT 0.5,
|
|
9
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
10
|
+
campaign_id INTEGER,
|
|
11
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
12
|
+
expires_at TEXT,
|
|
13
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
14
|
+
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE SET NULL
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS notifications (
|
|
18
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19
|
+
type TEXT NOT NULL,
|
|
20
|
+
title TEXT NOT NULL,
|
|
21
|
+
message TEXT NOT NULL,
|
|
22
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
23
|
+
campaign_id INTEGER,
|
|
24
|
+
acknowledged INTEGER NOT NULL DEFAULT 0,
|
|
25
|
+
acknowledged_at TEXT,
|
|
26
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
27
|
+
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE SET NULL
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_insights_type ON insights(type);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_insights_campaign ON insights(campaign_id);
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_insights_active ON insights(active);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_insights_priority ON insights(priority);
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_acknowledged ON notifications(acknowledged);
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=004_insights_schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"004_insights_schema.js","sourceRoot":"","sources":["../../../src/db/migrations/004_insights_schema.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,EAAE,CAAC,EAAqB;IACtC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function up(db) {
|
|
2
|
+
db.exec(`
|
|
3
|
+
-- Full-text search for posts
|
|
4
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5(
|
|
5
|
+
content, hashtags, platform,
|
|
6
|
+
content='posts',
|
|
7
|
+
content_rowid='id'
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
CREATE TRIGGER IF NOT EXISTS posts_ai AFTER INSERT ON posts BEGIN
|
|
11
|
+
INSERT INTO posts_fts(rowid, content, hashtags, platform)
|
|
12
|
+
VALUES (new.id, new.content, new.hashtags, new.platform);
|
|
13
|
+
END;
|
|
14
|
+
|
|
15
|
+
CREATE TRIGGER IF NOT EXISTS posts_ad AFTER DELETE ON posts BEGIN
|
|
16
|
+
INSERT INTO posts_fts(posts_fts, rowid, content, hashtags, platform)
|
|
17
|
+
VALUES ('delete', old.id, old.content, old.hashtags, old.platform);
|
|
18
|
+
END;
|
|
19
|
+
|
|
20
|
+
CREATE TRIGGER IF NOT EXISTS posts_au AFTER UPDATE ON posts BEGIN
|
|
21
|
+
INSERT INTO posts_fts(posts_fts, rowid, content, hashtags, platform)
|
|
22
|
+
VALUES ('delete', old.id, old.content, old.hashtags, old.platform);
|
|
23
|
+
INSERT INTO posts_fts(rowid, content, hashtags, platform)
|
|
24
|
+
VALUES (new.id, new.content, new.hashtags, new.platform);
|
|
25
|
+
END;
|
|
26
|
+
|
|
27
|
+
-- Full-text search for strategies
|
|
28
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS strategies_fts USING fts5(
|
|
29
|
+
description, approach, outcome,
|
|
30
|
+
content='strategies',
|
|
31
|
+
content_rowid='id'
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
CREATE TRIGGER IF NOT EXISTS strategies_ai AFTER INSERT ON strategies BEGIN
|
|
35
|
+
INSERT INTO strategies_fts(rowid, description, approach, outcome)
|
|
36
|
+
VALUES (new.id, new.description, new.approach, new.outcome);
|
|
37
|
+
END;
|
|
38
|
+
|
|
39
|
+
CREATE TRIGGER IF NOT EXISTS strategies_ad AFTER DELETE ON strategies BEGIN
|
|
40
|
+
INSERT INTO strategies_fts(strategies_fts, rowid, description, approach, outcome)
|
|
41
|
+
VALUES ('delete', old.id, old.description, old.approach, old.outcome);
|
|
42
|
+
END;
|
|
43
|
+
|
|
44
|
+
CREATE TRIGGER IF NOT EXISTS strategies_au AFTER UPDATE ON strategies BEGIN
|
|
45
|
+
INSERT INTO strategies_fts(strategies_fts, rowid, description, approach, outcome)
|
|
46
|
+
VALUES ('delete', old.id, old.description, old.approach, old.outcome);
|
|
47
|
+
INSERT INTO strategies_fts(rowid, description, approach, outcome)
|
|
48
|
+
VALUES (new.id, new.description, new.approach, new.outcome);
|
|
49
|
+
END;
|
|
50
|
+
|
|
51
|
+
-- Full-text search for content templates
|
|
52
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS content_templates_fts USING fts5(
|
|
53
|
+
name, structure, example,
|
|
54
|
+
content='content_templates',
|
|
55
|
+
content_rowid='id'
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
CREATE TRIGGER IF NOT EXISTS templates_ai AFTER INSERT ON content_templates BEGIN
|
|
59
|
+
INSERT INTO content_templates_fts(rowid, name, structure, example)
|
|
60
|
+
VALUES (new.id, new.name, new.structure, new.example);
|
|
61
|
+
END;
|
|
62
|
+
|
|
63
|
+
CREATE TRIGGER IF NOT EXISTS templates_ad AFTER DELETE ON content_templates BEGIN
|
|
64
|
+
INSERT INTO content_templates_fts(content_templates_fts, rowid, name, structure, example)
|
|
65
|
+
VALUES ('delete', old.id, old.name, old.structure, old.example);
|
|
66
|
+
END;
|
|
67
|
+
|
|
68
|
+
CREATE TRIGGER IF NOT EXISTS templates_au AFTER UPDATE ON content_templates BEGIN
|
|
69
|
+
INSERT INTO content_templates_fts(content_templates_fts, rowid, name, structure, example)
|
|
70
|
+
VALUES ('delete', old.id, old.name, old.structure, old.example);
|
|
71
|
+
INSERT INTO content_templates_fts(rowid, name, structure, example)
|
|
72
|
+
VALUES (new.id, new.name, new.structure, new.example);
|
|
73
|
+
END;
|
|
74
|
+
`);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=005_fts_indexes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"005_fts_indexes.js","sourceRoot":"","sources":["../../../src/db/migrations/005_fts_indexes.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,EAAE,CAAC,EAAqB;IACtC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getLogger } from '../../utils/logger.js';
|
|
2
|
+
import { up as coreSchema } from './001_core_schema.js';
|
|
3
|
+
import { up as learningSchema } from './002_learning_schema.js';
|
|
4
|
+
import { up as synapseSchema } from './003_synapse_schema.js';
|
|
5
|
+
import { up as insightsSchema } from './004_insights_schema.js';
|
|
6
|
+
import { up as ftsIndexes } from './005_fts_indexes.js';
|
|
7
|
+
const migrations = [
|
|
8
|
+
{ version: 1, name: '001_core_schema', up: coreSchema },
|
|
9
|
+
{ version: 2, name: '002_learning_schema', up: learningSchema },
|
|
10
|
+
{ version: 3, name: '003_synapse_schema', up: synapseSchema },
|
|
11
|
+
{ version: 4, name: '004_insights_schema', up: insightsSchema },
|
|
12
|
+
{ version: 5, name: '005_fts_indexes', up: ftsIndexes },
|
|
13
|
+
];
|
|
14
|
+
function ensureMigrationsTable(db) {
|
|
15
|
+
db.exec(`
|
|
16
|
+
CREATE TABLE IF NOT EXISTS migrations (
|
|
17
|
+
version INTEGER PRIMARY KEY,
|
|
18
|
+
name TEXT NOT NULL,
|
|
19
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
20
|
+
);
|
|
21
|
+
`);
|
|
22
|
+
}
|
|
23
|
+
function getCurrentVersion(db) {
|
|
24
|
+
const row = db.prepare('SELECT MAX(version) as version FROM migrations').get();
|
|
25
|
+
return row?.version ?? 0;
|
|
26
|
+
}
|
|
27
|
+
export function runMigrations(db) {
|
|
28
|
+
const logger = getLogger();
|
|
29
|
+
ensureMigrationsTable(db);
|
|
30
|
+
const currentVersion = getCurrentVersion(db);
|
|
31
|
+
const pending = migrations.filter(m => m.version > currentVersion);
|
|
32
|
+
if (pending.length === 0) {
|
|
33
|
+
logger.info('Database is up to date');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
logger.info(`Running ${pending.length} migration(s) from version ${currentVersion}`);
|
|
37
|
+
const runAll = db.transaction(() => {
|
|
38
|
+
for (const migration of pending) {
|
|
39
|
+
logger.info(`Applying migration ${migration.name}`);
|
|
40
|
+
migration.up(db);
|
|
41
|
+
db.prepare('INSERT INTO migrations (version, name) VALUES (?, ?)').run(migration.version, migration.name);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
runAll();
|
|
45
|
+
logger.info(`Migrations complete. Now at version ${pending[pending.length - 1].version}`);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=index.js.map
|