Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions docs/features/51-middleware-stack-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# 51 — Middleware Stack Overview

**NEW document** — Express middleware chain, auth middleware, CORS, rate limiting, raw body parsing for webhooks

---

## Feature Summary

Zync's Express middleware stack handles authentication (Firebase JWT verification), CORS, request parsing, rate limiting, and webhook signature verification. Middleware is applied globally and per-route, with special raw body parsing for GitHub webhooks.

---

## Architecture Diagram

```
┌─────────────────── EXPRESS APP ─────────────────────────┐
│ │
│ Global Middleware (applied to all routes): │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 1. cors({ origin: FRONTEND_URL, credentials }) │ │
│ │ 2. express.json({ limit: '10mb }) │ │
│ │ 3. express.urlencoded({ extended: true }) │ │
│ │ 4. cookieParser() │ │
│ │ 5. helmet() (security headers) │ │
│ │ 6. rateLimit({ windowMs, max }) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ Per-Route Middleware: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ verifyToken (authMiddleware) │ │
│ │ → Verifies Firebase JWT from Authorization │ │
│ │ → Sets req.user = { uid, email, ... } │ │
│ │ → 401 if invalid/missing │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ verifyGithub │ │
│ │ → HMAC SHA-256 webhook verification │ │
│ │ → Uses WEBHOOK_SECRET │ │
│ │ → 401 if signature mismatch │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ requireDb (chatRoutes only) │ │
│ │ → Checks mongoose.connection.readyState === 1 │ │
│ │ → 503 if DB disconnected │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ multer (upload routes) │ │
│ │ → Parses multipart/form-data │ │
│ │ → Memory storage, 10MB limit │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ Webhook Raw Body Parsing: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ express.json({ verify: (req) => { │ │
│ │ return req.path.includes('/webhook'); │ │
│ │ }}) │ │
│ │ → Stores raw body on req.rawBody for HMAC │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
```

---

## Middleware Details

### authMiddleware (verifyToken)
**File:** `backend/middleware/authMiddleware.js`

```js
const admin = require('firebase-admin');

const verifyToken = async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = await admin.auth().verifyIdToken(token);
req.user = { uid: decoded.uid, email: decoded.email };
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
};
```

- **Firebase Admin SDK:** Verifies JWT against Firebase
- **req.user:** Sets `uid` and `email` for downstream handlers
- **Bearer token:** Extracted from `Authorization` header

### verifyGithub
**File:** `backend/middleware/verifyGithub.js`
- HMAC SHA-256 verification using `WEBHOOK_SECRET`
- Timing-safe comparison to prevent timing attacks
- Detailed in [22-github-webhook-handler.md](./22-github-webhook-handler.md)

### CORS Configuration
```js
const cors = require('cors');
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:3000',
credentials: true,
}));
```
- Single origin (not wildcard) for security
- Credentials enabled for cookies/auth

### Rate Limiting
```js
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window per IP
message: { error: 'Too many requests' },
});
app.use('/api/', limiter);
```
- Applied to all `/api/` routes
- 100 requests per 15 minutes per IP
- Webhook routes exempt (GitHub needs fast response)

---

## Error Paths

| Scenario | HTTP Status | Response |
|---|---|---|
| No Authorization header | 401 | `{ error: "Unauthorized" }` |
| Invalid JWT | 401 | `{ error: "Invalid token" }` |
| Expired JWT | 401 | `{ error: "Invalid token" }` |
| Rate limited | 429 | `{ error: "Too many requests" }` |
| DB disconnected (requireDb) | 503 | `{ error: "Database not available" }` |
| Webhook signature invalid | 401 | Unauthorized |

---

## Cross-References

- [08-firebase-auth-flow.md](./08-firebase-auth-flow.md) — Firebase JWT verification
- [22-github-webhook-handler.md](./22-github-webhook-handler.md) — HMAC verification
- [02-security-auth-architecture.md](./02-security-auth-architecture.md) — Security overview
- [50-socket-io-initialization.md](./50-socket-io-initialization.md) — Socket.IO setup
140 changes: 140 additions & 0 deletions docs/features/52-database-schema-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# 52 — Database Schema & Models

