Skip to content

Commit

Permalink
feat/poa-mvp (#40)
Browse files Browse the repository at this point in the history
* Adding PoA to 3SpeakApp

* chore: pinning debug, fix CumulativeSize

* tweak: add date to

* partial: pinning progress

* PoA access fixes
Refactoring and optimizing

* Fix for pin page issue

* Update proofofaccess releases path

* partial: dag import ipfs

* partial: wire up pinning progress indicator

* Keep PoA running when changing pages

---------

Co-authored-by: vaultec <47548474+vaultec81@users.noreply.github.com>
  • Loading branch information
nathansenn and vaultec81 committed May 16, 2023
1 parent e1afcf1 commit cd4409b
Show file tree
Hide file tree
Showing 70 changed files with 3,586 additions and 2,136 deletions.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "3speak-app",
"version": "0.1.20",
"PoA": "0",
"description": "3Speak decentralized desktop app",
"main": "./dist/main.prod.js",
"scripts": {
Expand Down Expand Up @@ -48,7 +49,6 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"file-loader": "^3.0.1",
"go-ipfs": "^0.10.0",
"html-webpack-plugin": "^3.2.0",
"less": "^3.13.1",
"less-loader": "^4.1.0",
Expand Down Expand Up @@ -93,9 +93,11 @@
"date-and-time": "^0.14.2",
"dlv": "^1.1.3",
"dompurify": "^2.2.6",
"electron-better-ipc": "^2.0.1",
"electron-promise-ipc": "^2.2.4",
"execa": "^5.0.0",
"fluent-ffmpeg": "^2.1.2",
"go-ipfs": "^0.16.0",
"graphql": "^16.5.0",
"i18next": "^19.8.4",
"ipfs-core": "^0.12.0",
Expand Down Expand Up @@ -132,7 +134,9 @@
"tedious": "^14.0.0",
"wa-go-ipfs": "git+https://github.com/vaultec81/wa-go-ipfs.git",
"winston": "^3.3.3",
"winston-daily-rotate-file": "^4.5.5"
"winston-daily-rotate-file": "^4.5.5",
"xterm": "^5.1.0",
"xterm-addon-web-links": "^0.8.0"
},
"build": {
"productName": "3Speak-app",
Expand Down
18 changes: 18 additions & 0 deletions src/components/CustomToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { FaCogs } from 'react-icons/fa';
export const CustomToggle = React.forwardRef<any, any>(({ children, onClick }, ref) => (
<button
style={{ float: 'right' }}
ref={ref}
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
className="btn btn-sm dropdown-toggle btn-secondary"
id="videoOptions"
type="button"
data-toggle="dropdown"
>
<FaCogs />
</button>
));
38 changes: 38 additions & 0 deletions src/components/DHTProviders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useState, useEffect } from 'react';
import { FaSitemap } from 'react-icons/fa';
import * as IPFSHTTPClient from 'ipfs-http-client';
import { IPFS_HOST } from '../common/constants';

let ipfsClient;
try {
ipfsClient = IPFSHTTPClient.create({ host: IPFS_HOST });
} catch (error) {
console.error(`Error creating IPFS client in watch.tsx: `, error);
throw error;
}

export function DHTProviders(props) {
const [peers, setPeers] = useState(0);

useEffect(() => {
void load();

async function load() {
if (!props.rootCid) {
return;
}
let out = 0;
for await (const pov of ipfsClient.dht.findProvs(props.rootCid)) {
out = out + 1;
setPeers(out);
}
setPeers(out);
}
}, []);

return (
<div>
<FaSitemap /> DHT Providers <strong>{peers}</strong>
</div>
);
}
106 changes: 106 additions & 0 deletions src/main/AutoUpdaterPoA.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import os from 'os';
import Path from 'path';
import fs from 'fs';
import axios from 'axios';
import compareVersions from 'compare-versions';
import { EventEmitter } from 'events';

const isWin = process.platform === 'win32';

class PoAInstaller extends EventEmitter {
async main() {
try {
await this.install();
} catch (error) {
console.error(error);
this.emit('error', error);
process.exit(1);
}
}
async getCurrentVersion(installDir) {
const versionFilePath = Path.join(installDir, 'version.txt');
try {
const currentVersion = await fs.promises.readFile(versionFilePath, 'utf-8');
return currentVersion.trim();
} catch (error) {
// If the file doesn't exist or there is an error reading it, return a default version
return '0.0.0';
}
}
async getDefaultPath() {
let defaultPath;
switch (process.platform) {
case 'win32':
defaultPath = Path.join('AppData/Roaming/Microsoft/Windows/Start Menu/Programs/PoA');
return defaultPath;
case 'darwin':
defaultPath = Path.join(os.homedir(), 'Applications/PoA/poa');
break;
case 'linux':
defaultPath = Path.join(os.homedir(), 'bin/PoA/poa');
break;
default:
throw new Error(`Unsupported platform: ${process.platform}`);
}

// Check if the default path exists
try {
await fs.promises.access(defaultPath, fs.constants.F_OK);
return defaultPath;
} catch (error) {
// Default path does not exist, return null
return null;
}
}
async install() {
const installDir = Path.join(os.homedir(), (await this.getDefaultPath()) || '');
console.log(`Installing PoA to ${installDir}`);
if (!fs.existsSync(installDir)) {
fs.mkdirSync(installDir, { recursive: true });
}

console.log('Installing PoA...');

const { data } = await axios.get('https://api.github.com/repos/spknetwork/proofofaccess/releases/latest');
const { tag_name, assets } = data;

console.log(tag_name);
const currentVersion = await this.getCurrentVersion(installDir);
if (compareVersions.compare(tag_name, currentVersion, '>')) {
console.log('Update available');
this.emit('update-available', tag_name);
const asset = assets.find((a) => a.name.includes('win-main') && a.name.includes('exe') && isWin);

if (!asset) {
console.error('Could not find PoA asset for this platform');
return;
}

console.log(`Downloading PoA for ${process.platform}...`);

const response = await axios({
method: 'get',
url: asset.browser_download_url,
responseType: 'arraybuffer',
});

const installPath = Path.join(installDir, 'PoA.exe');

fs.writeFileSync(installPath, Buffer.from(response.data));

console.log(`PoA installed at: ${installPath}`);
this.emit('installed', installPath);

// Update version.txt file
const versionFilePath = Path.join(installDir, 'version.txt');
fs.writeFileSync(versionFilePath, tag_name);
console.log(`Version ${tag_name} saved to ${versionFilePath}`);
this.emit('version-updated', tag_name);
} else {
console.log('PoA is already up-to-date');
this.emit('up-to-date');
}
}
}

export default PoAInstaller;
1 change: 0 additions & 1 deletion src/main/core/components/DistillerDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ PouchDB.plugin(require('pouchdb-find'))
PouchDB.plugin(require('pouchdb-upsert'))

const hiveClient = new HiveClient([
'https://deathwing.me',
'https://api.openhive.network',
'https://hived.privex.io',
'https://anyx.io',
Expand Down
126 changes: 124 additions & 2 deletions src/main/core/components/Pins.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,99 @@
import { CID, globSource, IPFSHTTPClient } from 'ipfs-http-client'
import PouchDB from 'pouchdb'
import Axios from 'axios'
import fs from 'fs'
import fsPromises from 'fs/promises'
import { CoreService } from '..'
import RefLink from '../../RefLink'
import { IpfsHandler } from './ipfsHandler'
import tmp from 'tmp'
import execa from 'execa'
import waIpfs from 'wa-go-ipfs'
const Path = require('path')
const debug = require('debug')('3speak:pins')
const Schedule = require('node-schedule')
PouchDB.plugin(require('pouchdb-find'))
PouchDB.plugin(require('pouchdb-upsert'))

async function progressPin(ipfs: IPFSHTTPClient, pin: string, callback: Function) {

// ipfs.dag.exp

const tmpPath = tmp.dirSync();
console.log(tmpPath)
const writer = fs.createWriteStream(Path.join(tmpPath.name, 'download.car'));

const {data} = await Axios.get(`https://ipfs-3speak.b-cdn.net/api/v0/object/stat?arg=${pin}`)
const CumulativeSize = data.CumulativeSize

let totalSizeSoFar = 0;
await Axios.get(`https://ipfs-3speak.b-cdn.net/api/v0/dag/export?arg=${pin}`, {
responseType: 'stream',
}).then(response => {


response.data.on('data', (chunk) => {
totalSizeSoFar = totalSizeSoFar + chunk.length
const pct = Math.round((totalSizeSoFar / CumulativeSize) * 100)
callback(pct)
// console.log(`${pct}%`)
})
return new Promise((resolve, reject) => {
response.data.pipe(writer);
let error = null;
writer.on('error', err => {
error = err;
writer.close();
reject(err);
});
writer.on('close', () => {
if (!error) {
resolve(true);
}
});
});
})

// console.log('got here')
// for await(let importBlock of ipfs.dag.import(fs.createReadStream(Path.join(tmpPath.name, 'download.car')))) {
// console.log(importBlock)
// }

const ipfsInfo = await IpfsHandler.getIpfs()
const ipfsPath = await waIpfs.getPath(
waIpfs.getDefaultPath({ dev: process.env.NODE_ENV === 'development' }),
)


const output = await execa(ipfsPath, [
'dag',
'import',
Path.join(tmpPath.name, 'download.car')
], {
env: {
IPFS_PATH: ipfsInfo.ipfsPath
}
})
console.log(output)

await fsPromises.rmdir(tmpPath.name, {
recursive: true,
force: true
} as any)

// const refs = ipfs.refs(pin, {
// recursive: true
// // maxDepth: 1
// })
// const TotalSize = (await ipfs.object.stat(CID.parse(pin))).CumulativeSize
// let counter = 0
// for await(let result of refs) {
// // const CumulativeSize = (await ipfs.object.stat(CID.parse(result.ref))).CumulativeSize
// const dag = await ipfs.dag.get(CID.parse(result.ref))
// counter = counter + dag.value.Data.length;
// callback(Number((counter / TotalSize).toFixed(3)))
// }
}
class Pins {
self: CoreService
db: any
Expand Down Expand Up @@ -43,20 +130,55 @@ class Pins {
try {
await ipfs.swarm.connect(bt)
} catch (ex) {
console.error(ex)
// console.error(ex)
}
})
doc.cids = doc.cids.filter(function (item, pos, self) {
return self.indexOf(item) == pos
})
doc.size = 0
this.inProgressPins[doc._id] = doc


let totalSize = 0
let totalPercent;

console.log(doc)

const progressTick = setInterval(async() => {

try {
const bDoc = await this.db.get("hive:cowboyzlegend27:qauvdrmx")
console.log(bDoc)
} catch {

}
try {
const cDoc = await this.db.get(doc._id)
console.log(cDoc)
} catch {

}
await this.db.upsert(doc._id, (oldDoc) => {
console.log('change status', oldDoc, totalPercent)
oldDoc.percent = totalPercent;
doc.percent = totalPercent
return oldDoc
})
}, 1000)

for (const cid of doc.cids) {
console.log('Pinning CID', cid, new Date(), doc._id)
await progressPin(this.self.ipfs, cid, (percent) => {
totalPercent = percent
})
console.log('Done pinning, attempting full pin')
await this.self.ipfs.pin.add(cid)
console.log('Getting Cumulative size of', cid, new Date(), doc._id)
const objectInfo = await this.self.ipfs.object.stat(cid)
totalSize = +objectInfo.CumulativeSize
totalSize = totalSize + objectInfo.CumulativeSize
}
clearInterval(progressTick)
doc.size = totalSize
//Prevet old and new docs from stepping on eachother.
await this.db.upsert(doc._id, (oldDoc) => {
Expand Down
Loading

0 comments on commit cd4409b

Please sign in to comment.