feat: make sure thingiverse token is verified

This commit is contained in:
Tautvydas Derzinskas committed 2026-09-13 14:42:22 +02:00
1 parent f5816f8e78
commit 9d07db9dc9
2 files changed
+28 -1

No files matched your search

+11 -1
View File
@@ -27,6 +27,7 @@ import {
} from "../services/printService";
import { getUserMakerworldCookie, setUserMakerworldCookie } from "../services/makerworldCookieService";
import { verifyMakerworldCookie } from "../services/makerworldCloudApi";
import { verifyThingiverseAccessToken } from "../services/thingiverseApi";
import { SLICER_IDS, getUserSlicer, setUserSlicer } from "../services/slicerPreferenceService";
import { THEME_SELECTIONS, getUserTheme, setUserTheme } from "../services/themePreferenceService";
import { checkForUpdates } from "../services/versionService";
@@ -143,8 +144,17 @@ router.post(
requireAdmin,
asyncHandler(async (req, res) => {
const body = parseBody(thingiverseSettingsSchema, req.body);
const trimmed = (body.access_token ?? "").trim();
// Only a new, non-empty token needs testing -- clearing it always succeeds, since there's
// nothing to verify a logged-out state against.
if (trimmed && !(await verifyThingiverseAccessToken(trimmed))) {
throw new HttpError(
422,
"Couldn't verify this Thingiverse Access Token -- it may be invalid, revoked, or Thingiverse is rate-limiting this instance right now. Double-check the token at thingiverse.com/apps/create and try again.",
);
}
await setThingiverseAccessToken(body.access_token);
res.json({ configured: Boolean(body.access_token && body.access_token.trim()) });
res.json({ configured: Boolean(trimmed) });
}),
);
+17
View File
@@ -145,6 +145,23 @@ async function fetchThingiverseApiJson(path: string, accessToken: string): Promi
return data;
}
/** Called before storing an Access Token pasted into Admin Settings > Thingiverse (see
* routes/settings.ts' POST /settings/thingiverse), so a mistyped/revoked token is rejected up
* front instead of only surfacing as a failed import later. Reuses the same fetch path (and its
* FlareSolverr/rate-limit handling) as every other Thingiverse call here, against /users/me --
* the official API's own "who am I" endpoint, so a success means the token is genuinely valid,
* not just well-formed. A transient Cloudflare rate-limit (ThingiverseRateLimitError) is treated
* the same as an invalid token here: either way this specific check can't confirm the token
* right now, and the caller's message covers both cases rather than guessing which one it was. */
export async function verifyThingiverseAccessToken(accessToken: string): Promise<boolean> {
try {
const data = await fetchThingiverseApiJson("/users/me", accessToken);
return isRecord(data);
} catch {
return false;
}
}
export type ThingiversePlateFile = { name: string; url: string };
export type ThingiverseGalleryImage = { name: string; url: string };