mirror of
https://github.com/HeyPuter/puter.git
synced 2026-05-04 08:30:39 +00:00
fix(fs) optimize stat operation. (#2649)
This commit is contained in:
@@ -69,6 +69,8 @@ module.exports = class FSNodeContext {
|
||||
NodePathSelector,
|
||||
];
|
||||
|
||||
#writable;
|
||||
|
||||
/**
|
||||
* Creates an instance of FSNodeContext.
|
||||
* @param {*} opt_identifier
|
||||
@@ -401,30 +403,35 @@ module.exports = class FSNodeContext {
|
||||
|
||||
this.entry.subdomains = [];
|
||||
this.entry.workers = [];
|
||||
let subdomains = await db.read('SELECT * FROM subdomains WHERE root_dir_id = ? AND user_id = ?',
|
||||
[this.entry.id, user.id]);
|
||||
let subdomains = await db.read(
|
||||
'SELECT * FROM subdomains WHERE root_dir_id = ? AND user_id = ?',
|
||||
[this.entry.id, user.id],
|
||||
);
|
||||
if ( subdomains.length > 0 ) {
|
||||
subdomains.forEach((sd) => {
|
||||
if ( this.entry.is_dir ) {
|
||||
this.entry.subdomains.push({
|
||||
subdomain: sd.subdomain,
|
||||
address: `${config.protocol }://${ sd.subdomain }.` + 'puter.site',
|
||||
uuid: sd.uuid,
|
||||
});
|
||||
} else {
|
||||
const workerName = sd.subdomain.split('.').pop();
|
||||
this.entry.workers.push({
|
||||
subdomain: workerName,
|
||||
address: `https://${ workerName }.` + 'puter.work',
|
||||
uuid: sd.uuid,
|
||||
});
|
||||
}
|
||||
|
||||
this.applySingleSubdomain(sd);
|
||||
});
|
||||
this.entry.has_website = true;
|
||||
}
|
||||
}
|
||||
|
||||
applySingleSubdomain (sd) {
|
||||
if ( this.entry.is_dir ) {
|
||||
this.entry.subdomains.push({
|
||||
subdomain: sd.subdomain,
|
||||
address: `${config.protocol }://${ sd.subdomain }.` + 'puter.site',
|
||||
uuid: sd.uuid,
|
||||
});
|
||||
} else {
|
||||
const workerName = sd.subdomain.split('.').pop();
|
||||
this.entry.workers.push({
|
||||
subdomain: workerName,
|
||||
address: `https://${ workerName }.` + 'puter.work',
|
||||
uuid: sd.uuid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the owner of a directory or file and stores it on the
|
||||
* `owner` property of the fsentry.
|
||||
@@ -535,8 +542,10 @@ module.exports = class FSNodeContext {
|
||||
|
||||
const db = this.services.get('database').get(DB_READ, 'filesystem');
|
||||
|
||||
let versions = await db.read('SELECT * FROM fsentry_versions WHERE fsentry_id = ?',
|
||||
[this.entry.id]);
|
||||
let versions = await db.read(
|
||||
'SELECT * FROM fsentry_versions WHERE fsentry_id = ?',
|
||||
[this.entry.id],
|
||||
);
|
||||
const versions_tidy = [];
|
||||
for ( const version of versions ) {
|
||||
let username = version.user_id ? (await get_user({ id: version.user_id })).username : null;
|
||||
@@ -599,7 +608,7 @@ module.exports = class FSNodeContext {
|
||||
await this.fetchIsEmpty();
|
||||
}
|
||||
|
||||
async get (key) {
|
||||
async get (key, force) {
|
||||
/*
|
||||
This isn't supposed to stay like this!
|
||||
|
||||
@@ -719,10 +728,11 @@ module.exports = class FSNodeContext {
|
||||
}
|
||||
|
||||
if ( key === 'writable' ) {
|
||||
if ( this.#writable && !force ) return this.#writable;
|
||||
const actor = Context.get('actor');
|
||||
if ( !actor || !actor.type.user ) return undefined;
|
||||
const svc_acl = this.services.get('acl');
|
||||
return await svc_acl.check(actor, this, 'write');
|
||||
return this.#writable = await svc_acl.check(actor, this, 'write');
|
||||
}
|
||||
|
||||
throw new Error(`unrecognize key for FSNodeContext.get: ${key}`);
|
||||
|
||||
@@ -54,41 +54,86 @@ class HLStat extends HLFilesystemOperation {
|
||||
const user_unix_ts = Number((`${Date.parse(Context.get('actor')?.type?.user?.timestamp)}`).slice(0, -3));
|
||||
const paths_are_fine = user_unix_ts >= 1722385593;
|
||||
|
||||
if ( maybe_uid_selector || paths_are_fine ) {
|
||||
// We are able to fetch the entry and is_empty simultaneously
|
||||
await Promise.all([
|
||||
subject.fetchEntry(),
|
||||
subject.fetchIsEmpty(),
|
||||
]);
|
||||
} else {
|
||||
// We need the entry first in order for is_empty to work correctly
|
||||
await subject.fetchEntry();
|
||||
await subject.fetchIsEmpty();
|
||||
const do_after_fetchEntry = [];
|
||||
const do_alongside_fetchEntry = [];
|
||||
|
||||
if ( return_size ) {
|
||||
do_after_fetchEntry.push(async () => {
|
||||
await subject.fetchSize(user);
|
||||
});
|
||||
}
|
||||
|
||||
if ( return_subdomains ) {
|
||||
do_after_fetchEntry.push(async () => {
|
||||
await subject.fetchSubdomains(user);
|
||||
});
|
||||
}
|
||||
|
||||
if ( return_shares || return_permissions ) {
|
||||
do_after_fetchEntry.push(async () => {
|
||||
await subject.fetchShares();
|
||||
});
|
||||
}
|
||||
|
||||
if ( return_versions ) {
|
||||
do_after_fetchEntry.push(async () => {
|
||||
await subject.fetchVersions();
|
||||
});
|
||||
}
|
||||
|
||||
do_after_fetchEntry.push(async () => {
|
||||
await subject.fetchOwner();
|
||||
}, async () => {
|
||||
await subject.get('writable');
|
||||
});
|
||||
|
||||
((maybe_uid_selector || paths_are_fine)
|
||||
? do_alongside_fetchEntry
|
||||
: do_after_fetchEntry).push(subject.fetchIsEmpty.bind(subject));
|
||||
|
||||
// if ( maybe_uid_selector || paths_are_fine ) {
|
||||
// await Promise.all([
|
||||
// subject.fetchEntry(),
|
||||
// subject.fetchIsEmpty(),
|
||||
// ]);
|
||||
// } else {
|
||||
// // We need the entry first in order for is_empty to work correctly
|
||||
// await subject.fetchEntry();
|
||||
// await subject.fetchIsEmpty();
|
||||
// }
|
||||
|
||||
await Promise.all([
|
||||
(async () => {
|
||||
await subject.fetchEntry();
|
||||
const context = Context.get();
|
||||
const svc_acl = context.get('services').get('acl');
|
||||
const actor = context.get('actor');
|
||||
if ( ! await svc_acl.check(actor, subject, 'read') ) {
|
||||
throw await svc_acl.get_safe_acl_error(actor, subject, 'read');
|
||||
}
|
||||
if ( ! subject.found ) {
|
||||
throw APIError.create('subject_does_not_exist');
|
||||
}
|
||||
await Promise.all(do_after_fetchEntry.map(f => f()));
|
||||
})(),
|
||||
...(do_alongside_fetchEntry.map(f => f())),
|
||||
]);
|
||||
|
||||
// file not found
|
||||
if ( ! subject.found ) throw APIError.create('subject_does_not_exist');
|
||||
|
||||
await subject.fetchOwner();
|
||||
|
||||
const context = Context.get();
|
||||
const svc_acl = context.get('services').get('acl');
|
||||
const actor = context.get('actor');
|
||||
if ( ! await svc_acl.check(actor, subject, 'read') ) {
|
||||
throw await svc_acl.get_safe_acl_error(actor, subject, 'read');
|
||||
}
|
||||
// await subject.fetchOwner();
|
||||
|
||||
// TODO: why is this specific to stat?
|
||||
const mime = this.require('mime-types');
|
||||
const contentType = mime.contentType(subject.entry.name);
|
||||
subject.entry.type = contentType ? contentType : null;
|
||||
|
||||
if ( return_size ) await subject.fetchSize(user);
|
||||
if ( return_subdomains ) await subject.fetchSubdomains(user);
|
||||
if ( return_shares || return_permissions ) {
|
||||
await subject.fetchShares();
|
||||
}
|
||||
if ( return_versions ) await subject.fetchVersions();
|
||||
// if ( return_size ) await subject.fetchSize(user);
|
||||
// if ( return_subdomains ) await subject.fetchSubdomains(user);
|
||||
// if ( return_shares || return_permissions ) {
|
||||
// await subject.fetchShares();
|
||||
// }
|
||||
// if ( return_versions ) await subject.fetchVersions();
|
||||
|
||||
return await subject.getSafeEntry();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user