Files
freeCodeCamp/api-server/src/server/index.js
Oliver Eyton-Williams 5d46e2830a feat: add Docker build (#41187)
* feat(docker): build and use client and api images

* feat: always use .env

dotenv fails without throwing if the .env file is missing and never
overwrites variables if they already exist. As such, we can use it in
build pipelines.

* fix: remove quotes from env vars

dotenv normalises quoted and unquoted strings (X=x, X='x' and
X="x") all become the same .env object {X: 'x'}. However, Docker's
env_file does not (the three cases are distinct).  As a result, we
should use unquoted strings for consistency.

* fix: provide custom warning when .env is missing

* feat(docker): include client-config

* fix(docker): remove build packages from api image

* fix(docker): run script from correct dir

* fix(docker): correct permissions and dests

* fix(docker): consolidate run steps

This is standard practice, but did not have a noticable affect on the
image size

* fix(docker): clean the npm cache

Prior to this step the image was 1.11GB uncompressed and we got a modest
saving, 1.09GB after.

* refactor(docker): regexless COPY directives

* feat(docker): use alpine

This shrinks the image down to 259MB

* fix(docker): update build scripts

* fix: correct the server Dockerfile RUNs

* DEBUG: expose mysql port for seeding

* chore: update client Dockerfile's node versions

* fix: remove executable permissions from index.js

It's not a cli, so I don't think it needs to be executable.

* chore: update node and remove stale comments

* feat: use ENTRYPOINT + CMD to allow runtime config

* fix: add CURRICULUM_LOCALE arg

* feat: allow client port configuration

* feat: allow api port to be configured

* refactor: use unique variable names for ports

* fix: add default CLIENT_PORT

* refactor: clean up
2021-04-20 19:29:31 +05:30

103 lines
2.5 KiB
JavaScript

const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') });
const _ = require('lodash');
const Rx = require('rx');
const loopback = require('loopback');
const boot = require('loopback-boot');
const createDebugger = require('debug');
const morgan = require('morgan');
const Sentry = require('@sentry/node');
const { sentry } = require('../../../config/secrets');
const { setupPassport } = require('./component-passport');
const log = createDebugger('fcc:server');
const reqLogFormat = ':date[iso] :status :method :response-time ms - :url';
// force logger to always output
// this may be brittle
log.enabled = true;
if (sentry.dns === 'dsn_from_sentry_dashboard') {
log('Sentry reporting disabled unless DSN is provided.');
} else {
Sentry.init({
dsn: sentry.dns
});
log('Sentry initialized');
}
Rx.config.longStackSupport = process.env.NODE_DEBUG !== 'production';
const app = loopback();
app.set('state namespace', '__fcc__');
app.set('port', process.env.API_PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.use(loopback.token());
app.use(
morgan(reqLogFormat, { stream: { write: msg => log(_.split(msg, '\n')[0]) } })
);
app.disable('x-powered-by');
const createLogOnce = () => {
let called = false;
return str => {
if (called) {
return null;
}
called = true;
return log(str);
};
};
const logOnce = createLogOnce();
boot(app, __dirname, err => {
if (err) {
// rethrowing the error here because any error thrown in the boot stage
// is silent
logOnce('The below error was thrown in the boot stage');
throw err;
}
});
setupPassport(app);
const { db } = app.datasources;
db.on(
'connected',
_.once(() => log('db connected'))
);
app.start = _.once(function () {
const server = app.listen(app.get('port'), function () {
app.emit('started');
log(
'freeCodeCamp server listening on port %d in %s',
app.get('port'),
app.get('env')
);
log(`connecting to db at ${db.settings.url}`);
});
process.on('SIGINT', () => {
log('Shutting down server');
server.close(() => {
log('Server is closed');
});
log('closing db connection');
db.disconnect().then(() => {
log('DB connection closed');
// exit process
// this may close kept alive sockets
// eslint-disable-next-line no-process-exit
process.exit(0);
});
});
});
module.exports = app;
if (require.main === module) {
app.start();
}