Migrate to better-sqlite3

This commit is contained in:
Darius Kazemi 2019-03-25 22:17:24 -07:00
parent e2ab4dd51e
commit e989126e86
7 changed files with 148 additions and 164 deletions

View file

@ -2,8 +2,8 @@ const config = require('./config.json');
const { USER, PASS, DOMAIN, PRIVKEY_PATH, CERT_PATH, PORT } = config; const { USER, PASS, DOMAIN, PRIVKEY_PATH, CERT_PATH, PORT } = config;
const express = require('express'); const express = require('express');
const app = express(); const app = express();
const sqlite3 = require('sqlite3').verbose(); const Database = require('better-sqlite3');
const db = new sqlite3.Database('bot-node.db'); const db = new Database('bot-node.db');
const fs = require('fs'); const fs = require('fs');
const routes = require('./routes'), const routes = require('./routes'),
bodyParser = require('body-parser'), bodyParser = require('body-parser'),
@ -27,9 +27,9 @@ try {
} }
// if there is no `accounts` table in the DB, create an empty table // if there is no `accounts` table in the DB, create an empty table
db.run('CREATE TABLE IF NOT EXISTS accounts (name TEXT PRIMARY KEY, privkey TEXT, pubkey TEXT, webfinger TEXT, actor TEXT, apikey TEXT, followers TEXT, messages TEXT)'); db.prepare('CREATE TABLE IF NOT EXISTS accounts (name TEXT PRIMARY KEY, privkey TEXT, pubkey TEXT, webfinger TEXT, actor TEXT, apikey TEXT, followers TEXT, messages TEXT)').run();
// if there is no `messages` table in the DB, create an empty table // if there is no `messages` table in the DB, create an empty table
db.run('CREATE TABLE IF NOT EXISTS messages (guid TEXT PRIMARY KEY, message TEXT)'); db.prepare('CREATE TABLE IF NOT EXISTS messages (guid TEXT PRIMARY KEY, message TEXT)').run();
app.set('db', db); app.set('db', db);
app.set('domain', DOMAIN); app.set('domain', DOMAIN);

View file

@ -4,13 +4,13 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"better-sqlite3": "^5.4.0",
"body-parser": "^1.18.3", "body-parser": "^1.18.3",
"cors": "^2.8.4", "cors": "^2.8.4",
"express": "^4.16.3", "express": "^4.16.3",
"express-basic-auth": "^1.1.5", "express-basic-auth": "^1.1.5",
"generate-rsa-keypair": "^0.1.2", "generate-rsa-keypair": "^0.1.2",
"request": "^2.87.0", "request": "^2.87.0"
"sqlite3": "^4.0.2"
}, },
"engines": { "engines": {
"node": ">=10.10.0" "node": ">=10.10.0"

View file

@ -52,16 +52,13 @@ router.post('/create', function (req, res) {
let actorRecord = createActor(account, domain, pair.public); let actorRecord = createActor(account, domain, pair.public);
let webfingerRecord = createWebfinger(account, domain); let webfingerRecord = createWebfinger(account, domain);
const apikey = crypto.randomBytes(16).toString('hex'); const apikey = crypto.randomBytes(16).toString('hex');
db.run('insert or replace into accounts(name, actor, apikey, pubkey, privkey, webfinger) values($name, $actor, $apikey, $pubkey, $privkey, $webfinger)', { try {
$name: `${account}@${domain}`, db.prepare('insert or replace into accounts(name, actor, apikey, pubkey, privkey, webfinger) values(?, ?, ?, ?, ?, ?)').run(`${account}@${domain}`, JSON.stringify(actorRecord), apikey, pair.public, pair.private, JSON.stringify(webfingerRecord));
$apikey: apikey,
$pubkey: pair.public,
$privkey: pair.private,
$actor: JSON.stringify(actorRecord),
$webfinger: JSON.stringify(webfingerRecord)
}, (err, accounts) => {
res.status(200).json({msg: 'ok', apikey}); res.status(200).json({msg: 'ok', apikey});
}); }
catch(e) {
res.status(200).json({error: e});
}
}); });
module.exports = router; module.exports = router;

