@restroom-mw/files
Advanced tools
Comparing version 0.13.1-9445368.2 to 0.13.1-01c588e.79
export declare const DOWNLOAD = "download the {} and extract it into {}"; | ||
export declare const STORE_RESULT = "store {} in the file {}"; | ||
export declare const READ = "read the content of {}"; | ||
export declare const READ_AND_SAVE = "read the content of {} and save the output into {}"; | ||
export declare const LS = "list the content of directory {} as {}"; | ||
//# sourceMappingURL=actions.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.STORE_RESULT = exports.DOWNLOAD = void 0; | ||
exports.LS = exports.READ_AND_SAVE = exports.READ = exports.STORE_RESULT = exports.DOWNLOAD = void 0; | ||
exports.DOWNLOAD = "download the {} and extract it into {}"; | ||
exports.STORE_RESULT = "store {} in the file {}"; | ||
exports.READ = "read the content of {}"; | ||
exports.READ_AND_SAVE = "read the content of {} and save the output into {}"; | ||
exports.LS = "list the content of directory {} as {}"; |
import { NextFunction, Request, Response } from "express"; | ||
/** | ||
* Base dir to store data for the user | ||
* @constant | ||
* @type {string} | ||
*/ | ||
export declare const FILES_DIR: string; | ||
declare const _default: (req: Request, res: Response, next: NextFunction) => void; | ||
export default _default; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -15,7 +15,12 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.FILES_DIR = void 0; | ||
const core_1 = require("@restroom-mw/core"); | ||
const axios_1 = __importDefault(require("axios")); | ||
const fs_1 = __importDefault(require("fs")); | ||
const promises_1 = require("node:fs/promises"); | ||
const os_1 = __importDefault(require("os")); | ||
const extract_zip_1 = __importDefault(require("extract-zip")); | ||
const path_1 = __importDefault(require("path")); | ||
const utils_1 = require("@restroom-mw/utils"); | ||
require("dotenv").config(); | ||
/** | ||
@@ -35,4 +40,91 @@ * `download the 'myUrl' and extract it into 'myFolder'` | ||
const actions_2 = require("./actions"); | ||
const actions_3 = require("./actions"); | ||
/** | ||
* Base dir to store data for the user | ||
* @constant | ||
* @type {string} | ||
*/ | ||
exports.FILES_DIR = process.env.FILES_DIR; | ||
let input = {}; | ||
// save path must be subdirs of FILES_DIR | ||
const validatePath = (0, utils_1.validateSubdir)(exports.FILES_DIR); | ||
exports.default = (req, res, next) => { | ||
const rr = new core_1.Restroom(req, res); | ||
rr.onBefore((params) => __awaiter(void 0, void 0, void 0, function* () { | ||
const { zencode, keys, data } = params; | ||
input = rr.combineDataKeys(data, keys); | ||
const readFromFile = (file) => { | ||
let content; | ||
const f = input[file] || file; | ||
const absoluteFile = path_1.default.resolve(path_1.default.join(exports.FILES_DIR, f)); | ||
validatePath(absoluteFile); | ||
try { | ||
content = fs_1.default.readFileSync(absoluteFile, 'utf8'); | ||
} | ||
catch (e) { | ||
// TODO: add not-fatal (warning) log in restroom | ||
//throw new Error(`[FILES] error while reading the file ${absoluteFile}`); | ||
content = "{}"; | ||
} | ||
return JSON.parse(content); | ||
}; | ||
if (zencode.match(actions_3.READ)) { | ||
const params = zencode.paramsOf(actions_3.READ); | ||
for (const f of params) { | ||
Object.assign(data, readFromFile(f)); | ||
} | ||
} | ||
; | ||
if (zencode.match(actions_3.READ_AND_SAVE)) { | ||
const chkParams = zencode.chunkedParamsOf(actions_3.READ_AND_SAVE, 2); | ||
for (const [f, outputVariable] of chkParams) { | ||
data[outputVariable] = readFromFile(f); | ||
} | ||
} | ||
; | ||
if (zencode.match(actions_3.LS)) { | ||
const params = zencode.chunkedParamsOf(actions_3.LS, 2); | ||
const fileStats = {}; | ||
const allLs = (yield Promise.all(params.map(([p, name]) => __awaiter(void 0, void 0, void 0, function* () { | ||
const f = input[p] || p; | ||
validatePath(f); | ||
try { | ||
const content = yield (0, promises_1.readdir)(f); | ||
// I am not checking if `name` is used multiple times | ||
fileStats[name] = []; | ||
return content.map((current) => [name, f, current]); | ||
} | ||
catch (e) { | ||
throw new Error(`[FILES] error while reading the file ${f}`); | ||
} | ||
})))).flat(); | ||
// list with all files I want to see the stats of | ||
const allStats = yield Promise.all(allLs.map(([name, p, current]) => __awaiter(void 0, void 0, void 0, function* () { | ||
const currentFile = path_1.default.join(p, current); | ||
const fileStat = yield (0, promises_1.stat)(currentFile); | ||
return [name, current, fileStat]; | ||
}))); | ||
for (const [name, current, currentStat] of allStats) { | ||
// see https://unix.stackexchange.com/questions/317855/file-mode-on-macosx#317907 | ||
// for the meaning of the mode field | ||
fileStats[name].push({ | ||
'name': current, | ||
'mode': currentStat.mode.toString(8), | ||
'dev': currentStat.dev, | ||
'nlink': currentStat.nlink, | ||
'uid': currentStat.uid, | ||
'gid': currentStat.gid, | ||
'size': currentStat.size, | ||
'blksize': currentStat.blksize, | ||
'blocks': currentStat.blocks, | ||
'atime': currentStat.atime.toISOString(), | ||
'mtime': currentStat.mtime.toISOString(), | ||
'ctime': currentStat.ctime.toISOString(), | ||
'birthtime': currentStat.birthtime.toISOString(), | ||
}); | ||
} | ||
Object.assign(data, fileStats); | ||
} | ||
input = rr.combineDataKeys(data, keys); | ||
})); | ||
rr.onSuccess((params) => __awaiter(void 0, void 0, void 0, function* () { | ||
@@ -43,25 +135,29 @@ const { result, zencode } = params; | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const file = result[allPassOutputs[i]]; | ||
const folder = result[allPassOutputs[i + 1]]; | ||
if (file && folder) { | ||
try { | ||
const absoluteFolder = path_1.default.resolve(folder); | ||
const response = yield axios_1.default.get(file, { | ||
responseType: 'arraybuffer' | ||
}); | ||
const tempdir = fs_1.default.mkdtempSync("/tmp/restroom"); | ||
const tempfile = tempdir + "/downloaded"; | ||
fs_1.default.writeFileSync(tempfile, response.data); | ||
yield (0, extract_zip_1.default)(tempfile, { dir: absoluteFolder }); | ||
fs_1.default.unlinkSync(tempfile); | ||
fs_1.default.rmdirSync(tempdir); | ||
} | ||
catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error sending the result to "${file}": ${e}`); | ||
} | ||
const file = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const folder = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
if (!file) { | ||
throw new Error(`[FILES] url not defined`); | ||
} | ||
else { | ||
throw new Error(`[FILES] url or destination folder not defined`); | ||
if (!folder) { | ||
throw new Error(`[FILES] destination folder not defined ${Object.keys(input)}`); | ||
} | ||
try { | ||
const absoluteFolder = path_1.default.resolve(path_1.default.join(exports.FILES_DIR, folder)); | ||
validatePath(absoluteFolder); | ||
const response = yield axios_1.default.get(file, { | ||
responseType: 'arraybuffer' | ||
}); | ||
const tempdir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'restroom-')); | ||
const tempfile = path_1.default.join(tempdir, "downloaded"); | ||
fs_1.default.writeFileSync(tempfile, response.data); | ||
yield (0, extract_zip_1.default)(tempfile, { dir: absoluteFolder }); | ||
fs_1.default.unlinkSync(tempfile); | ||
fs_1.default.rmdirSync(tempdir); | ||
} | ||
catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error sending the result to "${file}": ${e}`); | ||
} | ||
} | ||
@@ -72,20 +168,24 @@ } | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const variable = result[allPassOutputs[i]]; | ||
const file = result[allPassOutputs[i + 1]]; | ||
if (variable && file) { | ||
const variableJson = JSON.stringify(variable); | ||
try { | ||
const absoluteFile = path_1.default.resolve(file); | ||
const absoluteFolder = path_1.default.dirname(absoluteFile); | ||
fs_1.default.mkdirSync(absoluteFolder, { recursive: true }); | ||
fs_1.default.writeFileSync(absoluteFile, variableJson); | ||
} | ||
catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error saving the result to "${file}": ${e}`); | ||
} | ||
const variable = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const file = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
if (!variable) { | ||
throw new Error(`[FILES] variable not defined`); | ||
} | ||
else { | ||
throw new Error(`[FILES] variable or destination folder not defined`); | ||
if (!file) { | ||
throw new Error(`[FILES] destination file not defined`); | ||
} | ||
const variableJson = JSON.stringify(variable); | ||
try { | ||
const absoluteFile = path_1.default.resolve(path_1.default.join(exports.FILES_DIR, file)); | ||
validatePath(absoluteFile); | ||
const absoluteFolder = path_1.default.dirname(absoluteFile); | ||
fs_1.default.mkdirSync(absoluteFolder, { recursive: true }); | ||
fs_1.default.writeFileSync(absoluteFile, variableJson); | ||
} | ||
catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error saving the result to "${file}": ${e}`); | ||
} | ||
} | ||
@@ -92,0 +192,0 @@ } |
{ | ||
"name": "@restroom-mw/files", | ||
"version": "0.13.1-9445368.2+9445368", | ||
"version": "0.13.1-01c588e.79+01c588e", | ||
"description": "Utilities middleware to work with files for Restroom", | ||
@@ -35,3 +35,3 @@ "author": "Alberto Lerda <alberto@dyne.org>", | ||
}, | ||
"gitHead": "94453683c017217fed572458127775169bff10c3" | ||
"gitHead": "01c588e01a51bba105b5076be7f7d007b266cd59" | ||
} |
@@ -1,2 +0,5 @@ | ||
export const DOWNLOAD= "download the {} and extract it into {}"; | ||
export const STORE_RESULT= "store {} in the file {}"; | ||
export const DOWNLOAD = "download the {} and extract it into {}"; | ||
export const STORE_RESULT = "store {} in the file {}"; | ||
export const READ = "read the content of {}"; | ||
export const READ_AND_SAVE = "read the content of {} and save the output into {}"; | ||
export const LS = "list the content of directory {} as {}"; |
190
src/index.ts
@@ -1,8 +0,13 @@ | ||
import { Restroom } from "@restroom-mw/core"; | ||
import {Restroom} from "@restroom-mw/core"; | ||
import axios from "axios"; | ||
import { NextFunction, Request, Response } from "express"; | ||
import {NextFunction, Request, Response} from "express"; | ||
import fs from 'fs' | ||
import { readdir, stat } from 'node:fs/promises'; | ||
import os from 'os' | ||
import extract from 'extract-zip'; | ||
import path from 'path'; | ||
import {ObjectLiteral} from "@restroom-mw/types"; | ||
import {validateSubdir} from "@restroom-mw/utils" | ||
require("dotenv").config(); | ||
/** | ||
@@ -14,3 +19,3 @@ * `download the 'myUrl' and extract it into 'myFolder'` | ||
*/ | ||
import { DOWNLOAD } from "./actions"; | ||
import {DOWNLOAD} from "./actions"; | ||
/** | ||
@@ -22,36 +27,134 @@ * `store 'myVariable' in the file 'myFolder'` | ||
*/ | ||
import {STORE_RESULT} from "./actions"; | ||
import { STORE_RESULT } from "./actions"; | ||
import {READ, READ_AND_SAVE, LS} from "./actions"; | ||
/** | ||
* Base dir to store data for the user | ||
* @constant | ||
* @type {string} | ||
*/ | ||
export const FILES_DIR = process.env.FILES_DIR; | ||
let input: ObjectLiteral = {}; | ||
// save path must be subdirs of FILES_DIR | ||
const validatePath = validateSubdir(FILES_DIR); | ||
export default (req: Request, res: Response, next: NextFunction) => { | ||
const rr = new Restroom(req, res); | ||
rr.onBefore(async (params) => { | ||
const { zencode, keys, data } = params; | ||
input = rr.combineDataKeys(data, keys); | ||
const readFromFile = ( file: string ) => { | ||
let content | ||
const f = input[file] || file; | ||
const absoluteFile = path.resolve(path.join(FILES_DIR, f)); | ||
validatePath(absoluteFile); | ||
try { | ||
content = fs.readFileSync(absoluteFile, 'utf8'); | ||
} catch(e) { | ||
// TODO: add not-fatal (warning) log in restroom | ||
//throw new Error(`[FILES] error while reading the file ${absoluteFile}`); | ||
content = "{}"; | ||
} | ||
return JSON.parse(content); | ||
} | ||
if (zencode.match(READ)) { | ||
const params = zencode.paramsOf(READ); | ||
for(const f of params) { | ||
Object.assign(data, readFromFile(f)); | ||
} | ||
}; | ||
if (zencode.match(READ_AND_SAVE)) { | ||
const chkParams = zencode.chunkedParamsOf(READ_AND_SAVE, 2); | ||
for(const [f, outputVariable] of chkParams) { | ||
data[ outputVariable ] = readFromFile(f); | ||
} | ||
}; | ||
if (zencode.match(LS)) { | ||
const params = zencode.chunkedParamsOf(LS, 2); | ||
const fileStats: Record<string, any> = {} | ||
const allLs = (await Promise.all(params.map( | ||
async ([p, name]: string[]) => { | ||
const f = input[p] || p; | ||
validatePath(f); | ||
try { | ||
const content = await readdir(f) | ||
// I am not checking if `name` is used multiple times | ||
fileStats[name] = [] | ||
return content.map((current) => [name, f, current]); | ||
} catch(e) { | ||
throw new Error(`[FILES] error while reading the file ${f}`); | ||
} | ||
}))).flat() | ||
// list with all files I want to see the stats of | ||
const allStats = await Promise.all(allLs.map(async ([name, p, current]) => { | ||
const currentFile = path.join(p, current) | ||
const fileStat = await stat(currentFile) | ||
return [name, current, fileStat] | ||
})) | ||
for(const [name, current, currentStat] of allStats) { | ||
// see https://unix.stackexchange.com/questions/317855/file-mode-on-macosx#317907 | ||
// for the meaning of the mode field | ||
fileStats[name].push({ | ||
'name': current, | ||
'mode': currentStat.mode.toString(8), | ||
'dev': currentStat.dev, | ||
'nlink': currentStat.nlink, | ||
'uid': currentStat.uid, | ||
'gid': currentStat.gid, | ||
'size': currentStat.size, | ||
'blksize': currentStat.blksize, | ||
'blocks': currentStat.blocks, | ||
'atime': currentStat.atime.toISOString(), | ||
'mtime': currentStat.mtime.toISOString(), | ||
'ctime': currentStat.ctime.toISOString(), | ||
'birthtime': currentStat.birthtime.toISOString(), | ||
}) | ||
} | ||
Object.assign(data, fileStats) | ||
} | ||
input = rr.combineDataKeys(data, keys); | ||
}); | ||
rr.onSuccess(async (params) => { | ||
const { result, zencode } = params; | ||
const {result, zencode} = params; | ||
if (zencode.match(DOWNLOAD)) { | ||
const allPassOutputs = zencode.paramsOf(DOWNLOAD); | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const file = result[allPassOutputs[i]] | ||
const folder = result[allPassOutputs[i + 1]] | ||
const file = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const folder = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
if (file && folder) { | ||
try { | ||
const absoluteFolder = path.resolve(folder); | ||
const response = await axios.get(file, { | ||
responseType: 'arraybuffer' | ||
}); | ||
const tempdir = fs.mkdtempSync("/tmp/restroom"); | ||
const tempfile = tempdir + "/downloaded"; | ||
fs.writeFileSync(tempfile, response.data); | ||
await extract(tempfile, { dir: absoluteFolder }); | ||
fs.unlinkSync(tempfile); | ||
fs.rmdirSync(tempdir); | ||
} catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error sending the result to "${file}": ${e}`); | ||
} | ||
if(!file) { | ||
throw new Error(`[FILES] url not defined`); | ||
} | ||
else { | ||
throw new Error(`[FILES] url or destination folder not defined`); | ||
if(!folder) { | ||
throw new Error(`[FILES] destination folder not defined ${Object.keys(input)}`); | ||
} | ||
try { | ||
const absoluteFolder = path.resolve(path.join(FILES_DIR, folder)); | ||
validatePath(absoluteFolder); | ||
const response = await axios.get(file, { | ||
responseType: 'arraybuffer' | ||
}); | ||
const tempdir = fs.mkdtempSync(path.join(os.tmpdir(), 'restroom-')); | ||
const tempfile = path.join(tempdir, "downloaded"); | ||
fs.writeFileSync(tempfile, response.data); | ||
await extract(tempfile, { dir: absoluteFolder }); | ||
fs.unlinkSync(tempfile); | ||
fs.rmdirSync(tempdir); | ||
} catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error sending the result to "${file}": ${e}`); | ||
} | ||
} | ||
@@ -62,22 +165,27 @@ } | ||
for (let i = 0; i < allPassOutputs.length; i += 2) { | ||
const variable = result[allPassOutputs[i]] | ||
const file = result[allPassOutputs[i + 1]] | ||
const variable = result[allPassOutputs[i]] || | ||
input[allPassOutputs[i]]; | ||
const file = result[allPassOutputs[i + 1]] || | ||
input[allPassOutputs[i + 1]]; | ||
if (variable && file) { | ||
const variableJson = JSON.stringify(variable) | ||
try { | ||
const absoluteFile = path.resolve(file); | ||
if(!variable) { | ||
throw new Error(`[FILES] variable not defined`); | ||
} | ||
if(!file) { | ||
throw new Error(`[FILES] destination file not defined`); | ||
} | ||
const absoluteFolder = path.dirname(absoluteFile); | ||
fs.mkdirSync(absoluteFolder, { recursive: true }); | ||
const variableJson = JSON.stringify(variable) | ||
try { | ||
const absoluteFile = path.resolve(path.join(FILES_DIR, file)); | ||
validatePath(absoluteFile); | ||
fs.writeFileSync(absoluteFile, variableJson); | ||
} catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error saving the result to "${file}": ${e}`); | ||
} | ||
const absoluteFolder = path.dirname(absoluteFile); | ||
fs.mkdirSync(absoluteFolder, { recursive: true }); | ||
fs.writeFileSync(absoluteFile, variableJson); | ||
} catch (e) { | ||
next(e); | ||
throw new Error(`[FILES] Error saving the result to "${file}": ${e}`); | ||
} | ||
else { | ||
throw new Error(`[FILES] variable or destination folder not defined`); | ||
} | ||
} | ||
@@ -84,0 +192,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
52946
391
2
2