- Add sendEmailViaLegacyAPI() function in legacyAPI.js - Update email.js to use Legacy API instead of Brevo - All emails (OTP, password reset, invitations) now go through eluxnetworks.net/function/well-api/api with send_message method - Fix syntax errors in alarm.js, beneficiaries.js, invitations.js (missing console.log statements) Brevo API is no longer used - credentials can be removed from .env 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
355 lines
11 KiB
JavaScript
355 lines
11 KiB
JavaScript
/**
|
|
* Legacy API Integration Service
|
|
*
|
|
* This service handles communication with the Legacy WellNuo API
|
|
* at eluxnetworks.net for device management and deployments.
|
|
*/
|
|
|
|
const axios = require('axios');
|
|
|
|
const LEGACY_API_BASE = 'https://eluxnetworks.net/function/well-api/api';
|
|
|
|
/**
|
|
* Room location codes mapping
|
|
* These numeric codes are used by Legacy API to identify room types
|
|
*/
|
|
const ROOM_LOCATIONS = {
|
|
'Bedroom': 102,
|
|
'Living Room': 103,
|
|
'Kitchen': 104,
|
|
'Bathroom': 105,
|
|
'Hallway': 106,
|
|
'Office': 107,
|
|
'Garage': 108,
|
|
'Dining Room': 109,
|
|
'Basement': 110,
|
|
'Other': 200
|
|
};
|
|
|
|
const LOCATION_NAMES = Object.fromEntries(
|
|
Object.entries(ROOM_LOCATIONS).map(([k, v]) => [v, k])
|
|
);
|
|
|
|
/**
|
|
* Get authentication token from Legacy API
|
|
* @param {string} username - Legacy API username
|
|
* @param {string} password - Legacy API password
|
|
* @returns {Promise<string>} Access token
|
|
*/
|
|
async function getLegacyToken(username, password) {
|
|
const formData = new URLSearchParams({
|
|
function: 'credentials',
|
|
user_name: username,
|
|
ps: password,
|
|
clientId: '001',
|
|
nonce: Date.now().toString()
|
|
});
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
if (response.data.status !== '200 OK') {
|
|
throw new Error('Legacy API authentication failed');
|
|
}
|
|
|
|
return response.data.access_token;
|
|
}
|
|
|
|
/**
|
|
* Create deployment in Legacy API
|
|
* @param {object} params - Deployment parameters
|
|
* @param {string} params.username - Installer username
|
|
* @param {string} params.token - Access token
|
|
* @param {string} params.beneficiaryName - Full name
|
|
* @param {string} params.beneficiaryEmail - Email
|
|
* @param {string} params.beneficiaryUsername - Login username
|
|
* @param {string} params.beneficiaryPassword - Password
|
|
* @param {string} params.address - Address
|
|
* @param {string} params.caretakerUsername - Caretaker username
|
|
* @param {string} params.caretakerEmail - Caretaker email
|
|
* @param {number} params.persons - Number of persons
|
|
* @param {number} params.pets - Number of pets
|
|
* @param {string} params.gender - Gender
|
|
* @param {number} params.race - Race index
|
|
* @param {number} params.born - Year born
|
|
* @param {number} params.lat - GPS latitude
|
|
* @param {number} params.lng - GPS longitude
|
|
* @param {Array<string>} params.wifis - WiFi credentials ["SSID|password", ...]
|
|
* @param {Array<number>} params.devices - Device well_ids [497, 523]
|
|
* @returns {Promise<number>} Created deployment_id
|
|
*/
|
|
async function createLegacyDeployment(params) {
|
|
// 1x1 pixel JPEG as base64 - required by Legacy API
|
|
const MINI_PHOTO = '/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAn/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCwAB//2Q==';
|
|
|
|
// Minimal params - exactly like working Postman request
|
|
const formData = new URLSearchParams({
|
|
function: 'set_deployment',
|
|
user_name: params.username,
|
|
token: params.token,
|
|
deployment: 'NEW',
|
|
beneficiary_name: params.beneficiaryName,
|
|
beneficiary_email: params.beneficiaryEmail,
|
|
beneficiary_user_name: params.beneficiaryUsername,
|
|
beneficiary_password: params.beneficiaryPassword,
|
|
beneficiary_address: params.address || 'test',
|
|
beneficiary_photo: MINI_PHOTO,
|
|
firstName: 'Test',
|
|
lastName: 'User',
|
|
first_name: 'Test',
|
|
last_name: 'User',
|
|
new_user_name: params.beneficiaryUsername,
|
|
phone_number: '+10000000000',
|
|
key: params.beneficiaryPassword,
|
|
signature: 'Test',
|
|
gps_age: '0',
|
|
wifis: '[]',
|
|
devices: '[]'
|
|
});
|
|
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
|
|
if (response.data.status !== '200 OK') {
|
|
throw new Error(`Failed to create deployment in Legacy API: ${response.data.status || JSON.stringify(response.data)}`);
|
|
}
|
|
|
|
// Extract deployment_id from response
|
|
// Response format varies, need to handle different cases
|
|
// Note: Current Legacy API returns only {ok: 1, status: "200 OK"} without deployment_id
|
|
const deploymentId = response.data.deployment_id || response.data.result || response.data.well_id;
|
|
|
|
if (!deploymentId) {
|
|
}
|
|
|
|
return deploymentId;
|
|
}
|
|
|
|
/**
|
|
* Assign device to deployment (set well_id)
|
|
* @param {string} username - Username
|
|
* @param {string} token - Access token
|
|
* @param {number} deviceId - Internal device ID
|
|
* @param {number} wellId - Well ID to assign
|
|
* @param {string} mac - Device MAC address
|
|
* @returns {Promise<boolean>} Success status
|
|
*/
|
|
async function assignDeviceToDeployment(username, token, deviceId, wellId, mac) {
|
|
const formData = new URLSearchParams({
|
|
function: 'device_set_well_id',
|
|
user_name: username,
|
|
token: token,
|
|
device_id: deviceId,
|
|
well_id: wellId,
|
|
mac: mac.toUpperCase().replace(/:/g, '')
|
|
});
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
return response.data.success === true;
|
|
}
|
|
|
|
/**
|
|
* Update device location/room
|
|
* @param {string} username - Username
|
|
* @param {string} token - Access token
|
|
* @param {number} wellId - Device well_id
|
|
* @param {string} deviceMac - MAC address
|
|
* @param {string} roomName - Room name (Bedroom, Kitchen, etc.)
|
|
* @param {object} options - Additional options
|
|
* @param {string} options.description - Device description
|
|
* @param {string} options.closeTo - Position description
|
|
* @param {number} options.radarThreshold - Radar sensitivity (0-100)
|
|
* @param {number} options.group - Group ID
|
|
* @returns {Promise<boolean>} Success status
|
|
*/
|
|
async function updateDeviceLocation(username, token, wellId, deviceMac, roomName, options = {}) {
|
|
const locationCode = ROOM_LOCATIONS[roomName] || ROOM_LOCATIONS['Other'];
|
|
|
|
const formData = new URLSearchParams({
|
|
function: 'device_form',
|
|
user_name: username,
|
|
token: token,
|
|
well_id: wellId,
|
|
device_mac: deviceMac.toUpperCase().replace(/:/g, ''),
|
|
location: locationCode.toString(),
|
|
description: options.description || '',
|
|
close_to: options.closeTo || '',
|
|
radar_threshold: options.radarThreshold || 50,
|
|
group: options.group || 0
|
|
});
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
return response.data.status === '200 OK';
|
|
}
|
|
|
|
/**
|
|
* Get devices for deployment (only recently active)
|
|
* @param {string} username - Username
|
|
* @param {string} token - Access token
|
|
* @param {number} deploymentId - Deployment ID
|
|
* @param {boolean} onlineOnly - If true, return only online devices (fresh=true)
|
|
* @returns {Promise<Array>} List of devices
|
|
*/
|
|
async function getDeploymentDevices(username, token, deploymentId, onlineOnly = true) {
|
|
const formData = new URLSearchParams({
|
|
function: 'request_devices',
|
|
user_name: username,
|
|
token: token,
|
|
deployment_id: deploymentId,
|
|
group_id: 'All',
|
|
location: 'All',
|
|
fresh: onlineOnly ? 'true' : 'false'
|
|
});
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
if (response.data.status !== '200 OK') {
|
|
return [];
|
|
}
|
|
|
|
// Parse device array format:
|
|
// [device_id, well_id, MAC, timestamp, location, description, deployment_id]
|
|
const devices = response.data.result_list || [];
|
|
|
|
return devices.map(device => ({
|
|
deviceId: device[0],
|
|
wellId: device[1],
|
|
mac: device[2],
|
|
lastSeen: device[3],
|
|
locationCode: device[4],
|
|
locationName: LOCATION_NAMES[device[4]] || 'Unknown',
|
|
description: device[5],
|
|
deploymentId: device[6],
|
|
isOnline: true // If fresh=true was used, all returned devices are online
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Reboot device
|
|
* @param {string} username - Username
|
|
* @param {string} token - Access token
|
|
* @param {number} deviceId - Device ID
|
|
* @returns {Promise<boolean>} Success status
|
|
*/
|
|
async function rebootDevice(username, token, deviceId) {
|
|
const formData = new URLSearchParams({
|
|
function: 'device_reboot',
|
|
user_name: username,
|
|
token: token,
|
|
device_id: deviceId
|
|
});
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
return response.data.status === '200 OK';
|
|
}
|
|
|
|
/**
|
|
* Find deployment ID by beneficiary username
|
|
* Used to retrieve deployment_id after creation (since set_deployment doesn't return it)
|
|
* @param {string} adminUsername - Admin username
|
|
* @param {string} adminToken - Admin access token
|
|
* @param {string} beneficiaryUsername - Username of the beneficiary
|
|
* @returns {Promise<number|null>} Deployment ID or null if not found
|
|
*/
|
|
async function findDeploymentByUsername(adminUsername, adminToken, beneficiaryUsername) {
|
|
try {
|
|
// Try logging in as the beneficiary user to get their deployment
|
|
// Note: This requires knowing the beneficiary's password
|
|
|
|
// Alternative: Use get_user_deployments if available
|
|
const formData = new URLSearchParams({
|
|
function: 'get_user_deployments',
|
|
user_name: adminUsername,
|
|
token: adminToken,
|
|
target_username: beneficiaryUsername
|
|
});
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
|
|
if (response.data.status === '200 OK' && response.data.result_list) {
|
|
// Return the first deployment ID
|
|
const deployments = response.data.result_list;
|
|
if (deployments.length > 0) {
|
|
return deployments[0].deployment_id || deployments[0][0]; // Handle both object and array format
|
|
}
|
|
}
|
|
|
|
return null;
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send email via Legacy API (eluxnetworks.net)
|
|
* Uses the send_message function with method=email
|
|
*
|
|
* @param {object} params - Email parameters
|
|
* @param {string} params.to - Recipient email address
|
|
* @param {string} params.subject - Email subject
|
|
* @param {string} params.content - Email content (HTML supported)
|
|
* @returns {Promise<boolean>} Success status
|
|
*/
|
|
async function sendEmailViaLegacyAPI({ to, subject, content }) {
|
|
const username = process.env.LEGACY_API_USERNAME;
|
|
const password = process.env.LEGACY_API_PASSWORD;
|
|
|
|
if (!username || !password) {
|
|
throw new Error('Legacy API credentials not configured');
|
|
}
|
|
|
|
// Get fresh token
|
|
const token = await getLegacyToken(username, password);
|
|
|
|
const formData = new URLSearchParams({
|
|
function: 'send_message',
|
|
token: token,
|
|
user_name: username,
|
|
user_id_or_recepient: to,
|
|
method: 'email',
|
|
subject: subject,
|
|
content: content
|
|
});
|
|
|
|
const response = await axios.post(LEGACY_API_BASE, formData, {
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
});
|
|
|
|
if (response.data.ok !== true && response.data.status !== '200 OK') {
|
|
throw new Error(`Failed to send email via Legacy API: ${JSON.stringify(response.data)}`);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
module.exports = {
|
|
getLegacyToken,
|
|
createLegacyDeployment,
|
|
findDeploymentByUsername,
|
|
assignDeviceToDeployment,
|
|
updateDeviceLocation,
|
|
getDeploymentDevices,
|
|
rebootDevice,
|
|
sendEmailViaLegacyAPI,
|
|
ROOM_LOCATIONS,
|
|
LOCATION_NAMES
|
|
};
|