FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
prashamtrivedi

prashamtrivedi

ganeshotsav-2025-forms

Public
Like
ganeshotsav-2025-forms
Home
Code
10
backend
3
frontend
2
quiz-implementation
3
shared
2
taskNotes
.vtignore
README.md
deno.json
frontend-complete.md
H
ganeshotsav-forms.http.tsx
Branches
1
Pull requests
Remixes
History
Environment variables
2
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
/
quiz-implementation
/
technical-requirements.md
Code
/
quiz-implementation
/
technical-requirements.md
Search
8/17/2025
Viewing readonly version of main branch: v235
View latest version
technical-requirements.md

Technical Requirements: Bharat Ek Khoj Quiz System

Architecture Overview

The quiz system integrates with the existing Ganeshotsav 2025 platform, extending the current Val Town-based architecture with new components for team-based registrations.

High-Level Architecture

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│                 │    │                  │    │                 │
│   Frontend      │    │   Backend        │    │   External      │
│   Components    │◄──►│   Services       │◄──►│   Services      │
│                 │    │                  │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘
│                      │                      │
├─ Quiz Form           ├─ Quiz API Routes     ├─ OpenAI API
├─ Team Management     ├─ Database Layer     ├─ Val Town Runtime
├─ Admin Interface     ├─ Validation         └─ SQLite
└─ Language Support    └─ Authentication

Platform Constraints

Val Town Specific Limitations

  1. Serverless Runtime: No persistent processes or local state
  2. Cold Start Latency: First request may have higher latency
  3. Memory Limits: Function execution memory constraints
  4. File System: Read-only file system, no local file storage
  5. Database: SQLite only, no external database connections
  6. Request Timeout: Maximum execution time limits

