From bca9e1fd19ff350f8bfe03ae98ad2f130ec6f144 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Sun, 27 Oct 2024 19:29:37 +0000 Subject: [PATCH 01/24] add dotenv --- .gitignore | 3 ++- package-lock.json | 13 +++++++++++++ package.json | 1 + src/index.ts | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c075b4a..4a01d8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules .dig dist -dig.config.json \ No newline at end of file +dig.config.json +.env diff --git a/package-lock.json b/package-lock.json index cff4197..4f3105d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "cheerio": "^1.0.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.19.2", "qs": "^6.13.0", "superagent": "^10.1.0" @@ -2535,6 +2536,18 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dotgitignore": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", diff --git a/package.json b/package.json index 9a2df03..acf2de8 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "cheerio": "^1.0.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.19.2", "qs": "^6.13.0", "superagent": "^10.1.0" diff --git a/src/index.ts b/src/index.ts index a177e6a..76cd618 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import 'dotenv/config' import { startContentServer } from "./server"; startContentServer(); \ No newline at end of file From 830f94bfd26b0a19945b28a7e2fcbe06d9de2b43 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Mon, 28 Oct 2024 23:38:08 +0000 Subject: [PATCH 02/24] first owrking version of urn format --- package-lock.json | 9 +++- package.json | 3 +- src/controllers/storeController.ts | 14 +++--- src/middleware/parseUdi.ts | 22 ++++++---- src/utils/udi.ts | 68 ++++++++++++++++++++++++++++++ src/views/indexView.ts | 11 ++--- src/views/peerRedirectView.ts | 2 +- 7 files changed, 107 insertions(+), 22 deletions(-) create mode 100644 src/utils/udi.ts diff --git a/package-lock.json b/package-lock.json index 4f3105d..d9049dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,8 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "qs": "^6.13.0", - "superagent": "^10.1.0" + "superagent": "^10.1.0", + "urns": "^0.6.0" }, "bin": { "dig": "dist/index.js" @@ -6039,6 +6040,12 @@ "node-int64": "^0.4.0" } }, + "node_modules/urns": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/urns/-/urns-0.6.0.tgz", + "integrity": "sha512-KqXGkRiq76KDvw+wHusJL0fSVltnF3Teqf1BK4f1xK3p1u1NAYYBQRsP89nw5CV/y+egjehITVPLh6upfqFdLg==", + "license": "MIT" + }, "node_modules/utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", diff --git a/package.json b/package.json index acf2de8..c90b396 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "qs": "^6.13.0", - "superagent": "^10.1.0" + "superagent": "^10.1.0", + "urns": "^0.6.0" }, "devDependencies": { "@types/cookie-parser": "^1.4.7", diff --git a/src/controllers/storeController.ts b/src/controllers/storeController.ts index 1a67190..cf78549 100644 --- a/src/controllers/storeController.ts +++ b/src/controllers/storeController.ts @@ -25,6 +25,7 @@ import { hexToUtf8 } from "../utils/hexUtils"; import { getStorageLocation } from "../utils/storage"; import NodeCache from "node-cache"; import { Readable } from "stream"; +import { Udi } from "../utils/udi"; const digFolderPath = getStorageLocation(); const chiaLispCache = new NodeCache({ stdTTL: 180 }); @@ -79,11 +80,11 @@ export const getStoresIndex = async (req: Request, res: Response) => { const rows = await Promise.all( storeList.map(async (storeId: string) => { const dataStore = DataStore.from(storeId); - const { latestStore: state} = await dataStore.fetchCoinInfo(); + const { latestStore: state } = await dataStore.fetchCoinInfo(); const formattedBytes = formatBytes(Number(state.metadata.bytes)); + const udi = new Udi(chainName, storeId); return renderIndexView( - chainName || "chia", - storeId, + udi, state, formattedBytes ); @@ -188,7 +189,7 @@ export const getKeysIndex = async (req: Request, res: Response) => { try { // Prepare the script tag to inject - const baseUrl = `${chainName}.${storeId}.${rootHash}`; + // const baseUrl = `${chainName}:${storeId}:${rootHash}`; // Read the stream and get the index.html content const indexContent = await streamToString(stream); @@ -217,7 +218,8 @@ export const getKeysIndex = async (req: Request, res: Response) => { const keys = datalayer.listKeys(rootHash); const links = keys.map((key: string) => { const utf8Key = hexToUtf8(key); - const link = `/${chainName}.${storeId}.${rootHash}/${utf8Key}`; + const udi = new Udi(chainName, storeId, rootHash, utf8Key); + const link = `/${udi.toUrn()}`; return { utf8Key, link }; }); @@ -352,7 +354,7 @@ export const getKey = async (req: Request, res: Response) => { res.setHeader("X-Store-Id", storeId); res.setHeader("X-Key-Exists", "true"); res.setHeader("Content-Type", "application/json"); - + return res.json({ clsp: clspCode, params: params, diff --git a/src/middleware/parseUdi.ts b/src/middleware/parseUdi.ts index 04044dc..76ea337 100644 --- a/src/middleware/parseUdi.ts +++ b/src/middleware/parseUdi.ts @@ -46,9 +46,6 @@ export const parseUdi = async ( // Apply removeDuplicatePathPart to the request path const modifiedPath = removeDuplicatePathPart(path); - // Re-append the query string if it exists - const modifiedUrl = queryString ? `${modifiedPath}?${queryString}` : modifiedPath; - const referrer = req.get("Referer") || ""; let cookieData = req.cookies.udiData || null; @@ -68,13 +65,22 @@ export const parseUdi = async ( : ""; // Split the pathSegment by periods to extract potential components - const parts = pathSegment.split("."); + const parts = pathSegment.split(":"); if (parts.length === 1 && parts[0].length !== 64) { appendPath = `/${parts[0]}${appendPath}`; } - if (parts.length === 3) { + if (parts.length === 5) { + chainName = parts[2]; + storeId = parts[3]; + rootHash = parts[4]; // rootHash provided in the URL + } + else if (parts.length === 4) { + chainName = parts[2]; + storeId = parts[3]; + } + else if (parts.length === 3) { chainName = parts[0]; storeId = parts[1]; rootHash = parts[2]; // rootHash provided in the URL @@ -108,7 +114,7 @@ export const parseUdi = async ( console.warn("Invalid storeId, redirecting to referrer:", referrer); return res.redirect( 302, - `/${cookieChainName}.${cookieStoreId}` + appendPath + `/urn:dig:${cookieChainName}:${cookieStoreId}` + appendPath ); } @@ -147,7 +153,7 @@ export const parseUdi = async ( const storeInfo = await dataStore.fetchCoinInfo(); rootHash = storeInfo.latestStore.metadata.rootHash.toString("hex"); - const redirect = `/chia.${storeId}.${rootHash}${appendPath}${queryString ? '?' + queryString : ''}`; + const redirect = `/urn:dig:chia:${storeId}:${rootHash}${appendPath}${queryString ? '?' + queryString : ''}`; console.log("Redirecting to:", redirect); return res.redirect(302, redirect); } @@ -155,7 +161,7 @@ export const parseUdi = async ( // If chainName is missing, assume "chia" if (!chainName) { console.log("ChainName omitted, defaulting to 'chia'."); - return res.redirect(302, `/chia.${pathSegment}${appendPath}${queryString ? '?' + queryString : ''}`); + return res.redirect(302, `/urn:dig:chia:${pathSegment}${appendPath}${queryString ? '?' + queryString : ''}`); } // Validate the chainName diff --git a/src/utils/udi.ts b/src/utils/udi.ts new file mode 100644 index 0000000..91ed6e4 --- /dev/null +++ b/src/utils/udi.ts @@ -0,0 +1,68 @@ +import * as urns from 'urns'; + +class Udi { + readonly chainName: string; + readonly storeId: string; + readonly rootHash: string | null; + readonly resourceKey: string | null; + static readonly nid: string = "dig"; + static readonly namespace: string = `urn:${Udi.nid}`; + + constructor(chainName: string, storeId: string, rootHash: string | null = null, resourceKey: string | null = null) { + this.chainName = chainName ?? "chia"; + this.storeId = storeId; + this.rootHash = rootHash; + this.resourceKey = resourceKey; + } + + fromRootHash(rootHash: string): Udi { + return new Udi(this.chainName, this.storeId, rootHash, this.resourceKey); + } + + static fromUrn(urn: string): Udi { + const parsedUrn = urns.parseURN(urn); + if (parsedUrn.nid !== Udi.nid) { + throw new Error(`Invalid namespace: ${parsedUrn.nid}`); + } + + const parts = parsedUrn.nss.split(':'); + if (parts.length < 2) { + throw new Error(`Invalid URN format: ${parsedUrn.nss}`); + } + const chainName = parts[0]; + const storeId = parts[1]; + let rootHash: string | null = null; + let resourceKey: string | null = null; + if (parts.length > 2) { + rootHash = parts[2]; + if (parts.length > 3) { + resourceKey = parts[3]; + } + } + return new Udi(chainName, storeId, rootHash, resourceKey); + } + + toUrn(): string { + let urn = `${Udi.namespace}:${this.chainName}:${this.storeId}`; + if (this.rootHash !== null) { + urn += `:${this.rootHash}`; + } + if (this.resourceKey !== null) { + urn += `/${this.resourceKey}`; + } + return urn; + } + + equals(other: Udi): boolean { + return this.storeId === other.storeId && + this.chainName === other.chainName && + this.rootHash === other.rootHash && + this.resourceKey === other.resourceKey; + } + + toString(): string { + return this.toUrn(); + } +} + +export { Udi }; \ No newline at end of file diff --git a/src/views/indexView.ts b/src/views/indexView.ts index adc5a49..df84c10 100644 --- a/src/views/indexView.ts +++ b/src/views/indexView.ts @@ -1,11 +1,12 @@ +import { Udi } from "../utils/udi"; + export const renderIndexView = ( - chainName: string, - storeId: string, + udi: Udi, state: any, formattedBytes: string ) => { const isSyncing = !state; // Check if state is null - const toastId = `toast-${storeId}`; // Unique ID for the toast + const toastId = `toast-${udi.storeId}`; // Unique ID for the toast return `
@@ -19,8 +20,8 @@ export const renderIndexView = ( ${isSyncing ? "Syncing data, please wait..." : (state.metadata.description || "No Description Available")}

- Store ID: ${storeId} - + Store ID: ${udi.storeId} + diff --git a/src/views/peerRedirectView.ts b/src/views/peerRedirectView.ts index f4f74dc..66d8814 100644 --- a/src/views/peerRedirectView.ts +++ b/src/views/peerRedirectView.ts @@ -98,7 +98,7 @@ export const renderStoreNotFoundView = (

Store Not Found on This Peer

Click the button below to redirect to another peer.

- + Redirect
From 5917ded2a546b82c85dda0a0c5db2096a26fe726 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Tue, 29 Oct 2024 01:10:26 +0000 Subject: [PATCH 03/24] use the Udi class to reconstitute the resource urn --- src/controllers/storeController.ts | 6 ++---- src/views/peerRedirectView.ts | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/controllers/storeController.ts b/src/controllers/storeController.ts index cf78549..c2fe30c 100644 --- a/src/controllers/storeController.ts +++ b/src/controllers/storeController.ts @@ -113,14 +113,12 @@ export const getKeysIndex = async (req: Request, res: Response) => { storeId, rootHash ); - + const udi = new Udi(chainName, storeId, rootHash); return res .status(400) .send( renderStoreNotFoundView( - storeId, - rootHash, - chainName, + udi, peerRedirect?.IpAddress ) ); diff --git a/src/views/peerRedirectView.ts b/src/views/peerRedirectView.ts index 66d8814..ba76977 100644 --- a/src/views/peerRedirectView.ts +++ b/src/views/peerRedirectView.ts @@ -1,7 +1,7 @@ +import { Udi } from "../utils/udi"; + export const renderStoreNotFoundView = ( - storeId: string, - rootHash: string, - chainName: string, + udi: Udi, peerIp?: string | null ) => { // If no peers are available, show "Store not found on this network" @@ -98,7 +98,7 @@ export const renderStoreNotFoundView = (

Store Not Found on This Peer

Click the button below to redirect to another peer.

- + Redirect
From 6c9218fbbb9417478400e983027d492a045d8737 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Tue, 29 Oct 2024 03:24:41 +0000 Subject: [PATCH 04/24] expand the redirect to referrer to support cases where the referrer url has a file component --- src/middleware/parseUdi.ts | 65 ++++++++++---------------------------- 1 file changed, 17 insertions(+), 48 deletions(-) diff --git a/src/middleware/parseUdi.ts b/src/middleware/parseUdi.ts index 76ea337..2eb3cb3 100644 --- a/src/middleware/parseUdi.ts +++ b/src/middleware/parseUdi.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { renderUnknownChainView } from "../views"; import { DataStore } from "@dignetwork/dig-sdk"; -import { app } from "../app"; +import { Udi } from "../utils/udi"; const validChainNames = ["chia"]; // List of valid chain names @@ -47,7 +47,6 @@ export const parseUdi = async ( const modifiedPath = removeDuplicatePathPart(path); const referrer = req.get("Referer") || ""; - let cookieData = req.cookies.udiData || null; let chainName: string | null = null; let storeId: string = ""; @@ -108,41 +107,18 @@ export const parseUdi = async ( // Validate storeId length if (!storeId || storeId.length !== 64) { - if (cookieData) { - const { chainName: cookieChainName, storeId: cookieStoreId } = cookieData; - - console.warn("Invalid storeId, redirecting to referrer:", referrer); - return res.redirect( - 302, - `/urn:dig:${cookieChainName}:${cookieStoreId}` + appendPath - ); - } - if (referrer) { - console.warn("Invalid storeId, redirecting to referrer:", referrer); - return res.redirect(302, referrer + appendPath); + // the referred might be the storeId or it might be a resource within the storeId + // like another script - in the code below we parse that and redirect to the storeId + const url = new URL(referrer); + const pathParts = url.pathname.split('/').filter(part => part.length > 0); + const store = pathParts.length > 0 ? `/${pathParts[0]}` : ""; + + console.warn(`Invalid storeId [${storeId}], redirecting to referrer:`, `${url.origin}${store}`); + return res.redirect(302, `${url.origin}${store}${appendPath}`); } - return res.status(400).send("Invalid or missing storeId."); - } - // Fallback to cookie only if storeId matches the cookie's storeId - if (!chainName || !rootHash) { - if (cookieData) { - const { - chainName: cookieChainName, - storeId: cookieStoreId, - rootHash: cookieRootHash, - } = cookieData; - - // Only use cookie data if the storeId matches - if (!storeId || cookieStoreId === storeId || cookieRootHash === rootHash) { - console.log("Using cookie data as storeId matches:", storeId); - chainName = chainName || cookieChainName; - rootHash = rootHash || cookieRootHash; - } else { - console.log("StoreId changed, ignoring cookie data."); - } - } + return res.status(400).send("Invalid or missing storeId."); } const dataStore = DataStore.from(storeId); @@ -153,7 +129,8 @@ export const parseUdi = async ( const storeInfo = await dataStore.fetchCoinInfo(); rootHash = storeInfo.latestStore.metadata.rootHash.toString("hex"); - const redirect = `/urn:dig:chia:${storeId}:${rootHash}${appendPath}${queryString ? '?' + queryString : ''}`; + const udi = new Udi("chia", storeId, rootHash, `${appendPath}${queryString ? '?' + queryString : ''}`); + const redirect = `/${udi.toUrn()}`; console.log("Redirecting to:", redirect); return res.redirect(302, redirect); } @@ -161,7 +138,10 @@ export const parseUdi = async ( // If chainName is missing, assume "chia" if (!chainName) { console.log("ChainName omitted, defaulting to 'chia'."); - return res.redirect(302, `/urn:dig:chia:${pathSegment}${appendPath}${queryString ? '?' + queryString : ''}`); + const udi = new Udi("chia", pathSegment, null, `${appendPath}${queryString ? '?' + queryString : ''}`); + const redirect = `/${udi.toUrn()}`; + console.log("Redirecting to:", redirect); + return res.redirect(302, redirect); } // Validate the chainName @@ -183,18 +163,7 @@ export const parseUdi = async ( // @ts-ignore req.storeId = storeId; // @ts-ignore - req.rootHash = rootHash; - - res.cookie( - "udiData", - { chainName, storeId, rootHash }, - { - httpOnly: true, - secure: false, - maxAge: 1 * 60 * 1000, // Cookie expires after 5 minutes - expires: new Date(Date.now() + 1 * 60 * 1000), - } - ); + req.rootHash = rootHash next(); } catch (error) { From 2c2240e2b766dc6b6f3e1d0d1e21e72cd107a87a Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Tue, 29 Oct 2024 03:44:32 +0000 Subject: [PATCH 05/24] only log in removeDuplicatePathPart if the input and output differ --- src/middleware/parseUdi.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/middleware/parseUdi.ts b/src/middleware/parseUdi.ts index 2eb3cb3..c2cafb5 100644 --- a/src/middleware/parseUdi.ts +++ b/src/middleware/parseUdi.ts @@ -22,8 +22,10 @@ function removeDuplicatePathPart(path: string): string { } const modifiedPath = '/' + parts.join('/'); - console.log('Original path:', path); - console.log('Modified path:', modifiedPath); + if (path !== modifiedPath) { + console.log('Original path:', path); + console.log('Modified path:', modifiedPath); + } // Reconstruct the path with a leading slash return modifiedPath; From db9df308a85706aa71dfd24a6ee5666d5c85a84d Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Tue, 29 Oct 2024 22:38:41 +0000 Subject: [PATCH 06/24] move urn parsing into Udi class --- src/middleware/parseUdi.ts | 194 ++++++++++++++----------------------- src/utils/udi.ts | 28 ++++-- 2 files changed, 96 insertions(+), 126 deletions(-) diff --git a/src/middleware/parseUdi.ts b/src/middleware/parseUdi.ts index c2cafb5..e14d70a 100644 --- a/src/middleware/parseUdi.ts +++ b/src/middleware/parseUdi.ts @@ -5,30 +5,38 @@ import { Udi } from "../utils/udi"; const validChainNames = ["chia"]; // List of valid chain names -function removeDuplicatePathPart(path: string): string { - // Split the path into segments, ignoring leading/trailing slashes - const parts = path.split('/').filter(part => part.length > 0); - - // Check if the path has at least two segments - if (parts.length >= 2) { - const firstPart = parts[0]; - const secondPart = parts[1]; - - // Check if the first two parts are identical and at least 64 characters long - if (firstPart === secondPart && firstPart.length >= 64) { - // Remove the duplicate second part - parts.splice(1, 1); +// Extracts the UDI from the request URL or referrer +function getUdi(requestUrl: string, referrer: string): [boolean, Udi | null] { + // Check if the request URL contains a valid UDI + if (requestUrl) { + try { + return [false, Udi.fromUrn(requestUrl.replace(/^\/+/, ''))]; + } catch (error) { } } - const modifiedPath = '/' + parts.join('/'); - if (path !== modifiedPath) { - console.log('Original path:', path); - console.log('Modified path:', modifiedPath); + // Check if the referrer URL contains a valid UDI + if (referrer) { + try { + const url = new URL(referrer); + if (url.pathname) { + let udi = Udi.fromUrn(url.pathname.replace(/^\/+/, '')); + + // now we have gotten the udi from the referrer, but we might have a path part in the request url + // so use it to update the udi. The referrer udi might have a reosurce key which is where the request orginated from + // like a script importing another script, NOT the resource being requested + if (requestUrl) { + udi = udi.fromResourceKey(requestUrl.replace(/^\/+/, '')); + } + // return true because calling code needs to redirect + return [true, udi]; + } + } catch (error) { + } } - // Reconstruct the path with a leading slash - return modifiedPath; + console.log('No UDI found in path or referrer: path:', requestUrl, 'referrer:', referrer); + return [false, null]; } export const parseUdi = async ( @@ -42,116 +50,36 @@ export const parseUdi = async ( return next(); } - // Extract the path and query string separately + // strip off the query string as that is not part of the udi const [path, queryString] = req.originalUrl.split("?"); - - // Apply removeDuplicatePathPart to the request path - const modifiedPath = removeDuplicatePathPart(path); - const referrer = req.get("Referer") || ""; - let chainName: string | null = null; - let storeId: string = ""; - let rootHash: string | null = null; - - // Use modifiedPath instead of req.originalUrl - const pathSegments = modifiedPath.split("/").filter(segment => segment.length > 0); - - // Extract the first path part as the storeId (assumed app identifier) - const pathSegment = pathSegments[0] || ""; // Expecting storeId to be the first path segment - const originalPathSegments = pathSegments.slice(1); // Remove the first segment, which is the storeId part - let appendPath = - originalPathSegments.length > 0 - ? `/${originalPathSegments.join("/")}` - : ""; - - // Split the pathSegment by periods to extract potential components - const parts = pathSegment.split(":"); - - if (parts.length === 1 && parts[0].length !== 64) { - appendPath = `/${parts[0]}${appendPath}`; + const [redirect, udi] = getUdi(removeDuplicatePathPart(path), referrer); + if (!udi) { + return res.status(400).send("Invalid or missing Udi."); } - if (parts.length === 5) { - chainName = parts[2]; - storeId = parts[3]; - rootHash = parts[4]; // rootHash provided in the URL - } - else if (parts.length === 4) { - chainName = parts[2]; - storeId = parts[3]; - } - else if (parts.length === 3) { - chainName = parts[0]; - storeId = parts[1]; - rootHash = parts[2]; // rootHash provided in the URL - } else if (parts.length === 2) { - if (parts[0].length === 64) { - storeId = parts[0]; - rootHash = parts[1]; // rootHash provided in the URL - } else { - chainName = parts[0]; - storeId = parts[1]; - } - } else if (parts.length === 1) { - storeId = parts[0]; + // Validate the chainName + if (!validChainNames.includes(udi.chainName)) { + console.warn("Invalid chain name:", udi.chainName); + return res.status(400).send(renderUnknownChainView(udi.storeId, udi.chainName)); } // Log extracted values - console.log( - "Extracted values - Chain Name:", - chainName, - "Store ID:", - storeId, - "Root Hash:", - rootHash - ); - - // Validate storeId length - if (!storeId || storeId.length !== 64) { - if (referrer) { - // the referred might be the storeId or it might be a resource within the storeId - // like another script - in the code below we parse that and redirect to the storeId - const url = new URL(referrer); - const pathParts = url.pathname.split('/').filter(part => part.length > 0); - const store = pathParts.length > 0 ? `/${pathParts[0]}` : ""; - - console.warn(`Invalid storeId [${storeId}], redirecting to referrer:`, `${url.origin}${store}`); - return res.redirect(302, `${url.origin}${store}${appendPath}`); - } - - return res.status(400).send("Invalid or missing storeId."); - } - - const dataStore = DataStore.from(storeId); - - // Early exit: If both chainName and rootHash are missing, fetch rootHash and redirect with both added - if (!chainName && !rootHash) { - console.log("Both chainName and rootHash omitted, fetching rootHash..."); - const storeInfo = await dataStore.fetchCoinInfo(); - rootHash = storeInfo.latestStore.metadata.rootHash.toString("hex"); - - const udi = new Udi("chia", storeId, rootHash, `${appendPath}${queryString ? '?' + queryString : ''}`); - const redirect = `/${udi.toUrn()}`; - console.log("Redirecting to:", redirect); - return res.redirect(302, redirect); + console.log(`Extracted urn:`, udi.toUrn()); + + if (redirect) { + // the referred might be the storeId or it might be a resource within the storeId + // like another script - in the code below we parse that and redirect to the storeId + const url = new URL(referrer); + const redirectUrl = `${url.origin}/${udi.toUrn()}` + console.warn(`Redirecting to referrer:`, redirectUrl); + return res.redirect(302, redirectUrl); } - // If chainName is missing, assume "chia" - if (!chainName) { - console.log("ChainName omitted, defaulting to 'chia'."); - const udi = new Udi("chia", pathSegment, null, `${appendPath}${queryString ? '?' + queryString : ''}`); - const redirect = `/${udi.toUrn()}`; - console.log("Redirecting to:", redirect); - return res.redirect(302, redirect); - } - - // Validate the chainName - if (!validChainNames.includes(chainName)) { - console.warn("Invalid chain name:", chainName); - return res.status(400).send(renderUnknownChainView(storeId, chainName)); - } + const dataStore = DataStore.from(udi.storeId); + let rootHash: string | null = udi.rootHash; // If rootHash is missing, fetch the latest one if (!rootHash) { console.log("RootHash omitted, fetching the latest rootHash..."); @@ -161,9 +89,9 @@ export const parseUdi = async ( // Attach extracted components to the request object // @ts-ignore - req.chainName = chainName; + req.chainName = udi.chainName; // @ts-ignore - req.storeId = storeId; + req.storeId = udi.storeId; // @ts-ignore req.rootHash = rootHash @@ -173,3 +101,29 @@ export const parseUdi = async ( res.status(500).send("An error occurred while verifying the identifier."); } }; + +function removeDuplicatePathPart(path: string): string { + // Split the path into segments, ignoring leading/trailing slashes + const parts = path.split('/').filter(part => part.length > 0); + + // Check if the path has at least two segments + if (parts.length >= 2) { + const firstPart = parts[0]; + const secondPart = parts[1]; + + // Check if the first two parts are identical and at least 64 characters long + if (firstPart === secondPart && firstPart.length >= 64) { + // Remove the duplicate second part + parts.splice(1, 1); + } + } + + const modifiedPath = '/' + parts.join('/'); + if (path !== modifiedPath) { + console.log('Original path:', path); + console.log('Modified path:', modifiedPath); + } + + // Reconstruct the path with a leading slash + return modifiedPath; +} \ No newline at end of file diff --git a/src/utils/udi.ts b/src/utils/udi.ts index 91ed6e4..dfe8f06 100644 --- a/src/utils/udi.ts +++ b/src/utils/udi.ts @@ -19,6 +19,10 @@ class Udi { return new Udi(this.chainName, this.storeId, rootHash, this.resourceKey); } + fromResourceKey(resourceKey: string | null): Udi { + return new Udi(this.chainName, this.storeId, this.rootHash, resourceKey); + } + static fromUrn(urn: string): Udi { const parsedUrn = urns.parseURN(urn); if (parsedUrn.nid !== Udi.nid) { @@ -26,19 +30,29 @@ class Udi { } const parts = parsedUrn.nss.split(':'); + // at a minimum we need chain name and store id if (parts.length < 2) { throw new Error(`Invalid URN format: ${parsedUrn.nss}`); } + + // this is what a nss looks like + //"chia:store id:optional_roothash/optional path/resource key" const chainName = parts[0]; - const storeId = parts[1]; + const storeId = parts[1].split('/')[0]; // need to strip off the optional path component + + // root hash will always be the part after the second : let rootHash: string | null = null; - let resourceKey: string | null = null; if (parts.length > 2) { - rootHash = parts[2]; - if (parts.length > 3) { - resourceKey = parts[3]; - } + rootHash = parts[2].split('/')[0]; // need to strip off the optional path component } + + // now see if we have a path component which will always follow the first / + const pathParts = parsedUrn.nss.split('/'); + let resourceKey: string | null = null; + if (pathParts.length > 1) { + resourceKey = pathParts.slice(1).join('/'); + } + return new Udi(chainName, storeId, rootHash, resourceKey); } @@ -47,9 +61,11 @@ class Udi { if (this.rootHash !== null) { urn += `:${this.rootHash}`; } + if (this.resourceKey !== null) { urn += `/${this.resourceKey}`; } + return urn; } From 3a0776b37ceedf3438a82419184b5f4534a43fa0 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Wed, 30 Oct 2024 00:29:53 +0000 Subject: [PATCH 07/24] make namesapce compare case insensitive per spec --- src/utils/udi.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/utils/udi.ts b/src/utils/udi.ts index dfe8f06..29f14bd 100644 --- a/src/utils/udi.ts +++ b/src/utils/udi.ts @@ -1,5 +1,21 @@ import * as urns from 'urns'; +// +// This class encapsulates the concept of a Universal Data Identifier (UDI) which is a +// standardized way to identify resources across the distributed DIG mesh netowrk. +// The UDI is a URN (Uniform Resource Name) that is used to identify resources +// in the DIG network. The UDI is composed of the following parts: +// - Chain Name: The name of the blockchain network where the resource is stored. +// - Store ID: The unique identifier of the store where the resource is stored. +// - Root Hash: The root hash of the resource in the store. +// - Resource Key: The key of the resource in the store. +// The UDI is formatted as follows: +// urn:dig:chainName:storeId:rootHash/resourceKey +// The chainName and storeId are required, while the rootHash and resourceKey are optional. +// The UDI can be used to uniquely identify resources across the DIG network. +// The UDI can be converted to a URN string and vice versa. +// https://github.com/DIG-Network/DIPS/blob/c6792331acf3c185ca87a8f4f847561d2b47fb31/DIPs/dip-0001.md +// class Udi { readonly chainName: string; readonly storeId: string; @@ -25,7 +41,7 @@ class Udi { static fromUrn(urn: string): Udi { const parsedUrn = urns.parseURN(urn); - if (parsedUrn.nid !== Udi.nid) { + if (parsedUrn.nid.toLowerCase() !== Udi.nid) { throw new Error(`Invalid namespace: ${parsedUrn.nid}`); } From ef1f6e3939563bbf9effbd114455bc7587c853b1 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Wed, 30 Oct 2024 17:46:00 +0000 Subject: [PATCH 08/24] incroporate SDK version of UDI class --- package-lock.json | 129 +++++++++++++++++++++++++++-- package.json | 2 +- src/controllers/storeController.ts | 4 +- src/middleware/parseUdi.ts | 15 ++-- src/utils/udi.ts | 100 ---------------------- src/views/indexView.ts | 10 +-- src/views/peerRedirectView.ts | 2 +- src/views/unknownChainView.ts | 22 ++--- 8 files changed, 148 insertions(+), 136 deletions(-) delete mode 100644 src/utils/udi.ts diff --git a/package-lock.json b/package-lock.json index d9049dc..d30b7cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "dependencies": { "@dignetwork/datalayer-driver": "^0.1.28", - "@dignetwork/dig-sdk": "^0.0.1-alpha.169", + "@dignetwork/dig-sdk": "^0.0.1-alpha.177", "add": "^2.0.6", "cheerio": "^1.0.0", "cookie-parser": "^1.4.6", @@ -247,13 +247,15 @@ } }, "node_modules/@dignetwork/dig-sdk": { - "version": "0.0.1-alpha.169", - "resolved": "https://registry.npmjs.org/@dignetwork/dig-sdk/-/dig-sdk-0.0.1-alpha.169.tgz", - "integrity": "sha512-lDrsTbNuDz1KXRkAmOy6D07S4pUpoqZPBdeYf94pKIOuFyOd79HgI2n+afpIJTdx6PjZbyKWCKpIQXDNQ258Fw==", + "version": "0.0.1-alpha.177", + "resolved": "https://registry.npmjs.org/@dignetwork/dig-sdk/-/dig-sdk-0.0.1-alpha.177.tgz", + "integrity": "sha512-SAiMmFF0mTmTnI6hv2nt/pETB63/nd50e6da/guJbOOsEMy7KvBRjjxrcOmLcH67NK97SAfVtWdEPuOrRjBwzA==", + "license": "ISC", "dependencies": { "@dignetwork/datalayer-driver": "^0.1.29", "@dignetwork/dig-sdk": "^0.0.1-alpha.158", "archiver": "^7.0.1", + "async-mutex": "^0.5.0", "axios": "^1.7.7", "bip39": "^3.1.0", "bottleneck": "^2.19.5", @@ -266,8 +268,9 @@ "crypto-js": "^4.2.0", "figures": "^6.1.0", "fs-extra": "^11.2.0", + "hi-base32": "^0.5.1", "ignore": "^5.3.2", - "inquirer": "^10.1.8", + "inquirer": "^10.2.2", "lodash": "^4.17.21", "merkletreejs": "^0.4.0", "nanospinner": "^1.1.0", @@ -276,8 +279,10 @@ "node-stun": "^0.1.2", "progress-stream": "^2.0.0", "proper-lockfile": "^4.1.2", + "redis": "^4.7.0", "superagent": "^10.0.0", - "unzipper": "^0.12.3" + "unzipper": "^0.12.3", + "urns": "^0.6.0" } }, "node_modules/@ethereumjs/rlp": { @@ -673,6 +678,65 @@ "node": ">=14" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", + "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@scure/base": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", @@ -1114,6 +1178,15 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1712,6 +1785,15 @@ "node": ">=0.8" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3097,6 +3179,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3413,6 +3504,12 @@ "node": ">=8" } }, + "node_modules/hi-base32": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", + "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==", + "license": "MIT" + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -5133,6 +5230,23 @@ "node": ">=8" } }, + "node_modules/redis": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", + "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.0", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6216,8 +6330,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "16.2.0", diff --git a/package.json b/package.json index c90b396..cb2d573 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ ], "dependencies": { "@dignetwork/datalayer-driver": "^0.1.28", - "@dignetwork/dig-sdk": "^0.0.1-alpha.169", + "@dignetwork/dig-sdk": "^0.0.1-alpha.177", "add": "^2.0.6", "cheerio": "^1.0.0", "cookie-parser": "^1.4.6", diff --git a/src/controllers/storeController.ts b/src/controllers/storeController.ts index c2fe30c..aec0a70 100644 --- a/src/controllers/storeController.ts +++ b/src/controllers/storeController.ts @@ -9,6 +9,7 @@ import { DataStore, DigChallenge, DigNetwork, + Udi } from "@dignetwork/dig-sdk"; import { formatBytes } from "../utils/formatBytes"; import { @@ -25,7 +26,6 @@ import { hexToUtf8 } from "../utils/hexUtils"; import { getStorageLocation } from "../utils/storage"; import NodeCache from "node-cache"; import { Readable } from "stream"; -import { Udi } from "../utils/udi"; const digFolderPath = getStorageLocation(); const chiaLispCache = new NodeCache({ stdTTL: 180 }); @@ -84,7 +84,7 @@ export const getStoresIndex = async (req: Request, res: Response) => { const formattedBytes = formatBytes(Number(state.metadata.bytes)); const udi = new Udi(chainName, storeId); return renderIndexView( - udi, + udi.toUrn(), state, formattedBytes ); diff --git a/src/middleware/parseUdi.ts b/src/middleware/parseUdi.ts index e14d70a..acfc4da 100644 --- a/src/middleware/parseUdi.ts +++ b/src/middleware/parseUdi.ts @@ -1,7 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { renderUnknownChainView } from "../views"; -import { DataStore } from "@dignetwork/dig-sdk"; -import { Udi } from "../utils/udi"; +import { DataStore, Udi } from "@dignetwork/dig-sdk"; const validChainNames = ["chia"]; // List of valid chain names @@ -26,7 +25,7 @@ function getUdi(requestUrl: string, referrer: string): [boolean, Udi | null] { // so use it to update the udi. The referrer udi might have a reosurce key which is where the request orginated from // like a script importing another script, NOT the resource being requested if (requestUrl) { - udi = udi.fromResourceKey(requestUrl.replace(/^\/+/, '')); + udi = udi.withResourceKey(requestUrl.replace(/^\/+/, '')); } // return true because calling code needs to redirect return [true, udi]; @@ -62,7 +61,7 @@ export const parseUdi = async ( // Validate the chainName if (!validChainNames.includes(udi.chainName)) { console.warn("Invalid chain name:", udi.chainName); - return res.status(400).send(renderUnknownChainView(udi.storeId, udi.chainName)); + return res.status(400).send(renderUnknownChainView(udi)); } // Log extracted values @@ -79,21 +78,21 @@ export const parseUdi = async ( const dataStore = DataStore.from(udi.storeId); - let rootHash: string | null = udi.rootHash; + let rootHash: Buffer | null = udi.rootHash; // If rootHash is missing, fetch the latest one if (!rootHash) { console.log("RootHash omitted, fetching the latest rootHash..."); const storeInfo = await dataStore.fetchCoinInfo(); - rootHash = storeInfo.latestStore.metadata.rootHash.toString("hex"); + rootHash = storeInfo.latestStore.metadata.rootHash; } // Attach extracted components to the request object // @ts-ignore req.chainName = udi.chainName; // @ts-ignore - req.storeId = udi.storeId; + req.storeId = udi.storeId.toString('hex'); // @ts-ignore - req.rootHash = rootHash + req.rootHash = rootHash ? rootHash.toString('hex') : null; next(); } catch (error) { diff --git a/src/utils/udi.ts b/src/utils/udi.ts deleted file mode 100644 index 29f14bd..0000000 --- a/src/utils/udi.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as urns from 'urns'; - -// -// This class encapsulates the concept of a Universal Data Identifier (UDI) which is a -// standardized way to identify resources across the distributed DIG mesh netowrk. -// The UDI is a URN (Uniform Resource Name) that is used to identify resources -// in the DIG network. The UDI is composed of the following parts: -// - Chain Name: The name of the blockchain network where the resource is stored. -// - Store ID: The unique identifier of the store where the resource is stored. -// - Root Hash: The root hash of the resource in the store. -// - Resource Key: The key of the resource in the store. -// The UDI is formatted as follows: -// urn:dig:chainName:storeId:rootHash/resourceKey -// The chainName and storeId are required, while the rootHash and resourceKey are optional. -// The UDI can be used to uniquely identify resources across the DIG network. -// The UDI can be converted to a URN string and vice versa. -// https://github.com/DIG-Network/DIPS/blob/c6792331acf3c185ca87a8f4f847561d2b47fb31/DIPs/dip-0001.md -// -class Udi { - readonly chainName: string; - readonly storeId: string; - readonly rootHash: string | null; - readonly resourceKey: string | null; - static readonly nid: string = "dig"; - static readonly namespace: string = `urn:${Udi.nid}`; - - constructor(chainName: string, storeId: string, rootHash: string | null = null, resourceKey: string | null = null) { - this.chainName = chainName ?? "chia"; - this.storeId = storeId; - this.rootHash = rootHash; - this.resourceKey = resourceKey; - } - - fromRootHash(rootHash: string): Udi { - return new Udi(this.chainName, this.storeId, rootHash, this.resourceKey); - } - - fromResourceKey(resourceKey: string | null): Udi { - return new Udi(this.chainName, this.storeId, this.rootHash, resourceKey); - } - - static fromUrn(urn: string): Udi { - const parsedUrn = urns.parseURN(urn); - if (parsedUrn.nid.toLowerCase() !== Udi.nid) { - throw new Error(`Invalid namespace: ${parsedUrn.nid}`); - } - - const parts = parsedUrn.nss.split(':'); - // at a minimum we need chain name and store id - if (parts.length < 2) { - throw new Error(`Invalid URN format: ${parsedUrn.nss}`); - } - - // this is what a nss looks like - //"chia:store id:optional_roothash/optional path/resource key" - const chainName = parts[0]; - const storeId = parts[1].split('/')[0]; // need to strip off the optional path component - - // root hash will always be the part after the second : - let rootHash: string | null = null; - if (parts.length > 2) { - rootHash = parts[2].split('/')[0]; // need to strip off the optional path component - } - - // now see if we have a path component which will always follow the first / - const pathParts = parsedUrn.nss.split('/'); - let resourceKey: string | null = null; - if (pathParts.length > 1) { - resourceKey = pathParts.slice(1).join('/'); - } - - return new Udi(chainName, storeId, rootHash, resourceKey); - } - - toUrn(): string { - let urn = `${Udi.namespace}:${this.chainName}:${this.storeId}`; - if (this.rootHash !== null) { - urn += `:${this.rootHash}`; - } - - if (this.resourceKey !== null) { - urn += `/${this.resourceKey}`; - } - - return urn; - } - - equals(other: Udi): boolean { - return this.storeId === other.storeId && - this.chainName === other.chainName && - this.rootHash === other.rootHash && - this.resourceKey === other.resourceKey; - } - - toString(): string { - return this.toUrn(); - } -} - -export { Udi }; \ No newline at end of file diff --git a/src/views/indexView.ts b/src/views/indexView.ts index df84c10..9178143 100644 --- a/src/views/indexView.ts +++ b/src/views/indexView.ts @@ -1,12 +1,12 @@ -import { Udi } from "../utils/udi"; +import { Udi } from "@dignetwork/dig-sdk"; export const renderIndexView = ( - udi: Udi, + urn: string, state: any, formattedBytes: string ) => { const isSyncing = !state; // Check if state is null - const toastId = `toast-${udi.storeId}`; // Unique ID for the toast + const toastId = `toast-${urn}`; // Unique ID for the toast return `
@@ -20,8 +20,8 @@ export const renderIndexView = ( ${isSyncing ? "Syncing data, please wait..." : (state.metadata.description || "No Description Available")}

- Store ID: ${udi.storeId} - + Store ID: ${urn} + diff --git a/src/views/peerRedirectView.ts b/src/views/peerRedirectView.ts index ba76977..373132e 100644 --- a/src/views/peerRedirectView.ts +++ b/src/views/peerRedirectView.ts @@ -1,4 +1,4 @@ -import { Udi } from "../utils/udi"; +import { Udi } from "@dignetwork/dig-sdk"; export const renderStoreNotFoundView = ( udi: Udi, diff --git a/src/views/unknownChainView.ts b/src/views/unknownChainView.ts index 3021280..5ae6444 100644 --- a/src/views/unknownChainView.ts +++ b/src/views/unknownChainView.ts @@ -1,14 +1,15 @@ +import { Udi } from "@dignetwork/dig-sdk"; + export const renderUnknownChainView = ( - storeId: string, - chainName: string - ) => { - return ` + udi: Udi, +) => { + return `

Unknown Chain

-

The chain ${chainName} is not recognized.

-

Store ID: ${storeId}

+

The chain ${udi.chainName} is not recognized.

+

Store ID: ${udi.toUrn()}

Chain Graphic @@ -17,13 +18,12 @@ export const renderUnknownChainView = (
`; - }; - - // Function to return a simple SVG chain graphic - const chainSvg = () => ` +}; + +// Function to return a simple SVG chain graphic +const chainSvg = () => ` `; - \ No newline at end of file From ea43a535be2ff0c0dcd293a61b69719b5632e4c6 Mon Sep 17 00:00:00 2001 From: Don Kackman Date: Wed, 30 Oct 2024 17:52:09 +0000 Subject: [PATCH 09/24] change Store Id labels --- src/views/indexView.ts | 4 ++-- src/views/unknownChainView.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/indexView.ts b/src/views/indexView.ts index 9178143..8d2dbd5 100644 --- a/src/views/indexView.ts +++ b/src/views/indexView.ts @@ -20,7 +20,7 @@ export const renderIndexView = ( ${isSyncing ? "Syncing data, please wait..." : (state.metadata.description || "No Description Available")}

- Store ID: ${urn} + Store: ${urn} @@ -39,7 +39,7 @@ export const renderIndexView = (