diff --git a/API_USAGE.md b/API_USAGE.md new file mode 100644 index 000000000..5d3d30ac9 --- /dev/null +++ b/API_USAGE.md @@ -0,0 +1,145 @@ +# urBackend API Usage Guide 📖 + +This guide provides technical examples for interacting with the urBackend API. Once your project is created in the dashboard, use your **Public API Key** (for frontend) or **Secret API Key** (for backend) to make requests. + +## Base URL + +``` +https://api.urbackend.bitbros.in +``` + +## 1. Authentication + +Manage users for your own applications using urBackend's built-in auth system. + +### Sign Up User +```javascript +await fetch('https://api.urbackend.bitbros.in/api/userAuth/signup', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'YOUR_API_KEY' + }, + body: JSON.stringify({ + email: "user@example.com", + password: "securePassword123", + name: "John Doe" // Optional + }) +}); +``` + +### Login User +```javascript +const res = await fetch('https://api.urbackend.bitbros.in/api/userAuth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'YOUR_API_KEY' + }, + body: JSON.stringify({ + email: "user@example.com", + password: "securePassword123" + }) +}); +const data = await res.json(); // Returns { token: "JWT_TOKEN", user: {...} } +``` + +### Get Profile (Me) +```javascript +await fetch('https://api.urbackend.bitbros.in/api/userAuth/me', { + method: 'GET', + headers: { + 'x-api-key': 'YOUR_API_KEY', + 'Authorization': 'Bearer ' // From login response + } +}); +``` + +## 2. Database API + +Direct JSON document storage with no database management required. + +### Get All Items +```javascript +// Replace :collectionName with your actual collection name (e.g., 'products') +const res = await fetch('https://api.urbackend.bitbros.in/api/data/products', { + headers: { 'x-api-key': 'YOUR_API_KEY' } +}); +const data = await res.json(); +``` + +### Insert Data +```javascript +await fetch('https://api.urbackend.bitbros.in/api/data/products', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'YOUR_API_KEY' + }, + body: JSON.stringify({ + name: "MacBook Pro", + price: 1299, + inStock: true + }) +}); +``` + +### Update / Delete by ID +```javascript +const id = "DOCUMENT_ID"; + +// Update +await fetch(`https://api.urbackend.bitbros.in/api/data/products/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'YOUR_API_KEY' + }, + body: JSON.stringify({ price: 1199 }) +}); + +// Delete +await fetch(`https://api.urbackend.bitbros.in/api/data/products/${id}`, { + method: 'DELETE', + headers: { 'x-api-key': 'YOUR_API_KEY' } +}); +``` + +## 3. Storage API + +Manage files and images with ease. + +### Upload File +```javascript +const formData = new FormData(); +formData.append('file', fileInput.files[0]); + +const res = await fetch('https://api.urbackend.bitbros.in/api/storage/upload', { + method: 'POST', + headers: { 'x-api-key': 'YOUR_API_KEY' }, + body: formData +}); +const data = await res.json(); +// Returns { url: "...", path: "project_id/filename.jpg" } +``` + +### Delete File +```javascript +await fetch('https://api.urbackend.bitbros.in/api/storage/file', { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'YOUR_API_KEY' + }, + body: JSON.stringify({ + path: "PROJECT_ID/filename.jpg" + }) +}); +``` + +## 🔐 Security Best Practices + +> [!IMPORTANT] +> **Key Separation**: +> - Use the **Publishable Key** (`pk_live_...`) for frontend requests (Read-Only). +> - Use the **Secret Key** (`sk_live_...`) ONLY for backend/secure environments. diff --git a/README.md b/README.md index bbfb45a83..de2ccb293 100644 --- a/README.md +++ b/README.md @@ -1,216 +1,79 @@ +

+ urBackend Banner +

+

+ The Instant "Backend-as-a-Service" for Frontend Developers.
+ Get a managed NoSQL database, JWT Auth, and Cloud Storage in 60 seconds. +

+ +

+ Dashboard · + Docs · + Discord +

