diff --git a/auth-middleware.js b/auth-middleware.js index fab4d6a..cd8cc21 100644 --- a/auth-middleware.js +++ b/auth-middleware.js @@ -40,29 +40,10 @@ function initAuth(app) { // Handle login form submission app.post('/auth/login', (req, res) => { - const { password } = req.body; - const storedHash = process.env.PASSWORD_HASH; - - if (!storedHash) { - console.error('PASSWORD_HASH environment variable not set'); - return res.redirect('/login?error=config'); - } - - // Verify password - bcrypt.compare(password, storedHash, (err, isMatch) => { - if (err) { - console.error('Error verifying password:', err); - return res.redirect('/login?error=server'); - } - - if (isMatch) { - // Set session as authenticated - req.session.authenticated = true; - res.redirect('/'); - } else { - res.redirect('/login?error=invalid'); - } - }); + // This server-side authentication middleware (auth-middleware.js) has been deprecated. + // Authentication for data access was tied to the server-side data API, which is now removed. + // Client-side data encryption could be an alternative if data protection is required. + res.redirect('/login?error=deprecated'); }); // Logout endpoint @@ -73,9 +54,9 @@ function initAuth(app) { // Authentication check endpoint for Nginx auth_request app.get('/auth/check', (req, res) => { - if (req.session.authenticated) { - return res.status(200).send('OK'); - } + // This server-side authentication middleware (auth-middleware.js) has been deprecated. + // Authentication for data access was tied to the server-side data API, which is now removed. + // Client-side data encryption could be an alternative if data protection is required. return res.status(401).send('Unauthorized'); }); diff --git a/data-api.js b/data-api.js index c5265cf..bd18628 100644 --- a/data-api.js +++ b/data-api.js @@ -1,168 +1,3 @@ -/** - * Simple data API for Weight Tracker - * This script handles data storage operations when deployed in Docker - * Includes authentication for password protection - */ - -const fs = require('fs'); -const path = require('path'); -const express = require('express'); -const bodyParser = require('body-parser'); -const cors = require('cors'); -const { initAuth, generateHash } = require('./auth-middleware'); - -// Create Express app -const app = express(); -const port = process.env.PORT || 3000; - -// Data file path in the Docker volume -const DATA_DIR = process.env.DATA_DIR || '/data'; -const DATA_FILE = path.join(DATA_DIR, 'weight-tracker-data.json'); - -// Print data file path for debugging -console.log(`[STARTUP] Data directory: ${DATA_DIR}`); -console.log(`[STARTUP] Data file path: ${DATA_FILE}`); - -// List files in data directory if it exists -if (fs.existsSync(DATA_DIR)) { - try { - const files = fs.readdirSync(DATA_DIR); - console.log(`[STARTUP] Files in ${DATA_DIR}:`, files); - - // If data file exists, log its size and content preview - if (fs.existsSync(DATA_FILE)) { - const stats = fs.statSync(DATA_FILE); - console.log(`[STARTUP] Data file exists: ${DATA_FILE}, size: ${stats.size} bytes`); - - if (stats.size > 0) { - const preview = fs.readFileSync(DATA_FILE, 'utf8').substring(0, 200); - console.log(`[STARTUP] Data file content preview: ${preview}...`); - } else { - console.log(`[STARTUP] Data file is empty`); - } - } else { - console.log(`[STARTUP] Data file does not exist: ${DATA_FILE}`); - } - } catch (error) { - console.error(`[STARTUP] Error reading data directory: ${error.message}`); - } -} else { - console.log(`[STARTUP] Data directory does not exist: ${DATA_DIR}`); -} - -// Middleware -app.use(cors()); -app.use(bodyParser.json({ limit: '5mb' })); -app.use(bodyParser.urlencoded({ extended: true })); // For parsing form data -app.use(express.static('public')); // Serve static files - -// Copy login.html to the correct location for serving -const loginHtmlPath = path.join(__dirname, 'login.html'); -if (fs.existsSync(loginHtmlPath)) { - // Ensure public directory exists - const publicDir = path.join(__dirname, 'public'); - if (!fs.existsSync(publicDir)) { - fs.mkdirSync(publicDir, { recursive: true }); - } - - // Copy login.html to public directory - fs.copyFileSync(loginHtmlPath, path.join(publicDir, 'login.html')); - console.log('Login page copied to public directory'); -} - -// Initialize authentication middleware -initAuth(app); - -// Ensure data directory exists -if (!fs.existsSync(DATA_DIR)) { - fs.mkdirSync(DATA_DIR, { recursive: true }); - console.log(`Created data directory: ${DATA_DIR}`); -} - -// Initialize data file if it doesn't exist -if (!fs.existsSync(DATA_FILE)) { - const defaultData = { - weights: [], - meals: [], - version: '1.0.0' - }; - - fs.writeFileSync(DATA_FILE, JSON.stringify(defaultData, null, 2)); - console.log(`Created initial data file: ${DATA_FILE}`); -} - -// GET endpoint to retrieve data -app.get('/data/weight-tracker-data.json', (req, res) => { - try { - console.log(`[DEBUG] GET request received for ${DATA_FILE}`); - console.log(`[DEBUG] File exists: ${fs.existsSync(DATA_FILE)}`); - - if (fs.existsSync(DATA_FILE)) { - const data = fs.readFileSync(DATA_FILE, 'utf8'); - console.log(`[DEBUG] Data read from file: ${data.substring(0, 100)}...`); - res.setHeader('Content-Type', 'application/json'); - res.send(data); - console.log(`[DEBUG] Data sent to client`); - } else { - console.log(`[DEBUG] Data file not found at ${DATA_FILE}`); - res.status(404).send({ error: 'Data file not found' }); - } - } catch (error) { - console.error('[DEBUG] Error reading data file:', error); - res.status(500).send({ error: 'Failed to read data file' }); - } -}); - -// PUT endpoint to update data -app.put('/data/weight-tracker-data.json', (req, res) => { - try { - console.log(`[DEBUG] PUT request received for ${DATA_FILE}`); - const data = req.body; - - // Log request body summary - console.log(`[DEBUG] Request body received:`, { - hasData: !!data, - hasWeights: data && !!data.weights, - weightCount: data && data.weights ? data.weights.length : 0, - hasMeals: data && !!data.meals, - mealCount: data && data.meals ? data.meals.length : 0 - }); - - // Validate data structure - if (!data || !data.weights || !data.meals) { - console.log(`[DEBUG] Invalid data structure received`); - return res.status(400).send({ error: 'Invalid data structure' }); - } - - // Ensure data directory exists - if (!fs.existsSync(DATA_DIR)) { - console.log(`[DEBUG] Creating data directory: ${DATA_DIR}`); - fs.mkdirSync(DATA_DIR, { recursive: true }); - } - - // Write to file - console.log(`[DEBUG] Writing data to file: ${DATA_FILE}`); - fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2)); - - // Verify file was written - const fileExists = fs.existsSync(DATA_FILE); - console.log(`[DEBUG] File exists after write: ${fileExists}`); - if (fileExists) { - const stats = fs.statSync(DATA_FILE); - console.log(`[DEBUG] File size after write: ${stats.size} bytes`); - } - - res.send({ success: true, message: 'Data saved successfully' }); - console.log(`[DEBUG] Data updated: ${new Date().toISOString()}`); - } catch (error) { - console.error('[DEBUG] Error writing data file:', error); - res.status(500).send({ error: 'Failed to write data file' }); - } -}); - -// Start server -app.listen(port, () => { - console.log(`Data API server running on port ${port}`); - console.log(`Data directory: ${DATA_DIR}`); - console.log(`Data file: ${DATA_FILE}`); -}); +// This server-side data API (data-api.js) has been deprecated. +// Data handling is now managed client-side using browser file APIs +// in accordance with the project vision for a static site. diff --git a/generate-htpasswd.js b/generate-htpasswd.js deleted file mode 100644 index 297f590..0000000 --- a/generate-htpasswd.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Generate .htpasswd file for Nginx basic authentication - * - * This script creates a .htpasswd file from the bcrypt hash provided in the - * PASSWORD_HASH environment variable. - */ - -const fs = require('fs'); -const path = require('path'); - -// Get username from environment variable or use default -const USERNAME = process.env.AUTH_USERNAME || 'user'; - -// Get password hash from environment variable -const passwordHash = process.env.PASSWORD_HASH || '$2a$10$EgxHKjDDFcZKtQY9hl/N4.QvEQHCXVnQXw9dzFYlUDVKOcLMGp9eq'; - -// For Nginx basic auth, we need to use the format: username:{PLAIN}password -// This is simpler and more reliable than trying to use bcrypt hashes with Nginx -// Extract the original password from environment variable if available -const plainPassword = process.env.AUTH_PASSWORD || 'password'; - -// Format for .htpasswd with plaintext password -const htpasswdContent = `${USERNAME}:{PLAIN}${plainPassword}`; - -// Path to the .htpasswd file -const htpasswdPath = '/etc/nginx/.htpasswd'; - -// Write the .htpasswd file -try { - fs.writeFileSync(htpasswdPath, htpasswdContent); - console.log(`Generated .htpasswd file at ${htpasswdPath}`); -} catch (error) { - console.error(`Error generating .htpasswd file: ${error.message}`); - process.exit(1); -} - -console.log('Basic authentication setup complete'); diff --git a/generate-password-hash.js b/generate-password-hash.js deleted file mode 100644 index 115456f..0000000 --- a/generate-password-hash.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Password Hash Generator for Weight Tracker - * - * This utility script generates a bcrypt hash for your password - * that can be used in the PASSWORD_HASH environment variable. - * - * Usage: node generate-password-hash.js - */ - -const bcrypt = require('bcryptjs'); - -async function generateHash() { - // Get password from command line argument - const password = process.argv[2]; - - if (!password) { - console.error('Error: No password provided'); - console.log('Usage: node generate-password-hash.js '); - process.exit(1); - } - - try { - // Generate hash with bcrypt (cost factor 10) - const hash = await bcrypt.hash(password, 10); - - console.log('\nPassword Hash Generated Successfully\n'); - console.log('Copy this hash to your PASSWORD_HASH environment variable in Coolify:'); - console.log('----------------------------------------------------------------'); - console.log(hash); - console.log('----------------------------------------------------------------\n'); - - console.log('For docker-compose.yml, use:'); - console.log(`PASSWORD_HASH=${hash}\n`); - - console.log('For .env file, use:'); - console.log(`PASSWORD_HASH=${hash}\n`); - - } catch (error) { - console.error('Error generating hash:', error); - } -} - -// Run the function -generateHash(); diff --git a/generate-password.js b/generate-password.js deleted file mode 100644 index eb4ce5c..0000000 --- a/generate-password.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Password Hash Generator for Weight Tracker - * - * This script generates a bcrypt hash for a given password that can be used - * with the Weight Tracker application's authentication system. - * - * Usage: - * node generate-password.js - */ - -const bcrypt = require('bcryptjs'); - -// Get password from command line arguments -const password = process.argv[2]; - -if (!password) { - console.error('Error: Password is required'); - console.log('Usage: node generate-password.js '); - process.exit(1); -} - -// Generate salt and hash -const saltRounds = 10; -bcrypt.genSalt(saltRounds, (err, salt) => { - if (err) { - console.error('Error generating salt:', err); - process.exit(1); - } - - bcrypt.hash(password, salt, (err, hash) => { - if (err) { - console.error('Error generating hash:', err); - process.exit(1); - } - - console.log('\nPassword Hash for Nginx Basic Authentication:'); - console.log(hash); - console.log('\nUse this hash in your PASSWORD_HASH environment variable in Coolify.'); - console.log('Example:'); - console.log(`PASSWORD_HASH=${hash}`); - }); -}); diff --git a/inject-password-hash.js b/inject-password-hash.js deleted file mode 100644 index ca8ab9a..0000000 --- a/inject-password-hash.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Password Hash Injector for Weight Tracker - * - * This script injects the password hash from environment variables - * into the HTML file before it's served to the client. - */ - -const fs = require('fs'); -const path = require('path'); - -// Default password hash (can be overridden via environment variables) -const DEFAULT_HASH = '$2a$10$EgxHKjDDFcZKtQY9hl/N4.QvEQHCXVnQXw9dzFYlUDVKOcLMGp9eq'; // hash for "password" - -/** - * Inject password hash into HTML file - * @param {string} htmlPath - Path to the HTML file - * @param {string} passwordHash - Bcrypt password hash - */ -function injectPasswordHash(htmlPath, passwordHash) { - try { - // Read the HTML file - let html = fs.readFileSync(htmlPath, 'utf8'); - - // Replace the placeholder with the actual password hash - html = html.replace('$PASSWORD_HASH$', passwordHash); - - // Write the modified HTML back to the file - fs.writeFileSync(htmlPath, html); - - console.log(`Password hash injected into ${htmlPath}`); - } catch (error) { - console.error(`Error injecting password hash: ${error.message}`); - } -} - -// Get password hash from environment variable -const passwordHash = process.env.PASSWORD_HASH || DEFAULT_HASH; - -// Path to the HTML file -const htmlPath = path.join(__dirname, 'public', 'index.html'); - -// Inject password hash -injectPasswordHash(htmlPath, passwordHash); - -console.log('Password hash injection complete'); diff --git a/js/dataManager.js b/js/dataManager.js index 55767b5..e77041e 100644 --- a/js/dataManager.js +++ b/js/dataManager.js @@ -14,63 +14,19 @@ const DataManager = (() => { // Current application data let appData = {...defaultData}; - // Determine if we're running in Docker (has /data endpoint) - const isDockerEnvironment = () => { - // Always return true for now since we're using Docker with Coolify - return true; - }; - - // API endpoint path for data operations - this must match the API endpoint in data-api.js - // Note: This is NOT a file system path, but an API endpoint URL path - const serverDataPath = '/data/weight-tracker-data.json'; - /** - * Initialize data - load from server if in Docker, otherwise use localStorage + * Initialize data - load from localStorage or use defaults. + * Actual file loading will be a separate user-initiated action. */ const init = async () => { try { console.log('Initializing data manager...'); - console.log('Docker environment detected:', isDockerEnvironment()); - - if (isDockerEnvironment()) { - // Try to load from server-side storage - try { - console.log('Attempting to load data from server at:', serverDataPath); - const response = await fetch(serverDataPath); - console.log('Server response status:', response.status, response.statusText); - - if (response.ok) { - const data = await response.json(); - console.log('Data received from server:', { - hasData: !!data, - hasWeights: data && !!data.weights, - weightCount: data && data.weights ? data.weights.length : 0, - hasMeals: data && !!data.meals, - mealCount: data && data.meals ? data.meals.length : 0 - }); - appData = data; - console.log('Data loaded from server storage successfully'); - } else { - console.log('No server data found or error response. Starting with empty data.'); - appData = {...defaultData}; - console.log('Saving default data to server...'); - await saveData(); // Save default data to server - } - } catch (serverError) { - console.warn('Exception loading from server:', serverError); - console.log('Falling back to localStorage'); - loadFromLocalStorage(); - } - } else { - // Use localStorage in development environment - console.log('Using localStorage in development environment'); - loadFromLocalStorage(); - } + loadFromLocalStorage(); // Load from localStorage or set defaults } catch (error) { console.error('Error initializing data:', error); - appData = {...defaultData}; - console.log('Saving default data due to initialization error'); - saveData(); // Save default data structure on error + appData = {...defaultData}; // Fallback to default data + // Attempt to save default data to localStorage if init failed badly + try { saveDataToLocalStorage(); } catch (e) { console.error('Failed to save default data to LS during init error handling', e); } } }; @@ -90,14 +46,11 @@ const DataManager = (() => { }; /** - * Save data to either server (in Docker) or localStorage (in development) + * Save data to localStorage. + * Actual file saving will be a separate user-initiated action (handled by exportData). */ const saveData = async () => { - if (isDockerEnvironment()) { - return saveDataToServer(); - } else { - return saveDataToLocalStorage(); - } + return saveDataToLocalStorage(); }; /** @@ -114,48 +67,6 @@ const DataManager = (() => { } }; - /** - * Save data to server (Docker environment) - */ - const saveDataToServer = async () => { - try { - console.log('Attempting to save data to server at:', serverDataPath); - console.log('Data to save:', { - weights: appData.weights.length, - meals: appData.meals.length, - version: appData.version - }); - - const response = await fetch(serverDataPath, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(appData) - }); - - if (response.ok) { - const result = await response.json(); - console.log('Server response:', result); - console.log('Data saved to server storage successfully'); - return true; - } else { - console.error('Error saving data to server. Status:', response.status, response.statusText); - try { - const errorData = await response.text(); - console.error('Error details:', errorData); - } catch (e) { - console.error('Could not parse error response'); - } - console.log('Falling back to localStorage'); - return saveDataToLocalStorage(); // Fallback to localStorage - } - } catch (error) { - console.error('Exception while saving data to server:', error); - console.log('Falling back to localStorage'); - return saveDataToLocalStorage(); // Fallback to localStorage - } - }; /** * Add a weight entry @@ -294,32 +205,23 @@ const DataManager = (() => { */ const exportData = () => { try { - const dataStr = JSON.stringify(appData, null, 2); - - // Create a blob instead of using data URI - const blob = new Blob([dataStr], { type: 'application/json' }); + const jsonData = JSON.stringify(appData, null, 2); // Pretty print JSON + const blob = new Blob([jsonData], { type: 'application/json;charset=utf-8;' }); + const link = document.createElement('a'); const url = URL.createObjectURL(blob); - const exportFileDefaultName = `weight-tracker-backup-${formatDateForFilename(new Date())}.json`; + link.setAttribute('href', url); + link.setAttribute('download', `weight-tracker-data-${formatDateForFilename(new Date())}.json`); + link.style.visibility = 'hidden'; - const linkElement = document.createElement('a'); - linkElement.setAttribute('href', url); - linkElement.setAttribute('download', exportFileDefaultName); - linkElement.style.display = 'none'; - - // Add to DOM, trigger click, and clean up - document.body.appendChild(linkElement); - linkElement.click(); - - // Clean up - setTimeout(() => { - document.body.removeChild(linkElement); - URL.revokeObjectURL(url); - }, 100); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + console.log('Data exported as JSON file.'); return true; } catch (error) { - console.error('Error exporting data:', error); + console.error('Error exporting data as JSON:', error); return false; } }; diff --git a/login.html b/login.html deleted file mode 100644 index 172e845..0000000 --- a/login.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - Login - Keep My Weight Tracker - - - - - - - - diff --git a/package.json b/package.json index d284e6a..93dd519 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.425.0", - "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", "connect-redis": "^7.1.0", "cookie-parser": "^1.4.6",