Skip to content

psykat1116/LinkVault

Repository files navigation

🎯 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.

i) Folder Structure

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 Types

ii) Getting Started

First, Clone The Repo The Repository

git clone https://github.com/psykat1116/LinkVault.git

iii) Install bun

It 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

iv) Start The Server

  • 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

v) Setup The .env.local File

  • For Backend create a .env.local file in backend folder 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 = 

vi) Setup MongoDB

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>

vii) Setup Appwrite Storage

  • 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 Storage in the Scope section. After creation you will get another environmental variable.
  VITE_APPWRITE_API_KEY = 
  • Finally create a new bucket in the Storage section and you will get the final environmental variable.
  VITE_APPWRITE_BUCKET_ID = 
  • Then go to the newly created bucket settings and in the Permissions Section and add new Any role and check all operations(Create, Update, Read, Delete). Optionally you can change the Maximum File Size as 10MB for futher safety.

Design Decisions

  1. React + Vite for fast local development and a lightweight frontend build pipeline.
  2. Bun runtime and package manager to keep install and dev cycles fast.
  3. Express + Mongoose for a simple, familiar REST API and MongoDB modeling.
  4. Appwrite Storage for file blobs, with the backend storing only file metadata.
  5. JWT-based auth with client-side storage to keep the API stateless.
  6. MongoDB TTL index (expires: 0) on Paste.expiresAt for automatic cleanup of expired text pastes β€” safe because paste documents carry no Appwrite blob.
  7. The File schema 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.
  8. 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.
  9. Background cleanup job runs every 60 seconds to purge expired File records: it calls storage.deleteFile first, 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.

Assumptions and Limitations

  1. File storage permissions are configured in Appwrite to allow the required operations.
  2. Authentication tokens are stored in localStorage, which is convenient but not as secure as HttpOnly cookies.
  3. The backend API is configured for http://localhost:5173 CORS by default.
  4. 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.
  5. 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.
  6. The cleanup job polls every 60 seconds; files can linger in Appwrite for up to 60 seconds after their expiresAt timestamp.
  7. Existing deployments: if the server was ever run before this change, a TTL index named expiresAt_1 may already exist on the files collection in MongoDB Atlas. Drop it manually to prevent MongoDB from bypassing the cleanup job: db.files.dropIndex("expiresAt_1").

About

A lightweight, secure pastebin alternative for developers to store and share text and code snippets instantly.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages