Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions app/imports/api/creature/archive/methods/archiveCreatureToFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
import Experiences from '/imports/api/creature/experience/Experiences';
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature';
import { toSoftArchive } from '/imports/api/creature/archive/methods/softArchiveCreature';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
import { getFilter } from '/imports/api/parenting/parentingFunctions';

Expand All @@ -34,7 +35,7 @@ export function getArchiveObj(creatureId) {
return archiveCreature;
}

export const archiveCreature = Meteor.wrapAsync(function archiveCreatureFn(creatureId, callback) {
export const archiveCreature = Meteor.wrapAsync(function archiveCreatureFn(creatureId, autoArchive, callback) {
const archive = getArchiveObj(creatureId);
const buffer = Buffer.from(JSON.stringify(archive, null, 2));
ArchiveCreatureFiles.write(buffer, {
Expand All @@ -45,14 +46,19 @@ export const archiveCreature = Meteor.wrapAsync(function archiveCreatureFn(creat
schemaVersion: SCHEMA_VERSION,
creatureId: archive.creature._id,
creatureName: archive.creature.name,
auto: !!autoArchive,
},
}, (error, fileRef) => {
if (error) {
// If there is an error already, just call the callback
callback(error);
} else if (!Meteor.settings.useS3) {
// If we aren't using s3, remove the creature and call the callback
removeCreatureWork(creatureId);
if (autoArchive) {
toSoftArchive(creatureId, fileRef._id);
} else {
removeCreatureWork(creatureId);
}
callback();
} else {
// Wait for s3Result event that occurs when the s3 attempt to write ends.
Expand All @@ -64,7 +70,11 @@ export const archiveCreature = Meteor.wrapAsync(function archiveCreatureFn(creat
ArchiveCreatureFiles.off('s3Result', resultHandler);
// Remove the creature if there was no error
if (!s3Error) {
removeCreatureWork(creatureId);
if (autoArchive) {
toSoftArchive(creatureId, fileRef._id);
} else {
removeCreatureWork(creatureId);
}
}
// Alert the callback that we're done
callback(s3Error);
Expand All @@ -74,6 +84,26 @@ export const archiveCreature = Meteor.wrapAsync(function archiveCreatureFn(creat
}, true);
});

export function softArchiveToArchive(creatureId) {
let creature = Creatures.findOne({ _id: creatureId });
Creatures.remove({ _id: creatureId });
ArchiveCreatureFiles.update(
{
_id: creature.archiveId,
'meta.creatureId': creature._id,
'meta.auto': true,
},
{
$set: { 'meta.auto': false },
}, error => {
this.archiveActionLoading = false;
if (!error) return;
console.error(error);
snackbar({text: error.reason});
}
);
}

const archiveCreatureToFile = new ValidatedMethod({
name: 'Creatures.methods.archiveCreatureToFile',
validate: new SimpleSchema({
Expand All @@ -90,7 +120,12 @@ const archiveCreatureToFile = new ValidatedMethod({
async run({ creatureId }) {
assertOwnership(creatureId, this.userId);
if (Meteor.isServer) {
archiveCreature(creatureId);
let creature = Creatures.findOne({ _id: creatureId }, { fields: { archiveId: 1 }});
if (creature.archiveId) {
softArchiveToArchive(creatureId);
} else {
archiveCreature(creatureId, false);
}
} else {
removeCreatureWork(creatureId);
}
Expand Down
10 changes: 10 additions & 0 deletions app/imports/api/creature/archive/methods/removeArchiveCreature.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
import Creatures from '/imports/api/creature/creatures/Creatures';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed';

const removeArchiveCreature = new ValidatedMethod({
Expand Down Expand Up @@ -30,6 +31,15 @@ const removeArchiveCreature = new ValidatedMethod({
throw new Meteor.Error('Permission denied',
'You can only restore creatures you own');
}
if (file.meta.auto) {
Creatures.remove(
{
_id: file.meta.creatureId,
owner: this.userId,
archiveId: file._id,
}
);
}
//Remove the archive once the restore succeeded
ArchiveCreatureFiles.remove({ _id: fileId });
// Update the user's file storage limits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,84 @@ const restoreCreaturefromFile = new ValidatedMethod({
},
});

export const restoreSoftArchive = Meteor.wrapAsync(async function restoreSoftArchiveFn(creatureId, callback) {
if (!Meteor.isServer) return;

// Verify that the creature and archive match
const existingCreature = Creatures.findOne(
{ _id: creatureId },
{
fields: {
_id: 1,
owner: 1,
archiveId: 1,
},
}
);
if (!existingCreature) throw new Meteor.Error('Doesn\'t exist',
'The creature you are trying to restore doesn\'t exist');

if (!existingCreature.archiveId) throw new Meteor.Error('No Linked Archive',
'The creature doesn\'t have a linked archive.');

const archiveFile = ArchiveCreatureFiles.findOne({
_id: existingCreature.archiveId,
userId: existingCreature.owner,
'meta.auto': true,
'meta.creatureId': creatureId,
});

const archive = await ArchiveCreatureFiles.readJSONFile(archiveFile);

if (SCHEMA_VERSION < archive.meta.schemaVersion) {
throw new Meteor.Error('Incompatible',
'The archive file is from a newer version. Update required to read.')
}

// Migrate and verify the archive meets the current schema
migrateArchive(archive);

// Asset that the archive is safe
verifyArchiveSafety(archive);

try {
// Add all the properties
if (archive.properties && archive.properties.length) {
CreatureProperties.batchInsert(archive.properties);
}
if (archive.experiences && archive.experiences.length) {
Experiences.batchInsert(archive.experiences);
}
if (archive.logs && archive.logs.length) {
CreatureLogs.batchInsert(archive.logs);
}

Creatures.update(
{ _id: creatureId },
{ $unset: {
archiveId: true,
}}
);
} catch (e) {
// If the above fails, delete the inserted creature
CreatureVariables.remove({ _creatureId: creatureId });
CreatureProperties.remove(getFilter.descendantsOfRoot(creatureId));
CreatureLogs.remove({ creatureId });
Experiences.remove({ creatureId });
Creatures.update(
{ _id: creatureId },
{ $set: {
archiveId: archiveFile._id,
}}
);
console.log(e);
throw e;
}
//Remove the archive once the restore succeeded
ArchiveCreatureFiles.remove({ _id: archiveFile._id });
// Update the user's file storage limits
incrementFileStorageUsed(archiveFile.userId, -archiveFile.size);
callback();
});

export default restoreCreaturefromFile;
27 changes: 27 additions & 0 deletions app/imports/api/creature/archive/methods/softArchiveCreature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Creatures from '/imports/api/creature/creatures/Creatures';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
import Experiences from '/imports/api/creature/experience/Experiences';
import { getFilter } from '/imports/api/parenting/parentingFunctions';

function removeRelatedDocuments(creatureId) {
CreatureVariables.remove({ _creatureId: creatureId });
CreatureProperties.remove(getFilter.descendantsOfRoot(creatureId));
CreatureLogs.remove({ creatureId });
Experiences.remove({ creatureId });
}

export function toSoftArchive(creatureId, archiveId) {
if (!Meteor.isServer) return;

Creatures.update(
{ _id: creatureId },
{
$set: {
archiveId: archiveId,
},
}
);
removeRelatedDocuments(creatureId);
}
6 changes: 6 additions & 0 deletions app/imports/api/creature/creatures/Creatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ const CreatureSchema = TypedSimpleSchema.from({
type: CreatureSettingsSchema,
defaultValue: {},
},

// Was auto archived
'archiveId': {
type: String,
optional: true,
},
})
.extend(ColorSchema)
.extend(SharingSchema);
Expand Down
10 changes: 5 additions & 5 deletions app/imports/api/users/methods/updateFileStorageUsed.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
import UserImages from '/imports/api/files/userImages/UserImages';
const fileCollections = [ArchiveCreatureFiles, UserImages];

const updateFileStorageUsed = new ValidatedMethod({
name: 'users.recalculateFileStorageUsed',
Expand Down Expand Up @@ -33,10 +32,11 @@ export function updateFileStorageUsedWork(userId) {
}

let sum = 0;
fileCollections.forEach(collection => {
collection.find({ userId }, { fields: { size: 1 } }).forEach(file => {
sum += file.size;
});
ArchiveCreatureFiles.find({ userId }, { fields: { size: 1 } }).forEach(file => {
sum += file.size;
});
UserImages.find({ userId }, { fields: { size: 1 } }).forEach(file => {
sum += file.size;
});

Meteor.users.update(userId, {
Expand Down
31 changes: 20 additions & 11 deletions app/imports/client/ui/creature/archive/ArchiveDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
selection
:creatures="mode === 'archive' ? CreaturesWithNoParty : archiveCreaturesWithNoParty"
:folders="mode === 'archive' ? folders : archivefolders"
:selected-creature="selectedCreature"
@creature-selected="id => selectedCreature = id"
:selected-creature-id="selectedCreatureId"
@creature-selected="id => selectedCreatureId = id"
/>
<v-spacer slot="actions" />
<v-btn
Expand Down Expand Up @@ -94,6 +94,7 @@ const creatureFields = {
'readers': 1,
'writers': 1,
'owner': 1,
'archiveId': 1,
};

export default {
Expand All @@ -102,44 +103,44 @@ export default {
CreatureFolderList,
},
data(){return {
selectedCreature: null,
selectedCreatureId: null,
mode: 'archive',
archiveActionLoading: false,
}},
computed: {
numSelected(){
return this.selectedCreature ? 1 : 0;
return this.selectedCreatureId ? 1 : 0;
},
},
watch: {
mode(){
this.selectedCreature = null;
this.selectedCreatureId = null;
},
},
methods: {
archiveAction(){
if (!this.selectedCreature) return;
if (!this.selectedCreatureId) return;
this.archiveActionLoading = true;
if (this.mode === 'archive'){
if (this.mode === 'archive') {
archiveCreatureToFile.call({
creatureId: this.selectedCreature,
creatureId: this.selectedCreatureId,
}, error => {
this.archiveActionLoading = false;
if (!error) return;
console.error(error);
snackbar({text: error.reason});
});
} else if (this.mode === 'restore'){
} else if (this.mode === 'restore') {
restoreCreatureFromFile.call({
fileId: this.selectedCreature,
fileId: this.selectedCreatureId,
}, error => {
this.archiveActionLoading = false;
if (!error) return;
console.error(error);
snackbar({text: error.reason});
});
}
this.selectedCreature = null;
this.selectedCreatureId = null;
}
},
meteor: {
Expand Down Expand Up @@ -194,6 +195,10 @@ export default {
folder.creatures = ArchiveCreatureFiles.find(
{
'meta.creatureId': {$in: folder.creatures || []},
$or: [
{ 'meta.auto': false },
{ 'meta.auto': { $exists: false } },
],
userId,
}, {
sort: {'meta.creatureName': 1},
Expand All @@ -211,6 +216,10 @@ export default {
return ArchiveCreatureFiles.find(
{
'meta.creatureId': {$nin: folderChars},
$or: [
{ 'meta.auto': false },
{ 'meta.auto': { $exists: false } },
],
userId,
}, {
sort: {'meta.creatureName': 1},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<creature-list
:creatures="creatures"
:selection="selection"
:selected-creature="selectedCreature"
:selected-creature-id="selectedCreatureId"
:dense="dense"
@creature-selected="id => $emit('creature-selected', id)"
/>
Expand All @@ -34,7 +34,7 @@
:creatures="folder.creatures"
:folder-id="folder._id"
:selection="selection"
:selected-creature="selectedCreature"
:selected-creature-id="selectedCreatureId"
:dense="dense"
@creature-selected="id => $emit('creature-selected', id)"
/>
Expand Down Expand Up @@ -62,7 +62,7 @@ export default {
default: () => [],
},
selection: Boolean,
selectedCreature: {
selectedCreatureId: {
type: String,
default: undefined,
},
Expand Down
Loading