View file

@ -6,61 +6,58 @@ const express = require('express'),
router.post('/sendMessage', function (req, res) { router.post('/sendMessage', function (req, res) {
let db = req.app.get('db'); let db = req.app.get('db');
console.log('DB',db);
let domain = req.app.get('domain'); let domain = req.app.get('domain');
let acct = req.body.acct; let acct = req.body.acct;
let apikey = req.body.apikey; let apikey = req.body.apikey;
let message = req.body.message; let message = req.body.message;
// check to see if your API key matches // check to see if your API key matches
db.get('select apikey from accounts where name = $name', {$name: `${acct}@${domain}`}, (err, result) => { let result = db.prepare('select apikey from accounts where name = ?').get(`${acct}@${domain}`);
if (result.apikey === apikey) { if (result.apikey === apikey) {
sendCreateMessage(message, acct, domain, req, res); sendCreateMessage(message, acct, domain, req, res);
} }
else { else {
res.status(403).json({msg: 'wrong api key'}); res.status(403).json({msg: 'wrong api key'});
} }
});
}); });
function signAndSend(message, name, domain, req, res, targetDomain, inbox) { function signAndSend(message, name, domain, req, res, targetDomain, inbox) {
// get the private key // get the private key
let db = req.app.get('db'); let db = req.app.get('db');
let inboxFragment = inbox.replace('https://'+targetDomain,''); let inboxFragment = inbox.replace('https://'+targetDomain,'');
db.get('select privkey from accounts where name = $name', {$name: `${name}@${domain}`}, (err, result) => { let result = db.prepare('select privkey from accounts where name = ?').get(`${name}@${domain}`);
if (result === undefined) { if (result === undefined) {
console.log(`No record found for ${name}.`); console.log(`No record found for ${name}.`);
} }
else { else {
let privkey = result.privkey; let privkey = result.privkey;
const signer = crypto.createSign('sha256'); const signer = crypto.createSign('sha256');
let d = new Date(); let d = new Date();
let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`; let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`;
signer.update(stringToSign); signer.update(stringToSign);
signer.end(); signer.end();
const signature = signer.sign(privkey); const signature = signer.sign(privkey);
const signature_b64 = signature.toString('base64'); const signature_b64 = signature.toString('base64');
let header = `keyId="https://${domain}/u/${name}",headers="(request-target) host date",signature="${signature_b64}"`; let header = `keyId="https://${domain}/u/${name}",headers="(request-target) host date",signature="${signature_b64}"`;
request({ request({
url: inbox, url: inbox,
headers: { headers: {
'Host': targetDomain, 'Host': targetDomain,
'Date': d.toUTCString(), 'Date': d.toUTCString(),
'Signature': header 'Signature': header
}, },
method: 'POST', method: 'POST',
json: true, json: true,
body: message body: message
}, function (error, response){ }, function (error, response){
console.log(`Sent message to an inbox at ${targetDomain}!`); console.log(`Sent message to an inbox at ${targetDomain}!`);
if (error) { if (error) {
console.log('Error:', error, response); console.log('Error:', error, response);
} }
else { else {
console.log('Response Status Code:', response.statusCode); console.log('Response Status Code:', response.statusCode);
} }
}); });
} }
});
} }
function createMessage(text, name, domain, req, res) { function createMessage(text, name, domain, req, res) {
@ -88,16 +85,8 @@ function createMessage(text, name, domain, req, res) {
'object': noteMessage 'object': noteMessage
}; };
db.run('insert or replace into messages(guid, message) values($guid, $message)', { db.prepare('insert or replace into messages(guid, message) values(?, ?)').run( guidCreate, JSON.stringify(createMessage));
$guid: guidCreate, db.prepare('insert or replace into messages(guid, message) values(?, ?)').run( guidNote, JSON.stringify(noteMessage));
$message: JSON.stringify(createMessage),
}, (err, accounts) => {
});
db.run('insert or replace into messages(guid, message) values($guid, $message)', {
$guid: guidNote,
$message: JSON.stringify(noteMessage),
}, (err, accounts) => {
});
return createMessage; return createMessage;
} }
@ -106,24 +95,23 @@ function sendCreateMessage(text, name, domain, req, res) {
let message = createMessage(text, name, domain, req, res); let message = createMessage(text, name, domain, req, res);
let db = req.app.get('db'); let db = req.app.get('db');
db.get('select followers from accounts where name = $name', {$name: `${name}@${domain}`}, (err, result) => { let result = db.prepare('select followers from accounts where name = ?').get(`${name}@${domain}`);
let followers = JSON.parse(result.followers); let followers = JSON.parse(result.followers);
console.log(followers); console.log(followers);
console.log('type',typeof followers); console.log('type',typeof followers);
if (followers === null) { if (followers === null) {
console.log('aaaa'); console.log('aaaa');
res.status(400).json({msg: `No followers for account ${name}@${domain}`}); res.status(400).json({msg: `No followers for account ${name}@${domain}`});
}
else {
for (let follower of followers) {
let inbox = follower+'/inbox';
let myURL = new URL(follower);
let targetDomain = myURL.hostname;
signAndSend(message, name, domain, req, res, targetDomain, inbox);
} }
else { res.status(200).json({msg: 'ok'});
for (let follower of followers) { }
let inbox = follower+'/inbox';
let myURL = new URL(follower);
let targetDomain = myURL.hostname;
signAndSend(message, name, domain, req, res, targetDomain, inbox);
}
res.status(200).json({msg: 'ok'});
}
});
} }
module.exports = router; module.exports = router;

