147 Commits
Author SHA1 Message Date
Tautvydas Derzinskas 58eb011793 fix: announce each version once, and don't flash old notes on reopen
- A downgrade leaves the recorded version alone, so going back up to it
  isn't announced again.
- The recorded version is claimed by moving it on from the value read, so
  of two processes starting at once only one notifies.
- The dialog clears the last notes before it paints.
- Clearer wording for fresh installs in the docs and for the picture move
  in the 1.2.0 post.
2026-10-21 21:48:58 +02:00
Tautvydas Derzinskas aab83df886 feat: let a release update without notifying users
Versions listed in release-notes/quiet-releases.json, e.g. a hotfix straight
after a release, update without a notification. A quiet version still counts
as the one that ran, so a restart doesn't announce it, while the version the
next announcement's notes start from stays on the last announced one, so
they cover the quiet release too. An unreadable list means not quiet.
2026-10-21 21:36:44 +02:00
Tautvydas Derzinskas 60ee06ba43 fix: keep skipped releases with curated notes, and show a failed copy
- Someone who skipped releases now gets them from the changelog after the
  curated notes, up to five releases in all, without the frontend release
  the curated notes already cover.
- The tests remove the update notifications sent to every user in the
  shared test database, not only their own user's.
- Where the clipboard API is missing (plain HTTP) or refuses, the command
  is selected and the tooltip says to copy it by hand.
2026-10-21 21:33:28 +02:00
Tautvydas Derzinskas aebc0ac97e feat: show what's new after an update
The first time the backend starts on a newer version than it last ran, every
user gets a system notification that opens a What's new dialog; a fresh
install isn't told. The dialog is also in the user menu.

Its content is fetched from main, so it can be written after a release:
release-notes/latest-release-notes.json when its version matches the
instance's, otherwise the backend's CHANGELOG.md entry with the frontend
release that went out with it. An instance that skipped versions gets every
release since the one it ran, up to five. Curated items can be highlighted,
carry screenshots and a command to copy. RELEASE_NOTES_URL points at another
copy, or turns fetching off when empty.
2026-10-21 21:15:29 +02:00
Tautvydas Derzinskas 88949d9d8c feat: let users choose what their API token can read over the REST API
The API token read every GET endpoint. It now reads only the scopes its user allows
(search, models, dashboard, activity, collections, tags, categories, authors, images,
model files, imports, everything else), set beside the MCP tools in a sectioned Token
permissions dialog. Existing and new tokens get every scope, so integrations such as
Homarr and holm keep working; a refused request names the scope it needs.
2026-10-21 17:57:28 +02:00
Tautvydas Derzinskas 0d0eeaf7a8 Merge pull request #111 from Ruivalim/feat/mcp-endpoint
feat: MCP endpoint for AI assistants, with tools chosen per API token
2026-10-21 16:57:17 +02:00
Mantas Kantautas 060be2f692 fix: keep non-ASCII names readable in zip downloads
The zip writer stores entry names as UTF-8 but did not set the UTF-8 flag
(general purpose bit 11), so readers decoded them as CP437 or Latin-1.
Any model, category or file name with a non-ASCII character came out
garbled, including when the zip was imported back into Thingport.
2026-10-21 14:07:33 +03:00
Tautvydas Derzinskas 6555875e29 fix: check an imported model's description once its import is done
The AI description check started as soon as the model was created, before
its pictures were attached and its description's images stored locally.
Storing them rewrites the description, so a suggestion that came back
after that was dropped and logged as unchanged. Imports now start the
check when they finish; uploads still start it straight away.
2026-10-21 12:48:46 +02:00
Tautvydas Derzinskas 8affbbb6c3 fix: count only MakerWorld profiles with an id before taking the raw files
The import now skips profile records without an id, as the profile jobs
already did, so a model with none usable gets its raw files there too.
2026-10-21 12:12:35 +02:00
Tautvydas Derzinskas 437d85be14 feat: import MakerWorld models that have only raw files
Some MakerWorld listings have no print profiles, only the STL/CAD files
behind "Download STL/CAD files". The backend now downloads those as the
page's own zip and unpacks every model file onto the model, and a zip
Grab passes on is unpacked the same way instead of being stored whole.
Every-profile and queued imports take the raw files instead of failing.
2026-10-21 11:58:06 +02:00
Tautvydas Derzinskas 8b4653e415 fix: harden mesh check against bad indices and tidy review follow-ups
- Reject triangle indices outside the vertex list instead of stalling the queue
- Leave degenerate triangles out of body bounding boxes
- Skip the quality fetch without a print id and never show a previous print's result
- Disable the per-check switches while a save is in flight
2026-10-21 08:55:58 +02:00
Tautvydas Derzinskas dec1b0dbe3 feat: admin MCP access, token permissions dialog, and review fixes
Follow-up to the review of the MCP endpoint, after merging develop.

