aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2022-12-27 22:51:33 -0800
committerAdam Soltys <[email protected]>2022-12-27 22:51:33 -0800
commit2d2c991645f3b0ac7fb3ad88afdad0bb67c28b74 (patch)
treececb95f1c16ba3f1cf020bedc945706032df9d64
parenta487b5a2b80d270771f9932c17d2b6babf0fd49a (diff)
downloadcoinos-server-2d2c991645f3b0ac7fb3ad88afdad0bb67c28b74.tar.gz
coinos-server-2d2c991645f3b0ac7fb3ad88afdad0bb67c28b74.zip
follower endpoints
-rw-r--r--lib/nostr.js59
-rw-r--r--lib/utils.js21
-rw-r--r--lib/whitelist.js2
-rw-r--r--routes/events.js32
-rw-r--r--routes/users.js130
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);
+ }
+});