Files
project-manager/src/models/Adapters/FSAdapter.ts
2024-10-09 17:47:31 +02:00

170 lines
4.7 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unused-vars */
import { objectLoop } from '@dzeio/object-util'
import type Schema from 'libs/Schema'
import type { Model, ModelInfer } from 'libs/Schema'
import type SchemaBuffer from 'libs/Schema/Items/SchemaBuffer'
import type SchemaNumber from 'libs/Schema/Items/SchemaNumber'
import type SchemaString from 'libs/Schema/Items/SchemaString'
import fileSystem from 'node:fs'
import fs from 'node:fs/promises'
import type { Query } from '../Query'
import type DaoAdapter from './DaoAdapter'
import type { DBPull } from './DaoAdapter'
interface FS extends Model {
filename: SchemaString
path: SchemaString
// eslint-disable-next-line no-undef
data: SchemaBuffer
type: SchemaString
size: SchemaNumber
}
export default class FSAdapter<T extends FS> implements DaoAdapter<Schema<T>> {
public constructor(
public readonly schema: Schema<T>,
public readonly basePath: string
) {
if (basePath.endsWith('/')) {
console.warn('the base path should not end wiath a "/", removing it')
basePath = basePath.slice(0, basePath.lastIndexOf('/'))
}
}
// TODO: make it clearer what it does
public async create(obj: Partial<ModelInfer<T>>): Promise<ModelInfer<T> | null> {
const realPath = this.getFullPath(obj.path!)
const finalFolder = realPath.slice(0, realPath.lastIndexOf('/'))
console.log('making the directory', finalFolder)
await fs.mkdir(finalFolder, { recursive: true })
if (obj.type === 'file') {
console.log('getting the data', finalFolder)
const data = obj.data
console.log('writing to', realPath)
if ((data as any) instanceof Buffer) {
await fs.writeFile(realPath, data as Buffer)
} else {
await fs.writeFile(realPath, data as string)
}
return obj as ModelInfer<T>
} else {
console.log('making the final directory', realPath)
await fs.mkdir(realPath)
return obj as ModelInfer<T>
}
}
// eslint-disable-next-line complexity
public async read(query?: Query<ModelInfer<T>> | undefined): Promise<DBPull<Schema<T>>> {
const localPath = query?.path as string ?? ''
const realPath = this.getFullPath(localPath)
console.log('get the full path', realPath)
try {
const stats = await fs.stat(realPath)
const files: Array<ModelInfer<T>> = []
if (stats.isDirectory()) {
const dirFiles = await fs.readdir(realPath)
for await (const file of dirFiles) {
files.push(await this.readFile(`${localPath}/${file}`))
}
} else {
files.push(await this.readFile(localPath))
}
const pageLimit = query?.$limit ?? Infinity
const pageOffset = query?.$offset ?? 0
return {
rows: files.length,
rowsTotal: files.length,
page: Math.floor(pageOffset / pageLimit),
pageTotal: Math.max(1, Math.ceil(files.length / pageLimit)),
data: files.slice(pageOffset, pageOffset + pageLimit)
}
} catch {
return {
rows: 0,
rowsTotal: 0,
page: 0,
pageTotal: 0,
data: []
}
}
}
public async update(_obj: ModelInfer<T>): Promise<ModelInfer<T> | null> {
throw new Error('not implemented')
}
public async patch(_id: string, _obj: Partial<ModelInfer<T>>): Promise<ModelInfer<T> | null> {
throw new Error('not implemented')
}
public async delete(obj: ModelInfer<T>): Promise<boolean> {
const localPath = obj?.path as string ?? ''
const realPath = this.getFullPath(localPath)
try {
await fs.stat(realPath)
await fs.rm(realPath, { recursive: true, force: true })
return true
} catch {
console.error('Could not remove file', localPath)
return false
}
}
private getFullPath(localPath?: string): string {
if (localPath && !localPath?.startsWith('/')) {
console.warn('Your path should start with a "/", adding it')
localPath = (`/${localPath}`)
}
let realPath = this.basePath + (localPath ? localPath : '')
if (realPath.includes('\\')) {
realPath = realPath.replace(/\\/g, '/')
}
return realPath
}
private async readFile(localPath: string): Promise<ModelInfer<T>> {
const path = this.getFullPath(localPath)
console.log('reading file at', path)
const stats = await fs.stat(path)
const type = stats.isFile() ? 'file' : 'directory'
console.log('file is a', type)
const obj: ModelInfer<T> = {
path: localPath,
filename: localPath.slice(localPath.lastIndexOf('/') + 1),
data: type === 'file' ? await fs.readFile(path) : '',
type: type,
size: stats.size
} as any
objectLoop(this.schema.model, (item, key) => {
if (item.attributes.includes('db:created')) {
// @ts-expect-error things get validated anyway
obj[key] = stats.ctime
} else if (item.attributes.includes('db:updated')) {
// @ts-expect-error things get validated anyway
obj[key] = stats.mtime
}
})
return obj
}
}