Files
wekan/models/lib/attachmentStoragePath.js
Lauri Ojansivu 31e569a18e Single-file WeKan for every platform with a Node.js bundle.
One executable per platform: that platform's published bundle repackaged
as a Node.js single executable application whose ZIP Node 26.11 mounts as
a virtual file system (useVfs + vfsArchive). Only FerretDB is copied out,
once and verified, to wekan-files/bin; native addons load from the VFS.

Everything WeKan writes is in wekan-files beside the executable, which
WRITABLE_PATH points at: attachments, avatars, db, bin, backup, logs,
versions. WeKan now treats a WRITABLE_PATH named wekan-files as the files
root (models/lib/filesRoot.js, shared by every place that derived it).

Started in a terminal it shows a menu: start, stop, status, version info
as Admin Panel / Version shows it, update to the newest release or change
to any other (checksum verified, backed up first, the old executable
kept), backup to wekan-files/backup/YYYY-MM-DD_HH-MM-SS, run at boot
(systemd, launchd, Task Scheduler, cron, Termux:Boot), log, settings.
Every entry is also a command. start runs the start-wekan.sh loop:
recovery, requested restore, FerretDB, recovery page, WeKan, restart.

Meteor's main.js chdir()s into the bundle, which a VFS cannot be, so the
server role keeps a virtual working directory. Node 26.11.1 breaks
fs.writev/readv while any VFS is mounted; they are rebuilt on fs.write
and fs.read.

single-file.yml builds every target in releases/single-file/targets.json
from its verified bundle (Macs on a Mac, to re-sign), starts it where a
runner exists with browser tests on amd64, and attaches what started.
Release All and Release All Missing call it.

Thanks to xet7 !
2026-10-13 21:08:09 +03:00

36 lines
1.6 KiB
JavaScript

// Pure, dependency-free storage path computation (no Meteor imports) so it can
// be unit tested directly with plain Node. Used by the attachment storage
// settings (Admin Panel > Attachments) and kept in sync with the path logic in
// models/attachments.js, models/avatars.js and server/initializeDirs.js.
//
// #6473: the Admin Panel used to compute these paths ON THE CLIENT from
// process.env.WRITABLE_PATH, which never exists in a browser, so it always
// showed "/data" — a path that does not exist on a Snap install
// (WRITABLE_PATH=$SNAP_COMMON/files there). The real paths are:
// - Docker (WRITABLE_PATH=/data): /data/files/{attachments,avatars}
// - Snap (WRITABLE_PATH=$SNAP_COMMON/files): $SNAP_COMMON/files/{attachments,avatars}
// - Dev (WRITABLE_PATH unset): <cwd>/files/{attachments,avatars}
//
// Snap already appends '/files' to WRITABLE_PATH, so that case is detected and
// '/files' is not appended twice.
const path = require('path');
const { isFilesRoot } = require('./filesRoot');
/**
* Compute the real attachment/avatar storage paths for a given base path.
* @param {string} [basePath] WRITABLE_PATH (falls back to process.cwd())
* @return {{writablePath: string, attachments: string, avatars: string}}
*/
function computeStoragePaths(basePath) {
const base = basePath || process.cwd();
const endsWithFiles = isFilesRoot(base);
const root = endsWithFiles ? base : path.join(base, 'files');
return {
writablePath: base,
attachments: path.join(root, 'attachments'),
avatars: path.join(root, 'avatars'),
};
}
module.exports = { computeStoragePaths };