Initial commit
This commit is contained in:
commit
7c497407df
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
.idea
|
||||||
|
tmp
|
||||||
|
admin/i18n/flat.txt
|
||||||
|
admin/i18n/*/flat.txt
|
||||||
|
iob_npm.done
|
||||||
|
package-lock.json
|
9
.npmignore
Normal file
9
.npmignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
gulpfile.js
|
||||||
|
tasks
|
||||||
|
tmp
|
||||||
|
test
|
||||||
|
.travis.yml
|
||||||
|
appveyor.yml
|
||||||
|
admin/i18n
|
||||||
|
iob_npm.done
|
||||||
|
package-lock.json
|
23
.travis.yml
Normal file
23
.travis.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- '4'
|
||||||
|
- '6'
|
||||||
|
- '8'
|
||||||
|
- '10'
|
||||||
|
before_script:
|
||||||
|
- export NPMVERSION=$(echo "$($(which npm) -v)"|cut -c1)
|
||||||
|
- 'if [[ $NPMVERSION == 5 ]]; then npm install -g npm@5; fi'
|
||||||
|
- npm -v
|
||||||
|
- npm install winston@2.3.1
|
||||||
|
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
|
||||||
|
env:
|
||||||
|
- CXX=g++-4.8
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-4.8
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017-2018 bluefox <dogafox@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
57
README.md
Normal file
57
README.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
![Logo](admin/proxy.png)
|
||||||
|
# yunkong2.proxy
|
||||||
|
=================
|
||||||
|
|
||||||
|
[![NPM version](http://img.shields.io/npm/v/yunkong2.proxy.svg)](https://www.npmjs.com/package/yunkong2.proxy)
|
||||||
|
[![Downloads](https://img.shields.io/npm/dm/yunkong2.proxy.svg)](https://www.npmjs.com/package/yunkong2.proxy)
|
||||||
|
[![Tests](https://travis-ci.org/yunkong2/yunkong2.proxy.svg?branch=master)](https://travis-ci.org/yunkong2/yunkong2.proxy)
|
||||||
|
|
||||||
|
[![NPM](https://nodei.co/npm/yunkong2.proxy.png?downloads=true)](https://nodei.co/npm/yunkong2.proxy/)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Allows to access defined URLs or local files via one web server.
|
||||||
|
|
||||||
|
Specified routes will be available under ```http://ip:8082/proxy.0/context/...```. Of course port, protocol, "proxy.0", can variate depends on settings.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
- Extend WEB adapter: For which web instance will active this proxy.
|
||||||
|
- Route path: Path for proxy. If "/proxy.0", so the routes will be available under ```http://webIP:8082/proxy.0/...```
|
||||||
|
- Error timeout(ms): Minimal interval between retries if the requested resource was unavailable or returned error.
|
||||||
|
|
||||||
|
## Sample settings
|
||||||
|
| Context | URL | Description |
|
||||||
|
|----------------|:---------------------------------------------------|:---------------------------------------------------|
|
||||||
|
| admin/ | http://localhost:8081 | access to admin page |
|
||||||
|
| router/ | http://192.168.1.1 | access to local router |
|
||||||
|
| cam/ | http://user:pass@192.168.1.123 | access to webcam (e.g. call http://ip:8082/proxy.0/cam/web/snapshot.jpg) |
|
||||||
|
| dir/ | /tmp/ | access to local directory "/tmp/" |
|
||||||
|
| dir/ | tmp/ | access to local directory "/opt/yunkong2/tmp" |
|
||||||
|
| file.jpg | /tmp/picture.jpg | access to local file "/tmp/picture.jpg" |
|
||||||
|
|
||||||
|
**Not all devices can be accessed via proxy.
|
||||||
|
|
||||||
|
Some devices wants to be located in the root ```http://ip/``` and cannot run under ```http://ip/proxy.0/context/```.
|
||||||
|
|
||||||
|
You can read more about context [here](https://www.npmjs.com/package/http-proxy-middleware#context-matching)
|
||||||
|
|
||||||
|
Additionally the user can define the route path for proxy requests.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
### 1.0.3 (2018-07-14)
|
||||||
|
* (bluefox) Newer mime version used
|
||||||
|
|
||||||
|
### 1.0.2 (2018-06-30)
|
||||||
|
* (bluefox) URI was decoded for usage of special chars in password and login
|
||||||
|
|
||||||
|
### 1.0.1 (2018-03-01)
|
||||||
|
* (bluefox) Fixed error: after 10 timeouts the web cam was never reachable
|
||||||
|
* (bluefox) Ready for Admin3
|
||||||
|
|
||||||
|
### 1.0.0 (2017-10-09)
|
||||||
|
* (bluefox) do not allow the error generation to fast
|
||||||
|
|
||||||
|
### 0.2.0 (2017-03-13)
|
||||||
|
* (bluefox) fix run-mode
|
||||||
|
|
||||||
|
### 0.0.1 (2017-01-09)
|
||||||
|
* (bluefox) initial commit
|
124
admin/index.html
Normal file
124
admin/index.html
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
|
||||||
|
<script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
|
||||||
|
<script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
|
||||||
|
<script type="text/javascript" src="../../js/translate.js"></script>
|
||||||
|
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
|
||||||
|
<script type="text/javascript" src="words.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.number {
|
||||||
|
width: 70px
|
||||||
|
}
|
||||||
|
.table-values th {
|
||||||
|
background: #686868;
|
||||||
|
color: #FFF;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.table-values tr:nth-child(even) {
|
||||||
|
background: #d0d0d0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function load(settings, onChange) {
|
||||||
|
if (!settings) return;
|
||||||
|
if (!settings.route) settings.route = 'proxy.' + instance + '/';
|
||||||
|
if (!settings.errorTimeout) settings.errorTimeout = 10000;
|
||||||
|
|
||||||
|
$('.value').each(function () {
|
||||||
|
var key = $(this).attr('id');
|
||||||
|
var $key = $('#' + key + '.value');
|
||||||
|
if ($key.attr('type') === 'checkbox') {
|
||||||
|
$key.prop('checked', settings[key]).change(function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$key.val(settings[key]).change(function() {
|
||||||
|
onChange();
|
||||||
|
}).keyup(function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getExtendableInstances(function (result) {
|
||||||
|
if (result) {
|
||||||
|
var text = '';
|
||||||
|
for (var r = 0; r < result.length; r++) {
|
||||||
|
var name = result[r]._id.substring('system.adapter.'.length);
|
||||||
|
text += '<option value="' + name + '" ' + (settings.webInstance === name ? 'selected' : '') + '>' + name + '</option>';
|
||||||
|
}
|
||||||
|
$('#webInstance').append(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onChange(false);
|
||||||
|
values2table('values', settings.rules, onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(callback) {
|
||||||
|
var obj = {};
|
||||||
|
$('.value').each(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
if ($this.attr('type') === 'checkbox') {
|
||||||
|
obj[$this.attr('id')] = $this.prop('checked');
|
||||||
|
} else {
|
||||||
|
obj[$this.attr('id')] = $this.val();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (obj.route === 'proxy.' + instance + '/') obj.route = '';
|
||||||
|
obj.rules = table2values();
|
||||||
|
|
||||||
|
callback(obj);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="adapter-container">
|
||||||
|
|
||||||
|
<table><tr><td><img src="proxy.png"></td><td><h3 class="translate">Proxy adapter settings</h3></td></tr></table>
|
||||||
|
|
||||||
|
<table style="width: 100%;">
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px;"><label for="webInstance" class="translate">Extend WEB adapter:</label></td>
|
||||||
|
<td><select class="value" id="webInstance">
|
||||||
|
<option value="*" class="translate">all</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px;"><label for="route" class="translate">Route path:</label></td>
|
||||||
|
<td><input class="value" id="route"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px;"><label for="errorTimeout" class="translate">Error timeout(ms):</label></td>
|
||||||
|
<td><input class="value" id="errorTimeout" type="number" min="1000"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div id="values" style="width: 100%; height: calc(100% - 245px)">
|
||||||
|
<button class="table-button-add" style="margin-left: 10px; width: 1.5em; height: 1.5em"></button>
|
||||||
|
<div style="width: 100%; height: calc(100% - 30px); overflow: auto;">
|
||||||
|
<table class="table-values" style="width: 100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-name="regex" style="width: 30%" class="translate">Context</th>
|
||||||
|
<th data-name="url" class="translate">URL</th>
|
||||||
|
<th data-name="timeout" style="width: 70px" data-style="width: 70px" data-type="number" class="translate">Timeout</th>
|
||||||
|
<th data-buttons="delete up down" style="width: 70px"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
162
admin/index_m.html
Normal file
162
admin/index_m.html
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Materialze style -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../lib/css/materialize.css">
|
||||||
|
|
||||||
|
<script type="text/javascript" src="../../lib/js/jquery-3.2.1.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="../../js/translate.js"></script>
|
||||||
|
<script type="text/javascript" src="../../lib/js/materialize.js"></script>
|
||||||
|
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
|
||||||
|
<script type="text/javascript" src="words.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function load(settings, onChange) {
|
||||||
|
if (!settings) return;
|
||||||
|
if (!settings.route) settings.route = 'proxy.' + instance + '/';
|
||||||
|
if (!settings.errorTimeout) settings.errorTimeout = 10000;
|
||||||
|
|
||||||
|
$('.value').each(function () {
|
||||||
|
var key = $(this).attr('id');
|
||||||
|
var $key = $('#' + key + '.value');
|
||||||
|
if ($key.attr('type') === 'checkbox') {
|
||||||
|
$key.prop('checked', settings[key]).change(function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$key.val(settings[key]).on('change', function() {
|
||||||
|
onChange();
|
||||||
|
}).on('keyup', function() {
|
||||||
|
onChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
getExtendableInstances(function (result) {
|
||||||
|
if (result) {
|
||||||
|
var text = '';
|
||||||
|
for (var r = 0; r < result.length; r++) {
|
||||||
|
var name = result[r]._id.substring('system.adapter.'.length);
|
||||||
|
text += '<option value="' + name + '" ' + (settings.webInstance === name ? 'selected' : '') + '>' + name + '</option>';
|
||||||
|
}
|
||||||
|
$('#webInstance').append(text).select();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onChange(false);
|
||||||
|
values2table('values', settings.rules, onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(callback) {
|
||||||
|
var obj = {};
|
||||||
|
$('.value').each(function () {
|
||||||
|
var $this = $(this);
|
||||||
|
if ($this.attr('type') === 'checkbox') {
|
||||||
|
obj[$this.attr('id')] = $this.prop('checked');
|
||||||
|
} else {
|
||||||
|
obj[$this.attr('id')] = $this.val();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (obj.route === 'proxy.' + instance + '/') obj.route = '';
|
||||||
|
obj.rules = table2values();
|
||||||
|
|
||||||
|
callback(obj);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
#values {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.table-values-div {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 30px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.table-values {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.adapter-container>.row {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
#tab-paths {
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#tab-paths>.row {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.m .select-wrapper+label {
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
.m td, .m th {
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
.m td input {
|
||||||
|
height: 2rem !important;
|
||||||
|
}
|
||||||
|
.table-values-div {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="adapter-container m">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
<ul class="tabs">
|
||||||
|
<li class="tab col s4"><a href="#tab-main" class="translate active">Main settings</a></li>
|
||||||
|
<li class="tab col s4"><a href="#tab-paths" class="translate">Paths</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="tab-main" class="col s12 page">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m4 l2">
|
||||||
|
<img src="proxy.png" class="logo">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m4">
|
||||||
|
<select class="value" id="webInstance">
|
||||||
|
<option value="*" class="translate">all</option>
|
||||||
|
</select>
|
||||||
|
<label for="webInstance" class="translate">Extend WEB adapter:</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m4">
|
||||||
|
<input class="value" id="route"/>
|
||||||
|
<label for="route" class="translate">Route path:</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m4">
|
||||||
|
<input class="value" id="errorTimeout" type="number" min="1000"/>
|
||||||
|
<label for="errorTimeout" class="translate">Error timeout(ms):</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="tab-paths" class="col s12 page">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12" id="values">
|
||||||
|
<a class="btn-floating waves-effect waves-light blue table-button-add"><i class="material-icons">add</i></a>
|
||||||
|
<div class="table-values-div">
|
||||||
|
<table class="table-values">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-name="regex" style="width: 30%" class="translate">Context</th>
|
||||||
|
<th data-name="url" class="translate">URL</th>
|
||||||
|
<th data-name="timeout" style="width: 70px" data-style="width: 70px" data-type="number" class="translate">Timeout</th>
|
||||||
|
<th data-buttons="delete up down" style="width: 70px"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
admin/proxy.png
Normal file
BIN
admin/proxy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
15
admin/words.js
Normal file
15
admin/words.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n
|
||||||
|
/*global systemDictionary:true */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
systemDictionary = {
|
||||||
|
"Proxy adapter settings": { "en": "Proxy adapter settings", "de": "Proxy Adapter Einstellungen", "ru": "Настройки Proxy драйвера", "pt": "Configurações do adaptador proxy", "nl": "Proxy-adapterinstellingen", "fr": "Paramètres de l'adaptateur proxy", "it": "Impostazioni dell'adattatore proxy", "es": "Configuración del adaptador proxy", "pl": "Ustawienia adaptera proxy"},
|
||||||
|
"Extend WEB adapter:": { "en": "Extend WEB adapter", "de": "Erweitere WEB Adapter", "ru": "Подключить к WEB драйверу", "pt": "Alargar o adaptador WEB", "nl": "Breid WEB-adapter uit", "fr": "Étendre l'adaptateur WEB", "it": "Estendi la scheda WEB", "es": "Extender el adaptador WEB", "pl": "Przedłuż adapter WEB"},
|
||||||
|
"all": { "en": "all", "de": "alle", "ru": "все", "pt": "todos", "nl": "alle", "fr": "tout", "it": "tutto", "es": "todo", "pl": "wszystko"},
|
||||||
|
"Context": { "en": "Context", "de": "Kontext", "ru": "Контекст", "pt": "Contexto", "nl": "verband", "fr": "Contexte", "it": "contesto", "es": "Contexto", "pl": "Kontekst"},
|
||||||
|
"URL": { "en": "URL", "de": "URL", "ru": "URL", "pt": "URL", "nl": "URL", "fr": "URL", "it": "URL", "es": "URL", "pl": "URL"},
|
||||||
|
"Route path:": { "en": "Route path", "de": "Route-Weg", "ru": "Путь в URL", "pt": "Caminho da rota", "nl": "Route pad", "fr": "Chemin d'itinéraire", "it": "Percorso percorso", "es": "Ruta de la ruta", "pl": "Ścieżka trasy"},
|
||||||
|
"Paths": { "en": "Paths", "de": "Pfade", "ru": "пути", "pt": "Caminhos", "nl": "Paths", "fr": "Chemins", "it": "percorsi", "es": "Caminos", "pl": "Ścieżki"},
|
||||||
|
"Error timeout(ms):": { "en": "Error timeout (ms)", "de": "Fehler-Timeout (ms)", "ru": "Тайм-аут ошибки (мс)", "pt": "Tempo limite de erro (ms)", "nl": "Fout-time-out (ms)", "fr": "Délai d'erreur (ms)", "it": "Timeout errore (ms)", "es": "Tiempo de espera de error (ms)", "pl": "Błąd limitu czasu (ms)"},
|
||||||
|
"Timeout": { "en": "Timeout", "de": "Auszeit", "ru": "Тайм-аут", "pt": "Tempo limite", "nl": "timeout", "fr": "Timeout", "it": "timeout", "es": "Tiempo de espera", "pl": "Limit czasu"}
|
||||||
|
};
|
32
appveyor.yml
Normal file
32
appveyor.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
version: 'test-{build}'
|
||||||
|
os: Visual Studio 2013
|
||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- nodejs_version: '4'
|
||||||
|
- nodejs_version: '6'
|
||||||
|
- nodejs_version: '8'
|
||||||
|
- nodejs_version: '10'
|
||||||
|
platform:
|
||||||
|
- x86
|
||||||
|
- x64
|
||||||
|
clone_folder: 'c:\projects\%APPVEYOR_PROJECT_NAME%'
|
||||||
|
services:
|
||||||
|
- mssql2014
|
||||||
|
- mysql
|
||||||
|
- postgresql
|
||||||
|
install:
|
||||||
|
- ps: 'Install-Product node $env:nodejs_version $env:platform'
|
||||||
|
- ps: '$NpmVersion = (npm -v).Substring(0,1)'
|
||||||
|
- ps: 'if($NpmVersion -eq 5) { npm install -g npm@5 }'
|
||||||
|
- ps: npm --version
|
||||||
|
- npm install -g npm@3
|
||||||
|
- npm install
|
||||||
|
- npm install winston@2.3.1
|
||||||
|
- 'npm install https://git.spacen.net/yunkong2/yunkong2.js-controller/tarball/master --production'
|
||||||
|
test_script:
|
||||||
|
- echo %cd%
|
||||||
|
- node --version
|
||||||
|
- npm --version
|
||||||
|
- ps: Start-Sleep -s 15
|
||||||
|
- npm test
|
||||||
|
build: 'off'
|
401
gulpfile.js
Normal file
401
gulpfile.js
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var gulp = require('gulp');
|
||||||
|
var fs = require('fs');
|
||||||
|
var pkg = require('./package.json');
|
||||||
|
var iopackage = require('./io-package.json');
|
||||||
|
var version = (pkg && pkg.version) ? pkg.version : iopackage.common.version;
|
||||||
|
/*var appName = getAppName();
|
||||||
|
|
||||||
|
function getAppName() {
|
||||||
|
var parts = __dirname.replace(/\\/g, '/').split('/');
|
||||||
|
return parts[parts.length - 1].split('.')[0].toLowerCase();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const fileName = 'words.js';
|
||||||
|
var languages = {
|
||||||
|
en: {},
|
||||||
|
de: {},
|
||||||
|
ru: {},
|
||||||
|
pt: {},
|
||||||
|
nl: {},
|
||||||
|
fr: {},
|
||||||
|
it: {},
|
||||||
|
es: {},
|
||||||
|
pl: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
function lang2data(lang, isFlat) {
|
||||||
|
var str = isFlat ? '' : '{\n';
|
||||||
|
var count = 0;
|
||||||
|
for (var w in lang) {
|
||||||
|
if (lang.hasOwnProperty(w)) {
|
||||||
|
count++;
|
||||||
|
if (isFlat) {
|
||||||
|
str += (lang[w] === '' ? (isFlat[w] || w) : lang[w]) + '\n';
|
||||||
|
} else {
|
||||||
|
var key = ' "' + w.replace(/"/g, '\\"') + '": ';
|
||||||
|
str += key + '"' + lang[w].replace(/"/g, '\\"') + '",\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!count) return isFlat ? '' : '{\n}';
|
||||||
|
if (isFlat) {
|
||||||
|
return str;
|
||||||
|
} else {
|
||||||
|
return str.substring(0, str.length - 2) + '\n}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readWordJs(src) {
|
||||||
|
try {
|
||||||
|
var words;
|
||||||
|
if (fs.existsSync(src + 'js/' + fileName)) {
|
||||||
|
words = fs.readFileSync(src + 'js/' + fileName).toString();
|
||||||
|
} else {
|
||||||
|
words = fs.readFileSync(src + fileName).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = words.split(/\r\n|\r|\n/g);
|
||||||
|
var i = 0;
|
||||||
|
while (!lines[i].match(/^systemDictionary = {/)) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
lines.splice(0, i);
|
||||||
|
|
||||||
|
// remove last empty lines
|
||||||
|
i = lines.length - 1;
|
||||||
|
while (!lines[i]) {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (i < lines.length - 1) {
|
||||||
|
lines.splice(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines[0] = lines[0].replace('systemDictionary = ', '');
|
||||||
|
lines[lines.length - 1] = lines[lines.length - 1].trim().replace(/};$/, '}');
|
||||||
|
words = lines.join('\n');
|
||||||
|
var resultFunc = new Function('return ' + words + ';');
|
||||||
|
|
||||||
|
return resultFunc();
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function padRight(text, totalLength) {
|
||||||
|
return text + (text.length < totalLength ? new Array(totalLength - text.length).join(' ') : '');
|
||||||
|
}
|
||||||
|
function writeWordJs(data, src) {
|
||||||
|
var text = '// DO NOT EDIT THIS FILE!!! IT WILL BE AUTOMATICALLY GENERATED FROM src/i18n\n';
|
||||||
|
text += '/*global systemDictionary:true */\n';
|
||||||
|
text += '\'use strict\';\n\n';
|
||||||
|
|
||||||
|
text += 'systemDictionary = {\n';
|
||||||
|
for (var word in data) {
|
||||||
|
if (data.hasOwnProperty(word)) {
|
||||||
|
text += ' ' + padRight('"' + word.replace(/"/g, '\\"') + '": {', 50);
|
||||||
|
var line = '';
|
||||||
|
for (var lang in data[word]) {
|
||||||
|
if (data[word].hasOwnProperty(lang)) {
|
||||||
|
line += '"' + lang + '": "' + padRight(data[word][lang].replace(/"/g, '\\"') + '",', 50) + ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line) {
|
||||||
|
line = line.trim();
|
||||||
|
line = line.substring(0, line.length - 1);
|
||||||
|
}
|
||||||
|
text += line + '},\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
text = text.replace(/},\n$/, '}\n');
|
||||||
|
text += '};';
|
||||||
|
|
||||||
|
if (fs.existsSync(src + 'js/' + fileName)) {
|
||||||
|
fs.writeFileSync(src + 'js/' + fileName, text);
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(src + '' + fileName, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMPTY = '';
|
||||||
|
|
||||||
|
function words2languages(src) {
|
||||||
|
var langs = Object.assign({}, languages);
|
||||||
|
var data = readWordJs(src);
|
||||||
|
if (data) {
|
||||||
|
for (var word in data) {
|
||||||
|
if (data.hasOwnProperty(word)) {
|
||||||
|
for (var lang in data[word]) {
|
||||||
|
if (data[word].hasOwnProperty(lang)) {
|
||||||
|
langs[lang][word] = data[word][lang];
|
||||||
|
// pre-fill all other languages
|
||||||
|
for (var j in langs) {
|
||||||
|
if (langs.hasOwnProperty(j)) {
|
||||||
|
langs[j][word] = langs[j][word] || EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(src + 'i18n/')) {
|
||||||
|
fs.mkdirSync(src + 'i18n/');
|
||||||
|
}
|
||||||
|
for (var l in langs) {
|
||||||
|
if (!langs.hasOwnProperty(l)) continue;
|
||||||
|
var keys = Object.keys(langs[l]);
|
||||||
|
//keys.sort();
|
||||||
|
var obj = {};
|
||||||
|
for (var k = 0; k < keys.length; k++) {
|
||||||
|
obj[keys[k]] = langs[l][keys[k]];
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(src + 'i18n/' + l)) {
|
||||||
|
fs.mkdirSync(src + 'i18n/' + l);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(src + 'i18n/' + l + '/translations.json', lang2data(obj));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('Cannot read or parse ' + fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function words2languagesFlat(src) {
|
||||||
|
var langs = Object.assign({}, languages);
|
||||||
|
var data = readWordJs(src);
|
||||||
|
if (data) {
|
||||||
|
for (var word in data) {
|
||||||
|
if (data.hasOwnProperty(word)) {
|
||||||
|
for (var lang in data[word]) {
|
||||||
|
if (data[word].hasOwnProperty(lang)) {
|
||||||
|
langs[lang][word] = data[word][lang];
|
||||||
|
// pre-fill all other languages
|
||||||
|
for (var j in langs) {
|
||||||
|
if (langs.hasOwnProperty(j)) {
|
||||||
|
langs[j][word] = langs[j][word] || EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var keys = Object.keys(langs.en);
|
||||||
|
//keys.sort();
|
||||||
|
for (var l in langs) {
|
||||||
|
if (!langs.hasOwnProperty(l)) continue;
|
||||||
|
var obj = {};
|
||||||
|
for (var k = 0; k < keys.length; k++) {
|
||||||
|
obj[keys[k]] = langs[l][keys[k]];
|
||||||
|
}
|
||||||
|
langs[l] = obj;
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(src + 'i18n/')) {
|
||||||
|
fs.mkdirSync(src + 'i18n/');
|
||||||
|
}
|
||||||
|
for (var ll in langs) {
|
||||||
|
if (!langs.hasOwnProperty(ll)) continue;
|
||||||
|
if (!fs.existsSync(src + 'i18n/' + ll)) {
|
||||||
|
fs.mkdirSync(src + 'i18n/' + ll);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(src + 'i18n/' + ll + '/flat.txt', lang2data(langs[ll], langs.en));
|
||||||
|
}
|
||||||
|
fs.writeFileSync(src + 'i18n/flat.txt', keys.join('\n'));
|
||||||
|
} else {
|
||||||
|
console.error('Cannot read or parse ' + fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function languagesFlat2words(src) {
|
||||||
|
var dirs = fs.readdirSync(src + 'i18n/');
|
||||||
|
var langs = {};
|
||||||
|
var bigOne = {};
|
||||||
|
var order = Object.keys(languages);
|
||||||
|
dirs.sort(function (a, b) {
|
||||||
|
var posA = order.indexOf(a);
|
||||||
|
var posB = order.indexOf(b);
|
||||||
|
if (posA === -1 && posB === -1) {
|
||||||
|
if (a > b) return 1;
|
||||||
|
if (a < b) return -1;
|
||||||
|
return 0;
|
||||||
|
} else if (posA === -1) {
|
||||||
|
return -1;
|
||||||
|
} else if (posB === -1) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
if (posA > posB) return 1;
|
||||||
|
if (posA < posB) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var keys = fs.readFileSync(src + 'i18n/flat.txt').toString().split('\n');
|
||||||
|
|
||||||
|
for (var l = 0; l < dirs.length; l++) {
|
||||||
|
if (dirs[l] === 'flat.txt') continue;
|
||||||
|
var lang = dirs[l];
|
||||||
|
var values = fs.readFileSync(src + 'i18n/' + lang + '/flat.txt').toString().split('\n');
|
||||||
|
langs[lang] = {};
|
||||||
|
keys.forEach(function (word, i) {
|
||||||
|
langs[lang][word] = values[i].replace(/<\/ i>/g, '</i>').replace(/<\/ b>/g, '</b>').replace(/<\/ span>/g, '</span>').replace(/% s/g, ' %s');
|
||||||
|
});
|
||||||
|
|
||||||
|
var words = langs[lang];
|
||||||
|
for (var word in words) {
|
||||||
|
if (words.hasOwnProperty(word)) {
|
||||||
|
bigOne[word] = bigOne[word] || {};
|
||||||
|
if (words[word] !== EMPTY) {
|
||||||
|
bigOne[word][lang] = words[word];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// read actual words.js
|
||||||
|
var aWords = readWordJs();
|
||||||
|
|
||||||
|
var temporaryIgnore = ['pt', 'fr', 'nl', 'flat.txt'];
|
||||||
|
if (aWords) {
|
||||||
|
// Merge words together
|
||||||
|
for (var w in aWords) {
|
||||||
|
if (aWords.hasOwnProperty(w)) {
|
||||||
|
if (!bigOne[w]) {
|
||||||
|
console.warn('Take from actual words.js: ' + w);
|
||||||
|
bigOne[w] = aWords[w]
|
||||||
|
}
|
||||||
|
dirs.forEach(function (lang) {
|
||||||
|
if (temporaryIgnore.indexOf(lang) !== -1) return;
|
||||||
|
if (!bigOne[w][lang]) {
|
||||||
|
console.warn('Missing "' + lang + '": ' + w);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
writeWordJs(bigOne, src);
|
||||||
|
}
|
||||||
|
function languages2words(src) {
|
||||||
|
var dirs = fs.readdirSync(src + 'i18n/');
|
||||||
|
var langs = {};
|
||||||
|
var bigOne = {};
|
||||||
|
var order = Object.keys(languages);
|
||||||
|
dirs.sort(function (a, b) {
|
||||||
|
var posA = order.indexOf(a);
|
||||||
|
var posB = order.indexOf(b);
|
||||||
|
if (posA === -1 && posB === -1) {
|
||||||
|
if (a > b) return 1;
|
||||||
|
if (a < b) return -1;
|
||||||
|
return 0;
|
||||||
|
} else if (posA === -1) {
|
||||||
|
return -1;
|
||||||
|
} else if (posB === -1) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
if (posA > posB) return 1;
|
||||||
|
if (posA < posB) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (var l = 0; l < dirs.length; l++) {
|
||||||
|
if (dirs[l] === 'flat.txt') continue;
|
||||||
|
var lang = dirs[l];
|
||||||
|
langs[lang] = fs.readFileSync(src + 'i18n/' + lang + '/translations.json').toString();
|
||||||
|
langs[lang] = JSON.parse(langs[lang]);
|
||||||
|
var words = langs[lang];
|
||||||
|
for (var word in words) {
|
||||||
|
if (words.hasOwnProperty(word)) {
|
||||||
|
bigOne[word] = bigOne[word] || {};
|
||||||
|
if (words[word] !== EMPTY) {
|
||||||
|
bigOne[word][lang] = words[word];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// read actual words.js
|
||||||
|
var aWords = readWordJs();
|
||||||
|
|
||||||
|
var temporaryIgnore = ['pt', 'fr', 'nl', 'it'];
|
||||||
|
if (aWords) {
|
||||||
|
// Merge words together
|
||||||
|
for (var w in aWords) {
|
||||||
|
if (aWords.hasOwnProperty(w)) {
|
||||||
|
if (!bigOne[w]) {
|
||||||
|
console.warn('Take from actual words.js: ' + w);
|
||||||
|
bigOne[w] = aWords[w]
|
||||||
|
}
|
||||||
|
dirs.forEach(function (lang) {
|
||||||
|
if (temporaryIgnore.indexOf(lang) !== -1) return;
|
||||||
|
if (!bigOne[w][lang]) {
|
||||||
|
console.warn('Missing "' + lang + '": ' + w);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
writeWordJs(bigOne, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
gulp.task('adminWords2languages', function (done) {
|
||||||
|
words2languages('./admin/');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('adminWords2languagesFlat', function (done) {
|
||||||
|
words2languagesFlat('./admin/');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('adminLanguagesFlat2words', function (done) {
|
||||||
|
languagesFlat2words('./admin/');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('adminLanguages2words', function (done) {
|
||||||
|
languages2words('./admin/');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
gulp.task('updatePackages', function (done) {
|
||||||
|
iopackage.common.version = pkg.version;
|
||||||
|
iopackage.common.news = iopackage.common.news || {};
|
||||||
|
if (!iopackage.common.news[pkg.version]) {
|
||||||
|
var news = iopackage.common.news;
|
||||||
|
var newNews = {};
|
||||||
|
|
||||||
|
newNews[pkg.version] = {
|
||||||
|
en: 'news',
|
||||||
|
de: 'neues',
|
||||||
|
ru: 'новое'
|
||||||
|
};
|
||||||
|
iopackage.common.news = Object.assign(newNews, news);
|
||||||
|
}
|
||||||
|
fs.writeFileSync('io-package.json', JSON.stringify(iopackage, null, 4));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('updateReadme', function (done) {
|
||||||
|
var readme = fs.readFileSync('README.md').toString();
|
||||||
|
var pos = readme.indexOf('## Changelog\n');
|
||||||
|
if (pos !== -1) {
|
||||||
|
var readmeStart = readme.substring(0, pos + '## Changelog\n'.length);
|
||||||
|
var readmeEnd = readme.substring(pos + '## Changelog\n'.length);
|
||||||
|
|
||||||
|
if (readme.indexOf(version) === -1) {
|
||||||
|
var timestamp = new Date();
|
||||||
|
var date = timestamp.getFullYear() + '-' +
|
||||||
|
('0' + (timestamp.getMonth() + 1).toString(10)).slice(-2) + '-' +
|
||||||
|
('0' + (timestamp.getDate()).toString(10)).slice(-2);
|
||||||
|
|
||||||
|
var news = '';
|
||||||
|
if (iopackage.common.news && iopackage.common.news[pkg.version]) {
|
||||||
|
news += '* ' + iopackage.common.news[pkg.version].en;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync('README.md', readmeStart + '### ' + version + ' (' + date + ')\n' + (news ? news + '\n\n' : '\n') + readmeEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('default', ['updatePackages', 'updateReadme']);
|
111
io-package.json
Normal file
111
io-package.json
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"name": "proxy",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"news": {
|
||||||
|
"1.0.3": {
|
||||||
|
"en": "Newer mime version used",
|
||||||
|
"de": "Neuere Mime-Version verwendet",
|
||||||
|
"ru": "Используется новая версия mime",
|
||||||
|
"pt": "Versão mais recente de mímica usada",
|
||||||
|
"nl": "Nieuwere mime-versie gebruikt",
|
||||||
|
"fr": "Nouvelle version mime utilisée",
|
||||||
|
"it": "È stata utilizzata la versione più recente di mimo",
|
||||||
|
"es": "Nueva versión mime utilizada",
|
||||||
|
"pl": "Zastosowano nowszą wersję mime"
|
||||||
|
},
|
||||||
|
"1.0.2": {
|
||||||
|
"en": "URI was decoded for usage of special chars in password and login",
|
||||||
|
"de": "URI wurde für die Verwendung spezieller Zeichen in Passwort und Login decodiert",
|
||||||
|
"ru": "URI был декодирован для использования специальных символов в пароле и логине",
|
||||||
|
"pt": "URI foi decodificado para uso de caracteres especiais em senha e login",
|
||||||
|
"nl": "URI is gedecodeerd voor het gebruik van speciale tekens in wachtwoord en aanmelding",
|
||||||
|
"fr": "URI a été décodé pour l'utilisation de caractères spéciaux dans le mot de passe et la connexion",
|
||||||
|
"it": "L'URI è stato decodificato per l'utilizzo di caratteri speciali in password e login",
|
||||||
|
"es": "El URI se decodificó para el uso de caracteres especiales en la contraseña y el inicio de sesión",
|
||||||
|
"pl": "Identyfikator URI został zdekodowany w celu użycia specjalnych znaków w haśle i logowaniu"
|
||||||
|
},
|
||||||
|
"1.0.1": {
|
||||||
|
"en": "Fixed error: after 10 timeouts the web cam was never reachable\nReady for Admin3",
|
||||||
|
"de": "Fehler behoben: Nach 10 Timeouts war die Webcam nie erreichbar\nBereit für Admin3",
|
||||||
|
"ru": "Исправлена ошибка: после 10 тайм-аутов веб-камера была недоступна\nГотово для Admin3",
|
||||||
|
"pt": "Erro corrigido: após 10 temporizações, a web cam nunca foi alcançada\nPronto para Admin3",
|
||||||
|
"nl": "Vaste fout: na 10 time-outs was de webcam nooit bereikbaar\nKlaar voor Admin3",
|
||||||
|
"fr": "Correction d'une erreur: après 10 timeouts, la webcam n'a jamais été accessible\nPrêt pour Admin3",
|
||||||
|
"it": "Risolto errore: dopo 10 timeout la web cam non era mai raggiungibile\nPronto per Admin3",
|
||||||
|
"es": "Error reparado: después de 10 tiempos de espera la cámara web nunca fue alcanzable\nListo para Admin3",
|
||||||
|
"pl": "Naprawiono błąd: po 10 przekroczeniach czasu kamera internetowa nigdy nie była dostępna\nGotowy na Admin3"
|
||||||
|
},
|
||||||
|
"1.0.0": {
|
||||||
|
"en": "do not allow the error generation to fast",
|
||||||
|
"de": "Erlaube nicht die Fehler zu oft zu generieren",
|
||||||
|
"ru": "Запрет на слишком частые ошибки запросов"
|
||||||
|
},
|
||||||
|
"0.2.0": {
|
||||||
|
"en": "fix run-mode",
|
||||||
|
"de": "Korrigiere Run-Modus",
|
||||||
|
"ru": "Исправлен режим запуска"
|
||||||
|
},
|
||||||
|
"0.1.0": {
|
||||||
|
"en": "Inital version",
|
||||||
|
"de": "Erste Version",
|
||||||
|
"ru": "Первая версия"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "proxy",
|
||||||
|
"desc": {
|
||||||
|
"en": "This adapter allows to reach other HTTP servers (like WEB CAM) in the same web server",
|
||||||
|
"de": "Dieser Adapter ermöglicht es, andere HTTP-Server (wie WEB CAM) auf demselben Webserver zu erreichen",
|
||||||
|
"ru": "Этот адаптер позволяет опрашивать другие HTTP-серверы (например, WEB CAM) на одном и том же веб-сервере",
|
||||||
|
"pt": "Este adaptador permite alcançar outros servidores HTTP (como WEB CAM) no mesmo servidor web",
|
||||||
|
"nl": "Met deze adapter kunnen andere HTTP-servers (zoals WEB CAM) op dezelfde webserver worden bereikt",
|
||||||
|
"fr": "Cet adaptateur permet d'atteindre d'autres serveurs HTTP (comme WEB CAM) sur le même serveur web",
|
||||||
|
"it": "Questo adattatore consente di raggiungere altri server HTTP (come WEB CAM) nello stesso server web",
|
||||||
|
"es": "Este adaptador permite llegar a otros servidores HTTP (como WEB CAM) en el mismo servidor web",
|
||||||
|
"pl": "Ten adapter umożliwia dotarcie do innych serwerów HTTP (takich jak WEB CAM) na tym samym serwerze internetowym"
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
"bluefox <dogafox@gmail.com>"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"platform": "Javascript/Node.js",
|
||||||
|
"mode": "extension",
|
||||||
|
"loglevel": "info",
|
||||||
|
"icon": "proxy.png",
|
||||||
|
"webExtension": "lib/proxy.js",
|
||||||
|
"readme": "https://git.spacen.net/yunkong2/yunkong2.proxy/blob/master/README.md",
|
||||||
|
"keywords": [
|
||||||
|
"web",
|
||||||
|
"proxy",
|
||||||
|
"communication"
|
||||||
|
],
|
||||||
|
"materialize": true,
|
||||||
|
"enabled": true,
|
||||||
|
"extIcon": "https://git.spacen.net/yunkong2/yunkong2.proxy/master/admin/proxy.png",
|
||||||
|
"type": "network",
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"js-controller": ">=0.15.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"native": {
|
||||||
|
"route": "",
|
||||||
|
"webInstance": "",
|
||||||
|
"errorTimeout": 10000,
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"regex": "sonos/",
|
||||||
|
"url": "http://localhost:8083"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": "api/",
|
||||||
|
"url": "http://localhost:8084"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regex": "node-red/",
|
||||||
|
"url": "http://localhost:1880"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
212
lib/proxy.js
Normal file
212
lib/proxy.js
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/* jshint -W097 */// jshint strict:false
|
||||||
|
/*jslint node: true */
|
||||||
|
/*jshint -W061 */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy class
|
||||||
|
*
|
||||||
|
* From settings used only secure, auth and crossDomain
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @param {object} server http or https node.js object
|
||||||
|
* @param {object} webSettings settings of the web server, like <pre><code>{secure: settings.secure, port: settings.port}</code></pre>
|
||||||
|
* @param {object} adapter web adapter object
|
||||||
|
* @param {object} instanceSettings instance object with common and native
|
||||||
|
* @param {object} app express application
|
||||||
|
* @return {object} object instance
|
||||||
|
*/
|
||||||
|
function Proxy(server, webSettings, adapter, instanceSettings, app) {
|
||||||
|
if (!(this instanceof Proxy)) return new Proxy(server, webSettings, adapter, instanceSettings, app);
|
||||||
|
|
||||||
|
this.app = app;
|
||||||
|
this.adapter = adapter;
|
||||||
|
this.settings = webSettings;
|
||||||
|
this.config = instanceSettings ? instanceSettings.native : {};
|
||||||
|
this.namespace = instanceSettings ? instanceSettings._id.substring('system.adapter.'.length) : 'simple-api';
|
||||||
|
this.request = {};
|
||||||
|
var that = this;
|
||||||
|
var proxy;
|
||||||
|
var path;
|
||||||
|
var fs;
|
||||||
|
|
||||||
|
this.config.errorTimeout = parseInt(this.config.errorTimeout, 10) || 10000;
|
||||||
|
if (this.config.errorTimeout < 1000) this.config.errorTimeout = 1000;
|
||||||
|
|
||||||
|
this.config.route = this.config.route || (that.namespace + '/');
|
||||||
|
var mime = require('mime');
|
||||||
|
|
||||||
|
this.interval = setInterval(function () {
|
||||||
|
var now = Date.now();
|
||||||
|
for (var e = 0; e < that.config.rules.length; e++) {
|
||||||
|
var rule = that.config.rules[e];
|
||||||
|
if (!rule.request) continue;
|
||||||
|
|
||||||
|
for (var u = rule.request.length - 1; u >= 0; u--) {
|
||||||
|
if (rule.request[u].res.finished) {
|
||||||
|
rule.request.splice(u, 1);
|
||||||
|
} else
|
||||||
|
if (now - rule.request[u].ts > rule.timeout) {
|
||||||
|
rule.lastError = new Date().getTime();
|
||||||
|
rule.lastErrorText = 'timeout';
|
||||||
|
|
||||||
|
adapter.log.error('[proxy] Cannot get "' + rule.request[u].req.url + '": timeout');
|
||||||
|
rule.request.splice(u, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
function finishReq(rule, req, res) {
|
||||||
|
if (rule.request) {
|
||||||
|
var entry = rule.request.find(function (e) {
|
||||||
|
return e.res === res;
|
||||||
|
});
|
||||||
|
if (!entry) {
|
||||||
|
adapter.log.error('Request "' + req.url + '" not found in requests');
|
||||||
|
} else {
|
||||||
|
var ppos = rule.request.indexOf(entry);
|
||||||
|
rule.request.splice(ppos, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adapter.log.error('URL "' + url + '" not found in requests');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function oneRule(rule) {
|
||||||
|
adapter.log.info('Install extension on /' + that.config.route + rule.regex);
|
||||||
|
|
||||||
|
rule.timeout = parseInt(rule.timeout, 10) || that.config.errorTimeout;
|
||||||
|
|
||||||
|
if (rule.url.match(/^https?:\/\//)) {
|
||||||
|
proxy = proxy || require('http-proxy-middleware');
|
||||||
|
var options = {
|
||||||
|
target: rule.url,
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: false,
|
||||||
|
proxyTimeout: rule.timeout,
|
||||||
|
xfwd: true,
|
||||||
|
onError: function (err, req, res/* , url*/) {
|
||||||
|
rule.lastError = new Date().getTime();
|
||||||
|
rule.lastErrorText = err.toString();
|
||||||
|
|
||||||
|
adapter.log.error('[proxy] Cannot get "' + rule.url + '": ' + err);
|
||||||
|
if (!res.finished) {
|
||||||
|
try {
|
||||||
|
if (typeof res.status === 'function') {
|
||||||
|
res.status(500).send(err);
|
||||||
|
} else if (typeof res.send === 'function') {
|
||||||
|
res.send(err);
|
||||||
|
} else {
|
||||||
|
adapter.log.error('[proxy] Cannot response');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
adapter.log.error('[proxy] Cannot response: ' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishReq(rule, req, res);
|
||||||
|
},
|
||||||
|
onProxyReq: function (req /* , origReq, res, options */) {
|
||||||
|
adapter.log.debug(req.method + ': ' + rule.url + req.path);
|
||||||
|
},
|
||||||
|
onProxyRes: function (req, reqOrig, res) {
|
||||||
|
finishReq(rule, reqOrig, res);
|
||||||
|
rule.lastError = 0;
|
||||||
|
rule.lastErrorText = '';
|
||||||
|
adapter.log.debug('[proxy] Response for ' + reqOrig.url + ': ' + req.statusCode + '(' + req.statusMessage + ')');
|
||||||
|
},
|
||||||
|
/*onProxyReqWs: function () {
|
||||||
|
console.log('onProxyReqWs');
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpen: function () {
|
||||||
|
console.log('onOpen');
|
||||||
|
},
|
||||||
|
onClose: function () {
|
||||||
|
console.log('onClose');
|
||||||
|
},*/
|
||||||
|
pathRewrite: {}
|
||||||
|
};
|
||||||
|
var m = rule.url.match(/^https?:\/\/(.+)@/);
|
||||||
|
if (m && m[1] && m[1].indexOf(':') !== -1) {
|
||||||
|
rule.url = rule.url.replace(m[1] + '@', '');
|
||||||
|
options.auth = decodeURIComponent(m[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.pathRewrite['^/' + that.config.route + rule.regex] = '/';
|
||||||
|
|
||||||
|
rule.handler = proxy(options);
|
||||||
|
that.app.use('/' + that.config.route + rule.regex, function (req, res, next) {
|
||||||
|
var now = Date.now();
|
||||||
|
if (rule.lastError && ((now - rule.lastError) < that.config.errorTimeout)) {
|
||||||
|
res.status(404).send('[proxy] Cannot read file: ' + rule.lastErrorText);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.request && rule.request.length > 10) {
|
||||||
|
res.status(500).send('[proxy] too many parallel requests for "' + req.url + '"!');
|
||||||
|
adapter.log.warn('[proxy] too many parallel requests for "' + req.url + '"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rule.request = rule.request || [];
|
||||||
|
rule.request.push({req: req, res: res, ts: now});
|
||||||
|
|
||||||
|
rule.handler(req, res, next);
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
path = path || require('path');
|
||||||
|
fs = fs || require('fs');
|
||||||
|
rule.url = rule.url.replace(/\\/g, '/');
|
||||||
|
if (rule.url[0] !== '/' && !rule.url.match(/^[A-Za-z]:/)) {
|
||||||
|
rule.url = path.normalize(__dirname + '/../../' + rule.url);
|
||||||
|
}
|
||||||
|
// file handler
|
||||||
|
that.app.use('/' + that.config.route + rule.regex, function (req, res, next) {
|
||||||
|
var fileName = rule.url + req.url;
|
||||||
|
if (fs.existsSync(fileName)) {
|
||||||
|
var stat = fs.statSync(fileName);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
var dirs = fs.readdirSync(fileName);
|
||||||
|
|
||||||
|
var text = '';
|
||||||
|
dirs.sort();
|
||||||
|
for (var d = 0; d < dirs.length; d++) {
|
||||||
|
text += (text ? '<br>' : '') + '<a href="./' + dirs[d] + '">' + dirs[d] + '</a>';
|
||||||
|
}
|
||||||
|
res.set('Content-Type', 'text/html');
|
||||||
|
res.status(200).send('<html><head><title>' + fileName + '</title></head><body>' + text + '</body>');
|
||||||
|
} else {
|
||||||
|
var data;
|
||||||
|
try {
|
||||||
|
data = fs.readFileSync(fileName);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send('[proxy] Cannot read file: ' + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.contentType(mime.lookup(path.extname(fileName).substring(1)));
|
||||||
|
res.status(200).send(data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(404).send('[proxy] File "' + fileName +'" not found.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.destroy = function () {
|
||||||
|
if (this.interval) {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var __construct = (function () {
|
||||||
|
for (var e = 0; e < this.config.rules.length; e++) {
|
||||||
|
oneRule(this.config.rules[e]);
|
||||||
|
}
|
||||||
|
}.bind(this))();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Proxy;
|
37
package.json
Normal file
37
package.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "yunkong2.proxy",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"description": "Proxy for WEB server.",
|
||||||
|
"author": {
|
||||||
|
"name": "bluefox",
|
||||||
|
"email": "dogafox@gmail.com"
|
||||||
|
},
|
||||||
|
"homepage": "https://git.spacen.net/yunkong2/yunkong2.proxy",
|
||||||
|
"keywords": [
|
||||||
|
"yunkong2",
|
||||||
|
"web",
|
||||||
|
"proxy"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.spacen.net/yunkong2/yunkong2.proxy"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"http-proxy-middleware": "^0.18.0",
|
||||||
|
"mime": "^2.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "^3.9.1",
|
||||||
|
"mocha": "^5.2.0",
|
||||||
|
"chai": "^4.1.2",
|
||||||
|
"request": "^2.87.0"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://git.spacen.net/yunkong2/yunkong2.proxy/issues"
|
||||||
|
},
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node node_modules/mocha/bin/mocha --exit"
|
||||||
|
},
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
728
test/lib/setup.js
Normal file
728
test/lib/setup.js
Normal file
@ -0,0 +1,728 @@
|
|||||||
|
/* jshint -W097 */// jshint strict:false
|
||||||
|
/*jslint node: true */
|
||||||
|
// check if tmp directory exists
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var child_process = require('child_process');
|
||||||
|
var rootDir = path.normalize(__dirname + '/../../');
|
||||||
|
var pkg = require(rootDir + 'package.json');
|
||||||
|
var debug = typeof v8debug === 'object';
|
||||||
|
pkg.main = pkg.main || 'main.js';
|
||||||
|
|
||||||
|
var adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/');
|
||||||
|
adapterName = adapterName[adapterName.length - 2];
|
||||||
|
var adapterStarted = false;
|
||||||
|
|
||||||
|
function getAppName() {
|
||||||
|
var parts = __dirname.replace(/\\/g, '/').split('/');
|
||||||
|
return parts[parts.length - 3].split('.')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var appName = getAppName().toLowerCase();
|
||||||
|
|
||||||
|
var objects;
|
||||||
|
var states;
|
||||||
|
|
||||||
|
var pid = null;
|
||||||
|
|
||||||
|
function copyFileSync(source, target) {
|
||||||
|
|
||||||
|
var targetFile = target;
|
||||||
|
|
||||||
|
//if target is a directory a new file with the same name will be created
|
||||||
|
if (fs.existsSync(target)) {
|
||||||
|
if ( fs.lstatSync( target ).isDirectory() ) {
|
||||||
|
targetFile = path.join(target, path.basename(source));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(targetFile, fs.readFileSync(source));
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log("file copy error: " +source +" -> " + targetFile + " (error ignored)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyFolderRecursiveSync(source, target, ignore) {
|
||||||
|
var files = [];
|
||||||
|
|
||||||
|
var base = path.basename(source);
|
||||||
|
if (base === adapterName) {
|
||||||
|
base = pkg.name;
|
||||||
|
}
|
||||||
|
//check if folder needs to be created or integrated
|
||||||
|
var targetFolder = path.join(target, base);
|
||||||
|
if (!fs.existsSync(targetFolder)) {
|
||||||
|
fs.mkdirSync(targetFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy
|
||||||
|
if (fs.lstatSync(source).isDirectory()) {
|
||||||
|
files = fs.readdirSync(source);
|
||||||
|
files.forEach(function (file) {
|
||||||
|
if (ignore && ignore.indexOf(file) !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var curSource = path.join(source, file);
|
||||||
|
var curTarget = path.join(targetFolder, file);
|
||||||
|
if (fs.lstatSync(curSource).isDirectory()) {
|
||||||
|
// ignore grunt files
|
||||||
|
if (file.indexOf('grunt') !== -1) return;
|
||||||
|
if (file === 'chai') return;
|
||||||
|
if (file === 'mocha') return;
|
||||||
|
copyFolderRecursiveSync(curSource, targetFolder, ignore);
|
||||||
|
} else {
|
||||||
|
copyFileSync(curSource, curTarget);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(rootDir + 'tmp')) {
|
||||||
|
fs.mkdirSync(rootDir + 'tmp');
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeOriginalFiles() {
|
||||||
|
console.log('Store original files...');
|
||||||
|
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||||
|
|
||||||
|
var f = fs.readFileSync(dataDir + 'objects.json');
|
||||||
|
var objects = JSON.parse(f.toString());
|
||||||
|
if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) {
|
||||||
|
objects['system.adapter.admin.0'].common.enabled = false;
|
||||||
|
}
|
||||||
|
if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) {
|
||||||
|
objects['system.adapter.admin.1'].common.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects));
|
||||||
|
try {
|
||||||
|
f = fs.readFileSync(dataDir + 'states.json');
|
||||||
|
fs.writeFileSync(dataDir + 'states.json.original', f);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log('no states.json found - ignore');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreOriginalFiles() {
|
||||||
|
console.log('restoreOriginalFiles...');
|
||||||
|
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||||
|
|
||||||
|
var f = fs.readFileSync(dataDir + 'objects.json.original');
|
||||||
|
fs.writeFileSync(dataDir + 'objects.json', f);
|
||||||
|
try {
|
||||||
|
f = fs.readFileSync(dataDir + 'states.json.original');
|
||||||
|
fs.writeFileSync(dataDir + 'states.json', f);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log('no states.json.original found - ignore');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIsAdapterInstalled(cb, counter, customName) {
|
||||||
|
customName = customName || pkg.name.split('.').pop();
|
||||||
|
counter = counter || 0;
|
||||||
|
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||||
|
console.log('checkIsAdapterInstalled...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
var f = fs.readFileSync(dataDir + 'objects.json');
|
||||||
|
var objects = JSON.parse(f.toString());
|
||||||
|
if (objects['system.adapter.' + customName + '.0']) {
|
||||||
|
console.log('checkIsAdapterInstalled: ready!');
|
||||||
|
setTimeout(function () {
|
||||||
|
if (cb) cb();
|
||||||
|
}, 100);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.warn('checkIsAdapterInstalled: still not ready');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter > 20) {
|
||||||
|
console.error('checkIsAdapterInstalled: Cannot install!');
|
||||||
|
if (cb) cb('Cannot install');
|
||||||
|
} else {
|
||||||
|
console.log('checkIsAdapterInstalled: wait...');
|
||||||
|
setTimeout(function() {
|
||||||
|
checkIsAdapterInstalled(cb, counter + 1);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIsControllerInstalled(cb, counter) {
|
||||||
|
counter = counter || 0;
|
||||||
|
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||||
|
|
||||||
|
console.log('checkIsControllerInstalled...');
|
||||||
|
try {
|
||||||
|
var f = fs.readFileSync(dataDir + 'objects.json');
|
||||||
|
var objects = JSON.parse(f.toString());
|
||||||
|
if (objects['system.adapter.admin.0']) {
|
||||||
|
console.log('checkIsControllerInstalled: installed!');
|
||||||
|
setTimeout(function () {
|
||||||
|
if (cb) cb();
|
||||||
|
}, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter > 20) {
|
||||||
|
console.log('checkIsControllerInstalled: Cannot install!');
|
||||||
|
if (cb) cb('Cannot install');
|
||||||
|
} else {
|
||||||
|
console.log('checkIsControllerInstalled: wait...');
|
||||||
|
setTimeout(function() {
|
||||||
|
checkIsControllerInstalled(cb, counter + 1);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function installAdapter(customName, cb) {
|
||||||
|
if (typeof customName === 'function') {
|
||||||
|
cb = customName;
|
||||||
|
customName = null;
|
||||||
|
}
|
||||||
|
customName = customName || pkg.name.split('.').pop();
|
||||||
|
console.log('Install adapter...');
|
||||||
|
var startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js';
|
||||||
|
// make first install
|
||||||
|
if (debug) {
|
||||||
|
child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', {
|
||||||
|
cwd: rootDir + 'tmp',
|
||||||
|
stdio: [0, 1, 2]
|
||||||
|
});
|
||||||
|
checkIsAdapterInstalled(function (error) {
|
||||||
|
if (error) console.error(error);
|
||||||
|
console.log('Adapter installed.');
|
||||||
|
if (cb) cb();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// add controller
|
||||||
|
var _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], {
|
||||||
|
cwd: rootDir + 'tmp',
|
||||||
|
stdio: [0, 1, 2, 'ipc']
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForEnd(_pid, function () {
|
||||||
|
checkIsAdapterInstalled(function (error) {
|
||||||
|
if (error) console.error(error);
|
||||||
|
console.log('Adapter installed.');
|
||||||
|
if (cb) cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForEnd(_pid, cb) {
|
||||||
|
if (!_pid) {
|
||||||
|
cb(-1, -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pid.on('exit', function (code, signal) {
|
||||||
|
if (_pid) {
|
||||||
|
_pid = null;
|
||||||
|
cb(code, signal);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_pid.on('close', function (code, signal) {
|
||||||
|
if (_pid) {
|
||||||
|
_pid = null;
|
||||||
|
cb(code, signal);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function installJsController(cb) {
|
||||||
|
console.log('installJsController...');
|
||||||
|
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') ||
|
||||||
|
!fs.existsSync(rootDir + 'tmp/' + appName + '-data')) {
|
||||||
|
// try to detect appName.js-controller in node_modules/appName.js-controller
|
||||||
|
// travis CI installs js-controller into node_modules
|
||||||
|
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) {
|
||||||
|
console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"');
|
||||||
|
// copy all
|
||||||
|
// stop controller
|
||||||
|
console.log('Stop controller if running...');
|
||||||
|
var _pid;
|
||||||
|
if (debug) {
|
||||||
|
// start controller
|
||||||
|
_pid = child_process.exec('node ' + appName + '.js stop', {
|
||||||
|
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
|
||||||
|
stdio: [0, 1, 2]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_pid = child_process.fork(appName + '.js', ['stop'], {
|
||||||
|
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
|
||||||
|
stdio: [0, 1, 2, 'ipc']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForEnd(_pid, function () {
|
||||||
|
// copy all files into
|
||||||
|
if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp');
|
||||||
|
if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules');
|
||||||
|
|
||||||
|
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){
|
||||||
|
console.log('Copy js-controller...');
|
||||||
|
copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Setup js-controller...');
|
||||||
|
var __pid;
|
||||||
|
if (debug) {
|
||||||
|
// start controller
|
||||||
|
_pid = child_process.exec('node ' + appName + '.js setup first --console', {
|
||||||
|
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||||
|
stdio: [0, 1, 2]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
__pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], {
|
||||||
|
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||||
|
stdio: [0, 1, 2, 'ipc']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
waitForEnd(__pid, function () {
|
||||||
|
checkIsControllerInstalled(function () {
|
||||||
|
// change ports for object and state DBs
|
||||||
|
var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
|
||||||
|
config.objects.port = 19001;
|
||||||
|
config.states.port = 19000;
|
||||||
|
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
|
||||||
|
console.log('Setup finished.');
|
||||||
|
|
||||||
|
copyAdapterToController();
|
||||||
|
|
||||||
|
installAdapter(function () {
|
||||||
|
storeOriginalFiles();
|
||||||
|
if (cb) cb(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// check if port 9000 is free, else admin adapter will be added to running instance
|
||||||
|
var client = new require('net').Socket();
|
||||||
|
client.connect(9000, '127.0.0.1', function() {
|
||||||
|
console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
client.destroy();
|
||||||
|
if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) {
|
||||||
|
console.log('installJsController: no js-controller => install from git');
|
||||||
|
|
||||||
|
child_process.execSync('npm install https://git.spacen.net/' + appName + '/' + appName + '.js-controller/tarball/master --prefix ./ --production', {
|
||||||
|
cwd: rootDir + 'tmp/',
|
||||||
|
stdio: [0, 1, 2]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('Setup js-controller...');
|
||||||
|
var __pid;
|
||||||
|
if (debug) {
|
||||||
|
// start controller
|
||||||
|
child_process.exec('node ' + appName + '.js setup first', {
|
||||||
|
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||||
|
stdio: [0, 1, 2]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
child_process.fork(appName + '.js', ['setup', 'first'], {
|
||||||
|
cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller',
|
||||||
|
stdio: [0, 1, 2, 'ipc']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let npm install admin and run setup
|
||||||
|
checkIsControllerInstalled(function () {
|
||||||
|
var _pid;
|
||||||
|
|
||||||
|
if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) {
|
||||||
|
_pid = child_process.fork(appName + '.js', ['stop'], {
|
||||||
|
cwd: rootDir + 'node_modules/' + appName + '.js-controller',
|
||||||
|
stdio: [0, 1, 2, 'ipc']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForEnd(_pid, function () {
|
||||||
|
// change ports for object and state DBs
|
||||||
|
var config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json');
|
||||||
|
config.objects.port = 19001;
|
||||||
|
config.states.port = 19000;
|
||||||
|
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
copyAdapterToController();
|
||||||
|
|
||||||
|
installAdapter(function () {
|
||||||
|
storeOriginalFiles();
|
||||||
|
if (cb) cb(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTimeout(function () {
|
||||||
|
console.log('installJsController: js-controller installed');
|
||||||
|
if (cb) cb(false);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyAdapterToController() {
|
||||||
|
console.log('Copy adapter...');
|
||||||
|
// Copy adapter to tmp/node_modules/appName.adapter
|
||||||
|
copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']);
|
||||||
|
console.log('Adapter copied.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearControllerLog() {
|
||||||
|
var dirPath = rootDir + 'tmp/log';
|
||||||
|
var files;
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(dirPath)) {
|
||||||
|
console.log('Clear controller log...');
|
||||||
|
files = fs.readdirSync(dirPath);
|
||||||
|
} else {
|
||||||
|
console.log('Create controller log directory...');
|
||||||
|
files = [];
|
||||||
|
fs.mkdirSync(dirPath);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.error('Cannot read "' + dirPath + '"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (files.length > 0) {
|
||||||
|
try {
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
var filePath = dirPath + '/' + files[i];
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
}
|
||||||
|
console.log('Controller log cleared');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('cannot clear log: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearDB() {
|
||||||
|
var dirPath = rootDir + 'tmp/yunkong2-data/sqlite';
|
||||||
|
var files;
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(dirPath)) {
|
||||||
|
console.log('Clear sqlite DB...');
|
||||||
|
files = fs.readdirSync(dirPath);
|
||||||
|
} else {
|
||||||
|
console.log('Create controller log directory...');
|
||||||
|
files = [];
|
||||||
|
fs.mkdirSync(dirPath);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.error('Cannot read "' + dirPath + '"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (files.length > 0) {
|
||||||
|
try {
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
var filePath = dirPath + '/' + files[i];
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
}
|
||||||
|
console.log('Clear sqlite DB');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('cannot clear DB: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupController(cb) {
|
||||||
|
installJsController(function (isInited) {
|
||||||
|
clearControllerLog();
|
||||||
|
clearDB();
|
||||||
|
|
||||||
|
if (!isInited) {
|
||||||
|
restoreOriginalFiles();
|
||||||
|
copyAdapterToController();
|
||||||
|
}
|
||||||
|
// read system.config object
|
||||||
|
var dataDir = rootDir + 'tmp/' + appName + '-data/';
|
||||||
|
|
||||||
|
var objs;
|
||||||
|
try {
|
||||||
|
objs = fs.readFileSync(dataDir + 'objects.json');
|
||||||
|
objs = JSON.parse(objs);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log('ERROR reading/parsing system configuration. Ignore');
|
||||||
|
objs = {'system.config': {}};
|
||||||
|
}
|
||||||
|
if (!objs || !objs['system.config']) {
|
||||||
|
objs = {'system.config': {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) cb(objs['system.config']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAdapter(objects, states, callback) {
|
||||||
|
if (adapterStarted) {
|
||||||
|
console.log('Adapter already started ...');
|
||||||
|
if (callback) callback(objects, states);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
adapterStarted = true;
|
||||||
|
console.log('startAdapter...');
|
||||||
|
if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) {
|
||||||
|
try {
|
||||||
|
if (debug) {
|
||||||
|
// start controller
|
||||||
|
pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', {
|
||||||
|
cwd: rootDir + 'tmp',
|
||||||
|
stdio: [0, 1, 2]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// start controller
|
||||||
|
pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], {
|
||||||
|
cwd: rootDir + 'tmp',
|
||||||
|
stdio: [0, 1, 2, 'ipc']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(JSON.stringify(error));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main);
|
||||||
|
}
|
||||||
|
if (callback) callback(objects, states);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startController(isStartAdapter, onObjectChange, onStateChange, callback) {
|
||||||
|
if (typeof isStartAdapter === 'function') {
|
||||||
|
callback = onStateChange;
|
||||||
|
onStateChange = onObjectChange;
|
||||||
|
onObjectChange = isStartAdapter;
|
||||||
|
isStartAdapter = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onStateChange === undefined) {
|
||||||
|
callback = onObjectChange;
|
||||||
|
onObjectChange = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid) {
|
||||||
|
console.error('Controller is already started!');
|
||||||
|
} else {
|
||||||
|
console.log('startController...');
|
||||||
|
adapterStarted = false;
|
||||||
|
var isObjectConnected;
|
||||||
|
var isStatesConnected;
|
||||||
|
|
||||||
|
var Objects = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/objects/objectsInMemServer');
|
||||||
|
objects = new Objects({
|
||||||
|
connection: {
|
||||||
|
"type" : "file",
|
||||||
|
"host" : "127.0.0.1",
|
||||||
|
"port" : 19001,
|
||||||
|
"user" : "",
|
||||||
|
"pass" : "",
|
||||||
|
"noFileCache": false,
|
||||||
|
"connectTimeout": 2000
|
||||||
|
},
|
||||||
|
logger: {
|
||||||
|
silly: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
debug: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
info: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
warn: function (msg) {
|
||||||
|
console.warn(msg);
|
||||||
|
},
|
||||||
|
error: function (msg) {
|
||||||
|
console.error(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
connected: function () {
|
||||||
|
isObjectConnected = true;
|
||||||
|
if (isStatesConnected) {
|
||||||
|
console.log('startController: started!');
|
||||||
|
if (isStartAdapter) {
|
||||||
|
startAdapter(objects, states, callback);
|
||||||
|
} else {
|
||||||
|
if (callback) {
|
||||||
|
callback(objects, states);
|
||||||
|
callback = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
change: onObjectChange
|
||||||
|
});
|
||||||
|
|
||||||
|
// Just open in memory DB itself
|
||||||
|
var States = require(rootDir + 'tmp/node_modules/' + appName + '.js-controller/lib/states/statesInMemServer');
|
||||||
|
states = new States({
|
||||||
|
connection: {
|
||||||
|
type: 'file',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 19000,
|
||||||
|
options: {
|
||||||
|
auth_pass: null,
|
||||||
|
retry_max_delay: 15000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logger: {
|
||||||
|
silly: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
debug: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
info: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
warn: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
error: function (msg) {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
connected: function () {
|
||||||
|
isStatesConnected = true;
|
||||||
|
if (isObjectConnected) {
|
||||||
|
console.log('startController: started!!');
|
||||||
|
if (isStartAdapter) {
|
||||||
|
startAdapter(objects, states, callback);
|
||||||
|
} else {
|
||||||
|
if (callback) {
|
||||||
|
callback(objects, states);
|
||||||
|
callback = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
change: onStateChange
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopAdapter(cb) {
|
||||||
|
if (!pid) {
|
||||||
|
console.error('Controller is not running!');
|
||||||
|
if (cb) {
|
||||||
|
setTimeout(function () {
|
||||||
|
cb(false);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adapterStarted = false;
|
||||||
|
pid.on('exit', function (code, signal) {
|
||||||
|
if (pid) {
|
||||||
|
console.log('child process terminated due to receipt of signal ' + signal);
|
||||||
|
if (cb) cb();
|
||||||
|
pid = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pid.on('close', function (code, signal) {
|
||||||
|
if (pid) {
|
||||||
|
if (cb) cb();
|
||||||
|
pid = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pid.kill('SIGTERM');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _stopController() {
|
||||||
|
if (objects) {
|
||||||
|
objects.destroy();
|
||||||
|
objects = null;
|
||||||
|
}
|
||||||
|
if (states) {
|
||||||
|
states.destroy();
|
||||||
|
states = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopController(cb) {
|
||||||
|
var timeout;
|
||||||
|
if (objects) {
|
||||||
|
console.log('Set system.adapter.' + pkg.name + '.0');
|
||||||
|
objects.setObject('system.adapter.' + pkg.name + '.0', {
|
||||||
|
common:{
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAdapter(function () {
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stopController();
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
cb(true);
|
||||||
|
cb = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
timeout = setTimeout(function () {
|
||||||
|
timeout = null;
|
||||||
|
console.log('child process NOT terminated');
|
||||||
|
|
||||||
|
_stopController();
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
cb(false);
|
||||||
|
cb = null;
|
||||||
|
}
|
||||||
|
pid = null;
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the adapter
|
||||||
|
function setAdapterConfig(common, native, instance) {
|
||||||
|
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
|
||||||
|
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
|
||||||
|
if (common) objects[id].common = common;
|
||||||
|
if (native) objects[id].native = native;
|
||||||
|
fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read config of the adapter
|
||||||
|
function getAdapterConfig(instance) {
|
||||||
|
var objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString());
|
||||||
|
var id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0);
|
||||||
|
return objects[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== undefined && module.parent) {
|
||||||
|
module.exports.getAdapterConfig = getAdapterConfig;
|
||||||
|
module.exports.setAdapterConfig = setAdapterConfig;
|
||||||
|
module.exports.startController = startController;
|
||||||
|
module.exports.stopController = stopController;
|
||||||
|
module.exports.setupController = setupController;
|
||||||
|
module.exports.stopAdapter = stopAdapter;
|
||||||
|
module.exports.startAdapter = startAdapter;
|
||||||
|
module.exports.installAdapter = installAdapter;
|
||||||
|
module.exports.appName = appName;
|
||||||
|
module.exports.adapterName = adapterName;
|
||||||
|
module.exports.adapterStarted = adapterStarted;
|
||||||
|
}
|
91
test/testPackageFiles.js
Normal file
91
test/testPackageFiles.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/* jshint -W097 */
|
||||||
|
/* jshint strict:false */
|
||||||
|
/* jslint node: true */
|
||||||
|
/* jshint expr: true */
|
||||||
|
var expect = require('chai').expect;
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
describe('Test package.json and io-package.json', function() {
|
||||||
|
it('Test package files', function (done) {
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
|
||||||
|
var ioPackage = JSON.parse(fileContentIOPackage);
|
||||||
|
|
||||||
|
var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
|
||||||
|
var npmPackage = JSON.parse(fileContentNPMPackage);
|
||||||
|
|
||||||
|
expect(ioPackage).to.be.an('object');
|
||||||
|
expect(npmPackage).to.be.an('object');
|
||||||
|
|
||||||
|
expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
|
||||||
|
expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;
|
||||||
|
|
||||||
|
expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);
|
||||||
|
|
||||||
|
if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
|
||||||
|
console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
|
||||||
|
expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;
|
||||||
|
|
||||||
|
if (ioPackage.common.name.indexOf('template') !== 0) {
|
||||||
|
if (Array.isArray(ioPackage.common.authors)) {
|
||||||
|
expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
|
||||||
|
if (ioPackage.common.authors.length === 1) {
|
||||||
|
expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
|
||||||
|
if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
|
||||||
|
console.log('WARNING: titleLang is not existing in io-package.json. Please add');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
|
||||||
|
ioPackage.common.title.indexOf('yunkong2') !== -1 ||
|
||||||
|
ioPackage.common.title.indexOf('adapter') !== -1 ||
|
||||||
|
ioPackage.common.title.indexOf('Adapter') !== -1
|
||||||
|
) {
|
||||||
|
console.log('WARNING: title contains Adapter or yunkong2. It is clear anyway, that it is adapter for yunkong2.');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioPackage.common.name.indexOf('vis-') !== 0) {
|
||||||
|
if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
|
||||||
|
console.log('WARNING: Admin3 support is missing! Please add it');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
if (ioPackage.common.materialize) {
|
||||||
|
expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
|
||||||
|
var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
|
||||||
|
if (fileContentReadme.indexOf('## Changelog') === -1) {
|
||||||
|
console.log('Warning: The README.md should have a section ## Changelog');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
|
||||||
|
if (!licenseFileExists) {
|
||||||
|
console.log('Warning: The License should also exist as LICENSE file');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
if (fileContentReadme.indexOf('## License') === -1) {
|
||||||
|
console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
30
test/testWebExtension.js
Normal file
30
test/testWebExtension.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* jshint -W097 */// jshint strict:false
|
||||||
|
/*jslint node: true */
|
||||||
|
var expect = require('chai').expect;
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
describe('Test package.json and io-package.json', function() {
|
||||||
|
it('Test package files', function (done) {
|
||||||
|
var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json');
|
||||||
|
var ioPackage = JSON.parse(fileContentIOPackage);
|
||||||
|
|
||||||
|
var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json');
|
||||||
|
var npmPackage = JSON.parse(fileContentNPMPackage);
|
||||||
|
|
||||||
|
expect(ioPackage).to.be.an('object');
|
||||||
|
expect(npmPackage).to.be.an('object');
|
||||||
|
|
||||||
|
expect(ioPackage.common.version).to.exist;
|
||||||
|
expect(npmPackage.version).to.exist;
|
||||||
|
|
||||||
|
if (!expect(ioPackage.common.version).to.be.equal(npmPackage.version)) {
|
||||||
|
console.log('ERROR: Version numbers in package.json and io-package.json differ!!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
|
||||||
|
console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
|
||||||
|
}
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user