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;
|