The challenge
Sharing a snippet or a file with a teammate usually means a chat app that mangles the formatting.
The approach
Built a self-hostable Next.js 15 service with two sharing scopes: anonymous LAN sharing and per-account cloud sharing behind JWT auth. Metadata lives in MongoDB with TTL indexes for automatic expiry; file bytes go to a local volume or any S3-compatible bucket (S3, R2, MinIO), with Prism.js highlighting, drag-and-drop uploads and a shared clipboard, packaged with Docker Compose.
The outcome
One-link sharing of highlighted code, files up to 1 GB and clipboard text, expiring after seven days, runnable on a laptop for the office network or deployed to the cloud.
How CodeShare is put together
A self-hostable Next.js service with two sharing scopes: anonymous LAN sharing for everyone on the network, and per-account cloud sharing behind JWT auth. Metadata lives in MongoDB with TTL indexes; bytes go to a local volume or any S3-compatible bucket.
- default expiry
- 7 days
- per-file upload limit
- 1 GB
- sharing scopes: LAN and cloud
- 2
01 · Clients
client
LAN devices
client
Signed-in users
cloud scope
worker
Cleanup sidecar
every 6 hours
02 · Next.js app
service
Share UI
Prism.js · react-dropzone
service
API routes
files, snippets, search, clipboard
service
JWT auth
bcrypt · httpOnly cookie
03 · Storage
database
MongoDB
TTL-indexed metadata
object store
Uploads volume
default
object store
S3-compatible bucket
S3, R2, MinIO
8 connections
- LAN devices→Share UIHTTP
- Signed-in users→Share UIHTTP
- Share UI→API routes
- API routes→JWT authcloud scope
- API routes→MongoDBmetadata
- API routes→Uploads volumebytes
- API routes→S3-compatible bucketif configured · optional
- Cleanup sidecar→API routesPOST /api/cleanup · async
Request flow · Sharing a file
- 1A file is dropped in the browser and posted to /api/upload with its scope; cloud scope requires a signed-in user.
- 2Bytes go to the S3-compatible bucket when one is configured, otherwise to the local uploads volume.
- 3A metadata document with an expiresAt date is saved and a share link returned.
- 4MongoDB’s TTL index drops expired records and the cleanup sidecar deletes the matching bytes from disk or the bucket.
Design decisions
- Expiry owned by the database
- TTL indexes delete expired metadata without an application scheduler; a sidecar only reclaims the bytes.
- Storage chosen by configuration
- The same build runs on a laptop with a local folder or in the cloud against S3, R2 or MinIO.
- One-command deploy
- Docker Compose brings up a standalone Next.js image, MongoDB with a health check, and the cleanup sidecar.
Trade-offs & known limits
- LAN scope is deliberately anonymous: anyone on the network can read local shares.
Have something similar in mind?
I'm open for freelance and contract work — let's talk about what you're building.