diff options
| author | Adam Soltys <[email protected]> | 2022-12-27 22:51:33 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2022-12-27 22:51:33 -0800 |
| commit | 2d2c991645f3b0ac7fb3ad88afdad0bb67c28b74 (patch) | |
| tree | cecb95f1c16ba3f1cf020bedc945706032df9d64 | |
| parent | a487b5a2b80d270771f9932c17d2b6babf0fd49a (diff) | |
| download | coinos-server-2d2c991645f3b0ac7fb3ad88afdad0bb67c28b74.tar.gz coinos-server-2d2c991645f3b0ac7fb3ad88afdad0bb67c28b74.zip | |
follower endpoints
| -rw-r--r-- | lib/nostr.js | 59 | ||||
| -rw-r--r-- | lib/utils.js | 21 | ||||
| -rw-r--r-- | lib/whitelist.js | 2 | ||||
| -rw-r--r-- | routes/events.js | 32 | ||||
| -rw-r--r-- | routes/users.js | 130 |
5 files changed, 205 insertions, 39 deletions
diff --git a/lib/nostr.js b/lib/nostr.js index 477d2b12..257bca9e 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -5,6 +5,7 @@ import store from "$lib/store"; import redis from "$lib/redis"; const { relays } = await got("https://nostr.watch/relays.json").json(); + export const pool = RelayPool(relays); pool.on("open", relay => { @@ -12,11 +13,18 @@ pool.on("open", relay => { relay.subscribe("live", { limit: 1 }); }); -let coinos; +export let coinos; let seen = []; let timeout; pool.on("event", async (relay, sub, ev) => { try { + let parsed; + try { + parsed = JSON.parse(ev.content); + } catch (e) { + parsed = ev.content; + } + if (sub === "live") { let { pubkey } = ev; @@ -28,26 +36,59 @@ pool.on("event", async (relay, sub, ev) => { seen.length > 1000 && seen.shift(); if (coinos && ev.kind < 5) { - let parsed; - try { - parsed = JSON.parse(ev.content); - } catch (e) { - parsed = ev.content; - } - coinos.send(["EVENT", ev]); - ev.user = JSON.parse(await redis.get(`user:${pubkey}`)) || { + let newuser = { username: pubkey.substr(0, 6), pubkey, anon: true }; + ev.user = JSON.parse(await redis.get(`user:${pubkey}`)) || newuser; + + if (!ev.user.initialized) { + relay.subscribe("zero", { + limit: 1, + kinds: [0], + authors: [pubkey] + }); + } + if (ev.kind === 1) { broadcast("event", ev); } } + } else if (sub === "zero") { + let { pubkey } = ev; + let newuser = { + username: pubkey.substr(0, 6), + pubkey, + anon: true + }; + + let user = JSON.parse(await redis.get(`user:${pubkey}`)) || newuser; + if (parsed.name && user.username === pubkey.substr(0, 6)) + user.username = parsed.name; + + delete parsed.name; + + user = { + ...user, + ...parsed, + initialized: true + }; + + await redis.set(`user:${pubkey}`, JSON.stringify(user)); + } else if (sub.includes("follows")) { + let pubkey = sub.split(":")[0]; + await redis.set(`${pubkey}:follows`, JSON.stringify(ev.tags)); + for (let f of ev.tags) { + await redis.sAdd(`${f[1]}:followers`, pubkey); + } + store.fetching[pubkey] = false; } else { + if (!relay.url.includes("coinos")) return; + let pubkey = sub; clearTimeout(store.timeouts[pubkey]); diff --git a/lib/utils.js b/lib/utils.js index 46267dbd..2854fefc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,3 +1,4 @@ +import store from "$lib/store"; import config from "$config"; import db from "$db"; import sequelize from "@sequelize/core"; @@ -37,6 +38,26 @@ export const getUser = async (username, transaction) => { user.haspin = !!user.pin; + let { pubkey } = user; + + if (pubkey) { + store.fetching[pubkey] = true; + store.timeouts[pubkey] = setTimeout( + () => (store.fetching[pubkey] = false), + 500 + ); + + coinos.subscribe(`${pubkey}:follows`, { + limit: 1, + kinds: [3], + authors: [pubkey] + }); + + await wait(() => !store.fetching[pubkey], 100, 10); + user.follows = JSON.parse(await redis.get(`${pubkey}:follows`)); + user.followers = await redis.sMembers(`${pubkey}:followers`); + } + return user; }; diff --git a/lib/whitelist.js b/lib/whitelist.js index 5a9c0ba3..e78bc8e5 100644 --- a/lib/whitelist.js +++ b/lib/whitelist.js @@ -10,6 +10,8 @@ export default [ "currency", "email", "fiat", + "follows", + "followers", "haspin", "id", "index", diff --git a/routes/events.js b/routes/events.js index 07062140..4dcc44f0 100644 --- a/routes/events.js +++ b/routes/events.js @@ -5,33 +5,41 @@ import { pool } from "$lib/nostr"; import store from "$lib/store"; import { wait } from "$lib/utils"; -app.get("/:pubkey/events", async (req, res) => { +app.get("/nostr/:pubkey", async (req, res) => { try { let { pubkey } = req.params; + store.fetching[pubkey] = true; store.timeouts[pubkey] = setTimeout( () => (store.fetching[pubkey] = false), 500 ); - let user = await db.User.findOne({ - where: { pubkey } - }); + let user = JSON.parse(await redis.get(`user:${pubkey}`)); - if (!user) user = { username: pubkey.substr(0,6), pubkey, anon: true } - await redis.set(`user:${pubkey}`, JSON.stringify(user)); + if (!user) { + user = await db.User.findOne({ + where: { pubkey } + }); + } + + if (!user) user = { username: pubkey.substr(0, 6), pubkey, anon: true }; + + let { since } = user; pool.subscribe(pubkey, { - limit: 500, + since, kinds: [1], authors: [pubkey] }); - let events = []; - await wait(() => !store.fetching[pubkey], 10, 10); - for (let id of await redis.sMembers(pubkey)) { - events.push(JSON.parse(await redis.get(`ev:${id}`))); - } + user.since = Math.round(Date.now() / 1000); + await redis.set(`user:${pubkey}`, JSON.stringify(user)); + + await wait(() => !store.fetching[pubkey], 100, 100); + let events = ( + await redis.mGet((await redis.sMembers(pubkey)).map(k => "ev:" + k)) + ).map(JSON.parse); res.send(events); } catch (e) { diff --git a/routes/users.js b/routes/users.js index 70186145..4c6305b5 100644 --- a/routes/users.js +++ b/routes/users.js @@ -1,8 +1,9 @@ import app from "$app"; +import redis from "$lib/redis"; import config from "$config"; import store from "$lib/store"; import { auth, optionalAuth } from "$lib/passport"; -import { getUser } from "$lib/utils"; +import { getUser, wait } from "$lib/utils"; import axios from "axios"; import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; @@ -21,6 +22,7 @@ import lq from "$lib/liquid"; import { emit } from "$lib/sockets"; import register from "$lib/register"; import { requirePin } from "$lib/utils"; +import { coinos } from "$lib/nostr"; const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {}); @@ -44,35 +46,84 @@ app.get("/me", auth, async (req, res) => { } ] }); + + let { pubkey } = user; + + if (pubkey) { + store.fetching[pubkey] = true; + store.timeouts[pubkey] = setTimeout( + () => (store.fetching[pubkey] = false), + 500 + ); + + coinos.subscribe(`${pubkey}:follows`, { + limit: 1, + kinds: [3], + authors: [pubkey] + }); + + await wait(() => !store.fetching[pubkey], 100, 10); + user.follows = JSON.parse(await redis.get(`${pubkey}:follows`)); + user.followers = await redis.sMembers(`${pubkey}:followers`); + } + user.accounts = await req.user.getAccounts(); user.payments = payments; user.haspin = !!user.pin; + res.send(pick(user, ...whitelist)); } catch (e) { + console.log("problem fetching user", e); res.code(500).send(e.message); } }); app.get("/users/:username", async (req, res) => { - const { username } = req.params; + try { + const { username } = req.params; - let user = await db.User.findOne({ - attributes: [ - "username", - "banner", - "profile", - "address", - "currency", - "pubkey" - ], - where: Sequelize.where( - Sequelize.fn("lower", Sequelize.col("username")), - username.toLowerCase() - ) - }); + let user = await db.User.findOne({ + attributes: [ + "username", + "banner", + "profile", + "address", + "currency", + "pubkey" + ], + where: Sequelize.where( + Sequelize.fn("lower", Sequelize.col("username")), + username.toLowerCase() + ) + }); - if (user) res.send(user); - else res.code(500).send("User not found"); + if (!user) return res.code(500).send("User not found"); + + user = user.get({ plain: true }); + let { pubkey } = user; + + if (pubkey) { + store.fetching[pubkey] = true; + store.timeouts[pubkey] = setTimeout( + () => (store.fetching[pubkey] = false), + 500 + ); + + coinos.subscribe(`${pubkey}:follows`, { + limit: 1, + kinds: [3], + authors: [pubkey] + }); + + await wait(() => !store.fetching[pubkey], 100, 10); + user.follows = JSON.parse(await redis.get(`${pubkey}:follows`)); + user.followers = await redis.sMembers(`${pubkey}:followers`); + } + + res.send(user); + } catch (e) { + res.code(500).send(e.message); + } }); app.post("/register", async (req, res) => { @@ -603,3 +654,46 @@ app.get("/contacts", auth, async function(req, res) { res.code(500).send(e.message); } }); + +app.post("/checkPassword", auth, async function(req, res) { + let { user } = req; + let result = await bcrypt.compare(req.body.password, user.password); + if (!result) return res.code(500).send("Invalid password"); + res.send(result); +}); + +app.get("/:pubkey/follows", async (req, res) => { + try { + let { pubkey } = req.params; + let follows = JSON.parse(await redis.get(`${pubkey}:follows`)) || []; + + if (follows.length) { + follows = await db.User.findAll({ + where: { pubkey: follows.map(t => t[1]) } + }); + + res.send(follows); + } + } catch (e) { + console.log(e); + res.code(500).send(e.message); + } +}); + +app.get("/:pubkey/followers", async (req, res) => { + try { + let { pubkey } = req.params; + let followers = await redis.sMembers(`${pubkey}:followers`); + + if (followers.length) { + followers = await db.User.findAll({ + where: { pubkey: followers } + }); + + res.send(followers); + } + } catch (e) { + console.log(e); + res.code(500).send(e.message); + } +}); |
