diff --git a/httpd-client/index.ts b/httpd-client/index.ts
index f2328f436..7cf0e39aa 100644
--- a/httpd-client/index.ts
+++ b/httpd-client/index.ts
@@ -103,6 +103,7 @@ const nodeInfoSchema = object({
message: string(),
service: string(),
version: string(),
+ apiVersion: string(),
nid: string(),
path: string(),
links: array(
diff --git a/httpd-client/lib/fetcher.ts b/httpd-client/lib/fetcher.ts
index 7c8157065..82ef422d5 100644
--- a/httpd-client/lib/fetcher.ts
+++ b/httpd-client/lib/fetcher.ts
@@ -3,6 +3,9 @@
import type { ZodIssue, ZodType, TypeOf } from "zod";
+import { config } from "@app/lib/config";
+import { compare } from "compare-versions";
+
export interface BaseUrl {
hostname: string;
port: number;
@@ -40,16 +43,41 @@ export class ResponseError extends Error {
export class ResponseParseError extends Error {
public method: string;
public body: unknown;
+ public description: string;
+ public apiVersion: string | undefined;
public zodIssues: ZodIssue[];
public path?: string;
public constructor(
method: string,
body: unknown,
+ apiVersion: string | undefined,
zodIssues: ZodIssue[],
path?: string,
) {
super("Failed to parse response body");
+
+ let description: string;
+ if (
+ apiVersion === undefined ||
+ compare(apiVersion, config.nodes.apiVersion, "<")
+ ) {
+ description = `The node you are fetching from seems to be outdated, make sure the httpd API version is at least ${config.nodes.apiVersion} currently ${apiVersion ?? "unknown"}.`;
+ } else if (
+ config.nodes.apiVersion === undefined ||
+ compare(apiVersion, config.nodes.apiVersion, ">")
+ ) {
+ description = `The web client you are using is outdated, make sure it supports at least ${apiVersion} to interact with this node currently ${config.nodes.apiVersion ?? "unknown"}.`;
+ } else {
+ description =
+ "This is usually due to a version mismatch between the seed and the web interface.";
+ }
+ this.apiVersion = apiVersion;
+ this.description =
+ "The response received from the seed does not match the expected schema.
".concat(
+ description,
+ );
+
this.method = method;
this.path = path;
this.body = body;
@@ -93,8 +121,7 @@ export class Fetcher {
): Promise> {
const response = await this.fetch({
...params,
- // eslint-disable-next-line @typescript-eslint/naming-convention
- query: { ...params.query, bust_cache: "1" },
+ query: { ...params.query, v: config.nodes.apiVersion },
});
if (!response.ok) {
@@ -112,9 +139,12 @@ export class Fetcher {
if (result.success) {
return result.data;
} else {
+ const response = await this.fetch({ method: "GET" });
+ const info = await response.json();
throw new ResponseParseError(
params.method,
responseBody,
+ info.apiVersion,
result.error.errors,
params.path,
);
diff --git a/package-lock.json b/package-lock.json
index 21aab0072..f702e4987 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"baconjs": "^3.0.17",
"bs58": "^5.0.0",
"buffer": "^6.0.3",
+ "compare-versions": "^6.1.0",
"dompurify": "^3.0.11",
"hast-util-to-dom": "^4.0.0",
"hast-util-to-html": "^9.0.0",
@@ -1880,6 +1881,11 @@
"node": ">= 12"
}
},
+ "node_modules/compare-versions": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz",
+ "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg=="
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
diff --git a/package.json b/package.json
index 7fe2611bf..2bfd66f67 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"baconjs": "^3.0.17",
"bs58": "^5.0.0",
"buffer": "^6.0.3",
+ "compare-versions": "^6.1.0",
"dompurify": "^3.0.11",
"hast-util-to-dom": "^4.0.0",
"hast-util-to-html": "^9.0.0",
diff --git a/src/config.json b/src/config.json
index 046bb09fe..e03c1bec9 100644
--- a/src/config.json
+++ b/src/config.json
@@ -1,6 +1,7 @@
{
"nodes": {
"fallbackPublicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path",
+ "apiVersion": "0.1.0",
"defaultHttpdPort": 443,
"defaultLocalHttpdPort": 8080,
"defaultHttpdHostname": "seed.radicle.garden",
diff --git a/src/lib/config.ts b/src/lib/config.ts
index f86c5b0dd..b6564656e 100644
--- a/src/lib/config.ts
+++ b/src/lib/config.ts
@@ -4,6 +4,7 @@ import configJson from "@app/config.json";
export interface Config {
nodes: {
+ apiVersion: string;
fallbackPublicExplorer: string;
defaultHttpdPort: number;
defaultHttpdHostname: string;
@@ -21,6 +22,7 @@ function getConfig(): Config {
return {
nodes: {
fallbackPublicExplorer: "https://app.radicle.xyz/nodes/$host/$rid$path",
+ apiVersion: "0.1.0",
defaultHttpdHostname: "127.0.0.1",
defaultHttpdPort: 8081,
defaultLocalHttpdPort: 8081,
diff --git a/src/views/home/error.ts b/src/views/home/error.ts
index 5a6af1e2f..d14fd7899 100644
--- a/src/views/home/error.ts
+++ b/src/views/home/error.ts
@@ -12,8 +12,7 @@ export function handleError(
return {
error,
title: "Could not parse the request",
- description:
- "The response received from the seed does not match the expected schema, this is usually due to a version mismatch between the seed and the web interface.",
+ description: error.description,
};
} else if (error instanceof ResponseError) {
return {
diff --git a/src/views/nodes/error.ts b/src/views/nodes/error.ts
index 0489cb90b..81b6ce789 100644
--- a/src/views/nodes/error.ts
+++ b/src/views/nodes/error.ts
@@ -11,8 +11,7 @@ export function handleError(
params: {
error,
title: "Could not parse the request",
- description:
- "The response received from the seed does not match the expected schema, this is usually due to a version mismatch between the seed and the web interface.",
+ description: error.description,
},
};
} else if (error instanceof ResponseError) {
diff --git a/src/views/projects/error.ts b/src/views/projects/error.ts
index 95c1e81ea..fae5df875 100644
--- a/src/views/projects/error.ts
+++ b/src/views/projects/error.ts
@@ -43,8 +43,7 @@ export function handleError(
params: {
error,
title: "Could not parse the request",
- description:
- "The response received from the seed does not match the expected schema, this is usually due to a version mismatch between the seed and the web interface.",
+ description: error.description,
},
};
} else if (
diff --git a/tests/support/fixtures.ts b/tests/support/fixtures.ts
index bbd890e24..bf4d76441 100644
--- a/tests/support/fixtures.ts
+++ b/tests/support/fixtures.ts
@@ -77,6 +77,7 @@ export const test = base.extend<{
nodes: {
fallbackPublicExplorer:
"https://app.radicle.xyz/nodes/$host/$rid$path",
+ apiVersion: "0.1.0",
defaultHttpdPort: 8081,
defaultHttpdHostname: "127.0.0.1",
defaultLocalHttpdPort: 8081,
@@ -214,6 +215,7 @@ export function appConfigWithFixture(defaultLocalHttpdPort = 8081) {
window.APP_CONFIG = {
nodes: {
fallbackPublicExplorer: "https://app.radicle.xyz/nodes/$host/$rid$path",
+ apiVersion: "0.1.0",
defaultHttpdPort: 8081,
defaultHttpdHostname: "127.0.0.1",
defaultLocalHttpdPort,