wiki-plugin-shoppe 0.0.31 → 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/server.js +32 -11
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -543,11 +543,32 @@ async function sanoraCreateProduct(tenant, title, category, description, price,
|
|
|
543
543
|
}
|
|
544
544
|
);
|
|
545
545
|
|
|
546
|
+
if (!resp.ok) {
|
|
547
|
+
const text = await resp.text().catch(() => '');
|
|
548
|
+
throw new Error(`Create product failed (${resp.status}): ${text.slice(0, 200)}`);
|
|
549
|
+
}
|
|
546
550
|
const product = await resp.json();
|
|
547
551
|
if (product.error) throw new Error(`Create product failed: ${product.error}`);
|
|
548
552
|
return product;
|
|
549
553
|
}
|
|
550
554
|
|
|
555
|
+
// Wrapper used by processArchive. On "not found" (Sanora Redis cleared mid-upload),
|
|
556
|
+
// re-registers the tenant and retries once. tenant.uuid may be updated in place.
|
|
557
|
+
async function sanoraCreateProductResilient(tenant, title, category, description, price, shipping, tags) {
|
|
558
|
+
try {
|
|
559
|
+
return await sanoraCreateProduct(tenant, title, category, description, price, shipping, tags);
|
|
560
|
+
} catch (err) {
|
|
561
|
+
if (err.message.includes('not found') || err.message.includes('404')) {
|
|
562
|
+
console.warn(`[shoppe] Sanora user lost mid-upload, re-registering and retrying: ${title}`);
|
|
563
|
+
const updated = await sanoraEnsureUser(tenant);
|
|
564
|
+
// Mutate tenant in place so all subsequent calls use the new UUID
|
|
565
|
+
tenant.uuid = updated.uuid;
|
|
566
|
+
return await sanoraCreateProduct(tenant, title, category, description, price, shipping, tags);
|
|
567
|
+
}
|
|
568
|
+
throw err;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
551
572
|
async function sanoraUploadArtifact(tenant, title, fileBuffer, filename, artifactType) {
|
|
552
573
|
const { uuid, keys } = tenant;
|
|
553
574
|
const timestamp = Date.now().toString();
|
|
@@ -811,7 +832,7 @@ async function processArchive(zipPath) {
|
|
|
811
832
|
const description = info.description || '';
|
|
812
833
|
const price = info.price || 0;
|
|
813
834
|
|
|
814
|
-
await
|
|
835
|
+
await sanoraCreateProductResilient(tenant, title, 'book', description, price, 0, buildTags('book', info.keywords));
|
|
815
836
|
|
|
816
837
|
// Cover image — use info.cover to pin a specific file, else first image found
|
|
817
838
|
const covers = fs.readdirSync(entryPath).filter(f => IMAGE_EXTS.has(path.extname(f).toLowerCase()));
|
|
@@ -853,7 +874,7 @@ async function processArchive(zipPath) {
|
|
|
853
874
|
try {
|
|
854
875
|
const description = info.description || `Album: ${albumTitle}`;
|
|
855
876
|
const price = info.price || 0;
|
|
856
|
-
await
|
|
877
|
+
await sanoraCreateProductResilient(tenant, albumTitle, 'music', description, price, 0, buildTags('music,album', info.keywords));
|
|
857
878
|
const coverFile = info.cover ? (covers.find(f => f === info.cover) || covers[0]) : covers[0];
|
|
858
879
|
if (coverFile) {
|
|
859
880
|
const coverBuf = fs.readFileSync(path.join(entryPath, coverFile));
|
|
@@ -882,7 +903,7 @@ async function processArchive(zipPath) {
|
|
|
882
903
|
const buf = fs.readFileSync(entryPath);
|
|
883
904
|
const description = trackInfo.description || `Track: ${title}`;
|
|
884
905
|
const price = trackInfo.price || 0;
|
|
885
|
-
await
|
|
906
|
+
await sanoraCreateProductResilient(tenant, title, 'music', description, price, 0, buildTags('music,track', trackInfo.keywords));
|
|
886
907
|
await sanoraUploadArtifact(tenant, title, buf, entry, 'audio');
|
|
887
908
|
results.music.push({ title, type: 'track' });
|
|
888
909
|
console.log(`[shoppe] 🎵 track: ${title}`);
|
|
@@ -922,7 +943,7 @@ async function processArchive(zipPath) {
|
|
|
922
943
|
// Register the series itself as a parent product
|
|
923
944
|
try {
|
|
924
945
|
const description = info.description || `A ${subDirs.length}-part series`;
|
|
925
|
-
await
|
|
946
|
+
await sanoraCreateProductResilient(tenant, seriesTitle, 'post-series', description, 0, 0, buildTags(`post,series,order:${order}`, info.keywords));
|
|
926
947
|
|
|
927
948
|
const covers = fs.readdirSync(entryPath).filter(f => IMAGE_EXTS.has(path.extname(f).toLowerCase()));
|
|
928
949
|
if (covers.length > 0) {
|
|
@@ -962,7 +983,7 @@ async function processArchive(zipPath) {
|
|
|
962
983
|
const productTitle = `${seriesTitle}: ${resolvedTitle}`;
|
|
963
984
|
const description = partInfo.description || partFm.body.split('\n\n')[0].replace(/^#+\s*/, '').trim() || resolvedTitle;
|
|
964
985
|
|
|
965
|
-
await
|
|
986
|
+
await sanoraCreateProductResilient(tenant, productTitle, 'post', description, 0, 0,
|
|
966
987
|
buildTags(`post,blog,series:${seriesTitle},part:${partIndex + 1},order:${order}`, partInfo.keywords));
|
|
967
988
|
|
|
968
989
|
await sanoraUploadArtifact(tenant, productTitle, mdBuf, partMdFiles[0], 'text');
|
|
@@ -1004,7 +1025,7 @@ async function processArchive(zipPath) {
|
|
|
1004
1025
|
const firstLine = fm.body.split('\n').find(l => l.trim()).replace(/^#+\s*/, '');
|
|
1005
1026
|
const description = info.description || fm.body.split('\n\n')[0].replace(/^#+\s*/, '').trim() || firstLine || title;
|
|
1006
1027
|
|
|
1007
|
-
await
|
|
1028
|
+
await sanoraCreateProductResilient(tenant, title, 'post', description, 0, 0, buildTags(`post,blog,order:${order}`, info.keywords));
|
|
1008
1029
|
await sanoraUploadArtifact(tenant, title, mdBuf, mdFiles[0], 'text');
|
|
1009
1030
|
|
|
1010
1031
|
const covers = fs.readdirSync(entryPath).filter(f => IMAGE_EXTS.has(path.extname(f).toLowerCase()));
|
|
@@ -1041,7 +1062,7 @@ async function processArchive(zipPath) {
|
|
|
1041
1062
|
if (!fs.statSync(entryPath).isDirectory()) continue;
|
|
1042
1063
|
const images = fs.readdirSync(entryPath).filter(f => IMAGE_EXTS.has(path.extname(f).toLowerCase()));
|
|
1043
1064
|
try {
|
|
1044
|
-
await
|
|
1065
|
+
await sanoraCreateProductResilient(tenant, entry, 'album', `Photo album: ${entry}`, 0, 0, 'album,photos');
|
|
1045
1066
|
if (images.length > 0) {
|
|
1046
1067
|
const coverBuf = fs.readFileSync(path.join(entryPath, images[0]));
|
|
1047
1068
|
await sanoraUploadImage(tenant, entry, coverBuf, images[0]);
|
|
@@ -1078,7 +1099,7 @@ async function processArchive(zipPath) {
|
|
|
1078
1099
|
const price = info.price || 0;
|
|
1079
1100
|
const shipping = info.shipping || 0;
|
|
1080
1101
|
|
|
1081
|
-
await
|
|
1102
|
+
await sanoraCreateProductResilient(tenant, title, 'product', description, price, shipping, buildTags(`product,physical,order:${order}`, info.keywords));
|
|
1082
1103
|
|
|
1083
1104
|
// Hero image: prefer hero.jpg / hero.png, fall back to first image
|
|
1084
1105
|
const images = fs.readdirSync(entryPath).filter(f => IMAGE_EXTS.has(path.extname(f).toLowerCase()));
|
|
@@ -1119,7 +1140,7 @@ async function processArchive(zipPath) {
|
|
|
1119
1140
|
renewalDays: info.renewalDays || 30
|
|
1120
1141
|
};
|
|
1121
1142
|
|
|
1122
|
-
await
|
|
1143
|
+
await sanoraCreateProductResilient(tenant, title, 'subscription', description, price, 0, buildTags('subscription', info.keywords));
|
|
1123
1144
|
|
|
1124
1145
|
// Upload tier metadata (benefits list, renewal period) as a JSON artifact
|
|
1125
1146
|
const tierBuf = Buffer.from(JSON.stringify(tierMeta));
|
|
@@ -1194,7 +1215,7 @@ async function processArchive(zipPath) {
|
|
|
1194
1215
|
(lucilleVideoId ? `,lucille-id:${lucilleVideoId},lucille-url:${lucilleBase}` : '');
|
|
1195
1216
|
|
|
1196
1217
|
// Sanora catalog entry (for discovery / storefront)
|
|
1197
|
-
await
|
|
1218
|
+
await sanoraCreateProductResilient(tenant, title, 'video', description, price, 0, videoTags);
|
|
1198
1219
|
|
|
1199
1220
|
// Cover / poster image (optional)
|
|
1200
1221
|
const images = fs.readdirSync(entryPath).filter(f => IMAGE_EXTS.has(path.extname(f).toLowerCase()));
|
|
@@ -1240,7 +1261,7 @@ async function processArchive(zipPath) {
|
|
|
1240
1261
|
advanceDays: info.advanceDays || 30
|
|
1241
1262
|
};
|
|
1242
1263
|
|
|
1243
|
-
await
|
|
1264
|
+
await sanoraCreateProductResilient(tenant, title, 'appointment', description, price, 0, buildTags('appointment', info.keywords));
|
|
1244
1265
|
|
|
1245
1266
|
// Upload schedule as a JSON artifact so the booking page can retrieve it
|
|
1246
1267
|
const scheduleBuf = Buffer.from(JSON.stringify(schedule));
|