No description
  • TypeScript 79.4%
  • HTML 10%
  • CSS 6.7%
  • JavaScript 2.2%
  • Shell 1.3%
  • Other 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-13 02:24:41 +02:00
bin sharing part 1, provisioning and ACLs 2026-09-11 13:14:14 +02:00
deploy fix keep executable bit 2026-09-11 13:18:01 +02:00
src fix share display when using and docker, need to use numeric Id's because the user does not exist inside the docker container, respectively it is deno inside 2026-09-13 02:24:41 +02:00
tests fix share display when using and docker, need to use numeric Id's because the user does not exist inside the docker container, respectively it is deno inside 2026-09-13 02:24:41 +02:00
.dockerignore docker deployment 2026-09-12 22:14:44 +02:00
.env.example thumbnail generation and display 2026-09-03 23:25:47 +02:00
.gitignore fix command and read me 2026-09-13 01:09:28 +02:00
deno.json fix command and read me 2026-09-13 01:09:28 +02:00
deno.lock render markdown in preview 2026-09-04 21:06:42 +02:00
docker-compose.example.yml docker deployment 2026-09-12 22:14:44 +02:00
Dockerfile docker deployment 2026-09-12 22:14:44 +02:00
README.md fix command and read me 2026-09-13 01:09:28 +02:00
SPEC.md fix share display when using and docker, need to use numeric Id's because the user does not exist inside the docker container, respectively it is deno inside 2026-09-13 02:24:41 +02:00

files-web-ui

This is the source code for my personal file manager: a small, self-hosted web interface for the files on my own server.

I used to run Nextcloud for this. Nextcloud is impressive software and it is very good at being everything for everyone — groupware, office, calendars, sync clients, an app store. That is exactly why it stopped being the right tool for me: I am one person who mostly wants to browse, upload, and edit the files in my home directory over HTTP. For that, Nextcloud asks me to run and maintain a PHP application server, a database, a job scheduler and an app ecosystem, and to give all of that resources that the actual job — serving some files — does not really need.

So I wrote a smaller thing instead. It covers the parts I actually used:

  • log in, browse folders as a list or a grid
  • upload files, create text files and folders, edit text files online
  • image thumbnails and a viewer, markdown preview
  • download, and delete as a soft delete with a trash you can restore from
  • share folders with other users on the server, as read or edit access, backed by POSIX ACLs so the web UI and Samba see the same permissions
  • a small JSON API so my phone can reach the same files

Your files stay on the disk where they are. There is no database holding your data — SQLite only stores accounts and sessions, the trash keeps its metadata in a sidecar file next to the moved item, and thumbnails live in their own cache folder. The server is Deno with server-rendered templates and a little HTMX: no build step, no SPA, no framework lock-in.

Running it

  • deno task user:add <name> — create a user
  • deno task start — serve on :3000 (port, file root, upload limits etc. are env vars with sensible defaults; everything it writes lives in data/)
  • deno task test — integration test suite

Design decisions and their reasoning are collected in SPEC.md.

Deployment

For a multi-user setup the app runs as a dedicated service account (default filesweb) that owns everything under data/. Human users never own files; their access — their own tree, and shares granted by other users — comes from named POSIX ACL entries that the service manages itself, so it needs no root privileges at runtime. getfacl shows the complete, auditable permission state on disk.

  • sudo deploy/setup.sh — one-time: create the service account, prepare the app directory
  • sudo deploy/migrate-fs.sh — one-time: migrate existing data (chown to the service account, setgid dirs, default ACLs per user tree)
  • sudo deploy/provision-user.sh <name> — create a user (system account + file tree + database entry); replaces deno task user:add on the server
  • deploy/files-web.service — sample systemd unit

If the files are exposed over Samba, the share must use inherit owner = yes and inherit acls = yes, so files created by clients stay owned by the service account and keep the ACL inheritance intact.

Known corners of this model: files created directly on the server (SSH/shell) are owned by that user — the app can still read and write them via group access, but it cannot manage their ACLs. A restrictive chmod clips the ACL mask and locks the app out of that file until it is chmod'd back. Per-user disk quotas are not possible in this model.

Docker

The image installs the acl package and runs as UID/GID 1000, so the bind mount matches a normal host user and the app can manage ACLs on it. Everything the app writes (SQLite db, files, thumbnails, trash) lives under data/, which must be owned by UID 1000.

  • docker build -t files-web-ui .
  • docker run -d --name files-web -p 3000:3000 -v ./data:/app/data -e COOKIE_SECURE=1 -e ACL_UID_MAP="alice:60101,bob:60102" files-web-ui
  • or with Compose: cp docker-compose.example.yml docker-compose.yml, adjust it, then docker compose up -d --build
  • create users with docker compose exec files-web deno task user:add <name> (interactive password prompt)
  • config comes from environment variables (docker run -e / compose environment:); they take precedence over a mounted .env. Behind a reverse proxy set COOKIE_SECURE=1

To do

  • sharing files ability
  • copy and move files
  • button to show/hide hidden files
  • close button for success messages
  • have view mode (list/grid) on a per folder basis
  • line break mode switch in text editor
  • in grid mode display name size etc. for sorting
  • collaborative editing [later / separate project]

NOTE ON AI USE

While I do use AI to write code I decide on the architecture, design of the software, design of the website, how it works and what it should do. I also decide which dependencies and tools to use.