Handling Update/Delete better

This commit is contained in:
Bryan Ashby 2023-03-13 12:39:36 -06:00
parent 3212d809df
commit 8b6d564ebf
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
4 changed files with 283 additions and 3 deletions

View File

@ -313,6 +313,89 @@ module.exports = class Note extends ActivityPubObject {
});
}
toUpdatedMessage(options, cb) {
const original = new Message();
original.load({ uuid: options.messageUuid }, err => {
if (err) {
return cb(err);
}
// rebuild message
options.areaTag = original.areaTag;
async.waterfall(
[
callback => {
if (!original.isPrivate()) {
options.toUser = 'All';
return callback(null);
}
const userId =
original.meta.System[Message.SystemMetaNames.LocalToUserID];
if (!userId) {
return cb(
Errors.MissingProperty(
`User is missing "${Message.SystemMetaNames.LocalToUserID}" property`
)
);
}
User.getUser(userId, (err, user) => {
if (err) {
return callback(err);
}
options.toUser = user;
return callback(null);
});
},
callback => {
this.toMessage(options, (err, message) => {
if (err) {
return callback(err);
}
// re-target as message to be updated
message.messageUuid = original.messageUuid;
return callback(null, message);
});
},
],
(err, message) => {
return cb(err, message);
}
);
});
}
static deleteAssocMessage(noteId, cb) {
const filter = {
resultType: 'uuid',
metaTuples: [
{
category: Message.WellKnownMetaCategories.ActivityPub,
name: Message.ActivityPubPropertyNames.NoteId,
value: noteId,
},
],
limit: 1,
};
return Message.findMessages(filter, (err, messageUuid) => {
if (!messageUuid) {
return cb(null);
}
messageUuid = messageUuid[0]; // limit 1
Message.deleteByMessageUuid(messageUuid, err => {
return cb(err);
});
});
}
_getSubject(message) {
if (this.summary) {
return this.summary.trim();

View File

@ -100,6 +100,7 @@ module.exports = () => {
'server',
'client',
'notme',
'public',
],
preAuthIdleLogoutSeconds: 60 * 3, // 3m

View File

@ -708,6 +708,17 @@ module.exports = class Message {
);
}
static deleteByMessageUuid(messageUuid, cb) {
msgDb.run(
`DELETE FROM message
WHERE message_uuid = ?;`,
[messageUuid],
err => {
return cb(err);
}
);
}
persistMetaValue(category, name, value, transOrDb, cb) {
if (!_.isFunction(cb) && _.isFunction(transOrDb)) {
cb = transOrDb;
@ -738,6 +749,34 @@ module.exports = class Message {
);
}
updateMetaValue(category, name, value, transOrDb, cb) {
if (!_.isFunction(cb) && _.isFunction(transOrDb)) {
cb = transOrDb;
transOrDb = msgDb;
}
const metaStmt = transOrDb.prepare(
`REPLACE INTO message_meta (message_id, meta_category, meta_name, meta_value)
VALUES (?, ?, ?, ?);`
);
if (!_.isArray(value)) {
value = [value];
}
async.each(
value,
(v, next) => {
metaStmt.run(this.messageId, category, name, v, err => {
return next(err);
});
},
err => {
return cb(err);
}
);
}
persist(cb) {
if (!this.isValid()) {
return cb(Errors.Invalid('Cannot persist invalid message!'));
@ -843,6 +882,90 @@ module.exports = class Message {
);
}
update(cb) {
if (!this.isValid()) {
return cb(Errors.Invalid('Cannot update invalid message!'));
}
if (!this.messageUuid) {
return cb(Errors.Invalid("Cannot update without a valid 'messageUUID'"));
}
const self = this;
async.waterfall(
[
callback => {
return msgDb.beginTransaction(callback);
},
(trans, callback) => {
trans.run(
`REPLACE INTO message (area_tag, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, message, modified_timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?);`,
[
this.areaTag,
this.messageUuid,
this.replyToMsgId,
this.toUserName,
this.fromUserName,
this.subject,
this.message,
getISOTimestampString(this.modTimestamp),
],
function inserted(err) {
// use non-arrow function for 'this' scope
if (!err) {
self.messageId = this.lastID;
}
return callback(err, trans);
}
);
},
(trans, callback) => {
if (!this.meta) {
return callback(null, trans);
}
async.each(
Object.keys(this.meta),
(category, nextCat) => {
async.each(
Object.keys(this.meta[category]),
(name, nextName) => {
this.updateMetaValue(
category,
name,
this.meta[category][name],
trans,
err => {
return nextName(err);
}
);
},
err => {
return nextCat(err);
}
);
},
err => {
return callback(err, trans);
}
);
},
],
(err, trans) => {
if (trans) {
trans[err ? 'rollback' : 'commit'](transErr => {
return cb(err ? err : transErr, self.messageId);
});
} else {
return cb(err);
}
}
);
}
deleteMessage(requestingUser, cb) {
if (!this.userHasDeleteRights(requestingUser)) {
return cb(

View File

@ -532,8 +532,28 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
objInfo.object,
stats,
() => {
if ('Note' === objInfo.object.type) {
// :TODO: delete associated message!
// if it was a Note before...
if (
Collections.Inbox ===
objInfo.info.name ||
Collections.SharedInbox ===
objInfo.info.name
) {
return Note.deleteAssocMessage(
objectId,
err => {
if (err) {
this.log.warn(
{
error: err.message,
noteId: objectId,
},
'Failed to remove message associated with Note'
);
}
return nextObjInfo(null);
}
);
}
return nextObjInfo(null);
@ -578,6 +598,58 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
return this.webServer.accepted(resp);
}
_updateMessageAssocWithNote(objectId, activity) {
const filter = {
resultType: 'uuid',
metaTuples: [
{
category: Message.WellKnownMetaCategories.ActivityPub,
name: Message.ActivityPubPropertyNames.NoteId,
value: objectId,
},
],
limit: 1,
};
Message.findMessages(filter, (err, messageUuid) => {
if (!messageUuid) {
return this.log.warn(
{ messageUuid },
'Failed to find message for Update Note'
);
}
messageUuid = messageUuid[0]; // limit 1
const note = new Note(activity.object);
if (!note.isValid()) {
return this.log.error('Note within Update does not appear to be valid');
}
const updateOpts = {
messageUuid,
};
note.toUpdatedMessage(updateOpts, (err, message) => {
if (err) {
return this.log.error(
{ error: err.message, messageUuid, step: 'Note to Message' },
'Note Update failed to update underlying message'
);
}
message.update(err => {
if (err) {
this.log.error(
{ error: err.message, messageUuid, step: 'Persist' },
'Note Update failed to update underlying message'
);
}
});
});
});
}
_deleteObjectWithStats(collectionName, object, stats, cb) {
const objectId = _.isString(object) ? object : object.id;
const type = object.type;
@ -806,7 +878,8 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
`${objectType} Updated`
);
// :TODO: we need to UPDATE the existing stored Message object if this is a Note
// Update any assoc Message object
this._updateMessageAssocWithNote(targetObjectId, activity);
return this.webServer.accepted(resp);
}