Update docs

This commit is contained in:
MartinBraquet
2026-03-01 05:35:46 +01:00
parent e29bc0ab82
commit 7037362b40
8 changed files with 1620 additions and 162 deletions

View File

@@ -520,7 +520,7 @@ What testing is not
How we apply it here
- Unit and integration tests live in each package and run with Jest (see `jest.config.js`).
- Unit and integration tests live in each package and run with Jest (see `jest.config.ts`).
- Critical user journeys are covered by Playwright E2E tests under `tests/e2e` (see `playwright.config.ts`).
### Test types at a glance
@@ -528,18 +528,20 @@ How we apply it here
This project uses three complementary test types. Use the right level for the job:
- Unit tests
- Purpose: Verify a single function/module in isolation; fast, deterministic.
- Where: Each package under `tests/unit` (e.g., `backend/api/tests/unit`, `web/tests/unit`, `common/tests/unit`,
etc.).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.unit.test.ts` (or `.tsx` for React in `web`).
- When to use: Pure logic, utilities, hooks, reducers, small components with mocked dependencies.
- Integration tests
- Purpose: Verify multiple units working together (e.g., function + DB/client, component + context/provider) without
spinning up the full app.
- Where: Each package under `tests/integration` (e.g., `backend/shared/tests/integration`, `web/tests/integration`).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.integration.test.ts` (or `.tsx` for React in `web`).
- When to use: Boundaries between modules, real serialization/parsing, API handlers with mocked network/DB,
component trees with providers.
@@ -565,7 +567,7 @@ yarn test:e2e
```filetree
# Config
jest.config.js (for unit and integration tests)
jest.config.ts (for unit and integration tests)
playwright.config.ts (for e2e tests)
# Top-level End-to-End (Playwright)
@@ -617,7 +619,7 @@ web/
- End-to-End tests live under `tests/e2e` and are executed by Playwright. The root `playwright.config.ts` sets `testDir`
to `./tests/e2e`.
- Unit and integration tests live in each packages `tests` folder and are executed by Jest via the root
`jest.config.js` projects array.
`jest.config.ts` projects array.
- Naming:
- Unit: `*.unit.test.ts` (or `.tsx` for React in `web`)
- Integration: `*.integration.test.ts`

View File

@@ -514,7 +514,7 @@ What testing is not
How we apply it here
- Unit and integration tests live in each package and run with Jest (see `jest.config.js`).
- Unit and integration tests live in each package and run with Jest (see `jest.config.ts`).
- Critical user journeys are covered by Playwright E2E tests under `tests/e2e` (see `playwright.config.ts`).
### Test types at a glance
@@ -522,18 +522,20 @@ How we apply it here
This project uses three complementary test types. Use the right level for the job:
- Unit tests
- Purpose: Verify a single function/module in isolation; fast, deterministic.
- Where: Each package under `tests/unit` (e.g., `backend/api/tests/unit`, `web/tests/unit`, `common/tests/unit`,
etc.).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.unit.test.ts` (or `.tsx` for React in `web`).
- When to use: Pure logic, utilities, hooks, reducers, small components with mocked dependencies.
- Integration tests
- Purpose: Verify multiple units working together (e.g., function + DB/client, component + context/provider) without
spinning up the full app.
- Where: Each package under `tests/integration` (e.g., `backend/shared/tests/integration`, `web/tests/integration`).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.integration.test.ts` (or `.tsx` for React in `web`).
- When to use: Boundaries between modules, real serialization/parsing, API handlers with mocked network/DB,
component trees with providers.
@@ -559,7 +561,7 @@ yarn test:e2e
```filetree
# Config
jest.config.js (for unit and integration tests)
jest.config.ts (for unit and integration tests)
playwright.config.ts (for e2e tests)
# Top-level End-to-End (Playwright)
@@ -611,7 +613,7 @@ web/
- End-to-End tests live under `tests/e2e` and are executed by Playwright. The root `playwright.config.ts` sets `testDir`
to `./tests/e2e`.
- Unit and integration tests live in each packages `tests` folder and are executed by Jest via the root
`jest.config.js` projects array.
`jest.config.ts` projects array.
- Naming:
- Unit: `*.unit.test.ts` (or `.tsx` for React in `web`)
- Integration: `*.integration.test.ts`

View File