Backend
- Instance-wide MCP access an admin sets (Off / Read only / Read and
  change, default Read and change), at GET/POST /settings/mcp and logged
  as mcp_access_changed. Off turns every token away at /api/mcp; Read
  only offers and answers only the tools that read, refuses turning a
  write tool on, and keeps a token's earlier write choices so they come
  back with Read and change. GET /profile/api-token carries mcp_access
  and only the tools it offers.
- Model links come from the public URL an admin saved, not the
  PUBLIC_URL env var it's only seeded from.
- An import refuses a category that isn't the user's own, for POST
  /import and import_model alike.
- add_to_collection inserts and logs only the models it actually adds.
- get_import_status refuses a task the web app started instead of
  failing on its result.
- The migration is now 20261030120000_api_token_mcp_tools, after every
  migration on develop and on open branches.
- Tests for each access level, the category check, a web app task id
  and the logged count.

Frontend
- The token's MCP tools move off the profile into a Token permissions
  dialog behind a cog beside the token, with MCP as its first section
  so the REST API's permissions can join it later.
- Each tool is a row: a small CSS tile picturing what it does (moving
  only while its row is hovered or focused, still for reduced motion,
  greyed when denied), its name and description, and a Deny / Allow
  Segmented control. Tools are grouped by whether they read or change
  the library.
- Admin Auth page: an AI assistants (MCP) section to set the access.
- The connect command gets a copy button and is hidden while MCP is off.
- Regenerating says the token's tools go back to the read-only ones.
- Save errors go through toasts.
- Spanish and French strings for every MCP key, and the new keys in all
  four languages.

Docs
- mcp.md and api-token.md: the Token permissions dialog, the admin
  setting, the regenerate reset, and what an assistant reading scraped
  descriptions could be talked into.
2026-10-21 08:47:49 +02:00
Tautvydas Derzinskas 12a6375832 Merge branch 'develop' into feat/mcp-endpoint 2026-10-21 08:47:09 +02:00
Tautvydas Derzinskas f913710cc4 feat: check models for print problems when they are opened
Closes #37. The first time a model is opened, each of its STL, OBJ and 3MF
files is checked in the render worker for holes, non-manifold edges, flipped
or inside-out faces, loose fragments, missing 3MF parts, damaged files and an
unlikely size. The rules follow ADMesh and Blender's 3D Print Toolbox.

The model page shows a scan overlay while it runs, then a tick or a
severity-coloured warning beside 3D Preview that opens a report with an
animated tile per problem. Results are saved per plate and rechecked only
when QUALITY_CHECK_VERSION changes.

Admins can switch checks off as a whole or per problem under Rendering;
severity and switched-off problems apply when results are read, so neither
needs a recheck. Checks are on by default.
2026-10-21 07:41:24 +02:00
Tautvydas Derzinskas 8db1c02f07 Merge pull request #108 from TautvydasDerzinskas/feat/reset-to-defaults
Feat/reset to defaults
2026-10-20 20:39:57 +02:00
Rui Valim 04e97197d7 fix(mcp): rank list_tags by use and let it narrow, instead of every tag at once
A real library of 488 models has 4,306 tags: the whole list came to 63 KB,
more of an assistant's context than one answer should take. It now
returns the most used 200 by default, with how many models have each,
and takes a query to narrow them.
2026-10-20 15:00:20 -03:00
Tautvydas Derzinskas 1a44b87cdc fix: count suffixed renames, roll back failed theme saves, keep reset dialog open on error 2026-10-20 19:58:08 +02:00
Rui Valim a36a99639a feat(backend): MCP tools that import models and file them into collections
import_model imports a link through the same checks, log and
notification as + Add > Import, optionally into a collection, and hands
an import still running after 25 seconds over to get_import_status.
create_collection and add_to_collection make and fill collections. None
deletes or overwrites anything.

