π― LinkVault provides a lightweight, secure pastebin alternative for developers to store and share text and code snippets instantly. It is made using ReactJS, Tailwind CSS For Frontend, Node JS, Express JS as Backend, Typescript for type safety, Appwrite For Storing Files, JWT for Authentication, MongoDB as our Database, Mongoose as ORM, bun as Package Manager.
LinkVault/
β
βββ README.md # Project overview, setup, usage
βββ .gitignore # Files to ignore in git
β
βββ backend/ # Backend Code
β βββ db/ # MongoDB Connection
β βββ controller/ # Router Controller
β βββ router/ # Router Details
β βββ schema/ # Database Schema
β βββ jobs/ # Background Jobs (cleanup)
β βββ lib/ # Shared Server-side Clients (Appwrite)
β βββ type.ts # Type Declarations
β βββ .env.local # All Backend environment Variable
β βββ README.md # Backend overview
β βββ .gitignore # Files to ignore in git
β βββ index.ts # Root Backend File
β
βββ src/ # Source code
β βββ components/ # All Components
β βββ lib/ # All Necessary Functions
β βββ index.css # Root CSS Functions
β βββ main.tsx # Root Frontend File
β
βββ data.ts # All Constant Data
β
βββ .env.local # All Frontend environment Variable
β
βββ types.ts # All TypesFirst, Clone The Repo The Repository
git clone https://github.com/psykat1116/LinkVault.gitIt is recommended to use bun as it is lot more faster than npm. you can still use npm if you don't want to install bun
curl -fsSL https://bun.sh/install | bash- Start Backend Server
cd LinkVault cd backend bun i // or use npm i bun run dev // or use npm run dev
- Run Frontend Server
cd .. bun i // or use npm i bun run dev // or use npm run dev
- For Backend create a .env.local file in
backendfolder with the given environment variable
JWT_SECRET =
DATABASE_URL =
APPWRITE_ENDPOINT =
APPWRITE_PROJECT_ID =
APPWRITE_API_KEY =- For Frontend create a .env.local file the root folder with the given environment variable
VITE_APPWRITE_API_KEY =
VITE_APPWRITE_BUCKET_ID =
VITE_APPWRITE_PROJECT_ID =
VITE_APPWRITE_PROJECT_ENDPOINT = Log In to the MongoDB and create a free cluster. Then from connect -> compass option you can get the url. but don't forgot the name of your database at end.
DATABASE_URL = mongodb+srv://<user>:<password>@<cluster-url>/<dbname>- Login Into The Appwrite Create a new project. From there you will get two enviromental variable.
VITE_APPWRITE_PROJECT_ID =
VITE_APPWRITE_PROJECT_ENDPOINT = - Create a new API Key and select the
Storagein theScopesection. After creation you will get another environmental variable.
VITE_APPWRITE_API_KEY = - Finally create a new
bucketin theStoragesection and you will get the final environmental variable.
VITE_APPWRITE_BUCKET_ID = - Then go to the newly created bucket settings and in the
Permissions Sectionand add newAnyrole and check all operations(Create, Update, Read, Delete). Optionally you can change theMaximum File Sizeas 10MB for futher safety.
- React + Vite for fast local development and a lightweight frontend build pipeline.
- Bun runtime and package manager to keep install and dev cycles fast.
- Express + Mongoose for a simple, familiar REST API and MongoDB modeling.
- Appwrite Storage for file blobs, with the backend storing only file metadata.
- JWT-based auth with client-side storage to keep the API stateless.
- MongoDB TTL index (
expires: 0) onPaste.expiresAtfor automatic cleanup of expired text pastes β safe because paste documents carry no Appwrite blob. - The
Fileschema intentionally has no MongoDB TTL index. Letting MongoDB auto-delete File documents would bypass the Appwrite deletion call and leave orphaned blobs. The background cleanup job is the sole owner of the File lifecycle. - Server-side Appwrite client (
node-appwrite) so the backend can delete blobs directly on paste expiry, download-limit hit, or manual delete β the frontend is no longer responsible for cleanup. - Background cleanup job runs every 60 seconds to purge expired File records: it calls
storage.deleteFilefirst, then removes the MongoDB document. If Appwrite is unreachable the MongoDB record is preserved so the next cycle can retry; a 404 response means the blob is already gone and the record is cleaned up immediately.
- File storage permissions are configured in Appwrite to allow the required operations.
- Authentication tokens are stored in
localStorage, which is convenient but not as secure as HttpOnly cookies. - The backend API is configured for
http://localhost:5173CORS by default. - Expiration and view/download limits are enforced server-side on read. The backend attempts to delete the Appwrite blob first; if that succeeds (or returns 404), the File document is removed from MongoDB. If Appwrite is unreachable, the File document is kept for the cleanup job to retry β the paste is still deleted and a 410 is returned to the caller.
- The server-side Appwrite API key (
APPWRITE_API_KEY) must have Storage delete scope β without it the cleanup job and all server-side file deletions will fail to remove blobs. - The cleanup job polls every 60 seconds; files can linger in Appwrite for up to 60 seconds after their
expiresAttimestamp. - Existing deployments: if the server was ever run before this change, a TTL index named
expiresAt_1may already exist on thefilescollection in MongoDB Atlas. Drop it manually to prevent MongoDB from bypassing the cleanup job:db.files.dropIndex("expiresAt_1").