193 lines
5.7 KiB
JavaScript
193 lines
5.7 KiB
JavaScript
const uuid = require('uuid').v4
|
|
const { Model } = require('flitter-orm')
|
|
const { NodeDescriptorType, NodeModeType } = require('../../enum')
|
|
|
|
class Node extends Model {
|
|
static get services() {
|
|
return [...super.services, 'models']
|
|
}
|
|
|
|
static get schema() {
|
|
return {
|
|
uuid: { type: String, default: uuid },
|
|
pied_name: String,
|
|
pied_parent_path: { type: String, default: '/' },
|
|
overlay_name: { type: String, default: 'mainline' },
|
|
mtime: { type: Date, default: () => new Date },
|
|
atime: { type: Date, default: () => new Date },
|
|
ctime: { type: Date, default: () => new Date },
|
|
mode: { type: Number, default: NodeModeType.File },
|
|
size: { type: Number, default: 0 },
|
|
|
|
descriptor_type: { type: String, default: NodeDescriptorType.File },
|
|
uploaded_file_id: String,
|
|
|
|
symlink_path: String, // If NodeDescriptorType.Symlink, the path to resolve
|
|
|
|
extended_attributes: [{
|
|
name: String,
|
|
value: String, // base64 encoded binary buffer value
|
|
// There's position, but it's legacy for macOS, so going to exclude it for now.
|
|
}],
|
|
|
|
deleted: { type: Boolean, default: false },
|
|
root: { type: Boolean, default: false },
|
|
}
|
|
}
|
|
|
|
static path_parts(path) {
|
|
const path_parts = path.split('/')
|
|
const pied_name = path_parts.pop()
|
|
let pied_parent_path = path_parts.join('/')
|
|
if ( !pied_parent_path.startsWith('/') ) pied_parent_path = `/${pied_parent_path}`
|
|
|
|
return [pied_parent_path, pied_name]
|
|
}
|
|
|
|
static async get_root() {
|
|
let root = await this.findOne({
|
|
deleted: false, root: true, pied_parent_path: '/', pied_name: '/',
|
|
})
|
|
|
|
if ( !root ) {
|
|
root = new this({
|
|
pied_name: '/',
|
|
mode: NodeModeType.Directory,
|
|
root: true,
|
|
descriptor_type: NodeDescriptorType.Directory,
|
|
})
|
|
|
|
await root.save()
|
|
}
|
|
|
|
return root
|
|
}
|
|
|
|
static async get_path(path, workspace = 'mainline') {
|
|
if ( path === '/' ) return this.get_root()
|
|
|
|
const [pied_parent_path, pied_name] = this.path_parts(path)
|
|
|
|
const nodes = await this.find({
|
|
pied_name,
|
|
pied_parent_path,
|
|
overlay_name: {
|
|
$in: [workspace, 'mainline'],
|
|
},
|
|
deleted: false,
|
|
root: false,
|
|
})
|
|
|
|
if ( nodes.length === 1 ) {
|
|
return nodes[0]
|
|
}
|
|
|
|
if ( nodes.length === 2 ) {
|
|
return nodes.find(x => x.overlay_name === workspace)
|
|
}
|
|
}
|
|
|
|
static async list_path(path, workspace = 'mainline') {
|
|
const nodes = await this.find({
|
|
pied_parent_path: path,
|
|
overlay_name: workspace,
|
|
// deleted: false,
|
|
root: false,
|
|
})
|
|
|
|
const mainline_nodes = await this.find({
|
|
pied_name: {
|
|
$nin: nodes.map(x => x.pied_name),
|
|
},
|
|
pied_parent_path: path,
|
|
overlay_name: 'mainline',
|
|
deleted: false,
|
|
root: false,
|
|
})
|
|
|
|
return [...nodes, ...mainline_nodes].filter(x => !x.deleted)
|
|
}
|
|
|
|
async uploaded_file() {
|
|
if ( !this.uploaded_file_id ) return;
|
|
|
|
const File = this.models.get('upload::File')
|
|
return File.findById(this.uploaded_file_id)
|
|
}
|
|
|
|
async all_descendants(workspace = 'mainline') {
|
|
const formatted_parent = `${this.pied_parent_path === '/' ? '/' : this.pied_parent_path + '/'}${this.pied_name}`
|
|
const nodes = await this.constructor.find({
|
|
pied_parent_path: {
|
|
$regex: this.root ? `/.*` : `(?:${formatted_parent}/.*)|(?:${formatted_parent}$)`,
|
|
},
|
|
overlay_name: workspace,
|
|
root: false,
|
|
// deleted: false,
|
|
})
|
|
|
|
const mainline_nodes = await this.constructor.find({
|
|
$and: [
|
|
{
|
|
pied_parent_path: {
|
|
$regex: this.root ? `/.*` : `(?:${formatted_parent}/.*)|(?:${formatted_parent}$)`,
|
|
},
|
|
overlay_name: 'mainline',
|
|
root: false,
|
|
deleted: false,
|
|
},
|
|
...nodes.map(node => {
|
|
return {
|
|
pied_parent_path: {
|
|
$ne: node.pied_parent_path,
|
|
},
|
|
pied_name: {
|
|
$ne: node.pied_name,
|
|
},
|
|
}
|
|
})
|
|
],
|
|
})
|
|
|
|
return [...nodes, ...mainline_nodes].filter(x => !x.deleted)
|
|
}
|
|
|
|
set_xattr(name, value) {
|
|
this.guarantee_xattrs()
|
|
this.remove_xattr(name)
|
|
this.extended_attributes.push({ name, value })
|
|
}
|
|
|
|
get_xattr(name) {
|
|
this.guarantee_xattrs()
|
|
return this.extended_attributes.find(attr => attr.name === name)
|
|
}
|
|
|
|
remove_xattr(name) {
|
|
this.guarantee_xattrs()
|
|
this.extended_attributes = this.extended_attributes.filter(attr => attr.name !== name)
|
|
}
|
|
|
|
guarantee_xattrs() {
|
|
if ( !Array.isArray(this.extended_attributes) ) {
|
|
this.extended_attributes = []
|
|
}
|
|
}
|
|
|
|
to_api() {
|
|
return {
|
|
pied_name: this.pied_name,
|
|
mtime: this.mtime,
|
|
atime: this.atime,
|
|
ctime: this.ctime,
|
|
mode: this.mode,
|
|
nlink: 1,
|
|
uid: 0, // TODO
|
|
gid: 0, // TODO
|
|
size: this.size,
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = exports = Node
|