Vu Minh Chien commited on
Commit
a22621a
Β·
1 Parent(s): fbf957b

Improve file permissions and error handling - use dedicated data directory

Browse files
Files changed (2) hide show
  1. Dockerfile +8 -5
  2. server.js +58 -39
Dockerfile CHANGED
@@ -10,16 +10,19 @@ COPY package*.json ./
10
  # Install dependencies
11
  RUN npm ci --only=production
12
 
 
 
 
 
13
  # Copy the rest of the application code
14
  COPY . .
15
 
16
- # Create a non-root user
17
- RUN addgroup -g 1001 -S nodejs
18
- RUN adduser -S nodejs -u 1001
19
 
20
  # Ensure devices.json exists and has proper permissions
21
- RUN touch /app/devices.json && \
22
- chmod 664 /app/devices.json
23
 
24
  # Change ownership of the app directory to the nodejs user
25
  RUN chown -R nodejs:nodejs /app
 
10
  # Install dependencies
11
  RUN npm ci --only=production
12
 
13
+ # Create a non-root user first
14
+ RUN addgroup -g 1001 -S nodejs
15
+ RUN adduser -S nodejs -u 1001
16
+
17
  # Copy the rest of the application code
18
  COPY . .
19
 
20
+ # Create data directory for persistent storage
21
+ RUN mkdir -p /app/data
 
22
 
23
  # Ensure devices.json exists and has proper permissions
24
+ RUN touch /app/data/devices.json && \
25
+ chmod 666 /app/data/devices.json
26
 
27
  # Change ownership of the app directory to the nodejs user
28
  RUN chown -R nodejs:nodejs /app
server.js CHANGED
@@ -3,7 +3,6 @@ const admin = require('firebase-admin');
3
  const cors = require('cors');
4
  const bodyParser = require('body-parser');
5
  const os = require('os');
6
- const HFDatasetManager = require('./hf-dataset');
7
  require('dotenv').config();
8
 
9
  const app = express();
@@ -60,52 +59,71 @@ const sampleProducts = [
60
  const fs = require('fs');
61
  const path = require('path');
62
 
63
- // Initialize HF Dataset Manager
64
- const hfDataset = new HFDatasetManager();
65
 
66
- // Device storage
 
 
 
67
  let deviceTokens = new Map();
68
 
69
- async function loadDevicesFromHF() {
70
  try {
71
- deviceTokens = await hfDataset.loadDevices();
72
- console.log(`πŸ“₯ Loaded ${deviceTokens.size} devices from HF dataset`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  } catch (error) {
74
- console.error('❌ Error loading devices from HF dataset:', error);
 
 
75
  deviceTokens = new Map();
76
  }
77
  }
78
 
79
- async function saveDevicesToHF() {
80
- try {
81
- const success = await hfDataset.saveDevices(deviceTokens);
82
- if (success) {
83
- console.log(`πŸ’Ύ Saved ${deviceTokens.size} devices to HF dataset`);
84
- } else {
85
- console.log('⚠️ Failed to save devices to HF dataset');
86
  }
87
- } catch (error) {
88
- console.error('❌ Error saving devices to HF dataset:', error);
89
  }
90
- }
91
-
92
- // Initialize HF dataset and load devices on startup
93
- async function initializeServer() {
94
  try {
95
- // Create dataset if it doesn't exist
96
- await hfDataset.createDatasetIfNotExists();
97
-
98
- // Load devices from HF dataset
99
- await loadDevicesFromHF();
100
-
101
- console.log('βœ… Server initialization completed');
102
  } catch (error) {
103
- console.error('❌ Error during server initialization:', error);
 
 
104
  }
105
  }
106
 
107
- // Start initialization (don't await here to avoid blocking)
108
- initializeServer();
109
 
110
  // Routes
111
 
@@ -115,7 +133,7 @@ app.get('/', (req, res) => {
115
  message: 'Houzou Medical Notification Server',
116
  status: 'running',
117
  timestamp: new Date().toISOString(),
118
- hfDataset: hfDataset.getStatus(),
119
  deviceCount: deviceTokens.size
120
  });
121
  });
