Compare commits
2 Commits
xo-web-v5.
...
xo-server/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f32a89015 | ||
|
|
20d1b7c481 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,6 +8,8 @@
|
|||||||
/packages/*/dist/
|
/packages/*/dist/
|
||||||
/packages/*/node_modules/
|
/packages/*/node_modules/
|
||||||
|
|
||||||
|
/@xen-orchestra/log/src/transports/index.js
|
||||||
|
|
||||||
/packages/vhd-cli/src/commands/index.js
|
/packages/vhd-cli/src/commands/index.js
|
||||||
|
|
||||||
/packages/xen-api/plot.dat
|
/packages/xen-api/plot.dat
|
||||||
@@ -22,6 +24,8 @@
|
|||||||
/packages/xo-web/src/common/intl/locales/index.js
|
/packages/xo-web/src/common/intl/locales/index.js
|
||||||
/packages/xo-web/src/common/themes/index.js
|
/packages/xo-web/src/common/themes/index.js
|
||||||
|
|
||||||
|
/packages/xo-server-rework/src/app/mixins/index.js
|
||||||
|
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
npm-debug.log.*
|
npm-debug.log.*
|
||||||
pnpm-debug.log
|
pnpm-debug.log
|
||||||
|
|||||||
3
@xen-orchestra/async-fs/.babelrc.js
Normal file
3
@xen-orchestra/async-fs/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
||||||
24
@xen-orchestra/async-fs/.npmignore
Normal file
24
@xen-orchestra/async-fs/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
49
@xen-orchestra/async-fs/README.md
Normal file
49
@xen-orchestra/async-fs/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ${pkg.name} [](https://travis-ci.org/${pkg.shortGitHubPath})
|
||||||
|
|
||||||
|
> ${pkg.description}
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --save ${pkg.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**TODO**
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](${pkg.bugs})
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
${pkg.license} © [${pkg.author.name}](${pkg.author.url})
|
||||||
47
@xen-orchestra/async-fs/package.json
Normal file
47
@xen-orchestra/async-fs/package.json
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@xen-orchestra/async-fs",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/@xen-orchestra/async-fs",
|
||||||
|
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@isonoe.net"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"promise-toolbox": "^0.9.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
|
"@babel/core": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
@xen-orchestra/async-fs/src/index.js
Normal file
10
@xen-orchestra/async-fs/src/index.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import fs from 'fs'
|
||||||
|
import { promisifyAll } from 'promise-toolbox'
|
||||||
|
|
||||||
|
const NOT_PROMISIFIABLE_RE = /^(?:[_A-Z]|exists$)|(?:Async|Stream|Sync)$/
|
||||||
|
|
||||||
|
module.exports = promisifyAll(fs, {
|
||||||
|
mapper: name => !NOT_PROMISIFIABLE_RE.test(name) && name,
|
||||||
|
})
|
||||||
3
@xen-orchestra/async-map/.babelrc.js
Normal file
3
@xen-orchestra/async-map/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
||||||
24
@xen-orchestra/async-map/.npmignore
Normal file
24
@xen-orchestra/async-map/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
49
@xen-orchestra/async-map/README.md
Normal file
49
@xen-orchestra/async-map/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ${pkg.name} [](https://travis-ci.org/${pkg.shortGitHubPath})
|
||||||
|
|
||||||
|
> ${pkg.description}
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --save ${pkg.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**TODO**
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](${pkg.bugs})
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
${pkg.license} © [${pkg.author.name}](${pkg.author.url})
|
||||||
50
@xen-orchestra/async-map/package.json
Normal file
50
@xen-orchestra/async-map/package.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@xen-orchestra/async-map",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/@xen-orchestra/async-map",
|
||||||
|
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@isonoe.net"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
">2%"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
|
"@babel/core": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build"
|
||||||
|
}
|
||||||
|
}
|
||||||
36
@xen-orchestra/async-map/src/index.js
Normal file
36
@xen-orchestra/async-map/src/index.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import { map } from 'lodash'
|
||||||
|
|
||||||
|
// Similar to map() + Promise.all() but wait for all promises to
|
||||||
|
// settle before rejecting (with the first error)
|
||||||
|
const asyncMap = <T1, T2>(
|
||||||
|
collection: Array<T1> | Promise<Array<T1>>,
|
||||||
|
iteratee: (value: T1, key: number, collection: Array<T1>) => T2
|
||||||
|
): Promise<Array<T2>> => {
|
||||||
|
if (!Array.isArray(collection)) {
|
||||||
|
return collection.then(collection => asyncMap(collection, iteratee))
|
||||||
|
}
|
||||||
|
|
||||||
|
let errorContainer
|
||||||
|
const onError = error => {
|
||||||
|
if (errorContainer === undefined) {
|
||||||
|
errorContainer = { error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
map(collection, (item, key, collection) =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
resolve(iteratee(item, key, collection))
|
||||||
|
}).catch(onError)
|
||||||
|
)
|
||||||
|
).then(values => {
|
||||||
|
if (errorContainer !== undefined) {
|
||||||
|
throw errorContainer.error
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export { asyncMap as default }
|
||||||
@@ -7,34 +7,46 @@ const NODE_ENV = process.env.NODE_ENV || 'development'
|
|||||||
const __PROD__ = NODE_ENV === 'production'
|
const __PROD__ = NODE_ENV === 'production'
|
||||||
const __TEST__ = NODE_ENV === 'test'
|
const __TEST__ = NODE_ENV === 'test'
|
||||||
|
|
||||||
|
const configs = {
|
||||||
|
'@babel/plugin-proposal-decorators': {
|
||||||
|
legacy: true,
|
||||||
|
},
|
||||||
|
'@babel/preset-env' (pkg) {
|
||||||
|
return {
|
||||||
|
debug: !__TEST__,
|
||||||
|
loose: true,
|
||||||
|
shippedProposals: true,
|
||||||
|
targets: __PROD__
|
||||||
|
? (() => {
|
||||||
|
let node = (pkg.engines || {}).node
|
||||||
|
if (node !== undefined) {
|
||||||
|
const trimChars = '^=>~'
|
||||||
|
while (trimChars.includes(node[0])) {
|
||||||
|
node = node.slice(1)
|
||||||
|
}
|
||||||
|
return { node: node }
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
: { browsers: '', node: 'current' },
|
||||||
|
useBuiltIns: '@babel/polyfill' in (pkg.dependencies || {}) && 'usage',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const getConfig = (key, ...args) => {
|
||||||
|
const config = configs[key]
|
||||||
|
return config === undefined ? {} : typeof config === 'function' ? config(...args) : config
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = function (pkg, plugins, presets) {
|
module.exports = function (pkg, plugins, presets) {
|
||||||
plugins === undefined && (plugins = {})
|
plugins === undefined && (plugins = {})
|
||||||
|
|
||||||
presets === undefined && (presets = {})
|
presets === undefined && (presets = {})
|
||||||
presets['@babel/preset-env'] = {
|
|
||||||
debug: !__TEST__,
|
|
||||||
loose: true,
|
|
||||||
shippedProposals: true,
|
|
||||||
targets: __PROD__
|
|
||||||
? (() => {
|
|
||||||
let node = (pkg.engines || {}).node
|
|
||||||
if (node !== undefined) {
|
|
||||||
const trimChars = '^=>~'
|
|
||||||
while (trimChars.includes(node[0])) {
|
|
||||||
node = node.slice(1)
|
|
||||||
}
|
|
||||||
return { node: node }
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
: { browsers: '', node: 'current' },
|
|
||||||
useBuiltIns: '@babel/polyfill' in (pkg.dependencies || {}) && 'usage',
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(pkg.devDependencies || {}).forEach(name => {
|
Object.keys(pkg.devDependencies || {}).forEach(name => {
|
||||||
if (!(name in presets) && PLUGINS_RE.test(name)) {
|
if (!(name in presets) && PLUGINS_RE.test(name)) {
|
||||||
plugins[name] = {}
|
plugins[name] = getConfig(name, pkg)
|
||||||
} else if (!(name in presets) && PRESETS_RE.test(name)) {
|
} else if (!(name in presets) && PRESETS_RE.test(name)) {
|
||||||
presets[name] = {}
|
presets[name] = getConfig(name, pkg)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,10 @@
|
|||||||
"moment-timezone": "^0.5.14"
|
"moment-timezone": "^0.5.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.0.0-beta.44",
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
"@babel/core": "7.0.0-beta.44",
|
"@babel/core": "7.0.0-beta.46",
|
||||||
"@babel/preset-env": "7.0.0-beta.44",
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
"@babel/preset-flow": "7.0.0-beta.44",
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
"cross-env": "^5.1.3",
|
"cross-env": "^5.1.3",
|
||||||
"rimraf": "^2.6.2"
|
"rimraf": "^2.6.2"
|
||||||
},
|
},
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"clean": "rimraf dist/",
|
"clean": "rimraf dist/",
|
||||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
"prebuild": "yarn run clean",
|
"prebuild": "yarn run clean",
|
||||||
"predev": "yarn run clean",
|
"predev": "yarn run prebuild",
|
||||||
"prepublishOnly": "yarn run build"
|
"prepublishOnly": "yarn run build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
@xen-orchestra/defined/.babelrc.js
Normal file
3
@xen-orchestra/defined/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
||||||
24
@xen-orchestra/defined/.npmignore
Normal file
24
@xen-orchestra/defined/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
49
@xen-orchestra/defined/README.md
Normal file
49
@xen-orchestra/defined/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ${pkg.name} [](https://travis-ci.org/${pkg.shortGitHubPath})
|
||||||
|
|
||||||
|
> ${pkg.description}
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --save ${pkg.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**TODO**
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](${pkg.bugs})
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
${pkg.license} © [${pkg.author.name}](${pkg.author.url})
|
||||||
48
@xen-orchestra/defined/package.json
Normal file
48
@xen-orchestra/defined/package.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@xen-orchestra/defined",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/@xen-orchestra/defined",
|
||||||
|
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@vates.fr"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
">2%"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
|
"@babel/core": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build"
|
||||||
|
}
|
||||||
|
}
|
||||||
65
@xen-orchestra/defined/src/index.js
Normal file
65
@xen-orchestra/defined/src/index.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// ```js
|
||||||
|
// const httpProxy = defined(
|
||||||
|
// process.env.HTTP_PROXY,
|
||||||
|
// process.env.http_proxy
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// const httpProxy = defined([
|
||||||
|
// process.env.HTTP_PROXY,
|
||||||
|
// process.env.http_proxy
|
||||||
|
// ])
|
||||||
|
// ```
|
||||||
|
export default function defined () {
|
||||||
|
let args = arguments
|
||||||
|
let n = args.length
|
||||||
|
if (n === 1) {
|
||||||
|
args = arguments[0]
|
||||||
|
n = args.length
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < n; ++i) {
|
||||||
|
let arg = arguments[i]
|
||||||
|
if (typeof arg === 'function') {
|
||||||
|
arg = get(arg)
|
||||||
|
}
|
||||||
|
if (arg !== undefined) {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// ```js
|
||||||
|
// const friendName = get(() => props.user.friends[0].name)
|
||||||
|
//
|
||||||
|
// // this form can be used to avoid recreating functions:
|
||||||
|
// const getFriendName = _ => _.friends[0].name
|
||||||
|
// const friendName = get(getFriendName, props.user)
|
||||||
|
// ```
|
||||||
|
export const get = (accessor: (input: ?any) => any, arg: ?any) => {
|
||||||
|
try {
|
||||||
|
return accessor(arg)
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof TypeError)) { // avoid hidding other errors
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// ```js
|
||||||
|
// const httpAgent = ifDef(
|
||||||
|
// process.env.HTTP_PROXY,
|
||||||
|
// _ => new ProxyAgent(_)
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
export const ifDef = (value: ?any, thenFn: (value: any) => any) =>
|
||||||
|
value !== undefined
|
||||||
|
? thenFn(value)
|
||||||
|
: value
|
||||||
3
@xen-orchestra/emit-async/.babelrc.js
Normal file
3
@xen-orchestra/emit-async/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
||||||
24
@xen-orchestra/emit-async/.npmignore
Normal file
24
@xen-orchestra/emit-async/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
49
@xen-orchestra/emit-async/README.md
Normal file
49
@xen-orchestra/emit-async/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ${pkg.name} [](https://travis-ci.org/${pkg.shortGitHubPath})
|
||||||
|
|
||||||
|
> ${pkg.description}
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --save ${pkg.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**TODO**
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](${pkg.bugs})
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
${pkg.license} © [${pkg.author.name}](${pkg.author.url})
|
||||||
48
@xen-orchestra/emit-async/package.json
Normal file
48
@xen-orchestra/emit-async/package.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@xen-orchestra/emit-async",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/@xen-orchestra/emit-async",
|
||||||
|
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@vates.fr"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
">2%"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
|
"@babel/core": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build"
|
||||||
|
}
|
||||||
|
}
|
||||||
24
@xen-orchestra/emit-async/src/index.js
Normal file
24
@xen-orchestra/emit-async/src/index.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export default function emitAsync (event) {
|
||||||
|
let opts
|
||||||
|
let i = 1
|
||||||
|
|
||||||
|
// an option object has been passed as first param
|
||||||
|
if (typeof event !== 'string') {
|
||||||
|
opts = event
|
||||||
|
event = arguments[i++]
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = arguments.length - i
|
||||||
|
const args = new Array(n)
|
||||||
|
for (let j = 0; j < n; ++j) {
|
||||||
|
args[j] = arguments[j + i]
|
||||||
|
}
|
||||||
|
|
||||||
|
const onError = opts != null && opts.onError
|
||||||
|
|
||||||
|
return Promise.all(this.listeners(event).map(
|
||||||
|
listener => new Promise(resolve => {
|
||||||
|
resolve(listener.apply(this, args))
|
||||||
|
}).catch(onError)
|
||||||
|
))
|
||||||
|
}
|
||||||
3
@xen-orchestra/log/.babelrc.js
Normal file
3
@xen-orchestra/log/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
||||||
24
@xen-orchestra/log/.npmignore
Normal file
24
@xen-orchestra/log/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
141
@xen-orchestra/log/README.md
Normal file
141
@xen-orchestra/log/README.md
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# @xen-orchestra/log [](https://travis-ci.org/vatesfr/xen-orchestra)
|
||||||
|
|
||||||
|
> ${pkg.description}
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/log):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --save @xen-orchestra/log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Everywhere something should be logged:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import createLogger from '@xen-orchestra/log'
|
||||||
|
|
||||||
|
const log = createLogger('xo-server-api')
|
||||||
|
log.warn('foo')
|
||||||
|
```
|
||||||
|
|
||||||
|
Then at application level you can choose how to handle these logs:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { configure, transports } from '@xen-orchestra/log'
|
||||||
|
|
||||||
|
configure([
|
||||||
|
{
|
||||||
|
// if filter is a string, then it is pattern
|
||||||
|
// (https://github.com/visionmedia/debug#wildcards) which is
|
||||||
|
// matched against the namespace of the logs
|
||||||
|
filter: process.env.DEBUG,
|
||||||
|
|
||||||
|
transport: transports.console()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// only levels >= warn
|
||||||
|
level: 'warn',
|
||||||
|
|
||||||
|
transport: transports.email({
|
||||||
|
service: 'gmail',
|
||||||
|
auth: {
|
||||||
|
user: 'jane.smith@gmail.com',
|
||||||
|
pass: 'H&NbECcpXF|pyXe#%ZEb'
|
||||||
|
},
|
||||||
|
from: 'jane.smith@gmail.com',
|
||||||
|
to: [
|
||||||
|
'jane.smith@gmail.com',
|
||||||
|
'sam.doe@yahoo.com'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transports
|
||||||
|
|
||||||
|
#### Console
|
||||||
|
|
||||||
|
```js
|
||||||
|
configure(transports.console())
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Email
|
||||||
|
|
||||||
|
Optional dependency:
|
||||||
|
|
||||||
|
```
|
||||||
|
> yarn add nodemailer pretty-format
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
|
||||||
|
```js
|
||||||
|
configure(transports.email({
|
||||||
|
service: 'gmail',
|
||||||
|
auth: {
|
||||||
|
user: 'jane.smith@gmail.com',
|
||||||
|
pass: 'H&NbECcpXF|pyXe#%ZEb'
|
||||||
|
},
|
||||||
|
from: 'jane.smith@gmail.com',
|
||||||
|
to: [
|
||||||
|
'jane.smith@gmail.com',
|
||||||
|
'sam.doe@yahoo.com'
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Syslog
|
||||||
|
|
||||||
|
Optional dependency:
|
||||||
|
|
||||||
|
```
|
||||||
|
> yarn add split-host syslog-client
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// By default, log to udp://localhost:514
|
||||||
|
configure(transports.syslog())
|
||||||
|
|
||||||
|
// But TCP, a different host, or a different port can be used
|
||||||
|
configure(transports.syslog('tcp://syslog.company.lan'))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](https://github.com/vatesfr/xo-web/issues/)
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
ISC © [Vates SAS](https://vates.fr)
|
||||||
1
@xen-orchestra/log/configure.js
Symbolic link
1
@xen-orchestra/log/configure.js
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
dist/configure.js
|
||||||
53
@xen-orchestra/log/package.json
Normal file
53
@xen-orchestra/log/package.json
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@xen-orchestra/log",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/@xen-orchestra/log",
|
||||||
|
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@vates.fr"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
">2%"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/polyfill": "7.0.0-beta.46",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"promise-toolbox": "^0.9.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
|
"@babel/core": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"index-modules": "^0.3.0",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "index-modules --cjs-lazy src/transports && cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "index-modules --cjs-lazy src/transports && cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build"
|
||||||
|
}
|
||||||
|
}
|
||||||
94
@xen-orchestra/log/src/configure.js
Normal file
94
@xen-orchestra/log/src/configure.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import createConsoleTransport from './transports/console'
|
||||||
|
import LEVELS, { resolve } from './levels'
|
||||||
|
import { compileGlobPattern } from './utils'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const createTransport = config => {
|
||||||
|
if (typeof config === 'function') {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(config)) {
|
||||||
|
const transports = config.map(createTransport)
|
||||||
|
const { length } = transports
|
||||||
|
return function () {
|
||||||
|
for (let i = 0; i < length; ++i) {
|
||||||
|
transports[i].apply(this, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let { filter, transport } = config
|
||||||
|
const level = resolve(config.level)
|
||||||
|
|
||||||
|
if (filter !== undefined) {
|
||||||
|
if (typeof filter === 'string') {
|
||||||
|
const re = compileGlobPattern(filter)
|
||||||
|
filter = log => re.test(log.namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
const orig = transport
|
||||||
|
transport = function (log) {
|
||||||
|
if ((level !== undefined && log.level >= level) || filter(log)) {
|
||||||
|
return orig.apply(this, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (level !== undefined) {
|
||||||
|
const orig = transport
|
||||||
|
transport = function (log) {
|
||||||
|
if (log.level >= level) {
|
||||||
|
return orig.apply(this, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport
|
||||||
|
}
|
||||||
|
|
||||||
|
let transport = createTransport({
|
||||||
|
// display warnings or above, and all that are enabled via DEBUG or
|
||||||
|
// NODE_DEBUG env
|
||||||
|
filter: process.env.DEBUG || process.env.NODE_DEBUG,
|
||||||
|
level: LEVELS.INFO,
|
||||||
|
|
||||||
|
transport: createConsoleTransport(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const configure = config => {
|
||||||
|
transport = createTransport(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const catchGlobalErrors = logger => {
|
||||||
|
// patch process
|
||||||
|
const onUncaughtException = error => {
|
||||||
|
logger.error('uncaught exception', { error })
|
||||||
|
}
|
||||||
|
const onUnhandledRejection = error => {
|
||||||
|
logger.warn('possibly unhandled rejection', { error })
|
||||||
|
}
|
||||||
|
process.on('uncaughtException', onUncaughtException)
|
||||||
|
process.on('unhandledRejection', onUnhandledRejection)
|
||||||
|
|
||||||
|
// patch EventEmitter
|
||||||
|
const EventEmitter = require('events')
|
||||||
|
const { prototype } = EventEmitter
|
||||||
|
const { emit } = prototype
|
||||||
|
function patchedEmit (event, error) {
|
||||||
|
event === 'error' && !this.listenerCount(event)
|
||||||
|
? logger.error('unhandled error event', { error })
|
||||||
|
: emit.apply(this, arguments)
|
||||||
|
}
|
||||||
|
prototype.emit = patchedEmit
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
process.removeListener('uncaughtException', onUncaughtException)
|
||||||
|
process.removeListener('unhandledRejection', onUnhandledRejection)
|
||||||
|
|
||||||
|
if (prototype.emit === patchedEmit) {
|
||||||
|
prototype.emit = emit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
@xen-orchestra/log/src/index.js
Normal file
62
@xen-orchestra/log/src/index.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import createTransport from './transports/console'
|
||||||
|
import LEVELS from './levels'
|
||||||
|
|
||||||
|
const symbol = typeof Symbol !== 'undefined' ? Symbol.for('@xen-orchestra/log') : '@@@xen-orchestra/log'
|
||||||
|
if (!(symbol in global)) {
|
||||||
|
// the default behavior, without requiring `configure` is to avoid
|
||||||
|
// logging anything unless it's a real error
|
||||||
|
const transport = createTransport()
|
||||||
|
global[symbol] = log => log.level > LEVEL.WARN && transport(log)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
function Log (data, level, namespace, message, time) {
|
||||||
|
this.data = data
|
||||||
|
this.level = level
|
||||||
|
this.namespace = namespace
|
||||||
|
this.message = message
|
||||||
|
this.time = time
|
||||||
|
}
|
||||||
|
|
||||||
|
function Logger (namespace) {
|
||||||
|
this._namespace = namespace
|
||||||
|
|
||||||
|
// bind all logging methods
|
||||||
|
for (const name in LEVELS) {
|
||||||
|
const lowerCase = name.toLowerCase()
|
||||||
|
this[lowerCase] = this[lowerCase].bind(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { prototype } = Logger
|
||||||
|
|
||||||
|
for (const name in LEVELS) {
|
||||||
|
const level = LEVELS[name]
|
||||||
|
|
||||||
|
prototype[name.toLowerCase()] = function (message, data) {
|
||||||
|
global[symbol](new Log(data, level, this._namespace, message, new Date()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prototype.wrap = function (message, fn) {
|
||||||
|
const logger = this
|
||||||
|
const warnAndRethrow = error => {
|
||||||
|
logger.warn(message, { error })
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
try {
|
||||||
|
const result = fn.apply(this, arguments)
|
||||||
|
const then = result != null && result.then
|
||||||
|
return typeof then === 'function'
|
||||||
|
? then.call(result, warnAndRethrow)
|
||||||
|
: result
|
||||||
|
} catch (error) {
|
||||||
|
warnAndRethrow(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createLogger = namespace => new Logger(namespace)
|
||||||
|
export { createLogger }
|
||||||
24
@xen-orchestra/log/src/levels.js
Normal file
24
@xen-orchestra/log/src/levels.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const LEVELS = Object.create(null)
|
||||||
|
export { LEVELS as default }
|
||||||
|
|
||||||
|
// https://github.com/trentm/node-bunyan#levels
|
||||||
|
LEVELS.FATAL = 60 // service/app is going to down
|
||||||
|
LEVELS.ERROR = 50 // fatal for current action
|
||||||
|
LEVELS.WARN = 40 // something went wrong but it's not fatal
|
||||||
|
LEVELS.INFO = 30 // detail on unusual but normal operation
|
||||||
|
LEVELS.DEBUG = 20
|
||||||
|
|
||||||
|
export const NAMES = Object.create(null)
|
||||||
|
for (const name in LEVELS) {
|
||||||
|
NAMES[LEVELS[name]] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
export const resolve = level => {
|
||||||
|
if (typeof level === 'string') {
|
||||||
|
level = LEVELS[level.toUpperCase()]
|
||||||
|
}
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.freeze(LEVELS)
|
||||||
|
Object.freeze(NAMES)
|
||||||
32
@xen-orchestra/log/src/levels.spec.js
Normal file
32
@xen-orchestra/log/src/levels.spec.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
import { forEach, isInteger } from 'lodash'
|
||||||
|
|
||||||
|
import LEVELS, { NAMES, resolve } from './levels'
|
||||||
|
|
||||||
|
describe('LEVELS', () => {
|
||||||
|
it('maps level names to their integer values', () => {
|
||||||
|
forEach(LEVELS, (value, name) => {
|
||||||
|
expect(isInteger(value)).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('NAMES', () => {
|
||||||
|
it('maps level values to their names', () => {
|
||||||
|
forEach(LEVELS, (value, name) => {
|
||||||
|
expect(NAMES[value]).toBe(name)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('resolve()', () => {
|
||||||
|
it('returns level values either from values or names', () => {
|
||||||
|
forEach(LEVELS, value => {
|
||||||
|
expect(resolve(value)).toBe(value)
|
||||||
|
})
|
||||||
|
forEach(NAMES, (name, value) => {
|
||||||
|
expect(resolve(name)).toBe(+value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
0
@xen-orchestra/log/src/transports/.index-modules
Normal file
0
@xen-orchestra/log/src/transports/.index-modules
Normal file
20
@xen-orchestra/log/src/transports/console.js
Normal file
20
@xen-orchestra/log/src/transports/console.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import LEVELS, { NAMES } from '../levels'
|
||||||
|
|
||||||
|
// Bind console methods (necessary for browsers)
|
||||||
|
const debugConsole = console.log.bind(console)
|
||||||
|
const infoConsole = console.info.bind(console)
|
||||||
|
const warnConsole = console.warn.bind(console)
|
||||||
|
const errorConsole = console.error.bind(console)
|
||||||
|
|
||||||
|
const { ERROR, INFO, WARN } = LEVELS
|
||||||
|
|
||||||
|
const consoleTransport = ({ data, level, namespace, message, time }) => {
|
||||||
|
const fn =
|
||||||
|
level < INFO
|
||||||
|
? debugConsole
|
||||||
|
: level < WARN ? infoConsole : level < ERROR ? warnConsole : errorConsole
|
||||||
|
|
||||||
|
fn('%s - %s - [%s] %s', time.toISOString(), namespace, NAMES[level], message)
|
||||||
|
data != null && fn(data)
|
||||||
|
}
|
||||||
|
export default () => consoleTransport
|
||||||
68
@xen-orchestra/log/src/transports/email.js
Normal file
68
@xen-orchestra/log/src/transports/email.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import prettyFormat from 'pretty-format' // eslint-disable-line node/no-extraneous-import
|
||||||
|
import { createTransport } from 'nodemailer' // eslint-disable-line node/no-extraneous-import
|
||||||
|
import { fromCallback } from 'promise-toolbox'
|
||||||
|
|
||||||
|
import { evalTemplate, required } from '../utils'
|
||||||
|
import { NAMES } from '../levels'
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
// transport options (https://nodemailer.com/smtp/)
|
||||||
|
auth,
|
||||||
|
authMethod,
|
||||||
|
host,
|
||||||
|
ignoreTLS,
|
||||||
|
port,
|
||||||
|
proxy,
|
||||||
|
requireTLS,
|
||||||
|
secure,
|
||||||
|
service,
|
||||||
|
tls,
|
||||||
|
|
||||||
|
// message options (https://nodemailer.com/message/)
|
||||||
|
bcc,
|
||||||
|
cc,
|
||||||
|
from = required('from'),
|
||||||
|
to = required('to'),
|
||||||
|
subject = '[{{level}} - {{namespace}}] {{time}} {{message}}',
|
||||||
|
}) => {
|
||||||
|
const transporter = createTransport(
|
||||||
|
{
|
||||||
|
auth,
|
||||||
|
authMethod,
|
||||||
|
host,
|
||||||
|
ignoreTLS,
|
||||||
|
port,
|
||||||
|
proxy,
|
||||||
|
requireTLS,
|
||||||
|
secure,
|
||||||
|
service,
|
||||||
|
tls,
|
||||||
|
|
||||||
|
disableFileAccess: true,
|
||||||
|
disableUrlAccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bcc,
|
||||||
|
cc,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return log =>
|
||||||
|
fromCallback(cb =>
|
||||||
|
transporter.sendMail(
|
||||||
|
{
|
||||||
|
subject: evalTemplate(
|
||||||
|
subject,
|
||||||
|
key =>
|
||||||
|
key === 'level'
|
||||||
|
? NAMES[log.level]
|
||||||
|
: key === 'time' ? log.time.toISOString() : log[key]
|
||||||
|
),
|
||||||
|
text: prettyFormat(log.data),
|
||||||
|
},
|
||||||
|
cb
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
7
@xen-orchestra/log/src/transports/memory.js
Normal file
7
@xen-orchestra/log/src/transports/memory.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default () => {
|
||||||
|
const memoryLogger = log => {
|
||||||
|
logs.push(log)
|
||||||
|
}
|
||||||
|
const logs = (memoryLogger.logs = [])
|
||||||
|
return memoryLogger
|
||||||
|
}
|
||||||
42
@xen-orchestra/log/src/transports/syslog.js
Normal file
42
@xen-orchestra/log/src/transports/syslog.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import splitHost from 'split-host' // eslint-disable-line node/no-extraneous-import node/no-missing-import
|
||||||
|
import { createClient, Facility, Severity, Transport } from 'syslog-client' // eslint-disable-line node/no-extraneous-import node/no-missing-import
|
||||||
|
import { fromCallback } from 'promise-toolbox'
|
||||||
|
import { startsWith } from 'lodash'
|
||||||
|
|
||||||
|
import LEVELS from '../levels'
|
||||||
|
|
||||||
|
// https://github.com/paulgrove/node-syslog-client#syslogseverity
|
||||||
|
const LEVEL_TO_SEVERITY = {
|
||||||
|
[LEVELS.FATAL]: Severity.Critical,
|
||||||
|
[LEVELS.ERROR]: Severity.Error,
|
||||||
|
[LEVELS.WARN]: Severity.Warning,
|
||||||
|
[LEVELS.INFO]: Severity.Informational,
|
||||||
|
[LEVELS.DEBUG]: Severity.Debug,
|
||||||
|
}
|
||||||
|
|
||||||
|
const facility = Facility.User
|
||||||
|
|
||||||
|
export default target => {
|
||||||
|
const opts = {}
|
||||||
|
if (target !== undefined) {
|
||||||
|
if (startsWith(target, 'tcp://')) {
|
||||||
|
target = target.slice(6)
|
||||||
|
opts.transport = Transport.Tcp
|
||||||
|
} else if (startsWith(target, 'udp://')) {
|
||||||
|
target = target.slice(6)
|
||||||
|
opts.transport = Transport.Ucp
|
||||||
|
}
|
||||||
|
|
||||||
|
({ host: target, port: opts.port } = splitHost(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = createClient(target, opts)
|
||||||
|
|
||||||
|
return log =>
|
||||||
|
fromCallback(cb =>
|
||||||
|
client.log(log.message, {
|
||||||
|
facility,
|
||||||
|
severity: LEVEL_TO_SEVERITY[log.level],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
64
@xen-orchestra/log/src/utils.js
Normal file
64
@xen-orchestra/log/src/utils.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import escapeRegExp from 'lodash/escapeRegExp'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const TPL_RE = /\{\{(.+?)\}\}/g
|
||||||
|
export const evalTemplate = (tpl, data) => {
|
||||||
|
const getData =
|
||||||
|
typeof data === 'function'
|
||||||
|
? (_, key) => data(key)
|
||||||
|
: (_, key) => data[key]
|
||||||
|
|
||||||
|
return tpl.replace(TPL_RE, getData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
const compileGlobPatternFragment = pattern =>
|
||||||
|
pattern
|
||||||
|
.split('*')
|
||||||
|
.map(escapeRegExp)
|
||||||
|
.join('.*')
|
||||||
|
|
||||||
|
export const compileGlobPattern = pattern => {
|
||||||
|
const no = []
|
||||||
|
const yes = []
|
||||||
|
pattern.split(/[\s,]+/).forEach(pattern => {
|
||||||
|
if (pattern[0] === '-') {
|
||||||
|
no.push(pattern.slice(1))
|
||||||
|
} else {
|
||||||
|
yes.push(pattern)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const raw = ['^']
|
||||||
|
|
||||||
|
if (no.length !== 0) {
|
||||||
|
raw.push('(?!', no.map(compileGlobPatternFragment).join('|'), ')')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yes.length !== 0) {
|
||||||
|
raw.push('(?:', yes.map(compileGlobPatternFragment).join('|'), ')')
|
||||||
|
} else {
|
||||||
|
raw.push('.*')
|
||||||
|
}
|
||||||
|
|
||||||
|
raw.push('$')
|
||||||
|
|
||||||
|
return new RegExp(raw.join(''))
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const required = name => {
|
||||||
|
throw new Error(`missing required arg ${name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const serializeError = error => ({
|
||||||
|
...error,
|
||||||
|
message: error.message,
|
||||||
|
name: error.name,
|
||||||
|
stack: error.stack,
|
||||||
|
})
|
||||||
13
@xen-orchestra/log/src/utils.spec.js
Normal file
13
@xen-orchestra/log/src/utils.spec.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
import { compileGlobPattern } from './utils'
|
||||||
|
|
||||||
|
describe('compileGlobPattern()', () => {
|
||||||
|
it('works', () => {
|
||||||
|
const re = compileGlobPattern('foo, ba*, -bar')
|
||||||
|
expect(re.test('foo')).toBe(true)
|
||||||
|
expect(re.test('bar')).toBe(false)
|
||||||
|
expect(re.test('baz')).toBe(true)
|
||||||
|
expect(re.test('qux')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
1
@xen-orchestra/log/transports
Symbolic link
1
@xen-orchestra/log/transports
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
dist/transports
|
||||||
3
@xen-orchestra/mixin/.babelrc.js
Normal file
3
@xen-orchestra/mixin/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
||||||
24
@xen-orchestra/mixin/.npmignore
Normal file
24
@xen-orchestra/mixin/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
49
@xen-orchestra/mixin/README.md
Normal file
49
@xen-orchestra/mixin/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# ${pkg.name} [](https://travis-ci.org/${pkg.shortGitHubPath})
|
||||||
|
|
||||||
|
> ${pkg.description}
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --save ${pkg.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**TODO**
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](${pkg.bugs})
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
${pkg.license} © [${pkg.author.name}](${pkg.author.url})
|
||||||
50
@xen-orchestra/mixin/package.json
Normal file
50
@xen-orchestra/mixin/package.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@xen-orchestra/mixin",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/@xen-orchestra/mixin",
|
||||||
|
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@vates.fr"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
">2%"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bind-property-descriptor": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
|
"@babel/core": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build"
|
||||||
|
}
|
||||||
|
}
|
||||||
128
@xen-orchestra/mixin/src/index.js
Normal file
128
@xen-orchestra/mixin/src/index.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { getBoundPropertyDescriptor } from 'bind-property-descriptor'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const { defineProperties, getOwnPropertyDescriptor } = Object
|
||||||
|
|
||||||
|
const isIgnoredProperty = name => name[0] === '_' || name === 'constructor'
|
||||||
|
|
||||||
|
const IGNORED_STATIC_PROPERTIES = {
|
||||||
|
__proto__: null,
|
||||||
|
|
||||||
|
arguments: true,
|
||||||
|
caller: true,
|
||||||
|
length: true,
|
||||||
|
name: true,
|
||||||
|
prototype: true,
|
||||||
|
}
|
||||||
|
const isIgnoredStaticProperty = name => name in IGNORED_STATIC_PROPERTIES
|
||||||
|
|
||||||
|
const ownKeys =
|
||||||
|
(typeof Reflect !== 'undefined' && Reflect.ownKeys) ||
|
||||||
|
(({ getOwnPropertyNames: names, getOwnPropertySymbols: symbols }) =>
|
||||||
|
symbols !== undefined ? obj => names(obj).concat(symbols(obj)) : names)(Object)
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
const mixin = Mixins => Class => {
|
||||||
|
if (__DEV__ && !Array.isArray(Mixins)) {
|
||||||
|
throw new TypeError('Mixins should be an array')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name } = Class
|
||||||
|
|
||||||
|
// Copy properties of plain object mix-ins to the prototype.
|
||||||
|
{
|
||||||
|
const allMixins = Mixins
|
||||||
|
Mixins = []
|
||||||
|
const { prototype } = Class
|
||||||
|
const descriptors = { __proto__: null }
|
||||||
|
allMixins.forEach(Mixin => {
|
||||||
|
if (typeof Mixin === 'function') {
|
||||||
|
Mixins.push(Mixin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop of ownKeys(Mixin)) {
|
||||||
|
if (__DEV__ && prop in prototype) {
|
||||||
|
throw new Error(`${name}#${prop} is already defined`)
|
||||||
|
}
|
||||||
|
|
||||||
|
;(descriptors[prop] = getOwnPropertyDescriptor(
|
||||||
|
Mixin,
|
||||||
|
prop
|
||||||
|
)).enumerable = false // Object methods are enumerable but class methods are not.
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defineProperties(prototype, descriptors)
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = Mixins.length
|
||||||
|
|
||||||
|
function DecoratedClass (...args) {
|
||||||
|
const instance = new Class(...args)
|
||||||
|
|
||||||
|
for (let i = 0; i < n; ++i) {
|
||||||
|
const Mixin = Mixins[i]
|
||||||
|
const { prototype } = Mixin
|
||||||
|
const mixinInstance = new Mixin(instance, ...args)
|
||||||
|
const descriptors = { __proto__: null }
|
||||||
|
const props = ownKeys(prototype)
|
||||||
|
for (let j = 0, m = props.length; j < m; ++j) {
|
||||||
|
const prop = props[j]
|
||||||
|
|
||||||
|
if (isIgnoredProperty(prop)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop in instance) {
|
||||||
|
throw new Error(`${name}#${prop} is already defined`)
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptors[prop] = getBoundPropertyDescriptor(
|
||||||
|
prototype,
|
||||||
|
prop,
|
||||||
|
mixinInstance
|
||||||
|
)
|
||||||
|
}
|
||||||
|
defineProperties(instance, descriptors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy original and mixed-in static properties on Decorator class.
|
||||||
|
const descriptors = { __proto__: null }
|
||||||
|
ownKeys(Class).forEach(prop => {
|
||||||
|
let descriptor
|
||||||
|
if (!(
|
||||||
|
// Special properties are not defined...
|
||||||
|
isIgnoredStaticProperty(prop) &&
|
||||||
|
|
||||||
|
// if they already exist...
|
||||||
|
(descriptor = getOwnPropertyDescriptor(DecoratedClass, prop)) !== undefined &&
|
||||||
|
|
||||||
|
// and are not configurable.
|
||||||
|
!descriptor.configurable
|
||||||
|
)) {
|
||||||
|
descriptors[prop] = getOwnPropertyDescriptor(Class, prop)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Mixins.forEach(Mixin => {
|
||||||
|
ownKeys(Mixin).forEach(prop => {
|
||||||
|
if (isIgnoredStaticProperty(prop)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__DEV__ && prop in descriptors) {
|
||||||
|
throw new Error(`${name}.${prop} is already defined`)
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptors[prop] = getOwnPropertyDescriptor(Mixin, prop)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
defineProperties(DecoratedClass, descriptors)
|
||||||
|
|
||||||
|
return DecoratedClass
|
||||||
|
}
|
||||||
|
export { mixin as default }
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/register": "^7.0.0-beta.44",
|
"@babel/register": "^7.0.0-beta.46",
|
||||||
"babel-7-jest": "^21.3.2",
|
"babel-7-jest": "^21.3.2",
|
||||||
"babel-eslint": "^8.1.2",
|
"babel-eslint": "^8.1.2",
|
||||||
"benchmark": "^2.1.4",
|
"benchmark": "^2.1.4",
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
"transform": {
|
"transform": {
|
||||||
"/@xen-orchestra/cron/.+\\.jsx?$": "babel-7-jest",
|
"/@xen-orchestra/cron/.+\\.jsx?$": "babel-7-jest",
|
||||||
"/@xen-orchestra/fs/.+\\.jsx?$": "babel-7-jest",
|
"/@xen-orchestra/fs/.+\\.jsx?$": "babel-7-jest",
|
||||||
|
"/@xen-orchestra/log/.+\\.jsx?$": "babel-7-jest",
|
||||||
"/packages/complex-matcher/.+\\.jsx?$": "babel-7-jest",
|
"/packages/complex-matcher/.+\\.jsx?$": "babel-7-jest",
|
||||||
"/packages/value-matcher/.+\\.jsx?$": "babel-7-jest",
|
"/packages/value-matcher/.+\\.jsx?$": "babel-7-jest",
|
||||||
"/packages/vhd-lib/.+\\.jsx?$": "babel-7-jest",
|
"/packages/vhd-lib/.+\\.jsx?$": "babel-7-jest",
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
"lodash": "^4.17.4"
|
"lodash": "^4.17.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.0.0-beta.44",
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
"@babel/core": "7.0.0-beta.44",
|
"@babel/core": "7.0.0-beta.46",
|
||||||
"@babel/preset-env": "7.0.0-beta.44",
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
"babel-plugin-lodash": "^3.3.2",
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
"cross-env": "^5.1.1",
|
"cross-env": "^5.1.1",
|
||||||
"rimraf": "^2.6.2"
|
"rimraf": "^2.6.2"
|
||||||
|
|||||||
@@ -28,10 +28,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.0.0-beta.44",
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
"@babel/core": "7.0.0-beta.44",
|
"@babel/core": "7.0.0-beta.46",
|
||||||
"@babel/preset-env": "7.0.0-beta.44",
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
"@babel/preset-flow": "7.0.0-beta.44",
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
"cross-env": "^5.1.3",
|
"cross-env": "^5.1.3",
|
||||||
"rimraf": "^2.6.2"
|
"rimraf": "^2.6.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/polyfill": "7.0.0-beta.44",
|
"@babel/polyfill": "7.0.0-beta.46",
|
||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"chalk": "^2.2.0",
|
"chalk": "^2.2.0",
|
||||||
"event-to-promise": "^0.8.0",
|
"event-to-promise": "^0.8.0",
|
||||||
@@ -49,10 +49,10 @@
|
|||||||
"xo-lib": "^0.9.0"
|
"xo-lib": "^0.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.0.0-beta.44",
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
"@babel/core": "7.0.0-beta.44",
|
"@babel/core": "7.0.0-beta.46",
|
||||||
"@babel/preset-env": "7.0.0-beta.44",
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
"@babel/preset-flow": "7.0.0-beta.44",
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
"babel-plugin-lodash": "^3.3.2",
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
"cross-env": "^5.1.3",
|
"cross-env": "^5.1.3",
|
||||||
"rimraf": "^2.6.2"
|
"rimraf": "^2.6.2"
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
"lodash": "^4.17.4"
|
"lodash": "^4.17.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.0.0-beta.44",
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
"@babel/core": "7.0.0-beta.44",
|
"@babel/core": "7.0.0-beta.46",
|
||||||
"@babel/preset-env": "7.0.0-beta.44",
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
"@babel/preset-flow": "^7.0.0-beta.44",
|
"@babel/preset-flow": "^7.0.0-beta.46",
|
||||||
"babel-plugin-lodash": "^3.3.2",
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
"cross-env": "^5.1.3",
|
"cross-env": "^5.1.3",
|
||||||
"rimraf": "^2.6.2"
|
"rimraf": "^2.6.2"
|
||||||
|
|||||||
3
packages/xo-server-rework/.babelrc.js
Normal file
3
packages/xo-server-rework/.babelrc.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
||||||
24
packages/xo-server-rework/.npmignore
Normal file
24
packages/xo-server-rework/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
||||||
51
packages/xo-server-rework/README.md
Normal file
51
packages/xo-server-rework/README.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# xo-server [](https://travis-ci.org/vatesfr/xo-server)
|
||||||
|
|
||||||
|
> Server part of [Xen Orchestra](https://xen-orchestra.com)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/xo-server):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --global xo-server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
> xo-server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](https://github.com/vatesfr/xo-web/issues)
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
AGPL3 © [Vates SAS](http://vates.fr)
|
||||||
40
packages/xo-server-rework/config.json
Normal file
40
packages/xo-server-rework/config.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Vendor config: DO NOT TOUCH!
|
||||||
|
//
|
||||||
|
// See sample.config.yaml to override.
|
||||||
|
{
|
||||||
|
|
||||||
|
// Should users be created on first sign in?
|
||||||
|
//
|
||||||
|
// Necessary for external authentication providers.
|
||||||
|
"createUserOnFirstSignin": true,
|
||||||
|
|
||||||
|
"datadir": "/var/lib/xo-server/data",
|
||||||
|
|
||||||
|
"http": {
|
||||||
|
"listen": [
|
||||||
|
{
|
||||||
|
"port": 80
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"mounts": {},
|
||||||
|
|
||||||
|
// Ciphers to use.
|
||||||
|
//
|
||||||
|
// These are the default ciphers in Node 4.2.6, we are setting
|
||||||
|
// them explicitly for older Node versions.
|
||||||
|
"ciphers": "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA",
|
||||||
|
|
||||||
|
// Tell Node to respect the cipher order.
|
||||||
|
"honorCipherOrder": true,
|
||||||
|
|
||||||
|
// Specify to use at least TLSv1.1.
|
||||||
|
// See: https://github.com/certsimple/minimum-tls-version
|
||||||
|
"secureOptions": 117440512
|
||||||
|
},
|
||||||
|
|
||||||
|
"jwt": {
|
||||||
|
"expiresIn": "7d",
|
||||||
|
"secret": "P],7x#cRhuy,wCR'$}'N?<2yOQ3v6.!b*|1B2P36(wKsYICH|6"
|
||||||
|
}
|
||||||
|
}
|
||||||
1047
packages/xo-server-rework/flow-typed/npm/lodash_v4.x.x.js
vendored
Normal file
1047
packages/xo-server-rework/flow-typed/npm/lodash_v4.x.x.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
81
packages/xo-server-rework/package.json
Normal file
81
packages/xo-server-rework/package.json
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "xo-server-rework",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "AGPL-3.0",
|
||||||
|
"description": "Server part of Xen Orchestra",
|
||||||
|
"keywords": [
|
||||||
|
"orchestra",
|
||||||
|
"server",
|
||||||
|
"xen",
|
||||||
|
"xen-orchestra"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-rework",
|
||||||
|
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@isonoe.net"
|
||||||
|
},
|
||||||
|
"preferGlobal": true,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {
|
||||||
|
"xo-server": "dist/index.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/polyfill": "7.0.0-beta.46",
|
||||||
|
"app-conf": "^0.5.0",
|
||||||
|
"base64url": "^2.0.0",
|
||||||
|
"bind-property-descriptor": "^1.0.0",
|
||||||
|
"bluebird": "^3.5.1",
|
||||||
|
"cuid": "^2.0.2",
|
||||||
|
"dataloader": "^1.3.0",
|
||||||
|
"event-to-promise": "^0.8.0",
|
||||||
|
"golike-defer": "^0.4.1",
|
||||||
|
"graphql": "^0.13.0",
|
||||||
|
"http-request-plus": "^0.5.0",
|
||||||
|
"http-server-plus": "^0.9.0",
|
||||||
|
"immutable": "^4.0.0-rc.4",
|
||||||
|
"index-modules": "^0.3.0",
|
||||||
|
"jsonwebtoken": "^8.1.0",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"mnemonist": "^0.21.0",
|
||||||
|
"promise-toolbox": "^0.9.5",
|
||||||
|
"proxy-agent": "^2.1.0",
|
||||||
|
"spdy": "^3.4.7",
|
||||||
|
"uuid": "^3.1.0",
|
||||||
|
"zen-observable": "^0.8.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
|
"@babel/core": "7.0.0-beta.46",
|
||||||
|
"@babel/plugin-proposal-decorators": "7.0.0-beta.46",
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "7.0.0-beta.46",
|
||||||
|
"@babel/plugin-proposal-pipeline-operator": "7.0.0-beta.46",
|
||||||
|
"@babel/plugin-proposal-throw-expressions": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
|
"babel-plugin-dev": "^1.0.0",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean && index-modules --auto src/",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build",
|
||||||
|
"start": "./dist/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
packages/xo-server-rework/src/app/index.js
Normal file
8
packages/xo-server-rework/src/app/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import EventEmitter from 'events'
|
||||||
|
import mixin from '@xen-orchestra/mixin'
|
||||||
|
import { values } from 'lodash'
|
||||||
|
|
||||||
|
import Mixins from './mixins'
|
||||||
|
|
||||||
|
@mixin(values(Mixins))
|
||||||
|
export default class App extends EventEmitter {}
|
||||||
51
packages/xo-server-rework/src/app/mixins/hooks.js
Normal file
51
packages/xo-server-rework/src/app/mixins/hooks.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import createLogger from '@xen-orchestra/log'
|
||||||
|
import emitAsync from '@xen-orchestra/emit-async'
|
||||||
|
|
||||||
|
const { debug, warn } = createLogger('hooks')
|
||||||
|
|
||||||
|
const makeSingletonHook = (hook, postEvent) => {
|
||||||
|
let promise
|
||||||
|
return function () {
|
||||||
|
if (promise === undefined) {
|
||||||
|
promise = runHook(this, hook)
|
||||||
|
promise.then(() => {
|
||||||
|
this.removeAllListeners(hook)
|
||||||
|
this.emit(postEvent)
|
||||||
|
this.removeAllListeners(postEvent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const runHook = (app, hook) => {
|
||||||
|
debug(`${hook} start…`)
|
||||||
|
const promise = emitAsync.call(app, {
|
||||||
|
onError: error => warn(`${hook} failure`, error),
|
||||||
|
}, hook)
|
||||||
|
promise.then(() => {
|
||||||
|
debug(`${hook} finished`)
|
||||||
|
})
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Run *clean* async listeners.
|
||||||
|
//
|
||||||
|
// They normalize existing data, clear invalid entries, etc.
|
||||||
|
clean () {
|
||||||
|
return runHook(this, 'clean')
|
||||||
|
},
|
||||||
|
|
||||||
|
// Run *start* async listeners.
|
||||||
|
//
|
||||||
|
// They initialize the application.
|
||||||
|
start: makeSingletonHook('start', 'started'),
|
||||||
|
|
||||||
|
// Run *stop* async listeners.
|
||||||
|
//
|
||||||
|
// They close connections, unmount file systems, save states, etc.
|
||||||
|
stop: makeSingletonHook('stop', 'stopped'),
|
||||||
|
}
|
||||||
13
packages/xo-server-rework/src/app/mixins/http-request.js
Normal file
13
packages/xo-server-rework/src/app/mixins/http-request.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import defined, { ifDef } from '@xen-orchestra/defined'
|
||||||
|
import hrp from 'http-request-plus'
|
||||||
|
import ProxyAgent from 'proxy-agent'
|
||||||
|
|
||||||
|
export default class HttpRequest {
|
||||||
|
constructor (_, {
|
||||||
|
httpProxy = defined(process.env.http_proxy, process.env.HTTP_PROXY),
|
||||||
|
}) {
|
||||||
|
this.httpRequest = hrp.extend({
|
||||||
|
agent: ifDef(httpProxy, _ => new ProxyAgent(_)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
132
packages/xo-server-rework/src/app/mixins/http-server.js
Normal file
132
packages/xo-server-rework/src/app/mixins/http-server.js
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import type EventEmitter from 'events'
|
||||||
|
import type {
|
||||||
|
IncomingMessage,
|
||||||
|
Server,
|
||||||
|
ServerResponse,
|
||||||
|
} from 'http'
|
||||||
|
|
||||||
|
import createLogger from '@xen-orchestra/log'
|
||||||
|
import generateToken from 'generate-token'
|
||||||
|
import { fromCallback } from 'promise-toolbox'
|
||||||
|
import { once } from 'lodash'
|
||||||
|
|
||||||
|
const HTTP_REQUEST_VALIDITY = 1e3 * 60 * 60
|
||||||
|
const { warn } = createLogger('web-server')
|
||||||
|
|
||||||
|
type Handler = (IncomingMessage, ServerResponse, any) => void
|
||||||
|
type HandlerInfo = {|
|
||||||
|
data: any,
|
||||||
|
handler: Handler,
|
||||||
|
method: ?string,
|
||||||
|
once: boolean,
|
||||||
|
path: string,
|
||||||
|
unregister: () => void
|
||||||
|
|}
|
||||||
|
|
||||||
|
type HandlerInfoMap = { [url: string]: HandlerInfo }
|
||||||
|
|
||||||
|
export default class httpServer {
|
||||||
|
_handlers: HandlerInfoMap
|
||||||
|
|
||||||
|
constructor (app: EventEmitter, { httpServer }: { httpServer: Server }) {
|
||||||
|
const openConnections = new Set()
|
||||||
|
httpServer.on('connection', connection => {
|
||||||
|
openConnections.add(connection)
|
||||||
|
connection.once('close', () => {
|
||||||
|
openConnections.delete(connection)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
app.on('stop', () => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
openConnections.forEach(connection => {
|
||||||
|
connection.end()
|
||||||
|
})
|
||||||
|
}, 5e3).unref()
|
||||||
|
|
||||||
|
return fromCallback(cb => httpServer.close(cb)).then(() => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const handlers = this._handlers = Object.create(null)
|
||||||
|
httpServer.on('request', (req, res) => {
|
||||||
|
const handler = handlers[req.url]
|
||||||
|
if (
|
||||||
|
handler !== undefined &&
|
||||||
|
(handler.method !== undefined || handler.method === req.method)
|
||||||
|
) {
|
||||||
|
if (handler.once) {
|
||||||
|
handler.unregister()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
handler.handler.call(app, req, res, handler.data)
|
||||||
|
} catch (error) {
|
||||||
|
warn('handler error', { error })
|
||||||
|
if (!res.headersSent) {
|
||||||
|
res.writeHead(500)
|
||||||
|
}
|
||||||
|
res.end()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(404)
|
||||||
|
res.end(`Page not found: ${req.url}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
registerHttpHandler (
|
||||||
|
handler: Handler,
|
||||||
|
{ data, method, once: once_ = false, path, ttl }
|
||||||
|
) {
|
||||||
|
const handlers = this._handlers
|
||||||
|
|
||||||
|
if (path in handlers) {
|
||||||
|
throw new Error(`there is already an HTTP handler for ${path}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unregister = once(() => {
|
||||||
|
delete handlers[path]
|
||||||
|
})
|
||||||
|
|
||||||
|
handlers[path] = {
|
||||||
|
data,
|
||||||
|
handler,
|
||||||
|
method: method && method.toUpperCase(),
|
||||||
|
once: once_,
|
||||||
|
path,
|
||||||
|
unregister,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ttl !== undefined) {
|
||||||
|
setTimeout(unregister, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return unregister
|
||||||
|
}
|
||||||
|
|
||||||
|
registerHttpRequest (
|
||||||
|
handler: Handler,
|
||||||
|
{ data, path, method = 'GET', suffix }
|
||||||
|
) {
|
||||||
|
return generateToken().then(token => {
|
||||||
|
let path = `/${token}`
|
||||||
|
if (suffix) {
|
||||||
|
path += `/${encodeURI(token)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
this.registerHttpHandler(handler, {
|
||||||
|
data,
|
||||||
|
method,
|
||||||
|
once: true,
|
||||||
|
path,
|
||||||
|
ttl: HTTP_REQUEST_VALIDITY,
|
||||||
|
})
|
||||||
|
|
||||||
|
return path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
28
packages/xo-server-rework/src/app/mixins/jwt.js
Normal file
28
packages/xo-server-rework/src/app/mixins/jwt.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import jwt from 'jsonwebtoken'
|
||||||
|
import { fromCallback } from 'promise-toolbox'
|
||||||
|
|
||||||
|
export default class JsonWebToken {
|
||||||
|
_encodeOpts: Object
|
||||||
|
_secret: string
|
||||||
|
|
||||||
|
constructor (_: any, {
|
||||||
|
config: { jwt: { expiresIn, secret } },
|
||||||
|
}: {
|
||||||
|
config: { jwt: { expiresIn?: string, secret: string } }
|
||||||
|
}) {
|
||||||
|
this._encodeOpts = { expiresIn }
|
||||||
|
this._secret = secret
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeJwt (token: string): Promise<any> {
|
||||||
|
return fromCallback(cb => jwt.verify(token, this._secret, cb))
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeJwt (payload: any): Promise<string> {
|
||||||
|
return fromCallback(cb =>
|
||||||
|
jwt.sign(payload, this._secret, this._encodeOpts, cb)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
110
packages/xo-server-rework/src/app/mixins/plugins.js
Normal file
110
packages/xo-server-rework/src/app/mixins/plugins.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import asyncMap from '@xen-orchestra/async-map'
|
||||||
|
import createLogger from '@xen-orchestra/log'
|
||||||
|
import { forEach, startsWith } from 'lodash'
|
||||||
|
import { join } from 'path'
|
||||||
|
import { readdir } from '@xen-orchestra/async-fs'
|
||||||
|
|
||||||
|
const { info, warn } = createLogger('plugins')
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const LOOKUP_PATHS = [
|
||||||
|
'/usr/local/lib/node_modules',
|
||||||
|
join(__dirname, '../../../node_modules'),
|
||||||
|
]
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
export default class Plugins {
|
||||||
|
_plugins: Object
|
||||||
|
_prefix: string
|
||||||
|
|
||||||
|
constructor (app: any, { appName, safeMode }: { appName: string, safeMode: ?boolean }) {
|
||||||
|
this._plugins = {}
|
||||||
|
this._prefix = `${appName}-`
|
||||||
|
|
||||||
|
app.on('start', () => {
|
||||||
|
if (!safeMode) {
|
||||||
|
return this.discoverPlugins()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
discoverPlugins () {
|
||||||
|
return asyncMap(this._listPlugins(), async (plugin, name) => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
_listPlugins () {
|
||||||
|
const plugins = { __proto__: null }
|
||||||
|
|
||||||
|
const prefix = this._prefix
|
||||||
|
const prefixLength = prefix.length
|
||||||
|
|
||||||
|
return asyncMap(LOOKUP_PATHS, lookupPath =>
|
||||||
|
readdir(lookupPath).then(
|
||||||
|
basenames =>
|
||||||
|
forEach(basenames, basename => {
|
||||||
|
if (startsWith(basename, prefix)) {
|
||||||
|
const name = basename.slice(prefixLength)
|
||||||
|
const path = join(lookupPath, basename)
|
||||||
|
|
||||||
|
const previous = plugins[name]
|
||||||
|
if (name in plugins) {
|
||||||
|
warn(`duplicate plugins ${name}`, {
|
||||||
|
name,
|
||||||
|
paths: [previous.path, path].sort(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let plugin
|
||||||
|
try {
|
||||||
|
plugin = require(path)
|
||||||
|
info(`successfully imported plugin ${name}`, {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
warn(`failed to import plugin ${name}`, {
|
||||||
|
error,
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let version
|
||||||
|
try {
|
||||||
|
;({ version } = require(join(path, 'package.json')))
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
// Supports both “normal” CommonJS and Babel's ES2015 modules.
|
||||||
|
const {
|
||||||
|
default: factory = plugin,
|
||||||
|
configurationSchema,
|
||||||
|
configurationPresets,
|
||||||
|
} = plugin
|
||||||
|
|
||||||
|
plugins[name] = {
|
||||||
|
configurationPresets,
|
||||||
|
configurationSchema,
|
||||||
|
factory,
|
||||||
|
version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
error => {
|
||||||
|
if (error.code !== 'ENOENT') {
|
||||||
|
warn('plugins', 'failed to read directory', {
|
||||||
|
error,
|
||||||
|
lookupPath,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
58
packages/xo-server-rework/src/app/mixins/store.js
Normal file
58
packages/xo-server-rework/src/app/mixins/store.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// import generateId from 'cuid'
|
||||||
|
// import mkdirp from 'simple-mkdirp'
|
||||||
|
// import { pCatch, pTap } from 'promise-toolbox'
|
||||||
|
// import { join } from 'path'
|
||||||
|
// import { readFile, writeFile } from '@xen-orchestra/async-fs'
|
||||||
|
|
||||||
|
// TODO: transition from in-memory database to a real database system.
|
||||||
|
export default class Store {
|
||||||
|
// constructor (app, { config: { datadir } }) {
|
||||||
|
// this._get = () => {
|
||||||
|
// const datafile = join(datadir, 'store.json')
|
||||||
|
//
|
||||||
|
// const promise = readFile(datafile)
|
||||||
|
// .then(JSON.parse)
|
||||||
|
// .catch(pCatch({ code: 'ENOENT' }, () => ({})))
|
||||||
|
// .then(pTap(data => {
|
||||||
|
// app.on('stop', data =>
|
||||||
|
// mkdirp(datadir)
|
||||||
|
// .then(() => writeFile(datafile, JSON.stringify(data)))
|
||||||
|
// )
|
||||||
|
// }))
|
||||||
|
//
|
||||||
|
// // Inline future accesses.
|
||||||
|
// this._get = () => promise
|
||||||
|
//
|
||||||
|
// return promise
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// this._types = {}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// registerType (name, spec) {
|
||||||
|
// const types = this._types
|
||||||
|
//
|
||||||
|
// if (__DEV__ && name in types) {
|
||||||
|
// throw new Error(`type ${name} is already registered`)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// types[name] = spec
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// async createObject ({ type, ...props }) {
|
||||||
|
// if (__DEV__ && !type) {
|
||||||
|
// throw new Error('missing type')
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const db = await this._get()
|
||||||
|
// const byType = db.byType || (db.byType = {})
|
||||||
|
// const collection = byType[type] || (byType[type] = {})
|
||||||
|
//
|
||||||
|
// let { id } = props
|
||||||
|
// if (!id) {
|
||||||
|
// props.id = id = generateId()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// collection[id] = props
|
||||||
|
// }
|
||||||
|
}
|
||||||
13
packages/xo-server-rework/src/app/mixins/users.js
Normal file
13
packages/xo-server-rework/src/app/mixins/users.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export default class Users {
|
||||||
|
// constructor (app) {
|
||||||
|
// app.on('start', async () => {
|
||||||
|
//
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// app.on('export')
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// createUser ({ name }) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
}
|
||||||
88
packages/xo-server-rework/src/app/mixins/workers.js
Normal file
88
packages/xo-server-rework/src/app/mixins/workers.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import { cpus as getCpus } from 'os'
|
||||||
|
import { ChildProcess, fork } from 'child_process'
|
||||||
|
|
||||||
|
const MAX = getCpus().length
|
||||||
|
const WORKER = `${__dirname}/../../worker.js`
|
||||||
|
|
||||||
|
class Task {
|
||||||
|
data: any
|
||||||
|
reject: (error: any) => void
|
||||||
|
resolve: (result: any) => void
|
||||||
|
|
||||||
|
constructor (data, resolve, reject) {
|
||||||
|
this.data = data
|
||||||
|
this.reject = reject
|
||||||
|
this.resolve = resolve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Workers {
|
||||||
|
_idleWorker: ?ChildProcess
|
||||||
|
_nWorkers: number
|
||||||
|
_tasksQueue: Array<Task>
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
this._idleWorker = undefined
|
||||||
|
this._nWorkers = 0
|
||||||
|
this._tasksQueue = []
|
||||||
|
}
|
||||||
|
|
||||||
|
callWorker (data: any): any {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const task = new Task(data, resolve, reject)
|
||||||
|
|
||||||
|
const worker = this._getWorker()
|
||||||
|
if (worker !== undefined) {
|
||||||
|
this._submitTask(worker, task)
|
||||||
|
} else {
|
||||||
|
this._tasksQueue.push(task)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_getWorker () {
|
||||||
|
let worker = this._idleWorker
|
||||||
|
if (worker !== undefined) {
|
||||||
|
this._idleWorker = undefined
|
||||||
|
return worker
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._nWorkers < MAX) {
|
||||||
|
this._nWorkers++
|
||||||
|
|
||||||
|
worker = fork(WORKER)
|
||||||
|
worker.on('error', error => {
|
||||||
|
console.error('worker error', error)
|
||||||
|
})
|
||||||
|
worker.on('exit', (code, signal) => {
|
||||||
|
console.log('worker exit', code, signal)
|
||||||
|
this._nWorkers--
|
||||||
|
})
|
||||||
|
|
||||||
|
return worker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_submitTask (worker: ChildProcess, task: Task) {
|
||||||
|
worker.once('message', response => {
|
||||||
|
if ('error' in response) {
|
||||||
|
task.reject(response.error)
|
||||||
|
} else {
|
||||||
|
task.resolve(response.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextTask = this._tasksQueue.shift()
|
||||||
|
if (nextTask !== undefined) {
|
||||||
|
this._submitTask(worker, nextTask)
|
||||||
|
} else if (this._idleWorker !== undefined) {
|
||||||
|
worker.kill()
|
||||||
|
} else {
|
||||||
|
this._idleWorker = worker
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
worker.send(task.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
115
packages/xo-server-rework/src/index.js
Executable file
115
packages/xo-server-rework/src/index.js
Executable file
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const APP_NAME = 'xo-server'
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
const { info, warn } = require('@xen-orchestra/log').default('bootstrap')
|
||||||
|
|
||||||
|
process.on('unhandledRejection', reason => {
|
||||||
|
warn('possibly unhandled rejection', reason)
|
||||||
|
})
|
||||||
|
;(({ prototype }) => {
|
||||||
|
const { emit } = prototype
|
||||||
|
prototype.emit = function (event, error) {
|
||||||
|
event === 'error' && !this.listenerCount(event)
|
||||||
|
? warn('unhandled error event', error)
|
||||||
|
: emit.apply(this, arguments)
|
||||||
|
}
|
||||||
|
})(require('events').EventEmitter)
|
||||||
|
|
||||||
|
const Bluebird = require('bluebird')
|
||||||
|
Bluebird.longStackTraces()
|
||||||
|
global.Promise = Bluebird
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
const main = async args => {
|
||||||
|
info('starting')
|
||||||
|
|
||||||
|
const config = await require('app-conf').load(APP_NAME)
|
||||||
|
|
||||||
|
const httpServer = new (require('http-server-plus'))()
|
||||||
|
|
||||||
|
const readFile = Bluebird.promisify(require('fs').readFile)
|
||||||
|
await require('@xen-orchestra/async-map').default(
|
||||||
|
config.http.listen,
|
||||||
|
async ({
|
||||||
|
certificate,
|
||||||
|
// The properties was called `certificate` before.
|
||||||
|
cert = certificate,
|
||||||
|
key,
|
||||||
|
...opts
|
||||||
|
}) => {
|
||||||
|
if (cert !== undefined && key !== undefined) {
|
||||||
|
[opts.cert, opts.key] = await Promise.all([
|
||||||
|
readFile(cert),
|
||||||
|
readFile(key),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const niceAddress = await httpServer.listen(opts)
|
||||||
|
info(`Web server listening on ${niceAddress}`)
|
||||||
|
} catch (error) {
|
||||||
|
if (error.niceAddress !== undefined) {
|
||||||
|
warn(`Web server could not listen on ${error.niceAddress}`)
|
||||||
|
|
||||||
|
const { code } = error
|
||||||
|
if (code === 'EACCES') {
|
||||||
|
warn(' Access denied.')
|
||||||
|
warn(' Ports < 1024 are often reserved to privileges users.')
|
||||||
|
} else if (code === 'EADDRINUSE') {
|
||||||
|
warn(' Address already in use.')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn('Web server could not listen', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { group, user } = config
|
||||||
|
group != null && process.setgid(group)
|
||||||
|
user != null && process.setuid(user)
|
||||||
|
} catch (error) {
|
||||||
|
warn('failed to change group/user', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Observable = require('zen-observable')
|
||||||
|
|
||||||
|
const App = require('./app').default
|
||||||
|
const app = new App({
|
||||||
|
appName: APP_NAME,
|
||||||
|
config,
|
||||||
|
httpServer,
|
||||||
|
safeMode: require('lodash/includes')(args, '--safe-mode'),
|
||||||
|
})
|
||||||
|
await app.start()
|
||||||
|
|
||||||
|
// Gracefully shutdown on signals.
|
||||||
|
//
|
||||||
|
// TODO: implements a timeout? (or maybe it is the services launcher
|
||||||
|
// responsibility?)
|
||||||
|
require('lodash/forEach')(['SIGINT', 'SIGTERM'], signal => {
|
||||||
|
let alreadyCalled = false
|
||||||
|
|
||||||
|
process.on(signal, () => {
|
||||||
|
if (alreadyCalled) {
|
||||||
|
warn('forced exit')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
alreadyCalled = true
|
||||||
|
|
||||||
|
info(`${signal} caught, closing…`)
|
||||||
|
app.stop()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return require('event-to-promise')(app, 'stopped')
|
||||||
|
}
|
||||||
|
main(process.argv.slice(2)).then(
|
||||||
|
() => info('bye :-)'),
|
||||||
|
error => warn('fatal error', error)
|
||||||
|
)
|
||||||
10
packages/xo-server-rework/src/node_modules/generate-token.js
generated
vendored
Normal file
10
packages/xo-server-rework/src/node_modules/generate-token.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import base64url from 'base64url'
|
||||||
|
import { fromCallback } from 'promise-toolbox'
|
||||||
|
import { randomBytes } from 'crypto'
|
||||||
|
|
||||||
|
const generateSecureToken = (bytes: number = 32): Promise<string> =>
|
||||||
|
fromCallback(cb => randomBytes(bytes, cb)).then(base64url)
|
||||||
|
|
||||||
|
export { generateSecureToken as default }
|
||||||
103
packages/xo-server-rework/src/node_modules/mp-atch.js
generated
vendored
Normal file
103
packages/xo-server-rework/src/node_modules/mp-atch.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
every,
|
||||||
|
forEach,
|
||||||
|
isArray,
|
||||||
|
isPlainObject,
|
||||||
|
some,
|
||||||
|
} from 'lodash'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const { hasOwnProperty } = Object.prototype
|
||||||
|
const getSingleKey = obj => {
|
||||||
|
let prop
|
||||||
|
for (const key in obj) {
|
||||||
|
if (hasOwnProperty.call(obj, key)) {
|
||||||
|
if (prop !== undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prop = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
const OPERATORS = {
|
||||||
|
__not: (pattern, value) => !match(pattern, value),
|
||||||
|
__or: (pattern, value) => some(pattern, subpattern => match(subpattern, value)),
|
||||||
|
}
|
||||||
|
|
||||||
|
export const match = (pattern, value) => {
|
||||||
|
if (isPlainObject(pattern)) {
|
||||||
|
const key = getSingleKey(pattern)
|
||||||
|
let operator
|
||||||
|
if (key !== undefined && (operator = OPERATORS[key]) !== undefined) {
|
||||||
|
return operator(pattern[key], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPlainObject(value) && every(pattern, (subpattern, key) => (
|
||||||
|
value[key] !== undefined && match(subpattern, value[key])
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArray(pattern)) {
|
||||||
|
return isArray(value) && every(pattern, subpattern =>
|
||||||
|
some(value, subvalue => match(subpattern, subvalue))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pattern === value
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
const createPredicate = pattern => pattern == null
|
||||||
|
? () => false
|
||||||
|
: value => match(pattern, value)
|
||||||
|
|
||||||
|
export const patch = (value, patchData) => {
|
||||||
|
if (isPlainObject(patchData)) {
|
||||||
|
if (isArray(value)) {
|
||||||
|
const toRemove = createPredicate(patchData['-'])
|
||||||
|
const tmp = []
|
||||||
|
forEach(value, (v, i) => {
|
||||||
|
if (i in patchData) {
|
||||||
|
const p = patchData[i]
|
||||||
|
if (p === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmp.push(patch(v, p))
|
||||||
|
} else if (!toRemove(v)) {
|
||||||
|
tmp.push(v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const toAdd = patchData['+']
|
||||||
|
if (toAdd) {
|
||||||
|
tmp.push.apply(tmp, toAdd)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPlainObject(value)) {
|
||||||
|
value = { ...value }
|
||||||
|
forEach(patchData, (v, k) => {
|
||||||
|
if (v === null) {
|
||||||
|
delete value[k]
|
||||||
|
} else {
|
||||||
|
value[k] = patch(value[k], v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
value = {}
|
||||||
|
forEach(patchData, (v, k) => {
|
||||||
|
if (v !== null) {
|
||||||
|
value[k] = patch(null, v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return patchData
|
||||||
|
}
|
||||||
103
packages/xo-server-rework/src/node_modules/mp-atch.spec.js
generated
vendored
Normal file
103
packages/xo-server-rework/src/node_modules/mp-atch.spec.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
import { forEach } from 'lodash'
|
||||||
|
|
||||||
|
import { match, patch } from './mp-atch'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
describe('match()', () => {
|
||||||
|
const data = {
|
||||||
|
'matches object properties': {
|
||||||
|
pattern: { foo: 'bar' },
|
||||||
|
nope: [
|
||||||
|
null,
|
||||||
|
{ },
|
||||||
|
{ foo: 'baz' },
|
||||||
|
],
|
||||||
|
yep: [
|
||||||
|
{ foo: 'bar' },
|
||||||
|
{ foo: 'bar', bar: 'baz' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'matches set items': {
|
||||||
|
pattern: [ 'foo', 'bar' ],
|
||||||
|
nope: [
|
||||||
|
[],
|
||||||
|
[ 'foo' ],
|
||||||
|
[ 'bar' ],
|
||||||
|
[ 'foo', 'baz' ],
|
||||||
|
],
|
||||||
|
yep: [
|
||||||
|
[ 'bar', 'foo' ],
|
||||||
|
[ 'bar', 'baz', 'foo' ],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'supports a __or operator': {
|
||||||
|
pattern: { __or: [
|
||||||
|
'foo',
|
||||||
|
{ },
|
||||||
|
] },
|
||||||
|
nope: [
|
||||||
|
'bar',
|
||||||
|
[],
|
||||||
|
],
|
||||||
|
yep: [
|
||||||
|
'foo',
|
||||||
|
{ 'foo': 'bar' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(data, ({ pattern, nope, yep }, desc) => {
|
||||||
|
if (!pattern) {
|
||||||
|
it(desc)
|
||||||
|
} else {
|
||||||
|
it(desc, () => {
|
||||||
|
forEach(nope, value => {
|
||||||
|
expect(match(pattern, value)).toBe(false)
|
||||||
|
})
|
||||||
|
forEach(yep, value => {
|
||||||
|
expect(match(pattern, value)).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('supports a __not operator', () => {
|
||||||
|
forEach(data, ({ pattern, nope, yep }) => {
|
||||||
|
if (!pattern) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern = { __not: pattern }
|
||||||
|
|
||||||
|
forEach(nope, value => {
|
||||||
|
expect(match(pattern, value)).toBe(true)
|
||||||
|
})
|
||||||
|
forEach(yep, value => {
|
||||||
|
expect(match(pattern, value)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('patch', () => {
|
||||||
|
it('can patch arrays', () => {
|
||||||
|
expect(patch(
|
||||||
|
[ 'foo', 'bar', 'quuz' ],
|
||||||
|
{ 0: null, '-': 'quuz', '+': [ 'baz' ] }
|
||||||
|
)).toEqual(
|
||||||
|
[ 'bar', 'baz' ]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can patch objects', () => {
|
||||||
|
expect(patch(
|
||||||
|
{ foo: 1, bar: 2 },
|
||||||
|
{ foo: null, bar: 3, baz: 4 }
|
||||||
|
)).toEqual(
|
||||||
|
{ bar: 3, baz: 4 }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
26
packages/xo-server-rework/src/node_modules/simple-mkdirp.js
generated
vendored
Normal file
26
packages/xo-server-rework/src/node_modules/simple-mkdirp.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import { dirname } from 'path'
|
||||||
|
import { mkdir, stat } from 'fs'
|
||||||
|
import { promisify } from 'promise-toolbox'
|
||||||
|
|
||||||
|
const simpleMkdirp = (
|
||||||
|
path: string,
|
||||||
|
cb: (error: ?Error) => void
|
||||||
|
) => mkdir(path, undefined, error => {
|
||||||
|
if (error == null) {
|
||||||
|
return cb()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
return simpleMkdirp(dirname(path), error =>
|
||||||
|
error != null ? cb(error) : simpleMkdirp(path, cb)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stat(path, (_, stats) =>
|
||||||
|
stats != null && stats.isDirectory() ? cb() : cb(error)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default promisify(simpleMkdirp)
|
||||||
71
packages/xo-server-rework/src/task.js
Normal file
71
packages/xo-server-rework/src/task.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import toDecorator from './to-decorator'
|
||||||
|
import { EventEmitter } from 'events'
|
||||||
|
import { CancelToken } from 'promise-toolbox'
|
||||||
|
|
||||||
|
class Task extends EventEmitter {
|
||||||
|
constructor (name, { cancelToken, parent, steps } = {}) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this._cancelToken = cancelToken || (parent && parent.cancelToken)
|
||||||
|
this._name = name
|
||||||
|
this._step = 0
|
||||||
|
this._steps = steps || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get cancelToken () {
|
||||||
|
return this._cancelToken
|
||||||
|
}
|
||||||
|
|
||||||
|
plan (n) {
|
||||||
|
this._steps += n
|
||||||
|
}
|
||||||
|
|
||||||
|
step (stepName) {
|
||||||
|
this.emit('progress', {
|
||||||
|
step: ++this._step,
|
||||||
|
stepName,
|
||||||
|
steps: this._steps,
|
||||||
|
})
|
||||||
|
|
||||||
|
const token = this._cancelToken
|
||||||
|
token && token.throwIfRequested()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default opts =>
|
||||||
|
toDecorator(
|
||||||
|
fn =>
|
||||||
|
function () {
|
||||||
|
const { name = fn.name, steps } = opts || {}
|
||||||
|
|
||||||
|
const n = arguments.length
|
||||||
|
const i = 0
|
||||||
|
let task
|
||||||
|
if (n !== 0) {
|
||||||
|
const arg = arguments[0]
|
||||||
|
if (arg instanceof Task) {
|
||||||
|
task = new Task(name, { parent: arg, steps })
|
||||||
|
} else if (CancelToken.isCancelToken(arg)) {
|
||||||
|
task = new Task(name, { cancelToken: arg, steps })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (task === undefined) {
|
||||||
|
task = new Task(name, { steps })
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = new Array(i + n)
|
||||||
|
args[0] = task
|
||||||
|
for (let j = 1; j < n; ++j) {
|
||||||
|
args[j] = arguments[j + i]
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = new Promise(resolve => resolve(fn.apply(this, args)))
|
||||||
|
promise.onProgress = cb => {
|
||||||
|
task.on('progress', cb)
|
||||||
|
|
||||||
|
return () => task.removeListener('progress', cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
)
|
||||||
54
packages/xo-server-rework/src/task.spec.js
Normal file
54
packages/xo-server-rework/src/task.spec.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
import { CancelToken } from 'promise-toolbox'
|
||||||
|
|
||||||
|
import task from './task'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const rejectionOf = promise =>
|
||||||
|
promise.then(result => {
|
||||||
|
throw result
|
||||||
|
}, reason => reason)
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const sleep = () => new Promise(resolve => setTimeout(resolve, 0))
|
||||||
|
|
||||||
|
describe('@task', () => {
|
||||||
|
const fn = task()(async $task => {
|
||||||
|
await sleep()
|
||||||
|
$task.step('foo')
|
||||||
|
await sleep()
|
||||||
|
$task.step('bar')
|
||||||
|
await sleep()
|
||||||
|
$task.step('baz')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('', () => {
|
||||||
|
const promise = fn()
|
||||||
|
promise.onProgress((...args) => console.log(args))
|
||||||
|
|
||||||
|
return promise
|
||||||
|
})
|
||||||
|
|
||||||
|
it('supports cancel tokens', async () => {
|
||||||
|
const { cancel, token } = CancelToken.source()
|
||||||
|
const promise = fn(token)
|
||||||
|
|
||||||
|
cancel('foo')
|
||||||
|
expect((await rejectionOf(promise)).message).toBe('foo')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('supports subtasks', async () => {
|
||||||
|
const fn2 = task()(async $task => {
|
||||||
|
await sleep()
|
||||||
|
await fn($task)
|
||||||
|
await sleep()
|
||||||
|
await fn($task)
|
||||||
|
})
|
||||||
|
|
||||||
|
const promise = fn2()
|
||||||
|
await promise
|
||||||
|
})
|
||||||
|
})
|
||||||
23
packages/xo-server-rework/src/to-decorator.js
Normal file
23
packages/xo-server-rework/src/to-decorator.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
type Descriptor = {
|
||||||
|
value: any
|
||||||
|
}
|
||||||
|
|
||||||
|
const toDecorator = (
|
||||||
|
wrapFunction: Function,
|
||||||
|
wrapMethod: Function = wrapFunction
|
||||||
|
) => (
|
||||||
|
target: Function | any,
|
||||||
|
key?: string,
|
||||||
|
descriptor?: Descriptor
|
||||||
|
) =>
|
||||||
|
descriptor === undefined
|
||||||
|
? wrapFunction(target)
|
||||||
|
: {
|
||||||
|
...descriptor,
|
||||||
|
value: (typeof target === 'function' ? wrapFunction : wrapMethod)(
|
||||||
|
descriptor.value
|
||||||
|
),
|
||||||
|
}
|
||||||
|
export { toDecorator as default }
|
||||||
23
packages/xo-server-rework/src/worker.js
Normal file
23
packages/xo-server-rework/src/worker.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const METHODS = {
|
||||||
|
add: ([ a, b ]) => a + b,
|
||||||
|
sleep: duration => new Promise(resolve => setTimeout(resolve, duration)),
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on('message', ({ method, arg }) => {
|
||||||
|
const fn = METHODS[method]
|
||||||
|
if (fn === undefined) {
|
||||||
|
return process.send(new Error('no such method'))
|
||||||
|
}
|
||||||
|
|
||||||
|
new Promise(resolve => resolve(fn(arg)))
|
||||||
|
.then(
|
||||||
|
result => process.send({ result }),
|
||||||
|
error => {
|
||||||
|
console.error(error)
|
||||||
|
process.send({ error })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch(error => {
|
||||||
|
console.error('worker error', error)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/polyfill": "7.0.0-beta.44",
|
"@babel/polyfill": "7.0.0-beta.46",
|
||||||
"@marsaud/smb2-promise": "^0.2.1",
|
"@marsaud/smb2-promise": "^0.2.1",
|
||||||
"@xen-orchestra/cron": "^1.0.3",
|
"@xen-orchestra/cron": "^1.0.3",
|
||||||
"@xen-orchestra/fs": "^0.0.0",
|
"@xen-orchestra/fs": "^0.0.0",
|
||||||
@@ -123,17 +123,17 @@
|
|||||||
"yazl": "^2.4.3"
|
"yazl": "^2.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.0.0-beta.44",
|
"@babel/cli": "7.0.0-beta.46",
|
||||||
"@babel/core": "7.0.0-beta.44",
|
"@babel/core": "7.0.0-beta.46",
|
||||||
"@babel/plugin-proposal-decorators": "7.0.0-beta.44",
|
"@babel/plugin-proposal-decorators": "7.0.0-beta.46",
|
||||||
"@babel/plugin-proposal-export-default-from": "7.0.0-beta.44",
|
"@babel/plugin-proposal-export-default-from": "7.0.0-beta.46",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0-beta.44",
|
"@babel/plugin-proposal-export-namespace-from": "7.0.0-beta.46",
|
||||||
"@babel/plugin-proposal-function-bind": "7.0.0-beta.44",
|
"@babel/plugin-proposal-function-bind": "7.0.0-beta.46",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.0.0-beta.44",
|
"@babel/plugin-proposal-optional-chaining": "^7.0.0-beta.46",
|
||||||
"@babel/plugin-proposal-pipeline-operator": "^7.0.0-beta.44",
|
"@babel/plugin-proposal-pipeline-operator": "^7.0.0-beta.46",
|
||||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0-beta.44",
|
"@babel/plugin-proposal-throw-expressions": "^7.0.0-beta.46",
|
||||||
"@babel/preset-env": "7.0.0-beta.44",
|
"@babel/preset-env": "7.0.0-beta.46",
|
||||||
"@babel/preset-flow": "7.0.0-beta.44",
|
"@babel/preset-flow": "7.0.0-beta.46",
|
||||||
"babel-plugin-lodash": "^3.3.2",
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
"cross-env": "^5.1.3",
|
"cross-env": "^5.1.3",
|
||||||
"index-modules": "^0.3.0",
|
"index-modules": "^0.3.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user