feat: add Flutter mobile clients and staff delivery API
This commit is contained in:
33
ui/scripts/check-mobile-runtime.mjs
Normal file
33
ui/scripts/check-mobile-runtime.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env node
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const lockPath = path.join(root, "mobile-runtime.lock.json");
|
||||
const lockedFiles = JSON.parse(readFileSync(lockPath, "utf8"));
|
||||
const failures = [];
|
||||
|
||||
for (const [relativePath, expectedHash] of Object.entries(lockedFiles)) {
|
||||
const filePath = path.join(root, relativePath);
|
||||
|
||||
if (!existsSync(filePath)) {
|
||||
failures.push(`${relativePath} is missing`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const actualHash = createHash("sha256").update(readFileSync(filePath)).digest("hex");
|
||||
if (actualHash !== expectedHash) {
|
||||
failures.push(`${relativePath} was modified`);
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error("Mobile runtime integrity check failed:\n");
|
||||
for (const failure of failures) console.error(`- ${failure}`);
|
||||
console.error("\nRestore the protected runtime. Put app UI in src/Prototype.tsx and src/prototype.css.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Mobile runtime integrity check passed (${Object.keys(lockedFiles).length} protected files).`);
|
||||
21
ui/scripts/prepare-sites-build.mjs
Normal file
21
ui/scripts/prepare-sites-build.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env node
|
||||
import { copyFileSync, existsSync, mkdirSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const dist = path.join(root, "dist");
|
||||
const index = path.join(dist, "client", "index.html");
|
||||
const worker = path.join(root, "worker", "index.js");
|
||||
const hosting = path.join(root, ".openai", "hosting.json");
|
||||
|
||||
for (const file of [index, worker, hosting]) {
|
||||
if (!existsSync(file)) throw new Error("Missing Sites build input: " + file);
|
||||
}
|
||||
|
||||
mkdirSync(path.join(dist, "server"), { recursive: true });
|
||||
mkdirSync(path.join(dist, ".openai"), { recursive: true });
|
||||
copyFileSync(worker, path.join(dist, "server", "index.js"));
|
||||
copyFileSync(hosting, path.join(dist, ".openai", "hosting.json"));
|
||||
|
||||
console.log("Prepared Sites build: dist/server/index.js and dist/.openai/hosting.json");
|
||||
48
ui/scripts/update-mobile-runtime-lock.mjs
Normal file
48
ui/scripts/update-mobile-runtime-lock.mjs
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env node
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const lockPath = path.join(root, "mobile-runtime.lock.json");
|
||||
const protectedFiles = [
|
||||
"scripts/check-mobile-runtime.mjs",
|
||||
"scripts/prepare-sites-build.mjs",
|
||||
"scripts/update-mobile-runtime-lock.mjs",
|
||||
"vite.config.ts",
|
||||
"src/App.tsx",
|
||||
"src/main.tsx",
|
||||
"src/styles.css",
|
||||
"src/mobile/BottomSheet.tsx",
|
||||
"src/mobile/Carousel.tsx",
|
||||
"src/mobile/Device.tsx",
|
||||
"src/mobile/FlowStack.tsx",
|
||||
"src/mobile/Keyboard.tsx",
|
||||
"src/mobile/MobileCursor.tsx",
|
||||
"src/mobile/MobileRuntime.tsx",
|
||||
"src/mobile/MobileScroll.tsx",
|
||||
"src/mobile/PhoneFrame.tsx",
|
||||
"src/mobile/assets.ts",
|
||||
"src/mobile/components.tsx",
|
||||
"src/mobile/geometry.ts",
|
||||
"src/mobile/index.ts",
|
||||
"public/assets/iphone/Bezel.png",
|
||||
"public/assets/iphone/Keyboard.png",
|
||||
"public/assets/android/Pixel10.png",
|
||||
"public/assets/android/Keyboard.png",
|
||||
"public/assets/android/navigation-bar.svg",
|
||||
"public/assets/status/status-icons.svg",
|
||||
"public/assets/status/ios-status-icons.svg",
|
||||
"worker/index.js",
|
||||
];
|
||||
|
||||
const hashes = {};
|
||||
for (const relativePath of protectedFiles) {
|
||||
const filePath = path.join(root, relativePath);
|
||||
if (!existsSync(filePath)) throw new Error(`Protected runtime file is missing: ${relativePath}`);
|
||||
hashes[relativePath] = createHash("sha256").update(readFileSync(filePath)).digest("hex");
|
||||
}
|
||||
|
||||
writeFileSync(lockPath, `${JSON.stringify(hashes, null, 2)}\n`);
|
||||
console.log(`Updated mobile-runtime.lock.json (${protectedFiles.length} protected files).`);
|
||||
Reference in New Issue
Block a user