chore(xo-server): convert to ESM

This commit is contained in:
Julien Fontanet 2021-05-18 11:58:35 +02:00
parent da0cd0b99c
commit 254558e9de
134 changed files with 371 additions and 261 deletions

6
.gitignore vendored
View File

@ -19,9 +19,9 @@
/packages/xen-api/plot.dat /packages/xen-api/plot.dat
/packages/xo-server/.xo-server.* /packages/xo-server/.xo-server.*
/packages/xo-server/src/api/index.js /packages/xo-server/src/api/index.mjs
/packages/xo-server/src/xapi/mixins/index.js /packages/xo-server/src/xapi/mixins/index.mjs
/packages/xo-server/src/xo-mixins/index.js /packages/xo-server/src/xo-mixins/index.mjs
/packages/xo-server-auth-ldap/ldap.cache.conf /packages/xo-server-auth-ldap/ldap.cache.conf

View File

@ -0,0 +1,5 @@
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'), {
'@babel/preset-env': {
modules: false,
},
})

View File

@ -1 +0,0 @@
module.exports = require('../../@xen-orchestra/babel-config')(require('./package.json'))

View File

@ -1,6 +1,12 @@
#!/usr/bin/env node #!/usr/bin/env node
'use strict' import * as SourceMapSupport from 'source-map-support'
import Bluebird from 'bluebird'
import execPromise from 'exec-promise'
import { catchGlobalErrors } from '@xen-orchestra/log/configure.js'
import { createLogger } from '@xen-orchestra/log'
import boostrap from '../index.mjs'
// =================================================================== // ===================================================================
@ -11,22 +17,20 @@ if (process.env.NODE_ENV === undefined) {
// Better stack traces if possible. // Better stack traces if possible.
try { try {
require('source-map-support').install({ SourceMapSupport.install({
handleUncaughtExceptions: false, handleUncaughtExceptions: false,
}) })
} catch (_) {} } catch (_) {}
// Use Bluebird for all promises as it provides better performance and
// less memory usage.
const Bluebird = require('bluebird')
// Enable `async_hooks` because it's used by `@xen-orchestra/backups/Task` via `node-zone` // Enable `async_hooks` because it's used by `@xen-orchestra/backups/Task` via `node-zone`
// //
// See: http://bluebirdjs.com/docs/api/promise.config.html#async-hooks // See: http://bluebirdjs.com/docs/api/promise.config.html#async-hooks
Bluebird.config({ asyncHooks: true }) Bluebird.config({ asyncHooks: true })
// Use Bluebird for all promises as it provides better performance and
// less memory usage.
global.Promise = Bluebird global.Promise = Bluebird
require('@xen-orchestra/log/configure').catchGlobalErrors(require('@xen-orchestra/log').default('xo:xo-server')) catchGlobalErrors(createLogger('xo:xo-server'))
require('exec-promise')(require('../')) execPromise(boostrap)

View File

@ -8,4 +8,4 @@ if (process.env.DEBUG === undefined) {
} }
// Import the real main module. // Import the real main module.
module.exports = require('./dist').default export { default } from './dist/index.mjs'

View File

