flux-image-space / scripts /verify.mjs
Z User
Flux Studio — Zero GPU: Hugging Face Docker Space (Express proxy to BFL FLUX API)
c9996a9
Raw History Blame Contribute Delete
4.92 kB
/**
* Build verification for Flux Studio — Zero GPU.
*
* Runs as `npm run build` inside the Docker image (and locally before
* every push). It fails loudly if anything in the app is broken, so a
* broken build can never reach the Hugging Face builder silently.
*
* Checks:
* 1. All required files exist.
* 2. server.mjs parses and imports cleanly (no listen — import only).
* 3. Config defaults match the spec
* (flux-2-pro-preview primary, flux-2-klein-9b fallback,
* https://api.bfl.ai base, port 7860).
* 4. Placeholder-key detection behaves.
* 5. Request validation behaves (accepts good input, rejects bad).
* 6. The image proxy host allowlist blocks non-BFL hosts.
* 7. The static client ships index.html/app.js/style.css and the
* client never references the API key.
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(__dirname, '..');
const failures = [];
function check(name, fn) {
try {
fn();
console.log(` ok ${name}`);
} catch (err) {
failures.push({ name, message: err.message });
console.error(` FAIL ${name}: ${err.message}`);
}
}
function assert(cond, msg) {
if (!cond) throw new Error(msg);
}
/* 1. Required files --------------------------------------------------- */
const REQUIRED = [
'server.mjs',
'package.json',
'package-lock.json',
'Dockerfile',
'README.md',
'public/index.html',
'public/style.css',
'public/app.js',
];
check('required files exist', () => {
for (const f of REQUIRED) {
assert(fs.existsSync(path.join(root, f)), `missing file: ${f}`);
}
});
/* 2/3. Server module imports cleanly with spec defaults --------------- */
const server = await import(path.join(root, 'server.mjs'));
check('config defaults match the spec', () => {
const { config } = server;
assert(config.modelId === 'flux-2-pro-preview', `modelId=${config.modelId}, expected flux-2-pro-preview`);
assert(config.fallbackModelId === 'flux-2-klein-9b', `fallbackModelId=${config.fallbackModelId}, expected flux-2-klein-9b`);
assert(config.apiBaseHost === 'api.bfl.ai', `apiBaseHost=${config.apiBaseHost}, expected api.bfl.ai`);
assert(config.port === 7860, `port=${config.port}, expected 7860`);
assert(typeof config.ready === 'boolean', 'config.ready should be boolean');
});
/* 4. Placeholder detection -------------------------------------------- */
check('placeholder key detection', () => {
assert(server.isPlaceholderKey('') === true, 'empty key must count as placeholder');
assert(server.isPlaceholderKey('placeholder-replace-with-real-bfl-key') === true, 'placeholder value must count');
assert(server.isPlaceholderKey('sk-1234567890abcdef') === false, 'normal-looking key must not count');
});
/* 5. Validation -------------------------------------------------------- */
check('request validation', () => {
const good = server.validateBody({ prompt: 'a cat', aspectRatio: '16:9', outputFormat: 'jpeg' });
assert(good.width === 1344 && good.height === 768, `aspect preset wrong: ${good.width}x${good.height}`);
const seeded = server.validateBody({ prompt: 'a cat', width: 1024, height: 1024, seed: '42' });
assert(seeded.seed === 42, 'seed coercion failed');
let threw = false;
try {
server.validateBody({ prompt: '', width: 99999 });
} catch {
threw = true;
}
assert(threw, 'invalid body must throw');
const payload = server.buildPayload({ prompt: 'x', width: 256, height: 256, outputFormat: 'png', seed: 7 });
assert(payload.seed === 7 && payload.output_format === 'png', 'BFL payload mapping wrong');
});
/* 6. Image proxy allowlist --------------------------------------------- */
check('image proxy host allowlist', () => {
assert(server.isAllowedDeliveryHost('delivery.bfl.ai') === true, 'delivery.bfl.ai should be allowed');
assert(server.isAllowedDeliveryHost('bflai.blob.core.windows.net') === true, 'azure blob host should be allowed');
assert(server.isAllowedDeliveryHost('evil.example.com') === false, 'random host must be blocked');
assert(server.isAllowedDeliveryHost('bfl.ai.evil.com') === false, 'suffix spoof must be blocked');
});
/* 7. Client hygiene ---------------------------------------------------- */
check('client has no server secret access', () => {
const appJs = fs.readFileSync(path.join(root, 'public/app.js'), 'utf8');
assert(!/BFL_API_KEY\s*=/.test(appJs), 'client must not assign BFL_API_KEY');
assert(!/x-key/i.test(appJs), 'client must not set the x-key header');
});
/* ---------------------------------------------------------------------- */
console.log('');
if (failures.length) {
console.error(`BUILD FAILED — ${failures.length} check(s) failed.`);
process.exit(1);
} else {
console.log('BUILD OK — all checks passed.');
process.exit(0);
}