Table of Contents
- Introduction
- The Problem
- Solution Overview
- Architecture Overview
- Core Features Deep Dive
- Security Design
- Multi‑Tenant Architecture
- Performance & Operations
- Development Workflow
- Deployment & Infrastructure
- Limitations & Roadmap
- Conclusion
Introduction
FileTank is a multi‑tenant file sharing platform I built to give organisations fine‑grained control over how large files are uploaded, managed, previewed, and shared, without handing everything to a third‑party cloud drive. It’s designed for teams that need auditable workflows, expiring public links, role‑based permissions, and predictable operational control.
The Problem
Teams that trade large assets (agencies, media houses, legal and finance, engineering) often outgrow consumer file sharing. They need:
- Organisation-scoped access and role‑based permissions
- Resumable uploads for very large files and deep folder trees
- Expiring public links with guest capture and auditability
- Lightweight previews so reviewers can skim quickly
- Operational guardrails such as clean‑ups and monitoring
Solution Overview
FileTank provides:
- Resumable, chunked uploads that reconstruct files on the server without loading a complete file into application memory
- Public links with expiry and optional guest details, plus detailed audit logs
- Workspace/folder hierarchy with permission inheritance and overrides
- Image, PDF, and video previews
- Organisation‑scoped access with JWT‑backed sessions
- Scheduled clean‑ups
Architecture Overview
Frontend
- Framework: Next.js (App Router), Tailwind CSS
- State: React Context for global state, focused components for upload and previews
- Upload UI: chunked/resumable uploads with progress
Backend
- Runtime: Node.js with Express
- Data: MariaDB via Sequelize; local filesystem for
uploads/andpreviews/ - Scheduling:
node-cronfor clean‑ups - Auth: JWT in httpOnly cookies; optional LDAP; organisation context carried in the token
- APIs: REST endpoints; WebSocket feed for monitoring
// MariaDB connection via Sequelize
import { Sequelize } from 'sequelize';
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, {
host: process.env.DB_HOST,
dialect: 'mariadb',
logging: false,
define: { underscored: true },
});
export default sequelize;// JWT authentication with organisation context
export async function authenticateToken(req, res, next) {
try {
const token = req.cookies.token;
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = { id: decoded.id, email: decoded.email, isSuperuser: decoded.isSuperuser };
if (decoded.organizationId) req.organization = { id: decoded.organizationId };
next();
} catch (err) {
return res.status(401).json({ error: 'Unauthorised' });
}
}Core Features Deep Dive
1. Resumable Uploads
Uploads are received as small chunks, written to a temporary area on disk, then finalised into the target location with streams rather than loading the complete file into application memory. Deep folder paths are created on the fly from the client’s relative path.
2. Sharing & Public Links
Create expiring download links for single files or multiple selections. Organisations may require guest details before download. Selected file, folder, and public-link events are logged, including link creation and downloads.
// Create download link
export async function createDownloadLink(req, res) {
const { items, expiresInDays } = req.body;
const expiresAt = new Date(Date.now() + expiresInDays * 86400_000);
const link = await DownloadLink.create({ items, expiresAt, organizationId: req.organization.id, createdBy: req.user.id });
return res.status(201).json({ linkId: link.linkId, expiresAt });
}// Download from link (single file fast‑path or streamed ZIP)
export async function downloadFromLink(req, res) {
const link = await DownloadLink.findOne({ where: { linkId: req.params.id } });
if (!link || new Date() > link.expiresAt) return res.status(410).json({ error: 'Link expired' });
const { files, folders } = await resolveItems(link.items);
if (files.length === 1 && folders.length === 0) return streamFile(files[0], res);
return streamZip(files, folders, res);
}3. Permissions & Inheritance
Effective access is resolved by combining explicit file/folder permissions, inheritance up the folder tree, workspace membership, and organisation admin status. Explicit permissions restrict access; absence implies full access for admins.
4. Workspaces & Folders
Top‑level folders act as workspaces. Nested folders are created automatically from upload paths. Permission inheritance applies on creation for predictable defaults.
5. Previews Pipeline
Thumbnails are generated for images, first‑page previews for PDFs, and representative frames for videos. Previews are stored under previews/ and referenced in the UI.
// Image preview via sharp
const buffer = await sharp(filePath)
.resize(400, 400, { fit: 'inside', withoutEnlargement: true })
.jpeg({ quality: 85 })
.toBuffer();
await fs.promises.writeFile(path.join(PREVIEWS_DIR, `thumbnail_${fileId}.jpg`), buffer);6. Search
Name‑based search scoped to the user’s accessible workspaces and filtered through effective permissions. (Roadmap includes content indexing and tags.)
Security Design
- Authentication & Authorisation: JWT cookies, organisation context, role checks, and effective permissions on covered objects
- Transport Security: HTTPS in production
- Audit Trails: Selected file, folder, public-link, and download events record actor and event metadata
- Upload Controls: Configured chunk and file size limits; roadmap includes antivirus and deeper content‑type detection
Multi‑Tenant Architecture
- Tenant:
Organisationentity; users join via membership records - Context:
organizationIdis carried in the JWT and checked by organisation-scoped middleware - Isolation: Single schema with app‑level enforcement; file paths are nested per organisation
- RBAC: Organisation admin role plus per‑workspace/folder/file permissions with inheritance
// Attach organisation context after verifying token and membership
req.organization = { id: org.id, name: org.name, role: membership.role };Performance & Operations
- Large Files: Chunked uploads and streamed merging (memory‑light)
- Operations: Scheduled clean‑ups for expired links and inactive workspaces
Development Workflow
- Database: Sequelize models; sync‑based migrations for speed during early development
- Testing: Unit and integration coverage for upload, link, and preview flows
- DX: Seed scripts and local
.envfor quick start
Deployment & Infrastructure
- Process Management: PM2 in production
- TLS: Node HTTPS server with provided certificates
- Static Serving:
/uploadsserved directly; previews resolved via API
Conclusion
FileTank brings role‑aware file management to organisations that want operational control without heavy vendor lock‑in. The current architecture provides large uploads and controlled sharing, with object storage, antivirus, and richer search left as possible future work.