Back to .md Directory

JusPri - System Architecture Document

JusPri is a cloud-to-edge printing ecosystem with Bluetooth-based file transfer, pull-based authorization, and multi-format document support.

May 2, 2026
0 downloads
0 views
ai agent
View source

JusPri - System Architecture Document

Overview

JusPri is a cloud-to-edge printing ecosystem with Bluetooth-based file transfer, pull-based authorization, and multi-format document support.


Core Principles

  1. Security First: Pi only executes after explicit cloud authorization
  2. Bluetooth Transfer: Files sent directly to Pi, not cloud
  3. Multi-Format Support: PDF, DOC/DOCX, TXT, MD, Images
  4. Pull-Based Model: Pi polls for authorization, not pushed
  5. Google OAuth: Single sign-on for users
  6. Session Scoped: Files deleted after job completion

Technology Stack

Cloud Backend

  • Runtime: Node.js 18+
  • Framework: Express.js
  • Database: SQLite (dev) / PostgreSQL (prod)
  • Auth: Google OAuth 2.0 via Passport.js
  • Real-time: Socket.io (optional, polling primary)
  • Host: AWS EC2 / Amplify

Frontend (Unified)

  • Framework: React 18 + Vite
  • Routing: React Router v6
  • UI: Tailwind CSS + shadcn/ui
  • State: React Query for API calls
  • Apps: 3 routes (Client, Admin, Kiosk)

Pi Agent (Edge Node)

  • Runtime: Node.js 18+
  • Bluetooth: noble (BLE) / bleno
  • Printing: CUPS (lp command)
  • Conversion:
    • LibreOffice (DOC/DOCX → PDF)
    • ImageMagick (Images → PDF)
    • Markdown → PDF (markdown-pdf)

Architecture Diagram

┌──────────────────────────────────────────────────────────────┐
│                      CLOUD BACKEND (AWS)                      │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Web App (React - 3 Routes)                            │  │
│  │  • /client  - User mobile UI                           │  │
│  │  • /admin   - Admin dashboard                          │  │
│  │  • /kiosk   - Pi display                              │  │
│  └─────────────────────┬──────────────────────────────────┘  │
│                        │                                      │
│  ┌─────────────────────▼──────────────────────────────────┐  │
│  │  API Server (Express + Socket.io)                      │  │
│  │  • Google OAuth                                         │  │
│  │  • Job Registry                                        │  │
│  │  • Authorization Logic                                 │  │
│  └─────────────────────┬──────────────────────────────────┘  │
│                        │                                      │
│  ┌─────────────────────▼──────────────────────────────────┐  │
│  │  Database (PostgreSQL)                                  │  │
│  │  • Users, Jobs, Nodes, Sessions                        │  │
│  └────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────┘
                         ▲
                         │ HTTPS API
                         │ (Polling/WebSocket)
                         │
┌────────────────────────▼─────────────────────────────────────┐
│                   RASPBERRY PI (Edge Node)                    │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  QR Code Display (node_id)                             │  │
│  └────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Bluetooth Server (BLE)                                │  │
│  │  • Receives files from mobile                          │  │
│  │  • Creates job on cloud                                │  │
│  └────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Document Converter                                     │  │
│  │  • DOC/DOCX → PDF (LibreOffice)                        │  │
│  │  • Images → PDF (ImageMagick)                          │  │
│  │  • TXT/MD → PDF                                        │  │
│  └────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Authorization Poller                                   │  │
│  │  • Polls cloud for job status                          │  │
│  │  • Waits for PAID/READY state                         │  │
│  └────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Printer Controller (CUPS)                             │  │
│  │  • Executes authorized jobs                            │  │
│  │  • Reports status back                                 │  │
│  └────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────┘
                         │ USB
                         ▼
                   ┌──────────┐
                   │ Printer  │
                   └──────────┘

Workflow (User Journey)

1. Discovery

User → Scans QR on Pi
     → Opens: https://juspri.com/client?node_id=pi_abc123

2. Authentication

User → Clicks "Login with Google"
     → Redirected to Google OAuth
     → Returns with auth token

3. File Transfer (Bluetooth)

User → Selects file on phone
     → App connects to Pi via BLE
     → File transferred directly to Pi
     → Pi receives file, generates job_id

4. Job Registration

Pi → POST /api/jobs/create
    Body: { node_id, user_id, filename, file_size }
    
Cloud → Creates job entry (status: CREATED)
      → Returns: { job_id, pages, cost }

5. User Configuration

User → Sees job details on web app
     → Selects: color/bw, copies, etc.
     → Clicks "Pay ₹X"

6. Payment (Future)

User → Razorpay checkout
Cloud → Verifies payment
      → Updates job: status = PAID

7. Authorization Polling

Pi → GET /api/jobs/{job_id}/status (every 3s)

Cloud → Returns: { status: "PAID", print_config: {...} }

Pi → Sees status = PAID
   → Proceeds to print

8. Document Conversion

