node-hp-scan-to 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,146 @@
1
+ import axios, { AxiosError } from "axios";
2
+ import { ScanContent } from "../ScanContent";
3
+ import { NextcloudConfig } from "./NextcloudConfig";
4
+ import fs from "fs/promises";
5
+ import path from "path";
6
+
7
+ export async function uploadImagesToNextcloud(
8
+ scanJobContent: ScanContent,
9
+ nextcloudConfig: NextcloudConfig,
10
+ ) {
11
+ await checkFolderAndUpload(nextcloudConfig, async () => {
12
+ for (const element of scanJobContent.elements) {
13
+ const { path: filePath } = element;
14
+ await uploadToNextcloud(filePath, nextcloudConfig);
15
+ }
16
+ });
17
+ }
18
+
19
+ export async function uploadPdfToNextcloud(
20
+ pdfFilePath: string | null,
21
+ nextcloudConfig: NextcloudConfig,
22
+ ) {
23
+ await checkFolderAndUpload(nextcloudConfig, async () => {
24
+ if (pdfFilePath) {
25
+ await uploadToNextcloud(pdfFilePath, nextcloudConfig);
26
+ } else {
27
+ console.log(
28
+ "Pdf generation has failed, nothing is going to be uploaded to Nextcloud",
29
+ );
30
+ }
31
+ });
32
+ }
33
+
34
+ async function uploadToNextcloud(
35
+ filePath: string,
36
+ nextcloudConfig: NextcloudConfig,
37
+ ): Promise<void> {
38
+ const { baseUrl, username, password, uploadFolder } = nextcloudConfig;
39
+ const fileName = path.basename(filePath);
40
+
41
+ const folderUrl = buildFolderUrl(baseUrl, username, uploadFolder);
42
+ const uploadUrl = buildUrl(folderUrl, [fileName]);
43
+
44
+ let fileBuffer: Buffer;
45
+ try {
46
+ fileBuffer = await fs.readFile(filePath);
47
+ } catch (e) {
48
+ console.error("Fail to read file:", e);
49
+ return;
50
+ }
51
+ const auth = { username, password };
52
+
53
+ console.log(`Start uploading to Nextcloud: ${fileName}`);
54
+ try {
55
+ const response = await axios({
56
+ method: "PUT",
57
+ url: uploadUrl,
58
+ auth,
59
+ data: fileBuffer,
60
+ });
61
+
62
+ let action: string;
63
+ if (response.status === 201) {
64
+ action = "created";
65
+ } else {
66
+ action = "updated";
67
+ }
68
+ console.log(
69
+ `Document successfully ${action} file at Nextcloud. (Folder: ${uploadFolder}, File: ${fileName})`,
70
+ );
71
+ } catch (error) {
72
+ console.error("Fail to upload document:", error);
73
+ }
74
+ }
75
+
76
+ async function checkFolderAndUpload(
77
+ nextcloudConfig: NextcloudConfig,
78
+ uploadFunction: () => Promise<void>,
79
+ ) {
80
+ const folderExists = await checkNextcloudFolderExists(nextcloudConfig);
81
+ if (!folderExists) {
82
+ console.log(
83
+ "Upload folder does not exist or user has no permission; skipping upload",
84
+ );
85
+ return;
86
+ }
87
+
88
+ await uploadFunction();
89
+ }
90
+
91
+ async function checkNextcloudFolderExists(
92
+ nextcloudConfig: NextcloudConfig,
93
+ ): Promise<boolean> {
94
+ const { baseUrl, username, password, uploadFolder } = nextcloudConfig;
95
+ const folderUrl = buildFolderUrl(baseUrl, username, uploadFolder);
96
+ const auth = { username, password };
97
+
98
+ console.log("Check if upload folder exists");
99
+ try {
100
+ // Check if the upload folder exists
101
+ await axios({
102
+ method: "PROPFIND",
103
+ url: folderUrl,
104
+ auth,
105
+ headers: { Depth: 0 },
106
+ });
107
+ console.log(`Found upload folder '${uploadFolder}' in Nextcloud`);
108
+ } catch (error) {
109
+ const axiosError = error as AxiosError;
110
+ if (axiosError.response?.status === 404) {
111
+ console.warn(`Upload folder '${uploadFolder}' not found in Nextcloud`);
112
+ } else if (axiosError.response?.status === 401) {
113
+ console.warn(
114
+ `User has no permission to access upload folder '${uploadFolder}' in Nextcloud`,
115
+ );
116
+ } else {
117
+ console.error("Fail to check upload folder exists:", axiosError.toJSON());
118
+ }
119
+ console.trace("Error response:", axiosError.response);
120
+ return false;
121
+ }
122
+ return true;
123
+ }
124
+
125
+ function buildFolderUrl(
126
+ baseUrl: string,
127
+ username: string,
128
+ uploadFolder: string,
129
+ ) {
130
+ return buildUrl(baseUrl, [
131
+ "remote.php",
132
+ "dav",
133
+ "files",
134
+ username,
135
+ uploadFolder,
136
+ ]);
137
+ }
138
+
139
+ function buildUrl(baseUrl: string, path: string[]): string {
140
+ const url = new URL(baseUrl);
141
+ const search = /^\/+|\/+$/g;
142
+ path.forEach((part) => {
143
+ url.pathname = `${url.pathname.replace(search, "")}/${encodeURIComponent(part.replace(search, ""))}`;
144
+ });
145
+ return url.toString();
146
+ }
@@ -14,12 +14,6 @@ export async function uploadImagesAsSeparateDocumentsToPaperless(
14
14
  for (let i = 0; i < scanJobContent.elements.length; ++i) {
15
15
  const filePath = scanJobContent.elements[i].path;
16
16
  await uploadToPaperless(filePath, paperlessConfig);
17
- if (!paperlessConfig.keepFiles) {
18
- await fs.unlink(filePath);
19
- console.log(
20
- `Image document ${filePath} has been removed from the filesystem`,
21
- );
22
- }
23
17
  }
24
18
  }
