parent
87034967ae
commit
6466220b6d
|
@ -14,17 +14,17 @@ const ActivityPubObject = require('./object');
|
||||||
const { ActivityStreamMediaType } = require('./const');
|
const { ActivityStreamMediaType } = require('./const');
|
||||||
const apDb = require('../database').dbs.activitypub;
|
const apDb = require('../database').dbs.activitypub;
|
||||||
const Config = require('../config').get;
|
const Config = require('../config').get;
|
||||||
const parseFullName = require('parse-full-name').parseFullName;
|
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const mimeTypes = require('mime-types');
|
const mimeTypes = require('mime-types');
|
||||||
const { getJson } = require('../http_util.js');
|
const { getJson } = require('../http_util.js');
|
||||||
const { getISOTimestampString } = require('../database.js');
|
const { getISOTimestampString } = require('../database.js');
|
||||||
|
const moment = require('moment');
|
||||||
const paths = require('path');
|
const paths = require('path');
|
||||||
|
|
||||||
|
const ActorCacheExpiration = moment.duration(15, 'days');
|
||||||
const ActorCacheMaxAgeDays = 125; // hasn't been used in >= 125 days, nuke it.
|
const ActorCacheMaxAgeDays = 125; // hasn't been used in >= 125 days, nuke it.
|
||||||
const MaxSearchResults = 10; // Maximum number of results to show for a search
|
|
||||||
|
|
||||||
// default context for Actor's
|
// default context for Actor's
|
||||||
const DefaultContext = ActivityPubObject.makeContext(['https://w3id.org/security/v1'], {
|
const DefaultContext = ActivityPubObject.makeContext(['https://w3id.org/security/v1'], {
|
||||||
|
@ -167,10 +167,14 @@ module.exports = class Actor extends ActivityPubObject {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Actor._fromCache(id, (err, actor, subject) => {
|
Actor._fromCache(id, (err, actor, subject, needsRefresh) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
// cache hit
|
// cache hit
|
||||||
callback(null, actor, subject);
|
callback(null, actor, subject);
|
||||||
|
|
||||||
|
if (!needsRefresh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache miss or needs refreshed; Try to do so now
|
// Cache miss or needs refreshed; Try to do so now
|
||||||
|
@ -190,76 +194,21 @@ module.exports = class Actor extends ActivityPubObject {
|
||||||
|
|
||||||
// cache our entry
|
// cache our entry
|
||||||
if (actor) {
|
if (actor) {
|
||||||
apDb.run(
|
|
||||||
'DELETE FROM actor_cache_search WHERE actor_id=?',
|
|
||||||
[id],
|
|
||||||
err => {
|
|
||||||
if (err) {
|
|
||||||
Log.warn(
|
|
||||||
{ err: err },
|
|
||||||
'Error deleting previous actor_cache_search'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
apDb.run(
|
apDb.run(
|
||||||
`REPLACE INTO actor_cache (actor_id, actor_json, subject, timestamp)
|
`REPLACE INTO actor_cache (actor_id, actor_json, subject, timestamp)
|
||||||
VALUES (?, ?, ?, ?);`,
|
VALUES (?, ?, ?, ?);`,
|
||||||
[id, JSON.stringify(actor), subject, getISOTimestampString()],
|
[id, JSON.stringify(actor), subject, getISOTimestampString()],
|
||||||
err => {
|
err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
Log.warn(
|
// :TODO: log me
|
||||||
{ err: err },
|
|
||||||
'Error replacing into the actor cache'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const searchNames = this._parseSearchNames(actor);
|
|
||||||
if (searchNames.length > 0) {
|
|
||||||
const searchStatement = apDb.prepare(
|
|
||||||
'INSERT INTO actor_cache_search (actor_id, search_name) VALUES (?, ?)'
|
|
||||||
);
|
|
||||||
searchNames.forEach(name => {
|
|
||||||
searchStatement.run([id, name], err => {
|
|
||||||
if (err) {
|
|
||||||
Log.warn(
|
|
||||||
{ err: err },
|
|
||||||
'Error inserting into actor cache search'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static _parseSearchNames(actor) {
|
|
||||||
const searchNames = [];
|
|
||||||
|
|
||||||
if (!_.isEmpty(actor.preferredUsername)) {
|
|
||||||
searchNames.push(actor.preferredUsername);
|
|
||||||
}
|
|
||||||
const name = parseFullName(actor.name);
|
|
||||||
if (!_.isEmpty(name.first)) {
|
|
||||||
searchNames.push(name.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_.isEmpty(name.last)) {
|
|
||||||
searchNames.push(name.last);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_.isEmpty(name.nick)) {
|
|
||||||
searchNames.push(name.nick);
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
static actorCacheMaintenanceTask(args, cb) {
|
static actorCacheMaintenanceTask(args, cb) {
|
||||||
const enabled = _.get(
|
const enabled = _.get(
|
||||||
Config(),
|
Config(),
|
||||||
|
@ -271,10 +220,10 @@ module.exports = class Actor extends ActivityPubObject {
|
||||||
|
|
||||||
apDb.run(
|
apDb.run(
|
||||||
`DELETE FROM actor_cache
|
`DELETE FROM actor_cache
|
||||||
WHERE DATETIME(timestamp) < DATETIME("now", "-${ActorCacheMaxAgeDays} day");`,
|
WHERE DATETIME(timestamp) > DATETIME("now", "+${ActorCacheMaxAgeDays}");`,
|
||||||
err => {
|
err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
Log.warn({ err: err }, 'Error clearing old cache entries.');
|
// :TODO: log me
|
||||||
}
|
}
|
||||||
|
|
||||||
return cb(null); // always non-fatal
|
return cb(null); // always non-fatal
|
||||||
|
@ -282,40 +231,6 @@ module.exports = class Actor extends ActivityPubObject {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromSearch(searchString, cb) {
|
|
||||||
// first try to find an exact match
|
|
||||||
this.fromId(searchString, (err, remoteActor) => {
|
|
||||||
if (!err) {
|
|
||||||
return cb(null, [remoteActor]);
|
|
||||||
} else {
|
|
||||||
Log.info({ err: err }, 'Not able to find based on id');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let returnRows = [];
|
|
||||||
// If not found, try searching database for known actors
|
|
||||||
apDb.each(
|
|
||||||
`SELECT actor_cache.actor_json
|
|
||||||
FROM actor_cache INNER JOIN actor_cache_search ON actor_cache.actor_id = actor_cache_search.actor_id
|
|
||||||
WHERE actor_cache_search.search_name like '%'||?||'%'
|
|
||||||
LIMIT ${MaxSearchResults}`,
|
|
||||||
(err, row) => {
|
|
||||||
if (err) {
|
|
||||||
Log.warn({ err: err }, 'error retrieving search results');
|
|
||||||
return cb(err, []);
|
|
||||||
}
|
|
||||||
this._fromJsonToActor(row.actor_json, (err, actor) => {
|
|
||||||
if (err) {
|
|
||||||
Log.warn({ err: err }, 'error converting search results');
|
|
||||||
return cb(err, []);
|
|
||||||
}
|
|
||||||
returnRows.push(actor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return cb(null, returnRows);
|
|
||||||
}
|
|
||||||
|
|
||||||
static _fromRemoteQuery(id, cb) {
|
static _fromRemoteQuery(id, cb) {
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: ActivityStreamMediaType,
|
Accept: ActivityStreamMediaType,
|
||||||
|
@ -338,10 +253,9 @@ module.exports = class Actor extends ActivityPubObject {
|
||||||
|
|
||||||
static _fromCache(actorIdOrSubject, cb) {
|
static _fromCache(actorIdOrSubject, cb) {
|
||||||
apDb.get(
|
apDb.get(
|
||||||
`SELECT actor_json, subject
|
`SELECT actor_json, subject, timestamp
|
||||||
FROM actor_cache
|
FROM actor_cache
|
||||||
WHERE actor_id = ? OR subject = ?
|
WHERE actor_id = ? OR subject = ?
|
||||||
AND DATETIME(timestamp) > DATETIME("now", "-${ActorCacheMaxAgeDays} day")
|
|
||||||
LIMIT 1;`,
|
LIMIT 1;`,
|
||||||
[actorIdOrSubject, actorIdOrSubject],
|
[actorIdOrSubject, actorIdOrSubject],
|
||||||
(err, row) => {
|
(err, row) => {
|
||||||
|
@ -353,31 +267,27 @@ module.exports = class Actor extends ActivityPubObject {
|
||||||
return cb(Errors.DoesNotExist());
|
return cb(Errors.DoesNotExist());
|
||||||
}
|
}
|
||||||
|
|
||||||
this._fromJsonToActor(row.actor_json, (err, actor) => {
|
const timestamp = moment(row.timestamp);
|
||||||
if (err) {
|
const needsRefresh = moment().isAfter(
|
||||||
return cb(err);
|
timestamp.add(ActorCacheExpiration)
|
||||||
}
|
);
|
||||||
const subject = row.subject || actor.id;
|
|
||||||
return cb(null, actor, subject);
|
const obj = ActivityPubObject.fromJsonString(row.actor_json);
|
||||||
});
|
if (!obj || !obj.isValid()) {
|
||||||
|
return cb(Errors.Invalid('Failed to create ActivityPub object'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const actor = new Actor(obj);
|
||||||
|
if (!actor.isValid()) {
|
||||||
|
return cb(Errors.Invalid('Failed to create Actor object'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const subject = row.subject || actor.id;
|
||||||
|
return cb(null, actor, subject, needsRefresh);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _fromJsonToActor(actorJson, cb) {
|
|
||||||
const obj = ActivityPubObject.fromJsonString(actorJson);
|
|
||||||
if (!obj || !obj.isValid()) {
|
|
||||||
return cb(Errors.Invalid('Failed to create ActivityPub object'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const actor = new Actor(obj);
|
|
||||||
if (!actor.isValid()) {
|
|
||||||
return cb(Errors.Invalid('Failed to create Actor object'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb(null, actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static _fromWebFinger(actorQuery, cb) {
|
static _fromWebFinger(actorQuery, cb) {
|
||||||
queryWebFinger(actorQuery, (err, res) => {
|
queryWebFinger(actorQuery, (err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -480,8 +480,7 @@ dbs.message.run(
|
||||||
actor_json VARCHAR NOT NULL, -- Actor document
|
actor_json VARCHAR NOT NULL, -- Actor document
|
||||||
subject VARCHAR, -- Subject obtained from WebFinger, e.g. @Username@some.domain
|
subject VARCHAR, -- Subject obtained from WebFinger, e.g. @Username@some.domain
|
||||||
timestamp DATETIME NOT NULL, -- Timestamp in which this Actor was cached
|
timestamp DATETIME NOT NULL, -- Timestamp in which this Actor was cached
|
||||||
preferredUsername VARCHAR, -- Denormalized username for search
|
|
||||||
name VARCHAR, -- Denormalized actor name for search
|
|
||||||
UNIQUE(actor_id)
|
UNIQUE(actor_id)
|
||||||
);`
|
);`
|
||||||
);
|
);
|
||||||
|
@ -491,25 +490,6 @@ dbs.message.run(
|
||||||
ON actor_cache (actor_id);`
|
ON actor_cache (actor_id);`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Denormalized actor information for search
|
|
||||||
dbs.activitypub.run(
|
|
||||||
`CREATE TABLE IF NOT EXISTS actor_cache_search (
|
|
||||||
actor_id VARCHAR NOT NULL, -- Fully qualified Actor ID/URL
|
|
||||||
search_name VARCHAR, -- Name to search
|
|
||||||
CONSTRAINT actor_cache_search_actor_cache_fk FOREIGN KEY(actor_id) REFERENCES actor_cache(actor_id) ON DELETE CASCADE
|
|
||||||
);`
|
|
||||||
);
|
|
||||||
|
|
||||||
dbs.activitypub.run(
|
|
||||||
`CREATE INDEX IF NOT EXISTS actor_cache_search_actor_id_index0
|
|
||||||
ON actor_cache_search (actor_id);`
|
|
||||||
);
|
|
||||||
|
|
||||||
dbs.activitypub.run(
|
|
||||||
`CREATE INDEX IF NOT EXISTS actor_cache_search_name_index0
|
|
||||||
ON actor_cache_search (search_name);`
|
|
||||||
);
|
|
||||||
|
|
||||||
// ActivityPub Collections of various types such as followers, following, likes, ...
|
// ActivityPub Collections of various types such as followers, following, likes, ...
|
||||||
dbs.activitypub.run(
|
dbs.activitypub.run(
|
||||||
`CREATE TABLE IF NOT EXISTS collection (
|
`CREATE TABLE IF NOT EXISTS collection (
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -57,7 +57,6 @@
|
||||||
"node-pty": "0.10.1",
|
"node-pty": "0.10.1",
|
||||||
"nodemailer": "6.7.7",
|
"nodemailer": "6.7.7",
|
||||||
"otplib": "11.0.1",
|
"otplib": "11.0.1",
|
||||||
"parse-full-name": "^1.2.6",
|
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
"rlogin": "^1.0.0",
|
"rlogin": "^1.0.0",
|
||||||
"sane": "5.0.1",
|
"sane": "5.0.1",
|
||||||
|
|
Loading…
Reference in New Issue