Building FileTank: A Multi‑Tenant File Sharing Platform

Today

FileTank Dashboard

Table of Contents

  1. Introduction
  2. The Problem
  3. Solution Overview
  4. Architecture Overview
  5. Core Features Deep Dive
  6. Security & Compliance
  7. Multi‑Tenant Architecture
  8. Performance & Operations
  9. Development Workflow
  10. Deployment & Infrastructure
  11. Limitations & Roadmap
  12. 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:

Solution Overview

FileTank provides:

Architecture Overview

Frontend

Backend

// 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 using streaming to avoid memory spikes. Deep folder paths are created on the fly from the client’s relative path.

// Multer storage for chunked uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => cb(null, TEMP_DIR),
  filename: (req, file, cb) => {
    const { resumableIdentifier, resumableChunkNumber } = req.body;
    cb(null, `${resumableIdentifier}.${resumableChunkNumber}.${Date.now()}`);
  },
});
 
const upload = multer({
  storage,
  limits: { fileSize: 6 * 1024 * 1024, files: 1, parts: 25 },
});
// Finalise: merge chunks into the final file with streams
router.post('/finalize', authenticateToken, express.json({ limit: '1mb' }), async (req, res) => {
  const { uuid, filename, relativePath, totalChunks, fileSize, folderId } = req.body;
  res.status(200).json({ success: true, message: 'Finalisation started' });
 
  setImmediate(async () => {
    const finalPath = ensureFoldersAndResolvePath(relativePath, filename);
    const writeStream = fs.createWriteStream(finalPath);
    for (let i = 1; i <= totalChunks; i++) {
      const chunkPath = path.join(TEMP_DIR, `${uuid}.${i}.chunk`);
      await new Promise((resolve, reject) => {
        fs.createReadStream(chunkPath).pipe(writeStream, { end: false })
          .on('finish', () => fs.unlink(chunkPath, resolve))
          .on('error', reject);
      });
    }
    writeStream.end();
    await recordFile({ folderId, filename, finalPath, fileSize });
  });
});

Create expiring download links for single files or multiple selections. Organisations may require guest details before download. All actions are audited.

// 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);

Name‑based search scoped to the user’s accessible workspaces and filtered through effective permissions. (Roadmap includes content indexing and tags.)

Security & Compliance

Multi‑Tenant Architecture

// Attach organisation context after verifying token and membership
req.organization = { id: org.id, name: org.name, role: membership.role };

Performance & Operations

// Nightly database backup at 03:00
cron.schedule('0 3 * * *', () => backupDatabase());

Development Workflow

Deployment & Infrastructure

Conclusion

FileTank brings auditable, role‑aware file management to organisations that want operational control without heavy vendor lock‑in. The current architecture has proven robust for large uploads, secure sharing, and day‑to‑day collaboration, with a clear path towards object storage, antivirus, and richer search.