Work on shifting code to generic collections, etc.

This commit is contained in:
Bryan Ashby 2023-01-20 16:03:27 -07:00
parent 930308e07f
commit 9517b292a4
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
5 changed files with 99 additions and 100 deletions

View File

@ -11,7 +11,6 @@ const { getOutboxEntries } = require('./db');
const { WellKnownLocations } = require('../servers/content/web'); const { WellKnownLocations } = require('../servers/content/web');
// deps // deps
//const { isString, isObject } = require('lodash');
const { v4: UUIDv4 } = require('uuid'); const { v4: UUIDv4 } = require('uuid');
const async = require('async'); const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
@ -30,22 +29,6 @@ module.exports = class Activity extends ActivityPubObject {
return new Activity(parsed); return new Activity(parsed);
} }
// isValid() {
// if (
// this['@context'] !== ActivityStreamsContext ||
// !isString(this.id) ||
// !isString(this.actor) ||
// (!isString(this.object) && !isObject(this.object)) ||
// !Activity.ActivityTypes.includes(this.type)
// ) {
// return false;
// }
// // :TODO: Additional validation
// return true;
// }
// https://www.w3.org/TR/activitypub/#accept-activity-inbox // https://www.w3.org/TR/activitypub/#accept-activity-inbox
static makeAccept(webServer, localActor, followRequest, id = null) { static makeAccept(webServer, localActor, followRequest, id = null) {
id = id || Activity._makeFullId(webServer, 'accept'); id = id || Activity._makeFullId(webServer, 'accept');

View File

@ -1,49 +1,99 @@
const { makeUserUrl } = require('./util'); const { makeUserUrl } = require('./util');
const { ActivityStreamsContext } = require('./const'); const ActivityPubObject = require('./object');
const { FollowerEntryStatus, getFollowerEntries } = require('./db'); const apDb = require('../database').dbs.activitypub;
const { getISOTimestampString } = require('../database');
const { isString } = require('lodash');
module.exports = class Collection { module.exports = class Collection extends ActivityPubObject {
constructor(obj) { constructor(obj) {
this['@context'] = ActivityStreamsContext; super(obj);
Object.assign(this, obj);
} }
static followers(owningUser, page, webServer, cb) { static getOrdered(name, owningUser, includePrivate, page, mapper, webServer, cb) {
if (!page) { // :TODD: |includePrivate| handling
const followersUrl = const followersUrl =
makeUserUrl(webServer, owningUser, '/ap/users/') + '/followers'; makeUserUrl(webServer, owningUser, '/ap/users/') + `/${name}`;
if (!page) {
return apDb.get(
`SELECT COUNT(id) AS count
FROM collection_entry
WHERE name = ?;`,
[name],
(err, row) => {
if (err) {
return cb(err);
}
const obj = { const obj = {
id: followersUrl, id: followersUrl,
type: 'OrderedCollection', type: 'OrderedCollection',
first: `${followersUrl}?page=1`, first: `${followersUrl}?page=1`,
totalItems: 1, totalItems: row.count,
}; };
return cb(null, new Collection(obj)); return cb(null, new Collection(obj));
} }
);
}
// :TODO: actually support paging... // :TODO: actual paging...
page = parseInt(page); apDb.all(
const getOpts = { `SELECT entry_json
status: FollowerEntryStatus.Accepted, FROM collection_entry
}; WHERE user_id = ? AND name = ?
getFollowerEntries(owningUser, getOpts, (err, followers) => { ORDER BY timestamp;`,
[owningUser.userId, name],
(err, entries) => {
if (err) { if (err) {
return cb(err); return cb(err);
} }
const baseId = makeUserUrl(webServer, owningUser, '/ap/users') + '/followers'; if (mapper) {
entries = entries.map(mapper);
}
const obj = { const obj = {
id: `${baseId}/page=${page}`, id: `${followersUrl}/page=${page}`,
type: 'OrderedCollectionPage', type: 'OrderedCollectionPage',
totalItems: followers.length, totalItems: entries.length,
orderedItems: followers, orderedItems: entries,
partOf: baseId, partOf: followersUrl,
}; };
return cb(null, new Collection(obj)); return cb(null, new Collection(obj));
}); }
);
}
static followers(owningUser, page, webServer, cb) {
return Collection.getOrdered(
'followers',
owningUser,
false,
page,
e => e.id,
webServer,
cb
);
}
static addToCollection(name, owningUser, entry, cb) {
if (!isString(entry)) {
entry = JSON.stringify(entry);
}
apDb.run(
`INSERT INTO collection_entry (name, timestamp, user_id, entry_json)
VALUES (?, ?, ?, ?);`,
[name, getISOTimestampString(), owningUser.userId, entry],
function res(err) {
// non-arrow for 'this' scope
return cb(err, this.lastID);
}
);
}
static addFollower(owningUser, followingActor, cb) {
return Collection.addToCollection('followers', owningUser, followingActor, cb);
} }
}; };

View File

@ -2,8 +2,6 @@ const apDb = require('../database').dbs.activitypub;
exports.persistToOutbox = persistToOutbox; exports.persistToOutbox = persistToOutbox;
exports.getOutboxEntries = getOutboxEntries; exports.getOutboxEntries = getOutboxEntries;
exports.persistFollower = persistFollower;
exports.getFollowerEntries = getFollowerEntries;
const FollowerEntryStatus = { const FollowerEntryStatus = {
Invalid: 0, // Invalid Invalid: 0, // Invalid
@ -65,36 +63,3 @@ function getOutboxEntries(owningUser, options, cb) {
} }
); );
} }
function persistFollower(localUser, remoteActor, options, cb) {
const status = options.status || FollowerEntryStatus.Requested;
apDb.run(
`INSERT OR IGNORE INTO followers (user_id, follower_id, status)
VALUES (?, ?, ?);`,
[localUser.userId, remoteActor.id, status],
function res(err) {
// non-arrow for 'this' scope
return cb(err, this.lastID);
}
);
}
function getFollowerEntries(localUser, options, cb) {
const status = options.status || FollowerEntryStatus.Accepted;
apDb.all(
`SELECT follower_id
FROM followers
WHERE user_id = ? AND status = ?;`,
[localUser.userId, status],
(err, rows) => {
if (err) {
return cb(err);
}
const entries = rows.map(r => r.follower_id);
return cb(null, entries);
}
);
}

