* new widget for gateway with the last telemetry type * add isStateForm for the last telemetry type * add config update save * update thingboard yaml * remove update thingboard yaml * add new widget to gateway_widgets.json * delete state gateway * delete state gateway * Revert "delete state gateway" This reverts commit d8024ead * change yml * edit name isReadOnly * fix bug isReadOnly * [Gateway remote configuration] Changed default logs.conf file Co-authored-by: nick <nick.@avalr.com.ua>
494 lines
22 KiB
JavaScript
494 lines
22 KiB
JavaScript
/*
|
|
* Copyright © 2016-2020 The Thingsboard Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
import './gateway-form.scss';
|
|
/* eslint-disable import/no-unresolved, import/default */
|
|
|
|
import gatewayFormTemplate from './gateway-form.tpl.html';
|
|
|
|
/* eslint-enable import/no-unresolved, import/default */
|
|
|
|
export default angular.module('thingsboard.directives.gatewayForm', [])
|
|
.directive('tbGatewayForm', GatewayForm)
|
|
.name;
|
|
|
|
/*@ngInject*/
|
|
function GatewayForm() {
|
|
return {
|
|
restrict: "E",
|
|
scope: true,
|
|
bindToController: {
|
|
formId: '=',
|
|
ctx: '=',
|
|
isStateForm: '=',
|
|
deviceName: '=',
|
|
isReadOnly: '=',
|
|
isState: '='
|
|
},
|
|
controller: GatewayFormController,
|
|
controllerAs: 'vm',
|
|
templateUrl: gatewayFormTemplate,
|
|
};
|
|
}
|
|
|
|
/*@ngInject*/
|
|
function GatewayFormController($scope, $injector, $document, $mdExpansionPanel, toast, importExport, attributeService, deviceService, userService, $mdDialog, $mdUtil, types, $window, $q, entityService, utils, $translate) {
|
|
let vm = this;
|
|
const currentConfigurationAttribute = "current_configuration";
|
|
const configurationDraftsAttribute = "configuration_drafts";
|
|
const configurationAttribute = "configuration";
|
|
const remoteLoggingLevelAttribute = "RemoteLoggingLevel";
|
|
|
|
const templateLogsConfig = '[loggers]}}keys=root, service, connector, converter, tb_connection, storage, extension}}[handlers]}}keys=consoleHandler, serviceHandler, connectorHandler, converterHandler, tb_connectionHandler, storageHandler, extensionHandler}}[formatters]}}keys=LogFormatter}}[logger_root]}}level=ERROR}}handlers=consoleHandler}}[logger_connector]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=connector}}[logger_storage]}}level={ERROR}}}handlers=storageHandler}}formatter=LogFormatter}}qualname=storage}}[logger_tb_connection]}}level={ERROR}}}handlers=tb_connectionHandler}}formatter=LogFormatter}}qualname=tb_connection}}[logger_service]}}level={ERROR}}}handlers=serviceHandler}}formatter=LogFormatter}}qualname=service}}[logger_converter]}}level={ERROR}}}handlers=converterHandler}}formatter=LogFormatter}}qualname=converter}}[logger_extension]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=extension}}[handler_consoleHandler]}}class=StreamHandler}}level={ERROR}}}formatter=LogFormatter}}args=(sys.stdout,)}}[handler_connectorHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}connector.log", "d", 1, 7,)}}[handler_storageHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}storage.log", "d", 1, 7,)}}[handler_serviceHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}service.log", "d", 1, 7,)}}[handler_converterHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}converter.log", "d", 1, 3,)}}[handler_extensionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}extension.log", "d", 1, 3,)}}[handler_tb_connectionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}tb_connection.log", "d", 1, 3,)}}[formatter_LogFormatter]}}format="%(asctime)s - %(levelname)s - [%(filename)s] - %(module)s - %(lineno)d - %(message)s" }}datefmt="%Y-%m-%d %H:%M:%S"';
|
|
|
|
vm.types = types;
|
|
vm.deviceNameForm = (vm.deviceName) ? vm.deviceName : null;
|
|
vm.idForm = Math.random().toString(36).replace(/^0\.[0-9]*/, '');
|
|
vm.configurations = {
|
|
gateway: '',
|
|
host: $document[0].domain,
|
|
port: 1883,
|
|
remoteConfiguration: true,
|
|
accessToken: '',
|
|
storageType: "memoryStorage",
|
|
readRecordsCount: 100,
|
|
maxRecordsCount: 10000,
|
|
dataFolderPath: './data/',
|
|
maxFilesCount: 5,
|
|
securityType: "accessToken",
|
|
caCertPath: '/etc/thingsboard-gateway/ca.pem',
|
|
privateKeyPath: '/etc/thingsboard-gateway/privateKey.pem',
|
|
certPath: '/etc/thingsboard-gateway/certificate.pem',
|
|
connectors: [],
|
|
remoteLoggingLevel: "DEBUG",
|
|
remoteLoggingPathToLogs: './logs/'
|
|
};
|
|
|
|
let archiveFileName = '';
|
|
let gatewayNameExists = '';
|
|
let successfulSaved = '';
|
|
vm.securityTypes = [{
|
|
name: 'gateway.security-types.access-token',
|
|
value: 'accessToken'
|
|
}, {
|
|
name: 'gateway.security-types.tls',
|
|
value: 'tls'
|
|
}];
|
|
|
|
vm.storageTypes = [{
|
|
name: 'gateway.storage-types.memory-storage',
|
|
value: 'memoryStorage'
|
|
}, {
|
|
name: 'gateway.storage-types.file-storage',
|
|
value: 'fileStorage'
|
|
}];
|
|
|
|
$scope.$watch('vm.ctx', function () {
|
|
if (vm.ctx) {
|
|
vm.isStateForm = $scope.isStateForm;
|
|
vm.settings = vm.ctx.settings;
|
|
vm.widgetConfig = vm.ctx.widgetConfig;
|
|
if (vm.ctx.datasources && vm.ctx.datasources.length) {
|
|
vm.deviceNameForm = vm.ctx.datasources[0].name;
|
|
}
|
|
}
|
|
initializeConfig();
|
|
});
|
|
|
|
|
|
$scope.$on('gateway-form-resize', function (event, formId) {
|
|
if (vm.formId == formId) {
|
|
updateWidgetDisplaying();
|
|
}
|
|
});
|
|
|
|
function updateWidgetDisplaying() {
|
|
if (vm.ctx) {
|
|
vm.changeAlignment = (vm.ctx.$container[0].offsetWidth <= 425);
|
|
}
|
|
}
|
|
|
|
function initWidgetSettings() {
|
|
let widgetTitle;
|
|
if (vm.settings) {
|
|
vm.isReadOnlyForm = (vm.settings.readOnly) ? vm.settings.readOnly : false;
|
|
if (vm.settings.gatewayTitle && vm.settings.gatewayTitle.length) {
|
|
widgetTitle = utils.customTranslation(vm.settings.gatewayTitle, vm.settings.gatewayTitle);
|
|
}
|
|
} else {
|
|
vm.isReadOnlyForm = false;
|
|
widgetTitle = $translate.instant('gateway.gateway');
|
|
}
|
|
if (vm.ctx) {
|
|
vm.ctx.widgetTitle = widgetTitle;
|
|
}
|
|
|
|
archiveFileName = vm.settings && vm.settings.archiveFileName && vm.settings.archiveFileName.length ? vm.settings.archiveFileName : 'gatewayConfiguration';
|
|
if (vm.settings) {
|
|
gatewayNameExists = utils.customTranslation(vm.settings.deviceNameExist, vm.settings.deviceNameExist) || $translate.instant('gateway.gateway-exists');
|
|
successfulSaved = utils.customTranslation(vm.settings.successfulSave, vm.settings.successfulSave) || $translate.instant('gateway.gateway-saved');
|
|
} else {
|
|
gatewayNameExists = $translate.instant('gateway.gateway-exists');
|
|
successfulSaved = $translate.instant('gateway.gateway-saved');
|
|
}
|
|
}
|
|
|
|
function initializeConfig() {
|
|
updateWidgetDisplaying();
|
|
initWidgetSettings();
|
|
getGatewaysList(true);
|
|
}
|
|
|
|
|
|
vm.getAccessToken = (deviceId) => {
|
|
if (deviceId.id) {
|
|
getDeviceCredentials(deviceId.id);
|
|
}
|
|
};
|
|
|
|
vm.collapsePanel = function (panelId) {
|
|
$mdExpansionPanel(panelId).collapse();
|
|
};
|
|
|
|
function getDeviceCredentials(deviceId) {
|
|
return deviceService.getDeviceCredentials(deviceId).then(
|
|
(deviceCredentials) => {
|
|
vm.configurations.accessToken = deviceCredentials.credentialsId;
|
|
getAttributes();
|
|
}
|
|
);
|
|
}
|
|
|
|
vm.createDevice = (deviceObj) => {
|
|
deviceService.findByName(deviceObj.name, {ignoreErrors: true})
|
|
.then(
|
|
function () {
|
|
toast.showError(gatewayNameExists, angular.element('.gateway-form'), 'top left');
|
|
},
|
|
function () {
|
|
if (vm.settings.gatewayType && vm.settings.gatewayType.length) {
|
|
deviceObj.type = vm.settings.gatewayType;
|
|
}
|
|
deviceService.saveDevice(deviceObj).then(
|
|
(device) => {
|
|
getDeviceCredentials(device.id.id).then(() => {
|
|
getGatewaysList();
|
|
});
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
vm.saveAttributeConfig = () => {
|
|
$q.all([
|
|
saveAttribute(configurationAttribute, $window.btoa(angular.toJson(getGatewayConfigJSON())), types.attributesScope.shared.value),
|
|
saveAttribute(configurationDraftsAttribute, $window.btoa(angular.toJson(getDraftConnectorJSON())), types.attributesScope.server.value),
|
|
saveAttribute(remoteLoggingLevelAttribute, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value)
|
|
]).then(() => {
|
|
toast.showSuccess(successfulSaved, 2000, angular.element('.gateway-form'), 'top left');
|
|
})
|
|
};
|
|
|
|
function getAttributes() {
|
|
let promises = [];
|
|
promises.push(getAttribute(currentConfigurationAttribute, types.attributesScope.client.value));
|
|
promises.push(getAttribute(configurationDraftsAttribute, types.attributesScope.server.value));
|
|
promises.push(getAttribute(remoteLoggingLevelAttribute, types.attributesScope.shared.value));
|
|
$q.all(promises).then((response) => {
|
|
processCurrentConfiguration(response[0]);
|
|
processConfigurationDrafts(response[1]);
|
|
processLoggingLevel(response[2]);
|
|
});
|
|
}
|
|
|
|
function getAttribute(attributeName, attributeScope) {
|
|
return attributeService.getEntityAttributesValues(vm.configurations.gateway.id.entityType, vm.configurations.gateway.id.id, attributeScope, attributeName);
|
|
}
|
|
|
|
function saveAttribute(attributeName, attributeValue, attributeScope) {
|
|
let attributes = [{
|
|
key: attributeName,
|
|
value: attributeValue
|
|
}];
|
|
return attributeService.saveEntityAttributes(vm.configurations.gateway.id.entityType, vm.configurations.gateway.id.id, attributeScope, attributes);
|
|
}
|
|
|
|
vm.exportConfig = () => {
|
|
let filesZip = {};
|
|
filesZip["tb_gateway.yaml"] = generateYAMLConfigurationFile();
|
|
generateConfigConnectorFiles(filesZip);
|
|
generateLogConfigFile(filesZip);
|
|
importExport.exportJSZip(filesZip, archiveFileName);
|
|
saveAttribute(remoteLoggingLevelAttribute, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value);
|
|
};
|
|
|
|
function generateYAMLConfigurationFile() {
|
|
let config;
|
|
config = 'thingsboard:\n';
|
|
config += ' host: ' + vm.configurations.host + '\n';
|
|
config += ' remoteConfiguration: ' + vm.configurations.remoteConfiguration + '\n';
|
|
config += ' port: ' + vm.configurations.port + '\n';
|
|
config += ' security:\n';
|
|
if (vm.configurations.securityType === 'accessToken') {
|
|
config += ' access-token: ' + vm.configurations.accessToken + '\n';
|
|
} else if (vm.configurations.securityType === 'tls') {
|
|
config += ' ca_cert: ' + vm.configurations.caCertPath + '\n';
|
|
config += ' privateKey: ' + vm.configurations.privateKeyPath + '\n';
|
|
config += ' cert: ' + vm.configurations.certPath + '\n';
|
|
}
|
|
config += 'storage:\n';
|
|
if (vm.configurations.storageType === 'memoryStorage') {
|
|
config += ' type: memory\n';
|
|
config += ' read_records_count: ' + vm.configurations.readRecordsCount + '\n';
|
|
config += ' max_records_count: ' + vm.configurations.maxRecordsCount + '\n';
|
|
} else if (vm.configurations.storageType === 'fileStorage') {
|
|
config += ' type: file\n';
|
|
config += ' data_folder_path: ' + vm.configurations.dataFolderPath + '\n';
|
|
config += ' max_file_count: ' + vm.configurations.maxFilesCount + '\n';
|
|
config += ' max_read_records_count: ' + vm.configurations.readRecordsCount + '\n';
|
|
config += ' max_records_per_file: ' + vm.configurations.maxRecordsCount + '\n';
|
|
}
|
|
config += 'connectors:\n';
|
|
for (let i = 0; i < vm.configurations.connectors.length; i++) {
|
|
if (vm.configurations.connectors[i].enabled) {
|
|
config += ' -\n';
|
|
config += ' name: ' + vm.configurations.connectors[i].name + '\n';
|
|
config += ' type: ' + vm.configurations.connectors[i].configType + '\n';
|
|
config += ' configuration: ' + generateFileName(vm.configurations.connectors[i].name) + '\n';
|
|
}
|
|
}
|
|
return config;
|
|
}
|
|
|
|
function generateConfigConnectorFiles(fileZipAdd) {
|
|
for (let i = 0; i < vm.configurations.connectors.length; i++) {
|
|
if (vm.configurations.connectors[i].enabled) {
|
|
fileZipAdd[generateFileName(vm.configurations.connectors[i].name)] = angular.toJson(vm.configurations.connectors[i].config);
|
|
}
|
|
}
|
|
}
|
|
|
|
function generateLogConfigFile(fileZipAdd) {
|
|
fileZipAdd["logs.conf"] = getLogsConfig();
|
|
}
|
|
|
|
function getLogsConfig() {
|
|
return templateLogsConfig
|
|
.replace(/{ERROR}/g, vm.configurations.remoteLoggingLevel)
|
|
.replace(/{.\/logs\/}/g, vm.configurations.remoteLoggingPathToLogs);
|
|
}
|
|
|
|
function getGatewayConfigJSON() {
|
|
let gatewayConfig = {};
|
|
gatewayConfig["thingsboard"] = gatewayMainConfigJSON();
|
|
gatewayConnectorConfigJSON(gatewayConfig);
|
|
return gatewayConfig;
|
|
}
|
|
|
|
function gatewayMainConfigJSON() {
|
|
let configuration = {};
|
|
|
|
let thingsBoard = {};
|
|
thingsBoard.host = vm.configurations.host;
|
|
thingsBoard.remoteConfiguration = vm.configurations.remoteConfiguration;
|
|
thingsBoard.port = vm.configurations.port;
|
|
let security = {};
|
|
if (vm.configurations.securityType === 'accessToken') {
|
|
security.accessToken = (vm.configurations.accessToken) ? vm.configurations.accessToken : ""
|
|
} else {
|
|
security.caCert = vm.configurations.caCertPath;
|
|
security.privateKey = vm.configurations.privateKeyPath;
|
|
security.cert = vm.configurations.certPath;
|
|
}
|
|
thingsBoard.security = security;
|
|
configuration.thingsboard = thingsBoard;
|
|
|
|
let storage = {};
|
|
if (vm.configurations.storageType === 'memoryStorage') {
|
|
storage.type = "memory";
|
|
storage.read_records_count = vm.configurations.readRecordsCount;
|
|
storage.max_records_count = vm.configurations.maxRecordsCount;
|
|
} else if (vm.configurations.storageType === 'fileStorage') {
|
|
storage.type = "file";
|
|
storage.data_folder_path = vm.configurations.dataFolderPath;
|
|
storage.max_file_count = vm.configurations.maxFilesCount;
|
|
storage.max_read_records_count = vm.configurations.readRecordsCount;
|
|
storage.max_records_per_file = vm.configurations.maxRecordsCount;
|
|
}
|
|
configuration.storage = storage;
|
|
|
|
let connectors = [];
|
|
for (let i = 0; i < vm.configurations.connectors.length; i++) {
|
|
if (vm.configurations.connectors[i].enabled) {
|
|
let connector = {
|
|
configuration: generateFileName(vm.configurations.connectors[i].name),
|
|
name: vm.configurations.connectors[i].name,
|
|
type: vm.configurations.connectors[i].configType
|
|
};
|
|
connectors.push(connector);
|
|
}
|
|
}
|
|
configuration.connectors = connectors;
|
|
|
|
configuration.logs = $window.btoa(getLogsConfig());
|
|
|
|
return configuration;
|
|
}
|
|
|
|
function gatewayConnectorConfigJSON(gatewayConfiguration) {
|
|
for (let i = 0; i < vm.configurations.connectors.length; i++) {
|
|
if (vm.configurations.connectors[i].enabled) {
|
|
let typeConnector = vm.configurations.connectors[i].configType;
|
|
if (!angular.isArray(gatewayConfiguration[typeConnector])) {
|
|
gatewayConfiguration[typeConnector] = [];
|
|
}
|
|
|
|
let connectorConfig = {
|
|
name: vm.configurations.connectors[i].name,
|
|
config: vm.configurations.connectors[i].config
|
|
};
|
|
gatewayConfiguration[typeConnector].push(connectorConfig);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getDraftConnectorJSON() {
|
|
let draftConnector = {};
|
|
for (let i = 0; i < vm.configurations.connectors.length; i++) {
|
|
if (!vm.configurations.connectors[i].enabled) {
|
|
let connector = {
|
|
connector: vm.configurations.connectors[i].configType,
|
|
config: vm.configurations.connectors[i].config
|
|
};
|
|
draftConnector[vm.configurations.connectors[i].name] = connector;
|
|
}
|
|
}
|
|
return draftConnector;
|
|
}
|
|
|
|
function getGatewaysList(firstInit) {
|
|
vm.gateways = [];
|
|
entityService.getEntitiesByNameFilter(types.entityType.device, "", -1).then((devices) => {
|
|
for (let i = 0; i < devices.length; i++) {
|
|
const device = devices[i];
|
|
if (device.additionalInfo !== null && device.additionalInfo.gateway === true) {
|
|
vm.gateways.push(device);
|
|
if (vm.deviceNameForm && firstInit && vm.gateways.length && device.name === vm.deviceNameForm) {
|
|
vm.configurations.gateway = device;
|
|
vm.getAccessToken(device.id);
|
|
} else if (firstInit && vm.gateways.length && device.name === vm.gateways[0].name) {
|
|
vm.configurations.gateway = device;
|
|
vm.getAccessToken(device.id);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function processCurrentConfiguration(response) {
|
|
if (response.length > 0) {
|
|
vm.configurations.connectors = [];
|
|
let attribute = angular.fromJson($window.atob(response[0].value));
|
|
for (var attributeKey in attribute) {
|
|
let keyValue = attribute[attributeKey];
|
|
if (attributeKey === "thingsboard") {
|
|
if (keyValue !== null && Object.keys(keyValue).length > 0) {
|
|
setConfigGateway(keyValue);
|
|
}
|
|
} else {
|
|
for (let connectorType in keyValue) {
|
|
let name = "No name";
|
|
if (Object.prototype.hasOwnProperty.call(keyValue[connectorType], 'name')) {
|
|
name = keyValue[connectorType].name;
|
|
}
|
|
let connector = {
|
|
enabled: true,
|
|
configType: attributeKey,
|
|
config: keyValue[connectorType].config,
|
|
name: name
|
|
};
|
|
vm.configurations.connectors.push(connector);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function processConfigurationDrafts(response) {
|
|
if (response.length > 0) {
|
|
let attribute = angular.fromJson($window.atob(response[0].value));
|
|
for (let key in attribute) {
|
|
let connector = {
|
|
enabled: false,
|
|
configType: attribute[key].connector,
|
|
config: attribute[key].config,
|
|
name: key
|
|
};
|
|
vm.configurations.connectors.push(connector);
|
|
}
|
|
}
|
|
}
|
|
|
|
function processLoggingLevel(response) {
|
|
if (response.length > 0) {
|
|
if (vm.types.gatewayLogLevel[response[0].value.toLowerCase()]) {
|
|
vm.configurations.remoteLoggingLevel = response[0].value.toUpperCase();
|
|
}
|
|
} else {
|
|
vm.configurations.remoteLoggingLevel = vm.types.gatewayLogLevel.debug;
|
|
}
|
|
}
|
|
|
|
function setConfigGateway(keyValue) {
|
|
if (Object.prototype.hasOwnProperty.call(keyValue, 'thingsboard')) {
|
|
vm.configurations.host = keyValue.thingsboard.host;
|
|
vm.configurations.port = keyValue.thingsboard.port;
|
|
vm.configurations.remoteConfiguration = keyValue.thingsboard.remoteConfiguration;
|
|
if (Object.prototype.hasOwnProperty.call(keyValue.thingsboard.security, 'accessToken')) {
|
|
vm.configurations.securityType = 'accessToken';
|
|
vm.configurations.accessToken = keyValue.thingsboard.security.accessToken;
|
|
} else {
|
|
vm.configurations.securityType = 'tls';
|
|
vm.configurations.caCertPath = keyValue.thingsboard.security.caCert;
|
|
vm.configurations.privateKeyPath = keyValue.thingsboard.security.private_key;
|
|
vm.configurations.certPath = keyValue.thingsboard.security.cert;
|
|
}
|
|
}
|
|
|
|
if (Object.prototype.hasOwnProperty.call(keyValue, 'storage') && Object.prototype.hasOwnProperty.call(keyValue.storage, 'type')) {
|
|
if (keyValue.storage.type === 'memory') {
|
|
vm.configurations.storageType = 'memoryStorage';
|
|
vm.configurations.readRecordsCount = keyValue.storage.read_records_count;
|
|
vm.configurations.maxRecordsCount = keyValue.storage.max_records_count;
|
|
} else if (keyValue.storage.type === 'file') {
|
|
vm.configurations.storageType = 'fileStorage';
|
|
vm.configurations.dataFolderPath = keyValue.storage.data_folder_path;
|
|
vm.configurations.maxFilesCount = keyValue.storage.max_file_count;
|
|
vm.configurations.readRecordsCount = keyValue.storage.read_records_count;
|
|
vm.configurations.maxRecordsCount = keyValue.storage.max_records_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
function generateFileName(fileName) {
|
|
return fileName.replace("_", "")
|
|
.replace("-", "")
|
|
.replace(/^\s+|\s+/g, '')
|
|
.toLowerCase() + '.json';
|
|
}
|
|
}
|
|
|
|
|