Skip to content

Commit 671d664

Browse files
committed
lets give it a whirl
1 parent 775cf0e commit 671d664

6 files changed

Lines changed: 264 additions & 0 deletions

File tree

.github/workflows/publish.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: NPM (GitHub) Publish
2+
3+
on:
4+
release:
5+
types: [created]
6+
7+
# on: [push]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v1
14+
- uses: actions/setup-node@v1
15+
with:
16+
node-version: 12
17+
# registry-url: https://npm.pkg.github.com/
18+
scope: '@hexlyoss'
19+
- run: npm install
20+
- run: git describe --exact-match --tags $(git log -n1 --pretty='%h') | xargs npm version --allow-same-version
21+
- run: npm publish
22+
env:
23+
NODE_AUTH_TOKEN: ${{secrets.NPMJS_PUBLISH_SECRET}}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.history
2+
node_modules
3+
.env
4+
.envrc
5+
.vscode

index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const log = require('./lib/log')
2+
const express = require('./lib/express')
3+
const security = require('./lib/security')
4+
5+
module.exports = {
6+
log,
7+
express,
8+
security
9+
}

lib/express.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const express = require('express')
2+
const bodyParser = require('body-parser')
3+
const cors = require('cors')
4+
const passport = require('passport')
5+
6+
const log = require('./log').manager.createLogger(__filename)
7+
8+
const initExpress = async (settings) => {
9+
const s = settings || {}
10+
const app = express()
11+
app.use(cors())
12+
app.use(bodyParser.json())
13+
app.use(passport.initialize());
14+
s.passport && s.passport.session && app.use(passport.session())
15+
app.use(passport.authenticate(['basic', 'jwt', 'anonymous'], { session: false }))
16+
return app
17+
}
18+
19+
function wrapper (callback) {
20+
return function (req, res, next) {
21+
callback(req, res, next)
22+
.catch(error => {
23+
log.warn(`Unhandled Express Route Error at path=[${req.path}]: `, error)
24+
next({
25+
message: 'Internal Server Error',
26+
error
27+
})
28+
})
29+
}
30+
}
31+
32+
module.exports = {
33+
wrapper,
34+
initExpress
35+
}

lib/log.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// TODO research async stacktraces
2+
// https://medium.com/@nodejs/introducing-node-js-12-76c41a1b3f3f
3+
4+
const util = require('util')
5+
const SimpleLogger = require('simple-node-logger')
6+
const { AbstractAppender } = SimpleLogger
7+
8+
const localOrTest = !!(['localhost', 'test'].find(e => e === process.env.NODE_ENV))
9+
const CWD = process.cwd()
10+
11+
let level
12+
if (process.env.LOGGER_LEVEL) {
13+
level = process.env.LOGGER_LEVEL
14+
} else {
15+
level = localOrTest ? 'debug' : 'info'
16+
}
17+
18+
const printer = output => localOrTest
19+
? process.stdout.write(util.inspect(output, false, null, true) + '\n')
20+
: process.stdout.write(util.inspect(output, { breakLength: Infinity }) + '\n')
21+
22+
const JSONAppender = function () {
23+
var appender = this;
24+
25+
var opts = {
26+
typeName: 'JSONAppender',
27+
timestampFormat: 'YYYY-MM-DD HH:mm:ss.SSS'
28+
};
29+
30+
AbstractAppender.extend(this, opts);
31+
32+
// format and write all entry/statements
33+
this.write = function (entry) {
34+
const msg = Array.isArray(entry.msg) ? [...entry.msg] : entry.msg
35+
36+
// if we monkey patched our line numbers, let's use that as the source
37+
// so that VSCode will give us nice clickable links
38+
let source
39+
if (localOrTest && msg.length && msg[0].__hexLine) {
40+
source = './' + msg.shift().__hexLine
41+
} else {
42+
source = entry.category
43+
}
44+
45+
if (Array.isArray(msg)) {
46+
const last = msg.length - 1
47+
if (msg[last] instanceof Error) {
48+
const err = msg[last]
49+
msg[last] = `Error: ${err.message}\n${err.stack}`
50+
}
51+
}
52+
53+
const output = {
54+
ts: appender.formatTimestamp(entry.ts),
55+
source,
56+
level: entry.level,
57+
message: msg
58+
}
59+
printer(output)
60+
};
61+
};
62+
63+
const timestampFormat = 'YYYY-MM-DD HH:mm:ss.SSS'
64+
const appender = new JSONAppender({ timestampFormat })
65+
66+
const manager = SimpleLogger.createLogManager({
67+
level,
68+
timestampFormat: 'YYYY-MM-DD HH:mm:ss.SSS',
69+
errorEventName: 'error',
70+
appenders: [appender]
71+
})
72+
73+
// If we're local or test, let's
74+
// monkey patch our loggers to include line numbers
75+
if (localOrTest) {
76+
// const REGEX = /.*(?<source>[^)]*):\d+\)/
77+
const pattern = `.*${CWD}/(?<source>[^\\)]*):\\d+\\)?`
78+
79+
const REGEX = new RegExp(pattern)
80+
const orig = manager.createLogger.bind(manager)
81+
manager.createLogger = (...args) => {
82+
const logger = orig(...args)
83+
const origLog = logger.log.bind(logger)
84+
logger.log = (...logArgs) => {
85+
const stack = new Error().stack
86+
const find = stack.split('\n')[3].match(REGEX)
87+
if (find && find.groups) {
88+
logArgs[1].splice(0, 0, { __hexLine: find.groups.source })
89+
} else {
90+
91+
}
92+
93+
origLog(...logArgs)
94+
}
95+
return logger
96+
}
97+
}
98+
99+
const createLogger = (...args) => manager.createLogger(...args)
100+
101+
module.exports = {
102+
JSONAppender,
103+
manager,
104+
createLogger,
105+
defaultLevel: level
106+
}