They're off for every token until its user turns them on. import_model
skips the captcha, as Thingport Grab does: an assistant has nowhere to
show one, and the user chose to give it the tool.
2026-10-20 14:54:41 -03:00
Tautvydas Derzinskas a0dbba8058 feat: sort, bulk actions and merge for collections and tags
Collections sort by newest, name or most models; tags gain a newest sort. Both pages get select mode with merge, bookmark and delete. Merging asks which one to keep and previews the result: the kept one's new model count, what is deleted, a bookmark moving over and syncs that stop. Built-in collections sit select mode out.
2026-10-20 19:50:56 +02:00
Rui Valim 8567f195c0 feat(backend): MCP endpoint at /api/mcp, with tools chosen per API token
An AI assistant connects over Streamable HTTP (stateless) with the user's
API token and gets read-only tools: search_models, list_models,
get_model, list_collections, get_collection, list_tags,
list_categories, get_author and library_stats. Each runs as the token's
user through the same services as the REST API.

Every MCP message is a POST, which requireAuth refuses to an API token,
so /api/mcp checks the token itself and is mounted before the routers
that apply requireAuth to every path. The token stays read-only over
REST; over MCP it only reaches the tools its user ticked, stored per
token in User.apiTokenMcpTools. Existing tokens get the read-only tools;
a tool that writes would be off for every token until its user turns it
on.

get_model reads through printOutById rather than GET /print/:id, so an
assistant looking a model up doesn't count as a view or land in
Browsing History.
2026-10-20 14:23:37 -03:00
Tautvydas Derzinskas f8424dcb23 feat: reset categories to the starter tree, previewing what changes 2026-10-20 19:22:56 +02:00
Tautvydas Derzinskas 67bea149a9 fix: list a nested pictures folder's files as its own model's 2026-10-20 16:14:50 +02:00
Tautvydas Derzinskas ecc551ef27 fix: touch only a model's own pictures, check picture files once, drop plate folders for other files 2026-10-20 15:46:22 +02:00
Tautvydas Derzinskas 0d505b716a feat: keep a model's pictures and supporting files beside its model files
Gallery and description images, plate thumbnails and renders now live in an
Images folder next to the model's files (Print.assetsPath), and supporting and
prepared files sit beside the plates, named through the storage template. They
move with the model when the storage structure changes, and the storage browser
lists them by kind.

