diff --git a/app/imports/api/creature/removeCreature.js b/app/imports/api/creature/removeCreature.js index 899db6b3..c8542343 100644 --- a/app/imports/api/creature/removeCreature.js +++ b/app/imports/api/creature/removeCreature.js @@ -28,10 +28,14 @@ const removeCreature = new ValidatedMethod({ }, run({charId}) { assertOwnership(charId, this.userId) - Creatures.remove(charId); - this.unblock(); - removeRelatedDocuments(charId); + this.unblock(); + removeCreatureWork(charId) }, }); +export function removeCreatureWork(creatureId){ + Creatures.remove(creatureId); + removeRelatedDocuments(creatureId); +} + export default removeCreature; diff --git a/app/imports/api/library/Libraries.js b/app/imports/api/library/Libraries.js index 886d7f11..4f7d1e52 100644 --- a/app/imports/api/library/Libraries.js +++ b/app/imports/api/library/Libraries.js @@ -118,10 +118,14 @@ const removeLibrary = new ValidatedMethod({ run({_id}){ let library = Libraries.findOne(_id); assertOwnership(library, this.userId); - Libraries.remove(_id); - this.unblock(); - LibraryNodes.remove({'ancestors.id': _id}); + this.unblock(); + removeLibaryWork(_id) } -}) +}); + +export function removeLibaryWork(libraryId){ + Libraries.remove(libraryId); + LibraryNodes.remove({'ancestors.id': libraryId}); +} export { LibrarySchema, insertLibrary, setLibraryDefault, updateLibraryName, removeLibrary }; diff --git a/app/imports/api/users/Users.js b/app/imports/api/users/Users.js index 70242af7..9bd75ac7 100644 --- a/app/imports/api/users/Users.js +++ b/app/imports/api/users/Users.js @@ -1,6 +1,7 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import '/imports/api/users/deleteMyAccount.js'; const userSchema = new SimpleSchema({ username: { diff --git a/app/imports/api/users/deleteMyAccount.js b/app/imports/api/users/deleteMyAccount.js new file mode 100644 index 00000000..6a5aa8c1 --- /dev/null +++ b/app/imports/api/users/deleteMyAccount.js @@ -0,0 +1,61 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import Libraries, {removeLibaryWork} from '/imports/api/library/Libraries.js'; +import Creatures from '/imports/api/creature/Creatures.js'; +import {removeCreatureWork} from '/imports/api/creature/removeCreature.js'; + +Meteor.users.deleteMyAccount = new ValidatedMethod({ + name: 'users.deleteMyAccount', + validate: null, + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 1, + timeInterval: 5000, + }, + run(){ + let userId = Meteor.userId(); + if (!userId) throw new Meteor.Error('No user', + 'You must be logged into to delete your account'); + + // Delete all creatures + let creatures = Creatures.find({owner: userId}, {fields: {_id: 1}}).fetch(); + creatures.forEach(creature => removeCreatureWork(creature._id)); + + // Remove permissions from all creatures + Creatures.update({ + $or: [ + {writers: userId}, + {readers: userId}, + ], + }, { + $pull: { + writers: userId, + readers: userId + }, + }, { + multi: true, + }); + + // Delete all libraries + let libraries = Libraries.find({owner: userId}, {fields: {_id: 1}}).fetch(); + libraries.forEach(library => removeLibaryWork(library._id)); + + // Remove permissions from all creatures + Libraries.update({ + $or: [ + {writers: userId}, + {readers: userId}, + ], + }, { + $pull: { + writers: userId, + readers: userId + }, + }, { + multi: true, + }); + + // delete the account + Meteor.users.remove(userId); + } +}); diff --git a/app/imports/server/publications/index.js b/app/imports/server/publications/index.js index 148fa279..c11cb255 100644 --- a/app/imports/server/publications/index.js +++ b/app/imports/server/publications/index.js @@ -7,3 +7,4 @@ import '/imports/server/publications/users.js'; import '/imports/server/publications/icons.js'; import '/imports/server/publications/tabletops.js'; import '/imports/server/publications/slotFillers.js' +import '/imports/server/publications/ownedDocuments.js' diff --git a/app/imports/server/publications/ownedDocuments.js b/app/imports/server/publications/ownedDocuments.js new file mode 100644 index 00000000..317b4e83 --- /dev/null +++ b/app/imports/server/publications/ownedDocuments.js @@ -0,0 +1,15 @@ +import Creatures from '/imports/api/creature/Creatures.js'; +import Libraries from '/imports/api/library/Libraries.js'; + +Meteor.publish('ownedDocuments', function(){ + this.autorun(function (){ + let userId = this.userId; + if (!userId) { + return []; + } + return [ + Creatures.find({owner: userId}), + Libraries.find({owner: userId}), + ] + }); +}); diff --git a/app/imports/ui/dialogStack/DialogComponentIndex.js b/app/imports/ui/dialogStack/DialogComponentIndex.js index ac0331c0..d19e27b7 100644 --- a/app/imports/ui/dialogStack/DialogComponentIndex.js +++ b/app/imports/ui/dialogStack/DialogComponentIndex.js @@ -4,6 +4,7 @@ import CreaturePropertyCreationDialog from '/imports/ui/creature/creaturePropert import CreaturePropertyDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue' import CreaturePropertyFromLibraryDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyFromLibraryDialog.vue' import DeleteConfirmationDialog from '/imports/ui/dialogStack/DeleteConfirmationDialog.vue'; +import DeleteUserAccountDialog from '/imports/ui/user/DeleteUserAccountDialog.vue'; import ExperienceInsertDialog from '/imports/ui/creature/experiences/ExperienceInsertDialog.vue'; import ExperienceListDialog from '/imports/ui/creature/experiences/ExperienceListDialog.vue'; import InviteDialog from '/imports/ui/user/InviteDialog.vue'; @@ -26,6 +27,7 @@ export default { CreaturePropertyDialog, CreaturePropertyFromLibraryDialog, DeleteConfirmationDialog, + DeleteUserAccountDialog, ExperienceInsertDialog, ExperienceListDialog, InviteDialog, diff --git a/app/imports/ui/layouts/AppLayout.vue b/app/imports/ui/layouts/AppLayout.vue index 7483d81e..9eb13fba 100644 --- a/app/imports/ui/layouts/AppLayout.vue +++ b/app/imports/ui/layouts/AppLayout.vue @@ -13,7 +13,7 @@ name="toolbar" /> + + + Delete Account + + @@ -270,6 +283,12 @@ this.updatePatreonLoading = false; if (error) this.updatePatreonError = error; }); + }, + deleteAccount(){ + this.$store.commit('pushDialogStack', { + component: 'delete-user-account-dialog', + elementId: 'delete-account-btn', + }); } }, } diff --git a/app/imports/ui/user/DeleteUserAccountDialog.vue b/app/imports/ui/user/DeleteUserAccountDialog.vue new file mode 100644 index 00000000..bebf2032 --- /dev/null +++ b/app/imports/ui/user/DeleteUserAccountDialog.vue @@ -0,0 +1,153 @@ + + + + +