Add singup process including picture
This commit is contained in:
parent
c8b0de7d77
commit
1b5cadbcf5
|
@ -5,12 +5,20 @@ const bodyParser = require("body-parser");
|
|||
const Client = require("ssb-client");
|
||||
const ssbKeys = require("ssb-keys");
|
||||
const ssbConfig = require("./ssb-config");
|
||||
const { asyncRouter, writeKey } = require("./utils");
|
||||
const {
|
||||
asyncRouter,
|
||||
writeKey,
|
||||
nextIdentityFilename,
|
||||
reconstructKeys,
|
||||
readKey,
|
||||
} = require("./utils");
|
||||
const queries = require("./queries");
|
||||
const serveBlobs = require("./serve-blobs");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const leftpad = require("left-pad"); // I don't believe I'm depending on this
|
||||
const debug = require("debug")("express");
|
||||
const fileUpload = require("express-fileupload");
|
||||
const pull = require("pull-stream");
|
||||
const split = require("split-buffer");
|
||||
|
||||
let ssbServer;
|
||||
let mode = process.env.MODE || "server";
|
||||
|
@ -39,6 +47,7 @@ app.use(bodyParser.urlencoded({ extended: true }));
|
|||
app.set("view engine", "ejs");
|
||||
app.use(express.static("public"));
|
||||
app.use(cookieParser());
|
||||
app.use(fileUpload());
|
||||
app.use(async (req, res, next) => {
|
||||
if (!ssbServer) {
|
||||
setTimeout(() => {
|
||||
|
@ -64,8 +73,7 @@ app.use(async (req, res, next) => {
|
|||
|
||||
const parsedKey = JSON.parse(key);
|
||||
if (!identities.includes(parsedKey.id)) {
|
||||
const filename =
|
||||
"secret_" + leftpad(identities.length - 1, 2, "0") + ".butt";
|
||||
const filename = await nextIdentityFilename(ssbServer);
|
||||
|
||||
writeKey(key, `/identities/${filename}`);
|
||||
ssbServer.identities.refresh();
|
||||
|
@ -78,7 +86,7 @@ app.use(async (req, res, next) => {
|
|||
next(e);
|
||||
}
|
||||
});
|
||||
app.use((req, res, next) => {
|
||||
app.use((_req, res, next) => {
|
||||
res.locals.profileUrl = profileUrl;
|
||||
res.locals.imageUrl = (blob) => {
|
||||
const imageHash = blob && typeof blob == "object" ? blob.link : blob;
|
||||
|
@ -124,21 +132,6 @@ router.get("/login", (_req, res) => {
|
|||
router.post("/login", async (req, res) => {
|
||||
const submittedKey = req.body.ssb_key;
|
||||
|
||||
// From ssb-keys
|
||||
const reconstructKeys = (keyfile) => {
|
||||
var privateKey = keyfile
|
||||
.replace(/\s*\#[^\n]*/g, "")
|
||||
.split("\n")
|
||||
.filter((x) => x)
|
||||
.join("");
|
||||
|
||||
var keys = JSON.parse(privateKey);
|
||||
const hasSigil = (x) => /^(@|%|&)/.test(x);
|
||||
|
||||
if (!hasSigil(keys.id)) keys.id = "@" + keys.public;
|
||||
return keys;
|
||||
};
|
||||
|
||||
try {
|
||||
const decodedKey = reconstructKeys(submittedKey);
|
||||
res.cookie("ssb_key", JSON.stringify(decodedKey));
|
||||
|
@ -158,10 +151,61 @@ router.get("/logout", async (_req, res) => {
|
|||
res.redirect("/");
|
||||
});
|
||||
|
||||
router.get("/signup", (_req, res) => {
|
||||
router.get("/signup", (req, res) => {
|
||||
if (req.context.profile) {
|
||||
return res.redirect("/");
|
||||
}
|
||||
|
||||
res.render("signup");
|
||||
});
|
||||
|
||||
router.post("/signup", async (req, res) => {
|
||||
const name = req.body.name;
|
||||
const picture = req.files && req.files.pic;
|
||||
|
||||
let pictureLink;
|
||||
if (picture) {
|
||||
const maxSize = 5 * 1024 * 1024; // 5 MB
|
||||
if (picture.size > maxSize) throw "Max size exceeded";
|
||||
|
||||
pictureLink = await new Promise((resolve, reject) =>
|
||||
pull(
|
||||
pull.values(split(picture.data, 64 * 1024)),
|
||||
ssbServer.blobs.add((err, result) => {
|
||||
if (err) return reject(err);
|
||||
return resolve(result);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const filename = await nextIdentityFilename(ssbServer);
|
||||
const profileId = await ssbServer.identities.create();
|
||||
const key = readKey(`/identities/${filename}`);
|
||||
if (key.id != profileId)
|
||||
throw "profileId and key.id don't match, probably race condition, bailing out for safety";
|
||||
|
||||
debug("Created new user with id", profileId);
|
||||
|
||||
res.cookie("ssb_key", JSON.stringify(key));
|
||||
key.private = "[removed]";
|
||||
debug("Generated key", key);
|
||||
|
||||
await ssbServer.identities.publishAs({
|
||||
id: profileId,
|
||||
private: false,
|
||||
content: {
|
||||
type: "about",
|
||||
about: profileId,
|
||||
name: name,
|
||||
...(pictureLink ? { image: pictureLink } : {}),
|
||||
},
|
||||
});
|
||||
debug("Published about", { about: profileId, name, image: pictureLink });
|
||||
|
||||
res.redirect("/");
|
||||
});
|
||||
|
||||
router.get("/profile/:id", async (req, res) => {
|
||||
const id = req.params.id;
|
||||
|
||||
|
@ -179,10 +223,14 @@ router.get("/profile/:id", async (req, res) => {
|
|||
});
|
||||
|
||||
router.post("/publish", async (req, res) => {
|
||||
await ssbServer.publish({
|
||||
type: "post",
|
||||
text: req.body.message,
|
||||
root: req.context.profile.id,
|
||||
await ssbServer.identities.publishAs({
|
||||
id: req.context.profile.id,
|
||||
private: false,
|
||||
content: {
|
||||
type: "post",
|
||||
text: req.body.message,
|
||||
root: req.context.profile.id,
|
||||
},
|
||||
});
|
||||
|
||||
res.redirect("/");
|
||||
|
@ -192,9 +240,13 @@ router.post("/publish", async (req, res) => {
|
|||
router.post("/vanish", async (req, res) => {
|
||||
const key = req.body.key;
|
||||
|
||||
await ssbServer.publish({
|
||||
type: "delete",
|
||||
dest: key,
|
||||
await ssbServer.identities.publishAs({
|
||||
id: req.context.profile.id,
|
||||
private: false,
|
||||
content: {
|
||||
type: "delete",
|
||||
dest: key,
|
||||
},
|
||||
});
|
||||
|
||||
res.send("ok");
|
||||
|
@ -205,19 +257,25 @@ router.post("/profile/:id/publish", async (req, res) => {
|
|||
const visibility = req.body.visibility;
|
||||
|
||||
if (visibility == "vanishing") {
|
||||
await ssbServer.private.publish(
|
||||
{
|
||||
await ssbServer.identities.publishAs({
|
||||
id: req.context.profile.id,
|
||||
private: true,
|
||||
content: {
|
||||
type: "post",
|
||||
text: req.body.message,
|
||||
root: id,
|
||||
recps: [id],
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await ssbServer.identities.publishAs({
|
||||
id: req.context.profile.id,
|
||||
private: false,
|
||||
content: {
|
||||
type: "post",
|
||||
text: req.body.message,
|
||||
root: id,
|
||||
},
|
||||
[id]
|
||||
);
|
||||
} else {
|
||||
await ssbServer.publish({
|
||||
type: "post",
|
||||
text: req.body.message,
|
||||
root: id,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -247,10 +305,14 @@ router.post("/about", async (req, res) => {
|
|||
const name = req.body.name;
|
||||
|
||||
if (name != req.context.profile.name) {
|
||||
await ssbServer.publish({
|
||||
type: "about",
|
||||
about: req.context.profile.id,
|
||||
name: name,
|
||||
await ssbServer.identities.publishAs({
|
||||
id: req.context.profile.id,
|
||||
private: false,
|
||||
content: {
|
||||
type: "about",
|
||||
about: req.context.profile.id,
|
||||
name: name,
|
||||
},
|
||||
});
|
||||
req.context.profile.name = name;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Monkeypatched to include the refresh function
|
||||
// 1) Monkeypatched to include the refresh function
|
||||
// 2) Monkeypatched to allow secret messages without published in the recps
|
||||
var leftpad = require("left-pad");
|
||||
var path = require("path");
|
||||
var mkdirp = require("mkdirp");
|
||||
|
@ -94,15 +95,11 @@ exports.init = function (sbot, config) {
|
|||
else if (!content.recps && opts.private)
|
||||
return cb(new Error("opts.private set, but content.recps not set"));
|
||||
else if (!!content.recps && opts.private) {
|
||||
if (!Array.isArray(content.recps) || !~recps.indexOf(id))
|
||||
if (!Array.isArray(content.recps) || !content.recps.length)
|
||||
return cb(
|
||||
new Error(
|
||||
"content.recps must be an array containing publisher id:" +
|
||||
id +
|
||||
" was:" +
|
||||
JSON.stringify(recps) +
|
||||
" indexOf:" +
|
||||
recps.indexOf(id)
|
||||
"content.recps must be an array containing at least one id, was:" +
|
||||
JSON.stringify(recps)
|
||||
)
|
||||
);
|
||||
content = ssbKeys.box(content, recps);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const fs = require("fs");
|
||||
const leftpad = require("left-pad"); // I don't believe I'm depending on this
|
||||
|
||||
module.exports.asyncRouter = (app) => {
|
||||
const debug = require("debug")("router");
|
||||
|
@ -22,13 +23,43 @@ module.exports.asyncRouter = (app) => {
|
|||
};
|
||||
};
|
||||
|
||||
module.exports.writeKey = (key, path) => {
|
||||
const ssbFolder = () => {
|
||||
let homeFolder =
|
||||
process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
|
||||
let ssbFolder = `${homeFolder}/.${process.env.CONFIG_FOLDER || "social"}`;
|
||||
let secretPath = `${ssbFolder}${path}`;
|
||||
return `${homeFolder}/.${process.env.CONFIG_FOLDER || "social"}`;
|
||||
};
|
||||
|
||||
module.exports.writeKey = (key, path) => {
|
||||
let secretPath = `${ssbFolder()}${path}`;
|
||||
|
||||
// Same options ssb-keys use
|
||||
fs.mkdirSync(ssbFolder, { recursive: true });
|
||||
fs.mkdirSync(ssbFolder(), { recursive: true });
|
||||
fs.writeFileSync(secretPath, key, { mode: 0x100, flag: "wx" });
|
||||
};
|
||||
|
||||
module.exports.nextIdentityFilename = async (ssbServer) => {
|
||||
const identities = await ssbServer.identities.list();
|
||||
return "secret_" + leftpad(identities.length - 1, 2, "0") + ".butt";
|
||||
};
|
||||
|
||||
// From ssb-keys
|
||||
module.exports.reconstructKeys = (keyfile) => {
|
||||
var privateKey = keyfile
|
||||
.replace(/\s*\#[^\n]*/g, "")
|
||||
.split("\n")
|
||||
.filter((x) => x)
|
||||
.join("");
|
||||
|
||||
var keys = JSON.parse(privateKey);
|
||||
const hasSigil = (x) => /^(@|%|&)/.test(x);
|
||||
|
||||
if (!hasSigil(keys.id)) keys.id = "@" + keys.public;
|
||||
return keys;
|
||||
};
|
||||
|
||||
module.exports.readKey = (path) => {
|
||||
let secretPath = `${ssbFolder()}${path}`;
|
||||
|
||||
let keyfile = fs.readFileSync(secretPath, "utf8");
|
||||
return module.exports.reconstructKeys(keyfile);
|
||||
};
|
||||
|
|
|
@ -191,6 +191,14 @@
|
|||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"busboy": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz",
|
||||
"integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==",
|
||||
"requires": {
|
||||
"dicer": "0.3.0"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||
|
@ -557,6 +565,14 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"dicer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
|
||||
"integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
|
||||
"requires": {
|
||||
"streamsearch": "0.1.2"
|
||||
}
|
||||
},
|
||||
"discontinuous-range": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
|
||||
|
@ -781,6 +797,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"express-fileupload": {
|
||||
"version": "1.1.7-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.7-alpha.3.tgz",
|
||||
"integrity": "sha512-2YRJQqjgfFcYiMr8inico+UQ0UsxuOUyO9wkWkx+vjsEcUI7c1ae38Nv5NKdGjHqL5+J01P6StT9mjZTI7Qzjg==",
|
||||
"requires": {
|
||||
"busboy": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"extract-zip": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
|
||||
|
@ -9637,6 +9661,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"streamsearch": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
||||
},
|
||||
"string.prototype.trim": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz",
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"debug": "^4.1.1",
|
||||
"ejs": "^3.0.2",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.1.7-alpha.3",
|
||||
"pull-stream": "^3.6.14",
|
||||
"ssb-about": "^2.0.1",
|
||||
"ssb-backlinks": "^1.0.0",
|
||||
|
|
|
@ -48,7 +48,7 @@ input.button.button-primary:hover {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
input:not([type="submit"]),
|
||||
input[type="text"],
|
||||
textarea {
|
||||
line-height: 32px;
|
||||
padding: 0 6px;
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
|
||||
<form action="/publish" method="POST">
|
||||
<textarea name="message"></textarea>
|
||||
<input type="submit" value="Send" />
|
||||
<input type="submit" class="button button-primary" value="Send" />
|
||||
</form>
|
||||
|
||||
<% posts.map(post => { %>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
</label>
|
||||
</div>
|
||||
<textarea name="message"></textarea>
|
||||
<input type="submit" value="Send" />
|
||||
<input type="submit" class="button button-primary" value="Send" />
|
||||
</form>
|
||||
|
||||
<% posts.map(post => { %>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div style="max-width: 800px; margin: 0 auto">
|
||||
<h1 style="padding-top: 50px">Create account</h1>
|
||||
|
||||
<form method="POST" action="/signup">
|
||||
<form method="POST" action="/signup" enctype="multipart/form-data">
|
||||
<div style="padding-top: 30px">
|
||||
<label>
|
||||
Profile picture:
|
||||
|
|
Loading…
Reference in New Issue