Pi → Checks file type
   → If DOC/DOCX: LibreOffice → PDF
   → If Image: ImageMagick → PDF
   → If TXT/MD: markdown-pdf → PDF
   → If PDF: Use directly

9. Printing

Pi → POST /api/jobs/{job_id}/status
    Body: { status: "PRINTING" }
    
   → lp -d printer_name converted.pdf
   
   → POST /api/jobs/{job_id}/status
    Body: { status: "COMPLETED", pages_printed: 5 }

10. Cleanup

Pi → Deletes local files
   → Removes from temp directory
   → Session complete

Database Schema

Users Table

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  google_id VARCHAR(255) UNIQUE NOT NULL,
  email VARCHAR(255) NOT NULL,
  name VARCHAR(255),
  avatar_url TEXT,
  created_at TIMESTAMP DEFAULT NOW(),
  last_login TIMESTAMP
);

Nodes Table (Printers)

CREATE TABLE nodes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  node_id VARCHAR(50) UNIQUE NOT NULL,
  name VARCHAR(255),
  location VARCHAR(255),
  bluetooth_address VARCHAR(17),
  status VARCHAR(20) DEFAULT 'offline',
  last_seen TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW()
);

Jobs Table

CREATE TABLE jobs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  job_id VARCHAR(100) UNIQUE NOT NULL,
  user_id UUID REFERENCES users(id),
  node_id VARCHAR(50) REFERENCES nodes(node_id),
  
  -- File info
  filename VARCHAR(255) NOT NULL,
  original_format VARCHAR(10),
  file_size INTEGER,
  pages INTEGER,
  
  -- Print config
  color BOOLEAN DEFAULT false,
  copies INTEGER DEFAULT 1,
  duplex BOOLEAN DEFAULT false,
  
  -- Cost
  cost_per_page DECIMAL(10,2) DEFAULT 3.00,
  total_cost DECIMAL(10,2),
  
  -- Payment
  payment_id VARCHAR(100),
  payment_status VARCHAR(20) DEFAULT 'pending',
  paid_at TIMESTAMP,
  
  -- Status
  status VARCHAR(20) DEFAULT 'CREATED',
  -- CREATED, PENDING, PAID, PRINTING, COMPLETED, ERROR
  
  -- Timestamps
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  print_started_at TIMESTAMP,
  print_completed_at TIMESTAMP,
  
  -- Error handling
  error_code VARCHAR(50),
  error_message TEXT
);

Sessions Table

CREATE TABLE sessions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  job_id VARCHAR(100) REFERENCES jobs(job_id),
  session_token VARCHAR(255) UNIQUE NOT NULL,
  expires_at TIMESTAMP NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

API Endpoints

Authentication

GET  /auth/google          - Initiate Google OAuth
GET  /auth/google/callback - OAuth callback
GET  /auth/me              - Get current user
POST /auth/logout          - Logout

Jobs

POST   /api/jobs/create           - Register new job
GET    /api/jobs/:job_id          - Get job details
GET    /api/jobs/:job_id/status   - Get job status (Pi polling)
POST   /api/jobs/:job_id/status   - Update job status (Pi reports)
POST   /api/jobs/:job_id/config   - Update print config
POST   /api/jobs/:job_id/payment  - Verify payment
GET    /api/jobs/user/:user_id    - Get user's jobs

Nodes

GET  /api/nodes/:node_id         - Get node info
POST /api/nodes/:node_id/status  - Update node status (heartbeat)
GET  /api/nodes                  - List all nodes (admin)

Admin

GET  /api/admin/jobs             - List all jobs
GET  /api/admin/stats            - System statistics
POST /api/admin/jobs/:job_id/cancel - Cancel job

Security Model

Session Scoping

  • Each job has unique session_token
  • Token expires after 30 minutes
  • Files deleted after job completion or timeout

Authorization Flow

1. User authenticates → Google OAuth token
2. Job created → session_token generated
3. Pi polls → uses session_token
4. Payment verified → status = PAID
5. Pi authorized → executes print
6. Completion → session expires, files deleted

Error Recovery

Network Lost:
- Pi caches job state locally
- Retries when connection restored
- No print without cloud authorization

Printer Error:
- Pi reports error_code to cloud
- Cloud marks job as ERROR
- Admin can retry or refund

File Format Support

FormatConversion MethodNotes
PDFDirectNo conversion needed
DOC/DOCXLibreOfficelibreoffice --headless --convert-to pdf
TXTmarkdown-pdfPlain text → PDF
MDmarkdown-pdfMarkdown → formatted PDF
PNG/JPGImageMagickconvert image.jpg output.pdf
Multiple ImagesImageMagickCombine into single PDF

Next Steps

  1. Backend Setup - Express + PostgreSQL + OAuth
  2. Frontend Setup - React + 3 routes + shared components
  3. Pi Agent - Bluetooth + Converters + CUPS
  4. Testing - End-to-end flow
  5. Deployment - AWS + Pi configuration

Ready to build JusPri! 🚀

Related Documents