+ +
+ ![Build Status](https://img.shields.io/github/actions/workflow/status/yash-pouranik/urbackend/ci.yml?branch=main) ![License](https://img.shields.io/github/license/yash-pouranik/urbackend) ![Issues](https://img.shields.io/github/issues/yash-pouranik/urbackend) ![Stars](https://img.shields.io/github/stars/yash-pouranik/urbackend) -[DISCORD](https://discord.gg/CXJjvJkNWn) - -# urBackend 🚀 - -urBackend is an instant **"Backend-as-a-Service" (BaaS)** platform designed for frontend developers. It empowers you to create projects, define database schemas, manage authentication, and handle file storage without writing a single line of backend code. - -Stop writing boilerplate. Get an instant Database, Authentication, and Storage API for your next big idea. - -## ✨ Features - -- **⚡ Instant NoSQL Database**: Create collections and push JSON data instantly. No server setup required. -- **🛡️ Authentication**: Built-in User Management (Sign Up, Login, Profile) secured with JWT. -- **📂 Cloud Storage**: Upload, manage, and delete files/images with public CDN links. -- **📊 Real-time Analytics**: Monitor API usage, traffic, and storage limits via the dashboard. -- **🛠️ Visual Schema Builder**: Define table columns (String, Number, Boolean, Date) through an intuitive UI. -- **🔒 Security**: API Key-based access control and Row Level Security. - -> [!IMPORTANT] -> **Security Warning**: Your `x-api-key` grants **Admin Access** (Read/Write/Delete). -> -> - ❌ **NEVER** use this key in client-side code (frontend). -> - ✅ **ONLY** use this key in secure server-side environments. - -## 🛠️ Tech Stack - -### Frontend -- **React.js (Vite)** -- React Router DOM -- Axios -- Lucide React (Icons) -- Recharts (Analytics) - -### Backend -- **Node.js & Express** -- MongoDB (Mongoose) -- JWT (JSON Web Tokens) -- Multer (File Handling) -- Supabase (Cloud Storage) - -## 📖 API Usage Guide - -Once your project is created in the dashboard, use your **Public API Key** to make requests. - -### Base URL - -``` -https://api.urbackend.bitbros.in -``` -### 1. Authentication +
-**Sign Up User:** +--- -```javascript -await fetch('https://api.urbackend.bitbros.in/api/userAuth/signup', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': 'YOUR_API_KEY' - }, - body: JSON.stringify({ - email: "user@example.com", - password: "securePassword123", - name: "John Doe" // Optional - }) -}); -``` +urBackend is an **Open-Source BaaS** built to eliminate the complexity of backend management. It provides everything you need to power your next big idea—accessible via a unified REST API. -**Login User:** +## 🟢 Powerful Features -```javascript -const res = await fetch('https://api.urbackend.bitbros.in/api/userAuth/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': 'YOUR_API_KEY' - }, - body: JSON.stringify({ - email: "user@example.com", - password: "securePassword123" - }) -}); -const data = await res.json(); // Returns { token: "JWT_TOKEN", user: {...} } -``` +- `>_` **Instant NoSQL Database**: Create collections and push JSON data instantly. +- `>_` **Managed Authentication**: Sign Up, Login, and Profile management with JWT. +- `>_` **Cloud Storage**: Managed file/image uploads with public CDN links. +- `>_` **Bring Your Own Database**: Connect your own MongoDB or Supabase instance. +- `>_` **Real-time Analytics**: Monitor traffic and resource usage from a premium dashboard. +- `>_` **Secure Architecture**: Dual-key separation (`pk_live` & `sk_live`) for total safety. -**Get Profile (Me):** +--- -```javascript -await fetch('https://api.urbackend.bitbros.in/api/userAuth/me', { - method: 'GET', - headers: { - 'x-api-key': 'YOUR_API_KEY', - 'Authorization': 'Bearer ' // From login response - } -}); -``` +## 🚀 Experience the Pulse -### 2. Database API +Go from zero to a live backend in **under 60 seconds**. -**Get All Items:** +1. **Initialize**: Create a project on the [Dashboard](https://urbackend.bitbros.in). +2. **Model**: Visually define your collections and schemas. +3. **Execute**: Push and pull data immediately using your Instant API Key. ```javascript -// Replace :collectionName with your actual collection name (e.g., 'products') +// Power your UI with zero backend boilerplate const res = await fetch('https://api.urbackend.bitbros.in/api/data/products', { - headers: { 'x-api-key': 'YOUR_API_KEY' } -}); -const data = await res.json(); -``` - -**Insert Data:** - -```javascript -await fetch('https://api.urbackend.bitbros.in/api/data/products', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': 'YOUR_API_KEY' - }, - body: JSON.stringify({ - name: "MacBook Pro", - price: 1299, - inStock: true - }) -}); -``` - -**Get / Update / Delete by ID:** - -```javascript -const id = "DOCUMENT_ID"; // The '_id' from the document - -// Get One -await fetch(`https://api.urbackend.bitbros.in/api/data/products/${id}`, { - headers: { 'x-api-key': 'YOUR_API_KEY' } -}); - -// Update -await fetch(`https://api.urbackend.bitbros.in/api/data/products/${id}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': 'YOUR_API_KEY' - }, - body: JSON.stringify({ price: 1199 }) -}); - -// Delete -await fetch(`https://api.urbackend.bitbros.in/api/data/products/${id}`, { - method: 'DELETE', - headers: { 'x-api-key': 'YOUR_API_KEY' } + headers: { 'x-api-key': 'your_pk_live_key' } }); ``` -### 3. Storage API - -**Upload File:** - -```javascript -const formData = new FormData(); -formData.append('file', fileInput.files[0]); - -const res = await fetch('https://api.urbackend.bitbros.in/api/storage/upload', { - method: 'POST', - headers: { 'x-api-key': 'YOUR_API_KEY' }, - body: formData -}); -const data = await res.json(); -// Returns { url: "...", path: "project_id/filename.jpg" } -``` - -**Delete File:** - -```javascript -await fetch('https://api.urbackend.bitbros.in/api/storage/file', { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': 'YOUR_API_KEY' - }, - body: JSON.stringify({ - path: "PROJECT_ID/filename.jpg" // The 'path' from upload response - }) -}); -``` +--- -## ⚠️ Limits & Quotas +## 🛠️ Infrastructure -- **Rate Limit**: 100 requests / 15 mins per IP. -- **Database Size**: Max 50 MB per project. -- **File Storage**: Max 100 MB per project. -- **File Upload Size**: Max 5 MB per file. +
-## 🤝 Contributing +| **Core System** | **Developer UI** | **Data Layer** | +| :--- | :--- | :--- | +| Node.js & Express | React.js (Vite) | MongoDB (Mongoose) | +| JWT Authentication | Lucide React | Redis & BullMQ | +| Storage Manager | Recharts | Supabase (BYOD) | -We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to get started, development workflow, and our code of conduct. +
--- -## 🗺️ Roadmap & Upcoming Features +## 🤝 Community -UrBackend is just getting started. Here’s what’s on the horizon: -- [ ] **MongoDB**: Apis for more complex queries on dashboard + for urbackend. -- [ ] **Webhooks**: Notify your frontend on database changes. -- [ ] **PostgreSQL Support**: Expand beyond MongoDB (Very tough but we will build together with the community ❤️). -- [ ] **Plug-and-Play UI Components**: React components for login/signup. -- [ ] **Advanced Logs**: Detailed API request tracing. -- [ ] **Role-Based Access Control (RBAC)**: Fine-grained permissions. +- [GitHub Issues](https://github.com/yash-pouranik/urbackend/issues): Report bugs & request features. +- [Discord Channel](https://discord.gg/CXJjvJkNWn): Join the conversation. +- [Contributing](CONTRIBUTING.md): Help us grow the ecosystem. -Want to see a feature? [Open an issue!](https://github.com/yash-pouranik/urbackend/issues) +--- ## Contributors @@ -218,6 +81,4 @@ Want to see a feature? [Open an issue!](https://github.com/yash-pouranik/urbacke -Made with [contrib.rocks](https://contrib.rocks). - -Built with ❤️ for **urbackend** +Built with ❤️ by the **urBackend** community. diff --git a/backend/controllers/release.controller.js b/backend/controllers/release.controller.js index 04d111423..9c94f6e76 100644 --- a/backend/controllers/release.controller.js +++ b/backend/controllers/release.controller.js @@ -4,7 +4,7 @@ const { emailQueue } = require("../queues/emailQueue"); const ADMIN_EMAIL = process.env.ADMIN_EMAIL; -// GET ALL RELEASES +// GET FOR - ALL RELEASES exports.getAllReleases = async (req, res) => { try { const releases = await Release.find().sort({ createdAt: -1 }); @@ -15,12 +15,11 @@ exports.getAllReleases = async (req, res) => { } }; -// CREATE RELEASE (Admin Only) +// POST FOR - CREATE RELEASE exports.createRelease = async (req, res) => { try { const { version, title, content } = req.body; - // Verify Admin if (req.user.email !== ADMIN_EMAIL) { return res.status(403).json({ error: "Access denied. Admin only." }); } @@ -36,12 +35,10 @@ exports.createRelease = async (req, res) => { publishedBy: req.user.email }); await newRelease.save(); - - // 1. Fetch all verified developers - const developers = await Developer.find({ isVerified: true }, 'email'); - const emails = developers.map(dev => dev.email); - - // 2. Queue emails + const developers = await Developer.find({ isVerified: true }) + .select("email") + .lean(); + const emails = developers.map(({ email }) => email); await Promise.all(emails.map(email => emailQueue.add('release-email', { email, diff --git a/backend/middleware/verifyApiKey.js b/backend/middleware/verifyApiKey.js index 6553f442b..8ef6ee1e7 100644 --- a/backend/middleware/verifyApiKey.js +++ b/backend/middleware/verifyApiKey.js @@ -65,13 +65,22 @@ module.exports = async (req, res, next) => { } try { - const originUrl = new URL(origin).origin; + const parsedOrigin = new URL(origin); + const originUrl = parsedOrigin.origin; + const originHostname = parsedOrigin.hostname; + const isAllowed = allowedDomains.some(domain => { - if (domain.startsWith('*.')) { - const baseDomain = domain.substring(2); - return originUrl === baseDomain || originUrl.endsWith('.' + baseDomain); + let cleanDomain = domain.trim(); + if (cleanDomain.endsWith('/')) { + cleanDomain = cleanDomain.slice(0, -1); + } + + if (cleanDomain.startsWith('*.')) { + const baseDomain = cleanDomain.substring(2); + return originHostname === baseDomain || originHostname.endsWith('.' + baseDomain); } - return originUrl === domain; + + return originUrl === cleanDomain || originHostname === cleanDomain; }); if (!isAllowed) { diff --git a/backend/routes/releases.js b/backend/routes/releases.js index 080cd2650..60594df7b 100644 --- a/backend/routes/releases.js +++ b/backend/routes/releases.js @@ -9,14 +9,14 @@ const getAllReleasesLimiter = RateLimit({ max: 1000, }); -// GET ALL RELEASES (Public) +// GET FOR - ALL RELEASES (Public) router.get('/', getAllReleasesLimiter, getAllReleases); const createReleaseLimiter = RateLimit({ windowMs: 15 * 60 * 1000, max: 5 , }); -// CREATE RELEASE (Admin Only) -router.post('/', createReleaseLimiter, createRelease); +// POST FOR - CREATE RELEASE +router.post('/', createReleaseLimiter, authorization, createRelease); module.exports = router; diff --git a/banner.png b/banner.png new file mode 100644 index 000000000..c31b1fd34 Binary files /dev/null and b/banner.png differ diff --git a/frontend/public/logo.png b/frontend/public/logo.png new file mode 100644 index 000000000..5dec8394f Binary files /dev/null and b/frontend/public/logo.png differ diff --git a/frontend/src/pages/ProjectSettings.jsx b/frontend/src/pages/ProjectSettings.jsx index abebda62e..a01f79400 100644 --- a/frontend/src/pages/ProjectSettings.jsx +++ b/frontend/src/pages/ProjectSettings.jsx @@ -924,15 +924,28 @@ function AllowedDomainsForm({ project, projectId, token, onProjectUpdate }) { }; const addDomain = () => { - const domain = newDomain.trim(); + let domain = newDomain.trim(); if (!domain) return; - // basic validation + // basic cleanup + if (domain !== "*" && domain.endsWith("/")) { + domain = domain.slice(0, -1); + } + if (domains.includes(domain)) { return toast.error("Domain already added"); } - const updated = [...domains, domain]; + let updated; + if (domain === "*") { + // If user adds '*', it overrides everything else + updated = ["*"]; + } else { + // If user adds a specific domain, ensure '*' is removed + updated = domains.filter((d) => d !== "*"); + updated.push(domain); + } + handleUpdate(updated); setNewDomain(""); }; diff --git a/logo.png b/logo.png new file mode 100644 index 000000000..5c3c643ee Binary files /dev/null and b/logo.png differ