@girardmedia/bootspring 1.2.0 → 2.0.3
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/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
# Monitoring and Observability Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for application monitoring, logging, and observability.
|
|
4
|
-
|
|
5
|
-
## OpenTelemetry Setup
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// instrumentation.ts (Next.js)
|
|
9
|
-
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
10
|
-
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
|
|
11
|
-
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
12
|
-
import { Resource } from '@opentelemetry/resources'
|
|
13
|
-
import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'
|
|
14
|
-
|
|
15
|
-
export function register() {
|
|
16
|
-
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
17
|
-
const sdk = new NodeSDK({
|
|
18
|
-
resource: new Resource({
|
|
19
|
-
[SEMRESATTRS_SERVICE_NAME]: 'my-nextjs-app',
|
|
20
|
-
[SEMRESATTRS_SERVICE_VERSION]: process.env.npm_package_version ?? '1.0.0'
|
|
21
|
-
}),
|
|
22
|
-
traceExporter: new OTLPTraceExporter({
|
|
23
|
-
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT
|
|
24
|
-
}),
|
|
25
|
-
instrumentations: [getNodeAutoInstrumentations()]
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
sdk.start()
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Custom Metrics
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
// lib/metrics.ts
|
|
37
|
-
import { Counter, Histogram, Registry } from 'prom-client'
|
|
38
|
-
|
|
39
|
-
const register = new Registry()
|
|
40
|
-
|
|
41
|
-
// HTTP request metrics
|
|
42
|
-
export const httpRequestsTotal = new Counter({
|
|
43
|
-
name: 'http_requests_total',
|
|
44
|
-
help: 'Total number of HTTP requests',
|
|
45
|
-
labelNames: ['method', 'path', 'status'],
|
|
46
|
-
registers: [register]
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
export const httpRequestDuration = new Histogram({
|
|
50
|
-
name: 'http_request_duration_seconds',
|
|
51
|
-
help: 'HTTP request duration in seconds',
|
|
52
|
-
labelNames: ['method', 'path'],
|
|
53
|
-
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
|
|
54
|
-
registers: [register]
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
// Business metrics
|
|
58
|
-
export const userSignups = new Counter({
|
|
59
|
-
name: 'user_signups_total',
|
|
60
|
-
help: 'Total number of user signups',
|
|
61
|
-
labelNames: ['plan'],
|
|
62
|
-
registers: [register]
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
export const apiCalls = new Counter({
|
|
66
|
-
name: 'api_calls_total',
|
|
67
|
-
help: 'Total API calls',
|
|
68
|
-
labelNames: ['endpoint', 'status'],
|
|
69
|
-
registers: [register]
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
export { register }
|
|
73
|
-
|
|
74
|
-
// API endpoint for Prometheus
|
|
75
|
-
// app/api/metrics/route.ts
|
|
76
|
-
import { NextResponse } from 'next/server'
|
|
77
|
-
import { register } from '@/lib/metrics'
|
|
78
|
-
|
|
79
|
-
export async function GET() {
|
|
80
|
-
const metrics = await register.metrics()
|
|
81
|
-
return new NextResponse(metrics, {
|
|
82
|
-
headers: {
|
|
83
|
-
'Content-Type': register.contentType
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Request Tracing Middleware
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
// middleware.ts
|
|
93
|
-
import { NextRequest, NextResponse } from 'next/server'
|
|
94
|
-
import { httpRequestsTotal, httpRequestDuration } from '@/lib/metrics'
|
|
95
|
-
import { v4 as uuidv4 } from 'uuid'
|
|
96
|
-
|
|
97
|
-
export function middleware(request: NextRequest) {
|
|
98
|
-
const requestId = uuidv4()
|
|
99
|
-
const start = Date.now()
|
|
100
|
-
|
|
101
|
-
// Add request ID header
|
|
102
|
-
const response = NextResponse.next()
|
|
103
|
-
response.headers.set('X-Request-ID', requestId)
|
|
104
|
-
|
|
105
|
-
// Log request
|
|
106
|
-
console.log(JSON.stringify({
|
|
107
|
-
type: 'request',
|
|
108
|
-
requestId,
|
|
109
|
-
method: request.method,
|
|
110
|
-
path: request.nextUrl.pathname,
|
|
111
|
-
timestamp: new Date().toISOString()
|
|
112
|
-
}))
|
|
113
|
-
|
|
114
|
-
return response
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Middleware for API routes
|
|
118
|
-
// lib/withMetrics.ts
|
|
119
|
-
import { NextRequest, NextResponse } from 'next/server'
|
|
120
|
-
import { httpRequestsTotal, httpRequestDuration } from '@/lib/metrics'
|
|
121
|
-
|
|
122
|
-
type Handler = (req: NextRequest) => Promise<NextResponse>
|
|
123
|
-
|
|
124
|
-
export function withMetrics(handler: Handler): Handler {
|
|
125
|
-
return async (req: NextRequest) => {
|
|
126
|
-
const start = Date.now()
|
|
127
|
-
const path = req.nextUrl.pathname
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
const response = await handler(req)
|
|
131
|
-
const duration = (Date.now() - start) / 1000
|
|
132
|
-
|
|
133
|
-
httpRequestsTotal.inc({
|
|
134
|
-
method: req.method,
|
|
135
|
-
path,
|
|
136
|
-
status: response.status
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
httpRequestDuration.observe({ method: req.method, path }, duration)
|
|
140
|
-
|
|
141
|
-
return response
|
|
142
|
-
} catch (error) {
|
|
143
|
-
httpRequestsTotal.inc({
|
|
144
|
-
method: req.method,
|
|
145
|
-
path,
|
|
146
|
-
status: 500
|
|
147
|
-
})
|
|
148
|
-
throw error
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Error Tracking with Sentry
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
// sentry.client.config.ts
|
|
158
|
-
import * as Sentry from '@sentry/nextjs'
|
|
159
|
-
|
|
160
|
-
Sentry.init({
|
|
161
|
-
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
162
|
-
environment: process.env.NODE_ENV,
|
|
163
|
-
tracesSampleRate: 1.0,
|
|
164
|
-
replaysSessionSampleRate: 0.1,
|
|
165
|
-
replaysOnErrorSampleRate: 1.0,
|
|
166
|
-
integrations: [
|
|
167
|
-
Sentry.replayIntegration({
|
|
168
|
-
maskAllText: true,
|
|
169
|
-
blockAllMedia: true
|
|
170
|
-
})
|
|
171
|
-
]
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
// sentry.server.config.ts
|
|
175
|
-
import * as Sentry from '@sentry/nextjs'
|
|
176
|
-
|
|
177
|
-
Sentry.init({
|
|
178
|
-
dsn: process.env.SENTRY_DSN,
|
|
179
|
-
environment: process.env.NODE_ENV,
|
|
180
|
-
tracesSampleRate: 1.0
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
// lib/error-tracking.ts
|
|
184
|
-
import * as Sentry from '@sentry/nextjs'
|
|
185
|
-
|
|
186
|
-
export function captureError(error: Error, context?: Record<string, any>) {
|
|
187
|
-
Sentry.withScope(scope => {
|
|
188
|
-
if (context) {
|
|
189
|
-
scope.setExtras(context)
|
|
190
|
-
}
|
|
191
|
-
Sentry.captureException(error)
|
|
192
|
-
})
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export function setUserContext(user: { id: string; email: string }) {
|
|
196
|
-
Sentry.setUser({ id: user.id, email: user.email })
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export function addBreadcrumb(message: string, data?: Record<string, any>) {
|
|
200
|
-
Sentry.addBreadcrumb({
|
|
201
|
-
message,
|
|
202
|
-
data,
|
|
203
|
-
level: 'info'
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
## Health Check Endpoints
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
// app/api/health/route.ts
|
|
212
|
-
import { NextResponse } from 'next/server'
|
|
213
|
-
import { prisma } from '@/lib/db'
|
|
214
|
-
import { redis } from '@/lib/redis'
|
|
215
|
-
|
|
216
|
-
interface HealthCheck {
|
|
217
|
-
status: 'healthy' | 'unhealthy'
|
|
218
|
-
checks: {
|
|
219
|
-
[key: string]: {
|
|
220
|
-
status: 'pass' | 'fail'
|
|
221
|
-
responseTime?: number
|
|
222
|
-
error?: string
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
timestamp: string
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export async function GET() {
|
|
229
|
-
const checks: HealthCheck['checks'] = {}
|
|
230
|
-
|
|
231
|
-
// Database check
|
|
232
|
-
const dbStart = Date.now()
|
|
233
|
-
try {
|
|
234
|
-
await prisma.$queryRaw`SELECT 1`
|
|
235
|
-
checks.database = {
|
|
236
|
-
status: 'pass',
|
|
237
|
-
responseTime: Date.now() - dbStart
|
|
238
|
-
}
|
|
239
|
-
} catch (error) {
|
|
240
|
-
checks.database = {
|
|
241
|
-
status: 'fail',
|
|
242
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Redis check
|
|
247
|
-
const redisStart = Date.now()
|
|
248
|
-
try {
|
|
249
|
-
await redis.ping()
|
|
250
|
-
checks.redis = {
|
|
251
|
-
status: 'pass',
|
|
252
|
-
responseTime: Date.now() - redisStart
|
|
253
|
-
}
|
|
254
|
-
} catch (error) {
|
|
255
|
-
checks.redis = {
|
|
256
|
-
status: 'fail',
|
|
257
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const allHealthy = Object.values(checks).every(c => c.status === 'pass')
|
|
262
|
-
|
|
263
|
-
const response: HealthCheck = {
|
|
264
|
-
status: allHealthy ? 'healthy' : 'unhealthy',
|
|
265
|
-
checks,
|
|
266
|
-
timestamp: new Date().toISOString()
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return NextResponse.json(response, {
|
|
270
|
-
status: allHealthy ? 200 : 503
|
|
271
|
-
})
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// app/api/health/ready/route.ts
|
|
275
|
-
export async function GET() {
|
|
276
|
-
// Kubernetes readiness probe
|
|
277
|
-
return NextResponse.json({ ready: true })
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// app/api/health/live/route.ts
|
|
281
|
-
export async function GET() {
|
|
282
|
-
// Kubernetes liveness probe
|
|
283
|
-
return NextResponse.json({ alive: true })
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## Performance Monitoring
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
// lib/performance.ts
|
|
291
|
-
import { trace, SpanStatusCode } from '@opentelemetry/api'
|
|
292
|
-
|
|
293
|
-
const tracer = trace.getTracer('my-app')
|
|
294
|
-
|
|
295
|
-
export async function withSpan<T>(
|
|
296
|
-
name: string,
|
|
297
|
-
fn: () => Promise<T>,
|
|
298
|
-
attributes?: Record<string, string | number>
|
|
299
|
-
): Promise<T> {
|
|
300
|
-
return tracer.startActiveSpan(name, async span => {
|
|
301
|
-
if (attributes) {
|
|
302
|
-
span.setAttributes(attributes)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
try {
|
|
306
|
-
const result = await fn()
|
|
307
|
-
span.setStatus({ code: SpanStatusCode.OK })
|
|
308
|
-
return result
|
|
309
|
-
} catch (error) {
|
|
310
|
-
span.setStatus({
|
|
311
|
-
code: SpanStatusCode.ERROR,
|
|
312
|
-
message: error instanceof Error ? error.message : 'Unknown error'
|
|
313
|
-
})
|
|
314
|
-
span.recordException(error as Error)
|
|
315
|
-
throw error
|
|
316
|
-
} finally {
|
|
317
|
-
span.end()
|
|
318
|
-
}
|
|
319
|
-
})
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Usage
|
|
323
|
-
const users = await withSpan(
|
|
324
|
-
'fetch-users',
|
|
325
|
-
() => prisma.user.findMany(),
|
|
326
|
-
{ 'db.operation': 'findMany', 'db.table': 'users' }
|
|
327
|
-
)
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
## Dashboard Configuration
|
|
331
|
-
|
|
332
|
-
```yaml
|
|
333
|
-
# docker-compose.monitoring.yml
|
|
334
|
-
version: '3.8'
|
|
335
|
-
services:
|
|
336
|
-
prometheus:
|
|
337
|
-
image: prom/prometheus:latest
|
|
338
|
-
ports:
|
|
339
|
-
- '9090:9090'
|
|
340
|
-
volumes:
|
|
341
|
-
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
|
342
|
-
- prometheus_data:/prometheus
|
|
343
|
-
command:
|
|
344
|
-
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
345
|
-
- '--storage.tsdb.path=/prometheus'
|
|
346
|
-
|
|
347
|
-
grafana:
|
|
348
|
-
image: grafana/grafana:latest
|
|
349
|
-
ports:
|
|
350
|
-
- '3001:3000'
|
|
351
|
-
volumes:
|
|
352
|
-
- grafana_data:/var/lib/grafana
|
|
353
|
-
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
|
|
354
|
-
- ./grafana/datasources:/etc/grafana/provisioning/datasources
|
|
355
|
-
environment:
|
|
356
|
-
- GF_SECURITY_ADMIN_PASSWORD=admin
|
|
357
|
-
|
|
358
|
-
jaeger:
|
|
359
|
-
image: jaegertracing/all-in-one:latest
|
|
360
|
-
ports:
|
|
361
|
-
- '16686:16686'
|
|
362
|
-
- '4317:4317'
|
|
363
|
-
- '4318:4318'
|
|
364
|
-
|
|
365
|
-
volumes:
|
|
366
|
-
prometheus_data:
|
|
367
|
-
grafana_data:
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
```yaml
|
|
371
|
-
# prometheus.yml
|
|
372
|
-
global:
|
|
373
|
-
scrape_interval: 15s
|
|
374
|
-
|
|
375
|
-
scrape_configs:
|
|
376
|
-
- job_name: 'nextjs-app'
|
|
377
|
-
static_configs:
|
|
378
|
-
- targets: ['host.docker.internal:3000']
|
|
379
|
-
metrics_path: '/api/metrics'
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
## When to Use
|
|
383
|
-
|
|
384
|
-
- Production deployments
|
|
385
|
-
- Performance debugging
|
|
386
|
-
- Error tracking
|
|
387
|
-
- SLA monitoring
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# Vercel Deployment Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for deploying to Vercel.
|
|
4
|
-
|
|
5
|
-
## Environment Variables
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# .env.local (local development)
|
|
9
|
-
DATABASE_URL="postgresql://..."
|
|
10
|
-
NEXTAUTH_SECRET="..."
|
|
11
|
-
|
|
12
|
-
# Vercel Dashboard: Settings > Environment Variables
|
|
13
|
-
# Or via CLI:
|
|
14
|
-
vercel env add DATABASE_URL production
|
|
15
|
-
vercel env add NEXTAUTH_SECRET production
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## vercel.json Configuration
|
|
19
|
-
|
|
20
|
-
```json
|
|
21
|
-
{
|
|
22
|
-
"framework": "nextjs",
|
|
23
|
-
"regions": ["iad1"],
|
|
24
|
-
"headers": [
|
|
25
|
-
{
|
|
26
|
-
"source": "/api/(.*)",
|
|
27
|
-
"headers": [
|
|
28
|
-
{ "key": "Access-Control-Allow-Origin", "value": "*" }
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
],
|
|
32
|
-
"redirects": [
|
|
33
|
-
{
|
|
34
|
-
"source": "/old-page",
|
|
35
|
-
"destination": "/new-page",
|
|
36
|
-
"permanent": true
|
|
37
|
-
}
|
|
38
|
-
],
|
|
39
|
-
"rewrites": [
|
|
40
|
-
{
|
|
41
|
-
"source": "/api/proxy/:path*",
|
|
42
|
-
"destination": "https://api.external.com/:path*"
|
|
43
|
-
}
|
|
44
|
-
],
|
|
45
|
-
"crons": [
|
|
46
|
-
{
|
|
47
|
-
"path": "/api/cron/daily",
|
|
48
|
-
"schedule": "0 0 * * *"
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Preview Deployments
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
// lib/env.ts
|
|
58
|
-
export function getBaseUrl() {
|
|
59
|
-
// Vercel preview deployments
|
|
60
|
-
if (process.env.VERCEL_URL) {
|
|
61
|
-
return `https://${process.env.VERCEL_URL}`
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Production
|
|
65
|
-
if (process.env.NEXT_PUBLIC_APP_URL) {
|
|
66
|
-
return process.env.NEXT_PUBLIC_APP_URL
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Local development
|
|
70
|
-
return 'http://localhost:3000'
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Cron Jobs
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
// app/api/cron/cleanup/route.ts
|
|
78
|
-
import { NextResponse } from 'next/server'
|
|
79
|
-
|
|
80
|
-
export async function GET(request: Request) {
|
|
81
|
-
// Verify cron secret
|
|
82
|
-
const authHeader = request.headers.get('authorization')
|
|
83
|
-
|
|
84
|
-
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
85
|
-
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Run cleanup task
|
|
89
|
-
await prisma.session.deleteMany({
|
|
90
|
-
where: { expiresAt: { lt: new Date() } }
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
return NextResponse.json({ success: true })
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// vercel.json
|
|
97
|
-
{
|
|
98
|
-
"crons": [{
|
|
99
|
-
"path": "/api/cron/cleanup",
|
|
100
|
-
"schedule": "0 */6 * * *"
|
|
101
|
-
}]
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## Edge Functions
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
// app/api/geo/route.ts
|
|
109
|
-
export const runtime = 'edge'
|
|
110
|
-
export const preferredRegion = ['iad1', 'sfo1', 'fra1']
|
|
111
|
-
|
|
112
|
-
export async function GET(request: Request) {
|
|
113
|
-
// Access Vercel geo headers
|
|
114
|
-
const country = request.headers.get('x-vercel-ip-country')
|
|
115
|
-
const city = request.headers.get('x-vercel-ip-city')
|
|
116
|
-
|
|
117
|
-
return Response.json({ country, city })
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## Build Configuration
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// next.config.js
|
|
125
|
-
module.exports = {
|
|
126
|
-
// Standalone output for containers
|
|
127
|
-
output: 'standalone',
|
|
128
|
-
|
|
129
|
-
// Image optimization
|
|
130
|
-
images: {
|
|
131
|
-
domains: ['cdn.example.com'],
|
|
132
|
-
remotePatterns: [
|
|
133
|
-
{ protocol: 'https', hostname: '**.example.com' }
|
|
134
|
-
]
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
// Environment at build time
|
|
138
|
-
env: {
|
|
139
|
-
BUILD_TIME: new Date().toISOString()
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## Deploy Hooks
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
# Trigger deploy via webhook
|
|
148
|
-
curl -X POST https://api.vercel.com/v1/integrations/deploy/prj_xxx/hook_xxx
|
|
149
|
-
|
|
150
|
-
# In CI/CD
|
|
151
|
-
- name: Deploy to Vercel
|
|
152
|
-
run: curl -X POST ${{ secrets.VERCEL_DEPLOY_HOOK }}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
## When to Use
|
|
156
|
-
|
|
157
|
-
- Next.js production
|
|
158
|
-
- Preview environments
|
|
159
|
-
- Scheduled tasks
|
|
160
|
-
- Edge computing
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# Resend Email Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for sending emails with Resend.
|
|
4
|
-
|
|
5
|
-
## Client Setup
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// lib/email.ts
|
|
9
|
-
import { Resend } from 'resend'
|
|
10
|
-
|
|
11
|
-
export const resend = new Resend(process.env.RESEND_API_KEY)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Send Basic Email
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
// lib/email.ts
|
|
18
|
-
export async function sendEmail({
|
|
19
|
-
to,
|
|
20
|
-
subject,
|
|
21
|
-
html
|
|
22
|
-
}: {
|
|
23
|
-
to: string
|
|
24
|
-
subject: string
|
|
25
|
-
html: string
|
|
26
|
-
}) {
|
|
27
|
-
const { data, error } = await resend.emails.send({
|
|
28
|
-
from: 'Your App <noreply@yourapp.com>',
|
|
29
|
-
to,
|
|
30
|
-
subject,
|
|
31
|
-
html
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
if (error) {
|
|
35
|
-
console.error('Email error:', error)
|
|
36
|
-
throw error
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return data
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## With React Email
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
// lib/email.ts
|
|
47
|
-
import { render } from '@react-email/render'
|
|
48
|
-
import { WelcomeEmail } from '@/emails/welcome'
|
|
49
|
-
|
|
50
|
-
export async function sendWelcomeEmail(to: string, name: string) {
|
|
51
|
-
const html = await render(
|
|
52
|
-
WelcomeEmail({ name, actionUrl: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard` })
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
return resend.emails.send({
|
|
56
|
-
from: 'Your App <welcome@yourapp.com>',
|
|
57
|
-
to,
|
|
58
|
-
subject: 'Welcome to Our App!',
|
|
59
|
-
html
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Email Service Pattern
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
// lib/email-service.ts
|
|
68
|
-
import { resend } from './email'
|
|
69
|
-
import { render } from '@react-email/render'
|
|
70
|
-
import { WelcomeEmail } from '@/emails/welcome'
|
|
71
|
-
import { PasswordResetEmail } from '@/emails/password-reset'
|
|
72
|
-
import { InviteEmail } from '@/emails/invite'
|
|
73
|
-
|
|
74
|
-
export const emailService = {
|
|
75
|
-
async sendWelcome(to: string, name: string) {
|
|
76
|
-
const html = await render(
|
|
77
|
-
WelcomeEmail({
|
|
78
|
-
name,
|
|
79
|
-
actionUrl: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`
|
|
80
|
-
})
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
return resend.emails.send({
|
|
84
|
-
from: 'Your App <welcome@yourapp.com>',
|
|
85
|
-
to,
|
|
86
|
-
subject: 'Welcome to Our App!',
|
|
87
|
-
html
|
|
88
|
-
})
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
async sendPasswordReset(to: string, resetUrl: string) {
|
|
92
|
-
const html = await render(PasswordResetEmail({ resetUrl }))
|
|
93
|
-
|
|
94
|
-
return resend.emails.send({
|
|
95
|
-
from: 'Your App <security@yourapp.com>',
|
|
96
|
-
to,
|
|
97
|
-
subject: 'Reset Your Password',
|
|
98
|
-
html
|
|
99
|
-
})
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
async sendInvite(
|
|
103
|
-
to: string,
|
|
104
|
-
inviterName: string,
|
|
105
|
-
teamName: string,
|
|
106
|
-
inviteUrl: string
|
|
107
|
-
) {
|
|
108
|
-
const html = await render(
|
|
109
|
-
InviteEmail({ inviterName, teamName, inviteUrl })
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
return resend.emails.send({
|
|
113
|
-
from: 'Your App <invites@yourapp.com>',
|
|
114
|
-
to,
|
|
115
|
-
subject: `${inviterName} invited you to join ${teamName}`,
|
|
116
|
-
html
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Batch Sending
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
// lib/email.ts
|
|
126
|
-
export async function sendBatch(
|
|
127
|
-
emails: { to: string; subject: string; html: string }[]
|
|
128
|
-
) {
|
|
129
|
-
return resend.batch.send(
|
|
130
|
-
emails.map((email) => ({
|
|
131
|
-
from: 'Your App <noreply@yourapp.com>',
|
|
132
|
-
...email
|
|
133
|
-
}))
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## When to Use
|
|
139
|
-
|
|
140
|
-
- Transactional emails
|
|
141
|
-
- Welcome sequences
|
|
142
|
-
- Password resets
|
|
143
|
-
- Notifications
|