View file

@ -10,41 +10,40 @@ function signAndSend(message, name, domain, req, res, targetDomain) {
let inboxFragment = inbox.replace('https://'+targetDomain,''); let inboxFragment = inbox.replace('https://'+targetDomain,'');
// get the private key // get the private key
let db = req.app.get('db'); let db = req.app.get('db');
db.get('select privkey from accounts where name = $name', {$name: `${name}@${domain}`}, (err, result) => { let result = db.prepare('select privkey from accounts where name = ?').get(name);
if (result === undefined) { if (result === undefined) {
return res.status(404).send(`No record found for ${name}.`); return res.status(404).send(`No record found for ${name}.`);
} }
else { else {
let privkey = result.privkey; let privkey = result.privkey;
const signer = crypto.createSign('sha256'); const signer = crypto.createSign('sha256');
let d = new Date(); let d = new Date();
let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`; let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`;
signer.update(stringToSign); signer.update(stringToSign);
signer.end(); signer.end();
const signature = signer.sign(privkey); const signature = signer.sign(privkey);
const signature_b64 = signature.toString('base64'); const signature_b64 = signature.toString('base64');
let header = `keyId="https://${domain}/u/${name}",headers="(request-target) host date",signature="${signature_b64}"`; let header = `keyId="https://${domain}/u/${name}",headers="(request-target) host date",signature="${signature_b64}"`;
request({ request({
url: inbox, url: inbox,
headers: { headers: {
'Host': targetDomain, 'Host': targetDomain,
'Date': d.toUTCString(), 'Date': d.toUTCString(),
'Signature': header 'Signature': header
}, },
method: 'POST', method: 'POST',
json: true, json: true,
body: message body: message
}, function (error, response){ }, function (error, response){
if (error) { if (error) {
console.log('Error:', error, response.body); console.log('Error:', error, response.body);
} }
else { else {
console.log('Response:', response.body); console.log('Response:', response.body);
} }
}); });
return res.status(200); return res.status(200);
} }
});
} }
function sendAcceptMessage(thebody, name, domain, req, res, targetDomain) { function sendAcceptMessage(thebody, name, domain, req, res, targetDomain) {
@ -79,28 +78,31 @@ router.post('/', function (req, res) {
// Add the user to the DB of accounts that follow the account // Add the user to the DB of accounts that follow the account
let db = req.app.get('db'); let db = req.app.get('db');
// get the followers JSON for the user // get the followers JSON for the user
db.get('select followers from accounts where name = $name', {$name: `${name}@${domain}`}, (err, result) => { let result = db.prepare('select followers from accounts where name = ?').get(`${name}@${domain}`);
if (result === undefined) { if (result === undefined) {
console.log(`No record found for ${name}.`); console.log(`No record found for ${name}.`);
}
else {
// update followers
let followers = parseJSON(result.followers);
if (followers) {
followers.push(req.body.actor);
// unique items
followers = [...new Set(followers)];
} }
else { else {
// update followers followers = [req.body.actor];
let followers = parseJSON(result.followers);
if (followers) {
followers.push(req.body.actor);
// unique items
followers = [...new Set(followers)];
}
else {
followers = [req.body.actor];
}
let followersText = JSON.stringify(followers);
// update into DB
db.run('update accounts set followers=$followers where name = $name', {$name: `${name}@${domain}`, $followers: followersText}, (err, result) => {
console.log('updated followers!', err, result);
});
} }
}); let followersText = JSON.stringify(followers);
try {
// update into DB
let newFollowers = db.prepare('update accounts set followers=? where name = ?').run(followersText, `${name}@${domain}`);
console.log('updated followers!', newFollowers);
}
catch(e) {
console.log('error', e);
}
}
} }
}); });

