Commit Graph
2 Commits
Author SHA1 Message Date
octoturge c7f85870db Add a Jobs page: upload a sliced file, watch it queue, delete it
The console could watch and control printers but not give them work. The
Jobs page uploads a .3mf/.gcode: it creates the job (POST /jobs), PUTs the
file straight to the presigned storage URL with a progress bar, and lists
the farm's jobs, polling every 5s since there is no job push channel yet.
The scheduler then starts it on the next idle printer, so there is no Start
button - a queued job IS the request.

Deliberate details: the PUT carries no Authorization header (the URL is
presigned and an uncovered header makes storage reject it) and the same
Content-Type the URL was signed for; a failed upload deletes the job row
it just created, so no queued job waits for a file that never arrives;
only queued/uploading jobs offer Delete, matching what the backend allows.

JobUpload and JobList live in @continuum/ui and take the API calls as
props, like the printer card. A shared apiErrorMessage() replaces the
inline copy on the fleet page.

Checked in real Chrome against the live stack with job dispatch OFF: upload
to the real bucket (57 bytes landed intact), listed as queued, still queued
after a scheduler tick, Delete clears it, no page errors; the test object
was removed from the bucket afterwards. pnpm -r typecheck is clean.
2026-10-29 14:39:37 +00:00
octoturge a558156968 Send the Supabase token the app already had
Auth is Supabase end to end and always was: the backend verifies access
tokens asymmetrically against the project's published JWKS and holds no
signing key of its own, and `requireUser` wants that token in
`Authorization: Bearer`. Nothing about that needed changing.

What was wrong is only here. The two REST calls forwarded
`useRequestHeaders(["cookie"])`, which is what you do when a Nuxt server
is proxying a browser's request - and this app sets `ssr: false` so the
same static bundle can serve the browser and the Tauri shell. There is no
server to forward anything, so the header arrived empty and every call
should have been getting a flat 401.

The token was to hand the whole time. `stores/telemetry.ts` has been
reading `session.access_token` for its socket since it was written; the
REST side simply never did.

`useApiFetch` is now the one way this app calls the backend, and it reads
the token inside the fetcher rather than capturing it at setup, because
supabase-js refreshes the session in the background about once an hour - a
captured token would work until it quietly stopped. With no session it
omits the header entirely rather than sending an empty Bearer, which the
backend rejects with a message about the header's shape and reads like a
client bug; getting the user a session is the Supabase module's redirect's
job.

The socket keeps using a `?token=` query parameter, which is not an
inconsistency: a browser WebSocket cannot set request headers.

`nuxt typecheck` clean.
2026-10-15 23:21:01 +00:00