Skip to content

Commit

Permalink
epic v2.3: NEW .env.example; csrf security
Browse files Browse the repository at this point in the history
  • Loading branch information
thadk committed Dec 1, 2023
1 parent 43d1f53 commit d85adb9
Show file tree
Hide file tree
Showing 40 changed files with 5,480 additions and 3,551 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
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)
}
}
13 changes: 7 additions & 6 deletions heat-stack/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ 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 @@ -95,13 +97,13 @@ export async function loader({ request }: DataFunctionArgs) {
userPrefs: {},
},
ENV: getEnv(),
// honeyProps,
// csrfToken,
honeyProps,
csrfToken,
},
{
headers: combineHeaders(
{ 'Server-Timing': timings.toString() },
// csrfCookieHeader ? { 'set-cookie': csrfCookieHeader } : null,
csrfCookieHeader ? { 'set-cookie': csrfCookieHeader } : null,
),
},
)
Expand All @@ -124,7 +126,6 @@ export default function HeatStack({ env = {} }) {
__html: `window.ENV = ${JSON.stringify(env)}`,
}}
/>
<div>left{nonce}right</div>
<Scripts nonce={nonce} />
</body>
</html>
Expand Down
12 changes: 6 additions & 6 deletions heat-stack/app/root_original.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import { withSentry } from '@sentry/remix'
import { Suspense, lazy, useEffect, useRef, useState } from 'react'
import { object, z } from 'zod'
import { Confetti } from './components/confetti.tsx'
// import { Confetti } from './components/confetti.tsx'
import { GeneralErrorBoundary } from './components/error-boundary.tsx'
import { ErrorList } from './components/forms.tsx'
import { SearchBar } from './components/search-bar.tsx'
Expand All @@ -44,7 +44,7 @@ import fontStyleSheetUrl from './styles/font.css'
import tailwindStyleSheetUrl from './styles/tailwind.css'
import { authenticator, getUserId } from './utils/auth.server.ts'
import { ClientHintCheck, getHints, useHints } from './utils/client-hints.tsx'
import { getConfetti } from './utils/confetti.server.ts'
// import { getConfetti } from './utils/confetti.server.ts'
import { prisma } from './utils/db.server.ts'
import { getEnv } from './utils/env.server.ts'
import {
Expand Down Expand Up @@ -162,7 +162,7 @@ export async function loader({ request }: DataFunctionArgs) {
await authenticator.logout(request, { redirectTo: '/' })
}
const { toast, headers: toastHeaders } = await getToast(request)
const { confettiId, headers: confettiHeaders } = getConfetti(request)
// const { confettiId, headers: confettiHeaders } = getConfetti(request)

// Weather station data
const w_href: string =
Expand All @@ -184,13 +184,13 @@ export async function loader({ request }: DataFunctionArgs) {
},
ENV: getEnv(),
toast,
confettiId,
// confettiId,
},
{
headers: combineHeaders(
{ 'Server-Timing': timings.toString() },
toastHeaders,
confettiHeaders,
// confettiHeaders,
),
},
)
Expand Down Expand Up @@ -329,7 +329,7 @@ function App() {
<ThemeSwitch userPreference={data.requestInfo.userPrefs.theme} />
</div>
</div>
<Confetti id={data.confettiId} />
{/* <Confetti id={data.confettiId} /> */}
<EpicToaster toast={data.toast} />
</Document>
)
Expand Down
Loading

0 comments on commit d85adb9

Please sign in to comment.