UNPKG

@oap75/api

Version:
367 lines (366 loc) 15.7 kB
"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);