From dfc34df3d1fa1693b31048b29eec092dcd06ae97 Mon Sep 17 00:00:00 2001 From: kikootwo Date: Mon, 9 Mar 2026 16:37:30 -0400 Subject: [PATCH] Add configurable file/dir perms and UMASK support Introduce file and directory permission settings (fileChmod, dirChmod) end-to-end. UI: new controls in Paths settings with octal validation and defaults (664/775). API: GET exposes defaults; PUT validates octal strings and upserts configuration keys (file_chmod, dir_chmod) and clears related cache keys. Runtime: read config values in file utilities and services (FileOrganizer, direct-download, chapter-merger, epub-fixer) to apply mkdir modes and chmod files/dirs; FileOrganizer now accepts fileMode/dirMode and getFileOrganizer reads/parses DB settings. Docker: add UMASK option to docker-compose and propagate/apply UMASK in entrypoint/app-start scripts. Tests: update mocks to account for config service usage. --- docker-compose.yml | 9 ++++ docker/unified/app-start.sh | 6 +++ docker/unified/entrypoint.sh | 1 + src/app/admin/settings/lib/types.ts | 2 + .../admin/settings/tabs/PathsTab/PathsTab.tsx | 48 +++++++++++++++++++ src/app/api/admin/settings/paths/route.ts | 47 +++++++++++++++++- src/app/api/admin/settings/route.ts | 2 + .../processors/direct-download.processor.ts | 7 ++- src/lib/utils/chapter-merger.ts | 3 +- src/lib/utils/epub-fixer.ts | 6 ++- src/lib/utils/file-organizer.ts | 33 +++++++++---- .../download-torrent.processor.test.ts | 7 ++- 12 files changed, 155 insertions(+), 16 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ebcdfa1..46d108f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,6 +49,15 @@ services: PUID: 1000 PGID: 1000 + # ======================================================================== + # OPTIONAL: File Permission Mask + # ======================================================================== + # Set a umask to control default file permissions for all files created + # by the application. Common values: + # - 002: Group-writable (files: 664, dirs: 775) - recommended for shared access + # - 022: Group-readable only (files: 644, dirs: 755) - more restrictive + # UMASK: "002" + # ======================================================================== # OPTIONAL: Secrets (auto-generated on first run if not provided) # ======================================================================== diff --git a/docker/unified/app-start.sh b/docker/unified/app-start.sh index ebf9b74..e3ffcc4 100644 --- a/docker/unified/app-start.sh +++ b/docker/unified/app-start.sh @@ -22,6 +22,12 @@ PGID=${PGID:-$(id -g node)} echo "[App] Starting Next.js server..." echo "[App] Process will run as UID:GID = $PUID:$PGID" +# Apply UMASK if set (controls default file permissions) +if [ -n "$UMASK" ]; then + echo "[App] Applying umask: $UMASK" + umask "$UMASK" +fi + cd /app # ============================================================================= diff --git a/docker/unified/entrypoint.sh b/docker/unified/entrypoint.sh index 76fb8c2..7bd08fe 100644 --- a/docker/unified/entrypoint.sh +++ b/docker/unified/entrypoint.sh @@ -387,6 +387,7 @@ PORT=$PORT HOSTNAME=$HOSTNAME PUID=${PUID:-} PGID=${PGID:-} +UMASK=${UMASK:-} ROOTLESS_CONTAINER=${ROOTLESS_CONTAINER:-} EOF diff --git a/src/app/admin/settings/lib/types.ts b/src/app/admin/settings/lib/types.ts index bbb4070..264c374 100644 --- a/src/app/admin/settings/lib/types.ts +++ b/src/app/admin/settings/lib/types.ts @@ -102,6 +102,8 @@ export interface PathsSettings { chapterMergingEnabled: boolean; fileRenameEnabled: boolean; fileRenameTemplate?: string; + fileChmod?: string; + dirChmod?: string; } /** diff --git a/src/app/admin/settings/tabs/PathsTab/PathsTab.tsx b/src/app/admin/settings/tabs/PathsTab/PathsTab.tsx index d1f3646..a1000f9 100644 --- a/src/app/admin/settings/tabs/PathsTab/PathsTab.tsx +++ b/src/app/admin/settings/tabs/PathsTab/PathsTab.tsx @@ -439,6 +439,54 @@ export function PathsTab({ paths, onChange, onValidationChange }: PathsTabProps) + {/* File Permissions */} +
+

+ File Permissions +

+

+ Octal permissions applied when organizing files into the media library. These may be further restricted by the container's UMASK setting. +

+
+
+ + updatePath('fileChmod', e.target.value)} + placeholder="664" + className={`font-mono max-w-32 ${paths.fileChmod && !/^[0-7]{3,4}$/.test(paths.fileChmod) ? 'border-red-500 dark:border-red-500' : ''}`} + /> + {paths.fileChmod && !/^[0-7]{3,4}$/.test(paths.fileChmod) && ( +

Must be 3-4 octal digits (0-7)

+ )} +

+ e.g. 664 = owner/group read-write, others read +

+
+
+ + updatePath('dirChmod', e.target.value)} + placeholder="775" + className={`font-mono max-w-32 ${paths.dirChmod && !/^[0-7]{3,4}$/.test(paths.dirChmod) ? 'border-red-500 dark:border-red-500' : ''}`} + /> + {paths.dirChmod && !/^[0-7]{3,4}$/.test(paths.dirChmod) && ( +

Must be 3-4 octal digits (0-7)

+ )} +

+ e.g. 775 = owner/group full access, others read-execute +

+
+
+
+ {/* Test Paths Button */}