import { DateTime } from 'luxon'; import { column, hasMany, HasMany, belongsTo, BelongsTo, SnakeCaseNamingStrategy, computed } from '@ioc:Adonis/Lucid/Orm'; import HashValue from './HashValue'; import Dataset from './Dataset'; import BaseModel from './BaseModel'; // import { Buffer } from 'buffer'; import * as fs from 'fs'; import crypto from 'crypto'; import { TransactionClientContract } from '@ioc:Adonis/Lucid/Database'; import Drive from '@ioc:Adonis/Core/Drive'; export default class File extends BaseModel { // private readonly _data: Uint8Array; // private readonly _type: string; // private readonly _size: number; public static namingStrategy = new SnakeCaseNamingStrategy(); public static primaryKey = 'id'; public static table = 'document_files'; public static selfAssignPrimaryKey = false; @column({ isPrimary: true, }) public id: number; @column({}) public document_id: number; @column({}) public pathName: string; @column() public label: string; @column() public comment: string; @column() public mimeType: string; @column() public language: string; @column() public fileSize: number; @column() public visibleInOai: boolean; @column() public visibleInFrontdoor: boolean; @column() public sortOrder: number; @column.dateTime({ autoCreate: true }) public createdAt: DateTime; @column.dateTime({ autoCreate: true, autoUpdate: true }) public updatedAt: DateTime; // public function dataset() // { // return $this->belongsTo(Dataset::class, 'document_id', 'id'); // } @belongsTo(() => Dataset, { foreignKey: 'document_id', }) public dataset: BelongsTo; @hasMany(() => HashValue, { foreignKey: 'file_id', }) public hashvalues: HasMany; @computed({ serializeAs: 'filePath', }) public get filePath() { return `/storage/app/public/${this.pathName}`; // const mainTitle = this.titles?.find((title) => title.type === 'Main'); // return mainTitle ? mainTitle.value : null; } @computed({ serializeAs: 'size', }) public get size() { return this.fileSize; } @computed({ serializeAs: 'type', }) public get type() { return this.mimeType; } @computed({ serializeAs: 'name', }) get name(): string { return this.label; } @computed({ serializeAs: 'lastModified', }) get lastModified(): number { return this.updatedAt.toUnixInteger(); //.toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); } readonly webkitRelativePath: string = ''; @computed({ serializeAs: 'fileData', }) public get fileData(): string { try { const fileContent: Buffer = fs.readFileSync(this.filePath); // Create a Blob from the file content // const blob = new Blob([fileContent], { type: this.type }); // Adjust // let fileSrc = URL.createObjectURL(blob); // return fileSrc; // create a JSON string that contains the data in the property "blob" const json = JSON.stringify({ blob: fileContent.toString('base64') }); return json; } catch (err) { // console.error(`Error reading file: ${err}`); return ''; } } public async createHashValues(trx?: TransactionClientContract) { const hashtypes: string[] = ['md5', 'sha512']; for (const type of hashtypes) { const hash = new HashValue(); hash.type = type; const hashString = await this._checksumFile(this.filePath, type); // Assuming getRealHash is a method in the same model hash.value = hashString; // https://github.com/adonisjs/core/discussions/1872#discussioncomment-132289 const file: File = this; if (trx) { await file.useTransaction(trx).related('hashvalues').save(hash); // Save the hash value to the database } else { await file.related('hashvalues').save(hash); // Save the hash value to the database } } } public async delete() { if (this.pathName) { // Delete file from additional storage await Drive.delete(this.pathName); } // Call the original delete method of the BaseModel to remove the record from the database await super.delete(); } private async _checksumFile(path, hashName = 'md5'): Promise { return new Promise((resolve, reject) => { const hash = crypto.createHash(hashName); const stream = fs.createReadStream(path); stream.on('error', (err) => reject(err)); stream.on('data', (chunk) => hash.update(chunk)); stream.on('end', () => resolve(hash.digest('hex'))); }); } }