@alinsafawi/aegis-auth 0.2.3 → 0.2.4
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/index.js +423 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23747,6 +23747,402 @@ function generatePackageJson(name, packageManager) {
|
|
|
23747
23747
|
2
|
|
23748
23748
|
);
|
|
23749
23749
|
}
|
|
23750
|
+
function generatePrismaLib() {
|
|
23751
|
+
return `import { PrismaClient } from '@prisma/client'
|
|
23752
|
+
|
|
23753
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
|
|
23754
|
+
|
|
23755
|
+
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
|
23756
|
+
|
|
23757
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
23758
|
+
`;
|
|
23759
|
+
}
|
|
23760
|
+
function generateLoginRoute() {
|
|
23761
|
+
return `import { createLoginHandler } from '@alinsafawi/aegis-auth-next'
|
|
23762
|
+
import { prisma } from '@/lib/prisma'
|
|
23763
|
+
import config from '../../../../../auth.config'
|
|
23764
|
+
|
|
23765
|
+
export const POST = createLoginHandler(config, prisma)
|
|
23766
|
+
`;
|
|
23767
|
+
}
|
|
23768
|
+
function generateLogoutRoute() {
|
|
23769
|
+
return `import { createLogoutHandler } from '@alinsafawi/aegis-auth-next'
|
|
23770
|
+
import config from '../../../../../auth.config'
|
|
23771
|
+
|
|
23772
|
+
export const POST = createLogoutHandler(config)
|
|
23773
|
+
`;
|
|
23774
|
+
}
|
|
23775
|
+
function generateTwoFactorRoute() {
|
|
23776
|
+
return `import { createTwoFactorHandler } from '@alinsafawi/aegis-auth-next'
|
|
23777
|
+
import { prisma } from '@/lib/prisma'
|
|
23778
|
+
import config from '../../../../../auth.config'
|
|
23779
|
+
|
|
23780
|
+
export const POST = createTwoFactorHandler(config, prisma)
|
|
23781
|
+
`;
|
|
23782
|
+
}
|
|
23783
|
+
function generateChangePasswordRoute() {
|
|
23784
|
+
return `import { createChangePasswordHandler } from '@alinsafawi/aegis-auth-next'
|
|
23785
|
+
import { prisma } from '@/lib/prisma'
|
|
23786
|
+
import config from '../../../../../auth.config'
|
|
23787
|
+
|
|
23788
|
+
export const POST = createChangePasswordHandler(config, prisma)
|
|
23789
|
+
`;
|
|
23790
|
+
}
|
|
23791
|
+
function generateForgotPasswordPage(language) {
|
|
23792
|
+
const ts = language === "typescript";
|
|
23793
|
+
return `'use client'
|
|
23794
|
+
|
|
23795
|
+
import { useState } from 'react'
|
|
23796
|
+
|
|
23797
|
+
export default function ForgotPasswordPage() {
|
|
23798
|
+
const [email, setEmail] = useState('')
|
|
23799
|
+
const [sent, setSent] = useState(false)
|
|
23800
|
+
const [error, setError] = useState('')
|
|
23801
|
+
const [loading, setLoading] = useState(false)
|
|
23802
|
+
|
|
23803
|
+
async function handleSubmit(e${ts ? ": React.FormEvent" : ""}) {
|
|
23804
|
+
e.preventDefault()
|
|
23805
|
+
setLoading(true)
|
|
23806
|
+
setError('')
|
|
23807
|
+
const res = await fetch('/api/auth/forgot-password', {
|
|
23808
|
+
method: 'POST',
|
|
23809
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23810
|
+
body: JSON.stringify({ email }),
|
|
23811
|
+
})
|
|
23812
|
+
setLoading(false)
|
|
23813
|
+
if (!res.ok) { setError((await res.json()).error ?? 'Something went wrong'); return }
|
|
23814
|
+
setSent(true)
|
|
23815
|
+
}
|
|
23816
|
+
|
|
23817
|
+
if (sent) {
|
|
23818
|
+
return (
|
|
23819
|
+
<main className="min-h-screen flex items-center justify-center p-4">
|
|
23820
|
+
<div className="w-full max-w-sm text-center space-y-3">
|
|
23821
|
+
<h1 className="text-2xl font-bold">Check your email</h1>
|
|
23822
|
+
<p className="text-[var(--muted)] text-sm">
|
|
23823
|
+
If an account exists for <strong>{email}</strong>, a reset link has been sent.
|
|
23824
|
+
</p>
|
|
23825
|
+
<a href="/login" className="block text-sm text-[var(--muted)] hover:underline mt-4">
|
|
23826
|
+
Back to login
|
|
23827
|
+
</a>
|
|
23828
|
+
</div>
|
|
23829
|
+
</main>
|
|
23830
|
+
)
|
|
23831
|
+
}
|
|
23832
|
+
|
|
23833
|
+
return (
|
|
23834
|
+
<main className="min-h-screen flex items-center justify-center p-4">
|
|
23835
|
+
<div className="w-full max-w-sm">
|
|
23836
|
+
<div className="text-center mb-8">
|
|
23837
|
+
<h1 className="text-2xl font-bold">Forgot password</h1>
|
|
23838
|
+
<p className="text-[var(--muted)] text-sm mt-1">We'll send you a reset link.</p>
|
|
23839
|
+
</div>
|
|
23840
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
23841
|
+
{error && <div className="text-sm text-red-500 bg-red-50 border border-red-200 rounded-md p-3">{error}</div>}
|
|
23842
|
+
<div>
|
|
23843
|
+
<label className="block text-sm font-medium mb-1">Email address</label>
|
|
23844
|
+
<input
|
|
23845
|
+
type="email"
|
|
23846
|
+
value={email}
|
|
23847
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
23848
|
+
required
|
|
23849
|
+
autoComplete="email"
|
|
23850
|
+
className="w-full px-3 py-2 rounded-md border bg-[var(--input)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)]"
|
|
23851
|
+
/>
|
|
23852
|
+
</div>
|
|
23853
|
+
<button
|
|
23854
|
+
type="submit"
|
|
23855
|
+
disabled={loading}
|
|
23856
|
+
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}
|
|
23857
|
+
className="w-full py-2 px-4 rounded-md font-medium disabled:opacity-50 transition-opacity"
|
|
23858
|
+
>
|
|
23859
|
+
{loading ? 'Sending\u2026' : 'Send reset link'}
|
|
23860
|
+
</button>
|
|
23861
|
+
<div className="text-center">
|
|
23862
|
+
<a href="/login" className="text-sm text-[var(--muted)] hover:underline">Back to login</a>
|
|
23863
|
+
</div>
|
|
23864
|
+
</form>
|
|
23865
|
+
</div>
|
|
23866
|
+
</main>
|
|
23867
|
+
)
|
|
23868
|
+
}
|
|
23869
|
+
`;
|
|
23870
|
+
}
|
|
23871
|
+
function generateResetPasswordPage(language) {
|
|
23872
|
+
const ts = language === "typescript";
|
|
23873
|
+
return `'use client'
|
|
23874
|
+
|
|
23875
|
+
import { useState } from 'react'
|
|
23876
|
+
import { useSearchParams, useRouter } from 'next/navigation'
|
|
23877
|
+
|
|
23878
|
+
export default function ResetPasswordPage() {
|
|
23879
|
+
const params = useSearchParams()
|
|
23880
|
+
const router = useRouter()
|
|
23881
|
+
const token = params.get('token') ?? ''
|
|
23882
|
+
const [password, setPassword] = useState('')
|
|
23883
|
+
const [confirm, setConfirm] = useState('')
|
|
23884
|
+
const [error, setError] = useState('')
|
|
23885
|
+
const [loading, setLoading] = useState(false)
|
|
23886
|
+
|
|
23887
|
+
async function handleSubmit(e${ts ? ": React.FormEvent" : ""}) {
|
|
23888
|
+
e.preventDefault()
|
|
23889
|
+
if (password !== confirm) { setError('Passwords do not match'); return }
|
|
23890
|
+
setLoading(true)
|
|
23891
|
+
setError('')
|
|
23892
|
+
const res = await fetch('/api/auth/reset-password', {
|
|
23893
|
+
method: 'POST',
|
|
23894
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23895
|
+
body: JSON.stringify({ token, newPassword: password }),
|
|
23896
|
+
})
|
|
23897
|
+
setLoading(false)
|
|
23898
|
+
if (!res.ok) { setError((await res.json()).error ?? 'Something went wrong'); return }
|
|
23899
|
+
router.push('/login?reset=1')
|
|
23900
|
+
}
|
|
23901
|
+
|
|
23902
|
+
return (
|
|
23903
|
+
<main className="min-h-screen flex items-center justify-center p-4">
|
|
23904
|
+
<div className="w-full max-w-sm">
|
|
23905
|
+
<div className="text-center mb-8">
|
|
23906
|
+
<h1 className="text-2xl font-bold">Reset password</h1>
|
|
23907
|
+
</div>
|
|
23908
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
23909
|
+
{error && <div className="text-sm text-red-500 bg-red-50 border border-red-200 rounded-md p-3">{error}</div>}
|
|
23910
|
+
<div>
|
|
23911
|
+
<label className="block text-sm font-medium mb-1">New password</label>
|
|
23912
|
+
<input
|
|
23913
|
+
type="password"
|
|
23914
|
+
value={password}
|
|
23915
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
23916
|
+
required
|
|
23917
|
+
autoComplete="new-password"
|
|
23918
|
+
className="w-full px-3 py-2 rounded-md border bg-[var(--input)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)]"
|
|
23919
|
+
/>
|
|
23920
|
+
</div>
|
|
23921
|
+
<div>
|
|
23922
|
+
<label className="block text-sm font-medium mb-1">Confirm new password</label>
|
|
23923
|
+
<input
|
|
23924
|
+
type="password"
|
|
23925
|
+
value={confirm}
|
|
23926
|
+
onChange={(e) => setConfirm(e.target.value)}
|
|
23927
|
+
required
|
|
23928
|
+
autoComplete="new-password"
|
|
23929
|
+
className="w-full px-3 py-2 rounded-md border bg-[var(--input)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)]"
|
|
23930
|
+
/>
|
|
23931
|
+
</div>
|
|
23932
|
+
<button
|
|
23933
|
+
type="submit"
|
|
23934
|
+
disabled={loading}
|
|
23935
|
+
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}
|
|
23936
|
+
className="w-full py-2 px-4 rounded-md font-medium disabled:opacity-50 transition-opacity"
|
|
23937
|
+
>
|
|
23938
|
+
{loading ? 'Resetting\u2026' : 'Reset password'}
|
|
23939
|
+
</button>
|
|
23940
|
+
</form>
|
|
23941
|
+
</div>
|
|
23942
|
+
</main>
|
|
23943
|
+
)
|
|
23944
|
+
}
|
|
23945
|
+
`;
|
|
23946
|
+
}
|
|
23947
|
+
function generateTwoFactorPage(language) {
|
|
23948
|
+
const ts = language === "typescript";
|
|
23949
|
+
return `'use client'
|
|
23950
|
+
|
|
23951
|
+
import { useState } from 'react'
|
|
23952
|
+
import { useRouter } from 'next/navigation'
|
|
23953
|
+
|
|
23954
|
+
export default function TwoFactorPage() {
|
|
23955
|
+
const [code, setCode] = useState('')
|
|
23956
|
+
const [error, setError] = useState('')
|
|
23957
|
+
const [loading, setLoading] = useState(false)
|
|
23958
|
+
const router = useRouter()
|
|
23959
|
+
|
|
23960
|
+
async function handleSubmit(e${ts ? ": React.FormEvent" : ""}) {
|
|
23961
|
+
e.preventDefault()
|
|
23962
|
+
setLoading(true)
|
|
23963
|
+
setError('')
|
|
23964
|
+
const res = await fetch('/api/auth/two-factor', {
|
|
23965
|
+
method: 'POST',
|
|
23966
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23967
|
+
body: JSON.stringify({ code }),
|
|
23968
|
+
})
|
|
23969
|
+
const data = await res.json()
|
|
23970
|
+
setLoading(false)
|
|
23971
|
+
if (!res.ok) { setError(data.error ?? 'Invalid code'); return }
|
|
23972
|
+
router.push(\`/\${data.role}/dashboard\`)
|
|
23973
|
+
}
|
|
23974
|
+
|
|
23975
|
+
return (
|
|
23976
|
+
<main className="min-h-screen flex items-center justify-center p-4">
|
|
23977
|
+
<div className="w-full max-w-sm">
|
|
23978
|
+
<div className="text-center mb-8">
|
|
23979
|
+
<h1 className="text-2xl font-bold">Two-factor authentication</h1>
|
|
23980
|
+
<p className="text-[var(--muted)] text-sm mt-1">Enter the 6-digit code from your authenticator app.</p>
|
|
23981
|
+
</div>
|
|
23982
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
23983
|
+
{error && <div className="text-sm text-red-500 bg-red-50 border border-red-200 rounded-md p-3">{error}</div>}
|
|
23984
|
+
<div>
|
|
23985
|
+
<label className="block text-sm font-medium mb-1">Authentication code</label>
|
|
23986
|
+
<input
|
|
23987
|
+
type="text"
|
|
23988
|
+
inputMode="numeric"
|
|
23989
|
+
pattern="[0-9]*"
|
|
23990
|
+
maxLength={6}
|
|
23991
|
+
value={code}
|
|
23992
|
+
onChange={(e) => setCode(e.target.value.replace(/D/g, ''))}
|
|
23993
|
+
required
|
|
23994
|
+
autoComplete="one-time-code"
|
|
23995
|
+
placeholder="000000"
|
|
23996
|
+
className="w-full px-3 py-2 rounded-md border bg-[var(--input)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-center tracking-widest text-lg"
|
|
23997
|
+
/>
|
|
23998
|
+
</div>
|
|
23999
|
+
<button
|
|
24000
|
+
type="submit"
|
|
24001
|
+
disabled={loading || code.length !== 6}
|
|
24002
|
+
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}
|
|
24003
|
+
className="w-full py-2 px-4 rounded-md font-medium disabled:opacity-50 transition-opacity"
|
|
24004
|
+
>
|
|
24005
|
+
{loading ? 'Verifying\u2026' : 'Verify'}
|
|
24006
|
+
</button>
|
|
24007
|
+
<div className="text-center">
|
|
24008
|
+
<a href="/login" className="text-sm text-[var(--muted)] hover:underline">Back to login</a>
|
|
24009
|
+
</div>
|
|
24010
|
+
</form>
|
|
24011
|
+
</div>
|
|
24012
|
+
</main>
|
|
24013
|
+
)
|
|
24014
|
+
}
|
|
24015
|
+
`;
|
|
24016
|
+
}
|
|
24017
|
+
function generateVerifyEmailNoticePage() {
|
|
24018
|
+
return `export default function VerifyEmailNoticePage() {
|
|
24019
|
+
return (
|
|
24020
|
+
<main className="min-h-screen flex items-center justify-center p-4">
|
|
24021
|
+
<div className="w-full max-w-sm text-center space-y-3">
|
|
24022
|
+
<h1 className="text-2xl font-bold">Verify your email</h1>
|
|
24023
|
+
<p className="text-[var(--muted)] text-sm">
|
|
24024
|
+
We sent a verification code to your email address. Enter it below to activate your account.
|
|
24025
|
+
</p>
|
|
24026
|
+
<a href="/verify-email" className="block mt-4 text-sm underline" style={{ color: 'var(--primary)' }}>
|
|
24027
|
+
Enter verification code \u2192
|
|
24028
|
+
</a>
|
|
24029
|
+
</div>
|
|
24030
|
+
</main>
|
|
24031
|
+
)
|
|
24032
|
+
}
|
|
24033
|
+
`;
|
|
24034
|
+
}
|
|
24035
|
+
function generateVerifyEmailPage(language) {
|
|
24036
|
+
const ts = language === "typescript";
|
|
24037
|
+
return `'use client'
|
|
24038
|
+
|
|
24039
|
+
import { useState } from 'react'
|
|
24040
|
+
import { useRouter } from 'next/navigation'
|
|
24041
|
+
|
|
24042
|
+
export default function VerifyEmailPage() {
|
|
24043
|
+
const [code, setCode] = useState('')
|
|
24044
|
+
const [error, setError] = useState('')
|
|
24045
|
+
const [loading, setLoading] = useState(false)
|
|
24046
|
+
const router = useRouter()
|
|
24047
|
+
|
|
24048
|
+
async function handleSubmit(e${ts ? ": React.FormEvent" : ""}) {
|
|
24049
|
+
e.preventDefault()
|
|
24050
|
+
setLoading(true)
|
|
24051
|
+
setError('')
|
|
24052
|
+
const res = await fetch('/api/auth/verify-email', {
|
|
24053
|
+
method: 'POST',
|
|
24054
|
+
headers: { 'Content-Type': 'application/json' },
|
|
24055
|
+
body: JSON.stringify({ code }),
|
|
24056
|
+
})
|
|
24057
|
+
const data = await res.json()
|
|
24058
|
+
setLoading(false)
|
|
24059
|
+
if (!res.ok) { setError(data.error ?? 'Invalid code'); return }
|
|
24060
|
+
router.push(\`/\${data.role}/dashboard\`)
|
|
24061
|
+
}
|
|
24062
|
+
|
|
24063
|
+
return (
|
|
24064
|
+
<main className="min-h-screen flex items-center justify-center p-4">
|
|
24065
|
+
<div className="w-full max-w-sm">
|
|
24066
|
+
<div className="text-center mb-8">
|
|
24067
|
+
<h1 className="text-2xl font-bold">Enter verification code</h1>
|
|
24068
|
+
<p className="text-[var(--muted)] text-sm mt-1">Check your email for the 6-digit code.</p>
|
|
24069
|
+
</div>
|
|
24070
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
24071
|
+
{error && <div className="text-sm text-red-500 bg-red-50 border border-red-200 rounded-md p-3">{error}</div>}
|
|
24072
|
+
<div>
|
|
24073
|
+
<label className="block text-sm font-medium mb-1">Verification code</label>
|
|
24074
|
+
<input
|
|
24075
|
+
type="text"
|
|
24076
|
+
inputMode="numeric"
|
|
24077
|
+
pattern="[0-9]*"
|
|
24078
|
+
maxLength={6}
|
|
24079
|
+
value={code}
|
|
24080
|
+
onChange={(e) => setCode(e.target.value.replace(/D/g, ''))}
|
|
24081
|
+
required
|
|
24082
|
+
autoComplete="one-time-code"
|
|
24083
|
+
placeholder="000000"
|
|
24084
|
+
className="w-full px-3 py-2 rounded-md border bg-[var(--input)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-center tracking-widest text-lg"
|
|
24085
|
+
/>
|
|
24086
|
+
</div>
|
|
24087
|
+
<button
|
|
24088
|
+
type="submit"
|
|
24089
|
+
disabled={loading || code.length !== 6}
|
|
24090
|
+
style={{ background: 'var(--primary)', color: 'var(--primary-foreground)' }}
|
|
24091
|
+
className="w-full py-2 px-4 rounded-md font-medium disabled:opacity-50 transition-opacity"
|
|
24092
|
+
>
|
|
24093
|
+
{loading ? 'Verifying\u2026' : 'Verify email'}
|
|
24094
|
+
</button>
|
|
24095
|
+
<div className="text-center">
|
|
24096
|
+
<a href="/login" className="text-sm text-[var(--muted)] hover:underline">Back to login</a>
|
|
24097
|
+
</div>
|
|
24098
|
+
</form>
|
|
24099
|
+
</div>
|
|
24100
|
+
</main>
|
|
24101
|
+
)
|
|
24102
|
+
}
|
|
24103
|
+
`;
|
|
24104
|
+
}
|
|
24105
|
+
function generateForgotPasswordRoute(language) {
|
|
24106
|
+
const ts = language === "typescript";
|
|
24107
|
+
return `import { NextRequest, NextResponse } from 'next/server'
|
|
24108
|
+
import { prisma } from '@/lib/prisma'
|
|
24109
|
+
import config from '../../../../../auth.config'
|
|
24110
|
+
|
|
24111
|
+
export async function POST(request${ts ? ": NextRequest" : ""}) {
|
|
24112
|
+
// TODO: implement \u2014 find user by email, generate reset token, send email
|
|
24113
|
+
// You can use: import { sendEmail } from '@alinsafawi/aegis-auth-core'
|
|
24114
|
+
const { email } = await request.json()
|
|
24115
|
+
return NextResponse.json({ success: true })
|
|
24116
|
+
}
|
|
24117
|
+
`;
|
|
24118
|
+
}
|
|
24119
|
+
function generateResetPasswordRoute(language) {
|
|
24120
|
+
const ts = language === "typescript";
|
|
24121
|
+
return `import { NextRequest, NextResponse } from 'next/server'
|
|
24122
|
+
import { prisma } from '@/lib/prisma'
|
|
24123
|
+
import { hashPassword } from '@alinsafawi/aegis-auth-core'
|
|
24124
|
+
import config from '../../../../../auth.config'
|
|
24125
|
+
|
|
24126
|
+
export async function POST(request${ts ? ": NextRequest" : ""}) {
|
|
24127
|
+
// TODO: implement \u2014 verify token, update password hash
|
|
24128
|
+
const { token, newPassword } = await request.json()
|
|
24129
|
+
return NextResponse.json({ success: true })
|
|
24130
|
+
}
|
|
24131
|
+
`;
|
|
24132
|
+
}
|
|
24133
|
+
function generateVerifyEmailRoute(language) {
|
|
24134
|
+
const ts = language === "typescript";
|
|
24135
|
+
return `import { NextRequest, NextResponse } from 'next/server'
|
|
24136
|
+
import { prisma } from '@/lib/prisma'
|
|
24137
|
+
import config from '../../../../../auth.config'
|
|
24138
|
+
|
|
24139
|
+
export async function POST(request${ts ? ": NextRequest" : ""}) {
|
|
24140
|
+
// TODO: implement \u2014 verify code, mark emailVerified = true, return role
|
|
24141
|
+
const { code } = await request.json()
|
|
24142
|
+
return NextResponse.json({ success: true, role: 'user' })
|
|
24143
|
+
}
|
|
24144
|
+
`;
|
|
24145
|
+
}
|
|
23750
24146
|
|
|
23751
24147
|
// src/commands/init.ts
|
|
23752
24148
|
var DEFAULT_COLOR = "oklch(0.6 0.2 240)";
|
|
@@ -23958,9 +24354,35 @@ dist
|
|
|
23958
24354
|
await step("Creating session helpers", async () => {
|
|
23959
24355
|
await writeFile(`src/lib/auth.${language === "typescript" ? "ts" : "js"}`, generateAuthLib(language));
|
|
23960
24356
|
});
|
|
23961
|
-
await step("Scaffolding auth pages", async () => {
|
|
24357
|
+
await step("Scaffolding auth pages and API routes", async () => {
|
|
23962
24358
|
const e = language === "typescript" ? "tsx" : "jsx";
|
|
24359
|
+
const r = language === "typescript" ? "ts" : "js";
|
|
24360
|
+
await writeFile(`src/lib/prisma.${r}`, generatePrismaLib());
|
|
24361
|
+
await writeFile(`src/app/api/auth/login/route.${r}`, generateLoginRoute());
|
|
24362
|
+
await writeFile(`src/app/api/auth/logout/route.${r}`, generateLogoutRoute());
|
|
24363
|
+
await writeFile(`src/app/api/auth/change-password/route.${r}`, generateChangePasswordRoute());
|
|
24364
|
+
if (features.twoFactor) {
|
|
24365
|
+
await writeFile(`src/app/api/auth/two-factor/route.${r}`, generateTwoFactorRoute());
|
|
24366
|
+
}
|
|
24367
|
+
if (features.passwordReset) {
|
|
24368
|
+
await writeFile(`src/app/api/auth/forgot-password/route.${r}`, generateForgotPasswordRoute(language));
|
|
24369
|
+
await writeFile(`src/app/api/auth/reset-password/route.${r}`, generateResetPasswordRoute(language));
|
|
24370
|
+
}
|
|
24371
|
+
if (features.emailVerification) {
|
|
24372
|
+
await writeFile(`src/app/api/auth/verify-email/route.${r}`, generateVerifyEmailRoute(language));
|
|
24373
|
+
}
|
|
23963
24374
|
await writeFile(`src/app/(auth)/login/page.${e}`, generateLoginPage(app.appName, DEFAULT_COLOR, language));
|
|
24375
|
+
if (features.passwordReset) {
|
|
24376
|
+
await writeFile(`src/app/(auth)/forgot-password/page.${e}`, generateForgotPasswordPage(language));
|
|
24377
|
+
await writeFile(`src/app/(auth)/reset-password/page.${e}`, generateResetPasswordPage(language));
|
|
24378
|
+
}
|
|
24379
|
+
if (features.twoFactor) {
|
|
24380
|
+
await writeFile(`src/app/(auth)/two-factor/page.${e}`, generateTwoFactorPage(language));
|
|
24381
|
+
}
|
|
24382
|
+
if (features.emailVerification) {
|
|
24383
|
+
await writeFile(`src/app/(auth)/verify-email-notice/page.${e}`, generateVerifyEmailNoticePage());
|
|
24384
|
+
await writeFile(`src/app/(auth)/verify-email/page.${e}`, generateVerifyEmailPage(language));
|
|
24385
|
+
}
|
|
23964
24386
|
});
|
|
23965
24387
|
await step("Creating .env and .env.example", async () => {
|
|
23966
24388
|
await writeFile(".env", generateEnvFile(app.cookiePrefix, infra, features));
|