25
19
 
@@ -79,12 +73,6 @@ export async function uploadPdfToPaperless(
79
73
  ) {
80
74
  if (pdfFilePath) {
81
75
  await uploadToPaperless(pdfFilePath, paperlessConfig);
82
- if (!paperlessConfig.keepFiles) {
83
- await fs.unlink(pdfFilePath);
84
- console.log(
85
- `Pdf document ${pdfFilePath} has been removed from the filesystem`,
86
- );
87
- }
88
76
  } else {
89
77
  console.log(
90
78
  "Pdf generation has failed, nothing is going to be uploaded to paperless",
@@ -6,7 +6,14 @@ import {
6
6
  uploadImagesAsSeparateDocumentsToPaperless,
7
7
  uploadPdfToPaperless,
8
8
  } from "./paperless/paperless";
9
+ import {
10
+ uploadPdfToNextcloud,
11
+ uploadImagesToNextcloud,
12
+ } from "./nextcloud/nextcloud";
9
13
  import { ScanConfig } from "./scanProcessing";
14
+ import fs from "fs/promises";
15
+ import { PaperlessConfig } from "./paperless/PaperlessConfig";
16
+ import { NextcloudConfig } from "./nextcloud/NextcloudConfig";
10
17
 
11
18
  export async function postProcessing(
12
19
  scanConfig: ScanConfig,
@@ -17,47 +24,95 @@ export async function postProcessing(
17
24
  scanDate: Date,
18
25
  toPdf: boolean,
19
26
  ) {
20
- const paperlessConfig = scanConfig.paperlessConfig;
21
27
  if (toPdf) {
22
- const pdfFilePath = await mergeToPdf(
23
- paperlessConfig ? tempFolder : folder,
28
+ await handlePdfProcessing(
29
+ folder,
30
+ tempFolder,
24
31
  scanCount,
25
32
  scanJobContent,
26
- scanConfig.directoryConfig.filePattern,
27
33
  scanDate,
28
- true,
34
+ scanConfig,
29
35
  );
30
- displayPdfScan(pdfFilePath, scanJobContent);
31
- if (paperlessConfig) {
32
- await uploadPdfToPaperless(pdfFilePath, paperlessConfig);
33
- }
34
36
  } else {
35
- displayJpegScan(scanJobContent);
36
- if (paperlessConfig) {
37
- if (paperlessConfig.groupMultiPageScanIntoAPdf) {
38
- await mergeToPdfAndUploadAsSingleDocumentToPaperless(
39
- folder,
40
- scanCount,
37
+ await handleImageProcessing(
38
+ folder,
39
+ scanCount,
40
+ scanJobContent,
41
+ scanDate,
42
+ scanConfig,
43
+ );
44
+ }
45
+ }
46
+
47
+ async function handlePdfProcessing(
48
+ folder: string,
49
+ tempFolder: string,
50
+ scanCount: number,
51
+ scanJobContent: ScanContent,
52
+ scanDate: Date,
53
+ scanConfig: ScanConfig,
54
+ ) {
55
+ const paperlessConfig = scanConfig.paperlessConfig;
56
+ const nextcloudConfig = scanConfig.nextcloudConfig;
57
+
58
+ const pdfFilePath = await mergeToPdf(
59
+ paperlessConfig ? tempFolder : folder,
60
+ scanCount,
61
+ scanJobContent,
62
+ scanConfig.directoryConfig.filePattern,
63
+ scanDate,
64
+ true,
65
+ );
66
+ displayPdfScan(pdfFilePath, scanJobContent);
67
+ if (paperlessConfig) {
68
+ await uploadPdfToPaperless(pdfFilePath, paperlessConfig);
69
+ }
70
+ if (nextcloudConfig) {
71
+ await uploadPdfToNextcloud(pdfFilePath, nextcloudConfig);
72
+ }
73
+ await cleanUpFilesIfNeeded([pdfFilePath], paperlessConfig, nextcloudConfig);
74
+ }
75
+
76
+ async function handleImageProcessing(
77
+ folder: string,
78
+ scanCount: number,
79
+ scanJobContent: ScanContent,
80
+ scanDate: Date,
81
+ scanConfig: ScanConfig,
82
+ ) {
83
+ const paperlessConfig = scanConfig.paperlessConfig;
84
+ const nextcloudConfig = scanConfig.nextcloudConfig;
85
+
86
+ displayJpegScan(scanJobContent);
87
+ if (paperlessConfig) {
88
+ if (paperlessConfig.groupMultiPageScanIntoAPdf) {
89
+ await mergeToPdfAndUploadAsSingleDocumentToPaperless(
90
+ folder,
91
+ scanCount,
92
+ scanJobContent,
93
+ scanConfig,
94
+ scanDate,
95
+ paperlessConfig,
96
+ );
97
+ } else {
98
+ if (paperlessConfig.alwaysSendAsPdfFile) {
99
+ await convertImagesToPdfAndUploadAsSeparateDocumentsToPaperless(
41
100
  scanJobContent,
42
- scanConfig,
43
- scanDate,
44
101
  paperlessConfig,
45
102
  );
46
103
  } else {
47
- if (paperlessConfig.alwaysSendAsPdfFile) {
48
- await convertImagesToPdfAndUploadAsSeparateDocumentsToPaperless(
49
- scanJobContent,
50
- paperlessConfig,
51
- );
52
- } else {
53
- await uploadImagesAsSeparateDocumentsToPaperless(
54
- scanJobContent,
55
- paperlessConfig,
56
- );
57
- }
104
+ await uploadImagesAsSeparateDocumentsToPaperless(
105
+ scanJobContent,
106
+ paperlessConfig,
107
+ );
58
108
  }
59
109
  }
60
110
  }
111
+ if (nextcloudConfig) {
112
+ await uploadImagesToNextcloud(scanJobContent, nextcloudConfig);
113
+ }
114
+ const filePaths = scanJobContent.elements.map((element) => element.path);
115
+ await cleanUpFilesIfNeeded(filePaths, paperlessConfig, nextcloudConfig);
61
116
  }
62
117
 
63
118
  function displayPdfScan(
@@ -89,3 +144,20 @@ function displayJpegScan(scanJobContent: ScanContent) {
89
144
  ),
90
145
  );
91
146
  }
147
+
148
+ async function cleanUpFilesIfNeeded(
149
+ filePaths: (string | null)[],
150
+ paperlessConfig: PaperlessConfig | undefined,
151
+ nextcloudConfig: NextcloudConfig | undefined,
152
+ ) {
153
+ if (!paperlessConfig?.keepFiles && !nextcloudConfig?.keepFiles) {
154
+ await Promise.all(
155
+ filePaths.map(async (filePath) => {
156
+ if (filePath) {
157
+ await fs.unlink(filePath);
158
+ console.log(`File ${filePath} has been removed from the filesystem`);
159
+ }
160
+ }),
161
+ );
162
+ }
163
+ }
@@ -14,6 +14,7 @@ import PathHelper from "./PathHelper";
14
14
  import ScanStatus from "./ScanStatus";
15
15
  import { InputSource } from "./InputSource";
16
16
  import { PaperlessConfig } from "./paperless/PaperlessConfig";
17
+ import { NextcloudConfig } from "./nextcloud/NextcloudConfig";
17
18
  import { postProcessing } from "./postProcessing";
18
19
 
19
20
  async function waitDeviceUntilItIsReadyToUploadOrCompleted(
@@ -455,6 +456,7 @@ export type ScanConfig = {
455
456
  height: number | null;
456
457
  directoryConfig: DirectoryConfig;
457
458
  paperlessConfig: PaperlessConfig | undefined;
459
+ nextcloudConfig: NextcloudConfig | undefined;
458
460
  };
459
461
  export type AdfAutoScanConfig = ScanConfig & {
460
462
  isDuplex: boolean;