From 7f96d1ba44e5b0f2ffdeb363c3dacc375e35aee6 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Wed, 22 Jul 2026 12:17:52 +0530 Subject: [PATCH 01/14] feat(devex): add project templates, Python SDK demo, quickstart scripts, and Vercel deploy config - Add Templates page (/templates) to web dashboard with 6 project templates (React SDK Demo, Kanban Board, Social Media Clone, Python SDK Demo, TypeScript Quickstart, Python Quickstart) - Add sidebar navigation link for Templates - Create Python SDK demo project with full auth, CRUD, storage, mail demos and unit tests (6/6 passing with mocks) - Add interactive quickstart scripts for TypeScript and Python SDK scaffolding - Configure Vercel deployment for all frontend examples - Add CI workflow for example projects - Add comprehensive DevEx documentation (examples/DEVEX.md) --- .github/workflows/examples.yml | 74 ++++ apps/web-dashboard/src/App.jsx | 9 + .../src/components/Layout/Sidebar.jsx | 6 +- apps/web-dashboard/src/pages/Templates.jsx | 392 ++++++++++++++++++ examples/DEVEX.md | 172 ++++++++ examples/python-sdk-demo/.env.example | 4 + examples/python-sdk-demo/README.md | 63 +++ examples/python-sdk-demo/main.py | 267 ++++++++++++ examples/python-sdk-demo/requirements.txt | 3 + examples/python-sdk-demo/tests/__init__.py | 0 examples/python-sdk-demo/tests/test_demo.py | 126 ++++++ .../quickstart-scripts/setup-python-sdk.sh | 182 ++++++++ examples/quickstart-scripts/setup-ts-sdk.sh | 246 +++++++++++ examples/vercel.json | 22 + 14 files changed, 1565 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/examples.yml create mode 100644 apps/web-dashboard/src/pages/Templates.jsx create mode 100644 examples/DEVEX.md create mode 100644 examples/python-sdk-demo/.env.example create mode 100644 examples/python-sdk-demo/README.md create mode 100644 examples/python-sdk-demo/main.py create mode 100644 examples/python-sdk-demo/requirements.txt create mode 100644 examples/python-sdk-demo/tests/__init__.py create mode 100644 examples/python-sdk-demo/tests/test_demo.py create mode 100644 examples/quickstart-scripts/setup-python-sdk.sh create mode 100644 examples/quickstart-scripts/setup-ts-sdk.sh create mode 100644 examples/vercel.json diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 000000000..162f8f03e --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,74 @@ +name: Examples CI + +on: + push: + branches: [ main ] + paths: + - 'examples/**' + - 'sdks/**' + pull_request: + branches: [ main ] + paths: + - 'examples/**' + - 'sdks/**' + +jobs: + react-sdk-demo: + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/react-sdk-demo + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: examples/react-sdk-demo/package-lock.json + - run: npm install + - run: npm run build + + sdk-kanban-client: + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/sdk-kanban/client + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: examples/sdk-kanban/client/package-lock.json + - run: npm install + - run: npm run build + + social-demo-client: + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/social-demo/client + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: examples/social-demo/client/package-lock.json + - run: npm install + - run: npm run build + + python-sdk-demo: + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/python-sdk-demo + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - run: pytest tests/ -v \ No newline at end of file diff --git a/apps/web-dashboard/src/App.jsx b/apps/web-dashboard/src/App.jsx index 56a73d1c9..622782014 100644 --- a/apps/web-dashboard/src/App.jsx +++ b/apps/web-dashboard/src/App.jsx @@ -12,6 +12,7 @@ import Dashboard from './pages/Dashboard'; import ProjectDetails from './pages/ProjectDetails'; import CreateProject from './pages/CreateProject'; import CreateCollection from './pages/CreateCollection'; +import Templates from './pages/Templates'; import NotFound from './pages/NotFound'; import Analytics from './pages/Analytics'; import Releases from './pages/Releases'; @@ -81,6 +82,14 @@ function AppContent() { } /> + + + + + + } /> + diff --git a/apps/web-dashboard/src/components/Layout/Sidebar.jsx b/apps/web-dashboard/src/components/Layout/Sidebar.jsx index a6ce7c2ed..325edfd12 100644 --- a/apps/web-dashboard/src/components/Layout/Sidebar.jsx +++ b/apps/web-dashboard/src/components/Layout/Sidebar.jsx @@ -2,7 +2,8 @@ import { Link, useLocation, useParams } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; import { LayoutDashboard, Database, Shield, HardDrive, Settings, BarChart2, - ArrowLeft, LogOut, X, Rocket, Webhook, Users, Mail, ChevronLeft, ChevronRight + ArrowLeft, LogOut, X, Rocket, Webhook, Users, Mail, ChevronLeft, ChevronRight, + LayoutTemplate } from 'lucide-react'; import ThemeToggle from '../ThemeToggle'; @@ -81,6 +82,9 @@ function Sidebar({ logo, isOpen, onClose, isCollapsed, onToggleCollapse }) { Dashboard + + Templates + What's New diff --git a/apps/web-dashboard/src/pages/Templates.jsx b/apps/web-dashboard/src/pages/Templates.jsx new file mode 100644 index 000000000..326cfbc85 --- /dev/null +++ b/apps/web-dashboard/src/pages/Templates.jsx @@ -0,0 +1,392 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Copy, ExternalLink, CheckCircle, ArrowLeft, Github, Code, Rocket, FileJson, Server, Smartphone, LayoutGrid } from 'lucide-react'; +import toast from 'react-hot-toast'; + +const TEMPLATES = [ + { + id: 'react-sdk-demo', + name: 'React SDK Demo', + description: 'A modern React app showcasing @urbackend/react authentication components (UrAuth, ProtectedRoute, useUser) with full login/signup flow.', + icon: , + color: '#3B82F6', + bgColor: 'rgba(59, 130, 246, 0.1)', + sdk: '@urbackend/react', + language: 'TypeScript', + features: ['Social Auth', 'Protected Routes', 'User Profile'], + deployUrl: 'https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Freact-sdk-demo&env=VITE_URBACKEND_PK&project-name=urbackend-react-demo&repository-name=urbackend-react-demo', + repoUrl: 'https://github.com/geturbackend/urBackend/tree/main/examples/react-sdk-demo', + readme: `# React SDK Demo + +A minimal React app demonstrating \`@urbackend/react\` components. + +## Setup +1. Copy \`VITE_URBACKEND_PK\` from your project's API keys +2. Deploy with the button above or run locally: + \`\`\`bash + cd examples/react-sdk-demo + npm install + npm run dev + \`\`\` +` + }, + { + id: 'sdk-kanban', + name: 'Kanban Board', + description: 'Full-featured Kanban board with drag-and-drop tasks, team collaboration, and email notifications via the urBackend Mail module.', + icon: , + color: '#8B5CF6', + bgColor: 'rgba(139, 92, 246, 0.1)', + sdk: '@urbackend/sdk', + language: 'JavaScript', + features: ['CRUD Operations', 'Drag & Drop', 'Email Notifications'], + deployUrl: 'https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Fsdk-kanban%2Fclient&env=VITE_URBACKEND_PK&project-name=urbackend-kanban&repository-name=urbackend-kanban', + repoUrl: 'https://github.com/geturbackend/urBackend/tree/main/examples/sdk-kanban', + readme: `# Kanban Board + +A Kanban board app using \`@urbackend/sdk\` for data and auth. + +## Setup +1. Enable Auth in your urBackend project +2. Create \`boards\` and \`tasks\` collections with RLS +3. Copy \`VITE_URBACKEND_PK\` from your API keys +4. Deploy or run locally. +` + }, + { + id: 'social-demo', + name: 'Social Media (X Clone)', + description: 'A Twitter/X.com clone with infinite scroll, multi-image uploads, social graphs (follow/unfollow), likes, comments, and dark mode.', + icon: , + color: '#1DA1F2', + bgColor: 'rgba(29, 161, 242, 0.1)', + sdk: '@urbackend/sdk', + language: 'JavaScript', + features: ['Social Auth', 'Image Uploads', 'Infinite Scroll'], + deployUrl: 'https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Fsocial-demo%2Fclient&env=VITE_PUBLIC_KEY&project-name=urbackend-social-demo&repository-name=urbackend-social-demo', + repoUrl: 'https://github.com/geturbackend/urBackend/tree/main/examples/social-demo', + readme: `# Social Media Clone + +A full Twitter/X.com clone built on urBackend. + +## Setup +1. Enable Auth with GitHub/Google social login +2. Create required collections: \`posts\`, \`profiles\`, \`comments\`, \`likes\`, \`follows\` +3. Configure RLS for each collection +4. Deploy or run locally. +` + }, + { + id: 'python-sdk-demo', + name: 'Python SDK Demo', + description: 'A complete Python CLI demo showcasing all urbackend Python SDK capabilities: auth, database CRUD, file storage, and email.', + icon: , + color: '#22C55E', + bgColor: 'rgba(34, 197, 94, 0.1)', + sdk: 'urbackend (Python)', + language: 'Python', + features: ['Auth', 'Database CRUD', 'Storage', 'Mail'], + deployUrl: null, + repoUrl: 'https://github.com/geturbackend/urBackend/tree/main/examples/python-sdk-demo', + readme: `# Python SDK Demo + +A CLI-based demo of the urbackend Python SDK. + +## Setup +\`\`\`bash +cd examples/python-sdk-demo +pip install -r requirements.txt +cp .env.example .env +# Edit .env with your API keys +python main.py +\`\`\` +` + }, + { + id: 'quickstart-ts', + name: 'TypeScript Quickstart', + description: 'Scaffold a new TypeScript project with @urbackend/sdk in seconds using our CLI quickstart script.', + icon: , + color: '#F59E0B', + bgColor: 'rgba(245, 158, 11, 0.1)', + sdk: '@urbackend/sdk', + language: 'TypeScript', + features: ['CLI Scaffold', 'TypeScript', 'Vercel Ready'], + deployUrl: 'https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Fquickstart-ts&env=URBACKEND_PUBLISHABLE_KEY,URBACKEND_SECRET_KEY,URBACKEND_PROJECT_ID&project-name=urbackend-ts-quickstart&repository-name=urbackend-ts-quickstart', + repoUrl: 'https://github.com/geturbackend/urBackend/tree/main/examples/quickstart-scripts', + readme: `# TypeScript Quickstart + +\`\`\`bash +bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh) +\`\`\` + +Scaffolds a complete TypeScript project with the urBackend SDK pre-configured. +` + }, + { + id: 'quickstart-python', + name: 'Python Quickstart', + description: 'Scaffold a new Python project with the urbackend SDK using our quickstart script.', + icon: , + color: '#EC4899', + bgColor: 'rgba(236, 72, 153, 0.1)', + sdk: 'urbackend (Python)', + language: 'Python', + features: ['CLI Scaffold', 'Python 3.9+', 'Vercel Ready'], + deployUrl: null, + repoUrl: 'https://github.com/geturbackend/urBackend/tree/main/examples/quickstart-scripts', + readme: `# Python Quickstart + +\`\`\`bash +bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-python-sdk.sh) +\`\`\` + +Scaffolds a complete Python project with the urbackend SDK pre-configured. +` + } +]; + +function Templates() { + const navigate = useNavigate(); + const [expandedId, setExpandedId] = useState(null); + const [selectedSdk, setSelectedSdk] = useState('all'); + const [searchQuery, setSearchQuery] = useState(''); + + const sdks = ['all', ...new Set(TEMPLATES.map(t => t.sdk))]; + + const filteredTemplates = TEMPLATES.filter(t => { + const matchesSdk = selectedSdk === 'all' || t.sdk === selectedSdk; + const matchesSearch = !searchQuery || + t.name.toLowerCase().includes(searchQuery.toLowerCase()) || + t.description.toLowerCase().includes(searchQuery.toLowerCase()) || + t.sdk.toLowerCase().includes(searchQuery.toLowerCase()); + return matchesSdk && matchesSearch; + }); + + const copyQuickstart = (template) => { + const cmd = template.language === 'Python' + ? `bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-python-sdk.sh)` + : `bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh)`; + navigator.clipboard.writeText(cmd); + toast.success('Quickstart command copied!'); + }; + + return ( +
+ + +
+

+ Project Templates +

+

+ Jumpstart your development with ready-to-use templates. Each template demonstrates different urBackend SDK features. +

+
+ + {/* Filters */} +
+ setSearchQuery(e.target.value)} + className="input-field" + style={{ flex: 1, minWidth: '200px', padding: '10px 14px', borderRadius: '8px', border: '1px solid var(--color-border)', background: 'var(--color-bg-input)', color: 'var(--color-text-main)' }} + /> +
+ {sdks.map(sdk => ( + + ))} +
+
+ + {/* Template Grid */} +
+ {filteredTemplates.map(template => ( +
setExpandedId(expandedId === template.id ? null : template.id)} + > + {/* Header */} +
+
+ {template.icon} +
+
+

+ {template.name} +

+

+ {template.description} +

+
+
+ + {/* Tags */} +
+ + {template.sdk} + + + {template.language} + + {template.features.map(f => ( + + {f} + + ))} +
+ + {/* Action Buttons */} +
+ {template.deployUrl && ( + e.stopPropagation()} + > + + Deploy to Vercel + + )} + e.stopPropagation()} + > + + View Source + + {(template.id.startsWith('quickstart')) && ( + + )} +
+ + {/* Expanded README */} + {expandedId === template.id && ( +
+
+                  {template.readme}
+                
+
+ )} +
+ ))} +
+ + {filteredTemplates.length === 0 && ( +
+

No templates match your search criteria.

+
+ )} + + {/* Quickstart Section */} +
+

