Skip to content

Commit 5be6ce0

Browse files
author
Pablo Mendez
committed
use kubo library
1 parent 596cda1 commit 5be6ce0

9 files changed

Lines changed: 532 additions & 111 deletions

File tree

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@
5151
"image-size": "^0.8.1",
5252
"inquirer": "^6.2.1",
5353
"js-yaml": "^4.1.0",
54+
"kubo-rpc-client": "^5.2.0",
5455
"listr": "^0.14.3",
5556
"lodash-es": "^4.17.21",
5657
"mime-types": "^2.1.24",
5758
"moment": "^2.27.0",
59+
"multiformats": "^13.3.7",
5860
"prettier": "^2.1.2",
5961
"request": "^2.88.2",
6062
"rimraf": "^3.0.2",
@@ -93,5 +95,6 @@
9395
},
9496
"engines": {
9597
"node": ">=20.0.0"
96-
}
98+
},
99+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
97100
}

src/commands/from_github.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import {
1212
GithubRelease
1313
} from "../utils/githubGetReleases.js";
1414
import { ipfsAddDirFromUrls } from "../releaseUploader/ipfsNode/addDirFromUrls.js";
15-
import { verifyIpfsConnection } from "../releaseUploader/ipfsNode/verifyConnection.js";
1615
import { CliGlobalOptions } from "../types.js";
1716
import { Manifest, defaultArch, releaseFiles } from "@dappnode/types";
1817
import { getLegacyImagePath } from "../utils/getLegacyImagePath.js";
1918
import { getImageFileName } from "../utils/getImageFileName.js";
2019
import { contentHashFileName, releaseFilesDefaultNames } from "../params.js";
20+
import { ReleaseUploaderIpfsNode } from "../releaseUploader/ipfsNode/index.js";
2121

