Skip to content

Commit d01335e

Browse files
committed
feat: overwrite data dir (+ maintenance)
1 parent dc63ccf commit d01335e

File tree

8 files changed

+48
-25
lines changed

8 files changed

+48
-25
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ There are several ways to authenticate:
6666

6767
If you have 2FA enabled and don't specify a 2FA code, you will be prompted for it.
6868

69+
### Data directory
70+
71+
The data directory is where configuration files, credentials, cache etc. are stored and read from. By default, it is `%APPDATA%/filen-cli` (Windows), `~/Library/Application Support/filen-cli` (macOS) or `$XDG_CONFIG_HOME/filen-cli` or `~/.config/filen-cli` (Unix). If there is a directory named `.filen-cli` at the home directory `~`, it is used instead (for instance, the install script installs to this location). You can overwrite the location using the `--data-dir` flag or the `FILEN_CLI_DATA_DIR` environment variable.
72+
6973

7074
## Access your Filen Drive
7175

@@ -136,7 +140,7 @@ $ filen sync [sync pairs...] [--continuous]
136140
Invoke `filen sync` to sync any locations with your Filen Drive. This is the same functionality you get with the Desktop app.
137141

138142
You must specify the sync pairs (`[sync pairs...]` above) as follows:
139-
- **(central registry)** `filen sync`: Read the sync pairs from `$APP_DATA/filen_cli/syncPairs.json`.
143+
- **(central registry)** `filen sync`: Read the sync pairs from `syncPairs.json` (inside the [data dir](#data-directory)).
140144
This file must contain JSON of the type[^type] `{local: string, remote: string, syncMode: string, alias?: string, disableLocalTrash?: boolean, ignore?: string[], excludeDotFiles?: boolean}[]`.
141145
`syncMode` can be `twoWay`, `localToCloud`, `localBackup`, `cloudToLocal` or `cloudBackup` (see [here](https://blog.filen.io/how-to-desktop-client/#:~:text=for%20this%20sync.-,Sync%20Modes,-%3A) on what that means). Note that since this is a JSON file, backslashes (`\`) in strings need to be escaped, e. g. `"C:\\some\\path"`).
142146
- **(custom registry)** `filen sync <file>`: Read the sync pairs from a custom JSON file (same type as above).

src/auth/auth.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import FilenSDK, { APIError } from "@filen/sdk"
22
import { err, errExit, out, outVerbose, prompt, promptConfirm } from "../interface/interface"
33
import fsModule from "node:fs"
4-
import { exists, platformConfigPath } from "../util/util"
4+
import { exists } from "../util/util"
55
import path from "path"
66
import { CredentialsCrypto } from "./credentialsCrypto"
77
import { wrapRedTerminalText } from "../interface/util"
88
import { ANONYMOUS_SDK_CONFIG } from "../constants"
9+
import { dataDir } from ".."
910

1011
export type Credentials = {
1112
email: string
@@ -20,7 +21,7 @@ export class Authentication {
2021
private readonly filen: FilenSDK
2122

2223
private readonly crypto = new CredentialsCrypto()
23-
private readonly credentialsDirectory = platformConfigPath()
24+
private readonly credentialsDirectory = dataDir
2425
private readonly credentialsFile = path.join(this.credentialsDirectory, ".credentials")
2526
private readonly sdkConfigFile = ".filen-cli-auth-config"
2627

src/auth/credentialsCrypto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import path from "path"
22
import fsModule from "node:fs"
33
import crypto from "node:crypto"
4-
import { platformConfigPath } from "../util/util"
54
import { key } from "../buildInfo"
65
import { FilenSDKConfig } from "@filen/sdk"
76
import { errExit } from "../interface/interface"
7+
import { dataDir } from ".."
88

99
/**
1010
* Handles cryptography for securely storing credentials in a file.
@@ -17,7 +17,7 @@ export class CredentialsCrypto {
1717
this.key = key
1818

1919
try {
20-
const saltFile = path.join(platformConfigPath(), ".credentials.salt")
20+
const saltFile = path.join(dataDir, ".credentials.salt")
2121
if (!fsModule.existsSync(saltFile)) {
2222
this.salt = crypto.randomBytes(32).toString("hex")
2323
fsModule.writeFileSync(saltFile, this.salt)

src/buildInfo.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
export const version: string = "{{INJECT: VERSION}}"
55

66
// @ts-expect-error will be injected
7-
export const disableUpdates: boolean = "{{INJECT: IS_CONTAINER}}"
7+
export const isRunningAsContainer: boolean = "{{INJECT: IS_CONTAINER}}"
88

99
// @ts-expect-error will be injected
1010
export const isRunningAsNPMPackage: boolean = "{{INJECT: IS_NPM_PACKAGE}}"
@@ -13,7 +13,7 @@ export const key: string = "{{INJECT: CRYPTO_BASE_KEY}}"
1313

1414
export function checkInjectedBuildInfo() {
1515
return version !== "{{INJECT: VERSION}}"
16-
&& (disableUpdates.toString() === "true" || disableUpdates.toString() === "false")
16+
&& (isRunningAsContainer.toString() === "true" || isRunningAsContainer.toString() === "false")
1717
&& (isRunningAsNPMPackage.toString() === "true" || isRunningAsNPMPackage.toString() === "false")
1818
&& key !== "{{INJECT: CRYPTO_BASE_KEY}}"
1919
}

src/featureInterfaces/syncInterface.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import pathModule from "path"
44
import { SyncMessage, SyncMode, SyncPair } from "@filen/sync/dist/types"
55
import { err, errExit, out, outVerbose, quiet } from "../interface/interface"
66
import fsModule, { PathLike } from "node:fs"
7-
import { exists, platformConfigPath } from "../util/util"
7+
import { exists } from "../util/util"
88
import getUuidByString from "uuid-by-string"
99
import { displayTransferProgressBar } from "../interface/util"
1010
import { InterruptHandler } from "../interface/interrupt"
1111
import os from "os"
12+
import { dataDir } from ".."
1213

1314
export const syncOptions = {
1415
"--continuous": Boolean,
@@ -51,7 +52,7 @@ const syncModeMappings = new Map<string, SyncMode>([
5152
export class SyncInterface {
5253
private readonly filen
5354

54-
private readonly defaultSyncPairsRegistry = pathModule.join(platformConfigPath(), "syncPairs.json")
55+
private readonly defaultSyncPairsRegistry = pathModule.join(dataDir, "syncPairs.json")
5556

5657
constructor(filen: FilenSDK) {
5758
this.filen = filen
@@ -99,7 +100,7 @@ export class SyncInterface {
99100
const progressBar = continuous ? null : displayTransferProgressBar("Transferring", "files", 0)
100101
const worker = new SyncWorker({
101102
syncPairs: fullSyncPairs,
102-
dbPath: pathModule.join(platformConfigPath(), "sync"),
103+
dbPath: pathModule.join(dataDir, "sync"),
103104
sdk: this.filen,
104105
onMessage: msg => {
105106
outVerbose(JSON.stringify(msg, null, 2))

src/index.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import path from "path"
66
import os from "os"
77
import { err, errExit, out, outVerbose, setOutputFlags, setupLogs } from "./interface/interface"
88
import { Authentication } from "./auth/auth"
9-
import { checkInjectedBuildInfo, version } from "./buildInfo"
9+
import { checkInjectedBuildInfo, isRunningAsContainer, isRunningAsNPMPackage, version } from "./buildInfo"
1010
import { Updater } from "./updater"
1111
import { HelpPage } from "./interface/helpPage"
1212
import { FSInterface, fsOptions } from "./featureInterfaces/fs/fsInterface"
@@ -17,6 +17,7 @@ import { TrashInterface } from "./featureInterfaces/trashInterface"
1717
import { PublicLinksInterface } from "./featureInterfaces/publicLinksInterface"
1818
import { DriveMountingInterface } from "./featureInterfaces/driveMountingInterface"
1919
import { ANONYMOUS_SDK_CONFIG } from "./constants"
20+
import { determineDataDir } from "./util/util"
2021

2122
const args = arg(
2223
{
@@ -42,6 +43,7 @@ const args = arg(
4243
"-c": "--two-factor-code",
4344

4445
"--log-file": String,
46+
"--data-dir": String,
4547

4648
"--skip-update": Boolean,
4749
"--force-update": Boolean,
@@ -66,6 +68,11 @@ if (!checkInjectedBuildInfo()) {
6668
*/
6769
export const isDevelopment = args["--dev"] ?? false
6870

71+
/**
72+
* The directory where data files (configuration files, cache, credentials etc.) are stored.
73+
*/
74+
export const dataDir = determineDataDir(args["--data-dir"])
75+
6976
// eslint-disable-next-line no-extra-semi
7077
;(async () => {
7178
if ((args["--version"] ?? false) || args["_"][0] === "version") {
@@ -77,7 +84,13 @@ export const isDevelopment = args["--dev"] ?? false
7784
setupLogs(args["--log-file"])
7885

7986
outVerbose(`Filen CLI ${version}`)
80-
if (isDevelopment) outVerbose("Running in development environment")
87+
88+
let environment = "Environment: "
89+
environment += `data-dir=${dataDir}`
90+
if (isRunningAsContainer) environment += ", in container"
91+
if (isRunningAsNPMPackage) environment += ", as NPM package"
92+
if (isDevelopment) environment += ", development"
93+
outVerbose(environment)
8194

8295
if ((args["--help"] ?? false) || args["_"][0] === "help") {
8396
const topic = (args["_"][0] === "help" ? args["_"][1] : args["_"][0])?.toLowerCase() ?? "general"

src/updater.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { disableUpdates, isRunningAsNPMPackage, version } from "./buildInfo"
1+
import { isRunningAsContainer, isRunningAsNPMPackage, version } from "./buildInfo"
22
import { err, errExit, out, outVerbose, promptYesNo } from "./interface/interface"
33
import path from "path"
44
import { spawn } from "node:child_process"
5-
import { downloadFile, exists, platformConfigPath } from "./util/util"
5+
import { downloadFile, exists } from "./util/util"
66
import * as fs from "node:fs"
77
import semver from "semver/preload"
8+
import { dataDir } from "."
89

910
type UpdateCache = {
1011
lastCheckedUpdate: number
@@ -26,7 +27,7 @@ type ReleaseInfo = {
2627
* Manages updates.
2728
*/
2829
export class Updater {
29-
private readonly updateCacheDirectory = platformConfigPath()
30+
private readonly updateCacheDirectory = dataDir
3031
private readonly updateCacheFile = path.join(this.updateCacheDirectory, "updateCache.json")
3132
private readonly updateCheckExpiration = 10 * 60 * 1000 // check again after 10min
3233

@@ -72,7 +73,7 @@ export class Updater {
7273
const currentVersion = version
7374
const latestVersion = latestRelease.tag_name
7475

75-
if (disableUpdates && latestDownloadUrl !== undefined && currentVersion !== latestVersion) {
76+
if (isRunningAsContainer && latestDownloadUrl !== undefined && currentVersion !== latestVersion) {
7677
// don't prompt for update in a container environment
7778
out(`${(semver.gt(latestVersion, currentVersion) ? "Update available" : "Other version recommended")}: ${currentVersion} -> ${latestVersion}`)
7879
return

src/util/util.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,14 @@ export async function directorySize(path: PathLike) {
5555
return (await Promise.all(stats)).reduce((accumulator, { size }) => accumulator + size, 0)
5656
}
5757

58-
let _platformConfigPath: string | undefined = undefined
5958
/**
60-
* Returns the platform-specific directory for storing configuration files.
59+
* Determines the platform-specific directory for storing data files.
6160
* Creates the directory if it doesn't exist.
62-
* - Windows: `%APPDATA%\filen-cli`
63-
* - OS X: `~/Library/Application Support/filen-cli` (or `~/.filen-cli`)
64-
* - Unix: `$XDG_CONFIG_HOME/filen-cli` or `~/.config/filen-cli` (or `~/.filen-cli`)
61+
* Default locations are: `%APPDATA%\filen-cli` (Windows), `~/Library/Application Support/filen-cli` (macOS), `$XDG_CONFIG_HOME/filen-cli` or `~/.config/filen-cli` (Unix).
62+
* If it exists, `~/.filen-cli` is used instead.
63+
* If the `--data-dir` flag or `FILEN_CLI_DATA_DIR` environment variable is set, its value is used instead.
6564
*/
66-
export function platformConfigPath(): string {
67-
if (_platformConfigPath !== undefined) return _platformConfigPath
68-
65+
export function determineDataDir(dataDirFlag: string | undefined): string {
6966
// default config path, see https://github.com/jprichardson/ospath/blob/master/index.js
7067
let configPath: string = (() => {
7168
switch (process.platform) {
@@ -94,13 +91,19 @@ export function platformConfigPath(): string {
9491
configPath = pathModule.join(configPath, "dev")
9592
}
9693

94+
if (dataDirFlag !== undefined) {
95+
configPath = dataDirFlag
96+
}
97+
if (process.env.FILEN_CLI_DATA_DIR !== undefined) {
98+
configPath = process.env.FILEN_CLI_DATA_DIR
99+
}
100+
97101
if (!fsModule.existsSync(configPath)) {
98102
fsModule.mkdirSync(configPath, {
99103
recursive: true
100104
})
101105
}
102106

103-
_platformConfigPath = configPath
104107
return configPath
105108
}
106109

0 commit comments

Comments
 (0)