Development Constraints

  • TypeScript/TSX only for Val Town compatibility
  • ESM import syntax required (https://esm.sh/ for npm packages)
  • No Node.js specific APIs (use Deno APIs instead)
  • Browser compatibility for shared code modules

Database Architecture

Schema Design

-- Quiz Teams Table CREATE TABLE IF NOT EXISTS quiz_teams ( team_name TEXT PRIMARY KEY, parent_name TEXT NOT NULL, contact_number TEXT NOT NULL CHECK(LENGTH(contact_number) = 10), members TEXT NOT NULL, -- JSON array: [{name: string, age: number}] member_count INTEGER NOT NULL CHECK(member_count >= 1 AND member_count <= 4), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE(contact_number) -- Prevent duplicate registrations by same contact ); -- Index for admin queries CREATE INDEX IF NOT EXISTS idx_quiz_teams_created ON quiz_teams(created_at); CREATE INDEX IF NOT EXISTS idx_quiz_teams_member_count ON quiz_teams(member_count);

Data Types and Validation

interface QuizTeamMember { name: string; // 2-50 characters, alphabetic with spaces age: number; // 6-18 years for quiz eligibility } interface QuizTeamData { team_name: string; // 3-30 characters, unique parent_name: string; // 2-100 characters contact_number: string; // 10-digit Indian mobile number members: QuizTeamMember[]; // 1-4 members member_count: number; // Computed field for queries } interface QuizRegistrationResponse { success: boolean; error?: string; message?: string; team_name?: string; }

Database Migration Strategy

// Migration function to be run during initialization export async function initializeQuizDatabase(): Promise<void> { await sqlite.execute(` CREATE TABLE IF NOT EXISTS quiz_teams ( team_name TEXT PRIMARY KEY, parent_name TEXT NOT NULL, contact_number TEXT NOT NULL CHECK(LENGTH(contact_number) = 10), members TEXT NOT NULL, member_count INTEGER NOT NULL CHECK(member_count >= 1 AND member_count <= 4), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE(contact_number) ) `); // Create indexes for performance await sqlite.execute(` CREATE INDEX IF NOT EXISTS idx_quiz_teams_created ON quiz_teams(created_at) `); await sqlite.execute(` CREATE INDEX IF NOT EXISTS idx_quiz_teams_member_count ON quiz_teams(member_count) `); }

API Specifications

Endpoint Design

1. Quiz Registration Submission

POST /quiz-registration
Content-Type: application/json

Request Body:
{
  "team_name": "भारत के वीर",
  "parent_name": "राजेश शर्मा", 
  "contact_number": "9876543210",
  "members": [
    {"name": "आर्यन", "age": 12},
    {"name": "प्रिया", "age": 10}
  ]
}

Response (200 OK):
{
  "success": true,
  "message": "Team registered successfully",
  "team_name": "भारत के वीर"
}

Response (400 Bad Request):
{
  "success": false,
  "error": "Team name already exists"
}

2. Admin Quiz Interface

GET /admin/quiz?key={ADMIN_KEY}
Response: HTML page with quiz registrations table

GET /admin/quiz/data?key={ADMIN_KEY}
Response (200 OK):
{
  "success": true,
  "teams": [...],
  "summary": {
    "total_teams": 45,
    "total_members": 156,
    "avg_team_size": 3.47
  }
}

3. Team Name Suggestions

POST /ai/suggest-team-names
Content-Type: application/json

Request Body:
{
  "language": "hindi",
  "context": "cultural quiz competition"
}

Response (200 OK):
{
  "success": true,
  "suggestions": [
    "भारत गौरव",
    "ज्ञान सागर", 
    "संस्कृति रत्न",
    "विद्या वीर",
    "राष्ट्र गर्व"
  ]
}

4. Team Deletion (Admin)

DELETE /admin/quiz/{team_name}?key={ADMIN_KEY}
Response (200 OK):
{
  "success": true,
  "message": "Team deleted successfully"
}

Request Validation

export function validateQuizRegistration(data: any): { valid: boolean; errors: string[]; } { const errors: string[] = []; // Team name validation if (!data.team_name || data.team_name.length < 3 || data.team_name.length > 30) { errors.push("Team name must be 3-30 characters"); } // Parent name validation if (!data.parent_name || data.parent_name.length < 2 || data.parent_name.length > 100) { errors.push("Parent name must be 2-100 characters"); } // Contact number validation if (!data.contact_number || !/^[6-9]\d{9}$/.test(data.contact_number)) { errors.push("Contact number must be a valid 10-digit Indian mobile number"); } // Members validation if (!Array.isArray(data.members) || data.members.length === 0 || data.members.length > 4) { errors.push("Team must have 1-4 members"); } data.members?.forEach((member: any, index: number) => { if (!member.name || member.name.length < 2 || member.name.length > 50) { errors.push(`Member ${index + 1}: Name must be 2-50 characters`); } if (!member.age || member.age < 6 || member.age > 18) { errors.push(`Member ${index + 1}: Age must be between 6-18 years`); } }); return { valid: errors.length === 0, errors }; }

Integration Requirements

Existing System Integration Points

1. Database Layer

  • Extend existing database.ts with quiz-specific functions
  • Reuse SQLite connection and error handling patterns
  • Maintain transaction consistency with existing tables

2. Translation System

  • Extend shared/translations.ts with quiz-specific keys
  • Reuse existing language switching functionality
  • Maintain consistency with existing translation patterns

3. Admin Interface

  • Extend existing admin-template.ts with quiz tab
  • Reuse admin authentication mechanism
  • Maintain consistent UI patterns and styling

4. Form Handling

  • Extend existing form validation patterns
  • Reuse error handling and response formatting
  • Maintain consistent user feedback mechanisms

New Translation Keys

// Addition to existing TranslationKeys interface interface QuizTranslationKeys extends TranslationKeys { // Quiz-specific keys quizRegistration: string; bharatEkKhoj: string; teamName: string; teamMembers: string; memberName: string; memberAge: string; addMember: string; removeMember: string; suggestTeamName: string; teamNameTaken: string; minTeamSize: string; maxTeamSize: string; ageRange: string; generateSuggestions: string; usingSuggestion: string; quizTab: string; totalTeams: string; averageTeamSize: string; teamComposition: string; }

OpenAI Integration

API Configuration

import { OpenAI } from "https://esm.town/v/std/openai"; export async function generateTeamNameSuggestions( language: 'english' | 'hindi' | 'gujarati', userContext?: string ): Promise<string[]> { const openai = new OpenAI(); const prompts = { english: `Generate 5 culturally appropriate team names for an Indian cultural knowledge quiz competition called "Bharat Ek Khoj" (Discovery of India). Names should be inspiring, easy to remember, and reflect Indian heritage. Format as a simple list.`, hindi: `"भारत एक खोज" सांस्कृतिक प्रतियोगिता के लिए 5 उपयुक्त टीम नाम सुझाएं। नाम प्रेरणादायक, याद रखने योग्य और भारतीय संस्कृति को दर्शाने वाले होने चाहिए। सूची के रूप में दें।`, gujarati: `"ભારત એક ખોજ" સાંસ્કૃતિક સ્પર્ધા માટે 5 યોગ્ય ટીમના નામ સૂચવો. નામો પ્રેરણાદાયક, યાદ રાખવા યોગ્ય અને ભારતીય સંસ્કૃતિને દર્શાવતા હોવા જોઈએ. યાદીના સ્વરૂપમાં આપો.` }; try { const completion = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [ { role: "user", content: prompts[language] + (userContext ? `\n\nAdditional context: ${userContext}` : '') } ], max_tokens: 150, temperature: 0.8 }); const response = completion.choices[0]?.message?.content || ""; // Parse response into array of suggestions return response .split('\n') .filter(line => line.trim()) .map(line => line.replace(/^\d+\.\s*/, '').trim()) .filter(name => name.length > 0) .slice(0, 5); } catch (error) { console.error("OpenAI API error:", error); return getFallbackSuggestions(language); } } function getFallbackSuggestions(language: string): string[] { const fallbacks = { english: ["Bharat Ratna", "Cultural Warriors", "Heritage Heroes", "Knowledge Seekers", "Tradition Keepers"], hindi: ["भारत रत्न", "संस्कृति वीर", "ज्ञान खोजी", "विरासत रक्षक", "राष्ट्र गौरव"], gujarati: ["ભારત રત્ન", "સંસ્કૃતિ વીર", "જ્ઞાન શોધક", "વિરાસત રક્ષક", "રાષ્ટ્ર ગૌરવ"] }; return fallbacks[language] || fallbacks.english; }

Rate Limiting and Error Handling

// Simple in-memory rate limiting (per session) const rateLimiter = new Map<string, { count: number; resetTime: number }>(); export function checkRateLimit(ip: string, maxRequests: number = 5, windowMs: number = 300000): boolean { const now = Date.now(); const userLimit = rateLimiter.get(ip); if (!userLimit || now > userLimit.resetTime) { rateLimiter.set(ip, { count: 1, resetTime: now + windowMs }); return true; } if (userLimit.count >= maxRequests) { return false; } userLimit.count++; return true; }

Performance Requirements

Response Time Targets

  • Quiz Registration Form Load: < 2 seconds
  • Form Submission Processing: < 1.5 seconds
  • AI Team Name Generation: < 5 seconds
  • Admin Interface Load: < 3 seconds with 100+ teams

Optimization Strategies

Database Optimization

// Efficient query patterns export async function getQuizTeamsSummary(): Promise<{ totalTeams: number; totalMembers: number; averageTeamSize: number; sizeDistribution: Record<number, number>; }> { // Single query to get all stats const result = await sqlite.execute(` SELECT COUNT(*) as total_teams, SUM(member_count) as total_members, AVG(member_count) as avg_team_size, member_count, COUNT(member_count) as count_by_size FROM quiz_teams GROUP BY member_count `); // Process results efficiently return processStatsResult(result); }

Frontend Optimization

  • Lazy load AI suggestions only when requested
  • Debounce team name validation checks
  • Progressive form validation (validate on blur, not on input)
  • Minimize re-renders with efficient state management

Caching Strategy

// Simple in-memory caching for admin data const adminCache = new Map<string, { data: any; expiry: number }>(); export function getCachedAdminData(key: string, ttlMs: number = 60000) { const cached = adminCache.get(key); if (cached && Date.now() < cached.expiry) { return cached.data; } adminCache.delete(key); return null; } export function setCachedAdminData(key: string, data: any, ttlMs: number = 60000) { adminCache.set(key, { data, expiry: Date.now() + ttlMs }); }

Security Requirements

Input Validation and Sanitization

export function sanitizeInput(input: string): string { return input .trim() .replace(/[<>]/g, '') // Remove potential HTML .replace(/['"]/g, '') // Remove quotes that could break SQL .substring(0, 200); // Limit length } export function validateTeamName(name: string): boolean { // Allow letters, numbers, spaces, and common punctuation in multiple languages const validPattern = /^[\w\s\u0900-\u097F\u0A80-\u0AFF\u0A00-\u0A7F\-\.,!]+$/; return validPattern.test(name) && name.length >= 3 && name.length <= 30; }

SQL Injection Prevention

  • Use parameterized queries exclusively
  • Validate all inputs before database operations
  • Escape special characters in dynamic queries

Admin Access Control

export function validateAdminAccess(request: Request): boolean { const url = new URL(request.url); const providedKey = url.searchParams.get("key"); const adminKey = Deno.env.get("ADMIN_KEY"); return providedKey === adminKey && adminKey !== null && adminKey.length > 0; }

Error Handling and Logging

Error Response Standards

export function createErrorResponse( error: string, statusCode: number = 400, details?: any ): Response { const errorResponse = { success: false, error, ...(details && { details }), timestamp: new Date().toISOString() }; console.error(`Quiz API Error [${statusCode}]:`, errorResponse); return new Response(JSON.stringify(errorResponse), { status: statusCode, headers: { 'Content-Type': 'application/json' } }); }

Logging Strategy

export function logQuizActivity( action: string, details: any, level: 'info' | 'warn' | 'error' = 'info' ) { const logEntry = { timestamp: new Date().toISOString(), component: 'quiz-system', action, details, level }; console[level]('Quiz Activity:', logEntry); }

Testing Requirements

Unit Test Coverage

  • Database operations (CRUD functions)
  • Validation functions
  • API endpoint handlers
  • Translation key resolution
  • Error handling scenarios

Integration Test Scenarios

  • Complete registration flow
  • Admin interface interactions
  • Multi-language form submission
  • AI service integration
  • Error recovery flows

Performance Test Requirements

  • Load testing with 100 concurrent registrations
  • Database performance with 1000+ teams
  • AI API response time under load
  • Memory usage monitoring

Deployment and Monitoring

Environment Variables

// Required environment variables interface QuizEnvironment { ADMIN_KEY: string; // Admin access key OPENAI_API_KEY?: string; // OpenAI API key (optional) QUIZ_ENABLED?: string; // Feature flag DEBUG_MODE?: string; // Debug logging }

Health Checks

export async function healthCheck(): Promise<{ status: 'healthy' | 'degraded' | 'unhealthy'; services: Record<string, 'up' | 'down'>; timestamp: string; }> { const checks = { database: await checkDatabase(), openai: await checkOpenAI(), quiz_system: 'up' as const }; const allUp = Object.values(checks).every(status => status === 'up'); const someUp = Object.values(checks).some(status => status === 'up'); return { status: allUp ? 'healthy' : someUp ? 'degraded' : 'unhealthy', services: checks, timestamp: new Date().toISOString() }; }

Monitoring Metrics

  • Registration success/failure rates
  • API response times
  • Database query performance
  • AI service usage and latency
  • Error frequency and types

Backup and Recovery

Data Backup Strategy

  • SQLite database automatic backups via Val Town platform
  • Export functionality for admin users
  • Regular data integrity checks

Recovery Procedures

  • Database corruption recovery
  • Service degradation handling
  • Roll-back procedures for problematic deployments

Document Version: 1.0
Last Updated: August 17, 2025
Technical Review: Required before implementation

FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
ExploreDocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareersBrandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.