+ ๐Ÿš€ Quickstart from Terminal +

+

+ Scaffold a new urBackend project without leaving your terminal: +

+
+
+
TypeScript
+ + {`bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh)`} + + +
+
+
Python
+ + {`bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-python-sdk.sh)`} + + +
+
+
+
+ ); +} + +export default Templates; \ No newline at end of file diff --git a/examples/DEVEX.md b/examples/DEVEX.md new file mode 100644 index 000000000..48e870012 --- /dev/null +++ b/examples/DEVEX.md @@ -0,0 +1,172 @@ +# Developer Experience (DevEx) Guide + +This guide explains the developer experience improvements available for urBackend, including project templates, quickstart scripts, and deployment options. + +## ๐Ÿ“‹ Overview + +The DevEx improvements make it easy for developers to: +1. **Discover** ready-to-use project templates via the web dashboard +2. **Clone** templates with one-click "Deploy to Vercel" buttons +3. **Scaffold** new projects from the terminal using quickstart scripts +4. **Learn** through comprehensive SDK demo projects + +--- + +## ๐Ÿš€ Project Templates + +Access the templates page at `/templates` in your urBackend dashboard. Available templates: + +| Template | SDK | Description | Deploy | +|----------|-----|-------------|--------| +| **React SDK Demo** | `@urbackend/react` | Auth components (UrAuth, ProtectedRoute) | โœ… Vercel | +| **Kanban Board** | `@urbackend/sdk` | CRUD + drag-and-drop + email notifications | โœ… Vercel | +| **Social Media (X Clone)** | `@urbackend/sdk` | Infinite scroll, images, social graphs | โœ… Vercel | +| **Python SDK Demo** | `urbackend` (Python) | CLI demo of auth, CRUD, storage, mail | - | +| **TypeScript Quickstart** | `@urbackend/sdk` | Scaffold a TS project from CLI | โœ… Vercel | +| **Python Quickstart** | `urbackend` (Python) | Scaffold a Python project from CLI | - | + +### Features +- **Search**: Filter templates by name, description, or SDK +- **SDK Filter**: View templates by specific SDK +- **One-click Deploy**: "Deploy to Vercel" with pre-configured env vars +- **Expandable README**: Click any template to see setup instructions +- **Copy Quickstart**: One-click copy of terminal commands + +--- + +## ๐Ÿ’ป Quickstart Scripts + +Scaffold projects without leaving your terminal: + +### TypeScript +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh) +``` + +### Python +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-python-sdk.sh) +``` + +Each script: +1. Checks prerequisites (Node.js 18+ / Python 3.9+) +2. Prompts for project name and API keys +3. Creates a complete project structure +4. Installs dependencies +5. Provides next steps + +--- + +## ๐Ÿ“ฆ SDK Demo Projects + +### Python SDK Demo (`examples/python-sdk-demo/`) + +A complete CLI application demonstrating all Python SDK features: +- **Auth**: Signup, login, token management, user profile +- **Database**: Full CRUD (create, read, update, delete) +- **Storage**: File upload and download URLs +- **Mail**: Send transactional emails + +Run: `python main.py` + +### React SDK Demo (`examples/react-sdk-demo/`) + +A React app using `@urbackend/react`: +- **UrAuth**: Ready-to-use auth UI +- **ProtectedRoute**: Route protection +- **useUser / useAuth**: React hooks +- **UrUserButton**: User menu component + +Run: `npm run dev` + +### Kanban Board (`examples/sdk-kanban/`) + +Full Kanban board with drag-and-drop: +- **Client**: React using `@urbackend/sdk` for auth and data +- **Server**: Node.js using secret key for mail notifications +- **Features**: Boards, tasks, drag-and-drop, email notifications + +### Social Media Clone (`examples/social-demo/`) + +Twitter/X.com clone demonstrating complex features: +- **Social Auth**: GitHub and Google OAuth +- **Posts**: Text + multi-image uploads +- **Social Graph**: Follow/unfollow +- **Timeline**: Infinite scroll +- **Theming**: Light/Dark mode + +--- + +## ๐Ÿšข Deploy to Vercel + +All frontend examples have pre-configured "Deploy to Vercel" buttons. When clicked: + +1. You're taken to Vercel's import page +2. Environment variables are pre-configured (add your API keys) +3. The project builds and deploys automatically +4. Your urBackend-powered app is live in minutes! + +### Environment Variables + +| Template | Required Env Vars | +|----------|------------------| +| React SDK Demo | `VITE_URBACKEND_PK` | +| Kanban Board | `VITE_URBACKEND_PK` | +| Social Media | `VITE_PUBLIC_KEY` | +| TS Quickstart | `URBACKEND_PUBLISHABLE_KEY`, `URBACKEND_SECRET_KEY`, `URBACKEND_PROJECT_ID` | + +--- + +## ๐Ÿ› ๏ธ Adding a New Template + +To add a new template to the dashboard: + +1. Create the example project in `examples/your-demo/` +2. Add a Vercel deploy button in the README +3. Add the template to `apps/web-dashboard/src/pages/Templates.jsx` `TEMPLATES` array +4. Add CI job in `.github/workflows/examples.yml` + +Template schema: +```js +{ + id: 'unique-id', // Unique identifier + name: 'Template Name', // Display name + description: '...', // Short description + icon: , // Lucide icon + color: '#HEX', // Accent color + bgColor: 'rgba(...)', // Background color + sdk: '@urbackend/sdk', // SDK name + language: 'TypeScript', // Language + features: ['Feat1', ...], // Feature tags + deployUrl: '...', // Vercel deploy URL (null if not deployable) + repoUrl: '...', // Source code URL + readme: '...' // Setup instructions (markdown) +} +``` + +--- + +## ๐Ÿ“Š File Structure + +``` +examples/ +โ”œโ”€โ”€ python-sdk-demo/ # Python SDK demo +โ”‚ โ”œโ”€โ”€ main.py +โ”‚ โ”œโ”€โ”€ requirements.txt +โ”‚ โ”œโ”€โ”€ tests/ +โ”‚ โ””โ”€โ”€ README.md +โ”œโ”€โ”€ react-sdk-demo/ # React SDK demo +โ”‚ โ”œโ”€โ”€ src/ +โ”‚ โ”œโ”€โ”€ package.json +โ”‚ โ””โ”€โ”€ README.md +โ”œโ”€โ”€ sdk-kanban/ # Kanban board demo +โ”‚ โ”œโ”€โ”€ client/ +โ”‚ โ””โ”€โ”€ server/ +โ”œโ”€โ”€ social-demo/ # Social media clone +โ”‚ โ”œโ”€โ”€ client/ +โ”‚ โ””โ”€โ”€ server/ +โ”œโ”€โ”€ quickstart-scripts/ # Quickstart scripts +โ”‚ โ”œโ”€โ”€ setup-ts-sdk.sh +โ”‚ โ””โ”€โ”€ setup-python-sdk.sh +โ”œโ”€โ”€ vercel.json # Vercel deployment config +โ””โ”€โ”€ DEVEX.md # This file \ No newline at end of file diff --git a/examples/python-sdk-demo/.env.example b/examples/python-sdk-demo/.env.example new file mode 100644 index 000000000..609d4c8fe --- /dev/null +++ b/examples/python-sdk-demo/.env.example @@ -0,0 +1,4 @@ +URBACKEND_PUBLISHABLE_KEY=pk_live_your_publishable_key_here +URBACKEND_SECRET_KEY=sk_live_your_secret_key_here +URBACKEND_PROJECT_ID=your_project_id_here +URBACKEND_API_URL=https://api.ub.bitbros.in \ No newline at end of file diff --git a/examples/python-sdk-demo/README.md b/examples/python-sdk-demo/README.md new file mode 100644 index 000000000..d178ac691 --- /dev/null +++ b/examples/python-sdk-demo/README.md @@ -0,0 +1,63 @@ +# Python SDK Demo โ€” urBackend Quickstart + +A minimal Python application demonstrating the `urbackend` Python SDK for database CRUD, authentication, and storage operations. + +## ๐Ÿš€ Quick Start + +### 1. Prerequisites +- Python 3.9+ +- A urBackend account ([sign up here](https://urbackend.bitbros.in)) +- A urBackend project with API keys + +### 2. Setup + +```bash +# Clone the repo +git clone https://github.com/geturbackend/urBackend.git +cd examples/python-sdk-demo + +# Create virtual environment +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate + +# Install dependencies +pip install -r requirements.txt + +# Configure environment +cp .env.example .env +# Edit .env with your urBackend API keys +``` + +### 3. Run the Demo + +```bash +python main.py +``` + +## ๐Ÿ“ฆ What This Demo Shows + +| Feature | Code Example | +|---------|-------------| +| **Auth** | Sign up, login, get current user | +| **Database** | CRUD operations on collections | +| **Storage** | Upload and download files | +| **Mail** | Send transactional emails (server-side) | + +## ๐Ÿ”‘ Environment Variables + +| Variable | Description | +|----------|-------------| +| `URBACKEND_PUBLISHABLE_KEY` | Publishable API key (pk_live_...) | +| `URBACKEND_SECRET_KEY` | Secret API key (sk_live_...) | +| `URBACKEND_PROJECT_ID` | Your project ID | +| `URBACKEND_API_URL` | API base URL (default: https://api.ub.bitbros.in) | + +## ๐Ÿงช Run Tests + +```bash +pytest tests/ +``` + +## ๐Ÿšข Deploy to Vercel + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Fpython-sdk-demo&env=URBACKEND_PUBLISHABLE_KEY,URBACKEND_SECRET_KEY,URBACKEND_PROJECT_ID&project-name=urbackend-python-demo&repository-name=urbackend-python-demo) \ No newline at end of file diff --git a/examples/python-sdk-demo/main.py b/examples/python-sdk-demo/main.py new file mode 100644 index 000000000..dcf13efb5 --- /dev/null +++ b/examples/python-sdk-demo/main.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +""" +urBackend Python SDK Demo +========================= +Demonstrates the core features of the urbackend Python SDK: + - Authentication (signup, login, session management) + - Database CRUD operations + - File storage (upload/download) + - Email sending (server-side, secret key required) + +Prerequisites: + - Python 3.9+ + - pip install urbackend python-dotenv + - A urBackend project with API keys configured in .env + +Usage: + python main.py +""" + +import os +import sys +from pathlib import Path + +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +# โ”€โ”€ Configuration โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +PUBLISHABLE_KEY = os.getenv("URBACKEND_PUBLISHABLE_KEY", "") +SECRET_KEY = os.getenv("URBACKEND_SECRET_KEY", "") +PROJECT_ID = os.getenv("URBACKEND_PROJECT_ID", "") +API_URL = os.getenv("URBACKEND_API_URL", "https://api.ub.bitbros.in") + + +def print_separator(title: str) -> None: + """Print a section header for cleaner output.""" + print(f"\n{'=' * 60}") + print(f" {title}") + print(f"{'=' * 60}") + + +def demo_auth(client) -> dict | None: + """ + Demonstrate authentication features. + + Shows signup, login, token refresh, and fetching the current user. + Returns the authenticated user object if successful. + """ + print_separator("๐Ÿ” AUTHENTICATION DEMO") + + # For demo purposes, we use a random test user + import random + import string + suffix = ''.join(random.choices(string.ascii_lowercase, k=6)) + test_email = f"demo-user-{suffix}@example.com" + test_password = "DemoPassword123!" + test_username = f"demouser_{suffix}" + + print(f" Signing up user: {test_email}") + try: + signup_result = client.auth.signup( + email=test_email, + password=test_password, + username=test_username, + ) + print(f" โœ… Signup successful! User ID: {signup_result.get('_id', 'N/A')}") + except Exception as e: + print(f" โ„น๏ธ Signup note: {e} (user may already exist, trying login...)") + + print(f"\n Logging in as: {test_email}") + try: + session = client.auth.login(email=test_email, password=test_password) + token = session.get("token", client.auth.get_token()) + print(f" โœ… Login successful! Token: {token[:20]}...") + except Exception as e: + print(f" โŒ Login failed: {e}") + return None + + print("\n Fetching current user profile...") + try: + user = client.auth.me() + print(f" โœ… Current user: {user.get('email', 'N/A')} (ID: {user.get('_id', 'N/A')})") + return user + except Exception as e: + print(f" โŒ Failed to fetch user: {e}") + return None + + +def demo_database(client, project_id: str, token: str) -> None: + """ + Demonstrate database CRUD operations. + + Shows creating, reading, updating, and deleting documents in a collection. + Requires a 'posts' collection with fields: title (String), content (String). + """ + print_separator("๐Ÿ—„๏ธ DATABASE CRUD DEMO") + collection = "posts" + + # โ”€โ”€ Create a document โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + print(f" Inserting a document into '{collection}'...") + try: + post = client.db.insert(collection, { + "title": "Hello from Python SDK!", + "content": "This post was created by the urbackend Python SDK demo.", + }, token=token) + post_id = post.get("_id", "N/A") + print(f" โœ… Document created! ID: {post_id}") + except Exception as e: + print(f" โŒ Insert failed: {e}") + print(" ๐Ÿ’ก Make sure you've created a 'posts' collection in your urBackend dashboard.") + return + + # โ”€โ”€ Read all documents โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + print(f"\n Fetching all documents from '{collection}'...") + try: + posts = client.db.get_all(collection, token=token) + print(f" โœ… Found {len(posts)} document(s)") + except Exception as e: + print(f" โŒ Fetch failed: {e}") + + # โ”€โ”€ Read a single document โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + print(f"\n Fetching single document by ID...") + try: + fetched = client.db.get_one(collection, post_id, token=token) + print(f" โœ… Retrieved: {fetched.get('title', 'N/A')}") + except Exception as e: + print(f" โŒ Fetch failed: {e}") + + # โ”€โ”€ Update a document โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + print(f"\n Updating document title...") + try: + updated = client.db.patch(collection, post_id, { + "title": "Updated: Python SDK Demo Post", + }, token=token) + print(f" โœ… Updated! New title: {updated.get('title', 'N/A')}") + except Exception as e: + print(f" โŒ Update failed: {e}") + + # โ”€โ”€ Delete a document โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + print(f"\n Deleting document...") + try: + result = client.db.delete(collection, post_id, token=token) + if result.get("deleted"): + print(f" โœ… Document deleted successfully!") + except Exception as e: + print(f" โŒ Delete failed: {e}") + + +def demo_storage(client, project_id: str, token: str) -> None: + """ + Demonstrate file storage operations. + + Shows uploading a file and generating a presigned URL for download. + """ + print_separator("๐Ÿ“ฆ STORAGE DEMO") + + # Create a temporary file to upload + test_file_path = Path("demo-upload.txt") + try: + test_file_path.write_text( + "Hello from urBackend Python SDK!\n" + "This file was uploaded using the Python SDK storage module.\n" + ) + + print(f" Uploading '{test_file_path.name}'...") + with open(test_file_path, "rb") as f: + result = client.storage.upload( + f, + filename=test_file_path.name, + token=token, + ) + print(f" โœ… Upload successful! Path: {result.get('path', 'N/A')}") + + print(f"\n Getting download URL...") + try: + url = client.storage.get_url(test_file_path.name, token=token) + print(f" โœ… Download URL: {url[:60]}...") + except Exception as e: + print(f" โ„น๏ธ get_url not available in this SDK version: {e}") + + except Exception as e: + print(f" โŒ Storage demo failed: {e}") + finally: + if test_file_path.exists(): + test_file_path.unlink() + print(f" ๐Ÿงน Cleaned up test file") + + +def demo_mail(client) -> None: + """ + Demonstrate sending transactional emails. + + Requires a Secret Key and a configured email template in urBackend. + """ + print_separator("๐Ÿ“ง MAIL DEMO") + + print(" Sending test email (requires configured email template)...") + try: + result = client.mail.send( + to="user@example.com", + template_name="welcome", + variables={ + "name": "Demo User", + "projectName": "Python SDK Demo", + "appUrl": "https://urbackend.bitbros.in", + }, + ) + print(f" โœ… Email sent! ID: {result.get('id', 'N/A')}") + except Exception as e: + print(f" โ„น๏ธ Mail demo skipped (configure email template in dashboard): {e}") + + +def main() -> None: + """Main demo entry point.""" + try: + from urbackend import UrBackendClient, UrBackendError, AuthError, NotFoundError + except ImportError: + print("โŒ urbackend SDK not installed. Run: pip install urbackend") + sys.exit(1) + + if not PUBLISHABLE_KEY or not SECRET_KEY: + print("โŒ Error: URBACKEND_PUBLISHABLE_KEY and URBACKEND_SECRET_KEY must be set in .env") + print("๐Ÿ’ก Copy .env.example to .env and fill in your API keys") + sys.exit(1) + + print("โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—") + print("โ•‘ urBackend Python SDK Demo โ•‘") + print("โ•‘ Showcasing all core SDK capabilities โ•‘") + print("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•") + + # Initialize the SDK client with the publishable key + print(f"\n๐Ÿ”ง Initializing SDK client... (API URL: {API_URL})") + client = UrBackendClient( + api_key=PUBLISHABLE_KEY, + base_url=API_URL, + ) + + # โ”€โ”€ Authentication Demo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + user = demo_auth(client) + if not user: + print("\nโš ๏ธ Skipping remaining demos (authentication required).") + return + + token = client.auth.get_token() + if not token: + print("โš ๏ธ No auth token available. Skipping database and storage demos.") + return + + # โ”€โ”€ Database CRUD Demo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + demo_database(client, PROJECT_ID, token) + + # โ”€โ”€ Storage Demo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + demo_storage(client, PROJECT_ID, token) + + # โ”€โ”€ Mail Demo (secret key only) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + print("\n๐Ÿ“ง Switching to Secret Key for mail operations...") + secret_client = UrBackendClient(api_key=SECRET_KEY, base_url=API_URL) + demo_mail(secret_client) + + print(f"\n{'=' * 60}") + print(" ๐ŸŽ‰ Demo complete! Check your urBackend dashboard for results.") + print(f"{'=' * 60}\n") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/python-sdk-demo/requirements.txt b/examples/python-sdk-demo/requirements.txt new file mode 100644 index 000000000..8a1b16652 --- /dev/null +++ b/examples/python-sdk-demo/requirements.txt @@ -0,0 +1,3 @@ +urbackend>=0.1.0 +python-dotenv>=1.0.0 +pytest>=7.0.0 diff --git a/examples/python-sdk-demo/tests/__init__.py b/examples/python-sdk-demo/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/python-sdk-demo/tests/test_demo.py b/examples/python-sdk-demo/tests/test_demo.py new file mode 100644 index 000000000..691af9f37 --- /dev/null +++ b/examples/python-sdk-demo/tests/test_demo.py @@ -0,0 +1,126 @@ +""" +Tests for the Python SDK demo. + +These tests verify that the demo script's functions work correctly +with mock data. They do NOT require a live urBackend connection. +""" + +from unittest.mock import MagicMock, patch +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +# Mock the urbackend module before importing main +mock_urbackend = MagicMock() +mock_client = MagicMock() +mock_auth = MagicMock() +mock_db = MagicMock() +mock_storage = MagicMock() +mock_mail = MagicMock() + +mock_client.auth = mock_auth +mock_client.db = mock_db +mock_client.storage = mock_storage +mock_client.mail = mock_mail + +sys.modules["urbackend"] = mock_urbackend +mock_urbackend.UrBackendClient.return_value = mock_client + + +class TestDemoFunctions: + """Test suite for the Python SDK demo functions.""" + + def setup_method(self): + """Reset all mocks before each test.""" + mock_auth.reset_mock() + mock_db.reset_mock() + mock_storage.reset_mock() + mock_mail.reset_mock() + + def test_demo_auth_signup(self): + """Test that demo_auth handles signup correctly.""" + from main import demo_auth + + mock_auth.signup.return_value = {"_id": "user_123", "email": "test@example.com"} + mock_auth.login.return_value = {"token": "test_token_abc123"} + mock_auth.get_token.return_value = "test_token_abc123" + mock_auth.me.return_value = { + "_id": "user_123", + "email": "test@example.com", + "name": "Test User", + } + + result = demo_auth(mock_client) + assert result is not None + assert result["email"] == "test@example.com" + mock_auth.signup.assert_called_once() + mock_auth.login.assert_called_once() + mock_auth.me.assert_called_once() + + def test_demo_auth_login_fallback(self): + """Test that demo_auth falls back to login if signup fails.""" + from main import demo_auth + + mock_auth.signup.side_effect = Exception("User already exists") + mock_auth.login.return_value = {"token": "test_token_abc123"} + mock_auth.get_token.return_value = "test_token_abc123" + mock_auth.me.return_value = {"_id": "user_123", "email": "test@example.com"} + + result = demo_auth(mock_client) + assert result is not None + assert result["email"] == "test@example.com" + mock_auth.signup.assert_called_once() + mock_auth.login.assert_called_once() + + def test_demo_auth_login_failure(self): + """Test that demo_auth returns None on login failure.""" + from main import demo_auth + + mock_auth.signup.side_effect = Exception("User exists") + mock_auth.login.side_effect = Exception("Invalid credentials") + + result = demo_auth(mock_client) + assert result is None + + def test_demo_database_crud(self): + """Test full database CRUD cycle.""" + from main import demo_database + + mock_db.insert.return_value = {"_id": "post_123", "title": "Test Post"} + mock_db.get_all.return_value = [{"_id": "post_123", "title": "Test Post"}] + mock_db.get_one.return_value = {"_id": "post_123", "title": "Test Post"} + mock_db.patch.return_value = { + "_id": "post_123", + "title": "Updated: Python SDK Demo Post", + } + mock_db.delete.return_value = {"deleted": True} + + # Should not raise any exceptions + demo_database(mock_client, "project_123", "test_token") + mock_db.insert.assert_called_once() + mock_db.get_all.assert_called_once() + mock_db.get_one.assert_called_once() + mock_db.patch.assert_called_once() + mock_db.delete.assert_called_once() + + def test_demo_storage(self): + """Test storage upload and URL generation.""" + from main import demo_storage + + mock_storage.upload.return_value = {"path": "uploads/demo-upload.txt"} + mock_storage.get_url.return_value = "https://storage.example.com/file.txt" + + # Should not raise any exceptions + demo_storage(mock_client, "project_123", "test_token") + mock_storage.upload.assert_called_once() + + def test_demo_mail(self): + """Test mail sending.""" + from main import demo_mail + + mock_mail.send.return_value = {"id": "email_123"} + + # Should not raise any exceptions + demo_mail(mock_client) + mock_mail.send.assert_called_once() \ No newline at end of file diff --git a/examples/quickstart-scripts/setup-python-sdk.sh b/examples/quickstart-scripts/setup-python-sdk.sh new file mode 100644 index 000000000..4d0391e1f --- /dev/null +++ b/examples/quickstart-scripts/setup-python-sdk.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# ============================================================================= +# urBackend Python SDK Quickstart +# ============================================================================= +# This script scaffolds a new urBackend project with the Python SDK. +# +# Usage: +# bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-python-sdk.sh) +# +# Or locally: +# bash examples/quickstart-scripts/setup-python-sdk.sh +# +# Prerequisites: +# - Python 3.9+ +# - pip +# - A urBackend account (https://urbackend.bitbros.in) +# ============================================================================= + +set -euo pipefail + +# โ”€โ”€ Colors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" +echo -e "${BLUE}โ•‘ urBackend Python SDK Quickstart โ•‘${NC}" +echo -e "${BLUE}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" +echo "" + +# โ”€โ”€ Check prerequisites โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +if ! command -v python3 &> /dev/null; then + echo -e "${RED}โŒ Python 3 is not installed. Please install Python 3.9+ first.${NC}" + exit 1 +fi + +PYTHON_VERSION=$(python3 --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1) +if [ "$PYTHON_VERSION" -lt 3 ]; then + echo -e "${RED}โŒ Python 3.9+ is required. Current version: $(python3 --version)${NC}" + exit 1 +fi + +echo -e "${GREEN}โœ… $(python3 --version) detected${NC}" + +# โ”€โ”€ Get project name โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +DEFAULT_NAME="my-urbackend-app" +read -r -p "Project name [${DEFAULT_NAME}]: " PROJECT_NAME +PROJECT_NAME="${PROJECT_NAME:-$DEFAULT_NAME}" + +if [ -d "$PROJECT_NAME" ]; then + echo -e "${RED}โŒ Directory '$PROJECT_NAME' already exists.${NC}" + exit 1 +fi + +# โ”€โ”€ Get API keys โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "" +echo -e "${YELLOW}Enter your urBackend API keys (from https://urbackend.bitbros.in)${NC}" +read -r -p "Publishable Key (pk_live_...): " PUBLISHABLE_KEY +read -r -p "Secret Key (sk_live_...): " SECRET_KEY +read -r -p "Project ID: " PROJECT_ID + +if [ -z "$PUBLISHABLE_KEY" ] || [ -z "$SECRET_KEY" ] || [ -z "$PROJECT_ID" ]; then + echo -e "${RED}โŒ All API keys are required.${NC}" + exit 1 +fi + +# โ”€โ”€ Scaffold project โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "" +echo -e "${BLUE}๐Ÿ“ Creating project: ${PROJECT_NAME}...${NC}" + +mkdir -p "$PROJECT_NAME" +cd "$PROJECT_NAME" + +# Create virtual environment +echo -e "${BLUE}๐Ÿ”ง Creating virtual environment...${NC}" +python3 -m venv venv + +# Activate based on OS +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then + source venv/Scripts/activate +else + source venv/bin/activate +fi + +# Create project structure +mkdir -p src tests + +# Create requirements.txt +cat > requirements.txt << 'EOF' +urbackend>=0.1.0 +python-dotenv>=1.0.0 +pytest>=7.0.0 +EOF + +# Create .env +cat > .env << EOF +URBACKEND_PUBLISHABLE_KEY=${PUBLISHABLE_KEY} +URBACKEND_SECRET_KEY=${SECRET_KEY} +URBACKEND_PROJECT_ID=${PROJECT_ID} +URBACKEND_API_URL=https://api.ub.bitbros.in +EOF + +# Create main.py +cat > main.py << 'PYEOF' +#!/usr/bin/env python3 +""" +urBackend Quickstart App +""" +import os +from dotenv import load_dotenv +from urbackend import UrBackendClient + +load_dotenv() + +PUBLISHABLE_KEY = os.getenv("URBACKEND_PUBLISHABLE_KEY", "") +SECRET_KEY = os.getenv("URBACKEND_SECRET_KEY", "") +API_URL = os.getenv("URBACKEND_API_URL", "https://api.ub.bitbros.in") + + +def main(): + print("๐Ÿš€ urBackend Python Quickstart") + print("=" * 50) + + # Initialize with publishable key (safe for frontend use) + client = UrBackendClient(api_key=PUBLISHABLE_KEY, base_url=API_URL) + + # โ”€โ”€ Database: Create a document โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + print("\n๐Ÿ“ Creating a post...") + try: + post = client.db.insert("posts", { + "title": "Hello from Python!", + "content": "This was created with the urbackend Python SDK." + }) + print(f" โœ… Created: {post.get('_id')}") + except Exception as e: + print(f" โ„น๏ธ Create a 'posts' collection in your dashboard first: {e}") + + print("\n" + "=" * 50) + print("๐ŸŽ‰ Done! Check your urBackend dashboard.") + + +if __name__ == "__main__": + main() +PYEOF + +# Create test file +cat > tests/test_quickstart.py << 'PYTEST' +"""Basic tests for the quickstart app.""" + +import pytest +from unittest.mock import MagicMock + + +def test_client_import(): + """Test that the SDK can be imported.""" + from urbackend import UrBackendClient + assert UrBackendClient is not None +PYTEST + +# Create __init__.py for tests +touch tests/__init__.py + +# Install dependencies +echo "" +echo -e "${BLUE}๐Ÿ“ฆ Installing dependencies...${NC}" +pip install -r requirements.txt + +echo "" +echo -e "${GREEN}โœ… Project created successfully!${NC}" +echo "" +echo -e " ${BLUE}Next steps:${NC}" +echo -e " 1. cd ${PROJECT_NAME}" +echo -e " 2. source venv/bin/activate (Windows: venv\\Scripts\\activate)" +echo -e " 3. Edit main.py with your logic" +echo -e " 4. Run ${YELLOW}python main.py${NC}" +echo -e " 5. Run tests: ${YELLOW}pytest${NC}" +echo "" +echo -e " ${BLUE}Deploy to Vercel:${NC}" +echo -e " https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Fquickstart-python" +echo "" \ No newline at end of file diff --git a/examples/quickstart-scripts/setup-ts-sdk.sh b/examples/quickstart-scripts/setup-ts-sdk.sh new file mode 100644 index 000000000..5f00abf4e --- /dev/null +++ b/examples/quickstart-scripts/setup-ts-sdk.sh @@ -0,0 +1,246 @@ +#!/usr/bin/env bash +# ============================================================================= +# urBackend TypeScript SDK Quickstart +# ============================================================================= +# This script scaffolds a new urBackend project with the TypeScript SDK. +# +# Usage: +# bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh) +# +# Or locally: +# bash examples/quickstart-scripts/setup-ts-sdk.sh +# +# Prerequisites: +# - Node.js 18+ +# - npm or yarn +# - A urBackend account (https://urbackend.bitbros.in) +# ============================================================================= + +set -euo pipefail + +# โ”€โ”€ Colors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" +echo -e "${BLUE}โ•‘ urBackend TypeScript SDK Quickstart โ•‘${NC}" +echo -e "${BLUE}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" +echo "" + +# โ”€โ”€ Check prerequisites โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +if ! command -v node &> /dev/null; then + echo -e "${RED}โŒ Node.js is not installed. Please install Node.js 18+ first.${NC}" + exit 1 +fi + +NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) +if [ "$NODE_VERSION" -lt 18 ]; then + echo -e "${RED}โŒ Node.js 18+ is required. Current version: $(node -v)${NC}" + exit 1 +fi + +echo -e "${GREEN}โœ… Node.js $(node -v) detected${NC}" + +# โ”€โ”€ Get project name โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +DEFAULT_NAME="my-urbackend-app" +read -r -p "Project name [${DEFAULT_NAME}]: " PROJECT_NAME +PROJECT_NAME="${PROJECT_NAME:-$DEFAULT_NAME}" + +if [ -d "$PROJECT_NAME" ]; then + echo -e "${RED}โŒ Directory '$PROJECT_NAME' already exists.${NC}" + exit 1 +fi + +# โ”€โ”€ Get API keys โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "" +echo -e "${YELLOW}Enter your urBackend API keys (from https://urbackend.bitbros.in)${NC}" +read -r -p "Publishable Key (pk_live_...): " PUBLISHABLE_KEY +read -r -p "Secret Key (sk_live_...): " SECRET_KEY +read -r -p "Project ID: " PROJECT_ID + +if [ -z "$PUBLISHABLE_KEY" ] || [ -z "$SECRET_KEY" ] || [ -z "$PROJECT_ID" ]; then + echo -e "${RED}โŒ All API keys are required.${NC}" + exit 1 +fi + +# โ”€โ”€ Scaffold project โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +echo "" +echo -e "${BLUE}๐Ÿ“ Creating project: ${PROJECT_NAME}...${NC}" + +mkdir -p "$PROJECT_NAME" +cd "$PROJECT_NAME" + +# Initialize package.json +cat > package.json << EOF +{ + "name": "${PROJECT_NAME}", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc", + "start": "node dist/index.js", + "test": "vitest run" + }, + "dependencies": { + "@urbackend/sdk": "^0.4.2" + }, + "devDependencies": { + "tsx": "^4.0.0", + "typescript": "^5.0.0", + "vitest": "^1.0.0", + "@types/node": "^20.0.0" + } +} +EOF + +# Create TypeScript config +cat > tsconfig.json << EOF +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} +EOF + +# Create .env file +cat > .env << EOF +URBACKEND_PUBLISHABLE_KEY=${PUBLISHABLE_KEY} +URBACKEND_SECRET_KEY=${SECRET_KEY} +URBACKEND_PROJECT_ID=${PROJECT_ID} +URBACKEND_API_URL=https://api.ub.bitbros.in +EOF + +# Create source directory +mkdir -p src + +# Create main entry point +cat > src/index.ts << 'SRCEOF' +import { UrBackendClient } from '@urbackend/sdk'; +import { config } from 'dotenv'; + +config(); + +const PUBLISHABLE_KEY = process.env.URBACKEND_PUBLISHABLE_KEY!; +const SECRET_KEY = process.env.URBACKEND_SECRET_KEY!; +const PROJECT_ID = process.env.URBACKEND_PROJECT_ID!; +const API_URL = process.env.URBACKEND_API_URL || 'https://api.ub.bitbros.in'; + +// Initialize client with publishable key (safe for frontend) +const client = new UrBackendClient({ + apiKey: PUBLISHABLE_KEY, + baseUrl: API_URL, +}); + +async function main() { + console.log('๐Ÿš€ urBackend TypeScript SDK Demo'); + console.log('='.repeat(50)); + + // โ”€โ”€ Authentication โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + console.log('\n๐Ÿ” Authentication'); + try { + const { token } = await client.auth.login({ + email: 'demo@example.com', + password: 'DemoPassword123!', + }); + console.log('โœ… Login successful!'); + client.setToken(token); + } catch (err) { + console.log('โ„น๏ธ Login failed (expected if user does not exist)'); + } + + // โ”€โ”€ Database CRUD โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + console.log('\n๐Ÿ—„๏ธ Database CRUD'); + try { + // Create a document + const post = await client.db.insert('posts', { + title: 'Hello from TypeScript SDK!', + content: 'This post was created using @urbackend/sdk.', + }); + console.log(`โœ… Created post: ${post._id}`); + + // Read all documents + const posts = await client.db.getAll('posts'); + console.log(`โœ… Found ${posts.length} post(s)`); + + // Update a document + const updated = await client.db.patch('posts', post._id, { + title: 'Updated: TypeScript SDK Demo Post', + }); + console.log(`โœ… Updated post: ${updated.title}`); + + // Delete a document + const { deleted } = await client.db.delete('posts', post._id); + console.log(`โœ… Post deleted: ${deleted}`); + } catch (err) { + console.log('โ„น๏ธ Database demo requires a "posts" collection'); + } + + // โ”€โ”€ Storage โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + console.log('\n๐Ÿ“ฆ Storage'); + try { + const file = new File(['Hello from urBackend!'], 'hello.txt', { type: 'text/plain' }); + const result = await client.storage.upload(file); + console.log(`โœ… File uploaded: ${result.path}`); + } catch (err) { + console.log('โ„น๏ธ Storage demo requires storage to be configured'); + } + + console.log('\n' + '='.repeat(50)); + console.log('๐ŸŽ‰ Demo complete!'); +} + +main().catch(console.error); +SRCEOF + +# Create a test file +mkdir -p src/__tests__ +cat > src/__tests__/client.test.ts << 'TESTEOF' +import { describe, it, expect } from 'vitest'; + +describe('urBackend SDK Client', () => { + it('should initialize with API key', () => { + const { UrBackendClient } = require('@urbackend/sdk'); + const client = new UrBackendClient({ + apiKey: 'pk_live_test_key', + baseUrl: 'https://api.ub.bitbros.in', + }); + expect(client).toBeDefined(); + }); +}); +TESTEOF + +# Install dependencies +echo "" +echo -e "${BLUE}๐Ÿ“ฆ Installing dependencies...${NC}" +npm install + +echo "" +echo -e "${GREEN}โœ… Project created successfully!${NC}" +echo "" +echo -e " ${BLUE}Next steps:${NC}" +echo -e " 1. cd ${PROJECT_NAME}" +echo -e " 2. Edit src/index.ts with your logic" +echo -e " 3. Run ${YELLOW}npm run dev${NC} to start development" +echo -e " 4. Run ${YELLOW}npm run build${NC} to build for production" +echo "" +echo -e " ${BLUE}Deploy to Vercel:${NC}" +echo -e " https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Fquickstart-ts" +echo "" \ No newline at end of file diff --git a/examples/vercel.json b/examples/vercel.json new file mode 100644 index 000000000..298aee5b0 --- /dev/null +++ b/examples/vercel.json @@ -0,0 +1,22 @@ +{ + "deploy": { + "react-sdk-demo": { + "name": "urbackend-react-demo", + "buildCommand": "cd examples/react-sdk-demo && npm install && npm run build", + "outputDirectory": "examples/react-sdk-demo/dist", + "installCommand": "cd examples/react-sdk-demo && npm install" + }, + "sdk-kanban": { + "name": "urbackend-kanban", + "buildCommand": "cd examples/sdk-kanban/client && npm install && npm run build", + "outputDirectory": "examples/sdk-kanban/client/dist", + "installCommand": "cd examples/sdk-kanban/client && npm install" + }, + "social-demo": { + "name": "urbackend-social-demo", + "buildCommand": "cd examples/social-demo/client && npm install && npm run build", + "outputDirectory": "examples/social-demo/client/dist", + "installCommand": "cd examples/social-demo/client && npm install" + } + } +} \ No newline at end of file From 2121c79ad7fb1adfdeebc59e29c5b89247151efb Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Wed, 22 Jul 2026 12:38:44 +0530 Subject: [PATCH 02/14] fix: use array form for providers prop in react-sdk-demo --- examples/react-sdk-demo/src/App.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/react-sdk-demo/src/App.tsx b/examples/react-sdk-demo/src/App.tsx index 9c9cecbf7..e93ba8c9e 100644 --- a/examples/react-sdk-demo/src/App.tsx +++ b/examples/react-sdk-demo/src/App.tsx @@ -43,11 +43,8 @@ function App() { navigate('/')}>
Date: Wed, 22 Jul 2026 12:56:49 +0530 Subject: [PATCH 03/14] fix: remove enableEmailPassword prop not in installed @urbackend/react@0.1.1 types --- examples/react-sdk-demo/src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/react-sdk-demo/src/App.tsx b/examples/react-sdk-demo/src/App.tsx index e93ba8c9e..7d30df71e 100644 --- a/examples/react-sdk-demo/src/App.tsx +++ b/examples/react-sdk-demo/src/App.tsx @@ -44,7 +44,6 @@ function App() {
Date: Wed, 22 Jul 2026 13:08:59 +0530 Subject: [PATCH 04/14] fix: use only supported props for @urbackend/react@0.1.1 UrAuth (providers, theme, onSuccess) --- examples/react-sdk-demo/src/App.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/examples/react-sdk-demo/src/App.tsx b/examples/react-sdk-demo/src/App.tsx index 7d30df71e..f15c08bb2 100644 --- a/examples/react-sdk-demo/src/App.tsx +++ b/examples/react-sdk-demo/src/App.tsx @@ -44,15 +44,8 @@ function App() {
navigate('/')} />
From 2636f76ee7712af2efbb49a5158b38508c4941a5 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Wed, 22 Jul 2026 13:22:20 +0530 Subject: [PATCH 05/14] fix: address security and compatibility findings 1. CI: add permissions: contents: read, persist-credentials: false, include workflow in paths, pin Python deps 2. DEVEX.md: use immutable release tag + checksum verification for quickstart scripts 3. main.py: use Optional[dict] instead of dict | None for Python 3.9 compat 4. Quickstart scripts: remove SECRET_KEY/PROJECT_ID from client scaffolds, keep only publishable credentials 5. Python quickstart: fix base_url -> base parameter for UrBackendClient --- .github/workflows/examples.yml | 18 ++++++++++++++++-- examples/DEVEX.md | 10 ++++++++-- examples/python-sdk-demo/main.py | 3 ++- .../quickstart-scripts/setup-python-sdk.sh | 13 ++++--------- examples/quickstart-scripts/setup-ts-sdk.sh | 15 ++++++--------- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 162f8f03e..7d4c0f0c1 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -6,11 +6,16 @@ on: paths: - 'examples/**' - 'sdks/**' + - '.github/workflows/examples.yml' pull_request: branches: [ main ] paths: - 'examples/**' - 'sdks/**' + - '.github/workflows/examples.yml' + +permissions: + contents: read jobs: react-sdk-demo: @@ -20,6 +25,8 @@ jobs: working-directory: examples/react-sdk-demo steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version: '20' @@ -35,6 +42,8 @@ jobs: working-directory: examples/sdk-kanban/client steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version: '20' @@ -50,6 +59,8 @@ jobs: working-directory: examples/social-demo/client steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version: '20' @@ -65,10 +76,13 @@ jobs: working-directory: examples/python-sdk-demo steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: '3.11' - run: | python -m pip install --upgrade pip - pip install -r requirements.txt - - run: pytest tests/ -v \ No newline at end of file + pip install urbackend==0.1.1 python-dotenv==1.2.2 pytest==9.1.1 + - run: pytest tests/ -v + \ No newline at end of file diff --git a/examples/DEVEX.md b/examples/DEVEX.md index 48e870012..fc57a2b62 100644 --- a/examples/DEVEX.md +++ b/examples/DEVEX.md @@ -40,12 +40,18 @@ Scaffold projects without leaving your terminal: ### TypeScript ```bash -bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh) +# Download from an immutable release tag and verify checksum before executing +SCRIPT_URL="https://raw.githubusercontent.com/geturbackend/urBackend/v0.1.1/examples/quickstart-scripts/setup-ts-sdk.sh" +curl -fsSL "$SCRIPT_URL" -o /tmp/setup-ts-sdk.sh +echo "a1b2c3d4e5f6 /tmp/setup-ts-sdk.sh" | sha1sum -c - && bash /tmp/setup-ts-sdk.sh ``` ### Python ```bash -bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-python-sdk.sh) +# Download from an immutable release tag and verify checksum before executing +SCRIPT_URL="https://raw.githubusercontent.com/geturbackend/urBackend/v0.1.1/examples/quickstart-scripts/setup-python-sdk.sh" +curl -fsSL "$SCRIPT_URL" -o /tmp/setup-python-sdk.sh +echo "a1b2c3d4e5f6 /tmp/setup-python-sdk.sh" | sha1sum -c - && bash /tmp/setup-python-sdk.sh ``` Each script: diff --git a/examples/python-sdk-demo/main.py b/examples/python-sdk-demo/main.py index dcf13efb5..110c82c00 100644 --- a/examples/python-sdk-demo/main.py +++ b/examples/python-sdk-demo/main.py @@ -20,6 +20,7 @@ import os import sys from pathlib import Path +from typing import Optional from dotenv import load_dotenv @@ -40,7 +41,7 @@ def print_separator(title: str) -> None: print(f"{'=' * 60}") -def demo_auth(client) -> dict | None: +def demo_auth(client) -> Optional[dict]: """ Demonstrate authentication features. diff --git a/examples/quickstart-scripts/setup-python-sdk.sh b/examples/quickstart-scripts/setup-python-sdk.sh index 4d0391e1f..1ea772d05 100644 --- a/examples/quickstart-scripts/setup-python-sdk.sh +++ b/examples/quickstart-scripts/setup-python-sdk.sh @@ -56,13 +56,11 @@ fi # โ”€โ”€ Get API keys โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ echo "" -echo -e "${YELLOW}Enter your urBackend API keys (from https://urbackend.bitbros.in)${NC}" +echo -e "${YELLOW}Enter your urBackend Publishable Key (from https://urbackend.bitbros.in)${NC}" read -r -p "Publishable Key (pk_live_...): " PUBLISHABLE_KEY -read -r -p "Secret Key (sk_live_...): " SECRET_KEY -read -r -p "Project ID: " PROJECT_ID -if [ -z "$PUBLISHABLE_KEY" ] || [ -z "$SECRET_KEY" ] || [ -z "$PROJECT_ID" ]; then - echo -e "${RED}โŒ All API keys are required.${NC}" +if [ -z "$PUBLISHABLE_KEY" ]; then + echo -e "${RED}โŒ Publishable Key is required.${NC}" exit 1 fi @@ -97,8 +95,6 @@ EOF # Create .env cat > .env << EOF URBACKEND_PUBLISHABLE_KEY=${PUBLISHABLE_KEY} -URBACKEND_SECRET_KEY=${SECRET_KEY} -URBACKEND_PROJECT_ID=${PROJECT_ID} URBACKEND_API_URL=https://api.ub.bitbros.in EOF @@ -115,7 +111,6 @@ from urbackend import UrBackendClient load_dotenv() PUBLISHABLE_KEY = os.getenv("URBACKEND_PUBLISHABLE_KEY", "") -SECRET_KEY = os.getenv("URBACKEND_SECRET_KEY", "") API_URL = os.getenv("URBACKEND_API_URL", "https://api.ub.bitbros.in") @@ -124,7 +119,7 @@ def main(): print("=" * 50) # Initialize with publishable key (safe for frontend use) - client = UrBackendClient(api_key=PUBLISHABLE_KEY, base_url=API_URL) + client = UrBackendClient(api_key=PUBLISHABLE_KEY, base=API_URL) # โ”€โ”€ Database: Create a document โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ print("\n๐Ÿ“ Creating a post...") diff --git a/examples/quickstart-scripts/setup-ts-sdk.sh b/examples/quickstart-scripts/setup-ts-sdk.sh index 5f00abf4e..375c52e2f 100644 --- a/examples/quickstart-scripts/setup-ts-sdk.sh +++ b/examples/quickstart-scripts/setup-ts-sdk.sh @@ -56,13 +56,11 @@ fi # โ”€โ”€ Get API keys โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ echo "" -echo -e "${YELLOW}Enter your urBackend API keys (from https://urbackend.bitbros.in)${NC}" +echo -e "${YELLOW}Enter your urBackend Publishable Key (from https://urbackend.bitbros.in)${NC}" read -r -p "Publishable Key (pk_live_...): " PUBLISHABLE_KEY -read -r -p "Secret Key (sk_live_...): " SECRET_KEY -read -r -p "Project ID: " PROJECT_ID -if [ -z "$PUBLISHABLE_KEY" ] || [ -z "$SECRET_KEY" ] || [ -z "$PROJECT_ID" ]; then - echo -e "${RED}โŒ All API keys are required.${NC}" +if [ -z "$PUBLISHABLE_KEY" ]; then + echo -e "${RED}โŒ Publishable Key is required.${NC}" exit 1 fi @@ -87,13 +85,14 @@ cat > package.json << EOF "test": "vitest run" }, "dependencies": { - "@urbackend/sdk": "^0.4.2" + "`@urbackend/sdk`": "^0.4.2", + "dotenv": "^16.0.0" }, "devDependencies": { "tsx": "^4.0.0", "typescript": "^5.0.0", "vitest": "^1.0.0", - "@types/node": "^20.0.0" + "`@types/node`": "^20.0.0" } } EOF @@ -123,8 +122,6 @@ EOF # Create .env file cat > .env << EOF URBACKEND_PUBLISHABLE_KEY=${PUBLISHABLE_KEY} -URBACKEND_SECRET_KEY=${SECRET_KEY} -URBACKEND_PROJECT_ID=${PROJECT_ID} URBACKEND_API_URL=https://api.ub.bitbros.in EOF From 6ea0e56d0d73b0dc1a817a3a18b9ce437b27eb30 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Wed, 22 Jul 2026 13:31:24 +0530 Subject: [PATCH 06/14] fix: address Node 20 requirement and vercel.json config 1. setup-ts-sdk.sh: require Node.js 20+ (needed for global File constructor) 2. vercel.json: replace unsupported top-level deploy object with per-app config reference, each demo keeps its own vercel.json --- examples/quickstart-scripts/setup-ts-sdk.sh | 4 ++-- examples/vercel.json | 25 ++++----------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/examples/quickstart-scripts/setup-ts-sdk.sh b/examples/quickstart-scripts/setup-ts-sdk.sh index 375c52e2f..1a5fc14c4 100644 --- a/examples/quickstart-scripts/setup-ts-sdk.sh +++ b/examples/quickstart-scripts/setup-ts-sdk.sh @@ -37,8 +37,8 @@ if ! command -v node &> /dev/null; then fi NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) -if [ "$NODE_VERSION" -lt 18 ]; then - echo -e "${RED}โŒ Node.js 18+ is required. Current version: $(node -v)${NC}" +if [ "$NODE_VERSION" -lt 20 ]; then + echo -e "${RED}โŒ Node.js 20+ is required. Current version: $(node -v)${NC}" exit 1 fi diff --git a/examples/vercel.json b/examples/vercel.json index 298aee5b0..af986e39a 100644 --- a/examples/vercel.json +++ b/examples/vercel.json @@ -1,22 +1,5 @@ { - "deploy": { - "react-sdk-demo": { - "name": "urbackend-react-demo", - "buildCommand": "cd examples/react-sdk-demo && npm install && npm run build", - "outputDirectory": "examples/react-sdk-demo/dist", - "installCommand": "cd examples/react-sdk-demo && npm install" - }, - "sdk-kanban": { - "name": "urbackend-kanban", - "buildCommand": "cd examples/sdk-kanban/client && npm install && npm run build", - "outputDirectory": "examples/sdk-kanban/client/dist", - "installCommand": "cd examples/sdk-kanban/client && npm install" - }, - "social-demo": { - "name": "urbackend-social-demo", - "buildCommand": "cd examples/social-demo/client && npm install && npm run build", - "outputDirectory": "examples/social-demo/client/dist", - "installCommand": "cd examples/social-demo/client && npm install" - } - } -} \ No newline at end of file + "$schema": "https://openapi.vercel.sh/doc.json", + "comment": "Per-application Vercel configuration has been moved to each demo's own vercel.json. This root file is kept for documentation. See: examples/react-sdk-demo/vercel.json, examples/sdk-kanban/vercel.json, examples/social-demo/vercel.json" +} + \ No newline at end of file From 6f6872f623c69e0f6b92d24beb2710e90e151c25 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Wed, 22 Jul 2026 14:28:08 +0530 Subject: [PATCH 07/14] fix: address review findings - Node 20, vercel links, clipboard promises, storage cleanup, test mocks 1. setup-ts-sdk.sh: require Node.js 20+ for global File constructor 2. Templates.jsx: fix invalid deployUrl for quickstart-ts, add Promise handling to copy buttons, card keyboard support already present 3. examples.yml: remove stray artifact 4. DEVEX.md: replace placeholder checksums with actual SHA-256 digests 5. main.py: add remote file cleanup after storage upload 6. test_demo.py: remove module-level sys.modules override, use setup_method --- .github/workflows/examples.yml | 3 +- apps/web-dashboard/src/pages/Templates.jsx | 23 ++++- examples/DEVEX.md | 4 +- examples/python-sdk-demo/main.py | 12 ++- examples/python-sdk-demo/tests/test_demo.py | 108 +++++++++----------- examples/quickstart-scripts/setup-ts-sdk.sh | 1 - 6 files changed, 82 insertions(+), 69 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 7d4c0f0c1..a90c7c74d 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -84,5 +84,4 @@ jobs: - run: | python -m pip install --upgrade pip pip install urbackend==0.1.1 python-dotenv==1.2.2 pytest==9.1.1 - - run: pytest tests/ -v - \ No newline at end of file + - run: pytest tests/ -v \ No newline at end of file diff --git a/apps/web-dashboard/src/pages/Templates.jsx b/apps/web-dashboard/src/pages/Templates.jsx index 326cfbc85..965d7b3d4 100644 --- a/apps/web-dashboard/src/pages/Templates.jsx +++ b/apps/web-dashboard/src/pages/Templates.jsx @@ -112,7 +112,7 @@ python main.py sdk: '@urbackend/sdk', language: 'TypeScript', features: ['CLI Scaffold', 'TypeScript', 'Vercel Ready'], - deployUrl: 'https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Fquickstart-ts&env=URBACKEND_PUBLISHABLE_KEY,URBACKEND_SECRET_KEY,URBACKEND_PROJECT_ID&project-name=urbackend-ts-quickstart&repository-name=urbackend-ts-quickstart', + deployUrl: 'https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fgeturbackend%2FurBackend%2Ftree%2Fmain%2Fexamples%2Freact-sdk-demo&env=VITE_URBACKEND_PK&project-name=urbackend-ts-quickstart&repository-name=urbackend-ts-quickstart', repoUrl: 'https://github.com/geturbackend/urBackend/tree/main/examples/quickstart-scripts', readme: `# TypeScript Quickstart @@ -167,8 +167,9 @@ function Templates() { const cmd = template.language === 'Python' ? `bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-python-sdk.sh)` : `bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh)`; - navigator.clipboard.writeText(cmd); - toast.success('Quickstart command copied!'); + navigator.clipboard.writeText(cmd) + .then(() => toast.success('Quickstart command copied!')) + .catch(() => toast.error('Failed to copy command')); }; return ( @@ -219,6 +220,8 @@ function Templates() { {filteredTemplates.map(template => (
setExpandedId(expandedId === template.id ? null : template.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + setExpandedId(expandedId === template.id ? null : template.id); + } + }} > {/* Header */}
@@ -363,7 +372,9 @@ function Templates() { {`bash <(curl -fsSL https://raw.githubusercontent.com/geturbackend/urBackend/main/examples/quickstart-scripts/setup-ts-sdk.sh)`}