@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.
Files changed (253) hide show
  1. package/README.md +107 -14
  2. package/bin/bootspring.js +166 -27
  3. package/cli/agent.js +189 -17
  4. package/cli/analyze.js +499 -0
  5. package/cli/audit.js +557 -0
  6. package/cli/auth.js +495 -38
  7. package/cli/billing.js +302 -0
  8. package/cli/build.js +695 -0
  9. package/cli/business.js +109 -26
  10. package/cli/checkpoint-utils.js +168 -0
  11. package/cli/checkpoint.js +639 -0
  12. package/cli/cloud-sync.js +447 -0
  13. package/cli/content.js +198 -0
  14. package/cli/context.js +1 -1
  15. package/cli/deploy.js +543 -0
  16. package/cli/fundraise.js +112 -50
  17. package/cli/github-cmd.js +435 -0
  18. package/cli/health.js +477 -0
  19. package/cli/init.js +84 -13
  20. package/cli/legal.js +107 -95
  21. package/cli/log.js +2 -2
  22. package/cli/loop.js +976 -73
  23. package/cli/manager.js +711 -0
  24. package/cli/metrics.js +480 -0
  25. package/cli/monitor.js +812 -0
  26. package/cli/onboard.js +521 -0
  27. package/cli/orchestrator.js +12 -24
  28. package/cli/prd.js +594 -0
  29. package/cli/preseed-start.js +1483 -0
  30. package/cli/preseed.js +2302 -0
  31. package/cli/project.js +436 -0
  32. package/cli/quality.js +233 -0
  33. package/cli/security.js +913 -0
  34. package/cli/seed.js +1441 -5
  35. package/cli/skill.js +273 -211
  36. package/cli/suggest.js +989 -0
  37. package/cli/switch.js +453 -0
  38. package/cli/visualize.js +527 -0
  39. package/cli/watch.js +769 -0
  40. package/cli/workspace.js +607 -0
  41. package/core/analyze-workflow.js +1134 -0
  42. package/core/api-client.js +535 -22
  43. package/core/audit-workflow.js +1350 -0
  44. package/core/build-orchestrator.js +480 -0
  45. package/core/build-state.js +577 -0
  46. package/core/checkpoint-engine.js +408 -0
  47. package/core/config.js +1109 -26
  48. package/core/context-loader.js +21 -1
  49. package/core/deploy-workflow.js +836 -0
  50. package/core/entitlements.js +93 -22
  51. package/core/github-sync.js +610 -0
  52. package/core/index.js +8 -1
  53. package/core/ingest.js +1111 -0
  54. package/core/metrics-engine.js +768 -0
  55. package/core/onboard-workflow.js +1007 -0
  56. package/core/preseed-workflow.js +934 -0
  57. package/core/preseed.js +1617 -0
  58. package/core/project-context.js +325 -0
  59. package/core/project-state.js +694 -0
  60. package/core/r2-sync.js +583 -0
  61. package/core/scaffold.js +525 -7
  62. package/core/session.js +258 -0
  63. package/core/task-extractor.js +758 -0
  64. package/core/telemetry.js +28 -6
  65. package/core/tier-enforcement.js +737 -0
  66. package/core/utils.js +38 -14
  67. package/generators/questionnaire.js +15 -12
  68. package/generators/sections/ai.js +7 -7
  69. package/generators/sections/content.js +300 -0
  70. package/generators/sections/index.js +3 -0
  71. package/generators/sections/plugins.js +7 -6
  72. package/generators/templates/build-planning.template.js +596 -0
  73. package/generators/templates/content.template.js +819 -0
  74. package/generators/templates/index.js +2 -1
  75. package/hooks/git-autopilot.js +1250 -0
  76. package/hooks/index.js +9 -0
  77. package/intelligence/agent-collab.js +2057 -0
  78. package/intelligence/auto-suggest.js +634 -0
  79. package/intelligence/content-gen.js +1589 -0
  80. package/intelligence/cross-project.js +1647 -0
  81. package/intelligence/index.js +184 -0
  82. package/intelligence/learning/insights.json +517 -7
  83. package/intelligence/learning/pattern-learner.js +1008 -14
  84. package/intelligence/memory/decision-tracker.js +1431 -31
  85. package/intelligence/memory/decisions.jsonl +0 -0
  86. package/intelligence/orchestrator.js +2896 -1
  87. package/intelligence/prd.js +92 -1
  88. package/intelligence/recommendation-weights.json +14 -2
  89. package/intelligence/recommendations.js +463 -9
  90. package/intelligence/workflow-composer.js +1451 -0
  91. package/marketplace/index.d.ts +324 -0
  92. package/marketplace/index.js +1921 -0
  93. package/mcp/contracts/mcp-contract.v1.json +342 -4
  94. package/mcp/registry.js +680 -3
  95. package/mcp/response-formatter.js +23 -0
  96. package/mcp/tools/assist-tool.js +78 -4
  97. package/mcp/tools/autopilot-tool.js +408 -0
  98. package/mcp/tools/content-tool.js +571 -0
  99. package/mcp/tools/dashboard-tool.js +251 -5
  100. package/mcp/tools/mvp-tool.js +344 -0
  101. package/mcp/tools/plugin-tool.js +23 -1
  102. package/mcp/tools/prd-tool.js +579 -0
  103. package/mcp/tools/seed-tool.js +447 -0
  104. package/mcp/tools/skill-tool.js +43 -14
  105. package/mcp/tools/suggest-tool.js +147 -0
  106. package/package.json +15 -6
  107. package/agents/README.md +0 -93
  108. package/agents/ai-integration-expert/context.md +0 -386
  109. package/agents/api-expert/context.md +0 -416
  110. package/agents/architecture-expert/context.md +0 -454
  111. package/agents/auth-expert/context.md +0 -399
  112. package/agents/backend-expert/context.md +0 -483
  113. package/agents/business-strategy-expert/context.md +0 -180
  114. package/agents/code-review-expert/context.md +0 -365
  115. package/agents/competitive-analysis-expert/context.md +0 -239
  116. package/agents/data-modeling-expert/context.md +0 -352
  117. package/agents/database-expert/context.md +0 -250
  118. package/agents/devops-expert/context.md +0 -446
  119. package/agents/email-expert/context.md +0 -379
  120. package/agents/financial-expert/context.md +0 -213
  121. package/agents/frontend-expert/context.md +0 -364
  122. package/agents/fundraising-expert/context.md +0 -257
  123. package/agents/growth-expert/context.md +0 -249
  124. package/agents/index.js +0 -140
  125. package/agents/investor-relations-expert/context.md +0 -266
  126. package/agents/legal-expert/context.md +0 -284
  127. package/agents/marketing-expert/context.md +0 -236
  128. package/agents/monitoring-expert/context.md +0 -362
  129. package/agents/operations-expert/context.md +0 -279
  130. package/agents/partnerships-expert/context.md +0 -286
  131. package/agents/payment-expert/context.md +0 -340
  132. package/agents/performance-expert/context.md +0 -377
  133. package/agents/private-equity-expert/context.md +0 -246
  134. package/agents/railway-expert/context.md +0 -284
  135. package/agents/research-expert/context.md +0 -245
  136. package/agents/sales-expert/context.md +0 -241
  137. package/agents/security-expert/context.md +0 -343
  138. package/agents/testing-expert/context.md +0 -414
  139. package/agents/ui-ux-expert/context.md +0 -448
  140. package/agents/vercel-expert/context.md +0 -426
  141. package/skills/index.js +0 -787
  142. package/skills/patterns/README.md +0 -163
  143. package/skills/patterns/ai/agents.md +0 -281
  144. package/skills/patterns/ai/claude.md +0 -138
  145. package/skills/patterns/ai/embeddings.md +0 -150
  146. package/skills/patterns/ai/rag.md +0 -266
  147. package/skills/patterns/ai/streaming.md +0 -170
  148. package/skills/patterns/ai/structured-output.md +0 -162
  149. package/skills/patterns/ai/tools.md +0 -154
  150. package/skills/patterns/analytics/tracking.md +0 -220
  151. package/skills/patterns/api/errors.md +0 -296
  152. package/skills/patterns/api/graphql.md +0 -440
  153. package/skills/patterns/api/middleware.md +0 -279
  154. package/skills/patterns/api/openapi.md +0 -285
  155. package/skills/patterns/api/rate-limiting.md +0 -231
  156. package/skills/patterns/api/route-handler.md +0 -217
  157. package/skills/patterns/api/server-action.md +0 -249
  158. package/skills/patterns/api/versioning.md +0 -443
  159. package/skills/patterns/api/webhooks.md +0 -247
  160. package/skills/patterns/auth/clerk.md +0 -132
  161. package/skills/patterns/auth/mfa.md +0 -313
  162. package/skills/patterns/auth/nextauth.md +0 -140
  163. package/skills/patterns/auth/oauth.md +0 -237
  164. package/skills/patterns/auth/rbac.md +0 -152
  165. package/skills/patterns/auth/session-management.md +0 -367
  166. package/skills/patterns/auth/session.md +0 -120
  167. package/skills/patterns/database/audit.md +0 -177
  168. package/skills/patterns/database/migrations.md +0 -177
  169. package/skills/patterns/database/pagination.md +0 -230
  170. package/skills/patterns/database/pooling.md +0 -357
  171. package/skills/patterns/database/prisma.md +0 -180
  172. package/skills/patterns/database/relations.md +0 -187
  173. package/skills/patterns/database/seeding.md +0 -246
  174. package/skills/patterns/database/soft-delete.md +0 -153
  175. package/skills/patterns/database/transactions.md +0 -162
  176. package/skills/patterns/deployment/ci-cd.md +0 -231
  177. package/skills/patterns/deployment/docker.md +0 -188
  178. package/skills/patterns/deployment/monitoring.md +0 -387
  179. package/skills/patterns/deployment/vercel.md +0 -160
  180. package/skills/patterns/email/resend.md +0 -143
  181. package/skills/patterns/email/templates.md +0 -245
  182. package/skills/patterns/email/transactional.md +0 -503
  183. package/skills/patterns/email/verification.md +0 -176
  184. package/skills/patterns/files/download.md +0 -243
  185. package/skills/patterns/files/upload.md +0 -239
  186. package/skills/patterns/i18n/nextintl.md +0 -188
  187. package/skills/patterns/logging/structured.md +0 -292
  188. package/skills/patterns/notifications/email-queue.md +0 -248
  189. package/skills/patterns/notifications/push.md +0 -279
  190. package/skills/patterns/payments/checkout.md +0 -303
  191. package/skills/patterns/payments/invoices.md +0 -287
  192. package/skills/patterns/payments/portal.md +0 -245
  193. package/skills/patterns/payments/stripe.md +0 -272
  194. package/skills/patterns/payments/subscriptions.md +0 -300
  195. package/skills/patterns/payments/usage.md +0 -279
  196. package/skills/patterns/performance/caching.md +0 -276
  197. package/skills/patterns/performance/code-splitting.md +0 -233
  198. package/skills/patterns/performance/edge.md +0 -254
  199. package/skills/patterns/performance/isr.md +0 -266
  200. package/skills/patterns/performance/lazy-loading.md +0 -281
  201. package/skills/patterns/realtime/sse.md +0 -327
  202. package/skills/patterns/realtime/websockets.md +0 -336
  203. package/skills/patterns/search/filtering.md +0 -329
  204. package/skills/patterns/search/fulltext.md +0 -260
  205. package/skills/patterns/security/audit-logging.md +0 -444
  206. package/skills/patterns/security/csrf.md +0 -234
  207. package/skills/patterns/security/headers.md +0 -252
  208. package/skills/patterns/security/sanitization.md +0 -258
  209. package/skills/patterns/security/secrets.md +0 -261
  210. package/skills/patterns/security/validation.md +0 -268
  211. package/skills/patterns/security/xss.md +0 -229
  212. package/skills/patterns/seo/metadata.md +0 -252
  213. package/skills/patterns/state/context.md +0 -349
  214. package/skills/patterns/state/react-query.md +0 -313
  215. package/skills/patterns/state/url-state.md +0 -482
  216. package/skills/patterns/state/zustand.md +0 -262
  217. package/skills/patterns/testing/api.md +0 -259
  218. package/skills/patterns/testing/component.md +0 -233
  219. package/skills/patterns/testing/coverage.md +0 -207
  220. package/skills/patterns/testing/fixtures.md +0 -225
  221. package/skills/patterns/testing/integration.md +0 -436
  222. package/skills/patterns/testing/mocking.md +0 -177
  223. package/skills/patterns/testing/playwright.md +0 -162
  224. package/skills/patterns/testing/snapshot.md +0 -175
  225. package/skills/patterns/testing/vitest.md +0 -307
  226. package/skills/patterns/ui/accordions.md +0 -395
  227. package/skills/patterns/ui/cards.md +0 -299
  228. package/skills/patterns/ui/dropdowns.md +0 -476
  229. package/skills/patterns/ui/empty-states.md +0 -320
  230. package/skills/patterns/ui/forms.md +0 -405
  231. package/skills/patterns/ui/inputs.md +0 -319
  232. package/skills/patterns/ui/layouts.md +0 -282
  233. package/skills/patterns/ui/loading.md +0 -291
  234. package/skills/patterns/ui/modals.md +0 -338
  235. package/skills/patterns/ui/navigation.md +0 -374
  236. package/skills/patterns/ui/tables.md +0 -407
  237. package/skills/patterns/ui/toasts.md +0 -300
  238. package/skills/patterns/ui/tooltips.md +0 -396
  239. package/skills/patterns/utils/dates.md +0 -435
  240. package/skills/patterns/utils/errors.md +0 -451
  241. package/skills/patterns/utils/formatting.md +0 -345
  242. package/skills/patterns/utils/validation.md +0 -434
  243. package/templates/bootspring.config.js +0 -83
  244. package/templates/business/business-model-canvas.md +0 -246
  245. package/templates/business/business-plan.md +0 -266
  246. package/templates/business/competitive-analysis.md +0 -312
  247. package/templates/fundraising/data-room-checklist.md +0 -300
  248. package/templates/fundraising/investor-research.md +0 -243
  249. package/templates/fundraising/pitch-deck-outline.md +0 -253
  250. package/templates/legal/gdpr-checklist.md +0 -339
  251. package/templates/legal/privacy-policy.md +0 -285
  252. package/templates/legal/terms-of-service.md +0 -222
  253. package/templates/mcp.json +0 -9
