Restore README
This commit is contained in:
348
README.md
348
README.md
@@ -1 +1,347 @@
|
|||||||
# P2P Poll App
|
# P2P Survey App
|
||||||
|
|
||||||
|
A serverless peer-to-peer survey application built with Ionic and Angular.
|
||||||
|
Survey creators store all data locally in their browser and participants connect
|
||||||
|
directly via WebRTC — no backend server required.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Architecture](#architecture)
|
||||||
|
3. [Features](#features)
|
||||||
|
4. [Setup & Installation](#setup--installation)
|
||||||
|
5. [How It Works](#how-it-works)
|
||||||
|
- [Creator Flow](#creator-flow)
|
||||||
|
- [Participant Flow](#participant-flow)
|
||||||
|
6. [Question Types](#question-types)
|
||||||
|
7. [Technology Stack](#technology-stack)
|
||||||
|
8. [Limitations](#limitations)
|
||||||
|
9. [Self-Hosting the PeerJS Signaling Server](#self-hosting-the-peerjs-signaling-server)
|
||||||
|
10. [Development Commands](#development-commands)
|
||||||
|
11. [Technology Choices](#technology-choices)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
P2P Survey App lets you create and distribute surveys without any server-side
|
||||||
|
infrastructure. All survey data — questions, participant tokens, and responses —
|
||||||
|
lives exclusively in the survey creator's browser (IndexedDB). Participants
|
||||||
|
connect directly to the creator's browser using WebRTC peer-to-peer data
|
||||||
|
channels, facilitated by the PeerJS library.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Creator Browser (Host)
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ Ionic/Angular App │
|
||||||
|
│ ┌─────────────────┐ ┌──────────────────┐ │
|
||||||
|
│ │ PeerService │ │ Dexie (IndexedDB)│ │
|
||||||
|
│ │ (PeerJS/WebRTC)│<─>│ surveys │ │
|
||||||
|
│ └────────┬────────┘ │ participants │ │
|
||||||
|
│ │ │ responses │ │
|
||||||
|
│ Peer ID: survey-{id}└──────────────────┘ │
|
||||||
|
└───────────┼─────────────────────────────────┘
|
||||||
|
│
|
||||||
|
[PeerJS Cloud – signaling only, not data relay]
|
||||||
|
│
|
||||||
|
Participant Browser
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ Opens: /participate?host=survey-{id} │
|
||||||
|
│ &token={uuid} │
|
||||||
|
│ │
|
||||||
|
│ 1. Connect to host peer via WebRTC │
|
||||||
|
│ 2. Send { type: 'join', token } │
|
||||||
|
│ 3. Receive survey questions │
|
||||||
|
│ 4. Fill in answers │
|
||||||
|
│ 5. Send { type: 'submit', answers } │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Star topology
|
||||||
|
|
||||||
|
The survey creator acts as the central hub. Each participant opens a direct
|
||||||
|
WebRTC data channel to the creator's browser. The creator's browser validates
|
||||||
|
tokens, stores responses, and optionally pushes aggregated results back.
|
||||||
|
|
||||||
|
The PeerJS signaling server is only used for the initial WebRTC handshake
|
||||||
|
(exchanging ICE candidates). Once connected, all data flows directly between
|
||||||
|
the two browsers — the signaling server never sees response data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Create surveys** with four question types: free text, multiple choice,
|
||||||
|
yes/no, and 1–5 rating
|
||||||
|
- **Generate unique participant links** — each link contains a UUID token that
|
||||||
|
identifies one participant
|
||||||
|
- **Token-based access control** — each token can only be submitted once;
|
||||||
|
reuse is rejected by the host
|
||||||
|
- **Draft saving** — participants can save answers before final submission
|
||||||
|
without locking their token
|
||||||
|
- **Live results** — the creator sees responses update in real time using
|
||||||
|
Dexie's `liveQuery`
|
||||||
|
- **Optional results sharing** — the creator can toggle whether participants
|
||||||
|
see aggregated results after submitting
|
||||||
|
- **CSV export** — download all responses as a comma-separated file
|
||||||
|
- **Local-only storage** — all data lives in the creator's IndexedDB; delete
|
||||||
|
the survey and the data is gone
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup & Installation
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Node.js 18+ and npm 9+
|
||||||
|
- A modern browser (Chrome, Firefox, Edge, Safari)
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install Ionic CLI globally (skip if already installed)
|
||||||
|
npm install -g @ionic/cli
|
||||||
|
|
||||||
|
# 2. Install project dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 3. Start the development server
|
||||||
|
ionic serve
|
||||||
|
```
|
||||||
|
|
||||||
|
The app opens at `http://localhost:8100`.
|
||||||
|
|
||||||
|
### Build for production
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ionic build --prod
|
||||||
|
```
|
||||||
|
|
||||||
|
Output is written to `www/`. Deploy the contents of `www/` to any static
|
||||||
|
web host (GitHub Pages, Netlify, Vercel, Nginx, etc.).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Creator Flow
|
||||||
|
|
||||||
|
1. **Open the app** and click **Create Survey**.
|
||||||
|
2. **Enter a title** (required) and optional description.
|
||||||
|
3. **Add questions** — choose a type for each and mark which are required.
|
||||||
|
4. Click **Create** to save the survey to IndexedDB and navigate to the
|
||||||
|
**Survey Detail** page.
|
||||||
|
5. On the detail page:
|
||||||
|
- Toggle **Show results to participants** if desired.
|
||||||
|
- Enter a number and click **Generate Links** to create unique participant
|
||||||
|
tokens. Copy links to share.
|
||||||
|
- Click **Start Hosting** to initialise the PeerJS peer. The app listens
|
||||||
|
for incoming connections using the peer ID `survey-{surveyId}`.
|
||||||
|
6. **Keep this page or the Results page open** while collecting responses.
|
||||||
|
If the creator navigates away, the peer is destroyed and participants
|
||||||
|
cannot connect.
|
||||||
|
7. Click **View Results** to see live aggregated and individual responses.
|
||||||
|
8. Click **Export CSV** to download all responses.
|
||||||
|
|
||||||
|
### Participant Flow
|
||||||
|
|
||||||
|
1. **Open the unique link** shared by the survey creator.
|
||||||
|
2. The app parses `host` and `token` from the URL query parameters.
|
||||||
|
3. A WebRTC connection is established to the host's peer ID.
|
||||||
|
4. The participant sends `{ type: 'join', token }`.
|
||||||
|
5. The host validates the token:
|
||||||
|
- **Invalid token** → error card is shown.
|
||||||
|
- **Already submitted** → error card is shown (token is locked).
|
||||||
|
- **Valid** → survey questions are sent back.
|
||||||
|
6. Participant fills in answers. Answers are auto-saved as drafts after each
|
||||||
|
blur event (free text) or selection change (other types).
|
||||||
|
7. Click **Submit** to finalise. The token is locked on the host side.
|
||||||
|
8. If the creator enabled result sharing, aggregated results are shown.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Question Types
|
||||||
|
|
||||||
|
| Type | Input widget | Aggregation |
|
||||||
|
|---|---|---|
|
||||||
|
| **Free Text** | Multi-line textarea | List of all answers |
|
||||||
|
| **Multiple Choice** | Radio buttons (creator-defined options) | Count per option + bar chart |
|
||||||
|
| **Yes / No** | Radio buttons (Yes / No) | Count per option + bar chart |
|
||||||
|
| **Rating** | 5 buttons (1–5) | Average score + distribution bar chart |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
| Library | Version | Role |
|
||||||
|
|---|---|---|
|
||||||
|
| `@ionic/angular` | 8.x | Mobile-first UI components |
|
||||||
|
| `@angular/core` | 20.x | Application framework |
|
||||||
|
| `peerjs` | 1.5.x | WebRTC data channel wrapper |
|
||||||
|
| `dexie` | 4.x | IndexedDB wrapper with liveQuery |
|
||||||
|
| `qrcode` | 1.5.x | QR code generation (optional usage) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
### Host must be online
|
||||||
|
|
||||||
|
The survey creator's browser **must be open** and on the Survey Detail or
|
||||||
|
Results page for participants to submit responses. There is no server that
|
||||||
|
relays or buffers messages. If the creator goes offline, participants see a
|
||||||
|
"Host is Offline" card.
|
||||||
|
|
||||||
|
### Data loss
|
||||||
|
|
||||||
|
All data is stored in the creator's browser IndexedDB. Data is lost if:
|
||||||
|
- The creator explicitly deletes the survey
|
||||||
|
- The browser's storage is cleared (private/incognito mode, clearing site data)
|
||||||
|
- The browser's IndexedDB quota is exceeded
|
||||||
|
|
||||||
|
Export responses to CSV regularly to prevent data loss.
|
||||||
|
|
||||||
|
### PeerJS Cloud rate limits
|
||||||
|
|
||||||
|
The free PeerJS Cloud signaling server (`0.peerjs.com`) has rate limits and
|
||||||
|
may occasionally be unavailable. For production use, self-host the signaling
|
||||||
|
server (see below).
|
||||||
|
|
||||||
|
### NAT traversal
|
||||||
|
|
||||||
|
WebRTC uses STUN to traverse most NAT configurations. Strict corporate
|
||||||
|
firewalls or symmetric NAT may block direct connections. For full reliability
|
||||||
|
in such environments, add a TURN server to the PeerJS configuration in
|
||||||
|
`src/app/services/peer.service.ts`.
|
||||||
|
|
||||||
|
### Browser compatibility
|
||||||
|
|
||||||
|
Requires a browser that supports:
|
||||||
|
- WebRTC (DataChannels)
|
||||||
|
- IndexedDB
|
||||||
|
- `crypto.randomUUID()`
|
||||||
|
|
||||||
|
All modern browsers (Chrome 86+, Firefox 78+, Safari 15.4+, Edge 86+) satisfy
|
||||||
|
these requirements.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Hosting the PeerJS Signaling Server
|
||||||
|
|
||||||
|
For production deployments, run your own PeerJS server to avoid rate limits.
|
||||||
|
|
||||||
|
### Option 1: npm
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g peer
|
||||||
|
peerjs --port 9000 --key peerjs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -p 9000:9000 peerjs/peerjs-server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Node.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// server.js
|
||||||
|
const { PeerServer } = require('peer');
|
||||||
|
const server = PeerServer({ port: 9000, path: '/peerjs' });
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the app to use your server
|
||||||
|
|
||||||
|
Edit `src/app/services/peer.service.ts` and update the `Peer` constructor:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
this.peer = new Peer(peerId, {
|
||||||
|
host: 'your-peer-server.example.com',
|
||||||
|
port: 9000,
|
||||||
|
path: '/peerjs',
|
||||||
|
secure: true, // use wss:// if your server has TLS
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start dev server with live reload
|
||||||
|
ionic serve
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
ionic build --prod
|
||||||
|
|
||||||
|
# Run TypeScript type check
|
||||||
|
npx tsc --noEmit
|
||||||
|
|
||||||
|
# Lint
|
||||||
|
npx ng lint
|
||||||
|
|
||||||
|
# Build Angular app only (no Ionic wrapper)
|
||||||
|
npx ng build
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technology Choices
|
||||||
|
|
||||||
|
### PeerJS over Gun.js
|
||||||
|
|
||||||
|
Gun.js is a decentralized graph database that syncs between peers. However,
|
||||||
|
a survey has a **star topology** (one host, many participants) — not a mesh
|
||||||
|
where every peer is equal. PeerJS maps directly to this model: the creator
|
||||||
|
opens one peer ID, participants connect to it, and all data flows through
|
||||||
|
that central point. Token validation and response deduplication are trivial
|
||||||
|
to enforce at the host. Gun.js would require additional complexity to achieve
|
||||||
|
the same guarantees.
|
||||||
|
|
||||||
|
### Dexie.js over raw IndexedDB
|
||||||
|
|
||||||
|
Raw IndexedDB uses a callback-based API that is verbose and error-prone.
|
||||||
|
Dexie wraps it in clean Promises, adds a TypeScript-friendly schema
|
||||||
|
definition, and provides `liveQuery` — a reactive subscription mechanism
|
||||||
|
that automatically re-runs queries when the underlying data changes. This
|
||||||
|
powers the live results view without any manual polling or event wiring.
|
||||||
|
|
||||||
|
### Module-based Angular over Standalone Components
|
||||||
|
|
||||||
|
The Ionic CLI scaffolds a module-based project by default with Angular 20.
|
||||||
|
Module-based components provide a clear separation of concern via `NgModule`
|
||||||
|
declarations and are well-supported by the Ionic ecosystem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
└── app/
|
||||||
|
├── shared/
|
||||||
|
│ └── models/
|
||||||
|
│ └── survey.models.ts # All TypeScript interfaces + P2PMessage type
|
||||||
|
├── database/
|
||||||
|
│ └── database.ts # Dexie singleton (AppDatabase)
|
||||||
|
├── services/
|
||||||
|
│ ├── peer.service.ts # PeerJS wrapper (NgZone-aware)
|
||||||
|
│ ├── survey.service.ts # Survey/participant CRUD
|
||||||
|
│ └── response.service.ts # Response storage + aggregation
|
||||||
|
├── pages/
|
||||||
|
│ ├── home/ # Survey list
|
||||||
|
│ ├── create-survey/ # Survey creation/editing
|
||||||
|
│ ├── survey-detail/ # Settings, link generation, hosting
|
||||||
|
│ ├── survey-results/ # Live results view
|
||||||
|
│ └── participate/ # Participant survey form
|
||||||
|
├── app-routing.module.ts
|
||||||
|
├── app.component.ts
|
||||||
|
└── app.module.ts
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user