diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5171c54 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cfcd966 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM node:6.11 +MAINTAINER Bono Lv + +# Working enviroment +ENV \ + CNPM_DIR="/var/app/cnpmjs.org" \ + CNPM_DATA_DIR="/var/data/cnpm_data" + +RUN mkdir -p ${CNPM_DIR} + +WORKDIR ${CNPM_DIR} + +COPY package.json ${CNPM_DIR} + +RUN npm set registry https://registry.npm.taobao.org + +RUN npm install + +COPY . ${CNPM_DIR} +COPY docs/dockerize/config.js ${CNPM_DIR}/config/ + +EXPOSE 7001/tcp 7002/tcp + +VOLUME ["/var/data/cnpm_data"] + +# Entrypoint +CMD ["node", "dispatch.js"] + diff --git a/README.md b/README.md index e5df932..cb8d14c 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,61 @@ $ make autod $ make dev ``` +### Dockerized cnpmjs.org Installation Guide + +Cnpmjs.org shipped with a simple but pragmatic Docker Compose configuration.With the configuration, you can set up a MySQL backed cnpmjs.org instance by executing just one command on Docker installed environment. + +#### Preparation + +* [Install Docker](https://www.docker.com/community-edition) +* [Install Docker Compose](https://docs.docker.com/compose/install/) (Docker for Mac, Docker for Windows include Docker Compose, so most Mac and Windows users do not need to install Docker Compose separately) +* (Optional) Speed up Docker images downloading by setting up [Docker images download accelerator](https://yq.aliyun.com/articles/29941) + + +#### Dockerized cnpmjs.org control command + +Make sure your current working directory is the root of this GitHub repository. + +##### Run dockerized cnpmjs.org + +```bash + $docker-compose up + ``` + +This command will build a Docker image using the current code of repository. Then set up a dockerized MySQL instance with data initialized. After Docker container running, you can access your cnpmjs.org web portal at http://127.0.0.1:7002 and npm register at http://127.0.0.1:7001. + +#### Run cnpmjs.org in the backend + +```bash +$docker-compose up -d +``` + +#### Rebuild cnpmjs.org Docker image + +```bash +$docker-compose build +``` + +#### Remove current dockerized cnpmjs.org instance + +The current configuration set 2 named Docker Volume for your persistent data. If you haven't change the repository directory name, them will be "cnpmjsorg_cnpm-files-volume" & "cnpmjsorg_cnpm-db-volume". + +Be Careful, the following commands will remove them. + +```bash +$docker-compose rm +$docker volume rm cnpmjsorg_cnpm-files-volume +$docker volume rm cnpmjsorg_cnpm-db-volume +``` + +You can get more information about your data volumes using the below commands: + +```bash +$docker volume ls // list all of your Docker volume +$docker volume inspect cnpmjsorg_cnpm-files-volume +$docker volume inspect cnpmjsorg_cnpm-db-volume +``` + ## How to contribute * Clone the project diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2ec7a9e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3' +services: + web: + build: + context: . + dockerfile: Dockerfile + image: cnpmjs.org + depends_on: + - mysql-db + volumes: + - cnpm-files-volume:/var/data/cnpm_data + ports: + - "7001:7001" + - "7002:7002" + + mysql-db: + image: mysql:5.6 + restart: always + environment : + MYSQL_USER: root + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + MYSQL_DATABASE: cnpmjs_test + MYSQL_ROOT_HOST: "%" + volumes: + - ./docs/db.sql:/docker-entrypoint-initdb.d/cnpm-init.sql + - cnpm-db-volume:/var/lib/mysql + +volumes: + cnpm-files-volume: + cnpm-db-volume: diff --git a/docs/dockerize/config.js b/docs/dockerize/config.js new file mode 100644 index 0000000..21f5ea2 --- /dev/null +++ b/docs/dockerize/config.js @@ -0,0 +1,261 @@ +'use strict'; + +var mkdirp = require('mkdirp'); +var copy = require('copy-to'); +var path = require('path'); +var fs = require('fs'); +var os = require('os'); + +var version = require('../package.json').version; + +var root = path.dirname(__dirname); +var dataDir = process.env.CNPM_DATA_DIR ; + +var config = { + version: version, + dataDir: dataDir, + + /** + * Cluster mode + */ + enableCluster: false, + numCPUs: os.cpus().length, + + /* + * server configure + */ + + registryPort: 7001, + webPort: 7002, + bindingHost: '0.0.0.0', // binding on 0.0.0.0 for outside of container access + + // debug mode + // if in debug mode, some middleware like limit wont load + // logger module will print to stdout + debug: process.env.NODE_ENV === 'development', + // page mode, enable on development env + pagemock: process.env.NODE_ENV === 'development', + // session secret + sessionSecret: 'cnpmjs.org test session secret', + // max request json body size + jsonLimit: '10mb', + // log dir name + logdir: path.join(dataDir, 'logs'), + // update file template dir + uploadDir: path.join(dataDir, 'downloads'), + // web page viewCache + viewCache: false, + + // config for koa-limit middleware + // for limit download rates + limit: { + enable: false, + token: 'koa-limit:download', + limit: 1000, + interval: 1000 * 60 * 60 * 24, + whiteList: [], + blackList: [], + message: 'request frequency limited, any question, please contact fengmk2@gmail.com', + }, + + enableCompress: false, // enable gzip response or not + + // default system admins + admins: { + // name: email + fengmk2: 'fengmk2@gmail.com', + admin: 'admin@cnpmjs.org', + dead_horse: 'dead_horse@qq.com', + }, + + // email notification for errors + // check https://github.com/andris9/Nodemailer for more informations + mail: { + enable: false, + appname: 'cnpmjs.org', + from: 'cnpmjs.org mail sender ', + service: 'gmail', + auth: { + user: 'address@gmail.com', + pass: 'your password' + } + }, + + logoURL: 'https://os.alipayobjects.com/rmsportal/oygxuIUkkrRccUz.jpg', // cnpm logo image url + adBanner: '', + customReadmeFile: '', // you can use your custom readme file instead the cnpm one + customFooter: '', // you can add copyright and site total script html here + npmClientName: 'cnpm', // use `${name} install package` + packagePageContributorSearch: true, // package page contributor link to search, default is true + + // max handle number of package.json `dependencies` property + maxDependencies: 200, + // backup filepath prefix + backupFilePrefix: '/cnpm/backup/', + + /** + * database config + */ + + database: { + db: 'cnpmjs_test', + username: 'root', + password: '', + + // the sql dialect of the database + // - currently supported: 'mysql', 'sqlite', 'postgres', 'mariadb' + dialect: 'mysql', + + // the Docker container network hostname defined at docker-compose.yml + host: 'mysql-db', + + // custom port; default: 3306 + port: 3306, + + // use pooling in order to reduce db connection overload and to increase speed + // currently only for mysql and postgresql (since v1.5.0) + pool: { + maxConnections: 10, + minConnections: 0, + maxIdleTime: 30000 + }, + + // the storage engine for 'sqlite' + // default store into ~/.cnpmjs.org/data.sqlite + //storage: path.join(dataDir, 'data.sqlite'), + + logging: !!process.env.SQL_DEBUG, + }, + + // package tarball store in local filesystem by default + nfs: require('fs-cnpm')({ + dir: path.join(dataDir, 'nfs') + }), + // if set true, will 302 redirect to `nfs.url(dist.key)` + downloadRedirectToNFS: false, + + // registry url name + registryHost: '127.0.0.1:7001', + + /** + * registry mode config + */ + + // enable private mode or not + // private mode: only admins can publish, other users just can sync package from source npm + // public mode: all users can publish + enablePrivate: false, + + // registry scopes, if don't set, means do not support scopes + scopes: [ '@cnpm', '@cnpmtest', '@cnpm-test' ], + + // some registry already have some private packages in global scope + // but we want to treat them as scoped private packages, + // so you can use this white list. + privatePackages: [], + + /** + * sync configs + */ + + // the official npm registry + // cnpm wont directly sync from this one + // but sometimes will request it for some package infomations + // please don't change it if not necessary + officialNpmRegistry: 'https://registry.npmjs.com', + officialNpmReplicate: 'https://replicate.npmjs.com', + + // sync source, upstream registry + // If you want to directly sync from official npm's registry + // please drop them an email first + sourceNpmRegistry: 'https://registry.npm.taobao.org', + + // upstream registry is base on cnpm/cnpmjs.org or not + // if your upstream is official npm registry, please turn it off + sourceNpmRegistryIsCNpm: true, + + // if install return 404, try to sync from source registry + syncByInstall: true, + + // sync mode select + // none: do not sync any module, proxy all public modules from sourceNpmRegistry + // exist: only sync exist modules + // all: sync all modules + syncModel: 'none', // 'none', 'all', 'exist' + + syncConcurrency: 1, + // sync interval, default is 10 minutes + syncInterval: '10m', + + // sync polular modules, default to false + // because cnpm can't auto sync tag change for now + // so we want to sync popular modules to ensure their tags + syncPopular: false, + syncPopularInterval: '1h', + // top 100 + topPopular: 100, + + // sync devDependencies or not, default is false + syncDevDependencies: false, + + // changes streaming sync + syncChangesStream: false, + handleSyncRegistry: 'http://127.0.0.1:7001', + + // badge subject on http://shields.io/ + badgePrefixURL: 'https://img.shields.io/badge', + badgeSubject: 'cnpm', + + // custom user service, @see https://github.com/cnpm/cnpmjs.org/wiki/Use-Your-Own-User-Authorization + // when you not intend to ingegrate with your company's user system, then use null, it would + // use the default cnpm user system + userService: null, + + // always-auth https://docs.npmjs.com/misc/config#always-auth + // Force npm to always require authentication when accessing the registry, even for GET requests. + alwaysAuth: false, + + // if you're behind firewall, need to request through http proxy, please set this + // e.g.: `httpProxy: 'http://proxy.mycompany.com:8080'` + httpProxy: null, + + // snyk.io root url + snykUrl: 'https://snyk.io', + + // https://github.com/cnpm/cnpmjs.org/issues/1149 + // if enable this option, must create module_abbreviated and package_readme table in database + enableAbbreviatedMetadata: true, + + // global hook function: function* (envelope) {} + // envelope format please see https://github.com/npm/registry/blob/master/docs/hooks/hooks-payload.md#payload + globalHook: null, +}; + +if (process.env.NODE_ENV !== 'test') { + var customConfig; + if (process.env.NODE_ENV === 'development') { + customConfig = path.join(root, 'config', 'config.js'); + } else { + // 1. try to load `$dataDir/config.json` first, not exists then goto 2. + // 2. load config/config.js, everything in config.js will cover the same key in index.js + customConfig = path.join(dataDir, 'config.json'); + if (!fs.existsSync(customConfig)) { + customConfig = path.join(root, 'config', 'config.js'); + } + } + if (fs.existsSync(customConfig)) { + copy(require(customConfig)).override(config); + } +} + +mkdirp.sync(config.logdir); +mkdirp.sync(config.uploadDir); + +module.exports = config; + +config.loadConfig = function (customConfig) { + if (!customConfig) { + return; + } + copy(customConfig).override(config); +};