@@ -514,7 +514,7 @@ What testing is not
How we apply it here
- Unit and integration tests live in each package and run with Jest (see `jest.config.js`).
- Unit and integration tests live in each package and run with Jest (see `jest.config.ts`).
- Critical user journeys are covered by Playwright E2E tests under `tests/e2e` (see `playwright.config.ts`).
### Test types at a glance
@@ -522,18 +522,20 @@ How we apply it here
This project uses three complementary test types. Use the right level for the job:
- Unit tests
- Purpose: Verify a single function/module in isolation; fast, deterministic.
- Where: Each package under `tests/unit` (e.g., `backend/api/tests/unit`, `web/tests/unit`, `common/tests/unit`,
etc.).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.unit.test.ts` (or `.tsx` for React in `web`).
- When to use: Pure logic, utilities, hooks, reducers, small components with mocked dependencies.
- Integration tests
- Purpose: Verify multiple units working together (e.g., function + DB/client, component + context/provider) without
spinning up the full app.
- Where: Each package under `tests/integration` (e.g., `backend/shared/tests/integration`, `web/tests/integration`).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.integration.test.ts` (or `.tsx` for React in `web`).
- When to use: Boundaries between modules, real serialization/parsing, API handlers with mocked network/DB,
component trees with providers.
@@ -559,7 +561,7 @@ yarn test:e2e
```filetree
# Config
jest.config.js (for unit and integration tests)
jest.config.ts (for unit and integration tests)
playwright.config.ts (for e2e tests)
# Top-level End-to-End (Playwright)
@@ -611,7 +613,7 @@ web/
- End-to-End tests live under `tests/e2e` and are executed by Playwright. The root `playwright.config.ts` sets `testDir`
to `./tests/e2e`.
- Unit and integration tests live in each packages `tests` folder and are executed by Jest via the root
`jest.config.js` projects array.
`jest.config.ts` projects array.
- Naming:
- Unit: `*.unit.test.ts` (or `.tsx` for React in `web`)
- Integration: `*.integration.test.ts`

View File

@@ -1,128 +1,491 @@
# Contributing to This Repository
# Contributing to Compass
We welcome pull requests, but only if they meet the project's quality and design standards. Follow the process below precisely to avoid wasting time—yours or ours.
Thank you for your interest in contributing to Compass! This document provides comprehensive guidelines for contributing to this open-source project.
## Prerequisites
## Table of Contents
- Familiarity with Git and GitHub (basic commands, branching, forking, etc.)
- A functioning development environment
- Node.js, Python, or other relevant runtime/tools installed (check the `README.md`)
- Read the [Development Documentation](docs/development.md) for project-specific setup and guidelines (adding languages,
profile fields, etc.)
- [Code of Conduct](#code-of-conduct)
- [Getting Started](#getting-started)
- [Development Environment](#development-environment)
- [Project Structure](#project-structure)
- [Coding Standards](#coding-standards)
- [Making Changes](#making-changes)
- [Testing](#testing)
- [Pull Request Guidelines](#pull-request-guidelines)
- [Commit Message Guidelines](#commit-message-guidelines)
- [Documentation](#documentation)
- [Questions and Support](#questions-and-support)
## Fork & Clone
## Code of Conduct
1. **Fork the repository** using the GitHub UI.
2. **Clone your fork** locally:
Please read and follow our [Code of Conduct](./CODE_OF_CONDUCT.md). We are committed to providing a welcoming and inclusive environment for all contributors.
## Getting Started
### Prerequisites
Before contributing, ensure you have the following installed:
- **Node.js** 20.x or later
- **Yarn** 1.x (classic)
- **Git**
- **Docker** (optional, for isolated development)
### Fork and Clone
1. Fork the [repository](https://github.com/CompassConnections/Compass) on GitHub
2. Clone your fork:
```bash
git clone https://github.com/your-username/Compass.git
cd your-fork
git clone https://github.com/<your-username>/Compass.git
cd Compass
```
3. **Add the upstream remote**:
3. Add the upstream remote:
```bash
git remote add upstream https://github.com/CompassConnections/Compass.git
```
## Create a New Branch
Never work on `main` or `master`.
### Install Dependencies
```bash
git checkout -b fix/brief-but-specific-description
yarn install --frozen-lockfile
```
Use a clear, descriptive branch name. Avoid vague names like `patch-1`.
## Development Environment
## Stay Updated
Before you start, make sure your fork is up to date:
### Running the Development Server
```bash
git fetch upstream
git checkout main
git merge upstream/main
yarn dev
```
Then rebase your feature branch if needed:
Visit http://localhost:3000 to see the application.
### Isolated Development (Recommended)
For full isolation with local Supabase and Firebase emulators:
```bash
git checkout fix/your-feature
git rebase main
yarn dev:isolated
```
## Make Atomic Commits
Benefits:
Each commit should represent a single logical change. Follow this format:
- No conflicts with other contributors
- Works offline
- Faster database queries
- Free to reset and reseed data
```text
type(scope): concise description
Requirements:
body explaining what and why, if necessary
- Docker (~500MB)
- Supabase CLI
- Java 21+ (for Firebase emulators)
- Firebase CLI
See the [README](./README.md) for detailed setup instructions.
### Running Tests
```bash
# Run all tests
yarn test
# Run tests with coverage
yarn test:coverage
# Run tests in watch mode
yarn test:watch
# Run E2E tests
yarn test:e2e
```
### Linting and Type Checking
```bash
# Lint all packages
yarn lint
# Fix linting issues
yarn lint-fix
# Type check all packages
yarn typecheck
```
## Project Structure
This is a Yarn workspaces monorepo with the following packages:
```
Compass/
├── web/ # Next.js web application
│ ├── components/ # React components
│ ├── hooks/ # Custom React hooks
│ ├── lib/ # Utilities and services
│ ├── pages/ # Next.js pages
│ └── messages/ # Internationalization files
├── backend/
│ ├── api/ # Express API server
│ ├── shared/ # Shared backend utilities
│ ├── email/ # React email templates
│ └── scripts/ # Database migration scripts
├── common/ # Shared TypeScript types and utilities
├── supabase/ # Database migrations and config
├── android/ # Capacitor Android app
└── docs/ # Project documentation
```
### Key Technologies
| Layer | Technology |
| -------- | -------------------------------- |
| Frontend | Next.js 14, React 18, TypeScript |
| Styling | Tailwind CSS |
| Backend | Express.js, Node.js |
| Database | PostgreSQL (Supabase) |
| Auth | Firebase Auth |
| Storage | Firebase Storage |
| Mobile | Capacitor (Android) |
| Testing | Jest, Playwright |
## Coding Standards
### TypeScript
- Use strict TypeScript typing
- Avoid `any` type; use `unknown` when necessary
- Prefer interfaces over types for object shapes
- Use `const` assertions where appropriate
### React Components
- Use functional components with hooks
- Name components after their file name
- Export primary component at the top of the file
- Use composition over inheritance
- Keep components small and focused
Example component structure:
```tsx
import clsx from 'clsx'
import {useState} from 'react'
interface ProfileCardProps {
name: string
age: number
onSelect?: (id: string) => void
className?: string
}
export function ProfileCard({name, age, onSelect, className}: ProfileCardProps) {
const [selected, setSelected] = useState(false)
const handleClick = () => {
setSelected(!selected)
onSelect?.(name)
}
return (
<div className={clsx('card', selected && 'selected', className)}>
<h3>
{name}, {age}
</h3>
<button onClick={handleClick}>Select</button>
</div>
)
}
```
### Naming Conventions
- **Files**: kebab-case (`profile-card.tsx`)
- **Components**: PascalCase (`ProfileCard`)
- **Hooks**: camelCase with `use` prefix (`useUserProfile`)
- **Constants**: SCREAMING_SNAKE_CASE
- **Types/Interfaces**: PascalCase
### Import Order
Run `yarn lint-fix` to automatically sort imports:
1. External libraries (React, Next.js, etc.)
2. Internal packages (`common/`, `shared/`)
3. Relative imports (`../`, `./`)
4. Type imports
### Error Handling
- Use try-catch for async operations
- Create custom error types for API errors
- Implement error boundaries for React components
- Log errors with appropriate context
Example:
```typescript
import {APIError} from './errors'
try {
const result = await api('endpoint', params)
return result
} catch (err) {
if (err instanceof APIError) {
logger.error('API error', {status: err.status, message: err.message})
} else {
logger.error('Unexpected error', err)
}
throw err
}
```
### Accessibility
- Use semantic HTML elements
- Include ARIA labels where appropriate
- Ensure keyboard navigation works
- Use the `SkipLink` component for main content
- Announce dynamic content changes with `useLiveRegion`
```tsx
import {useLiveRegion} from 'web/components/live-region'
function MyComponent() {
const {announce} = useLiveRegion()
const handleAction = () => {
// Action completed
announce('Action successful', 'polite')
}
}
```
## Making Changes
### Creating a Branch
Never work directly on `main`. Create a new branch:
```bash
git checkout -b type/short-description
```
Branch types:
- `feat/` - New features
- `fix/` - Bug fixes
- `docs/` - Documentation
- `refactor/` - Code refactoring
- `test/` - Adding/updating tests
- `chore/` - Maintenance tasks
### Making Commits
Keep commits atomic and descriptive:
```bash
git add .
git commit -m "feat(profiles): add compatibility score display
- Added compatibility score calculation
- Display score on profile cards
- Added tests for scoring algorithm"
```
See [Commit Message Guidelines](#commit-message-guidelines) for details.
### Keeping Your Fork Updated
```bash
# Fetch latest from upstream
git fetch upstream
# Update main branch
git checkout main
git merge upstream/main
# Rebase your feature branch
git checkout feat/your-feature
git rebase main
```
## Testing
### Writing Tests
#### Unit Tests
Place tests in `tests/unit/` within each package:
```typescript
// web/tests/unit/my-function.test.ts
import {myFunction} from '../my-function'
describe('myFunction', () => {
it('should return correct output', () => {
expect(myFunction('input')).toBe('expected')
})
})
```
#### Integration Tests
Place in `tests/integration/`:
```typescript
// web/tests/integration/api.test.ts
import {render, screen} from '@testing-library/react'
import {MyComponent} from '../MyComponent'
describe('MyComponent', () => {
it('renders correctly', () => {
render(<MyComponent />)
expect(screen.getByText('Hello')).toBeInTheDocument()
})
})
```
#### E2E Tests
Place in `tests/e2e/` at the root:
```typescript
// tests/e2e/web/specs/onboarding.spec.ts
import {test, expect} from '@playwright/test'
test('onboarding flow', async ({page}) => {
await page.goto('/signup')
await page.fill('[name="email"]', 'test@example.com')
await page.click('button[type="submit"]')
await expect(page).toHaveURL('/onboarding')
})
```
### Running Specific Tests
```bash
# Run unit tests for web
yarn workspace web test
# Run tests matching pattern
yarn test --testPathPattern="profile"
# Run E2E tests
yarn test:e2e
```
### Test Coverage
Aim for meaningful test coverage. Focus on:
- Business logic
- User interactions
- Error handling
- Edge cases
## Pull Request Guidelines
### Before Submitting
1. **Run all tests**: `yarn test`
2. **Run linter**: `yarn lint`
3. **Run type check**: `yarn typecheck`
4. **Update documentation** if needed
5. **Rebase on main** if necessary
### Pull Request Format
**Title**: Clear, descriptive title
**Description**:
```markdown
## Summary
Brief description of changes
## Changes
- Added compatibility score to profile cards
- Updated search algorithm for better results
## Testing
- Added unit tests for scoring algorithm
- Tested manually with synthetic data
## Screenshots (if UI changes)
```
### PR Checklist
- [ ] Code follows style guidelines
- [ ] Tests added/updated and passing
- [ ] Documentation updated
- [ ] No console.log statements (except debugging)
- [ ] No debug code left behind
### Review Process
1. Maintainers review within 48 hours
2. Address feedback promptly
3. Do not open new PRs for changes - update existing one
4. Squash commits before merging
## Commit Message Guidelines
Follow [Conventional Commits](https://www.conventionalcommits.org/):
```
<type>(<scope>): <description>
[optional body]
[optional footer]
```
### Types
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation
- `style`: Formatting
- `refactor`: Code restructuring
- `test`: Tests
- `chore`: Maintenance
### Examples
```text
fix(api): handle 500 error on invalid payload
feat(profiles): add compatibility scoring algorithm
fix(api): handle rate limiting gracefully
docs(readme): update installation instructions
refactor(auth): simplify token refresh logic
test(profiles): add unit tests for scoring
```
Types include: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`.
## Documentation
## Test Everything
### Updating Documentation
If the project has tests, run them. If it doesnt, write some. Do **not** submit code that hasn't been tested.
- Update relevant README files
- Add JSDoc comments to complex functions
- Update the `/docs` folder for architectural changes
```bash
# Example for Node.js
npm test
```
### API Documentation
No exceptions. If you don't validate your changes, your PR will be closed.
API docs are auto-generated and available at:
## Lint & Format
- Production: https://api.compassmeet.com
- Local: http://localhost:3001 (when running locally)
Ensure code matches the project style. If the repo uses a linter or formatter, run them:
## Questions and Support
```bash
npm run lint
npm run format
```
- **Discord**: https://discord.gg/8Vd7jzqjun
- **Email**: hello@compassmeet.com
- **GitHub Issues**: For bug reports and feature requests
Or whatever command is defined in the repo.
---
## Write a Good Pull Request
When opening a pull request:
- **Title**: Describe what the PR does, clearly and specifically.
- **Description**: Explain the context. Link related issues (use `Fixes #123` if applicable).
- **Checklist**:
- [ ] My code is clean and follows the style guide
- [ ] Ive added or updated tests
- [ ] Ive run all tests and they pass
- [ ] Ive documented my changes (if necessary)
## Code Review Process
- PRs are reviewed by maintainers or core contributors.
- If feedback is given, respond and push updates. Do **not** open new PRs for changes to an existing one.
- PRs that are incomplete, sloppy, or violate the above will be closed.
## Don't Do This
- Dont commit directly to `main`
- Dont submit multiple unrelated changes in a single PR
- Dont ignore CI/test failures
- Dont expect hand-holding—read the docs and the source first
## Security Issues
Do **not** open public issues for security vulnerabilities. Email the development team instead.
## License
By contributing, you agree that your code will be licensed under the same license as the rest of the project.
Thank you for contributing to Compass! Together we're building a platform for meaningful connections.

View File

@@ -1,11 +1,67 @@
# Backend API
This is the code for the API running at https://api.compassmeet.com.
It runs in a docker inside a Google Cloud virtual machine.
Express.js REST API for Compass, running at https://api.compassmeet.com.
### Requirements
## Overview
You must have the `gcloud` CLI.
The API handles:
- User authentication and management
- Profile CRUD operations
- Search and filtering
- Messaging
- Notifications
- Compatibility scoring
- Events management
- WebSocket connections for real-time features
## Tech Stack
- **Runtime**: Node.js 20+
- **Framework**: Express.js 5.0
- **Language**: TypeScript
- **Database**: PostgreSQL (via Supabase)
- **ORM**: pg-promise
- **Validation**: Zod
- **WebSocket**: ws library
- **API Docs**: Swagger/OpenAPI
## Project Structure
```
backend/api/
├── src/
│ ├── app.ts # Express app setup
│ ├── routes.ts # Route definitions
│ ├── test.ts # Test utilities
│ ├── get-*.ts # GET endpoints
│ ├── create-*.ts # POST endpoints
│ ├── update-*.ts # PUT/PATCH endpoints
│ ├── delete-*.ts # DELETE endpoints
│ └── helpers/ # Shared utilities
├── tests/
│ └── unit/ # Unit tests
├── package.json
├── tsconfig.json
└── README.md
```
## Getting Started
### Prerequisites
- Node.js 20.x or later
- Yarn
- Access to Supabase project (for database)
### Installation
```bash
# From root directory
yarn install
```
You must also have the `gcloud` CLI.
On macOS:
@@ -35,6 +91,356 @@ You also need `opentofu` and `docker`. Try running this (from root) on Linux or
If it doesn't work, you can install them manually (google how to install `opentofu` and `docker` for your OS).
### Running Locally
```bash
# Run all services (web + API)
yarn dev
# Run API only (from backend/api)
cd backend/api
yarn serve
```
The API runs on http://localhost:3001 when running locally with the full stack.
### Testing
```bash
# Run unit tests
yarn test
# Run with coverage
yarn test --coverage
```
### Linting
```bash
# Check lint
yarn lint
# Fix issues
yarn lint-fix
```
## API Endpoints
### Authentication
| Method | Endpoint | Description |
| ------ | ------------------- | --------------- |
| POST | `/auth/google` | Google Sign-In |
| POST | `/auth/create-user` | Create new user |
### Users
| Method | Endpoint | Description |
| ------ | ------------ | ------------------- |
| GET | `/get-me` | Get current user |
| PUT | `/update-me` | Update current user |
| DELETE | `/delete-me` | Delete account |
### Profiles
| Method | Endpoint | Description |
| ------ | ----------------- | ------------------ |
| GET | `/get-profiles` | List profiles |
| GET | `/get-profile` | Get single profile |
| POST | `/create-profile` | Create profile |
| PUT | `/update-profile` | Update profile |
| DELETE | `/delete-profile` | Delete profile |
### Messaging
| Method | Endpoint | Description |
| ------ | ------------------------------ | -------------- |
| GET | `/get-private-messages` | Get messages |
| POST | `/create-private-user-message` | Send message |
| PUT | `/edit-message` | Edit message |
| DELETE | `/delete-message` | Delete message |
### Notifications
| Method | Endpoint | Description |
| ------ | ----------------------- | ------------------ |
| GET | `/get-notifications` | List notifications |
| PUT | `/update-notif-setting` | Update settings |
### Search
| Method | Endpoint | Description |
| ------ | ------------------ | ------------------ |
| GET | `/search-users` | Search users |
| GET | `/search-location` | Search by location |
### Compatibility
| Method | Endpoint | Description |
| ------ | ------------------------------ | ----------------------- |
| GET | `/get-compatibility-questions` | List questions |
| POST | `/set-compatibility-answers` | Submit answers |
| GET | `/compatible-profiles` | Get compatible profiles |
## Writing Endpoints
### 1. Define Schema
Add endpoint definition in `common/src/api/schema.ts`:
```typescript
myEndpoint: {
method: 'POST',
authed
:
true,
returns
:
z.object({
success: z.boolean(),
data: z.any()
}),
props
:
z.object({
userId: z.string(),
option: z.string().optional()
}).strict()
}
```
### 2. Implement Handler
Create handler file in `backend/api/src/`:
```typescript
import {z} from 'zod'
import {APIHandler} from './helpers/endpoint'
export const myEndpoint: APIHandler<'myEndpoint'> = async (props, auth) => {
const {userId, option} = props
// Implementation
return {
success: true,
data: {userId},
}
}
```
### 3. Register Route
Add to `routes.ts`:
```typescript
import {myEndpoint} from './my-endpoint'
const handlers = {
myEndpoint,
// ...
}
```
## Authentication
### Authenticated Endpoints
Use the `authed: true` schema property. The auth object is passed to the handler:
```typescript
export const getProfile: APIHandler<'get-profile'> = async (props, auth) => {
// auth.uid - user ID
// auth.creds - credentials type
}
```
### Auth Types
- `firebase` - Firebase Auth token
- `session` - Session-based auth
## Database Access
### Using pg-promise
```typescript
import {createSupabaseDirectClient} from 'shared/supabase/init'
const pg = createSupabaseDirectClient()
const result = await pg.oneOrNone<User>('SELECT * FROM users WHERE id = $1', [userId])
```
### Using Supabase Client
```typescript
import {db} from 'web/lib/supabase/db'
const {data, error} = await db.from('profiles').select('*').eq('user_id', userId)
```
## Rate Limiting
The API includes built-in rate limiting:
```typescript
export const myEndpoint: APIHandler<'myEndpoint'> = withRateLimit(
async (props, auth) => {
// Handler implementation
},
{
name: 'my-endpoint',
limit: 100,
windowMs: 60 * 1000, // 1 minute
},
)
```
## Error Handling
Use `APIError` for consistent error responses:
```typescript
import {APIError} from './helpers/endpoint'
throw APIError(404, 'User not found')
throw APIError(400, 'Invalid input', {field: 'email'})
```
Error codes:
- `400` - Bad Request
- `401` - Unauthorized
- `403` - Forbidden
- `404` - Not Found
- `429` - Too Many Requests
- `500` - Internal Server Error
## WebSocket
WebSocket connections are handled for real-time features:
```typescript
// Subscribe to updates
ws.subscribe('user/123', (data) => {
console.log('User updated:', data)
})
// Unsubscribe
ws.unsubscribe('user/123', callback)
```
Available topics:
- `user/{userId}` - User updates
- `private-user/{userId}` - Private user updates
- `message/{channelId}` - New messages
## Logging
Use the shared logger:
```typescript
import {log} from 'shared/monitoring/log'
log.info('Processing request', {userId: auth.uid})
log.error('Failed to process', error)
```
## Deployment
### Production Deployment
Deployments are automated via GitHub Actions. Push to main triggers deployment:
```bash
# Increment version
# Update package.json version
git add package.json
git commit -m "chore: bump version"
git push origin main
```
### Manual Deployment
```bash
cd backend/api
./deploy-api.sh prod
```
### Server Access
Run in this directory to connect to the API server running as virtual machine in Google Cloud. You can access logs,
files, debug, etc.
```bash
# SSH into production server
cd backend/api
./ssh-api.sh prod
```
Useful commands on server:
```bash
sudo journalctl -u konlet-startup --no-pager -ef # View logs
sudo docker logs -f $(sudo docker ps -alq) # Container logs
docker exec -it $(sudo docker ps -alq) sh # Shell access
docker run -it --rm $(docker images -q | head -n 1) sh
docker rmi -f $(docker images -aq)
```
## Environment Variables
Required secrets (set in Google Cloud Secrets Manager):
| Variable | Description |
| ---------------------- | ---------------------------- |
| `DATABASE_URL` | PostgreSQL connection string |
| `FIREBASE_PROJECT_ID` | Firebase project ID |
| `FIREBASE_PRIVATE_KEY` | Firebase private key |
| `SUPABASE_SERVICE_KEY` | Supabase service role key |
| `JWT_SECRET` | JWT signing secret |
## Testing
### Writing Unit Tests
```typescript
// tests/unit/my-endpoint.unit.test.ts
import {myEndpoint} from '../my-endpoint'
describe('myEndpoint', () => {
it('should return success', async () => {
const result = await myEndpoint({userId: '123'}, mockAuth)
expect(result.success).toBe(true)
})
})
```
### Mocking Database
```typescript
const mockPg = {
oneOrNone: jest.fn().mockResolvedValue({id: '123'}),
}
```
## API Documentation
Full API docs available at:
- Production: https://api.compassmeet.com
- Local: http://localhost:8088 (when running)
Docs are generated from route definitions in `app.ts`.
## See Also
- [Main README](../../README.md)
- [Contributing Guide](../../CONTRIBUTING.md)
- [Shared Backend Utils](../shared/README.md)
- [Database Migrations](../../supabase/migrations)
### Setup
This section is only for the people who are creating a server from scratch, for instance for a forked project.
@@ -155,48 +561,3 @@ in [Google Cloud Secrets manager](https://console.cloud.google.com/security/secr
can access them.
For Compass, the name of the secrets are in [secrets.ts](../../common/src/secrets.ts).
### Run Locally
In root directory, run the local api with hot reload, along with all the other backend and web code.
```bash
./run_local.sh prod
```
### Deploy
To deploy the backend code, simply increment the version number in [package.json](package.json) and push to the `main` branch.
Or if you have access to the project on google cloud, run in this directory:
```bash
./deploy-api.sh prod
```
### Connect to the server
Run in this directory to connect to the API server running as virtual machine in Google Cloud. You can access logs,
files, debug, etc.
```bash
./ssh-api.sh prod
```
Useful commands once inside the server:
```bash
sudo journalctl -u konlet-startup --no-pager -efb
sudo docker logs -f $(sudo docker ps -alq)
docker exec -it $(sudo docker ps -alq) sh
docker run -it --rm $(docker images -q | head -n 1) sh
docker rmi -f $(docker images -aq)
```
### Documentation
The API doc is available at https://api.compassmeet.com. It's dynamically prepared in [app.ts](src/app.ts).
### Todo (Tests)
- [ ] Finish get-supabase-token unit test when endpoint is implemented

View File

@@ -17,7 +17,7 @@ What testing is not
How we apply it here
- Unit and integration tests live in each package and run with Jest (see `jest.config.js`).
- Unit and integration tests live in each package and run with Jest (see `jest.config.ts`).
- Critical user journeys are covered by Playwright E2E tests under `tests/e2e` (see `playwright.config.ts`).
### Test types at a glance
@@ -25,18 +25,20 @@ How we apply it here
This project uses three complementary test types. Use the right level for the job:
- Unit tests
- Purpose: Verify a single function/module in isolation; fast, deterministic.
- Where: Each package under `tests/unit` (e.g., `backend/api/tests/unit`, `web/tests/unit`, `common/tests/unit`,
etc.).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.unit.test.ts` (or `.tsx` for React in `web`).
- When to use: Pure logic, utilities, hooks, reducers, small components with mocked dependencies.
- Integration tests
- Purpose: Verify multiple units working together (e.g., function + DB/client, component + context/provider) without
spinning up the full app.
- Where: Each package under `tests/integration` (e.g., `backend/shared/tests/integration`, `web/tests/integration`).
- Runner: Jest (configured via root `jest.config.js`).
- Runner: Jest (configured via root `jest.config.ts`).
- Naming: `*.integration.test.ts` (or `.tsx` for React in `web`).
- When to use: Boundaries between modules, real serialization/parsing, API handlers with mocked network/DB,
component trees with providers.
@@ -62,7 +64,7 @@ yarn test:e2e
```filetree
# Config
jest.config.js (for unit and integration tests)
jest.config.ts (for unit and integration tests)
playwright.config.ts (for e2e tests)
# Top-level End-to-End (Playwright)
@@ -114,7 +116,7 @@ web/
- End-to-End tests live under `tests/e2e` and are executed by Playwright. The root `playwright.config.ts` sets `testDir`
to `./tests/e2e`.
- Unit and integration tests live in each packages `tests` folder and are executed by Jest via the root
`jest.config.js` projects array.
`jest.config.ts` projects array.
- Naming:
- Unit: `*.unit.test.ts` (or `.tsx` for React in `web`)
- Integration: `*.integration.test.ts`
@@ -348,7 +350,6 @@ jest.mock('path/to/module')
* This creates an object containing all named exports from ./path/to/module
*/
import * as mockModule from 'path/to/module'
;(mockModule.module as jest.Mock).mockResolvedValue(mockReturnValue)
```
@@ -756,7 +757,7 @@ Firebase emulator supports multiple projects. You can spin up isolated project I
too slow):
```js
// jest.config.js - per worker
// jest.config.ts - per worker
projectId: `test-${process.env.JEST_WORKER_ID}`
```

385
docs/architecture.md Normal file
View File

@@ -0,0 +1,385 @@
# Architecture Documentation
> [!WARNING]
> This document is a work in progress. Please help us improve it!
## System Overview
Compass is a monorepo containing a Next.js web application, Express API server, Capacitor Android app, and shared packages. The platform is designed for forming deep, authentic 1-on-1 connections.
## High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Users │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Vercel (Frontend) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Next.js │ │ Static │ │ API Routes │ │
│ │ Web App │ │ Assets │ │ (Serverless) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ HTTPS
┌─────────────────────────────────────────────────────────────────┐
│ Google Cloud Platform (Backend) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Express │ │ WebSocket │ │ Docker Container │ │
│ │ API │ │ Server │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ Connection
┌─────────────────────────────────────────────────────────────────┐
│ Supabase (PostgreSQL) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Database │ │ Edge │ │ Realtime │ │
│ │ (Postgres)│ │ Functions │ │ Subscriptions │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Firebase │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Auth │ │ Storage │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## Monorepo Structure
```
Compass/
├── web/ # Next.js web application
│ ├── components/ # React components
│ ├── hooks/ # Custom React hooks
│ ├── lib/ # Utilities & services
│ ├── pages/ # Next.js pages
│ ├── messages/ # i18n translations
│ └── tests/ # Unit & integration tests
├── backend/
│ ├── api/ # Express REST API
│ │ ├── src/ # Handler implementations
│ │ └── tests/ # Unit tests
│ ├── shared/ # Shared backend utilities
│ │ ├── src/
│ │ │ ├── supabase/ # Database utilities
│ │ │ ├── monitoring/ # Logging
│ │ │ └── mobile/ # Push notifications
│ │ └── tests/
│ ├── email/ # React email templates
│ └── scripts/ # Database migrations
├── common/ # Shared types & utilities
│ └── src/
│ ├── types/ # TypeScript definitions
│ ├── api/ # API schema definitions
│ └── constants/ # App constants
├── supabase/ # Database migrations
│ └── migrations/
├── android/ # Capacitor Android app
└── tests/ # E2E tests (Playwright)
└── e2e/
```
## Technology Stack
### Frontend (web)
| Category | Technology | Version |
| ---------- | --------------------- | ------- |
| Framework | Next.js | 14.1.0 |
| UI Library | React | 18.2.0 |
| Language | TypeScript | 5.5.4 |
| Styling | Tailwind CSS | 3.3.3 |
| State | React Context + Hooks | - |
| Forms | React Hook Form | 7.65.0 |
| Rich Text | TipTap | 2.10.4 |
| i18n | Custom JSON | - |
| Testing | Jest | 29.3.1 |
| E2E | Playwright | 1.58.2 |
### Backend (api)
| Category | Technology | Version |
| ---------- | ---------- | ------- |
| Runtime | Node.js | 20+ |
| Framework | Express | 5.0.0 |
| Language | TypeScript | 5.5.4 |
| Database | PostgreSQL | - |
| ORM | pg-promise | - |
| Validation | Zod | - |
| WebSocket | ws | - |
| Testing | Jest | 29.3.1 |
### Infrastructure
| Service | Purpose |
| ---------------- | ------------------- |
| Vercel | Frontend hosting |
| Google Cloud | Backend hosting |
| Supabase | PostgreSQL database |
| Firebase Auth | User authentication |
| Firebase Storage | Media storage |
| PostHog | Analytics |
## Data Flow
### User Request Flow
```
1. User clicks button
2. React component handles event
3. useAPIGetter/useMutation hook called
4. API client sends HTTP request
5. Express API receives request
6. Auth middleware validates token
7. Handler processes request
8. Database query executed
9. Response returned to client
10. React state updated, UI re-renders
```
### Authentication Flow
```
User Sign-In:
┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌────────────┐
│ Client │───▶│ Firebase │───▶│ Auth Token │───▶│ Backend │
└──────────┘ └──────────┘ │ (JWT) │ │ Validates │
└──────────────┘ └────────────┘
┌────────────┐
│ Session │
│ Created │
└────────────┘
```
## Database Schema
### Key Tables
```sql
-- Users table
users (
id UUID PRIMARY KEY,
username TEXT UNIQUE,
email TEXT,
created_at TIMESTAMP,
deleted_at TIMESTAMP
)
-- Profile information
profiles (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users,
name TEXT,
age INTEGER,
bio TEXT,
-- many more fields
)
-- Private user data
private_users (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users,
email TEXT,
notification_settings JSONB
)
-- Messages
private_user_messages (
id UUID PRIMARY KEY,
from_user_id UUID,
to_user_id UUID,
content TEXT,
created_at TIMESTAMP
)
```
See `supabase/migrations/` for full schema.
## API Design
### REST Principles
- Resource-based URLs: `/get-profiles`, `/create-profile`
- HTTP methods: GET (read), POST (create), PUT (update), DELETE (delete)
- JSON request/response format
- Authentication via Bearer tokens
### Endpoint Structure
```
/{action} POST - Perform action
```
### Request Format
```typescript
{
// Endpoint-specific parameters
}
```
### Response Format
```typescript
// Success
{
// Response data
}
// Error
{
error: {
status: 400,
message: "Error description"
}
}
```
## State Management
### Frontend State
1. **Server State**: React Query-style hooks (`useAPIGetter`, `useMutation`)
2. **Client State**: React `useState`, `useReducer`
3. **Persisted State**: `usePersistentLocalState`, `usePersistentInMemoryState`
4. **Global State**: React Context (`AuthProvider`, `I18nProvider`)
### State Persistence
```typescript
// In-memory (lost on refresh)
const [state, setState] = useState(initialValue)
// Local storage (persists)
const [state, setState] = usePersistentLocalState(initialValue, 'key')
// Session storage (persists until tab closed)
const [state, setState] = usePersistentInMemoryState(initialValue, 'key')
```
## Security
### Authentication
- Firebase Auth for user authentication
- JWT tokens for API requests
- Session-based auth for web
### Authorization
- Role-based access (user, moderator, admin)
- User ID verification on protected endpoints
### Data Protection
- Row-level security in PostgreSQL
- Environment-based secrets
- Input validation with Zod
## Performance Optimizations
### Frontend
- Next.js static generation for public pages
- Image optimization with `next/image`
- Code splitting per route
- Memoization with `useMemo`, `useCallback`
- Virtualized lists for large datasets
### Backend
- Database connection pooling
- Query optimization (indexes)
- Caching strategies
- Rate limiting
## Monitoring
### Logging
- Structured logging with context
- Different log levels: debug, info, warn, error
- Environment-aware (development vs production)
### Error Tracking
- Error boundaries in React
- API error handling
- Server-side error logging
### Analytics
- PostHog for user analytics
- Custom event tracking
## Deployment
### CI/CD Pipeline
```
GitHub Push
┌─────────────┐
│ CI Tests │ ──▶ Lint, TypeCheck, Unit Tests
└─────────────┘
┌─────────────┐
│ CD Deploy │ ──▶ Vercel (Web), GCP (API)
└─────────────┘
```
### Environments
| Environment | URL | Purpose |
| ----------- | --------------- | ----------------- |
| Development | localhost | Local development |
| Staging | - | Testing changes |
| Production | compassmeet.com | Live users |
## Development Workflow
1. **Create branch**: `feat/description`
2. **Make changes**: Implement feature/fix
3. **Test locally**: `yarn dev`
4. **Run tests**: `yarn test`
5. **Submit PR**: Code review
6. **Merge**: Automated deployment
## See Also
- [Knowledge Base](knowledge.md)
- [Development Guide](development.md)
- [Testing Guide](TESTING.md)
- [Next.js Documentation](Next.js.md)

View File

@@ -1,12 +1,353 @@
# Web
# Web Application
This is the folder for the web application.
The Compass web application built with Next.js, React, and TypeScript.
### Information
## Overview
##### Setup
This is the frontend of the Compass platform, a transparent platform for forming deep, authentic 1-on-1 connections.
This is the setup for deployment on Vercel, which you only need to do if you create a new platform from scratch, not if you are contributing to Compass
## Tech Stack
- **Framework**: Next.js 14.1.0
- **Language**: TypeScript
- **UI Library**: React 18.2.0
- **Styling**: Tailwind CSS 3.3.3
- **State Management**: React Context + Custom Hooks
- **Forms**: React Hook Form
- **Rich Text**: TipTap (ProseMirror)
- **Charts**: Recharts
- **i18n**: Custom solution with JSON message files
## Project Structure
```
web/
├── components/ # React components
│ ├── auth-context.tsx # Authentication state
│ ├── buttons/ # Button components
│ ├── chat/ # Chat/messaging components
│ ├── comments/ # Comment components
│ ├── editor/ # Rich text editor
│ ├── events/ # Event components
│ ├── filters/ # Search filters
│ ├── matches/ # Match components
│ ├── nav/ # Navigation components
│ ├── profile/ # Profile components
│ └── widgets/ # Reusable widgets
├── hooks/ # Custom React hooks (50+)
├── lib/ # Utilities and services
│ ├── api.ts # API client
│ ├── firebase/ # Firebase configuration
│ ├── locale/ # Internationalization
│ ├── service/ # Analytics, push notifications
│ ├── supabase/ # Supabase client
│ └── logger.ts # Structured logging
├── pages/ # Next.js pages
│ ├── api/ # API routes
│ ├── _app.tsx # App wrapper
│ ├── _document.tsx # Document setup
│ └── [username].tsx # Dynamic routes
├── messages/ # Translation JSON files
├── public/ # Static assets
├── styles/ # Global CSS
├── tests/
│ ├── unit/ # Unit tests
│ └── integration/ # Integration tests
└── types/ # TypeScript type definitions
```
## Getting Started
### Prerequisites
- Node.js 20.x or later
- Yarn 1.x
### Installation
```bash
# From root directory
yarn install
```
### Development
```bash
# Run web app with hot reload
yarn dev
# Or from web directory
cd web
yarn serve
```
Visit http://localhost:3000
### Build
```bash
# Production build
yarn build
# Start production server
yarn start
```
### Testing
```bash
# Run tests
yarn test
# Run with coverage
yarn test --coverage
# Watch mode
yarn test --watch
```
### Linting
```bash
# Check lint
yarn lint
# Fix lint issues
yarn lint-fix
```
## Key Concepts
### Components
Components are organized by feature in `/components`. Reusable widgets are in `/components/widgets`.
Example component:
```tsx
// components/profile/profile-card.tsx
import {User} from 'common/src/user'
interface ProfileCardProps {
user: User
onLike?: (userId: string) => void
}
export function ProfileCard({user, onLike}: ProfileCardProps) {
return (
<div className="profile-card">
<img src={user.avatarUrl} alt={user.name} />
<h3>{user.name}</h3>
<button onClick={() => onLike?.(user.id)}>Like</button>
</div>
)
}
```
### Hooks
Use custom hooks for stateful logic. Common hooks:
- `useUser()` - Get current user
- `useAPIGetter()` - Fetch API data with caching
- `useMutation()` - Handle form submissions
- `usePersistentInMemoryState()` - Cache state across pages
```tsx
import {useAPIGetter} from 'web/hooks/use-api-getter'
function ProfileList() {
const {data, refresh} = useAPIGetter('get-profiles', {})
if (!data) return <Loading />
return (
<div>
{data.profiles.map((profile) => (
<ProfileCard key={profile.id} user={profile} />
))}
<button onClick={refresh}>Refresh</button>
</div>
)
}
```
### API Calls
Backend API is called through the `api` helper:
```tsx
import {api} from 'web/lib/api'
// Server-side (getStaticProps, getServerSideProps)
const profiles = await api('get-profiles', {})
// Client-side - use hooks
const {data} = useAPIGetter('get-profiles', {})
```
### Internationalization
Translation files are in `/messages` (e.g., `en.json`, `fr.json`).
```tsx
import {useT} from 'web/lib/locale'
function MyComponent() {
const t = useT()
return <h1>{t('welcome', 'Welcome to Compass')}</h1>
}
```
### Styling
Tailwind CSS is used for styling. Use utility classes:
```tsx
<div className="flex items-center justify-between p-4 bg-canvas-50 rounded-lg">
<span className="text-ink-900 font-medium">Content</span>
</div>
```
## Accessibility
The app includes several accessibility features:
### Error Boundary
Catches React errors and shows user-friendly message:
```tsx
import {ErrorBoundary} from 'web/components/error-boundary'
;<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
```
### Live Region
Announces dynamic content changes to screen readers:
```tsx
import {useLiveRegion} from 'web/components/live-region'
const {announce} = useLiveRegion()
// Announce status changes
announce('Profile liked', 'polite')
```
Priority levels:
- `polite` - Waits for screen reader to finish (default)
- `assertive` - Interrupts immediately
### Skip Links
Keyboard users can skip to main content:
```tsx
import {SkipLink, MainContent} from 'web/components/skip-link'
;<>
<SkipLink />
<MainContent>...</MainContent>
</>
```
## Logging
Use the structured logger for debug logging that's filtered out in production:
```tsx
import {debug, logApiError} from 'common/logger'
// Simple logging
debug('User logged in', {userId: '123'})
// API errors with context
try {
await api('endpoint', {})
} catch (err) {
logApiError('get-profiles', err, {userId: '123'})
}
```
## Environment Variables
Key environment variables for the web app:
| Variable | Description |
| ------------------------------- | --------------------- |
| `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL |
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase anon key |
| `NEXT_PUBLIC_FIREBASE_API_KEY` | Firebase API key |
| `NEXT_PUBLIC_POSTHOG_KEY` | PostHog analytics key |
| `NEXT_PUBLIC_VERCEL_ENV` | Vercel environment |
## Common Tasks
### Adding a New Page
1. Create file in `/pages/`:
```tsx
// pages/new-page.tsx
import {Page} from 'web/components/page-base'
export default function NewPage() {
return (
<Page>
<h1>New Page</h1>
</Page>
)
}
```
### Adding a Component
1. Create file in appropriate `/components` subdirectory
2. Export the component
3. Add to parent component
### Adding a Hook
1. Create file in `/hooks/`
2. Follow naming convention:\*.ts`
### `use- Adding Translations
1. Add key to `/messages/en.json`
2. Add translations to other locale files
## Troubleshooting
### Slow local development
Running `yarn dev:isolated` uses local emulators and is faster.
### Type errors
Run `yarn typecheck` to see all type errors.
### Build failures
Check `yarn lint` first, as linting issues can cause build failures.
## See Also
- [Main README](../README.md)
- [Contributing Guide](../CONTRIBUTING.md)
- [Development Docs](../docs/development.md)
- [Testing Guide](../docs/TESTING.md)
- [Architecture Docs](../docs/knowledge.md)
## Setup
This is the setup for deployment on Vercel, which you only need to do if you create a new platform from scratch, not if
you are contributing to Compass
Set up a Vercel account and link it to your GitHub repository.
@@ -16,9 +357,10 @@ Add the following environment variables and the ones in `.env` in the Vercel das
NEXT_PUBLIC_VERCEL=1
```
##### `next` version
## `next` version
The `next` version is 14.1.0, as we get the following error with 15.1.2 and above when accessing `/[username]` pages on Vercel:
The `next` version is 14.1.0, as we get the following error with 15.1.2 and above when accessing `/[username]` pages on
Vercel:
```
Cannot find module 'next/dist/compiled/source-map'