Default profile pic, more logging, a bit of speedup with memory cache of profiles
This commit is contained in:
parent
c9632e6f93
commit
42062a6fc4
|
@ -23,6 +23,7 @@ Client(ssbSecret, ssbConfig, async (err, server) => {
|
||||||
context.profile = await queries.getProfile(server, whoami.id);
|
context.profile = await queries.getProfile(server, whoami.id);
|
||||||
|
|
||||||
ssbServer = server;
|
ssbServer = server;
|
||||||
|
console.log("SSB Client ready");
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
@ -41,6 +42,12 @@ app.use((_req, res, next) => {
|
||||||
|
|
||||||
return imageHash && `/blob/${encodeURIComponent(imageHash)}`;
|
return imageHash && `/blob/${encodeURIComponent(imageHash)}`;
|
||||||
};
|
};
|
||||||
|
res.locals.profileImageUrl = (profile) => {
|
||||||
|
if (profile.image) {
|
||||||
|
return res.locals.imageUrl(profile.image);
|
||||||
|
}
|
||||||
|
return "/images/no-avatar.png";
|
||||||
|
};
|
||||||
res.locals.context = context;
|
res.locals.context = context;
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -73,11 +80,10 @@ router.get("/profile/:id", async (req, res) => {
|
||||||
return res.redirect("/");
|
return res.redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
const profile = await queries.getProfile(ssbServer, id);
|
const [profile, posts, friends] = await Promise.all([
|
||||||
|
queries.getProfile(ssbServer, id),
|
||||||
const [posts, friends] = await Promise.all([
|
queries.getPosts(ssbServer, { id }),
|
||||||
queries.getPosts(ssbServer, profile),
|
queries.getFriends(ssbServer, { id }),
|
||||||
queries.getFriends(ssbServer, profile),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
res.render("profile", { profile, posts, friends });
|
res.render("profile", { profile, posts, friends });
|
||||||
|
@ -139,10 +145,12 @@ router.post("/about", async (req, res) => {
|
||||||
res.redirect("/");
|
res.redirect("/");
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/debug", async (_req, res) => {
|
router.get("/debug", async (req, res) => {
|
||||||
const entries = await queries.getAllEntries(ssbServer);
|
const query = req.query || {};
|
||||||
|
|
||||||
res.render("debug", { entries });
|
const entries = await queries.getAllEntries(ssbServer, query);
|
||||||
|
|
||||||
|
res.render("debug", { entries, query });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/search", async (req, res) => {
|
router.get("/search", async (req, res) => {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const { promisify } = require("./utils");
|
const { promisify } = require("./utils");
|
||||||
const pull = require("pull-stream");
|
const pull = require("pull-stream");
|
||||||
const cat = require("pull-cat");
|
const cat = require("pull-cat");
|
||||||
|
const debug = require("debug")("queries");
|
||||||
|
const paramap = require("pull-paramap");
|
||||||
|
|
||||||
const latestOwnerValue = (ssbServer) => ({ key, dest }, cb) => {
|
const latestOwnerValue = (ssbServer) => ({ key, dest }, cb) => {
|
||||||
let value = null;
|
let value = null;
|
||||||
|
@ -43,57 +45,24 @@ const latestOwnerValue = (ssbServer) => ({ key, dest }, cb) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapProfiles = (ssbServer) => (data, callback) => {
|
const mapProfiles = (ssbServer) => (data, callback) => {
|
||||||
let promises = [];
|
const authorPromise = getProfile(ssbServer, data.value.author);
|
||||||
|
const contactPromise =
|
||||||
|
data.value.content.type == "contact" &&
|
||||||
|
getProfile(ssbServer, data.value.content.contact);
|
||||||
|
|
||||||
promises.push(
|
return Promise.all([authorPromise, contactPromise])
|
||||||
promisify(latestOwnerValue(ssbServer), {
|
.then(([author, contact]) => {
|
||||||
key: "name",
|
data.value.authorProfile = author;
|
||||||
dest: data.value.author,
|
if (contact) {
|
||||||
})
|
data.value.content.contactProfile = contact;
|
||||||
);
|
|
||||||
promises.push(
|
|
||||||
promisify(latestOwnerValue(ssbServer), {
|
|
||||||
key: "image",
|
|
||||||
dest: data.value.author,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data.value.content.type == "contact") {
|
|
||||||
promises.push(
|
|
||||||
promisify(latestOwnerValue(ssbServer), {
|
|
||||||
key: "name",
|
|
||||||
dest: data.value.content.contact,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
promises.push(
|
|
||||||
promisify(latestOwnerValue(ssbServer), {
|
|
||||||
key: "image",
|
|
||||||
dest: data.value.content.contact,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(promises)
|
|
||||||
.then(([authorName, authorImage, contactName, contactImage]) => {
|
|
||||||
data.value.authorProfile = {
|
|
||||||
id: data.value.author,
|
|
||||||
name: authorName,
|
|
||||||
image: authorImage,
|
|
||||||
};
|
|
||||||
if (contactName) {
|
|
||||||
data.value.content.contactProfile = {
|
|
||||||
id: data.value.content.contact,
|
|
||||||
name: contactName,
|
|
||||||
image: contactImage,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, data);
|
callback(null, data);
|
||||||
})
|
})
|
||||||
.catch((err) => callback(err, null));
|
.catch((err) => callback(err, null));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPosts = (ssbServer, profile) =>
|
const getPosts = (ssbServer, profile) =>
|
||||||
|
debug("Fetching posts") ||
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
pull(
|
pull(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -130,8 +99,9 @@ const getPosts = (ssbServer, profile) =>
|
||||||
limit: 100,
|
limit: 100,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
pull.asyncMap(mapProfiles(ssbServer)),
|
paramap(mapProfiles(ssbServer)),
|
||||||
pull.collect((err, msgs) => {
|
pull.collect((err, msgs) => {
|
||||||
|
debug("Done fetching posts");
|
||||||
const entries = msgs.map((x) => x.value);
|
const entries = msgs.map((x) => x.value);
|
||||||
|
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
@ -141,6 +111,7 @@ const getPosts = (ssbServer, profile) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchPeople = (ssbServer, search) =>
|
const searchPeople = (ssbServer, search) =>
|
||||||
|
debug("Searching people") ||
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
pull(
|
pull(
|
||||||
ssbServer.query.read({
|
ssbServer.query.read({
|
||||||
|
@ -166,6 +137,7 @@ const searchPeople = (ssbServer, search) =>
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
pull.collect((err, msgs) => {
|
pull.collect((err, msgs) => {
|
||||||
|
debug("Done searching people");
|
||||||
const entries = msgs.map((x) => x.value);
|
const entries = msgs.map((x) => x.value);
|
||||||
|
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
@ -175,6 +147,7 @@ const searchPeople = (ssbServer, search) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
const getFriends = (ssbServer, profile) =>
|
const getFriends = (ssbServer, profile) =>
|
||||||
|
debug("Fetching friends") ||
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
pull(
|
pull(
|
||||||
ssbServer.query.read({
|
ssbServer.query.read({
|
||||||
|
@ -191,10 +164,11 @@ const getFriends = (ssbServer, profile) =>
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
limit: 500,
|
limit: 20,
|
||||||
}),
|
}),
|
||||||
pull.asyncMap(mapProfiles(ssbServer)),
|
paramap(mapProfiles(ssbServer)),
|
||||||
pull.collect((err, msgs) => {
|
pull.collect((err, msgs) => {
|
||||||
|
debug("Done fetching friends");
|
||||||
const entries = msgs.map((x) => x.value);
|
const entries = msgs.map((x) => x.value);
|
||||||
|
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
@ -203,14 +177,25 @@ const getFriends = (ssbServer, profile) =>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const getAllEntries = (ssbServer) =>
|
const getAllEntries = (ssbServer, query) =>
|
||||||
|
debug("Fetching All Entries") ||
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
|
let queries = [];
|
||||||
|
if (query.author) {
|
||||||
|
queries.push({ $filter: { value: { author: query.author } } });
|
||||||
|
}
|
||||||
|
if (query.type) {
|
||||||
|
queries.push({ $filter: { value: { content: { type: query.type } } } });
|
||||||
|
}
|
||||||
|
|
||||||
pull(
|
pull(
|
||||||
ssbServer.query.read({
|
ssbServer.query.read({
|
||||||
reverse: true,
|
reverse: true,
|
||||||
limit: 500,
|
limit: 500,
|
||||||
|
query: queries,
|
||||||
}),
|
}),
|
||||||
pull.collect((err, msgs) => {
|
pull.collect((err, msgs) => {
|
||||||
|
debug("Done fetching all entries");
|
||||||
const entries = msgs.map((x) => x.value);
|
const entries = msgs.map((x) => x.value);
|
||||||
|
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
@ -219,15 +204,32 @@ const getAllEntries = (ssbServer) =>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let profileCache = {};
|
||||||
const getProfile = async (ssbServer, id) => {
|
const getProfile = async (ssbServer, id) => {
|
||||||
let [name, image] = await Promise.all([
|
if (profileCache[id]) return profileCache[id];
|
||||||
promisify(latestOwnerValue(ssbServer), { key: "name", dest: id }),
|
|
||||||
promisify(latestOwnerValue(ssbServer), { key: "image", dest: id }),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return { id, name, image };
|
let getKey = (key) =>
|
||||||
|
promisify(latestOwnerValue(ssbServer), { key, dest: id });
|
||||||
|
|
||||||
|
let [name, image, description] = await Promise.all([
|
||||||
|
getKey("name"),
|
||||||
|
getKey("image"),
|
||||||
|
getKey("description"),
|
||||||
|
]).catch((err) => {
|
||||||
|
console.error("Could not retrieve profile for", id, err);
|
||||||
|
});
|
||||||
|
|
||||||
|
let profile = { id, name, image, description };
|
||||||
|
profileCache[id] = profile;
|
||||||
|
|
||||||
|
return profile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
debug("Clearing profile cache");
|
||||||
|
profileCache = {};
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mapProfiles,
|
mapProfiles,
|
||||||
getPosts,
|
getPosts,
|
||||||
|
|
|
@ -4,11 +4,13 @@ const toPull = require("stream-to-pull-stream");
|
||||||
const ident = require("pull-identify-filetype");
|
const ident = require("pull-identify-filetype");
|
||||||
const mime = require("mime-types");
|
const mime = require("mime-types");
|
||||||
const URL = require("url");
|
const URL = require("url");
|
||||||
|
const debug = require("debug")("server-blobs");
|
||||||
|
|
||||||
const serveBlobs = (sbot) => {
|
const serveBlobs = (sbot) => {
|
||||||
return (req, res) => {
|
return (req, res) => {
|
||||||
const parsed = URL.parse(req.url, true);
|
const parsed = URL.parse(req.url, true);
|
||||||
const hash = decodeURIComponent(parsed.pathname.replace("/blob/", ""));
|
const hash = decodeURIComponent(parsed.pathname.replace("/blob/", ""));
|
||||||
|
debug("fetching", hash);
|
||||||
|
|
||||||
waitFor(hash, function (_, has) {
|
waitFor(hash, function (_, has) {
|
||||||
if (!has) return respond(res, 404, "File not found");
|
if (!has) return respond(res, 404, "File not found");
|
||||||
|
@ -22,17 +24,32 @@ const serveBlobs = (sbot) => {
|
||||||
|
|
||||||
// serve
|
// serve
|
||||||
res.setHeader("Content-Security-Policy", BlobCSP());
|
res.setHeader("Content-Security-Policy", BlobCSP());
|
||||||
|
|
||||||
respondSource(res, sbot.blobs.get(hash), false);
|
respondSource(res, sbot.blobs.get(hash), false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function waitFor(hash, cb) {
|
function waitFor(hash, cb) {
|
||||||
|
let finished = false;
|
||||||
|
let wrappedCb = (err, result) => {
|
||||||
|
if (finished) return;
|
||||||
|
finished = true;
|
||||||
|
cb(err, result);
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
debug("timeout for", hash);
|
||||||
|
wrappedCb(null, false);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
sbot.blobs.has(hash, function (err, has) {
|
sbot.blobs.has(hash, function (err, has) {
|
||||||
if (err) return cb(err);
|
if (err) return wrappedCb(err);
|
||||||
|
debug("has is ", has, "for", hash);
|
||||||
if (has) {
|
if (has) {
|
||||||
cb(null, has);
|
wrappedCb(null, has);
|
||||||
} else {
|
} else {
|
||||||
sbot.blobs.want(hash, cb);
|
debug("calling want for", hash);
|
||||||
|
sbot.blobs.want(hash, wrappedCb);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,11 @@ module.exports.promisify = (method, options = null) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.asyncRouter = (app) => {
|
module.exports.asyncRouter = (app) => {
|
||||||
let wrapper = (fn) => async (req, res, next) => {
|
const debug = require("debug")("router");
|
||||||
|
|
||||||
|
let wrapper = (debugMsg, fn) => async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
|
debug(debugMsg);
|
||||||
await fn(req, res);
|
await fn(req, res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next(e);
|
next(e);
|
||||||
|
@ -22,10 +25,11 @@ module.exports.asyncRouter = (app) => {
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
get: (path, fn) => {
|
get: (path, fn) => {
|
||||||
app.get(path, wrapper(fn));
|
app.get(path, wrapper(`GET ${path}`, fn));
|
||||||
},
|
},
|
||||||
post: (path, fn) => {
|
post: (path, fn) => {
|
||||||
app.post(path, wrapper(fn));
|
debug(`POST ${path}`);
|
||||||
|
app.post(path, wrapper(`POST ${path}`, fn));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,15 +4,16 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "SSB_PORT=8009 EXPRESS_PORT=3000 electron .",
|
||||||
"start:user-2": "SSB_PORT=8009 EXPRESS_PORT=3001 CONFIG_FOLDER=social-user2 electron .",
|
"start:user-2": "SSB_PORT=8010 EXPRESS_PORT=3001 CONFIG_FOLDER=social-user2 electron .",
|
||||||
"start:user-3": "SSB_PORT=8010 EXPRESS_PORT=3002 CONFIG_FOLDER=social-user3 electron .",
|
"start:user-3": "SSB_PORT=8011 EXPRESS_PORT=3002 CONFIG_FOLDER=social-user3 electron .",
|
||||||
"clear": "rm -rf ~/.social; rm -rf ~/.social-user2; rm -rf ~/.social-user3"
|
"clear": "rm -rf ~/.social; rm -rf ~/.social-user2; rm -rf ~/.social-user3"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.3.1",
|
"chokidar": "^3.3.1",
|
||||||
|
"debug": "^4.1.1",
|
||||||
"ejs": "^3.0.2",
|
"ejs": "^3.0.2",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"pull-stream": "^3.6.14",
|
"pull-stream": "^3.6.14",
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
|
@ -2,6 +2,7 @@ body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.3em;
|
line-height: 1.3em;
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
header {
|
header {
|
||||||
padding: 0 30px;
|
padding: 0 30px;
|
||||||
|
@ -55,15 +56,18 @@ main {
|
||||||
|
|
||||||
.columns {
|
.columns {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-content: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
line-height: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wall {
|
.wall {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
|
@ -90,3 +94,11 @@ h1 {
|
||||||
font-family: Georgia, Times, "Times New Roman", serif;
|
font-family: Georgia, Times, "Times New Roman", serif;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.about {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-pic {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
<%- include('_header') %>
|
<%- include('_header') %>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<form action="/debug" method="GET">
|
||||||
|
Author: <input type="text" name="author" value="<%= query.author %>">
|
||||||
|
Type: <input type="text" name="type" value="<%= query.type %>">
|
||||||
|
<input type="submit" value="Filter">
|
||||||
|
</form>
|
||||||
|
</p>
|
||||||
|
|
||||||
<% entries.map(entry => { %>
|
<% entries.map(entry => { %>
|
||||||
<code style="width: 80%; word-wrap: break-word;"><%= JSON.stringify(entry) %></code>
|
<code style="width: 80%; word-wrap: break-word;"><%= JSON.stringify(entry) %></code>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="about">
|
<div class="about">
|
||||||
<% if (profile.image) { %>
|
<img class="profile-pic" src="<%= profileImageUrl(profile) %>" />
|
||||||
<img src="<%= imageUrl(profile.image) %>" />
|
<h1><%= profile.name %></h1>
|
||||||
<% } %>
|
|
||||||
<h1><%= profile.name %>'s Profile</h1>
|
<%= profile.description %>
|
||||||
|
|
||||||
<h2>Friends</h2>
|
<h2>Friends</h2>
|
||||||
|
|
||||||
|
@ -31,7 +31,9 @@
|
||||||
|
|
||||||
<% posts.map(entry => { %>
|
<% posts.map(entry => { %>
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<img src="<%= imageUrl(entry.authorProfile.image) %>" class="post-profile-pic" />
|
<div>
|
||||||
|
<img src="<%= profileImageUrl(entry.authorProfile) %>" class="post-profile-pic" />
|
||||||
|
</div>
|
||||||
<div class="post-content">
|
<div class="post-content">
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
<%= entry.authorProfile.name %>
|
<%= entry.authorProfile.name %>
|
||||||
|
|
Loading…
Reference in New Issue