Spaces:
Sleeping
Sleeping
Vu Minh Chien
commited on
Commit
Β·
a22621a
1
Parent(s):
fbf957b
Improve file permissions and error handling - use dedicated data directory
Browse files- Dockerfile +8 -5
- 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
|
17 |
-
RUN
|
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
|
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 |
-
//
|
64 |
-
const
|
65 |
|
66 |
-
//
|
|
|
|
|
|
|
67 |
let deviceTokens = new Map();
|
68 |
|
69 |
-
|
70 |
try {
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
} catch (error) {
|
74 |
-
console.error('β Error loading devices
|
|
|
|
|
75 |
deviceTokens = new Map();
|
76 |
}
|
77 |
}
|
78 |
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
if (
|
83 |
-
console.log(
|
84 |
-
} else {
|
85 |
-
console.log('β οΈ Failed to save devices to HF dataset');
|
86 |
}
|
87 |
-
|
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 |
-
|
96 |
-
|
97 |
-
|
98 |
-
// Load devices from HF dataset
|
99 |
-
await loadDevicesFromHF();
|
100 |
-
|
101 |
-
console.log('β
Server initialization completed');
|
102 |
} catch (error) {
|
103 |
-
console.error('β Error
|
|
|
|
|
104 |
}
|
105 |
}
|
106 |
|
107 |
-
//
|
108 |
-
|
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 |
-
|
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
|
430 |
-
|
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
|
473 |
-
|
474 |
|
475 |
res.json({
|
476 |
success: true,
|
@@ -524,7 +542,7 @@ app.post('/debug-add-device', (req, res) => {
|
|
524 |
registered: true
|
525 |
});
|
526 |
|
527 |
-
|
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 |
-
|
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(
|
|
|
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;
|