Commit 1f8f79e5 authored by Vadym Gidulian's avatar Vadym Gidulian

Merge branch 'dev'

parents 399e40a1 3e977c04
/.data
.env
# Dependency directories
node_modules/
# Logs
logs/
*.log
npm-debug.log*
# Optional npm cache directory
.npm/
# Optional REPL history
.node_repl_history/
services:
- docker:dind
variables:
IMAGE_SNAPSHOT: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
IMAGE_LATEST: $CI_REGISTRY_IMAGE:latest
stages:
- build
- release
before_script:
- docker version
- docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
build:
stage: build
script:
- docker build --pull -t $IMAGE_SNAPSHOT .
- docker push $IMAGE_SNAPSHOT
tags:
- dind
release:
stage: release
variables:
GIT_STRATEGY: none
script:
- docker pull $IMAGE_SNAPSHOT
- docker tag $IMAGE_SNAPSHOT $IMAGE
- docker push $IMAGE
tags:
- dind
release:latest:
stage: release
variables:
GIT_STRATEGY: none
script:
- docker pull $IMAGE_SNAPSHOT
- docker tag $IMAGE_SNAPSHOT $IMAGE_LATEST
- docker push $IMAGE_LATEST
when: manual
tags:
- dind
only:
- master
FROM node:10-alpine
COPY package.json yarn.lock rules.json /app/
COPY src/ /app/src/
WORKDIR /app
ENV NODE_ENV production
RUN yarn install --production &&\
yarn cache clean
CMD ["node", "src"]
EXPOSE 80
version: '3'
services:
server:
build: .
volumes:
- .:/app
- ./.data/files:/files
entrypoint: sh
command: -c "yarn install --production=false && npx nodemon --signal SIGHUP src"
environment:
NODE_ENV: $NODE_ENV
CORS_ALLOW_ORIGIN: $CORS_ALLOW_ORIGIN
networks:
default:
aliases:
- api
{ {
"name": "@gviagroup/release-server", "name": "@gviagroup/release-server",
"version": "0.1.0" "version": "0.1.0",
"dependencies": {
"express": "^4.17.1",
"response-time": "^2.3.2",
"@gviagroup/release-server-middleware": "^1.0.0",
"@gviagroup/token-based-authz-middleware": "^1.0.0"
},
"devDependencies": {
"nodemon": "^1.19.4"
},
"_projectTemplateVersion": "1.1.2"
} }
{
"": true
}
'use strict';
const {Router} = require('express');
const router = module.exports = Router();
router.use('/status', require('./status'));
router.use(require('@gviagroup/token-based-authz-middleware')({pathToRules: '/app/rules.json'}));
router.use(require('@gviagroup/release-server-middleware')());
'use strict';
const {Router} = require('express');
const {handle, methodNotAllowed} = require('../utils/api');
const router = module.exports = Router();
const methodNotAllowedHandler = methodNotAllowed(router);
router.route('/')
.get(handle(undefined, () => ({
isAlive: true,
dependencies: {},
modules: {}
})))
.all(methodNotAllowedHandler);
'use strict';
require('./utils/logging');
const express = require('express');
const responseTime = require('response-time');
const grace = require('./utils/grace');
const NODE_ENV = process.env.NODE_ENV || 'development';
const CORS_ALLOW_ORIGIN = process.env.CORS_ALLOW_ORIGIN || '*';
console.info(`Running in ${NODE_ENV} environment`);
const app = express();
app.use(express.json({
limit: 0
}));
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', CORS_ALLOW_ORIGIN);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Accept, Content-Type, X-Token, X-File-Name, X-Private');
res.header('Access-Control-Allow-Methods', 'OPTIONS, HEAD, GET, PUT, DELETE');
res.header('Access-Control-Expose-Headers', 'Content-Length');
if (req.method === 'OPTIONS') return res.status(204).send();
next();
});
app.use(responseTime());
app.use(require('./apis/index'));
switch (NODE_ENV) {
case 'production':
app.use((req, res) => res.status(404).send());
app.use((err, req, res, next) => {
console.error(err);
if (!res.finished) res.status(500).send();
});
break;
default:
app.use((req, res) => {
console.warn(`${req.url} was not handled`);
res.status(404).set('Content-Type', 'text/plain').send(`${req.url} was not handled`);
});
app.use((err, req, res, next) => {
console.error(err);
if (!res.finished) res.status(500).set('Content-Type', 'text/plain').send(err.stack);
});
}
const server = app.listen(80, () => {
console.info('Server is started.');
});
grace.registerShutdownHandler(() => new Promise((resolve, reject) => server.close(err => err ? reject(err) : resolve())),
{description: 'Closing server connections'});
'use strict';
const IS_EMPTY = Symbol('isEmpty');
module.exports = {
SYMBOLS: {
IS_EMPTY
},
handle(requestValidator, handler, responseValidator) {
return async function (req, res, next) {
const nextWatcher = new NextWatcher(next);
const _next = nextWatcher.watcher.bind(nextWatcher);
let response;
if (requestValidator) {
try {
await requestValidator(req, res, _next);
if (nextWatcher.wasCalled) return nextWatcher.doNext();
} catch (e) { // Probably bad request
return res.status(400)
.set('Content-Type', 'text/plain')
.send(e.message);
}
}
if (handler) {
try {
response = await handler(req, res, _next);
if (nextWatcher.wasCalled) return nextWatcher.doNext();
} catch (e) { // Operation error
const code = e.code || (e.response && e.response.code);
if (/^4\d\d/.test(code)) return res.status(code)
.set('Content-Type', 'text/plain')
.send(e.message);
return next(e);
}
}
if (responseValidator) {
try {
response = await responseValidator(response, req, res, _next);
if (nextWatcher.wasCalled) return nextWatcher.doNext();
} catch (e) { // Probably bad response
return next(e);
}
}
if (!response && !res[IS_EMPTY]) return res.status(404).send();
if (res[IS_EMPTY] || [204, 304].includes(res.statusCode)) response = null;
res.status(res.statusCode || 200)
.send((typeof response === 'number') ? String(response) : response);
}
},
methodNotAllowed(router) {
return function (req, res) {
const layer = router.stack.filter(layer => layer.path === req.path)[0];
if (!layer) return res.status(404).send();
const routeMethods = Object.keys(layer.route.methods);
const allowedMethods = routeMethods
.filter(method => method[0] !== '_')
.map(method => method.toUpperCase())
.join(', ');
return res.status(405).set('Allow', allowedMethods).send();
};
}
};
class NextWatcher {
constructor(next) {
this._next = next;
this.wasCalled = false;
}
doNext() {
return this._next(...this._passedArgs);
}
watcher(...args) {
this._passedArgs = args;
this.wasCalled = true;
}
}
'use strict';
const shutdownHandlers = {};
['SIGHUP', 'SIGINT', 'SIGTERM'].forEach(signal => {
process.once(signal, async () => {
console.info(`Received ${signal}. Server is shutting down gracefully...`);
await module.exports.handleShutdown();
console.info('Shutting down now...');
process.kill(process.pid, signal);
});
});
module.exports = {
async handleShutdown() {
const priorities = Object.keys(shutdownHandlers).sort((p1, p2) => p1 - p2);
for (const priority of priorities) {
for (const h of shutdownHandlers[priority]) {
try {
if (h.description) console.info(` - ${h.description}`);
await h.handler();
} catch (e) {
console.error(e);
}
}
}
},
registerShutdownHandler(handler, options) {
options = Object.assign({}, {priority: 0}, options);
if (!shutdownHandlers[options.priority]) shutdownHandlers[options.priority] = [];
shutdownHandlers[options.priority].push({
description: options.description,
handler
});
},
async shutdown(code) {
console.info('Server is shutting down gracefully...');
await module.exports.handleShutdown();
process.exit(code || 0);
}
};
'use strict';
const _consoleLog = console.log;
const _consoleDebug = console.debug;
const _consoleError = console.error;
const _consoleInfo = console.info;
const _consoleWarn = console.warn;
console.log = function () {
_consoleLog(`[${getCurrentDateString()}]`, ...arguments);
};
console.debug = function () {
_consoleDebug(`[${getCurrentDateString()}][🔹DEBUG]`, ...arguments);
};
console.error = function () {
_consoleError(`[${getCurrentDateString()}][‼️ ERROR]`, ...arguments);
};
console.info = function () {
_consoleInfo(`[${getCurrentDateString()}][ℹ️ INFO]`, ...arguments);
};
console.warn = function () {
_consoleWarn(`[${getCurrentDateString()}][⚠️ WARN]`, ...arguments);
};
function getCurrentDateString() {
return new Date().toISOString();
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment