Skip to content

Commit

Permalink
Merge pull request #3 from itsmegood/vite
Browse files Browse the repository at this point in the history
Vite
  • Loading branch information
itsmegood authored Feb 23, 2024
2 parents 11882ad + 49fdb02 commit e5ca9a5
Show file tree
Hide file tree
Showing 45 changed files with 1,152 additions and 1,255 deletions.
50 changes: 26 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,41 +29,43 @@ To run Book Breeze, you'll need the following:
To set up the Book Breeze on your local machine, execute the following steps:

```sh
# Clone the repository
git clone https://github.com/itsmegood/BookBreeze.git
cd BookBreeze

# Install dependencies
npm install

# Set up your environment variables
cp .env.local.example .env.local
# Edit .env.local with your database credentials and any other configurations
npx create-epic-app@latest
```

# Run the development server
npm run dev
[![The Epic Stack](https://github-production-user-asset-6210df.s3.amazonaws.com/1500684/246885449-1b00286c-aa3d-44b2-9ef2-04f694eb3592.png)](https://www.epicweb.dev/epic-stack)

# Open http://localhost:3000 with your browser to see the result. The app should be up and running!
```
[The Epic Stack](https://www.epicweb.dev/epic-stack)

<!-- ## Documentation
<hr />

In-depth documentation covering Book Breeze's features, configuration, and usage instructions can be found in the `docs` folder. -->
## Watch Kent's Introduction to The Epic Stack

<!-- ## Contributing
[![Epic Stack Talk slide showing Flynn Rider with knives, the text "I've been around and I've got opinions" and Kent speaking in the corner](https://github-production-user-asset-6210df.s3.amazonaws.com/1500684/277818553-47158e68-4efc-43ae-a477-9d1670d4217d.png)](https://www.epicweb.dev/talks/the-epic-stack)

We welcome contributions to Book Breeze! Please read our `CONTRIBUTING.md` file to learn how you can contribute to the project. -->
["The Epic Stack" by Kent C. Dodds](https://www.epicweb.dev/talks/the-epic-stack)

## Acknowledgements
## Docs

Built on top of <a href="https://www.epicweb.dev/epic-stack">Epic Stack</a> by <a href="https://kentcdodds.com">Kent C. Dodds</a>.
[Read the docs](https://github.com/epicweb-dev/epic-stack/blob/main/docs)
(please 🙏).

## Contact
## Support

For help and support, please join [discord](https://discord.com/invite/rswhkuujJB). For any additional queries, open an issue in the repository, or if you're looking to collaborate, feel free to reach out!
- 🆘 Join the
[discussion on GitHub](https://github.com/epicweb-dev/epic-stack/discussions)
and the [KCD Community on Discord](https://kcd.im/discord).
- 💡 Create an
[idea discussion](https://github.com/epicweb-dev/epic-stack/discussions/new?category=ideas)
for suggestions.
- 🐛 Open a [GitHub issue](https://github.com/epicweb-dev/epic-stack/issues) to
report a bug.

## License
## Branding

Book Breeze is released under the [MIT License](LICENSE).
Want to talk about the Epic Stack in a blog post or talk? Great! Here are some
assets you can use in your material:
[EpicWeb.dev/brand](https://epicweb.dev/brand)

## Thanks

You rock 🪨
2 changes: 1 addition & 1 deletion app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
import { RemixServer } from '@remix-run/react'
import * as Sentry from '@sentry/remix'
import { isbot } from 'isbot'
import { getInstanceInfo } from 'litefs-js'
import { renderToPipeableStream } from 'react-dom/server'
import { getEnv, init } from './utils/env.server.ts'
import { getInstanceInfo } from './utils/litefs.server.ts'
import { NonceProvider } from './utils/nonce-provider.ts'
import { makeTimings } from './utils/timing.server.ts'

Expand Down
16 changes: 7 additions & 9 deletions app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import {
type MetaFunction,
} from '@remix-run/node'
import {
Links,
LiveReload,
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useFetchers,
useFetchers,
useLoaderData,
useRouteLoaderData,
} from '@remix-run/react'
Expand All @@ -28,7 +27,7 @@ import { EpicProgress } from './components/progress-bar.tsx'
import { useToast } from './components/toaster.tsx'
import { href as iconsHref } from './components/ui/icon.tsx'
import { EpicToaster } from './components/ui/sonner.tsx'
import tailwindStyleSheetUrl from './styles/tailwind.css'
import tailwindStyleSheetUrl from './styles/tailwind.css?url'
import { getUserId, logout } from './utils/auth.server.ts'
import { ClientHintCheck, getHints, useHints } from './utils/client-hints.tsx'
import { prisma } from './utils/db.server.ts'
Expand Down Expand Up @@ -195,7 +194,6 @@ function Document({
/>
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
<LiveReload nonce={nonce} />
</body>
</html>
)
Expand All @@ -204,8 +202,8 @@ function Document({
function App() {
const data = useLoaderData<typeof loader>()
const nonce = useNonce()
const theme = useTheme()

const theme = useTheme()
useToast(data.toast)

return (
Expand All @@ -218,8 +216,8 @@ function App() {
<div className="container flex justify-between p-6">
<Logo />
<ThemeSwitch />
</div>
</div> */}
</div>
</div> */}
</div>
<EpicToaster
closeButton
Expand Down
2 changes: 0 additions & 2 deletions app/routes/_auth+/auth.$provider.callback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ test('gives an error if the account is already connected to another user', async
await prisma.user.create({
data: {
...createUser(),
platformStatusKey: 'ACTIVE',
connections: {
create: {
providerName: GITHUB_PROVIDER_NAME,
Expand Down Expand Up @@ -247,7 +246,6 @@ async function setupUser(userData = createUser()) {
user: {
create: {
...userData,
platformStatusKey: 'active',
},
},
},
Expand Down
9 changes: 3 additions & 6 deletions app/routes/_auth+/auth.$provider.callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,9 @@ import {
redirectWithToast,
} from '#app/utils/toast.server.ts'
import { verifySessionStorage } from '#app/utils/verification.server.ts'
import { handleNewSession } from './login.tsx'
import {
onboardingEmailSessionKey,
prefilledProfileKey,
providerIdKey,
} from './onboarding_.$provider.tsx'
import { handleNewSession } from './login.server.ts'
import { onboardingEmailSessionKey } from './onboarding.tsx'
import { prefilledProfileKey, providerIdKey } from './onboarding_.$provider.tsx'

const destroyRedirectTo = { 'set-cookie': destroyRedirectToHeader }

Expand Down
6 changes: 2 additions & 4 deletions app/routes/_auth+/forgot-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { StatusButton } from '#app/components/ui/status-button.tsx'
import { prisma } from '#app/utils/db.server.ts'
import { sendEmail } from '#app/utils/email.server.ts'
import { checkHoneypot } from '#app/utils/honeypot.server.ts'
import { EmailSchema } from '#app/utils/user-validation.ts'
import { prepareVerification } from './verify.tsx'
import { EmailSchema} from '#app/utils/user-validation.ts'
import { prepareVerification } from './verify.server.ts'

const ForgotPasswordSchema = z.object({
email: EmailSchema,
Expand All @@ -26,7 +26,6 @@ const ForgotPasswordSchema = z.object({
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData()
checkHoneypot(formData)

const submission = await parseWithZod(formData, {
schema: ForgotPasswordSchema.superRefine(async (data, ctx) => {
const user = await prisma.user.findFirst({
Expand All @@ -46,7 +45,6 @@ export async function action({ request }: ActionFunctionArgs) {
}),
async: true,
})

if (submission.status !== 'success') {
return json(
{ result: submission.reply() },
Expand Down
158 changes: 158 additions & 0 deletions app/routes/_auth+/login.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { invariant } from '@epic-web/invariant'
import { redirect } from '@remix-run/node'
import { safeRedirect } from 'remix-utils/safe-redirect'
import { twoFAVerificationType } from '#app/routes/settings+/profile.two-factor.tsx'
import { getUserId, sessionKey } from '#app/utils/auth.server.ts'
import { prisma } from '#app/utils/db.server.ts'
import { combineResponseInits } from '#app/utils/misc.tsx'
import { authSessionStorage } from '#app/utils/session.server.ts'
import { redirectWithToast } from '#app/utils/toast.server.ts'
import { verifySessionStorage } from '#app/utils/verification.server.ts'
import { getRedirectToUrl, type VerifyFunctionArgs } from './verify.server.ts'

const verifiedTimeKey = 'verified-time'
const unverifiedSessionIdKey = 'unverified-session-id'
const rememberKey = 'remember'

export async function handleNewSession(
{
request,
session,
redirectTo,
remember,
}: {
request: Request
session: { userId: string; id: string; expirationDate: Date }
redirectTo?: string
remember: boolean
},
responseInit?: ResponseInit,
) {
const verification = await prisma.verification.findUnique({
select: { id: true },
where: {
target_type: { target: session.userId, type: twoFAVerificationType },
},
})
const userHasTwoFactor = Boolean(verification)

if (userHasTwoFactor) {
const verifySession = await verifySessionStorage.getSession()
verifySession.set(unverifiedSessionIdKey, session.id)
verifySession.set(rememberKey, remember)
const redirectUrl = getRedirectToUrl({
request,
type: twoFAVerificationType,
target: session.userId,
redirectTo,
})
return redirect(
`${redirectUrl.pathname}?${redirectUrl.searchParams}`,
combineResponseInits(
{
headers: {
'set-cookie':
await verifySessionStorage.commitSession(verifySession),
},
},
responseInit,
),
)
} else {
const authSession = await authSessionStorage.getSession(
request.headers.get('cookie'),
)
authSession.set(sessionKey, session.id)

return redirect(
safeRedirect(redirectTo),
combineResponseInits(
{
headers: {
'set-cookie': await authSessionStorage.commitSession(authSession, {
expires: remember ? session.expirationDate : undefined,
}),
},
},
responseInit,
),
)
}
}

export async function handleVerification({
request,
submission,
}: VerifyFunctionArgs) {
invariant(
submission.status === 'success',
'Submission should be successful by now',
)
const authSession = await authSessionStorage.getSession(
request.headers.get('cookie'),
)
const verifySession = await verifySessionStorage.getSession(
request.headers.get('cookie'),
)

const remember = verifySession.get(rememberKey)
const { redirectTo } = submission.value
const headers = new Headers()
authSession.set(verifiedTimeKey, Date.now())

const unverifiedSessionId = verifySession.get(unverifiedSessionIdKey)
if (unverifiedSessionId) {
const session = await prisma.session.findUnique({
select: { expirationDate: true },
where: { id: unverifiedSessionId },
})
if (!session) {
throw await redirectWithToast('/login', {
type: 'error',
title: 'Invalid session',
description: 'Could not find session to verify. Please try again.',
})
}
authSession.set(sessionKey, unverifiedSessionId)

headers.append(
'set-cookie',
await authSessionStorage.commitSession(authSession, {
expires: remember ? session.expirationDate : undefined,
}),
)
} else {
headers.append(
'set-cookie',
await authSessionStorage.commitSession(authSession),
)
}

headers.append(
'set-cookie',
await verifySessionStorage.destroySession(verifySession),
)

return redirect(safeRedirect(redirectTo), { headers })
}

export async function shouldRequestTwoFA(request: Request) {
const authSession = await authSessionStorage.getSession(
request.headers.get('cookie'),
)
const verifySession = await verifySessionStorage.getSession(
request.headers.get('cookie'),
)
if (verifySession.has(unverifiedSessionIdKey)) return true
const userId = await getUserId(request)
if (!userId) return false
// if it's over two hours since they last verified, we should request 2FA again
const userHasTwoFA = await prisma.verification.findUnique({
select: { id: true },
where: { target_type: { target: userId, type: twoFAVerificationType } },
})
if (!userHasTwoFA) return false
const verifiedTime = authSession.get(verifiedTimeKey) ?? new Date(0)
const twoHours = 1000 * 60 * 2
return Date.now() - verifiedTime > twoHours
}
Loading

0 comments on commit e5ca9a5

Please sign in to comment.