Improved database error handling, new Let's Encrypt server console commands.

This commit is contained in:
Ylian Saint-Hilaire 2020-03-04 14:57:03 -08:00
parent 2741344268
commit 672517f27d
7 changed files with 137 additions and 94 deletions

View File

@ -21,8 +21,7 @@ limitations under the License.
*/
// Construct a WSMAN communication object
function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
{
function CreateWsmanComm(/*host, port, user, pass, tls, extra*/) {
var obj = {};
obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
@ -31,15 +30,13 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
obj.digest = null;
obj.RequestCount = 0;
if (arguments.length == 1 && typeof(arguments[0] == 'object'))
{
if (arguments.length == 1 && typeof (arguments[0] == 'object')) {
obj.host = arguments[0].host;
obj.port = arguments[0].port;
obj.authToken = arguments[0].authToken;
obj.tls = arguments[0].tls;
}
else
{
else {
obj.host = arguments[0];
obj.port = arguments[1];
obj.user = arguments[2];
@ -75,14 +72,10 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
if (globalDebugFlags & 1) { console.log("SEND: " + postdata + "\r\n\r\n"); } // DEBUG
// We are in a DukTape environement
if (obj.digest == null)
{
if (obj.authToken)
{
if (obj.digest == null) {
if (obj.authToken) {
obj.digest = require('http-digest').create({ authToken: obj.authToken });
}
else
{
} else {
obj.digest = require('http-digest').create(obj.user, obj.pass);
}
obj.digest.http = require('http');

41
db.js
View File

@ -42,7 +42,8 @@ module.exports.CreateDB = function (parent, func) {
// Check if the database unique identifier is present
// This is used to check that in server peering mode, everyone is using the same database.
obj.Get('DatabaseIdentifier', function (err, docs) {
if ((docs.length == 1) && (docs[0].value != null)) {
if (err != null) { parent.debug('db', 'ERROR (Get DatabaseIdentifier): ' + err); }
if ((err == null) && (docs.length == 1) && (docs[0].value != null)) {
obj.identifier = docs[0].value;
} else {
obj.identifier = Buffer.from(require('crypto').randomBytes(48), 'binary').toString('hex');
@ -52,8 +53,9 @@ module.exports.CreateDB = function (parent, func) {
// Load database schema version and check if we need to update
obj.Get('SchemaVersion', function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR (Get SchemaVersion): ' + err); }
var ver = 0;
if (docs && docs.length == 1) { ver = docs[0].value; }
if ((err == null) && (docs.length == 1)) { ver = docs[0].value; }
if (ver == 1) { console.log('This is an unsupported beta 1 database, delete it to create a new one.'); process.exit(0); }
// TODO: Any schema upgrades here...
@ -87,6 +89,7 @@ module.exports.CreateDB = function (parent, func) {
// Remove all objects that have a "meshid" that no longer points to a valid mesh.
obj.GetAllType('mesh', function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR (GetAll mesh): ' + err); }
var meshlist = [];
if ((err == null) && (docs.length > 0)) { for (var i in docs) { meshlist.push(docs[i]._id); } }
if ((obj.databaseType == 4) || (obj.databaseType == 5)) {
@ -102,7 +105,8 @@ module.exports.CreateDB = function (parent, func) {
// Fix all of the creating & login to ticks by seconds, not milliseconds.
obj.GetAllType('user', function (err, docs) {
if (err == null && docs.length > 0) {
if (err != null) { parent.debug('db', 'ERROR (GetAll user): ' + err); }
if ((err == null) && (docs.length > 0)) {
for (var i in docs) {
var fixed = false;
@ -182,14 +186,14 @@ module.exports.CreateDB = function (parent, func) {
// MongoDB
obj.file.aggregate([{ "$group": { _id: "$type", count: { $sum: 1 } } }]).toArray(function (err, docs) {
var counters = {}, totalCount = 0;
for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } }
if (err == null) { for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } } }
func(counters);
});
} else if (obj.databaseType == 2) {
// MongoJS
obj.file.aggregate([{ "$group": { _id: "$type", count: { $sum: 1 } } }], function (err, docs) {
var counters = {}, totalCount = 0;
for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } }
if (err == null) { for (var i in docs) { if (docs[i]._id != null) { counters[docs[i]._id] = docs[i].count; totalCount += docs[i].count; } } }
func(counters);
});
} else if (obj.databaseType == 1) {
@ -219,13 +223,14 @@ module.exports.CreateDB = function (parent, func) {
}
// This is used to rate limit a number of operation per day. Returns a startValue each new days, but you can substract it and save the value in the db.
obj.getValueOfTheDay = function (id, startValue, func) { obj.Get(id, function (err, docs) { var date = new Date(), t = date.toLocaleDateString(); if (docs.length == 1) { var r = docs[0]; if (r.day == t) { func({ _id: id, value: r.value, day: t }); return; } } func({ _id: id, value: startValue, day: t }); }); };
obj.getValueOfTheDay = function (id, startValue, func) { obj.Get(id, function (err, docs) { var date = new Date(), t = date.toLocaleDateString(); if ((err == null) && (docs.length == 1)) { var r = docs[0]; if (r.day == t) { func({ _id: id, value: r.value, day: t }); return; } } func({ _id: id, value: startValue, day: t }); }); };
obj.escapeBase64 = function escapeBase64(val) { return (val.replace(/\+/g, '@').replace(/\//g, '$')); }
// Encrypt an database object
obj.performRecordEncryptionRecode = function (func) {
var count = 0;
obj.GetAllType('user', function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR (performRecordEncryptionRecode): ' + err); }
if (err == null) { for (var i in docs) { count++; obj.Set(docs[i]); } }
obj.GetAllType('node', function (err, docs) {
if (err == null) { for (var i in docs) { count++; obj.Set(docs[i]); } }
@ -236,7 +241,7 @@ module.exports.CreateDB = function (parent, func) {
// Encrypt an database object
function performTypedRecordDecrypt(data) {
if ((obj.dbRecordsDecryptKey == null) || (typeof data != 'object')) return data;
if ((data == null) || (obj.dbRecordsDecryptKey == null) || (typeof data != 'object')) return data;
for (var i in data) {
if (data[i].type == 'user') {
data[i] = performPartialRecordDecrypt(data[i]);
@ -335,7 +340,9 @@ module.exports.CreateDB = function (parent, func) {
}
//sqlDbQuery('DROP DATABASE MeshCentral', null, function (err, docs) { console.log('DROP'); }); return;
sqlDbQuery('USE meshcentral', null, function (err, docs) {
if (err != null) { parent.debug('db', 'ERROR: USE meshcentral: ' + err); }
if (err == null) { setupFunctions(func); } else {
parent.debug('db', 'Creating database...');
sqlDbBatchExec([
'CREATE DATABASE meshcentral',
// Main table
@ -364,7 +371,10 @@ module.exports.CreateDB = function (parent, func) {
'CREATE INDEX ndxsmbiosexpire ON meshcentral.smbios (expire)',
// Plugins table
'CREATE TABLE meshcentral.plugin (id INT NOT NULL AUTO_INCREMENT, doc JSON, PRIMARY KEY(id), CHECK (json_valid(doc)))'
], function (err) { /*if (err != null) { console.log(err); }*/ setupFunctions(func); });
], function (err) {
if (err != null) { parent.debug('db', 'BatchSetupDb: ' + err); }
setupFunctions(func);
});
}
});
} else if (parent.args.mongodb) {
@ -373,6 +383,7 @@ module.exports.CreateDB = function (parent, func) {
require('mongodb').MongoClient.connect(parent.args.mongodb, { useNewUrlParser: true, useUnifiedTopology: true }, function (err, client) {
if (err != null) { console.log("Unable to connect to database: " + err); process.exit(); return; }
Datastore = client;
parent.debug('db', 'Connected to MongoDB database...');
// Get the database name and setup the database client
var dbname = 'meshcentral';
@ -785,8 +796,8 @@ module.exports.CreateDB = function (parent, func) {
obj.Get = function (_id, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [_id], func); }
obj.GetAll = function (func) { sqlDbQuery('SELECT domain, doc FROM meshcentral.main', null, func); }
obj.GetHash = function (id, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ?', [id], func); }
obj.GetAllTypeNoTypeField = function (type, domain, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ?', [type, domain], function (err, docs) { for (var i in docs) { delete docs[i].type } func(err, docs); }); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) { if (id && (id != '')) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ? AND type = ? AND domain = ? AND extra IN (?)', [id, type, domain, meshes], function (err, docs) { for (var i in docs) { delete docs[i].type } func(err, docs); }); } else { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ? AND extra IN (?)', [type, domain, meshes], function (err, docs) { for (var i in docs) { delete docs[i].type } func(err, docs); }); } };
obj.GetAllTypeNoTypeField = function (type, domain, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ?', [type, domain], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); }); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) { if (id && (id != '')) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id = ? AND type = ? AND domain = ? AND extra IN (?)', [id, type, domain, meshes], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); }); } else { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ? AND domain = ? AND extra IN (?)', [type, domain, meshes], function (err, docs) { if (err == null) { for (var i in docs) { delete docs[i].type } } func(err, docs); }); } };
obj.GetAllType = function (type, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = ?', [type], func); }
obj.GetAllIdsOfType = function (ids, domain, type, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE id IN (?) AND domain = ? AND type = ?', [ids, domain, type], func); }
obj.GetUserWithEmail = function (domain, email, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE domain = ? AND extra = ?', [domain, 'email/' + email], func); }
@ -796,11 +807,11 @@ module.exports.CreateDB = function (parent, func) {
obj.RemoveAllOfType = function (type, func) { sqlDbQuery('DELETE FROM meshcentral.main WHERE type = ?', [type], func); };
obj.InsertMany = function (data, func) { var pendingOps = 0; for (var i in data) { pendingOps++; obj.Set(data[i], function () { if (--pendingOps == 0) { func(); } }); } };
obj.RemoveMeshDocuments = function (id) { sqlDbQuery('DELETE FROM meshcentral.main WHERE extra = ?', [id], function () { sqlDbQuery('DELETE FROM meshcentral.main WHERE id = ?', ['nt' + id], func); } ); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.DeleteDomain = function (domain, func) { sqlDbQuery('DELETE FROM meshcentral.main WHERE domain = ?', [domain], func); };
obj.SetUser = function (user) { if (user.subscriptions != null) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); } else { obj.Set(user); } };
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
obj.getLocalAmtNodes = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE (type = "node") AND (extraex IS NOT NULL)', null, function (err, docs) { var r = []; for (var i in docs) { if (docs[i].host != null) { r.push(docs[i]); } } func(err, r); }); };
obj.getLocalAmtNodes = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE (type = "node") AND (extraex IS NOT NULL)', null, function (err, docs) { var r = []; if (err == null) { for (var i in docs) { if (docs[i].host != null) { r.push(docs[i]); } } } func(err, r); }); };
obj.getAmtUuidMeshNode = function (meshid, uuid, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE meshid = ? AND extraex = ?', [meshid, 'uuid/' + uuid], func); };
obj.getAmtUuidNode = function (uuid, func) { sqlDbQuery('SELECT doc FROM meshcentral.main WHERE type = "node" AND extraex = ?', ['uuid/' + uuid], func); };
obj.isMaxType = function (max, type, domainid, func) { if (max == null) { func(false); } else { sqlDbExec('SELECT COUNT(id) FROM meshcentral.main WHERE domain = ? AND type = ?', [domainid, type], function (err, response) { func((response['COUNT(id)'] == null) || (response['COUNT(id)'] > max), response['COUNT(id)']) }); } }
@ -904,7 +915,7 @@ module.exports.CreateDB = function (parent, func) {
obj.getPlugins = function (func) { sqlDbQuery('SELECT doc FROM meshcentral.plugin', null, func); }; // Get all plugins
obj.getPlugin = function (id, func) { sqlDbQuery('SELECT doc FROM meshcentral.plugin WHERE id = ?', [id], func); }; // Get plugin
obj.deletePlugin = function (id, func) { sqlDbQuery('DELETE FROM meshcentral.plugin WHERE id = ?', [id], func); }; // Delete plugin
obj.setPluginStatus = function (id, status, func) { obj.getPlugin(id, function (err, docs) { if (docs.length == 1) { docs[0].status = status; obj.updatePlugin(id, docs[0], func); } }); };
obj.setPluginStatus = function (id, status, func) { obj.getPlugin(id, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].status = status; obj.updatePlugin(id, docs[0], func); } }); };
obj.updatePlugin = function (id, args, func) { delete args._id; sqlDbQuery('REPLACE INTO meshcentral.plugin VALUE (?, ?)', [id, JSON.stringify(args)], func); };
}
} else if (obj.databaseType == 3) {
@ -939,7 +950,7 @@ module.exports.CreateDB = function (parent, func) {
obj.RemoveAllOfType = function (type, func) { obj.file.deleteMany({ type: type }, { multi: true }, func); };
obj.InsertMany = function (data, func) { obj.file.insertMany(data, func); };
obj.RemoveMeshDocuments = function (id) { obj.file.deleteMany({ meshid: id }, { multi: true }); obj.file.deleteOne({ _id: 'nt' + id }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.DeleteDomain = function (domain, func) { obj.file.deleteMany({ domain: domain }, { multi: true }, func); };
obj.SetUser = function (user) { if (user.subscriptions != null) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); } else { obj.Set(user); } };
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };
@ -1071,7 +1082,7 @@ module.exports.CreateDB = function (parent, func) {
obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); };
obj.InsertMany = function (data, func) { obj.file.insert(data, func); };
obj.RemoveMeshDocuments = function (id) { obj.file.remove({ meshid: id }, { multi: true }); obj.file.remove({ _id: 'nt' + id }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if (docs.length == 1) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.MakeSiteAdmin = function (username, domain) { obj.Get('user/' + domain + '/' + username, function (err, docs) { if ((err == null) && (docs.length == 1)) { docs[0].siteadmin = 0xFFFFFFFF; obj.Set(docs[0]); } }); };
obj.DeleteDomain = function (domain, func) { obj.file.remove({ domain: domain }, { multi: true }, func); };
obj.SetUser = function (user) { if (user.subscriptions != null) { var u = Clone(user); if (u.subscriptions) { delete u.subscriptions; } obj.Set(u); } else { obj.Set(user); } };
obj.dispose = function () { for (var x in obj) { if (obj[x].close) { obj[x].close(); } delete obj[x]; } };

View File

@ -251,7 +251,9 @@ module.exports.CreateLetsEncrypt = function (parent) {
} catch (ex) {
parent.debug('cert', "checkCertificate main exception: (" + JSON.stringify(ex) + ")");
console.log(ex);
return ex;
}
return null;
}
return obj;

View File

@ -689,7 +689,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
switch (cmd) {
case 'help': {
var fin = '', f = '', availcommands = 'help,info,versions,args,resetserver,showconfig,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths';
var fin = '', f = '', availcommands = 'help,info,versions,args,resetserver,showconfig,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths,letsencrypt';
availcommands = availcommands.split(',').sort();
while (availcommands.length > 0) {
if (f.length > 80) { fin += (f + ',\r\n'); f = ''; }
@ -699,6 +699,37 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
r = 'Available commands: \r\n' + fin + '.';
break;
}
case 'le':
case 'letsencrypt': {
if (parent.parent.letsencrypt == null) {
r = "Let's Encrypt not in use.";
} else {
var leinfo = {};
var greenLockVersion = null;
try { greenLockVersion = require('greenlock/package.json').version; } catch (ex) { }
if (greenLockVersion) { leinfo.greenLockVer = greenLockVersion; }
leinfo.redirWebServerHooked = parent.parent.letsencrypt.redirWebServerHooked;
leinfo.leDomains = parent.parent.letsencrypt.leDomains;
leinfo.leResults = parent.parent.letsencrypt.leResults;
leinfo.leResultsStaging = parent.parent.letsencrypt.leResultsStaging;
leinfo.performRestart = parent.parent.letsencrypt.performRestart;
leinfo.performMoveToProduction = parent.parent.letsencrypt.performMoveToProduction;
leinfo.runAsProduction = parent.parent.letsencrypt.runAsProduction;
leinfo.leDefaults = parent.parent.letsencrypt.leDefaults;
leinfo.leDefaultsStaging = parent.parent.letsencrypt.leDefaultsStaging;
r = JSON.stringify(leinfo, null, 4);
}
break;
}
case 'lecheck': {
if (parent.parent.letsencrypt == null) {
r = "Let's Encrypt not in use.";
} else {
var err = parent.parent.letsencrypt.checkRenewCertificate();
if (err == null) { r = "Called Let's Encrypt certificate check."; } else { r = err; }
}
break;
}
case 'badlogins': {
if (parent.parent.config.settings.maxinvalidlogin == false) {
r = 'Bad login filter is disabled.';
@ -784,7 +815,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (error != null) { r2 += 'Exception: ' + error + '\r\n'; }
try { ws.send(JSON.stringify({ action: 'serverconsole', value: r2, tag: command.tag })); } catch (ex) { }
});
r = 'Checking server update...';
r = "Checking server update...";
break;
}
case 'info': {
@ -800,6 +831,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
try { info.version = process.version; } catch (ex) { }
try { info.cpuUsage = process.cpuUsage(); } catch (ex) { }
try { info.warnings = parent.parent.getServerWarnings(); } catch (ex) { }
try { info.database = ["Unknown", "NeDB", "MongoJS", "MongoDB", "MariaDB", "MySQL"][parent.parent.db.databaseType]; } catch (ex) { }
r = JSON.stringify(info, null, 4);
break;
}

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.4.9-t",
"version": "0.4.9-v",
"keywords": [
"Remote Management",
"Intel AMT",
@ -28,6 +28,7 @@
"sample-config.json"
],
"dependencies": {
"aedes": "^0.40.1",
"archiver": "^3.0.0",
"body-parser": "^1.19.0",
"cbor": "^4.1.5",

View File

@ -19,8 +19,8 @@
"cs": " - Resetovat za 1 den.",
"de": " - Zurücksetzen in 1 Tag.",
"en": " - Reset in 1 day.",
"fr": " - Réinitialiser en 1 jour.",
"es": " - Reiniciar en 1 dia.",
"fr": " - Réinitialiser en 1 jour.",
"nl": " - Herstart in {0} dag.",
"ru": " - Сброс через 1 день.",
"xloc": [
@ -32,8 +32,8 @@
"cs": " - Resetovat za 1 hodinu.",
"de": " - Zurücksetzen in 1 Stunde.",
"en": " - Reset in 1 hour.",
"fr": " - Réinitialiser en 1 heure.",
"es": " - Reiniciar en 1 hora.",
"fr": " - Réinitialiser en 1 heure.",
"nl": " - Herstart in 1 uur.",
"ru": " - Сброс через 1 час.",
"xloc": [
@ -45,8 +45,8 @@
"cs": " - Resetovat za 1 minutu.",
"de": " - Zurücksetzen in 1 Minute.",
"en": " - Reset in 1 minute.",
"fr": " - Réinitialiser en 1 minute.",
"es": " - Reiniciar en 1 minuto.",
"fr": " - Réinitialiser en 1 minute.",
"nl": " - Herstart in {0} days.",
"ru": " - Сброс через 1 минуту.",
"xloc": [
@ -58,8 +58,8 @@
"cs": " - Resetovat za {0} dnů.",
"de": " - Zurücksetzen in {0} Tagen.",
"en": " - Reset in {0} days.",
"fr": " - Réinitialiser dans {0} jours.",
"es": " - Reiniciar en {0} dias.",
"fr": " - Réinitialiser dans {0} jours.",
"nl": " - Herstart in {0} dagen.",
"ru": " - Сброс через {0} дней.",
"xloc": [
@ -82,8 +82,8 @@
"cs": " - Resetovat za {0} hodin.",
"de": " - Zurücksetzen in {0} Stunden.",
"en": " - Reset in {0} hours.",
"fr": " - Réinitialiser dans {0} heures.",
"es": " - Reiniciar en {0} horas.",
"fr": " - Réinitialiser dans {0} heures.",
"nl": " - Herstart in {0} uren.",
"ru": " - Сброс через {0} часов.",
"xloc": [
@ -106,8 +106,8 @@
"cs": " - Resetovat za {0} minut.",
"de": " - Zurücksetzen in {0} Minuten.",
"en": " - Reset in {0} minutes.",
"fr": " - Réinitialiser dans {0} minutes.",
"es": " - Reiniciar en {0} minutos.",
"fr": " - Réinitialiser dans {0} minutes.",
"nl": " - Herstart in {0} minuten.",
"ru": "- Сброс через {0} минут.",
"xloc": [
@ -655,8 +655,8 @@
"cs": "1 minuta do odpojení",
"de": "1 Minute bis zur Trennung",
"en": "1 minute until disconnect",
"fr": "1 minute jusqu'à déconnexion",
"es": "Un minuto hasta desconectar",
"fr": "1 minute jusqu'à déconnexion",
"nl": "1 minuut totdat de verbinding wordt verbroken",
"ru": "1 минута до разъединения",
"xloc": [
@ -711,8 +711,8 @@
"cs": "1 sekunda do odpojení",
"de": "1 Sekunde bis zur Trennung",
"en": "1 second until disconnect",
"fr": "1 seconde jusqu'à déconnexion",
"es": "Un segundo hasta desconectar",
"fr": "1 seconde jusqu'à déconnexion",
"nl": "1 seconde totdat de verbinding wordt verbroken",
"ru": "1 секунда до разъединения",
"xloc": [
@ -1200,8 +1200,8 @@
"cs": "Stav přihlášení za 7 dnů",
"de": "7-Tage-Anmelde-Status",
"en": "7 Day Login State",
"fr": "État de connexion sur 7 jours",
"es": "Estado de inicio de sesión de 7 días",
"fr": "État de connexion sur 7 jours",
"nl": "Inlogstatus afgelopen 7 dagen",
"ru": "7-дневная статистика входов"
},
@ -2130,12 +2130,12 @@
]
},
{
"en": "Admin Control Mode (ACM)",
"nl": "Admin Control Mode (ACM)",
"es": "Admin Control Mode (ACM)",
"cs": "Admin Control Mode (ACM)",
"fr": "Admin Control Mode (ACM)",
"de": "Admin Control Mode (ACM)",
"en": "Admin Control Mode (ACM)",
"es": "Admin Control Mode (ACM)",
"fr": "Admin Control Mode (ACM)",
"nl": "Admin Control Mode (ACM)",
"pt": "Admin Control Mode (ACM)",
"xloc": [
"default.handlebars->27->715"
@ -4223,12 +4223,12 @@
]
},
{
"en": "Client Control Mode (CCM)",
"nl": "Client Control Mode (CCM)",
"es": "Client Control Mode (CCM)",
"ce": "Client Control Mode (CCM)",
"de": "Client Control Mode (CCM)",
"en": "Client Control Mode (CCM)",
"es": "Client Control Mode (CCM)",
"fr": "Client Control Mode (CCM)",
"nl": "Client Control Mode (CCM)",
"pt": "Client Control Mode (CCM)",
"xloc": [
"default.handlebars->27->714"
@ -8789,12 +8789,12 @@
]
},
{
"en": "IP: {0}",
"nl": "IP: {0}",
"es": "IP: {0}",
"cs": "IP: {0}",
"de": "IP: {0}",
"en": "IP: {0}",
"es": "IP: {0}",
"fr": "IP: {0}",
"nl": "IP: {0}",
"pt": "IP: {0}",
"ru": "IP: {0}",
"xloc": [
@ -9464,12 +9464,12 @@
]
},
{
"en": "Intel® Active Management Technology (Intel® AMT)",
"nl": "Intel® Active Management Technology (Intel® AMT)",
"es": "Intel® Active Management Technology (Intel® AMT)",
"cs": "Intel® Active Management Technology (Intel® AMT)",
"fr": "Intel® Active Management Technology (Intel® AMT)",
"de": "Intel® Active Management Technology (Intel® AMT)",
"en": "Intel® Active Management Technology (Intel® AMT)",
"es": "Intel® Active Management Technology (Intel® AMT)",
"fr": "Intel® Active Management Technology (Intel® AMT)",
"nl": "Intel® Active Management Technology (Intel® AMT)",
"pt": "Intel® Active Management Technology (Intel® AMT)",
"ru": "Intel® Active Management Technology (Intel® AMT)",
"xloc": [
@ -9573,12 +9573,12 @@
]
},
{
"en": "Intel®AMT",
"nl": "Intel®AMT",
"es": "Intel®AMT",
"cs": "Intel®AMT",
"de": "Intel®AMT",
"en": "Intel®AMT",
"es": "Intel®AMT",
"fr": "Intel®AMT",
"nl": "Intel®AMT",
"pt": "Intel®AMT",
"ru": "Intel®AMT",
"xloc": [
@ -11104,12 +11104,12 @@
]
},
{
"en": "MAC: {0}",
"nl": "MAC: {0}",
"es": "MAC: {0}",
"cs": "MAC: {0}",
"de": "MAC: {0}",
"en": "MAC: {0}",
"es": "MAC: {0}",
"fr": "MAC: {0}",
"nl": "MAC: {0}",
"pt": "MAC: {0}",
"ru": "MAC: {0}",
"xloc": [
@ -14643,7 +14643,7 @@
]
},
{
"en": "Provisioning Mode"
"en": "Provisioning Mode",
"nl": "Provisioning Modus"
},
{
@ -21356,8 +21356,8 @@
"cs": "{0} gigabajtů zbývá",
"de": "{0} Gigabytes verbleibend",
"en": "{0} gigabytes remaining",
"fr": "{0} gigaoctets restants",
"es": "Queda {0} gigabytes.",
"fr": "{0} gigaoctets restants",
"ja": "残り{0}ギガバイト",
"nl": "{0} rsterende gigabytes",
"pt": "{0} gigabytes restantes",
@ -21370,8 +21370,8 @@
"cs": "{0} skupin",
"de": "{0} Gruppen",
"en": "{0} groups",
"fr": "{0} groupes",
"es": "{0} grupos",
"fr": "{0} groupes",
"ja": "{0}グループ",
"nl": "{0} groepen",
"pt": "{0} grupos",
@ -21395,8 +21395,8 @@
"cs": "{0} hodina{1}",
"de": "{0} Stunde{1}",
"en": "{0} hour{1}",
"fr": "{0} heure{1}",
"es": "{0} hora {1}",
"fr": "{0} heure{1}",
"ja": "{0}時間{1}",
"nl": "{0} uur{1}",
"pt": "{0} horas{1}",
@ -21406,8 +21406,8 @@
"cs": "{0} kilobajtů zbývá",
"de": "{0} Kilobytes verbleibend",
"en": "{0} kilobytes remaining",
"fr": "{0} kilo-octets restants",
"es": "Quedan {0} kilobytes",
"fr": "{0} kilo-octets restants",
"ja": "残り{0}キロバイト",
"nl": "{0} resterende kilobytes",
"pt": "{0} kilobytes restantes",
@ -21420,8 +21420,8 @@
"cs": "{0} malých písmen",
"de": "{0} Kleinbuchstaben",
"en": "{0} lower case",
"fr": "{0} minuscule",
"es": "{0} minúscula",
"fr": "{0} minuscule",
"ja": "{0}小文字",
"nl": "{0} kleine letters",
"pt": "{0} letras minúsculas",
@ -21435,8 +21435,8 @@
"cs": "{0} megabajtů zbývá",
"de": "{0} Megabytes verbleibend",
"en": "{0} megabytes remaining",
"fr": "{0} mégaoctets restants",
"es": "Quedan {0} megabytes",
"fr": "{0} mégaoctets restants",
"ja": "残り{0}メガバイト",
"nl": "{0} resterende megabytes",
"pt": "{0} megabytes restantes",
@ -21460,8 +21460,8 @@
"cs": "{0} minut{1} do odpojení",
"de": "{0} Minute{1} bis zur Trennung",
"en": "{0} minute{1} until disconnect",
"fr": "{0} minute {1} jusqu'à la déconnexion",
"es": "{0} minutes {1} hasta desconectar",
"fr": "{0} minute {1} jusqu'à la déconnexion",
"ja": "切断するまで{0}分{1}",
"nl": "{0} minuut {1} tot verbreken",
"pt": "{0} minutos{1} até desconectar",
@ -21471,8 +21471,8 @@
"cs": "{0} dalších uživatelů není zobrazeno, vyhledejte je pomocí kolonky pro vyhledávání…",
"de": "{0} weitere Benutzer nicht angezeigt, Suchfeld verwenden um weitere Benutzer zu suchen...",
"en": "{0} more users not shown, use search box to look for users...",
"fr": "{0} autres utilisateurs non affichés, utilisez le champ de recherche pour rechercher des utilisateurs...",
"es": "{0} no se muestran más usuarios, use la opcion de busqueda para encontrar los usuarios...",
"fr": "{0} autres utilisateurs non affichés, utilisez le champ de recherche pour rechercher des utilisateurs...",
"ja": "{0}個のユーザーが表示されていません。検索ボックスを使用してユーザーを検索してください...",
"nl": "{0} meer gebruikers niet getoond, gebruik zoekvak om gebruikers te zoeken ...",
"pt": "{0} mais usuários não exibidos, use a caixa de pesquisa para procurar usuários ...",
@ -21499,8 +21499,8 @@
"cs": "{0} speciálních znaků",
"de": "{0} nicht-alphanumerisch",
"en": "{0} non-alphanumeric",
"fr": "{0} non alphanumérique",
"es": "{0} no alfanumérico",
"fr": "{0} non alphanumérique",
"ja": "{0}英数字以外",
"nl": "{0} niet-alfanumeriek",
"pt": "{0} não alfanumérico",
@ -21514,8 +21514,8 @@
"cs": "{0} čísel",
"de": "{0} numerisch",
"en": "{0} numeric",
"fr": "{0} numérique",
"es": "{0} numerico",
"fr": "{0} numérique",
"ja": "{0}数値",
"nl": "{0} numeriek",
"pt": "{0} numérico",
@ -21525,12 +21525,23 @@
"login.handlebars->5->32"
]
},
{
"cs": "{0} sekund {1}",
"de": "{0} Sekunde {1}",
"en": "{0} second {1}",
"es": "{0} segundo {1}",
"fr": "{0} seconde {1}",
"ja": "{0}秒 {1}",
"nl": "{0} seconde {1}",
"pt": "{0} segundo {1}",
"ru": "{0} секунд {1}"
},
{
"cs": "{0} sekund do odpojení",
"de": "{0} Sekunden bis zur Trennung",
"en": "{0} seconds until disconnect",
"fr": "{0} secondes jusqu'à la déconnexion",
"es": "{0} segundos hasta desconectar",
"fr": "{0} secondes jusqu'à la déconnexion",
"nl": "{0} seconden totdat de verbinding wordt verbroken",
"ru": "{0} секунд до разъединения",
"xloc": [
@ -21538,15 +21549,7 @@
]
},
{
"cs": "{0} sekund {1}",
"de": "{0} Sekunde {1}",
"en": "{0} second {1}",
"fr": "{0} seconde {1}",
"es": "{0} segundo {1}",
"ja": "{0}秒 {1}",
"nl": "{0} seconde {1}",
"pt": "{0} segundo {1}",
"ru": "{0} секунд {1}",
"en": "{0} second{1}",
"xloc": [
"player.handlebars->3->3"
]
@ -21555,8 +21558,8 @@
"cs": "{0} sekund{1} do odpojení",
"de": "{0} Sekunde{1} bis zur Trennung",
"en": "{0} second{1} until disconnect",
"fr": "{0} seconde {1} jusqu'à la déconnexion",
"es": "{0} segundos {1} hasta desconectar",
"fr": "{0} seconde {1} jusqu'à la déconnexion",
"ja": "切断するまで{0}秒{1}",
"nl": "{0} seconde {1} tot verbreken",
"pt": "{0} segundo{1} até desconectar",
@ -21566,8 +21569,8 @@
"cs": "{0} relací",
"de": "{0} Sitzungen",
"en": "{0} sessions",
"fr": "{0} sessions",
"es": "{0} sesiones",
"fr": "{0} sessions",
"ja": "{0}セッション",
"nl": "{0} sessies",
"pt": "{0} sessões",
@ -21580,8 +21583,8 @@
"cs": "{0} nastavení (.msh)",
"de": "{0} Einstellungen (.msh)",
"en": "{0} settings (.msh)",
"fr": "{0} paramètres (.msh)",
"es": "Configuración {0} (.msh)",
"fr": "{0} paramètres (.msh)",
"ja": "{0}設定(.msh",
"nl": "{0} instellingen (.msh)",
"pt": "{0} configurações (.msh)",
@ -21594,8 +21597,8 @@
"cs": "{0} na {1}",
"de": "{0} bis {1}",
"en": "{0} to {1}",
"fr": "{0} à {1}",
"es": "{0} a {1}",
"fr": "{0} à {1}",
"ja": "{0}から{1}",
"nl": "{0} tot {1}",
"pt": "{0} para {1}",
@ -21608,8 +21611,8 @@
"cs": "{0} velkých písmen",
"de": "{0} Großbuchstaben",
"en": "{0} upper case",
"fr": "{0} majuscule",
"es": "{0} mayuscula",
"fr": "{0} majuscule",
"ja": "{0}大文字",
"nl": "{0} hoofdletters",
"pt": "{0} maiúsculas",
@ -21623,8 +21626,8 @@
"cs": "{0} uživatelů",
"de": "{0} Benutzer",
"en": "{0} users",
"fr": "{0} utilisateurs",
"es": "{0} usuarios",
"fr": "{0} utilisateurs",
"ja": "{0}ユーザー",
"nl": "{0} gebruikers",
"pt": "{0} usuários",
@ -21637,8 +21640,8 @@
"cs": "{0}b zbývá",
"de": "{0}B verbleibend",
"en": "{0}b left",
"fr": "{0}b gauche",
"es": "Quedan {0}b ",
"fr": "{0}b gauche",
"ja": "{0} b残り",
"nl": "{0}b over",
"pt": "{0}b restante",
@ -21651,8 +21654,8 @@
"cs": "{0}g zbývá",
"de": "{0}G verbleibend",
"en": "{0}g left",
"fr": "{0}g restantt",
"es": "Quedan {0}g",
"fr": "{0}g restantt",
"ja": "{0} g残り",
"nl": "{0}g over",
"pt": "{0}g restante",
@ -21665,8 +21668,8 @@
"cs": "{0}k v 1 souboru. {1}k maximum",
"de": "{0}k in 1 Datei. {1}k maximal",
"en": "{0}k in 1 file. {1}k maximum",
"fr": "{0} k dans 1 fichier.{1} k maximum",
"es": "{0}k en 1 archivo. {1}k maximo",
"fr": "{0} k dans 1 fichier.{1} k maximum",
"ja": "{0} k in 1ファイル。 {1} k最大",
"nl": "{0}k in 1 bestand. {1}k maximum",
"pt": "{0} k em 1 arquivo. {1} k no máximo",
@ -21679,8 +21682,8 @@
"cs": "{0}k v {1} souborech. {2}k maximum",
"de": "{0}k in {1} Dateien. {2}k maximal",
"en": "{0}k in {1} files. {2}k maximum",
"fr": "{0} k dans {1} fichiers.{2} k maximum",
"es": "{0}k en {1} archivos. {2}k maximo",
"fr": "{0} k dans {1} fichiers.{2} k maximum",
"ja": "{0} k個の{1}ファイル。 {2} k最大",
"nl": "{0}k in 1 file. {2}k maximum",
"pt": "{0} k em {1} arquivos. {2} k no máximo",
@ -21693,8 +21696,8 @@
"cs": "{0}k zbývá",
"de": "{0}k verbleibend",
"en": "{0}k left",
"fr": "{0}k restant",
"es": "Quedan {0}k",
"fr": "{0}k restant",
"ja": "{0} k残り",
"nl": "{0}k over",
"pt": "{0}k restante",
@ -21707,8 +21710,8 @@
"cs": "{0}m zbývá",
"de": "{0}M verbleibend",
"en": "{0}m left",
"fr": "{0} m restant",
"es": "Quedan {0}m",
"fr": "{0} m restant",
"ja": "残り{0} m",
"nl": "{0}m over",
"pt": "{0}m restante",
@ -21851,4 +21854,4 @@
]
}
]
}
}

1
x.json Normal file

File diff suppressed because one or more lines are too long