@oap75/api
Version:
JavaScript API for Subsocial blockchain.
367 lines (366 loc) • 15.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SubsocialIpfsApi = exports.getCidsOfStructs = exports.getIpfsCidOfStruct = exports.getIpfsCidOfSocialAccount = void 0;
const utils_1 = require("@subsocial/utils");
const axios_1 = __importDefault(require("axios"));
const common_1 = require("../utils/common");
/** Return IPFS cid by social account struct */
function getIpfsCidOfSocialAccount(struct) {
const profile = struct === null || struct === void 0 ? void 0 : struct.profile;
if (profile && profile.isSome) {
return getIpfsCidOfStruct(profile.unwrap());
}
return undefined;
}
exports.getIpfsCidOfSocialAccount = getIpfsCidOfSocialAccount;
/** Try to resolve a corresponding IPFS CID of a given struct. */
function getIpfsCidOfStruct(struct) {
if ((0, common_1.isIpfs)(struct.content)) {
return struct.content.asIpfs.toHuman();
}
else if (struct.profile) {
return getIpfsCidOfSocialAccount(struct);
}
return undefined;
}
exports.getIpfsCidOfStruct = getIpfsCidOfStruct;
/** Extract ids of an array of structs. */
function getCidsOfStructs(structs) {
return structs
.map(getIpfsCidOfStruct)
.filter(cid => typeof cid !== 'undefined');
}
exports.getCidsOfStructs = getCidsOfStructs;
/** Aggregated API to work with IPFS: get the content of the spaces of posts and profiles. */
class SubsocialIpfsApi {
/** Sets values for ptivate fields from props and trying to make a test connection */
constructor(props) {
const { ipfsNodeUrl, offchainUrl, useServer } = props;
this.ipfsNodeUrl = `${ipfsNodeUrl}/api/v0`;
this.offchainUrl = `${offchainUrl}/v1`;
this.useServer = useServer;
this.testConnection();
}
/** Trying to make a test connection */
testConnection() {
return __awaiter(this, void 0, void 0, function* () {
if (this.useServer)
return;
try {
// Test IPFS Node connection by requesting its version
const res = yield this.ipfsNodeRequest('version');
log.info('Connected to IPFS Node with version ', res.data.version);
}
catch (err) {
log.error('Failed to connect to IPFS node:', err.stack);
}
});
}
// ---------------------------------------------------------------------
// IPFS Request wrapper
/** Makes a request to the IPFS node */
ipfsNodeRequest(endpoint, cid) {
return __awaiter(this, void 0, void 0, function* () {
const config = {
method: 'GET',
url: `${this.ipfsNodeUrl}/${endpoint}`
};
if (typeof cid !== undefined) {
config.url += `?arg=${cid}`;
}
return (0, axios_1.default)(config);
});
}
// ---------------------------------------------------------------------
// Find multiple
/** Return unique cids from cids array */
getUniqueCids(cids, contentName) {
contentName = (0, utils_1.nonEmptyStr)(contentName) ? `${contentName} content` : 'content';
const ipfsCids = (0, common_1.getUniqueIds)(cids.map(common_1.asIpfsCid));
if ((0, utils_1.isEmptyArray)(ipfsCids)) {
log.debug(`No ${contentName} to load from IPFS: no cids provided`);
return [];
}
return ipfsCids;
}
/** Return object with contents from IPFS by cids array */
getContentArrayFromIpfs(cids, contentName = 'content') {
return __awaiter(this, void 0, void 0, function* () {
try {
const ipfsCids = this.getUniqueCids(cids, contentName);
const content = {};
const getFormatedContent = (cid) => __awaiter(this, void 0, void 0, function* () {
const res = yield this.ipfsNodeRequest('dag/get', cid);
const cidStr = cid.toString();
content[cidStr] = res.data;
});
const loadContentFns = ipfsCids.map(getFormatedContent);
yield Promise.all(loadContentFns);
log.debug(`Loaded ${(0, utils_1.pluralize)({ count: cids.length, singularText: contentName })}`);
return content;
}
catch (err) {
console.error(`Failed to load ${contentName}(s) by ${cids.length} cid(s):`, err);
return {};
}
});
}
/** Return object with contents from IPFS through offchain by cids array */
getContentArrayFromOffchain(cids, contentName = 'content') {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
const res = ((_a = this.useServer) === null || _a === void 0 ? void 0 : _a.httpRequestMethod) === 'get'
? yield axios_1.default.get(`${this.offchainUrl}/ipfs/get?cids=${cids.join('&cids=')}`)
: yield axios_1.default.post(`${this.offchainUrl}/ipfs/get`, { cids });
if (res.status !== 200) {
log.error(`${this.getContentArrayFromIpfs.name}: Offchain server responded with status code ${res.status} and message: ${res.statusText}`);
return {};
}
const contents = res.data;
log.debug(`Loaded ${cids.length} ${contentName}`);
return contents;
}
catch (error) {
log.error('Failed to get content from IPFS via Offchain API:', error);
return {};
}
});
}
getContentArray(cids, contentName = 'content') {
return __awaiter(this, void 0, void 0, function* () {
return this.useServer
? this.getContentArrayFromOffchain(cids, contentName)
: this.getContentArrayFromIpfs(cids, contentName);
});
}
/**
* Find and load an array of off-chain information about spaces from IPFS by a given array of `cids`.
*
* Space information only exists if there is a corresponding JSON file that represents the spaces' content on IPFS.
*
* @param cids - An array of IPFS content ids of desired spaces.
*
* @returns An array of data about desired spaces from IPFS. If no corresponding spaces to given array of `cids`, an
* empty array is returned.
*/
findSpaces(cids) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContentArray(cids, 'space');
});
}
/**
* Find and load an array of off-chain information about posts from IPFS by a given array of `cids`.
*
* Post information only exists if there is a corresponding JSON file that represents the posts' content on IPFS.
*
* @param cids - An array of IPFS content ids of desired posts.
*
* @returns An array of data about desired posts from IPFS. If no corresponding posts to given array of `cids`, an
* empty array is returned.
*/
findPosts(cids) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContentArray(cids, 'post');
});
}
/**
* Find and load an array of off-chain information about comments from IPFS by a given array of `cids`.
*
* Comment information only exists if there is a corresponding JSON file that represents the comments' content on
* IPFS.
*
* @param cids - An array of IPFS content ids of desired comments.
*
* @returns An array of data about desired comments from IPFS. If no corresponding comments to given array of `cids`,
* an empty array is returned.
*/
findComments(cids) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContentArray(cids, 'comment');
});
}
/**
* Find and load an array of off-chain information about profiles from IPFS by a given array of `cids`.
*
* Profile information only exists if there is a corresponding JSON file that represents the profiles' content on
* IPFS.
*
* @param cids - An array of IPFS content ids of desired profiles.
*
* @returns An array of data about desired profiles from IPFS. If no corresponding profiles to given array of `cids`,
* an empty array is returned.
*/
findProfiles(cids) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContentArray(cids, 'account');
});
}
// ---------------------------------------------------------------------
// Find single
getContent(cid, contentName) {
return __awaiter(this, void 0, void 0, function* () {
const content = yield this.getContentArray([cid], contentName);
return content[cid.toString()];
});
}
/**
* Find and load off-chain information about a space from IPFS by a given `cid`.
*
* Space information only exists if there is a corresponding JSON file that represents the space's content on IPFS.
*
* @param cid - IPFS content id of a desired space.
*
* @returns Data about a desired space from IPFS. If no corresponding space to given `id`, `undefined` is returned.
*/
findSpace(cid) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContent(cid, 'space');
});
}
/**
* Find and load off-chain information about a post from IPFS by a given `cid`.
*
* Post information only exists if there is a corresponding JSON file that represents the post's content on IPFS.
*
* @param cid - IPFS content id of a desired post.
*
* @returns Data about a desired post from IPFS. If no corresponding post to given `id`, `undefined` is returned.
*/
findPost(cid) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContent(cid, 'post');
});
}
/**
* Find and load off-chain information about a comment from IPFS by a given `cid`.
*
* Comment information only exists if there is a corresponding JSON file that represents the comment's content on
* IPFS.
*
* @param cid - IPFS content id of a desired comment.
*
* @returns Data about a desired comment from IPFS. If no corresponding comments to given `id`, `undefined` is
* returned.
*/
findComment(cid) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContent(cid, 'comment');
});
}
/**
* Find and load off-chain information about a profile from IPFS by a given `cid`.
*
* Profile information only exists if there is a corresponding JSON file that represents the profile's content on
* IPFS.
*
* @param cid - IPFS content id of a desired profile.
*
* @returns Data about a desired profile from IPFS. If no corresponding profiles to given `id`, `undefined` is
* returned.
*/
findProfile(cid) {
return __awaiter(this, void 0, void 0, function* () {
return this.getContent(cid, 'account');
});
}
// ---------------------------------------------------------------------
// Remove
/** Unpin content in IPFS */
removeContent(cid) {
return __awaiter(this, void 0, void 0, function* () {
try {
const res = yield axios_1.default.delete(`${this.offchainUrl}/ipfs/pins/${cid}`);
if (res.status !== 200) {
log.error(`${this.removeContent.name}: offchain server responded with status code ${res.status} and message: ${res.statusText}`);
return;
}
log.info(`Unpinned content with hash: ${cid}`);
}
catch (error) {
log.error('Failed to unpin content in IPFS from client side via offchain: ', error);
}
});
}
/** Add and pin content in IPFS */
saveContent(content) {
return __awaiter(this, void 0, void 0, function* () {
try {
const res = yield axios_1.default.post(`${this.offchainUrl}/ipfs/add`, content);
if (res.status !== 200) {
log.error(`${this.saveContent.name}: Offchain server responded with status code ${res.status} and message: ${res.statusText}`);
return undefined;
}
return res.data;
}
catch (error) {
log.error('Failed to add content to IPFS from client side via offchain: ', error);
return undefined;
}
});
}
/** Add and pit file in IPFS */
saveFile(file) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof window === 'undefined') {
throw new Error('This function works only in a browser');
}
try {
const formData = new FormData();
formData.append('file', file);
const res = yield axios_1.default.post(`${this.offchainUrl}/ipfs/addFile`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
if (res.status !== 200) {
log.error(`${this.saveFile.name}: Offchain server responded with status code ${res.status} and message: ${res.statusText}`);
return undefined;
}
return res.data;
}
catch (error) {
log.error('Failed to add file to IPFS from client side via offchain: ', error);
return undefined;
}
});
}
/** Add and pin space content in IPFS */
saveSpace(content) {
return __awaiter(this, void 0, void 0, function* () {
const hash = yield this.saveContent(content);
log.debug(`Saved space with hash: ${hash}`);
return hash;
});
}
/** Add and pin post content in IPFS */
savePost(content) {
return __awaiter(this, void 0, void 0, function* () {
const hash = yield this.saveContent(content);
log.debug(`Saved post with hash: ${hash}`);
return hash;
});
}
/** Add and pin comment content in IPFS */
saveComment(content) {
return __awaiter(this, void 0, void 0, function* () {
const hash = yield this.saveContent(content);
log.debug(`Saved comment with hash: ${hash}`);
return hash;
});
}
}
exports.SubsocialIpfsApi = SubsocialIpfsApi;
const log = (0, utils_1.newLogger)(SubsocialIpfsApi.name);