fix: correct category updates and improve NexPrint error handling

This commit is contained in:
Tautvydas Derzinskas committed 2026-10-19 18:39:24 +02:00
1 parent 4037151e4a
commit 1cf2903b8e
6 files changed
+30 -14

No files matched your search

+14 -8
View File
@@ -185,6 +185,12 @@ function parseCatIdsInput(raw: string | null | undefined): number[] {
}
const catIdsField = z.string().trim().max(1000).nullable().optional();
/** A field the request leaves out is left as it is (Prisma skips undefined), so a client from before a provider was
* added can't wipe that provider's ids; blank or null still clears them. */
function catIdsUpdate(raw: string | null | undefined): number[] | undefined {
return raw === undefined ? undefined : parseCatIdsInput(raw);
}
const categoryMetaSchema = z.object({
meta_title: z.string().trim().max(200).nullable().optional(),
meta_description: z.string().trim().max(2000).nullable().optional(),
@@ -209,14 +215,14 @@ router.patch(
data: {
metaTitle: body.meta_title || null,
metaDescription: body.meta_description || null,
makerworldCatIds: parseCatIdsInput(body.makerworld_cat_ids),
thingiverseCatIds: parseCatIdsInput(body.thingiverse_cat_ids),
printablesCatIds: parseCatIdsInput(body.printables_cat_ids),
myminifactoryCatIds: parseCatIdsInput(body.myminifactory_cat_ids),
snapmakerCatIds: parseCatIdsInput(body.snapmaker_cat_ids),
makeronlineCatIds: parseCatIdsInput(body.makeronline_cat_ids),
crealitycloudCatIds: parseCatIdsInput(body.crealitycloud_cat_ids),
nexprintCatIds: parseCatIdsInput(body.nexprint_cat_ids).map(String),
makerworldCatIds: catIdsUpdate(body.makerworld_cat_ids),
thingiverseCatIds: catIdsUpdate(body.thingiverse_cat_ids),
printablesCatIds: catIdsUpdate(body.printables_cat_ids),
myminifactoryCatIds: catIdsUpdate(body.myminifactory_cat_ids),
snapmakerCatIds: catIdsUpdate(body.snapmaker_cat_ids),
makeronlineCatIds: catIdsUpdate(body.makeronline_cat_ids),
crealitycloudCatIds: catIdsUpdate(body.crealitycloud_cat_ids),
nexprintCatIds: catIdsUpdate(body.nexprint_cat_ids)?.map(String),
},
});
res.json(toCategoryOut(updated));
+3 -2
View File
@@ -174,8 +174,9 @@ export async function fetchListing(
return listing ? listing.entries.map((e) => e.modelId) : null;
}
if (sync.provider === "nexprint") {
// Collections, the private ones too, list without a sign-in; only their files need one.
const listing = await fetchNexprintCollection(id);
// Collections, the private ones too, list without a sign-in; only their files need one. All of it, not the import
// dialog's first 600, or models past those would never be found.
const listing = await fetchNexprintCollection(id, Number.POSITIVE_INFINITY);
return listing ? listing.entries.map((e) => e.modelId) : null;
}
throw new Error(`Unknown provider "${sync.provider}"`);
+3 -1
View File
@@ -154,7 +154,9 @@ export async function checkNexprintAuth(
try {
const { code, data } = await fetchApi("/model-user-server/member/user_data", { body: {} }, token);
const user = isRecord(data) && isRecord(data.userData) ? data.userData : null;
if (code !== CODE_OK || !user || !outId(user.userId, "U")) return code === null ? "unreachable" : "rejected";
// Only a refusal says the token is bad; any other error code is NexPrint's own trouble.
if (code !== CODE_OK) return code === CODE_UNAUTHORIZED ? "rejected" : "unreachable";
if (!user || !outId(user.userId, "U")) return "rejected";
return { userId: outId(user.userId, "U")!, name: str(user.nickname) };
} catch (err) {
return err instanceof NexprintAuthError ? "rejected" : "unreachable";
+4
View File
@@ -124,6 +124,10 @@ describe("NexPrint sign-in", () => {
stubApi({ "/model-user-server/member/user_data": () => apiResponse(401, null, "账号未登录") });
expect(await checkNexprintAuth(TOKEN)).toBe("rejected");
// Its own trouble isn't the token's fault.
stubApi({ "/model-user-server/member/user_data": () => apiResponse(500, null, "系统繁忙") });
expect(await checkNexprintAuth(TOKEN)).toBe("unreachable");
});
});
+2 -2
View File
@@ -42,8 +42,8 @@ from Thingport Grab wait in **Administration → Import queue** until they're st
Printables works with no setup. MakerWorld needs your session cookie, and Thingiverse and MyMiniFactory a key set by an
admin; MyMiniFactory models you bought come in once you connect your MyMiniFactory account. Snapmaker Space needs your
own sign-in, which Thingport Grab keeps fresh, MakerOnline Thingport Grab for its files, and Creality Cloud and NexPrint your own
sign-in, which Grab sends on when you visit them. See
own sign-in, which Thingport Grab keeps fresh; MakerOnline's files come only through Thingport Grab; and Creality Cloud
and NexPrint need your own sign-in, which Grab sends to Thingport when you visit them. See
[provider setup](../PROVIDER_SETUP.md). Admins can also
[turn a provider off](../PROVIDER_SETUP.md#turning-a-provider-off) for the whole instance, and then nothing is imported
from it.
+4 -1
View File
@@ -20,7 +20,10 @@ export async function syncNexprintSession(): Promise<void> {
// Per Thingport account, so switching the extension to another one sends it there too.
const account = `${config.instanceUrl} ${config.email}`;
const last = config.lastSyncedNexprintSession;
const token = cookie?.value ? decodeURIComponent(cookie.value) : "";
let token = "";
try {
token = cookie?.value ? decodeURIComponent(cookie.value) : "";
} catch {} // Malformed percent-encoding: not a token.
// Signed out, the site clears the cookie or leaves a placeholder in it.
if (!/^[0-9a-f]{32,128}$/i.test(token)) {
// Clear the sign-in Grab sent, but not one pasted in Thingport.