Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add explicit file length in form data for BLOBs #156

Merged
merged 16 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Upgrade NPM
run: npm i -g npm

- name: Install dependencies
run: npm ci

Expand Down
5,695 changes: 3,469 additions & 2,226 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.5.1",
"formdata-node": "^6.0.3",
"jest": "^29.7.0",
"prettier": "^2.6.2",
"ts-jest": "^27.1.4",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typedoc": "^0.22.15",
"typescript": "^4.6.3"
Expand Down
2 changes: 1 addition & 1 deletion src/global.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const DEFAULT_API_V2 = "https://api2.aleph.im";
export const DEFAULT_API_V2 = "http://127.0.0.1:4024/";
31 changes: 20 additions & 11 deletions src/messages/create/publish.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import shajs from "sha.js";

import { BaseMessage, ItemType } from "../types";
import axios from "axios";
import axios, { AxiosError } from "axios";
import FormDataNode from "form-data";
import { getSocketPath, stripTrailingSlash } from "../../utils/url";

Expand Down Expand Up @@ -69,17 +69,26 @@ export async function PutContentToStorageEngine<T>(configuration: PutConfigurati
}

async function PushToStorageEngine<T>(configuration: PushConfiguration<T>): Promise<string> {
const response = await axios.post<PushResponse>(
`${stripTrailingSlash(configuration.APIServer)}/api/v0/${configuration.storageEngine.toLowerCase()}/add_json`,
configuration.content,
{
headers: {
"Content-Type": "application/json",
try {
const response = await axios.post<PushResponse>(
`${stripTrailingSlash(
configuration.APIServer,
)}/api/v0/${configuration.storageEngine.toLowerCase()}/add_json`,
configuration.content,
{
headers: {
"Content-Type": "application/json",
},
socketPath: getSocketPath(),
},
socketPath: getSocketPath(),
},
);
return response.data.hash;
);
return response.data.hash;
} catch (err) {
if (err instanceof AxiosError) {
console.error(err.response?.data);
}
throw err;
}
}

export async function PushFileToStorageEngine(configuration: PushFileConfiguration): Promise<string> {
Expand Down
1 change: 1 addition & 0 deletions src/messages/store/pin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export async function Pin(spc: StorePinConfiguration): Promise<StoreMessage> {
channel: spc.channel,
fileHash: spc.fileHash,
APIServer: spc.APIServer || DEFAULT_API_V2,
inlineRequested: true,
storageEngine: ItemType.ipfs,
});
}
90 changes: 52 additions & 38 deletions src/messages/store/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { RequireOnlyOne } from "../../utils/requiredOnlyOne";
import { DEFAULT_API_V2 } from "../../global";
import { MessageBuilder } from "../../utils/messageBuilder";
import { stripTrailingSlash } from "../../utils/url";
import FormData from "form-data";
import axios from "axios";
import axios, { AxiosError } from "axios";
import { blobToBuffer, calculateSHA256Hash } from "./utils";

