@creezio/observability 0.22.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/README.md +328 -0
- package/dist/.creezio-src-hash.json +4 -0
- package/dist/api-endpoints/http-routes.d.ts +11 -0
- package/dist/api-endpoints/http-routes.d.ts.map +1 -0
- package/dist/api-endpoints/http-routes.js +11 -0
- package/dist/api-endpoints/http-routes.js.map +1 -0
- package/dist/api-endpoints/index.d.ts +4 -0
- package/dist/api-endpoints/index.d.ts.map +1 -0
- package/dist/api-endpoints/index.js +3 -0
- package/dist/api-endpoints/index.js.map +1 -0
- package/dist/api-endpoints/registry.d.ts +66 -0
- package/dist/api-endpoints/registry.d.ts.map +1 -0
- package/dist/api-endpoints/registry.js +102 -0
- package/dist/api-endpoints/registry.js.map +1 -0
- package/dist/api-mount.d.ts +7 -0
- package/dist/api-mount.d.ts.map +1 -0
- package/dist/api-mount.js +114 -0
- package/dist/api-mount.js.map +1 -0
- package/dist/helpers.d.ts +23 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +36 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/memory-store.d.ts +3 -0
- package/dist/memory-store.d.ts.map +1 -0
- package/dist/memory-store.js +113 -0
- package/dist/memory-store.js.map +1 -0
- package/dist/ops/emit.d.ts +8 -0
- package/dist/ops/emit.d.ts.map +1 -0
- package/dist/ops/emit.js +15 -0
- package/dist/ops/emit.js.map +1 -0
- package/dist/ops/fleet-activity.d.ts +50 -0
- package/dist/ops/fleet-activity.d.ts.map +1 -0
- package/dist/ops/fleet-activity.js +123 -0
- package/dist/ops/fleet-activity.js.map +1 -0
- package/dist/ops/fleet-agent.d.ts +102 -0
- package/dist/ops/fleet-agent.d.ts.map +1 -0
- package/dist/ops/fleet-agent.js +352 -0
- package/dist/ops/fleet-agent.js.map +1 -0
- package/dist/ops/fleet-samples.d.ts +33 -0
- package/dist/ops/fleet-samples.d.ts.map +1 -0
- package/dist/ops/fleet-samples.js +223 -0
- package/dist/ops/fleet-samples.js.map +1 -0
- package/dist/ops/journal.d.ts +56 -0
- package/dist/ops/journal.d.ts.map +1 -0
- package/dist/ops/journal.js +307 -0
- package/dist/ops/journal.js.map +1 -0
- package/dist/ops/rules.d.ts +16 -0
- package/dist/ops/rules.d.ts.map +1 -0
- package/dist/ops/rules.js +98 -0
- package/dist/ops/rules.js.map +1 -0
- package/dist/ops/types.d.ts +69 -0
- package/dist/ops/types.d.ts.map +1 -0
- package/dist/ops/types.js +119 -0
- package/dist/ops/types.js.map +1 -0
- package/dist/request-logs/config.d.ts +19 -0
- package/dist/request-logs/config.d.ts.map +1 -0
- package/dist/request-logs/config.js +36 -0
- package/dist/request-logs/config.js.map +1 -0
- package/dist/request-logs/http-routes.d.ts +8 -0
- package/dist/request-logs/http-routes.d.ts.map +1 -0
- package/dist/request-logs/http-routes.js +29 -0
- package/dist/request-logs/http-routes.js.map +1 -0
- package/dist/request-logs/index.d.ts +6 -0
- package/dist/request-logs/index.d.ts.map +1 -0
- package/dist/request-logs/index.js +5 -0
- package/dist/request-logs/index.js.map +1 -0
- package/dist/request-logs/middleware.d.ts +11 -0
- package/dist/request-logs/middleware.d.ts.map +1 -0
- package/dist/request-logs/middleware.js +198 -0
- package/dist/request-logs/middleware.js.map +1 -0
- package/dist/request-logs/request-logs.d.ts +60 -0
- package/dist/request-logs/request-logs.d.ts.map +1 -0
- package/dist/request-logs/request-logs.js +260 -0
- package/dist/request-logs/request-logs.js.map +1 -0
- package/dist/schema.d.ts +3 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +21 -0
- package/dist/schema.js.map +1 -0
- package/dist/sqlite-driver.d.ts +16 -0
- package/dist/sqlite-driver.d.ts.map +1 -0
- package/dist/sqlite-driver.js +15 -0
- package/dist/sqlite-driver.js.map +1 -0
- package/dist/sqlite-store.d.ts +12 -0
- package/dist/sqlite-store.d.ts.map +1 -0
- package/dist/sqlite-store.js +176 -0
- package/dist/sqlite-store.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/usage/adapters.d.ts +31 -0
- package/dist/usage/adapters.d.ts.map +1 -0
- package/dist/usage/adapters.js +37 -0
- package/dist/usage/adapters.js.map +1 -0
- package/dist/usage/http-routes.d.ts +23 -0
- package/dist/usage/http-routes.d.ts.map +1 -0
- package/dist/usage/http-routes.js +150 -0
- package/dist/usage/http-routes.js.map +1 -0
- package/dist/usage/index.d.ts +11 -0
- package/dist/usage/index.d.ts.map +1 -0
- package/dist/usage/index.js +7 -0
- package/dist/usage/index.js.map +1 -0
- package/dist/usage/ui-brand.d.ts +23 -0
- package/dist/usage/ui-brand.d.ts.map +1 -0
- package/dist/usage/ui-brand.js +18 -0
- package/dist/usage/ui-brand.js.map +1 -0
- package/dist/usage/usage-analytics-productivity.d.ts +73 -0
- package/dist/usage/usage-analytics-productivity.d.ts.map +1 -0
- package/dist/usage/usage-analytics-productivity.js +317 -0
- package/dist/usage/usage-analytics-productivity.js.map +1 -0
- package/dist/usage/usage-analytics-shared.d.ts +5 -0
- package/dist/usage/usage-analytics-shared.d.ts.map +1 -0
- package/dist/usage/usage-analytics-shared.js +20 -0
- package/dist/usage/usage-analytics-shared.js.map +1 -0
- package/dist/usage/usage-analytics.d.ts +127 -0
- package/dist/usage/usage-analytics.d.ts.map +1 -0
- package/dist/usage/usage-analytics.js +338 -0
- package/dist/usage/usage-analytics.js.map +1 -0
- package/dist-cjs/api-endpoints/http-routes.js +14 -0
- package/dist-cjs/api-endpoints/http-routes.js.map +1 -0
- package/dist-cjs/api-endpoints/index.js +10 -0
- package/dist-cjs/api-endpoints/index.js.map +1 -0
- package/dist-cjs/api-endpoints/registry.js +107 -0
- package/dist-cjs/api-endpoints/registry.js.map +1 -0
- package/dist-cjs/api-mount.js +117 -0
- package/dist-cjs/api-mount.js.map +1 -0
- package/dist-cjs/helpers.js +41 -0
- package/dist-cjs/helpers.js.map +1 -0
- package/dist-cjs/index.js +118 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/memory-store.js +119 -0
- package/dist-cjs/memory-store.js.map +1 -0
- package/dist-cjs/ops/emit.js +18 -0
- package/dist-cjs/ops/emit.js.map +1 -0
- package/dist-cjs/ops/fleet-activity.js +131 -0
- package/dist-cjs/ops/fleet-activity.js.map +1 -0
- package/dist-cjs/ops/fleet-agent.js +358 -0
- package/dist-cjs/ops/fleet-agent.js.map +1 -0
- package/dist-cjs/ops/fleet-samples.js +229 -0
- package/dist-cjs/ops/fleet-samples.js.map +1 -0
- package/dist-cjs/ops/journal.js +326 -0
- package/dist-cjs/ops/journal.js.map +1 -0
- package/dist-cjs/ops/rules.js +102 -0
- package/dist-cjs/ops/rules.js.map +1 -0
- package/dist-cjs/ops/types.js +126 -0
- package/dist-cjs/ops/types.js.map +1 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/request-logs/config.js +42 -0
- package/dist-cjs/request-logs/config.js.map +1 -0
- package/dist-cjs/request-logs/http-routes.js +32 -0
- package/dist-cjs/request-logs/http-routes.js.map +1 -0
- package/dist-cjs/request-logs/index.js +27 -0
- package/dist-cjs/request-logs/index.js.map +1 -0
- package/dist-cjs/request-logs/middleware.js +203 -0
- package/dist-cjs/request-logs/middleware.js.map +1 -0
- package/dist-cjs/request-logs/request-logs.js +277 -0
- package/dist-cjs/request-logs/request-logs.js.map +1 -0
- package/dist-cjs/schema.js +24 -0
- package/dist-cjs/schema.js.map +1 -0
- package/dist-cjs/sqlite-driver.js +18 -0
- package/dist-cjs/sqlite-driver.js.map +1 -0
- package/dist-cjs/sqlite-store.js +182 -0
- package/dist-cjs/sqlite-store.js.map +1 -0
- package/dist-cjs/types.js +12 -0
- package/dist-cjs/types.js.map +1 -0
- package/dist-cjs/usage/adapters.js +47 -0
- package/dist-cjs/usage/adapters.js.map +1 -0
- package/dist-cjs/usage/http-routes.js +154 -0
- package/dist-cjs/usage/http-routes.js.map +1 -0
- package/dist-cjs/usage/index.js +34 -0
- package/dist-cjs/usage/index.js.map +1 -0
- package/dist-cjs/usage/ui-brand.js +23 -0
- package/dist-cjs/usage/ui-brand.js.map +1 -0
- package/dist-cjs/usage/usage-analytics-productivity.js +321 -0
- package/dist-cjs/usage/usage-analytics-productivity.js.map +1 -0
- package/dist-cjs/usage/usage-analytics-shared.js +23 -0
- package/dist-cjs/usage/usage-analytics-shared.js.map +1 -0
- package/dist-cjs/usage/usage-analytics.js +353 -0
- package/dist-cjs/usage/usage-analytics.js.map +1 -0
- package/fleet-collector/AGENTS.md +7 -0
- package/fleet-collector/README.md +83 -0
- package/fleet-collector/configure-fleet-npm.sh +108 -0
- package/fleet-collector/env.mjs +174 -0
- package/fleet-collector/ops-api.mjs +455 -0
- package/fleet-collector/public/admin.html +696 -0
- package/fleet-collector/public/index.html +941 -0
- package/fleet-collector/server.mjs +564 -0
- package/fleet-collector/test-fleet-collector.mjs +413 -0
- package/fleet-collector/test-server-admin.mjs +475 -0
- package/package.json +109 -0
- package/scripts/test-api-endpoints-registry.mjs +60 -0
- package/ui/analytics-client.tsx +834 -0
- package/ui/analytics-productivity-panel.tsx +513 -0
- package/ui/api-endpoints-client.tsx +228 -0
- package/ui/index.ts +39 -0
- package/ui/primitives/badge.tsx +37 -0
- package/ui/primitives/button.tsx +44 -0
- package/ui/primitives/card.tsx +44 -0
- package/ui/primitives/cn.ts +6 -0
- package/ui/primitives/input.tsx +19 -0
- package/ui/primitives/tabs.tsx +51 -0
- package/ui/request-logs-client.tsx +327 -0
- package/ui/session-usage-analytics-provider.tsx +28 -0
- package/ui/usage-analytics-client.ts +479 -0
- package/ui/usage-analytics-provider.tsx +84 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatDuration = void 0;
|
|
4
|
+
exports.usageAnalyticsReady = usageAnalyticsReady;
|
|
5
|
+
exports.ensureUsageAnalyticsSchema = ensureUsageAnalyticsSchema;
|
|
6
|
+
exports.insertUsageEvents = insertUsageEvents;
|
|
7
|
+
exports.recordUsageEvent = recordUsageEvent;
|
|
8
|
+
exports.resolvePeriodFilters = resolvePeriodFilters;
|
|
9
|
+
exports.getUsageOverview = getUsageOverview;
|
|
10
|
+
exports.getUsageTimeline = getUsageTimeline;
|
|
11
|
+
exports.getTopPages = getTopPages;
|
|
12
|
+
exports.getTopClicks = getTopClicks;
|
|
13
|
+
exports.getUserStats = getUserStats;
|
|
14
|
+
exports.listUsageEvents = listUsageEvents;
|
|
15
|
+
exports.purgeUsageEvents = purgeUsageEvents;
|
|
16
|
+
/**
|
|
17
|
+
* Analytics d'usage — persistance SQLite + agrégations Admin.
|
|
18
|
+
* Événements UI (pages, clics, dwell) + actions serveur (agent IA…).
|
|
19
|
+
*/
|
|
20
|
+
const adapters_js_1 = require("./adapters.js");
|
|
21
|
+
const usage_analytics_shared_js_1 = require("./usage-analytics-shared.js");
|
|
22
|
+
Object.defineProperty(exports, "formatDuration", { enumerable: true, get: function () { return usage_analytics_shared_js_1.formatDuration; } });
|
|
23
|
+
let schemaReady = false;
|
|
24
|
+
function usageAnalyticsReady() {
|
|
25
|
+
return (0, adapters_js_1.uaTableExists)("usage_events");
|
|
26
|
+
}
|
|
27
|
+
/** Crée la table si absente (dev / avant migration Electron). */
|
|
28
|
+
function ensureUsageAnalyticsSchema() {
|
|
29
|
+
if (schemaReady && usageAnalyticsReady())
|
|
30
|
+
return;
|
|
31
|
+
const db = (0, adapters_js_1.uaGetWriteDb)();
|
|
32
|
+
db.exec(`
|
|
33
|
+
CREATE TABLE IF NOT EXISTS usage_events (
|
|
34
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
36
|
+
event_type TEXT NOT NULL,
|
|
37
|
+
category TEXT NOT NULL DEFAULT 'ui',
|
|
38
|
+
label TEXT,
|
|
39
|
+
path TEXT,
|
|
40
|
+
referrer_path TEXT,
|
|
41
|
+
user_id TEXT,
|
|
42
|
+
username TEXT,
|
|
43
|
+
user_kind TEXT NOT NULL DEFAULT 'human',
|
|
44
|
+
user_role TEXT,
|
|
45
|
+
session_id TEXT,
|
|
46
|
+
duration_ms INTEGER,
|
|
47
|
+
meta_json TEXT,
|
|
48
|
+
user_agent TEXT,
|
|
49
|
+
surface TEXT NOT NULL DEFAULT 'crm'
|
|
50
|
+
);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_usage_events_created
|
|
52
|
+
ON usage_events(created_at);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_usage_events_user_created
|
|
54
|
+
ON usage_events(user_id, created_at);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_usage_events_type_created
|
|
56
|
+
ON usage_events(event_type, created_at);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_usage_events_path_created
|
|
58
|
+
ON usage_events(path, created_at);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_usage_events_kind_created
|
|
60
|
+
ON usage_events(user_kind, created_at);
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_usage_events_session
|
|
62
|
+
ON usage_events(session_id, created_at);
|
|
63
|
+
`);
|
|
64
|
+
schemaReady = true;
|
|
65
|
+
}
|
|
66
|
+
function normalizeKind(v) {
|
|
67
|
+
const s = String(v || "").toLowerCase();
|
|
68
|
+
if (s === "ai")
|
|
69
|
+
return "ai";
|
|
70
|
+
if (s === "system")
|
|
71
|
+
return "system";
|
|
72
|
+
if (s === "human")
|
|
73
|
+
return "human";
|
|
74
|
+
return "unknown";
|
|
75
|
+
}
|
|
76
|
+
function clampStr(v, max) {
|
|
77
|
+
if (v == null)
|
|
78
|
+
return null;
|
|
79
|
+
const s = String(v).trim();
|
|
80
|
+
if (!s)
|
|
81
|
+
return null;
|
|
82
|
+
return s.slice(0, max);
|
|
83
|
+
}
|
|
84
|
+
function periodBounds(period, now = new Date()) {
|
|
85
|
+
const to = now.toISOString();
|
|
86
|
+
const d = new Date(now);
|
|
87
|
+
if (period === "day")
|
|
88
|
+
d.setUTCDate(d.getUTCDate() - 1);
|
|
89
|
+
else if (period === "week")
|
|
90
|
+
d.setUTCDate(d.getUTCDate() - 7);
|
|
91
|
+
else if (period === "month")
|
|
92
|
+
d.setUTCDate(d.getUTCDate() - 30);
|
|
93
|
+
else
|
|
94
|
+
d.setUTCFullYear(d.getUTCFullYear() - 1);
|
|
95
|
+
return { from: d.toISOString(), to };
|
|
96
|
+
}
|
|
97
|
+
function buildWhere(filters) {
|
|
98
|
+
const parts = ["1=1"];
|
|
99
|
+
const params = [];
|
|
100
|
+
if (filters.from) {
|
|
101
|
+
parts.push("created_at >= ?");
|
|
102
|
+
params.push(filters.from);
|
|
103
|
+
}
|
|
104
|
+
if (filters.to) {
|
|
105
|
+
parts.push("created_at <= ?");
|
|
106
|
+
params.push(filters.to);
|
|
107
|
+
}
|
|
108
|
+
if (filters.userId) {
|
|
109
|
+
parts.push("user_id = ?");
|
|
110
|
+
params.push(filters.userId);
|
|
111
|
+
}
|
|
112
|
+
if (filters.kind && filters.kind !== "all") {
|
|
113
|
+
parts.push("user_kind = ?");
|
|
114
|
+
params.push(filters.kind);
|
|
115
|
+
}
|
|
116
|
+
if (filters.eventType) {
|
|
117
|
+
parts.push("event_type = ?");
|
|
118
|
+
params.push(filters.eventType);
|
|
119
|
+
}
|
|
120
|
+
if (filters.path) {
|
|
121
|
+
parts.push("path LIKE ?");
|
|
122
|
+
params.push(`${filters.path}%`);
|
|
123
|
+
}
|
|
124
|
+
if (filters.q) {
|
|
125
|
+
parts.push("(label LIKE ? OR path LIKE ? OR username LIKE ? OR event_type LIKE ?)");
|
|
126
|
+
const like = `%${filters.q}%`;
|
|
127
|
+
params.push(like, like, like, like);
|
|
128
|
+
}
|
|
129
|
+
return { sql: parts.join(" AND "), params };
|
|
130
|
+
}
|
|
131
|
+
function insertUsageEvents(events) {
|
|
132
|
+
if (!events.length)
|
|
133
|
+
return 0;
|
|
134
|
+
ensureUsageAnalyticsSchema();
|
|
135
|
+
const db = (0, adapters_js_1.uaGetWriteDb)();
|
|
136
|
+
const stmt = db.prepare(`
|
|
137
|
+
INSERT INTO usage_events (
|
|
138
|
+
created_at, event_type, category, label, path, referrer_path,
|
|
139
|
+
user_id, username, user_kind, user_role, session_id,
|
|
140
|
+
duration_ms, meta_json, user_agent, surface
|
|
141
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
142
|
+
`);
|
|
143
|
+
const now = new Date().toISOString();
|
|
144
|
+
let inserted = 0;
|
|
145
|
+
const insertRows = (rows) => {
|
|
146
|
+
for (const e of rows) {
|
|
147
|
+
const eventType = clampStr(e.eventType, 80);
|
|
148
|
+
if (!eventType)
|
|
149
|
+
continue;
|
|
150
|
+
const duration = typeof e.durationMs === "number" && Number.isFinite(e.durationMs) && e.durationMs >= 0
|
|
151
|
+
? Math.min(Math.round(e.durationMs), 86_400_000)
|
|
152
|
+
: null;
|
|
153
|
+
stmt.run(clampStr(e.createdAt, 40) || now, eventType, clampStr(e.category, 40) || "ui", clampStr(e.label, 240), clampStr(e.path, 500), clampStr(e.referrerPath, 500), clampStr(e.userId, 80), clampStr(e.username, 120), normalizeKind(e.userKind), clampStr(e.userRole, 40), clampStr(e.sessionId, 80), duration, e.meta ? JSON.stringify(e.meta).slice(0, 4000) : null, clampStr(e.userAgent, 300), clampStr(e.surface, 40) || "crm");
|
|
154
|
+
inserted += 1;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
if (db.transaction)
|
|
158
|
+
db.transaction(insertRows)(events);
|
|
159
|
+
else
|
|
160
|
+
insertRows(events);
|
|
161
|
+
return inserted;
|
|
162
|
+
}
|
|
163
|
+
function recordUsageEvent(event) {
|
|
164
|
+
try {
|
|
165
|
+
insertUsageEvents([event]);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
/* ne jamais casser le flux métier */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function resolvePeriodFilters(period, from, to) {
|
|
172
|
+
const p = ["day", "week", "month", "year"].includes(period)
|
|
173
|
+
? period
|
|
174
|
+
: "week";
|
|
175
|
+
if (from || to) {
|
|
176
|
+
return { from: from || undefined, to: to || undefined };
|
|
177
|
+
}
|
|
178
|
+
return periodBounds(p);
|
|
179
|
+
}
|
|
180
|
+
function getUsageOverview(filters) {
|
|
181
|
+
ensureUsageAnalyticsSchema();
|
|
182
|
+
const { sql, params } = buildWhere(filters);
|
|
183
|
+
const totals = (0, adapters_js_1.uaQueryOne)(`SELECT
|
|
184
|
+
COUNT(*) AS events,
|
|
185
|
+
SUM(CASE WHEN event_type = 'page.view' THEN 1 ELSE 0 END) AS page_views,
|
|
186
|
+
SUM(CASE WHEN event_type = 'ui.click' THEN 1 ELSE 0 END) AS clicks,
|
|
187
|
+
COUNT(DISTINCT session_id) AS sessions,
|
|
188
|
+
COUNT(DISTINCT user_id) AS active_users,
|
|
189
|
+
COUNT(DISTINCT CASE WHEN user_kind = 'human' THEN user_id END) AS active_humans,
|
|
190
|
+
COUNT(DISTINCT CASE WHEN user_kind = 'ai' THEN user_id END) AS active_ai,
|
|
191
|
+
COALESCE(SUM(CASE WHEN duration_ms IS NOT NULL THEN duration_ms ELSE 0 END), 0) AS total_time_ms
|
|
192
|
+
FROM usage_events
|
|
193
|
+
WHERE ${sql}`, params);
|
|
194
|
+
const byKind = (0, adapters_js_1.uaQueryAll)(`SELECT
|
|
195
|
+
user_kind AS kind,
|
|
196
|
+
COUNT(*) AS events,
|
|
197
|
+
COUNT(DISTINCT user_id) AS users,
|
|
198
|
+
COALESCE(SUM(CASE WHEN duration_ms IS NOT NULL THEN duration_ms ELSE 0 END), 0) AS time_ms
|
|
199
|
+
FROM usage_events
|
|
200
|
+
WHERE ${sql}
|
|
201
|
+
GROUP BY user_kind
|
|
202
|
+
ORDER BY events DESC`, params);
|
|
203
|
+
const sessions = totals?.sessions ?? 0;
|
|
204
|
+
const totalTimeMs = totals?.total_time_ms ?? 0;
|
|
205
|
+
return {
|
|
206
|
+
period: { from: filters.from || "", to: filters.to || "" },
|
|
207
|
+
totals: {
|
|
208
|
+
events: totals?.events ?? 0,
|
|
209
|
+
pageViews: totals?.page_views ?? 0,
|
|
210
|
+
clicks: totals?.clicks ?? 0,
|
|
211
|
+
sessions,
|
|
212
|
+
activeUsers: totals?.active_users ?? 0,
|
|
213
|
+
activeHumans: totals?.active_humans ?? 0,
|
|
214
|
+
activeAi: totals?.active_ai ?? 0,
|
|
215
|
+
totalTimeMs,
|
|
216
|
+
avgSessionTimeMs: sessions > 0 ? Math.round(totalTimeMs / sessions) : 0,
|
|
217
|
+
},
|
|
218
|
+
byKind: byKind.map((r) => ({
|
|
219
|
+
kind: r.kind,
|
|
220
|
+
events: r.events,
|
|
221
|
+
users: r.users,
|
|
222
|
+
timeMs: r.time_ms,
|
|
223
|
+
})),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function bucketExpr(period) {
|
|
227
|
+
if (period === "day")
|
|
228
|
+
return `strftime('%Y-%m-%d %H:00', created_at)`;
|
|
229
|
+
if (period === "year")
|
|
230
|
+
return `strftime('%Y-%m', created_at)`;
|
|
231
|
+
return `strftime('%Y-%m-%d', created_at)`;
|
|
232
|
+
}
|
|
233
|
+
function getUsageTimeline(filters, period = "week") {
|
|
234
|
+
ensureUsageAnalyticsSchema();
|
|
235
|
+
const { sql, params } = buildWhere(filters);
|
|
236
|
+
const bucket = bucketExpr(period);
|
|
237
|
+
return (0, adapters_js_1.uaQueryAll)(`SELECT
|
|
238
|
+
${bucket} AS bucket,
|
|
239
|
+
COUNT(*) AS events,
|
|
240
|
+
SUM(CASE WHEN event_type = 'page.view' THEN 1 ELSE 0 END) AS page_views,
|
|
241
|
+
SUM(CASE WHEN event_type = 'ui.click' THEN 1 ELSE 0 END) AS clicks,
|
|
242
|
+
SUM(CASE WHEN user_kind = 'human' THEN 1 ELSE 0 END) AS humans,
|
|
243
|
+
SUM(CASE WHEN user_kind = 'ai' THEN 1 ELSE 0 END) AS ai,
|
|
244
|
+
COALESCE(SUM(CASE WHEN duration_ms IS NOT NULL THEN duration_ms ELSE 0 END), 0) AS time_ms
|
|
245
|
+
FROM usage_events
|
|
246
|
+
WHERE ${sql}
|
|
247
|
+
GROUP BY bucket
|
|
248
|
+
ORDER BY bucket ASC`, params).map((r) => ({
|
|
249
|
+
bucket: r.bucket,
|
|
250
|
+
events: r.events,
|
|
251
|
+
pageViews: r.page_views,
|
|
252
|
+
clicks: r.clicks,
|
|
253
|
+
humans: r.humans,
|
|
254
|
+
ai: r.ai,
|
|
255
|
+
timeMs: r.time_ms,
|
|
256
|
+
}));
|
|
257
|
+
}
|
|
258
|
+
function getTopPages(filters, limit = 25) {
|
|
259
|
+
ensureUsageAnalyticsSchema();
|
|
260
|
+
const { sql, params } = buildWhere(filters);
|
|
261
|
+
return (0, adapters_js_1.uaQueryAll)(`SELECT
|
|
262
|
+
COALESCE(path, '(inconnu)') AS path,
|
|
263
|
+
SUM(CASE WHEN event_type = 'page.view' THEN 1 ELSE 0 END) AS views,
|
|
264
|
+
COUNT(DISTINCT user_id) AS unique_users,
|
|
265
|
+
COALESCE(AVG(CASE WHEN event_type IN ('page.hide','page.dwell') AND duration_ms IS NOT NULL THEN duration_ms END), 0) AS avg_time_ms,
|
|
266
|
+
COALESCE(SUM(CASE WHEN duration_ms IS NOT NULL THEN duration_ms ELSE 0 END), 0) AS total_time_ms
|
|
267
|
+
FROM usage_events
|
|
268
|
+
WHERE ${sql} AND path IS NOT NULL AND path != ''
|
|
269
|
+
GROUP BY path
|
|
270
|
+
HAVING views > 0
|
|
271
|
+
ORDER BY views DESC
|
|
272
|
+
LIMIT ?`, [...params, Math.min(Math.max(limit, 1), 200)]).map((r) => ({
|
|
273
|
+
path: r.path,
|
|
274
|
+
views: r.views,
|
|
275
|
+
uniqueUsers: r.unique_users,
|
|
276
|
+
avgTimeMs: Math.round(r.avg_time_ms || 0),
|
|
277
|
+
totalTimeMs: r.total_time_ms,
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
function getTopClicks(filters, limit = 25) {
|
|
281
|
+
ensureUsageAnalyticsSchema();
|
|
282
|
+
const { sql, params } = buildWhere(filters);
|
|
283
|
+
return (0, adapters_js_1.uaQueryAll)(`SELECT
|
|
284
|
+
COALESCE(NULLIF(TRIM(label), ''), '(sans label)') AS label,
|
|
285
|
+
COUNT(*) AS clicks,
|
|
286
|
+
COUNT(DISTINCT user_id) AS unique_users,
|
|
287
|
+
COUNT(DISTINCT path) AS paths
|
|
288
|
+
FROM usage_events
|
|
289
|
+
WHERE ${sql} AND event_type = 'ui.click'
|
|
290
|
+
GROUP BY label
|
|
291
|
+
ORDER BY clicks DESC
|
|
292
|
+
LIMIT ?`, [...params, Math.min(Math.max(limit, 1), 200)]).map((r) => ({
|
|
293
|
+
label: r.label,
|
|
294
|
+
clicks: r.clicks,
|
|
295
|
+
uniqueUsers: r.unique_users,
|
|
296
|
+
paths: r.paths,
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
function getUserStats(filters, limit = 50) {
|
|
300
|
+
ensureUsageAnalyticsSchema();
|
|
301
|
+
const { sql, params } = buildWhere(filters);
|
|
302
|
+
return (0, adapters_js_1.uaQueryAll)(`SELECT
|
|
303
|
+
COALESCE(user_id, 'anonymous') AS user_id,
|
|
304
|
+
COALESCE(MAX(username), user_id, 'anonymous') AS username,
|
|
305
|
+
MAX(user_kind) AS kind,
|
|
306
|
+
MAX(user_role) AS role,
|
|
307
|
+
COUNT(*) AS events,
|
|
308
|
+
SUM(CASE WHEN event_type = 'page.view' THEN 1 ELSE 0 END) AS page_views,
|
|
309
|
+
SUM(CASE WHEN event_type = 'ui.click' THEN 1 ELSE 0 END) AS clicks,
|
|
310
|
+
COUNT(DISTINCT session_id) AS sessions,
|
|
311
|
+
COALESCE(SUM(CASE WHEN duration_ms IS NOT NULL THEN duration_ms ELSE 0 END), 0) AS time_ms,
|
|
312
|
+
MAX(created_at) AS last_seen
|
|
313
|
+
FROM usage_events
|
|
314
|
+
WHERE ${sql}
|
|
315
|
+
GROUP BY COALESCE(user_id, 'anonymous')
|
|
316
|
+
ORDER BY events DESC
|
|
317
|
+
LIMIT ?`, [...params, Math.min(Math.max(limit, 1), 200)]).map((r) => ({
|
|
318
|
+
userId: r.user_id,
|
|
319
|
+
username: r.username,
|
|
320
|
+
kind: r.kind,
|
|
321
|
+
role: r.role,
|
|
322
|
+
events: r.events,
|
|
323
|
+
pageViews: r.page_views,
|
|
324
|
+
clicks: r.clicks,
|
|
325
|
+
sessions: r.sessions,
|
|
326
|
+
timeMs: r.time_ms,
|
|
327
|
+
lastSeen: r.last_seen,
|
|
328
|
+
}));
|
|
329
|
+
}
|
|
330
|
+
function listUsageEvents(filters) {
|
|
331
|
+
ensureUsageAnalyticsSchema();
|
|
332
|
+
const { sql, params } = buildWhere(filters);
|
|
333
|
+
const limit = Math.min(Math.max(filters.limit ?? 100, 1), 500);
|
|
334
|
+
const offset = Math.max(filters.offset ?? 0, 0);
|
|
335
|
+
const total = (0, adapters_js_1.uaQueryOne)(`SELECT COUNT(*) AS c FROM usage_events WHERE ${sql}`, params)?.c ??
|
|
336
|
+
0;
|
|
337
|
+
const events = (0, adapters_js_1.uaQueryAll)(`SELECT * FROM usage_events WHERE ${sql} ORDER BY created_at DESC, id DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
338
|
+
return { events, total };
|
|
339
|
+
}
|
|
340
|
+
function purgeUsageEvents(opts = {}) {
|
|
341
|
+
ensureUsageAnalyticsSchema();
|
|
342
|
+
const db = (0, adapters_js_1.uaGetWriteDb)();
|
|
343
|
+
if (opts.all) {
|
|
344
|
+
const info = db.prepare(`DELETE FROM usage_events`).run();
|
|
345
|
+
return info.changes;
|
|
346
|
+
}
|
|
347
|
+
if (opts.before) {
|
|
348
|
+
const info = db.prepare(`DELETE FROM usage_events WHERE created_at < ?`).run(opts.before);
|
|
349
|
+
return info.changes;
|
|
350
|
+
}
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=usage-analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-analytics.js","sourceRoot":"","sources":["../../src/usage/usage-analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# AGENTS — observability/fleet-collector
|
|
2
|
+
|
|
3
|
+
Service collector flotte (bin `creezio-fleet-collector`).
|
|
4
|
+
|
|
5
|
+
- Config / env : voir [README.md](./README.md)
|
|
6
|
+
- Package parent : [../README.md](../README.md) · [../AGENTS.md](../AGENTS.md)
|
|
7
|
+
- Ne pas coupler au métier d’une marque ; payloads anonymisés / agrégats
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Fleet Collector — `@creezio/observability`
|
|
2
|
+
|
|
3
|
+
Service ops autonome en Node pur (loopback HTTP + store JSON), **SoT kit**
|
|
4
|
+
— 0 domaine marque hardcodé. Le dossier porte le collector télémétrie :
|
|
5
|
+
|
|
6
|
+
| Fichier | Service | Câblé en prod |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `server.mjs` (+ `ops-api.mjs`, `env.mjs`) | Collector télémétrie (ingest heartbeat/crash/bundle + cockpit ops) — **SoT ici** | Service dédié (ex. legacy `:8665`) |
|
|
9
|
+
|
|
10
|
+
**Backend flotte / agent hôte** : la SoT vit dans
|
|
11
|
+
[`packages/fleet`](../../fleet/README.md) (`@creezio/fleet`, TS strict —
|
|
12
|
+
P2.b 0.15.0). Les 7 wrappers `.mjs` de compat de ce dossier et le bin npm
|
|
13
|
+
`creezio-server-admin` ont été **retirés en 0.19.0**. Les images embarquent
|
|
14
|
+
`packages/fleet/dist` (CMD `node_modules/@creezio/fleet/dist/bin/*-main.js`,
|
|
15
|
+
contexte stagé fail-closed).
|
|
16
|
+
|
|
17
|
+
**Piège prod** : le code flotte est **copié dans les images** au build —
|
|
18
|
+
après toute modif de `packages/fleet/src`, `npm run build:packages` puis
|
|
19
|
+
re-runner `creezio server-docker admin up …` / `agent up …` (sinon nouvelles
|
|
20
|
+
routes → 404 silencieux).
|
|
21
|
+
|
|
22
|
+
## Lancement
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Depuis le kit
|
|
26
|
+
CREEZIO_FLEET_INGEST_TOKEN=… \
|
|
27
|
+
CREEZIO_FLEET_OPS_USER=ops \
|
|
28
|
+
CREEZIO_FLEET_OPS_PASS=… \
|
|
29
|
+
CREEZIO_FLEET_UI_TITLE="TempoFlow Fleet" \
|
|
30
|
+
CREEZIO_FLEET_TUNNEL_SUFFIX=tempoflow.fr \
|
|
31
|
+
FLEET_PUBLIC_DOMAIN=fleet.tempoflow.fr \
|
|
32
|
+
npm run fleet-collector -w @creezio/observability
|
|
33
|
+
|
|
34
|
+
# Ou binaire
|
|
35
|
+
npx creezio-fleet-collector
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Marques (package npm installé) :
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node node_modules/@creezio/observability/fleet-collector/server.mjs
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Env (neutre + dual-read legacy)
|
|
45
|
+
|
|
46
|
+
| Variable | Alias legacy | Rôle |
|
|
47
|
+
|----------|--------------|------|
|
|
48
|
+
| `CREEZIO_FLEET_PORT` / `FLEET_PORT` | `TF2_FLEET_PORT`, `CERTIVAN_FLEET_PORT` | Listen (défaut 8665) |
|
|
49
|
+
| `CREEZIO_FLEET_INGEST_TOKEN` / `FLEET_INGEST_TOKEN` | `TF2_*`, `CERTIVAN_*` | Path ingest `/i-<token>/…` |
|
|
50
|
+
| `CREEZIO_FLEET_OPS_USER` / `_PASS` / `_TOKEN` | idem | Auth ops Basic / Bearer |
|
|
51
|
+
| `CREEZIO_FLEET_DIR` / `FLEET_DIR` | idem | State JSON |
|
|
52
|
+
| `FLEET_PUBLIC_DOMAIN` / `CREEZIO_FLEET_DOMAIN` | — | Domaine public (docs / NPM) |
|
|
53
|
+
| `CREEZIO_FLEET_TUNNEL_SUFFIX` | — | Fallback hostname `${slug}.${suffix}` |
|
|
54
|
+
| `CREEZIO_FLEET_UI_TITLE` / `_MARK` / `_HOME_TITLE` / `_REALM` | — | Branding UI |
|
|
55
|
+
| `CREEZIO_FLEET_UI_EXTRAS_TITLE` | — | Carte extras (ex. « Dossiers VASP ») |
|
|
56
|
+
| `CREEZIO_FLEET_UI_ETAT_LABELS` | — | JSON labels états extras |
|
|
57
|
+
|
|
58
|
+
## Endpoints
|
|
59
|
+
|
|
60
|
+
- Ingest : `POST /i-<token>/heartbeat|crash|bundle`, `GET/POST …/commands`
|
|
61
|
+
- Ops : `/` UI + `/ops/api/*` (Basic / Bearer)
|
|
62
|
+
|
|
63
|
+
## Extras métier (marques)
|
|
64
|
+
|
|
65
|
+
Le collector accepte `dossierStats` opaque dans le heartbeat (agrégation + UI
|
|
66
|
+
si présent). L’émission métier reste marque — ex. Certivan
|
|
67
|
+
`electron/fleet-dossier-samples.ts` via `getHeartbeatExtras`.
|
|
68
|
+
|
|
69
|
+
## Tests
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm run test:fleet-collector -w @creezio/observability
|
|
73
|
+
# ou
|
|
74
|
+
node packages/observability/fleet-collector/test-fleet-collector.mjs
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## DNS / NPM (VPS)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
FLEET_PUBLIC_DOMAIN=fleet.example.com FLEET_PORT=8665 \
|
|
81
|
+
CF_ENV=/path/to/.cloudflare.env \
|
|
82
|
+
bash packages/observability/fleet-collector/configure-fleet-npm.sh
|
|
83
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DNS Cloudflare + NPM proxy host pour le cockpit fleet (domaine injecté).
|
|
3
|
+
#
|
|
4
|
+
# Requis :
|
|
5
|
+
# FLEET_PUBLIC_DOMAIN (ex. fleet.example.com)
|
|
6
|
+
# FLEET_PORT (ex. 8665)
|
|
7
|
+
# CF_ENV chemin .env Cloudflare (token + zone)
|
|
8
|
+
# Optionnel :
|
|
9
|
+
# NPM_CONTAINER, NPM_API, WP_ENV, PROV_SCRIPTS, VPS_IP, FORWARD_HOST
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
: "${FLEET_PUBLIC_DOMAIN:?set FLEET_PUBLIC_DOMAIN}"
|
|
13
|
+
: "${FLEET_PORT:?set FLEET_PORT}"
|
|
14
|
+
: "${CF_ENV:?set CF_ENV (cloudflare env file)}"
|
|
15
|
+
|
|
16
|
+
NPM_CONTAINER="${NPM_CONTAINER:-nginx-proxy-manager}"
|
|
17
|
+
NPM_API="${NPM_API:-http://127.0.0.1:81/api}"
|
|
18
|
+
WP_ENV="${WP_ENV:-/opt/docker/wp-provisioner/.env}"
|
|
19
|
+
PROV_SCRIPTS="${PROV_SCRIPTS:-/opt/docker/wp-provisioner/scripts}"
|
|
20
|
+
CF_API="https://api.cloudflare.com/client/v4"
|
|
21
|
+
|
|
22
|
+
DOMAIN="$FLEET_PUBLIC_DOMAIN"
|
|
23
|
+
PORT="$FLEET_PORT"
|
|
24
|
+
FORWARD_HOST="${FORWARD_HOST:-127.0.0.1}"
|
|
25
|
+
VPS_IP="${VPS_IP:-104.168.10.36}"
|
|
26
|
+
|
|
27
|
+
set -a; . "$WP_ENV"; . "$CF_ENV"; set +a
|
|
28
|
+
: "${NPM_EMAIL:?}"; : "${NPM_PASSWORD:?}"; : "${CF_API_TOKEN:?}"; : "${CF_ZONE_ID:?}"
|
|
29
|
+
|
|
30
|
+
echo "==> DNS A $DOMAIN → $VPS_IP (proxied)"
|
|
31
|
+
EXIST_DNS=$(curl -sS -H "Authorization: Bearer $CF_API_TOKEN" \
|
|
32
|
+
"$CF_API/zones/$CF_ZONE_ID/dns_records?name=$DOMAIN&type=A" | jq -r '.result[0].id // empty')
|
|
33
|
+
DNS_BODY=$(jq -nc --arg n "$DOMAIN" --arg ip "$VPS_IP" \
|
|
34
|
+
'{type:"A",name:$n,content:$ip,ttl:1,proxied:true,comment:"Creezio fleet cockpit"}')
|
|
35
|
+
if [ -n "$EXIST_DNS" ]; then
|
|
36
|
+
curl -sS -X PUT -H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" \
|
|
37
|
+
-d "$DNS_BODY" "$CF_API/zones/$CF_ZONE_ID/dns_records/$EXIST_DNS" | jq -e '.success==true' >/dev/null
|
|
38
|
+
echo " DNS mis à jour ($EXIST_DNS)"
|
|
39
|
+
else
|
|
40
|
+
curl -sS -X POST -H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" \
|
|
41
|
+
-d "$DNS_BODY" "$CF_API/zones/$CF_ZONE_ID/dns_records" | jq -e '.success==true' >/dev/null
|
|
42
|
+
echo " DNS créé"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "==> Authentification NPM"
|
|
46
|
+
TOKEN=$(docker exec "$NPM_CONTAINER" curl -sS -X POST "$NPM_API/tokens" \
|
|
47
|
+
-H "Content-Type: application/json" \
|
|
48
|
+
-d "$(jq -nc --arg i "$NPM_EMAIL" --arg s "$NPM_PASSWORD" '{identity:$i,secret:$s}')" \
|
|
49
|
+
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
|
|
50
|
+
[ -n "$TOKEN" ] || { echo "ERREUR: token NPM vide"; exit 1; }
|
|
51
|
+
|
|
52
|
+
echo "==> Proxy host $DOMAIN -> $FORWARD_HOST:$PORT"
|
|
53
|
+
EXIST=$(docker exec "$NPM_CONTAINER" curl -sS -H "Authorization: Bearer $TOKEN" "$NPM_API/nginx/proxy-hosts" \
|
|
54
|
+
| jq -r --arg d "$DOMAIN" '(if type=="array" then . else [] end)[]|select((.domain_names//[])|index($d)!=null)|.id' | head -1)
|
|
55
|
+
if [ -z "$EXIST" ] || [ "$EXIST" = "null" ]; then
|
|
56
|
+
PAYLOAD=$(jq -nc --arg d "$DOMAIN" --arg fh "$FORWARD_HOST" --argjson p "$PORT" \
|
|
57
|
+
'{domain_names:[$d],forward_scheme:"http",forward_host:$fh,forward_port:$p,
|
|
58
|
+
certificate_id:0,ssl_forced:false,hsts_enabled:false,
|
|
59
|
+
hsts_subdomains:false,http2_support:true,block_exploits:true,caching_enabled:false,
|
|
60
|
+
allow_websocket_upgrade:true,advanced_config:"",enabled:true,
|
|
61
|
+
meta:{letsencrypt_agree:false,dns_challenge:false},locations:[]}')
|
|
62
|
+
HOST_ID=$(docker exec "$NPM_CONTAINER" curl -sS -X POST "$NPM_API/nginx/proxy-hosts" \
|
|
63
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$PAYLOAD" | jq -r '.id // empty')
|
|
64
|
+
[ -n "$HOST_ID" ] || { echo "ERREUR: création proxy host échouée"; exit 1; }
|
|
65
|
+
echo " proxy host créé (id=$HOST_ID)"
|
|
66
|
+
else
|
|
67
|
+
HOST_ID="$EXIST"
|
|
68
|
+
echo " proxy host déjà présent (id=$HOST_ID)"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo "==> Certificat Cloudflare Origin pour $DOMAIN"
|
|
72
|
+
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
|
|
73
|
+
cat > "$TMP/openssl.cnf" <<EOF
|
|
74
|
+
[ req ]
|
|
75
|
+
default_bits = 2048
|
|
76
|
+
prompt = no
|
|
77
|
+
default_md = sha256
|
|
78
|
+
distinguished_name = dn
|
|
79
|
+
req_extensions = v3_req
|
|
80
|
+
[ dn ]
|
|
81
|
+
CN = $DOMAIN
|
|
82
|
+
[ v3_req ]
|
|
83
|
+
subjectAltName = @alt_names
|
|
84
|
+
[ alt_names ]
|
|
85
|
+
DNS.1 = $DOMAIN
|
|
86
|
+
EOF
|
|
87
|
+
openssl req -new -newkey rsa:2048 -nodes -keyout "$TMP/origin.key" -out "$TMP/origin.csr" -config "$TMP/openssl.cnf" >/dev/null 2>&1
|
|
88
|
+
CF_BODY=$(jq -n --rawfile csr "$TMP/origin.csr" --arg d "$DOMAIN" \
|
|
89
|
+
'{hostnames:[$d],requested_validity:5475,request_type:"origin-rsa",csr:$csr,requested_bundle:"false"}')
|
|
90
|
+
CF_RESP=$(curl -sS -X POST "$CF_API/certificates" -H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" -d "$CF_BODY")
|
|
91
|
+
echo "$CF_RESP" | jq -e '.success==true' >/dev/null 2>&1 || { echo "ERREUR Cloudflare Origin CA: $(echo "$CF_RESP" | jq -c '.errors // .' | head -c 600)"; exit 1; }
|
|
92
|
+
CERT_PEM=$(echo "$CF_RESP" | jq -r '.result.certificate')
|
|
93
|
+
|
|
94
|
+
echo "==> Upload TLS NPM"
|
|
95
|
+
export DOM="$DOMAIN"
|
|
96
|
+
export FRONT_DOM=""
|
|
97
|
+
export FRONT_DEV_DOM=""
|
|
98
|
+
export EMAIL="$NPM_EMAIL"
|
|
99
|
+
export PASS="$NPM_PASSWORD"
|
|
100
|
+
export CERT_B64="$(printf '%s' "$CERT_PEM" | base64 | tr -d '\n')"
|
|
101
|
+
export KEY_B64="$(base64 < "$TMP/origin.key" | tr -d '\n')"
|
|
102
|
+
RESULT=$(bash "$PROV_SCRIPTS/n8n-npm-tls-install.sh" 2>&1 | grep -E '^\{.*\}' | tail -1)
|
|
103
|
+
echo " $RESULT"
|
|
104
|
+
echo "$RESULT" | jq -e '.ok==true' >/dev/null 2>&1 || { echo "ERREUR install TLS NPM"; exit 1; }
|
|
105
|
+
|
|
106
|
+
echo "=================================================="
|
|
107
|
+
echo " OK -> https://$DOMAIN/"
|
|
108
|
+
echo "=================================================="
|