1 line
16 KiB
JavaScript
1 line
16 KiB
JavaScript
|
|
import fs from"node:fs";import path from"node:path";import NeDB from"nedb";import Files from"../../files/files.mjs";import LevelDatabase from"./level-database.mjs";import{DOCUMENT_OWNERSHIP_LEVELS}from"../../../common/constants.mjs";import{deepClone,isEmpty,isNewerVersion,getProperty,setProperty,getType}from"../../../common/utils/helpers.mjs";import*as fields from"../../../common/data/fields.mjs";import{DocumentStatsField,EmbeddedCollectionField,EmbeddedDocumentField}from"../../../common/data/fields.mjs";import Document from"../../../common/abstract/document.mjs";import{tagModelStats}from"../../core/utils.mjs";const DATABASE_STATES=Object.freeze({DISCONNECTED:0,CONNECTING:1,CONNECTION_FAILED:-1,CONNECTED:2,MIGRATING:3,MIGRATION_FAILED:-3,PARTIALLY_MIGRATED:4,FULLY_MIGRATED:5});export default function ServerDocumentMixin(e){class t extends e{constructor(e={},t={}){super(e,t);const{db:i,sublevelName:s,sublevel:a}=this._configureDB();Object.defineProperties(this,{db:{value:i,writable:!1},sublevelName:{value:s,writable:!1},sublevel:{value:a,writable:!1}})}static name="ServerDocumentMixin";static isCached=!1;static get sanitizedFields(){return t.#e}static#e;closestDeltaAncestor(){return this.parent?this.parent.constructor.isDelta?this.parent:this.parent.closestDeltaAncestor():null}_initialize(e={}){super._initialize(e),Object.defineProperty(this,"dbKey",{value:this._getDBKey(),writable:!1,configurable:!0})}static fromImport(e,t){if(!CONST.PRIMARY_DOCUMENT_TYPES.includes(this.documentName))throw new Error("Only primary Documents may be imported");return this._migrateRecord(e),this.fromSource(e,{strict:!0,...t})}static _migrationRegistry=[{fn:migratePermissionToOwnership,version:12}];static async migrateDocuments(){if(this._dbWait)return await this._dbWait,this.migrateDocuments();this._dbState=DATABASE_STATES.MIGRATING;try{this._dbWait=this._migrateDocuments(),this._dbWait.then((e=>{e?this._dbState=DATABASE_STATES.FULLY_MIGRATED:(this._dbState=DATABASE_STATES.PARTIALLY_MIGRATED,global.logger.warn(`Database ${this.collectionName} was not fully migrated due to errors during migration.`))})),await this._dbWait}catch(e){this._dbState=DATABASE_STATES.MIGRATION_FAILED,global.logger.error(`Migration failed for database "${this.collectionName}":\n${e.stack}`)}finally{this._dbWait=void 0}}static async _migrateDocuments(){if(!CONST.PRIMARY_DOCUMENT_TYPES.includes(this.documentName))throw new Error("Only primary Documents may be migrated");let e=!0;const t=await this.sublevel.find({},{map:e=>this.expandEmbedded(e)}),i=[];for(const s of t)try{if(this._migrateRecord(s)){this.sanitizeUserInput(s,{documentId:s._id,skipSystem:!0});const e=this.fromSource(s);i.push(e),global.logger.info(`Migrated ${this.documentName} record [${e._id}] of database "${this.collectionName}".`)}}catch(t){e=!1,global.logger.error(`An error occurred during the migration of ${this.documentName} record [${s._id}] of database "${this.collectionName}":\n${t.stack}`)}if(!i.length)return e;const s=this.db.batch();for(const e of i)global.logger.info(`Persisting migrated ${this.documentName} record [${e._id}] of database "${this.collectionName}".`),e.batchWrite(s,{generateIds:!0});return await s.write(),global.logger.info(`Completed migration of database "${this.collectionName}."`),e}static _migrateRecord(e,{ancestorStats:i,ancestorMigrated:s=!1}={}){if("Object"!==getType(e)||e._tombstone)return!1;let a=t.#t(e);const o=e._stats??i,n=o.coreVersion;if(n&&isNewerVersion(n,global.release.version))throw new Error("Documents from a core version newer than the running version cannot be migrated");const r=this._migrationRegistry;for(const{fn:t,version:i}of r)n&&!isNewerVersion(i,n)||t(e)&&(a=!0);(!n||isNewerVersion(this.metadata.schemaVersion,n))&&(a=!0);const d={ancestorStats:o,ancestorMigrated:a||s},c=this._migrateEmbeddedRecords(e,d);return a||=c,(a||s)&&e._stats&&e._stats.coreVersion!==global.release.version&&(e._stats.coreVersion=global.release.version,a=!0),a}static _migrateEmbeddedRecords(e,t){let i=!1;for(const[s,a]of Object.entries(this.hierarchy))if(a instanceof EmbeddedD
|