@ -22,7 +22,7 @@
"bin": "bin" "bin": "bin"
}, },
"engines": { "engines": {
"node": ">=11" "node": ">=14.13"
}, },
"dependencies": { "dependencies": {
"@iarna/toml": "^2.2.1", "@iarna/toml": "^2.2.1",
@ -58,7 +58,7 @@
"cookie": "^0.4.0", "cookie": "^0.4.0",
"cookie-parser": "^1.4.3", "cookie-parser": "^1.4.3",
"d3-time-format": "^3.0.0", "d3-time-format": "^3.0.0",
"decorator-synchronized": "^0.5.0", "decorator-synchronized": "^0.6.0",
"deptree": "^1.0.0", "deptree": "^1.0.0",
"exec-promise": "^0.7.0", "exec-promise": "^0.7.0",
"execa": "^5.0.0", "execa": "^5.0.0",
@ -142,17 +142,16 @@
"@babel/plugin-proposal-pipeline-operator": "^7.0.0", "@babel/plugin-proposal-pipeline-operator": "^7.0.0",
"@babel/plugin-proposal-throw-expressions": "^7.0.0", "@babel/plugin-proposal-throw-expressions": "^7.0.0",
"@babel/preset-env": "^7.0.0", "@babel/preset-env": "^7.0.0",
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-transform-dev": "^2.0.1", "babel-plugin-transform-dev": "^2.0.1",
"cross-env": "^7.0.2", "cross-env": "^7.0.2",
"index-modules": "^0.3.0", "index-modules": "^0.4.2",
"rimraf": "^3.0.0" "rimraf": "^3.0.0"
}, },
"scripts": { "scripts": {
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/", "build": "cross-env NODE_ENV=production babel --keep-file-extension --source-maps --out-dir=dist/ src/",
"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 --keep-file-extension --source-maps --out-dir=dist/ src/",
"prebuild": "index-modules src/api src/xapi/mixins src/xo-mixins && yarn run clean", "prebuild": "index-modules --index-file index.mjs src/api src/xapi/mixins src/xo-mixins && yarn run clean",
"predev": "yarn run prebuild", "predev": "yarn run prebuild",
"prepublishOnly": "yarn run build", "prepublishOnly": "yarn run build",
"start": "node bin/xo-server" "start": "node bin/xo-server"

View File

@ -12,7 +12,7 @@ function* values(object) {
* *
* @param {(Array|Object)} collection * @param {(Array|Object)} collection
*/ */
module.exports = asyncIteratorToStream(function* (collection) { export default asyncIteratorToStream(function* (collection) {
for (const value of Array.isArray(collection) ? collection : values(collection)) { for (const value of Array.isArray(collection) ? collection : values(collection)) {
yield JSON.stringify(value) yield JSON.stringify(value)
yield '\n' yield '\n'

View File

@ -1,6 +1,6 @@
/* eslint-env jest */ /* eslint-env jest */
import ensureArray from './_ensureArray.js' import ensureArray from './_ensureArray.mjs'
describe('ensureArray()', function () { describe('ensureArray()', function () {
it('wrap the value in an array', function () { it('wrap the value in an array', function () {

View File

@ -1,6 +1,6 @@
import { MultiKeyMap } from '@vates/multi-key-map' import { MultiKeyMap } from '@vates/multi-key-map'
import ensureArray from './_ensureArray.js' import ensureArray from './_ensureArray.mjs'
function removeCacheEntry(cache, keys) { function removeCacheEntry(cache, keys) {
cache.delete(keys) cache.delete(keys)

View File

@ -1,6 +1,6 @@
/* eslint-env jest */ /* eslint-env jest */
import { debounceWithKey, REMOVE_CACHE_ENTRY } from './_pDebounceWithKey.js' import { debounceWithKey, REMOVE_CACHE_ENTRY } from './_pDebounceWithKey.mjs'
describe('REMOVE_CACHE_ENTRY', () => { describe('REMOVE_CACHE_ENTRY', () => {
it('clears the cache', async () => { it('clears the cache', async () => {

View File

@ -2,9 +2,9 @@ import { basename } from 'path'
import { fromCallback } from 'promise-toolbox' import { fromCallback } from 'promise-toolbox'
import { pipeline } from 'readable-stream' import { pipeline } from 'readable-stream'
import createNdJsonStream from '../_createNdJsonStream.js' import createNdJsonStream from '../_createNdJsonStream.mjs'
import { REMOVE_CACHE_ENTRY } from '../_pDebounceWithKey.js' import { REMOVE_CACHE_ENTRY } from '../_pDebounceWithKey.mjs'
import { safeDateFormat } from '../utils.js' import { safeDateFormat } from '../utils.mjs'
export function createJob({ schedules, ...job }) { export function createJob({ schedules, ...job }) {
job.userId = this.user.id job.userId = this.user.id

View File

@ -8,7 +8,7 @@ import { noSuchObject } from 'xo-common/api-errors.js'
import { peekFooterFromVhdStream } from 'vhd-lib' import { peekFooterFromVhdStream } from 'vhd-lib'
import { vmdkToVhd } from 'xo-vmdk-to-vhd' import { vmdkToVhd } from 'xo-vmdk-to-vhd'
import { VDI_FORMAT_VHD } from '../xapi/index.js' import { VDI_FORMAT_VHD } from '../xapi/index.mjs'
const log = createLogger('xo:disk') const log = createLogger('xo:disk')

View File

@ -1,4 +1,4 @@
import xapiObjectToXo from '../xapi-object-to-xo.js' import xapiObjectToXo from '../xapi-object-to-xo.mjs'
export function getBondModes() { export function getBondModes() {
return ['balance-slb', 'active-backup', 'lacp'] return ['balance-slb', 'active-backup', 'lacp']

View File

@ -1,8 +1,9 @@
// TODO: too low level, move into host. // TODO: too low level, move into host.
import { filter, find } from 'lodash' import filter from 'lodash/filter.js'
import find from 'lodash/find.js'
import { IPV4_CONFIG_MODES, IPV6_CONFIG_MODES } from '../xapi/index.js' import { IPV4_CONFIG_MODES, IPV6_CONFIG_MODES } from '../xapi/index.mjs'
export function getIpv4ConfigurationModes() { export function getIpv4ConfigurationModes() {
return IPV4_CONFIG_MODES return IPV4_CONFIG_MODES

View File

@ -1,6 +1,6 @@
import { deprecate } from 'util' import { deprecate } from 'util'
import { getUserPublicProperties } from '../utils.js' import { getUserPublicProperties } from '../utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,9 +1,9 @@
import asyncMapSettled from '@xen-orchestra/async-map/legacy.js' import asyncMapSettled from '@xen-orchestra/async-map/legacy.js'
import { some } from 'lodash' import some from 'lodash/some.js'
import ensureArray from '../_ensureArray.js' import ensureArray from '../_ensureArray.mjs'
import { asInteger } from '../xapi/utils.js' import { asInteger } from '../xapi/utils.mjs'
import { forEach, parseXml } from '../utils.js' import { forEach, parseXml } from '../utils.mjs'
// =================================================================== // ===================================================================

View File

@ -3,7 +3,6 @@ import getKeys from 'lodash/keys.js'
import moment from 'moment-timezone' import moment from 'moment-timezone'
import { noSuchObject } from 'xo-common/api-errors.js' import { noSuchObject } from 'xo-common/api-errors.js'
import { version as xoServerVersion } from '../../package.json'
// =================================================================== // ===================================================================
@ -30,7 +29,9 @@ getServerTimezone.description = 'return the timezone server'
// ------------------------------------------------------------------- // -------------------------------------------------------------------
export const getServerVersion = () => xoServerVersion export function getServerVersion() {
return this.version
}
getServerVersion.description = 'return the version of xo-server' getServerVersion.description = 'return the version of xo-server'
getServerVersion.permission = null // user does not need to be authenticated getServerVersion.permission = null // user does not need to be authenticated

View File

@ -1,6 +1,6 @@
import isEmpty from 'lodash/isEmpty.js'
import { forbiddenOperation, invalidParameters } from 'xo-common/api-errors.js' import { forbiddenOperation, invalidParameters } from 'xo-common/api-errors.js'
import { isEmpty } from 'lodash' import { getUserPublicProperties } from '../utils.mjs'
import { getUserPublicProperties } from '../utils.js'
// =================================================================== // ===================================================================

View File

@ -1,10 +1,10 @@
// FIXME: rename to disk.* // FIXME: rename to disk.*
import reduce from 'lodash/reduce.js'
import { defer } from 'golike-defer' import { defer } from 'golike-defer'
import { invalidParameters } from 'xo-common/api-errors.js' import { invalidParameters } from 'xo-common/api-errors.js'
import { reduce } from 'lodash'
import { parseSize } from '../utils.js' import { parseSize } from '../utils.mjs'
// ==================================================================== // ====================================================================

View File

@ -1,6 +1,6 @@
import { ignoreErrors } from 'promise-toolbox' import { ignoreErrors } from 'promise-toolbox'
import { diffItems } from '../utils.js' import { diffItems } from '../utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,12 +1,13 @@
import * as multiparty from 'multiparty' import * as multiparty from 'multiparty'
import assignWith from 'lodash/assignWith.js'
import asyncMapSettled from '@xen-orchestra/async-map/legacy.js' import asyncMapSettled from '@xen-orchestra/async-map/legacy.js'
import concat from 'lodash/concat.js'
import getStream from 'get-stream' import getStream from 'get-stream'
import { createLogger } from '@xen-orchestra/log' import { createLogger } from '@xen-orchestra/log'
import { defer } from 'golike-defer' import { defer } from 'golike-defer'
import { FAIL_ON_QUEUE } from 'limit-concurrency-decorator' import { FAIL_ON_QUEUE } from 'limit-concurrency-decorator'
import { format } from 'json-rpc-peer' import { format } from 'json-rpc-peer'
import { ignoreErrors } from 'promise-toolbox' import { ignoreErrors } from 'promise-toolbox'
import { assignWith, concat } from 'lodash'
import { import {
forbiddenOperation, forbiddenOperation,
invalidParameters, invalidParameters,
@ -15,7 +16,7 @@ import {
unauthorized, unauthorized,
} from 'xo-common/api-errors.js' } from 'xo-common/api-errors.js'
import { forEach, map, mapFilter, parseSize, safeDateFormat } from '../utils.js' import { forEach, map, mapFilter, parseSize, safeDateFormat } from '../utils.mjs'
const log = createLogger('xo:vm') const log = createLogger('xo:vm')

View File

@ -2,7 +2,7 @@ import getStream from 'get-stream'
import { fromCallback } from 'promise-toolbox' import { fromCallback } from 'promise-toolbox'
import { pipeline } from 'readable-stream' import { pipeline } from 'readable-stream'
import createNdJsonStream from '../_createNdJsonStream.js' import createNdJsonStream from '../_createNdJsonStream.mjs'
// =================================================================== // ===================================================================

View File

@ -1,17 +1,21 @@
import assert from 'assert' import assert from 'assert'
import asyncMapSettled from '@xen-orchestra/async-map/legacy.js' import asyncMapSettled from '@xen-orchestra/async-map/legacy.js'
import execa from 'execa' import execa from 'execa'
import filter from 'lodash/filter.js'
import find from 'lodash/find.js'
import fs from 'fs-extra' import fs from 'fs-extra'
import includes from 'lodash/includes.js'
import map from 'lodash/map.js' import map from 'lodash/map.js'
import range from 'lodash/range.js'
import remove from 'lodash/remove.js'
import { createLogger } from '@xen-orchestra/log' import { createLogger } from '@xen-orchestra/log'
import { defer } from 'golike-defer' import { defer } from 'golike-defer'
import { tap, delay } from 'promise-toolbox' import { tap, delay } from 'promise-toolbox'
import { invalidParameters } from 'xo-common/api-errors.js' import { invalidParameters } from 'xo-common/api-errors.js'
import { includes, remove, filter, find, range } from 'lodash'
import { Ref } from 'xen-api' import { Ref } from 'xen-api'
import ensureArray from '../_ensureArray.js' import ensureArray from '../_ensureArray.mjs'
import { parseXml } from '../utils.js' import { parseXml } from '../utils.mjs'
const log = createLogger('xo:xosan') const log = createLogger('xo:xosan')

View File

@ -1,7 +1,7 @@
import Model from './model.js' import Model from './model.mjs'
import { BaseError } from 'make-error' import { BaseError } from 'make-error'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { isObject, map } from './utils.js' import { isObject, map } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,10 +1,15 @@
import asyncMapSettled from '@xen-orchestra/async-map/legacy.js' import asyncMapSettled from '@xen-orchestra/async-map/legacy.js'
import difference from 'lodash/difference.js'
import filter from 'lodash/filter.js'
import forEach from 'lodash/forEach.js'
import getKeys from 'lodash/keys.js'
import isEmpty from 'lodash/isEmpty.js'
import map from 'lodash/map.js'
import { createClient as createRedisClient } from 'redis' import { createClient as createRedisClient } from 'redis'
import { difference, filter, forEach, isEmpty, keys as getKeys, map } from 'lodash'
import { ignoreErrors, promisifyAll } from 'promise-toolbox' import { ignoreErrors, promisifyAll } from 'promise-toolbox'
import { v4 as generateUuid } from 'uuid' import { v4 as generateUuid } from 'uuid'
import Collection, { ModelAlreadyExists } from '../collection.js' import Collection, { ModelAlreadyExists } from '../collection.mjs'
// =================================================================== // ===================================================================

View File

@ -1,6 +1,6 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { noop } from './utils.js' import { noop } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -4,7 +4,7 @@
// //
// ```js // ```js
// import fatfs from 'fatfs' // import fatfs from 'fatfs'
// import fatfsBuffer, { init as fatfsBufferInit } from './fatfs-buffer.js' // import fatfsBuffer, { init as fatfsBufferInit } from './fatfs-buffer.mjs'
// //
// const buffer = fatfsBufferinit() // const buffer = fatfsBufferinit()
// //

View File

@ -1,8 +1,8 @@
// See: https://gist.github.com/julien-f/5b9a3537eb82a34b04e2 // See: https://gist.github.com/julien-f/5b9a3537eb82a34b04e2
const matcher = require('micromatch').matcher import { matcher } from 'micromatch'
module.exports = function globMatcher(patterns, opts) { export default function globMatcher(patterns, opts) {
if (!Array.isArray(patterns)) { if (!Array.isArray(patterns)) {
if (patterns[0] === '!') { if (patterns[0] === '!') {
const m = matcher(patterns.slice(1), opts) const m = matcher(patterns.slice(1), opts)

View File

@ -2,15 +2,21 @@ import appConf from 'app-conf'
import assert from 'assert' import assert from 'assert'
import authenticator from 'otplib/authenticator.js' import authenticator from 'otplib/authenticator.js'
import blocked from 'blocked-at' import blocked from 'blocked-at'
import Bluebird from 'bluebird'
import compression from 'compression' import compression from 'compression'
import createExpress from 'express' import createExpress from 'express'
import crypto from 'crypto' import crypto from 'crypto'
import forOwn from 'lodash/forOwn.js'
import has from 'lodash/has.js' import has from 'lodash/has.js'
import helmet from 'helmet' import helmet from 'helmet'
import httpProxy from 'http-proxy'
import includes from 'lodash/includes.js' import includes from 'lodash/includes.js'
import map from 'lodash/map.js'
import memoryStoreFactory from 'memorystore' import memoryStoreFactory from 'memorystore'
import merge from 'lodash/merge.js'
import ms from 'ms' import ms from 'ms'
import proxyConsole from './proxy-console.js' import once from 'lodash/once.js'
import proxyConsole from './proxy-console.mjs'
import pw from 'pw' import pw from 'pw'
import serveStatic from 'serve-static' import serveStatic from 'serve-static'
import stoppable from 'stoppable' import stoppable from 'stoppable'
@ -18,23 +24,21 @@ import WebServer from 'http-server-plus'
import WebSocket from 'ws' import WebSocket from 'ws'
import xdg from 'xdg-basedir' import xdg from 'xdg-basedir'
import { createLogger } from '@xen-orchestra/log' import { createLogger } from '@xen-orchestra/log'
import { forOwn, map, merge, once } from 'lodash'
import { genSelfSignedCert } from '@xen-orchestra/self-signed' import { genSelfSignedCert } from '@xen-orchestra/self-signed'
import { parseDuration } from '@vates/parse-duration' import { parseDuration } from '@vates/parse-duration'
import { URL } from 'url' import { URL } from 'url'
import { compile as compilePug } from 'pug' import { compile as compilePug } from 'pug'
import { createServer as createProxyServer } from 'http-proxy'
import { fromCallback, fromEvent } from 'promise-toolbox' import { fromCallback, fromEvent } from 'promise-toolbox'
import { ifDef } from '@xen-orchestra/defined' import { ifDef } from '@xen-orchestra/defined'
import { join as joinPath } from 'path' import { join as joinPath } from 'path'
import JsonRpcPeer from 'json-rpc-peer' import fse from 'fs-extra'
import { invalidCredentials } from 'xo-common/api-errors.js' import { invalidCredentials } from 'xo-common/api-errors.js'
import { ensureDir, outputFile, readdir, readFile } from 'fs-extra' import { Peer as JsonRpcPeer } from 'json-rpc-peer'
import ensureArray from './_ensureArray.js' import ensureArray from './_ensureArray.mjs'
import Xo from './xo.js' import Xo from './xo.mjs'
import bodyParser from 'body-parser' import bodyParser from 'body-parser'
import connectFlash from 'connect-flash' import connectFlash from 'connect-flash'
@ -46,7 +50,15 @@ import { Strategy as LocalStrategy } from 'passport-local'
import transportConsole from '@xen-orchestra/log/transports/console.js' import transportConsole from '@xen-orchestra/log/transports/console.js'
import { configure } from '@xen-orchestra/log/configure.js' import { configure } from '@xen-orchestra/log/configure.js'
import { generateToken } from './utils.js' import { generateToken } from './utils.mjs'
// ===================================================================
const APP_DIR = new URL('..', import.meta.url).pathname
const [APP_NAME, APP_VERSION] = (() => {
const { name, version } = JSON.parse(fse.readFileSync(new URL('../package.json', import.meta.url)))
return [name, version]
})()
// =================================================================== // ===================================================================
@ -67,9 +79,6 @@ const log = createLogger('xo:main')
// =================================================================== // ===================================================================
const APP_DIR = joinPath(__dirname, '..')
const APP_NAME = 'xo-server'
const DEPRECATED_ENTRIES = ['users', 'servers'] const DEPRECATED_ENTRIES = ['users', 'servers']
async function loadConfiguration() { async function loadConfiguration() {
@ -93,9 +102,9 @@ async function loadConfiguration() {
const LOCAL_CONFIG_FILE = `${xdg.config}/${APP_NAME}/config.z-auto.json` const LOCAL_CONFIG_FILE = `${xdg.config}/${APP_NAME}/config.z-auto.json`
async function updateLocalConfig(diff) { async function updateLocalConfig(diff) {
// TODO lock file // TODO lock file
const localConfig = await readFile(LOCAL_CONFIG_FILE).then(JSON.parse, () => ({})) const localConfig = await fse.readFile(LOCAL_CONFIG_FILE).then(JSON.parse, () => ({}))
merge(localConfig, diff) merge(localConfig, diff)
await outputFile(LOCAL_CONFIG_FILE, JSON.stringify(localConfig), { await fse.outputFile(LOCAL_CONFIG_FILE, JSON.stringify(localConfig), {
mode: 0o600, mode: 0o600,
}) })
} }
@ -160,7 +169,7 @@ async function setUpPassport(express, xo, { authentication: authCfg, http: { coo
} }
// Registers the sign in form. // Registers the sign in form.
const signInPage = compilePug(await readFile(joinPath(__dirname, '..', 'signin.pug'))) const signInPage = compilePug(await fse.readFile(new URL('../signin.pug', import.meta.url)))
express.get('/signin', (req, res, next) => { express.get('/signin', (req, res, next) => {
res.send( res.send(
signInPage({ signInPage({
@ -282,14 +291,10 @@ async function setUpPassport(express, xo, { authentication: authCfg, http: { coo
// =================================================================== // ===================================================================
async function registerPlugin(pluginPath, pluginName) { async function registerPlugin(pluginPath, pluginName) {
const plugin = require(pluginPath) const plugin = (await import(pluginPath)).default
const { description, version = 'unknown' } = (() => { const { description, version = 'unknown' } = await fse
try { .readFile(pluginPath + '/package.json')
return require(pluginPath + '/package.json') .then(JSON.stringify, error => ({}))
} catch (_) {
return {}
}
})()
// Supports both “normal” CommonJS and Babel's ES2015 modules. // Supports both “normal” CommonJS and Babel's ES2015 modules.
let { default: factory = plugin, configurationSchema, configurationPresets, testSchema } = plugin let { default: factory = plugin, configurationSchema, configurationPresets, testSchema } = plugin
@ -304,7 +309,7 @@ async function registerPlugin(pluginPath, pluginName) {
xo: this, xo: this,
getDataDir: () => { getDataDir: () => {
const dir = `${datadir}/${pluginName}` const dir = `${datadir}/${pluginName}`
return ensureDir(dir).then(() => dir) return fse.ensureDir(dir).then(() => dir)
}, },
}) })
: factory : factory
@ -343,7 +348,7 @@ function registerPluginWrapper(pluginPath, pluginName) {
} }
async function registerPluginsInPath(path, prefix) { async function registerPluginsInPath(path, prefix) {
const files = await readdir(path).catch(error => { const files = await fse.readdir(path).catch(error => {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
return [] return []
} }
@ -361,7 +366,7 @@ async function registerPluginsInPath(path, prefix) {
async function registerPlugins(xo) { async function registerPlugins(xo) {
await Promise.all( await Promise.all(
[`${__dirname}/../node_modules`, '/usr/local/lib/node_modules'].map(path => [new URL('../node_modules', import.meta.url).pathname, '/usr/local/lib/node_modules'].map(path =>
Promise.all([ Promise.all([
registerPluginsInPath.call(xo, path, 'xo-server-'), registerPluginsInPath.call(xo, path, 'xo-server-'),
registerPluginsInPath.call(xo, `${path}/@xen-orchestra`, 'server-'), registerPluginsInPath.call(xo, `${path}/@xen-orchestra`, 'server-'),
@ -389,7 +394,7 @@ async function makeWebServerListen(
try { try {
if (cert && key) { if (cert && key) {
try { try {
;[opts.cert, opts.key] = await Promise.all([readFile(cert), readFile(key)]) ;[opts.cert, opts.key] = await Promise.all([fse.readFile(cert), fse.readFile(key)])
if (opts.key.includes('ENCRYPTED')) { if (opts.key.includes('ENCRYPTED')) {
opts.passphrase = await new Promise(resolve => { opts.passphrase = await new Promise(resolve => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -405,8 +410,8 @@ async function makeWebServerListen(
const pems = await genSelfSignedCert() const pems = await genSelfSignedCert()
await Promise.all([ await Promise.all([
outputFile(cert, pems.cert, { flag: 'wx', mode: 0o400 }), fse.outputFile(cert, pems.cert, { flag: 'wx', mode: 0o400 }),
outputFile(key, pems.key, { flag: 'wx', mode: 0o400 }), fse.outputFile(key, pems.key, { flag: 'wx', mode: 0o400 }),
]) ])
log.info('new certificate generated', { cert, key }) log.info('new certificate generated', { cert, key })
opts.cert = pems.cert opts.cert = pems.cert
@ -448,25 +453,27 @@ const setUpProxies = (express, opts, xo) => {
return return
} }
const proxy = createProxyServer({ const proxy = httpProxy
changeOrigin: true, .createServer({
ignorePath: true, changeOrigin: true,
xfwd: true, ignorePath: true,
}).on('error', (error, req, res) => { xfwd: true,
// `res` can be either a `ServerResponse` or a `Socket` (which does not have })
// `writeHead`) .on('error', (error, req, res) => {
if (!res.headersSent && typeof res.writeHead === 'function') { // `res` can be either a `ServerResponse` or a `Socket` (which does not have
res.writeHead(500, { 'content-type': 'text/plain' }) // `writeHead`)
res.write('There was a problem proxying this request.') if (!res.headersSent && typeof res.writeHead === 'function') {
} res.writeHead(500, { 'content-type': 'text/plain' })
res.end() res.write('There was a problem proxying this request.')
}
const { method, url } = req res.end()
log.error('failed to proxy request', {
error, const { method, url } = req
req: { method, url }, log.error('failed to proxy request', {
error,
req: { method, url },
})
}) })
})
// TODO: sort proxies by descending prefix length. // TODO: sort proxies by descending prefix length.
@ -673,15 +680,15 @@ const setUpConsoleProxy = (webServer, xo) => {
// =================================================================== // ===================================================================
const USAGE = (({ name, version }) => `Usage: ${name} [--safe-mode] const USAGE = `Usage: ${APP_NAME} [--safe-mode]
${name} v${version}`)(require('../package.json')) ${APP_NAME} v${APP_VERSION}`
// =================================================================== // ===================================================================
export default async function main(args) { export default async function main(args) {
// makes sure the global Promise has not been changed by a lib // makes sure the global Promise has not been changed by a lib
assert(global.Promise === require('bluebird')) assert(global.Promise === Bluebird)
if (includes(args, '--help') || includes(args, '-h')) { if (includes(args, '--help') || includes(args, '-h')) {
return USAGE return USAGE
@ -725,6 +732,7 @@ export default async function main(args) {
const xo = new Xo({ const xo = new Xo({
appDir: APP_DIR, appDir: APP_DIR,
appName: APP_NAME, appName: APP_NAME,
appVersion: APP_VERSION,
config, config,
httpServer: webServer, httpServer: webServer,
safeMode, safeMode,

View File

@ -8,8 +8,8 @@ import sublevel from 'subleveldown'
import util from 'util' import util from 'util'
import { join as joinPath } from 'path' import { join as joinPath } from 'path'
import { forEach } from './utils.js' import { forEach } from './utils.mjs'
import globMatcher from './glob-matcher.js' import globMatcher from './glob-matcher.mjs'
// =================================================================== // ===================================================================
@ -274,11 +274,14 @@ export default async function main() {
} }
const config = await appConf.load('xo-server', { const config = await appConf.load('xo-server', {
appDir: joinPath(__dirname, '..'), appDir: new URL('..', import.meta.url).pathname,
ignoreUnknownFormats: true, ignoreUnknownFormats: true,
}) })
if (args.repair) { if (args.repair) {
// TODO: remove once `import.meta.resolve` is stabilized
const require = (await import('module')).createRequire(import.meta.url)
// eslint-disable-next-line node/no-extraneous-require // eslint-disable-next-line node/no-extraneous-require
const { repair } = require(require.resolve('level', { const { repair } = require(require.resolve('level', {
paths: [require.resolve('level-party')], paths: [require.resolve('level-party')],

View File

@ -1,8 +1,8 @@
/* eslint-env jest */ /* eslint-env jest */
import { forEach } from 'lodash' import forEach from 'lodash/forEach.js'
import { thunkToArray } from './utils.js' import { thunkToArray } from './utils.mjs'
import { crossProduct, mergeObjects } from './math.js' import { crossProduct, mergeObjects } from './math.mjs'
describe('mergeObjects', function () { describe('mergeObjects', function () {
forEach( forEach(

View File

@ -1,6 +1,6 @@
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { forEach, isEmpty } from './utils.js' import { forEach, isEmpty } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,6 +1,6 @@
import Collection from '../collection/redis.js' import Collection from '../collection/redis.mjs'
import Model from '../model.js' import Model from '../model.mjs'
import { forEach, multiKeyHash } from '../utils.js' import { forEach, multiKeyHash } from '../utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,11 +1,11 @@
import isEmpty from 'lodash/isEmpty.js' import isEmpty from 'lodash/isEmpty.js'
import Collection from '../collection/redis.js' import Collection from '../collection/redis.mjs'
import Model from '../model.js' import Model from '../model.mjs'
import { forEach } from '../utils.js' import { forEach } from '../utils.mjs'
import { parseProp } from './utils.js' import { parseProp } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,7 +1,7 @@
import Collection from '../collection/redis.js' import Collection from '../collection/redis.mjs'
import Model from '../model.js' import Model from '../model.mjs'
import { createLogger } from '@xen-orchestra/log' import { createLogger } from '@xen-orchestra/log'
import { forEach } from '../utils.js' import { forEach } from '../utils.mjs'
const log = createLogger('xo:plugin-metadata') const log = createLogger('xo:plugin-metadata')

View File

@ -1,8 +1,8 @@
import Collection from '../collection/redis.js' import Collection from '../collection/redis.mjs'
import Model from '../model.js' import Model from '../model.mjs'
import { forEach } from '../utils.js' import { forEach } from '../utils.mjs'
import { parseProp } from './utils.js' import { parseProp } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,8 +1,8 @@
import Collection from '../collection/redis.js' import Collection from '../collection/redis.mjs'
import Model from '../model.js' import Model from '../model.mjs'
import { forEach, serializeError } from '../utils.js' import { forEach, serializeError } from '../utils.mjs'
import { parseProp } from './utils.js' import { parseProp } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,5 +1,5 @@
import Collection from '../collection/redis.js' import Collection from '../collection/redis.mjs'
import Model from '../model.js' import Model from '../model.mjs'
// =================================================================== // ===================================================================

View File

@ -1,9 +1,9 @@
import isEmpty from 'lodash/isEmpty.js' import isEmpty from 'lodash/isEmpty.js'
import Collection from '../collection/redis.js' import Collection from '../collection/redis.mjs'
import Model from '../model.js' import Model from '../model.mjs'
import { parseProp } from './utils.js' import { parseProp } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -2,8 +2,8 @@ import appConf from 'app-conf'
import pw from 'pw' import pw from 'pw'
import { join as joinPath } from 'path' import { join as joinPath } from 'path'
import Xo from './xo.js' import Xo from './xo.mjs'
import { generateToken } from './utils.js' import { generateToken } from './utils.mjs'
const recoverAccount = async ([name]) => { const recoverAccount = async ([name]) => {
if (name === undefined || name === '--help' || name === '-h') { if (name === undefined || name === '--help' || name === '-h') {
@ -27,7 +27,7 @@ xo-server-recover-account <user name or email>
const xo = new Xo({ const xo = new Xo({
config: await appConf.load('xo-server', { config: await appConf.load('xo-server', {
appDir: joinPath(__dirname, '..'), appDir: new URL('..', import.meta.url).pathname,
ignoreUnknownFormats: true, ignoreUnknownFormats: true,
}), }),
}) })

View File

@ -3,13 +3,13 @@
import { createReadStream, readFile } from 'fs' import { createReadStream, readFile } from 'fs'
import { fromCallback } from 'promise-toolbox' import { fromCallback } from 'promise-toolbox'
import streamToExistingBuffer from './stream-to-existing-buffer.js' import streamToExistingBuffer from './stream-to-existing-buffer.mjs'
describe('streamToExistingBuffer()', () => { describe('streamToExistingBuffer()', () => {
it('read the content of a stream in a buffer', async () => { it('read the content of a stream in a buffer', async () => {
const stream = createReadStream(__filename) const stream = createReadStream(import.meta.url)
const expected = await fromCallback(readFile, __filename, 'utf-8') const expected = await fromCallback(readFile, import.meta.url, 'utf-8')
const buf = Buffer.allocUnsafe(expected.length + 1) const buf = Buffer.allocUnsafe(expected.length + 1)
buf[0] = 'A'.charCodeAt() buf[0] = 'A'.charCodeAt()

View File

@ -1,6 +1,6 @@
/* eslint-env jest */ /* eslint-env jest */
import { camelToSnakeCase, diffItems, extractProperty, generateToken, parseSize, parseXml } from './utils.js' import { camelToSnakeCase, diffItems, extractProperty, generateToken, parseSize, parseXml } from './utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,11 +1,11 @@
import { isDefaultTemplate } from '@xen-orchestra/xapi' import { isDefaultTemplate } from '@xen-orchestra/xapi'
import * as sensitiveValues from './sensitive-values.js' import * as sensitiveValues from './sensitive-values.mjs'
import ensureArray from './_ensureArray.js' import ensureArray from './_ensureArray.mjs'
import { extractIpFromVmNetworks } from './_extractIpFromVmNetworks.js' import { extractIpFromVmNetworks } from './_extractIpFromVmNetworks.mjs'
import { extractProperty, forEach, isEmpty, mapFilter, parseXml } from './utils.js' import { extractProperty, forEach, isEmpty, mapFilter, parseXml } from './utils.mjs'
import { getVmDomainType, isHostRunning, isVmRunning, parseDateTime } from './xapi/index.js' import { getVmDomainType, isHostRunning, isVmRunning, parseDateTime } from './xapi/index.mjs'
import { useUpdateSystem } from './xapi/utils.js' import { useUpdateSystem } from './xapi/utils.mjs'
// =================================================================== // ===================================================================

View File

@ -1,10 +1,19 @@
import defaults from 'lodash/defaults.js'
import findKey from 'lodash/findKey.js'
import forEach from 'lodash/forEach.js'
import identity from 'lodash/identity.js'
import JSON5 from 'json5' import JSON5 from 'json5'
import limitConcurrency from 'limit-concurrency-decorator' import map from 'lodash/map.js'
import synchronized from 'decorator-synchronized' import mapValues from 'lodash/mapValues.js'
import mean from 'lodash/mean.js'
import sum from 'lodash/sum.js'
import uniq from 'lodash/uniq.js'
import zipWith from 'lodash/zipWith.js'
import { BaseError } from 'make-error' import { BaseError } from 'make-error'
import { defaults, findKey, forEach, identity, map, mapValues, mean, sum, uniq, zipWith } from 'lodash' import { limitConcurrency } from 'limit-concurrency-decorator'
import { synchronized } from 'decorator-synchronized'
import { parseDateTime } from './xapi/index.js' import { parseDateTime } from './xapi/index.mjs'
export class FaultyGranularity extends BaseError {} export class FaultyGranularity extends BaseError {}

View File

@ -1,34 +1,45 @@
/* eslint eslint-comments/disable-enable-pair: [error, {allowWholeFile: true}] */ /* eslint eslint-comments/disable-enable-pair: [error, {allowWholeFile: true}] */
/* eslint-disable camelcase */ /* eslint-disable camelcase */
import asyncMapSettled from '@xen-orchestra/async-map/legacy.js' import asyncMapSettled from '@xen-orchestra/async-map/legacy.js'
import concurrency from 'limit-concurrency-decorator'
import fatfs from 'fatfs' import fatfs from 'fatfs'
import filter from 'lodash/filter.js'
import find from 'lodash/find.js'
import flatMap from 'lodash/flatMap.js'
import flatten from 'lodash/flatten.js'
import groupBy from 'lodash/groupBy.js'
import identity from 'lodash/identity.js'
import includes from 'lodash/includes.js'
import isEmpty from 'lodash/isEmpty.js'
import mapToArray from 'lodash/map.js' import mapToArray from 'lodash/map.js'
import mixin from '@xen-orchestra/mixin/legacy.js' import mixin from '@xen-orchestra/mixin/legacy.js'
import ms from 'ms' import ms from 'ms'
import synchronized from 'decorator-synchronized' import noop from 'lodash/noop.js'
import omit from 'lodash/omit.js'
import once from 'lodash/once.js'
import semver from 'semver'
import tarStream from 'tar-stream' import tarStream from 'tar-stream'
import uniq from 'lodash/uniq.js'
import { asyncMap } from '@xen-orchestra/async-map' import { asyncMap } from '@xen-orchestra/async-map'
import { vmdkToVhd } from 'xo-vmdk-to-vhd' import { vmdkToVhd } from 'xo-vmdk-to-vhd'
import { cancelable, fromEvents, ignoreErrors, pCatch, pRetry } from 'promise-toolbox' import { cancelable, fromEvents, ignoreErrors, pCatch, pRetry } from 'promise-toolbox'
import { createLogger } from '@xen-orchestra/log' import { createLogger } from '@xen-orchestra/log'
import { decorateWith } from '@vates/decorate-with' import { decorateWith } from '@vates/decorate-with'
import { defer as deferrable } from 'golike-defer' import { defer as deferrable } from 'golike-defer'
import { limitConcurrency } from 'limit-concurrency-decorator'
import { parseDuration } from '@vates/parse-duration' import { parseDuration } from '@vates/parse-duration'
import { PassThrough } from 'stream' import { PassThrough } from 'stream'
import { forbiddenOperation, operationFailed } from 'xo-common/api-errors.js' import { forbiddenOperation, operationFailed } from 'xo-common/api-errors.js'
import { Xapi as XapiBase } from '@xen-orchestra/xapi' import { Xapi as XapiBase } from '@xen-orchestra/xapi'
import { filter, find, flatMap, flatten, groupBy, identity, includes, isEmpty, noop, omit, once, uniq } from 'lodash'
import { Ref } from 'xen-api' import { Ref } from 'xen-api'
import { satisfies as versionSatisfies } from 'semver' import { synchronized } from 'decorator-synchronized'
import ensureArray from '../_ensureArray.js' import ensureArray from '../_ensureArray.mjs'
import fatfsBuffer, { init as fatfsBufferInit } from '../fatfs-buffer.js' import fatfsBuffer, { init as fatfsBufferInit } from '../fatfs-buffer.mjs'
import { asyncMapValues } from '../_asyncMapValues.js' import { asyncMapValues } from '../_asyncMapValues.mjs'
import { camelToSnakeCase, forEach, map, parseSize, pDelay, promisifyAll } from '../utils.js' import { camelToSnakeCase, forEach, map, parseSize, pDelay, promisifyAll } from '../utils.mjs'
import mixins from './mixins/index.js' import mixins from './mixins/index.mjs'
import OTHER_CONFIG_TEMPLATE from './other-config-template.js' import OTHER_CONFIG_TEMPLATE from './other-config-template.mjs'
import { import {
asBoolean, asBoolean,
asInteger, asInteger,
@ -40,7 +51,7 @@ import {
optional, optional,
parseDateTime, parseDateTime,
prepareXapiParam, prepareXapiParam,
} from './utils.js' } from './utils.mjs'
import { createVhdStreamWithLength } from 'vhd-lib' import { createVhdStreamWithLength } from 'vhd-lib'
const log = createLogger('xo:xapi') const log = createLogger('xo:xapi')
@ -52,9 +63,7 @@ export const TAG_COPY_SRC = 'xo:copy_of'
// =================================================================== // ===================================================================
// FIXME: remove this work around when fixed, https://phabricator.babeljs.io/T2877 export * from './utils.mjs'
// export * from './utils.js'
Object.assign(module.exports, require('./utils.js'))
// VDI formats. (Raw is not available for delta vdi.) // VDI formats. (Raw is not available for delta vdi.)
export const VDI_FORMAT_VHD = 'vhd' export const VDI_FORMAT_VHD = 'vhd'
@ -84,10 +93,10 @@ export default class Xapi extends XapiBase {
// close event is emitted when the export is canceled via browser. See https://github.com/vatesfr/xen-orchestra/issues/5535 // close event is emitted when the export is canceled via browser. See https://github.com/vatesfr/xen-orchestra/issues/5535
const waitStreamEnd = async stream => fromEvents(await stream, ['end', 'close']) const waitStreamEnd = async stream => fromEvents(await stream, ['end', 'close'])
this._exportVdi = concurrency(vdiExportConcurrency, waitStreamEnd)(this._exportVdi) this._exportVdi = limitConcurrency(vdiExportConcurrency, waitStreamEnd)(this._exportVdi)
this.exportVm = concurrency(vmExportConcurrency, waitStreamEnd)(this.exportVm) this.exportVm = limitConcurrency(vmExportConcurrency, waitStreamEnd)(this.exportVm)
this._snapshotVm = concurrency(vmSnapshotConcurrency)(this._snapshotVm) this._snapshotVm = limitConcurrency(vmSnapshotConcurrency)(this._snapshotVm)
// Patch getObject to resolve _xapiId property. // Patch getObject to resolve _xapiId property.
this.getObject = (getObject => (...args) => { this.getObject = (getObject => (...args) => {
@ -665,7 +674,7 @@ export default class Xapi extends XapiBase {
) { ) {
const { version } = delta const { version } = delta
if (!versionSatisfies(version, '^1')) { if (!semver.satisfies(version, '^1')) {
throw new Error(`Unsupported delta backup version: ${version}`) throw new Error(`Unsupported delta backup version: ${version}`)
} }

View File

@ -1,4 +1,4 @@
import { makeEditObject } from '../utils.js' import { makeEditObject } from '../utils.mjs'
export default { export default {
async _connectVif(vif) { async _connectVif(vif) {

View File

@ -1,15 +1,20 @@
import filter from 'lodash/filter.js'
import find from 'lodash/find.js'
import groupBy from 'lodash/groupBy.js'
import mapValues from 'lodash/mapValues.js'
import pickBy from 'lodash/pickBy.js'
import some from 'lodash/some.js'
import unzip from 'unzipper' import unzip from 'unzipper'
import { createLogger } from '@xen-orchestra/log' import { createLogger } from '@xen-orchestra/log'
import { decorateWith } from '@vates/decorate-with' import { decorateWith } from '@vates/decorate-with'
import { defer as deferrable } from 'golike-defer' import { defer as deferrable } from 'golike-defer'
import { filter, find, groupBy, mapValues, pickBy, some } from 'lodash'
import { timeout } from 'promise-toolbox' import { timeout } from 'promise-toolbox'
import ensureArray from '../../_ensureArray.js' import ensureArray from '../../_ensureArray.mjs'
import { debounceWithKey } from '../../_pDebounceWithKey.js' import { debounceWithKey } from '../../_pDebounceWithKey.mjs'
import { forEach, mapFilter, parseXml } from '../../utils.js' import { forEach, mapFilter, parseXml } from '../../utils.mjs'
import { extractOpaqueRef, parseDateTime, useUpdateSystem } from '../utils.js' import { extractOpaqueRef, parseDateTime, useUpdateSystem } from '../utils.mjs'
// TOC ------------------------------------------------------------------------- // TOC -------------------------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More