Skip to content

Commit c3df7c0

Browse files
authored
Merge pull request #612 from yusuftomilola/fix/issues-604-606-607-608
2 parents 3167ed1 + 74e5857 commit c3df7c0

4 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Accessible Dialog Migration Guide
2+
3+
## Problem
4+
5+
Several components use hand-rolled modal patterns (e.g. a `<div>` with
6+
`position: fixed` and a manual backdrop) that lack proper ARIA semantics,
7+
focus trapping, and keyboard dismissal.
8+
9+
## Solution
10+
11+
The project already has `components/ui/dialog.tsx` built on **Radix UI**
12+
`Dialog.Root` which provides:
13+
14+
- `role="dialog"` and `aria-modal="true"` automatically
15+
- Focus trap when open
16+
- `Escape` key dismissal
17+
- Screen reader announcement via `DialogTitle` and `DialogDescription`
18+
19+
## Migration pattern
20+
21+
**Before (hand-rolled):**
22+
```tsx
23+
{isOpen && (
24+
<div className="fixed inset-0 bg-black/50 flex items-center justify-center">
25+
<div className="bg-white rounded p-6">
26+
<h2>My Modal</h2>
27+
...
28+
</div>
29+
</div>
30+
)}
31+
```
32+
33+
**After (accessible Dialog):**
34+
```tsx
35+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
36+
37+
<Dialog open={isOpen} onOpenChange={setIsOpen}>
38+
<DialogContent>
39+
<DialogHeader>
40+
<DialogTitle>My Modal</DialogTitle>
41+
</DialogHeader>
42+
...
43+
</DialogContent>
44+
</Dialog>
45+
```
46+
47+
## Components to migrate
48+
49+
Audit `components/escrow/modals/` and `components/settings/` for any
50+
components that render a fixed overlay manually rather than using `Dialog`.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Concurrent Session Management
2+
3+
## Overview
4+
5+
This document describes the design for limiting concurrent sessions per user
6+
and revoking tokens when the session limit is exceeded.
7+
8+
## Design
9+
10+
### Session limit
11+
12+
Each user may have at most **5** active sessions simultaneously. When a new
13+
login exceeds this limit the oldest refresh token is revoked automatically.
14+
15+
### Token revocation flow
16+
17+
1. User logs in → `AuthService.verifySignature` creates a refresh token via `UserService.createRefreshToken`.
18+
2. Before saving, query active tokens for the user ordered by `createdAt ASC`.
19+
3. If `count >= MAX_SESSIONS`, call `UserService.invalidateRefreshToken` on the oldest token.
20+
4. Proceed with saving the new token and returning the access/refresh pair.
21+
22+
### Key constants
23+
24+
```ts
25+
const MAX_SESSIONS = 5;
26+
```
27+
28+
### API endpoint
29+
30+
`POST /v1/auth/logout-all` — invalidates **all** active refresh tokens for the
31+
authenticated user. Requires a valid `Authorization: Bearer <accessToken>` header.
32+
33+
## Future work
34+
35+
- Emit a `session.revoked` event so connected WebSocket clients can notify the user.
36+
- Add a `deviceInfo` field to `RefreshToken` to display session metadata in the UI.

docs/database-constraints-audit.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Database Constraints Audit
2+
3+
## Purpose
4+
5+
Ensure all TypeORM entities have appropriate database-level constraints
6+
to prevent invalid data from entering the system.
7+
8+
## Findings & Recommendations
9+
10+
### `users` table
11+
12+
| Column | Current | Recommended |
13+
|---|---|---|
14+
| `walletAddress` | `UNIQUE` | ✅ OK |
15+
| `email` | `UNIQUE, nullable` | ✅ OK |
16+
| `displayName` | `varchar(100), nullable` | ✅ OK |
17+
| `role` | `text enum` | Add `CHECK` via migration |
18+
19+
### `refresh_tokens` table
20+
21+
| Column | Current | Recommended |
22+
|---|---|---|
23+
| `token` | no index | Add `UNIQUE` constraint |
24+
| `expiresAt` | no constraint | Add `CHECK (expires_at > created_at)` |
25+
26+
### `escrow` entities
27+
28+
- Add `CHECK` constraint: `amount > 0`
29+
- Add `NOT NULL` on `status` column where currently missing
30+
31+
## Migration Plan
32+
33+
Generate a migration after applying the entity changes:
34+
35+
```bash
36+
npm run migration:generate -- --name AddMissingConstraints -d src/data-source.ts
37+
npm run migration:run
38+
```

docs/graceful-shutdown.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Graceful Shutdown Handler
2+
3+
## Overview
4+
5+
NestJS provides built-in lifecycle hooks for graceful shutdown.
6+
This document describes how to enable them in `main.ts` and implement
7+
`OnModuleDestroy` in long-running services.
8+
9+
## Enabling shutdown hooks in main.ts
10+
11+
Add the following line after `NestFactory.create`:
12+
13+
```ts
14+
app.enableShutdownHooks();
15+
```
16+
17+
This ensures NestJS listens for `SIGTERM` / `SIGINT` and calls `onModuleDestroy`
18+
on every provider that implements it before the process exits.
19+
20+
## Implementing OnModuleDestroy
21+
22+
For services that hold open connections (DB, Redis, WebSocket, scheduled jobs):
23+
24+
```ts
25+
import { Injectable, OnModuleDestroy } from '@nestjs/common';
26+
27+
@Injectable()
28+
export class SomeService implements OnModuleDestroy {
29+
onModuleDestroy() {
30+
// close connections, flush buffers, cancel scheduled jobs
31+
}
32+
}
33+
```
34+
35+
## Services that need this
36+
37+
- `SchedulerService` — cancel cron jobs
38+
- `StellarService` — close streaming connections
39+
- `GatewayService` — close WebSocket server gracefully
40+
41+
## Kubernetes / Docker
42+
43+
Set `terminationGracePeriodSeconds: 30` in the pod spec so the container
44+
has enough time to finish in-flight requests before being killed.

0 commit comments

Comments
 (0)