Models from before keep working: reads fall back to the old shared folders, and
the first start runs the storage move to bring their files over, then clears
what's left in those folders.
2026-10-20 15:09:19 +02:00
Tautvydas Derzinskas 4c301bb460 test: cover render colour and hover slideshow, drop dashboard thumbnails tests 2026-10-20 13:49:15 +02:00
Tautvydas Derzinskas 9a81ad8c9e test: remove incorrect cults3d_no_key assertion 2026-10-20 06:07:40 +02:00
Tautvydas Derzinskas e1ed0c6ff0 refactor: move Cults3D credential checks upstream and prefer keyed users
Cults3D API requires each model's owner's own API key. Move credential checks
into the lookup loop to detect unavailable keys earlier, and restructure the
selection query to prefer models from owners with configured API keys.
2026-10-20 06:03:25 +02:00
Tautvydas Derzinskas 37094b56db refactor: add Cults3D error handling and enhance sync provider picker with logos 2026-10-20 05:34:53 +02:00
Tautvydas Derzinskas 930b9b48db feat: add Cults3D provider
Imports Cults3D models, anyone's likes and the user's own printlists, and
syncs them. Metadata comes from Cults3D's GraphQL API with each user's own
nickname and API key. The API never hands out files, and the site's pages are
behind a Cloudflare check only a browser passes, so Thingport Grab sends the
browser's _session_id and cf_clearance cookies with its User-Agent; with
them the server orders a free model for nothing, as its Download button
does, and follows the download page to the signed file link (CloudFront, or
Cults3D's Scaleway bucket for older models). Paid models download once
bought. A Cloudflare block defers a sync until the next visit with Grab.
2026-10-20 05:08:08 +02:00
Tautvydas Derzinskas 68a1481707 fix: page a Thangs collection to the end without a total
A collection answer without totalCount stopped after its first page, so syncs missed every model past 50.
2026-10-20 00:02:34 +02:00
Tautvydas Derzinskas 2591714c0c feat: add Thangs provider
Imports Thangs models, likes and collections, and syncs them. Metadata is
public; files need the user's own sign-in, which Thingport Grab sends and
Thingport renews from the refresh token. Paid models download once owned;
print-only ones are refused before any download. Free accounts' daily
download limit stops a batch and defers the rest of a sync to its next check.
2026-10-19 23:36:07 +02:00
Tautvydas Derzinskas cf0653b005 fix: models with images should use them as thumbnails
Uploaded images now replace generated renders for cover and thumbnail.
2026-10-19 21:01:50 +02:00
Tautvydas Derzinskas f34a488fb2 fix: tell print profiles apart by name and picture on every provider
Profile files often come named alike, so the files on a model read the
same. A profile whose file clashes with another profile's is now named
after its profile, or by its title alone when that already holds the
file's name; profiles with the same title keep the site's name.

Each profile's own picture now becomes its file's thumbnail on Snapmaker
Space, MakerOnline, Creality Cloud and NexPrint too, not just MakerWorld.

Earlier profiles are renamed before the new file is placed, and a preview
render that loses its file to a rename retries from where it went.
2026-10-19 20:34:24 +02:00
Tautvydas Derzinskas c6c8161a74 fix: Makerworld profile thumbnails being all the same 2026-10-19 20:06:07 +02:00
Tautvydas Derzinskas 1cf2903b8e fix: correct category updates and improve NexPrint error handling 2026-10-19 18:39:24 +02:00
Tautvydas Derzinskas 4037151e4a feat: enable NextPrint provider 2026-10-19 18:18:26 +02:00
Tautvydas Derzinskas 42be6fb988 refactor: validate Creality Cloud account and improve session handling 2026-10-19 14:34:50 +02:00
Tautvydas Derzinskas 772b1acfef feat: enable Creality Cloud provider 2026-10-19 14:13:45 +02:00
Tautvydas Derzinskas 5bf3457e8c feat(providers): enable MakerOnline provider 2026-10-19 07:41:05 +02:00
Tautvydas Derzinskas 44f4bf2635 fix: handle malformed tokens, add refresh concurrency control, and improve list URL validation
- Wrap decodeURIComponent in try-catch to handle malformed percent-encoding
- Add inFlight promise to prevent concurrent callers from reusing single-use refresh tokens
- Require list parameter to be "likes" or "bookmarks" for Snapmaker list URLs
- Update documentation to clarify Snapmaker sign-in renewal and token transmission
2026-10-19 01:20:18 +02:00
Tautvydas Derzinskas ac5bf253c7 feat(providers): enable Snapmaker Space provider 2026-10-19 00:57:12 +02:00
Tautvydas Derzinskas 24bb8d430a refactor: update extension popup and address pr comments 2026-10-18 21:21:42 +02:00
Tautvydas Derzinskas a7a91902a0 feat: add MyMiniFactory as a provider
Import, collection import and synced collections from MyMiniFactory, alongside MakerWorld,
Printables and Thingiverse.

- Backend: API v2 client with an instance-wide API key for metadata, collections and
  authors; files from MyMiniFactory's own download links (free models need no login).
  Paid models, and free ones whose files are archives, download as the user from their
  MyMiniFactory library through their session (PHPSESSID), fetching the model files
  MyMiniFactory unpacked from each archive. Paid models are left out of collection
  listings and syncs.
- One migration: MyMiniFactory category ids, username and session columns, plus a data
  migration giving existing users' starter categories the new category ids. Category
  matching now prefers the most specific site category.
- Frontend: provider everywhere the others appear (badges, Configuration and
  Administration providers, sync Browse tab, user menu, link detection, System Health,
  category ids), shared instance-key form for Thingiverse and MyMiniFactory, en and lt.
- Thingport Grab: new provider sites are allowed once from the popup (optional
  all-sites access, content script registered at runtime), so adding one never makes
  the browser disable the extension; captures the signed-in MyMiniFactory username and
  session; paid-model notice.
- Docs, website, store assets and screenshots updated.
2026-10-18 18:19:43 +02:00
Tim MooreandClaude Opus 5.5 b30b0cefd8 feat: replace the AI Thinking toggle with a per-connection reasoning effort
The Thinking switch had two states: send nothing, or send
`reasoning_effort: "none"`. That suits qwen3, which stops thinking on
"none", but not gpt-oss, which cannot stop reasoning, ignores "none" and
only takes low/medium/high. With Thinking off, gpt-oss:20b reasoned more
than with it on and ran out of the 1024-token categorisation budget three
times as often, and there was no way to ask it for "low".

Each AI connection now has a reasoning effort: the provider's default
(send nothing), none, low, medium or high. A migration keeps every
existing connection sending exactly what it sent before: Thinking on
becomes the provider default, off becomes "none". A provider that answers
400 naming reasoning_effort is still asked again without it, whichever
value was set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:16:20 +11:00
Tautvydas Derzinskas 8e6e5b2e0e fix: download from Thingiverse with a Chrome TLS fingerprint
Thingiverse's Cloudflare now challenges every request whose TLS handshake
isn't a browser's, whatever the headers or cookies, so file downloads,
preview images and API calls all failed. Route all thingiverse.com
requests through impit, which impersonates Chrome.
2026-10-18 04:38:47 +02:00
Tautvydas Derzinskas b7c2f36ace fix: retry file downloads blocked by Cloudflare or rate limits
Retry downloads once with a delay when blocked by a 429 or Cloudflare
challenge, then report the failure so the caller can handle it. Limit
Thingiverse download concurrency to 2 to reduce triggering rate limits.
2026-10-17 20:48:38 +02:00
Tautvydas Derzinskas cc4107694a fix: rename Thingiverse Access Token to App Token to match what it provides 2026-10-17 20:27:58 +02:00
Tautvydas Derzinskas 358dfc1a03 feat: system notifications updates, Thingport grab in System health check, node 24+ (#52)
* feat: update system notifications

* feat: add Thingport grab to system health check

* ci: upgrade to Node.js 24 and centralize version management
2026-10-17 10:44:13 +02:00
Tautvydas Derzinskas 0bc0f99482 Fix/various minor ux improvements (#51)
* fix: improve context menu interaction and update check display

Add reopenContextMenuBelow utility to close context menus when clicking
outside and forward the right-click event to underlying elements. This
prevents clicks from being lost when users right-click beside a menu.

Replace UpdateCheckSection status text with UpdateCheckAnimation, an
animated component showing orbiting models during checking and colored
status indicator bubbles after completion.

* feat: add admin pages to search

* feat: add internet check to system health
2026-10-17 09:26:10 +02:00
Tautvydas Derzinskas 9a2238691d feat: administration settings remake + OIDC support (#49)
* feat: expand storage usage reporting with disk breakdown

* feat: add providers admin view

* feat(storage): add background reorganization and admin browser

Adds background-driven storage reorganization for moving model files
when the storage template changes, with an admin interface to preview
changes, check impact, monitor progress, and browse the storage tree
built from plate records rather than filesystem walks.

* feat: new auth admin settings page + OIDC support

* test: fix failing test
2026-10-17 05:07:24 +02:00