**NEW document** — Mongoose models overview, schema definitions, indexes, relationships, dual ORM strategy

---

## Feature Summary

Zync uses MongoDB with Mongoose as the primary ODM. All models are defined in `backend/models/` with explicit schemas, indexes, and validation. The database stores users, projects, tasks, steps, notes, folders, messages, teams, sessions, meetings, activities, and collaborators.

---

## Model Inventory

| Model | File | Collection | Purpose |
|---|---|---|---|
| User | `User.js` | users | User profiles, integrations, settings |
| Project | `Project.js` | projects | Project metadata, GitHub repo link |
| Step | `Step.js` | steps | Kanban pipeline stages |
| ProjectTask | `ProjectTask.js` | projecttasks | Tasks within project steps |
| Note | `Note.js` | notes | Rich text notes |
| Folder | `Folder.js` | folders | Note organization, sharing |
| Message | `Message.js` | messages | Chat messages |
| Team | `Team.js` | teams | Team groups, roles, invites |
| Activity | `Activity.js` | activities | Team activity logs |
| Session | `Session.js` | sessions | Work/meeting sessions |
| Meeting | `Meeting.js` | meetings | Google Meet meetings |
| Collaborator | `Collaborator.js` | collaborators | Beta applications |

---

## Key Schema Details

### User Model
```
{
uid: String (Firebase UID, primary key),
email: String (unique, indexed),
displayName: String,
photoURL: String,
bio: String,
location: { city, country, lat, lng, timezone, manual },
githubIntegration: { connected, accessToken (encrypted), username, installationId },
googleIntegration: { connected, accessToken (encrypted), refreshToken, expiryDate },
linkedinIntegration: { connected, profileUrl },
securityPin: String (hashed),
createdAt, updatedAt
}
```
**Indexes:** `email` (unique), `uid` (unique), `displayName` (text)

### Project Model
```
{
name: String,
description: String,
ownerUid: String (indexed),
team: [String] (member UIDs),
githubRepoId: Number,
githubRepoName: String,
githubRepoOwner: String,
githubDefaultBranch: String,
steps: [ObjectId] (ref: Step),
createdAt, updatedAt
}
```
**Indexes:** `ownerUid`, `team`

### Message Model
```
{
chatId: String (format: uidA_uidB, indexed),
senderId: String (indexed),
receiverId: String (indexed),
text: String,
type: String (text/image/file),
fileUrl, fileName, fileSize,
senderName, senderPhotoURL,
projectId, projectName, projectOwnerId,
delivered: Boolean,
deliveredAt: Date,
seen: Boolean,
seenAt: Date,
createdAt
}
```
**Indexes:** `{ chatId: 1, createdAt: 1 }`, `{ receiverId: 1, seen: 1 }`, `{ receiverId: 1, delivered: 1 }`

### Note Model
```
{
title: String,
content: String (rich text HTML),
ownerId: String (indexed),
folderId: ObjectId (ref: Folder),
projectId: ObjectId (ref: Project),
sharedWith: [String],
isPublic: Boolean,
createdAt, updatedAt
}
```

### Team Model
```
{
name: String,
ownerUid: String (indexed),
admins: [String],
members: [String],
pendingMembers: [String],
inviteCode: String (unique),
type: String,
pin: String (hashed),
createdAt
}
```

---

## Index Strategy

### Compound Indexes
- Message: `{ chatId: 1, createdAt: 1 }` — efficient history queries
- Message: `{ receiverId: 1, seen: 1 }` — unread count queries
- Message: `{ receiverId: 1, delivered: 1 }` — delivery catch-up queries

### Text Indexes
- User: `{ displayName: 'text', email: 'text' }` — user search

### Unique Indexes
- User: `email`, `uid`
- Team: `inviteCode`

---

## Cross-References

- [03-performance-caching-strategy.md](./03-performance-caching-strategy.md) — Redis caching on top of MongoDB
- [05-database-schema-and-models.md](./05-database-schema-and-models.md) — Original schema doc
- All feature docs reference their respective models
Loading
Loading