View file

@ -12,20 +12,19 @@ router.get('/:name', function (req, res) {
let domain = req.app.get('domain'); let domain = req.app.get('domain');
let username = name; let username = name;
name = `${name}@${domain}`; name = `${name}@${domain}`;
db.get('select actor from accounts where name = $name', {$name: name}, (err, result) => { let result = db.prepare('select actor from accounts where name = ?').get(name);
if (result === undefined) { if (result === undefined) {
return res.status(404).send(`No record found for ${name}.`); return res.status(404).send(`No record found for ${name}.`);
}
else {
let tempActor = JSON.parse(result.actor);
// Added this followers URI for Pleroma compatibility, see https://github.com/dariusk/rss-to-activitypub/issues/11#issuecomment-471390881
// New Actors should have this followers URI but in case of migration from an old version this will add it in on the fly
if (tempActor.followers === undefined) {
tempActor.followers = `https://${domain}/u/${username}/followers`;
} }
else { res.json(tempActor);
let tempActor = JSON.parse(result.actor); }
// Added this followers URI for Pleroma compatibility, see https://github.com/dariusk/rss-to-activitypub/issues/11#issuecomment-471390881
// New Actors should have this followers URI but in case of migration from an old version this will add it in on the fly
if (tempActor.followers === undefined) {
tempActor.followers = `https://${domain}/u/${username}/followers`;
}
res.json(tempActor);
}
});
} }
}); });
@ -38,10 +37,9 @@ router.get('/:name/followers', function (req, res) {
let db = req.app.get('db'); let db = req.app.get('db');
let domain = req.app.get('domain'); let domain = req.app.get('domain');
let result = db.prepare('select followers from accounts where name = ?').get(`${name}@${domain}`); let result = db.prepare('select followers from accounts where name = ?').get(`${name}@${domain}`);
console.log(result);
result.followers = result.followers || '[]';
let followers = JSON.parse(result.followers); let followers = JSON.parse(result.followers);
if (!followers) {
followers = [];
}
let followersCollection = { let followersCollection = {
"type":"OrderedCollection", "type":"OrderedCollection",
"totalItems":followers.length, "totalItems":followers.length,

View file

@ -10,14 +10,13 @@ router.get('/', function (req, res) {
else { else {
let name = resource.replace('acct:',''); let name = resource.replace('acct:','');
let db = req.app.get('db'); let db = req.app.get('db');
db.get('select webfinger from accounts where name = $name', {$name: name}, (err, result) => { let result = db.prepare('select webfinger from accounts where name = ?').get(name);
if (result === undefined) { if (result === undefined) {
return res.status(404).send(`No record found for ${name}.`); return res.status(404).send(`No record found for ${name}.`);
} }
else { else {
res.json(JSON.parse(result.webfinger)); res.json(JSON.parse(result.webfinger));
} }
});
} }
}); });