@mattduffy/banner 1.3.1 → 1.4.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 +28 -0
- package/package.json +1 -1
- package/src/index.js +41 -19
- package/test/test.js +26 -0
package/Readme.md
CHANGED
|
@@ -83,3 +83,31 @@ the ```ctx.request.method``` property. The supported request methods are ```GET
|
|
|
83
83
|
* Timestamp: 1/7/2026, 10:34:14 AM
|
|
84
84
|
**********************************************************************
|
|
85
85
|
```
|
|
86
|
+
|
|
87
|
+
Under the right conditions, the request banner can also include geo-location details
|
|
88
|
+
about the client. For each new request, Koajs creates a context object containing the
|
|
89
|
+
request and response objects ```ctx.request``` and ```ctx.response```. It is a common
|
|
90
|
+
technique to add a state property to the ctx object, like ```ctx.state```. If geo-location
|
|
91
|
+
is available, save it to ```ctx.state``` like so:
|
|
92
|
+
```javascript
|
|
93
|
+
ctx.state.logEntry = {
|
|
94
|
+
geos: [
|
|
95
|
+
{
|
|
96
|
+
country: <client-country>,
|
|
97
|
+
city: <client-city>,
|
|
98
|
+
subdivision: <client-subdivision>, // the state in USA
|
|
99
|
+
coords: [<latitude>, <longitude>],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
###########################################################################################
|
|
105
|
+
# GET: https://banner.test/a/really/long/url/to/a/special/page
|
|
106
|
+
# Referer: https://googoogle.com
|
|
107
|
+
# From IP: 192.168.1.250
|
|
108
|
+
# Location: Country: United States, City: New York City, lat/lon: 40.775697, -73.971727
|
|
109
|
+
# Timestamp: 1/7/2026, 8:22:41 PM
|
|
110
|
+
###########################################################################################
|
|
111
|
+
```
|
|
112
|
+
The geo-location details must be saved to the context state property before the
|
|
113
|
+
```app.use(banner.use())``` is executed for this to work.
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -279,7 +279,7 @@ export class Banner {
|
|
|
279
279
|
use(Log = null, Error = null) {
|
|
280
280
|
const _log = Log ?? console.log
|
|
281
281
|
// const _error = Error ?? console.error
|
|
282
|
-
_log('adding request banner to the app.')
|
|
282
|
+
// _log('adding request banner to the app.')
|
|
283
283
|
const gGET = this.#borderGlyphGET
|
|
284
284
|
const gPUT = this.#borderGlyphPUT
|
|
285
285
|
const gPOST = this.#borderGlyphPOST
|
|
@@ -322,6 +322,7 @@ export class Banner {
|
|
|
322
322
|
const values = []
|
|
323
323
|
const lines = []
|
|
324
324
|
const _urlLabel = `${ctx.request.method}:`
|
|
325
|
+
labels.push(_urlLabel)
|
|
325
326
|
let _url = `${ctx.request.protocol}://${ctx.request.header.host}${ctx.request.url}`
|
|
326
327
|
const _queryStringIndex = _url.indexOf('?')
|
|
327
328
|
const _queryStringLabel = 'Query Params:'
|
|
@@ -333,51 +334,65 @@ export class Banner {
|
|
|
333
334
|
labels.push(_queryStringLabel)
|
|
334
335
|
values.push(_queryString)
|
|
335
336
|
lines.push(_queryStringLine)
|
|
336
|
-
//
|
|
337
|
+
// _log('adding query string to request banner')
|
|
337
338
|
}
|
|
338
339
|
_url = (_queryString.length > 0) ? _url.slice(0, _url.indexOf('?')) : _url
|
|
339
340
|
values.push(_url)
|
|
340
341
|
let _urlLine = `${_urlLabel} ${_url}`
|
|
342
|
+
lines.push(_urlLine)
|
|
341
343
|
const _refLabel = 'Referer:'
|
|
344
|
+
labels.push(_refLabel)
|
|
342
345
|
const _ref = ctx.request.header.referer ?? '<emtpy header field>'
|
|
343
346
|
values.push(_ref)
|
|
344
347
|
let _refLine = `${_refLabel} ${_ref}`
|
|
348
|
+
lines.push(_refLine)
|
|
345
349
|
const _ipLabel = 'From IP:'
|
|
350
|
+
labels.push(_ipLabel)
|
|
346
351
|
const _ip = ctx.request.ip
|
|
347
352
|
values.push(_ip)
|
|
348
353
|
let _ipLine = `${_ipLabel} ${_ip}`
|
|
354
|
+
lines.push(_ipLine)
|
|
355
|
+
let _locationLine = ''
|
|
356
|
+
if (ctx?.state?.logEntry?.geos[0]) {
|
|
357
|
+
// _log('banner geolocation says what?', ctx?.state?.logEntry)
|
|
358
|
+
const l = ctx.state.logEntry.geos[0]
|
|
359
|
+
const _locationLabel = 'Location:'
|
|
360
|
+
labels.push(_locationLabel)
|
|
361
|
+
/* eslint-disable prefer-template */
|
|
362
|
+
const _location = `${(l.country) ? 'Country: ' + l.country + ', ' : ''}`
|
|
363
|
+
+ `${(l.subdivision) ? 'Subdivision: ' + l.subdivision + ', ' : ''}`
|
|
364
|
+
+ `${(l.city) ? 'City: ' + l.city + ', ' : ''}`
|
|
365
|
+
+ `${(l.coords) ? 'lat/lon: ' + l.coords[0] + ', ' + l.coords[1] : ''}`
|
|
366
|
+
/* eslint-enable prefer-template */
|
|
367
|
+
values.push(_location)
|
|
368
|
+
_locationLine = `${_locationLabel} ${_location}`
|
|
369
|
+
lines.push(_locationLine)
|
|
370
|
+
}
|
|
349
371
|
const _timestampLabel = 'Timestamp:'
|
|
372
|
+
labels.push(_timestampLabel)
|
|
350
373
|
const _timestamp = new Date().toLocaleString()
|
|
351
374
|
values.push(_timestamp)
|
|
352
375
|
let _timestampLine = `${_timestampLabel} ${_timestamp}`
|
|
353
|
-
|
|
354
|
-
labels
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// console.log(labels)
|
|
358
|
-
|
|
359
|
-
// console.log(values)
|
|
376
|
+
lines.push(_timestampLine)
|
|
377
|
+
// _log(labels)
|
|
378
|
+
// _log(values)
|
|
379
|
+
// _log(lines)
|
|
360
380
|
const _longestValue = values
|
|
361
381
|
.reduce((a, c) => {
|
|
362
382
|
if (a > c.length) return a
|
|
363
383
|
return c.length
|
|
364
384
|
}, '')
|
|
365
|
-
//
|
|
385
|
+
// _log('longest value', _longestValue)
|
|
366
386
|
|
|
367
|
-
lines.push(_ipLine)
|
|
368
|
-
lines.push(_timestampLine)
|
|
369
|
-
lines.push(_urlLine)
|
|
370
|
-
lines.push(_refLine)
|
|
371
|
-
// console.log(lines)
|
|
372
387
|
const _longestLabel = labels
|
|
373
388
|
.reduce((a, c) => {
|
|
374
|
-
//
|
|
389
|
+
// _log(a, c.indexOf(':') + 1)
|
|
375
390
|
if (a > (c.indexOf(':') + 1)) {
|
|
376
391
|
return a
|
|
377
392
|
}
|
|
378
393
|
return (c.indexOf(':') + 1)
|
|
379
394
|
}, '')
|
|
380
|
-
//
|
|
395
|
+
// _log('longest label', _longestLabel)
|
|
381
396
|
_refLine = _refLine.padStart(
|
|
382
397
|
(_longestLabel - _refLine.indexOf(':')) + _refLine.length,
|
|
383
398
|
' ',
|
|
@@ -396,6 +411,12 @@ export class Banner {
|
|
|
396
411
|
(_longestLabel - _ipLine.indexOf(':')) + _ipLine.length,
|
|
397
412
|
' ',
|
|
398
413
|
)
|
|
414
|
+
if (_locationLine.length > 0) {
|
|
415
|
+
_locationLine = _locationLine.padStart(
|
|
416
|
+
(_longestLabel - _locationLine.indexOf(':')) + _locationLine.length,
|
|
417
|
+
' ',
|
|
418
|
+
)
|
|
419
|
+
}
|
|
399
420
|
_timestampLine = _timestampLine.padStart(
|
|
400
421
|
(_longestLabel - _timestampLine.indexOf(':')) + _timestampLine.length,
|
|
401
422
|
' ',
|
|
@@ -403,11 +424,11 @@ export class Banner {
|
|
|
403
424
|
const _longestLine = _longestLabel + _longestValue
|
|
404
425
|
// const _longestLine = lines
|
|
405
426
|
// .reduce((a, c) => {
|
|
406
|
-
// //
|
|
427
|
+
// // _log(a, c.length)
|
|
407
428
|
// if (a > c.length) return a
|
|
408
429
|
// return c.length
|
|
409
430
|
// }, '')
|
|
410
|
-
//
|
|
431
|
+
// _log('longest line', _longestLine)
|
|
411
432
|
/* eslint-disable prefer-template */
|
|
412
433
|
const endDangler = 6
|
|
413
434
|
_requestBanner = `${_g.padEnd(_longestLine + endDangler, _g)}\n`
|
|
@@ -415,6 +436,7 @@ export class Banner {
|
|
|
415
436
|
+ `${(_queryStringLine.length > 0) ? _g + ' ' + _queryStringLine + '\n' : ''}`
|
|
416
437
|
+ `${_g} ${_refLine}\n`
|
|
417
438
|
+ `${_g} ${_ipLine}\n`
|
|
439
|
+
+ `${(_locationLine.length > 0) ? _g + ' ' + _locationLine + '\n' : ''}`
|
|
418
440
|
+ `${_g} ${_timestampLine}\n`
|
|
419
441
|
+ `${_g.padEnd(_longestLine + endDangler, _g)}`
|
|
420
442
|
/* eslint-enable prefer-template */
|
package/test/test.js
CHANGED
|
@@ -45,6 +45,7 @@ describe('First test suite for banner package', async () => {
|
|
|
45
45
|
return localIpAddress
|
|
46
46
|
}
|
|
47
47
|
ctx = {
|
|
48
|
+
state: {},
|
|
48
49
|
request: {
|
|
49
50
|
ip: getLocalIpAddress(),
|
|
50
51
|
protocol: 'https',
|
|
@@ -109,6 +110,31 @@ describe('First test suite for banner package', async () => {
|
|
|
109
110
|
assert(await get.use()(ctx_GET, next))
|
|
110
111
|
})
|
|
111
112
|
|
|
113
|
+
it('Should work as a koajs middleware function - GET method, check for geos.', async () => {
|
|
114
|
+
// ctx_GET = Object.assign({}, ctx)
|
|
115
|
+
ctx_GET = { ...ctx }
|
|
116
|
+
ctx_GET.request.method = 'GET'
|
|
117
|
+
ctx_GET.throw = (code, err) => {
|
|
118
|
+
throw new Error(`${code}, ${err}`)
|
|
119
|
+
}
|
|
120
|
+
ctx_GET.state.logEntry = {
|
|
121
|
+
geos: [
|
|
122
|
+
{
|
|
123
|
+
ip: '10.20.30.40',
|
|
124
|
+
country: 'United States',
|
|
125
|
+
city: 'New York City',
|
|
126
|
+
coords: [40.775697, -73.971727],
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
}
|
|
130
|
+
console.log(ctx_GET)
|
|
131
|
+
assert(ctx_GET.state.logEntry.geos[0].coords.length === 2)
|
|
132
|
+
const get = new Banner(ctx_GET)
|
|
133
|
+
const banner = await get.use()(ctx_GET, next)
|
|
134
|
+
assert(/40\.775697/m.test(banner))
|
|
135
|
+
delete ctx_GET.state.logEntry
|
|
136
|
+
})
|
|
137
|
+
|
|
112
138
|
it('Should work as a koajs middleware function - PUT method.', async () => {
|
|
113
139
|
// ctx_PUT = Object.assign({}, ctx)
|
|
114
140
|
ctx_PUT = { ...ctx }
|