From 5ee9dd94e6bf62c4e67496e7f5b06480c0d59ef0 Mon Sep 17 00:00:00 2001 From: Dwynr Date: Mon, 9 Sep 2024 20:34:31 +0200 Subject: [PATCH] feat: logging --- README.md | 12 +-- package-lock.json | 224 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 8 +- src/index.ts | 74 ++++++++++----- src/logger.ts | 137 ++++++++++++++++++++++++++++ 5 files changed, 412 insertions(+), 43 deletions(-) create mode 100644 src/logger.ts diff --git a/README.md b/README.md index eeb4c69..2f81610 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@

-

Filen Virtual Drive

+

Filen Network Drive

- A package to mount a Filen account as a virtual drive. + A package to mount a Filen account as a network drive.

@@ -27,7 +27,7 @@ npm install @filen/network-drive@latest ```typescript import FilenSDK from "@filen/sdk" -import VirtualDrive from "@filen/network-drive" +import NetworkDrive from "@filen/network-drive" import path from "path" import os from "os" @@ -44,14 +44,14 @@ await filen.login({ twoFactorCode: "123456" }) -const virtualDrive = new VirtualDrive({ +const networkDrive = new NetworkDrive({ sdk: filen, mountPoint: "X:" // or /path/to/mount on Linux/macOS }) -await virtualDrive.start() +await networkDrive.start() -console.log("Virtual drive started") +console.log("Network drive started") ``` ## License diff --git a/package-lock.json b/package-lock.json index a7e2a4e..fd755ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,22 @@ { "name": "@filen/network-drive", - "version": "0.9.1", + "version": "0.9.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@filen/network-drive", - "version": "0.9.1", + "version": "0.9.3", "license": "AGPLv3", "dependencies": { - "@filen/sdk": "^0.1.163", - "@filen/webdav": "^0.2.41", + "@filen/sdk": "^0.1.164", + "@filen/webdav": "^0.2.44", "axios": "^1.7.6", "diskusage-ng": "^1.0.4", "find-free-ports": "^3.1.1", "fs-extra": "^11.2.0", + "pino": "^9.4.0", + "rotating-file-stream": "^3.2.3", "sudo-prompt": "^9.2.1", "uuid": "^10.0.0", "write-file-atomic": "^5.0.1" @@ -1167,9 +1169,9 @@ } }, "node_modules/@filen/sdk": { - "version": "0.1.163", - "resolved": "https://registry.npmjs.org/@filen/sdk/-/sdk-0.1.163.tgz", - "integrity": "sha512-r2gd0RA6bI7LFAdoCSIzDsgYqDvgkqJZrk+u8Pt9Olb/wD5YuKJlOIkIm4ILjvgukur/tmzowFFWcZvd3YHmjA==", + "version": "0.1.164", + "resolved": "https://registry.npmjs.org/@filen/sdk/-/sdk-0.1.164.tgz", + "integrity": "sha512-xWJ95/nmcybG0cH0kb6/pb3HdhGEEOWVnUeFD63kJxGCRkUQus22ebX2sp3vZbaYulFpyPRuxau0VAUyBtT1fA==", "license": "AGPLv3", "dependencies": { "agentkeepalive": "^4.5.0", @@ -1204,12 +1206,12 @@ } }, "node_modules/@filen/webdav": { - "version": "0.2.41", - "resolved": "https://registry.npmjs.org/@filen/webdav/-/webdav-0.2.41.tgz", - "integrity": "sha512-QdmG723UGJaXVEBpWqt/z6Xba/o6jSbZ6UXvN2BTmESJLxqJWHXTXmj12GmVcl/N+SxoEnRCSPupDX978ghq1A==", + "version": "0.2.44", + "resolved": "https://registry.npmjs.org/@filen/webdav/-/webdav-0.2.44.tgz", + "integrity": "sha512-g6EQQqIMMKtcBNigsHPU0swtMRA/loydXtsC6jQtGruwpB1DLACGZA5ma7XxhLk0ARfx3Rm7dZrHAyqL+P1HTg==", "license": "AGPLv3", "dependencies": { - "@filen/sdk": "^0.1.163", + "@filen/sdk": "^0.1.164", "body-parser": "^1.20.2", "dayjs": "^1.11.11", "express": "^4.19.2", @@ -1217,6 +1219,8 @@ "fs-extra": "^11.2.0", "mime-types": "^2.1.35", "node-cache": "^5.1.2", + "pino": "^9.4.0", + "rotating-file-stream": "^3.2.3", "selfsigned": "^2.4.1", "uuid": "^10.0.0", "write-file-atomic": "^5.0.1", @@ -2325,6 +2329,18 @@ "dev": true, "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2512,6 +2528,15 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/axios": { "version": "1.7.6", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.6.tgz", @@ -3753,6 +3778,24 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3926,6 +3969,15 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -5969,6 +6021,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -6227,6 +6288,69 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pino": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz", + "integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "license": "MIT", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -6344,12 +6468,27 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/process-warning": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", + "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==", + "license": "MIT" + }, "node_modules/progress-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-2.0.0.tgz", @@ -6456,6 +6595,12 @@ ], "license": "MIT" }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -6514,6 +6659,15 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6666,6 +6820,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rotating-file-stream": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/rotating-file-stream/-/rotating-file-stream-3.2.3.tgz", + "integrity": "sha512-cfmm3tqdnbuYw2FBmRTPBDaohYEbMJ3211T35o6eZdr4d7v69+ZeK1Av84Br7FLj2dlzyeZSbN6qTuXXE6dawQ==", + "license": "MIT", + "engines": { + "node": ">=14.0" + }, + "funding": { + "url": "https://www.blockchain.com/btc/address/12p1p5q7sK75tPyuesZmssiMYr4TKzpSCN" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6710,6 +6876,15 @@ ], "license": "MIT" }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -6965,6 +7140,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/sonic-boom": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz", + "integrity": "sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6992,6 +7176,15 @@ "integrity": "sha512-lgxErLl/7A5+vgIIXsh9MbeukOaCb2axgQ+bKCdIE+ibNT4XNYGNCR1qFEGq6F+YDASXK3Fh/c5FgtZchFolxw==", "license": "MIT" }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -7235,6 +7428,15 @@ "dev": true, "license": "MIT" }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", diff --git a/package.json b/package.json index 734cfbc..e124196 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@filen/network-drive", - "version": "0.9.2", + "version": "0.9.3", "description": "Filen Network Drive", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -47,12 +47,14 @@ "typescript": "^5.5.4" }, "dependencies": { - "@filen/sdk": "^0.1.163", - "@filen/webdav": "^0.2.41", + "@filen/sdk": "^0.1.164", + "@filen/webdav": "^0.2.44", "axios": "^1.7.6", "diskusage-ng": "^1.0.4", "find-free-ports": "^3.1.1", "fs-extra": "^11.2.0", + "pino": "^9.4.0", + "rotating-file-stream": "^3.2.3", "sudo-prompt": "^9.2.1", "uuid": "^10.0.0", "write-file-atomic": "^5.0.1" diff --git a/src/index.ts b/src/index.ts index 6aa2eae..d502a8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ import writeFileAtomic from "write-file-atomic" import axios from "axios" import { type RCCoreStats, type RCVFSStats, type GetStats } from "./types" import { writeMonitorScriptAndReturnPath } from "./monitor" +import Logger from "./logger" export const RCLONE_VERSION = "1670" export const rcloneBinaryName = `filen_rclone_${process.platform}_${process.arch}_${RCLONE_VERSION}${ @@ -56,10 +57,10 @@ export const RCLONE_HASHES: Record = { * Description placeholder * * @export - * @class VirtualDrive - * @typedef {VirtualDrive} + * @class NetworkDrive + * @typedef {NetworkDrive} */ -export class VirtualDrive { +export class NetworkDrive { private readonly sdk: FilenSDK private webdavServer: WebDAVServer | null = null private rcloneProcess: ChildProcess | null = null @@ -82,28 +83,31 @@ export class VirtualDrive { private readonly readOnly: boolean private rclonePort: number = 1906 private monitorScriptPath: string | null = null + public logger: Logger /** - * Creates an instance of VirtualDrive. + * Creates an instance of NetworkDrive. * * @constructor * @public * @param {{ * sdk?: FilenSDK - * sdkConfig: FilenSDKConfig + * sdkConfig?: FilenSDKConfig * cachePath?: string * mountPoint: string * cacheSize?: number * logFilePath?: string * readOnly?: boolean + * disableLogging?: boolean * }} param0 * @param {FilenSDK} param0.sdk * @param {FilenSDKConfig} param0.sdkConfig * @param {string} param0.cachePath - * @param {string} param0.mountPoint "X:" uppercase drive letter following : on windows, /path/to/mount of linux/macos - * @param {number} param0.cacheSize in Gibibytes + * @param {string} param0.mountPoint + * @param {number} param0.cacheSize * @param {string} param0.logFilePath - * @param {boolean} param0.readOnly set the mount to readOnly mode + * @param {boolean} [param0.readOnly=false] + * @param {boolean} [param0.disableLogging=false] */ public constructor({ sdk, @@ -112,7 +116,8 @@ export class VirtualDrive { mountPoint, cacheSize, logFilePath, - readOnly + readOnly = false, + disableLogging = false }: { sdk?: FilenSDK sdkConfig?: FilenSDKConfig @@ -121,6 +126,7 @@ export class VirtualDrive { cacheSize?: number logFilePath?: string readOnly?: boolean + disableLogging?: boolean }) { if (!sdk && !sdkConfig) { throw new Error("Either pass a configured SDK instance OR a SDKConfig object to the constructor.") @@ -137,7 +143,8 @@ export class VirtualDrive { this.mountPoint = mountPoint this.cacheSize = cacheSize ? cacheSize : 10 this.logFilePath = logFilePath - this.readOnly = typeof readOnly === "boolean" ? readOnly : false + this.readOnly = readOnly + this.logger = new Logger(disableLogging, false) // Start downloading the binary in the background this.getRCloneBinaryPath().catch(() => {}) @@ -146,7 +153,7 @@ export class VirtualDrive { } /** - * Monitor the virtual drive in the background. + * Monitor the network drive in the background. * If the underlying WebDAV server or the drive itself becomes unavailable, cleanup. * * @private @@ -162,6 +169,9 @@ export class VirtualDrive { if (!(await this.isMountActuallyActive())) { await this.stop() } + } catch (e) { + this.logger.log("error", e, "monitor") + this.logger.log("error", e) } finally { await new Promise(resolve => setTimeout(resolve, 15000)) @@ -258,6 +268,11 @@ export class VirtualDrive { } this.rcloneBinaryPath = binaryPath + } catch (e) { + this.logger.log("error", e, "getRCloneBinaryPath") + this.logger.log("error", e) + + throw e } finally { this.getRCloneBinaryMutex.release() } @@ -380,7 +395,10 @@ export class VirtualDrive { const stat = await fs.stat(this.mountPoint) return process.platform === "darwin" || process.platform === "linux" ? stat.ino === 0 || stat.birthtimeMs === 0 : stat.ino === 1 - } catch { + } catch (e) { + this.logger.log("error", e, "isMountActuallyActive") + this.logger.log("error", e) + return false } } @@ -528,11 +546,11 @@ export class VirtualDrive { ]) if (!(await fs.exists(binaryPath))) { - throw new Error(`Virtual drive binary not found at ${binaryPath}.`) + throw new Error(`Network drive binary not found at ${binaryPath}.`) } if (!(await fs.exists(configPath))) { - throw new Error(`Virtual drive config not found at ${configPath}.`) + throw new Error(`Network drive config not found at ${configPath}.`) } await fs.rm(cachePath, { @@ -659,7 +677,7 @@ export class VirtualDrive { await this.stop() - reject(new Error("Could not start virtual drive.")) + reject(new Error("Could not start network drive.")) } catch (e) { reject(e) } @@ -694,7 +712,7 @@ export class VirtualDrive { clearInterval(checkInterval) clearTimeout(checkTimeout) - reject(new Error("Could not start virtual drive.")) + reject(new Error("Could not start network drive.")) }) }) } @@ -756,7 +774,7 @@ export class VirtualDrive { } /** - * Start the virtual drive. + * Start the network drive. * * @public * @async @@ -780,7 +798,7 @@ export class VirtualDrive { const availableDriveLetters = await getAvailableDriveLetters() if (!availableDriveLetters.includes(this.mountPoint)) { - throw new Error(`Cannot mount virtual drive at ${this.mountPoint}: Drive letter exists.`) + throw new Error(`Cannot mount network drive at ${this.mountPoint}: Drive letter exists.`) } } else { if (process.platform === "linux" && !this.mountPoint.startsWith(`/home/${process.env.USER ?? "user"}`)) { @@ -792,11 +810,11 @@ export class VirtualDrive { } if (!(await isUnixMountPointValid(this.mountPoint))) { - throw new Error(`Cannot mount virtual drive at ${this.mountPoint}: Mount point does not exist.`) + throw new Error(`Cannot mount network drive at ${this.mountPoint}: Mount point does not exist.`) } if (!(await isUnixMountPointEmpty(this.mountPoint))) { - throw new Error(`Cannot mount virtual drive at ${this.mountPoint}: Mount point not empty.`) + throw new Error(`Cannot mount network drive at ${this.mountPoint}: Mount point not empty.`) } } @@ -838,17 +856,22 @@ export class VirtualDrive { await this.spawnRClone() if (!(await this.isMountActuallyActive())) { - throw new Error("Could not start virtual drive.") + throw new Error("Could not start network drive.") } this.active = true + } catch (e) { + this.logger.log("error", e, "start") + this.logger.log("error", e) + + throw e } finally { this.startMutex.release() } } /** - * Stop the virtual drive. + * Stop the network drive. * * @public * @async @@ -869,6 +892,11 @@ export class VirtualDrive { this.webdavServer = null this.rcloneProcess = null this.active = false + } catch (e) { + this.logger.log("error", e, "stop") + this.logger.log("error", e) + + throw e } finally { this.stopMutex.release() } @@ -876,4 +904,4 @@ export class VirtualDrive { } export * from "./utils" -export default VirtualDrive +export default NetworkDrive diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..1a0555e --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,137 @@ +import pathModule from "path" +import pino, { type Logger as PinoLogger } from "pino" +import os from "os" +import fs from "fs-extra" +import { createStream } from "rotating-file-stream" + +export async function filenLogsPath(): Promise { + let configPath = "" + + switch (process.platform) { + case "win32": + configPath = pathModule.resolve(process.env.APPDATA!) + + break + case "darwin": + configPath = pathModule.resolve(pathModule.join(os.homedir(), "Library/Application Support/")) + + break + default: + configPath = process.env.XDG_CONFIG_HOME + ? pathModule.resolve(process.env.XDG_CONFIG_HOME) + : pathModule.resolve(pathModule.join(os.homedir(), ".config/")) + + break + } + + if (!configPath || configPath.length === 0) { + throw new Error("Could not find homedir path.") + } + + configPath = pathModule.join(configPath, "@filen", "logs") + + if (!(await fs.exists(configPath))) { + await fs.mkdir(configPath, { + recursive: true + }) + } + + return configPath +} + +export class Logger { + private logger: PinoLogger | null = null + private dest: string | null = null + private isCleaning: boolean = false + private readonly disableLogging: boolean + private readonly isWorker: boolean + + public constructor(disableLogging: boolean = false, isWorker: boolean = false) { + this.disableLogging = disableLogging + this.isWorker = isWorker + + this.init() + } + + public async init(): Promise { + try { + this.dest = pathModule.join(await filenLogsPath(), this.isWorker ? "network-drive-worker.log" : "network-drive.log") + + this.logger = pino( + createStream(pathModule.basename(this.dest), { + size: "10M", + interval: "7d", + compress: "gzip", + encoding: "utf-8", + maxFiles: 3, + path: pathModule.dirname(this.dest) + }) + ) + } catch (e) { + console.error(e) + } + } + + public async waitForPino(): Promise { + if (this.logger) { + return + } + + await new Promise(resolve => { + const wait = setInterval(() => { + if (this.logger) { + clearInterval(wait) + + resolve() + } + }, 100) + }) + } + + public log(level: "info" | "debug" | "warn" | "error" | "trace" | "fatal", object?: unknown, where?: string): void { + if (this.isCleaning || this.disableLogging) { + return + } + + // eslint-disable-next-line no-extra-semi + ;(async () => { + try { + if (!this.logger) { + await this.waitForPino() + } + + const log = `${where ? `[${where}] ` : ""}${ + typeof object !== "undefined" + ? typeof object === "string" || typeof object === "number" + ? object + : JSON.stringify(object) + : "" + }` + + if (level === "info") { + this.logger?.info(log) + } else if (level === "debug") { + this.logger?.debug(log) + } else if (level === "error") { + this.logger?.error(log) + + if (object instanceof Error) { + this.logger?.error(object) + } + } else if (level === "warn") { + this.logger?.warn(log) + } else if (level === "trace") { + this.logger?.trace(log) + } else if (level === "fatal") { + this.logger?.fatal(log) + } else { + this.logger?.info(log) + } + } catch (e) { + console.error(e) + } + })() + } +} + +export default Logger