diff --git a/package.json b/package.json index abdddefb0168b2966d228333fe3745f53dc07513..86fff61a0686eaecbb47c4ae3d6b9454ca51f5b8 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,10 @@ "private": true, "type": "module", "scripts": { - "dev": "vite", + "kubo-browser": "cp ./src/kubo-browser.ts ./src/kubo.ts", + "kubo-node": "cp ./src/kubo-node.ts ./src/kubo.ts", + "dev": "pnpm kubo-browser && vite", + "start": "pnpm kubo-node && pnpm exec tsx ./src/indexer/start.ts", "build": "run-p type-check \"build-only {@}\" --", "preview": "vite preview", "build-only": "vite build", diff --git a/scripts/kubo.ts b/scripts/kubo.ts new file mode 100644 index 0000000000000000000000000000000000000000..8b9f56f6cbe0241c330edd583e956883936d4285 --- /dev/null +++ b/scripts/kubo.ts @@ -0,0 +1,13 @@ +import { create } from 'kubo-rpc-client' + +// env +export const KUBO_RPC = 'http://127.0.0.1:5001' +export const KUBO_GATEWAY = 'http://127.0.0.1:8080' + +export function getKuboClientsBrower() { + return { kubo: create(), kubo2: null } +} + +const getKuboClientsPlatform = getKuboClientsBrower + +export const {kubo, kubo2} = getKuboClientsPlatform() \ No newline at end of file diff --git a/src/cesium-plus.ts b/src/cesium-plus.ts index 553e8143c3b89c190a16c57bcf535debbf2105be..9262ec25be5044a590da56748cb7e89a19777a5f 100644 --- a/src/cesium-plus.ts +++ b/src/cesium-plus.ts @@ -4,43 +4,7 @@ import { Buffer } from 'buffer' import { timestampToKey, arrayToVinode, mergeInodesSyncCID } from './processor' import { type IndexRequest } from './types' import { CESIUM_PLUS_PROFILE_IMPORT } from './consts' - -// ========================= types - -// for reference see -// https://doc.e-is.pro/cesium-plus-pod/REST_API.html -export interface CplusProfile { - version: number - title: string - description: string - time: number - issuer: string - hash: string - signature: string - city?: string - geoPoint?: Geoloc - socials?: Social[] - tags?: string[] - avatar?: Avatar | CID -} - -// social -interface Social { - type: string - url: string -} - -// avatar field will be managed as an IPFS file -interface Avatar { - _content_type: string // image/png for instance - _content: string // base64 encoded -} - -// geoloc -interface Geoloc { - lat: number - lon: number -} +import type{ CplusProfile, Avatar } from './types' // ========================= import functions diff --git a/src/components/IndexNode.vue b/src/components/IndexNode.vue index 41ce829e33a0ac243352193b20615535f7ec3960..d0ba1c6cca246036faf83fb947eb4c8e82da72cf 100644 --- a/src/components/IndexNode.vue +++ b/src/components/IndexNode.vue @@ -3,8 +3,8 @@ import { ref, type Ref, computed } from 'vue' import type { IndexInode, IndexLeaf } from '../types' import { formatDate } from '../utils' import { CID } from 'multiformats' -import { kubo, exploreUrl } from '@/kubo' -import { BASE } from '@/consts' +import { kubo } from '@/kubo' +import { exploreUrl, BASE } from '@/consts' const inode: Ref<IndexInode | null> = ref(null) const leaf: Ref<IndexLeaf | null> = ref(null) diff --git a/src/consts.ts b/src/consts.ts index 39de441af2f8e2c9433d7b9094163b5e63736d0d..68a23ae50d0b778cb0d22f46a1845fa44eb5460b 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -1,4 +1,5 @@ import { CID } from 'multiformats' +import { KUBO_RPC, KUBO_GATEWAY } from './kubo' // topic used for diffusion of index requests export const TOPIC = 'ddd' @@ -20,3 +21,15 @@ export const EMPTY_NODE_CID = CID.parse('bafyreicvlp2p65agkxpzcboedba7zit55us4zv export const CESIUM_PLUS_PROFILE_IMPORT = CID.parse('bafkreiawtammeqc55cssr2zepfpaxbmp7kquhikkagipvtefeadsw4mqvq') export const CESIUM_PLUS_PROFILE_INSERT = CID.parse('bafkreigi5phtqpo6a2f3tx4obaja4fzevy3nyvnl4bnkcxylyqnfeowzbm') export const CESIUM_PLUS_PROFILE_DELETE = CID.parse('bafkreic5bv5ytl7zv5rh5j2bd5mw6nfrn33mxhiobgmpsiu65yjw3eeduu') + +// ========== + +// explorer resources +const EXPLORER_CID = 'bafybeidf7cpkwsjkq6xs3r6fbbxghbugilx3jtezbza7gua3k5wjixpmba' +export const EXPLORER_URL = KUBO_RPC + '/ipfs/' + EXPLORER_CID + '/#' +export function exploreUrl(cid: CID): string { + return EXPLORER_URL + '/explore/ipfs/' + cid +} +export function gatewayUrl(cid: CID): string { + return KUBO_GATEWAY + '/ipfs/' + cid +} diff --git a/src/indexer/start.ts b/src/indexer/start.ts index c489d4c89d0023a3548279e7ccc26b68083166e6..26b972fc35c9bddbaca70cd64d6e920a70db7792 100644 --- a/src/indexer/start.ts +++ b/src/indexer/start.ts @@ -1,7 +1,7 @@ import { TOPIC } from '../consts' import { timestampToKey, arrayToVinode, publishHistory, mergeInodesSyncCID } from '../processor' import { getPubSubHandler } from '../collector' -import { kubo, kubo2, KUBO_RPC } from '../kubo' +import { kubo, kubo2 } from '../kubo' import type { IndexRequest } from '../types' import { CID } from 'multiformats' import { events, evtype, indexKnownDiff, indexStart } from './handlers' @@ -134,6 +134,6 @@ events.emit(evtype.indexStart, rootCID) // publishHistory(rootCID) // process loop -console.log('👂 listening on ' + KUBO_RPC + ', topic ' + TOPIC + '...') +console.log('👂 listening on topic ' + TOPIC + '...') setInterval(() => {}, 1 << 30) ;['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach((signal) => process.on(signal, process.exit)) diff --git a/src/kubo-browser.ts b/src/kubo-browser.ts new file mode 100644 index 0000000000000000000000000000000000000000..9928059d709495ee14d7d2fa03a73a44757c92d1 --- /dev/null +++ b/src/kubo-browser.ts @@ -0,0 +1,13 @@ +import { create } from 'kubo-rpc-client' + +// env +export const KUBO_RPC = 'http://127.0.0.1:5001' +export const KUBO_GATEWAY = 'http://127.0.0.1:8080' + +export function getKuboClientsBrower() { + return { kubo: create(KUBO_RPC), kubo2: null } +} + +const getKuboClientsPlatform = getKuboClientsBrower + +export const {kubo, kubo2} = getKuboClientsPlatform() \ No newline at end of file diff --git a/src/kubo-node.ts b/src/kubo-node.ts new file mode 100644 index 0000000000000000000000000000000000000000..66508469f42df8eddab34582db9f2c60b86fd460 --- /dev/null +++ b/src/kubo-node.ts @@ -0,0 +1,33 @@ +import { create } from 'kubo-rpc-client' +import type { KuboRPCClient } from 'kubo-rpc-client' +import { Agent } from 'http' + +// env +export const KUBO_RPC = process.env.KUBO_RPC || 'http://127.0.0.1:5001' +export const KUBO_GATEWAY = process.env.KUBO_GATEWAY || 'http://127.0.0.1:8080' + +function getKuboClientsNode() { + // create a RPC HTTP client // TODO unix socket for optimization + const kubo: KuboRPCClient = create({ + url: new URL(KUBO_RPC), + agent: new Agent({ + maxSockets: 50000 + }) + }) + // create an other RPC client only for pubsub + const kubo2: KuboRPCClient = create({ + url: new URL(KUBO_RPC), + agent: new Agent({ + keepAlive: true, // to prevent UND_ERR_BODY_TIMEOUT + keepAliveMsecs: 1000 + // maxSockets: 1, + // timeout: 100 + }) + }) + + return { kubo, kubo2 } +} + +const getKuboClientsPlatform = getKuboClientsNode + +export const { kubo, kubo2 } = getKuboClientsPlatform() diff --git a/src/kubo.ts b/src/kubo.ts index 2ca24a688b0641b5e2a48b0b9e4709e269606483..9928059d709495ee14d7d2fa03a73a44757c92d1 100644 --- a/src/kubo.ts +++ b/src/kubo.ts @@ -1,38 +1,13 @@ import { create } from 'kubo-rpc-client' -import type { KuboRPCClient } from 'kubo-rpc-client' -import { CID } from 'multiformats' -import { Agent } from 'http' // env -// TODO clean way to declare KUBO_RPC for Vue App -// var process : NodeJS.Process | undefined = typeof(process) == "undefined" ? undefined : process -export const KUBO_RPC = process?.env.KUBO_RPC || 'http://127.0.0.1:5001' -export const KUBO_GATEWAY = process?.env.KUBO_GATEWAY || 'http://127.0.0.1:8080' +export const KUBO_RPC = 'http://127.0.0.1:5001' +export const KUBO_GATEWAY = 'http://127.0.0.1:8080' -// create a RPC HTTP client // TODO unix socket for optimization -export const kubo: KuboRPCClient = create({ - url: new URL(KUBO_RPC), - agent: new Agent({ - maxSockets: 50000 - }) -}) -// create an other RPC client only for pubsub -export const kubo2: KuboRPCClient = create({ - url: new URL(KUBO_RPC), - agent: new Agent({ - keepAlive: true, // to prevent UND_ERR_BODY_TIMEOUT - keepAliveMsecs: 1000 - // maxSockets: 1, - // timeout: 100 - }) -}) - -// explorer resources -const EXPLORER_CID = 'bafybeidf7cpkwsjkq6xs3r6fbbxghbugilx3jtezbza7gua3k5wjixpmba' -export const EXPLORER_URL = KUBO_RPC + '/ipfs/' + EXPLORER_CID + '/#' -export function exploreUrl(cid: CID): string { - return EXPLORER_URL + '/explore/ipfs/' + cid -} -export function gatewayUrl(cid: CID): string { - return KUBO_GATEWAY + '/ipfs/' + cid +export function getKuboClientsBrower() { + return { kubo: create(KUBO_RPC), kubo2: null } } + +const getKuboClientsPlatform = getKuboClientsBrower + +export const {kubo, kubo2} = getKuboClientsPlatform() \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index bd1e09d9fb82ccf59031748aefe4ad6d805891c4..628af2660ec9bdc9863aaefd1b4162ce93dfd6f5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -85,3 +85,40 @@ export interface IndexHist { // timestamp timestamp: number } + +// =================== cplus + +// for reference see +// https://doc.e-is.pro/cesium-plus-pod/REST_API.html +export interface CplusProfile { + version: number + title: string + description: string + time: number + issuer: string + hash: string + signature: string + city?: string + geoPoint?: Geoloc + socials?: Social[] + tags?: string[] + avatar?: Avatar | CID +} + +// social +interface Social { + type: string + url: string +} + +// avatar field will be managed as an IPFS file +export interface Avatar { + _content_type: string // image/png for instance + _content: string // base64 encoded +} + +// geoloc +interface Geoloc { + lat: number + lon: number +} diff --git a/src/views/CplusView.vue b/src/views/CplusView.vue index f6852266ce434b398a242dfd7e8202b3ccf2d469..838731be234998cd9d1a31aa72c95ac76bbe14eb 100644 --- a/src/views/CplusView.vue +++ b/src/views/CplusView.vue @@ -1,7 +1,8 @@ <script setup lang="ts"> import { ref, type Ref, onMounted } from 'vue' -import { processCesiumPlusProfile, type CplusProfile } from '../cesium-plus' +import type { CplusProfile, Avatar } from '../types' import { CID } from 'multiformats' +import { kubo } from '../kubo' const file: Ref<File | null> = ref(null) const sample = ref('') @@ -45,6 +46,25 @@ function importCplus() { } } +// this is like processCesiumPlusProfile but does not use node-specific buffer +async function processCesiumPlusProfile(obj: CplusProfile): Promise<CID> { + const { avatar, ...profileWithoutAvatar } = obj + if (avatar != undefined && (avatar as Avatar)._content != undefined) { + const base64String = (avatar as Avatar)._content + const byteCharacters = atob(base64String) + const byteNumbers = new Array(byteCharacters.length) + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i) + } + const byteArray = new Uint8Array(byteNumbers) + const fileCandidate = { content: byteArray } + return kubo + .add(fileCandidate) + .then((result) => kubo.dag.put({ ...profileWithoutAvatar, avatar: result.cid as CID }) as Promise<CID>) + } else { + return kubo.dag.put(obj) as Promise<CID> + } +} </script> <template> diff --git a/src/views/IndexView.vue b/src/views/IndexView.vue index 1f66536653dbefeb9afd027220b5b374e0fa54b4..345102abb33d2e16bf737076b593bee483a02b7d 100644 --- a/src/views/IndexView.vue +++ b/src/views/IndexView.vue @@ -1,6 +1,5 @@ <script setup lang="ts"> import { kubo } from '@/kubo' -import { emptyInode, type IndexHist } from '../types' import { IPNS, IPNS_HIST } from '../consts' import { CID } from 'multiformats' import { ref, type Ref, computed, onMounted } from 'vue' diff --git a/src/views/KuboView.vue b/src/views/KuboView.vue index af480a4a1f8552deb0320f3d7e45e46ca233031b..8a5e3e3814f5fb4b2028497e42cd0bf850d8d60c 100644 --- a/src/views/KuboView.vue +++ b/src/views/KuboView.vue @@ -1,10 +1,11 @@ <script setup lang="ts"> import { ref, type Ref } from 'vue' -import { kubo, EXPLORER_URL } from '@/kubo' +import { kubo } from '@/kubo' +import { EXPLORER_URL } from '../consts' import prettyBytes from 'pretty-bytes' -import type { StatResult } from 'kubo-rpc-client' +import type { RepoStatResult } from 'kubo-rpc-client' -const stats: Ref<null | StatResult> = ref(null) // Ref<StatResult> +const stats: Ref<null | RepoStatResult> = ref(null) // Ref<StatResult> async function refresh() { stats.value = await kubo.repo.stat() @@ -19,13 +20,13 @@ refresh() <p>Kubo node status and control panel <button @click="refresh">refresh</button></p> <h2>Stats</h2> <p v-if="stats"> - TODO update to latest StatResult<br /> NumObjects: {{ stats.numObjects.toString() }}<br /> RepoSize: {{ prettyBytes(Number(stats.repoSize)) }}<br /> StorageMax: {{ prettyBytes(Number(stats.storageMax)) }}<br /> RepoPath: {{ stats.repoPath }}<br /> Version: {{ stats.version }}<br /> </p> + <p v-else>loading stats...</p> <p> for more info see <a :href="EXPLORER_URL" target="_blank">explorer</a>