diff --git a/Dockerfile b/Dockerfile index e1ca86db..048901a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ FROM node:24-alpine +RUN apk add --no-cache jq + WORKDIR /app COPY . . @@ -13,3 +15,5 @@ RUN chmod +x /app/docker/entrypoint.sh RUN ln -s '/app/bin/run.js' /usr/local/bin/internxt ENTRYPOINT ["/app/docker/entrypoint.sh"] + +HEALTHCHECK --interval=60s --timeout=20s --start-period=30s --retries=3 CMD /app/docker/health_check.sh diff --git a/README.md b/README.md index d41419f9..cdc1ed8a 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ $ npm install -g @internxt/cli $ internxt COMMAND running command... $ internxt (--version) -@internxt/cli/1.5.5 win32-x64 node-v23.7.0 +@internxt/cli/1.5.6 win32-x64 node-v23.7.0 $ internxt --help [COMMAND] USAGE $ internxt COMMAND @@ -120,7 +120,7 @@ EXAMPLES $ internxt add-cert ``` -_See code: [src/commands/add-cert.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/add-cert.ts)_ +_See code: [src/commands/add-cert.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/add-cert.ts)_ ## `internxt autocomplete [SHELL]` @@ -171,7 +171,7 @@ EXAMPLES $ internxt config ``` -_See code: [src/commands/config.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/config.ts)_ +_See code: [src/commands/config.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/config.ts)_ ## `internxt create-folder` @@ -200,7 +200,7 @@ EXAMPLES $ internxt create-folder ``` -_See code: [src/commands/create-folder.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/create-folder.ts)_ +_See code: [src/commands/create-folder.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/create-folder.ts)_ ## `internxt delete-permanently-file` @@ -230,7 +230,7 @@ EXAMPLES $ internxt delete-permanently-file ``` -_See code: [src/commands/delete-permanently-file.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/delete-permanently-file.ts)_ +_See code: [src/commands/delete-permanently-file.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/delete-permanently-file.ts)_ ## `internxt delete-permanently-folder` @@ -260,7 +260,7 @@ EXAMPLES $ internxt delete-permanently-folder ``` -_See code: [src/commands/delete-permanently-folder.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/delete-permanently-folder.ts)_ +_See code: [src/commands/delete-permanently-folder.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/delete-permanently-folder.ts)_ ## `internxt delete permanently file` @@ -349,7 +349,7 @@ EXAMPLES $ internxt download-file ``` -_See code: [src/commands/download-file.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/download-file.ts)_ +_See code: [src/commands/download-file.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/download-file.ts)_ ## `internxt download file` @@ -408,7 +408,7 @@ EXAMPLES $ internxt list ``` -_See code: [src/commands/list.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/list.ts)_ +_See code: [src/commands/list.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/list.ts)_ ## `internxt login` @@ -439,7 +439,7 @@ EXAMPLES $ internxt login ``` -_See code: [src/commands/login.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/login.ts)_ +_See code: [src/commands/login.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/login.ts)_ ## `internxt logout` @@ -459,7 +459,7 @@ EXAMPLES $ internxt logout ``` -_See code: [src/commands/logout.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/logout.ts)_ +_See code: [src/commands/logout.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/logout.ts)_ ## `internxt logs` @@ -479,7 +479,7 @@ EXAMPLES $ internxt logs ``` -_See code: [src/commands/logs.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/logs.ts)_ +_See code: [src/commands/logs.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/logs.ts)_ ## `internxt move-file` @@ -511,7 +511,7 @@ EXAMPLES $ internxt move-file ``` -_See code: [src/commands/move-file.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/move-file.ts)_ +_See code: [src/commands/move-file.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/move-file.ts)_ ## `internxt move-folder` @@ -543,7 +543,7 @@ EXAMPLES $ internxt move-folder ``` -_See code: [src/commands/move-folder.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/move-folder.ts)_ +_See code: [src/commands/move-folder.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/move-folder.ts)_ ## `internxt move file` @@ -634,7 +634,7 @@ EXAMPLES $ internxt rename-file ``` -_See code: [src/commands/rename-file.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/rename-file.ts)_ +_See code: [src/commands/rename-file.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/rename-file.ts)_ ## `internxt rename-folder` @@ -665,7 +665,7 @@ EXAMPLES $ internxt rename-folder ``` -_See code: [src/commands/rename-folder.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/rename-folder.ts)_ +_See code: [src/commands/rename-folder.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/rename-folder.ts)_ ## `internxt rename file` @@ -753,7 +753,7 @@ EXAMPLES $ internxt trash-clear ``` -_See code: [src/commands/trash-clear.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/trash-clear.ts)_ +_See code: [src/commands/trash-clear.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/trash-clear.ts)_ ## `internxt trash-file` @@ -783,7 +783,7 @@ EXAMPLES $ internxt trash-file ``` -_See code: [src/commands/trash-file.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/trash-file.ts)_ +_See code: [src/commands/trash-file.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/trash-file.ts)_ ## `internxt trash-folder` @@ -813,7 +813,7 @@ EXAMPLES $ internxt trash-folder ``` -_See code: [src/commands/trash-folder.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/trash-folder.ts)_ +_See code: [src/commands/trash-folder.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/trash-folder.ts)_ ## `internxt trash-list` @@ -839,7 +839,7 @@ EXAMPLES $ internxt trash-list ``` -_See code: [src/commands/trash-list.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/trash-list.ts)_ +_See code: [src/commands/trash-list.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/trash-list.ts)_ ## `internxt trash-restore-file` @@ -870,7 +870,7 @@ EXAMPLES $ internxt trash-restore-file ``` -_See code: [src/commands/trash-restore-file.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/trash-restore-file.ts)_ +_See code: [src/commands/trash-restore-file.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/trash-restore-file.ts)_ ## `internxt trash-restore-folder` @@ -901,7 +901,7 @@ EXAMPLES $ internxt trash-restore-folder ``` -_See code: [src/commands/trash-restore-folder.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/trash-restore-folder.ts)_ +_See code: [src/commands/trash-restore-folder.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/trash-restore-folder.ts)_ ## `internxt trash clear` @@ -1098,7 +1098,7 @@ EXAMPLES $ internxt upload-file ``` -_See code: [src/commands/upload-file.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/upload-file.ts)_ +_See code: [src/commands/upload-file.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/upload-file.ts)_ ## `internxt upload file` @@ -1153,7 +1153,7 @@ EXAMPLES $ internxt webdav status ``` -_See code: [src/commands/webdav.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/webdav.ts)_ +_See code: [src/commands/webdav.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/webdav.ts)_ ## `internxt webdav-config` @@ -1161,10 +1161,11 @@ Edit the configuration of the Internxt CLI WebDav server as the port or the prot ``` USAGE - $ internxt webdav-config [--json] [-p ] [-s | -h] [-t ] + $ internxt webdav-config [--json] [-l ] [-p ] [-s | -h] [-t ] FLAGS -h, --http Configures the WebDAV server to use insecure plain HTTP. + -l, --host= The listening host for the WebDAV server. -p, --port= The new port for the WebDAV server. -s, --https Configures the WebDAV server to use HTTPS with self-signed certificates. -t, --timeout= Configures the WebDAV server to use this timeout in minutes. @@ -1179,7 +1180,7 @@ EXAMPLES $ internxt webdav-config ``` -_See code: [src/commands/webdav-config.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/webdav-config.ts)_ +_See code: [src/commands/webdav-config.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/webdav-config.ts)_ ## `internxt whoami` @@ -1199,7 +1200,7 @@ EXAMPLES $ internxt whoami ``` -_See code: [src/commands/whoami.ts](https://github.com/internxt/cli/blob/v1.5.5/src/commands/whoami.ts)_ +_See code: [src/commands/whoami.ts](https://github.com/internxt/cli/blob/v1.5.6/src/commands/whoami.ts)_ # Current Limitations diff --git a/docker/health_check.sh b/docker/health_check.sh new file mode 100644 index 00000000..91a6507e --- /dev/null +++ b/docker/health_check.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +# Check login +WHOAMI_OUTPUT=$(internxt whoami --json 2>/dev/null || true) +WHOAMI_EMAIL=$(echo "$WHOAMI_OUTPUT" | jq -r '.login.user.email // empty') + +# Check WebDAV status +STATUS_OUTPUT=$(internxt webdav status --json 2>/dev/null || true) +WEBDAV_STATUS=$(echo "$STATUS_OUTPUT" | jq -r '.message | split(" ") | last // empty') + +if [ "$WHOAMI_EMAIL" = "$INXT_USER" ] && [ "$WEBDAV_STATUS" = "online" ]; then + echo "Healthcheck passed. User: $INXT_USER, WebDAV status: $WEBDAV_STATUS" + exit 0 +else + echo "Healthcheck failed. Expected user: $INXT_USER, got: $WHOAMI_EMAIL. WebDAV status: $WEBDAV_STATUS" + exit 1 +fi diff --git a/package.json b/package.json index 92f98ffe..4f317621 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "author": "Internxt ", - "version": "1.5.5", + "version": "1.5.6", "description": "Internxt CLI to manage your encrypted storage", "scripts": { "build": "yarn clean && tsc", @@ -47,7 +47,7 @@ "body-parser": "2.2.0", "cli-progress": "3.12.0", "dayjs": "1.11.18", - "dotenv": "17.2.2", + "dotenv": "17.2.3", "express": "5.1.0", "express-async-handler": "1.2.0", "fast-xml-parser": "5.2.5", @@ -58,7 +58,7 @@ "range-parser": "1.2.1", "selfsigned": "3.0.1", "tty-table": "4.2.3", - "winston": "3.17.0" + "winston": "3.18.3" }, "devDependencies": { "@internxt/eslint-config-internxt": "2.0.1", @@ -79,7 +79,7 @@ "prettier": "3.6.2", "rimraf": "6.0.1", "ts-node": "10.9.2", - "typescript": "5.9.2", + "typescript": "5.9.3", "vitest": "3.2.4", "vitest-mock-express": "2.2.0" }, diff --git a/src/commands/webdav-config.ts b/src/commands/webdav-config.ts index 9d6d74f3..e3fe4043 100644 --- a/src/commands/webdav-config.ts +++ b/src/commands/webdav-config.ts @@ -10,6 +10,11 @@ export default class WebDAVConfig extends Command { static readonly aliases = []; static readonly examples = ['<%= config.bin %> <%= command.id %>']; static readonly flags = { + host: Flags.string({ + char: 'l', + description: 'The listening host for the WebDAV server.', + required: false, + }), port: Flags.string({ char: 'p', description: 'The new port for the WebDAV server.', @@ -40,6 +45,11 @@ export default class WebDAVConfig extends Command { const { flags } = await this.parse(WebDAVConfig); const webdavConfig = await ConfigService.instance.readWebdavConfig(); + const host = flags['host']; + if (host) { + webdavConfig['host'] = host; + } + const port = flags['port']; if (port) { if (ValidationService.instance.validateTCPIntegerPort(port)) { diff --git a/src/commands/webdav.ts b/src/commands/webdav.ts index 3528eef7..220feecd 100644 --- a/src/commands/webdav.ts +++ b/src/commands/webdav.ts @@ -83,15 +83,11 @@ export default class Webdav extends Command { const webdavConfigs = await ConfigService.instance.readWebdavConfig(); if (status === 'online') { - // eslint-disable-next-line max-len - const message = `Internxt WebDav server started successfully at ${webdavConfigs.protocol}://${ConfigService.WEBDAV_LOCAL_URL}:${webdavConfigs.port}`; + const message = + 'Internxt WebDav server started successfully at ' + + `${webdavConfigs.protocol}://${webdavConfigs.host}:${webdavConfigs.port}`; CLIUtils.log(this.log.bind(this), `\nWebDav server status: ${ux.colorize('green', 'online')}\n`); CLIUtils.success(this.log.bind(this), message); - CLIUtils.log( - this.log.bind(this), - // eslint-disable-next-line max-len - `\n[If the above URL is not working, the WebDAV server can be accessed directly via your localhost IP at: ${webdavConfigs.protocol}://127.0.0.1:${webdavConfigs.port} ]\n`, - ); return message; } else { const message = `WebDav server status: ${ux.colorize('red', status)}`; diff --git a/src/commands/whoami.ts b/src/commands/whoami.ts index 39138db5..4e8afc51 100644 --- a/src/commands/whoami.ts +++ b/src/commands/whoami.ts @@ -3,6 +3,7 @@ import { CLIUtils } from '../utils/cli.utils'; import { ConfigService } from '../services/config.service'; import { ValidationService } from '../services/validation.service'; import { LoginCredentials } from '../types/command.types'; +import { AuthService } from '../services/auth.service'; export default class Whoami extends Command { static readonly args = {}; @@ -20,12 +21,19 @@ export default class Whoami extends Command { return { success: false, message }; } else { const validCreds = this.checkUserAndTokens(userCredentials); - if (!validCreds) { + if (!validCreds.valid) { const message = 'Your session has expired. You have been logged out. Please log in again.'; await ConfigService.instance.clearUser(); CLIUtils.error(this.log.bind(this), message); return { success: false, message }; } else { + if (validCreds.refreshRequired) { + try { + await AuthService.instance.refreshUserToken(userCredentials); + } catch { + /* noop */ + } + } const message = `You are logged in as: ${userCredentials.user.email}.`; CLIUtils.success(this.log.bind(this), message); return { success: true, message, login: userCredentials }; @@ -45,14 +53,14 @@ export default class Whoami extends Command { this.exit(1); }; - private checkUserAndTokens = (loginCreds: LoginCredentials): boolean => { + private checkUserAndTokens = (loginCreds: LoginCredentials): { valid: boolean; refreshRequired: boolean } => { if (!(loginCreds?.token && loginCreds?.user?.mnemonic)) { - return false; + return { valid: false, refreshRequired: false }; } const tokenDetails = ValidationService.instance.validateTokenAndCheckExpiration(loginCreds.token); const goodMnemonic = ValidationService.instance.validateMnemonic(loginCreds.user.mnemonic); const goodToken = tokenDetails.isValid && !tokenDetails.expiration.expired; - return goodToken && goodMnemonic; + return { valid: goodToken && goodMnemonic, refreshRequired: tokenDetails.expiration.refreshRequired }; }; } diff --git a/src/services/config.service.ts b/src/services/config.service.ts index 88d89ded..f5d75dcc 100644 --- a/src/services/config.service.ts +++ b/src/services/config.service.ts @@ -13,7 +13,7 @@ export class ConfigService { static readonly DRIVE_SQLITE_FILE = path.join(this.INTERNXT_CLI_DATA_DIR, 'internxt-cli-drive.sqlite'); static readonly WEBDAV_SSL_CERTS_DIR = path.join(this.INTERNXT_CLI_DATA_DIR, 'certs'); static readonly WEBDAV_CONFIGS_FILE = path.join(this.INTERNXT_CLI_DATA_DIR, 'config.webdav.inxt'); - static readonly WEBDAV_LOCAL_URL = 'webdav.local.internxt.com'; + static readonly WEBDAV_DEFAULT_HOST = '127.0.0.1'; static readonly WEBDAV_DEFAULT_PORT = '3005'; static readonly WEBDAV_DEFAULT_PROTOCOL = 'https'; static readonly WEBDAV_DEFAULT_TIMEOUT = 0; @@ -86,12 +86,14 @@ export class ConfigService { const configsData = await fs.readFile(ConfigService.WEBDAV_CONFIGS_FILE, 'utf8'); const configs = JSON.parse(configsData); return { + host: configs?.host ?? ConfigService.WEBDAV_DEFAULT_HOST, port: configs?.port ?? ConfigService.WEBDAV_DEFAULT_PORT, protocol: configs?.protocol ?? ConfigService.WEBDAV_DEFAULT_PROTOCOL, timeoutMinutes: configs?.timeoutMinutes ?? ConfigService.WEBDAV_DEFAULT_TIMEOUT, }; } catch { return { + host: ConfigService.WEBDAV_DEFAULT_HOST, port: ConfigService.WEBDAV_DEFAULT_PORT, protocol: ConfigService.WEBDAV_DEFAULT_PROTOCOL, timeoutMinutes: ConfigService.WEBDAV_DEFAULT_TIMEOUT, diff --git a/src/types/command.types.ts b/src/types/command.types.ts index 0e8e34eb..994d760f 100644 --- a/src/types/command.types.ts +++ b/src/types/command.types.ts @@ -8,6 +8,7 @@ export interface LoginCredentials { } export interface WebdavConfig { + host: string; port: string; protocol: 'http' | 'https'; timeoutMinutes: number; diff --git a/src/utils/network.utils.ts b/src/utils/network.utils.ts index c0237fb4..eabe5ca3 100644 --- a/src/utils/network.utils.ts +++ b/src/utils/network.utils.ts @@ -5,6 +5,7 @@ import path from 'node:path'; import selfsigned from 'selfsigned'; import parseRange from 'range-parser'; import { ConfigService } from '../services/config.service'; +import { WebdavConfig } from '../types/command.types'; export class NetworkUtils { static getAuthFromCredentials(creds: NetworkCredentials): { username: string; password: string } { @@ -19,8 +20,8 @@ export class NetworkUtils { privateKey: path.join(ConfigService.WEBDAV_SSL_CERTS_DIR, 'priv.key'), }; - static generateNewSelfsignedCerts(): SelfsignedCert { - const newCerts = this.generateSelfSignedSSLCerts(); + static generateNewSelfsignedCerts(configs: WebdavConfig): SelfsignedCert { + const newCerts = this.generateSelfSignedSSLCerts(configs); this.saveWebdavSSLCerts(newCerts); return { cert: newCerts.cert, @@ -28,11 +29,11 @@ export class NetworkUtils { }; } - static async getWebdavSSLCerts() { + static async getWebdavSSLCerts(configs: WebdavConfig) { const existCert = !!(await stat(this.WEBDAV_SSL_CERTS_PATH.cert).catch(() => false)); const existKey = !!(await stat(this.WEBDAV_SSL_CERTS_PATH.privateKey).catch(() => false)); if (!existCert || !existKey) { - return this.generateNewSelfsignedCerts(); + return this.generateNewSelfsignedCerts(configs); } else { let selfsignedCert: SelfsignedCert = { cert: await readFile(this.WEBDAV_SSL_CERTS_PATH.cert), @@ -44,7 +45,7 @@ export class NetworkUtils { const dateValid = new Date(validTo); if (dateToday > dateValid) { - const newCerts = this.generateNewSelfsignedCerts(); + const newCerts = this.generateNewSelfsignedCerts(configs); selfsignedCert = { cert: newCerts.cert, key: newCerts.key, @@ -59,15 +60,15 @@ export class NetworkUtils { await writeFile(this.WEBDAV_SSL_CERTS_PATH.privateKey, pems.private, 'utf8'); } - static generateSelfSignedSSLCerts(): selfsigned.GenerateResult { - const attrs = [{ name: 'commonName', value: ConfigService.WEBDAV_LOCAL_URL }]; + static generateSelfSignedSSLCerts(configs: WebdavConfig): selfsigned.GenerateResult { + const attrs = [{ name: 'commonName', value: configs.host }]; const extensions = [ { name: 'subjectAltName', altNames: [ { type: 2, - value: ConfigService.WEBDAV_LOCAL_URL, + value: configs.host, }, ], }, diff --git a/src/webdav/webdav-server.ts b/src/webdav/webdav-server.ts index 1d18b04e..536092fe 100644 --- a/src/webdav/webdav-server.ts +++ b/src/webdav/webdav-server.ts @@ -172,17 +172,17 @@ export class WebDavServer { if (plainHttp) { server = http.createServer(this.app); } else { - const httpsCerts = await NetworkUtils.getWebdavSSLCerts(); + const httpsCerts = await NetworkUtils.getWebdavSSLCerts(configs); server = https.createServer(httpsCerts, this.app); } // Allow long uploads/downloads from WebDAV clients: server.requestTimeout = configs.timeoutMinutes * 60 * 1000; - server.listen(configs.port, () => { + server.listen(Number(configs.port), configs.host, undefined, () => { webdavLogger.info( `Internxt ${SdkManager.getAppDetails().clientVersion} WebDav server ` + - `listening at ${configs.protocol}://${ConfigService.WEBDAV_LOCAL_URL}:${configs.port}`, + `listening at ${configs.protocol}://${configs.host}:${configs.port}`, ); }); }; diff --git a/test/services/config.service.test.ts b/test/services/config.service.test.ts index 8ab44f8a..ba87ea6a 100644 --- a/test/services/config.service.test.ts +++ b/test/services/config.service.test.ts @@ -116,7 +116,8 @@ describe('Config service', () => { it('When webdav config options are saved, then they are written to a file', async () => { const webdavConfig: WebdavConfig = { - port: String(crypto.randomInt(65000)), + host: '127.0.0.1', + port: crypto.randomInt(65000).toString(), protocol: 'https', timeoutMinutes: crypto.randomInt(100), }; @@ -130,7 +131,8 @@ describe('Config service', () => { it('When webdav config options are read and exist, then they are read from a file', async () => { const webdavConfig: WebdavConfig = { - port: String(crypto.randomInt(65000)), + host: '127.0.0.1', + port: crypto.randomInt(65000).toString(), protocol: 'http', timeoutMinutes: crypto.randomInt(100), }; @@ -145,6 +147,7 @@ describe('Config service', () => { it('When webdav config options are read but not exist, then they are returned from defaults', async () => { const defaultWebdavConfig: WebdavConfig = { + host: ConfigService.WEBDAV_DEFAULT_HOST, port: ConfigService.WEBDAV_DEFAULT_PORT, protocol: ConfigService.WEBDAV_DEFAULT_PROTOCOL, timeoutMinutes: ConfigService.WEBDAV_DEFAULT_TIMEOUT, @@ -159,6 +162,7 @@ describe('Config service', () => { it('When webdav config options are read but an error is thrown, then they are returned from defaults', async () => { const defaultWebdavConfig: WebdavConfig = { + host: ConfigService.WEBDAV_DEFAULT_HOST, port: ConfigService.WEBDAV_DEFAULT_PORT, protocol: ConfigService.WEBDAV_DEFAULT_PROTOCOL, timeoutMinutes: ConfigService.WEBDAV_DEFAULT_TIMEOUT, diff --git a/test/utils/network.utils.test.ts b/test/utils/network.utils.test.ts index 9b053217..24ce03f8 100644 --- a/test/utils/network.utils.test.ts +++ b/test/utils/network.utils.test.ts @@ -5,6 +5,7 @@ import { readFile, stat, writeFile } from 'node:fs/promises'; import { NetworkUtils } from '../../src/utils/network.utils'; import { Stats } from 'node:fs'; import { fail } from 'node:assert'; +import { WebdavConfig } from '../../src/types/command.types'; vi.mock('node:fs/promises', async () => { const actual = await vi.importActual('node:fs/promises'); @@ -43,6 +44,12 @@ describe('Network utils', () => { }); it('When webdav ssl certs do not exist, then they should be self signed and saved to files', async () => { + const webdavConfig: WebdavConfig = { + host: '127.0.0.1', + port: randomInt(65535).toString(), + protocol: 'https', + timeoutMinutes: randomInt(900), + }; const sslSelfSigned: GenerateResult = { private: randomBytes(8).toString('hex'), public: randomBytes(8).toString('hex'), @@ -59,7 +66,7 @@ describe('Network utils', () => { }); const selfsignedSpy = vi.spyOn(selfsigned, 'generate').mockImplementation(() => sslSelfSigned); - const result = await NetworkUtils.getWebdavSSLCerts(); + const result = await NetworkUtils.getWebdavSSLCerts(webdavConfig); expect(result).to.deep.equal({ cert: sslSelfSigned.cert, key: sslSelfSigned.private }); expect(selfsignedSpy).toHaveBeenCalledOnce(); @@ -68,6 +75,12 @@ describe('Network utils', () => { }); it('When webdav ssl certs exist, then they are read from the files', async () => { + const webdavConfig: WebdavConfig = { + host: '127.0.0.1', + port: randomInt(65535).toString(), + protocol: 'https', + timeoutMinutes: randomInt(900), + }; const sslMock = { private: randomBytes(8).toString('hex'), cert: randomBytes(8).toString('hex'), @@ -94,7 +107,7 @@ describe('Network utils', () => { validTo: future.toDateString(), })); - const result = await NetworkUtils.getWebdavSSLCerts(); + const result = await NetworkUtils.getWebdavSSLCerts(webdavConfig); expect(result).to.deep.equal({ cert: sslMock.cert, key: sslMock.private }); expect(mock509Certificate).toHaveBeenCalledOnce(); @@ -103,6 +116,12 @@ describe('Network utils', () => { }); it('When webdav ssl certs exist but they are expired, then they are generated and saved to files', async () => { + const webdavConfig: WebdavConfig = { + host: '127.0.0.1', + port: randomInt(65535).toString(), + protocol: 'https', + timeoutMinutes: randomInt(900), + }; const sslSelfSigned: GenerateResult = { private: randomBytes(8).toString('hex'), public: randomBytes(8).toString('hex'), @@ -135,7 +154,7 @@ describe('Network utils', () => { const selfsignedSpy = vi.spyOn(selfsigned, 'generate').mockImplementation(() => sslSelfSigned); - const result = await NetworkUtils.getWebdavSSLCerts(); + const result = await NetworkUtils.getWebdavSSLCerts(webdavConfig); expect(result).to.deep.equal({ cert: sslSelfSigned.cert, key: sslSelfSigned.private }); expect(selfsignedSpy).toHaveBeenCalledOnce(); diff --git a/test/webdav/webdav-server.test.ts b/test/webdav/webdav-server.test.ts index 3b6e0624..806ad6cf 100644 --- a/test/webdav/webdav-server.test.ts +++ b/test/webdav/webdav-server.test.ts @@ -22,6 +22,7 @@ describe('WebDav server', () => { it('When the WebDav server is started with https, it should generate self-signed certificates', async () => { const webdavConfig: WebdavConfig = { + host: '127.0.0.1', port: randomInt(65535).toString(), protocol: 'https', timeoutMinutes: randomInt(900), @@ -68,6 +69,7 @@ describe('WebDav server', () => { it('When the WebDav server is started with http, it should run http', async () => { const webdavConfig: WebdavConfig = { + host: '127.0.0.1', port: randomInt(65535).toString(), protocol: 'http', timeoutMinutes: randomInt(900), diff --git a/yarn.lock b/yarn.lock index 169c1722..9ce851f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -787,6 +787,15 @@ enabled "2.0.x" kuler "^2.0.0" +"@dabh/diagnostics@^2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.8.tgz#ead97e72ca312cf0e6dd7af0d300b58993a31a5e" + integrity sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q== + dependencies: + "@so-ric/colorspace" "^1.1.6" + enabled "2.0.x" + kuler "^2.0.0" + "@emnapi/runtime@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.5.0.tgz#9aebfcb9b17195dce3ab53c86787a6b7d058db73" @@ -2413,6 +2422,14 @@ dependencies: tslib "^2.6.2" +"@so-ric/colorspace@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@so-ric/colorspace/-/colorspace-1.1.6.tgz#62515d8b9f27746b76950a83bde1af812d91923b" + integrity sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw== + dependencies: + color "^5.0.2" + text-hex "1.0.x" + "@szmarczak/http-timer@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" @@ -3325,6 +3342,13 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-convert@^3.0.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-3.1.2.tgz#cef9e0fd4cb90b07c14697b3fa70af9d7f4870f1" + integrity sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg== + dependencies: + color-name "^2.0.0" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" @@ -3335,6 +3359,11 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-2.0.2.tgz#85054825a23e6d6f81d3503f660c4c4a2a15f04f" + integrity sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A== + color-string@^1.6.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" @@ -3343,6 +3372,13 @@ color-string@^1.6.0: color-name "^1.0.0" simple-swizzle "^0.2.2" +color-string@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-2.1.2.tgz#db1dd52414cc9037ada8fa7d936b8e9f6c3366c9" + integrity sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA== + dependencies: + color-name "^2.0.0" + color@^3.1.3: version "3.2.1" resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" @@ -3351,6 +3387,14 @@ color@^3.1.3: color-convert "^1.9.3" color-string "^1.6.0" +color@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/color/-/color-5.0.2.tgz#712ec894007ab27b37207732d182784e001b4a3d" + integrity sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA== + dependencies: + color-convert "^3.0.1" + color-string "^2.0.0" + colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" @@ -3653,10 +3697,10 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dotenv@17.2.2: - version "17.2.2" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.2.tgz#4010cfe1c2be4fc0f46fd3d951afb424bc067ac6" - integrity sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q== +dotenv@17.2.3: + version "17.2.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.3.tgz#ad995d6997f639b11065f419a22fabf567cdb9a2" + integrity sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w== dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" @@ -6961,10 +7005,10 @@ typescript-eslint@^8.40.0: "@typescript-eslint/typescript-estree" "8.43.0" "@typescript-eslint/utils" "8.43.0" -typescript@5.9.2: - version "5.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" - integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== +typescript@5.9.3: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== unbox-primitive@^1.1.0: version "1.1.0" @@ -7254,6 +7298,23 @@ winston@3.17.0: triple-beam "^1.3.0" winston-transport "^4.9.0" +winston@3.18.3: + version "3.18.3" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.18.3.tgz#93ac10808c8e1081d723bc8811cd2f445ddfdcd1" + integrity sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.8" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.7.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.9.0" + word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"