Export JSON… gives scripts and agents one file with endpoint, key, secret and drive. The desktop app now ships for Linux too (AppImage, .deb, tar.gz), using per-user s3d paths there.
73 lines
3.2 KiB
JavaScript
73 lines
3.2 KiB
JavaScript
// Where s3d keeps its config and data on each OS, mirroring the lookup in
|
|
// s3d's cmd/s3d/main.go (configSearchPaths / applicationDirectoryOS) so the
|
|
// GUI and a hand-run `s3d` always agree on which instance they are touching.
|
|
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
// s3d's Linux defaults (/etc/s3d, /var/lib/s3d) suit a system service. A
|
|
// desktop user who cannot write there, and has no system config, gets the
|
|
// XDG per-user places instead; new configs then name that directory
|
|
// explicitly, so a hand-run s3d with S3D_CONFIG_FILE agrees.
|
|
const xdg = (envName, fallback) => process.env[envName] || path.join(os.homedir(), ...fallback);
|
|
const userConfigFile = () => path.join(xdg('XDG_CONFIG_HOME', ['.config']), 's3d', 's3d.yml');
|
|
const userDataDir = () => path.join(xdg('XDG_DATA_HOME', ['.local', 'share']), 's3d');
|
|
|
|
function writable(p) {
|
|
// the nearest existing folder decides whether p could be created
|
|
for (let d = p; ; d = path.dirname(d)) {
|
|
if (fs.existsSync(d)) { try { fs.accessSync(d, fs.constants.W_OK); return true; } catch { return false; } }
|
|
if (path.dirname(d) === d) return false;
|
|
}
|
|
}
|
|
|
|
export function linuxUserMode() {
|
|
if (process.platform === 'win32' || process.platform === 'darwin') return false;
|
|
if (['/etc/s3d/s3d.yml', '/var/lib/s3d/s3d.yml'].some((p) => fs.existsSync(p))) return false;
|
|
return !(writable('/etc/s3d') && writable('/var/lib/s3d'));
|
|
}
|
|
|
|
export function defaultDataDir() {
|
|
switch (process.platform) {
|
|
case 'win32': return path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 's3d');
|
|
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support', 's3d');
|
|
default: return linuxUserMode() ? userDataDir() : '/var/lib/s3d';
|
|
}
|
|
}
|
|
|
|
export function configSearchPaths() {
|
|
const paths = [];
|
|
if (process.env.S3D_DATA_DIR) paths.push(path.join(process.env.S3D_DATA_DIR, 's3d.yml'));
|
|
switch (process.platform) {
|
|
case 'win32': paths.push(path.join(defaultDataDir(), 's3d.yml')); break;
|
|
case 'darwin': paths.push(path.join(defaultDataDir(), 's3d.yml')); break;
|
|
default:
|
|
if (linuxUserMode()) paths.push(userConfigFile());
|
|
paths.push('/etc/s3d/s3d.yml', '/var/lib/s3d/s3d.yml');
|
|
}
|
|
return paths;
|
|
}
|
|
|
|
// The config file this GUI manages: an explicit S3D_CONFIG_FILE wins, then the
|
|
// first existing search path, then the first search path (created on save).
|
|
export function resolveConfigPath(explicit) {
|
|
if (explicit) return path.resolve(explicit);
|
|
if (process.env.S3D_CONFIG_FILE) return path.resolve(process.env.S3D_CONFIG_FILE);
|
|
const candidates = configSearchPaths();
|
|
return candidates.find((p) => fs.existsSync(p)) || candidates[0];
|
|
}
|
|
|
|
const EXE = process.platform === 'win32' ? 's3d.exe' : 's3d';
|
|
|
|
// Binary lookup: explicit path, S3D_BIN, the vendored copy next to Pithos, then PATH.
|
|
export function resolveBinary(explicit, vendorDir) {
|
|
const tries = [explicit, process.env.S3D_BIN, vendorDir && path.join(vendorDir, EXE)].filter(Boolean);
|
|
for (const p of tries) if (fs.existsSync(p)) return path.resolve(p);
|
|
for (const dir of (process.env.PATH || '').split(path.delimiter)) {
|
|
if (!dir) continue;
|
|
const p = path.join(dir, EXE);
|
|
if (fs.existsSync(p)) return p;
|
|
}
|
|
return null;
|
|
}
|