v1
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
import express from "express";
|
||||
import { config } from "../config";
|
||||
import {
|
||||
createDownloadLinkDb,
|
||||
deviceGroups,
|
||||
deviceTypes,
|
||||
findLatestReleaseDb,
|
||||
getDeviceByIdDb,
|
||||
getUpdateByIdDb,
|
||||
registerUpdateLogDb,
|
||||
saveDevice,
|
||||
} from "../data";
|
||||
import { generateDeviceToken, sensorboxAuth, verifyDeviceToken } from "../auth";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post("/", (req, res) => {
|
||||
const authorization = req.headers.authorization;
|
||||
const deviceId = String(req.body.deviceId || "").trim();
|
||||
const type = String(req.body.type || "").trim();
|
||||
const hardwareVersion = String(req.body.hardwareVersion || "");
|
||||
const softwareVersion = String(req.body.softwareVersion || "");
|
||||
const group = String("release") as (typeof deviceGroups)[number];
|
||||
const token = authorization?.startsWith("Bearer ")
|
||||
? authorization.slice(7)
|
||||
: String(req.body.token || "");
|
||||
|
||||
if (!deviceId || !type) {
|
||||
console.log("Missing deviceId or type in registration request");
|
||||
res.status(400).send("deviceId and type are required");
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceType = deviceTypes.find((entry) => entry.id === type);
|
||||
if (!deviceType) {
|
||||
console.log(`Unknown device type: ${type}`);
|
||||
res.status(400).send("Unknown device type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!deviceType.supportedGroups.includes(group)) {
|
||||
console.log(`Unsupported group: ${group}`);
|
||||
res.status(400).send("Unsupported group for device type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (token !== config.sensorboxBootstrapToken) {
|
||||
const existingDevice = verifyDeviceToken(token);
|
||||
if (!existingDevice || existingDevice.id !== deviceId) {
|
||||
console.log("Invalid bootstrap token or device token");
|
||||
res.status(401).send("Invalid bootstrap token or existing device token");
|
||||
return;
|
||||
}
|
||||
console.log(`Device with ID ${deviceId} already registered, updating info`);
|
||||
// Regenerate token for existing device
|
||||
const jwtToken = generateDeviceToken(existingDevice.id);
|
||||
existingDevice.token = jwtToken;
|
||||
existingDevice.lastSeen = new Date().toISOString();
|
||||
saveDevice(existingDevice);
|
||||
res.send(jwtToken);
|
||||
return;
|
||||
}
|
||||
|
||||
const existingDevice = getDeviceByIdDb(deviceId);
|
||||
if (existingDevice) {
|
||||
console.log(`Device with ID ${deviceId} already exists`);
|
||||
res.status(409).send("Device already registered");
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceRecord = {
|
||||
id: deviceId,
|
||||
type,
|
||||
group,
|
||||
token: "",
|
||||
registeredAt: new Date().toISOString(),
|
||||
currentVersion: softwareVersion || undefined,
|
||||
hardwareVersion: hardwareVersion || undefined,
|
||||
status: "registered",
|
||||
lastSeen: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const jwtToken = generateDeviceToken(deviceRecord.id);
|
||||
deviceRecord.token = jwtToken;
|
||||
deviceRecord.type = type;
|
||||
deviceRecord.group = group;
|
||||
deviceRecord.currentVersion = softwareVersion || deviceRecord.currentVersion;
|
||||
deviceRecord.hardwareVersion =
|
||||
hardwareVersion || deviceRecord.hardwareVersion;
|
||||
deviceRecord.lastSeen = new Date().toISOString();
|
||||
|
||||
saveDevice(deviceRecord);
|
||||
// Add Content-Length header to prevent chunked encoding which can cause issues for some clients
|
||||
res.contentType("text/plain").send(jwtToken);
|
||||
});
|
||||
|
||||
router.get("/ota/check", sensorboxAuth, (req, res) => {
|
||||
const device = req.device!;
|
||||
const hardwareVersion = String(
|
||||
req.query.hardwareVersion || device.hardwareVersion || "",
|
||||
);
|
||||
const softwareVersion = String(
|
||||
req.query.softwareVersion || device.currentVersion || "0.0.0",
|
||||
);
|
||||
const group = String(
|
||||
req.query.group || device.group,
|
||||
) as (typeof deviceGroups)[number];
|
||||
|
||||
if (!hardwareVersion) {
|
||||
res.status(400).send("hardwareVersion is required");
|
||||
return;
|
||||
}
|
||||
|
||||
device.lastSeen = new Date().toISOString();
|
||||
device.currentVersion = softwareVersion;
|
||||
device.hardwareVersion = hardwareVersion;
|
||||
|
||||
// Update the device
|
||||
saveDevice(device);
|
||||
|
||||
const update = findLatestReleaseDb(device.type, group, hardwareVersion);
|
||||
if (!update || update.version === softwareVersion) {
|
||||
res.sendStatus(204);
|
||||
return;
|
||||
}
|
||||
|
||||
const link = createDownloadLinkDb(device.id, update.id);
|
||||
const downloadUrl = `${config.baseUrl}/download/${link.id}`;
|
||||
// Get size of the update file to include in the response header
|
||||
const fs = require("fs");
|
||||
let fileSize = 0;
|
||||
try {
|
||||
const stats = fs.statSync(update.filePath);
|
||||
fileSize = stats.size;
|
||||
} catch (err) {
|
||||
console.error("Error getting update file size:", err);
|
||||
}
|
||||
|
||||
res
|
||||
.contentType("text/plain")
|
||||
.send(
|
||||
"" +
|
||||
update.id +
|
||||
"\n" +
|
||||
update.version +
|
||||
"\n" +
|
||||
downloadUrl +
|
||||
"\n" +
|
||||
new Date(link.expiresAt).toISOString() +
|
||||
"\n" +
|
||||
fileSize,
|
||||
);
|
||||
});
|
||||
|
||||
router.post("/ota/report", sensorboxAuth, (req, res) => {
|
||||
const device = req.device!;
|
||||
const updateId = String(req.body.updateId || "");
|
||||
const success = req.body.success === true;
|
||||
const message = String(req.body.message || "");
|
||||
|
||||
if (!updateId) {
|
||||
res.status(400).send("updateId is required");
|
||||
return;
|
||||
}
|
||||
|
||||
const update = getUpdateByIdDb(updateId);
|
||||
if (!update) {
|
||||
res.status(404).send("Update not found");
|
||||
return;
|
||||
}
|
||||
|
||||
registerUpdateLogDb(device.id, updateId, success, message);
|
||||
|
||||
if (success) {
|
||||
device.currentVersion = update.version;
|
||||
device.hardwareVersion = update.hardwareVersion;
|
||||
device.status = "updated";
|
||||
} else {
|
||||
device.status = "update_failed";
|
||||
}
|
||||
|
||||
device.lastSeen = new Date().toISOString();
|
||||
res.send(true);
|
||||
});
|
||||
|
||||
router.get("/device", sensorboxAuth, (req, res) => {
|
||||
res.json({ device: req.device });
|
||||
});
|
||||
|
||||
export { router as sensorboxRouter };
|
||||
Reference in New Issue
Block a user