From 6cde38709ce47b3982dbe66b683d36003e47cd06 Mon Sep 17 00:00:00 2001
From: cgeek <cem.moreau@gmail.com>
Date: Fri, 30 Dec 2016 13:23:03 +0100
Subject: [PATCH] [fix] #738 Duniter-ui is now a Duniter module

---
 .gitmodules                          |   3 -
 app/cli.js                           | 112 ++----
 app/controllers/webmin.controller.js | 524 ---------------------------
 app/lib/constants.js                 |   3 -
 app/lib/dal/sqliteDAL/BlockDAL.js    |   2 -
 app/lib/streams/routes.js            | 131 +------
 app/lib/streams/webmin.js            |  32 --
 app/service/BlockchainService.js     |  20 -
 bin/daemon                           |  25 +-
 duniter.sh                           |   2 +-
 index.js                             |   5 -
 package.json                         |   5 +-
 server.js                            |  71 +++-
 test/integration/tools/node.js       |   9 +-
 web-ui                               |   1 -
 15 files changed, 93 insertions(+), 852 deletions(-)
 delete mode 100644 app/controllers/webmin.controller.js
 delete mode 100644 app/lib/streams/webmin.js
 delete mode 160000 web-ui

diff --git a/.gitmodules b/.gitmodules
index dd15f02a5..e69de29bb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "web-ui"]
-	path = web-ui
-	url = https://github.com/duniter/duniter-ui.git
diff --git a/app/cli.js b/app/cli.js
index 623fdc34c..204e2387a 100644
--- a/app/cli.js
+++ b/app/cli.js
@@ -40,16 +40,7 @@ module.exports = (programArgs) => {
       program.parse(programArgs);
 
       if (programArgs.length <= 2) {
-
-        logger.info('No command given, using default: duniter webwait');
-        return co(function *() {
-          try {
-            yield webWait();
-          } catch (e) {
-            logger.error(e);
-            throw Error("Exiting");
-          }
-        });
+        onReject('No command given.');
       }
 
       const res = yield currentCommand;
@@ -99,10 +90,6 @@ program
   .option('--addep <endpoint>', 'With `config` command, add given endpoint to the list of endpoints of this node')
   .option('--remep <endpoint>', 'With `config` command, remove given endpoint to the list of endpoints of this node')
 
-  // Webmin options
-  .option('--webmhost <host>', 'Local network interface to connect to (IP)')
-  .option('--webmport <port>', 'Local network port to connect', parseInt)
-
   .option('--salt <salt>', 'Key salt to generate this key\'s secret key')
   .option('--passwd <password>', 'Password to generate this key\'s secret key')
   .option('--participate <Y|N>', 'Participate to writing the blockchain')
@@ -149,7 +136,22 @@ program
   .action(subCommand(service((server, conf) => new Promise((resolve, reject) => {
     co(function*() {
         try {
-          yield duniter.statics.startNode(server, conf);
+          const bma = require('./lib/streams/bma');
+
+          logger.info(">> NODE STARTING");
+
+          // Public http interface
+          let bmapi = yield bma(server, null, conf.httplogs);
+
+          // Routing documents
+          server.routing();
+
+          // Services
+          yield server.startServices();
+          yield bmapi.openConnections();
+
+          logger.info('>> Server ready!');
+
         } catch (e) {
           reject(e);
         }
@@ -166,26 +168,6 @@ program
   .description('Restart Duniter node daemon.')
   .action(subCommand(needsToBeLaunchedByScript));
 
-program
-  .command('webwait')
-  .description('Start Duniter web admin.')
-  .action(subCommand(webWait));
-
-program
-  .command('webstart')
-  .description('Start Duniter node daemon and web admin.')
-  .action(subCommand(webStart));
-
-program
-  .command('webstop')
-  .description('Stop Duniter node daemon and web admin.')
-  .action(subCommand(needsToBeLaunchedByScript));
-
-program
-  .command('webrestart')
-  .description('Restart Duniter node daemon and web admin.')
-  .action(subCommand(needsToBeLaunchedByScript));
-
 program
   .command('wizard [step]')
   .description('Launch the configuration wizard.')
@@ -778,60 +760,12 @@ program
     throw Error("Exiting");
   });
 
-function webWait() {
-  return new Promise(() => {
-    co(function *() {
-      let webminapi = yield webInit(onService);
-      yield webminapi.httpLayer.openConnections();
-      yield new Promise(() => null); // Never stop this command, unless Ctrl+C
-    })
-      .catch(mainError);
-  });
-}
-
-function webStart() {
-  return co(function *() {
-    let webminapi = yield webInit(onService);
-    yield webminapi.httpLayer.openConnections();
-    yield webminapi.webminCtrl.startHTTP();
-    yield webminapi.webminCtrl.startAllServices();
-    yield webminapi.webminCtrl.regularUPnP();
-    yield new Promise(() => null); // Never stop this command, unless Ctrl+C
-  })
-    .catch(mainError);
-}
-
-function webInit(onService) {
-  return co(function *() {
-    var dbName = program.mdb;
-    var dbHome = program.home;
-    if (!program.memory) {
-      let params = yield directory.getHomeFS(program.memory, dbHome);
-      yield directory.createHomeIfNotExists(params.fs, params.home);
-
-      // Add log files for this instance
-      logger.addHomeLogs(params.home);
-    }
-    const server = duniter({home: dbHome, name: dbName}, commandLineConf());
-    yield server.plugFileSystem();
-    const cnf = yield server.loadConf();
-    yield configure(server, cnf);
-    onService && onService(server);
-    return yield duniter.statics.enableHttpAdmin({
-      home: dbHome,
-      name: dbName,
-      memory: program.memory
-    }, commandLineConf(), false, program.webmhost, program.webmport);
-  });
-}
-
-function mainError(err) {
-  if (err.stack) {
-    logger.error(err.stack);
-  }
-  logger.error(err.code || err.message || err);
-  throw Error("Exiting");
-}
+module.exports.addCommand = (command, requirements, promiseCallback) => {
+  program
+    .command(command.name)
+    .description(command.desc)
+    .action(subCommand(service(promiseCallback)));
+};
 
 function needsToBeLaunchedByScript() {
     logger.error('This command must not be launched directly, using duniter.sh script');
diff --git a/app/controllers/webmin.controller.js b/app/controllers/webmin.controller.js
deleted file mode 100644
index c647c77b9..000000000
--- a/app/controllers/webmin.controller.js
+++ /dev/null
@@ -1,524 +0,0 @@
-"use strict";
-
-const path = require('path');
-const util = require('util');
-const es = require('event-stream');
-const rp = require('request-promise');
-const stream      = require('stream');
-const _ = require('underscore');
-const Q = require('q');
-const co = require('co');
-const ucoin = require('../../index');
-const ucp = require('../lib/ucp/buid');
-const constants = require('../lib/constants');
-const base58 = require('../lib/crypto/base58');
-const rawer = require('../lib/ucp/rawer');
-const keyring = require('../lib/crypto/keyring');
-const http2raw = require('../lib/helpers/http2raw');
-const bma = require('../lib/streams/bma');
-const Identity = require('../lib/entity/identity');
-const network = require('../lib/system/network');
-const AbstractController = require('../controllers/abstract');
-const contacter = require('../lib/contacter');
-const logger = require('../lib/logger')('webmin');
-
-module.exports = (dbConf, overConf) => {
-  return new WebAdmin(dbConf, overConf);
-};
-
-function WebAdmin (dbConf, overConf) {
-
-  // Node instance: this is the object to be managed by the web admin
-  const server = this.server = ucoin(dbConf, overConf);
-  let bmapi;
-  const that = this;
-
-  server.pipe(es.mapSync(function(data) {
-    if (data.pulling !== undefined || data.pow !== undefined) {
-      that.push(data);
-    }
-  }));
-
-  AbstractController.call(this, server);
-
-  stream.Duplex.call(this, { objectMode: true });
-
-  // Unused, but made mandatory by Duplex interface
-  this._read = () => null;
-  this._write = () => null;
-
-  let startServicesP, stopServicesP;
-
-  let pluggedConfP = plugForConf();
-
-  let pluggedDALP = replugDAL();
-
-  function replugDAL() {
-    return co(function *() {
-      yield pluggedConfP;
-
-      // Routing documents
-      server.routing();
-
-      return plugForDAL();
-    });
-  }
-
-  this.summary = () => co(function *() {
-    yield pluggedDALP;
-    const host = server.conf ? [server.conf.ipv4, server.conf.port].join(':') : '';
-    const current = yield server.dal.getCurrentBlockOrNull();
-    const rootBlock = yield server.dal.getBlock(0);
-    const lastUDBlock = yield server.dal.blockDAL.lastBlockWithDividend();
-    const parameters = yield server.dal.getParameters();
-    return {
-      "version": server.version,
-      "host": host,
-      "current": current,
-      "rootBlock": rootBlock,
-      "pubkey": server.keyPair.publicKey,
-      "seckey": server.keyPair.secretKey,
-      "conf": {
-        "cpu": server.conf.cpu
-      },
-      "parameters": parameters,
-      "lastUDBlock": lastUDBlock
-    };
-  });
-
-  this.powSummary = () => co(function *() {
-    yield pluggedDALP;
-    return {
-      "total": yield server.getCountOfSelfMadePoW(),
-      "mirror": !(yield server.isServerMember()),
-      "waiting": server.isPoWWaiting()
-    };
-  });
-
-  this.previewPubkey = (req) => co(function *() {
-    const conf = http2raw.conf(req);
-    const pair = yield keyring.scryptKeyPair(conf.idty_entropy, conf.idty_password);
-    return {
-      "pubkey": pair.publicKey
-    };
-  });
-
-  this.startHTTP = () => co(function *() {
-    yield pluggedDALP;
-    try {
-      yield bmapi.openConnections();
-      return { success: true };
-    } catch (e) {
-      logger.error(e);
-      return { success: false };
-    }
-  });
-
-  this.openUPnP = () => co(function *() {
-    yield pluggedDALP;
-    return server.upnp();
-  });
-
-  this.regularUPnP = () => co(function *() {
-    yield pluggedDALP;
-    if (server.upnpAPI) {
-      server.upnpAPI.stopRegular();
-    }
-    if (server.conf.upnp) {
-      try {
-        yield server.upnp();
-        server.upnpAPI.startRegular();
-      } catch (e) {
-        logger.error(e);
-      }
-    }
-    return {};
-  });
-
-  this.stopHTTP = () => co(function *() {
-    yield pluggedDALP;
-    return bmapi.closeConnections();
-  });
-
-  this.previewNext = () => co(function *() {
-    yield pluggedDALP;
-    const block = yield server.doMakeNextBlock();
-    block.raw = rawer.getBlock(block);
-    return block;
-  });
-
-  this.sendConf = (req) => co(function *() {
-    yield pluggedConfP;
-    const conf = http2raw.conf(req);
-    const pair = yield keyring.scryptKeyPair(conf.idty_entropy, conf.idty_password);
-    yield server.dal.saveConf({
-      routing: true,
-      createNext: true,
-      cpu: constants.DEFAULT_CPU,
-      ipv4: conf.local_ipv4,
-      ipv6: conf.local_ipv6,
-      port: conf.lport,
-      remoteipv4: conf.remote_ipv4,
-      remoteipv6: conf.remote_ipv6,
-      remoteport: conf.rport,
-      upnp: conf.upnp,
-      salt: conf.idty_entropy,
-      passwd: conf.idty_password,
-      pair: pair.json(),
-      avgGenTime: conf.avgGenTime,
-      blocksRot: conf.blocksRot,
-      c: conf.c,
-      currency: conf.currency,
-      dt: conf.dt,
-      dtDiffEval: conf.dtDiffEval,
-      medianTimeBlocks: conf.medianTimeBlocks,
-      msValidity: conf.msValidity,
-      percentRot: conf.percentRot,
-      sigDelay: conf.sigDelay,
-      sigPeriod: conf.sigPeriod,
-      sigQty: conf.sigQty,
-      sigStock: conf.sigStock,
-      sigValidity: conf.sigValidity,
-      sigWindow: conf.sigWindow,
-      stepMax: conf.stepMax,
-      ud0: conf.ud0,
-      xpercent: conf.xpercent,
-      idtyWindow: conf.idtyWindow,
-      msWindow: conf.msWindow
-    });
-    pluggedConfP = co(function *() {
-      yield bmapi.closeConnections();
-      yield server.loadConf();
-      bmapi = yield bma(server, null, true);
-      return bmapi.openConnections();
-    });
-    yield pluggedConfP;
-    const buid = '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855';
-    const entity = Identity.statics.fromJSON({
-      buid: buid,
-      uid: conf.idty_uid,
-      issuer: pair.publicKey,
-      currency: conf.currency
-    });
-    let found = yield server.dal.getIdentityByHashOrNull(entity.getTargetHash());
-    if (!found) {
-      let createIdentity = rawer.getOfficialIdentity(entity);
-      createIdentity += pair.signSync(createIdentity) + '\n';
-      found = yield that.pushEntity({ body: { identity: createIdentity }}, http2raw.identity, constants.ENTITY_IDENTITY);
-    }
-    yield server.dal.fillInMembershipsOfIdentity(Q(found));
-    if (_.filter(found.memberships, { membership: 'IN'}).length == 0) {
-      const block = ucp.format.buid(null);
-      let join = rawer.getMembershipWithoutSignature({
-        "version": constants.DOCUMENTS_VERSION,
-        "currency": conf.currency,
-        "issuer": pair.publicKey,
-        "block": block,
-        "membership": "IN",
-        "userid": conf.idty_uid,
-        "certts": block
-      });
-      join += pair.signSync(join) + '\n';
-      yield that.pushEntity({ body: { membership: join }}, http2raw.membership, constants.ENTITY_MEMBERSHIP);
-      yield server.recomputeSelfPeer();
-    }
-    //
-    return found;
-  });
-
-  this.publishANewSelfPeer = (req) => co(function *() {
-    yield pluggedConfP;
-    yield server.recomputeSelfPeer();
-    return {};
-  });
-
-  this.applyNetworkConf = (req) => co(function *() {
-    yield pluggedConfP;
-    const conf = http2raw.conf(req);
-    yield server.dal.saveConf(_.extend(server.conf, {
-      ipv4: conf.local_ipv4,
-      ipv6: conf.local_ipv6,
-      port: conf.lport,
-      remoteipv4: conf.remote_ipv4,
-      remoteipv6: conf.remote_ipv6,
-      remoteport: conf.rport,
-      remotehost: conf.dns,
-      upnp: conf.upnp
-    }));
-    pluggedConfP = co(function *() {
-      yield bmapi.closeConnections();
-      yield server.loadConf();
-      bmapi = yield bma(server, null, true);
-      yield bmapi.openConnections();
-      yield server.recomputeSelfPeer();
-    });
-    yield pluggedConfP;
-    return {};
-  });
-
-  this.applyNewKeyConf = (req) => co(function *() {
-    yield pluggedConfP;
-    const conf = http2raw.conf(req);
-    const keyPair = yield keyring.scryptKeyPair(conf.idty_entropy, conf.idty_password);
-    const publicKey = keyPair.publicKey;
-    const secretKey = keyPair.secretKey;
-    yield server.dal.saveConf(_.extend(server.conf, {
-      salt: conf.idty_entropy,
-      passwd: conf.idty_password,
-      pair: {
-        pub: publicKey,
-        sec: secretKey
-      }
-    }));
-    pluggedConfP = yield server.loadConf();
-    yield pluggedConfP;
-    return {};
-  });
-
-  this.applyCPUConf = (req) => co(function *() {
-    yield pluggedConfP;
-    server.conf.cpu = http2raw.cpu(req);
-    yield server.dal.saveConf(server.conf);
-    yield server.applyCPU(server.conf.cpu);
-    pluggedConfP = yield server.loadConf();
-    yield pluggedConfP;
-    return {};
-  });
-
-  this.listInterfaces = () => co(function *() {
-    const upnp = {
-      name: 'upnp',
-      addresses: []
-    };
-    const manual = {
-      name: 'conf',
-      addresses: []
-    };
-    const lan = {
-      name: 'lan',
-      addresses: []
-    };
-    yield pluggedConfP;
-    const conf = server.conf;
-    if (conf.remoteipv4) {
-      manual.addresses.push({ family: 'IPv4', address: conf.remoteipv4 });
-    }
-    if (conf && conf.remoteipv6) {
-      manual.addresses.push({ family: 'IPv6', address: conf.remoteipv6 });
-    }
-    let upnpConf;
-    try {
-      upnpConf = yield network.upnpConf();
-      if (upnpConf.remoteipv4) {
-        upnp.addresses.push({
-          family: 'IPv4',
-          address: upnpConf.remoteipv4
-        });
-      }
-      if (upnpConf.remoteipv6) {
-        upnp.addresses.push({
-          family: 'IPv6',
-          address: upnpConf.remoteipv6
-        });
-      }
-    } catch (e) {
-      logger.error(e.stack || e);
-    }
-    const lanIPv4 = network.getLANIPv4();
-    lanIPv4.forEach(function(addr) {
-      lan.addresses.push({
-        family: 'IPv4',
-        address: addr.value
-      });
-    });
-    const lanIPv6 = network.getLANIPv6();
-    lanIPv6.forEach(function(addr) {
-      lan.addresses.push({
-        family: 'IPv6',
-        address: addr.value
-      });
-    });
-    const randomPort = network.getRandomPort(conf);
-    return {
-      local: network.listInterfaces(),
-      remote: [upnp, manual, lan],
-      auto: {
-        local: {
-          ipv4: network.getBestLocalIPv4(),
-          ipv6: network.getBestLocalIPv6(),
-          port: randomPort
-        },
-        remote: {
-          ipv4: upnpConf && upnpConf.remoteipv4,
-          ipv6: upnpConf && upnpConf.remoteipv6,
-          dns: '',
-          port: randomPort,
-          upnp: upnpConf ? true : false
-        }
-      },
-      conf: {
-        local: {
-          ipv4: conf && conf.ipv4,
-          ipv6: conf && conf.ipv6,
-          port: conf && conf.port
-        },
-        remote: {
-          ipv4: conf && conf.remoteipv4,
-          ipv6: conf && conf.remoteipv6,
-          dns:  conf && conf.remotehost,
-          port: conf && conf.remoteport,
-          upnp: conf && conf.upnp
-        }
-      }
-    };
-  });
-
-  this.startAllServices = () => co(function *() {
-    // Allow services to be stopped
-    stopServicesP = null;
-    if (!server.conf.salt && !server.conf.passwd) {
-      const conf = {
-        idty_entropy: ~~(Math.random() * 2147483647) + "",
-        idty_password: ~~(Math.random() * 2147483647) + ""
-      };
-      yield that.applyNewKeyConf({ body: { conf :conf } });
-    }
-    yield startServicesP || (startServicesP = ucoin.statics.startServices(server));
-    that.push({ started: true });
-    return {};
-  });
-
-  this.stopAllServices = () => co(function *() {
-    // Allow services to be started
-    startServicesP = null;
-    yield stopServicesP || (stopServicesP = ucoin.statics.stopServices(server));
-    that.push({ stopped: true });
-    return {};
-  });
-
-  this.autoConfNetwork = () => co(function *() {
-    // Reconfigure the network if it has not been initialized yet
-    if (!server.conf.remoteipv4 && !server.conf.remoteipv6 && !server.conf.remotehost) {
-      const bestLocal4 = network.getBestLocalIPv4();
-      const bestLocal6 = network.getBestLocalIPv6();
-      let upnpConf = {
-        remoteipv4: bestLocal4,
-        remoteipv6: bestLocal6,
-        upnp: false
-      };
-      try {
-        upnpConf = yield network.upnpConf();
-        upnpConf.upnp = true;
-      } catch (e) {
-        logger.error(e.stack || e);
-      }
-      let randomPort = network.getRandomPort(server.conf);
-      _.extend(server.conf, {
-        ipv4: bestLocal4,
-        ipv6: bestLocal6,
-        port: randomPort,
-        remoteipv4: upnpConf.remoteipv4,
-        remoteipv6: upnpConf.remoteipv6,
-        remoteport: randomPort,
-        upnp: upnpConf.upnp
-      });
-      yield server.dal.saveConf(server.conf);
-      pluggedConfP = co(function *() {
-        yield bmapi.closeConnections();
-        yield server.loadConf();
-        bmapi = yield bma(server, null, true);
-      });
-    }
-    return {};
-  });
-
-  this.startSync = (req) => co(function *() {
-    const sync = server.synchronize(req.body.host, parseInt(req.body.port), parseInt(req.body.to), parseInt(req.body.chunkLen));
-    sync.flow.pipe(es.mapSync(function(data) {
-      // Broadcast block
-      that.push(data);
-    }));
-    yield sync.syncPromise;
-    return {};
-  });
-
-  this.resetData = () => co(function *() {
-    yield pluggedDALP;
-    // We have to wait for a non-breaking window to process reset
-    yield server.BlockchainService.pushFIFO(() => co(function *() {
-      yield that.stopHTTP();
-      yield that.stopAllServices();
-      yield server.unplugFileSystem();
-      yield server.cleanDBData();
-      yield pluggedDALP;
-      pluggedConfP = plugForConf();
-      pluggedDALP = replugDAL();
-    }));
-    return {};
-  });
-
-  this.exportData = () => co(function *() {
-    yield pluggedDALP;
-    return server.exportAllDataAsZIP();
-  });
-
-  this.importData = (req) => co(function *() {
-    yield that.stopHTTP();
-    yield that.stopAllServices();
-    yield server.unplugFileSystem();
-    yield pluggedDALP;
-    if (!req.files.importData) {
-      throw "Wrong upload file name";
-    }
-    const importZipPath = path.join(server.home, 'import.zip');
-    yield new Promise((resolve, reject) => {
-      req.files.importData.mv(importZipPath, (err) => {
-        err ? reject(err) : resolve();
-      });
-    });
-    yield server.importAllDataFromZIP(importZipPath);
-    pluggedConfP = plugForConf();
-    pluggedDALP = replugDAL();
-    return {};
-  });
-
-  this.isNodePubliclyReachable = (req) => co(function *() {
-    const peer = yield server.PeeringService.peer();
-    const reachable = yield contacter.statics.isReachableFromTheInternet(peer, { timeout: 5000 });
-    return { success: reachable };
-  });
-
-  this.testPeer = (req) => co(function *() {
-    return server.testForSync(req.body.host, parseInt(req.body.port));
-  });
-
-  this.logsExport = (req) => co(function *() {
-    yield pluggedDALP;
-    const logs = yield server.getLastLogLines(req.params.quantity || 1500);
-    const body = yield rp.post({
-      url: 'http://hastebin.com/documents',
-      body: logs
-    });
-    const res = JSON.parse(body);
-    return {
-      link: 'http://hastebin.com/' + res.key
-    };
-  });
-
-  function plugForConf() {
-    return co(function *() {
-      yield server.plugFileSystem();
-      yield server.loadConf();
-      bmapi = yield bma(server, null, true);
-    });
-  }
-
-  function plugForDAL() {
-    return co(function *() {
-      yield pluggedConfP;
-      return server.initDAL();
-    });
-  }
-}
-
-util.inherits(WebAdmin, stream.Duplex);
diff --git a/app/lib/constants.js b/app/lib/constants.js
index 62dcd5d8f..aab2b89ac 100644
--- a/app/lib/constants.js
+++ b/app/lib/constants.js
@@ -343,12 +343,9 @@ module.exports = {
   INVALIDATE_CORE_CACHE: true,
   WITH_SIGNATURES_AND_POW: true,
 
-  WEBMIN_LOGS_CACHE: 2000,
-
   NO_FORK_ALLOWED: false,
   FORK_ALLOWED: true,
 
-  MEMORY_CLEAN_INTERVAL: 60 * 60, // hourly
   SAFE_FACTOR: 3,
   BLOCKS_COLLECT_THRESHOLD: 30, // Blocks to collect from memory and persist
 
diff --git a/app/lib/dal/sqliteDAL/BlockDAL.js b/app/lib/dal/sqliteDAL/BlockDAL.js
index 647d6bc59..729158041 100644
--- a/app/lib/dal/sqliteDAL/BlockDAL.js
+++ b/app/lib/dal/sqliteDAL/BlockDAL.js
@@ -152,8 +152,6 @@ function BlockDAL(driver) {
     current = previousBlock;
   });
 
-  this.migrateOldBlocks = () => Q();
-
   this.getNextForkBlocks = (number, hash) => {
     return that.query('SELECT * FROM block WHERE fork AND number = ? AND previousHash like ? ORDER BY number', [number + 1, hash]);
   };
diff --git a/app/lib/streams/routes.js b/app/lib/streams/routes.js
index cd7975dd4..a6cf8ad9c 100644
--- a/app/lib/streams/routes.js
+++ b/app/lib/streams/routes.js
@@ -6,7 +6,7 @@ const dtos = require('./dtos');
 const sanitize = require('./sanitize');
 const limiter = require('../system/limiter');
 const constants = require('../../lib/constants');
-const logger = require('../logger')('webmin');
+const logger = require('../logger')('routes');
 
 const WebSocketServer = require('ws').Server;
 
@@ -68,33 +68,6 @@ module.exports = {
     httpMethods.httpGET(  prefix + '/ud/history/:pubkey/times/:from/:to',    dividend.getHistoryBetweenTimes,      dtos.UDHistory,      limiter.limitAsHighUsage());
   },
   
-  webmin: function(webminCtrl, app, httpMethods) {
-    httpMethods.httpGET(  '/webmin/summary',                   webminCtrl.summary,    dtos.AdminSummary);
-    httpMethods.httpGET(  '/webmin/summary/pow',               webminCtrl.powSummary, dtos.PoWSummary);
-    httpMethods.httpGET(  '/webmin/logs/export/:quantity',     webminCtrl.logsExport, dtos.LogLink);
-    httpMethods.httpPOST( '/webmin/key/preview',               webminCtrl.previewPubkey, dtos.PreviewPubkey);
-    httpMethods.httpGET(  '/webmin/server/reachable',          webminCtrl.isNodePubliclyReachable, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/http/start',         webminCtrl.startHTTP, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/http/stop',          webminCtrl.stopHTTP,  dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/http/upnp/open',     webminCtrl.openUPnP,  dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/http/upnp/regular',  webminCtrl.regularUPnP,  dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/preview_next',       webminCtrl.previewNext,  dtos.Block);
-    httpMethods.httpPOST( '/webmin/server/send_conf',          webminCtrl.sendConf, dtos.Identity);
-    httpMethods.httpPOST( '/webmin/server/net_conf',           webminCtrl.applyNetworkConf, dtos.Boolean);
-    httpMethods.httpPOST( '/webmin/server/key_conf',           webminCtrl.applyNewKeyConf, dtos.Boolean);
-    httpMethods.httpPOST( '/webmin/server/cpu_conf',           webminCtrl.applyCPUConf, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/republish_selfpeer', webminCtrl.publishANewSelfPeer, dtos.Boolean);
-    httpMethods.httpPOST( '/webmin/server/test_sync',          webminCtrl.testPeer, dtos.Block);
-    httpMethods.httpPOST( '/webmin/server/start_sync',         webminCtrl.startSync, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/auto_conf_network',  webminCtrl.autoConfNetwork, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/services/start_all', webminCtrl.startAllServices, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/services/stop_all',  webminCtrl.stopAllServices, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/reset/data',         webminCtrl.resetData, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/network/interfaces',        webminCtrl.listInterfaces, dtos.NetworkInterfaces);
-    httpMethods.httpGETFile('/webmin/data/duniter_export',     webminCtrl.exportData);
-    httpMethods.httpPOST( '/webmin/data/duniter_import',       webminCtrl.importData);
-  },
-  
   bmaWS: function(server, prefix) {
     return (httpServer) => {
 
@@ -149,107 +122,5 @@ module.exports = {
           }
         }));
     };
-  },
-
-  webminWS: function(webminCtrl) {
-    return (httpServer) => {
-
-      // Socket for synchronization events
-      let wssEvents = new WebSocketServer({
-        server: httpServer,
-        path: '/webmin/ws'
-      });
-
-      let lastLogs = [];
-      wssEvents.on('connection', function connection(ws) {
-
-        ws.on('message', () => {
-          wssEvents.broadcast(JSON.stringify({
-            type: 'log',
-            value: lastLogs
-          }));
-        });
-
-        wssEvents.broadcast(JSON.stringify({
-          type: 'log',
-          value: lastLogs
-        }));
-
-        // The callback which write each new log message to websocket
-        logger.addCallbackLogs((level, msg, timestamp) => {
-          lastLogs.splice(0, Math.max(0, lastLogs.length - constants.WEBMIN_LOGS_CACHE + 1));
-          lastLogs.push({
-            timestamp: timestamp,
-            level: level,
-            msg: msg
-          });
-          wssEvents.broadcast(JSON.stringify({
-            type: 'log',
-            value: [{
-              timestamp: timestamp,
-              level: level,
-              msg: msg
-            }]
-          }));
-        });
-      });
-
-      wssEvents.broadcast = (data) => wssEvents.clients.forEach((client) => {
-        try {
-          client.send(data);
-        } catch (e) {
-          console.log(e);
-        }
-      });
-
-      // Forward blocks & peers
-      webminCtrl
-        .pipe(es.mapSync(function(data) {
-          // Broadcast block
-          if (data.download !== undefined) {
-            wssEvents.broadcast(JSON.stringify({
-              type: 'download',
-              value: data.download
-            }));
-          }
-          if (data.applied !== undefined) {
-            wssEvents.broadcast(JSON.stringify({
-              type: 'applied',
-              value: data.applied
-            }));
-          }
-          if (data.sync !== undefined) {
-            wssEvents.broadcast(JSON.stringify({
-              type: 'sync',
-              value: data.sync,
-              msg: (data.msg && (data.msg.message || data.msg))
-            }));
-          }
-          if (data.started !== undefined) {
-            wssEvents.broadcast(JSON.stringify({
-              type: 'started',
-              value: data.started
-            }));
-          }
-          if (data.stopped !== undefined) {
-            wssEvents.broadcast(JSON.stringify({
-              type: 'stopped',
-              value: data.stopped
-            }));
-          }
-          if (data.pulling !== undefined) {
-            wssEvents.broadcast(JSON.stringify({
-              type: 'pulling',
-              value: data.pulling
-            }));
-          }
-          if (data.pow !== undefined) {
-            wssEvents.broadcast(JSON.stringify({
-              type: 'pow',
-              value: data.pow
-            }));
-          }
-        }));
-    };
   }
 };
diff --git a/app/lib/streams/webmin.js b/app/lib/streams/webmin.js
deleted file mode 100644
index 770250380..000000000
--- a/app/lib/streams/webmin.js
+++ /dev/null
@@ -1,32 +0,0 @@
-"use strict";
-
-const path = require('path');
-const routes = require('./routes');
-const network = require('../system/network');
-const dtos = require('../../lib/streams/dtos');
-
-const ENABLE_FILE_UPLOAD = true;
-
-let WebSocketServer = require('ws').Server;
-
-module.exports = function(dbConf, overConf, interfaces, httpLogs) {
-
-  const webminCtrl = require('../../controllers/webmin.controller')(dbConf, overConf);
-
-  const fullPath = path.join(__dirname, '../../../web-ui/public');
-
-  let httpLayer = network.createServersAndListen('Duniter web admin', interfaces, httpLogs, fullPath, (app, httpMethods) => {
-
-    routes.bma(webminCtrl.server, '/bma', app, httpMethods);
-    routes.webmin(webminCtrl, app, httpMethods);
-
-  }, (httpServer) => {
-    routes.bmaWS(webminCtrl.server, '/bma')(httpServer);
-    routes.webminWS(webminCtrl)(httpServer);
-  }, ENABLE_FILE_UPLOAD);
-
-  return {
-    httpLayer: httpLayer,
-    webminCtrl: webminCtrl
-  };
-};
diff --git a/app/service/BlockchainService.js b/app/service/BlockchainService.js
index d2d7d0bad..09cd4fb33 100644
--- a/app/service/BlockchainService.js
+++ b/app/service/BlockchainService.js
@@ -457,25 +457,5 @@ function BlockchainService (server) {
     return dal.getBlocksBetween(from, from + count - 1);
   });
 
-  const cleanMemFifo = async.queue((task, callback) => task(callback), 1);
-  let cleanMemFifoInterval = null;
-  this.regularCleanMemory = function (done) {
-    if (cleanMemFifoInterval)
-      clearInterval(cleanMemFifoInterval);
-    cleanMemFifoInterval = setInterval(() => cleanMemFifo.push(cleanMemory), 1000 * constants.MEMORY_CLEAN_INTERVAL);
-    cleanMemory(done);
-  };
-
-  this.stopCleanMemory = () => clearInterval(cleanMemFifoInterval);
-
-  const cleanMemory = (done) => {
-    dal.blockDAL.migrateOldBlocks()
-      .then(() => done())
-      .catch((err) => {
-        logger.warn(err);
-        done();
-      });
-  };
-
   this.changeProverCPUSetting = (cpu) => prover.changeCPU(cpu);
 }
diff --git a/bin/daemon b/bin/daemon
index 6c4dd2258..801f3fb3c 100755
--- a/bin/daemon
+++ b/bin/daemon
@@ -38,36 +38,13 @@ switch (process.argv[2]) {
     });
     break;
 
-  case "webwait":
-    daemon = getDaemon('webwait');
-    start(daemon);
-    break;
-
-  case "webstart":
-    daemon = getDaemon('webstart');
-    start(daemon);
-    break;
-
-  case "webstop":
-    daemon = getDaemon();
-    daemon.stop();
-    break;
-
-  case "webrestart":
-    daemon = getDaemon('webstart');
-    daemon.stop(function(err) {
-      err && console.error(err);
-      start(daemon);
-    });
-    break;
-
   case "logs":
     console.log(directory.INSTANCE_HOMELOG_FILE);
     process.exit(0);
     break;
 
   default:
-    console.log("Usage: [webstart|webwait|webstop|webrestart|start|stop|restart]");
+    console.log("Usage: [start|stop|restart]");
 }
 
 function getDaemon(overrideCommand) {
diff --git a/duniter.sh b/duniter.sh
index 4d4b60154..e43c69bfe 100755
--- a/duniter.sh
+++ b/duniter.sh
@@ -45,7 +45,7 @@ duniter() {
 		#  DUNITER DAEMON MANAGEMENT
 		#---------------------------------
 
-		reset|webwait|webstart|webstop|webrestart|start|stop|restart)
+		reset|start|stop|restart)
 		$NODE "$DUNITER_DIR/bin/daemon" $*
 		;;
 
diff --git a/index.js b/index.js
index b1b988df5..696261657 100644
--- a/index.js
+++ b/index.js
@@ -3,7 +3,6 @@
 const co = require('co');
 const _ = require('underscore');
 const Server = require('./server');
-const webmin  = require('./app/lib/streams/webmin');
 const logger = require('./app/lib/logger')('duniter');
 
 module.exports = function (dbConf, overConf) {
@@ -57,10 +56,6 @@ module.exports.statics = {
 
   },
 
-  enableHttpAdmin: (dbConf, overConf, httpLogs, wmHost, wmPort) => webmin(dbConf, overConf, [{
-    ip:  wmHost || 'localhost',
-    port: wmPort || 9220
-  }], httpLogs !== false),
   autoStack: () => {
 
     const cli = require('./app/cli');
diff --git a/package.json b/package.json
index 6a5ea3753..87d1e3ce9 100644
--- a/package.json
+++ b/package.json
@@ -13,10 +13,9 @@
     "test": "test"
   },
   "scripts": {
-    "full-install": "npm install && git submodule init && git submodule update && cd web-ui && npm install",
     "test": "mocha --growl --timeout 20000 test test/fast test/fast/block test/integration test/",
     "start": "node bin/duniter start",
-    "webstart": "cd web-ui && parallelshell \"node ../bin/duniter webstart\" \"npm start\"",
+    "build": "cd \"node_modules/duniter-ui\" && npm install && npm run build",
     "test-travis": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec --timeout 20000 test test/fast test/fast/block test/integration test/"
   },
   "repository": {
@@ -58,6 +57,7 @@
     "multimeter": "0.1.1",
     "naclb": "1.3.7",
     "nnupnp": "1.0.2",
+    "node-pre-gyp": "^0.6.32",
     "optimist": "0.6.1",
     "parallelshell": "^2.0.0",
     "q": "1.4.1",
@@ -78,6 +78,7 @@
   },
   "devDependencies": {
     "coveralls": "2.11.4",
+    "duniter-ui": "0.2.0",
     "eslint": "0.21.1",
     "eslint-plugin-mocha": "0.2.2",
     "istanbul": "0.4.0",
diff --git a/server.js b/server.js
index b52289efe..7aa926746 100644
--- a/server.js
+++ b/server.js
@@ -20,7 +20,6 @@ const dos2unix    = require('./app/lib/system/dos2unix');
 const Synchroniser = require('./app/lib/sync');
 const multicaster = require('./app/lib/streams/multicaster');
 const upnp        = require('./app/lib/system/upnp');
-const bma         = require('./app/lib/streams/bma');
 const rawer       = require('./app/lib/ucp/rawer');
 const permanentProver = require('./app/lib/computation/permanentProver');
 
@@ -37,6 +36,20 @@ function Server (dbConf, overrideConf) {
   that.conf = null;
   that.dal = null;
   that.version = jsonpckg.version;
+  that.logger = logger;
+
+  // External libs
+  that.lib = {};
+  that.lib.keyring = require('./app/lib/crypto/keyring');
+  that.lib.Identity = require('./app/lib/entity/identity');
+  that.lib.rawer = require('./app/lib/ucp/rawer');
+  that.lib.http2raw = require('./app/lib/helpers/http2raw');
+  that.lib.dos2unix = require('./app/lib/system/dos2unix');
+  that.lib.contacter = require('./app/lib/contacter');
+  that.lib.bma = require('./app/lib/streams/bma');
+  that.lib.network = require('./app/lib/system/network');
+  that.lib.constants = require('./app/lib/constants');
+  that.lib.ucp = require('./app/lib/ucp/buid');
 
   that.MerkleService       = require("./app/lib/helpers/merkle");
   that.ParametersService   = require("./app/lib/helpers/parameters")();
@@ -198,11 +211,6 @@ function Server (dbConf, overrideConf) {
     return that.initPeer();
   });
 
-  this.stop = () => {
-    that.BlockchainService.stopCleanMemory();
-    return that.PeeringService.stopRegular();
-  };
-
   this.recomputeSelfPeer = () => that.PeeringService.generateSelfPeer(that.conf, 0);
 
   this.initPeer = () => co(function*(){
@@ -212,7 +220,6 @@ function Server (dbConf, overrideConf) {
       yield that.PeeringService.regularPeerSignal();
       yield Q.nbind(that.PeeringService.regularTestPeers, that.PeeringService);
       yield Q.nbind(that.PeeringService.regularSyncBlock, that.PeeringService);
-      yield Q.nbind(that.BlockchainService.regularCleanMemory, that.BlockchainService);
   });
 
   this.stopBlockComputation = () => permaProver.stopEveryting();
@@ -471,14 +478,6 @@ function Server (dbConf, overrideConf) {
 
   this.applyCPU = (cpu) => that.BlockchainService.changeProverCPUSetting(cpu);
   
-  this.listenToTheWeb = (showLogs) => co(function *() {
-    const bmapi = yield bma(that, [{
-      ip: that.conf.ipv4,
-      port: that.conf.port
-    }], showLogs);
-    return bmapi.openConnections();
-  });
-
   this.rawer = rawer;
 
   this.writeRaw = (raw, type) => co(function *() {
@@ -492,6 +491,48 @@ function Server (dbConf, overrideConf) {
    * @param linesQuantity
    */
   this.getLastLogLines = (linesQuantity) => this.dal.getLogContent(linesQuantity);
+
+  this.startServices = () => co(function*(){
+
+    /***************
+     * HTTP ROUTING
+     **************/
+    that.router(that.conf.routing);
+
+    /***************
+     *    UPnP
+     **************/
+    if (that.conf.upnp) {
+      try {
+        if (that.upnpAPI) {
+          that.upnpAPI.stopRegular();
+        }
+        yield that.upnp();
+        that.upnpAPI.startRegular();
+      } catch (e) {
+        logger.warn(e);
+      }
+    }
+
+    /*******************
+     * BLOCK COMPUTING
+     ******************/
+    if (that.conf.participate) {
+      that.startBlockComputation();
+    }
+
+    /***********************
+     * CRYPTO NETWORK LAYER
+     **********************/
+    yield that.start();
+  });
+
+  this.stopServices = () => co(function*(){
+    that.router(false);
+    if (that.conf.participate) {
+      that.stopBlockComputation();
+    }
+  });
 }
 
 util.inherits(Server, stream.Duplex);
diff --git a/test/integration/tools/node.js b/test/integration/tools/node.js
index d5f787bd3..180c02706 100644
--- a/test/integration/tools/node.js
+++ b/test/integration/tools/node.js
@@ -13,6 +13,7 @@ var Configuration = require('../../../app/lib/entity/configuration');
 var Peer          = require('../../../app/lib/entity/peer');
 var user   = require('./user');
 var http   = require('./http');
+const bma = require('../../../app/lib/streams/bma');
 
 var MEMORY_MODE = true;
 
@@ -203,7 +204,13 @@ function Node (dbName, options) {
         done && done(err);
       });
     })
-      .then((server) => server.listenToTheWeb());
+      .then((server) => co(function*() {
+        const bmapi = yield bma(server, [{
+          ip: server.conf.ipv4,
+          port: server.conf.port
+        }], true);
+        return bmapi.openConnections();
+      }));
   };
 
   function service(callback) {
diff --git a/web-ui b/web-ui
deleted file mode 160000
index f06bce8a5..000000000
--- a/web-ui
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f06bce8a5cb4bb952f3fcaf03aca0f209ac5aa7e
-- 
GitLab