@@ -1,367 +0,0 @@
1
- # Session Management Patterns
2
-
3
- Patterns for managing user sessions.
4
-
5
- ## JWT Session Configuration
6
-
7
- ```typescript
8
- // auth.config.ts
9
- import { NextAuthConfig } from 'next-auth'
10
-
11
- export const authConfig: NextAuthConfig = {
12
- session: {
13
- strategy: 'jwt',
14
- maxAge: 30 * 24 * 60 * 60, // 30 days
15
- updateAge: 24 * 60 * 60 // 24 hours
16
- },
17
- callbacks: {
18
- jwt({ token, user, trigger, session }) {
19
- // Initial sign in
20
- if (user) {
21
- token.id = user.id
22
- token.role = user.role
23
- token.organizationId = user.organizationId
24
- }
25
-
26
- // Update session when trigger is 'update'
27
- if (trigger === 'update' && session) {
28
- token.name = session.name
29
- token.organizationId = session.organizationId
30
- }
31
-
32
- return token
33
- },
34
- session({ session, token }) {
35
- session.user.id = token.id as string
36
- session.user.role = token.role as string
37
- session.user.organizationId = token.organizationId as string
38
-
39
- return session
40
- }
41
- }
42
- }
43
- ```
44
-
45
- ## Database Sessions
46
-
47
- ```typescript
48
- // auth.ts
49
- import NextAuth from 'next-auth'
50
- import { PrismaAdapter } from '@auth/prisma-adapter'
51
- import { prisma } from '@/lib/db'
52
-
53
- export const { handlers, auth, signIn, signOut } = NextAuth({
54
- adapter: PrismaAdapter(prisma),
55
- session: {
56
- strategy: 'database',
57
- maxAge: 30 * 24 * 60 * 60, // 30 days
58
- updateAge: 24 * 60 * 60 // Update session every 24 hours
59
- },
60
- callbacks: {
61
- session({ session, user }) {
62
- session.user.id = user.id
63
- session.user.role = user.role
64
- return session
65
- }
66
- },
67
- providers: [
68
- // ... providers
69
- ]
70
- })
71
- ```
72
-
73
- ## Session Refresh
74
-
75
- ```typescript
76
- // lib/auth/session.ts
77
- import { auth, signIn } from '@/auth'
78
- import { redirect } from 'next/navigation'
79
-
80
- export async function getSession() {
81
- const session = await auth()
82
-
83
- if (!session) {
84
- return null
85
- }
86
-
87
- // Check if session is about to expire
88
- const expiresAt = new Date(session.expires)
89
- const now = new Date()
90
- const daysUntilExpiry = (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
91
-
92
- if (daysUntilExpiry < 7) {
93
- // Session will expire soon, refresh it
94
- // This triggers the updateAge callback
95
- }
96
-
97
- return session
98
- }
99
-
100
- export async function requireAuth() {
101
- const session = await getSession()
102
-
103
- if (!session) {
104
- redirect('/login')
105
- }
106
-
107
- return session
108
- }
109
-
110
- export async function requireRole(requiredRoles: string[]) {
111
- const session = await requireAuth()
112
-
113
- if (!requiredRoles.includes(session.user.role)) {
114
- redirect('/unauthorized')
115
- }
116
-
117
- return session
118
- }
119
- ```
120
-
121
- ## Session Update
122
-
123
- ```tsx
124
- // components/UpdateSessionForm.tsx
125
- 'use client'
126
-
127
- import { useSession } from 'next-auth/react'
128
- import { useState } from 'react'
129
-
130
- export function UpdateSessionForm() {
131
- const { data: session, update } = useSession()
132
- const [name, setName] = useState(session?.user?.name ?? '')
133
-
134
- const handleSubmit = async (e: React.FormEvent) => {
135
- e.preventDefault()
136
-
137
- // Update user in database
138
- await fetch('/api/user/profile', {
139
- method: 'PATCH',
140
- body: JSON.stringify({ name })
141
- })
142
-
143
- // Update session
144
- await update({ name })
145
- }
146
-
147
- return (
148
- <form onSubmit={handleSubmit}>
149
- <input
150
- type="text"
151
- value={name}
152
- onChange={e => setName(e.target.value)}
153
- />
154
- <button type="submit">Update</button>
155
- </form>
156
- )
157
- }
158
- ```
159
-
160
- ## Active Sessions Management
161
-
162
- ```typescript
163
- // prisma/schema.prisma
164
- model Session {
165
- id String @id @default(cuid())
166
- sessionToken String @unique
167
- userId String
168
- expires DateTime
169
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
170
-
171
- // Additional fields for session management
172
- userAgent String?
173
- ipAddress String?
174
- lastActive DateTime @default(now())
175
- createdAt DateTime @default(now())
176
-
177
- @@index([userId])
178
- }
179
- ```
180
-
181
- ```typescript
182
- // lib/auth/sessions.ts
183
- import { prisma } from '@/lib/db'
184
- import { headers } from 'next/headers'
185
-
186
- export async function getUserSessions(userId: string) {
187
- return prisma.session.findMany({
188
- where: { userId },
189
- orderBy: { lastActive: 'desc' },
190
- select: {
191
- id: true,
192
- userAgent: true,
193
- ipAddress: true,
194
- lastActive: true,
195
- createdAt: true,
196
- expires: true
197
- }
198
- })
199
- }
200
-
201
- export async function revokeSession(sessionId: string, userId: string) {
202
- return prisma.session.deleteMany({
203
- where: {
204
- id: sessionId,
205
- userId // Ensure user owns the session
206
- }
207
- })
208
- }
209
-
210
- export async function revokeAllOtherSessions(
211
- currentSessionToken: string,
212
- userId: string
213
- ) {
214
- return prisma.session.deleteMany({
215
- where: {
216
- userId,
217
- NOT: { sessionToken: currentSessionToken }
218
- }
219
- })
220
- }
221
-
222
- export async function updateSessionActivity(sessionToken: string) {
223
- const headersList = headers()
224
-
225
- await prisma.session.update({
226
- where: { sessionToken },
227
- data: {
228
- lastActive: new Date(),
229
- userAgent: headersList.get('user-agent'),
230
- ipAddress:
231
- headersList.get('x-forwarded-for') ??
232
- headersList.get('x-real-ip')
233
- }
234
- })
235
- }
236
- ```
237
-
238
- ## Session List UI
239
-
240
- ```tsx
241
- // app/settings/sessions/page.tsx
242
- import { auth } from '@/auth'
243
- import { getUserSessions, revokeSession } from '@/lib/auth/sessions'
244
- import { formatDistanceToNow } from 'date-fns'
245
- import { UAParser } from 'ua-parser-js'
246
-
247
- export default async function SessionsPage() {
248
- const session = await auth()
249
- if (!session) return null
250
-
251
- const sessions = await getUserSessions(session.user.id)
252
-
253
- return (
254
- <div className="space-y-6">
255
- <h2 className="text-xl font-semibold">Active Sessions</h2>
256
-
257
- <div className="space-y-4">
258
- {sessions.map(s => {
259
- const ua = new UAParser(s.userAgent ?? '').getResult()
260
- const isCurrent = s.id === session.sessionId
261
-
262
- return (
263
- <div
264
- key={s.id}
265
- className="flex items-center justify-between rounded-lg border p-4"
266
- >
267
- <div>
268
- <div className="flex items-center gap-2">
269
- <span className="font-medium">
270
- {ua.browser.name} on {ua.os.name}
271
- </span>
272
- {isCurrent && (
273
- <span className="rounded bg-green-100 px-2 py-0.5 text-xs text-green-800">
274
- Current
275
- </span>
276
- )}
277
- </div>
278
- <div className="text-sm text-gray-500">
279
- <span>{s.ipAddress}</span>
280
- <span className="mx-2">•</span>
281
- <span>
282
- Last active {formatDistanceToNow(s.lastActive, { addSuffix: true })}
283
- </span>
284
- </div>
285
- </div>
286
-
287
- {!isCurrent && (
288
- <form action={async () => {
289
- 'use server'
290
- await revokeSession(s.id, session.user.id)
291
- }}>
292
- <button
293
- type="submit"
294
- className="text-sm text-red-600 hover:underline"
295
- >
296
- Revoke
297
- </button>
298
- </form>
299
- )}
300
- </div>
301
- )
302
- })}
303
- </div>
304
-
305
- <form action={async () => {
306
- 'use server'
307
- await revokeAllOtherSessions(session.sessionToken!, session.user.id)
308
- }}>
309
- <button
310
- type="submit"
311
- className="text-sm text-red-600 hover:underline"
312
- >
313
- Sign out all other sessions
314
- </button>
315
- </form>
316
- </div>
317
- )
318
- }
319
- ```
320
-
321
- ## Session Middleware
322
-
323
- ```typescript
324
- // middleware.ts
325
- import { NextRequest, NextResponse } from 'next/server'
326
- import { auth } from '@/auth'
327
-
328
- export async function middleware(request: NextRequest) {
329
- const session = await auth()
330
-
331
- // Check if session exists for protected routes
332
- if (request.nextUrl.pathname.startsWith('/dashboard')) {
333
- if (!session) {
334
- return NextResponse.redirect(new URL('/login', request.url))
335
- }
336
-
337
- // Check session expiry
338
- if (new Date(session.expires) < new Date()) {
339
- const response = NextResponse.redirect(new URL('/login', request.url))
340
- // Clear invalid session cookie
341
- response.cookies.delete('next-auth.session-token')
342
- return response
343
- }
344
- }
345
-
346
- // Add session info to headers for logging
347
- const requestHeaders = new Headers(request.headers)
348
- if (session?.user) {
349
- requestHeaders.set('x-user-id', session.user.id)
350
- }
351
-
352
- return NextResponse.next({
353
- request: { headers: requestHeaders }
354
- })
355
- }
356
-
357
- export const config = {
358
- matcher: ['/dashboard/:path*', '/api/:path*']
359
- }
360
- ```
361
-
362
- ## When to Use
363
-
364
- - User authentication
365
- - Multi-device management
366
- - Security auditing
367
- - Session invalidation
@@ -1,120 +0,0 @@
1
- # Session Management Patterns
2
-
3
- Patterns for managing user sessions with JWT and cookies.
4
-
5
- ## JWT Session Creation
6
-
7
- ```typescript
8
- // lib/session.ts
9
- import { SignJWT, jwtVerify } from 'jose'
10
- import { cookies } from 'next/headers'
11
-
12
- const secret = new TextEncoder().encode(process.env.JWT_SECRET)
13
- const COOKIE_NAME = 'session'
14
-
15
- export async function createSession(userId: string) {
16
- const token = await new SignJWT({ userId })
17
- .setProtectedHeader({ alg: 'HS256' })
18
- .setIssuedAt()
19
- .setExpirationTime('7d')
20
- .sign(secret)
21
-
22
- cookies().set(COOKIE_NAME, token, {
23
- httpOnly: true,
24
- secure: process.env.NODE_ENV === 'production',
25
- sameSite: 'lax',
26
- maxAge: 60 * 60 * 24 * 7, // 7 days
27
- path: '/'
28
- })
29
-
30
- return token
31
- }
32
- ```
33
-
34
- ## Session Verification
35
-
36
- ```typescript
37
- // lib/session.ts
38
- export async function getSession() {
39
- const token = cookies().get(COOKIE_NAME)?.value
40
-
41
- if (!token) return null
42
-
43
- try {
44
- const { payload } = await jwtVerify(token, secret)
45
- return payload as { userId: string }
46
- } catch {
47
- return null
48
- }
49
- }
50
-
51
- export async function requireSession() {
52
- const session = await getSession()
53
-
54
- if (!session) {
55
- throw new Error('Unauthorized')
56
- }
57
-
58
- return session
59
- }
60
- ```
61
-
62
- ## Session Refresh
63
-
64
- ```typescript
65
- // lib/session.ts
66
- export async function refreshSession() {
67
- const session = await getSession()
68
-
69
- if (!session) return null
70
-
71
- // Create new token with extended expiry
72
- return createSession(session.userId)
73
- }
74
- ```
75
-
76
- ## Session Destruction
77
-
78
- ```typescript
79
- // lib/session.ts
80
- export async function destroySession() {
81
- cookies().delete(COOKIE_NAME)
82
- }
83
- ```
84
-
85
- ## Session Middleware
86
-
87
- ```typescript
88
- // middleware.ts
89
- import { NextResponse } from 'next/server'
90
- import type { NextRequest } from 'next/server'
91
- import { jwtVerify } from 'jose'
92
-
93
- const secret = new TextEncoder().encode(process.env.JWT_SECRET)
94
-
95
- export async function middleware(request: NextRequest) {
96
- const token = request.cookies.get('session')?.value
97
-
98
- if (!token) {
99
- return NextResponse.redirect(new URL('/login', request.url))
100
- }
101
-
102
- try {
103
- await jwtVerify(token, secret)
104
- return NextResponse.next()
105
- } catch {
106
- return NextResponse.redirect(new URL('/login', request.url))
107
- }
108
- }
109
-
110
- export const config = {
111
- matcher: ['/dashboard/:path*', '/settings/:path*']
112
- }
113
- ```
114
-
115
- ## When to Use
116
-
117
- - Custom authentication systems
118
- - Stateless session management
119
- - Edge-compatible auth
120
- - API authentication
@@ -1,177 +0,0 @@
1
- # Audit Log Patterns
2
-
3
- Patterns for tracking data changes with audit logs.
4
-
5
- ## Audit Log Schema
6
-
7
- ```prisma
8
- // schema.prisma
9
- model AuditLog {
10
- id String @id @default(cuid())
11
- entityType String // "User", "Post", etc.
12
- entityId String
13
- action String // "CREATE", "UPDATE", "DELETE"
14
- changes Json? // What changed
15
- userId String? // Who made the change
16
- user User? @relation(fields: [userId], references: [id])
17
- ipAddress String?
18
- userAgent String?
19
- createdAt DateTime @default(now())
20
-
21
- @@index([entityType, entityId])
22
- @@index([userId])
23
- @@index([createdAt])
24
- }
25
- ```
26
-
27
- ## Audit Logger
28
-
29
- ```typescript
30
- // lib/audit.ts
31
- import { prisma } from '@/lib/db'
32
-
33
- interface AuditContext {
34
- userId?: string
35
- ipAddress?: string
36
- userAgent?: string
37
- }
38
-
39
- export async function createAuditLog(
40
- entityType: string,
41
- entityId: string,
42
- action: 'CREATE' | 'UPDATE' | 'DELETE',
43
- changes?: object,
44
- context?: AuditContext
45
- ) {
46
- return prisma.auditLog.create({
47
- data: {
48
- entityType,
49
- entityId,
50
- action,
51
- changes: changes ? JSON.parse(JSON.stringify(changes)) : null,
52
- userId: context?.userId,
53
- ipAddress: context?.ipAddress,
54
- userAgent: context?.userAgent
55
- }
56
- })
57
- }
58
- ```
59
-
60
- ## Change Detection
61
-
62
- ```typescript
63
- // lib/audit.ts
64
- export function detectChanges(
65
- before: Record<string, any>,
66
- after: Record<string, any>
67
- ) {
68
- const changes: Record<string, { from: any; to: any }> = {}
69
-
70
- for (const key of Object.keys(after)) {
71
- if (JSON.stringify(before[key]) !== JSON.stringify(after[key])) {
72
- changes[key] = {
73
- from: before[key],
74
- to: after[key]
75
- }
76
- }
77
- }
78
-
79
- return Object.keys(changes).length > 0 ? changes : null
80
- }
81
- ```
82
-
83
- ## Audited Update
84
-
85
- ```typescript
86
- // lib/users.ts
87
- import { prisma } from '@/lib/db'
88
- import { createAuditLog, detectChanges } from '@/lib/audit'
89
-
90
- export async function updateUser(
91
- id: string,
92
- data: Partial<User>,
93
- context: AuditContext
94
- ) {
95
- // Get current state
96
- const before = await prisma.user.findUnique({ where: { id } })
97
- if (!before) throw new Error('User not found')
98
-
99
- // Update
100
- const after = await prisma.user.update({
101
- where: { id },
102
- data
103
- })
104
-
105
- // Log changes
106
- const changes = detectChanges(before, after)
107
- if (changes) {
108
- await createAuditLog('User', id, 'UPDATE', changes, context)
109
- }
110
-
111
- return after
112
- }
113
- ```
114
-
115
- ## Prisma Middleware Approach
116
-
117
- ```typescript
118
- // lib/db.ts
119
- import { PrismaClient } from '@prisma/client'
120
-
121
- const prisma = new PrismaClient()
122
-
123
- // Global audit middleware
124
- prisma.$use(async (params, next) => {
125
- const result = await next(params)
126
-
127
- // Skip audit log model itself
128
- if (params.model === 'AuditLog') return result
129
-
130
- // Log mutations
131
- if (['create', 'update', 'delete'].includes(params.action)) {
132
- await prisma.auditLog.create({
133
- data: {
134
- entityType: params.model!,
135
- entityId: result?.id || params.args.where?.id,
136
- action: params.action.toUpperCase(),
137
- changes: params.args.data
138
- }
139
- })
140
- }
141
-
142
- return result
143
- })
144
-
145
- export { prisma }
146
- ```
147
-
148
- ## Query Audit History
149
-
150
- ```typescript
151
- // lib/audit.ts
152
- export async function getEntityHistory(
153
- entityType: string,
154
- entityId: string
155
- ) {
156
- return prisma.auditLog.findMany({
157
- where: { entityType, entityId },
158
- orderBy: { createdAt: 'desc' },
159
- include: { user: { select: { name: true, email: true } } }
160
- })
161
- }
162
-
163
- export async function getUserActivity(userId: string, limit = 50) {
164
- return prisma.auditLog.findMany({
165
- where: { userId },
166
- orderBy: { createdAt: 'desc' },
167
- take: limit
168
- })
169
- }
170
- ```
171
-
172
- ## When to Use
173
-
174
- - Compliance requirements (HIPAA, SOX)
175
- - Debug and troubleshooting
176
- - User activity tracking
177
- - Undo functionality