FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
lightweight
lightweightglimpse2-runbook-view-glimpse-save-login-react
Remix of lightweight/glimpse2-runbook-view-glimpse-save-login
Public
Like
glimpse2-runbook-view-glimpse-save-login-react
Home
Code
8
_townie
13
backend
7
frontend
5
shared
3
.vtignore
README.md
deno.json
H
main.tsx
Branches
2
Pull requests
Remixes
History
Environment variables
6
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
Code
/
Search
main.tsx
https://lightweight--01991bd6e87d71bdb757c4cf2f32ff6d.web.val.run
README.md

Glance Demos

A Val Town application for managing and viewing demos with authentication.

Features

  • Authentication: Google OAuth via LastLogin
  • Dashboard: User-friendly interface showing system status
  • Health Monitoring: Real-time system health checks
  • Modular Architecture: Clean separation of concerns

Authentication

This application uses LastLogin for authentication:

  • Login: Users sign in with their Google account
  • Protected Routes: All routes require authentication except public endpoints
  • Logout: Users can logout via /auth/logout (handled automatically by LastLogin)
  • Session Management: Automatic session validation and renewal

Public Routes

The following routes are accessible without authentication:

  • /api/health - System health status

Protected Routes

Routes are protected by different authentication mechanisms:

User Authentication (Google OAuth via LastLogin):

  • / - Main dashboard (shows user info and system status)
  • /api/* - API endpoints (except health)
  • /views/* - View routes including /views/glimpse/:id
  • /glimpse/* - Shortcut routes including /glimpse/:id (equivalent to /views/glimpse/:id)

Webhook Authentication (X-API-KEY header):

  • /tasks/* - Webhook endpoints for external integrations (POST requests only)
    • GET requests to /tasks/* are allowed without authentication for debug endpoints

Project Structure

├── backend/
│   ├── controllers/         # Business logic controllers
│   ├── routes/             # Route definitions and HTTP handling
│   │   ├── api/            # API endpoints
│   │   ├── glimpse/        # Glimpse routes (enhanced with React frontend)
│   │   ├── tasks/          # Task-related routes
│   │   ├── views/          # User-facing views
│   │   └── authCheck.ts    # Authentication middleware
│   └── services/           # External service integrations
├── frontend/               # React frontend assets
│   ├── glimpse.html        # HTML template for glimpse views
│   ├── glimpse.tsx         # React entry point
│   ├── components/         # React components
│   │   ├── GlimpseView.tsx # Main glimpse display component
│   │   ├── NotionBlock.tsx # Notion block renderer
│   │   └── NotionProperty.tsx # Property display component
│   └── README.md
├── shared/                 # Shared utilities and types
│   ├── types.ts           # TypeScript interfaces for Notion data
│   ├── utils.ts           # Shared utility functions
│   └── README.md
└── main.tsx               # Application entry point with static file serving

Architecture

The application follows a clean MVC architecture with proper separation of concerns:

Route Layer (HTTP Handling)

  • Handles HTTP request/response formatting
  • Extracts parameters from requests
  • Applies authentication middleware
  • Calls controller functions and formats responses
  • Manages HTTP status codes and error responses

Controller Layer (Business Logic)

  • Contains business logic and orchestrates service calls
  • Returns plain data objects (not HTTP responses)
  • Handles data validation and transformation
  • Filters sensitive data (e.g., button properties)
  • Provides consistent success/error response structure

Service Layer (External Integrations)

  • Handles external API calls (Notion, databases)
  • Manages data persistence
  • Returns structured results with success/error information

Response Format

All controller functions return a consistent structure:

{ success: boolean, data: any | null, error: string | null, details?: string // Additional error context }

Routes then format these into appropriate HTTP responses.

Glimpse Routes

The application provides multiple routes for accessing page data and user authentication:

Authentication Routes

  • GET /glimpse/login - User-specific login redirect

    • Requires user authentication (Google OAuth via LastLogin)
    • Looks up authenticated user's email in GLANCE_DEMOS_DB_ID database
    • If user found: Redirects to user's personal URL
    • If user not found: Creates new user record and redirects to /glimpse/thanks
    • Shows detailed error information for debugging database structure issues
  • GET /glimpse/thanks - New user welcome page

    • Shows confirmation that user account was created
    • Explains next steps (admin review, email with demo link)
    • Provides timeline expectations (1-2 business days)

Data Access Routes

  • GET /views/glimpse/:id - Get complete page data with blocks by Notion page ID (JSON only)
  • GET /glimpse/:id - Enhanced with React Frontend - Content negotiation based on Accept header:
    • Browser requests (Accept: text/html): Returns rich React frontend with interactive Notion content display
    • API requests (Accept: application/json): Returns raw JSON data (same as before)
    • Fallback: If HTML template fails to load, automatically serves JSON

React Frontend Features

The /glimpse/:id endpoint now includes a rich React frontend when accessed via browser:

Content Rendering

  • Rich Notion Blocks: Supports headings, paragraphs, lists, code blocks, callouts, images, videos, tables, and more
  • Property Display: Shows all page properties with type-specific formatting and icons
  • Responsive Design: Mobile-friendly layout using TailwindCSS
  • Server-Side Data Injection: Initial data injected to eliminate loading states

User Experience

  • Error Handling: Graceful error states with retry functionality
  • Loading States: Smooth loading indicators
  • Navigation: Easy return to dashboard
  • Debug Mode: Raw data view in development environments
  • Dynamic Remote Support: Remote support section appears only when agents are assigned to the page

Agent Data Integration

  • Real-time Agent Updates: Fetches agent data every 5 seconds when user is authorized
  • Conditional Display: Remote support section only appears when agents array has at least one item
  • Complete Agent Information: Shows all blob contents including pageId, agents array, lastUpdated, and assignedAt timestamps
  • Authorization-based: Only fetches agent data for authorized users (matching email addresses)
  • Error Handling: Silent console logging for missing agent data without disrupting user experience

Technical Implementation

  • Content Negotiation: Single endpoint serves both HTML and JSON
  • Static File Serving: Frontend assets served via /frontend/* route
  • React 18.2.0: Pinned version for consistency
  • TypeScript Support: Shared types for Notion data structures

Note: The glimpse endpoints now provide both programmatic access (JSON) and user-friendly viewing (React frontend) from the same URL, maintaining backward compatibility while adding rich content display capabilities.

Demo API Endpoints

API endpoints for accessing Notion page data with different levels of detail:

  • GET /api/demo/:id/properties - Returns page properties only
  • GET /api/demo/:id - Returns page properties + all blocks recursively
  • GET /api/agent/:id - Returns agent blob data for a specific page ID

Architecture:

  • Routes: Handle HTTP concerns (parameter extraction, response formatting, status codes)
  • Controllers: Contain business logic (getDemoProperties, getDemoFull)
  • Services: Handle Notion API integration

Authentication Behavior:

  • Browser requests: Require user authentication (Google OAuth via LastLogin)
  • Internal requests: Bypass authentication when called from within the Val (identified by Deno user agent)

Response Format: Routes return the data directly from controllers on success:

{ // Notion page object with properties // For full endpoint: also includes "blocks" array with recursive block data }

On error, routes return:

{ "error": "Error message", "details": "Additional error context" }

Usage Examples:

// Internal call from within Val (no authentication needed) const response = await fetch('/api/demo/page-id/properties'); const data = await response.json(); // External browser request (requires authentication) // User must be logged in via Google OAuth

All glimpse routes:

  • Require user authentication
  • Return complete page data including properties and blocks recursively
  • Filter out button properties from Notion page data
  • Return standardized JSON responses (except authentication routes which redirect or show HTML)
  • Use the same controller functions as the API endpoints for consistency

New User Registration Flow

  1. Authentication: User must be authenticated via Google OAuth (handled by LastLogin)
  2. Database Lookup: System queries GLANCE_DEMOS_DB_ID database for user's email
  3. User Creation: If not found, creates new user record with email address
  4. Welcome Page: Redirects to /glimpse/thanks with next steps information
  5. Admin Process: Admin reviews new users and adds demo URLs manually
  6. User Return: User can return to /glimpse/login once URL is configured

Database Requirements for Login

The GLANCE_DEMOS_DB_ID database must contain:

  • Email property: Contains user's email address (exact match with authenticated email)
  • URL property: Contains user's redirect URL (optional for new users)

Supported URL property names: URL, Link, Redirect URL, Demo URL, url, link Supported URL property types: url, rich_text, title

Error Handling

The login endpoint provides detailed error information for debugging:

  • Missing environment variables
  • Database query failures
  • User creation failures (falls back to access denied page)
  • Invalid or missing URL properties
  • URL format validation errors

The dashboard displays both routes in a comparison table for easy testing.

Development

The application is built with:

  • Hono: Web framework for routing and middleware
  • LastLogin: Authentication service
  • TypeScript: Type-safe development
  • Val Town: Hosting platform

Webhook Authentication

The application supports webhook endpoints for external integrations (like Notion webhooks):

Configuration

Set the webhook secret in your environment:

NOTION_WEBHOOK_SECRET=your-secret-key-here

Webhook Endpoints

  • POST /tasks/notion-webhook - Main webhook endpoint for Notion integrations (requires X-API-KEY header)
  • POST /tasks/url - Updates Notion page URL property with glimpse URL (requires X-API-KEY header)
  • POST /tasks/assign - Assigns agents to tasks based on Person property matching (requires X-API-KEY header)
  • POST /tasks/unassign - Clears Person and Glimpse agents properties for a specific page (requires X-API-KEY header)
  • POST /tasks/test - Test endpoint for webhook authentication (requires X-API-KEY header)
  • GET /tasks/debug-webhook - Debug endpoint to check webhook configuration

Task Assignment Webhook (/tasks/assign)

The assignment webhook automatically assigns agents to tasks based on Person property matching:

Workflow:

  1. Receives webhook with page ID from Notion
  2. Retrieves page properties to extract Person and Viewing properties
  3. Checks if Viewing property is true - if not, skips assignment and logs result
  4. Queries GLANCE_AGENTS_DB_ID database for agents with matching Person property
  5. PHASE 1: Data Collection - Fetches complete page data for all agents (no side effects)
  6. Validates all data collection succeeded - aborts if any agent data fetch fails
  7. PHASE 2: Atomic Updates - Selective clearing and assignment updates
    • Step 2a: Checks each agent's current demo assignments
    • Selective Clearing: Only clears agents assigned to OTHER demos (not this one)
    • Agent Blob Clearing: Removes cleared agents from affected demo blobs
    • Step 2b: Updates demo page's "Glimpse agents" property
  8. PHASE 3: Non-Critical Updates - Updates agent blob (non-blocking, won't fail webhook)

Relation Management:

  • Smart Clearing: Only clears agents when they're assigned to different demos
  • Preserves Same-Demo Assignments: No clearing when reassigning to the same demo
  • Prevents Multiple Assignments: Ensures agents are only assigned to one demo at a time
  • Efficient: Minimal API calls - only clears when necessary
  • Reliable: Handles new agents, existing agents, and reassignments correctly

Relation Management:

  • Simplified Approach: Only updates the demo page's "Glimpse agents" property
  • Automatic Bidirectional Updates: Notion automatically updates agents' "Glimpse demos" properties
  • No Manual Clearing: Eliminates the risk of clearing relations without repopulating them
  • Reliable Reassignment: Works correctly even when reassigning the same agents

Error Handling & Reliability:

  • Transactional Approach: Collects all required data before making any changes
  • Early Validation: Aborts assignment if any agent data cannot be fetched
  • Atomic Updates: Critical Notion updates happen together or not at all
  • Non-Blocking Blob Operations: Both blob updates and blob clearing are non-blocking
  • Comprehensive Logging: Detailed checkpoint logging at each phase for debugging
  • Graceful Degradation: Continues with available data when possible
  • Synchronized Blob Clearing: Removes agents from affected demo blobs when reassigning

Agent Blob Storage:

  • Key Pattern: glimpse--agent--{pageId} (where pageId is from the webhook)
  • Data Structure:
{ "pageId": "page-id", "agents": [ { "agentId": "agent-page-id", "agentName": "Agent Name", "agentEmail": "agent@example.com", "agentMeetUrl": "https://meet.google.com/...", "agentPhone": "+1234567890" } ], "lastUpdated": "2025-09-10T19:52:00.000Z", "assignedAt": "2025-09-10T19:52:00.000Z" }

Requirements:

  • Page must have a Viewing property set to true (assignment only occurs for actively viewed pages)
  • Page must have a Person property with assigned person
  • Agents database must have pages with Person properties matching the assigned person
  • Original page must have a "Glimpse agents" relation property
  • Agent pages must have a "Glimpse demos" relation property

Viewing Property Support:

  • Checkbox: true value
  • Select: "true" option name
  • Rich Text: "true" or "yes" text content (case-insensitive)

Response Format (Assignment Completed):

{ "success": true, "message": "Task assignment completed successfully", "pageId": "page-id", "personId": "person-id", "agentsAssigned": 2, "agentsClearedCount": 1, "agentsSkippedClearing": 1, "agentBlobUpdated": true, "blobClearsAttempted": 2, "blobClearsSuccessful": 2, "timestamp": "2025-09-10T16:51:24.733Z" }

Response Format (Assignment Skipped):

{ "success": true, "message": "Page is not being viewed - assignment skipped", "pageId": "page-id", "viewing": false, "timestamp": "2025-09-10T16:51:24.733Z" }

Relation Management:

  • Smart Clearing: Only clears agents when they're assigned to different demos
  • Preserves Same-Demo Assignments: No clearing when reassigning to the same demo
  • Prevents Multiple Assignments: Ensures agents are only assigned to one demo at a time
  • Efficient: Minimal API calls - only clears when necessary
  • Reliable: Handles new agents, existing agents, and reassignments correctly

Task Unassignment Webhook (/tasks/unassign)

The unassignment webhook clears agent assignments for a specific page:

Workflow:

  1. Receives webhook with page ID
  2. Retrieves current page properties to verify state
  3. Clears Notion Properties: Sets Person and Glimpse agents properties to empty
  4. Clears Agent Blob: Sets agents array to empty while preserving metadata
  5. Returns Results: Provides detailed success/failure information

Request Format:

{ "pageId": "notion-page-id" }

Agent Blob Clearing:

  • agents array: Set to empty array []
  • lastUpdated: Updated to current timestamp
  • assignedAt: Preserved (historical record)
  • pageId: Remains unchanged

Response Format:

{ "success": true, "message": "Page assignments cleared successfully", "pageId": "page-id", "clearedPerson": true, "clearedAgents": true, "clearedBlob": true, "timestamp": "2025-09-10T21:50:00.000Z" }

Error Handling:

  • Partial Success: Some operations may succeed while others fail
  • Non-blocking Blob Operations: Notion updates are critical, blob clearing is non-fatal
  • Detailed Reporting: Clear indication of which operations succeeded/failed

Automated Agent Cleanup

The application includes automated cleanup of stale agent assignments through a dedicated cron job:

Agent Cleanup Cron (cleanup-agent.ts)

Purpose: Automatically clears agent assignments for pages that are no longer being viewed.

Schedule: Runs every 15 minutes (configurable via Val Town cron settings)

Workflow:

  1. Optimized Database Query: Queries demos database specifically for pages with Viewing = false
  2. Batch Processing: Processes all qualifying pages in a single run
  3. Assignment Clearing: For each page, clears:
    • Person property: Removes assigned person
    • Glimpse agents property: Clears all agent relations
    • Agent blob: Sets agents array to empty []
  4. Comprehensive Logging: Detailed success/failure reporting for monitoring

Query Optimization:

  • Targeted Query: Only fetches pages that need processing (Viewing = false)
  • Efficient Processing: Scales with actual cleanup needs, not total database size
  • Multiple Conditions: Handles false, no, and empty select values

Viewing Property Support (Select Type):

  • Triggers Cleanup: "false", "no", or empty/null selection
  • Skips Cleanup: "true", "yes" selections

Error Handling:

  • Resilient Processing: Continues with other pages if one fails
  • Non-blocking Blob Operations: Notion updates are critical, blob clearing is non-fatal
  • Detailed Reporting: Success/failure statistics for each run

Performance Benefits:

  • API Efficient: Minimal Notion API calls through targeted queries
  • Predictable Runtime: Processing time correlates with actual cleanup needs
  • Resource Friendly: Low impact on system resources

Monitoring Output:

{ "success": true, "message": "Agent cleanup completed", "pagesProcessed": 5, "pagesSuccessful": 4, "pagesFailed": 1, "processingTime": "2.3s", "timestamp": "2025-09-10T22:15:00.000Z" }

This automated cleanup ensures that agent assignments don't persist after viewing sessions end, maintaining clean data and preventing stale information from appearing in the frontend.

Authentication

Webhook endpoints require the X-API-KEY header:

curl -X POST https://your-val.web.val.run/tasks/test \ -H "X-API-KEY: your-secret-key-here"

Testing

Use the webhook testing form in the dashboard:

  1. Navigate to your dashboard at /
  2. Find the "Webhook Endpoint" section
  3. Enter your NOTION_WEBHOOK_SECRET value
  4. Click "Test Webhook" to verify authentication

Environment Variables

Configure these environment variables for full functionality:

  • GLANCE_DEMOS_DB_ID - Notion database ID for demos
  • GLANCE_CONTENT_DB_ID - Notion database ID for content
  • GLANCE_INTERACTIONS_DB_ID - Notion database ID for interactions
  • GLANCE_AGENTS_DB_ID - Notion database ID for agents
  • NOTION_API_KEY - Notion API key for database access
  • NOTION_WEBHOOK_SECRET - Secret key for webhook authentication

Real-time Viewing Analytics

The application includes real-time viewing analytics with immediate Notion synchronization and email-based authorization:

Authorization & Security

  • Email-based Access Control: Viewing analytics are only tracked when the authenticated user's email matches the page's Email property
  • Frontend Authorization: Email comparison happens on the frontend before any API calls are made
  • Zero Unauthorized Calls: Users without matching emails generate no viewing API requests
  • Automatic Detection: System automatically extracts emails from Notion page properties and user authentication

Blob Storage + Notion Sync

  • Fast blob updates: Page viewing status stored in Val Town blob storage for instant response (~100ms)
  • Immediate Notion sync: When users start viewing pages, Notion database is updated immediately
  • Automatic cleanup: Cron job runs every minute to mark stale sessions (>1 minute old) as not viewing

Viewing Data Flow

  1. Authorization Check: Frontend compares user email with page Email property
  2. Authorized Users: Frontend calls /api/viewing → Blob updated → Notion synced immediately
  3. User continues viewing: Frontend updates blob every 4 seconds (no Notion calls)
  4. User leaves/session stale: Cron detects stale session → Blob updated → Notion synced immediately
  5. Unauthorized Users: No API calls made, no viewing analytics tracked

Email Property Requirements

Pages must include an Email property in Notion for viewing analytics to work:

  • Property Name: "Email" or "email"
  • Property Type: Email type or Rich Text type
  • Content: Must exactly match the authenticated user's email address

Notion Database Schema

Pages in your Notion databases should include these properties for viewing analytics:

  • Email (Email or Rich Text) - Required for authorization
  • Viewing (Checkbox) - Real-time viewing status
  • Last Viewed (Date) - Timestamp of last viewing activity
  • Current Viewer (Rich Text) - Email of current viewer
  • Tab Visible (Checkbox) - Whether page tab is currently visible

Frontend Integration

  • Email Authorization: Compares authenticated user email with page Email property before tracking
  • Page Visibility API: Tracks when users switch tabs or minimize windows
  • Periodic updates: Calls viewing API every 4 seconds while page is active (authorized users only)
  • Session management: Automatic cleanup handles crashed browsers and network issues
  • Performance: Non-blocking API calls don't impact user experience, zero calls for unauthorized users
HTTP
  • main.tsx
    lightweight--01991bd6e87d71bdb757c4cf2f32ff6d.web.val.run
Cron
  • cleanup-agent.ts
  • cleanup-viewing.ts
Code
_towniebackendfrontendshared.vtignoreREADME.mddeno.json
H
main.tsx
Go to top
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Product
FeaturesPricing
Developers
DocsStatusAPI ExamplesNPM Package Examples
Explore
ShowcaseTemplatesNewest ValsTrending ValsNewsletter
Company
AboutBlogCareersBrandhi@val.town
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.