Work on shifting code to generic collections, etc.
This commit is contained in:
parent
930308e07f
commit
9517b292a4
|
@ -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');
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Loading…
Reference in New Issue