2222
interface CliCommandOptions extends CliGlobalOptions {
2323
repoSlug: string;
@@ -69,12 +69,11 @@ export async function fromGithubHandler({
6969
latest,
7070
version
7171
}: CliCommandOptions): Promise<{ releaseMultiHash: string }> {
72-
// Parse options
73-
const ipfsProvider = provider;
7472
// Assume incomplete repo slugs refer to DAppNode core packages
7573
if (!repoSlug.includes("/")) repoSlug = `dappnode/DNP_${repoSlug}`;
7674

77-
await verifyIpfsConnection(ipfsProvider);
75+
const kuboClient = new ReleaseUploaderIpfsNode({ url: provider });
76+
await kuboClient.testConnection();
7877

7978
// Pick version interactively
8079
const release = await getSelectedGithubRelease({
@@ -135,7 +134,7 @@ export async function fromGithubHandler({
135134

136135
const releaseMultiHash = await ipfsAddDirFromUrls(
137136
files,
138-
ipfsProvider,
137+
provider,
139138
onProgress
140139
);
141140

src/releaseUploader/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type ReleaseUploaderProvider =
1313
| {
1414
network: "ipfs";
1515
type: "node";
16-
ipfsProvider: string;
16+
url: string;
1717
}
1818
| {
1919
network: "ipfs";
@@ -88,7 +88,7 @@ export function cliArgsToReleaseUploaderProvider({
8888
return {
8989
network: "ipfs",
9090
type: "node",
91-
ipfsProvider: contentProvider
91+
url: contentProvider
9292
};
9393
}
9494

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import got from "got";
2-
import { normalizeIpfsProvider } from "./ipfsProvider.js";
3-
import { getFormDataFileUpload } from "../utils/formDataFileUpload.js";
1+
import { KuboRPCClient } from "kubo-rpc-client";
2+
import fs from "fs";
3+
import path from "path";
44

55
/**
66
* Uploads a directory or file from the fs
@@ -11,37 +11,64 @@ import { getFormDataFileUpload } from "../utils/formDataFileUpload.js";
1111
*/
1212
export async function ipfsAddFromFs(
1313
dirOrFilePath: string,
14-
ipfsProvider: string,
14+
kuboClient: KuboRPCClient,
1515
onProgress?: (percent: number) => void
1616
): Promise<string> {
17-
// Create form and append all files recursively
18-
const form = getFormDataFileUpload(dirOrFilePath);
19-
20-
// Parse the ipfsProvider the a full base apiUrl
21-
let lastPercent = -1;
22-
const apiUrl = normalizeIpfsProvider(ipfsProvider);
23-
const res = await got({
24-
prefixUrl: apiUrl,
25-
url: "api/v0/add",
26-
method: "POST",
27-
headers: form.getHeaders(),
28-
body: form
29-
}).on("uploadProgress", progress => {
30-
// Report upload progress, and throttle to one update per percent point
31-
// { percent: 0.9995998225975282, transferred: 733675762, total: 733969480 }
32-
const currentRoundPercent = Math.round(100 * progress.percent);
33-
if (lastPercent !== currentRoundPercent) {
34-
lastPercent = currentRoundPercent;
35-
if (onProgress) onProgress(progress.percent);
17+
// Helper to recursively collect files from a directory
18+
function* getFiles(
19+
dir: string
20+
): Generator<{ path: string; content: fs.ReadStream }> {
21+
const stat = fs.statSync(dir);
22+
if (stat.isDirectory()) {
23+
for (const entry of fs.readdirSync(dir)) {
24+
yield* getFiles(path.join(dir, entry));
25+
}
26+
} else {
27+
yield {
28+
path: path.relative(path.dirname(dirOrFilePath), dir),
29+
content: fs.createReadStream(dir)
30+
};
3631
}
37-
});
32+
}
33+
34+
let files: any;
35+
if (fs.statSync(dirOrFilePath).isDirectory()) {
36+
files = Array.from(getFiles(dirOrFilePath));
37+
} else {
38+
files = [
39+
{
40+
path: path.basename(dirOrFilePath),
41+
content: fs.createReadStream(dirOrFilePath)
42+
}
43+
];
44+
}
3845

39-
// res.body = '{"Name":"dir/file","Hash":"Qm...","Size":"2203"}\n{"Name":"dir","Hash":"Qm...","Size":"24622"}\n'
40-
// Trim last \n, split entries by \n and then select the last which is the root directory
41-
const lastFileUnparsed = res.body.trim().split("\n").slice(-1)[0];
42-
if (!lastFileUnparsed) throw Error(`No files in response body ${res.body}`);
46+
// Track progress
47+
let totalSize = 0;
48+
let uploadedSize = 0;
49+
for (const file of files) {
50+
const filePath = path.resolve(dirOrFilePath, file.path);
51+
if (fs.existsSync(filePath)) {
52+
const stat = fs.statSync(filePath);
53+
totalSize += stat.size;
54+
} else {
55+
// Optionally log or handle missing files
56+
continue;
57+
}
58+
}
4359

44-
// Parse the JSON and return the hash of the root directory
45-
const lastFile = JSON.parse(lastFileUnparsed);
46-
return `/ipfs/${lastFile.Hash}`;
60+
// Add files to IPFS
61+
let lastCid = "";
62+
for await (const result of kuboClient.addAll(files, {
63+
wrapWithDirectory: true
64+
})) {
65+
lastCid = result.cid.toString();
66+
// Progress callback (approximate)
67+
if (onProgress && result.size) {
68+
uploadedSize += result.size;
69+
onProgress(Math.min(uploadedSize / totalSize, 1));
70+
}
71+
}
72+
if (!lastCid) throw Error("No CID returned from IPFS add");
73+
return `/ipfs/${lastCid}`;
4774
}
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { IReleaseUploader } from "../interface.js";
22
import { ipfsAddFromFs } from "./addFromFs.js";
3-
import { verifyIpfsConnection } from "./verifyConnection.js";
3+
import { normalizeIpfsProvider } from "./ipfsProvider.js";
4+
import { create, KuboRPCClient } from "kubo-rpc-client";
45

56
export class ReleaseUploaderIpfsNode implements IReleaseUploader {
67
networkName = "IPFS node";
7-
ipfsProvider: string;
8+
kuboClient: KuboRPCClient;
89

9-
constructor({ ipfsProvider }: { ipfsProvider: string }) {
10-
this.ipfsProvider = ipfsProvider;
10+
constructor({ url }: { url: string }) {
11+
this.kuboClient = create({ url: normalizeIpfsProvider(url) });
1112
}
1213

1314
async addFromFs({
@@ -17,10 +18,10 @@ export class ReleaseUploaderIpfsNode implements IReleaseUploader {
1718
dirPath: string;
1819
onProgress?: (percent: number) => void;
1920
}): Promise<string> {
20-
return await ipfsAddFromFs(dirPath, this.ipfsProvider, onProgress);
21+
return await ipfsAddFromFs(dirPath, this.kuboClient, onProgress);
2122
}
2223

2324
async testConnection(): Promise<void> {
24-
await verifyIpfsConnection(this.ipfsProvider);
25+
await this.kuboClient.version();
2526
}
2627
}

src/releaseUploader/ipfsNode/ipfsProvider.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ function parseIpfsProviderUrl(provider: string) {
3131
export function normalizeIpfsProvider(provider: string): string {
3232
const providerUrl = getIpfsProviderUrl(provider);
3333
const { host, port, protocol } = parseIpfsProviderUrl(providerUrl);
34-
const fullUrl = `${protocol}://${host}:${port}`;
34+
let fullUrl: string;
35+
if (Number(port) === 443) {
36+
fullUrl = `${protocol}://${host}`;
37+
} else {
38+
fullUrl = `${protocol}://${host}:${port}`;
39+
}
3540
// #### TEMP: Make sure the URL is correct
3641
new URL(fullUrl);
3742
return fullUrl;

src/releaseUploader/ipfsNode/ipfsVersion.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/releaseUploader/ipfsNode/verifyConnection.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)