Refactor: Complete codebase reorganization and modernization

- Split server.js routes into modular files (server/routes/)
  - departures.js: Departure data endpoints
  - sites.js: Site search and nearby sites
  - config.js: Configuration endpoints

- Reorganized file structure following Node.js best practices:
  - Moved sites-config.json to config/sites.json
  - Moved API_RESPONSE_DOCUMENTATION.md to docs/
  - Moved raspberry-pi-setup.sh to scripts/
  - Archived legacy files to archive/ directory

- Updated all code references to new file locations
- Added archive/ to .gitignore to exclude legacy files from repo
- Updated README.md with new structure and organization
- All functionality tested and working correctly

Version: 1.2.0
This commit is contained in:
2026-01-01 10:51:58 +01:00
parent d15142f1c6
commit 1c44b8ccde
28 changed files with 3196 additions and 3294 deletions

View File

@@ -0,0 +1,89 @@
/**
* Application constants and configuration values
* Centralizes magic numbers, API endpoints, and default settings
*/
const Constants = {
// Server configuration
PORT: 3002,
API_BASE_URL: 'http://localhost:3002',
// API Endpoints
ENDPOINTS: {
DEPARTURES: '/api/departures',
SITES_SEARCH: '/api/sites/search',
SITES_NEARBY: '/api/sites/nearby',
CONFIG_UPDATE: '/api/config/update'
},
// External API URLs
EXTERNAL_APIS: {
SL_TRANSPORT_BASE: 'https://transport.integration.sl.se/v1',
OPENWEATHERMAP_BASE: 'https://api.openweathermap.org/data/2.5'
},
// Refresh intervals (in milliseconds)
REFRESH: {
DEPARTURES: 5000, // 5 seconds
WEATHER: 30 * 60 * 1000, // 30 minutes
DARK_MODE_CHECK: 60000 // 1 minute
},
// Default location (Stockholm, Sweden)
DEFAULT_LOCATION: {
LATITUDE: 59.3293,
LONGITUDE: 18.0686,
NAME: 'Stockholm'
},
// Default site configuration
DEFAULT_SITE: {
ID: '1411',
NAME: 'Ambassaderna'
},
// Default search radius (in meters)
DEFAULT_SEARCH_RADIUS: 5000, // 5km
// Transport mode identifiers
TRANSPORT_MODES: {
BUS: 'bus',
METRO: 'metro',
TRAIN: 'train',
TRAM: 'tram',
SHIP: 'ship'
},
// CSS class names (for consistency)
CSS_CLASSES: {
DARK_MODE: 'dark-mode',
ORIENTATION_NORMAL: 'normal',
ORIENTATION_LANDSCAPE: 'landscape',
ORIENTATION_VERTICAL: 'vertical',
ORIENTATION_UPSIDE_DOWN: 'upsidedown',
ORIENTATION_VERTICAL_REVERSE: 'vertical-reverse'
},
// Time thresholds (in minutes)
TIME_THRESHOLDS: {
URGENT: 5, // Less than 5 minutes is urgent (red)
WITHIN_HOUR: 60 // Within next hour
},
// LocalStorage keys
STORAGE_KEYS: {
CONFIG: 'sl-departures-config'
},
// Timezone
TIMEZONE: 'Europe/Stockholm',
// Date/time formats
DATE_FORMAT: {
TIMEZONE: 'Europe/Stockholm',
LOCALE: 'sv-SE'
}
};
// Export constants
window.Constants = Constants;

100
public/js/utils/logger.js Normal file
View File

@@ -0,0 +1,100 @@
/**
* Logger utility for consistent logging throughout the application
* Supports different log levels and can be configured for production vs development
*/
class Logger {
constructor() {
this.level = this.getLogLevel();
}
/**
* Get the current log level from environment or default to 'info'
* @returns {string} Log level ('debug', 'info', 'warn', 'error')
*/
getLogLevel() {
// In production, you might want to check window.location.hostname
// or a global config. For now, default to 'info'
if (typeof window !== 'undefined' && window.LOG_LEVEL) {
return window.LOG_LEVEL;
}
// Default to 'info' level
return 'info';
}
/**
* Check if a log level should be output
* @param {string} level - Log level to check
* @returns {boolean} Whether to output this level
*/
shouldLog(level) {
const levels = ['debug', 'info', 'warn', 'error'];
const currentLevelIndex = levels.indexOf(this.level);
const messageLevelIndex = levels.indexOf(level);
return messageLevelIndex >= currentLevelIndex;
}
/**
* Format a log message with timestamp
* @param {string} level - Log level
* @param {string} message - Message to log
* @param {any[]} args - Additional arguments
* @returns {string} Formatted message
*/
formatMessage(level, message, args = []) {
const timestamp = new Date().toISOString();
const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
return args.length > 0 ? [prefix, message, ...args] : [prefix, message];
}
/**
* Log a debug message
* @param {string} message - Message to log
* @param {...any} args - Additional arguments
*/
debug(message, ...args) {
if (this.shouldLog('debug')) {
console.debug(...this.formatMessage('debug', message, args));
}
}
/**
* Log an info message
* @param {string} message - Message to log
* @param {...any} args - Additional arguments
*/
info(message, ...args) {
if (this.shouldLog('info')) {
console.info(...this.formatMessage('info', message, args));
}
}
/**
* Log a warning message
* @param {string} message - Message to log
* @param {...any} args - Additional arguments
*/
warn(message, ...args) {
if (this.shouldLog('warn')) {
console.warn(...this.formatMessage('warn', message, args));
}
}
/**
* Log an error message
* @param {string} message - Message to log
* @param {...any} args - Additional arguments
*/
error(message, ...args) {
if (this.shouldLog('error')) {
console.error(...this.formatMessage('error', message, args));
}
}
}
// Create a singleton instance
const logger = new Logger();
// Export both the class and the singleton instance
window.Logger = Logger;
window.logger = logger;