mirror of
https://github.com/HeyPuter/puter.git
synced 2025-01-23 14:20:22 +08:00
refactor: rm legacy FSAccessContext and systemfs
This commit is contained in:
parent
539b31cc5b
commit
f7196991fc
@ -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 ) {
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
@ -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
|
||||||
|
@ -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;
|
|
@ -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){
|
||||||
|
@ -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;
|
|
@ -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());
|
||||||
|
|
||||||
|
@ -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() ) {
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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');
|
||||||
|
|
||||||
// -----------------------------------------------------------------------//
|
// -----------------------------------------------------------------------//
|
||||||
|
@ -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;
|
||||||
|
@ -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) => {
|
||||||
|
@ -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'));
|
||||||
};
|
};
|
||||||
|
@ -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 = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user