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);
|
||||
|
||||
ssbServer = server;
|
||||
console.log("SSB Client ready");
|
||||
});
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
@ -41,6 +42,12 @@ app.use((_req, res, next) => {
|
|||
|
||||
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;
|
||||
next();
|
||||
});
|
||||
|
@ -73,11 +80,10 @@ router.get("/profile/:id", async (req, res) => {
|
|||
return res.redirect("/");
|
||||
}
|
||||
|
||||
const profile = await queries.getProfile(ssbServer, id);
|
||||
|
||||
const [posts, friends] = await Promise.all([
|
||||
queries.getPosts(ssbServer, profile),
|
||||
queries.getFriends(ssbServer, profile),
|
||||
const [profile, posts, friends] = await Promise.all([
|
||||
queries.getProfile(ssbServer, id),
|
||||
queries.getPosts(ssbServer, { id }),
|
||||
queries.getFriends(ssbServer, { id }),
|
||||
]);
|
||||
|
||||
res.render("profile", { profile, posts, friends });
|
||||
|
@ -139,10 +145,12 @@ router.post("/about", async (req, res) => {
|
|||
res.redirect("/");
|
||||
});
|
||||
|
||||
router.get("/debug", async (_req, res) => {
|
||||
const entries = await queries.getAllEntries(ssbServer);
|
||||
router.get("/debug", async (req, res) => {
|
||||
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) => {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const { promisify } = require("./utils");
|
||||
const pull = require("pull-stream");
|
||||
const cat = require("pull-cat");
|
||||
const debug = require("debug")("queries");
|
||||
const paramap = require("pull-paramap");
|
||||
|
||||
const latestOwnerValue = (ssbServer) => ({ key, dest }, cb) => {
|
||||
let value = null;
|
||||
|
@ -43,57 +45,24 @@ const latestOwnerValue = (ssbServer) => ({ key, dest }, cb) => {
|
|||
};
|
||||
|
||||
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(
|
||||
promisify(latestOwnerValue(ssbServer), {
|
||||
key: "name",
|
||||
dest: data.value.author,
|
||||
})
|
||||
);
|
||||
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,
|
||||
};
|
||||
return Promise.all([authorPromise, contactPromise])
|
||||
.then(([author, contact]) => {
|
||||
data.value.authorProfile = author;
|
||||
if (contact) {
|
||||
data.value.content.contactProfile = contact;
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
})
|
||||
.catch((err) => callback(err, null));
|
||||
};
|
||||
|
||||
const getPosts = (ssbServer, profile) =>
|
||||
debug("Fetching posts") ||
|
||||
new Promise((resolve, reject) => {
|
||||
pull(
|
||||
// @ts-ignore
|
||||
|
@ -130,8 +99,9 @@ const getPosts = (ssbServer, profile) =>
|
|||
limit: 100,
|
||||
}),
|
||||
]),
|
||||
pull.asyncMap(mapProfiles(ssbServer)),
|
||||
paramap(mapProfiles(ssbServer)),
|
||||
pull.collect((err, msgs) => {
|
||||
debug("Done fetching posts");
|
||||
const entries = msgs.map((x) => x.value);
|
||||
|
||||
if (err) return reject(err);
|
||||
|
@ -141,6 +111,7 @@ const getPosts = (ssbServer, profile) =>
|
|||
});
|
||||
|
||||
const searchPeople = (ssbServer, search) =>
|
||||
debug("Searching people") ||
|
||||
new Promise((resolve, reject) => {
|
||||
pull(
|
||||
ssbServer.query.read({
|
||||
|
@ -166,6 +137,7 @@ const searchPeople = (ssbServer, search) =>
|
|||
);
|
||||
}),
|
||||
pull.collect((err, msgs) => {
|
||||
debug("Done searching people");
|
||||
const entries = msgs.map((x) => x.value);
|
||||
|
||||
if (err) return reject(err);
|
||||
|
@ -175,6 +147,7 @@ const searchPeople = (ssbServer, search) =>
|
|||
});
|
||||
|
||||
const getFriends = (ssbServer, profile) =>
|
||||
debug("Fetching friends") ||
|
||||
new Promise((resolve, reject) => {
|
||||
pull(
|
||||
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) => {
|
||||
debug("Done fetching friends");
|
||||
const entries = msgs.map((x) => x.value);
|
||||
|
||||
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) => {
|
||||
let queries = [];
|
||||
if (query.author) {
|
||||
queries.push({ $filter: { value: { author: query.author } } });
|
||||
}
|
||||
if (query.type) {
|
||||
queries.push({ $filter: { value: { content: { type: query.type } } } });
|
||||
}
|
||||
|
||||
pull(
|
||||
ssbServer.query.read({
|
||||
reverse: true,
|
||||
limit: 500,
|
||||
query: queries,
|
||||
}),
|
||||
pull.collect((err, msgs) => {
|
||||
debug("Done fetching all entries");
|
||||
const entries = msgs.map((x) => x.value);
|
||||
|
||||
if (err) return reject(err);
|
||||
|
@ -219,15 +204,32 @@ const getAllEntries = (ssbServer) =>
|
|||
);
|
||||
});
|
||||
|
||||
let profileCache = {};
|
||||
const getProfile = async (ssbServer, id) => {
|
||||
let [name, image] = await Promise.all([
|
||||
promisify(latestOwnerValue(ssbServer), { key: "name", dest: id }),
|
||||
promisify(latestOwnerValue(ssbServer), { key: "image", dest: id }),
|
||||
]);
|
||||
if (profileCache[id]) return profileCache[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 = {
|
||||
mapProfiles,
|
||||
getPosts,
|
||||
|
|
|
@ -4,11 +4,13 @@ const toPull = require("stream-to-pull-stream");
|
|||
const ident = require("pull-identify-filetype");
|
||||
const mime = require("mime-types");
|
||||
const URL = require("url");
|
||||
const debug = require("debug")("server-blobs");
|
||||
|
||||
const serveBlobs = (sbot) => {
|
||||
return (req, res) => {
|
||||
const parsed = URL.parse(req.url, true);
|
||||
const hash = decodeURIComponent(parsed.pathname.replace("/blob/", ""));
|
||||
debug("fetching", hash);
|
||||
|
||||
waitFor(hash, function (_, has) {
|
||||
if (!has) return respond(res, 404, "File not found");
|
||||
|
@ -22,17 +24,32 @@ const serveBlobs = (sbot) => {
|
|||
|
||||
// serve
|
||||
res.setHeader("Content-Security-Policy", BlobCSP());
|
||||
|
||||
respondSource(res, sbot.blobs.get(hash), false);
|
||||
});
|
||||
};
|
||||
|
||||
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) {
|
||||
if (err) return cb(err);
|
||||
if (err) return wrappedCb(err);
|
||||
debug("has is ", has, "for", hash);
|
||||
if (has) {
|
||||
cb(null, has);
|
||||
wrappedCb(null, has);
|
||||
} 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) => {
|
||||
let wrapper = (fn) => async (req, res, next) => {
|
||||
const debug = require("debug")("router");
|
||||
|
||||
let wrapper = (debugMsg, fn) => async (req, res, next) => {
|
||||
try {
|
||||
debug(debugMsg);
|
||||
await fn(req, res);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
|
@ -22,10 +25,11 @@ module.exports.asyncRouter = (app) => {
|
|||
};
|
||||
return {
|
||||
get: (path, fn) => {
|
||||
app.get(path, wrapper(fn));
|
||||
app.get(path, wrapper(`GET ${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": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"start:user-2": "SSB_PORT=8009 EXPRESS_PORT=3001 CONFIG_FOLDER=social-user2 electron .",
|
||||
"start:user-3": "SSB_PORT=8010 EXPRESS_PORT=3002 CONFIG_FOLDER=social-user3 electron .",
|
||||
"start": "SSB_PORT=8009 EXPRESS_PORT=3000 electron .",
|
||||
"start:user-2": "SSB_PORT=8010 EXPRESS_PORT=3001 CONFIG_FOLDER=social-user2 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"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.3.1",
|
||||
"debug": "^4.1.1",
|
||||
"ejs": "^3.0.2",
|
||||
"express": "^4.17.1",
|
||||
"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;
|
||||
margin: 0;
|
||||
line-height: 1.3em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
header {
|
||||
padding: 0 30px;
|
||||
|
@ -55,15 +56,18 @@ main {
|
|||
|
||||
.columns {
|
||||
display: flex;
|
||||
align-content: stretch;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
.wall {
|
||||
padding: 20px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.post {
|
||||
|
@ -90,3 +94,11 @@ h1 {
|
|||
font-family: Georgia, Times, "Times New Roman", serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.about {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.profile-pic {
|
||||
width: 300px;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
<%- 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 => { %>
|
||||
<code style="width: 80%; word-wrap: break-word;"><%= JSON.stringify(entry) %></code>
|
||||
<hr />
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
<div class="columns">
|
||||
<div class="about">
|
||||
<% if (profile.image) { %>
|
||||
<img src="<%= imageUrl(profile.image) %>" />
|
||||
<% } %>
|
||||
<h1><%= profile.name %>'s Profile</h1>
|
||||
<img class="profile-pic" src="<%= profileImageUrl(profile) %>" />
|
||||
<h1><%= profile.name %></h1>
|
||||
|
||||
<%= profile.description %>
|
||||
|
||||
<h2>Friends</h2>
|
||||
|
||||
|
@ -31,7 +31,9 @@
|
|||
|
||||
<% posts.map(entry => { %>
|
||||
<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="content-header">
|
||||
<%= entry.authorProfile.name %>
|
||||
|
|
Loading…
Reference in New Issue