/**
Expand All @@ -28,7 +27,7 @@ import { blobToBuffer, calculateSHA256Hash } from "./utils";
type StorePublishConfiguration = {
channel: string;
account: Account;
fileObject?: Buffer | Blob;
fileObject?: Buffer | Blob | File;
fileHash?: string;
storageEngine?: ItemType.ipfs | ItemType.storage;
inlineRequested?: boolean;
Expand Down Expand Up @@ -56,39 +55,41 @@ export async function Publish({
if (fileObject && fileHash) throw new Error("You can't pin a file and upload it at the same time.");
if (fileHash && storageEngine !== ItemType.ipfs) throw new Error("You must choose ipfs to pin the file.");

const myHash = await getHash(fileObject, storageEngine, fileHash, APIServer);

let hash: string | undefined = fileHash;
if (!hash) {
const buffer = await processFileObject(fileObject);
hash = await getHash(buffer, storageEngine, fileHash, APIServer);
if (fileObject instanceof File) {
fileObject = new File([buffer], fileObject.name);
} else {
fileObject = new Blob([buffer]);
}
}
const message = await createAndSendStoreMessage(
account,
channel,
myHash,
hash,
storageEngine,
APIServer,
inlineRequested,
sync,
fileObject,
fileObject as Blob | File | undefined,
MHHukiewitz marked this conversation as resolved.
Show resolved Hide resolved
);

return message;
}

async function getHash(
fileObject: Buffer | Blob | null | undefined,
storageEngine: ItemType,
fileHash: string | undefined,
APIServer: string,
) {
if (fileObject && storageEngine !== ItemType.ipfs) {
const hash = await processFileObject(fileObject);
async function getHash(buffer: Buffer, storageEngine: ItemType, fileHash: string | undefined, APIServer: string) {
if (buffer && storageEngine !== ItemType.ipfs) {
const hash = calculateSHA256Hash(buffer);
if (hash === null || hash === undefined) {
throw new Error("Cannot process file");
}
return hash;
} else if (fileObject && storageEngine === ItemType.ipfs) {
} else if (buffer && storageEngine === ItemType.ipfs) {
return await PushFileToStorageEngine({
APIServer,
storageEngine,
file: fileObject,
file: buffer,
});
} else if (fileHash) {
return fileHash;
Expand All @@ -100,18 +101,18 @@ async function getHash(
async function createAndSendStoreMessage(
account: Account,
channel: string,
myHash: string,
fileHash: string,
storageEngine: ItemType,
APIServer: string,
inlineRequested: boolean,
sync: boolean,
fileObject: Buffer | Blob | undefined,
fileObject: Blob | File | undefined,
) {
const timestamp = Date.now() / 1000;
const storeContent: StoreContent = {
address: account.address,
item_type: storageEngine,
item_hash: myHash,
item_hash: fileHash,
time: timestamp,
};

Expand All @@ -130,8 +131,7 @@ async function createAndSendStoreMessage(
inline: inlineRequested,
APIServer,
});

if (ItemType.ipfs === message.item_type) {
if (ItemType.ipfs == storageEngine && inlineRequested) {
await SignAndBroadcast({
message: message,
account,
Expand All @@ -140,7 +140,7 @@ async function createAndSendStoreMessage(
} else if (!fileObject) {
throw new Error("You need to specify a File to upload or a Hash to pin.");
} else {
return await sendMessage(
await sendMessage(
{
message: message,
account,
Expand All @@ -154,13 +154,16 @@ async function createAndSendStoreMessage(
return message;
}

async function processFileObject(fileObject: Blob | Buffer | null): Promise<string> {
if (!fileObject) throw new Error("fileObject is null");
async function processFileObject(fileObject: Blob | Buffer | File | null | undefined): Promise<Buffer> {
if (!fileObject) {
throw new Error("fileObject is null");
}

if (fileObject instanceof Blob) {
fileObject = await blobToBuffer(fileObject);
if (fileObject instanceof Buffer) {
return fileObject;
}
return calculateSHA256Hash(fileObject);

return await blobToBuffer(fileObject);
}

type SignAndBroadcastConfiguration = {
Expand All @@ -170,7 +173,7 @@ type SignAndBroadcastConfiguration = {
sync: boolean;
};

async function sendMessage(configuration: SignAndBroadcastConfiguration, fileObject: Blob | Buffer) {
async function sendMessage(configuration: SignAndBroadcastConfiguration, file: Blob | File): Promise<any> {
const form = new FormData();
const metadata = {
message: {
Expand All @@ -180,14 +183,25 @@ async function sendMessage(configuration: SignAndBroadcastConfiguration, fileObj
sync: configuration.sync,
};

form.append("file", fileObject);
form.append("file", file);
form.append("metadata", JSON.stringify(metadata));

const response = await axios.post(`${stripTrailingSlash(configuration.APIServer)}/api/v0/storage/add_file`, form, {
headers: {
Accept: "application/json",
"Content-Type": "multipart/form-data",
},
});
return response.data;
try {
const response = await axios.post(
`${stripTrailingSlash(configuration.APIServer)}/api/v0/storage/add_file`,
form,
{
headers: {
Accept: "application/json",
"Content-Type": "multipart/form-data",
},
},
);
return response.data;
} catch (err) {
if (err instanceof AxiosError) {
console.error(err.response?.data);
}
throw err;
}
}
15 changes: 3 additions & 12 deletions src/messages/store/utils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import shajs from "sha.js";

export async function blobToBuffer(blob: Blob): Promise<Buffer> {
return new Promise<Buffer>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
if (reader.result instanceof ArrayBuffer) {
resolve(Buffer.from(reader.result));
} else {
reject("Failed to convert Blob to Buffer.");
}
};
reader.readAsArrayBuffer(blob);
});
export async function blobToBuffer(blob: Blob | File): Promise<Buffer> {
const arrayBuffer = await blob.arrayBuffer();
return Buffer.from(arrayBuffer);
}

export function calculateSHA256Hash(data: ArrayBuffer | Buffer): string {
Expand Down
Loading
Loading