securenow 5.0.5 → 5.2.1
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/CONSUMING-APPS-GUIDE.md +17 -17
- package/README.md +7 -7
- package/cli.js +6 -6
- package/console-instrumentation.js +2 -2
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/AUTO-BODY-CAPTURE.md +1 -1
- package/docs/AUTO-SETUP-SUMMARY.md +2 -2
- package/docs/AUTO-SETUP.md +4 -4
- package/docs/AUTOMATIC-IP-CAPTURE.md +5 -5
- package/docs/BODY-CAPTURE-FIX.md +1 -1
- package/docs/BODY-CAPTURE-QUICKSTART.md +2 -2
- package/docs/CHANGELOG-NEXTJS.md +1 -1
- package/docs/COMPLETION-REPORT.md +5 -5
- package/docs/CUSTOMER-GUIDE.md +16 -16
- package/docs/EASIEST-SETUP.md +5 -5
- package/docs/EXPRESS-BODY-CAPTURE.md +10 -10
- package/docs/IMPLEMENTATION-SUMMARY.md +10 -10
- package/docs/LOGGING-GUIDE.md +27 -27
- package/docs/LOGGING-QUICKSTART.md +13 -13
- package/docs/NEXTJS-BODY-CAPTURE.md +2 -2
- package/docs/NEXTJS-GUIDE.md +14 -14
- package/docs/NEXTJS-QUICKSTART.md +1 -1
- package/docs/NEXTJS-WRAPPER-APPROACH.md +1 -1
- package/docs/QUICKSTART-BODY-CAPTURE.md +2 -2
- package/docs/REDACTION-EXAMPLES.md +1 -1
- package/docs/REQUEST-BODY-CAPTURE.md +4 -4
- package/docs/SOLUTION-SUMMARY.md +4 -4
- package/docs/VERCEL-OTEL-MIGRATION.md +3 -3
- package/examples/README.md +6 -6
- package/examples/instrumentation-with-auto-capture.ts +1 -1
- package/examples/nextjs-env-example.txt +2 -2
- package/examples/nextjs-instrumentation.js +1 -1
- package/examples/nextjs-instrumentation.ts +1 -1
- package/examples/nextjs-with-logging-example.md +6 -6
- package/examples/nextjs-with-options.ts +1 -1
- package/examples/test-nextjs-setup.js +1 -1
- package/free-trial-banner.js +154 -0
- package/nextjs.d.ts +1 -1
- package/nextjs.js +62 -1
- package/package.json +3 -3
- package/postinstall.js +6 -6
- package/register.d.ts +1 -1
- package/tracing.d.ts +1 -1
- package/tracing.js +6 -0
- package/web-vite.mjs +61 -0
|
@@ -39,9 +39,9 @@ SECURENOW_LOGGING_ENABLED=1
|
|
|
39
39
|
SECURENOW_APPID=my-nextjs-app
|
|
40
40
|
SECURENOW_INSTANCE=http://localhost:4318
|
|
41
41
|
|
|
42
|
-
# For
|
|
43
|
-
# SECURENOW_INSTANCE=https://ingest.<region>.
|
|
44
|
-
# OTEL_EXPORTER_OTLP_HEADERS="
|
|
42
|
+
# For SecureNow or managed OTLP (example):
|
|
43
|
+
# SECURENOW_INSTANCE=https://ingest.<region>.securenow.ai:443
|
|
44
|
+
# OTEL_EXPORTER_OTLP_HEADERS="x-api-key=<your-key>"
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
### 4. Enable Instrumentation in `next.config.js`
|
|
@@ -239,9 +239,9 @@ You should see in the console:
|
|
|
239
239
|
SecureNow initialized with logging enabled
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
## View Logs in
|
|
242
|
+
## View Logs in SecureNow
|
|
243
243
|
|
|
244
|
-
1. Open your
|
|
244
|
+
1. Open your SecureNow dashboard
|
|
245
245
|
2. Navigate to **Logs** section
|
|
246
246
|
3. Filter by `service.name = my-nextjs-app`
|
|
247
247
|
4. See all your application logs with:
|
|
@@ -298,4 +298,4 @@ await import('securenow/console-instrumentation'); // Second
|
|
|
298
298
|
|
|
299
299
|
- [Complete Logging Guide](../docs/LOGGING-GUIDE.md)
|
|
300
300
|
- [Next.js Complete Guide](../docs/NEXTJS-GUIDE.md)
|
|
301
|
-
- [
|
|
301
|
+
- [SecureNow](https://securenow.ai/)
|
|
@@ -10,7 +10,7 @@ import { registerSecureNow } from 'securenow/nextjs';
|
|
|
10
10
|
export function register() {
|
|
11
11
|
registerSecureNow({
|
|
12
12
|
serviceName: 'my-nextjs-app',
|
|
13
|
-
endpoint: 'http://your-
|
|
13
|
+
endpoint: 'http://your-otlp-collector:4318',
|
|
14
14
|
noUuid: false,
|
|
15
15
|
disableInstrumentations: ['fs', 'dns'],
|
|
16
16
|
headers: {
|
|
@@ -58,7 +58,7 @@ setTimeout(() => {
|
|
|
58
58
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
59
59
|
console.log('✅ All tests passed!');
|
|
60
60
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
61
|
-
console.log('\nCheck your
|
|
61
|
+
console.log('\nCheck your SecureNow dashboard for traces from "test-nextjs-app"\n');
|
|
62
62
|
process.exit(0);
|
|
63
63
|
}, 2000);
|
|
64
64
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Free Trial Banner — auto-injects a visible "Testing Environment" banner
|
|
5
|
+
* into HTML pages served by apps using the SecureNow free trial instance.
|
|
6
|
+
*
|
|
7
|
+
* Opt-out: set SECURENOW_HIDE_BANNER=1
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const FREETRIAL_HOST = 'freetrial.securenow.ai';
|
|
11
|
+
|
|
12
|
+
function isFreeTrial(endpointBase) {
|
|
13
|
+
return !!endpointBase && endpointBase.includes(FREETRIAL_HOST);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* istanbul ignore next — runs in browser, not Node */
|
|
17
|
+
function _bannerClientCode() {
|
|
18
|
+
if (window.__snBanner) return;
|
|
19
|
+
window.__snBanner = 1;
|
|
20
|
+
|
|
21
|
+
function create() {
|
|
22
|
+
if (document.getElementById('sn-ft-banner')) return;
|
|
23
|
+
|
|
24
|
+
var d = document.createElement('div');
|
|
25
|
+
d.id = 'sn-ft-banner';
|
|
26
|
+
d.style.cssText =
|
|
27
|
+
'position:fixed;top:0;left:0;right:0;z-index:2147483647;' +
|
|
28
|
+
'background:#FEF3CD;color:#856404;padding:10px 16px;' +
|
|
29
|
+
'font-family:system-ui,-apple-system,sans-serif;font-size:13px;' +
|
|
30
|
+
'text-align:center;border-bottom:2px solid #FFE69C;' +
|
|
31
|
+
'display:flex;align-items:center;justify-content:center;gap:6px;' +
|
|
32
|
+
'box-shadow:0 2px 8px rgba(0,0,0,0.12)';
|
|
33
|
+
|
|
34
|
+
var icon = document.createElement('span');
|
|
35
|
+
icon.textContent = '\u26a0\ufe0f';
|
|
36
|
+
d.appendChild(icon);
|
|
37
|
+
|
|
38
|
+
var msg = document.createElement('span');
|
|
39
|
+
var strong = document.createElement('strong');
|
|
40
|
+
strong.textContent = 'Testing Environment:';
|
|
41
|
+
msg.appendChild(strong);
|
|
42
|
+
msg.appendChild(document.createTextNode(
|
|
43
|
+
' Only add test applications. For production usage, please '
|
|
44
|
+
));
|
|
45
|
+
|
|
46
|
+
var link = document.createElement('a');
|
|
47
|
+
link.href = 'https://securenow.ai/contact';
|
|
48
|
+
link.target = '_blank';
|
|
49
|
+
link.rel = 'noopener';
|
|
50
|
+
link.style.cssText = 'color:#664D03;font-weight:600;text-decoration:underline';
|
|
51
|
+
link.textContent = 'contact our team for a demo';
|
|
52
|
+
msg.appendChild(link);
|
|
53
|
+
msg.appendChild(document.createTextNode('.'));
|
|
54
|
+
d.appendChild(msg);
|
|
55
|
+
|
|
56
|
+
var close = document.createElement('button');
|
|
57
|
+
close.textContent = '\u00d7';
|
|
58
|
+
close.style.cssText =
|
|
59
|
+
'background:none;border:none;color:#856404;font-size:18px;' +
|
|
60
|
+
'cursor:pointer;margin-left:12px;padding:0 4px;line-height:1';
|
|
61
|
+
close.onclick = function () { d.style.display = 'none'; };
|
|
62
|
+
d.appendChild(close);
|
|
63
|
+
|
|
64
|
+
document.body.prepend(d);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (document.readyState === 'loading') {
|
|
68
|
+
document.addEventListener('DOMContentLoaded', create);
|
|
69
|
+
} else {
|
|
70
|
+
create();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var BANNER_SCRIPT =
|
|
75
|
+
'<script data-securenow-banner>(' +
|
|
76
|
+
_bannerClientCode.toString() +
|
|
77
|
+
')()</scr' + 'ipt>';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Monkey-patch http.ServerResponse to inject the banner script into HTML
|
|
81
|
+
* responses. Searches for `<head...>` and inserts the script right after it.
|
|
82
|
+
* Skips compressed responses and non-HTML content types.
|
|
83
|
+
*/
|
|
84
|
+
function patchHttpForBanner() {
|
|
85
|
+
try {
|
|
86
|
+
var http = require('http');
|
|
87
|
+
var _origWrite = http.ServerResponse.prototype.write;
|
|
88
|
+
var _origEnd = http.ServerResponse.prototype.end;
|
|
89
|
+
|
|
90
|
+
function maybeInject(res, chunk) {
|
|
91
|
+
if (res._snBannerDone || !chunk) return chunk;
|
|
92
|
+
|
|
93
|
+
if (res._snIsHtml === undefined) {
|
|
94
|
+
var ct = res.getHeader('content-type');
|
|
95
|
+
var ce = res.getHeader('content-encoding');
|
|
96
|
+
res._snIsHtml = !!(ct && String(ct).includes('text/html') && !ce);
|
|
97
|
+
}
|
|
98
|
+
if (!res._snIsHtml) {
|
|
99
|
+
res._snBannerDone = true;
|
|
100
|
+
return chunk;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
var isStr = typeof chunk === 'string';
|
|
104
|
+
var isBuf = Buffer.isBuffer(chunk);
|
|
105
|
+
if (!isStr && !isBuf) return chunk;
|
|
106
|
+
|
|
107
|
+
var str = isStr ? chunk : chunk.toString('utf8');
|
|
108
|
+
var headIdx = str.indexOf('<head');
|
|
109
|
+
if (headIdx === -1) return chunk;
|
|
110
|
+
|
|
111
|
+
var gt = str.indexOf('>', headIdx);
|
|
112
|
+
if (gt === -1) return chunk;
|
|
113
|
+
|
|
114
|
+
res._snBannerDone = true;
|
|
115
|
+
var result = str.slice(0, gt + 1) + BANNER_SCRIPT + str.slice(gt + 1);
|
|
116
|
+
return isStr ? result : Buffer.from(result, 'utf8');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
http.ServerResponse.prototype.write = function (chunk, encoding, cb) {
|
|
120
|
+
try {
|
|
121
|
+
var modified = maybeInject(this, chunk);
|
|
122
|
+
if (modified !== chunk) {
|
|
123
|
+
var enc = typeof encoding === 'function' ? 'utf8' : encoding;
|
|
124
|
+
var callback = typeof encoding === 'function' ? encoding : cb;
|
|
125
|
+
return _origWrite.call(this, modified, enc, callback);
|
|
126
|
+
}
|
|
127
|
+
} catch (_) { /* never break the app */ }
|
|
128
|
+
return _origWrite.call(this, chunk, encoding, cb);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
http.ServerResponse.prototype.end = function (chunk, encoding, cb) {
|
|
132
|
+
try {
|
|
133
|
+
var modified = maybeInject(this, chunk);
|
|
134
|
+
if (modified !== chunk) {
|
|
135
|
+
var enc = typeof encoding === 'function' ? 'utf8' : encoding;
|
|
136
|
+
var callback = typeof encoding === 'function' ? encoding : cb;
|
|
137
|
+
try {
|
|
138
|
+
if (this.getHeader('content-length')) {
|
|
139
|
+
this.setHeader('content-length', Buffer.byteLength(modified));
|
|
140
|
+
}
|
|
141
|
+
} catch (_) { /* headers already sent */ }
|
|
142
|
+
return _origEnd.call(this, modified, enc, callback);
|
|
143
|
+
}
|
|
144
|
+
} catch (_) { /* never break the app */ }
|
|
145
|
+
return _origEnd.call(this, chunk, encoding, cb);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
console.log('[securenow] Free trial banner injection enabled');
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.warn('[securenow] Could not setup free trial banner:', err.message);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { isFreeTrial, patchHttpForBanner, BANNER_SCRIPT };
|
package/nextjs.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export interface RegisterOptions {
|
|
|
51
51
|
* export function register() {
|
|
52
52
|
* registerSecureNow({
|
|
53
53
|
* serviceName: 'my-nextjs-app',
|
|
54
|
-
* endpoint: 'http://
|
|
54
|
+
* endpoint: 'http://your-otlp-backend.example.com:4318',
|
|
55
55
|
* noUuid: true,
|
|
56
56
|
* });
|
|
57
57
|
* }
|
package/nextjs.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* 2. Set environment variables:
|
|
16
16
|
* SECURENOW_APPID=my-nextjs-app
|
|
17
|
-
* SECURENOW_INSTANCE=http://your-
|
|
17
|
+
* SECURENOW_INSTANCE=http://your-otlp-backend:4318
|
|
18
18
|
*
|
|
19
19
|
* That's it! 🎉 No webpack warnings!
|
|
20
20
|
*/
|
|
@@ -261,6 +261,7 @@ function registerSecureNow(options = {}) {
|
|
|
261
261
|
).replace(/\/$/, '');
|
|
262
262
|
|
|
263
263
|
const tracesUrl = env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
|
|
264
|
+
const logsUrl = env('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT') || `${endpointBase}/v1/logs`;
|
|
264
265
|
|
|
265
266
|
// Set environment variables for @vercel/otel to pick up
|
|
266
267
|
process.env.OTEL_SERVICE_NAME = serviceName;
|
|
@@ -504,9 +505,69 @@ function registerSecureNow(options = {}) {
|
|
|
504
505
|
|
|
505
506
|
sdk.start();
|
|
506
507
|
console.log('[securenow] 🎯 Vanilla SDK initialized for self-hosted environment');
|
|
508
|
+
|
|
509
|
+
// -------- Logging (self-hosted only) --------
|
|
510
|
+
const loggingEnabled = String(env('SECURENOW_LOGGING_ENABLED')) === '1' || String(env('SECURENOW_LOGGING_ENABLED')).toLowerCase() === 'true';
|
|
511
|
+
if (loggingEnabled) {
|
|
512
|
+
try {
|
|
513
|
+
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
|
|
514
|
+
const { LoggerProvider, BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs');
|
|
515
|
+
|
|
516
|
+
const logExporter = new OTLPLogExporter({
|
|
517
|
+
url: logsUrl,
|
|
518
|
+
headers: parseHeaders(env('OTEL_EXPORTER_OTLP_HEADERS')),
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const loggerProvider = new LoggerProvider({
|
|
522
|
+
resource: new Resource({
|
|
523
|
+
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
|
|
524
|
+
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: env('NODE_ENV') || 'production',
|
|
525
|
+
}),
|
|
526
|
+
});
|
|
527
|
+
loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));
|
|
528
|
+
|
|
529
|
+
// Patch console to forward logs as OTLP log records
|
|
530
|
+
const logger = loggerProvider.getLogger('console', '1.0.0');
|
|
531
|
+
const SeverityNumber = { INFO: 9, WARN: 13, ERROR: 17 };
|
|
532
|
+
const origLog = console.log;
|
|
533
|
+
const origWarn = console.warn;
|
|
534
|
+
const origError = console.error;
|
|
535
|
+
|
|
536
|
+
console.log = (...args) => {
|
|
537
|
+
origLog.apply(console, args);
|
|
538
|
+
try { logger.emit({ severityNumber: SeverityNumber.INFO, severityText: 'INFO', body: args.map(String).join(' ') }); } catch (_) {}
|
|
539
|
+
};
|
|
540
|
+
console.warn = (...args) => {
|
|
541
|
+
origWarn.apply(console, args);
|
|
542
|
+
try { logger.emit({ severityNumber: SeverityNumber.WARN, severityText: 'WARN', body: args.map(String).join(' ') }); } catch (_) {}
|
|
543
|
+
};
|
|
544
|
+
console.error = (...args) => {
|
|
545
|
+
origError.apply(console, args);
|
|
546
|
+
try { logger.emit({ severityNumber: SeverityNumber.ERROR, severityText: 'ERROR', body: args.map(String).join(' ') }); } catch (_) {}
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
console.log('[securenow] 📋 Logging: ENABLED → %s', logsUrl);
|
|
550
|
+
|
|
551
|
+
// Graceful shutdown for logs
|
|
552
|
+
const origShutdown = sdk.shutdown?.bind(sdk);
|
|
553
|
+
process.on('SIGTERM', async () => { try { await loggerProvider.shutdown(); } catch (_) {} });
|
|
554
|
+
process.on('SIGINT', async () => { try { await loggerProvider.shutdown(); } catch (_) {} });
|
|
555
|
+
} catch (e) {
|
|
556
|
+
console.warn('[securenow] ⚠️ Logging setup failed (missing @opentelemetry/exporter-logs-otlp-http or @opentelemetry/sdk-logs):', e.message);
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
console.log('[securenow] 📋 Logging: DISABLED (set SECURENOW_LOGGING_ENABLED=1 to enable)');
|
|
560
|
+
}
|
|
507
561
|
}
|
|
508
562
|
|
|
509
563
|
isRegistered = true;
|
|
564
|
+
|
|
565
|
+
// Free trial banner
|
|
566
|
+
const { isFreeTrial, patchHttpForBanner } = require('./free-trial-banner');
|
|
567
|
+
if (isFreeTrial(endpointBase) && String(env('SECURENOW_HIDE_BANNER')) !== '1') {
|
|
568
|
+
patchHttpForBanner();
|
|
569
|
+
}
|
|
570
|
+
|
|
510
571
|
console.log('[securenow] ✅ OpenTelemetry started for Next.js → %s', tracesUrl);
|
|
511
572
|
console.log('[securenow] 📊 Auto-capturing comprehensive request metadata:');
|
|
512
573
|
console.log('[securenow] • IP addresses (x-forwarded-for, x-real-ip, socket)');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securenow",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "OpenTelemetry instrumentation for Node.js and Next.js - Send traces and logs to
|
|
3
|
+
"version": "5.2.1",
|
|
4
|
+
"description": "OpenTelemetry instrumentation for Node.js and Next.js - Send traces and logs to any OTLP-compatible backend",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "register.js",
|
|
7
7
|
"types": "register.d.ts",
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"monitoring",
|
|
23
23
|
"nextjs",
|
|
24
24
|
"next.js",
|
|
25
|
-
"signoz",
|
|
26
25
|
"instrumentation",
|
|
27
26
|
"telemetry",
|
|
28
27
|
"distributed-tracing",
|
|
@@ -85,6 +84,7 @@
|
|
|
85
84
|
"nextjs-wrapper.js",
|
|
86
85
|
"nextjs-wrapper.d.ts",
|
|
87
86
|
"cli.js",
|
|
87
|
+
"free-trial-banner.js",
|
|
88
88
|
"postinstall.js",
|
|
89
89
|
"register-vite.js",
|
|
90
90
|
"web-vite.mjs",
|
package/postinstall.js
CHANGED
|
@@ -53,7 +53,7 @@ export function register() {
|
|
|
53
53
|
* SECURENOW_APPID=my-nextjs-app
|
|
54
54
|
*
|
|
55
55
|
* Optional:
|
|
56
|
-
* SECURENOW_INSTANCE=http://your-
|
|
56
|
+
* SECURENOW_INSTANCE=http://your-otlp-backend:4318
|
|
57
57
|
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
58
58
|
* OTEL_LOG_LEVEL=info
|
|
59
59
|
*/
|
|
@@ -77,7 +77,7 @@ export function register() {
|
|
|
77
77
|
* SECURENOW_APPID=my-nextjs-app
|
|
78
78
|
*
|
|
79
79
|
* Optional:
|
|
80
|
-
* SECURENOW_INSTANCE=http://your-
|
|
80
|
+
* SECURENOW_INSTANCE=http://your-otlp-backend:4318
|
|
81
81
|
* OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
|
|
82
82
|
* OTEL_LOG_LEVEL=info
|
|
83
83
|
*
|
|
@@ -150,9 +150,9 @@ function createEnvTemplate(targetPath) {
|
|
|
150
150
|
# Required: Your application identifier
|
|
151
151
|
SECURENOW_APPID=my-nextjs-app
|
|
152
152
|
|
|
153
|
-
# Optional: Your
|
|
153
|
+
# Optional: Your OTLP-compatible backend / collector endpoint
|
|
154
154
|
# Default: https://freetrial.securenow.ai:4318
|
|
155
|
-
SECURENOW_INSTANCE=http://your-
|
|
155
|
+
SECURENOW_INSTANCE=http://your-otlp-backend:4318
|
|
156
156
|
|
|
157
157
|
# Optional: API key or authentication headers
|
|
158
158
|
# OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-api-key-here"
|
|
@@ -270,14 +270,14 @@ async function setup() {
|
|
|
270
270
|
console.log('│ │');
|
|
271
271
|
console.log('│ 1. Edit .env.local and set: │');
|
|
272
272
|
console.log('│ SECURENOW_APPID=your-app-name │');
|
|
273
|
-
console.log('│ SECURENOW_INSTANCE=http://
|
|
273
|
+
console.log('│ SECURENOW_INSTANCE=http://your-otlp-backend:4318 │');
|
|
274
274
|
if (shouldCreateMiddleware) {
|
|
275
275
|
console.log('│ SECURENOW_CAPTURE_BODY=1 │');
|
|
276
276
|
}
|
|
277
277
|
console.log('│ │');
|
|
278
278
|
console.log('│ 2. Run your app: npm run dev │');
|
|
279
279
|
console.log('│ │');
|
|
280
|
-
console.log('│ 3. Check
|
|
280
|
+
console.log('│ 3. Check SecureNow for traces! │');
|
|
281
281
|
console.log('│ │');
|
|
282
282
|
if (shouldCreateMiddleware) {
|
|
283
283
|
console.log('│ 📝 Body capture enabled with auto-redaction │');
|
package/register.d.ts
CHANGED
package/tracing.d.ts
CHANGED
|
@@ -112,7 +112,7 @@ export interface LoggerProvider {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
* Get a logger instance for sending structured logs to
|
|
115
|
+
* Get a logger instance for sending structured logs to any OTLP-compatible backend
|
|
116
116
|
*
|
|
117
117
|
* @param name - Logger name (e.g., 'my-service', 'auth-module')
|
|
118
118
|
* @param version - Logger version (optional, defaults to '1.0.0')
|
package/tracing.js
CHANGED
|
@@ -303,6 +303,12 @@ const sdk = new NodeSDK({
|
|
|
303
303
|
const tracer = api.trace.getTracer('securenow-smoke');
|
|
304
304
|
const span = tracer.startSpan('securenow.startup.smoke'); span.end();
|
|
305
305
|
}
|
|
306
|
+
|
|
307
|
+
// Free trial banner
|
|
308
|
+
const { isFreeTrial, patchHttpForBanner } = require('./free-trial-banner');
|
|
309
|
+
if (isFreeTrial(endpointBase) && String(env('SECURENOW_HIDE_BANNER')) !== '1') {
|
|
310
|
+
patchHttpForBanner();
|
|
311
|
+
}
|
|
306
312
|
} catch (e) {
|
|
307
313
|
console.error('[securenow] OTel start failed:', e && e.stack || e);
|
|
308
314
|
}
|
package/web-vite.mjs
CHANGED
|
@@ -144,9 +144,70 @@ export function startSecurenowWeb() {
|
|
|
144
144
|
console.log('[securenow] Web OTel started → %s', tracesUrl);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
// ---- Free trial banner (browser DOM injection) ----
|
|
148
|
+
function injectFreeTrialBanner(): void {
|
|
149
|
+
const FREETRIAL_HOST = 'freetrial.securenow.ai';
|
|
150
|
+
const hideBanner = String(env('SECURENOW_HIDE_BANNER')) === '1';
|
|
151
|
+
if (hideBanner || !endpointBase.includes(FREETRIAL_HOST)) return;
|
|
152
|
+
if (typeof document === 'undefined') return;
|
|
153
|
+
|
|
154
|
+
function create(): void {
|
|
155
|
+
if (document.getElementById('sn-ft-banner')) return;
|
|
156
|
+
|
|
157
|
+
const d = document.createElement('div');
|
|
158
|
+
d.id = 'sn-ft-banner';
|
|
159
|
+
d.style.cssText =
|
|
160
|
+
'position:fixed;top:0;left:0;right:0;z-index:2147483647;' +
|
|
161
|
+
'background:#FEF3CD;color:#856404;padding:10px 16px;' +
|
|
162
|
+
'font-family:system-ui,-apple-system,sans-serif;font-size:13px;' +
|
|
163
|
+
'text-align:center;border-bottom:2px solid #FFE69C;' +
|
|
164
|
+
'display:flex;align-items:center;justify-content:center;gap:6px;' +
|
|
165
|
+
'box-shadow:0 2px 8px rgba(0,0,0,0.12)';
|
|
166
|
+
|
|
167
|
+
const icon = document.createElement('span');
|
|
168
|
+
icon.textContent = '\u26a0\ufe0f';
|
|
169
|
+
d.appendChild(icon);
|
|
170
|
+
|
|
171
|
+
const msg = document.createElement('span');
|
|
172
|
+
const strong = document.createElement('strong');
|
|
173
|
+
strong.textContent = 'Testing Environment:';
|
|
174
|
+
msg.appendChild(strong);
|
|
175
|
+
msg.appendChild(document.createTextNode(
|
|
176
|
+
' Only add test applications. For production usage, please '
|
|
177
|
+
));
|
|
178
|
+
|
|
179
|
+
const link = document.createElement('a');
|
|
180
|
+
link.href = 'https://securenow.ai/contact';
|
|
181
|
+
link.target = '_blank';
|
|
182
|
+
link.rel = 'noopener';
|
|
183
|
+
link.style.cssText = 'color:#664D03;font-weight:600;text-decoration:underline';
|
|
184
|
+
link.textContent = 'contact our team for a demo';
|
|
185
|
+
msg.appendChild(link);
|
|
186
|
+
msg.appendChild(document.createTextNode('.'));
|
|
187
|
+
d.appendChild(msg);
|
|
188
|
+
|
|
189
|
+
const close = document.createElement('button');
|
|
190
|
+
close.textContent = '\u00d7';
|
|
191
|
+
close.style.cssText =
|
|
192
|
+
'background:none;border:none;color:#856404;font-size:18px;' +
|
|
193
|
+
'cursor:pointer;margin-left:12px;padding:0 4px;line-height:1';
|
|
194
|
+
close.onclick = () => { d.style.display = 'none'; };
|
|
195
|
+
d.appendChild(close);
|
|
196
|
+
|
|
197
|
+
document.body.prepend(d);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (document.readyState === 'loading') {
|
|
201
|
+
document.addEventListener('DOMContentLoaded', create);
|
|
202
|
+
} else {
|
|
203
|
+
create();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
147
207
|
// Auto-start
|
|
148
208
|
try {
|
|
149
209
|
startSecurenowWeb();
|
|
210
|
+
injectFreeTrialBanner();
|
|
150
211
|
} catch (e: any) {
|
|
151
212
|
if (String(e?.message) !== '__SECURENOW_NO_START__') {
|
|
152
213
|
console.error('[securenow/web-vite] failed to start:', e);
|