Contributing
This guide covers development setup, code conventions, and workflow for contributing to StreamGate.
Development Setup
Prerequisites
- Node.js 18+ (LTS recommended)
- npm 9+ (comes with Node.js)
- Git
First-Time Setup
# 1. Clone the repository
git clone <repository-url>
cd VideoPlayer
# 2. Install all dependencies (npm workspaces)
npm install
# 3. Set up the Platform App database
cd platform
cp .env.example .env # Create environment file
npx prisma migrate dev # Initialize SQLite + apply migrations
# 4. Start the Platform App
npm run dev # → http://localhost:3000
# 5. Start the HLS Server (new terminal)
cd hls-server
npm run dev # → http://localhost:4000
Verify the Setup
- Open
http://localhost:3000— you should see the Viewer Portal - Open
http://localhost:3000/admin— you should see the Admin login - Open
http://localhost:4000/health— you should see{ "status": "ok", ... }
Code Conventions
TypeScript Strict Mode
All code uses TypeScript with strict mode enabled. Ensure:
- No
anytypes without explicit justification - All function parameters and return types are typed
- Use types from
@streaming/sharedfor cross-service contracts
API Route Patterns
Next.js App Router API routes follow this structure:
// platform/src/app/api/example/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
try {
// 1. Rate limiting (if applicable)
// 2. Input validation
// 3. Business logic
// 4. Return success response
return NextResponse.json({ data: result });
} catch (error) {
// 5. Return error response
return NextResponse.json(
{ error: 'Something went wrong' },
{ status: 500 }
);
}
}
Conventions:
- Return
{ data: T }on success,{ error: string }on failure - Use semantic HTTP status codes (see API Reference)
- Validate all input at the boundary
- Use vague error messages for security-sensitive endpoints
Component Patterns
React components follow these conventions:
- shadcn/ui components live in
platform/src/components/ui/ - Custom components are organized by domain:
player/,admin/,viewer/ - Use Tailwind CSS for styling
- Use Lucide for icons
- Use Framer Motion for animations (200–300ms ease-out for controls, 100ms for hover states)
// Example component structure
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Play, Pause } from 'lucide-react';
import { motion } from 'framer-motion';
interface PlayPauseButtonProps {
isPlaying: boolean;
onToggle: () => void;
}
export function PlayPauseButton({ isPlaying, onToggle }: PlayPauseButtonProps) {
return (
<motion.div whileHover={{ scale: 1.1 }} transition={{ duration: 0.1 }}>
<Button onClick={onToggle} variant="ghost" size="icon">
{isPlaying ? <Pause /> : <Play />}
</Button>
</motion.div>
);
}
JWT Patterns
- Use the
joselibrary for all JWT operations - Platform App:
mintPlaybackToken()andverifyPlaybackToken()inplatform/src/lib/jwt.ts - HLS Server:
JwtVerifierclass inhls-server/src/services/jwt-verifier.ts - Shared: Use types and constants from
@streaming/shared - Never store raw access codes in browser memory after initial validation
Shared Library Changes
When modifying shared/src/:
- Export new symbols from
shared/src/index.ts - Run
cd shared && npm run typecheckto validate - Both services pick up changes immediately (no build step)
Testing
Running Tests
# Run all tests for a service
cd platform && npm test
cd hls-server && npm test
# Run tests in watch mode
npm test -- --watch
# Run a specific test file
npm test -- path/to/test.test.ts
Linting
# Run ESLint + Prettier checks
cd platform && npm run lint
cd hls-server && npm run lint
Pre-Commit Checklist
Before committing, ensure:
-
npm testpasses in bothplatform/andhls-server/ -
npm run lintpasses in both services -
cd shared && npm run typecheckpasses - No
console.logstatements left in production code - No hardcoded secrets or credentials
- New environment variables are documented
Project Structure Overview
VideoPlayer/
├── platform/ # Next.js Platform App
│ ├── prisma/ # Database schema + migrations
│ └── src/
│ ├── app/ # Pages + API routes (App Router)
│ ├── components/ # React components (ui, player, admin, viewer)
│ ├── hooks/ # Custom React hooks
│ └── lib/ # Utility modules
├── hls-server/ # Express HLS Media Server
│ └── src/
│ ├── middleware/ # Express middleware
│ ├── routes/ # Route handlers
│ ├── services/ # Business logic services
│ └── utils/ # Utility functions
├── shared/ # Shared TypeScript library
│ └── src/
│ ├── types.ts # Type definitions
│ ├── constants.ts # Constants (from PDR)
│ ├── jwt.ts # JWT utilities
│ └── validation.ts # Input validation
├── docs/ # Docusaurus documentation site
├── scripts/ # Build/deployment scripts
├── docker-compose.yml # Docker Compose config
├── package.json # Root workspace config
└── tsconfig.base.json # Shared TypeScript config
PR Guidelines
PR Structure
- Keep PRs focused on a single feature or fix
- Include a clear description of what changed and why
- Reference related issues or tasks in the description
- Include test changes alongside code changes
Review Checklist
Reviewers should verify:
- Code follows TypeScript strict mode conventions
- API responses use consistent
{ data }/{ error }shape - Security-sensitive endpoints use vague error messages
- Rate limiting is applied where required
- Shared types are used for cross-service contracts
- No secrets or credentials in the code
- Database schema changes include a migration
Security Non-Negotiables
These rules must never be violated, regardless of the feature:
- Never expose raw
.m3u8URLs without JWT protection - One token = one active viewer at a time (single-device enforcement)
- Rate-limit all public endpoints: token validation (5/min/IP), JWT refresh (12/hr/token), admin login (10/min/IP)
- Admin password stored as bcrypt hash — never plaintext
- All token codes alphanumeric only — reject non-alphanumeric input server-side
- HLS server error responses must be vague — no internal state leakage
- Never store raw access codes in browser after initial validation (use JWT
subclaim) - Token codes generated with
crypto.randomBytes()— neverMath.random() - Strip
__tokenquery parameter from all logs to prevent JWT leakage - HTTPS required in production — JWTs in plaintext headers are unacceptable
danger
If a PR violates any of these non-negotiables, it must be rejected regardless of other merits. These rules exist to protect viewer access and prevent unauthorized stream access.