Updated CHANGELOG

This commit is contained in:
maziggy
2026-10-02 16:16:55 +02:00
parent 6f2cdbcde1
commit a61fda52ba
+9
View File
@@ -24,10 +24,18 @@ All notable changes to Bambuddy will be documented in this file.
- **Connected apps: other applications can sign people in with their Bambuddy account** — Settings → API Keys → Connected Apps registers an app with one exact callback URL and gives it a client ID and a secret, shown once. The app sends the browser to `/connect/authorize`; the user confirms once ("Allow"), and after that sign-in is automatic, so an app opened from the sidebar needs no second login. The app's server then swaps a single-use code for the user's name, email, groups and permissions at `POST /api/v1/connect/token`. It is the OAuth 2.0 authorization-code flow with PKCE (S256 only), kept to what an app on the same network needs: a code lives 60 seconds, works once, is bound to its app, callback URL and PKCE challenge, and is redeemable only with the app's secret; codes and secrets are stored hashed, failed exchanges are rate-limited per app and per IP, and the page never redirects to a callback URL the backend has not matched against the registration. The app never receives the user's password or Bambuddy session, and an API key cannot sign its owner in to an app. Connected apps require authentication to be enabled; with it off, both endpoints answer `auth_disabled` so an app falls back to its own login instead of letting everyone in. The consent page may be shown inside Bambuddy's own frames (`frame-ancestors 'self'`), so an app opened from the sidebar can sign in there; every other page still refuses all framing. See the wiki page *Connected Apps*.
- **A batch can record the external order it fulfils, so an integration can't queue the same order twice** — `POST /queue/batches` accepts `external_source` (e.g. `shopify`) and `external_ref`, a reference to the record in that system; both are returned on every batch and can be filtered on with `GET /queue/batches?external_source=&external_ref=`. The pair is unique: a second create for the same record answers 409 instead of making a second batch, so a connector that retries after losing the response can't print an order twice. Both fields are optional and must be given together; batches created without them behave exactly as before.
- **Colour swatches for printer slots in the Print / Schedule dialog's filament mapping (#3159, requested by @frantiseklorenc)** — The dialog's filament mapping compares the colour the slice asked for against the colour actually in the machine, and until now only the left-hand side of that comparison had a swatch. The right-hand side — the slot, and every slot in its dropdown — was text, and the text is not reliable: a slot's colour name comes from the Color Catalog or, failing that, from hue, so a third-party beige is announced as "Orange". On a farm running twenty or thirty non-Bambu colours that are swapped between machines daily, a "Color mismatch" warning then gives no way to tell a real mismatch from two names for the same hex without opening the printer card in another tab. Each slot now carries its colour, in the picker and on the row, together with its hex; the slot whose colour is exactly the one the slice asked for is ticked. It works for a slot bound to an inventory spool and for one configured through Configure Slot or on the printer itself, which is the distinction that mattered — the second kind has no inventory row behind it and draws the colour the printer reports. A bound spool also contributes what a tray record cannot carry: its remaining colour stops and its effect, so a two-tone or glittery spool draws as itself rather than as its base colour. The same treatment is applied to the filament-override picker used for model-based assignment, which is the same choice on the other dispatch path and would otherwise have stayed text-only. Both controls stop being `<select>`s to do it — an `<option>` renders text and nothing else — and both keep full keyboard operation, listbox semantics and the border colouring that encodes match, same-type-different-colour and not-loaded.
- **Post-print outcome confirmation: say whether a completed print is actually a good part (#1898, requested by @FedericoPuntelli, contributed by @Thomansky in #3047)** — The printer can only report that a print *completed*; a warped, out-of-tolerance or wrong-colour part counted as a success anyway. A print can now ask for a verdict: turn on **Ask for Outcome** in the print dialog, or set it as the default under Settings → Workflow → Queue & Dispatch → Default Print Options, and when the print finishes Bambuddy asks "How did your print come out?" with the finish photo and **Good** / **Reject**. It can be answered in the dialog that opens in the web UI, from the printer card next to the plate-clear button, or from the phone: the new **Outcome Confirmation** notification event puts Good/Reject buttons into ntfy and Telegram notifications and a link to the dialog into every other channel. A reject takes an optional reason from the failure-reason list and offers **Print again**. *Ask me later* leaves an amber **outcome?** badge on the archive card, and the Archives page gets an **Unconfirmed** filter; the verdict stays editable from the card's menu or Edit Archive, and says where it came from (in the app, a link, the printer card, or a plate release). Two further options, both off by default: **Also ask for prints started outside Bambuddy** gives prints started at the printer, in Bambu Studio or in the Handy app the same prompt, and **Count unanswered outcomes as good on plate release** (Plate-Clear Confirmation card) records a still-open prompt as good when the plate is released. Rejected prints don't touch the machine failure rate; Failure Analysis shows them as their own line with a yield figure, and project part counts only count parts that were not rejected. A one-tap link shows a confirmation page and records nothing on a plain open, so a link preview, a mail scanner or a browser prefetch can't answer the question for you; only the notification buttons record in one tap. Nothing changes for anyone who doesn't turn it on: the option is off by default, and the notification event only fires for prints that asked.
- **The outcome prompt can be answered by reacting to the Telegram message (#3046, requested and contributed by @Thomansky in #3129)** — The Good/Reject buttons in a Telegram outcome prompt open a Bambuddy link, which only works when the phone can reach Bambuddy. A thumbs-up or thumbs-down on the message now records the verdict instead: the phone only talks to Telegram, so it works away from home, behind NAT or with the VPN down, and needs no external URL. A Telegram provider gains **Outcome verdict via**: **Inline buttons (link)** (the default, unchanged), **Reaction (👍 / 👎)**, or **Buttons and reaction**. Bambuddy then polls the bot for reactions (one poll per bot, outbound only) and edits the message to say "marked as good" or "marked as reject". The first verdict wins, whichever way it arrives, and a reaction stops counting exactly when the prompt's links would, so a message from an earlier run can't answer a reprint. Use a bot only Bambuddy uses for reactions: another app polling the same bot, such as Home Assistant's Telegram integration, stops receiving its messages. In a group the bot must be an admin to see reactions. The Archives page's **Unconfirmed** filter now lists completed prints only, like the **outcome?** badge.
- **Library files can carry an external link, notes and photos (#3077, requested by @SergioFuchs, contributed by @Thomansky in #3128)** — The File Manager now gives library files what archives already have. **File details** in a file's card menu, or the info icon on a list row, opens a window with the file's facts (size, print details, the MakerWorld source if there is one), an editable notes field, a link, and a photo gallery. Photos can be JPG, PNG or WebP, up to 10 MB each, and open full size when clicked. Only http and https links are accepted. The grid shows a globe icon that opens the link, a notes marker and a photo count. Edits you haven't saved survive the file being refreshed in the background. A file's photos are deleted with it when the trash is emptied.
- **File Manager previews open larger, zoom, go fullscreen and cover images (#2976, requested and contributed by @Thomansky in #2990)** — PDF, spreadsheet and 3D previews now open in the same large window, with a fullscreen button; double-click the preview to toggle fullscreen. PDFs zoom with Ctrl/⌘ + wheel, a trackpad or touch pinch, or the `+`, `-` and `0` keys. In the 3D viewer the wheel no longer zooms past the model into empty space, and the new zoom and reset buttons return to the framed view. PNG, JPG, GIF, WebP and BMP files get a preview with zoom and pan; TIFF keeps its thumbnail but no preview, because most browsers cannot show it. Double-click a file in the grid or list to open its preview, or select one file and use **Preview** in the toolbar. PDFs get **Generate Thumbnail** in the card menu and list row, like STLs. PDFs with Japanese, Chinese or Korean text, JPEG2000 images or ICC colour now render correctly; they came out blank or wrong before, because pdf.js could not load its font maps and image decoders. The page's security policy no longer allows WebAssembly; only the two preview workers that need it do. Very large zooms on iPhone and iPad stop gaining sharpness instead of showing a blank page. All previews run in the browser, not on the Bambuddy server.
- **An app shown in the sidebar can open a Bambuddy page in place** — The frame Bambuddy shows an external link in can't navigate the Bambuddy window, so a link such as Bambuddy Orders' "In Bambuddy" did nothing when clicked inside the sidebar. The framed page can now ask with `{type: "bambuddy:navigate", path: "/queue?batch=12"}`, and Bambuddy opens that page as if its own menu had been used. The request is accepted only from the frame, only from the link's own origin, and only for a path inside Bambuddy; anything else, such as another site or a `javascript:` address, is ignored. The theme message now carries `canNavigate: true`, so a page can tell this Bambuddy from older ones and open a new tab there instead. The frame's sandbox is unchanged.
- **PDFs in the File Manager get their thumbnail the moment they arrive (#2976)** — A PDF's grid thumbnail used to exist only once somebody had opened its preview, because the browser rendered it and posted it back; until then the card was blank. Page one is now rendered on the server with PDFium (`pypdfium2`, a new dependency whose wheels carry the PDFium binary for every platform Bambuddy ships, so no system package is needed) on upload, inside an extracted ZIP and when an external folder is scanned. **Generate Thumbnails** covers PDFs too, so existing ones can be backfilled in one click; its tooltip and its nothing-to-do toast say so in all 15 languages. The thumbnail has the same shape as the browser's (a 256 px square, page centred on white), so old and new cards match. PDFium is not thread-safe, so renders are serialised behind a lock and run off the event loop; the render scale comes from the page's own size, so a PDF declaring a huge page cannot make it allocate a huge bitmap. A PDF it cannot read (damaged, encrypted) gets no thumbnail and still lands in the library, and the browser preview remains the fallback.
### Changed
- **The Slice dialog uses more of a large screen** — The dialog stopped at 1024 px wide however large the screen, and its left column was fixed at 320 px. So preset names were cut off, and every filament row wrapped its label and its "hidden / Show all" note, while the process-settings panel beside it sat half empty. The dialog now widens to 1280 px on wide screens and 1536 px on very wide ones, and the left column takes two fifths of it instead of a fixed width. Below 1024 px it is still a single column.
- **Leftover Web Push code and the unused `pywebpush` dependency are gone (#3171)** — Browser push notifications were removed earlier, but the service worker still carried their push handlers and `pywebpush` was still installed, pulling in `py-vapid` and `http_ece`. Both are removed. `aiohttp` stays, now listed as the direct dependency it is for external cameras, with the same version floor.
- **A STEP preview says it is converting, and for how long (#2976)** — A large STEP export can take over a minute to turn into a mesh in the browser (a 25 MB FreeCAD file took about 75 seconds; coarser meshing settings do not help, the time goes into reading the file), and for all that time the preview showed only a small spinner, which looked like a preview that never opens. STEP loads now show **Converting STEP model… N s** with a running counter and a note that large files can take a minute or more, in all 15 languages. Other formats load in a moment and keep the plain spinner.
- **The frontend build no longer warns about `path` and `crypto` being externalized for the STEP previewer (#2976)** — `occt-import-js`, the Emscripten build behind STEP previews, requires both modules, but only inside its `ENVIRONMENT_IS_NODE` branches; in the browser it loads its `.wasm` from the URL the preview worker passes and draws randomness from `crypto.getRandomValues`. Vite still externalized both and printed two warnings on every build. `vite.config.ts` now drops exactly those two warnings for that one package through `build.rolldownOptions.onLog`, so an externalization anywhere else, or of any other module, still shows.
### Fixed
- **PostgreSQL installs no longer warn at every start that the connection pool may exceed the server** — The built-in pool allowed up to 100 connections (`pool_size` 20 + `max_overflow` 80), but a stock PostgreSQL allows 97 (`max_connections` 100 minus 3 reserved for superusers). So every install that hadn't set `DB_POOL_SIZE` / `DB_MAX_OVERFLOW` logged `DB pool may exceed what PostgreSQL allows` at startup, and under heavy load could hit `TooManyConnectionsError`. The default `max_overflow` is now 60, an 80-connection ceiling that fits. Installs that set their own values are unaffected, and large farms can still raise both, along with the server's `max_connections`.
@@ -69,6 +77,7 @@ All notable changes to Bambuddy will be documented in this file.
- **Filament printed from the external spool was deducted from an AMS spool (#3166)** — The firmware doesn't accept the external spool in a print command's `ams_mapping` list. So BambuStudio and Bambuddy's own dispatch both write it there as -1, the same value as an unused slot, and put the real target in `ams_mapping2`. Bambuddy read only the first list, so an external-spool print looked unmapped, and usage tracking fell back to guessing by position: the first loaded AMS slot. The reporter's H2D charged every external-spool print that way, including 225 g of ABS taken from the PLA spool in AMS slot 1; the same was reported on H2S and P2S. The external spool is now read from `ams_mapping2` (left or right on dual-nozzle printers). A slot the mapping marks as unused is no longer given a tray by position. When a mapping names no tray at all, the tray the printer reports decides instead. The last change also stops an unused slot from taking over the external spool that another slot really used and dropping that slot's weight (#2880).
- **A connection check that overruns in the support bundle now says which step hung, and keeps what finished (#3164)** — The support bundle gives each printer's connection diagnostic 15 seconds and used to discard the whole result when it overran, recording only `timed_out`. A 14-printer farm's bundle came back with that marker on every printer and nothing else, so it could not show which check was slow or whether the printers were reachable at all. The diagnostic now records each check as it finishes and the step it is on; a timed-out entry carries `stalled_in` (the step that was still running), `elapsed_s`, and the checks that completed before it.
- **Two inventory notification toggles could never be turned on, so stock alerts have never been able to fire (#2945, reported by @ojimpo; regression tests contributed by @ojimpo in #2956)** — `on_stock_reorder_alert` and `on_stock_break_alert` exist as columns on a notification provider, have their own templates, and `notification_service` looks providers up under exactly those names before sending. The whole UI is there too: a toggle in Add/Edit Notification, a badge on the provider card, the field in the API client's types, and tests for all of it. The one thing missing was the schema. `NotificationProviderCreate`/`Update` never declared either field, and Pydantic drops what it does not declare, so every request that carried them came back `200 OK` with the row unchanged — and `_provider_to_dict`, which is a hand-maintained field-by-field map, never returned them either, so the toggle read back off no matter what the database held. Nothing errored anywhere along that path. Both directions are wired now, and the round-trip tests that already covered the Home Assistant toggles cover these too, because the failure is structural rather than particular to one field: any column missing from those two maps is invisible to a test that builds providers through the ORM, and only a create-then-re-read through the route catches it. This makes the setting stick and report itself honestly; what actually sends the alerts is the #2955 entry above. The tests no longer depend on anyone remembering the next field, either. They now read the list of provider fields from the database model itself, so a toggle or setting added later is checked from the day its column exists, with no new test to write. It must be accepted when a provider is created and when it is edited, and every toggle must survive a save and read back from both the single-provider and the list endpoint. Each toggle is set to the opposite of its default for that check, because nine of them default to on and a test that saves "on" and reads back "on" would pass even if the value were silently dropped.
- **PDF previews in the File Manager failed on any browser older than Chrome 145 or Firefox 144 (#2976)** — Every PDF showed "This file cannot be previewed." pdf.js 6 calls `Map.prototype.getOrInsertComputed` and other recent APIs directly, and the preview loaded its standard build, which assumes them. It now loads pdf.js's legacy build, which bundles polyfills for both the page and the worker. The worker is also bundled through Vite (`?worker&url`) instead of being copied as-is: the copied file kept a class static block that Safari 16.0-16.3 cannot parse, and `build.target` now lowers it like the rest of the app. The Safari 16 baseline check missed that file because it only scanned `.js` output; it now scans `.mjs` too. Checked in a real Chromium without the API, under the app's own Content-Security-Policy: a two-page text PDF renders, pages, and reopens.
### Security
- **Bumped `PyJWT` to 2.15.1 and `urllib3` to 2.8.0** — PyJWT 2.14 and 2.15 fix thirteen advisories, most of them algorithm confusion when one `decode()` call accepts both an HMAC and an asymmetric algorithm, and JWKS fetching through `PyJWKClient`. Bambuddy's session tokens accept only HS256, and SSO fetches the identity provider's key set itself before handing it to PyJWT, so neither path was open to these. The fixes for deeply nested or malformed tokens, and for JWK Sets with one bad key (which now skip that key instead of failing the whole set), do reach the SSO sign-in. urllib3 2.8.0 fixes three advisories in response streaming and HTTPS-proxy TLS; Bambuddy doesn't use urllib3 itself, it arrives through other packages. `virtualenv`, which only the development tools pull in, is pinned to 21.7.13 or later so `pip-audit` stays clean.