test(mobile): testing for ota updated on android scanner
This commit is contained in:
211
app/src/internal/mobile/route.ts
Normal file
211
app/src/internal/mobile/route.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
import type { Express, Request, Response } from "express";
|
||||
import express, { Router } from "express";
|
||||
import { readdirSync, readFileSync, statSync } from "fs";
|
||||
import { dirname, join } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import crypto from "crypto";
|
||||
import fs from "fs";
|
||||
|
||||
export const setupMobileRoutes = (app: Express, basePath: string) => {
|
||||
const router = Router();
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const distPath = join(__dirname, "../../../../mobileLst/dist");
|
||||
|
||||
function generateAssetManifest(baseUrl: string) {
|
||||
const assets: any[] = [];
|
||||
const assetsDir = join(distPath, "assets");
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(assetsDir)) {
|
||||
return assets;
|
||||
}
|
||||
|
||||
const files = readdirSync(assetsDir);
|
||||
files.forEach((file) => {
|
||||
const filePath = join(assetsDir, file);
|
||||
const stats = statSync(filePath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
const content = readFileSync(filePath);
|
||||
const hash = crypto
|
||||
.createHash("sha256")
|
||||
.update(content)
|
||||
.digest("hex");
|
||||
|
||||
assets.push({
|
||||
hash: hash,
|
||||
key: file,
|
||||
fileExtension: `.${file.split(".").pop()}`,
|
||||
contentType: getContentType(file),
|
||||
url: `${baseUrl}/assets/${file}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("Error reading assets:", err);
|
||||
}
|
||||
|
||||
return assets;
|
||||
}
|
||||
|
||||
function getContentType(filename: string): string {
|
||||
const ext = filename.split(".").pop()?.toLowerCase();
|
||||
const contentTypes: { [key: string]: string } = {
|
||||
hbc: "application/javascript",
|
||||
bundle: "application/javascript",
|
||||
js: "application/javascript",
|
||||
json: "application/json",
|
||||
png: "image/png",
|
||||
jpg: "image/jpeg",
|
||||
jpeg: "image/jpeg",
|
||||
gif: "image/gif",
|
||||
ttf: "font/ttf",
|
||||
otf: "font/otf",
|
||||
woff: "font/woff",
|
||||
woff2: "font/woff2",
|
||||
};
|
||||
return contentTypes[ext || ""] || "application/octet-stream";
|
||||
}
|
||||
|
||||
app.get(basePath + "/api/mobile/updates", (req, res) => {
|
||||
console.log("=== OTA Update Request ===");
|
||||
console.log("Headers:", JSON.stringify(req.headers, null, 2));
|
||||
|
||||
const runtimeVersion = req.headers["expo-runtime-version"];
|
||||
const platform = req.headers["expo-platform"] || "android";
|
||||
const expectedRuntimeVersion = "1.0.0";
|
||||
|
||||
if (runtimeVersion !== expectedRuntimeVersion) {
|
||||
console.log(
|
||||
`Runtime mismatch: got ${runtimeVersion}, expected ${expectedRuntimeVersion}`
|
||||
);
|
||||
return res.status(404).json({
|
||||
error: "No update available for this runtime version",
|
||||
requestedVersion: runtimeVersion,
|
||||
availableVersion: expectedRuntimeVersion,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// const host = req.get('host');
|
||||
// // If it's the production domain, force https
|
||||
// const protocol = host.includes('alpla.net') ? 'https' : req.protocol;
|
||||
|
||||
// const baseUrl = `${protocol}://${host}/lst/api/mobile/updates`
|
||||
|
||||
const host = req.get('host'); // Should be "usmcd1vms036:4000"
|
||||
const protocol = 'http';
|
||||
const baseUrl = `${protocol}://${host}/api/mobile/updates`;
|
||||
|
||||
// Find the .hbc file
|
||||
const bundleDir = join(distPath, "_expo/static/js/android");
|
||||
|
||||
if (!fs.existsSync(bundleDir)) {
|
||||
console.error("Bundle directory does not exist:", bundleDir);
|
||||
return res
|
||||
.status(500)
|
||||
.json({ error: "Bundle directory not found" });
|
||||
}
|
||||
|
||||
const bundleFiles = readdirSync(bundleDir);
|
||||
console.log("Available bundle files:", bundleFiles);
|
||||
|
||||
const bundleFile = bundleFiles.find((f) => f.endsWith(".hbc"));
|
||||
|
||||
if (!bundleFile) {
|
||||
console.error("No .hbc file found in:", bundleDir);
|
||||
return res
|
||||
.status(500)
|
||||
.json({ error: "Hermes bundle (.hbc) not found" });
|
||||
}
|
||||
|
||||
console.log("Using bundle file:", bundleFile);
|
||||
|
||||
const bundlePath = join(bundleDir, bundleFile);
|
||||
const bundleContent = readFileSync(bundlePath);
|
||||
const bundleHash = crypto
|
||||
.createHash("sha256")
|
||||
.update(bundleContent)
|
||||
.digest("hex");
|
||||
|
||||
const updateId = crypto.randomUUID();
|
||||
const createdAt = new Date().toISOString();
|
||||
|
||||
// This is the NEW manifest format for Expo SDK 50+
|
||||
const manifest = {
|
||||
id: updateId,
|
||||
createdAt: createdAt,
|
||||
runtimeVersion: expectedRuntimeVersion,
|
||||
launchAsset: {
|
||||
hash: bundleHash,
|
||||
key: bundleFile,
|
||||
contentType: "application/javascript",
|
||||
fileExtension: ".hbc",
|
||||
url: `${baseUrl}/_expo/static/js/android/${bundleFile}`,
|
||||
},
|
||||
assets: generateAssetManifest(baseUrl),
|
||||
metadata: {},
|
||||
extra: {
|
||||
expoClient: {
|
||||
name: "LSTScanner",
|
||||
slug: "lst-scanner-app",
|
||||
version: "1.0.0",
|
||||
runtimeVersion: expectedRuntimeVersion,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
console.log(
|
||||
"Returning manifest:",
|
||||
JSON.stringify(manifest, null, 2)
|
||||
);
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("expo-protocol-version", "1");
|
||||
res.setHeader("expo-sfv-version", "0");
|
||||
res.json(manifest);
|
||||
} catch (error: any) {
|
||||
console.error("Error generating manifest:", error);
|
||||
res.status(500).json({
|
||||
error: "Failed to generate manifest",
|
||||
details: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Serve static files
|
||||
app.use(
|
||||
basePath + "/api/mobile/updates",
|
||||
express.static(distPath, {
|
||||
setHeaders(res, path) {
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Cache-Control", "public, max-age=31536000");
|
||||
|
||||
if (path.endsWith(".hbc")) {
|
||||
res.setHeader("Content-Type", "application/javascript");
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
// app.use(
|
||||
// basePath + "/api/mobile/updates",
|
||||
// express.static(join(__dirname, mobileDir), {
|
||||
// setHeaders(res) {
|
||||
// // OTA runtime needs to fetch these from the device
|
||||
// console.log("OTA check called");
|
||||
// res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
// app.get(basePath + "/api/mobile/updates", (req, res) => {
|
||||
// res.redirect(basePath + "/api/mobile/updates/metadata.json");
|
||||
// });
|
||||
|
||||
app.get(basePath + "/api/mobile", (_, res) =>
|
||||
res.status(200).json({ message: "LST OTA server is up." })
|
||||
);
|
||||
};
|
||||
@@ -4,22 +4,24 @@ import { setupAuthRoutes } from "../auth/routes/routes.js";
|
||||
import { setupForkliftRoutes } from "../forklifts/routes/routes.js";
|
||||
import { setupLogisticsRoutes } from "../logistics/routes.js";
|
||||
import { setupSystemRoutes } from "../system/routes.js";
|
||||
import { setupMobileRoutes } from "../mobile/route.js";
|
||||
|
||||
export const setupRoutes = (app: Express, basePath: string) => {
|
||||
// all routes
|
||||
setupAuthRoutes(app, basePath);
|
||||
setupAdminRoutes(app, basePath);
|
||||
setupSystemRoutes(app, basePath);
|
||||
setupLogisticsRoutes(app, basePath);
|
||||
setupForkliftRoutes(app, basePath);
|
||||
// all routes
|
||||
setupAuthRoutes(app, basePath);
|
||||
setupAdminRoutes(app, basePath);
|
||||
setupSystemRoutes(app, basePath);
|
||||
setupLogisticsRoutes(app, basePath);
|
||||
setupForkliftRoutes(app, basePath);
|
||||
setupMobileRoutes(app, basePath);
|
||||
|
||||
// always try to go to the app weather we are in dev or in production.
|
||||
app.get(basePath + "/", (req: Request, res: Response) => {
|
||||
res.redirect(basePath + "/app");
|
||||
});
|
||||
// always try to go to the app weather we are in dev or in production.
|
||||
app.get(basePath + "/", (req: Request, res: Response) => {
|
||||
res.redirect(basePath + "/app");
|
||||
});
|
||||
|
||||
// Fallback 404 handler
|
||||
app.use((req: Request, res: Response) => {
|
||||
res.status(404).json({ error: "Not Found" });
|
||||
});
|
||||
// Fallback 404 handler
|
||||
app.use((req: Request, res: Response) => {
|
||||
res.status(404).json({ error: "Not Found" });
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user