View File

@ -532,16 +532,20 @@ dbs.message.run(
); );
dbs.activitypub.run( dbs.activitypub.run(
`CREATE TABLE IF NOT EXISTS followers ( `CREATE TABLE IF NOT EXISTS collection_entry (
id INTEGER PRIMARY KEY, -- Local ID id INTEGER PRIMARY KEY, -- Auto-generated key
user_id INTEGER NOT NULL, -- Local user ID name VARCHAR NOT NULL, -- examples: followers, follows, ...
follower_id VARCHAR NOT NULL, -- Actor ID of follower timestamp DATETIME NOT NULL, -- Timestamp in which this entry was created
status INTEGER NOT NULL, -- Status: See FollowerEntryStatus user_id INTEGER NOT NULL, -- Local, owning user ID
entry_json VARCHAR NOT NULL -- Varies by collection
UNIQUE(user_id, follower_id)
);` );`
); );
dbs.activitypub.run(
`CREATE INDEX IF NOT EXISTS collection_entry_unique_index0
ON collection_entry (name, user_id, json_extract(entry_json, '$.id'))`
);
return cb(null); return cb(null);
}, },
}; };

View File

@ -10,7 +10,6 @@ const Activity = require('../../../activitypub/activity');
const ActivityPubSettings = require('../../../activitypub/settings'); const ActivityPubSettings = require('../../../activitypub/settings');
const Actor = require('../../../activitypub/actor'); const Actor = require('../../../activitypub/actor');
const Collection = require('../../../activitypub/collection'); const Collection = require('../../../activitypub/collection');
const { persistFollower, FollowerEntryStatus } = require('../../../activitypub/db');
// deps // deps
const _ = require('lodash'); const _ = require('lodash');
@ -383,10 +382,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
async.series( async.series(
[ [
callback => { callback => {
const persistOpts = { return Collection.addFollower(localUser, remoteActor, callback);
status: FollowerEntryStatus.Accepted,
};
return persistFollower(localUser, remoteActor, persistOpts, callback);
}, },
callback => { callback => {
Actor.fromLocalUser(localUser, this.webServer, (err, localActor) => { Actor.fromLocalUser(localUser, this.webServer, (err, localActor) => {
@ -444,6 +440,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
], ],
err => { err => {
if (err) { if (err) {
// :TODO: move this request to the "Request queue" for the user to try later
this.log.error( this.log.error(
{ error: err.message }, { error: err.message },
'Failed processing Follow request' 'Failed processing Follow request'