lib/security.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
2+
const Config = require('config')
3+
const passport = require('passport')
4+
const { BasicStrategy } = require('passport-http')
5+
const { Strategy: AnonymousStrategy } = require('passport-anonymous')
6+
7+
const JwtStrategy = require('passport-jwt').Strategy;
8+
const ExtractJwt = require('passport-jwt').ExtractJwt
9+
10+
const log = require('./log').createLogger(__filename)
11+
12+
const initSecurity = (config = {}) => {
13+
const pp = config.passport || {}
14+
initPassport(pp)
15+
}
16+
17+
const initPassport = ( config = {} ) => {
18+
const { validateBasic } = config
19+
20+
if( validateBasic ){
21+
log.info('Configuring basic strategy')
22+
passport.use(new BasicStrategy(
23+
async (username, password, done) => {
24+
try {
25+
let user = await validateBasic(username, password)
26+
done(null, user)
27+
}catch(err){
28+
done(err)
29+
}
30+
}
31+
));
32+
}
33+
34+
const legacySecret = Config.get('security.jwt.legacy')
35+
if( legacySecret ){
36+
var opts = {}
37+
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
38+
opts.secretOrKey = legacySecret;
39+
// opts.issuer = 'accounts.examplesoft.com';
40+
// opts.audience = 'yoursite.net';
41+
passport.use(new JwtStrategy(opts, async (claims, done) => {
42+
// legacy token
43+
const p = {
44+
authenticated: true
45+
}
46+
if (claims.aud === 'postgraphile') {
47+
Object.assign(p, {
48+
type: 'member',
49+
anonymous: false,
50+
identity: claims.identityId,
51+
member: claims.memberId,
52+
tenant: claims.tenantId,
53+
audit: claims.auditId
54+
})
55+
}
56+
57+
done(null, p)
58+
}));
59+
}else{
60+
log.warn('No legacy secret provided')
61+
}
62+
63+
passport.use(new AnonymousStrategy())
64+
}
65+
66+
const createApolloContextFromExpress = (apolloServerArgs) => {
67+
const { req, res } = apolloServerArgs
68+
69+
const context = {}
70+
const { user } = req
71+
Object.assign(context, {
72+
user,
73+
express: {
74+
request: req,
75+
response: res,
76+
arguments: args
77+
}
78+
})
79+
return context
80+
}
81+
82+
module.exports = {
83+
initSecurity,
84+
initPassport,
85+
createApolloContextFromExpress
86+
}

0 commit comments

Comments
 (0)