Spaces:
Sleeping
Sleeping
Vu Minh Chien
commited on
Commit
Β·
8896972
1
Parent(s):
758d55d
Implement HF Dataset for persistent device storage
Browse files- Add HFDatasetManager for HF dataset operations
- Replace file-based storage with HF dataset
- Add automatic dataset creation and management
- Implement fallback to in-memory storage
- Update all device save/load operations
- Add comprehensive error handling
- Include setup documentation
- HF-DATASET-SETUP.md +169 -0
- hf-dataset.js +177 -0
- package.json +3 -1
- server.js +40 -47
HF-DATASET-SETUP.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# π€ Hugging Face Dataset Setup Guide
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
This server now uses **Hugging Face Dataset** for persistent storage of device tokens instead of local files. This ensures that device data is preserved across Space restarts and provides better data management.
|
| 6 |
+
|
| 7 |
+
## π Quick Setup
|
| 8 |
+
|
| 9 |
+
### 1. Generate Hugging Face Token
|
| 10 |
+
|
| 11 |
+
1. Go to [Hugging Face Settings](https://huggingface.co/settings/tokens)
|
| 12 |
+
2. Click "Create new token"
|
| 13 |
+
3. Name: `houzou-notification-server`
|
| 14 |
+
4. Type: **Write** (required for dataset operations)
|
| 15 |
+
5. Copy the token
|
| 16 |
+
|
| 17 |
+
### 2. Configure Space Secrets
|
| 18 |
+
|
| 19 |
+
1. Go to your Space Settings β **Secrets and variables**
|
| 20 |
+
2. Add these secrets:
|
| 21 |
+
|
| 22 |
+
```
|
| 23 |
+
HF_TOKEN = hf_your_token_here
|
| 24 |
+
HF_DATASET_ID = Detomo/houzou-devices (optional, defaults to this)
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
### 3. Required Secrets Summary
|
| 28 |
+
|
| 29 |
+
Your Space needs these secrets:
|
| 30 |
+
|
| 31 |
+
```
|
| 32 |
+
FIREBASE_SERVICE_ACCOUNT = {your firebase config JSON}
|
| 33 |
+
HF_TOKEN = hf_your_write_token_here
|
| 34 |
+
HF_DATASET_ID = Detomo/houzou-devices (optional)
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
## π Dataset Structure
|
| 38 |
+
|
| 39 |
+
The dataset will be automatically created with:
|
| 40 |
+
|
| 41 |
+
```
|
| 42 |
+
datasets/Detomo/houzou-devices/
|
| 43 |
+
βββ README.md # Dataset documentation
|
| 44 |
+
βββ devices.json # Device tokens and metadata
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
### devices.json Format:
|
| 48 |
+
```json
|
| 49 |
+
[
|
| 50 |
+
[
|
| 51 |
+
"device-id-1",
|
| 52 |
+
{
|
| 53 |
+
"token": "fcm_token_here",
|
| 54 |
+
"lastUpdated": "2024-01-01T00:00:00.000Z",
|
| 55 |
+
"platform": "iOS",
|
| 56 |
+
"appVersion": "1.0.0",
|
| 57 |
+
"registered": true
|
| 58 |
+
}
|
| 59 |
+
]
|
| 60 |
+
]
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
## π§ Features
|
| 64 |
+
|
| 65 |
+
### β
Automatic Dataset Management
|
| 66 |
+
- Creates dataset if it doesn't exist
|
| 67 |
+
- Handles permissions automatically
|
| 68 |
+
- Provides fallback to in-memory storage
|
| 69 |
+
|
| 70 |
+
### β
Persistent Storage
|
| 71 |
+
- Device tokens survive Space restarts
|
| 72 |
+
- Automatic backup to HF dataset
|
| 73 |
+
- Version control for device data
|
| 74 |
+
|
| 75 |
+
### β
Error Handling
|
| 76 |
+
- Graceful fallback if HF API is unavailable
|
| 77 |
+
- Detailed logging for troubleshooting
|
| 78 |
+
- Continues operation even if dataset access fails
|
| 79 |
+
|
| 80 |
+
## π Verification
|
| 81 |
+
|
| 82 |
+
### Check Dataset Status
|
| 83 |
+
```bash
|
| 84 |
+
curl https://your-space-url.hf.space/
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
Response includes:
|
| 88 |
+
```json
|
| 89 |
+
{
|
| 90 |
+
"message": "Houzou Medical Notification Server",
|
| 91 |
+
"status": "running",
|
| 92 |
+
"hfDataset": {
|
| 93 |
+
"enabled": true,
|
| 94 |
+
"datasetId": "Detomo/houzou-devices",
|
| 95 |
+
"hasToken": true
|
| 96 |
+
},
|
| 97 |
+
"deviceCount": 5
|
| 98 |
+
}
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
### View Dataset
|
| 102 |
+
1. Go to: https://huggingface.co/datasets/Detomo/houzou-devices
|
| 103 |
+
2. Check `devices.json` for your device data
|
| 104 |
+
|
| 105 |
+
## π Troubleshooting
|
| 106 |
+
|
| 107 |
+
### Dataset Not Created
|
| 108 |
+
- Verify `HF_TOKEN` has **Write** permissions
|
| 109 |
+
- Check Space logs for error messages
|
| 110 |
+
- Ensure token is valid and not expired
|
| 111 |
+
|
| 112 |
+
### Data Not Saving
|
| 113 |
+
- Check if HF API is responding
|
| 114 |
+
- Verify dataset permissions
|
| 115 |
+
- Server will continue with in-memory storage as fallback
|
| 116 |
+
|
| 117 |
+
### Common Errors
|
| 118 |
+
|
| 119 |
+
**403 Forbidden**
|
| 120 |
+
- Token doesn't have write permissions
|
| 121 |
+
- Token is expired or invalid
|
| 122 |
+
|
| 123 |
+
**404 Not Found**
|
| 124 |
+
- Dataset doesn't exist (will be auto-created)
|
| 125 |
+
- Wrong dataset ID
|
| 126 |
+
|
| 127 |
+
**Rate Limiting**
|
| 128 |
+
- HF API has rate limits
|
| 129 |
+
- Server will retry automatically
|
| 130 |
+
|
| 131 |
+
## π Migration from File Storage
|
| 132 |
+
|
| 133 |
+
If you're migrating from file-based storage:
|
| 134 |
+
|
| 135 |
+
1. **Backup existing data**: Download your current `devices.json`
|
| 136 |
+
2. **Set up HF dataset**: Follow steps above
|
| 137 |
+
3. **Deploy updated server**: The server will automatically use HF dataset
|
| 138 |
+
4. **Verify**: Check that devices are loaded correctly
|
| 139 |
+
|
| 140 |
+
## π Environment Variables
|
| 141 |
+
|
| 142 |
+
```bash
|
| 143 |
+
# Required
|
| 144 |
+
HF_TOKEN=hf_your_write_token_here
|
| 145 |
+
FIREBASE_SERVICE_ACCOUNT={"type":"service_account",...}
|
| 146 |
+
|
| 147 |
+
# Optional
|
| 148 |
+
HF_DATASET_ID=Detomo/houzou-devices
|
| 149 |
+
PORT=7860
|
| 150 |
+
NODE_ENV=production
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
## π― Benefits
|
| 154 |
+
|
| 155 |
+
- **β
Persistent**: Data survives Space restarts
|
| 156 |
+
- **β
Reliable**: Automatic fallback mechanisms
|
| 157 |
+
- **β
Scalable**: No file permission issues
|
| 158 |
+
- **β
Traceable**: Version history in dataset
|
| 159 |
+
- **β
Secure**: Tokens stored in HF infrastructure
|
| 160 |
+
|
| 161 |
+
## π Links
|
| 162 |
+
|
| 163 |
+
- [Hugging Face Hub API](https://huggingface.co/docs/hub/api)
|
| 164 |
+
- [Dataset Management](https://huggingface.co/docs/datasets/)
|
| 165 |
+
- [Access Tokens](https://huggingface.co/settings/tokens)
|
| 166 |
+
|
| 167 |
+
---
|
| 168 |
+
|
| 169 |
+
**Ready to deploy!** Your notification server will now use HF Dataset for persistent storage. π
|
hf-dataset.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const { HfApi } = require("@huggingface/hub");
|
| 2 |
+
const fetch = require('node-fetch');
|
| 3 |
+
|
| 4 |
+
class HFDatasetManager {
|
| 5 |
+
constructor() {
|
| 6 |
+
this.hfToken = process.env.HF_TOKEN;
|
| 7 |
+
this.datasetId = process.env.HF_DATASET_ID || "Detomo/houzou-devices";
|
| 8 |
+
this.fileName = "devices.json";
|
| 9 |
+
this.hfApi = new HfApi({ accessToken: this.hfToken });
|
| 10 |
+
this.isEnabled = !!this.hfToken;
|
| 11 |
+
|
| 12 |
+
console.log(`π€ HF Dataset Manager initialized`);
|
| 13 |
+
console.log(` Dataset: ${this.datasetId}`);
|
| 14 |
+
console.log(` Enabled: ${this.isEnabled}`);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
async loadDevices() {
|
| 18 |
+
if (!this.isEnabled) {
|
| 19 |
+
console.log('β οΈ HF Dataset disabled - no token provided');
|
| 20 |
+
return new Map();
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
try {
|
| 24 |
+
console.log('π₯ Loading devices from HF dataset...');
|
| 25 |
+
|
| 26 |
+
const fileUrl = `https://huggingface.co/datasets/${this.datasetId}/resolve/main/${this.fileName}`;
|
| 27 |
+
const response = await fetch(fileUrl, {
|
| 28 |
+
headers: {
|
| 29 |
+
'Authorization': `Bearer ${this.hfToken}`
|
| 30 |
+
}
|
| 31 |
+
});
|
| 32 |
+
|
| 33 |
+
if (response.status === 404) {
|
| 34 |
+
console.log('π No devices file found in dataset, creating new one');
|
| 35 |
+
await this.saveDevices(new Map());
|
| 36 |
+
return new Map();
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
if (!response.ok) {
|
| 40 |
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
const data = await response.json();
|
| 44 |
+
const deviceMap = new Map(data);
|
| 45 |
+
|
| 46 |
+
console.log(`π₯ Loaded ${deviceMap.size} devices from HF dataset`);
|
| 47 |
+
return deviceMap;
|
| 48 |
+
|
| 49 |
+
} catch (error) {
|
| 50 |
+
console.error('β Error loading devices from HF dataset:', error);
|
| 51 |
+
console.log('β οΈ Falling back to empty device list');
|
| 52 |
+
return new Map();
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
async saveDevices(deviceMap) {
|
| 57 |
+
if (!this.isEnabled) {
|
| 58 |
+
console.log('β οΈ HF Dataset disabled - cannot save devices');
|
| 59 |
+
return false;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
try {
|
| 63 |
+
console.log(`πΎ Saving ${deviceMap.size} devices to HF dataset...`);
|
| 64 |
+
|
| 65 |
+
const devicesArray = Array.from(deviceMap.entries());
|
| 66 |
+
const jsonData = JSON.stringify(devicesArray, null, 2);
|
| 67 |
+
|
| 68 |
+
// Create a blob from the JSON data
|
| 69 |
+
const blob = new Blob([jsonData], { type: 'application/json' });
|
| 70 |
+
|
| 71 |
+
// Upload to HF dataset
|
| 72 |
+
await this.hfApi.uploadFile({
|
| 73 |
+
repo: this.datasetId,
|
| 74 |
+
file: {
|
| 75 |
+
path: this.fileName,
|
| 76 |
+
content: blob
|
| 77 |
+
},
|
| 78 |
+
commitMessage: `Update devices data - ${new Date().toISOString()}`,
|
| 79 |
+
repoType: "dataset"
|
| 80 |
+
});
|
| 81 |
+
|
| 82 |
+
console.log(`β
Successfully saved ${deviceMap.size} devices to HF dataset`);
|
| 83 |
+
return true;
|
| 84 |
+
|
| 85 |
+
} catch (error) {
|
| 86 |
+
console.error('β Error saving devices to HF dataset:', error);
|
| 87 |
+
return false;
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
async createDatasetIfNotExists() {
|
| 92 |
+
if (!this.isEnabled) {
|
| 93 |
+
console.log('β οΈ HF Dataset disabled - cannot create dataset');
|
| 94 |
+
return false;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
try {
|
| 98 |
+
console.log('π Checking if dataset exists...');
|
| 99 |
+
|
| 100 |
+
// Try to get dataset info
|
| 101 |
+
const datasetInfo = await this.hfApi.datasetInfo({
|
| 102 |
+
repo: this.datasetId
|
| 103 |
+
});
|
| 104 |
+
|
| 105 |
+
console.log('β
Dataset already exists');
|
| 106 |
+
return true;
|
| 107 |
+
|
| 108 |
+
} catch (error) {
|
| 109 |
+
if (error.statusCode === 404) {
|
| 110 |
+
console.log('π Dataset not found, creating new one...');
|
| 111 |
+
|
| 112 |
+
try {
|
| 113 |
+
// Create the dataset
|
| 114 |
+
await this.hfApi.createRepo({
|
| 115 |
+
repo: this.datasetId,
|
| 116 |
+
type: "dataset",
|
| 117 |
+
private: false
|
| 118 |
+
});
|
| 119 |
+
|
| 120 |
+
console.log('β
Dataset created successfully');
|
| 121 |
+
|
| 122 |
+
// Create initial README
|
| 123 |
+
const readmeContent = `# Houzou Medical Devices Dataset
|
| 124 |
+
|
| 125 |
+
This dataset stores FCM tokens and device information for the Houzou Medical app notification system.
|
| 126 |
+
|
| 127 |
+
## Files
|
| 128 |
+
|
| 129 |
+
- \`devices.json\`: Contains device tokens and metadata
|
| 130 |
+
|
| 131 |
+
## Usage
|
| 132 |
+
|
| 133 |
+
This dataset is automatically managed by the Houzou Medical Notification Server.
|
| 134 |
+
|
| 135 |
+
Last updated: ${new Date().toISOString()}
|
| 136 |
+
`;
|
| 137 |
+
|
| 138 |
+
await this.hfApi.uploadFile({
|
| 139 |
+
repo: this.datasetId,
|
| 140 |
+
file: {
|
| 141 |
+
path: "README.md",
|
| 142 |
+
content: new Blob([readmeContent], { type: 'text/markdown' })
|
| 143 |
+
},
|
| 144 |
+
commitMessage: "Initial dataset setup",
|
| 145 |
+
repoType: "dataset"
|
| 146 |
+
});
|
| 147 |
+
|
| 148 |
+
// Create initial empty devices file
|
| 149 |
+
await this.saveDevices(new Map());
|
| 150 |
+
|
| 151 |
+
return true;
|
| 152 |
+
|
| 153 |
+
} catch (createError) {
|
| 154 |
+
console.error('β Error creating dataset:', createError);
|
| 155 |
+
return false;
|
| 156 |
+
}
|
| 157 |
+
} else {
|
| 158 |
+
console.error('β Error checking dataset:', error);
|
| 159 |
+
return false;
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
isReady() {
|
| 165 |
+
return this.isEnabled;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
getStatus() {
|
| 169 |
+
return {
|
| 170 |
+
enabled: this.isEnabled,
|
| 171 |
+
datasetId: this.datasetId,
|
| 172 |
+
hasToken: !!this.hfToken
|
| 173 |
+
};
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
module.exports = HFDatasetManager;
|
package.json
CHANGED
|
@@ -12,7 +12,9 @@
|
|
| 12 |
"firebase-admin": "^12.0.0",
|
| 13 |
"cors": "^2.8.5",
|
| 14 |
"body-parser": "^1.20.2",
|
| 15 |
-
"dotenv": "^16.3.1"
|
|
|
|
|
|
|
| 16 |
},
|
| 17 |
"devDependencies": {
|
| 18 |
"nodemon": "^3.0.2"
|
|
|
|
| 12 |
"firebase-admin": "^12.0.0",
|
| 13 |
"cors": "^2.8.5",
|
| 14 |
"body-parser": "^1.20.2",
|
| 15 |
+
"dotenv": "^16.3.1",
|
| 16 |
+
"@huggingface/hub": "^0.15.1",
|
| 17 |
+
"node-fetch": "^3.3.2"
|
| 18 |
},
|
| 19 |
"devDependencies": {
|
| 20 |
"nodemon": "^3.0.2"
|
server.js
CHANGED
|
@@ -3,6 +3,7 @@ const admin = require('firebase-admin');
|
|
| 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,60 +60,52 @@ const sampleProducts = [
|
|
| 59 |
const fs = require('fs');
|
| 60 |
const path = require('path');
|
| 61 |
|
| 62 |
-
//
|
| 63 |
-
const
|
| 64 |
|
| 65 |
-
//
|
| 66 |
-
let fileOperationsEnabled = true;
|
| 67 |
-
|
| 68 |
-
// Load devices from file or create empty storage
|
| 69 |
let deviceTokens = new Map();
|
| 70 |
|
| 71 |
-
function
|
| 72 |
try {
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
const devicesArray = JSON.parse(data);
|
| 76 |
-
deviceTokens = new Map(devicesArray);
|
| 77 |
-
console.log(`π Loaded ${deviceTokens.size} devices from file`);
|
| 78 |
-
} else {
|
| 79 |
-
console.log('π No devices file found, starting fresh');
|
| 80 |
-
// Try to create an empty file to test write permissions
|
| 81 |
-
try {
|
| 82 |
-
fs.writeFileSync(DEVICES_FILE, '[]');
|
| 83 |
-
console.log('π Created empty devices file');
|
| 84 |
-
} catch (writeError) {
|
| 85 |
-
console.warn('β οΈ Cannot create devices file, will use in-memory storage only');
|
| 86 |
-
fileOperationsEnabled = false;
|
| 87 |
-
}
|
| 88 |
-
}
|
| 89 |
} catch (error) {
|
| 90 |
-
console.error('β Error loading devices
|
| 91 |
-
console.log('β οΈ Using in-memory storage only');
|
| 92 |
-
fileOperationsEnabled = false;
|
| 93 |
deviceTokens = new Map();
|
| 94 |
}
|
| 95 |
}
|
| 96 |
|
| 97 |
-
function
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
}
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
| 103 |
try {
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
} catch (error) {
|
| 108 |
-
console.error('β Error
|
| 109 |
-
console.log('β οΈ Disabling file operations, using in-memory storage only');
|
| 110 |
-
fileOperationsEnabled = false;
|
| 111 |
}
|
| 112 |
}
|
| 113 |
|
| 114 |
-
//
|
| 115 |
-
|
| 116 |
|
| 117 |
// Routes
|
| 118 |
|
|
@@ -122,7 +115,7 @@ app.get('/', (req, res) => {
|
|
| 122 |
message: 'Houzou Medical Notification Server',
|
| 123 |
status: 'running',
|
| 124 |
timestamp: new Date().toISOString(),
|
| 125 |
-
|
| 126 |
deviceCount: deviceTokens.size
|
| 127 |
});
|
| 128 |
});
|
|
@@ -433,8 +426,8 @@ app.post('/register-token', async (req, res) => {
|
|
| 433 |
console.warn(`β οΈ Failed to subscribe device to topic: ${topicError.message}`);
|
| 434 |
}
|
| 435 |
|
| 436 |
-
// Save to
|
| 437 |
-
|
| 438 |
|
| 439 |
res.json({
|
| 440 |
success: true,
|
|
@@ -476,8 +469,8 @@ app.post('/unregister-token', async (req, res) => {
|
|
| 476 |
console.log(`π± Token unregistered for device ${deviceId}`);
|
| 477 |
}
|
| 478 |
|
| 479 |
-
// Save to
|
| 480 |
-
|
| 481 |
|
| 482 |
res.json({
|
| 483 |
success: true,
|
|
@@ -531,7 +524,7 @@ app.post('/debug-add-device', (req, res) => {
|
|
| 531 |
registered: true
|
| 532 |
});
|
| 533 |
|
| 534 |
-
|
| 535 |
|
| 536 |
console.log(`π§ Debug: Added device ${deviceId} (${platform})`);
|
| 537 |
|
|
@@ -642,7 +635,7 @@ app.post('/send-broadcast-notification', async (req, res) => {
|
|
| 642 |
}
|
| 643 |
}
|
| 644 |
});
|
| 645 |
-
|
| 646 |
} else {
|
| 647 |
console.log(`β οΈ No tokens removed - all failures appear to be temporary`);
|
| 648 |
}
|
|
@@ -701,7 +694,7 @@ app.listen(PORT, () => {
|
|
| 701 |
console.log(` Local: http://localhost:${PORT}`);
|
| 702 |
console.log(` Network: http://${localIP}:${PORT}`);
|
| 703 |
console.log(`\nπ§ For iPhone app, use: http://${localIP}:${PORT}`);
|
| 704 |
-
console.log(
|
| 705 |
});
|
| 706 |
|
| 707 |
module.exports = app;
|
|
|
|
| 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 |
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 |
message: 'Houzou Medical Notification Server',
|
| 116 |
status: 'running',
|
| 117 |
timestamp: new Date().toISOString(),
|
| 118 |
+
hfDataset: hfDataset.getStatus(),
|
| 119 |
deviceCount: deviceTokens.size
|
| 120 |
});
|
| 121 |
});
|
|
|
|
| 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 |
console.log(`π± Token unregistered for device ${deviceId}`);
|
| 470 |
}
|
| 471 |
|
| 472 |
+
// Save to HF dataset
|
| 473 |
+
saveDevicesToHF();
|
| 474 |
|
| 475 |
res.json({
|
| 476 |
success: true,
|
|
|
|
| 524 |
registered: true
|
| 525 |
});
|
| 526 |
|
| 527 |
+
saveDevicesToHF();
|
| 528 |
|
| 529 |
console.log(`π§ Debug: Added device ${deviceId} (${platform})`);
|
| 530 |
|
|
|
|
| 635 |
}
|
| 636 |
}
|
| 637 |
});
|
| 638 |
+
saveDevicesToHF();
|
| 639 |
} else {
|
| 640 |
console.log(`β οΈ No tokens removed - all failures appear to be temporary`);
|
| 641 |
}
|
|
|
|
| 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;
|