@@ -426,8 +444,8 @@ app.post('/register-token', async (req, res) => {
426
  console.warn(`⚠️ Failed to subscribe device to topic: ${topicError.message}`);
427
  }
428
 
429
- // Save to HF dataset
430
- saveDevicesToHF();
431
 
432
  res.json({
433
  success: true,
@@ -469,8 +487,8 @@ app.post('/unregister-token', async (req, res) => {
469
  console.log(`πŸ“± Token unregistered for device ${deviceId}`);
470
  }
471
 
472
- // Save to HF dataset
473
- saveDevicesToHF();
474
 
475
  res.json({
476
  success: true,
@@ -524,7 +542,7 @@ app.post('/debug-add-device', (req, res) => {
524
  registered: true
525
  });
526
 
527
- saveDevicesToHF();
528
 
529
  console.log(`πŸ”§ Debug: Added device ${deviceId} (${platform})`);
530
 
@@ -635,7 +653,7 @@ app.post('/send-broadcast-notification', async (req, res) => {
635
  }
636
  }
637
  });
638
- saveDevicesToHF();
639
  } else {
640
  console.log(`⚠️ No tokens removed - all failures appear to be temporary`);
641
  }
@@ -694,7 +712,8 @@ app.listen(PORT, () => {
694
  console.log(` Local: http://localhost:${PORT}`);
695
  console.log(` Network: http://${localIP}:${PORT}`);
696
  console.log(`\nπŸ”§ For iPhone app, use: http://${localIP}:${PORT}`);
697
- console.log(`πŸ€— Devices stored in HF dataset: ${hfDataset.datasetId}`);
 
698
  });
699
 
700
  module.exports = app;
 
3
  const cors = require('cors');
4
  const bodyParser = require('body-parser');
5
  const os = require('os');
 
6
  require('dotenv').config();
7
 
8
  const app = express();
 
59
  const fs = require('fs');
60
  const path = require('path');
61
 
62
+ // File storage for FCM tokens
63
+ const DEVICES_FILE = path.join(__dirname, 'data', 'devices.json');
64
 
65
+ // Fallback to in-memory storage if file operations fail
66
+ let fileOperationsEnabled = true;
67
+
68
+ // Load devices from file or create empty storage
69
  let deviceTokens = new Map();
70
 
71
+ function loadDevicesFromFile() {
72
  try {
73
+ // Ensure data directory exists
74
+ const dataDir = path.dirname(DEVICES_FILE);
75
+ if (!fs.existsSync(dataDir)) {
76
+ console.log('πŸ“ Creating data directory');
77
+ fs.mkdirSync(dataDir, { recursive: true });
78
+ }
79
+
80
+ if (fs.existsSync(DEVICES_FILE)) {
81
+ const data = fs.readFileSync(DEVICES_FILE, 'utf8');
82
+ const devicesArray = JSON.parse(data);
83
+ deviceTokens = new Map(devicesArray);
84
+ console.log(`πŸ“ Loaded ${deviceTokens.size} devices from file`);
85
+ } else {
86
+ console.log('πŸ“ No devices file found, starting fresh');
87
+ // Try to create an empty file to test write permissions
88
+ try {
89
+ fs.writeFileSync(DEVICES_FILE, '[]');
90
+ console.log('πŸ“ Created empty devices file');
91
+ } catch (writeError) {
92
+ console.warn('⚠️ Cannot create devices file, will use in-memory storage only');
93
+ console.warn('⚠️ File write error:', writeError.message);
94
+ fileOperationsEnabled = false;
95
+ }
96
+ }
97
  } catch (error) {
98
+ console.error('❌ Error loading devices file:', error.message);
99
+ console.log('⚠️ Using in-memory storage only');
100
+ fileOperationsEnabled = false;
101
  deviceTokens = new Map();
102
  }
103
  }
104
 
105
+ function saveDevicesToFile() {
106
+ if (!fileOperationsEnabled) {
107
+ // Only log this occasionally to avoid spam
108
+ if (Math.random() < 0.1) {
109
+ console.log('πŸ’Ύ File operations disabled, keeping devices in memory only');
 
 
110
  }
111
+ return;
 
112
  }
113
+
 
 
 
114
  try {
115
+ const devicesArray = Array.from(deviceTokens.entries());
116
+ fs.writeFileSync(DEVICES_FILE, JSON.stringify(devicesArray, null, 2));
117
+ console.log(`πŸ’Ύ Saved ${deviceTokens.size} devices to file`);
 
 
 
 
118
  } catch (error) {
119
+ console.error('❌ Error saving devices file:', error.message);
120
+ console.log('⚠️ Disabling file operations, using in-memory storage only');
121
+ fileOperationsEnabled = false;
122
  }
123
  }
124
 
125
+ // Load devices on startup
126
+ loadDevicesFromFile();
127
 
128
  // Routes
129
 
 
133
  message: 'Houzou Medical Notification Server',
134
  status: 'running',
135
  timestamp: new Date().toISOString(),
136
+ fileOperationsEnabled: fileOperationsEnabled,
137
  deviceCount: deviceTokens.size
138
  });
139
  });
 
444
  console.warn(`⚠️ Failed to subscribe device to topic: ${topicError.message}`);
445
  }
446
 
447
+ // Save to file
448
+ saveDevicesToFile();
449
 
450
  res.json({
451
  success: true,
 
487
  console.log(`πŸ“± Token unregistered for device ${deviceId}`);
488
  }
489
 
490
+ // Save to file
491
+ saveDevicesToFile();
492
 
493
  res.json({
494
  success: true,
 
542
  registered: true
543
  });
544
 
545
+ saveDevicesToFile();
546
 
547
  console.log(`πŸ”§ Debug: Added device ${deviceId} (${platform})`);
548
 
 
653
  }
654
  }
655
  });
656
+ saveDevicesToFile();
657
  } else {
658
  console.log(`⚠️ No tokens removed - all failures appear to be temporary`);
659
  }
 
712
  console.log(` Local: http://localhost:${PORT}`);
713
  console.log(` Network: http://${localIP}:${PORT}`);
714
  console.log(`\nπŸ”§ For iPhone app, use: http://${localIP}:${PORT}`);
715
+ console.log(`πŸ“ Devices storage: ${fileOperationsEnabled ? DEVICES_FILE : 'In-memory only'}`);
716
+ console.log(`πŸ’Ύ File operations: ${fileOperationsEnabled ? 'Enabled' : 'Disabled'}`);
717
  });
718
 
719
  module.exports = app;