- App.tsx: full navigation (Auth stack + Main tabs with 5 screens) - Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen - HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors - MapScreen: interactive map with layer toggles (6 layers) - MarketplaceScreen: categories (6), products (5), search - ChatScreen: AI chat with quick prompts (4), bot responses - ProfileScreen: user info, stats, menu (9 items), logout - AlertsScreen: alert list with severity, acknowledge - SensorsScreen: sensor list with type filters (6 types), search - ZonesScreen: zone cards with stats - SettingsScreen: language picker (FR/EN/ES/DE), privacy, about - Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n - Hooks: useSensors, useAlerts, useNotifications, useLocation - Components: Card, Button, LoadingSpinner, ErrorBoundary, Header - Services: iotService, notificationService (with axios API client) - Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN) - Theme: colors.ts with full design system (Blue Ocean palette) - Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
132 lines
4.7 KiB
JavaScript
132 lines
4.7 KiB
JavaScript
var fs = require('fs'),
|
||
mm = require('minimatch'),
|
||
path = require('path');
|
||
|
||
/**
|
||
* merge two objects by extending target object with source object
|
||
* @param target object to merge
|
||
* @param source object to merge
|
||
* @param {Boolean} [modify] whether to modify the target
|
||
* @returns {Object} extended object
|
||
*/
|
||
function extend(target, source, modify) {
|
||
var result = target ? modify ? target : extend({}, target, true) : {};
|
||
if (!source) return result;
|
||
for (var key in source) {
|
||
if (source.hasOwnProperty(key) && source[key] !== undefined) {
|
||
result[key] = source[key];
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* determine if a string is contained within an array or matches a regular expression
|
||
* @param {String} str string to match
|
||
* @param {Array|Regex} match array or regular expression to match against
|
||
* @returns {Boolean} whether there is a match
|
||
*/
|
||
function matches(str, match) {
|
||
if (Array.isArray(match)) {
|
||
var l = match.length;
|
||
for( var s=0; s < l; s++) {
|
||
if ( mm(str,match[s])) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
return match.test(str);
|
||
}
|
||
|
||
/**
|
||
* read files and call a function with the contents of each file
|
||
* @param {String} dir path of dir containing the files to be read
|
||
* @param {String} encoding file encoding (default is 'utf8')
|
||
* @param {Object} options options hash for encoding, recursive, and match/exclude
|
||
* @param {Function(error, string)} callback callback for each files content
|
||
* @param {Function(error)} complete fn to call when finished
|
||
*/
|
||
function readFilesStream(dir, options, callback, complete) {
|
||
if (typeof options === 'function') {
|
||
complete = callback;
|
||
callback = options;
|
||
options = {};
|
||
}
|
||
if (typeof options === 'string') options = {
|
||
encoding: options
|
||
};
|
||
options = extend({
|
||
recursive: true,
|
||
encoding: 'utf8',
|
||
doneOnErr: true
|
||
}, options);
|
||
var files = [];
|
||
|
||
var done = function(err) {
|
||
if (typeof complete === 'function') {
|
||
if (err) return complete(err);
|
||
complete(null, files);
|
||
}
|
||
};
|
||
|
||
fs.readdir(dir, function(err, list) {
|
||
if (err) {
|
||
if (options.doneOnErr === true) {
|
||
if (err.code === 'EACCES') return done();
|
||
return done(err);
|
||
}
|
||
}
|
||
var i = 0;
|
||
|
||
if (options.reverse === true ||
|
||
(typeof options.sort == 'string' &&
|
||
(/reverse|desc/i).test(options.sort))) {
|
||
list = list.reverse();
|
||
} else if (options.sort !== false) list = list.sort();
|
||
|
||
(function next() {
|
||
var filename = list[i++];
|
||
if (!filename) return done(null, files);
|
||
var file = path.join(dir, filename);
|
||
fs.stat(file, function(err, stat) {
|
||
if (err && options.doneOnErr === true) return done(err);
|
||
if (stat && stat.isDirectory()) {
|
||
if (options.recursive) {
|
||
if (options.matchDir && !matches(filename, options.matchDir)) return next();
|
||
if (options.excludeDir && matches(filename, options.excludeDir)) return next();
|
||
readFilesStream(file, options, callback, function(err, sfiles) {
|
||
if (err && options.doneOnErr === true) return done(err);
|
||
files = files.concat(sfiles);
|
||
next();
|
||
});
|
||
} else next();
|
||
} else if (stat && stat.isFile()) {
|
||
if (options.match && !matches(filename, options.match)) return next();
|
||
if (options.exclude && matches(filename, options.exclude)) return next();
|
||
if (options.filter && !options.filter(filename)) return next();
|
||
if (options.shortName) files.push(filename);
|
||
else files.push(file);
|
||
var stream = fs.createReadStream(file);
|
||
if (options.encoding !== null) {
|
||
stream.setEncoding(options.encoding);
|
||
}
|
||
stream.on('error',function(err) {
|
||
if (options.doneOnErr === true) return done(err);
|
||
next();
|
||
});
|
||
if (callback.length > 3)
|
||
if (options.shortName) callback(null, stream, filename, next);
|
||
else callback(null, stream, file, next);
|
||
else callback(null, stream, next);
|
||
}
|
||
else {
|
||
next();
|
||
}
|
||
});
|
||
})();
|
||
|
||
});
|
||
}
|
||
module.exports = readFilesStream;
|