A personal note gallery with file uploads, folders, tags, and password protection. Backend powered by Supabase (free tier). No Firebase needed.
- What You Need (Prerequisites)
- Download & Extract the Project
- Create a Free Supabase Account
- Create a Supabase Project
- Run the Database Migrations (SQL)
- Deploy the Edge Functions
- Get Your Supabase API Keys
- Edit the .env File
- Install & Run the App
- Set Your Admin Password
- Log In & Use the App
- Troubleshooting
- How the App Works (Quick Reference)
Before starting, make sure you have these installed on your computer:
| Tool | Purpose | Download |
|---|---|---|
| Node.js (v18 or newer) | Runs the app | https://nodejs.org → click "LTS" |
| npm | Installs packages (comes with Node.js) | Included with Node.js |
| Supabase CLI | Deploys backend functions | See Step 6 below |
| A web browser | Chrome, Firefox, Edge | Already have it |
| A text editor | Edit config files | VS Code: https://code.visualstudio.com |
Check Node.js is installed — open your Terminal and run:
node --version
You should see something like v20.0.0. If you see an error, install Node.js first.
Download the
ratulnotehub-clean.zipfile (the one Claude gave you).Open your Downloads folder.
Right-click on
ratulnotehub-clean.zip→ Extract Here (or Extract All on Windows).You will now have a folder called
notehub-clean-srcinside a folder calledratulnotehub-clean.Open your Terminal (or Command Prompt on Windows) and navigate into the project:
cd~/Downloads/ratulnotehub-clean/notehub-clean-src💡 Tip: On Windows, use
cd %USERPROFILE%\Downloads\ratulnotehub-clean\notehub-clean-src
- Confirm you are in the right place:
lsYou should see files like: index.html, package.json, vite.config.ts, src/, supabase/
Supabase is a free backend service that stores your files, notes, and password.
Go to https://supabase.com
Click "Start your project" (green button, top right).
Sign up with GitHub (easiest) or your email.
Verify your email if asked.
You are now on the Supabase Dashboard.
On the Supabase Dashboard, click "New project".
Fill in the form:
- Organization: Your name (auto-created)
- Project name:
notehub(or anything you like) - Database Password: Create a strong password and save it somewhere — you'll need it later
- Region: Pick the one closest to you (e.g.,
Southeast Asiaif you're in Bangladesh)
Click "Create new project".
Wait about 1-2 minutes while Supabase sets up your database. You'll see a loading spinner.
Once done, you'll see your project dashboard. Keep this tab open — you'll need it.
This step creates all the database tables the app needs (notes, folders, tags, password storage, file storage).
In your Supabase project dashboard, look at the left sidebar.
Click "SQL Editor" (the icon looks like
</>).Click "New query" (top left of the SQL editor).
You need to run 3 SQL files from your project. Open the folder:
notehub-clean-src/supabase/migrations/You will see 3
.sqlfiles. Run them in order (oldest timestamp first):
-- Create auth_user table for single admin userCREATETABLEpublic.auth_user (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
username TEXTNOT NULL UNIQUE,
password_hash TEXTNOT NULL
);
ALTERTABLEpublic.auth_user ENABLE ROW LEVEL SECURITY;
-- Create login_attempts table for rate limitingCREATETABLEpublic.login_attempts (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
ip_address TEXTNOT NULL,
attempt_count INTEGERNOT NULL DEFAULT 0,
last_attempt_time TIMESTAMP WITH TIME ZONENOT NULL DEFAULT now(),
blocked_until TIMESTAMP WITH TIME ZONE
);
CREATEUNIQUE INDEXidx_login_attempts_ipONpublic.login_attempts (ip_address);
ALTERTABLEpublic.login_attempts ENABLE ROW LEVEL SECURITY;
-- Create sessions tableCREATETABLEpublic.sessions (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULLREFERENCESpublic.auth_user(id) ON DELETE CASCADE,
token TEXTNOT NULL UNIQUE,
expires_at TIMESTAMP WITH TIME ZONENOT NULL,
last_activity TIMESTAMP WITH TIME ZONENOT NULL DEFAULT now(),
created_at TIMESTAMP WITH TIME ZONENOT NULL DEFAULT now()
);
ALTERTABLEpublic.sessions ENABLE ROW LEVEL SECURITY;After pasting, click the green "Run" button (or press
Ctrl+Enter).You should see "Success. No rows returned" at the bottom. That means it worked.
-- Folders tableCREATETABLEpublic.folders (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXTNOT NULL,
parent_id UUID REFERENCESpublic.folders(id) ON DELETE CASCADE,
created_at TIMESTAMP WITH TIME ZONENOT NULL DEFAULT now()
);
ALTERTABLEpublic.folders ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all access to folders"ONpublic.folders FOR ALL USING (true) WITH CHECK (true);
-- Notes tableCREATETABLEpublic.notes (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
title TEXTNOT NULL,
file_url TEXT,
file_type TEXT,
file_size BIGINT DEFAULT 0,
folder_id UUID REFERENCESpublic.folders(id) ON DELETESETNULL,
order_index INTEGERNOT NULL DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONENOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONENOT NULL DEFAULT now()
);
ALTERTABLEpublic.notes ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all access to notes"ONpublic.notes FOR ALL USING (true) WITH CHECK (true);
CREATEINDEXidx_notes_folderONpublic.notes(folder_id);
CREATEINDEXidx_notes_orderONpublic.notes(order_index);
-- Tags tableCREATETABLEpublic.tags (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXTNOT NULL UNIQUE,
created_at TIMESTAMP WITH TIME ZONENOT NULL DEFAULT now()
);
ALTERTABLEpublic.tags ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all access to tags"ONpublic.tags FOR ALL USING (true) WITH CHECK (true);
-- Note-Tags junctionCREATETABLEpublic.note_tags (
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
note_id UUID NOT NULLREFERENCESpublic.notes(id) ON DELETE CASCADE,
tag_id UUID NOT NULLREFERENCESpublic.tags(id) ON DELETE CASCADE,
UNIQUE(note_id, tag_id)
);
ALTERTABLEpublic.note_tags ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all access to note_tags"ONpublic.note_tags FOR ALL USING (true) WITH CHECK (true);
-- Auto-update updated_at on notesCREATE OR REPLACEFUNCTIONpublic.update_updated_at_column()
RETURNS TRIGGER AS $$
BEGINNEW.updated_at= now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql SET search_path = public;
CREATETRIGGERupdate_notes_updated_at
BEFORE UPDATEONpublic.notes
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at_column();
-- Storage bucket for filesINSERT INTOstorage.buckets (id, name, public) VALUES ('note-files', 'note-files', true);
CREATE POLICY "Anyone can read note files"ONstorage.objects FOR SELECT USING (bucket_id ='note-files');
CREATE POLICY "Anyone can upload note files"ONstorage.objects FOR INSERT WITH CHECK (bucket_id ='note-files');
CREATE POLICY "Anyone can update note files"ONstorage.objects FOR UPDATE USING (bucket_id ='note-files');
CREATE POLICY "Anyone can delete note files"ONstorage.objects FOR DELETE USING (bucket_id ='note-files');Click Run. You should see "Success".
-- Add favorites columnALTERTABLEpublic.notes ADD COLUMN is_favorited booleanNOT NULL DEFAULT false;
-- Version history tableCREATETABLEpublic.note_versions (
id uuid NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
note_id uuid NOT NULLREFERENCESpublic.notes(id) ON DELETE CASCADE,
content text,
file_url text,
version_number integerNOT NULL DEFAULT 1,
created_at timestamp with time zoneNOT NULL DEFAULT now()
);
ALTERTABLEpublic.note_versions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow all access to note_versions"ONpublic.note_versions FOR ALL TO public
USING (true) WITH CHECK (true);
CREATEINDEXidx_note_versions_note_idONpublic.note_versions(note_id);Click Run. You should see "Success".
✅ All 3 migrations done! Your database tables are now created.
Edge Functions are the backend logic that handles login, logout, and password management. You need to deploy them from your terminal using the Supabase CLI.
On Linux/macOS:
npm install -g supabaseOn Windows (PowerShell as Administrator):
npm install -g supabaseVerify it installed:
supabase --versionYou should see a version number like 1.x.x.
supabase loginThis will open your browser. Log in with your Supabase account. Then come back to the terminal — it should say "Logged in".
- Go to your Supabase project dashboard in the browser.
- Click "Project Settings" (gear icon, bottom of left sidebar).
- Click "General".
- Find "Reference ID" — it looks like:
srmpdcpvfecpeomsgqlq - Copy it.
In your terminal, make sure you are inside the project folder:
cd~/Downloads/ratulnotehub-clean/notehub-clean-srcThen run (replace YOUR_PROJECT_REF with your actual Reference ID):
supabase link --project-ref YOUR_PROJECT_REFIt will ask for your database password — enter the password you set in Step 4.
Now deploy all 5 Edge Functions with one command:
supabase functions deploy login
supabase functions deploy logout
supabase functions deploy validate-session
supabase functions deploy change-password
supabase functions deploy setup-adminEach one should say "Done: Deployed Function".
✅ Edge Functions are now live on Supabase!
In your Supabase project dashboard, click "Project Settings" (gear icon).
Click "API" in the settings menu.
You will see two important things — copy both:
Project URL — looks like:
https://srmpdcpvfecpeomsgqlq.supabase.coanon / public key — a long string starting with
eyJ...eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
⚠️ Do NOT share theservice_rolekey with anyone. Only copy theanonkey.
The .env file tells the app where your Supabase database is and how to connect to it.
Open the project folder
notehub-clean-srcin your text editor (VS Code).Find the file named
.envin the root of the folder.💡 On Linux/macOS, files starting with
.are hidden by default. In your file manager, pressCtrl+Hto show hidden files. In VS Code it shows automatically.Open
.env. It looks like this:VITE_SUPABASE_PROJECT_ID="srmpdcpvfecpeomsgqlq" VITE_SUPABASE_PUBLISHABLE_KEY="eyJhbGci..." VITE_SUPABASE_URL="https://srmpdcpvfecpeomsgqlq.supabase.co"Replace all three values with YOUR values from Step 7:
VITE_SUPABASE_PROJECT_ID="YOUR_REFERENCE_ID_HERE" VITE_SUPABASE_PUBLISHABLE_KEY="YOUR_ANON_KEY_HERE" VITE_SUPABASE_URL="YOUR_PROJECT_URL_HERE"Example (with real-looking fake values):
VITE_SUPABASE_PROJECT_ID="abcdefghijklmnop" VITE_SUPABASE_PUBLISHABLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.abc123" VITE_SUPABASE_URL="https://abcdefghijklmnop.supabase.co"Save the file (
Ctrl+S).
Open your terminal and go to the project folder:
cd~/Downloads/ratulnotehub-clean/notehub-clean-src
Install all dependencies (only needed once):
npm install
This will take 1-2 minutes. You will see a progress bar.
Start the development server:
npm run dev
You will see output like:
VITE v5.x.x ready in 500 ms ➜ Local: http://localhost:8080/Open your browser and go to: http://localhost:8080
You should see the Note Hub login page! 🎉
The app needs a password to protect your notes. You need to set it once using the setup-admin Edge Function.
There are two ways to do this:
- Go to your Supabase project dashboard.
- Click "Edge Functions" in the left sidebar.
- Click on "setup-admin".
- Click the "Test" button (or use the "Invoke" section).
- In the request body, enter:
Replace
{ "password": "your_password_here" }your_password_herewith your actual password (minimum 6 characters). - Click "Send".
- You should see:
{"message": "Admin user created"}
curl -X POST https://YOUR_PROJECT_REF.supabase.co/functions/v1/setup-admin \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-d '{"password": "your_password_here"}'Replace:
YOUR_PROJECT_REFwith your project Reference IDYOUR_ANON_KEYwith your anon/public keyyour_password_herewith your chosen password
You should see: {"message":"Admin user created"}
✅ Your password is now saved securely in the database (hashed — not stored as plain text).
- Go to http://localhost:8080 in your browser.
- You will be redirected to the login page.
- Enter the password you set in Step 10.
- Click "Continue".
- You are now in your Note Hub dashboard!
| Feature | How to use |
|---|---|
| Upload files | Click "New" → "Upload file" or drag & drop files onto the screen |
| Create text notes | Click "New" → "New text file" |
| Create folders | Click "New folder" in the left sidebar |
| Preview notes | Click on any note card |
| Edit text files | Click ⋮ menu on a note → "Edit" |
| View CSV files | Click a .csv note — opens as a table |
| View Word docs | Click a .docx note — renders the text |
| Search notes | Type in the search bar at the top |
| Add tags | Click ⋮ on a note → "Add tag" |
| Favorite a note | Click ⋮ → "Favorite" (pins it to top) |
| Move to folder | Click ⋮ → "Move to…" |
| Drag & reorder | Click and drag note cards to reorder them |
| Bulk select | Click "Select" button → check multiple notes → Delete or Move |
| Change password | Bottom of left sidebar → "Change password" |
- 📄 Text:
.txt,.md - 📊 Spreadsheet:
.csv - 📑 Word:
.docx - 📕 PDF:
.pdf - 🖼️ Images:
.png,.jpg,.jpeg,.gif,.webp
You are in the wrong folder. Make sure you are inside notehub-clean-src:
cd~/Downloads/ratulnotehub-clean/notehub-clean-src
npm install- Open browser DevTools (
F12) → Console tab - If you see "Failed to fetch" or "Invalid API key" — your
.envfile has wrong values. Re-check Step 8. - Make sure the
.envfile is saved. - Stop the server (
Ctrl+C) and restart:npm run dev
- Make sure you completed Step 10 (set admin password).
- The password is case-sensitive.
- If you forgot your password, call
setup-adminagain with a new password — it will update it.
Wait 15 minutes. This is a security feature — 5 wrong attempts triggers a lockout.
To reset immediately, go to Supabase → SQL Editor → run:
DELETEFROMpublic.login_attempts;- Check your Supabase Storage bucket exists: Supabase dashboard → "Storage" → you should see
note-filesbucket. - If the bucket is missing, go to SQL Editor and run Migration 2 again (the storage part).
Make sure you are logged in:
supabase login
supabase link --project-ref YOUR_PROJECT_REF
supabase functions deploy setup-adminSessions last 30 minutes by default. To change this, edit supabase/functions/login/index.ts:
constSESSION_DURATION_MINUTES=30;// Change this numberThen re-deploy: supabase functions deploy login
Architecture overview:
Your Browser (React app)
│
├── Supabase Database (stores notes metadata, folders, tags, password hash)
├── Supabase Storage (stores actual files: PDFs, images, text files)
└── Supabase Edge Functions (handles login, logout, password change)
Security model:
- Your password is hashed (SHA-256) before saving — Supabase never stores plain text passwords
- Login creates a session token valid for 30 minutes
- After 5 wrong password attempts, login is locked for 15 minutes (brute-force protection)
- All file storage is tied to your Supabase project — only you have access
Free tier limits (Supabase):
- Database: 500 MB
- Storage: 1 GB
- Edge Function calls: 500,000/month
- All plenty for personal use
Your Note Hub is running locally. Every file you upload goes to Supabase Storage, and all your note data is saved in Supabase Database — both are free and persist even after you close your computer.
To stop the app: press Ctrl+C in the terminal.
To start it again later:
cd~/Downloads/ratulnotehub-clean/notehub-clean-src
npm run devThen open http://localhost:8080.
Built with React, TypeScript, Vite, Tailwind CSS, shadcn/ui, and Supabase.