@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,237 +0,0 @@
|
|
|
1
|
-
# OAuth Integration Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for OAuth authentication providers.
|
|
4
|
-
|
|
5
|
-
## Multi-Provider Setup
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// auth.ts
|
|
9
|
-
import NextAuth from 'next-auth'
|
|
10
|
-
import Google from 'next-auth/providers/google'
|
|
11
|
-
import GitHub from 'next-auth/providers/github'
|
|
12
|
-
import Apple from 'next-auth/providers/apple'
|
|
13
|
-
import { PrismaAdapter } from '@auth/prisma-adapter'
|
|
14
|
-
import { prisma } from '@/lib/db'
|
|
15
|
-
|
|
16
|
-
export const { handlers, auth, signIn, signOut } = NextAuth({
|
|
17
|
-
adapter: PrismaAdapter(prisma),
|
|
18
|
-
providers: [
|
|
19
|
-
Google({
|
|
20
|
-
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
21
|
-
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
|
|
22
|
-
}),
|
|
23
|
-
GitHub({
|
|
24
|
-
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
25
|
-
clientSecret: process.env.GITHUB_CLIENT_SECRET!
|
|
26
|
-
}),
|
|
27
|
-
Apple({
|
|
28
|
-
clientId: process.env.APPLE_CLIENT_ID!,
|
|
29
|
-
clientSecret: process.env.APPLE_CLIENT_SECRET!
|
|
30
|
-
})
|
|
31
|
-
],
|
|
32
|
-
callbacks: {
|
|
33
|
-
async signIn({ user, account, profile }) {
|
|
34
|
-
// Custom validation
|
|
35
|
-
if (account?.provider === 'google') {
|
|
36
|
-
return profile?.email_verified === true
|
|
37
|
-
}
|
|
38
|
-
return true
|
|
39
|
-
},
|
|
40
|
-
async session({ session, user }) {
|
|
41
|
-
session.user.id = user.id
|
|
42
|
-
return session
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
pages: {
|
|
46
|
-
signIn: '/login',
|
|
47
|
-
error: '/auth/error'
|
|
48
|
-
}
|
|
49
|
-
})
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Social Login Buttons
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
// components/auth/SocialButtons.tsx
|
|
56
|
-
'use client'
|
|
57
|
-
|
|
58
|
-
import { signIn } from 'next-auth/react'
|
|
59
|
-
import { Github, Mail } from 'lucide-react'
|
|
60
|
-
|
|
61
|
-
export function SocialButtons() {
|
|
62
|
-
return (
|
|
63
|
-
<div className="space-y-3">
|
|
64
|
-
<button
|
|
65
|
-
onClick={() => signIn('google', { callbackUrl: '/dashboard' })}
|
|
66
|
-
className="flex w-full items-center justify-center gap-2 rounded-lg border py-2 hover:bg-gray-50"
|
|
67
|
-
>
|
|
68
|
-
<GoogleIcon className="h-5 w-5" />
|
|
69
|
-
Continue with Google
|
|
70
|
-
</button>
|
|
71
|
-
|
|
72
|
-
<button
|
|
73
|
-
onClick={() => signIn('github', { callbackUrl: '/dashboard' })}
|
|
74
|
-
className="flex w-full items-center justify-center gap-2 rounded-lg border py-2 hover:bg-gray-50"
|
|
75
|
-
>
|
|
76
|
-
<Github className="h-5 w-5" />
|
|
77
|
-
Continue with GitHub
|
|
78
|
-
</button>
|
|
79
|
-
|
|
80
|
-
<button
|
|
81
|
-
onClick={() => signIn('apple', { callbackUrl: '/dashboard' })}
|
|
82
|
-
className="flex w-full items-center justify-center gap-2 rounded-lg bg-black py-2 text-white"
|
|
83
|
-
>
|
|
84
|
-
<AppleIcon className="h-5 w-5" />
|
|
85
|
-
Continue with Apple
|
|
86
|
-
</button>
|
|
87
|
-
</div>
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function GoogleIcon({ className }: { className?: string }) {
|
|
92
|
-
return (
|
|
93
|
-
<svg className={className} viewBox="0 0 24 24">
|
|
94
|
-
<path
|
|
95
|
-
fill="currentColor"
|
|
96
|
-
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
97
|
-
/>
|
|
98
|
-
<path
|
|
99
|
-
fill="currentColor"
|
|
100
|
-
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
101
|
-
/>
|
|
102
|
-
<path
|
|
103
|
-
fill="currentColor"
|
|
104
|
-
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
105
|
-
/>
|
|
106
|
-
<path
|
|
107
|
-
fill="currentColor"
|
|
108
|
-
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
109
|
-
/>
|
|
110
|
-
</svg>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Account Linking
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
// auth.ts
|
|
119
|
-
export const { handlers, auth } = NextAuth({
|
|
120
|
-
callbacks: {
|
|
121
|
-
async signIn({ user, account }) {
|
|
122
|
-
// Check if user exists with different provider
|
|
123
|
-
const existingUser = await prisma.user.findUnique({
|
|
124
|
-
where: { email: user.email! },
|
|
125
|
-
include: { accounts: true }
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
if (existingUser) {
|
|
129
|
-
// Check if this provider is already linked
|
|
130
|
-
const hasProvider = existingUser.accounts.some(
|
|
131
|
-
a => a.provider === account?.provider
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
if (!hasProvider && account) {
|
|
135
|
-
// Link new provider to existing account
|
|
136
|
-
await prisma.account.create({
|
|
137
|
-
data: {
|
|
138
|
-
userId: existingUser.id,
|
|
139
|
-
type: account.type,
|
|
140
|
-
provider: account.provider,
|
|
141
|
-
providerAccountId: account.providerAccountId,
|
|
142
|
-
access_token: account.access_token,
|
|
143
|
-
refresh_token: account.refresh_token,
|
|
144
|
-
expires_at: account.expires_at
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return true
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## Custom Provider
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
// lib/custom-provider.ts
|
|
160
|
-
import type { OAuthConfig } from 'next-auth/providers'
|
|
161
|
-
|
|
162
|
-
interface CustomProfile {
|
|
163
|
-
id: string
|
|
164
|
-
email: string
|
|
165
|
-
name: string
|
|
166
|
-
avatar_url: string
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function CustomProvider(): OAuthConfig<CustomProfile> {
|
|
170
|
-
return {
|
|
171
|
-
id: 'custom',
|
|
172
|
-
name: 'Custom Provider',
|
|
173
|
-
type: 'oauth',
|
|
174
|
-
authorization: {
|
|
175
|
-
url: 'https://custom.com/oauth/authorize',
|
|
176
|
-
params: { scope: 'user:email' }
|
|
177
|
-
},
|
|
178
|
-
token: 'https://custom.com/oauth/token',
|
|
179
|
-
userinfo: 'https://custom.com/api/user',
|
|
180
|
-
profile(profile) {
|
|
181
|
-
return {
|
|
182
|
-
id: profile.id,
|
|
183
|
-
email: profile.email,
|
|
184
|
-
name: profile.name,
|
|
185
|
-
image: profile.avatar_url
|
|
186
|
-
}
|
|
187
|
-
},
|
|
188
|
-
clientId: process.env.CUSTOM_CLIENT_ID,
|
|
189
|
-
clientSecret: process.env.CUSTOM_CLIENT_SECRET
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## OAuth Error Handling
|
|
195
|
-
|
|
196
|
-
```tsx
|
|
197
|
-
// app/auth/error/page.tsx
|
|
198
|
-
import Link from 'next/link'
|
|
199
|
-
|
|
200
|
-
export default function AuthError({
|
|
201
|
-
searchParams
|
|
202
|
-
}: {
|
|
203
|
-
searchParams: { error?: string }
|
|
204
|
-
}) {
|
|
205
|
-
const errorMessages: Record<string, string> = {
|
|
206
|
-
OAuthSignin: 'Error starting OAuth flow',
|
|
207
|
-
OAuthCallback: 'Error in OAuth callback',
|
|
208
|
-
OAuthCreateAccount: 'Could not create account',
|
|
209
|
-
EmailCreateAccount: 'Could not create account',
|
|
210
|
-
Callback: 'Error in callback',
|
|
211
|
-
OAuthAccountNotLinked: 'Email already registered with different provider',
|
|
212
|
-
EmailSignin: 'Error sending email',
|
|
213
|
-
CredentialsSignin: 'Invalid credentials',
|
|
214
|
-
SessionRequired: 'Please sign in to continue',
|
|
215
|
-
default: 'An error occurred'
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const message = errorMessages[searchParams.error ?? 'default']
|
|
219
|
-
|
|
220
|
-
return (
|
|
221
|
-
<div className="mx-auto max-w-md py-12 text-center">
|
|
222
|
-
<h1 className="mb-4 text-2xl font-bold">Authentication Error</h1>
|
|
223
|
-
<p className="mb-6 text-gray-600">{message}</p>
|
|
224
|
-
<Link href="/login" className="text-blue-600 hover:underline">
|
|
225
|
-
Try again
|
|
226
|
-
</Link>
|
|
227
|
-
</div>
|
|
228
|
-
)
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
## When to Use
|
|
233
|
-
|
|
234
|
-
- Social login
|
|
235
|
-
- SSO integration
|
|
236
|
-
- Enterprise auth
|
|
237
|
-
- Third-party apps
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# Role-Based Access Control (RBAC) Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for implementing role-based permissions.
|
|
4
|
-
|
|
5
|
-
## Role Definition
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// lib/rbac.ts
|
|
9
|
-
export const ROLES = {
|
|
10
|
-
USER: 'USER',
|
|
11
|
-
ADMIN: 'ADMIN',
|
|
12
|
-
SUPER_ADMIN: 'SUPER_ADMIN'
|
|
13
|
-
} as const
|
|
14
|
-
|
|
15
|
-
export type Role = keyof typeof ROLES
|
|
16
|
-
|
|
17
|
-
export const PERMISSIONS = {
|
|
18
|
-
// User permissions
|
|
19
|
-
'user:read': [ROLES.USER, ROLES.ADMIN, ROLES.SUPER_ADMIN],
|
|
20
|
-
'user:update': [ROLES.USER, ROLES.ADMIN, ROLES.SUPER_ADMIN],
|
|
21
|
-
|
|
22
|
-
// Admin permissions
|
|
23
|
-
'user:delete': [ROLES.ADMIN, ROLES.SUPER_ADMIN],
|
|
24
|
-
'user:create': [ROLES.ADMIN, ROLES.SUPER_ADMIN],
|
|
25
|
-
|
|
26
|
-
// Super admin permissions
|
|
27
|
-
'admin:manage': [ROLES.SUPER_ADMIN],
|
|
28
|
-
'system:configure': [ROLES.SUPER_ADMIN]
|
|
29
|
-
} as const
|
|
30
|
-
|
|
31
|
-
export type Permission = keyof typeof PERMISSIONS
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Permission Check
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
// lib/rbac.ts
|
|
38
|
-
import { auth } from '@/auth'
|
|
39
|
-
import { prisma } from '@/lib/db'
|
|
40
|
-
|
|
41
|
-
export async function getUserRole(userId: string): Promise<Role> {
|
|
42
|
-
const user = await prisma.user.findUnique({
|
|
43
|
-
where: { id: userId },
|
|
44
|
-
select: { role: true }
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
return (user?.role as Role) ?? 'USER'
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export async function hasPermission(permission: Permission): Promise<boolean> {
|
|
51
|
-
const session = await auth()
|
|
52
|
-
if (!session?.user) return false
|
|
53
|
-
|
|
54
|
-
const role = await getUserRole(session.user.id)
|
|
55
|
-
return PERMISSIONS[permission].includes(role)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function requirePermission(permission: Permission) {
|
|
59
|
-
const allowed = await hasPermission(permission)
|
|
60
|
-
|
|
61
|
-
if (!allowed) {
|
|
62
|
-
throw new Error('Forbidden')
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Role Guard Wrapper
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
// lib/rbac.ts
|
|
71
|
-
export function withRole(allowedRoles: Role[]) {
|
|
72
|
-
return async function guard() {
|
|
73
|
-
const session = await auth()
|
|
74
|
-
|
|
75
|
-
if (!session?.user) {
|
|
76
|
-
throw new Error('Unauthorized')
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const role = await getUserRole(session.user.id)
|
|
80
|
-
|
|
81
|
-
if (!allowedRoles.includes(role)) {
|
|
82
|
-
throw new Error('Forbidden')
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return { session, role }
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Usage in Server Action
|
|
90
|
-
export async function deleteUser(userId: string) {
|
|
91
|
-
await withRole(['ADMIN', 'SUPER_ADMIN'])()
|
|
92
|
-
|
|
93
|
-
await prisma.user.delete({ where: { id: userId } })
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Component-Level Protection
|
|
98
|
-
|
|
99
|
-
```tsx
|
|
100
|
-
// components/admin-only.tsx
|
|
101
|
-
import { auth } from '@/auth'
|
|
102
|
-
import { getUserRole } from '@/lib/rbac'
|
|
103
|
-
|
|
104
|
-
export async function AdminOnly({
|
|
105
|
-
children
|
|
106
|
-
}: {
|
|
107
|
-
children: React.ReactNode
|
|
108
|
-
}) {
|
|
109
|
-
const session = await auth()
|
|
110
|
-
|
|
111
|
-
if (!session?.user) return null
|
|
112
|
-
|
|
113
|
-
const role = await getUserRole(session.user.id)
|
|
114
|
-
|
|
115
|
-
if (!['ADMIN', 'SUPER_ADMIN'].includes(role)) {
|
|
116
|
-
return null
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return <>{children}</>
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## API Route Protection
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
// app/api/admin/users/route.ts
|
|
127
|
-
import { NextResponse } from 'next/server'
|
|
128
|
-
import { requirePermission } from '@/lib/rbac'
|
|
129
|
-
|
|
130
|
-
export async function DELETE(req: Request) {
|
|
131
|
-
try {
|
|
132
|
-
await requirePermission('user:delete')
|
|
133
|
-
|
|
134
|
-
const { userId } = await req.json()
|
|
135
|
-
await prisma.user.delete({ where: { id: userId } })
|
|
136
|
-
|
|
137
|
-
return NextResponse.json({ success: true })
|
|
138
|
-
} catch (error) {
|
|
139
|
-
if (error.message === 'Forbidden') {
|
|
140
|
-
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
|
|
141
|
-
}
|
|
142
|
-
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## When to Use
|
|
148
|
-
|
|
149
|
-
- Multi-tier user systems
|
|
150
|
-
- Admin dashboards
|
|
151
|
-
- Feature access control
|
|
152
|
-
- API endpoint protection
|