Skip to content

Commit

Permalink
Incorporate new Epic-Stack template updates like Remix v2.3.1 (#117)
Browse files Browse the repository at this point in the history
* epic v2.3: NEW .env.example; csrf security

* epic v2.3: NEW .env.example; csrf security

* Comment about potentially unneccesary & readme

Co-authored-by: plocket <[email protected]>
Co-authored-by: Gavin Kimball <[email protected]>
Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: Jeff Korenstein <[email protected]>
Co-authored-by: Tai <[email protected]>
Co-authored-by: Tai Phuong <[email protected]>

---------

Co-authored-by: plocket <[email protected]>
Co-authored-by: Gavin Kimball <[email protected]>
Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: Jeff Korenstein <[email protected]>
Co-authored-by: Tai <[email protected]>
  • Loading branch information
6 people authored Dec 6, 2023
1 parent 43d1f53 commit 4e7a068
Show file tree
Hide file tree
Showing 90 changed files with 6,270 additions and 4,003 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/heat-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ permissions:
jobs:
lint:
name: ⬣ ESLint
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
Expand All @@ -50,7 +50,7 @@ jobs:

typecheck:
name: ʦ TypeScript
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
Expand All @@ -73,7 +73,7 @@ jobs:

vitest:
name: ⚡ Vitest pyodide.test.ts
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
Expand All @@ -100,7 +100,7 @@ jobs:
# playwright tests work great but slight jank/inconsistency passing, and not used yet, so disabling for now
# playwright:
# name: 🎭 Playwright
# runs-on: ubuntu-latest
# runs-on: ubuntu-22.04
# timeout-minutes: 60
# steps:
# - name: ⬇️ Checkout repo
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ node_modules

# Easy way to create temporary files/folders that won't accidentally be added to git
*.local.*

#local temporary folders
heat-app
venv
heat-tmp
11 changes: 11 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"recommendations": [
"bradlc.vscode-tailwindcss",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"prisma.prisma",
"qwtel.sqlite-viewer",
"yoavbls.pretty-ts-errors",
"github.vscode-github-actions"
]
}
1 change: 1 addition & 0 deletions heat-stack/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ DATABASE_PATH="./prisma/data.db"
DATABASE_URL="file:./data.db?connection_limit=1"
CACHE_DATABASE_PATH="./other/cache.db"
SESSION_SECRET="super-duper-s3cret"
HONEYPOT_SECRET="super-duper-s3cret"
INTERNAL_COMMAND_TOKEN="some-made-up-token"
RESEND_API_KEY="re_blAh_blaHBlaHblahBLAhBlAh"
SENTRY_DSN="your-dsn"
Expand Down
4 changes: 4 additions & 0 deletions heat-stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ npm install
npm run dev
```


### Set up in a new GitHub CodingSpace:

```
Expand All @@ -30,6 +31,9 @@ npm run dev

If you have the node version manager (`nvm`), then `nvm use 18` avoids engine error with node v19+ or newer which is default. nvm is preinstalled in coding spaces.


In Coding Spaces VSCode always go to hamburger menu -> File-> untick AutoSave. For a pic, see https://stackoverflow.com/a/76659316/14144258

### Under special circumstances:

Assume you don't need to, but if the version of pyodide changes run:
Expand Down
21 changes: 0 additions & 21 deletions heat-stack/app/components/confetti.tsx

This file was deleted.

4 changes: 3 additions & 1 deletion heat-stack/app/components/error-boundary.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {
type ErrorResponse,
isRouteErrorResponse,
useParams,
useRouteError,
} from '@remix-run/react'
import { type ErrorResponse } from '@remix-run/router'
import { captureRemixErrorBoundaryError } from '@sentry/remix'
import { getErrorMessage } from '#app/utils/misc.tsx'

type StatusHandler = (info: {
Expand All @@ -25,6 +26,7 @@ export function GeneralErrorBoundary({
unexpectedErrorHandler?: (error: unknown) => JSX.Element | null
}) {
const error = useRouteError()
captureRemixErrorBoundaryError(error)
const params = useParams()

if (typeof document !== 'undefined') {
Expand Down
4 changes: 2 additions & 2 deletions heat-stack/app/components/forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function ErrorList({
return (
<ul id={id} className="flex flex-col gap-1">
{errorsToRender.map(e => (
<li key={e} className="text-[10px] text-foreground-danger">
<li key={e} className="text-foreground-destructive text-[10px]">
{e}
</li>
))}
Expand Down Expand Up @@ -64,7 +64,7 @@ export function TextareaField({
className,
}: {
labelProps: React.LabelHTMLAttributes<HTMLLabelElement>
textareaProps: React.InputHTMLAttributes<HTMLTextAreaElement>
textareaProps: React.TextareaHTMLAttributes<HTMLTextAreaElement>
errors?: ListOfErrors
className?: string
}) {
Expand Down
63 changes: 63 additions & 0 deletions heat-stack/app/components/progress-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useNavigation } from '@remix-run/react'
import { useEffect, useRef, useState } from 'react'
import { useSpinDelay } from 'spin-delay'
import { cn } from '#app/utils/misc.tsx'
import { Icon } from './ui/icon.tsx'

function EpicProgress() {
const transition = useNavigation()
const busy = transition.state !== 'idle'
const delayedPending = useSpinDelay(busy, {
delay: 600,
minDuration: 400,
})
const ref = useRef<HTMLDivElement>(null)
const [animationComplete, setAnimationComplete] = useState(true)

useEffect(() => {
if (!ref.current) return
if (delayedPending) setAnimationComplete(false)

const animationPromises = ref.current
.getAnimations()
.map(({ finished }) => finished)

Promise.allSettled(animationPromises).then(() => {
if (!delayedPending) setAnimationComplete(true)
})
}, [delayedPending])

return (
<div
role="progressbar"
aria-hidden={delayedPending ? undefined : true}
aria-valuetext={delayedPending ? 'Loading' : undefined}
className="fixed inset-x-0 left-0 top-0 z-50 h-[0.20rem] animate-pulse"
>
<div
ref={ref}
className={cn(
'h-full w-0 bg-foreground duration-500 ease-in-out',
transition.state === 'idle' &&
(animationComplete
? 'transition-none'
: 'w-full opacity-0 transition-all'),
delayedPending && transition.state === 'submitting' && 'w-5/12',
delayedPending && transition.state === 'loading' && 'w-8/12',
)}
/>
{delayedPending && (
<div className="absolute flex items-center justify-center">
<Icon
name="update"
size="md"
className="m-1 animate-spin text-foreground"
aria-hidden
/>
</div>
)}
</div>
)
}

export { EpicProgress }
6 changes: 4 additions & 2 deletions heat-stack/app/components/search-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Form, useSearchParams, useSubmit } from '@remix-run/react'
import { useId } from 'react'
import { useDebounce, useIsPending } from '#app/utils/misc.tsx'
import { Icon } from './ui/icon.tsx'
import { Input } from './ui/input.tsx'
Expand All @@ -14,6 +15,7 @@ export function SearchBar({
autoFocus?: boolean
autoSubmit?: boolean
}) {
const id = useId()
const [searchParams] = useSearchParams()
const submit = useSubmit()
const isSubmitting = useIsPending({
Expand All @@ -33,13 +35,13 @@ export function SearchBar({
onChange={e => autoSubmit && handleFormChange(e.currentTarget)}
>
<div className="flex-1">
<Label htmlFor="search" className="sr-only">
<Label htmlFor={id} className="sr-only">
Search
</Label>
<Input
type="search"
name="search"
id="search"
id={id}
defaultValue={searchParams.get('search') ?? ''}
placeholder="Search"
className="w-full"
Expand Down
1 change: 1 addition & 0 deletions heat-stack/app/components/spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* deprecated by epic stack 2.3, delete me */
export function Spinner({ showSpinner }: { showSpinner: boolean }) {
return (
<div
Expand Down
6 changes: 3 additions & 3 deletions heat-stack/app/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import * as React from 'react'
import { cn } from '#app/utils/misc.tsx'

const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors outline-none focus-visible:ring-4 focus-within:ring-4 ring-ring ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
default: 'bg-primary text-primary-foreground hover:bg-primary/80',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
'bg-destructive text-destructive-foreground hover:bg-destructive/80',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
Expand Down
14 changes: 14 additions & 0 deletions heat-stack/app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { PassThrough } from 'stream'
import {
createReadableStreamFromReadable,
type DataFunctionArgs,
type HandleDocumentRequestFunction,
} from '@remix-run/node'
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'
Expand Down Expand Up @@ -72,6 +74,7 @@ export default async function handleRequest(...args: DocRequestArgs) {

console.error(error)
},
nonce,
},
)

Expand All @@ -88,3 +91,14 @@ export async function handleDataRequest(response: Response) {

return response
}

export function handleError(
error: unknown,
{ request }: DataFunctionArgs,
): void {
if (error instanceof Error) {
Sentry.captureRemixServerException(error, 'remix.server', request)
} else {
Sentry.captureException(error)
}
}
30 changes: 21 additions & 9 deletions heat-stack/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ import fontStyleSheetUrl from './styles/font.css'
import tailwindStyleSheetUrl from './styles/tailwind.css'

Check warning on line 3 in heat-stack/app/root.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

`./styles/tailwind.css` import should occur after import of `./components/WeatherExample.tsx`
import { Links, Scripts } from '@remix-run/react'

Check warning on line 4 in heat-stack/app/root.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

`@remix-run/react` import should occur after import of `@remix-run/node`
import { href as iconsHref } from './components/ui/icon.tsx'

Check warning on line 5 in heat-stack/app/root.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

`./components/ui/icon.tsx` import should occur after import of `./components/CaseSummary.tsx`
import { DataFunctionArgs, json, type LinksFunction } from '@remix-run/node'
import { DataFunctionArgs, HeadersFunction, json, type LinksFunction } from '@remix-run/node'

Check warning on line 6 in heat-stack/app/root.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

Imports "DataFunctionArgs" and "HeadersFunction" are only used as types

import { CaseSummary } from './components/CaseSummary.tsx'
import './App.css'
import { useNonce } from './utils/nonce-provider.ts'
import { makeTimings, time } from './utils/timing.server.ts'
import { combineServerTimings, makeTimings, time } from './utils/timing.server.ts'
import { combineHeaders, getDomainUrl } from './utils/misc.tsx'
// import { csrf } from './utils/csrf.server.ts'
import { getEnv } from './utils/env.server.ts'
// Hints may not be required. Double check.
import { getHints } from './utils/client-hints.tsx'
import { WeatherExample } from './components/WeatherExample.tsx'
import { Weather } from './WeatherExample.js'
import { getUserId } from './utils/auth.server.ts'
import { prisma } from './utils/db.server.ts'
import { csrf } from './utils/csrf.server.ts'
import { honeypot } from './utils/honeypot.server.ts'

export const links: LinksFunction = () => {
return [
Expand Down Expand Up @@ -76,8 +78,8 @@ export async function loader({ request }: DataFunctionArgs) {
{ timings, type: 'find user', desc: 'find user in root' },
)
: null
// const honeyProps = honeypot.getInputProps()
// const [csrfToken, csrfCookieHeader] = await csrf.commitToken()
const honeyProps = honeypot.getInputProps()
const [csrfToken, csrfCookieHeader] = await csrf.commitToken()
// Weather station data
const w_href: string =
'https://archive-api.open-meteo.com/v1/archive?latitude=52.52&longitude=13.41&daily=temperature_2m_max&timezone=America%2FNew_York&start_date=2022-01-01&end_date=2023-08-30&temperature_unit=fahrenheit'
Expand All @@ -89,24 +91,35 @@ export async function loader({ request }: DataFunctionArgs) {
weather: weather,
user: user,
requestInfo: {
/* hints may not be absolutely required, double check */
hints: getHints(request),
origin: getDomainUrl(request),
path: new URL(request.url).pathname,
userPrefs: {},
},
ENV: getEnv(),
// honeyProps,
// csrfToken,
honeyProps,
csrfToken,
},
{
headers: combineHeaders(
{ 'Server-Timing': timings.toString() },
// csrfCookieHeader ? { 'set-cookie': csrfCookieHeader } : null,
csrfCookieHeader ? { 'set-cookie': csrfCookieHeader } : null,
),
},
)
}

/**
* Step 4 of making Server Timings
* https://github.com/epicweb-dev/epic-stack/blob/main/docs/server-timing.md
*/
export const headers: HeadersFunction = ({ loaderHeaders, parentHeaders }) => {
return {
'Server-Timing': combineServerTimings(parentHeaders, loaderHeaders),
}
}

export default function HeatStack({ env = {} }) {
const nonce = useNonce()
return (
Expand All @@ -124,7 +137,6 @@ export default function HeatStack({ env = {} }) {
__html: `window.ENV = ${JSON.stringify(env)}`,
}}
/>
<div>left{nonce}right</div>
<Scripts nonce={nonce} />
</body>
</html>
Expand Down
Loading

0 comments on commit 4e7a068

Please sign in to comment.