mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-30 00:41:12 +00:00
Upgrade nostr-tools, add agent
This commit is contained in:
parent
74eebf8d79
commit
e3cf09ce50
@ -20,6 +20,7 @@ module.exports = {
|
||||
"rules": {
|
||||
"a11y-click-events-have-key-events": "off",
|
||||
"no-unused-vars": ["error", {args: "none"}],
|
||||
"no-async-promise-executor": "off",
|
||||
},
|
||||
"ignorePatterns": ["*.svg"]
|
||||
}
|
||||
|
@ -44,6 +44,12 @@ If you like Coracle and want to support its development, you can donate sats via
|
||||
|
||||
# Changelog
|
||||
|
||||
## Current
|
||||
|
||||
- [ ] Upgrade nostr-tools
|
||||
- [ ] Close connections that haven't been used in a while
|
||||
- [ ] Move key management stuff
|
||||
|
||||
## 0.2.6
|
||||
|
||||
- [x] Add support for at-mentions in note and reply composition
|
||||
|
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -30,7 +30,7 @@
|
||||
"extract-urls": "^1.3.2",
|
||||
"fuse.js": "^6.6.2",
|
||||
"hurdak": "github:ConsignCloud/hurdak",
|
||||
"nostr-tools": "github:fiatjaf/nostr-tools#1b798b2",
|
||||
"nostr-tools": "^1.1.1",
|
||||
"ramda": "^0.28.0",
|
||||
"svelte-link-preview": "^0.3.3",
|
||||
"svelte-loading-spinners": "^0.3.4",
|
||||
|
4
src/agent/index.js
Normal file
4
src/agent/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
import keys from 'src/agent/keys'
|
||||
import pool from 'src/agent/pool'
|
||||
|
||||
export default {pool, keys}
|
33
src/agent/keys.js
Normal file
33
src/agent/keys.js
Normal file
@ -0,0 +1,33 @@
|
||||
import {getPublicKey, getEventHash, signEvent} from 'nostr-tools'
|
||||
|
||||
let pubkey
|
||||
let privkey
|
||||
let signingFunction
|
||||
|
||||
const getPubkey = () => {
|
||||
return pubkey || getPublicKey(privkey)
|
||||
}
|
||||
|
||||
const setPrivateKey = _privkey => {
|
||||
privkey = _privkey
|
||||
}
|
||||
|
||||
const setPublicKey = _pubkey => {
|
||||
signingFunction = async event => {
|
||||
const {sig} = await window.nostr.signEvent(event)
|
||||
|
||||
return sig
|
||||
}
|
||||
|
||||
pubkey = _pubkey
|
||||
}
|
||||
|
||||
const sign = async event => {
|
||||
event.pubkey = pubkey
|
||||
event.id = getEventHash(event)
|
||||
event.sig = signingFunction ? await signingFunction(event) : signEvent(event, privkey)
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
export default {getPubkey, setPrivateKey, setPublicKey, sign}
|
109
src/agent/pool.js
Normal file
109
src/agent/pool.js
Normal file
@ -0,0 +1,109 @@
|
||||
import {relayInit} from 'nostr-tools'
|
||||
import {partial, uniqBy, prop} from 'ramda'
|
||||
import {ensurePlural} from 'hurdak/lib/hurdak'
|
||||
|
||||
const relays = {}
|
||||
|
||||
const connect = async url => {
|
||||
if (!relays[url]) {
|
||||
relays[url] = relayInit(url)
|
||||
relays[url].url = url
|
||||
relays[url].stats = {
|
||||
count: 0,
|
||||
timer: 0,
|
||||
timeouts: 0,
|
||||
activeCount: 0,
|
||||
}
|
||||
|
||||
relays[url].on('disconnect', () => {
|
||||
delete relays[url]
|
||||
})
|
||||
|
||||
relays[url].connected = relays[url].connect()
|
||||
}
|
||||
|
||||
await relays[url].connected
|
||||
|
||||
return relays[url]
|
||||
}
|
||||
|
||||
const publish = (urls, event) => {
|
||||
urls.forEach(async url => {
|
||||
const relay = await connect(url)
|
||||
|
||||
relay.publish(event)
|
||||
})
|
||||
}
|
||||
|
||||
const sub = async (urls, filters) => {
|
||||
const subs = await Promise.all(
|
||||
urls.map(async url => {
|
||||
const relay = await connect(url)
|
||||
const sub = relay.sub(ensurePlural(filters))
|
||||
|
||||
sub.relay = relay
|
||||
sub.relay.stats.activeCount += 1
|
||||
|
||||
if (sub.relay.stats.activeCount > 10) {
|
||||
console.warning(`Relay ${url} has >10 active subscriptions`)
|
||||
}
|
||||
|
||||
return sub
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
unsub: () => {
|
||||
subs.forEach(sub => {
|
||||
sub.unsub()
|
||||
sub.relay.stats.activeCount -= 1
|
||||
})
|
||||
},
|
||||
on: (type, cb) => {
|
||||
subs.forEach(sub => {
|
||||
sub.on(type, partial(cb, [sub.relay.url]))
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const all = (urls, filters) => {
|
||||
return new Promise(async resolve => {
|
||||
const subscription = await sub(urls, filters)
|
||||
const now = Date.now()
|
||||
const events = []
|
||||
const eose = []
|
||||
|
||||
const done = () => {
|
||||
resolve(uniqBy(prop('id'), events))
|
||||
|
||||
// Keep track of relay timeouts
|
||||
urls.forEach(url => {
|
||||
if (!eose.includes(url)) {
|
||||
relays[url].stats.count += 1
|
||||
relays[url].stats.timer += Date.now() - now
|
||||
relays[url].stats.timeouts += 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
subscription.on('event', (url, e) => events.push(e))
|
||||
|
||||
subscription.on('eose', url => {
|
||||
eose.push(url)
|
||||
|
||||
// Keep track of relay timing stats
|
||||
relays[url].stats.count += 1
|
||||
relays[url].stats.timer += Date.now() - now
|
||||
|
||||
if (eose.length === urls.length) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
// If a relay takes too long, give up
|
||||
setTimeout(done, 5000)
|
||||
})
|
||||
}
|
||||
|
||||
export default {relays, connect, publish, sub, all}
|
@ -1,15 +1,13 @@
|
||||
import {uniqBy, sortBy, find, propEq, prop, uniq} from 'ramda'
|
||||
import {get} from 'svelte/store'
|
||||
import {relayPool, getPublicKey} from 'nostr-tools'
|
||||
import {noop, range, sleep} from 'hurdak/lib/hurdak'
|
||||
import {getTagValues, filterTags} from "src/util/nostr"
|
||||
import agent from 'src/agent'
|
||||
import {db} from 'src/relay/db'
|
||||
|
||||
// ============================================================================
|
||||
// Utils/config
|
||||
|
||||
const pool = relayPool()
|
||||
|
||||
class Channel {
|
||||
constructor(name) {
|
||||
this.name = name
|
||||
@ -21,8 +19,8 @@ class Channel {
|
||||
release() {
|
||||
this.status = 'idle'
|
||||
}
|
||||
sub(filter, onEvent, onEose = noop, opts = {}) {
|
||||
const relays = Object.keys(pool.relays)
|
||||
async sub(filter, onEvent, onEose = noop, opts = {}) {
|
||||
const relays = getRelays()
|
||||
|
||||
// If we don't have any relays, we'll wait forever for an eose, but
|
||||
// we already know we're done. Use a timeout since callers are
|
||||
@ -36,18 +34,20 @@ class Channel {
|
||||
// Start our subscription, wait for only our fastest relays to eose before calling it done.
|
||||
// We were waiting for all before, but that made the slowest relay a bottleneck. Waiting for
|
||||
// only one meant we might be settling for very incomplete data
|
||||
const start = new Date().valueOf()
|
||||
const lastEvent = {}
|
||||
const eoseRelays = []
|
||||
|
||||
// Create our subscription
|
||||
const sub = await agent.pool.sub(relays, filter)
|
||||
|
||||
// Keep track of when we last heard from each relay, and close unresponsive ones
|
||||
const cb = (e, r) => {
|
||||
sub.on('event', (r, e) => {
|
||||
lastEvent[r] = new Date().valueOf()
|
||||
onEvent(e)
|
||||
}
|
||||
})
|
||||
|
||||
// If we have lots of relays, ignore the slowest ones
|
||||
const onRelayEose = r => {
|
||||
sub.on('eose', r => {
|
||||
eoseRelays.push(r)
|
||||
|
||||
// If we have only a few, wait for all of them, otherwise ignore the slowest 1/5
|
||||
@ -55,28 +55,14 @@ class Channel {
|
||||
if (eoseRelays.length >= relays.length - threshold) {
|
||||
onEose()
|
||||
}
|
||||
}
|
||||
|
||||
// Create our subscription
|
||||
const sub = pool.sub({filter, cb}, this.name, onRelayEose)
|
||||
|
||||
// Watch for relays that are slow to respond and give up on them
|
||||
const interval = !opts.timeout ? null : setInterval(() => {
|
||||
for (const r of relays) {
|
||||
if ((lastEvent[r] || start) < new Date().valueOf() - opts.timeout) {
|
||||
onRelayEose(r)
|
||||
}
|
||||
}
|
||||
}, 300)
|
||||
})
|
||||
|
||||
// Clean everything up when we're done
|
||||
const done = () => {
|
||||
if (this.status === 'busy') {
|
||||
sub.unsub()
|
||||
this.release()
|
||||
}
|
||||
|
||||
clearInterval(interval)
|
||||
this.release()
|
||||
}
|
||||
|
||||
return {unsub: done}
|
||||
@ -86,7 +72,7 @@ class Channel {
|
||||
return new Promise(async resolve => {
|
||||
const result = []
|
||||
|
||||
const sub = this.sub(
|
||||
const sub = await this.sub(
|
||||
filter,
|
||||
e => result.push(e),
|
||||
r => {
|
||||
@ -122,39 +108,25 @@ const getChannel = async () => {
|
||||
const req = async (...args) => (await getChannel()).all(...args)
|
||||
const sub = async (...args) => (await getChannel()).sub(...args)
|
||||
|
||||
const getPubkey = () => {
|
||||
return pool._pubkey || getPublicKey(pool._privkey)
|
||||
}
|
||||
|
||||
const getRelays = () => {
|
||||
return Object.keys(pool.relays)
|
||||
return get(db.connections)
|
||||
}
|
||||
|
||||
const addRelay = url => {
|
||||
pool.addRelay(url)
|
||||
agent.pool.connect(url)
|
||||
}
|
||||
|
||||
const removeRelay = url => {
|
||||
pool.removeRelay(url)
|
||||
}
|
||||
const removeRelay = async url => {
|
||||
const relay = await agent.pool.connect(url)
|
||||
|
||||
const setPrivateKey = privkey => {
|
||||
pool.setPrivateKey(privkey)
|
||||
pool._privkey = privkey
|
||||
}
|
||||
|
||||
const setPublicKey = pubkey => {
|
||||
pool.registerSigningFunction(async event => {
|
||||
const {sig} = await window.nostr.signEvent(event)
|
||||
|
||||
return sig
|
||||
})
|
||||
|
||||
pool._pubkey = pubkey
|
||||
relay.close()
|
||||
}
|
||||
|
||||
const publishEvent = event => {
|
||||
pool.publish(event)
|
||||
const relays = getRelays()
|
||||
|
||||
event = agent.keys.sign(event)
|
||||
agent.publish(relays, event)
|
||||
db.events.process(event)
|
||||
}
|
||||
|
||||
@ -240,6 +212,6 @@ const syncNetwork = async () => {
|
||||
}
|
||||
|
||||
export default {
|
||||
getPubkey, getRelays, addRelay, removeRelay, setPrivateKey, setPublicKey,
|
||||
publishEvent, loadEvents, listenForEvents, syncNetwork, loadPeople,
|
||||
getRelays, addRelay, removeRelay, publishEvent, loadEvents, listenForEvents,
|
||||
syncNetwork, loadPeople,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user