refactor: rm legacy FSAccessContext and systemfs

This commit is contained in:
KernelDeimos 2024-12-31 10:34:07 -05:00
parent 539b31cc5b
commit f7196991fc
15 changed files with 13 additions and 243 deletions

View File

@ -32,7 +32,7 @@ module.exports = class FSNodeParam {
async consolidate ({ req, getParam }) { async consolidate ({ req, getParam }) {
const log = globalThis.services.get('log-service').create('fsnode-param'); const log = globalThis.services.get('log-service').create('fsnode-param');
const fs = req.fs ?? Context.get('services').get('filesystem'); const fs = Context.get('services').get('filesystem');
let uidOrPath = getParam(this.srckey); let uidOrPath = getParam(this.srckey);
if ( uidOrPath === undefined ) { if ( uidOrPath === undefined ) {

View File

@ -1,117 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const FSNodeContext = require("./FSNodeContext");
const { NodePathSelector, NodeUIDSelector, NodeInternalIDSelector } = require("./node/selectors");
/**
* Container for access implementations.
*
* Access implementations may vary depending on region,
* user privileges, and other factors.
*
* @class FSAccessContext
*/
module.exports = class FSAccessContext {
constructor () {
this.fsEntryFetcher = null;
}
/**
* get_entry_by_path() returns a filesystem entry using
* the path to the entry. Use this method when you need
* to get a filesystem entry but don't need to collect
* any other information about the entry.
*
* @warning The entry returned by this method is not
* client-safe. Use FSNodeContext to get a client-safe
* entry by calling it's fetchEntry() method.
*
* @param {*} path
* @returns
* @deprecated use get_entry({ path }) instead
*/
async get_entry_by_path (path) {
return await this.get_entry({ path });
}
/**
* get_entry() returns a filesystem entry using
* path, uid, or id associated with a filesystem
* node. Use this method when you need to get a
* filesystem entry but don't need to collect any
* other information about the entry.
*
* @warning The entry returned by this method is not
* client-safe. Use FSNodeContext to get a client-safe
* entry by calling it's fetchEntry() method.
*
* @param {*} param0 options for getting the entry
* @param {*} param0.path
* @param {*} param0.uid
* @param {*} param0.id please use mysql_id instead
* @param {*} param0.mysql_id
*/
async get_entry ({ path, uid, id, mysql_id, ...options }) {
let fsNode = await this.node({ path, uid, id, mysql_id });
await fsNode.fetchEntry(options);
return fsNode.entry;
}
/**
* node() returns a filesystem node using path, uid,
* or id associated with a filesystem node. Use this
* method when you need to get a filesystem node and
* need to collect information about the entry.
*
* @param {*} location - path, uid, or id associated with a filesystem node
* @returns
*/
async node (selector) {
if ( typeof selector === 'string' ) {
if ( selector.startsWith('/') ) {
selector = new NodePathSelector(selector);
} else {
selector = new NodeUIDSelector(selector);
}
}
// TEMP: remove when these objects aren't used anymore
if (
typeof selector === 'object' &&
selector.constructor.name === 'Object'
) {
if ( selector.path ) {
selector = new NodePathSelector(selector.path);
} else if ( selector.uid ) {
selector = new NodeUIDSelector(selector.uid);
} else {
selector = new NodeInternalIDSelector(
'mysql', selector.mysql_id);
}
}
let fsNode = new FSNodeContext({
services: this.services,
selector,
fs: this
});
return fsNode;
}
};

View File

@ -19,7 +19,6 @@
// TODO: database access can be a service // TODO: database access can be a service
const { RESOURCE_STATUS_PENDING_CREATE } = require('../modules/puterfs/ResourceService.js'); const { RESOURCE_STATUS_PENDING_CREATE } = require('../modules/puterfs/ResourceService.js');
const { TraceService } = require('../services/TraceService.js'); const { TraceService } = require('../services/TraceService.js');
const FSAccessContext = require('./FSAccessContext.js');
const PerformanceMonitor = require('../monitor/PerformanceMonitor.js'); const PerformanceMonitor = require('../monitor/PerformanceMonitor.js');
const { NodePathSelector, NodeUIDSelector, NodeInternalIDSelector } = require('./node/selectors.js'); const { NodePathSelector, NodeUIDSelector, NodeInternalIDSelector } = require('./node/selectors.js');
const FSNodeContext = require('./FSNodeContext.js'); const FSNodeContext = require('./FSNodeContext.js');
@ -158,22 +157,6 @@ class FilesystemService extends BaseService {
})); }));
} }
/**
* @deprecated - temporary migration method
*/
get_systemfs () {
if ( ! this.systemfs_ ) {
this.systemfs_ = new FSAccessContext();
this.systemfs_.fsEntryFetcher = this.services.get('fsEntryFetcher');
this.systemfs_.fsEntryService = this.services.get('fsEntryService');
this.systemfs_.resourceService = this.services.get('resourceService');
this.systemfs_.sizeService = this.services.get('sizeService');
this.systemfs_.traceService = this.services.get('traceService');
this.systemfs_.services = this.services;
}
return this.systemfs_;
}
async mkshortcut ({ parent, name, user, target }) { async mkshortcut ({ parent, name, user, target }) {
// Access Control // Access Control

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const fs_ = require('fs');
const FSAccessContext = require("./FSAccessContext");
const DatabaseFSEntryFetcher = require("./storage/DatabaseFSEntryFetcher");
const { DatabaseFSEntryService } = require('./storage/DatabaseFSEntryService.js');
const { ResourceService } = require('./storage/ResourceService.js');
const { SizeService } = require('./storage/SizeService.js');
const config = require('../config.js');
const { TraceService: FSTracer } = require('../services/TraceService.js');
const systemfs = new FSAccessContext();
systemfs.fsEntryFetcher = new DatabaseFSEntryFetcher();
systemfs.fsEntryService = new DatabaseFSEntryService();
systemfs.resourceService = new ResourceService();
systemfs.sizeService = new SizeService();
systemfs.traceService = new FSTracer();
// Log usages every 10 seconds for debugging
if ( config.usages_debug ) setInterval(async ()=>{
await fs_.promises.writeFile('/tmp/user_usages.json', JSON.stringify(systemfs.sizeService.usages, null, 4));
}, 10*1000);
module.exports = systemfs;

View File

@ -30,12 +30,10 @@ const { Context } = require('./util/context');
const { NodeUIDSelector } = require('./filesystem/node/selectors'); const { NodeUIDSelector } = require('./filesystem/node/selectors');
const { stream_to_buffer } = require('./util/streamutil.js'); const { stream_to_buffer } = require('./util/streamutil.js');
let systemfs = null;
let services = null; let services = null;
const tmp_provide_services = async ss => { const tmp_provide_services = async ss => {
services = ss; services = ss;
await services.ready; await services.ready;
systemfs = services.get('filesystem').get_systemfs();
} }
async function is_empty(dir_uuid){ async function is_empty(dir_uuid){

View File

@ -1,47 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const FSAccessContext = require('../filesystem/FSAccessContext.js');
const fs = (req, res, next)=>{
const systemfs = req.services.get('filesystem').get_systemfs();
try {
const fs = new FSAccessContext();
fs.fsEntryFetcher = systemfs.fsEntryFetcher;
fs.fsEntryService = systemfs.fsEntryService;
fs.resourceService = systemfs.resourceService;
fs.sizeService = systemfs.sizeService;
fs.traceService = systemfs.traceService;
fs.user = req.user;
fs.services = req.services;
// TODO: Decorate with AuthEntryFetcher
req.fs = fs;
} catch (e) {
// TODO: log details about this error to another service
console.error(e);
return res.status(500).send({
// TODO: standardize 500 errors to avoid inference attacks
error: 'the operation could not be completed'
});
}
next();
}
module.exports = fs;

View File

@ -21,7 +21,6 @@ const multer = require('multer');
const multest = require('@heyputer/multest'); const multest = require('@heyputer/multest');
const api_error_handler = require('./api_error_handler.js'); const api_error_handler = require('./api_error_handler.js');
const fsBeforeMW = require('../../../middleware/fs.js');
const APIError = require('../../../api/APIError.js'); const APIError = require('../../../api/APIError.js');
const { Context } = require('../../../util/context.js'); const { Context } = require('../../../util/context.js');
const { subdomain } = require('../../../helpers.js'); const { subdomain } = require('../../../helpers.js');
@ -49,9 +48,6 @@ module.exports = function eggspress (route, settings, handler) {
if ( settings.abuse ) mw.push(require('../../../middleware/abuse')(settings.abuse)); if ( settings.abuse ) mw.push(require('../../../middleware/abuse')(settings.abuse));
if ( settings.auth ) mw.push(require('../../../middleware/auth')); if ( settings.auth ) mw.push(require('../../../middleware/auth'));
if ( settings.auth2 ) mw.push(require('../../../middleware/auth2')); if ( settings.auth2 ) mw.push(require('../../../middleware/auth2'));
if ( settings.fs ) {
mw.push(fsBeforeMW);
}
if ( settings.verified ) mw.push(require('../../../middleware/verified')); if ( settings.verified ) mw.push(require('../../../middleware/verified'));
if ( settings.json ) mw.push(express.json()); if ( settings.json ) mw.push(express.json());

View File

@ -21,7 +21,6 @@ const express = require('express');
const router = express.Router(); const router = express.Router();
const auth = require('../middleware/auth.js'); const auth = require('../middleware/auth.js');
const config = require('../config.js'); const config = require('../config.js');
const fs = require('../middleware/fs.js');
const { DB_WRITE } = require('../services/database/consts.js'); const { DB_WRITE } = require('../services/database/consts.js');
const { NodePathSelector } = require('../filesystem/node/selectors.js'); const { NodePathSelector } = require('../filesystem/node/selectors.js');
const { HLRead } = require('../filesystem/hl_operations/hl_read.js'); const { HLRead } = require('../filesystem/hl_operations/hl_read.js');
@ -30,7 +29,7 @@ const { UserActorType } = require('../services/auth/Actor.js');
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
// GET /down // GET /down
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
router.post('/down', auth, fs, express.json(), express.urlencoded({ extended: true }), async (req, res, next)=>{ router.post('/down', auth, express.json(), express.urlencoded({ extended: true }), async (req, res, next)=>{
// check subdomain // check subdomain
const actor = req.actor; const actor = req.actor;
@ -70,7 +69,8 @@ router.post('/down', auth, fs, express.json(), express.urlencoded({ extended: tr
return res.status(400).send('Cannot download a directory.'); return res.status(400).send('Cannot download a directory.');
// resolve path to its FSEntry // resolve path to its FSEntry
const fsnode = await req.fs.node(new NodePathSelector(path)); const svc_fs = req.services.get('filesystem');
const fsnode = await svc_fs.node(new NodePathSelector(path));
// not found // not found
if( ! fsnode.exists() ) { if( ! fsnode.exists() ) {

View File

@ -32,7 +32,6 @@ module.exports = eggspress('/batch', {
subdomain: 'api', subdomain: 'api',
verified: true, verified: true,
auth2: true, auth2: true,
fs: true,
// json: true, // json: true,
// files: ['file'], // files: ['file'],
// multest: true, // multest: true,
@ -85,7 +84,8 @@ module.exports = eggspress('/batch', {
} }
// Make sure usage is cached // Make sure usage is cached
await req.fs.sizeService.get_usage(req.user.id); const sizeService = x.get('services').get('sizeService');
await sizeService.get_usage(req.user.id);
globalThis.average_chunk_size = new MovingMode({ globalThis.average_chunk_size = new MovingMode({
alpha: 0.7, alpha: 0.7,

View File

@ -28,7 +28,6 @@ const FSNodeParam = require('../../api/filesystem/FSNodeParam.js');
module.exports = eggspress('/delete', { module.exports = eggspress('/delete', {
subdomain: 'api', subdomain: 'api',
auth2: true, auth2: true,
fs: true,
json: true, json: true,
allowedMethods: ['POST'], allowedMethods: ['POST'],
}, async (req, res, next) => { }, async (req, res, next) => {
@ -52,7 +51,7 @@ module.exports = eggspress('/delete', {
// TODO: remove this pseudo-batch // TODO: remove this pseudo-batch
for ( const item_path of paths ) { for ( const item_path of paths ) {
const target = await (new FSNodeParam('path')).consolidate({ const target = await (new FSNodeParam('path')).consolidate({
req: { fs: req.fs, user }, req: { user },
getParam: () => item_path, getParam: () => item_path,
}); });
const hl_remove = new HLRemove(); const hl_remove = new HLRemove();

View File

@ -22,7 +22,6 @@ const router = new express.Router();
const auth = require('../middleware/auth.js'); const auth = require('../middleware/auth.js');
const config = require('../config'); const config = require('../config');
const { Context } = require('../util/context.js'); const { Context } = require('../util/context.js');
const fs = require('../middleware/fs.js');
const { NodeInternalIDSelector } = require('../filesystem/node/selectors.js'); const { NodeInternalIDSelector } = require('../filesystem/node/selectors.js');
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//

View File

@ -29,7 +29,6 @@ const timeago = (() => {
const { get_taskbar_items, is_shared_with_anyone, suggest_app_for_fsentry, get_app, get_descendants, id2uuid } = require('../helpers'); const { get_taskbar_items, is_shared_with_anyone, suggest_app_for_fsentry, get_app, get_descendants, id2uuid } = require('../helpers');
const auth = require('../middleware/auth.js'); const auth = require('../middleware/auth.js');
const fs = require('../middleware/fs.js');
const _path = require('path'); const _path = require('path');
const eggspress = require('../api/eggspress'); const eggspress = require('../api/eggspress');
const { Context } = require('../util/context'); const { Context } = require('../util/context');
@ -122,7 +121,7 @@ const WHOAMI_GET = eggspress('/whoami', {
// POST /whoami // POST /whoami
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
const WHOAMI_POST = new express.Router(); const WHOAMI_POST = new express.Router();
WHOAMI_POST.post('/whoami', auth, fs, express.json(), async (req, response, next)=>{ WHOAMI_POST.post('/whoami', auth, express.json(), async (req, response, next)=>{
// check subdomain // check subdomain
if(require('../helpers').subdomain(req) !== 'api') { if(require('../helpers').subdomain(req) !== 'api') {
return; return;

View File

@ -37,7 +37,6 @@ const { HLRemove } = require('../filesystem/hl_operations/hl_remove');
// POST /writeFile // POST /writeFile
// -----------------------------------------------------------------------// // -----------------------------------------------------------------------//
module.exports = eggspress('/writeFile', { module.exports = eggspress('/writeFile', {
fs: true,
files: ['file'], files: ['file'],
allowedMethods: ['POST'], allowedMethods: ['POST'],
}, async (req, res, next) => { }, async (req, res, next) => {

View File

@ -21,6 +21,8 @@ module.exports = async function writeFile_handle_mkdir ({
actor, actor,
}); });
const newdir_node = await req.fs.node(new NodeUIDSelector(r.uid)); const svc_fs = req.services.get('filesystem');
const newdir_node = await svc_fs.node(new NodeUIDSelector(r.uid));
return res.send(await sign_file(await newdir_node.get('entry'), 'write')); return res.send(await sign_file(await newdir_node.get('entry'), 'write'));
}; };

View File

@ -25,7 +25,8 @@ module.exports = async function writeFile_handle_write ({
return res.status(500).send(e); return res.status(500).send(e);
} }
const dirNode = await req.fs.node(new NodePathSelector(dirname)); const svc_fs = req.services.get('filesystem');
const dirNode = await svc_fs.node(new NodePathSelector(dirname));
// Upload files one by one // Upload files one by one
const returns = []; const returns = [];