freealberta/map/app/utils/helpers.js

202 lines
7.3 KiB
JavaScript

// 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']) {
// Keep as strings to preserve precision
const lat = typeof data.latitude === 'string' ? data.latitude : String(data.latitude);
const lng = typeof data.longitude === 'string' ? data.longitude : String(data.longitude);
data['Geo-Location'] = `${lat};${lng}`;
data.geodata = `${lat};${lng}`;
// Keep original values without parsing
data.latitude = lat;
data.longitude = 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) {
// Keep as strings to preserve precision
const lat = parts[0].trim();
const lng = parts[1].trim();
// Only validate they're numeric, don't convert
if (!isNaN(parseFloat(lat)) && !isNaN(parseFloat(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 = parts[0].trim();
const lng = parts[1].trim();
if (!isNaN(parseFloat(lat)) && !isNaN(parseFloat(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 coordinates
function validateCoordinates(lat, lng) {
// Keep original string values
const latStr = typeof lat === 'string' ? lat : String(lat);
const lngStr = typeof lng === 'string' ? lng : String(lng);
// Parse only for validation
const latitude = parseFloat(latStr);
const longitude = parseFloat(lngStr);
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 the original string values to preserve precision
return { valid: true, latitude: latStr, longitude: lngStr };
}
// Check if coordinates are within bounds
function checkBounds(lat, lng, bounds) {
if (!bounds) return true;
// Parse only for comparison
const latitude = parseFloat(lat);
const longitude = parseFloat(lng);
return latitude <= bounds.north &&
latitude >= bounds.south &&
longitude <= bounds.east &&
longitude >= bounds.west;
}
// 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;
}
// 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
};