wiki-plugin-shoppe 0.0.29 → 0.0.31

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server/server.js +50 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-plugin-shoppe",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Multi-tenant digital goods shoppe for federated wiki, powered by Sanora",
5
5
  "keywords": [
6
6
  "wiki",
package/server/server.js CHANGED
@@ -477,6 +477,44 @@ function getMimeType(filename) {
477
477
  })[ext] || 'application/octet-stream';
478
478
  }
479
479
 
480
+ // Ensure the tenant's Sanora user exists (Redis may have been wiped).
481
+ // If the user is found by pubKey but has a different UUID (new registration),
482
+ // updates tenants.json so all subsequent product calls use the correct UUID.
483
+ async function sanoraEnsureUser(tenant) {
484
+ const { keys } = tenant;
485
+ const timestamp = Date.now().toString();
486
+ const message = timestamp + keys.pubKey;
487
+ sessionless.getKeys = () => keys;
488
+ const signature = await sessionless.sign(message);
489
+
490
+ const resp = await fetch(`${getSanoraUrl()}/user/create`, {
491
+ method: 'PUT',
492
+ headers: { 'Content-Type': 'application/json' },
493
+ body: JSON.stringify({ timestamp, pubKey: keys.pubKey, signature }),
494
+ timeout: 15000
495
+ });
496
+
497
+ if (!resp.ok) {
498
+ const text = await resp.text().catch(() => '');
499
+ throw new Error(`Sanora user ensure failed (${resp.status}): ${text.slice(0, 200)}`);
500
+ }
501
+
502
+ const sanoraUser = await resp.json();
503
+ if (sanoraUser.error) throw new Error(`Sanora user ensure: ${sanoraUser.error}`);
504
+
505
+ if (sanoraUser.uuid !== tenant.uuid) {
506
+ console.log(`[shoppe] Sanora UUID changed ${tenant.uuid} → ${sanoraUser.uuid} (Redis was reset). Updating tenants.json.`);
507
+ const tenants = loadTenants();
508
+ const oldUuid = tenant.uuid;
509
+ delete tenants[oldUuid];
510
+ tenant.uuid = sanoraUser.uuid;
511
+ tenants[sanoraUser.uuid] = tenant;
512
+ saveTenants(tenants);
513
+ }
514
+
515
+ return tenant; // tenant.uuid is now correct
516
+ }
517
+
480
518
  async function sanoraCreateProduct(tenant, title, category, description, price, shipping, tags) {
481
519
  const { uuid, keys } = tenant;
482
520
  const timestamp = Date.now().toString();
@@ -678,7 +716,13 @@ async function processArchive(zipPath) {
678
716
  // Use system unzip to stream-extract without loading entire archive into RAM.
679
717
  // AdmZip loads the whole zip into memory upfront, which OOM-kills Node on large archives.
680
718
  try {
681
- execSync(`unzip -o "${zipPath}" -d "${tmpDir}"`, { stdio: 'pipe' });
719
+ const unzipBin = (() => {
720
+ for (const p of ['/usr/bin/unzip', '/bin/unzip', '/usr/local/bin/unzip']) {
721
+ if (fs.existsSync(p)) return p;
722
+ }
723
+ return 'unzip'; // fallback, let it fail with a clear error
724
+ })();
725
+ execSync(`"${unzipBin}" -o "${zipPath}" -d "${tmpDir}"`, { stdio: 'pipe' });
682
726
  } catch (err) {
683
727
  throw new Error(`Failed to extract archive: ${err.stderr ? err.stderr.toString().trim() : err.message}`);
684
728
  }
@@ -711,7 +755,7 @@ async function processArchive(zipPath) {
711
755
  throw new Error('manifest.json must contain uuid and emojicode');
712
756
  }
713
757
 
714
- const tenant = getTenantByIdentifier(manifest.uuid);
758
+ let tenant = getTenantByIdentifier(manifest.uuid);
715
759
  if (!tenant) throw new Error(`Unknown UUID: ${manifest.uuid}`);
716
760
  if (tenant.emojicode !== manifest.emojicode) {
717
761
  throw new Error('emojicode does not match registered tenant');
@@ -735,6 +779,10 @@ async function processArchive(zipPath) {
735
779
  Object.assign(tenant, tenantUpdates);
736
780
  }
737
781
 
782
+ // Ensure the Sanora user exists before uploading any products.
783
+ // If Redis was wiped, this re-creates the user and updates tenant.uuid.
784
+ tenant = await sanoraEnsureUser(tenant);
785
+
738
786
  const results = { books: [], music: [], posts: [], albums: [], products: [], videos: [], appointments: [], subscriptions: [], warnings: [] };
739
787
 
740
788
  function readInfo(entryPath) {