Skip to content

Commit

Permalink
feat: add systemic scaffolding schematic
Browse files Browse the repository at this point in the history
  • Loading branch information
neodmy committed Nov 30, 2023
1 parent 02331f4 commit a4bc5d2
Show file tree
Hide file tree
Showing 31 changed files with 584 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/@guidesmiths/cuckoojs-schematics/src/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
"description": "Add basic tooling to the project",
"factory": "./basic-tooling/basic-tooling.factory#main",
"schema": "./basic-tooling/schema.json"
},
"systemic-scaffolding": {
"description": "Add Systemic project scaffolding",
"factory": "./systemic-scaffolding/systemic-scaffolding.factory#main",
"schema": "./systemic-scaffolding/schema.json"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:18

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
COPY package*.json ./

RUN npm install

# Bundle app source
COPY . .

EXPOSE 4000

RUN npm run manifest

CMD [ "npm", "start" ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const System = require('systemic');
const optional = require('optional');
const { join } = require('path');

const manifest = optional(join(process.cwd(), 'manifest.json')) || {};
const pkg = require(join(process.cwd(), 'package.json')); // eslint-disable-line import/no-dynamic-require

module.exports = new System({ name: '<%=directory%>' }).add('manifest', manifest).add('pkg', pkg);
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const path = require('path');
const Conf = require('confabulous');

module.exports = ({ confabulous } = {}) => {
const Confabulous = confabulous || Conf;
const { loaders } = Confabulous;

const start = (params, cb) =>
new Confabulous()
.add(() => loaders.require({ path: path.join(process.cwd(), 'config', 'default.js'), watch: true }))
.add(() =>
loaders.require({
path: path.join(process.cwd(), 'config', `${process.env.SERVICE_ENV}.js`),
mandatory: false,
}),
)
.add(() =>
loaders.require({ path: path.join(process.cwd(), 'secrets', 'secrets.json'), watch: true, mandatory: false }),
)
.add(() => loaders.args())
.on('loaded', cb)
.on('error', cb)
.end(cb);

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const System = require('systemic');
const confabulous = require('./confabulous');

module.exports = new System({ name: 'config' }).add('config', confabulous(), { scoped: true });
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const System = require('systemic');
const { defaultMiddleware, app, server } = require('systemic-express');

module.exports = new System({ name: 'express' })
.add('app', app()).dependsOn('config', 'logger')
.add('middleware.default', defaultMiddleware())
.dependsOn('logger', 'app', 'routes')
.add('server', server())
.dependsOn('config', 'app', 'middleware.default');
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const bunyan = require('bunyan');
const R = require('ramda');

module.exports = () => {
let log;

const onMessage = event => {
log[event.level](R.omit(['level', 'message'], event), event.message);
};

const start = async ({ pkg }) => {
log = bunyan.createLogger({ name: pkg.name });
return onMessage;
};

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const chalk = require('chalk');
const hogan = require('hogan.js');
const R = require('ramda');

const response = hogan.compile(
'{{{timestamp}}} | {{{displayLevel}}} [{{package.name}}] {{{displayTracer}}} {{{request.method}}} {{{response.statusCode}}} {{{request.url}}}',
);
const error = hogan.compile(
'{{{timestamp}}} | {{{displayLevel}}} [{{package.name}}] {{{displayTracer}}} {{{errorMessage}}} {{{code}}}\n{{{error.stack}}} {{{details}}}',
);
const info = hogan.compile(
'{{{timestamp}}} | {{{displayLevel}}} [{{package.name}}] {{{displayTracer}}} {{{message}}} {{{details}}}',
);

const colours = {
debug: chalk.gray,
info: chalk.white,
warn: chalk.yellow,
error: chalk.red,
default: chalk.white,
};

module.exports = () => {
const onMessage = event => {
const details = R.pluck(event, []);
const data = R.merge(event, {
displayTracer: R.has('tracer', event) ? event.tracer.substr(0, 6) : '|',
displayLevel: event.level.toUpperCase().padStart(event.level.length + Math.floor((6 - event.level.length) / 2), ' ')
.padEnd(6, ' '),
details: Object.keys(details).length ? `\n ${JSON.stringify(details, null, 2)}` : '',
errorMessage:
event.error &&
(event.error.message instanceof Object ? JSON.stringify(event.error.message) : event.error.message),
timestamp: event.timestamp.toISOString(),
});
const colour = colours[event.level] || colours.default;
const log = console[event.level] || console.info; // eslint-disable-line no-console
if (R.has('response.statusCode', event)) log(colour(response.render(data)));
else if (data.errorMessage) log(colour(error.render(data)));
else log(colour(info.render(data)));
};


const start = async () => onMessage;

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const System = require('systemic');
const prepper = require('./prepper');
const bunyan = require('./bunyan');
const console = require('./console');
const prepperMiddleware = require('./prepper-middleware');

module.exports = new System({ name: 'logging' })
.add('transports.console', console())
.add('transports.bunyan', bunyan())
.dependsOn('pkg')
.add('transports')
.dependsOn(
{ component: 'transports.console', destination: 'console' },
{ component: 'transports.bunyan', destination: 'bunyan' },
)
.add('logger', prepper())
.dependsOn('config', 'pkg', 'transports')
.add('middleware.prepper', prepperMiddleware())
.dependsOn('app');
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const onHeaders = require('on-headers');
const R = require('ramda');
const Prepper = require('prepper');

module.exports = ({ prepper } = {}) => {
const { handlers } = prepper || Prepper;

const start = async ({ app }) => {
app.use((req, res, next) => {
const logger = req.app.locals.logger.child({
handlers: [
new handlers.Tracer(),
new handlers.Merge(R.pick(['url', 'method', 'headers', 'params'], req), { key: 'request' }),
],
});

onHeaders(res, () => {
const response = { response: { statusCode: res.statusCode, headers: res.headers } };
if (res.statusCode === 400) logger.error(req.url, response);
if (res.statusCode < 500) logger.info(req.url, response);
else logger.error(req.url, response);
});

res.locals.logger = logger;

next();
});

return Promise.resolve();
};

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const R = require('ramda');
const Prepper = require('prepper');

module.exports = ({ prepper, transport } = {}) => {
const prepperFn = prepper || Prepper;
const { handlers } = prepperFn;

const start = async ({ config, transports, pkg = { name: 'unknown' } }) => {
const transportFn = transport || R.path([config.transport], transports);
const finalConfig = R.merge({ include: [], exclude: [] }, config);
const logger = new prepperFn.Logger({
handlers: [
new handlers.Merge({ package: pkg }),
new handlers.Merge({ service: { env: process.env.SERVICE_ENV } }),
new handlers.Process(),
new handlers.System(),
new handlers.Timestamp(),
new handlers.Flatten(),
new handlers.KeyFilter({ include: finalConfig.include, exclude: finalConfig.exclude }),
new handlers.Unflatten(),
],
}).on('message', event => {
if (transportFn) transportFn(event);
});

return logger;
};

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const helmet = require('helmet');
const bodyParser = require('body-parser');
const validator = require('swagger-endpoint-validator');

module.exports = () => {
const start = async ({ manifest = {}, app, config }) => {
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(helmet());

await validator.init(app, config.swaggerValidator);

app.get('/__/manifest', (req, res) => res.json(manifest));

return Promise.resolve();
};

return { start };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const System = require('systemic');
const adminRoutes = require('./admin-routes');

module.exports = new System({ name: 'routes' })
.add('routes.admin', adminRoutes())
.dependsOn('config', 'logger', 'app', 'middleware.prepper', 'manifest')
.add('routes')
.dependsOn('routes.admin');
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { logger: { transport: null } };
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module.exports = {
server: {
host: '0.0.0.0',
port: 4000,
},
routes: {
admin: {
swaggerValidator: {
apiDocEndpoint: '/__/docs/api',
validateRequests: true,
validateResponses: true,
validationEndpoint: '/test',
format: 'yaml',
yaml: {
file: './docs/syncapi.yaml',
},
},
},
},
logger: {
transport: 'console',
include: [
'tracer',
'timestamp',
'level',
'message',
'error.message',
'error.code',
'error.stack',
'request.url',
'request.headers',
'request.params',
'request.method',
'response.statusCode',
'response.headers',
'response.time',
'process',
'system',
'package.name',
'service',
],
exclude: ['password', 'secret', 'token', 'request.headers.cookie', 'dependencies', 'devDependencies'],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
logger: { transport: 'console' },
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
logger: { transport: null },
};
Loading

0 comments on commit a4bc5d2

Please sign in to comment.