mirror of
https://github.com/dariusk/express-activitypub.git
synced 2024-11-23 15:51:00 +00:00
Migrate to better-sqlite3
This commit is contained in:
parent
e2ab4dd51e
commit
e989126e86
7 changed files with 148 additions and 164 deletions
8
index.js
8
index.js
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
130
routes/api.js
130
routes/api.js
|
@ -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;
|
||||||
|
|
110
routes/inbox.js
110
routes/inbox.js
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue