- Settings → Slicer: pick Bambu Studio, OrcaSlicer, PrusaSlicer, Cura, a custom open-link template, or plain download (remembered per browser) - Bambu Studio / OrcaSlicer launch via their URL schemes with a 10-minute signed download link (/dl/<token>/<name>) that needs no session cookie - PrusaSlicer and Cura fall back to a download (they can't open our links) - 3MF projects report which app saved them; opening one in a different slicer asks first - "slicer" action on every file row and archive entry - Drag model cards (file, or zip of all files) and file/entry rows out to the desktop via Chromium's DownloadURL Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
42 lines
2 KiB
JavaScript
42 lines
2 KiB
JavaScript
// Short-lived "Open in slicer" download links (lib/handoff.js).
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const jwt = require('jsonwebtoken');
|
|
const handoff = require('../lib/handoff');
|
|
|
|
const SECRET = 'a'.repeat(96);
|
|
|
|
test('a link grants the file it was made for', () => {
|
|
const token = handoff.createToken(SECRET, { kind: 'entry', id: '42' });
|
|
assert.deepEqual(handoff.readToken(SECRET, token), { kind: 'entry', id: 42 });
|
|
});
|
|
|
|
test('expired, altered or foreign links grant nothing', () => {
|
|
const expired = handoff.createToken(SECRET, { kind: 'file', id: 1 }, -5);
|
|
assert.equal(handoff.readToken(SECRET, expired), null);
|
|
|
|
const token = handoff.createToken(SECRET, { kind: 'file', id: 1 });
|
|
const [head, , sig] = token.split('.');
|
|
const otherBody = Buffer.from(JSON.stringify({ k: 'file', id: 2, aud: 'printhive-handoff', exp: 9999999999 })).toString('base64url');
|
|
assert.equal(handoff.readToken(SECRET, `${head}.${otherBody}.${sig}`), null);
|
|
assert.equal(handoff.readToken('b'.repeat(96), token), null);
|
|
assert.equal(handoff.readToken(SECRET, 'not-a-token'), null);
|
|
});
|
|
|
|
test('a login token is not a hand-off link', () => {
|
|
const session = jwt.sign({ sub: 1, email: 'x@example.com' }, SECRET, { expiresIn: '30d' });
|
|
assert.equal(handoff.readToken(SECRET, session), null);
|
|
// …and a hand-off link doesn't verify as a login.
|
|
assert.throws(() => jwt.verify(handoff.createToken(SECRET, { kind: 'file', id: 1 }), SECRET));
|
|
});
|
|
|
|
test('only files and archive entries can be handed off', () => {
|
|
assert.throws(() => handoff.createToken(SECRET, { kind: 'model', id: 1 }), /Unknown hand-off kind/);
|
|
});
|
|
|
|
test('link file names stay plain ASCII and keep their extension', () => {
|
|
assert.equal(handoff.urlName('Toy Keychain (v2).3mf'), 'Toy_Keychain_v2.3mf');
|
|
assert.equal(handoff.urlName('Café déco.STL'), 'Cafe_deco.stl');
|
|
assert.equal(handoff.urlName('日本.stl'), 'model.stl');
|
|
assert.equal(handoff.urlName('../../etc/passwd.obj'), 'passwd.obj');
|
|
});
|