1.0.14
This commit is contained in:
@@ -13,82 +13,151 @@ import { FolderPaths } from "../util/serverInit.js";
|
||||
/** @description Bills will use the hierarchy of PDFs stored under the Job first, and then the Bills folder. */
|
||||
export async function BillsListMedia(req: Request, res: Response) {
|
||||
const jobid: string = (req.body.jobid || "").trim();
|
||||
//const vendorid: string = (req.body.vendorid || "").trim();
|
||||
const invoice_number: string = (req.body.invoice_number || "").trim();
|
||||
let ret: MediaFile[];
|
||||
//Ensure all directories exist.
|
||||
await fs.ensureDir(PathToRoBillsFolder(jobid));
|
||||
|
||||
if (req.files) {
|
||||
ret = await Promise.all(
|
||||
(req.files as Express.Multer.File[]).map(async (file) => {
|
||||
const relativeFilePath: string = path.join(PathToRoBillsFolder(jobid), file.filename);
|
||||
try {
|
||||
if (req.files) {
|
||||
const uploadedFiles = await processUploadedBillFiles(req.files as Express.Multer.File[], jobid);
|
||||
if (!res.headersSent) res.json(uploadedFiles);
|
||||
} else {
|
||||
const existingFiles = await processExistingBillFiles(jobid, invoice_number);
|
||||
if (!res.headersSent) res.json(existingFiles);
|
||||
}
|
||||
} catch (error) {
|
||||
// Optionally add logger here if you use one
|
||||
if (!res.headersSent) res.status(500).json(error);
|
||||
}
|
||||
}
|
||||
|
||||
const relativeThumbPath: string = await GenerateThumbnail(relativeFilePath);
|
||||
const type: FileTypeResult | undefined = await fileTypeFromFile(relativeFilePath);
|
||||
async function processUploadedBillFiles(files: Express.Multer.File[], jobid: string): Promise<MediaFile[]> {
|
||||
const processFile = async (file: Express.Multer.File): Promise<MediaFile> => {
|
||||
const relativeFilePath: string = path.join(PathToRoBillsFolder(jobid), file.filename);
|
||||
|
||||
try {
|
||||
const relativeThumbPath: string = await GenerateThumbnail(relativeFilePath);
|
||||
const type: FileTypeResult | undefined = await Promise.race([
|
||||
fileTypeFromFile(relativeFilePath),
|
||||
new Promise<undefined>((resolve) => setTimeout(() => resolve(undefined), 5000))
|
||||
]);
|
||||
|
||||
return {
|
||||
type,
|
||||
size: file.size,
|
||||
src: GenerateUrl([
|
||||
FolderPaths.StaticPath,
|
||||
FolderPaths.JobsFolder,
|
||||
jobid,
|
||||
FolderPaths.BillsSubDir,
|
||||
file.filename
|
||||
]),
|
||||
thumbnail: GenerateUrl([
|
||||
FolderPaths.StaticPath,
|
||||
FolderPaths.JobsFolder,
|
||||
jobid,
|
||||
FolderPaths.BillsSubDir,
|
||||
relativeThumbPath
|
||||
]),
|
||||
thumbnailHeight: 250,
|
||||
thumbnailWidth: 250,
|
||||
filename: file.filename,
|
||||
name: file.filename,
|
||||
path: relativeFilePath,
|
||||
thumbnailPath: relativeThumbPath
|
||||
};
|
||||
} catch (error) {
|
||||
// Return basic info if thumbnail/type fails
|
||||
return {
|
||||
type: undefined,
|
||||
size: file.size,
|
||||
src: GenerateUrl([
|
||||
FolderPaths.StaticPath,
|
||||
FolderPaths.JobsFolder,
|
||||
jobid,
|
||||
FolderPaths.BillsSubDir,
|
||||
file.filename
|
||||
]),
|
||||
thumbnail: GenerateUrl([FolderPaths.StaticPath, "assets", "file.svg"]),
|
||||
thumbnailHeight: 250,
|
||||
thumbnailWidth: 250,
|
||||
filename: file.filename,
|
||||
name: file.filename,
|
||||
path: relativeFilePath,
|
||||
thumbnailPath: ""
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return (await Promise.all(files.map(processFile))).filter((r): r is MediaFile => r !== null);
|
||||
}
|
||||
|
||||
async function processExistingBillFiles(jobid: string, invoice_number: string): Promise<MediaFile[]> {
|
||||
let filesList: fs.Dirent[] = (
|
||||
await fs.readdir(PathToRoBillsFolder(jobid), {
|
||||
withFileTypes: true
|
||||
})
|
||||
).filter(
|
||||
(f) =>
|
||||
f.isFile() &&
|
||||
!/(^|\/)\.[^\/\.]/g.test(f.name) &&
|
||||
(invoice_number !== "" ? f.name.toLowerCase().includes(invoice_number.toLowerCase()) : true) &&
|
||||
ListableChecker(f)
|
||||
);
|
||||
|
||||
const processFile = async (file: fs.Dirent): Promise<MediaFile | null> => {
|
||||
const relativeFilePath: string = path.join(PathToRoBillsFolder(jobid), file.name);
|
||||
|
||||
try {
|
||||
if (!(await fs.pathExists(relativeFilePath))) {
|
||||
return null;
|
||||
}
|
||||
const fileStats = await Promise.race([
|
||||
fs.stat(relativeFilePath),
|
||||
new Promise<never>((_, reject) => setTimeout(() => reject(new Error("File stat timeout")), 5000))
|
||||
]);
|
||||
const relativeThumbPath: string = await GenerateThumbnail(relativeFilePath);
|
||||
const type: FileTypeResult | undefined = await Promise.race([
|
||||
fileTypeFromFile(relativeFilePath),
|
||||
new Promise<undefined>((resolve) => setTimeout(() => resolve(undefined), 5000))
|
||||
]);
|
||||
return {
|
||||
type,
|
||||
size: fileStats.size,
|
||||
src: GenerateUrl([FolderPaths.StaticPath, FolderPaths.JobsFolder, jobid, FolderPaths.BillsSubDir, file.name]),
|
||||
thumbnail: GenerateUrl([
|
||||
FolderPaths.StaticPath,
|
||||
FolderPaths.JobsFolder,
|
||||
jobid,
|
||||
FolderPaths.BillsSubDir,
|
||||
relativeThumbPath
|
||||
]),
|
||||
thumbnailHeight: 250,
|
||||
thumbnailWidth: 250,
|
||||
filename: file.name,
|
||||
name: file.name,
|
||||
path: relativeFilePath,
|
||||
thumbnailPath: relativeThumbPath
|
||||
};
|
||||
} catch (error) {
|
||||
try {
|
||||
const fileStats = await fs.stat(relativeFilePath);
|
||||
return {
|
||||
type,
|
||||
size: file.size,
|
||||
src: GenerateUrl([
|
||||
FolderPaths.StaticPath,
|
||||
FolderPaths.JobsFolder,
|
||||
jobid,
|
||||
FolderPaths.BillsSubDir,
|
||||
file.filename
|
||||
]),
|
||||
thumbnail: GenerateUrl([
|
||||
FolderPaths.StaticPath,
|
||||
FolderPaths.JobsFolder,
|
||||
jobid,
|
||||
FolderPaths.BillsSubDir,
|
||||
relativeThumbPath
|
||||
]),
|
||||
thumbnailHeight: 250,
|
||||
thumbnailWidth: 250,
|
||||
filename: file.filename,
|
||||
relativeFilePath
|
||||
};
|
||||
})
|
||||
);
|
||||
} else {
|
||||
let filesList: fs.Dirent[] = (
|
||||
await fs.readdir(PathToRoBillsFolder(jobid), {
|
||||
withFileTypes: true
|
||||
})
|
||||
).filter(
|
||||
(f) =>
|
||||
f.isFile() &&
|
||||
!/(^|\/)\.[^\/\.]/g.test(f.name) &&
|
||||
(invoice_number !== "" ? f.name.toLowerCase().includes(invoice_number.toLowerCase()) : true) &&
|
||||
ListableChecker(f)
|
||||
);
|
||||
|
||||
ret = await Promise.all(
|
||||
filesList.map(async (file) => {
|
||||
const relativeFilePath: string = path.join(PathToRoBillsFolder(jobid), file.name);
|
||||
|
||||
const relativeThumbPath: string = await GenerateThumbnail(relativeFilePath);
|
||||
const type: FileTypeResult | undefined = await fileTypeFromFile(relativeFilePath);
|
||||
const fileSize = await fs.stat(relativeFilePath);
|
||||
return {
|
||||
type,
|
||||
size: fileSize.size,
|
||||
type: undefined,
|
||||
size: fileStats.size,
|
||||
src: GenerateUrl([FolderPaths.StaticPath, FolderPaths.JobsFolder, jobid, FolderPaths.BillsSubDir, file.name]),
|
||||
thumbnail: GenerateUrl([
|
||||
FolderPaths.StaticPath,
|
||||
FolderPaths.JobsFolder,
|
||||
jobid,
|
||||
FolderPaths.BillsSubDir,
|
||||
relativeThumbPath
|
||||
]),
|
||||
thumbnail: GenerateUrl([FolderPaths.StaticPath, "assets", "file.svg"]),
|
||||
thumbnailHeight: 250,
|
||||
thumbnailWidth: 250,
|
||||
filename: file.name,
|
||||
relativeFilePath
|
||||
name: file.name,
|
||||
path: relativeFilePath,
|
||||
thumbnailPath: ""
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
res.json(ret);
|
||||
return (await Promise.all(filesList.map(processFile))).filter((r): r is MediaFile => r !== null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user