@odvi/create-dtt-framework 0.1.3 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +16 -13
- package/dist/commands/create.js.map +1 -1
- package/package.json +3 -2
- package/template/.env.example +106 -0
- package/template/components.json +22 -0
- package/template/docs/framework/01-overview.md +289 -0
- package/template/docs/framework/02-techstack.md +503 -0
- package/template/docs/framework/api-layer.md +681 -0
- package/template/docs/framework/clerk-authentication.md +649 -0
- package/template/docs/framework/cli-installation.md +564 -0
- package/template/docs/framework/deployment/ci-cd.md +907 -0
- package/template/docs/framework/deployment/digitalocean.md +991 -0
- package/template/docs/framework/deployment/domain-setup.md +972 -0
- package/template/docs/framework/deployment/environment-variables.md +862 -0
- package/template/docs/framework/deployment/monitoring.md +927 -0
- package/template/docs/framework/deployment/production-checklist.md +649 -0
- package/template/docs/framework/deployment/vercel.md +791 -0
- package/template/docs/framework/environment-variables.md +646 -0
- package/template/docs/framework/health-check-system.md +583 -0
- package/template/docs/framework/implementation.md +559 -0
- package/template/docs/framework/snowflake-integration.md +594 -0
- package/template/docs/framework/state-management.md +615 -0
- package/template/docs/framework/supabase-integration.md +582 -0
- package/template/docs/framework/testing-guide.md +544 -0
- package/template/docs/framework/what-did-i-miss.md +526 -0
- package/template/drizzle.config.ts +11 -0
- package/template/next.config.js +21 -0
- package/template/postcss.config.js +5 -0
- package/template/prettier.config.js +4 -0
- package/template/public/favicon.ico +0 -0
- package/template/src/app/(auth)/layout.tsx +4 -0
- package/template/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +10 -0
- package/template/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx +10 -0
- package/template/src/app/(dashboard)/dashboard/page.tsx +8 -0
- package/template/src/app/(dashboard)/health/page.tsx +16 -0
- package/template/src/app/(dashboard)/layout.tsx +17 -0
- package/template/src/app/api/[[...route]]/route.ts +11 -0
- package/template/src/app/api/debug-files/route.ts +33 -0
- package/template/src/app/api/webhooks/clerk/route.ts +112 -0
- package/template/src/app/layout.tsx +28 -0
- package/template/src/app/page.tsx +12 -0
- package/template/src/app/providers.tsx +20 -0
- package/template/src/components/layouts/navbar.tsx +14 -0
- package/template/src/components/shared/loading-spinner.tsx +6 -0
- package/template/src/components/ui/badge.tsx +46 -0
- package/template/src/components/ui/button.tsx +62 -0
- package/template/src/components/ui/card.tsx +92 -0
- package/template/src/components/ui/collapsible.tsx +33 -0
- package/template/src/components/ui/scroll-area.tsx +58 -0
- package/template/src/components/ui/sheet.tsx +139 -0
- package/template/src/config/__tests__/env.test.ts +164 -0
- package/template/src/config/__tests__/site.test.ts +46 -0
- package/template/src/config/env.ts +36 -0
- package/template/src/config/site.ts +10 -0
- package/template/src/env.js +44 -0
- package/template/src/features/__tests__/health-check-config.test.ts +142 -0
- package/template/src/features/__tests__/health-check-types.test.ts +201 -0
- package/template/src/features/documentation/components/doc-sidebar.tsx +109 -0
- package/template/src/features/documentation/components/doc-viewer.tsx +70 -0
- package/template/src/features/documentation/index.tsx +92 -0
- package/template/src/features/documentation/utils/doc-loader.ts +177 -0
- package/template/src/features/health-check/components/health-dashboard.tsx +374 -0
- package/template/src/features/health-check/config.ts +71 -0
- package/template/src/features/health-check/index.ts +4 -0
- package/template/src/features/health-check/stores/health-store.ts +14 -0
- package/template/src/features/health-check/types.ts +18 -0
- package/template/src/hooks/__tests__/use-debounce.test.tsx +28 -0
- package/template/src/hooks/queries/use-health-checks.ts +16 -0
- package/template/src/hooks/utils/use-debounce.ts +20 -0
- package/template/src/lib/__tests__/utils.test.ts +52 -0
- package/template/src/lib/__tests__/validators.test.ts +114 -0
- package/template/src/lib/nextbank/client.ts +67 -0
- package/template/src/lib/snowflake/client.ts +102 -0
- package/template/src/lib/supabase/admin.ts +7 -0
- package/template/src/lib/supabase/client.ts +7 -0
- package/template/src/lib/supabase/server.ts +23 -0
- package/template/src/lib/utils.ts +6 -0
- package/template/src/lib/validators.ts +9 -0
- package/template/src/middleware.ts +22 -0
- package/template/src/server/api/index.ts +22 -0
- package/template/src/server/api/middleware/auth.ts +19 -0
- package/template/src/server/api/middleware/logger.ts +4 -0
- package/template/src/server/api/routes/health/clerk.ts +214 -0
- package/template/src/server/api/routes/health/database.ts +141 -0
- package/template/src/server/api/routes/health/edge-functions.ts +107 -0
- package/template/src/server/api/routes/health/framework.ts +48 -0
- package/template/src/server/api/routes/health/index.ts +102 -0
- package/template/src/server/api/routes/health/nextbank.ts +46 -0
- package/template/src/server/api/routes/health/snowflake.ts +83 -0
- package/template/src/server/api/routes/health/storage.ts +177 -0
- package/template/src/server/api/routes/users.ts +79 -0
- package/template/src/server/db/index.ts +17 -0
- package/template/src/server/db/queries/users.ts +8 -0
- package/template/src/server/db/schema/__tests__/health-checks.test.ts +31 -0
- package/template/src/server/db/schema/__tests__/users.test.ts +46 -0
- package/template/src/server/db/schema/health-checks.ts +11 -0
- package/template/src/server/db/schema/index.ts +2 -0
- package/template/src/server/db/schema/users.ts +16 -0
- package/template/src/server/db/schema.ts +1 -0
- package/template/src/stores/__tests__/ui-store.test.ts +87 -0
- package/template/src/stores/ui-store.ts +14 -0
- package/template/src/styles/globals.css +129 -0
- package/template/src/test/mocks/clerk.ts +35 -0
- package/template/src/test/mocks/snowflake.ts +28 -0
- package/template/src/test/mocks/supabase.ts +37 -0
- package/template/src/test/setup.ts +69 -0
- package/template/src/test/utils/test-helpers.ts +158 -0
- package/template/src/types/index.ts +14 -0
- package/template/tsconfig.json +43 -0
- package/template/vitest.config.ts +44 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
# DTT Framework - Health Check System
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Health Check System provides comprehensive monitoring of all integrated services. It verifies that each service is properly configured and functioning correctly, providing real-time visibility into the system's health.
|
|
6
|
+
|
|
7
|
+
### Purpose
|
|
8
|
+
|
|
9
|
+
- **Service Verification**: Ensure all services are properly connected
|
|
10
|
+
- **Performance Monitoring**: Track response times for each service
|
|
11
|
+
- **Error Detection**: Identify and report service issues
|
|
12
|
+
- **Dashboard UI**: Visual representation of system health
|
|
13
|
+
- **Troubleshooting**: Quick diagnosis of integration issues
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Health Check Architecture
|
|
18
|
+
|
|
19
|
+
### System Architecture
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ Health Check Dashboard │
|
|
24
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
25
|
+
│ │ Service │ │ Service │ │ Service │ │
|
|
26
|
+
│ │ Status │ │ Status │ │ Status │ │
|
|
27
|
+
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
28
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
29
|
+
│
|
|
30
|
+
▼
|
|
31
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
32
|
+
│ TanStack Query Hook │
|
|
33
|
+
│ useHealthChecks() │
|
|
34
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
38
|
+
│ Hono API Routes │
|
|
39
|
+
│ /api/health/all │
|
|
40
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
41
|
+
│
|
|
42
|
+
┌─────────────────────┼─────────────────────┐
|
|
43
|
+
▼ ▼ ▼
|
|
44
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
45
|
+
│ Service │ │ Service │ │ Service │
|
|
46
|
+
│ Health │ │ Health │ │ Health │
|
|
47
|
+
│ Check │ │ Check │ │ Check │
|
|
48
|
+
└──────────────┘ └──────────────┘ └──────────────┘
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Component Structure
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
src/features/health-check/
|
|
55
|
+
├── components/
|
|
56
|
+
│ └── health-dashboard.tsx # Dashboard UI component
|
|
57
|
+
├── config.ts # Service configuration
|
|
58
|
+
├── types.ts # TypeScript types
|
|
59
|
+
└── index.ts # Feature exports
|
|
60
|
+
|
|
61
|
+
src/hooks/queries/
|
|
62
|
+
└── use-health-checks.ts # TanStack Query hook
|
|
63
|
+
|
|
64
|
+
src/server/api/routes/health/
|
|
65
|
+
├── index.ts # Health route aggregation
|
|
66
|
+
├── clerk.ts # Clerk health checks
|
|
67
|
+
├── database.ts # Database health checks
|
|
68
|
+
├── storage.ts # Storage health checks
|
|
69
|
+
├── edge-functions.ts # Edge function checks
|
|
70
|
+
├── snowflake.ts # Snowflake health checks
|
|
71
|
+
└── nextbank.ts # NextBank health checks
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## All Available Endpoints
|
|
77
|
+
|
|
78
|
+
### Health Check Endpoints
|
|
79
|
+
|
|
80
|
+
| Service | Endpoint | Method | Description |
|
|
81
|
+
|----------|----------|--------|-------------|
|
|
82
|
+
| **Clerk** | `/api/health/clerk/user` | GET | Get current user |
|
|
83
|
+
| **Clerk** | `/api/health/clerk/org` | GET | Get organization membership |
|
|
84
|
+
| **Clerk** | `/api/health/clerk/members` | GET | List organization members |
|
|
85
|
+
| **Database** | `/api/health/database/write` | POST | Write test row to database |
|
|
86
|
+
| **Database** | `/api/health/database/read` | GET | Read test row from database |
|
|
87
|
+
| **Database** | `/api/health/database/delete` | DELETE | Delete test row from database |
|
|
88
|
+
| **Storage** | `/api/health/storage/upload` | POST | Upload test file to storage |
|
|
89
|
+
| **Storage** | `/api/health/storage/download` | GET | Generate signed URL for test file |
|
|
90
|
+
| **Storage** | `/api/health/storage/delete` | DELETE | Delete test file from storage |
|
|
91
|
+
| **Edge Functions** | `/api/health/edge/ping` | GET | Ping edge function (placeholder) |
|
|
92
|
+
| **Edge Functions** | `/api/health/edge/auth` | GET | Test auth header passthrough (placeholder) |
|
|
93
|
+
| **Snowflake** | `/api/health/snowflake/connect` | GET | Test Snowflake connection |
|
|
94
|
+
| **Snowflake** | `/api/health/snowflake/query` | GET | Execute test query |
|
|
95
|
+
| **NextBank** | `/api/health/nextbank/ping` | GET | Ping NextBank API |
|
|
96
|
+
| **NextBank** | `/api/health/nextbank/auth` | GET | Test NextBank authentication |
|
|
97
|
+
| **All** | `/api/health/all` | GET | Run all health checks |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Dashboard Usage
|
|
102
|
+
|
|
103
|
+
### Accessing the Dashboard
|
|
104
|
+
|
|
105
|
+
Navigate to `/health` in your application:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
http://localhost:3000/health
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Dashboard Components
|
|
112
|
+
|
|
113
|
+
**Overall Status Card**
|
|
114
|
+
|
|
115
|
+
Shows the overall system status:
|
|
116
|
+
- **Status Badge**: `HEALTHY`, `UNHEALTHY`, `PARTIAL`, or `PENDING`
|
|
117
|
+
- **Response Time**: Total time to run all checks
|
|
118
|
+
- **"Run All" Button**: Execute all health checks with counter showing total number of checks
|
|
119
|
+
- **Last Updated**: Timestamp of last check
|
|
120
|
+
|
|
121
|
+
**Service Cards**
|
|
122
|
+
|
|
123
|
+
Each service has its own card:
|
|
124
|
+
- **Service Name**: Display name of the service
|
|
125
|
+
- **Status Badge**: Service-level status
|
|
126
|
+
- **Check Count**: Number of checks for this service
|
|
127
|
+
- **Individual Check Buttons**: Each check has a dedicated "Run Check" button
|
|
128
|
+
- **Check Results**: Results display below each button
|
|
129
|
+
|
|
130
|
+
**Individual Check Buttons**
|
|
131
|
+
|
|
132
|
+
Each health check has its own button:
|
|
133
|
+
- **"Run Check" Button**: Execute individual health check
|
|
134
|
+
- **Loading State**: Spinner shows while check is in progress
|
|
135
|
+
- **Disabled State**: Button is disabled during execution
|
|
136
|
+
|
|
137
|
+
**Check Results Display**
|
|
138
|
+
|
|
139
|
+
Results appear below each check button:
|
|
140
|
+
- **Status Badge**: Individual check status with color coding
|
|
141
|
+
- **Response Time**: Time taken for the check (in milliseconds)
|
|
142
|
+
- **Error Message**: Detailed error if check failed
|
|
143
|
+
- **HTTP Status Code**: HTTP response status
|
|
144
|
+
- **Timestamp**: When the check was executed
|
|
145
|
+
|
|
146
|
+
### Status Types
|
|
147
|
+
|
|
148
|
+
| Status | Description | Color |
|
|
149
|
+
|---------|-------------|--------|
|
|
150
|
+
| **healthy** | Service is functioning correctly | Green |
|
|
151
|
+
| **unhealthy** | Service is not functioning | Red |
|
|
152
|
+
| **error** | Check failed with an error | Red |
|
|
153
|
+
| **pending** | Check is in progress | Yellow |
|
|
154
|
+
| **unconfigured** | Service is not configured | Gray |
|
|
155
|
+
|
|
156
|
+
### Using the Dashboard Hook
|
|
157
|
+
|
|
158
|
+
Located at [`src/hooks/queries/use-health-checks.ts`](../../src/hooks/queries/use-health-checks.ts):
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
'use client'
|
|
162
|
+
|
|
163
|
+
import { useQuery } from '@tanstack/react-query'
|
|
164
|
+
|
|
165
|
+
export function useHealthChecks() {
|
|
166
|
+
return useQuery({
|
|
167
|
+
queryKey: ['health-checks'],
|
|
168
|
+
queryFn: async () => {
|
|
169
|
+
const response = await fetch('/api/health/all')
|
|
170
|
+
if (!response.ok) throw new Error('Failed to fetch health checks')
|
|
171
|
+
return response.json()
|
|
172
|
+
},
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Usage:**
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { useHealthChecks } from '@/hooks/queries/use-health-checks'
|
|
181
|
+
|
|
182
|
+
function MyComponent() {
|
|
183
|
+
const { data, isLoading, error, refetch } = useHealthChecks()
|
|
184
|
+
|
|
185
|
+
if (isLoading) return <div>Loading...</div>
|
|
186
|
+
if (error) return <div>Error: {error.message}</div>
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div>
|
|
190
|
+
<h1>Status: {data?.status}</h1>
|
|
191
|
+
<button onClick={() => refetch()}>Refresh</button>
|
|
192
|
+
</div>
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Adding New Health Checks
|
|
200
|
+
|
|
201
|
+
### Step 1: Define Service Configuration
|
|
202
|
+
|
|
203
|
+
Add to [`src/features/health-check/config.ts`](../../src/features/health-check/config.ts):
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
export const SERVICES = [
|
|
207
|
+
// ... existing services
|
|
208
|
+
{
|
|
209
|
+
name: 'Your Service',
|
|
210
|
+
icon: 'icon-name',
|
|
211
|
+
checks: [
|
|
212
|
+
{ name: 'Check Name', endpoint: '/your-service/check' },
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
] as const
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Step 2: Create Health Check Routes
|
|
219
|
+
|
|
220
|
+
Create [`src/server/api/routes/health/your-service.ts`](../../src/server/api/routes/health/your-service.ts):
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { Hono } from 'hono'
|
|
224
|
+
|
|
225
|
+
export const yourServiceHealthRoutes = new Hono()
|
|
226
|
+
|
|
227
|
+
yourServiceHealthRoutes.get('/check', async (c) => {
|
|
228
|
+
const start = performance.now()
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
// Perform your health check logic
|
|
232
|
+
const result = await checkYourService()
|
|
233
|
+
|
|
234
|
+
return c.json({
|
|
235
|
+
status: 'healthy',
|
|
236
|
+
responseTimeMs: Math.round(performance.now() - start),
|
|
237
|
+
message: 'Successfully checked service',
|
|
238
|
+
data: result,
|
|
239
|
+
})
|
|
240
|
+
} catch (error) {
|
|
241
|
+
return c.json(
|
|
242
|
+
{
|
|
243
|
+
status: 'error',
|
|
244
|
+
responseTimeMs: Math.round(performance.now() - start),
|
|
245
|
+
error: error instanceof Error ? error.message : 'Check failed',
|
|
246
|
+
},
|
|
247
|
+
500
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Step 3: Register Routes
|
|
254
|
+
|
|
255
|
+
Add to [`src/server/api/routes/health/index.ts`](../../src/server/api/routes/health/index.ts):
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { yourServiceHealthRoutes } from './your-service'
|
|
259
|
+
|
|
260
|
+
export const healthRoutes = new Hono()
|
|
261
|
+
|
|
262
|
+
// ... existing routes
|
|
263
|
+
healthRoutes.route('/your-service', yourServiceHealthRoutes)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Step 4: Add to Aggregated Check
|
|
267
|
+
|
|
268
|
+
Update the `/all` endpoint in [`src/server/api/routes/health/index.ts`](../../src/server/api/routes/health/index.ts):
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
healthRoutes.get('/all', async (c) => {
|
|
272
|
+
const start = performance.now()
|
|
273
|
+
const baseUrl = new URL(c.req.url).origin
|
|
274
|
+
|
|
275
|
+
const checks = [
|
|
276
|
+
// ... existing checks
|
|
277
|
+
{ name: 'Your Service Check', url: `${baseUrl}/api/health/your-service/check` },
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
// ... rest of the implementation
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Response Format Specification
|
|
287
|
+
|
|
288
|
+
### Standard Response Format
|
|
289
|
+
|
|
290
|
+
All health check endpoints follow this response format:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
interface HealthCheckResponse {
|
|
294
|
+
status: 'healthy' | 'error' | 'unconfigured' | 'pending'
|
|
295
|
+
responseTimeMs: number
|
|
296
|
+
message: string
|
|
297
|
+
data?: any
|
|
298
|
+
error?: string
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Successful Response
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"status": "healthy",
|
|
307
|
+
"responseTimeMs": 45,
|
|
308
|
+
"message": "Successfully retrieved current user",
|
|
309
|
+
"data": {
|
|
310
|
+
"userId": "user_abc123",
|
|
311
|
+
"hasOrg": true
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Error Response
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"status": "error",
|
|
321
|
+
"responseTimeMs": 123,
|
|
322
|
+
"error": "Connection failed"
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Unconfigured Response
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"status": "unconfigured",
|
|
331
|
+
"responseTimeMs": 5,
|
|
332
|
+
"message": "Service not configured"
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Aggregated Response
|
|
337
|
+
|
|
338
|
+
The `/api/health/all` endpoint returns:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
interface AggregatedHealthResponse {
|
|
342
|
+
status: 'healthy' | 'unhealthy' | 'partial'
|
|
343
|
+
responseTimeMs: number
|
|
344
|
+
timestamp: string
|
|
345
|
+
services: {
|
|
346
|
+
clerk: CheckResult[]
|
|
347
|
+
database: CheckResult[]
|
|
348
|
+
storage: CheckResult[]
|
|
349
|
+
edge: CheckResult[]
|
|
350
|
+
snowflake: CheckResult[]
|
|
351
|
+
nextbank: CheckResult[]
|
|
352
|
+
}
|
|
353
|
+
checks: CheckResult[]
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"status": "healthy",
|
|
360
|
+
"responseTimeMs": 1234,
|
|
361
|
+
"timestamp": "2025-12-29T08:00:00.000Z",
|
|
362
|
+
"services": {
|
|
363
|
+
"clerk": [
|
|
364
|
+
{
|
|
365
|
+
"name": "Clerk User",
|
|
366
|
+
"status": "healthy",
|
|
367
|
+
"responseTimeMs": 45,
|
|
368
|
+
"httpStatus": 200
|
|
369
|
+
}
|
|
370
|
+
],
|
|
371
|
+
"database": [
|
|
372
|
+
{
|
|
373
|
+
"name": "Database Write",
|
|
374
|
+
"status": "healthy",
|
|
375
|
+
"responseTimeMs": 123,
|
|
376
|
+
"httpStatus": 200
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
},
|
|
380
|
+
"checks": [
|
|
381
|
+
{
|
|
382
|
+
"name": "Clerk User",
|
|
383
|
+
"status": "healthy",
|
|
384
|
+
"responseTimeMs": 45,
|
|
385
|
+
"httpStatus": 200
|
|
386
|
+
}
|
|
387
|
+
]
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Health Check Types
|
|
394
|
+
|
|
395
|
+
Located at [`src/features/health-check/types.ts`](../../src/features/health-check/types.ts):
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
export type HealthStatus = 'healthy' | 'unhealthy' | 'error' | 'pending' | 'unconfigured'
|
|
399
|
+
|
|
400
|
+
export interface ServiceCheck {
|
|
401
|
+
name: string
|
|
402
|
+
endpoint: string
|
|
403
|
+
status: HealthStatus
|
|
404
|
+
responseTimeMs?: number
|
|
405
|
+
error?: string
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export interface ServiceHealth {
|
|
409
|
+
name: string
|
|
410
|
+
icon: string
|
|
411
|
+
status: HealthStatus
|
|
412
|
+
responseTimeMs: number
|
|
413
|
+
checks: ServiceCheck[]
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Service Configuration
|
|
420
|
+
|
|
421
|
+
Located at [`src/features/health-check/config.ts`](../../src/features/health-check/config.ts):
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
export const SERVICES = [
|
|
425
|
+
{
|
|
426
|
+
name: 'Clerk Authentication',
|
|
427
|
+
icon: 'key',
|
|
428
|
+
checks: [
|
|
429
|
+
{ name: 'Get Current User', endpoint: '/clerk/user' },
|
|
430
|
+
{ name: 'Get Org Membership', endpoint: '/clerk/org' },
|
|
431
|
+
{ name: 'List Org Members', endpoint: '/clerk/members' },
|
|
432
|
+
],
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: 'Supabase Database',
|
|
436
|
+
icon: 'database',
|
|
437
|
+
checks: [
|
|
438
|
+
{ name: 'Write Test Row', endpoint: '/database/write' },
|
|
439
|
+
{ name: 'Read Test Row', endpoint: '/database/read' },
|
|
440
|
+
{ name: 'Delete Test Row', endpoint: '/database/delete' },
|
|
441
|
+
],
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: 'Supabase Storage',
|
|
445
|
+
icon: 'folder',
|
|
446
|
+
checks: [
|
|
447
|
+
{ name: 'Upload Test File', endpoint: '/storage/upload' },
|
|
448
|
+
{ name: 'Download Test File', endpoint: '/storage/download' },
|
|
449
|
+
{ name: 'Delete Test File', endpoint: '/storage/delete' },
|
|
450
|
+
],
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: 'Supabase Edge Functions',
|
|
454
|
+
icon: 'zap',
|
|
455
|
+
checks: [
|
|
456
|
+
{ name: 'Ping Edge Function', endpoint: '/edge/ping' },
|
|
457
|
+
{ name: 'Test Auth Header', endpoint: '/edge/auth' },
|
|
458
|
+
],
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: 'Snowflake',
|
|
462
|
+
icon: 'snowflake',
|
|
463
|
+
checks: [
|
|
464
|
+
{ name: 'Test Connection', endpoint: '/snowflake/connect' },
|
|
465
|
+
{ name: 'Execute Query', endpoint: '/snowflake/query' },
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: 'NextBank',
|
|
470
|
+
icon: 'building',
|
|
471
|
+
checks: [
|
|
472
|
+
{ name: 'Ping API', endpoint: '/nextbank/ping' },
|
|
473
|
+
{ name: 'Test Authentication', endpoint: '/nextbank/auth' },
|
|
474
|
+
],
|
|
475
|
+
},
|
|
476
|
+
] as const
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Best Practices
|
|
482
|
+
|
|
483
|
+
### 1. Keep Checks Fast
|
|
484
|
+
|
|
485
|
+
Health checks should be quick and lightweight:
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
// ✅ Good - Fast check
|
|
489
|
+
const result = await db.select().from(table).limit(1)
|
|
490
|
+
|
|
491
|
+
// ❌ Bad - Slow check
|
|
492
|
+
const result = await db.select().from(table) // No limit
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### 2. Use Meaningful Messages
|
|
496
|
+
|
|
497
|
+
Provide clear, actionable error messages:
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
// ✅ Good - Clear message
|
|
501
|
+
return c.json({
|
|
502
|
+
status: 'error',
|
|
503
|
+
error: 'Failed to connect to database: connection timeout',
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
// ❌ Bad - Vague message
|
|
507
|
+
return c.json({
|
|
508
|
+
status: 'error',
|
|
509
|
+
error: 'Error',
|
|
510
|
+
})
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### 3. Handle Unconfigured Services
|
|
514
|
+
|
|
515
|
+
Gracefully handle services that aren't configured:
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
if (!env.SERVICE_API_KEY) {
|
|
519
|
+
return c.json({
|
|
520
|
+
status: 'unconfigured',
|
|
521
|
+
message: 'Service not configured',
|
|
522
|
+
})
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### 4. Measure Response Time
|
|
527
|
+
|
|
528
|
+
Always track response time for performance monitoring:
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
const start = performance.now()
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
// Perform check
|
|
535
|
+
const result = await performCheck()
|
|
536
|
+
|
|
537
|
+
return c.json({
|
|
538
|
+
status: 'healthy',
|
|
539
|
+
responseTimeMs: Math.round(performance.now() - start),
|
|
540
|
+
})
|
|
541
|
+
} catch (error) {
|
|
542
|
+
return c.json({
|
|
543
|
+
status: 'error',
|
|
544
|
+
responseTimeMs: Math.round(performance.now() - start),
|
|
545
|
+
error: error.message,
|
|
546
|
+
})
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## Troubleshooting
|
|
553
|
+
|
|
554
|
+
### Common Issues
|
|
555
|
+
|
|
556
|
+
**Issue: Health check shows "unconfigured"**
|
|
557
|
+
|
|
558
|
+
- Verify environment variables are set
|
|
559
|
+
- Check if service credentials are correct
|
|
560
|
+
- Ensure service is enabled in configuration
|
|
561
|
+
|
|
562
|
+
**Issue: Health check shows "error"**
|
|
563
|
+
|
|
564
|
+
- Check service logs for detailed error messages
|
|
565
|
+
- Verify network connectivity to the service
|
|
566
|
+
- Check if service is running and accessible
|
|
567
|
+
- For Supabase Database: Ensure migrations are applied (`pnpm db:push` or `pnpm db:migrate`) so `health_check_tests` table exists
|
|
568
|
+
|
|
569
|
+
**Issue: Health check is slow**
|
|
570
|
+
|
|
571
|
+
- Optimize the check query or operation
|
|
572
|
+
- Check if service is experiencing high load
|
|
573
|
+
- Consider caching results for frequently checked services
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## Related Documentation
|
|
578
|
+
|
|
579
|
+
- [API Layer](./api-layer.md) - API route implementation
|
|
580
|
+
- [Clerk Authentication](./clerk-authentication.md) - Clerk health checks
|
|
581
|
+
- [Supabase Integration](./supabase-integration.md) - Database and storage checks
|
|
582
|
+
- [Snowflake Integration](./snowflake-integration.md) - Snowflake health checks
|
|
583
|
+
- [State Management](./state-management.md) - Data fetching with TanStack Query
|