updates to saving settings
This commit is contained in:
184
map/app/utils/helpers.js
Normal file
184
map/app/utils/helpers.js
Normal file
@@ -0,0 +1,184 @@
|
||||
// Sync geographic fields between different formats
|
||||
function syncGeoFields(data) {
|
||||
// If we have latitude and longitude but no Geo-Location, create it
|
||||
if (data.latitude && data.longitude && !data['Geo-Location']) {
|
||||
const lat = parseFloat(data.latitude);
|
||||
const lng = parseFloat(data.longitude);
|
||||
if (!isNaN(lat) && !isNaN(lng)) {
|
||||
data['Geo-Location'] = `${lat};${lng}`;
|
||||
data.geodata = `${lat};${lng}`;
|
||||
}
|
||||
}
|
||||
// If we have Geo-Location but no lat/lng, parse it
|
||||
else if (data['Geo-Location'] && (!data.latitude || !data.longitude)) {
|
||||
const geoLocation = data['Geo-Location'].toString();
|
||||
|
||||
// Try semicolon-separated first
|
||||
let parts = geoLocation.split(';');
|
||||
if (parts.length === 2) {
|
||||
const lat = parseFloat(parts[0].trim());
|
||||
const lng = parseFloat(parts[1].trim());
|
||||
if (!isNaN(lat) && !isNaN(lng)) {
|
||||
data.latitude = lat;
|
||||
data.longitude = lng;
|
||||
data.geodata = `${lat};${lng}`;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
// Try comma-separated
|
||||
parts = geoLocation.split(',');
|
||||
if (parts.length === 2) {
|
||||
const lat = parseFloat(parts[0].trim());
|
||||
const lng = parseFloat(parts[1].trim());
|
||||
if (!isNaN(lat) && !isNaN(lng)) {
|
||||
data.latitude = lat;
|
||||
data.longitude = lng;
|
||||
data.geodata = `${lat};${lng}`;
|
||||
// Normalize Geo-Location to semicolon format for NocoDB GeoData
|
||||
data['Geo-Location'] = `${lat};${lng}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Validate URL format
|
||||
function validateUrl(url) {
|
||||
if (!url || typeof url !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const trimmed = url.trim();
|
||||
if (!trimmed) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Basic URL validation
|
||||
try {
|
||||
new URL(trimmed);
|
||||
return trimmed;
|
||||
} catch (e) {
|
||||
// If not a valid URL, check if it's a relative path or missing protocol
|
||||
if (trimmed.startsWith('/') || !trimmed.includes('://')) {
|
||||
// For relative paths or missing protocol, return as-is
|
||||
return trimmed;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Get cookie configuration based on request
|
||||
function getCookieConfig(req) {
|
||||
const host = req?.get('host') || '';
|
||||
const isLocalhost = host.includes('localhost') ||
|
||||
host.includes('127.0.0.1') ||
|
||||
host.match(/^\d+\.\d+\.\d+\.\d+/);
|
||||
|
||||
const config = {
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
sameSite: 'lax',
|
||||
secure: false,
|
||||
domain: undefined
|
||||
};
|
||||
|
||||
// Only set domain and secure for production non-localhost access
|
||||
if (process.env.NODE_ENV === 'production' && !isLocalhost && process.env.COOKIE_DOMAIN) {
|
||||
const cookieDomain = process.env.COOKIE_DOMAIN.replace(/^\./, '');
|
||||
if (host.includes(cookieDomain)) {
|
||||
config.domain = process.env.COOKIE_DOMAIN;
|
||||
config.secure = true;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Extract ID from NocoDB response
|
||||
function extractId(record) {
|
||||
return record.Id || record.id || record.ID || record._id;
|
||||
}
|
||||
|
||||
// Validate coordinates
|
||||
function validateCoordinates(lat, lng) {
|
||||
const latitude = parseFloat(lat);
|
||||
const longitude = parseFloat(lng);
|
||||
|
||||
if (isNaN(latitude) || isNaN(longitude)) {
|
||||
return { valid: false, error: 'Invalid coordinate values' };
|
||||
}
|
||||
|
||||
if (latitude < -90 || latitude > 90) {
|
||||
return { valid: false, error: 'Latitude must be between -90 and 90' };
|
||||
}
|
||||
|
||||
if (longitude < -180 || longitude > 180) {
|
||||
return { valid: false, error: 'Longitude must be between -180 and 180' };
|
||||
}
|
||||
|
||||
return { valid: true, latitude, longitude };
|
||||
}
|
||||
|
||||
// Check if coordinates are within bounds
|
||||
function checkBounds(lat, lng, bounds) {
|
||||
if (!bounds) return true;
|
||||
|
||||
return lat <= bounds.north &&
|
||||
lat >= bounds.south &&
|
||||
lng <= bounds.east &&
|
||||
lng >= bounds.west;
|
||||
}
|
||||
|
||||
// Sanitize user data for response
|
||||
function sanitizeUser(user) {
|
||||
const { Password, password, ...safeUser } = user;
|
||||
return safeUser;
|
||||
}
|
||||
|
||||
// Extract walk sheet configuration from NocoDB data, handling different field name formats
|
||||
function extractWalkSheetConfig(data, defaults = {}) {
|
||||
if (!data) return defaults;
|
||||
|
||||
return {
|
||||
walk_sheet_title: data.walk_sheet_title !== undefined ? data.walk_sheet_title :
|
||||
data['Walk Sheet Title'] !== undefined ? data['Walk Sheet Title'] :
|
||||
defaults.walk_sheet_title,
|
||||
walk_sheet_subtitle: data.walk_sheet_subtitle !== undefined ? data.walk_sheet_subtitle :
|
||||
data['Walk Sheet Subtitle'] !== undefined ? data['Walk Sheet Subtitle'] :
|
||||
defaults.walk_sheet_subtitle,
|
||||
walk_sheet_footer: data.walk_sheet_footer !== undefined ? data.walk_sheet_footer :
|
||||
data['Walk Sheet Footer'] !== undefined ? data['Walk Sheet Footer'] :
|
||||
defaults.walk_sheet_footer,
|
||||
qr_code_1_url: data.qr_code_1_url !== undefined ? data.qr_code_1_url :
|
||||
data['QR Code 1 URL'] !== undefined ? data['QR Code 1 URL'] :
|
||||
defaults.qr_code_1_url,
|
||||
qr_code_1_label: data.qr_code_1_label !== undefined ? data.qr_code_1_label :
|
||||
data['QR Code 1 Label'] !== undefined ? data['QR Code 1 Label'] :
|
||||
defaults.qr_code_1_label,
|
||||
qr_code_2_url: data.qr_code_2_url !== undefined ? data.qr_code_2_url :
|
||||
data['QR Code 2 URL'] !== undefined ? data['QR Code 2 URL'] :
|
||||
defaults.qr_code_2_url,
|
||||
qr_code_2_label: data.qr_code_2_label !== undefined ? data.qr_code_2_label :
|
||||
data['QR Code 2 Label'] !== undefined ? data['QR Code 2 Label'] :
|
||||
defaults.qr_code_2_label,
|
||||
qr_code_3_url: data.qr_code_3_url !== undefined ? data.qr_code_3_url :
|
||||
data['QR Code 3 URL'] !== undefined ? data['QR Code 3 URL'] :
|
||||
defaults.qr_code_3_url,
|
||||
qr_code_3_label: data.qr_code_3_label !== undefined ? data.qr_code_3_label :
|
||||
data['QR Code 3 Label'] !== undefined ? data['QR Code 3 Label'] :
|
||||
defaults.qr_code_3_label
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
syncGeoFields,
|
||||
validateUrl,
|
||||
getCookieConfig,
|
||||
extractId,
|
||||
validateCoordinates,
|
||||
checkBounds,
|
||||
sanitizeUser,
|
||||
extractWalkSheetConfig
|
||||
};
|
||||
33
map/app/utils/logger.js
Normal file
33
map/app/utils/logger.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const winston = require('winston');
|
||||
const config = require('../config');
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: config.isProduction ? 'info' : 'debug',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.json()
|
||||
),
|
||||
defaultMeta: { service: 'bnkops-map' },
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.simple()
|
||||
)
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// Add file transport in production
|
||||
if (config.isProduction) {
|
||||
logger.add(new winston.transports.File({
|
||||
filename: 'error.log',
|
||||
level: 'error'
|
||||
}));
|
||||
logger.add(new winston.transports.File({
|
||||
filename: 'combined.log'
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = logger;
|
||||
Reference in New Issue
Block a user