@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattduffy/banner",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Displays server start-up information.",
5
5
  "author": "Matthew Duffy",
6
6
  "license": "ISC",
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
- // console.log('adding query string to request banner')
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
- labels.push(_urlLabel)
354
- labels.push(_refLabel)
355
- labels.push(_ipLabel)
356
- labels.push(_timestampLabel)
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
- // console.log('longest value', _longestValue)
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
- // console.log(a, c.indexOf(':') + 1)
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
- // console.log('longest label', _longestLabel)
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
- // // console.log(a, c.length)
427
+ // // _log(a, c.length)
407
428
  // if (a > c.length) return a
408
429
  // return c.length
409
430
  // }, '')
410
- // console.log('longest line', _longestLine)
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 }