Skip to content
This repository has been archived by the owner on Oct 2, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from isoppp/server-runner
Browse files Browse the repository at this point in the history
support express app runenr
  • Loading branch information
isoppp authored Sep 7, 2024
2 parents 258e1f1 + 67a7ba7 commit dc1e7fc
Show file tree
Hide file tree
Showing 14 changed files with 1,295 additions and 37 deletions.
39 changes: 37 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
/node_modules
*.log
.DS_Store
.env*
/.cache
/public/build

# ============ filter from ls -al .
/.dockerignore
/.env.local
#/.env.production
/.env.test
/.git
/.github
/.gitignore
/.idea
/.npmrc
/.scaffdog
/.storybook
#/.tool-versions
/Dockerfile
/README.md
#/app
/biome.jsonc
/build
/components.json
/docker-compose.yml
/eslint.config.js
#/index.ts
/lefthook.yml
/node_modules
#/package.json
#/pnpm-lock.yaml
#/postcss.config.js
#/prisma
#/public
#/server
/server-build
#/tailwind.config.ts
#/tsconfig.json
/vite.config.sb.ts
#/vite.config.ts
/vitest-example
/vitest.workspace.ts
5 changes: 5 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
NODE_ENV=production
APP_ENV=production

# this is for local testing, set by actual env variable in production
#DATABASE_URL="postgresql://postgres:postgres@localhost:5456/mydb?schema=public"
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ node_modules

/.cache
/build
/server-build
.env

*storybook.log
*storybook.log
43 changes: 27 additions & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# syntax = docker/dockerfile:1

# TODO almost 477MB, might be able to reduce it
# Adjust NODE_VERSION as desired
ARG NODE_VERSION=20
ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-slim AS base

RUN apt-get update -qq && \
apt-get install --no-install-recommends -y ca-certificates curl

# LABEL fly_launch_runtime="Node.js"
RUN apt-get update -qq
RUN apt-get install --no-install-recommends -y ca-certificates curl
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

# Node.js app lives here
WORKDIR /app
Expand All @@ -16,34 +17,44 @@ WORKDIR /app
RUN corepack enable
RUN pnpm --version

# Throw-away build stage to reduce size of final image
FROM base AS build
FROM base AS builder

# Install packages needed to build node modules
RUN apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3
WORKDIR /app

# Install node modules
# Install node modules and build
COPY . .
RUN pnpm i
RUN pnpm build

# Remove dev dependencies
# for monorepo: RUN rm -rf node_modules
# for monorepo: RUN pnpm install --prod
RUN pnpm prune --prod --ignore-scripts
RUN pnpm prune --prod

#CMD ["tail", "-f", "/dev/null"]

# Final stage for app image
FROM base AS prod

WORKDIR /app

# Copy built application
COPY --from=build /app /app

COPY --from=builder /app/node_modules /app/node_modules
COPY --from=builder /app/server-build /app/server-build
COPY --from=builder /app/build /app/build

COPY --from=builder /app/prisma /app/prisma
COPY --from=builder /app/package.json /app/package.json
COPY --from=builder /app/index.js /app/index.js

# Set production environment
ENV NODE_ENV="production"
ENV PORT=3000

# Set production environment
ENV PORT=3000

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 CMD curl --fail http://localhost:3000 || exit 1
CMD [ "pnpm", "start" ]
CMD [ "npm", "start" ]

18 changes: 14 additions & 4 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { PassThrough } from 'node:stream'
import { NonceProvider } from '@/lib/nonce'
import type { AppLoadContext, EntryContext } from '@remix-run/node'
import { createReadableStreamFromReadable } from '@remix-run/node'
import { RemixServer } from '@remix-run/react'
Expand All @@ -23,22 +24,27 @@ export default function handleRequest(
// biome-ignore lint/correctness/noUnusedVariables: ↑
loadContext: AppLoadContext,
) {
const nonce = loadContext.cspNonce?.toString() ?? ''
return isbot(request.headers.get('user-agent') || '')
? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext)
: handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext)
? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext, nonce)
: handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext, nonce)
}

function handleBotRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
nonce: string,
) {
return new Promise((resolve, reject) => {
let shellRendered = false
const { pipe, abort } = renderToPipeableStream(
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
<NonceProvider value={nonce}>
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} nonce={nonce} />
</NonceProvider>,
{
nonce,
onAllReady() {
shellRendered = true
const body = new PassThrough()
Expand Down Expand Up @@ -80,12 +86,16 @@ function handleBrowserRequest(
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
nonce: string,
) {
return new Promise((resolve, reject) => {
let shellRendered = false
const { pipe, abort } = renderToPipeableStream(
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
<NonceProvider value={nonce}>
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} nonce={nonce} />
</NonceProvider>,
{
nonce,
onShellReady() {
shellRendered = true
const body = new PassThrough()
Expand Down
5 changes: 5 additions & 0 deletions app/lib/nonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createContext, useContext } from 'react'

export const NonceContext = createContext<string>('')
export const NonceProvider = NonceContext.Provider
export const useNonce = () => useContext(NonceContext)
14 changes: 12 additions & 2 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { useNonce } from '@/lib/nonce'
import { trpc } from '@/lib/trpcClient'
import type { HeadersFunction } from '@remix-run/node'
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'
import './tailwind.css'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { httpBatchLink } from '@trpc/client'
import type { ReactNode } from 'react'

export const headers: HeadersFunction = ({ loaderHeaders }) => {
const headers = {
'Server-Timing': loaderHeaders.get('Server-Timing') ?? '',
}
return headers
}

export function Layout({ children }: { children: ReactNode }) {
const nonce = useNonce()
return (
<html lang='en'>
<head>
Expand All @@ -16,8 +26,8 @@ export function Layout({ children }: { children: ReactNode }) {
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
</body>
</html>
)
Expand Down
33 changes: 33 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// https://github.com/epicweb-dev/epic-stack/blob/main/index.js

import * as fs from 'node:fs'
import chalk from 'chalk'
import closeWithGrace from 'close-with-grace'
import sourceMapSupport from 'source-map-support'
sourceMapSupport.install({
retrieveSourceMap: (source) => {
// get source file without the `file://` prefix or `?t=...` suffix
const match = source.match(/^file:\/\/(.*)\?t=[.\d]+$/)
if (match) {
return {
url: source,
map: fs.readFileSync(`${match[1]}.map`, 'utf8'),
}
}
return null
},
})

closeWithGrace(async ({ err }) => {
if (err) {
console.error(chalk.red(err))
console.error(chalk.red(err.stack))
process.exit(1)
}
})

if (process.env.APP_ENV !== 'local') {
await import('./server-build/index.js')
} else {
await import('./server/index.ts')
}
32 changes: 27 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
"type": "module",
"packageManager": "[email protected]",
"scripts": {
"start": "echo $DATABASE_URL && pnpm prisma migrate deploy && remix-serve ./build/server/index.js",
"build": "remix vite:build",
"dev": "dotenv -e .env.local remix vite:dev",
"start": "dotenv -e .env.production -- bash -c 'echo $DATABASE_URL && npx prisma migrate deploy && node index.js'",
"build": "run-s build:*",
"build:remix": "remix vite:build",
"build:server": "tsx ./scripts/build-server.ts",
"dev": "dotenv -e .env.local -- node ./server/dev-server.js",
"lint": "eslint .",
"check": "pnpm biome check",
"lint-fix": "pnpm lint --fix && pnpm check --write",
Expand All @@ -26,20 +28,32 @@
"dependencies": {
"@prisma/client": "5.19.1",
"@radix-ui/react-icons": "1.3.0",
"@remix-run/express": "2.11.2",
"@remix-run/node": "2.11.2",
"@remix-run/react": "2.11.2",
"@remix-run/serve": "2.11.2",
"@tanstack/react-query": "5.55.0",
"@trpc/client": "11.0.0-rc.502",
"@trpc/react-query": "11.0.0-rc.502",
"@trpc/server": "11.0.0-rc.502",
"chalk": "5.3.0",
"class-variance-authority": "0.7.0",
"close-with-grace": "2.1.0",
"clsx": "2.1.1",
"compression": "1.7.4",
"dotenv-cli": "7.4.2",
"execa": "9.3.1",
"express": "4.19.2",
"express-rate-limit": "7.4.0",
"get-port": "7.1.0",
"helmet": "7.1.0",
"isbot": "5.1.17",
"lucide-react": "0.438.0",
"morgan": "1.10.0",
"prisma": "5.19.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"source-map-support": "0.5.21",
"tailwind-merge": "2.5.2",
"tailwindcss-animate": "1.0.7",
"valibot": "0.41.0"
Expand All @@ -55,18 +69,26 @@
"@storybook/react-vite": "8.2.9",
"@storybook/test": "8.2.9",
"@testing-library/react": "16.0.1",
"@types/compression": "1.7.5",
"@types/express": "4.17.21",
"@types/fs-extra": "11.0.4",
"@types/morgan": "1.9.9",
"@types/react": "18.3.5",
"@types/react-dom": "18.3.0",
"@types/source-map-support": "0.5.10",
"@typescript-eslint/parser": "8.4.0",
"@vitejs/plugin-react": "4.3.1",
"@vitest/browser": "2.0.5",
"autoprefixer": "10.4.20",
"dotenv-cli": "7.4.2",
"esbuild": "0.23.1",
"eslint": "9.9.1",
"eslint-plugin-storybook": "0.8.0",
"eslint-plugin-tailwindcss": "3.17.4",
"fs-extra": "11.2.0",
"glob": "11.0.0",
"globals": "15.9.0",
"lefthook": "1.7.15",
"npm-run-all": "4.1.5",
"playwright": "1.47.0",
"postcss": "8.4.45",
"scaffdog": "4.0.0",
Expand All @@ -79,7 +101,7 @@
"vitest": "2.0.5"
},
"engines": {
"node": ">=20.0.0"
"node": "22"
},
"eslintConfig": {
"extends": ["plugin:storybook/recommended"]
Expand Down
Loading

0 comments on commit dc1e7fc

Please sign in to comment.