From 659b3924c0aae8c737a5f6e884be78507c2e6785 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 8 Jun 2026 01:25:15 +0000 Subject: [PATCH 1/3] fix manage_users.sh: strip fields that cause 400 on newer FileBrowser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed dateFormat, hideDotfiles, singleClick, and sorting from the POST /api/users payload — FileBrowser rejects them as invalid data types on some versions. Only stable fields are now sent. https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt --- tools/manage_users.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/manage_users.sh b/tools/manage_users.sh index e0d3186..03212f3 100644 --- a/tools/manage_users.sh +++ b/tools/manage_users.sh @@ -360,8 +360,7 @@ cmd_add() { --arg u "$_username" --arg p "$password" --arg s "$_scope" \ --argjson perms "$_perms" \ '{username:$u, password:$p, scope:$s, locale:"en", viewMode:"list", - singleClick:false, sorting:{by:"name",asc:true}, perm:$perms, - commands:[], lockPassword:false, hideDotfiles:false, dateFormat:false}') + perm:$perms, commands:[], lockPassword:false}') local _resp _http_code _resp=$(curl -s -w '\n%{http_code}' -X POST "$FB_URL/api/users" \ From 39c85326e624cfbd01492a1808d91a47d5878404 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 8 Jun 2026 02:23:07 +0000 Subject: [PATCH 2/3] =?UTF-8?q?filebrowser:=20named=20volume=20for=20user?= =?UTF-8?q?=20dirs,=20rename=20linked=E2=86=92additional=20dirs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docker-compose now uses two separate mounts: fb_users named volume → /srv (user home dirs + shortcuts, Docker only) FB_PATH bind mount → /srv/data (actual files, unchanged on host) User dirs and their shortcuts live entirely in the Docker named volume — they persist across reboots but never appear on the host filesystem. Full-access scope is /data; per-user scopes are /alice etc. manage_users.sh: - Detect layout: get_data_root() returns /srv/data (new) or /srv (legacy) so the script works with both old and new installs automatically - "Linked directories" renamed to "additional directories" throughout (menu labels, prompts, error messages, usage text) - prompt_add_links → prompt_add_dirs, menu_links → menu_add_dirs - Available-folder listing and symlink targets use get_data_root() - Scope examples updated to show /data for full access (new layout) - Don't offer additional directories when scope is /data (full access) https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt --- services/filebrowser.sh | 11 +++- tools/manage_users.sh | 138 ++++++++++++++++++++++------------------ 2 files changed, 84 insertions(+), 65 deletions(-) diff --git a/services/filebrowser.sh b/services/filebrowser.sh index 57e611a..3cff91a 100644 --- a/services/filebrowser.sh +++ b/services/filebrowser.sh @@ -184,7 +184,8 @@ services: environment: - TZ=${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)} volumes: - - ${FB_PATH}:/srv + - fb_users:/srv + - ${FB_PATH}:/srv/data - ./database/filebrowser.db:/database/filebrowser.db - ./config/settings.json:/config/settings.json ports: @@ -192,6 +193,9 @@ services: networks: - caddy_net +volumes: + fb_users: + networks: caddy_net: external: true @@ -243,7 +247,10 @@ Web-based file manager. Browse, upload, and download files through a browser. - Default login: admin / admin (change immediately!) ## Data -- Browsed path: $FB_PATH (mounted to /srv) +- Browsed path: $FB_PATH (mounted to /srv/data inside container) +- User home dirs: Docker named volume fb_users (no host clutter) +- Admin full-access scope: /data +- Per-user scope example: /alice - Database: ./database/filebrowser.db - Settings: ./config/settings.json diff --git a/tools/manage_users.sh b/tools/manage_users.sh index 03212f3..bc019a0 100644 --- a/tools/manage_users.sh +++ b/tools/manage_users.sh @@ -15,25 +15,24 @@ # Minimum 8 characters. No maximum. # Must contain at least one letter and one number. # -# ── Scope (file path) ───────────────────────────────────────────────────────── -# FileBrowser supports ONE scope path per user. -# Scope is an absolute path inside the container, relative to /srv (= FB_PATH). +# ── Scope ───────────────────────────────────────────────────────────────────── +# Scope is the root folder a user sees when they log in. +# It is a path inside the container relative to /srv. # -# If FB_PATH=~/drives/data1: -# / → full access (all of ~/drives/data1) -# /alice → alice's own subdir (pair with linked dirs below) -# /music → music subdir only +# New layout (fb_users named volume + /srv/data bind mount): +# /data → full access to all files (FB_PATH on the host) +# /alice → alice's private home folder (stored in Docker volume only) # -# ── Multi-directory access via linked directories ──────────────────────────── -# FileBrowser follows symlinks inside the scope dir. -# Give a user scope=/alice, then link extra folders into /alice: +# Legacy layout (FB_PATH mounted directly at /srv): +# / → full access +# /alice → alice's private subdir inside FB_PATH on the host # -# /alice/music → /srv/music (alice sees "music" in her root) -# /alice/photos → /srv/photos (alice sees "photos" too) -# -# This script creates those symlinks via "docker exec", so they live in -# the bind-mount on the host as well (target is /srv/... so it appears -# broken from the host, but resolves correctly inside the container). +# ── Additional directories ──────────────────────────────────────────────────── +# Each user has exactly one scope, but you can give them access to more +# folders by adding directory shortcuts inside their scope. +# FileBrowser follows symlinks, so a link placed in /alice pointing to +# /data/music lets alice browse "music" alongside her own files. +# These shortcuts live in the Docker named volume — no clutter on the host. # set -Eeuo pipefail @@ -169,7 +168,17 @@ check_container() { || { errmsg "Container '$1' is not running. Start it: docker compose up -d"; return 1; } } -# Print symlinks inside /srv, one per line. Paths shown as FileBrowser sees them (no /srv). +# Returns /srv/data if the new named-volume layout is in use, else /srv (legacy). +get_data_root() { + local _c="$1" + if docker exec "$_c" test -d /srv/data 2>/dev/null; then + echo "/srv/data" + else + echo "/srv" + fi +} + +# Print additional directories inside /srv, one per line. list_links() { local _c="$1" _scope="$2" docker exec "$_c" find "/srv$_scope" -maxdepth 1 -type l \ @@ -182,14 +191,15 @@ list_links() { ' _ {} \; 2>/dev/null | sort || true } -# ── prompt_add_links SCOPE ──────────────────────────────────────────────────── +# ── prompt_add_dirs SCOPE ───────────────────────────────────────────────────── # Loop: user types folder names as they appear in FileBrowser — blank to finish. -# Shared by cmd_add (offered inline) and menu_links (add option). -prompt_add_links() { +# Shared by cmd_add (offered inline) and menu_add_dirs (add option). +prompt_add_dirs() { local _scope="$1" - local _c + local _c _data_root _c=$(get_container_name) check_container "$_c" || return 1 + _data_root=$(get_data_root "$_c") local _scope_dir="/srv$_scope" if ! docker exec "$_c" mkdir -p "$_scope_dir" 2>/dev/null; then @@ -203,15 +213,16 @@ prompt_add_links() { while true; do local _src="" - read -r -p " Folder to add [done]: " _src + read -r -p " Directory to add [done]: " _src [[ -n "$_src" ]] || break if [[ "$_src" == "?" ]]; then local _avail - _avail=$(docker exec "$_c" find /srv -maxdepth 1 -mindepth 1 \ + _avail=$(docker exec "$_c" find "$_data_root" -maxdepth 1 -mindepth 1 \ \( -type d -o -type l \) \ - -not -name ".*" 2>/dev/null | sed 's|^/srv/||' | sort | tr '\n' ' ') || true - echo " Available: ${_avail:-(none — no subdirectories in /srv)}" + -not -name ".*" 2>/dev/null \ + | sed "s|^${_data_root}/||" | sort | tr '\n' ' ') || true + echo " Available: ${_avail:-(none — no subdirectories found)}" echo continue fi @@ -221,43 +232,43 @@ prompt_add_links() { _src="${_src%/}" [[ -n "$_src" ]] || continue - local _target="/srv/$_src" + local _target="$_data_root/$_src" local _link_name _link_name=$(basename "$_src") if ! docker exec "$_c" test -e "$_target" 2>/dev/null; then - errmsg "'$_src' not found in FileBrowser — check the path and try again." + errmsg "'$_src' not found — type ? to see available folders." continue fi local _link_path="$_scope_dir/$_link_name" if docker exec "$_c" test -e "$_link_path" 2>/dev/null; then - errmsg "'$_link_name' already exists in this user's folder — use Remove to clear it first." + errmsg "'$_link_name' already added — use Remove to clear it first." continue fi if ! docker exec "$_c" ln -s "$_target" "$_link_path" 2>/dev/null; then - errmsg "Failed to create symlink inside container (check container logs)." + errmsg "Failed to add directory (check: docker logs filebrowser)." continue fi if [[ "$_src" == "$_link_name" ]]; then - ok "User can now see '$_link_name'" + ok "Added '$_link_name' to user's view" else - ok "User can now see '$_link_name' ${DIM}(from $_src)${R}" + ok "Added '$_link_name' ${DIM}(from $_src)${R}" fi done } -# ── Linked-directory submenu (shown from Modify option 5) ──────────────────── -menu_links() { +# ── Additional directories submenu (shown from Modify option 5) ────────────── +menu_add_dirs() { local _scope="$1" local _c _c=$(get_container_name) check_container "$_c" || return 1 while true; do - banner "Linked directories (scope: $_scope)" - echo " Folders this user can see beyond their scope:" + banner "Additional directories (scope: $_scope)" + echo " Folders this user can access beyond their scope:" echo local _links _links=$(list_links "$_c" "$_scope") @@ -267,22 +278,22 @@ menu_links() { echo " (none)" fi echo - echo " 1 Add folders" - echo " 2 Remove a folder" + echo " 1 Add a directory" + echo " 2 Remove a directory" echo " 0 Back" echo local _ch="" read -r -p " Choice: " _ch case "$_ch" in - 1) prompt_add_links "$_scope" || true ;; + 1) prompt_add_dirs "$_scope" || true ;; 2) local _link_name="" echo - read -r -p " Folder name to remove: " _link_name + read -r -p " Directory name to remove: " _link_name [[ -n "$_link_name" ]] || continue local _link_path="/srv${_scope}/${_link_name}" if ! docker exec "$_c" test -L "$_link_path" 2>/dev/null; then - errmsg "'$_link_name' is not a linked folder — refusing to delete." + errmsg "'$_link_name' is not an added directory — refusing to delete." continue fi docker exec "$_c" rm "$_link_path" @@ -319,7 +330,7 @@ cmd_add() { echo echo " ${B}Username:${R} letters, numbers, hyphens, underscores only. No dots or @." echo " ${B}Password:${R} min 8 chars, at least 1 letter and 1 number." - echo " ${B}Scope:${R} one path per user — use linked dirs for multi-folder access." + echo " ${B}Scope:${R} the root folder the user sees — add extra directories after." echo read -r -p " Username: " _username local _adm="" @@ -331,10 +342,11 @@ cmd_add() { if [[ -z "$_scope" ]]; then echo - echo " Scope examples:" - echo " / full access (all of FB_PATH)" - echo " $_username user's own private subdir (pair with linked dirs)" - echo " music music subdir only" + echo " Scope — what the user sees as their root when they log in:" + echo " /data full access to all files (new layout)" + echo " / full access to all files (legacy layout)" + echo " /$_username private home folder for this user" + echo " /music music folder only (no home, can't add extras)" echo read -r -p " Scope for '$_username': " _scope _scope="/${_scope#/}" # ensure leading / @@ -382,12 +394,12 @@ cmd_add() { echo ok "User '$_username' created | scope: $_scope | admin: $_is_admin" - # Offer linked directories (pointless if scope is already /) - if command -v docker &>/dev/null && [[ "$_scope" != "/" ]]; then - local _do_links="" - read -r -p " Add linked directories for '$_username'? [y/N]: " _do_links - if [[ "${_do_links,,}" == "y" ]]; then - prompt_add_links "$_scope" || true + # Offer additional directories when user has a private home (not full access) + if command -v docker &>/dev/null && [[ "$_scope" != "/" && "$_scope" != "/data" ]]; then + local _do_dirs="" + read -r -p " Add additional directories for '$_username'? [y/N]: " _do_dirs + if [[ "${_do_dirs,,}" == "y" ]]; then + prompt_add_dirs "$_scope" || true fi fi } @@ -411,7 +423,7 @@ cmd_delete() { [[ "${_c,,}" == "y" ]] || { echo " Aborted."; return 0; } api_delete "/api/users/$_uid" >/dev/null ok "User '$_username' deleted." - echo " ${DIM}Note: symlinks in their scope dir still exist on disk if you want to reuse them.${R}" + echo " ${DIM}Note: their scope directory and additional directories still exist in the Docker volume.${R}" } # ── cmd: passwd ─────────────────────────────────────────────────────────────── @@ -461,7 +473,7 @@ cmd_scope() { if [[ -z "$_new_scope" ]]; then echo echo " Current scope: $_old_scope" - echo " ${DIM}Linked folders in the old scope are not moved automatically.${R}" + echo " ${DIM}Additional directories in the old scope are not moved automatically.${R}" echo read -r -p " New scope: " _new_scope _new_scope="/${_new_scope#/}" @@ -474,12 +486,12 @@ cmd_scope() { echo ok "Scope updated for '$_username': $_old_scope → $_new_scope" - # Offer to add links into the new scope - if command -v docker &>/dev/null && [[ "$_new_scope" != "/" ]]; then - local _do_links="" - read -r -p " Add linked directories into '$_new_scope'? [y/N]: " _do_links - if [[ "${_do_links,,}" == "y" ]]; then - prompt_add_links "$_new_scope" || true + # Offer to add directories into the new scope + if command -v docker &>/dev/null && [[ "$_new_scope" != "/" && "$_new_scope" != "/data" ]]; then + local _do_dirs="" + read -r -p " Add additional directories into '$_new_scope'? [y/N]: " _do_dirs + if [[ "${_do_dirs,,}" == "y" ]]; then + prompt_add_dirs "$_new_scope" || true fi fi } @@ -558,7 +570,7 @@ menu_modify() { echo " 2 Change password" echo " 3 Change scope (file path)" echo " 4 Toggle admin status" - echo " 5 Linked directories ${DIM}(add/remove multi-folder symlinks)${R}" + echo " 5 Additional directories ${DIM}(add/remove extra folder access)${R}" echo " 0 Back" echo local _ch="" @@ -595,7 +607,7 @@ menu_modify() { errmsg "Toggle failed." fi ;; - 5) menu_links "$_scope" || true ;; + 5) menu_add_dirs "$_scope" || true ;; 0) break ;; *) errmsg "Invalid choice." ;; esac @@ -622,7 +634,7 @@ Scope is relative to /srv inside the container (= FB_PATH on the host). Username: letters, numbers, hyphens, underscores only. No dots or @. Password: min 8 chars, at least one letter and one number. -Multi-directory access: use the interactive menu — linked directories +Additional directories: use the interactive menu — extra folders are offered automatically when you add a user or change their scope. Override URL: FB_URL=http://localhost:8085 ./manage_users.sh @@ -639,7 +651,7 @@ run_interactive() { echo " 1 List users" echo " 2 Add user" echo " 3 Delete user" - echo " 4 Modify user (username / password / scope / admin / links)" + echo " 4 Modify user (username / password / scope / admin / directories)" echo " 5 View user details" echo " 0 Exit" echo From c2df116a72aeb6e301e256abb7756c6719f72919 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 8 Jun 2026 02:30:53 +0000 Subject: [PATCH 3/3] =?UTF-8?q?manage=5Fusers.sh:=20don't=20auto-create=20?= =?UTF-8?q?user=20dir,=20rename=20scope=E2=86=92directory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove silent mkdir -p when adding extra directories. If the user's directory doesn't exist in the container, ask before creating it. This avoids creating folders the admin didn't intend. - Rename all user-facing "scope" text to "directory" throughout: prompts, banners, menu labels, column headers, help text, error messages. The FileBrowser API field is still called "scope" internally, and the CLI subcommand name stays "scope" for compat. https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt --- tools/manage_users.sh | 86 +++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/tools/manage_users.sh b/tools/manage_users.sh index bc019a0..280c888 100644 --- a/tools/manage_users.sh +++ b/tools/manage_users.sh @@ -15,24 +15,24 @@ # Minimum 8 characters. No maximum. # Must contain at least one letter and one number. # -# ── Scope ───────────────────────────────────────────────────────────────────── -# Scope is the root folder a user sees when they log in. +# ── Directory ───────────────────────────────────────────────────────────────── +# Each user has a starting directory — the root they see when they log in. # It is a path inside the container relative to /srv. # # New layout (fb_users named volume + /srv/data bind mount): # /data → full access to all files (FB_PATH on the host) -# /alice → alice's private home folder (stored in Docker volume only) +# /alice → alice's folder (must already exist, or be created separately) # # Legacy layout (FB_PATH mounted directly at /srv): # / → full access -# /alice → alice's private subdir inside FB_PATH on the host +# /alice → alice's subfolder inside FB_PATH on the host # # ── Additional directories ──────────────────────────────────────────────────── -# Each user has exactly one scope, but you can give them access to more -# folders by adding directory shortcuts inside their scope. -# FileBrowser follows symlinks, so a link placed in /alice pointing to +# Each user has exactly one starting directory, but you can give them access +# to more folders by adding shortcuts inside it. +# FileBrowser follows symlinks, so a shortcut placed in /alice pointing to # /data/music lets alice browse "music" alongside her own files. -# These shortcuts live in the Docker named volume — no clutter on the host. +# Shortcuts live in the Docker named volume — no clutter on the host. # set -Eeuo pipefail @@ -76,7 +76,7 @@ validate_password() { } validate_scope() { - [[ "$1" == /* ]] || { errmsg "Scope must start with / (e.g. / or /alice or /music)"; return 1; } + [[ "$1" == /* ]] || { errmsg "Directory must start with / (e.g. / or /alice or /music)"; return 1; } } # prompt_password VARNAME [label] @@ -202,9 +202,21 @@ prompt_add_dirs() { _data_root=$(get_data_root "$_c") local _scope_dir="/srv$_scope" - if ! docker exec "$_c" mkdir -p "$_scope_dir" 2>/dev/null; then - errmsg "Could not create scope directory '$_scope_dir' in container (permission denied?)." - return 1 + if ! docker exec "$_c" test -d "$_scope_dir" 2>/dev/null; then + echo + echo " The directory '$_scope' does not exist in the container yet." + local _mk="" + read -r -p " Create it now? [y/N]: " _mk + if [[ "${_mk,,}" == "y" ]]; then + if ! docker exec "$_c" mkdir -p "$_scope_dir" 2>/dev/null; then + errmsg "Could not create '$_scope_dir' in container (permission denied?)." + return 1 + fi + ok "Created '$_scope'" + else + errmsg "Cannot add directories — '$_scope' must exist first." + return 1 + fi fi echo @@ -267,8 +279,8 @@ menu_add_dirs() { check_container "$_c" || return 1 while true; do - banner "Additional directories (scope: $_scope)" - echo " Folders this user can access beyond their scope:" + banner "Additional directories ($_scope)" + echo " Extra folders this user can access:" echo local _links _links=$(list_links "$_c" "$_scope") @@ -309,8 +321,8 @@ menu_add_dirs() { cmd_list() { ensure_token echo - printf " ${B}%-22s %-5s %s${R}\n" "USERNAME" "ADMIN" "SCOPE" - printf " %-22s %-5s %s\n" "--------" "-----" "-----" + printf " ${B}%-22s %-5s %s${R}\n" "USERNAME" "ADMIN" "DIRECTORY" + printf " %-22s %-5s %s\n" "--------" "-----" "---------" api_get "/api/users" | \ jq -r '.[] | [.username, (if .perm.admin then "yes" else "no" end), .scope] | @tsv' | \ while IFS=$'\t' read -r _u _a _s; do @@ -330,7 +342,7 @@ cmd_add() { echo echo " ${B}Username:${R} letters, numbers, hyphens, underscores only. No dots or @." echo " ${B}Password:${R} min 8 chars, at least 1 letter and 1 number." - echo " ${B}Scope:${R} the root folder the user sees — add extra directories after." + echo " ${B}Directory:${R} the folder the user sees as their root — add extras after." echo read -r -p " Username: " _username local _adm="" @@ -342,13 +354,13 @@ cmd_add() { if [[ -z "$_scope" ]]; then echo - echo " Scope — what the user sees as their root when they log in:" + echo " Starting directory — what the user sees as their root when they log in:" echo " /data full access to all files (new layout)" echo " / full access to all files (legacy layout)" - echo " /$_username private home folder for this user" - echo " /music music folder only (no home, can't add extras)" + echo " /$_username this user's own folder (must already exist)" + echo " /music music folder only" echo - read -r -p " Scope for '$_username': " _scope + read -r -p " Directory for '$_username': " _scope _scope="/${_scope#/}" # ensure leading / fi @@ -392,7 +404,7 @@ cmd_add() { fi echo - ok "User '$_username' created | scope: $_scope | admin: $_is_admin" + ok "User '$_username' created | directory: $_scope | admin: $_is_admin" # Offer additional directories when user has a private home (not full access) if command -v docker &>/dev/null && [[ "$_scope" != "/" && "$_scope" != "/data" ]]; then @@ -423,7 +435,7 @@ cmd_delete() { [[ "${_c,,}" == "y" ]] || { echo " Aborted."; return 0; } api_delete "/api/users/$_uid" >/dev/null ok "User '$_username' deleted." - echo " ${DIM}Note: their scope directory and additional directories still exist in the Docker volume.${R}" + echo " ${DIM}Note: their directory and any added extras still exist in the Docker volume.${R}" } # ── cmd: passwd ─────────────────────────────────────────────────────────────── @@ -472,10 +484,10 @@ cmd_scope() { if [[ -z "$_new_scope" ]]; then echo - echo " Current scope: $_old_scope" - echo " ${DIM}Additional directories in the old scope are not moved automatically.${R}" + echo " Current directory: $_old_scope" + echo " ${DIM}Additional directories from the old location are not moved automatically.${R}" echo - read -r -p " New scope: " _new_scope + read -r -p " New directory: " _new_scope _new_scope="/${_new_scope#/}" fi validate_scope "$_new_scope" || return 1 @@ -484,7 +496,7 @@ cmd_scope() { _body=$(echo "$_user" | jq --arg s "$_new_scope" '. + {scope: $s}') api_put "/api/users/$_uid" "$_body" >/dev/null echo - ok "Scope updated for '$_username': $_old_scope → $_new_scope" + ok "Directory updated for '$_username': $_old_scope → $_new_scope" # Offer to add directories into the new scope if command -v docker &>/dev/null && [[ "$_new_scope" != "/" && "$_new_scope" != "/data" ]]; then @@ -563,12 +575,12 @@ menu_modify() { _admin=$(echo "$_user" | jq -r 'if .perm.admin then "yes" else "no" end') banner "Modify: $_cur" - echo " ${B}Scope:${R} $_scope" - echo " ${B}Admin:${R} $_admin" + echo " ${B}Directory:${R} $_scope" + echo " ${B}Admin:${R} $_admin" echo echo " 1 Change username" echo " 2 Change password" - echo " 3 Change scope (file path)" + echo " 3 Change directory" echo " 4 Toggle admin status" echo " 5 Additional directories ${DIM}(add/remove extra folder access)${R}" echo " 0 Back" @@ -623,19 +635,21 @@ FileBrowser user management One-shot usage: manage_users.sh list - manage_users.sh add [--admin] + manage_users.sh add [--admin] manage_users.sh delete manage_users.sh passwd - manage_users.sh scope + manage_users.sh scope manage_users.sh rename manage_users.sh info -Scope is relative to /srv inside the container (= FB_PATH on the host). +Directory is a path relative to /srv inside the container. + New layout: /data = full access, /alice = alice's folder + Legacy: / = full access, /alice = alice's folder Username: letters, numbers, hyphens, underscores only. No dots or @. Password: min 8 chars, at least one letter and one number. -Additional directories: use the interactive menu — extra folders -are offered automatically when you add a user or change their scope. +Additional directories: use the interactive menu — extras +are offered automatically when you add a user or change their directory. Override URL: FB_URL=http://localhost:8085 ./manage_users.sh EOF @@ -651,7 +665,7 @@ run_interactive() { echo " 1 List users" echo " 2 Add user" echo " 3 Delete user" - echo " 4 Modify user (username / password / scope / admin / directories)" + echo " 4 Modify user (username / password / directory / admin / extras)" echo " 5 View user details" echo " 0 Exit" echo