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": {
|
"rules": {
|
||||||
"a11y-click-events-have-key-events": "off",
|
"a11y-click-events-have-key-events": "off",
|
||||||
"no-unused-vars": ["error", {args: "none"}],
|
"no-unused-vars": ["error", {args: "none"}],
|
||||||
|
"no-async-promise-executor": "off",
|
||||||
},
|
},
|
||||||
"ignorePatterns": ["*.svg"]
|
"ignorePatterns": ["*.svg"]
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,12 @@ If you like Coracle and want to support its development, you can donate sats via
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Current
|
||||||
|
|
||||||
|
- [ ] Upgrade nostr-tools
|
||||||
|
- [ ] Close connections that haven't been used in a while
|
||||||
|
- [ ] Move key management stuff
|
||||||
|
|
||||||
## 0.2.6
|
## 0.2.6
|
||||||
|
|
||||||
- [x] Add support for at-mentions in note and reply composition
|
- [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",
|
"extract-urls": "^1.3.2",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"hurdak": "github:ConsignCloud/hurdak",
|
"hurdak": "github:ConsignCloud/hurdak",
|
||||||
"nostr-tools": "github:fiatjaf/nostr-tools#1b798b2",
|
"nostr-tools": "^1.1.1",
|
||||||
"ramda": "^0.28.0",
|
"ramda": "^0.28.0",
|
||||||
"svelte-link-preview": "^0.3.3",
|
"svelte-link-preview": "^0.3.3",
|
||||||
"svelte-loading-spinners": "^0.3.4",
|
"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 {uniqBy, sortBy, find, propEq, prop, uniq} from 'ramda'
|
||||||
import {get} from 'svelte/store'
|
import {get} from 'svelte/store'
|
||||||
import {relayPool, getPublicKey} from 'nostr-tools'
|
|
||||||
import {noop, range, sleep} from 'hurdak/lib/hurdak'
|
import {noop, range, sleep} from 'hurdak/lib/hurdak'
|
||||||
import {getTagValues, filterTags} from "src/util/nostr"
|
import {getTagValues, filterTags} from "src/util/nostr"
|
||||||
|
import agent from 'src/agent'
|
||||||
import {db} from 'src/relay/db'
|
import {db} from 'src/relay/db'
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Utils/config
|
// Utils/config
|
||||||
|
|
||||||
const pool = relayPool()
|
|
||||||
|
|
||||||
class Channel {
|
class Channel {
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
this.name = name
|
this.name = name
|
||||||
@ -21,8 +19,8 @@ class Channel {
|
|||||||
release() {
|
release() {
|
||||||
this.status = 'idle'
|
this.status = 'idle'
|
||||||
}
|
}
|
||||||
sub(filter, onEvent, onEose = noop, opts = {}) {
|
async sub(filter, onEvent, onEose = noop, opts = {}) {
|
||||||
const relays = Object.keys(pool.relays)
|
const relays = getRelays()
|
||||||
|
|
||||||
// If we don't have any relays, we'll wait forever for an eose, but
|
// 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
|
// 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.
|
// 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
|
// 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
|
// only one meant we might be settling for very incomplete data
|
||||||
const start = new Date().valueOf()
|
|
||||||
const lastEvent = {}
|
const lastEvent = {}
|
||||||
const eoseRelays = []
|
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
|
// 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()
|
lastEvent[r] = new Date().valueOf()
|
||||||
onEvent(e)
|
onEvent(e)
|
||||||
}
|
})
|
||||||
|
|
||||||
// If we have lots of relays, ignore the slowest ones
|
// If we have lots of relays, ignore the slowest ones
|
||||||
const onRelayEose = r => {
|
sub.on('eose', r => {
|
||||||
eoseRelays.push(r)
|
eoseRelays.push(r)
|
||||||
|
|
||||||
// If we have only a few, wait for all of them, otherwise ignore the slowest 1/5
|
// 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) {
|
if (eoseRelays.length >= relays.length - threshold) {
|
||||||
onEose()
|
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
|
// Clean everything up when we're done
|
||||||
const done = () => {
|
const done = () => {
|
||||||
if (this.status === 'busy') {
|
if (this.status === 'busy') {
|
||||||
sub.unsub()
|
sub.unsub()
|
||||||
|
this.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
clearInterval(interval)
|
|
||||||
this.release()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {unsub: done}
|
return {unsub: done}
|
||||||
@ -86,7 +72,7 @@ class Channel {
|
|||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
const result = []
|
const result = []
|
||||||
|
|
||||||
const sub = this.sub(
|
const sub = await this.sub(
|
||||||
filter,
|
filter,
|
||||||
e => result.push(e),
|
e => result.push(e),
|
||||||
r => {
|
r => {
|
||||||
@ -122,39 +108,25 @@ const getChannel = async () => {
|
|||||||
const req = async (...args) => (await getChannel()).all(...args)
|
const req = async (...args) => (await getChannel()).all(...args)
|
||||||
const sub = async (...args) => (await getChannel()).sub(...args)
|
const sub = async (...args) => (await getChannel()).sub(...args)
|
||||||
|
|
||||||
const getPubkey = () => {
|
|
||||||
return pool._pubkey || getPublicKey(pool._privkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getRelays = () => {
|
const getRelays = () => {
|
||||||
return Object.keys(pool.relays)
|
return get(db.connections)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addRelay = url => {
|
const addRelay = url => {
|
||||||
pool.addRelay(url)
|
agent.pool.connect(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeRelay = url => {
|
const removeRelay = async url => {
|
||||||
pool.removeRelay(url)
|
const relay = await agent.pool.connect(url)
|
||||||
}
|
|
||||||
|
|
||||||
const setPrivateKey = privkey => {
|
relay.close()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishEvent = event => {
|
const publishEvent = event => {
|
||||||
pool.publish(event)
|
const relays = getRelays()
|
||||||
|
|
||||||
|
event = agent.keys.sign(event)
|
||||||
|
agent.publish(relays, event)
|
||||||
db.events.process(event)
|
db.events.process(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,6 +212,6 @@ const syncNetwork = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getPubkey, getRelays, addRelay, removeRelay, setPrivateKey, setPublicKey,
|
getRelays, addRelay, removeRelay, publishEvent, loadEvents, listenForEvents,
|
||||||
publishEvent, loadEvents, listenForEvents, syncNetwork, loadPeople,
|
syncNetwork, loadPeople,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user